Snapshot of all the packages used by the Dart VM's observatory
diff --git a/analyzer/lib/analyzer.dart b/analyzer/lib/analyzer.dart
new file mode 100644
index 0000000..83a72a1
--- /dev/null
+++ b/analyzer/lib/analyzer.dart
@@ -0,0 +1,129 @@
+// Copyright (c) 2013, 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 analyzer;
+
+import 'dart:io';
+
+import 'package:path/path.dart' as pathos;
+
+import 'src/error.dart';
+import 'src/generated/ast.dart';
+import 'src/generated/error.dart';
+import 'src/generated/parser.dart';
+import 'src/generated/scanner.dart';
+import 'src/generated/source_io.dart';
+import 'src/string_source.dart';
+
+export 'src/error.dart';
+export 'src/generated/ast.dart';
+export 'src/generated/error.dart';
+export 'src/generated/utilities_dart.dart';
+
+/// Parses a string of Dart code into an AST.
+///
+/// If [name] is passed, it's used in error messages as the name of the code
+/// being parsed.
+///
+/// Throws an [AnalyzerErrorGroup] if any errors occurred, unless
+/// [suppressErrors] is `true`, in which case any errors are discarded.
+///
+/// If [parseFunctionBodies] is [false] then only function signatures will be
+/// parsed.
+CompilationUnit parseCompilationUnit(String contents,
+    {String name, bool suppressErrors: false, bool parseFunctionBodies: true}) {
+  if (name == null) name = '<unknown source>';
+  var source = new StringSource(contents, name);
+  return _parseSource(contents, source,
+      suppressErrors: suppressErrors, parseFunctionBodies: parseFunctionBodies);
+}
+
+/// Parses a Dart file into an AST.
+///
+/// Throws an [AnalyzerErrorGroup] if any errors occurred, unless
+/// [suppressErrors] is `true`, in which case any errors are discarded.
+///
+/// If [parseFunctionBodies] is [false] then only function signatures will be
+/// parsed.
+CompilationUnit parseDartFile(String path,
+    {bool suppressErrors: false, bool parseFunctionBodies: true}) {
+  String contents = new File(path).readAsStringSync();
+  var sourceFactory = new SourceFactory([new FileUriResolver()]);
+
+  var absolutePath = pathos.absolute(path);
+  var source = sourceFactory.forUri(pathos.toUri(absolutePath).toString());
+  if (source == null) {
+    throw new ArgumentError("Can't get source for path $path");
+  }
+  if (!source.exists()) {
+    throw new ArgumentError("Source $source doesn't exist");
+  }
+
+  return _parseSource(contents, source,
+      suppressErrors: suppressErrors, parseFunctionBodies: parseFunctionBodies);
+}
+
+CompilationUnit _parseSource(String contents, Source source,
+    {bool suppressErrors: false, bool parseFunctionBodies: true}) {
+  var reader = new CharSequenceReader(contents);
+  var errorCollector = new _ErrorCollector();
+  var scanner = new Scanner(source, reader, errorCollector);
+  var token = scanner.tokenize();
+  var parser = new Parser(source, errorCollector)
+    ..parseFunctionBodies = parseFunctionBodies;
+  var unit = parser.parseCompilationUnit(token)
+    ..lineInfo = new LineInfo(scanner.lineStarts);
+
+  if (errorCollector.hasErrors && !suppressErrors) throw errorCollector.group;
+
+  return unit;
+}
+
+/// Parses the script tag and directives in a string of Dart code into an AST.
+///
+/// Stops parsing when the first non-directive is encountered. The rest of the
+/// string will not be parsed.
+///
+/// If [name] is passed, it's used in error messages as the name of the code
+/// being parsed.
+///
+/// Throws an [AnalyzerErrorGroup] if any errors occurred, unless
+/// [suppressErrors] is `true`, in which case any errors are discarded.
+CompilationUnit parseDirectives(String contents,
+    {String name, bool suppressErrors: false}) {
+  if (name == null) name = '<unknown source>';
+  var source = new StringSource(contents, name);
+  var errorCollector = new _ErrorCollector();
+  var reader = new CharSequenceReader(contents);
+  var scanner = new Scanner(source, reader, errorCollector);
+  var token = scanner.tokenize();
+  var parser = new Parser(source, errorCollector);
+  var unit = parser.parseDirectives(token);
+  unit.lineInfo = new LineInfo(scanner.lineStarts);
+
+  if (errorCollector.hasErrors && !suppressErrors) throw errorCollector.group;
+
+  return unit;
+}
+
+/// Converts an AST node representing a string literal into a [String].
+String stringLiteralToString(StringLiteral literal) {
+  return literal.stringValue;
+}
+
+/// A simple error listener that collects errors into an [AnalysisErrorGroup].
+class _ErrorCollector extends AnalysisErrorListener {
+  final _errors = <AnalysisError>[];
+
+  _ErrorCollector();
+
+  /// The group of errors collected.
+  AnalyzerErrorGroup get group =>
+      new AnalyzerErrorGroup.fromAnalysisErrors(_errors);
+
+  /// Whether any errors where collected.
+  bool get hasErrors => !_errors.isEmpty;
+
+  void onError(AnalysisError error) => _errors.add(error);
+}
diff --git a/analyzer/lib/file_system/file_system.dart b/analyzer/lib/file_system/file_system.dart
new file mode 100644
index 0000000..6fb9885
--- /dev/null
+++ b/analyzer/lib/file_system/file_system.dart
@@ -0,0 +1,184 @@
+// 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 file_system;
+
+import 'dart:async';
+
+import 'package:analyzer/src/generated/source.dart';
+import 'package:path/path.dart';
+import 'package:watcher/watcher.dart';
+
+/**
+ * [File]s are leaf [Resource]s which contain data.
+ */
+abstract class File extends Resource {
+  /**
+   * Return the last-modified stamp of the file.
+   * Throws [FileSystemException] if the file does not exist.
+   */
+  int get modificationStamp;
+
+  /**
+   * Create a new [Source] instance that serves this file.
+   */
+  Source createSource([Uri uri]);
+
+  /**
+   * Synchronously read the entire file contents as a [String].
+   * Throws [FileSystemException] if the file does not exist.
+   */
+  String readAsStringSync();
+}
+
+/**
+ * Base class for all file system exceptions.
+ */
+class FileSystemException implements Exception {
+  final String path;
+  final String message;
+
+  FileSystemException(this.path, this.message);
+
+  String toString() => 'FileSystemException(path=$path; message=$message)';
+}
+
+/**
+ * [Folder]s are [Resource]s which may contain files and/or other folders.
+ */
+abstract class Folder extends Resource {
+  /**
+   * Watch for changes to the files inside this folder (and in any nested
+   * folders, including folders reachable via links).
+   */
+  Stream<WatchEvent> get changes;
+
+  /**
+   * If the path [path] is a relative path, convert it to an absolute path
+   * by interpreting it relative to this folder.  If it is already an aboslute
+   * path, then don't change it.
+   *
+   * However, regardless of whether [path] is relative or absolute, normalize
+   * it by removing path components of the form '.' or '..'.
+   */
+  String canonicalizePath(String path);
+
+  /**
+   * Return `true` if absolute [path] references a resource in this folder.
+   */
+  bool contains(String path);
+
+  /**
+   * Return an existing child [Resource] with the given [relPath].
+   * Return a not existing [File] if no such child exist.
+   */
+  Resource getChild(String relPath);
+
+  /**
+   * Return a [Folder] representing a child [Resource] with the given
+   * [relPath].  This call does not check whether a folder with the given name
+   * exists on the filesystem--client must call the [Folder]'s `exists` getter
+   * to determine whether the folder actually exists.
+   */
+  Folder getChildAssumingFolder(String relPath);
+
+  /**
+   * Return a list of existing direct children [Resource]s (folders and files)
+   * in this folder, in no particular order.
+   */
+  List<Resource> getChildren();
+}
+
+/**
+ * The abstract class [Resource] is an abstraction of file or folder.
+ */
+abstract class Resource {
+  /**
+   * Return `true` if this resource exists.
+   */
+  bool get exists;
+
+  /**
+   * Return the [Folder] that contains this resource, or `null` if this resource
+   * is a root folder.
+   */
+  Folder get parent;
+
+  /**
+   * Return the full path to this resource.
+   */
+  String get path;
+
+  /**
+   * Return a short version of the name that can be displayed to the user to
+   * denote this resource.
+   */
+  String get shortName;
+
+  /**
+   * Return `true` if absolute [path] references this resource or a resource in
+   * this folder.
+   */
+  bool isOrContains(String path);
+}
+
+/**
+ * Instances of the class [ResourceProvider] convert [String] paths into
+ * [Resource]s.
+ */
+abstract class ResourceProvider {
+  /**
+   * Get the path context used by this resource provider.
+   */
+  Context get pathContext;
+
+  /**
+   * Return the [Resource] that corresponds to the given [path].
+   */
+  Resource getResource(String path);
+
+  /**
+   * Return the folder in which the plugin with the given [pluginId] can store
+   * state that will persist across sessions. The folder returned for a given id
+   * will not be returned for a different id, ensuring that plugins do not need
+   * to be concerned with file name collisions with other plugins, assuming that
+   * the plugin ids are unique. The plugin ids must be valid folder names.
+   */
+  Folder getStateLocation(String pluginId);
+}
+
+/**
+ * A [UriResolver] for [Resource]s.
+ */
+class ResourceUriResolver extends UriResolver {
+  /**
+   * The name of the `file` scheme.
+   */
+  static String _FILE_SCHEME = "file";
+
+  final ResourceProvider _provider;
+
+  ResourceUriResolver(this._provider);
+
+  @override
+  Source resolveAbsolute(Uri uri) {
+    if (!_isFileUri(uri)) {
+      return null;
+    }
+    Resource resource =
+        _provider.getResource(_provider.pathContext.fromUri(uri));
+    if (resource is File) {
+      return resource.createSource(uri);
+    }
+    return null;
+  }
+
+  @override
+  Uri restoreAbsolute(Source source) => source.uri;
+
+  /**
+   * Return `true` if the given [uri] is a `file` URI.
+   */
+  static bool _isFileUri(Uri uri) => uri.scheme == _FILE_SCHEME;
+}
diff --git a/analyzer/lib/file_system/memory_file_system.dart b/analyzer/lib/file_system/memory_file_system.dart
new file mode 100644
index 0000000..8d1f114
--- /dev/null
+++ b/analyzer/lib/file_system/memory_file_system.dart
@@ -0,0 +1,470 @@
+// 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 memory_file_system;
+
+import 'dart:async';
+import 'dart:collection';
+
+import 'package:analyzer/src/generated/engine.dart' show TimestampedData;
+import 'package:analyzer/src/generated/source_io.dart';
+import 'package:path/path.dart';
+import 'package:watcher/watcher.dart';
+
+import 'file_system.dart';
+
+/**
+ * An in-memory implementation of [ResourceProvider].
+ * Use `/` as a path separator.
+ */
+class MemoryResourceProvider implements ResourceProvider {
+  final Map<String, _MemoryResource> _pathToResource =
+      new HashMap<String, _MemoryResource>();
+  final Map<String, String> _pathToContent = new HashMap<String, String>();
+  final Map<String, int> _pathToTimestamp = new HashMap<String, int>();
+  final Map<String, List<StreamController<WatchEvent>>> _pathToWatchers =
+      new HashMap<String, List<StreamController<WatchEvent>>>();
+  int nextStamp = 0;
+
+  @override
+  Context get pathContext => posix;
+
+  /**
+   * Delete the file with the given path.
+   */
+  void deleteFile(String path) {
+    _checkFileAtPath(path);
+    _pathToResource.remove(path);
+    _pathToContent.remove(path);
+    _pathToTimestamp.remove(path);
+    _notifyWatchers(path, ChangeType.REMOVE);
+  }
+
+  /**
+   * Delete the folder with the given path
+   * and recurively delete nested files and folders.
+   */
+  void deleteFolder(String path) {
+    _checkFolderAtPath(path);
+    _MemoryFolder folder = _pathToResource[path];
+    for (Resource child in folder.getChildren()) {
+      if (child is File) {
+        deleteFile(child.path);
+      } else if (child is Folder) {
+        deleteFolder(child.path);
+      } else {
+        throw 'failed to delete resource: $child';
+      }
+    }
+    _pathToResource.remove(path);
+    _pathToContent.remove(path);
+    _pathToTimestamp.remove(path);
+    _notifyWatchers(path, ChangeType.REMOVE);
+  }
+
+  @override
+  Resource getResource(String path) {
+    path = posix.normalize(path);
+    Resource resource = _pathToResource[path];
+    if (resource == null) {
+      resource = new _MemoryFile(this, path);
+    }
+    return resource;
+  }
+
+  @override
+  Folder getStateLocation(String pluginId) {
+    return newFolder('/user/home/$pluginId');
+  }
+
+  void modifyFile(String path, String content) {
+    _checkFileAtPath(path);
+    _pathToContent[path] = content;
+    _pathToTimestamp[path] = nextStamp++;
+    _notifyWatchers(path, ChangeType.MODIFY);
+  }
+
+  /**
+   * Create a resource representing a dummy link (that is, a File object which
+   * appears in its parent directory, but whose `exists` property is false)
+   */
+  File newDummyLink(String path) {
+    path = posix.normalize(path);
+    newFolder(posix.dirname(path));
+    _MemoryDummyLink link = new _MemoryDummyLink(this, path);
+    _pathToResource[path] = link;
+    _pathToTimestamp[path] = nextStamp++;
+    _notifyWatchers(path, ChangeType.ADD);
+    return link;
+  }
+
+  File newFile(String path, String content, [int stamp]) {
+    path = posix.normalize(path);
+    newFolder(posix.dirname(path));
+    _MemoryFile file = new _MemoryFile(this, path);
+    _pathToResource[path] = file;
+    _pathToContent[path] = content;
+    _pathToTimestamp[path] = stamp != null ? stamp : nextStamp++;
+    _notifyWatchers(path, ChangeType.ADD);
+    return file;
+  }
+
+  Folder newFolder(String path) {
+    path = posix.normalize(path);
+    if (!path.startsWith('/')) {
+      throw new ArgumentError("Path must start with '/'");
+    }
+    _MemoryResource resource = _pathToResource[path];
+    if (resource == null) {
+      String parentPath = posix.dirname(path);
+      if (parentPath != path) {
+        newFolder(parentPath);
+      }
+      _MemoryFolder folder = new _MemoryFolder(this, path);
+      _pathToResource[path] = folder;
+      _pathToTimestamp[path] = nextStamp++;
+      return folder;
+    } else if (resource is _MemoryFolder) {
+      return resource;
+    } else {
+      String message =
+          'Folder expected at ' "'$path'" 'but ${resource.runtimeType} found';
+      throw new ArgumentError(message);
+    }
+  }
+
+  File updateFile(String path, String content, [int stamp]) {
+    path = posix.normalize(path);
+    newFolder(posix.dirname(path));
+    _MemoryFile file = new _MemoryFile(this, path);
+    _pathToResource[path] = file;
+    _pathToContent[path] = content;
+    _pathToTimestamp[path] = stamp != null ? stamp : nextStamp++;
+    _notifyWatchers(path, ChangeType.MODIFY);
+    return file;
+  }
+
+  void _checkFileAtPath(String path) {
+    _MemoryResource resource = _pathToResource[path];
+    if (resource is! _MemoryFile) {
+      throw new ArgumentError(
+          'File expected at "$path" but ${resource.runtimeType} found');
+    }
+  }
+
+  void _checkFolderAtPath(String path) {
+    _MemoryResource resource = _pathToResource[path];
+    if (resource is! _MemoryFolder) {
+      throw new ArgumentError(
+          'Folder expected at "$path" but ${resource.runtimeType} found');
+    }
+  }
+
+  void _notifyWatchers(String path, ChangeType changeType) {
+    _pathToWatchers.forEach((String watcherPath,
+        List<StreamController<WatchEvent>> streamControllers) {
+      if (posix.isWithin(watcherPath, path)) {
+        for (StreamController<WatchEvent> streamController
+            in streamControllers) {
+          streamController.add(new WatchEvent(changeType, path));
+        }
+      }
+    });
+  }
+}
+
+/**
+ * An in-memory implementation of [File] which acts like a symbolic link to a
+ * non-existent file.
+ */
+class _MemoryDummyLink extends _MemoryResource implements File {
+  _MemoryDummyLink(MemoryResourceProvider provider, String path)
+      : super(provider, path);
+
+  @override
+  bool get exists => false;
+
+  int get modificationStamp {
+    int stamp = _provider._pathToTimestamp[path];
+    if (stamp == null) {
+      throw new FileSystemException(path, "File does not exist");
+    }
+    return stamp;
+  }
+
+  String get _content {
+    throw new FileSystemException(path, 'File could not be read');
+  }
+
+  @override
+  Source createSource([Uri uri]) {
+    throw new FileSystemException(path, 'File could not be read');
+  }
+
+  @override
+  bool isOrContains(String path) {
+    return path == this.path;
+  }
+
+  @override
+  String readAsStringSync() {
+    throw new FileSystemException(path, 'File could not be read');
+  }
+}
+
+/**
+ * An in-memory implementation of [File].
+ */
+class _MemoryFile extends _MemoryResource implements File {
+  _MemoryFile(MemoryResourceProvider provider, String path)
+      : super(provider, path);
+
+  @override
+  bool get exists => _provider._pathToResource[path] is _MemoryFile;
+
+  int get modificationStamp {
+    int stamp = _provider._pathToTimestamp[path];
+    if (stamp == null) {
+      throw new FileSystemException(path, 'File "$path" does not exist.');
+    }
+    return stamp;
+  }
+
+  String get _content {
+    String content = _provider._pathToContent[path];
+    if (content == null) {
+      throw new FileSystemException(path, 'File "$path" does not exist.');
+    }
+    return content;
+  }
+
+  @override
+  Source createSource([Uri uri]) {
+    if (uri == null) {
+      uri = posix.toUri(path);
+    }
+    return new _MemoryFileSource(this, uri);
+  }
+
+  @override
+  bool isOrContains(String path) {
+    return path == this.path;
+  }
+
+  @override
+  String readAsStringSync() {
+    String content = _provider._pathToContent[path];
+    if (content == null) {
+      throw new FileSystemException(path, 'File "$path" does not exist.');
+    }
+    return content;
+  }
+}
+
+/**
+ * An in-memory implementation of [Source].
+ */
+class _MemoryFileSource extends Source {
+  /**
+   * Map from encoded URI/filepath pair to a unique integer identifier.  This
+   * identifier is used for equality tests and hash codes.
+   *
+   * The URI and filepath are joined into a pair by separating them with an '@'
+   * character.
+   */
+  static final Map<String, int> _idTable = new HashMap<String, int>();
+
+  final _MemoryFile file;
+
+  final Uri uri;
+
+  /**
+   * The unique ID associated with this [_MemoryFileSource].
+   */
+  final int id;
+
+  _MemoryFileSource(_MemoryFile file, Uri uri)
+      : uri = uri,
+        file = file,
+        id = _idTable.putIfAbsent('$uri@${file.path}', () => _idTable.length);
+
+  @override
+  TimestampedData<String> get contents {
+    return new TimestampedData<String>(modificationStamp, file._content);
+  }
+
+  @override
+  String get encoding {
+    return uri.toString();
+  }
+
+  @override
+  String get fullName => file.path;
+
+  @override
+  int get hashCode => id;
+
+  @override
+  bool get isInSystemLibrary => uriKind == UriKind.DART_URI;
+
+  @override
+  int get modificationStamp {
+    try {
+      return file.modificationStamp;
+    } on FileSystemException {
+      return -1;
+    }
+  }
+
+  @override
+  String get shortName => file.shortName;
+
+  @override
+  UriKind get uriKind {
+    String scheme = uri.scheme;
+    if (scheme == PackageUriResolver.PACKAGE_SCHEME) {
+      return UriKind.PACKAGE_URI;
+    } else if (scheme == DartUriResolver.DART_SCHEME) {
+      return UriKind.DART_URI;
+    } else if (scheme == FileUriResolver.FILE_SCHEME) {
+      return UriKind.FILE_URI;
+    }
+    return UriKind.FILE_URI;
+  }
+
+  @override
+  bool operator ==(other) {
+    return other is _MemoryFileSource && other.id == id;
+  }
+
+  @override
+  bool exists() => file.exists;
+
+  @override
+  Uri resolveRelativeUri(Uri relativeUri) {
+    return uri.resolveUri(relativeUri);
+  }
+
+  @override
+  String toString() => file.toString();
+}
+
+/**
+ * An in-memory implementation of [Folder].
+ */
+class _MemoryFolder extends _MemoryResource implements Folder {
+  _MemoryFolder(MemoryResourceProvider provider, String path)
+      : super(provider, path);
+
+  @override
+  Stream<WatchEvent> get changes {
+    StreamController<WatchEvent> streamController =
+        new StreamController<WatchEvent>();
+    if (!_provider._pathToWatchers.containsKey(path)) {
+      _provider._pathToWatchers[path] = <StreamController<WatchEvent>>[];
+    }
+    _provider._pathToWatchers[path].add(streamController);
+    streamController.done.then((_) {
+      _provider._pathToWatchers[path].remove(streamController);
+      if (_provider._pathToWatchers[path].isEmpty) {
+        _provider._pathToWatchers.remove(path);
+      }
+    });
+    return streamController.stream;
+  }
+
+  @override
+  bool get exists => _provider._pathToResource[path] is _MemoryFolder;
+
+  @override
+  String canonicalizePath(String relPath) {
+    relPath = posix.normalize(relPath);
+    String childPath = posix.join(path, relPath);
+    childPath = posix.normalize(childPath);
+    return childPath;
+  }
+
+  @override
+  bool contains(String path) {
+    return posix.isWithin(this.path, path);
+  }
+
+  @override
+  Resource getChild(String relPath) {
+    String childPath = canonicalizePath(relPath);
+    _MemoryResource resource = _provider._pathToResource[childPath];
+    if (resource == null) {
+      resource = new _MemoryFile(_provider, childPath);
+    }
+    return resource;
+  }
+
+  @override
+  _MemoryFolder getChildAssumingFolder(String relPath) {
+    String childPath = canonicalizePath(relPath);
+    _MemoryResource resource = _provider._pathToResource[childPath];
+    if (resource is _MemoryFolder) {
+      return resource;
+    }
+    return new _MemoryFolder(_provider, childPath);
+  }
+
+  @override
+  List<Resource> getChildren() {
+    if (!exists) {
+      throw new FileSystemException(path, 'Folder does not exist.');
+    }
+    List<Resource> children = <Resource>[];
+    _provider._pathToResource.forEach((resourcePath, resource) {
+      if (posix.dirname(resourcePath) == path) {
+        children.add(resource);
+      }
+    });
+    return children;
+  }
+
+  @override
+  bool isOrContains(String path) {
+    if (path == this.path) {
+      return true;
+    }
+    return contains(path);
+  }
+}
+
+/**
+ * An in-memory implementation of [Resource].
+ */
+abstract class _MemoryResource implements Resource {
+  final MemoryResourceProvider _provider;
+  final String path;
+
+  _MemoryResource(this._provider, this.path);
+
+  @override
+  get hashCode => path.hashCode;
+
+  @override
+  Folder get parent {
+    String parentPath = posix.dirname(path);
+    if (parentPath == path) {
+      return null;
+    }
+    return _provider.getResource(parentPath);
+  }
+
+  @override
+  String get shortName => posix.basename(path);
+
+  @override
+  bool operator ==(other) {
+    if (runtimeType != other.runtimeType) {
+      return false;
+    }
+    return path == other.path;
+  }
+
+  @override
+  String toString() => path;
+}
diff --git a/analyzer/lib/file_system/physical_file_system.dart b/analyzer/lib/file_system/physical_file_system.dart
new file mode 100644
index 0000000..2cd391a
--- /dev/null
+++ b/analyzer/lib/file_system/physical_file_system.dart
@@ -0,0 +1,214 @@
+// 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 physical_file_system;
+
+import 'dart:async';
+import 'dart:io' as io;
+
+import 'package:analyzer/src/generated/java_io.dart';
+import 'package:analyzer/src/generated/source_io.dart';
+import 'package:path/path.dart';
+import 'package:watcher/watcher.dart';
+
+import 'file_system.dart';
+
+/**
+ * A `dart:io` based implementation of [ResourceProvider].
+ */
+class PhysicalResourceProvider implements ResourceProvider {
+  static final NORMALIZE_EOL_ALWAYS =
+      (String string) => string.replaceAll(new RegExp('\r\n?'), '\n');
+
+  static final PhysicalResourceProvider INSTANCE =
+      new PhysicalResourceProvider(null);
+
+  /**
+   * The name of the directory containing plugin specific subfolders used to
+   * store data across sessions.
+   */
+  static final String SERVER_DIR = ".dartServer";
+
+  PhysicalResourceProvider(String fileReadMode(String s)) {
+    if (fileReadMode != null) {
+      FileBasedSource.fileReadMode = fileReadMode;
+    }
+  }
+
+  @override
+  Context get pathContext => io.Platform.isWindows ? windows : posix;
+
+  @override
+  Resource getResource(String path) {
+    if (io.FileSystemEntity.isDirectorySync(path)) {
+      io.Directory directory = new io.Directory(path);
+      return new _PhysicalFolder(directory);
+    } else {
+      io.File file = new io.File(path);
+      return new _PhysicalFile(file);
+    }
+  }
+
+  @override
+  Folder getStateLocation(String pluginId) {
+    String home;
+    if (io.Platform.isWindows) {
+      home = io.Platform.environment['LOCALAPPDATA'];
+    } else {
+      home = io.Platform.environment['HOME'];
+    }
+    if (home != null && io.FileSystemEntity.isDirectorySync(home)) {
+      io.Directory directory =
+          new io.Directory(join(home, SERVER_DIR, pluginId));
+      directory.createSync(recursive: true);
+      return new _PhysicalFolder(directory);
+    }
+    return null;
+  }
+}
+
+/**
+ * A `dart:io` based implementation of [File].
+ */
+class _PhysicalFile extends _PhysicalResource implements File {
+  _PhysicalFile(io.File file) : super(file);
+
+  @override
+  int get modificationStamp {
+    try {
+      io.File file = _entry as io.File;
+      return file.lastModifiedSync().millisecondsSinceEpoch;
+    } on io.FileSystemException catch (exception) {
+      throw new FileSystemException(exception.path, exception.message);
+    }
+  }
+
+  @override
+  Source createSource([Uri uri]) {
+    io.File file = _entry as io.File;
+    JavaFile javaFile = new JavaFile(file.absolute.path);
+    if (uri == null) {
+      uri = javaFile.toURI();
+    }
+    return new FileBasedSource.con2(uri, javaFile);
+  }
+
+  @override
+  bool isOrContains(String path) {
+    return path == this.path;
+  }
+
+  @override
+  String readAsStringSync() {
+    try {
+      io.File file = _entry as io.File;
+      return file.readAsStringSync();
+    } on io.FileSystemException catch (exception) {
+      throw new FileSystemException(exception.path, exception.message);
+    }
+  }
+}
+
+/**
+ * A `dart:io` based implementation of [Folder].
+ */
+class _PhysicalFolder extends _PhysicalResource implements Folder {
+  _PhysicalFolder(io.Directory directory) : super(directory);
+
+  @override
+  Stream<WatchEvent> get changes => new DirectoryWatcher(_entry.path).events;
+
+  @override
+  String canonicalizePath(String relPath) {
+    return normalize(join(_entry.absolute.path, relPath));
+  }
+
+  @override
+  bool contains(String path) {
+    return isWithin(this.path, path);
+  }
+
+  @override
+  Resource getChild(String relPath) {
+    String canonicalPath = canonicalizePath(relPath);
+    return PhysicalResourceProvider.INSTANCE.getResource(canonicalPath);
+  }
+
+  @override
+  _PhysicalFolder getChildAssumingFolder(String relPath) {
+    String canonicalPath = canonicalizePath(relPath);
+    io.Directory directory = new io.Directory(canonicalPath);
+    return new _PhysicalFolder(directory);
+  }
+
+  @override
+  List<Resource> getChildren() {
+    try {
+      List<Resource> children = <Resource>[];
+      io.Directory directory = _entry as io.Directory;
+      List<io.FileSystemEntity> entries = directory.listSync(recursive: false);
+      int numEntries = entries.length;
+      for (int i = 0; i < numEntries; i++) {
+        io.FileSystemEntity entity = entries[i];
+        if (entity is io.Directory) {
+          children.add(new _PhysicalFolder(entity));
+        } else if (entity is io.File) {
+          children.add(new _PhysicalFile(entity));
+        }
+      }
+      return children;
+    } on io.FileSystemException catch (exception) {
+      throw new FileSystemException(exception.path, exception.message);
+    }
+  }
+
+  @override
+  bool isOrContains(String path) {
+    if (path == this.path) {
+      return true;
+    }
+    return contains(path);
+  }
+}
+
+/**
+ * A `dart:io` based implementation of [Resource].
+ */
+abstract class _PhysicalResource implements Resource {
+  final io.FileSystemEntity _entry;
+
+  _PhysicalResource(this._entry);
+
+  @override
+  bool get exists => _entry.existsSync();
+
+  @override
+  get hashCode => path.hashCode;
+
+  @override
+  Folder get parent {
+    String parentPath = dirname(path);
+    if (parentPath == path) {
+      return null;
+    }
+    return new _PhysicalFolder(new io.Directory(parentPath));
+  }
+
+  @override
+  String get path => _entry.absolute.path;
+
+  @override
+  String get shortName => basename(path);
+
+  @override
+  bool operator ==(other) {
+    if (runtimeType != other.runtimeType) {
+      return false;
+    }
+    return path == other.path;
+  }
+
+  @override
+  String toString() => path;
+}
diff --git a/analyzer/lib/instrumentation/file_instrumentation.dart b/analyzer/lib/instrumentation/file_instrumentation.dart
new file mode 100644
index 0000000..ba23b68
--- /dev/null
+++ b/analyzer/lib/instrumentation/file_instrumentation.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2015, 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 file_instrumentation;
+
+import 'dart:io';
+
+import 'package:analyzer/instrumentation/instrumentation.dart';
+
+/**
+ * An [InstrumentationServer] that writes to a file.
+ */
+class FileInstrumentationServer implements InstrumentationServer {
+  IOSink _sink;
+
+  FileInstrumentationServer(String path) {
+    File file = new File(path);
+    _sink = file.openWrite();
+  }
+
+  @override
+  void log(String message) {
+    _sink.writeln(message);
+  }
+
+  @override
+  void logWithPriority(String message) {
+    log(message);
+  }
+
+  @override
+  void shutdown() {
+    _sink.close();
+    _sink = null;
+  }
+}
diff --git a/analyzer/lib/instrumentation/instrumentation.dart b/analyzer/lib/instrumentation/instrumentation.dart
new file mode 100644
index 0000000..ccb2149
--- /dev/null
+++ b/analyzer/lib/instrumentation/instrumentation.dart
@@ -0,0 +1,366 @@
+// 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 instrumentation;
+
+import 'dart:convert';
+
+/**
+ * A container with analysis performance constants.
+ */
+class AnalysisPerformanceKind {
+  static const String FULL = 'analysis_full';
+  static const String INCREMENTAL = 'analysis_incremental';
+}
+
+/**
+ * The interface used by client code to communicate with an instrumentation
+ * server.
+ */
+abstract class InstrumentationServer {
+  /**
+   * Pass the given [message] to the instrumentation server so that it will be
+   * logged with other messages.
+   *
+   * This method should be used for most logging.
+   */
+  void log(String message);
+
+  /**
+   * Pass the given [message] to the instrumentation server so that it will be
+   * logged with other messages.
+   *
+   * This method should only be used for logging high priority messages, such as
+   * exceptions that cause the server to shutdown.
+   */
+  void logWithPriority(String message);
+
+  /**
+   * Signal that the client is done communicating with the instrumentation
+   * server. This method should be invoked exactly one time and no other methods
+   * should be invoked on this instance after this method has been invoked.
+   */
+  void shutdown();
+}
+
+/**
+ * The interface used by client code to communicate with an instrumentation
+ * server by wrapping an [InstrumentationServer].
+ */
+class InstrumentationService {
+  /**
+   * An instrumentation service that will not log any instrumentation data.
+   */
+  static final InstrumentationService NULL_SERVICE =
+      new InstrumentationService(null);
+
+  static const String TAG_ANALYSIS_TASK = 'Task';
+  static const String TAG_ERROR = 'Err';
+  static const String TAG_EXCEPTION = 'Ex';
+  static const String TAG_FILE_READ = 'Read';
+  static const String TAG_LOG_ENTRY = 'Log';
+  static const String TAG_NOTIFICATION = 'Noti';
+  static const String TAG_PERFORMANCE = 'Perf';
+  static const String TAG_REQUEST = 'Req';
+  static const String TAG_RESPONSE = 'Res';
+  static const String TAG_SUBPROCESS_START = 'SPStart';
+  static const String TAG_SUBPROCESS_RESULT = 'SPResult';
+  static const String TAG_VERSION = 'Ver';
+  static const String TAG_WATCH_EVENT = 'Watch';
+
+  /**
+   * The instrumentation server used to communicate with the server, or `null`
+   * if instrumentation data should not be logged.
+   */
+  InstrumentationServer _instrumentationServer;
+
+  /**
+   * Counter used to generate unique ID's for [logSubprocessStart].
+   */
+  int _subprocessCounter = 0;
+
+  /**
+   * Initialize a newly created instrumentation service to comunicate with the
+   * given [instrumentationServer].
+   */
+  InstrumentationService(this._instrumentationServer);
+
+  /**
+   * Return `true` if this [InstrumentationService] was initialized with a
+   * non-`null` server (and hence instrumentation is active).
+   */
+  bool get isActive => _instrumentationServer != null;
+
+  /**
+   * The current time, expressed as a decimal encoded number of milliseconds.
+   */
+  String get _timestamp => new DateTime.now().millisecondsSinceEpoch.toString();
+
+  /**
+   * Log that an analysis task is being performed in the given [context]. The
+   * task has the given [description].
+   */
+  void logAnalysisTask(String context, String description) {
+    if (_instrumentationServer != null) {
+      _instrumentationServer
+          .log(_join([TAG_ANALYSIS_TASK, context, description]));
+    }
+  }
+
+  /**
+   * Log the fact that an error, described by the given [message], has occurred.
+   */
+  void logError(String message) {
+    _log(TAG_ERROR, message);
+  }
+
+  /**
+   * Log that the given non-priority [exception] was thrown, with the given
+   * [stackTrace].
+   */
+  void logException(dynamic exception, StackTrace stackTrace) {
+    if (_instrumentationServer != null) {
+      String message = _toString(exception);
+      String trace = _toString(stackTrace);
+      _instrumentationServer.log(_join([TAG_EXCEPTION, message, trace]));
+    }
+  }
+
+  /**
+   * Log that the contents of the file with the given [path] were read. The file
+   * had the given [content] and [modificationTime].
+   */
+  void logFileRead(String path, int modificationTime, String content) {
+    if (_instrumentationServer != null) {
+      String timeStamp = _toString(modificationTime);
+      _instrumentationServer
+          .log(_join([TAG_FILE_READ, path, timeStamp, content]));
+    }
+  }
+
+  /**
+   * Log that a log entry that was written to the analysis engine's log. The log
+   * entry has the given [level] and [message], and was created at the given
+   * [time].
+   */
+  void logLogEntry(String level, DateTime time, String message) {
+    if (_instrumentationServer != null) {
+      String timeStamp =
+          time == null ? 'null' : time.millisecondsSinceEpoch.toString();
+      _instrumentationServer
+          .log(_join([TAG_LOG_ENTRY, level, timeStamp, message]));
+    }
+  }
+
+  /**
+   * Log that a notification has been sent to the client.
+   */
+  void logNotification(String notification) {
+    _log(TAG_NOTIFICATION, notification);
+  }
+
+  /**
+   * Log the given performance fact.
+   */
+  void logPerformance(String kind, Stopwatch sw, String message) {
+    sw.stop();
+    String elapsed = sw.elapsedMilliseconds.toString();
+    if (_instrumentationServer != null) {
+      _instrumentationServer
+          .log(_join([TAG_PERFORMANCE, kind, elapsed, message]));
+    }
+  }
+
+  /**
+   * Log that the given priority [exception] was thrown, with the given
+   * [stackTrace].
+   */
+  void logPriorityException(dynamic exception, StackTrace stackTrace) {
+    if (_instrumentationServer != null) {
+      String message = _toString(exception);
+      String trace = _toString(stackTrace);
+      _instrumentationServer
+          .logWithPriority(_join([TAG_EXCEPTION, message, trace]));
+    }
+  }
+
+  /**
+   * Log that a request has been sent to the client.
+   */
+  void logRequest(String request) {
+    _log(TAG_REQUEST, request);
+  }
+
+  /**
+   * Log that a response has been sent to the client.
+   */
+  void logResponse(String response) {
+    _log(TAG_RESPONSE, response);
+  }
+
+  /**
+   * Log the result of executing a subprocess.  [subprocessId] should be the
+   * unique IDreturned by [logSubprocessStart].
+   */
+  void logSubprocessResult(
+      int subprocessId, int exitCode, String stdout, String stderr) {
+    if (_instrumentationServer != null) {
+      _instrumentationServer.log(_join([
+        TAG_SUBPROCESS_RESULT,
+        subprocessId.toString(),
+        exitCode.toString(),
+        JSON.encode(stdout),
+        JSON.encode(stderr)
+      ]));
+    }
+  }
+
+  /**
+   * Log that the given subprocess is about to be executed.  Returns a unique
+   * identifier that can be used to identify the subprocess for later log
+   * entries.
+   */
+  int logSubprocessStart(
+      String executablePath, List<String> arguments, String workingDirectory) {
+    int subprocessId = _subprocessCounter++;
+    if (_instrumentationServer != null) {
+      _instrumentationServer.log(_join([
+        TAG_SUBPROCESS_START,
+        subprocessId.toString(),
+        executablePath,
+        workingDirectory,
+        JSON.encode(arguments)
+      ]));
+    }
+    return subprocessId;
+  }
+
+  /**
+   * Signal that the client has started analysis server.
+   * This method should be invoked exactly one time.
+   */
+  void logVersion(String uuid, String clientId, String clientVersion,
+      String serverVersion, String sdkVersion) {
+    String normalize(String value) =>
+        value != null && value.length > 0 ? value : 'unknown';
+
+    if (_instrumentationServer != null) {
+      _instrumentationServer.logWithPriority(_join([
+        TAG_VERSION,
+        uuid,
+        normalize(clientId),
+        normalize(clientVersion),
+        serverVersion,
+        sdkVersion
+      ]));
+    }
+  }
+
+  /**
+   * Log that the file system watcher sent an event. The [folderPath] is the
+   * path to the folder containing the changed file, the [filePath] is the path
+   * of the file that changed, and the [changeType] indicates what kind of
+   * change occurred.
+   */
+  void logWatchEvent(String folderPath, String filePath, String changeType) {
+    if (_instrumentationServer != null) {
+      _instrumentationServer
+          .log(_join([TAG_WATCH_EVENT, folderPath, filePath, changeType]));
+    }
+  }
+
+  /**
+   * Signal that the client is done communicating with the instrumentation
+   * server. This method should be invoked exactly one time and no other methods
+   * should be invoked on this instance after this method has been invoked.
+   */
+  void shutdown() {
+    if (_instrumentationServer != null) {
+      _instrumentationServer.shutdown();
+      _instrumentationServer = null;
+    }
+  }
+
+  /**
+   * Write an escaped version of the given [field] to the given [buffer].
+   */
+  void _escape(StringBuffer buffer, String field) {
+    int index = field.indexOf(':');
+    if (index < 0) {
+      buffer.write(field);
+      return;
+    }
+    int start = 0;
+    while (index >= 0) {
+      buffer.write(field.substring(start, index));
+      buffer.write('::');
+      start = index + 1;
+      index = field.indexOf(':', start);
+    }
+    buffer.write(field.substring(start));
+  }
+
+  /**
+   * Return the result of joining the values of the given fields, escaping the
+   * separator character by doubling it.
+   */
+  String _join(List<String> fields) {
+    StringBuffer buffer = new StringBuffer();
+    buffer.write(_timestamp);
+    for (String field in fields) {
+      buffer.write(':');
+      _escape(buffer, field);
+    }
+    return buffer.toString();
+  }
+
+  /**
+   * Log the given message with the given tag.
+   */
+  void _log(String tag, String message) {
+    if (_instrumentationServer != null) {
+      _instrumentationServer.log(_join([tag, message]));
+    }
+  }
+
+  /**
+   * Convert the given [object] to a string.
+   */
+  String _toString(Object object) {
+    if (object == null) {
+      return 'null';
+    }
+    return object.toString();
+  }
+}
+
+/**
+ * An [InstrumentationServer] that sends messages to multiple instances.
+ */
+class MulticastInstrumentationServer implements InstrumentationServer {
+  final List<InstrumentationServer> _servers;
+
+  MulticastInstrumentationServer(this._servers);
+
+  @override
+  void log(String message) {
+    for (InstrumentationServer server in _servers) {
+      server.log(message);
+    }
+  }
+
+  @override
+  void logWithPriority(String message) {
+    for (InstrumentationServer server in _servers) {
+      server.logWithPriority(message);
+    }
+  }
+
+  @override
+  void shutdown() {
+    for (InstrumentationServer server in _servers) {
+      server.shutdown();
+    }
+  }
+}
diff --git a/analyzer/lib/plugin/plugin.dart b/analyzer/lib/plugin/plugin.dart
new file mode 100644
index 0000000..c81c83c
--- /dev/null
+++ b/analyzer/lib/plugin/plugin.dart
@@ -0,0 +1,130 @@
+// Copyright (c) 2015, 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 analyzer.plugin.plugin;
+
+/**
+ * A function used to register the given [extension] to the extension point with
+ * the given unique [identifier].
+ *
+ * An [ExtensionError] will be thrown if the [extension] is not appropriate
+ * for the extension point, such as an [extension] that does not implement the
+ * required interface.
+ */
+typedef void RegisterExtension(String identifier, Object extension);
+
+/**
+ * A function used to register an extension point with the given simple
+ * [identifier]. If given, the [validator] will be used to validate extensions
+ * to the extension point.
+ *
+ * An [ExtensionError] will be thrown if the extension point cannot be
+ * registered, such as when a plugin attempts to define two extension points
+ * with the same simple identifier.
+ */
+typedef ExtensionPoint RegisterExtensionPoint(String identifier,
+    [ValidateExtension validateExtension]);
+
+/**
+ * A function used by a plugin to validate an [extension] to a extension point.
+ *
+ * An [ExtensionError] should be thrown if the [extension] is not valid for the
+ * extension point, such as an [extension] that does not implement the required
+ * interface.
+ */
+typedef void ValidateExtension(Object extension);
+
+/**
+ * An exception indicating that an error occurred while attempting to register
+ * either an extension or an extension point.
+ *
+ * Clients are not expected to subtype this class.
+ */
+class ExtensionError implements Exception {
+  /**
+   * The message describing the error that occurred.
+   */
+  final String message;
+
+  /**
+   * Initialize a newly created error to have the given message.
+   */
+  ExtensionError(this.message);
+}
+
+/**
+ * A representation of an extension point.
+ *
+ * Clients are not expected to subtype this class.
+ */
+abstract class ExtensionPoint {
+  /**
+   * Return an immutable list containing all of the extensions that were
+   * registered for this extension point.
+   */
+  List<Object> get extensions;
+
+  /**
+   * Return the plugin that defined this extension point.
+   */
+  Plugin get plugin;
+
+  /**
+   * Return the identifier used to uniquely identify this extension point within
+   * the defining plugin.
+   */
+  String get simpleIdentifier;
+
+  /**
+   * Return the identifier used to uniquely identify this extension point. The
+   * unique identifier is the identifier for the plugin, followed by a period
+   * (`.`), followed by the [simpleIdentifier] for the extension point.
+   */
+  String get uniqueIdentifier;
+}
+
+/**
+ * A contribution to the analysis server that can extend the behavior of the
+ * server while also allowing other plugins to extend it's behavior.
+ *
+ * Clients are expected to subtype this class when implementing plugins.
+ */
+abstract class Plugin {
+  /**
+   * Return the identifier used to uniquely identify this plugin.
+   */
+  String get uniqueIdentifier;
+
+  /**
+   * Use the [register] function to register all of the extension points
+   * contributed by this plugin.
+   *
+   * Clients should not invoke the [register] function after this method has
+   * returned.
+   */
+  void registerExtensionPoints(RegisterExtensionPoint register);
+
+  /**
+   * Use the [register] function to register all of the extensions contributed
+   * by this plugin.
+   *
+   * Clients should not invoke the [register] function after this method has
+   * returned.
+   */
+  void registerExtensions(RegisterExtension register);
+
+  /**
+   * Return a unique identifier created from the unique identifier from the
+   * [plugin] and the [simpleIdentifier].
+   */
+  static String buildUniqueIdentifier(Plugin plugin, String simpleIdentifier) =>
+      join(plugin.uniqueIdentifier, simpleIdentifier);
+
+  /**
+   * Return an identifier created by joining the [pluginIdentifier] and the
+   * [simpleIdentifier].
+   */
+  static String join(String pluginIdentifier, String simpleIdentifier) =>
+      '$pluginIdentifier.$simpleIdentifier';
+}
diff --git a/analyzer/lib/plugin/task.dart b/analyzer/lib/plugin/task.dart
new file mode 100644
index 0000000..42c9214
--- /dev/null
+++ b/analyzer/lib/plugin/task.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2015, 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.
+
+/**
+ * Support for client code that extends the analysis engine by adding new
+ * analysis tasks.
+ */
+library analyzer.plugin.task;
+
+import 'package:analyzer/plugin/plugin.dart';
+import 'package:analyzer/src/plugin/engine_plugin.dart';
+import 'package:analyzer/task/model.dart';
+
+/**
+ * The identifier of the extension point that allows plugins to register new
+ * analysis tasks with the analysis engine. The object used as an extension must
+ * be a [TaskDescriptor].
+ */
+final String TASK_EXTENSION_POINT_ID = Plugin.join(
+    EnginePlugin.UNIQUE_IDENTIFIER, EnginePlugin.TASK_EXTENSION_POINT);
diff --git a/analyzer/lib/source/package_map_provider.dart b/analyzer/lib/source/package_map_provider.dart
new file mode 100644
index 0000000..85b493d
--- /dev/null
+++ b/analyzer/lib/source/package_map_provider.dart
@@ -0,0 +1,44 @@
+// 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 source.package_map_provider;
+
+import 'package:analyzer/file_system/file_system.dart';
+
+/**
+ * Data structure output by PackageMapProvider.  This contains both the package
+ * map and dependency information.
+ */
+class PackageMapInfo {
+  /**
+   * The package map itself.  This is a map from package name to a list of
+   * the folders containing source code for the package.
+   *
+   * `null` if an error occurred.
+   */
+  Map<String, List<Folder>> packageMap;
+
+  /**
+   * Dependency information.  This is a set of the paths which were consulted
+   * in order to generate the package map.  If any of these files is
+   * modified, the package map will need to be regenerated.
+   */
+  Set<String> dependencies;
+
+  PackageMapInfo(this.packageMap, this.dependencies);
+}
+
+/**
+ * A PackageMapProvider is an entity capable of determining the mapping from
+ * package name to source directory for a given folder.
+ */
+abstract class PackageMapProvider {
+  /**
+   * Compute a package map for the given folder, if possible.
+   *
+   * If a package map can't be computed (e.g. because an error occurred), a
+   * [PackageMapInfo] will still be returned, but its packageMap will be null.
+   */
+  PackageMapInfo computePackageMap(Folder folder);
+}
diff --git a/analyzer/lib/source/package_map_resolver.dart b/analyzer/lib/source/package_map_resolver.dart
new file mode 100644
index 0000000..58240c5
--- /dev/null
+++ b/analyzer/lib/source/package_map_resolver.dart
@@ -0,0 +1,132 @@
+// 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 source.package_map_resolver;
+
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/util/asserts.dart' as asserts;
+import 'package:path/path.dart' as pathos;
+
+/**
+ * A [UriResolver] implementation for the `package:` scheme that uses a map of
+ * package names to their directories.
+ */
+class PackageMapUriResolver extends UriResolver {
+  /**
+   * The name of the `package` scheme.
+   */
+  static const String PACKAGE_SCHEME = "package";
+
+  /**
+   * A table mapping package names to the path of the directories containing
+   * the package.
+   */
+  final Map<String, List<Folder>> packageMap;
+
+  /**
+   * The [ResourceProvider] for this resolver.
+   */
+  final ResourceProvider resourceProvider;
+
+  /**
+   * Create a new [PackageMapUriResolver].
+   *
+   * [packageMap] is a table mapping package names to the paths of the
+   * directories containing the package
+   */
+  PackageMapUriResolver(this.resourceProvider, this.packageMap) {
+    asserts.notNull(resourceProvider);
+    asserts.notNull(packageMap);
+  }
+
+  @override
+  Source resolveAbsolute(Uri uri) {
+    if (!isPackageUri(uri)) {
+      return null;
+    }
+    // Prepare path.
+    String path = uri.path;
+    // Prepare path components.
+    int index = path.indexOf('/');
+    if (index == -1 || index == 0) {
+      return null;
+    }
+    // <pkgName>/<relPath>
+    String pkgName = path.substring(0, index);
+    String relPath = path.substring(index + 1);
+    // Try to find an existing file.
+    List<Folder> packageDirs = packageMap[pkgName];
+    if (packageDirs != null) {
+      for (Folder packageDir in packageDirs) {
+        if (packageDir.exists) {
+          Resource result = packageDir.getChild(relPath);
+          if (result is File && result.exists) {
+            return result.createSource(uri);
+          }
+        }
+      }
+    }
+    // Return a NonExistingSource instance.
+    // This helps provide more meaningful error messages to users
+    // (a missing file error, as opposed to an invalid URI error).
+    return new NonExistingSource(uri.toString(), UriKind.PACKAGE_URI);
+  }
+
+  @override
+  Uri restoreAbsolute(Source source) {
+    String sourcePath = source.fullName;
+    Uri bestMatch;
+    int bestMatchLength = -1;
+    pathos.Context pathContext = resourceProvider.pathContext;
+    for (String pkgName in packageMap.keys) {
+      List<Folder> pkgFolders = packageMap[pkgName];
+      for (int i = 0; i < pkgFolders.length; i++) {
+        Folder pkgFolder = pkgFolders[i];
+        String pkgFolderPath = pkgFolder.path;
+        if (pkgFolderPath.length > bestMatchLength &&
+            sourcePath.startsWith(pkgFolderPath + pathContext.separator)) {
+          String relPath = sourcePath.substring(pkgFolderPath.length + 1);
+          if (_isReversibleTranslation(pkgFolders, i, relPath)) {
+            List<String> relPathComponents = pathContext.split(relPath);
+            String relUriPath = pathos.posix.joinAll(relPathComponents);
+            bestMatch = Uri.parse('$PACKAGE_SCHEME:$pkgName/$relUriPath');
+            bestMatchLength = pkgFolderPath.length;
+          }
+        }
+      }
+    }
+    return bestMatch;
+  }
+
+  /**
+   * A translation from file path to package URI has just been found for
+   * using the [packageDirIndex]th element of [packageDirs], and appending the
+   * relative path [relPath].  Determine whether the translation is reversible;
+   * that is, whether translating the package URI pack to a file path will
+   * produce the file path we started with.
+   */
+  bool _isReversibleTranslation(
+      List<Folder> packageDirs, int packageDirIndex, String relPath) {
+    // The translation is reversible provided there is no prior element of
+    // [packageDirs] containing a file matching [relPath].
+    for (int i = 0; i < packageDirIndex; i++) {
+      Folder packageDir = packageDirs[i];
+      if (packageDir.exists) {
+        Resource result = packageDir.getChild(relPath);
+        if (result is File && result.exists) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Returns `true` if [uri] is a `package` URI.
+   */
+  static bool isPackageUri(Uri uri) {
+    return uri.scheme == PACKAGE_SCHEME;
+  }
+}
diff --git a/analyzer/lib/source/pub_package_map_provider.dart b/analyzer/lib/source/pub_package_map_provider.dart
new file mode 100644
index 0000000..a78d4ad
--- /dev/null
+++ b/analyzer/lib/source/pub_package_map_provider.dart
@@ -0,0 +1,170 @@
+// 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 source.pub_package_map_provider;
+
+import 'dart:collection';
+import 'dart:convert';
+import 'dart:io' as io;
+
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/source/package_map_provider.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/sdk_io.dart';
+
+/**
+ * The function used to run pub list.
+ */
+typedef io.ProcessResult RunPubList(Folder folder);
+
+/**
+ * Implementation of PackageMapProvider that operates by executing pub.
+ */
+class PubPackageMapProvider implements PackageMapProvider {
+  static const String PUB_LIST_COMMAND = 'list-package-dirs';
+
+  /**
+   * The name of the 'pubspec.lock' file, which we assume is the dependency
+   * in the event that [PUB_LIST_COMMAND] fails.
+   */
+  static const String PUBSPEC_LOCK_NAME = 'pubspec.lock';
+
+  /**
+   * [ResourceProvider] that is used to create the [Folder]s that populate the
+   * package map.
+   */
+  final ResourceProvider resourceProvider;
+
+  /**
+   * Sdk that we use to find the pub executable.
+   */
+  final DirectoryBasedDartSdk sdk;
+
+  /**
+   * The function used to run pub list.
+   */
+  RunPubList _runPubList;
+
+  /**
+   * Construct a new instance.
+   * A [RunPubList] implementation may be injected for testing
+   */
+  PubPackageMapProvider(this.resourceProvider, this.sdk, [this._runPubList]) {
+    if (_runPubList == null) {
+      _runPubList = _runPubListDefault;
+    }
+  }
+
+  @override
+  PackageMapInfo computePackageMap(Folder folder) {
+    // TODO(paulberry) make this asynchronous so that we can (a) do other
+    // analysis while it's in progress, and (b) time out if it takes too long
+    // to respond.
+    io.ProcessResult result;
+    try {
+      result = _runPubList(folder);
+    } on io.ProcessException catch (exception, stackTrace) {
+      AnalysisEngine.instance.logger.logInformation(
+          "Error running pub $PUB_LIST_COMMAND\n$exception\n$stackTrace");
+    }
+    if (result == null || result.exitCode != 0) {
+      String exitCode =
+          result != null ? 'exit code ${result.exitCode}' : 'null';
+      AnalysisEngine.instance.logger
+          .logInformation("pub $PUB_LIST_COMMAND failed: $exitCode");
+      return computePackageMapError(folder);
+    }
+    try {
+      PackageMapInfo packageMap =
+          parsePackageMap(JSON.decode(result.stdout), folder);
+      return packageMap;
+    } catch (exception, stackTrace) {
+      AnalysisEngine.instance.logger.logError(
+          "Malformed output from pub $PUB_LIST_COMMAND\n$exception\n$stackTrace");
+    }
+
+    return computePackageMapError(folder);
+  }
+
+  /**
+   * Create a PackageMapInfo object representing an error condition.
+   */
+  PackageMapInfo computePackageMapError(Folder folder) {
+    // Even if an error occurs, we still need to know the dependencies, so that
+    // we'll know when to try running "pub list-package-dirs" again.
+    // Unfortunately, "pub list-package-dirs" doesn't tell us dependencies when
+    // an error occurs, so just assume there is one dependency, "pubspec.lock".
+    List<String> dependencies = <String>[
+      resourceProvider.pathContext.join(folder.path, PUBSPEC_LOCK_NAME)
+    ];
+    return new PackageMapInfo(null, dependencies.toSet());
+  }
+
+  /**
+   * Decode the JSON output from pub into a package map.  Paths in the
+   * output are considered relative to [folder].
+   */
+  PackageMapInfo parsePackageMap(Map obj, Folder folder) {
+    // The output of pub looks like this:
+    // {
+    //   "packages": {
+    //     "foo": "path/to/foo",
+    //     "bar": ["path/to/bar1", "path/to/bar2"],
+    //     "myapp": "path/to/myapp",  // self link is included
+    //   },
+    //   "input_files": [
+    //     "path/to/myapp/pubspec.lock"
+    //   ]
+    // }
+    Map<String, List<Folder>> packageMap = new HashMap<String, List<Folder>>();
+    Map packages = obj['packages'];
+    processPaths(String packageName, List paths) {
+      List<Folder> folders = <Folder>[];
+      for (var path in paths) {
+        if (path is String) {
+          Resource resource = folder.getChildAssumingFolder(path);
+          if (resource is Folder) {
+            folders.add(resource);
+          }
+        }
+      }
+      if (folders.isNotEmpty) {
+        packageMap[packageName] = folders;
+      }
+    }
+    packages.forEach((key, value) {
+      if (value is String) {
+        processPaths(key, [value]);
+      } else if (value is List) {
+        processPaths(key, value);
+      }
+    });
+    Set<String> dependencies = new Set<String>();
+    List inputFiles = obj['input_files'];
+    if (inputFiles != null) {
+      for (var path in inputFiles) {
+        if (path is String) {
+          dependencies.add(folder.canonicalizePath(path));
+        }
+      }
+    }
+    return new PackageMapInfo(packageMap, dependencies);
+  }
+
+  /**
+   * Run pub list to determine the packages and input files.
+   */
+  io.ProcessResult _runPubListDefault(Folder folder) {
+    String executablePath = sdk.pubExecutable.getAbsolutePath();
+    List<String> arguments = [PUB_LIST_COMMAND];
+    String workingDirectory = folder.path;
+    int subprocessId = AnalysisEngine.instance.instrumentationService
+        .logSubprocessStart(executablePath, arguments, workingDirectory);
+    io.ProcessResult result = io.Process.runSync(executablePath, arguments,
+        workingDirectory: workingDirectory);
+    AnalysisEngine.instance.instrumentationService.logSubprocessResult(
+        subprocessId, result.exitCode, result.stdout, result.stderr);
+    return result;
+  }
+}
diff --git a/analyzer/lib/src/cancelable_future.dart b/analyzer/lib/src/cancelable_future.dart
new file mode 100644
index 0000000..c072be2
--- /dev/null
+++ b/analyzer/lib/src/cancelable_future.dart
@@ -0,0 +1,300 @@
+// 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 cancelable_future;
+
+import 'dart:async';
+
+/**
+ * Type of callback called when the future returned by a CancelableCompleter
+ * is canceled.
+ */
+typedef void CancelHandler();
+
+/**
+ * A way to produce [CancelableFuture] objects and to complete them later with
+ * a value or error.
+ *
+ * This class behaves like the standard library [Completer] class, except that
+ * its [future] getter returns a [CancelableFuture].
+ *
+ * If the future is canceled before being completed, the [CancelHandler] which
+ * was passed to the constructor is invoked, and any further attempt to
+ * complete the future has no effect.  For example, in the following code:
+ *
+ *     main() {
+ *       var cc = new CancelableCompleter(() {
+ *         print('cancelled'); // (2)
+ *       });
+ *       cc.future.then((value) {
+ *         print('completed with value $value');
+ *       }, onError: (error) {
+ *         print('completed with error $error'); // (3)
+ *       });
+ *       cc.future.cancel(); // (1)
+ *     }
+ *
+ * The call at (1) causes (2) to be invoked immediately.  (3) will be invoked
+ * later (on a microtask), with an error that is an instance of
+ * [FutureCanceledError].
+ *
+ * Note that since the closure passed to then() is executed on a microtask,
+ * there is a short window of time between the call to [complete] and the
+ * client being informed that the future has completed.  During this window,
+ * any attempt to cancel the future will have no effect.  For example, in the
+ * following code:
+ *
+ *     main() {
+ *       var cc = new CancelableCompleter(() {
+ *         print('cancelled'); // (3)
+ *       });
+ *       cc.future.then((value) {
+ *         print('completed with value $value'); // (4)
+ *       }, onError: (error) {
+ *         print('completed with error $error');
+ *       });
+ *       cc.complete(100); // (1)
+ *       cc.future.cancel(); // (2)
+ *     }
+ *
+ * The call at (1) will place the completer in the "completed" state, so the
+ * call at (2) will have no effect (in particular, (3) won't ever execute).
+ * Later, (4) will be invoked on a microtask.
+ */
+class CancelableCompleter<T> implements Completer<T> {
+  /**
+   * The completer which holds the state of the computation.  If the
+   * computation is canceled, this completer will remain in the non-completed
+   * state.
+   */
+  final Completer<T> _innerCompleter = new Completer<T>.sync();
+
+  /**
+   * The completer which holds the future that is exposed to the client
+   * through [future].  If the computation is canceled, this completer will
+   * be completed with a FutureCanceledError.
+   */
+  final Completer<T> _outerCompleter = new Completer<T>();
+
+  /**
+   * The callback to invoke if the 'cancel' method is called on the future
+   * returned by [future].  This callback will only be invoked if the future
+   * is canceled before being completed.
+   */
+  final CancelHandler _onCancel;
+
+  _CancelableCompleterFuture<T> _future;
+
+  /**
+   * Create a CancelableCompleter that will invoke the given callback
+   * synchronously if its future is canceled.  The callback will not be
+   * invoked if the future is completed before being canceled.
+   */
+  CancelableCompleter(this._onCancel) {
+    _future = new _CancelableCompleterFuture<T>(this);
+
+    // When the client completes the inner completer, we need to check whether
+    // the outer completer has been completed.  If it has, then the operation
+    // was canceled before it finished, and it's too late to un-cancel it, so
+    // we just ignore the result from the inner completer.  If it hasn't, then
+    // we simply pass along the result from the inner completer to the outer
+    // completer.
+    //
+    // Note that the reason it is safe for the inner completer to be
+    // synchronous is that we don't expose its future to client code, and we
+    // only use it to complete the outer completer (which is asynchronous).
+    _innerCompleter.future.then((T value) {
+      if (!_outerCompleter.isCompleted) {
+        _outerCompleter.complete(value);
+      }
+    }, onError: (Object error, StackTrace stackTrace) {
+      if (!_outerCompleter.isCompleted) {
+        _outerCompleter.completeError(error, stackTrace);
+      }
+    });
+  }
+
+  /**
+   * The [CancelableFuture] that will contain the result provided to this
+   * completer.
+   */
+  @override
+  CancelableFuture<T> get future => _future;
+
+  /**
+   * Whether the future has been completed.  This is independent of whether
+   * the future has been canceled.
+   */
+  @override
+  bool get isCompleted => _innerCompleter.isCompleted;
+
+  /**
+   * Complete [future] with the supplied value.  If the future has previously
+   * been canceled, this will have no effect on [future], however it will
+   * still set [isCompleted] to true.
+   */
+  @override
+  void complete([value]) {
+    _innerCompleter.complete(value);
+  }
+
+  /**
+   * Complete [future] with an error.  If the future has previously been
+   * canceled, this will have no effect on [future], however it will still set
+   * [isCompleted] to true.
+   */
+  @override
+  void completeError(Object error, [StackTrace stackTrace]) {
+    _innerCompleter.completeError(error, stackTrace);
+  }
+
+  void _cancel() {
+    if (!_outerCompleter.isCompleted) {
+      _outerCompleter.completeError(new FutureCanceledError());
+      _onCancel();
+    }
+  }
+}
+
+/**
+ * An object representing a delayed computation that can be canceled.
+ */
+abstract class CancelableFuture<T> implements Future<T> {
+  /**
+   * A CancelableFuture containing the result of calling [computation]
+   * asynchronously.  Since the computation is started without delay, calling
+   * the future's cancel method will have no effect.
+   */
+  factory CancelableFuture(computation()) =>
+      new _WrappedFuture<T>(new Future<T>(computation));
+
+  /**
+   * A CancelableFuture containing the result of calling [computation] after
+   * [duration] has passed.
+   *
+   * TODO(paulberry): if the future is canceled before the duration has
+   * elapsed, the computation should not be performed.
+   */
+  factory CancelableFuture.delayed(Duration duration, [computation()]) =>
+      new _WrappedFuture<T>(new Future<T>.delayed(duration, computation));
+
+  /**
+   * A CancelableFuture that completes with error.  Since the future is
+   * completed without delay, calling the future's cancel method will have no
+   * effect.
+   */
+  factory CancelableFuture.error(Object error, [StackTrace stackTrace]) =>
+      new _WrappedFuture<T>(new Future<T>.error(error, stackTrace));
+
+  /**
+   * A CancelableFuture containing the result of calling [computation]
+   * asynchronously with scheduleMicrotask.  Since the computation is started
+   * without delay, calling the future's cancel method will have no effect.
+   */
+  factory CancelableFuture.microtask(computation()) =>
+      new _WrappedFuture<T>(new Future<T>.microtask(computation));
+
+  /**
+   * A CancelableFuture containing the result of immediately calling
+   * [computation].  Since the computation is started without delay, calling
+   * the future's cancel method will have no effect.
+   */
+  factory CancelableFuture.sync(computation()) =>
+      new _WrappedFuture<T>(new Future<T>.sync(computation));
+
+  /**
+   * A CancelableFuture whose value is available in the next event-loop
+   * iteration.  Since the value is available without delay, calling the
+   * future's cancel method will have no effect.
+   */
+  factory CancelableFuture.value([value]) =>
+      new _WrappedFuture<T>(new Future<T>.value(value));
+
+  /**
+   * If the delayed computation has not yet completed, attempt to cancel it.
+   * Note that the cancellation is not always possible.  If the computation
+   * could be canceled, the future is completed with a FutureCanceledError.
+   * Otherwise it will behave as though cancel() was not called.
+   *
+   * Note that attempting to cancel a future that has already completed will
+   * never succeed--futures that have already completed retain their final
+   * state forever.
+   */
+  void cancel();
+}
+
+/**
+ * Error which is used to complete any [CancelableFuture] which has been
+ * successfully canceled by calling its 'cancel' method.
+ */
+class FutureCanceledError {}
+
+class _CancelableCompleterFuture<T> implements CancelableFuture<T> {
+  final CancelableCompleter<T> _completer;
+
+  _CancelableCompleterFuture(this._completer);
+
+  @override
+  Stream<T> asStream() {
+    // TODO(paulberry): Implement this in such a way that
+    // StreamSubscription.cancel() cancels the future.
+    return _completer._outerCompleter.future.asStream();
+  }
+
+  @override
+  void cancel() {
+    _completer._cancel();
+  }
+
+  @override
+  Future catchError(Function onError, {bool test(Object error)}) =>
+      _completer._outerCompleter.future.catchError(onError, test: test);
+
+  @override
+  Future then(onValue(T value), {Function onError}) =>
+      _completer._outerCompleter.future.then(onValue, onError: onError);
+
+  @override
+  Future timeout(Duration timeLimit, {onTimeout()}) {
+    // TODO(paulberry): Implement this in such a way that a timeout cancels
+    // the future.
+    return _completer._outerCompleter.future.timeout(timeLimit,
+        onTimeout: onTimeout);
+  }
+
+  @override
+  Future<T> whenComplete(action()) =>
+      _completer._outerCompleter.future.whenComplete(action);
+}
+
+/**
+ * A CancelableFuture that wraps an ordinary Future.  Attempting to cancel a
+ * _WrappedFuture has no effect.
+ */
+class _WrappedFuture<T> implements CancelableFuture<T> {
+  final Future<T> _future;
+
+  _WrappedFuture(this._future);
+
+  @override
+  Stream asStream() => _future.asStream();
+
+  @override
+  void cancel() {}
+
+  @override
+  Future catchError(Function onError, {bool test(Object error)}) =>
+      _future.catchError(onError, test: test);
+
+  @override
+  Future then(onValue(value), {Function onError}) =>
+      _future.then(onValue, onError: onError);
+
+  @override
+  Future timeout(Duration timeLimit, {onTimeout()}) =>
+      _future.timeout(timeLimit, onTimeout: onTimeout);
+
+  @override
+  Future whenComplete(action()) => _future.whenComplete(action);
+}
diff --git a/analyzer/lib/src/context/cache.dart b/analyzer/lib/src/context/cache.dart
new file mode 100644
index 0000000..67e71bc
--- /dev/null
+++ b/analyzer/lib/src/context/cache.dart
@@ -0,0 +1,829 @@
+// Copyright (c) 2015, 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 analyzer.src.context.cache;
+
+import 'dart:collection';
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/engine.dart'
+    show AnalysisEngine, CacheState, InternalAnalysisContext, RetentionPriority;
+import 'package:analyzer/src/generated/html.dart';
+import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/utilities_collection.dart';
+import 'package:analyzer/task/model.dart';
+
+/**
+ * An LRU cache of results produced by analysis.
+ */
+class AnalysisCache {
+  /**
+   * A flag used to control whether trace information should be produced when
+   * the content of the cache is modified.
+   */
+  static bool _TRACE_CHANGES = false;
+
+  /**
+   * An array containing the partitions of which this cache is comprised.
+   */
+  final List<CachePartition> _partitions;
+
+  /**
+   * Initialize a newly created cache to have the given [partitions]. The
+   * partitions will be searched in the order in which they appear in the array,
+   * so the most specific partition (usually an [SdkCachePartition]) should be
+   * first and the most general (usually a [UniversalCachePartition]) last.
+   */
+  AnalysisCache(this._partitions);
+
+  /**
+   * Return the number of entries in this cache that have an AST associated with
+   * them.
+   */
+  int get astSize => _partitions[_partitions.length - 1].astSize;
+
+  // TODO(brianwilkerson) Implement or delete this.
+//  /**
+//   * Return information about each of the partitions in this cache.
+//   */
+//  List<AnalysisContextStatistics_PartitionData> get partitionData {
+//    int count = _partitions.length;
+//    List<AnalysisContextStatistics_PartitionData> data =
+//        new List<AnalysisContextStatistics_PartitionData>(count);
+//    for (int i = 0; i < count; i++) {
+//      CachePartition partition = _partitions[i];
+//      data[i] = new AnalysisContextStatisticsImpl_PartitionDataImpl(
+//          partition.astSize,
+//          partition.map.length);
+//    }
+//    return data;
+//  }
+
+  /**
+   * Record that the AST associated with the given [target] was just read from
+   * the cache.
+   */
+  void accessedAst(AnalysisTarget target) {
+    // TODO(brianwilkerson) Extract this logic to a helper method (here and
+    // elsewhere)
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(target)) {
+        _partitions[i].accessedAst(target);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Return the entry associated with the given [target].
+   */
+  CacheEntry get(AnalysisTarget target) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(target)) {
+        return _partitions[i].get(target);
+      }
+    }
+    //
+    // We should never get to this point because the last partition should
+    // always be a universal partition, except in the case of the SDK context,
+    // in which case the target should always be part of the SDK.
+    //
+    return null;
+  }
+
+  /**
+   * Return the context to which the given [target] was explicitly added.
+   */
+  InternalAnalysisContext getContextFor(AnalysisTarget target) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(target)) {
+        return _partitions[i].context;
+      }
+    }
+    //
+    // We should never get to this point because the last partition should
+    // always be a universal partition, except in the case of the SDK context,
+    // in which case the target should always be part of the SDK.
+    //
+    // TODO(brianwilkerson) Throw an exception here.
+    AnalysisEngine.instance.logger.logInformation(
+        'Could not find context for $target',
+        new CaughtException(new AnalysisException(), null));
+    return null;
+  }
+
+  /**
+   * Return an iterator returning all of the map entries mapping targets to
+   * cache entries.
+   */
+  MapIterator<AnalysisTarget, CacheEntry> iterator() {
+    int count = _partitions.length;
+    List<Map<AnalysisTarget, CacheEntry>> maps = new List<Map>(count);
+    for (int i = 0; i < count; i++) {
+      maps[i] = _partitions[i].map;
+    }
+    return new MultipleMapIterator<AnalysisTarget, CacheEntry>(maps);
+  }
+
+  /**
+   * Associate the given [entry] with the given [target].
+   */
+  void put(AnalysisTarget target, CacheEntry entry) {
+    entry.fixExceptionState();
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(target)) {
+        if (_TRACE_CHANGES) {
+          CacheEntry oldEntry = _partitions[i].get(target);
+          if (oldEntry == null) {
+            AnalysisEngine.instance.logger
+                .logInformation('Added a cache entry for $target.');
+          } else {
+            AnalysisEngine.instance.logger
+                .logInformation('Modified the cache entry for $target.');
+//                'Diff = ${entry.getDiff(oldEntry)}');
+          }
+        }
+        _partitions[i].put(target, entry);
+        return;
+      }
+    }
+    // TODO(brianwilkerson) Handle the case where no partition was found,
+    // possibly by throwing an exception.
+  }
+
+  /**
+   * Remove all information related to the given [target] from this cache.
+   */
+  void remove(AnalysisTarget target) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(target)) {
+        if (_TRACE_CHANGES) {
+          AnalysisEngine.instance.logger
+              .logInformation('Removed the cache entry for $target.');
+        }
+        _partitions[i].remove(target);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Record that the AST associated with the given [target] was just removed
+   * from the cache.
+   */
+  void removedAst(AnalysisTarget target) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(target)) {
+        _partitions[i].removedAst(target);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Return the number of targets that are mapped to cache entries.
+   */
+  int size() {
+    int size = 0;
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      size += _partitions[i].size();
+    }
+    return size;
+  }
+
+  /**
+   * Record that the AST associated with the given [target] was just stored to
+   * the cache.
+   */
+  void storedAst(AnalysisTarget target) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(target)) {
+        _partitions[i].storedAst(target);
+        return;
+      }
+    }
+  }
+}
+
+/**
+ * The information cached by an analysis context about an individual target.
+ */
+class CacheEntry {
+  /**
+   * The index of the flag indicating whether the source was explicitly added to
+   * the context or whether the source was implicitly added because it was
+   * referenced by another source.
+   */
+  static int _EXPLICITLY_ADDED_FLAG = 0;
+
+  /**
+   * The most recent time at which the state of the target matched the state
+   * represented by this entry.
+   */
+  int modificationTime = 0;
+
+  /**
+   * The exception that caused one or more values to have a state of
+   * [CacheState.ERROR].
+   */
+  CaughtException _exception;
+
+  /**
+   * A bit-encoding of boolean flags associated with this entry's target.
+   */
+  int _flags = 0;
+
+  /**
+   * A table mapping result descriptors to the cached values of those results.
+   */
+  Map<ResultDescriptor, ResultData> _resultMap =
+      new HashMap<ResultDescriptor, ResultData>();
+
+  /**
+   * The exception that caused one or more values to have a state of
+   * [CacheState.ERROR].
+   */
+  CaughtException get exception => _exception;
+
+  /**
+   * Return `true` if the source was explicitly added to the context or `false`
+   * if the source was implicitly added because it was referenced by another
+   * source.
+   */
+  bool get explicitlyAdded => _getFlag(_EXPLICITLY_ADDED_FLAG);
+
+  /**
+   * Set whether the source was explicitly added to the context to match the
+   * [explicitlyAdded] flag.
+   */
+  void set explicitlyAdded(bool explicitlyAdded) {
+    _setFlag(_EXPLICITLY_ADDED_FLAG, explicitlyAdded);
+  }
+
+  /**
+   * Return `true` if this entry contains at least one result whose value is an
+   * AST structure.
+   */
+  bool get hasAstStructure {
+    for (ResultData data in _resultMap.values) {
+      if (data.value is AstNode || data.value is XmlNode) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Fix the state of the [exception] to match the current state of the entry.
+   */
+  void fixExceptionState() {
+    if (!hasErrorState()) {
+      _exception = null;
+    }
+  }
+
+  /**
+   * Mark any AST structures associated with this cache entry as being flushed.
+   */
+  void flushAstStructures() {
+    _resultMap.forEach((ResultDescriptor descriptor, ResultData data) {
+      if (data.value is AstNode || data.value is XmlNode) {
+        _validateStateChange(descriptor, CacheState.FLUSHED);
+        data.state = CacheState.FLUSHED;
+        data.value = descriptor.defaultValue;
+      }
+    });
+  }
+
+  /**
+   * Return the state of the result represented by the given [descriptor].
+   */
+  CacheState getState(ResultDescriptor descriptor) {
+    ResultData data = _resultMap[descriptor];
+    if (data == null) {
+      return CacheState.INVALID;
+    }
+    return data.state;
+  }
+
+  /**
+   * Return the value of the result represented by the given [descriptor], or
+   * the default value for the result if this entry does not have a valid value.
+   */
+  /*<V>*/ dynamic /*V*/ getValue(ResultDescriptor /*<V>*/ descriptor) {
+    ResultData data = _resultMap[descriptor];
+    if (data == null) {
+      return descriptor.defaultValue;
+    }
+    return data.value;
+  }
+
+  /**
+   * Return `true` if the state of any data value is [CacheState.ERROR].
+   */
+  bool hasErrorState() {
+    for (ResultData data in _resultMap.values) {
+      if (data.state == CacheState.ERROR) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Invalidate all of the information associated with this entry's target.
+   */
+  void invalidateAllInformation() {
+    _resultMap.clear();
+    _exception = null;
+  }
+
+  /**
+   * Return `true` if the state of the result represented by the given
+   * [descriptor] is [CacheState.INVALID].
+   */
+  bool isInvalid(ResultDescriptor descriptor) =>
+      getState(descriptor) == CacheState.INVALID;
+
+  /**
+   * Return `true` if the state of the result represented by the given
+   * [descriptor] is [CacheState.VALID].
+   */
+  bool isValid(ResultDescriptor descriptor) =>
+      getState(descriptor) == CacheState.VALID;
+
+  /**
+   * Set the [CacheState.ERROR] state for given [descriptors], their values to
+   * the corresponding default values, and remember the [exception] that caused
+   * this state.
+   */
+  void setErrorState(
+      CaughtException exception, List<ResultDescriptor> descriptors) {
+    if (descriptors == null || descriptors.isEmpty) {
+      throw new ArgumentError('at least one descriptor is expected');
+    }
+    if (exception == null) {
+      throw new ArgumentError('an exception is expected');
+    }
+    this._exception = exception;
+    for (ResultDescriptor descriptor in descriptors) {
+      ResultData data = _getResultData(descriptor);
+      data.state = CacheState.ERROR;
+      data.value = descriptor.defaultValue;
+    }
+  }
+
+  /**
+   * Set the state of the result represented by the given [descriptor] to the
+   * given [state].
+   */
+  void setState(ResultDescriptor descriptor, CacheState state) {
+    if (state == CacheState.ERROR) {
+      throw new ArgumentError('use setErrorState() to set the state to ERROR');
+    }
+    if (state == CacheState.VALID) {
+      throw new ArgumentError('use setValue() to set the state to VALID');
+    }
+    _validateStateChange(descriptor, state);
+    if (state == CacheState.INVALID) {
+      _resultMap.remove(descriptor);
+    } else {
+      ResultData data = _getResultData(descriptor);
+      data.state = state;
+      if (state != CacheState.IN_PROCESS) {
+        //
+        // If the state is in-process, we can leave the current value in the
+        // cache for any 'get' methods to access.
+        //
+        data.value = descriptor.defaultValue;
+      }
+    }
+  }
+
+  /**
+   * Set the value of the result represented by the given [descriptor] to the
+   * given [value].
+   */
+  /*<V>*/ void setValue(ResultDescriptor /*<V>*/ descriptor, dynamic /*V*/
+      value) {
+    _validateStateChange(descriptor, CacheState.VALID);
+    ResultData data = _getResultData(descriptor);
+    data.state = CacheState.VALID;
+    data.value = value == null ? descriptor.defaultValue : value;
+  }
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    _writeOn(buffer);
+    return buffer.toString();
+  }
+
+  /**
+   * Return the value of the flag with the given [index].
+   */
+  bool _getFlag(int index) => BooleanArray.get(_flags, index);
+
+  /**
+   * Look up the [ResultData] of [descriptor], or add a new one if it isn't
+   * there.
+   */
+  ResultData _getResultData(ResultDescriptor descriptor) {
+    return _resultMap.putIfAbsent(descriptor, () => new ResultData(descriptor));
+  }
+
+  /**
+   * Set the value of the flag with the given [index] to the given [value].
+   */
+  void _setFlag(int index, bool value) {
+    _flags = BooleanArray.set(_flags, index, value);
+  }
+
+  /**
+   * If the state of the value described by the given [descriptor] is changing
+   * from ERROR to anything else, capture the information. This is an attempt to
+   * discover the underlying cause of a long-standing bug.
+   */
+  void _validateStateChange(ResultDescriptor descriptor, CacheState newState) {
+    // TODO(brianwilkerson) Decide whether we still want to capture this data.
+//    if (descriptor != CONTENT) {
+//      return;
+//    }
+//    ResultData data = resultMap[CONTENT];
+//    if (data != null && data.state == CacheState.ERROR) {
+//      String message =
+//          'contentState changing from ${data.state} to $newState';
+//      InstrumentationBuilder builder =
+//          Instrumentation.builder2('CacheEntry-validateStateChange');
+//      builder.data3('message', message);
+//      //builder.data('source', source.getFullName());
+//      builder.record(new CaughtException(new AnalysisException(message), null));
+//      builder.log();
+//    }
+  }
+
+  /**
+   * Write a textual representation of this entry to the given [buffer]. The
+   * result should only be used for debugging purposes.
+   */
+  void _writeOn(StringBuffer buffer) {
+    buffer.write('time = ');
+    buffer.write(modificationTime);
+    List<ResultDescriptor> results = _resultMap.keys.toList();
+    results.sort((ResultDescriptor first, ResultDescriptor second) =>
+        first.toString().compareTo(second.toString()));
+    for (ResultDescriptor result in results) {
+      ResultData data = _resultMap[result];
+      buffer.write('; ');
+      buffer.write(result.toString());
+      buffer.write(' = ');
+      buffer.write(data..state);
+    }
+  }
+}
+
+/**
+ * A single partition in an LRU cache of information related to analysis.
+ */
+abstract class CachePartition {
+  /**
+   * The context that owns this partition. Multiple contexts can reference a
+   * partition, but only one context can own it.
+   */
+  final InternalAnalysisContext context;
+
+  /**
+   * The maximum number of sources for which AST structures should be kept in
+   * the cache.
+   */
+  int _maxCacheSize = 0;
+
+  /**
+   * The policy used to determine which results to remove from the cache.
+   */
+  final CacheRetentionPolicy _retentionPolicy;
+
+  /**
+   * A table mapping the targets belonging to this partition to the information
+   * known about those targets.
+   */
+  HashMap<AnalysisTarget, CacheEntry> _targetMap =
+      new HashMap<AnalysisTarget, CacheEntry>();
+
+  /**
+   * A list containing the most recently accessed targets with the most recently
+   * used at the end of the list. When more targets are added than the maximum
+   * allowed then the least recently used target will be removed and will have
+   * it's cached AST structure flushed.
+   */
+  List<AnalysisTarget> _recentlyUsed = <AnalysisTarget>[];
+
+  /**
+   * Initialize a newly created cache partition, belonging to the given
+   * [context]. The partition will maintain at most [_maxCacheSize] AST
+   * structures in the cache, using the [_retentionPolicy] to determine which
+   * AST structures to flush.
+   */
+  CachePartition(this.context, this._maxCacheSize, this._retentionPolicy);
+
+  /**
+   * Return the number of entries in this partition that have an AST associated
+   * with them.
+   */
+  int get astSize {
+    int astSize = 0;
+    int count = _recentlyUsed.length;
+    for (int i = 0; i < count; i++) {
+      AnalysisTarget target = _recentlyUsed[i];
+      CacheEntry entry = _targetMap[target];
+      if (entry.hasAstStructure) {
+        astSize++;
+      }
+    }
+    return astSize;
+  }
+
+  /**
+   * Return a table mapping the targets known to the context to the information
+   * known about the target.
+   *
+   * <b>Note:</b> This method is only visible for use by [AnalysisCache] and
+   * should not be used for any other purpose.
+   */
+  Map<AnalysisTarget, CacheEntry> get map => _targetMap;
+
+  /**
+   * Return the maximum size of the cache.
+   */
+  int get maxCacheSize => _maxCacheSize;
+
+  /**
+   * Set the maximum size of the cache to the given [size].
+   */
+  void set maxCacheSize(int size) {
+    _maxCacheSize = size;
+    while (_recentlyUsed.length > _maxCacheSize) {
+      if (!_flushAstFromCache()) {
+        break;
+      }
+    }
+  }
+
+  /**
+   * Record that the AST associated with the given [target] was just read from
+   * the cache.
+   */
+  void accessedAst(AnalysisTarget target) {
+    if (_recentlyUsed.remove(target)) {
+      _recentlyUsed.add(target);
+      return;
+    }
+    while (_recentlyUsed.length >= _maxCacheSize) {
+      if (!_flushAstFromCache()) {
+        break;
+      }
+    }
+    _recentlyUsed.add(target);
+  }
+
+  /**
+   * Return `true` if the given [target] is contained in this partition.
+   */
+  // TODO(brianwilkerson) Rename this to something more meaningful, such as
+  // isResponsibleFor.
+  bool contains(AnalysisTarget target);
+
+  /**
+   * Return the entry associated with the given [target].
+   */
+  CacheEntry get(AnalysisTarget target) => _targetMap[target];
+
+  /**
+   * Return an iterator returning all of the map entries mapping targets to
+   * cache entries.
+   */
+  MapIterator<AnalysisTarget, CacheEntry> iterator() =>
+      new SingleMapIterator<AnalysisTarget, CacheEntry>(_targetMap);
+
+  /**
+   * Associate the given [entry] with the given [target].
+   */
+  void put(AnalysisTarget target, CacheEntry entry) {
+    entry.fixExceptionState();
+    _targetMap[target] = entry;
+  }
+
+  /**
+   * Remove all information related to the given [target] from this cache.
+   */
+  void remove(AnalysisTarget target) {
+    _recentlyUsed.remove(target);
+    _targetMap.remove(target);
+  }
+
+  /**
+   * Record that the AST associated with the given [target] was just removed
+   * from the cache.
+   */
+  void removedAst(AnalysisTarget target) {
+    _recentlyUsed.remove(target);
+  }
+
+  /**
+   * Return the number of targets that are mapped to cache entries.
+   */
+  int size() => _targetMap.length;
+
+  /**
+   * Record that the AST associated with the given [target] was just stored to
+   * the cache.
+   */
+  void storedAst(AnalysisTarget target) {
+    if (_recentlyUsed.contains(target)) {
+      return;
+    }
+    while (_recentlyUsed.length >= _maxCacheSize) {
+      if (!_flushAstFromCache()) {
+        break;
+      }
+    }
+    _recentlyUsed.add(target);
+  }
+
+  /**
+   * Attempt to flush one AST structure from the cache. Return `true` if a
+   * structure was flushed.
+   */
+  bool _flushAstFromCache() {
+    AnalysisTarget removedTarget = _removeAstToFlush();
+    if (removedTarget == null) {
+      return false;
+    }
+    CacheEntry entry = _targetMap[removedTarget];
+    entry.flushAstStructures();
+    return true;
+  }
+
+  /**
+   * Remove and return one target from the list of recently used targets whose
+   * AST structure can be flushed from the cache,  or `null` if none of the
+   * targets can be removed. The target that will be returned will be the target
+   * that has been unreferenced for the longest period of time but that is not a
+   * priority for analysis.
+   */
+  AnalysisTarget _removeAstToFlush() {
+    int targetToRemove = -1;
+    for (int i = 0; i < _recentlyUsed.length; i++) {
+      AnalysisTarget target = _recentlyUsed[i];
+      RetentionPriority priority =
+          _retentionPolicy.getAstPriority(target, _targetMap[target]);
+      if (priority == RetentionPriority.LOW) {
+        return _recentlyUsed.removeAt(i);
+      } else if (priority == RetentionPriority.MEDIUM && targetToRemove < 0) {
+        targetToRemove = i;
+      }
+    }
+    if (targetToRemove < 0) {
+      // This happens if the retention policy returns a priority of HIGH for all
+      // of the targets that have been recently used. This is the case, for
+      // example, when the list of priority sources is bigger than the current
+      // cache size.
+      return null;
+    }
+    return _recentlyUsed.removeAt(targetToRemove);
+  }
+}
+
+/**
+ * A policy objecy that determines how important it is for data to be retained
+ * in the analysis cache.
+ */
+abstract class CacheRetentionPolicy {
+  /**
+   * Return the priority of retaining the AST structure for the given [target]
+   * in the given [entry].
+   */
+  // TODO(brianwilkerson) Find a more general mechanism, probably based on task
+  // descriptors, to determine which data is still needed for analysis and which
+  // can be removed from the cache. Ideally we could (a) remove the need for
+  // this class and (b) be able to flush all result data (not just AST's).
+  RetentionPriority getAstPriority(AnalysisTarget target, CacheEntry entry);
+}
+
+/**
+ * A retention policy that will keep AST's in the cache if there is analysis
+ * information that needs to be computed for a source, where the computation is
+ * dependent on having the AST.
+ */
+class DefaultRetentionPolicy implements CacheRetentionPolicy {
+  /**
+   * An instance of this class that can be shared.
+   */
+  static const DefaultRetentionPolicy POLICY = const DefaultRetentionPolicy();
+
+  /**
+   * Initialize a newly created instance of this class.
+   */
+  const DefaultRetentionPolicy();
+
+  // TODO(brianwilkerson) Implement or delete this.
+//  /**
+//   * Return `true` if there is analysis information in the given entry that needs to be
+//   * computed, where the computation is dependent on having the AST.
+//   *
+//   * @param dartEntry the entry being tested
+//   * @return `true` if there is analysis information that needs to be computed from the AST
+//   */
+//  bool astIsNeeded(DartEntry dartEntry) =>
+//      dartEntry.hasInvalidData(DartEntry.HINTS) ||
+//          dartEntry.hasInvalidData(DartEntry.LINTS) ||
+//          dartEntry.hasInvalidData(DartEntry.VERIFICATION_ERRORS) ||
+//          dartEntry.hasInvalidData(DartEntry.RESOLUTION_ERRORS);
+
+  @override
+  RetentionPriority getAstPriority(AnalysisTarget target, CacheEntry entry) {
+    // TODO(brianwilkerson) Implement or replace this.
+//    if (sourceEntry is DartEntry) {
+//      DartEntry dartEntry = sourceEntry;
+//      if (astIsNeeded(dartEntry)) {
+//        return RetentionPriority.MEDIUM;
+//      }
+//    }
+//    return RetentionPriority.LOW;
+    return RetentionPriority.MEDIUM;
+  }
+}
+
+/**
+ * The data about a single analysis result that is stored in a [CacheEntry].
+ */
+// TODO(brianwilkerson) Consider making this a generic class so that the value
+// can be typed.
+class ResultData {
+  /**
+   * The state of the cached value.
+   */
+  CacheState state;
+
+  /**
+   * The value being cached, or the default value for the result if there is no
+   * value (for example, when the [state] is [CacheState.INVALID]).
+   */
+  Object value;
+
+  /**
+   * Initialize a newly created result holder to represent the value of data
+   * described by the given [descriptor].
+   */
+  ResultData(ResultDescriptor descriptor) {
+    state = CacheState.INVALID;
+    value = descriptor.defaultValue;
+  }
+}
+
+/**
+ * A cache partition that contains all of the targets in the SDK.
+ */
+class SdkCachePartition extends CachePartition {
+  /**
+   * Initialize a newly created cache partition, belonging to the given
+   * [context]. The partition will maintain at most [maxCacheSize] AST
+   * structures in the cache.
+   */
+  SdkCachePartition(InternalAnalysisContext context, int maxCacheSize)
+      : super(context, maxCacheSize, DefaultRetentionPolicy.POLICY);
+
+  @override
+  bool contains(AnalysisTarget target) {
+    Source source = target.source;
+    return source != null && source.isInSystemLibrary;
+  }
+}
+
+/**
+ * A cache partition that contains all targets not contained in other partitions.
+ */
+class UniversalCachePartition extends CachePartition {
+  /**
+   * Initialize a newly created cache partition, belonging to the given
+   * [context]. The partition will maintain at most [maxCacheSize] AST
+   * structures in the cache, using the [retentionPolicy] to determine which
+   * AST structures to flush.
+   */
+  UniversalCachePartition(InternalAnalysisContext context, int maxCacheSize,
+      CacheRetentionPolicy retentionPolicy)
+      : super(context, maxCacheSize, retentionPolicy);
+
+  @override
+  bool contains(AnalysisTarget target) => true;
+}
diff --git a/analyzer/lib/src/context/context.dart b/analyzer/lib/src/context/context.dart
new file mode 100644
index 0000000..29204a1
--- /dev/null
+++ b/analyzer/lib/src/context/context.dart
@@ -0,0 +1,1986 @@
+// Copyright (c) 2015, 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 analyzer.src.context.context;
+
+import 'dart:async';
+import 'dart:collection';
+
+import 'package:analyzer/src/cancelable_future.dart';
+import 'package:analyzer/src/context/cache.dart' as cache;
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/constant.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/error.dart';
+import 'package:analyzer/src/generated/html.dart' as ht;
+import 'package:analyzer/src/generated/java_core.dart';
+import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/utilities_collection.dart';
+import 'package:analyzer/src/task/dart.dart';
+import 'package:analyzer/src/task/driver.dart';
+import 'package:analyzer/src/task/manager.dart';
+import 'package:analyzer/task/dart.dart';
+import 'package:analyzer/task/general.dart';
+import 'package:analyzer/task/model.dart';
+
+/**
+ * An [AnalysisContext] in which analysis can be performed.
+ */
+class AnalysisContextImpl implements InternalAnalysisContext {
+  /**
+   * A client-provided name used to identify this context, or `null` if the
+   * client has not provided a name.
+   */
+  String name;
+
+  /**
+   * The set of analysis options controlling the behavior of this context.
+   */
+  AnalysisOptionsImpl _options = new AnalysisOptionsImpl();
+
+  /**
+   * A flag indicating whether this context is disposed.
+   */
+  bool _disposed = false;
+
+  /**
+   * A cache of content used to override the default content of a source.
+   */
+  ContentCache _contentCache = new ContentCache();
+
+  /**
+   * The source factory used to create the sources that can be analyzed in this
+   * context.
+   */
+  SourceFactory _sourceFactory;
+
+  /**
+   * The set of declared variables used when computing constant values.
+   */
+  DeclaredVariables _declaredVariables = new DeclaredVariables();
+
+  /**
+   * The partition that contains analysis results that are not shared with other
+   * contexts.
+   */
+  cache.CachePartition _privatePartition;
+
+  /**
+   * The cache in which information about the results associated with targets
+   * are stored.
+   */
+  cache.AnalysisCache _cache;
+
+  /**
+   * The task manager used to manage the tasks used to analyze code.
+   */
+  TaskManager _taskManager;
+
+  /**
+   * The analysis driver used to perform analysis.
+   */
+  AnalysisDriver _driver;
+
+  /**
+   * A list containing sources for which data should not be flushed.
+   */
+  List<Source> _priorityOrder = Source.EMPTY_ARRAY;
+
+  /**
+   * A map from all sources for which there are futures pending to a list of
+   * the corresponding PendingFuture objects.  These sources will be analyzed
+   * in the same way as priority sources, except with higher priority.
+   */
+  HashMap<Source, List<PendingFuture>> _pendingFutureSources =
+      new HashMap<Source, List<PendingFuture>>();
+
+  /**
+   * A table mapping sources to the change notices that are waiting to be
+   * returned related to that source.
+   */
+  HashMap<Source, ChangeNoticeImpl> _pendingNotices =
+      new HashMap<Source, ChangeNoticeImpl>();
+
+  /**
+   * Cached information used in incremental analysis or `null` if none.
+   */
+  IncrementalAnalysisCache _incrementalAnalysisCache;
+
+  /**
+   * The [TypeProvider] for this context, `null` if not yet created.
+   */
+  TypeProvider _typeProvider;
+
+  /**
+   * The controller for sending [SourcesChangedEvent]s.
+   */
+  StreamController<SourcesChangedEvent> _onSourcesChangedController;
+
+  /**
+   * The listeners that are to be notified when various analysis results are
+   * produced in this context.
+   */
+  List<AnalysisListener> _listeners = new List<AnalysisListener>();
+
+  /**
+   * The most recently incrementally resolved source, or `null` when it was
+   * already validated, or the most recent change was not incrementally resolved.
+   */
+  Source incrementalResolutionValidation_lastUnitSource;
+
+  /**
+   * The most recently incrementally resolved library source, or `null` when it
+   * was already validated, or the most recent change was not incrementally
+   * resolved.
+   */
+  Source incrementalResolutionValidation_lastLibrarySource;
+
+  /**
+   * The result of incremental resolution result of
+   * [incrementalResolutionValidation_lastSource].
+   */
+  CompilationUnit incrementalResolutionValidation_lastUnit;
+
+  /**
+   * A factory to override how the [ResolverVisitor] is created.
+   */
+  ResolverVisitorFactory resolverVisitorFactory;
+
+  /**
+   * A factory to override how the [TypeResolverVisitor] is created.
+   */
+  TypeResolverVisitorFactory typeResolverVisitorFactory;
+
+  /**
+   * A factory to override how [LibraryResolver] is created.
+   */
+  LibraryResolverFactory libraryResolverFactory;
+
+  /**
+   * Initialize a newly created analysis context.
+   */
+  AnalysisContextImpl() {
+    _privatePartition = new cache.UniversalCachePartition(this,
+        AnalysisOptionsImpl.DEFAULT_CACHE_SIZE,
+        new ContextRetentionPolicy(this));
+    _cache = createCacheFromSourceFactory(null);
+    _taskManager = AnalysisEngine.instance.taskManager;
+    _driver = new AnalysisDriver(_taskManager, this);
+    _onSourcesChangedController =
+        new StreamController<SourcesChangedEvent>.broadcast();
+  }
+
+  @override
+  AnalysisOptions get analysisOptions => _options;
+
+  @override
+  void set analysisOptions(AnalysisOptions options) {
+    bool needsRecompute = this._options.analyzeFunctionBodiesPredicate !=
+            options.analyzeFunctionBodiesPredicate ||
+        this._options.generateImplicitErrors !=
+            options.generateImplicitErrors ||
+        this._options.generateSdkErrors != options.generateSdkErrors ||
+        this._options.dart2jsHint != options.dart2jsHint ||
+        (this._options.hint && !options.hint) ||
+        this._options.preserveComments != options.preserveComments ||
+        this._options.enableNullAwareOperators !=
+            options.enableNullAwareOperators ||
+        this._options.enableStrictCallChecks != options.enableStrictCallChecks;
+    int cacheSize = options.cacheSize;
+    if (this._options.cacheSize != cacheSize) {
+      this._options.cacheSize = cacheSize;
+      _privatePartition.maxCacheSize = cacheSize;
+    }
+    this._options.analyzeFunctionBodiesPredicate =
+        options.analyzeFunctionBodiesPredicate;
+    this._options.generateImplicitErrors = options.generateImplicitErrors;
+    this._options.generateSdkErrors = options.generateSdkErrors;
+    this._options.dart2jsHint = options.dart2jsHint;
+    this._options.enableNullAwareOperators = options.enableNullAwareOperators;
+    this._options.enableStrictCallChecks = options.enableStrictCallChecks;
+    this._options.hint = options.hint;
+    this._options.incremental = options.incremental;
+    this._options.incrementalApi = options.incrementalApi;
+    this._options.incrementalValidation = options.incrementalValidation;
+    this._options.lint = options.lint;
+    this._options.preserveComments = options.preserveComments;
+    if (needsRecompute) {
+      _invalidateAllLocalResolutionInformation(false);
+    }
+  }
+
+  @override
+  void set analysisPriorityOrder(List<Source> sources) {
+    if (sources == null || sources.isEmpty) {
+      _priorityOrder = Source.EMPTY_ARRAY;
+    } else {
+      while (sources.remove(null)) {
+        // Nothing else to do.
+      }
+      if (sources.isEmpty) {
+        _priorityOrder = Source.EMPTY_ARRAY;
+      } else {
+        _priorityOrder = sources;
+      }
+    }
+  }
+
+  @override
+  set contentCache(ContentCache value) {
+    _contentCache = value;
+  }
+
+  @override
+  DeclaredVariables get declaredVariables => _declaredVariables;
+
+  @override
+  List<AnalysisTarget> get explicitTargets {
+    List<AnalysisTarget> targets = <AnalysisTarget>[];
+    MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      if (iterator.value.explicitlyAdded) {
+        targets.add(iterator.key);
+      }
+    }
+    return targets;
+  }
+
+  @override
+  List<Source> get htmlSources => _getSources(SourceKind.HTML);
+
+  @override
+  bool get isDisposed => _disposed;
+
+  @override
+  List<Source> get launchableClientLibrarySources {
+    // TODO(brianwilkerson) This needs to filter out libraries that do not
+    // reference dart:html, either directly or indirectly.
+    List<Source> sources = new List<Source>();
+    MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      AnalysisTarget target = iterator.key;
+      cache.CacheEntry entry = iterator.value;
+      if (target is Source &&
+          entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY &&
+          !target.isInSystemLibrary) {
+//          DartEntry dartEntry = (DartEntry) sourceEntry;
+//          if (dartEntry.getValue(DartEntry.IS_LAUNCHABLE) && !dartEntry.getValue(DartEntry.IS_CLIENT)) {
+        sources.add(target);
+//          }
+      }
+    }
+    return sources;
+  }
+
+  @override
+  List<Source> get launchableServerLibrarySources {
+    // TODO(brianwilkerson) This needs to filter out libraries that reference
+    // dart:html, either directly or indirectly.
+    List<Source> sources = new List<Source>();
+    MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      AnalysisTarget target = iterator.key;
+      cache.CacheEntry entry = iterator.value;
+      if (target is Source &&
+          entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY &&
+          !target.isInSystemLibrary) {
+//          DartEntry dartEntry = (DartEntry) sourceEntry;
+//          if (dartEntry.getValue(DartEntry.IS_LAUNCHABLE) && !dartEntry.getValue(DartEntry.IS_CLIENT)) {
+        sources.add(target);
+//          }
+      }
+    }
+    return sources;
+  }
+
+  @override
+  List<Source> get librarySources => _getSources(SourceKind.LIBRARY);
+
+  @override
+  Stream<SourcesChangedEvent> get onSourcesChanged =>
+      _onSourcesChangedController.stream;
+
+  /**
+   * Make _pendingFutureSources available to unit tests.
+   */
+  HashMap<Source, List<PendingFuture>> get pendingFutureSources_forTesting =>
+      _pendingFutureSources;
+
+  @override
+  List<Source> get prioritySources => _priorityOrder;
+
+  @override
+  List<AnalysisTarget> get priorityTargets => prioritySources;
+
+  @override
+  List<Source> get refactoringUnsafeSources {
+    // TODO(brianwilkerson) Implement this.
+    List<Source> sources = new List<Source>();
+//    MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator();
+//    while (iterator.moveNext()) {
+//      cache.CacheEntry entry = iterator.value;
+//      AnalysisTarget target = iterator.key;
+//      if (target is Source &&
+//          !target.isInSystemLibrary &&
+//          !entry.isRefactoringSafe) {
+//        sources.add(target);
+//      }
+//    }
+    return sources;
+  }
+
+  @override
+  SourceFactory get sourceFactory => _sourceFactory;
+
+  @override
+  void set sourceFactory(SourceFactory factory) {
+    if (identical(_sourceFactory, factory)) {
+      return;
+    } else if (factory.context != null) {
+      throw new IllegalStateException(
+          "Source factories cannot be shared between contexts");
+    }
+    if (_sourceFactory != null) {
+      _sourceFactory.context = null;
+    }
+    factory.context = this;
+    _sourceFactory = factory;
+    _cache = createCacheFromSourceFactory(factory);
+    _invalidateAllLocalResolutionInformation(true);
+  }
+
+  @override
+  List<Source> get sources {
+    List<Source> sources = new List<Source>();
+    MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      AnalysisTarget target = iterator.key;
+      if (target is Source) {
+        sources.add(target);
+      }
+    }
+    return sources;
+  }
+
+  /**
+   * Return a list of the sources that would be processed by
+   * [performAnalysisTask]. This method duplicates, and must therefore be kept
+   * in sync with, [getNextAnalysisTask]. This method is intended to be used for
+   * testing purposes only.
+   */
+  List<Source> get sourcesNeedingProcessing {
+    HashSet<Source> sources = new HashSet<Source>();
+    bool hintsEnabled = _options.hint;
+    bool lintsEnabled = _options.lint;
+
+    MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      AnalysisTarget target = iterator.key;
+      if (target is Source) {
+        _getSourcesNeedingProcessing(
+            target, iterator.value, false, hintsEnabled, lintsEnabled, sources);
+      }
+    }
+    return new List<Source>.from(sources);
+  }
+
+  @override
+  AnalysisContextStatistics get statistics {
+    AnalysisContextStatisticsImpl statistics =
+        new AnalysisContextStatisticsImpl();
+    // TODO(brianwilkerson) Implement this.
+//    visitCacheItems(statistics._internalPutCacheItem);
+//    statistics.partitionData = _cache.partitionData;
+    return statistics;
+  }
+
+  IncrementalAnalysisCache get test_incrementalAnalysisCache {
+    return _incrementalAnalysisCache;
+  }
+
+  set test_incrementalAnalysisCache(IncrementalAnalysisCache value) {
+    _incrementalAnalysisCache = value;
+  }
+
+  List<Source> get test_priorityOrder => _priorityOrder;
+
+  @override
+  TypeProvider get typeProvider {
+    if (_typeProvider != null) {
+      return _typeProvider;
+    }
+    Source coreSource = sourceFactory.forUri(DartSdk.DART_CORE);
+    if (coreSource == null) {
+      throw new AnalysisException("Could not create a source for dart:core");
+    }
+    LibraryElement coreElement = computeLibraryElement(coreSource);
+    if (coreElement == null) {
+      throw new AnalysisException("Could not create an element for dart:core");
+    }
+    Source asyncSource = sourceFactory.forUri(DartSdk.DART_ASYNC);
+    if (asyncSource == null) {
+      throw new AnalysisException("Could not create a source for dart:async");
+    }
+    LibraryElement asyncElement = computeLibraryElement(asyncSource);
+    if (asyncElement == null) {
+      throw new AnalysisException("Could not create an element for dart:async");
+    }
+    _typeProvider = new TypeProviderImpl(coreElement, asyncElement);
+    return _typeProvider;
+  }
+
+  /**
+   * Sets the [TypeProvider] for this context.
+   */
+  void set typeProvider(TypeProvider typeProvider) {
+    _typeProvider = typeProvider;
+  }
+
+  /**
+   * Return `true` if the (new) task model should be used to perform analysis.
+   */
+  bool get useTaskModel => AnalysisEngine.instance.useTaskModel;
+
+  @override
+  void addListener(AnalysisListener listener) {
+    if (!_listeners.contains(listener)) {
+      _listeners.add(listener);
+    }
+  }
+
+  @override
+  void addSourceInfo(Source source, SourceEntry info) {
+    // TODO(brianwilkerson) This method needs to be replaced by something that
+    // will copy CacheEntry's.
+//    _cache.put(source, info);
+  }
+
+  @override
+  void applyAnalysisDelta(AnalysisDelta delta) {
+    ChangeSet changeSet = new ChangeSet();
+    delta.analysisLevels.forEach((Source source, AnalysisLevel level) {
+      if (level == AnalysisLevel.NONE) {
+        changeSet.removedSource(source);
+      } else {
+        changeSet.addedSource(source);
+      }
+    });
+    applyChanges(changeSet);
+  }
+
+  @override
+  void applyChanges(ChangeSet changeSet) {
+    if (changeSet.isEmpty) {
+      return;
+    }
+    //
+    // First, compute the list of sources that have been removed.
+    //
+    List<Source> removedSources =
+        new List<Source>.from(changeSet.removedSources);
+    for (SourceContainer container in changeSet.removedContainers) {
+      _addSourcesInContainer(removedSources, container);
+    }
+    //
+    // Then determine which cached results are no longer valid.
+    //
+    for (Source source in changeSet.addedSources) {
+      _sourceAvailable(source);
+    }
+    for (Source source in changeSet.changedSources) {
+      if (_contentCache.getContents(source) != null) {
+        // This source is overridden in the content cache, so the change will
+        // have no effect. Just ignore it to avoid wasting time doing
+        // re-analysis.
+        continue;
+      }
+      _sourceChanged(source);
+    }
+    changeSet.changedContents.forEach((Source key, String value) {
+      _contentsChanged(key, value, false);
+    });
+    changeSet.changedRanges
+        .forEach((Source source, ChangeSet_ContentChange change) {
+      _contentRangeChanged(source, change.contents, change.offset,
+          change.oldLength, change.newLength);
+    });
+    for (Source source in changeSet.deletedSources) {
+      _sourceDeleted(source);
+    }
+    for (Source source in removedSources) {
+      _sourceRemoved(source);
+    }
+    _onSourcesChangedController.add(new SourcesChangedEvent(changeSet));
+  }
+
+  @override
+  String computeDocumentationComment(Element element) {
+    if (element == null) {
+      return null;
+    }
+    Source source = element.source;
+    if (source == null) {
+      return null;
+    }
+    CompilationUnit unit = parseCompilationUnit(source);
+    if (unit == null) {
+      return null;
+    }
+    NodeLocator locator = new NodeLocator.con1(element.nameOffset);
+    AstNode nameNode = locator.searchWithin(unit);
+    while (nameNode != null) {
+      if (nameNode is AnnotatedNode) {
+        Comment comment = nameNode.documentationComment;
+        if (comment == null) {
+          return null;
+        }
+        StringBuffer buffer = new StringBuffer();
+        List<Token> tokens = comment.tokens;
+        for (int i = 0; i < tokens.length; i++) {
+          if (i > 0) {
+            buffer.write("\n");
+          }
+          buffer.write(tokens[i].lexeme);
+        }
+        return buffer.toString();
+      }
+      nameNode = nameNode.parent;
+    }
+    return null;
+  }
+
+  @override
+  List<AnalysisError> computeErrors(Source source) =>
+      _computeResult(source, DART_ERRORS);
+
+  @override
+  List<Source> computeExportedLibraries(Source source) =>
+      _computeResult(source, EXPORTED_LIBRARIES);
+
+  @override
+  // TODO(brianwilkerson) Implement this.
+  HtmlElement computeHtmlElement(Source source) => null;
+
+  @override
+  List<Source> computeImportedLibraries(Source source) => _computeResult(
+      source, IMPORTED_LIBRARIES);
+
+  @override
+  SourceKind computeKindOf(Source source) =>
+      _computeResult(source, SOURCE_KIND);
+
+  @override
+  LibraryElement computeLibraryElement(Source source) =>
+      _computeResult(source, LIBRARY_ELEMENT); //_computeResult(source, HtmlEntry.ELEMENT);
+
+  @override
+  LineInfo computeLineInfo(Source source) => _computeResult(source, LINE_INFO);
+
+  @override
+  @deprecated
+  CompilationUnit computeResolvableCompilationUnit(Source source) {
+    return null;
+  }
+
+  @override
+  CancelableFuture<CompilationUnit> computeResolvedCompilationUnitAsync(
+      Source unitSource, Source librarySource) {
+    // TODO(brianwilkerson) Implement this.
+    return new CancelableFuture<CompilationUnit>(() => null);
+//    return new _AnalysisFutureHelper<CompilationUnit>(this).computeAsync(
+//        unitSource, (SourceEntry sourceEntry) {
+//      if (sourceEntry is DartEntry) {
+//        if (sourceEntry.getStateInLibrary(
+//                DartEntry.RESOLVED_UNIT, librarySource) ==
+//            CacheState.ERROR) {
+//          throw sourceEntry.exception;
+//        }
+//        return sourceEntry.getValueInLibrary(
+//            DartEntry.RESOLVED_UNIT, librarySource);
+//      }
+//      throw new AnalysisNotScheduledError();
+//    });
+  }
+
+  /**
+   * Create an analysis cache based on the given source [factory].
+   */
+  cache.AnalysisCache createCacheFromSourceFactory(SourceFactory factory) {
+    if (factory == null) {
+      return new cache.AnalysisCache(<cache.CachePartition>[_privatePartition]);
+    }
+    DartSdk sdk = factory.dartSdk;
+    if (sdk == null) {
+      return new cache.AnalysisCache(<cache.CachePartition>[_privatePartition]);
+    }
+    return new cache.AnalysisCache(<cache.CachePartition>[
+      AnalysisEngine.instance.partitionManager_new.forSdk(sdk),
+      _privatePartition
+    ]);
+  }
+
+  @override
+  void dispose() {
+    _disposed = true;
+    for (List<PendingFuture> pendingFutures in _pendingFutureSources.values) {
+      for (PendingFuture pendingFuture in pendingFutures) {
+        pendingFuture.forciblyComplete();
+      }
+    }
+    _pendingFutureSources.clear();
+  }
+
+  @override
+  List<CompilationUnit> ensureResolvedDartUnits(Source unitSource) {
+    // TODO(brianwilkerson) Implement this.
+    return null;
+//    cache.CacheEntry entry = _cache.get(unitSource);
+//    // Check every library.
+//    List<CompilationUnit> units = <CompilationUnit>[];
+//    List<Source> containingLibraries = entry.containingLibraries;
+//    for (Source librarySource in containingLibraries) {
+//      CompilationUnit unit =
+//          entry.getValueInLibrary(DartEntry.RESOLVED_UNIT, librarySource);
+//      if (unit == null) {
+//        units = null;
+//        break;
+//      }
+//      units.add(unit);
+//    }
+//    // Invalidate the flushed RESOLVED_UNIT to force it eventually.
+//    if (units == null) {
+//      bool shouldBeScheduled = false;
+//      for (Source librarySource in containingLibraries) {
+//        if (entry.getStateInLibrary(DartEntry.RESOLVED_UNIT, librarySource) ==
+//            CacheState.FLUSHED) {
+//          entry.setStateInLibrary(
+//              DartEntry.RESOLVED_UNIT, librarySource, CacheState.INVALID);
+//          shouldBeScheduled = true;
+//        }
+//      }
+//      if (shouldBeScheduled) {
+//        _workManager.add(unitSource, SourcePriority.UNKNOWN);
+//      }
+//      // We cannot provide resolved units right now,
+//      // but the future analysis will.
+//      return null;
+//    }
+//    // done
+//    return units;
+  }
+
+  @override
+  bool exists(Source source) {
+    if (source == null) {
+      return false;
+    }
+    if (_contentCache.getContents(source) != null) {
+      return true;
+    }
+    return source.exists();
+  }
+
+  Element findElementById(int id) {
+    // TODO(brianwilkerson) Implement this.
+    return null;
+//    _ElementByIdFinder finder = new _ElementByIdFinder(id);
+//    try {
+//      MapIterator<AnalysisTarget, cache.CacheEntry> iterator =
+//          _cache.iterator();
+//      while (iterator.moveNext()) {
+//        cache.CacheEntry entry = iterator.value;
+//        if (entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY) {
+//          DartEntry dartEntry = entry;
+//          LibraryElement library = dartEntry.getValue(DartEntry.ELEMENT);
+//          if (library != null) {
+//            library.accept(finder);
+//          }
+//        }
+//      }
+//    } on _ElementByIdFinderException {
+//      return finder.result;
+//    }
+//    return null;
+  }
+
+  @override
+  cache.CacheEntry getCacheEntry(AnalysisTarget target) {
+    cache.CacheEntry entry = _cache.get(target);
+    if (entry == null) {
+      entry = new cache.CacheEntry();
+      _cache.put(target, entry);
+    }
+    return entry;
+  }
+
+  @override
+  CompilationUnitElement getCompilationUnitElement(
+      Source unitSource, Source librarySource) {
+    AnalysisTarget target = new LibrarySpecificUnit(librarySource, unitSource);
+    return _getResult(target, COMPILATION_UNIT_ELEMENT);
+  }
+
+  @override
+  TimestampedData<String> getContents(Source source) {
+    String contents = _contentCache.getContents(source);
+    if (contents != null) {
+      return new TimestampedData<String>(
+          _contentCache.getModificationStamp(source), contents);
+    }
+    return source.contents;
+  }
+
+  @override
+  InternalAnalysisContext getContextFor(Source source) {
+    InternalAnalysisContext context = _cache.getContextFor(source);
+    return context == null ? this : context;
+  }
+
+  @override
+  Element getElement(ElementLocation location) {
+    // TODO(brianwilkerson) This should not be a "get" method.
+    try {
+      List<String> components = location.components;
+      Source source = _computeSourceFromEncoding(components[0]);
+      String sourceName = source.shortName;
+      if (AnalysisEngine.isDartFileName(sourceName)) {
+        ElementImpl element = computeLibraryElement(source) as ElementImpl;
+        for (int i = 1; i < components.length; i++) {
+          if (element == null) {
+            return null;
+          }
+          element = element.getChild(components[i]);
+        }
+        return element;
+      }
+      if (AnalysisEngine.isHtmlFileName(sourceName)) {
+        return computeHtmlElement(source);
+      }
+    } catch (exception) {
+      // If the location cannot be decoded for some reason then the underlying
+      // cause should have been logged already and we can fall though to return
+      // null.
+    }
+    return null;
+  }
+
+  @override
+  AnalysisErrorInfo getErrors(Source source) => _getResult(source, DART_ERRORS);
+
+  @override
+  HtmlElement getHtmlElement(Source source) {
+    // TODO(brianwilkerson) Implement this.
+//    SourceEntry sourceEntry = getReadableSourceEntryOrNull(source);
+//    if (sourceEntry is HtmlEntry) {
+//      return sourceEntry.getValue(HtmlEntry.ELEMENT);
+//    }
+    return null;
+  }
+
+  @override
+  List<Source> getHtmlFilesReferencing(Source source) {
+    SourceKind sourceKind = getKindOf(source);
+    if (sourceKind == null) {
+      return Source.EMPTY_ARRAY;
+    }
+    List<Source> htmlSources = new List<Source>();
+    while (true) {
+      if (sourceKind == SourceKind.PART) {
+        List<Source> librarySources = getLibrariesContaining(source);
+        MapIterator<AnalysisTarget, cache.CacheEntry> iterator =
+            _cache.iterator();
+        while (iterator.moveNext()) {
+          cache.CacheEntry entry = iterator.value;
+          if (entry.getValue(SOURCE_KIND) == SourceKind.HTML) {
+            List<Source> referencedLibraries =
+                (entry as HtmlEntry).getValue(HtmlEntry.REFERENCED_LIBRARIES);
+            if (_containsAny(referencedLibraries, librarySources)) {
+              htmlSources.add(iterator.key);
+            }
+          }
+        }
+      } else {
+        MapIterator<AnalysisTarget, cache.CacheEntry> iterator =
+            _cache.iterator();
+        while (iterator.moveNext()) {
+          cache.CacheEntry entry = iterator.value;
+          if (entry.getValue(SOURCE_KIND) == SourceKind.HTML) {
+            List<Source> referencedLibraries =
+                (entry as HtmlEntry).getValue(HtmlEntry.REFERENCED_LIBRARIES);
+            if (_contains(referencedLibraries, source)) {
+              htmlSources.add(iterator.key);
+            }
+          }
+        }
+      }
+      break;
+    }
+    if (htmlSources.isEmpty) {
+      return Source.EMPTY_ARRAY;
+    }
+    return htmlSources;
+  }
+
+  @override
+  SourceKind getKindOf(Source source) => _getResult(source, SOURCE_KIND);
+
+  @override
+  List<Source> getLibrariesContaining(Source source) {
+    // TODO(brianwilkerson) Implement this.
+//    cache.CacheEntry sourceEntry = _cache.get(source);
+//    if (sourceEntry is DartEntry) {
+//      return sourceEntry.containingLibraries;
+//    }
+    return Source.EMPTY_ARRAY;
+  }
+
+  @override
+  List<Source> getLibrariesDependingOn(Source librarySource) {
+    List<Source> dependentLibraries = new List<Source>();
+    MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      cache.CacheEntry entry = iterator.value;
+      if (entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY) {
+        if (_contains(entry.getValue(EXPORTED_LIBRARIES), librarySource)) {
+          dependentLibraries.add(iterator.key);
+        }
+        if (_contains(entry.getValue(IMPORTED_LIBRARIES), librarySource)) {
+          dependentLibraries.add(iterator.key);
+        }
+      }
+    }
+    if (dependentLibraries.isEmpty) {
+      return Source.EMPTY_ARRAY;
+    }
+    return dependentLibraries;
+  }
+
+  @override
+  List<Source> getLibrariesReferencedFromHtml(Source htmlSource) {
+    // TODO(brianwilkerson) Implement this.
+//    cache.CacheEntry entry = getReadableSourceEntryOrNull(htmlSource);
+//    if (entry is HtmlEntry) {
+//      HtmlEntry htmlEntry = entry;
+//      return htmlEntry.getValue(HtmlEntry.REFERENCED_LIBRARIES);
+//    }
+    return Source.EMPTY_ARRAY;
+  }
+
+  @override
+  LibraryElement getLibraryElement(Source source) =>
+      _getResult(source, LIBRARY_ELEMENT);
+
+  @override
+  LineInfo getLineInfo(Source source) => _getResult(source, LINE_INFO);
+
+  @override
+  int getModificationStamp(Source source) {
+    int stamp = _contentCache.getModificationStamp(source);
+    if (stamp != null) {
+      return stamp;
+    }
+    return source.modificationStamp;
+  }
+
+  @override
+  Namespace getPublicNamespace(LibraryElement library) {
+    // TODO(brianwilkerson) Rename this to not start with 'get'.
+    // Note that this is not part of the API of the interface.
+    // TODO(brianwilkerson) The public namespace used to be cached, but no
+    // longer is. Konstantin adds:
+    // The only client of this method is NamespaceBuilder._createExportMapping(),
+    // and it is not used with tasks - instead we compute export namespace once
+    // using BuildExportNamespaceTask and reuse in scopes.
+    NamespaceBuilder builder = new NamespaceBuilder();
+    return builder.createPublicNamespaceForLibrary(library);
+  }
+
+  /**
+   * Return the cache entry associated with the given [source], or `null` if
+   * there is no entry associated with the source.
+   */
+  cache.CacheEntry getReadableSourceEntryOrNull(Source source) =>
+      _cache.get(source);
+
+  @override
+  CompilationUnit getResolvedCompilationUnit(
+      Source unitSource, LibraryElement library) {
+    if (library == null) {
+      return null;
+    }
+    return getResolvedCompilationUnit2(unitSource, library.source);
+  }
+
+  @override
+  CompilationUnit getResolvedCompilationUnit2(
+      Source unitSource, Source librarySource) => _getResult(
+          new LibrarySpecificUnit(librarySource, unitSource), RESOLVED_UNIT);
+
+  @override
+  ht.HtmlUnit getResolvedHtmlUnit(Source htmlSource) {
+    // TODO(brianwilkerson) Implement this.
+//    SourceEntry sourceEntry = getReadableSourceEntryOrNull(htmlSource);
+//    if (sourceEntry is HtmlEntry) {
+//      HtmlEntry htmlEntry = sourceEntry;
+//      return htmlEntry.getValue(HtmlEntry.RESOLVED_UNIT);
+//    }
+    return null;
+  }
+
+  @override
+  List<Source> getSourcesWithFullName(String path) {
+    List<Source> sources = <Source>[];
+    MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      AnalysisTarget target = iterator.key;
+      if (target is Source && target.fullName == path) {
+        sources.add(target);
+      }
+    }
+    return sources;
+  }
+
+  @override
+  bool handleContentsChanged(
+      Source source, String originalContents, String newContents, bool notify) {
+    cache.CacheEntry entry = _cache.get(source);
+    if (entry == null) {
+      return false;
+    }
+    bool changed = newContents != originalContents;
+    if (newContents != null) {
+      if (newContents != originalContents) {
+        _incrementalAnalysisCache =
+            IncrementalAnalysisCache.clear(_incrementalAnalysisCache, source);
+        if (!analysisOptions.incremental ||
+            !_tryPoorMansIncrementalResolution(source, newContents)) {
+          _sourceChanged(source);
+        }
+        entry.modificationTime = _contentCache.getModificationStamp(source);
+        entry.setValue(CONTENT, newContents);
+      } else {
+        entry.modificationTime = _contentCache.getModificationStamp(source);
+      }
+    } else if (originalContents != null) {
+      _incrementalAnalysisCache =
+          IncrementalAnalysisCache.clear(_incrementalAnalysisCache, source);
+      changed = newContents != originalContents;
+      // We are removing the overlay for the file, check if the file's
+      // contents is the same as it was in the overlay.
+      try {
+        TimestampedData<String> fileContents = getContents(source);
+        String fileContentsData = fileContents.data;
+        if (fileContentsData == originalContents) {
+          entry.setValue(CONTENT, fileContentsData);
+          entry.modificationTime = fileContents.modificationTime;
+          changed = false;
+        }
+      } catch (e) {}
+      // If not the same content (e.g. the file is being closed without save),
+      // then force analysis.
+      if (changed) {
+        _sourceChanged(source);
+      }
+    }
+    if (notify && changed) {
+      _onSourcesChangedController
+          .add(new SourcesChangedEvent.changedContent(source, newContents));
+    }
+    return changed;
+  }
+
+  /**
+   * Invalidates hints in the given [librarySource] and included parts.
+   */
+  void invalidateLibraryHints(Source librarySource) {
+    cache.CacheEntry entry = _cache.get(librarySource);
+    // Prepare sources to invalidate hints in.
+    List<Source> sources = <Source>[librarySource];
+    sources.addAll(entry.getValue(INCLUDED_PARTS));
+    // Invalidate hints.
+    for (Source source in sources) {
+      LibrarySpecificUnit unitTarget =
+          new LibrarySpecificUnit(librarySource, source);
+      cache.CacheEntry unitEntry = _cache.get(unitTarget);
+      if (unitEntry.getState(HINTS) == CacheState.VALID) {
+        unitEntry.setState(HINTS, CacheState.INVALID);
+      }
+    }
+  }
+
+  @override
+  bool isClientLibrary(Source librarySource) {
+    cache.CacheEntry entry = _cache.get(librarySource);
+    return entry.getValue(IS_CLIENT) && entry.getValue(IS_LAUNCHABLE);
+  }
+
+  @override
+  bool isServerLibrary(Source librarySource) {
+    cache.CacheEntry entry = _cache.get(librarySource);
+    return !entry.getValue(IS_CLIENT) && entry.getValue(IS_LAUNCHABLE);
+  }
+
+  @override
+  CompilationUnit parseCompilationUnit(Source source) {
+    if (!AnalysisEngine.isDartFileName(source.shortName)) {
+      return null;
+    }
+    return _computeResult(source, PARSED_UNIT);
+  }
+
+  @override
+  ht.HtmlUnit parseHtmlUnit(Source source) {
+    if (!AnalysisEngine.isHtmlFileName(source.shortName)) {
+      return null;
+    }
+    // TODO(brianwilkerson) Implement HTML analysis.
+    return null; //_computeResult(source, null);
+  }
+
+  @override
+  AnalysisResult performAnalysisTask() {
+    return PerformanceStatistics.performAnaysis.makeCurrentWhile(() {
+      bool done = !_driver.performAnalysisTask();
+      if (done) {
+        done = !_validateCacheConsistency();
+      }
+      List<ChangeNotice> notices = _getChangeNotices(done);
+      if (notices != null) {
+        int noticeCount = notices.length;
+        for (int i = 0; i < noticeCount; i++) {
+          ChangeNotice notice = notices[i];
+          _notifyErrors(notice.source, notice.errors, notice.lineInfo);
+        }
+      }
+      return new AnalysisResult(notices, -1, '', -1);
+    });
+  }
+
+  @override
+  void recordLibraryElements(Map<Source, LibraryElement> elementMap) {
+    elementMap.forEach((Source librarySource, LibraryElement library) {
+      //
+      // Cache the element in the library's info.
+      //
+      cache.CacheEntry entry = getCacheEntry(librarySource);
+      entry.setValue(BUILD_DIRECTIVES_ERRORS, AnalysisError.NO_ERRORS);
+      entry.setValue(
+          BUILD_FUNCTION_TYPE_ALIASES_ERRORS, AnalysisError.NO_ERRORS);
+      entry.setValue(BUILD_LIBRARY_ERRORS, AnalysisError.NO_ERRORS);
+      // CLASS_ELEMENTS
+      entry.setValue(COMPILATION_UNIT_ELEMENT, library.definingCompilationUnit);
+      // CONSTRUCTORS
+      // CONSTRUCTORS_ERRORS
+      entry.setState(CONTENT, CacheState.FLUSHED);
+      entry.setValue(EXPORTED_LIBRARIES, Source.EMPTY_ARRAY);
+      // EXPORT_SOURCE_CLOSURE
+      entry.setValue(IMPORTED_LIBRARIES, Source.EMPTY_ARRAY);
+      // IMPORT_SOURCE_CLOSURE
+      entry.setValue(INCLUDED_PARTS, Source.EMPTY_ARRAY);
+      entry.setValue(IS_CLIENT, true);
+      entry.setValue(IS_LAUNCHABLE, false);
+      entry.setValue(LIBRARY_ELEMENT, library);
+      entry.setValue(LIBRARY_ELEMENT1, library);
+      entry.setValue(LIBRARY_ELEMENT2, library);
+      entry.setValue(LIBRARY_ELEMENT3, library);
+      entry.setValue(LIBRARY_ELEMENT4, library);
+      entry.setValue(LIBRARY_ELEMENT5, library);
+      entry.setValue(LINE_INFO, new LineInfo(<int>[0]));
+      entry.setValue(PARSE_ERRORS, AnalysisError.NO_ERRORS);
+      entry.setState(PARSED_UNIT, CacheState.FLUSHED);
+      entry.setState(RESOLVE_TYPE_NAMES_ERRORS, CacheState.FLUSHED);
+      entry.setValue(SCAN_ERRORS, AnalysisError.NO_ERRORS);
+      entry.setValue(SOURCE_KIND, SourceKind.LIBRARY);
+      entry.setState(TOKEN_STREAM, CacheState.FLUSHED);
+      entry.setValue(UNITS, <Source>[librarySource]);
+
+      LibrarySpecificUnit unit =
+          new LibrarySpecificUnit(librarySource, librarySource);
+      entry = getCacheEntry(unit);
+      entry.setValue(HINTS, AnalysisError.NO_ERRORS);
+      // dartEntry.setValue(LINTS, AnalysisError.NO_ERRORS);
+      entry.setState(RESOLVE_REFERENCES_ERRORS, CacheState.FLUSHED);
+      entry.setState(RESOLVED_UNIT, CacheState.FLUSHED);
+      entry.setState(RESOLVED_UNIT1, CacheState.FLUSHED);
+      entry.setState(RESOLVED_UNIT2, CacheState.FLUSHED);
+      entry.setState(RESOLVED_UNIT3, CacheState.FLUSHED);
+      entry.setState(RESOLVED_UNIT4, CacheState.FLUSHED);
+      entry.setState(RESOLVED_UNIT5, CacheState.FLUSHED);
+      // USED_IMPORTED_ELEMENTS
+      // USED_LOCAL_ELEMENTS
+      entry.setValue(VERIFY_ERRORS, AnalysisError.NO_ERRORS);
+    });
+
+    cache.CacheEntry entry = getCacheEntry(AnalysisContextTarget.request);
+    entry.setValue(TYPE_PROVIDER, typeProvider);
+  }
+
+  @override
+  void removeListener(AnalysisListener listener) {
+    _listeners.remove(listener);
+  }
+
+  @override
+  CompilationUnit resolveCompilationUnit(
+      Source unitSource, LibraryElement library) {
+    if (library == null) {
+      return null;
+    }
+    return resolveCompilationUnit2(unitSource, library.source);
+  }
+
+  @override
+  CompilationUnit resolveCompilationUnit2(
+      Source unitSource, Source librarySource) => _computeResult(
+          new LibrarySpecificUnit(librarySource, unitSource), RESOLVED_UNIT);
+
+  @override
+  ht.HtmlUnit resolveHtmlUnit(Source htmlSource) {
+    computeHtmlElement(htmlSource);
+    return parseHtmlUnit(htmlSource);
+  }
+
+  @override
+  void setChangedContents(Source source, String contents, int offset,
+      int oldLength, int newLength) {
+    if (_contentRangeChanged(source, contents, offset, oldLength, newLength)) {
+      _onSourcesChangedController.add(new SourcesChangedEvent.changedRange(
+          source, contents, offset, oldLength, newLength));
+    }
+  }
+
+  @override
+  void setContents(Source source, String contents) {
+    _contentsChanged(source, contents, true);
+  }
+
+  @override
+  void visitCacheItems(void callback(Source source, SourceEntry dartEntry,
+      DataDescriptor rowDesc, CacheState state)) {
+    // TODO(brianwilkerson) Figure out where this is used and adjust the call
+    // sites to use CacheEntry's.
+//    bool hintsEnabled = _options.hint;
+//    bool lintsEnabled = _options.lint;
+//    MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator();
+//    while (iterator.moveNext()) {
+//      Source source = iterator.key;
+//      cache.CacheEntry sourceEntry = iterator.value;
+//      for (DataDescriptor descriptor in sourceEntry.descriptors) {
+//        if (descriptor == DartEntry.SOURCE_KIND) {
+//          // The source kind is always valid, so the state isn't interesting.
+//          continue;
+//        } else if (descriptor == DartEntry.CONTAINING_LIBRARIES) {
+//          // The list of containing libraries is always valid, so the state
+//          // isn't interesting.
+//          continue;
+//        } else if (descriptor == DartEntry.PUBLIC_NAMESPACE) {
+//          // The public namespace isn't computed by performAnalysisTask()
+//          // and therefore isn't interesting.
+//          continue;
+//        } else if (descriptor == HtmlEntry.HINTS) {
+//          // We are not currently recording any hints related to HTML.
+//          continue;
+//        }
+//        callback(
+//            source, sourceEntry, descriptor, sourceEntry.getState(descriptor));
+//      }
+//      if (sourceEntry is DartEntry) {
+//        // get library-specific values
+//        List<Source> librarySources = getLibrariesContaining(source);
+//        for (Source librarySource in librarySources) {
+//          for (DataDescriptor descriptor in sourceEntry.libraryDescriptors) {
+//            if (descriptor == DartEntry.BUILT_ELEMENT ||
+//                descriptor == DartEntry.BUILT_UNIT) {
+//              // These values are not currently being computed, so their state
+//              // is not interesting.
+//              continue;
+//            } else if (!sourceEntry.explicitlyAdded &&
+//                !_generateImplicitErrors &&
+//                (descriptor == DartEntry.VERIFICATION_ERRORS ||
+//                    descriptor == DartEntry.HINTS ||
+//                    descriptor == DartEntry.LINTS)) {
+//              continue;
+//            } else if (source.isInSystemLibrary &&
+//                !_generateSdkErrors &&
+//                (descriptor == DartEntry.VERIFICATION_ERRORS ||
+//                    descriptor == DartEntry.HINTS ||
+//                    descriptor == DartEntry.LINTS)) {
+//              continue;
+//            } else if (!hintsEnabled && descriptor == DartEntry.HINTS) {
+//              continue;
+//            } else if (!lintsEnabled && descriptor == DartEntry.LINTS) {
+//              continue;
+//            }
+//            callback(librarySource, sourceEntry, descriptor,
+//                sourceEntry.getStateInLibrary(descriptor, librarySource));
+//          }
+//        }
+//      }
+//    }
+  }
+
+  /**
+   * Visit all entries of the content cache.
+   */
+  void visitContentCache(ContentCacheVisitor visitor) {
+    _contentCache.accept(visitor);
+  }
+
+  /**
+   * Add all of the sources contained in the given source [container] to the
+   * given list of [sources].
+   */
+  void _addSourcesInContainer(List<Source> sources, SourceContainer container) {
+    MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      Source source = iterator.key;
+      if (container.contains(source)) {
+        sources.add(source);
+      }
+    }
+  }
+
+  /**
+   * Return the priority that should be used when the source associated with
+   * the given [entry] is added to the work manager.
+   */
+  SourcePriority _computePriority(cache.CacheEntry entry) {
+    // Used in commented out code.
+    SourceKind kind = entry.getValue(SOURCE_KIND);
+    if (kind == SourceKind.LIBRARY) {
+      return SourcePriority.LIBRARY;
+    } else if (kind == SourceKind.PART) {
+      return SourcePriority.NORMAL_PART;
+    }
+    return SourcePriority.UNKNOWN;
+  }
+
+  Object /*V*/ _computeResult(
+      AnalysisTarget target, ResultDescriptor /*<V>*/ descriptor) {
+    cache.CacheEntry entry = _cache.get(target);
+    if (entry == null) {
+      return descriptor.defaultValue;
+    }
+    if (descriptor is CompositeResultDescriptor) {
+      List compositeResults = [];
+      for (ResultDescriptor descriptor in descriptor.contributors) {
+        List value = _computeResult(target, descriptor);
+        compositeResults.addAll(value);
+      }
+      return compositeResults;
+    }
+    CacheState state = entry.getState(descriptor);
+    if (state == CacheState.FLUSHED || state == CacheState.INVALID) {
+      _driver.computeResult(target, descriptor);
+    }
+    return entry.getValue(descriptor);
+  }
+
+  /**
+   * Given the encoded form of a source ([encoding]), use the source factory to
+   * reconstitute the original source.
+   */
+  Source _computeSourceFromEncoding(String encoding) =>
+      _sourceFactory.fromEncoding(encoding);
+
+  /**
+   * Return `true` if the given list of [sources] contains the given
+   * [targetSource].
+   */
+  bool _contains(List<Source> sources, Source targetSource) {
+    for (Source source in sources) {
+      if (source == targetSource) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the given list of [sources] contains any of the given
+   * [targetSources].
+   */
+  bool _containsAny(List<Source> sources, List<Source> targetSources) {
+    for (Source targetSource in targetSources) {
+      if (_contains(sources, targetSource)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Set the contents of the given [source] to the given [contents] and mark the
+   * source as having changed. The additional [offset], [oldLength] and
+   * [newLength] information is used by the context to determine what reanalysis
+   * is necessary. The method [setChangedContents] triggers a source changed
+   * event where as this method does not.
+   */
+  bool _contentRangeChanged(Source source, String contents, int offset,
+      int oldLength, int newLength) {
+    bool changed = false;
+    String originalContents = _contentCache.setContents(source, contents);
+    if (contents != null) {
+      if (contents != originalContents) {
+        // TODO(brianwilkerson) Find a better way to do incremental analysis.
+//        if (_options.incremental) {
+//          _incrementalAnalysisCache = IncrementalAnalysisCache.update(
+//              _incrementalAnalysisCache, source, originalContents, contents,
+//              offset, oldLength, newLength, _cache.get(source));
+//        }
+        _sourceChanged(source);
+        changed = true;
+        cache.CacheEntry entry = _cache.get(source);
+        if (entry != null) {
+          entry.modificationTime = _contentCache.getModificationStamp(source);
+          entry.setValue(CONTENT, contents);
+        }
+      }
+    } else if (originalContents != null) {
+      _incrementalAnalysisCache =
+          IncrementalAnalysisCache.clear(_incrementalAnalysisCache, source);
+      _sourceChanged(source);
+      changed = true;
+    }
+    return changed;
+  }
+
+  /**
+   * Set the contents of the given [source] to the given [contents] and mark the
+   * source as having changed. This has the effect of overriding the default
+   * contents of the source. If the contents are `null` the override is removed
+   * so that the default contents will be returned. If [notify] is true, a
+   * source changed event is triggered.
+   */
+  void _contentsChanged(Source source, String contents, bool notify) {
+    String originalContents = _contentCache.setContents(source, contents);
+    handleContentsChanged(source, originalContents, contents, notify);
+  }
+
+  /**
+   * Create a cache entry for the given [source]. The source was explicitly
+   * added to this context if [explicitlyAdded] is `true`. Return the cache
+   * entry that was created.
+   */
+  cache.CacheEntry _createCacheEntry(Source source, bool explicitlyAdded) {
+    cache.CacheEntry entry = new cache.CacheEntry();
+    entry.modificationTime = getModificationStamp(source);
+    entry.explicitlyAdded = explicitlyAdded;
+    _cache.put(source, entry);
+    return entry;
+  }
+
+  /**
+   * Return a list containing all of the change notices that are waiting to be
+   * returned. If there are no notices, then return either `null` or an empty
+   * list, depending on the value of [nullIfEmpty].
+   */
+  List<ChangeNotice> _getChangeNotices(bool nullIfEmpty) {
+    if (_pendingNotices.isEmpty) {
+      if (nullIfEmpty) {
+        return null;
+      }
+      return ChangeNoticeImpl.EMPTY_ARRAY;
+    }
+    List<ChangeNotice> notices = new List.from(_pendingNotices.values);
+    _pendingNotices.clear();
+    return notices;
+  }
+
+  /**
+   * Return a change notice for the given [source], creating one if one does not
+   * already exist.
+   */
+  ChangeNoticeImpl _getNotice(Source source) {
+    // Used in commented out code.
+    ChangeNoticeImpl notice = _pendingNotices[source];
+    if (notice == null) {
+      notice = new ChangeNoticeImpl(source);
+      _pendingNotices[source] = notice;
+    }
+    return notice;
+  }
+
+  Object _getResult(AnalysisTarget target, ResultDescriptor descriptor) {
+    cache.CacheEntry entry = _cache.get(target);
+    if (entry != null && entry.isValid(descriptor)) {
+      return entry.getValue(descriptor);
+    }
+    return descriptor.defaultValue;
+  }
+
+  /**
+   * Return a list containing all of the sources known to this context that have
+   * the given [kind].
+   */
+  List<Source> _getSources(SourceKind kind) {
+    List<Source> sources = new List<Source>();
+    MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      if (iterator.value.getValue(SOURCE_KIND) == kind &&
+          iterator.key is Source) {
+        sources.add(iterator.key);
+      }
+    }
+    return sources;
+  }
+
+  /**
+   * Look at the given [source] to see whether a task needs to be performed
+   * related to it. If so, add the source to the set of sources that need to be
+   * processed. This method is intended to be used for testing purposes only.
+   */
+  void _getSourcesNeedingProcessing(Source source, cache.CacheEntry sourceEntry,
+      bool isPriority, bool hintsEnabled, bool lintsEnabled,
+      HashSet<Source> sources) {
+    CacheState state = sourceEntry.getState(CONTENT);
+    if (state == CacheState.INVALID ||
+        (isPriority && state == CacheState.FLUSHED)) {
+      sources.add(source);
+      return;
+    } else if (state == CacheState.ERROR) {
+      return;
+    }
+    state = sourceEntry.getState(SOURCE_KIND);
+    if (state == CacheState.INVALID ||
+        (isPriority && state == CacheState.FLUSHED)) {
+      sources.add(source);
+      return;
+    } else if (state == CacheState.ERROR) {
+      return;
+    }
+    SourceKind kind = sourceEntry.getValue(SOURCE_KIND);
+    if (kind == SourceKind.LIBRARY || kind == SourceKind.PART) {
+      state = sourceEntry.getState(SCAN_ERRORS);
+      if (state == CacheState.INVALID ||
+          (isPriority && state == CacheState.FLUSHED)) {
+        sources.add(source);
+        return;
+      } else if (state == CacheState.ERROR) {
+        return;
+      }
+      state = sourceEntry.getState(PARSE_ERRORS);
+      if (state == CacheState.INVALID ||
+          (isPriority && state == CacheState.FLUSHED)) {
+        sources.add(source);
+        return;
+      } else if (state == CacheState.ERROR) {
+        return;
+      }
+//      if (isPriority) {
+//        if (!sourceEntry.hasResolvableCompilationUnit) {
+//          sources.add(source);
+//          return;
+//        }
+//      }
+      for (Source librarySource in getLibrariesContaining(source)) {
+        cache.CacheEntry libraryEntry = _cache.get(librarySource);
+        state = libraryEntry.getState(LIBRARY_ELEMENT);
+        if (state == CacheState.INVALID ||
+            (isPriority && state == CacheState.FLUSHED)) {
+          sources.add(source);
+          return;
+        } else if (state == CacheState.ERROR) {
+          return;
+        }
+        cache.CacheEntry unitEntry =
+            _cache.get(new LibrarySpecificUnit(librarySource, source));
+        state = unitEntry.getState(RESOLVED_UNIT);
+        if (state == CacheState.INVALID ||
+            (isPriority && state == CacheState.FLUSHED)) {
+          sources.add(source);
+          return;
+        } else if (state == CacheState.ERROR) {
+          return;
+        }
+        if (_shouldErrorsBeAnalyzed(source, unitEntry)) {
+          state = unitEntry.getState(VERIFY_ERRORS);
+          if (state == CacheState.INVALID ||
+              (isPriority && state == CacheState.FLUSHED)) {
+            sources.add(source);
+            return;
+          } else if (state == CacheState.ERROR) {
+            return;
+          }
+          if (hintsEnabled) {
+            state = unitEntry.getState(HINTS);
+            if (state == CacheState.INVALID ||
+                (isPriority && state == CacheState.FLUSHED)) {
+              sources.add(source);
+              return;
+            } else if (state == CacheState.ERROR) {
+              return;
+            }
+          }
+//          if (lintsEnabled) {
+//            state = unitEntry.getState(LINTS);
+//            if (state == CacheState.INVALID ||
+//                (isPriority && state == CacheState.FLUSHED)) {
+//              sources.add(source);
+//              return;
+//            } else if (state == CacheState.ERROR) {
+//              return;
+//            }
+//          }
+        }
+      }
+//    } else if (kind == SourceKind.HTML) {
+//      CacheState parsedUnitState = sourceEntry.getState(HtmlEntry.PARSED_UNIT);
+//      if (parsedUnitState == CacheState.INVALID ||
+//          (isPriority && parsedUnitState == CacheState.FLUSHED)) {
+//        sources.add(source);
+//        return;
+//      }
+//      CacheState resolvedUnitState =
+//          sourceEntry.getState(HtmlEntry.RESOLVED_UNIT);
+//      if (resolvedUnitState == CacheState.INVALID ||
+//          (isPriority && resolvedUnitState == CacheState.FLUSHED)) {
+//        sources.add(source);
+//        return;
+//      }
+    }
+  }
+
+  /**
+   * Invalidate all of the resolution results computed by this context. The flag
+   * [invalidateUris] should be `true` if the cached results of converting URIs
+   * to source files should also be invalidated.
+   */
+  void _invalidateAllLocalResolutionInformation(bool invalidateUris) {
+    HashMap<Source, List<Source>> oldPartMap =
+        new HashMap<Source, List<Source>>();
+    // TODO(brianwilkerson) Implement this
+//    MapIterator<AnalysisTarget, cache.CacheEntry> iterator =
+//        _privatePartition.iterator();
+//    while (iterator.moveNext()) {
+//      AnalysisTarget target = iterator.key;
+//      cache.CacheEntry entry = iterator.value;
+//      if (entry is HtmlEntry) {
+//        HtmlEntry htmlEntry = entry;
+//        htmlEntry.invalidateAllResolutionInformation(invalidateUris);
+//        iterator.value = htmlEntry;
+//        _workManager.add(target, SourcePriority.HTML);
+//      } else if (entry is DartEntry) {
+//        DartEntry dartEntry = entry;
+//        oldPartMap[target] = dartEntry.getValue(DartEntry.INCLUDED_PARTS);
+//        dartEntry.invalidateAllResolutionInformation(invalidateUris);
+//        iterator.value = dartEntry;
+//        _workManager.add(target, _computePriority(dartEntry));
+//      }
+//    }
+    _removeFromPartsUsingMap(oldPartMap);
+  }
+
+  /**
+   * In response to a change to at least one of the compilation units in the
+   * library defined by the given [librarySource], invalidate any results that
+   * are dependent on the result of resolving that library.
+   *
+   * <b>Note:</b> Any cache entries that were accessed before this method was
+   * invoked must be re-accessed after this method returns.
+   */
+  void _invalidateLibraryResolution(Source librarySource) {
+    // TODO(brianwilkerson) Figure out whether we still need this.
+    // TODO(brianwilkerson) This could be optimized. There's no need to flush
+    // all of these entries if the public namespace hasn't changed, which will
+    // be a fairly common case. The question is whether we can afford the time
+    // to compute the namespace to look for differences.
+//    DartEntry libraryEntry = _getReadableDartEntry(librarySource);
+//    if (libraryEntry != null) {
+//      List<Source> includedParts =
+//          libraryEntry.getValue(DartEntry.INCLUDED_PARTS);
+//      libraryEntry.invalidateAllResolutionInformation(false);
+//      _workManager.add(librarySource, SourcePriority.LIBRARY);
+//      for (Source partSource in includedParts) {
+//        SourceEntry partEntry = _cache.get(partSource);
+//        if (partEntry is DartEntry) {
+//          partEntry.invalidateAllResolutionInformation(false);
+//        }
+//      }
+//    }
+  }
+
+  /**
+   * Log the given debugging [message].
+   */
+  void _logInformation(String message) {
+    AnalysisEngine.instance.logger.logInformation(message);
+  }
+
+  /**
+   * Notify all of the analysis listeners that the errors associated with the
+   * given [source] has been updated to the given [errors].
+   */
+  void _notifyErrors(
+      Source source, List<AnalysisError> errors, LineInfo lineInfo) {
+    int count = _listeners.length;
+    for (int i = 0; i < count; i++) {
+      _listeners[i].computedErrors(this, source, errors, lineInfo);
+    }
+  }
+
+  /**
+   * Remove the given libraries that are keys in the given map from the list of
+   * containing libraries for each of the parts in the corresponding value.
+   */
+  void _removeFromPartsUsingMap(HashMap<Source, List<Source>> oldPartMap) {
+    // TODO(brianwilkerson) Figure out whether we still need this.
+//    oldPartMap.forEach((Source librarySource, List<Source> oldParts) {
+//      for (int i = 0; i < oldParts.length; i++) {
+//        Source partSource = oldParts[i];
+//        if (partSource != librarySource) {
+//          DartEntry partEntry = _getReadableDartEntry(partSource);
+//          if (partEntry != null) {
+//            partEntry.removeContainingLibrary(librarySource);
+//            if (partEntry.containingLibraries.length == 0 &&
+//                !exists(partSource)) {
+//              _cache.remove(partSource);
+//            }
+//          }
+//        }
+//      }
+//    });
+  }
+
+  /**
+   * Remove the given [source] from the priority order if it is in the list.
+   */
+  void _removeFromPriorityOrder(Source source) {
+    int count = _priorityOrder.length;
+    List<Source> newOrder = new List<Source>();
+    for (int i = 0; i < count; i++) {
+      if (_priorityOrder[i] != source) {
+        newOrder.add(_priorityOrder[i]);
+      }
+    }
+    if (newOrder.length < count) {
+      analysisPriorityOrder = newOrder;
+    }
+  }
+
+  /**
+   * Return `true` if errors should be produced for the given [source]. The
+   * [entry] associated with the source is passed in for efficiency.
+   */
+  bool _shouldErrorsBeAnalyzed(Source source, cache.CacheEntry entry) {
+    if (source.isInSystemLibrary) {
+      return _options.generateSdkErrors;
+    } else if (!entry.explicitlyAdded) {
+      return _options.generateImplicitErrors;
+    } else {
+      return true;
+    }
+  }
+
+  /**
+   * Create an entry for the newly added [source] and invalidate any sources
+   * that referenced the source before it existed.
+   */
+  void _sourceAvailable(Source source) {
+    cache.CacheEntry entry = _cache.get(source);
+    if (entry == null) {
+      _createCacheEntry(source, true);
+    } else {
+      // TODO(brianwilkerson) Implement this.
+//      _propagateInvalidation(source, entry);
+    }
+  }
+
+  /**
+   * Invalidate the [source] that was changed and any sources that referenced
+   * the source before it existed.
+   */
+  void _sourceChanged(Source source) {
+    cache.CacheEntry entry = _cache.get(source);
+    // If the source is removed, we don't care about it.
+    if (entry == null) {
+      return;
+    }
+    // Check whether the content of the source is the same as it was the last
+    // time.
+    String sourceContent = entry.getValue(CONTENT);
+    if (sourceContent != null) {
+      entry.setState(CONTENT, CacheState.FLUSHED);
+      try {
+        TimestampedData<String> fileContents = getContents(source);
+        if (fileContents.data == sourceContent) {
+          return;
+        }
+      } catch (e) {}
+    }
+    // We need to invalidate the cache.
+    // TODO(brianwilkerson) Implement this.
+//    _propagateInvalidation(source, entry);
+  }
+
+  /**
+   * Record that the give [source] has been deleted.
+   */
+  void _sourceDeleted(Source source) {
+    // TODO(brianwilkerson) Implement this.
+//    SourceEntry sourceEntry = _cache.get(source);
+//    if (sourceEntry is HtmlEntry) {
+//      HtmlEntry htmlEntry = sourceEntry;
+//      htmlEntry.recordContentError(new CaughtException(
+//          new AnalysisException("This source was marked as being deleted"),
+//          null));
+//    } else if (sourceEntry is DartEntry) {
+//      DartEntry dartEntry = sourceEntry;
+//      HashSet<Source> libraries = new HashSet<Source>();
+//      for (Source librarySource in getLibrariesContaining(source)) {
+//        libraries.add(librarySource);
+//        for (Source dependentLibrary
+//            in getLibrariesDependingOn(librarySource)) {
+//          libraries.add(dependentLibrary);
+//        }
+//      }
+//      for (Source librarySource in libraries) {
+//        _invalidateLibraryResolution(librarySource);
+//      }
+//      dartEntry.recordContentError(new CaughtException(
+//          new AnalysisException("This source was marked as being deleted"),
+//          null));
+//    }
+    _removeFromPriorityOrder(source);
+  }
+
+  /**
+   * Record that the given [source] has been removed.
+   */
+  void _sourceRemoved(Source source) {
+    List<Source> containingLibraries = getLibrariesContaining(source);
+    if (containingLibraries != null && containingLibraries.isNotEmpty) {
+      HashSet<Source> libraries = new HashSet<Source>();
+      for (Source librarySource in containingLibraries) {
+        libraries.add(librarySource);
+        for (Source dependentLibrary
+            in getLibrariesDependingOn(librarySource)) {
+          libraries.add(dependentLibrary);
+        }
+      }
+      for (Source librarySource in libraries) {
+        _invalidateLibraryResolution(librarySource);
+      }
+    }
+    _cache.remove(source);
+    _removeFromPriorityOrder(source);
+  }
+
+  /**
+   * TODO(scheglov) A hackish, limited incremental resolution implementation.
+   */
+  bool _tryPoorMansIncrementalResolution(Source unitSource, String newCode) {
+    // TODO(brianwilkerson) Implement this.
+    return false;
+//    return PerformanceStatistics.incrementalAnalysis.makeCurrentWhile(() {
+//      incrementalResolutionValidation_lastUnitSource = null;
+//      incrementalResolutionValidation_lastLibrarySource = null;
+//      incrementalResolutionValidation_lastUnit = null;
+//      // prepare the entry
+//      cache.CacheEntry entry = _cache.get(unitSource);
+//      if (entry == null) {
+//        return false;
+//      }
+//      // prepare the (only) library source
+//      List<Source> librarySources = getLibrariesContaining(unitSource);
+//      if (librarySources.length != 1) {
+//        return false;
+//      }
+//      Source librarySource = librarySources[0];
+//      // prepare the library element
+//      LibraryElement libraryElement = getLibraryElement(librarySource);
+//      if (libraryElement == null) {
+//        return false;
+//      }
+//      // prepare the existing unit
+//      CompilationUnit oldUnit =
+//          getResolvedCompilationUnit2(unitSource, librarySource);
+//      if (oldUnit == null) {
+//        return false;
+//      }
+//      // do resolution
+//      Stopwatch perfCounter = new Stopwatch()..start();
+//      PoorMansIncrementalResolver resolver = new PoorMansIncrementalResolver(
+//          typeProvider, unitSource, entry, oldUnit,
+//          analysisOptions.incrementalApi, analysisOptions);
+//      bool success = resolver.resolve(newCode);
+//      AnalysisEngine.instance.instrumentationService.logPerformance(
+//          AnalysisPerformanceKind.INCREMENTAL, perfCounter,
+//          'success=$success,context_id=$_id,code_length=${newCode.length}');
+//      if (!success) {
+//        return false;
+//      }
+//      // if validation, remember the result, but throw it away
+//      if (analysisOptions.incrementalValidation) {
+//        incrementalResolutionValidation_lastUnitSource = oldUnit.element.source;
+//        incrementalResolutionValidation_lastLibrarySource =
+//            oldUnit.element.library.source;
+//        incrementalResolutionValidation_lastUnit = oldUnit;
+//        return false;
+//      }
+//      // prepare notice
+//      {
+//        LineInfo lineInfo = getLineInfo(unitSource);
+//        ChangeNoticeImpl notice = _getNotice(unitSource);
+//        notice.resolvedDartUnit = oldUnit;
+//        notice.setErrors(entry.allErrors, lineInfo);
+//      }
+//      // OK
+//      return true;
+//    });
+  }
+
+  /**
+   * Check the cache for any invalid entries (entries whose modification time
+   * does not match the modification time of the source associated with the
+   * entry). Invalid entries will be marked as invalid so that the source will
+   * be re-analyzed. Return `true` if at least one entry was invalid.
+   */
+  bool _validateCacheConsistency() {
+    int consistencyCheckStart = JavaSystem.nanoTime();
+    List<AnalysisTarget> changedTargets = new List<AnalysisTarget>();
+    List<AnalysisTarget> missingTargets = new List<AnalysisTarget>();
+    MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      AnalysisTarget target = iterator.key;
+      cache.CacheEntry entry = iterator.value;
+      if (target is Source) {
+        int sourceTime = getModificationStamp(target);
+        if (sourceTime != entry.modificationTime) {
+          changedTargets.add(target);
+        }
+      }
+      if (entry.exception != null) {
+        if (!exists(target)) {
+          missingTargets.add(target);
+        }
+      }
+    }
+    int count = changedTargets.length;
+    for (int i = 0; i < count; i++) {
+      _sourceChanged(changedTargets[i]);
+    }
+    int removalCount = 0;
+    for (AnalysisTarget target in missingTargets) {
+      if (target is Source &&
+          getLibrariesContaining(target).isEmpty &&
+          getLibrariesDependingOn(target).isEmpty) {
+        _cache.remove(target);
+        removalCount++;
+      }
+    }
+    int consistencyCheckEnd = JavaSystem.nanoTime();
+    if (changedTargets.length > 0 || missingTargets.length > 0) {
+      StringBuffer buffer = new StringBuffer();
+      buffer.write("Consistency check took ");
+      buffer.write((consistencyCheckEnd - consistencyCheckStart) / 1000000.0);
+      buffer.writeln(" ms and found");
+      buffer.write("  ");
+      buffer.write(changedTargets.length);
+      buffer.writeln(" inconsistent entries");
+      buffer.write("  ");
+      buffer.write(missingTargets.length);
+      buffer.write(" missing sources (");
+      buffer.write(removalCount);
+      buffer.writeln(" removed");
+      for (Source source in missingTargets) {
+        buffer.write("    ");
+        buffer.writeln(source.fullName);
+      }
+      _logInformation(buffer.toString());
+    }
+    return changedTargets.length > 0;
+  }
+}
+
+/**
+ * A retention policy used by an analysis context.
+ */
+class ContextRetentionPolicy implements cache.CacheRetentionPolicy {
+  /**
+   * The context associated with this policy.
+   */
+  final AnalysisContextImpl context;
+
+  /**
+   * Initialize a newly created policy to be associated with the given
+   * [context].
+   */
+  ContextRetentionPolicy(this.context);
+
+  @override
+  RetentionPriority getAstPriority(
+      AnalysisTarget target, cache.CacheEntry entry) {
+    int priorityCount = context._priorityOrder.length;
+    for (int i = 0; i < priorityCount; i++) {
+      if (target == context._priorityOrder[i]) {
+        return RetentionPriority.HIGH;
+      }
+    }
+    if (_astIsNeeded(entry)) {
+      return RetentionPriority.MEDIUM;
+    }
+    return RetentionPriority.LOW;
+  }
+
+  bool _astIsNeeded(cache.CacheEntry entry) =>
+      entry.isInvalid(BUILD_FUNCTION_TYPE_ALIASES_ERRORS) ||
+          entry.isInvalid(BUILD_LIBRARY_ERRORS) ||
+          entry.isInvalid(CONSTRUCTORS_ERRORS) ||
+          entry.isInvalid(HINTS) ||
+          //entry.isInvalid(LINTS) ||
+          entry.isInvalid(RESOLVE_REFERENCES_ERRORS) ||
+          entry.isInvalid(RESOLVE_TYPE_NAMES_ERRORS) ||
+          entry.isInvalid(VERIFY_ERRORS);
+}
+
+/**
+ * An object that manages the partitions that can be shared between analysis
+ * contexts.
+ */
+class PartitionManager {
+  /**
+   * The default cache size for a Dart SDK partition.
+   */
+  static int _DEFAULT_SDK_CACHE_SIZE = 256;
+
+  /**
+   * A table mapping SDK's to the partitions used for those SDK's.
+   */
+  HashMap<DartSdk, cache.SdkCachePartition> _sdkPartitions =
+      new HashMap<DartSdk, cache.SdkCachePartition>();
+
+  /**
+   * Clear any cached data being maintained by this manager.
+   */
+  void clearCache() {
+    _sdkPartitions.clear();
+  }
+
+  /**
+   * Return the partition being used for the given [sdk], creating the partition
+   * if necessary.
+   */
+  cache.SdkCachePartition forSdk(DartSdk sdk) {
+    // Call sdk.context now, because when it creates a new
+    // InternalAnalysisContext instance, it calls forSdk() again, so creates an
+    // SdkCachePartition instance.
+    // So, if we initialize context after "partition == null", we end up
+    // with two SdkCachePartition instances.
+    InternalAnalysisContext sdkContext = sdk.context;
+    // Check cache for an existing partition.
+    cache.SdkCachePartition partition = _sdkPartitions[sdk];
+    if (partition == null) {
+      partition =
+          new cache.SdkCachePartition(sdkContext, _DEFAULT_SDK_CACHE_SIZE);
+      _sdkPartitions[sdk] = partition;
+    }
+    return partition;
+  }
+}
diff --git a/analyzer/lib/src/error.dart b/analyzer/lib/src/error.dart
new file mode 100644
index 0000000..60f5cf0
--- /dev/null
+++ b/analyzer/lib/src/error.dart
@@ -0,0 +1,93 @@
+// Copyright (c) 2013, 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 error;
+
+import 'dart:collection';
+
+import 'generated/error.dart';
+
+/// The maximum line length when printing extracted source code when converting
+/// an [AnalyzerError] to a string.
+final _MAX_ERROR_LINE_LENGTH = 120;
+
+/// A wrapper around [AnalysisError] that provides a more user-friendly string
+/// representation.
+class AnalyzerError implements Exception {
+  final AnalysisError error;
+
+  AnalyzerError(this.error);
+
+  String get message => toString();
+
+  String toString() {
+    var builder = new StringBuffer();
+
+    // Print a less friendly string representation to ensure that
+    // error.source.contents is not executed, as .contents it isn't async
+    builder.write("Error in ${error.source.shortName}: ${error.message}");
+
+//    var content = error.source.contents.data;
+//    var beforeError = content.substring(0, error.offset);
+//    var lineNumber = "\n".allMatches(beforeError).length + 1;
+//    builder.writeln("Error on line $lineNumber of ${error.source.fullName}: "
+//        "${error.message}");
+
+//    var errorLineIndex = beforeError.lastIndexOf("\n") + 1;
+//    var errorEndOfLineIndex = content.indexOf("\n", error.offset);
+//    if (errorEndOfLineIndex == -1) errorEndOfLineIndex = content.length;
+//    var errorLine = content.substring(
+//        errorLineIndex, errorEndOfLineIndex);
+//    var errorColumn = error.offset - errorLineIndex;
+//    var errorLength = error.length;
+//
+//    // Ensure that the error line we display isn't too long.
+//    if (errorLine.length > _MAX_ERROR_LINE_LENGTH) {
+//      var leftLength = errorColumn;
+//      var rightLength = errorLine.length - leftLength;
+//      if (leftLength > _MAX_ERROR_LINE_LENGTH ~/ 2 &&
+//          rightLength > _MAX_ERROR_LINE_LENGTH ~/ 2) {
+//        errorLine = "..." + errorLine.substring(
+//            errorColumn - _MAX_ERROR_LINE_LENGTH ~/ 2 + 3,
+//            errorColumn + _MAX_ERROR_LINE_LENGTH ~/ 2 - 3)
+//            + "...";
+//        errorColumn = _MAX_ERROR_LINE_LENGTH ~/ 2;
+//      } else if (rightLength > _MAX_ERROR_LINE_LENGTH ~/ 2) {
+//        errorLine = errorLine.substring(0, _MAX_ERROR_LINE_LENGTH - 3) + "...";
+//      } else {
+//        assert(leftLength > _MAX_ERROR_LINE_LENGTH ~/ 2);
+//        errorColumn -= errorLine.length - _MAX_ERROR_LINE_LENGTH;
+//        errorLine = "..." + errorLine.substring(
+//            errorLine.length - _MAX_ERROR_LINE_LENGTH + 3, errorLine.length);
+//      }
+//      errorLength = math.min(errorLength, _MAX_ERROR_LINE_LENGTH - errorColumn);
+//    }
+//    builder.writeln(errorLine);
+//
+//    for (var i = 0; i < errorColumn; i++) builder.write(" ");
+//    for (var i = 0; i < errorLength; i++) builder.write("^");
+    builder.writeln();
+
+    return builder.toString();
+  }
+}
+
+/// An error class that collects multiple [AnalyzerError]s that are emitted
+/// during a single analysis.
+class AnalyzerErrorGroup implements Exception {
+  final List<AnalyzerError> _errors;
+  AnalyzerErrorGroup(Iterable<AnalyzerError> errors)
+      : _errors = errors.toList();
+
+  /// Creates an [AnalyzerErrorGroup] from a list of lower-level
+  /// [AnalysisError]s.
+  AnalyzerErrorGroup.fromAnalysisErrors(Iterable<AnalysisError> errors)
+      : this(errors.map((e) => new AnalyzerError(e)));
+
+  /// The errors in this collection.
+  List<AnalyzerError> get errors =>
+      new UnmodifiableListView<AnalyzerError>(_errors);
+
+  String get message => toString();
+  String toString() => errors.join("\n");
+}
diff --git a/analyzer/lib/src/generated/ast.dart b/analyzer/lib/src/generated/ast.dart
new file mode 100644
index 0000000..b7929f4
--- /dev/null
+++ b/analyzer/lib/src/generated/ast.dart
@@ -0,0 +1,20109 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.ast;
+
+import 'dart:collection';
+
+import 'constant.dart';
+import 'element.dart';
+import 'engine.dart' show AnalysisEngine;
+import 'java_core.dart';
+import 'java_engine.dart';
+import 'parser.dart';
+import 'scanner.dart';
+import 'source.dart' show LineInfo, Source;
+import 'utilities_collection.dart' show TokenMap;
+import 'utilities_dart.dart';
+
+/**
+ * Two or more string literals that are implicitly concatenated because of being
+ * adjacent (separated only by whitespace).
+ *
+ * While the grammar only allows adjacent strings when all of the strings are of
+ * the same kind (single line or multi-line), this class doesn't enforce that
+ * restriction.
+ *
+ * > adjacentStrings ::=
+ * >     [StringLiteral] [StringLiteral]+
+ */
+class AdjacentStrings extends StringLiteral {
+  /**
+   * The strings that are implicitly concatenated.
+   */
+  NodeList<StringLiteral> _strings;
+
+  /**
+   * Initialize a newly created list of adjacent strings. To be syntactically
+   * valid, the list of [strings] must contain at least two elements.
+   */
+  AdjacentStrings(List<StringLiteral> strings) {
+    _strings = new NodeList<StringLiteral>(this, strings);
+  }
+
+  @override
+  Token get beginToken => _strings.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..addAll(_strings);
+
+  @override
+  Token get endToken => _strings.endToken;
+
+  /**
+   * Return the strings that are implicitly concatenated.
+   */
+  NodeList<StringLiteral> get strings => _strings;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitAdjacentStrings(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _strings.accept(visitor);
+  }
+
+  @override
+  void _appendStringValue(StringBuffer buffer) {
+    for (StringLiteral stringLiteral in strings) {
+      stringLiteral._appendStringValue(buffer);
+    }
+  }
+}
+
+/**
+ * An AST node that can be annotated with both a documentation comment and a
+ * list of annotations.
+ */
+abstract class AnnotatedNode extends AstNode {
+  /**
+   * The documentation comment associated with this node, or `null` if this node
+   * does not have a documentation comment associated with it.
+   */
+  Comment _comment;
+
+  /**
+   * The annotations associated with this node.
+   */
+  NodeList<Annotation> _metadata;
+
+  /**
+   * Initialize a newly created annotated node. Either or both of the [comment]
+   * and [metadata] can be `null` if the node does not have the corresponding
+   * attribute.
+   */
+  AnnotatedNode(Comment comment, List<Annotation> metadata) {
+    _comment = _becomeParentOf(comment);
+    _metadata = new NodeList<Annotation>(this, metadata);
+  }
+
+  @override
+  Token get beginToken {
+    if (_comment == null) {
+      if (_metadata.isEmpty) {
+        return firstTokenAfterCommentAndMetadata;
+      }
+      return _metadata.beginToken;
+    } else if (_metadata.isEmpty) {
+      return _comment.beginToken;
+    }
+    Token commentToken = _comment.beginToken;
+    Token metadataToken = _metadata.beginToken;
+    if (commentToken.offset < metadataToken.offset) {
+      return commentToken;
+    }
+    return metadataToken;
+  }
+
+  /**
+   * Return the documentation comment associated with this node, or `null` if
+   * this node does not have a documentation comment associated with it.
+   */
+  Comment get documentationComment => _comment;
+
+  /**
+   * Set the documentation comment associated with this node to the given
+   * [comment].
+   */
+  void set documentationComment(Comment comment) {
+    _comment = _becomeParentOf(comment);
+  }
+
+  /**
+   * Return the first token following the comment and metadata.
+   */
+  Token get firstTokenAfterCommentAndMetadata;
+
+  /**
+   * Return the annotations associated with this node.
+   */
+  NodeList<Annotation> get metadata => _metadata;
+
+  /**
+   * Set the metadata associated with this node to the given [metadata].
+   */
+  @deprecated // Directly modify the list returned by "this.metadata"
+  void set metadata(List<Annotation> metadata) {
+    _metadata.clear();
+    _metadata.addAll(metadata);
+  }
+
+  /**
+   * Return a list containing the comment and annotations associated with this
+   * node, sorted in lexical order.
+   */
+  List<AstNode> get sortedCommentAndAnnotations {
+    return <AstNode>[]
+      ..add(_comment)
+      ..addAll(_metadata)
+      ..sort(AstNode.LEXICAL_ORDER);
+  }
+
+  /**
+   * Return a holder of child entities that subclasses can add to.
+   */
+  ChildEntities get _childEntities {
+    ChildEntities result = new ChildEntities();
+    if (_commentIsBeforeAnnotations()) {
+      result
+        ..add(_comment)
+        ..addAll(_metadata);
+    } else {
+      result.addAll(sortedCommentAndAnnotations);
+    }
+    return result;
+  }
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    if (_commentIsBeforeAnnotations()) {
+      _safelyVisitChild(_comment, visitor);
+      _metadata.accept(visitor);
+    } else {
+      for (AstNode child in sortedCommentAndAnnotations) {
+        child.accept(visitor);
+      }
+    }
+  }
+
+  /**
+   * Return `true` if there are no annotations before the comment. Note that a
+   * result of `true` does not imply that there is a comment, nor that there are
+   * annotations associated with this node.
+   */
+  bool _commentIsBeforeAnnotations() {
+    // TODO(brianwilkerson) Convert this to a getter.
+    if (_comment == null || _metadata.isEmpty) {
+      return true;
+    }
+    Annotation firstAnnotation = _metadata[0];
+    return _comment.offset < firstAnnotation.offset;
+  }
+}
+
+/**
+ * An annotation that can be associated with an AST node.
+ *
+ * > metadata ::=
+ * >     annotation*
+ * >
+ * > annotation ::=
+ * >     '@' [Identifier] ('.' [SimpleIdentifier])? [ArgumentList]?
+ */
+class Annotation extends AstNode {
+  /**
+   * The at sign that introduced the annotation.
+   */
+  Token atSign;
+
+  /**
+   * The name of the class defining the constructor that is being invoked or the
+   * name of the field that is being referenced.
+   */
+  Identifier _name;
+
+  /**
+   * The period before the constructor name, or `null` if this annotation is not
+   * the invocation of a named constructor.
+   */
+  Token period;
+
+  /**
+   * The name of the constructor being invoked, or `null` if this annotation is
+   * not the invocation of a named constructor.
+   */
+  SimpleIdentifier _constructorName;
+
+  /**
+   * The arguments to the constructor being invoked, or `null` if this
+   * annotation is not the invocation of a constructor.
+   */
+  ArgumentList _arguments;
+
+  /**
+   * The element associated with this annotation, or `null` if the AST structure
+   * has not been resolved or if this annotation could not be resolved.
+   */
+  Element _element;
+
+  /**
+   * The element annotation representing this annotation in the element model.
+   */
+  ElementAnnotation elementAnnotation;
+
+  /**
+   * Initialize a newly created annotation. Both the [period] and the
+   * [constructorName] can be `null` if the annotation is not referencing a
+   * named constructor. The [arguments] can be `null` if the annotation is not
+   * referencing a constructor.
+   */
+  Annotation(this.atSign, Identifier name, this.period,
+      SimpleIdentifier constructorName, ArgumentList arguments) {
+    _name = _becomeParentOf(name);
+    _constructorName = _becomeParentOf(constructorName);
+    _arguments = _becomeParentOf(arguments);
+  }
+
+  /**
+   * Return the arguments to the constructor being invoked, or `null` if this
+   * annotation is not the invocation of a constructor.
+   */
+  ArgumentList get arguments => _arguments;
+
+  /**
+   * Set the arguments to the constructor being invoked to the given arguments.
+   */
+  void set arguments(ArgumentList arguments) {
+    _arguments = _becomeParentOf(arguments);
+  }
+
+  @override
+  Token get beginToken => atSign;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(atSign)
+    ..add(_name)
+    ..add(period)
+    ..add(_constructorName)
+    ..add(_arguments);
+
+  /**
+   * Return the name of the constructor being invoked, or `null` if this
+   * annotation is not the invocation of a named constructor.
+   */
+  SimpleIdentifier get constructorName => _constructorName;
+
+  /**
+   * Set the name of the constructor being invoked to the given [name].
+   */
+  void set constructorName(SimpleIdentifier name) {
+    _constructorName = _becomeParentOf(name);
+  }
+
+  /**
+   * Return the element associated with this annotation, or `null` if the AST
+   * structure has not been resolved or if this annotation could not be
+   * resolved.
+   */
+  Element get element {
+    if (_element != null) {
+      return _element;
+    } else if (_name != null) {
+      return _name.staticElement;
+    }
+    return null;
+  }
+
+  /**
+   * Set the element associated with this annotation to the given [element].
+   */
+  void set element(Element element) {
+    _element = element;
+  }
+
+  @override
+  Token get endToken {
+    if (_arguments != null) {
+      return _arguments.endToken;
+    } else if (_constructorName != null) {
+      return _constructorName.endToken;
+    }
+    return _name.endToken;
+  }
+
+  /**
+   * Return the name of the class defining the constructor that is being invoked
+   * or the name of the field that is being referenced.
+   */
+  Identifier get name => _name;
+
+  /**
+   * Set the name of the class defining the constructor that is being invoked or
+   * the name of the field that is being referenced to the given [name].
+   */
+  void set name(Identifier name) {
+    _name = _becomeParentOf(name);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitAnnotation(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_name, visitor);
+    _safelyVisitChild(_constructorName, visitor);
+    _safelyVisitChild(_arguments, visitor);
+  }
+}
+
+/**
+ * A list of arguments in the invocation of an executable element (that is, a
+ * function, method, or constructor).
+ *
+ * > argumentList ::=
+ * >     '(' arguments? ')'
+ * >
+ * > arguments ::=
+ * >     [NamedExpression] (',' [NamedExpression])*
+ * >   | [Expression] (',' [Expression])* (',' [NamedExpression])*
+ */
+class ArgumentList extends AstNode {
+  /**
+   * The left parenthesis.
+   */
+  Token leftParenthesis;
+
+  /**
+   * The expressions producing the values of the arguments.
+   */
+  NodeList<Expression> _arguments;
+
+  /**
+   * The right parenthesis.
+   */
+  Token rightParenthesis;
+
+  /**
+   * A list containing the elements representing the parameters corresponding to
+   * each of the arguments in this list, or `null` if the AST has not been
+   * resolved or if the function or method being invoked could not be determined
+   * based on static type information. The list must be the same length as the
+   * number of arguments, but can contain `null` entries if a given argument
+   * does not correspond to a formal parameter.
+   */
+  List<ParameterElement> _correspondingStaticParameters;
+
+  /**
+   * A list containing the elements representing the parameters corresponding to
+   * each of the arguments in this list, or `null` if the AST has not been
+   * resolved or if the function or method being invoked could not be determined
+   * based on propagated type information. The list must be the same length as
+   * the number of arguments, but can contain `null` entries if a given argument
+   * does not correspond to a formal parameter.
+   */
+  List<ParameterElement> _correspondingPropagatedParameters;
+
+  /**
+   * Initialize a newly created list of arguments. The list of [arguments] can
+   * be `null` if there are no arguments.
+   */
+  ArgumentList(
+      this.leftParenthesis, List<Expression> arguments, this.rightParenthesis) {
+    _arguments = new NodeList<Expression>(this, arguments);
+  }
+
+  /**
+   * Return the expressions producing the values of the arguments. Although the
+   * language requires that positional arguments appear before named arguments,
+   * this class allows them to be intermixed.
+   */
+  NodeList<Expression> get arguments => _arguments;
+
+  @override
+  Token get beginToken => leftParenthesis;
+
+  /**
+   * TODO(paulberry): Add commas.
+   */
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(leftParenthesis)
+    ..addAll(_arguments)
+    ..add(rightParenthesis);
+
+  /**
+   * Set the parameter elements corresponding to each of the arguments in this
+   * list to the given list of [parameters]. The list of parameters must be the
+   * same length as the number of arguments, but can contain `null` entries if a
+   * given argument does not correspond to a formal parameter.
+   */
+  void set correspondingPropagatedParameters(
+      List<ParameterElement> parameters) {
+    if (parameters.length != _arguments.length) {
+      throw new IllegalArgumentException(
+          "Expected ${_arguments.length} parameters, not ${parameters.length}");
+    }
+    _correspondingPropagatedParameters = parameters;
+  }
+
+  /**
+   * Set the parameter elements corresponding to each of the arguments in this
+   * list to the given list of parameters. The list of parameters must be the
+   * same length as the number of arguments, but can contain `null` entries if a
+   * given argument does not correspond to a formal parameter.
+   */
+  void set correspondingStaticParameters(List<ParameterElement> parameters) {
+    if (parameters.length != _arguments.length) {
+      throw new IllegalArgumentException(
+          "Expected ${_arguments.length} parameters, not ${parameters.length}");
+    }
+    _correspondingStaticParameters = parameters;
+  }
+
+  @override
+  Token get endToken => rightParenthesis;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitArgumentList(this);
+
+  /**
+   * If
+   * * the given [expression] is a child of this list,
+   * * the AST structure has been resolved,
+   * * the function being invoked is known based on propagated type information,
+   *   and
+   * * the expression corresponds to one of the parameters of the function being
+   *   invoked,
+   * then return the parameter element representing the parameter to which the
+   * value of the given expression will be bound. Otherwise, return `null`.
+   */
+  @deprecated // Use "expression.propagatedParameterElement"
+  ParameterElement getPropagatedParameterElementFor(Expression expression) {
+    return _getPropagatedParameterElementFor(expression);
+  }
+
+  /**
+   * If
+   * * the given [expression] is a child of this list,
+   * * the AST structure has been resolved,
+   * * the function being invoked is known based on static type information, and
+   * * the expression corresponds to one of the parameters of the function being
+   *   invoked,
+   * then return the parameter element representing the parameter to which the
+   * value of the given expression will be bound. Otherwise, return `null`.
+   */
+  @deprecated // Use "expression.staticParameterElement"
+  ParameterElement getStaticParameterElementFor(Expression expression) {
+    return _getStaticParameterElementFor(expression);
+  }
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _arguments.accept(visitor);
+  }
+
+  /**
+   * If
+   * * the given [expression] is a child of this list,
+   * * the AST structure has been resolved,
+   * * the function being invoked is known based on propagated type information,
+   *   and
+   * * the expression corresponds to one of the parameters of the function being
+   *   invoked,
+   * then return the parameter element representing the parameter to which the
+   * value of the given expression will be bound. Otherwise, return `null`.
+   */
+  ParameterElement _getPropagatedParameterElementFor(Expression expression) {
+    if (_correspondingPropagatedParameters == null ||
+        _correspondingPropagatedParameters.length != _arguments.length) {
+      // Either the AST structure has not been resolved, the invocation of which
+      // this list is a part could not be resolved, or the argument list was
+      // modified after the parameters were set.
+      return null;
+    }
+    int index = _arguments.indexOf(expression);
+    if (index < 0) {
+      // The expression isn't a child of this node.
+      return null;
+    }
+    return _correspondingPropagatedParameters[index];
+  }
+
+  /**
+   * If
+   * * the given [expression] is a child of this list,
+   * * the AST structure has been resolved,
+   * * the function being invoked is known based on static type information, and
+   * * the expression corresponds to one of the parameters of the function being
+   *   invoked,
+   * then return the parameter element representing the parameter to which the
+   * value of the given expression will be bound. Otherwise, return `null`.
+   */
+  ParameterElement _getStaticParameterElementFor(Expression expression) {
+    if (_correspondingStaticParameters == null ||
+        _correspondingStaticParameters.length != _arguments.length) {
+      // Either the AST structure has not been resolved, the invocation of which
+      // this list is a part could not be resolved, or the argument list was
+      // modified after the parameters were set.
+      return null;
+    }
+    int index = _arguments.indexOf(expression);
+    if (index < 0) {
+      // The expression isn't a child of this node.
+      return null;
+    }
+    return _correspondingStaticParameters[index];
+  }
+}
+
+/**
+ * An as expression.
+ *
+ * > asExpression ::=
+ * >     [Expression] 'as' [TypeName]
+ */
+class AsExpression extends Expression {
+  /**
+   * The expression used to compute the value being cast.
+   */
+  Expression _expression;
+
+  /**
+   * The 'as' operator.
+   */
+  Token asOperator;
+
+  /**
+   * The name of the type being cast to.
+   */
+  TypeName _type;
+
+  /**
+   * Initialize a newly created as expression.
+   */
+  AsExpression(Expression expression, this.asOperator, TypeName type) {
+    _expression = _becomeParentOf(expression);
+    _type = _becomeParentOf(type);
+  }
+
+  @override
+  Token get beginToken => _expression.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_expression)
+    ..add(asOperator)
+    ..add(_type);
+
+  @override
+  Token get endToken => _type.endToken;
+
+  /**
+   * Return the expression used to compute the value being cast.
+   */
+  Expression get expression => _expression;
+
+  /**
+   * Set the expression used to compute the value being cast to the given
+   * [expression].
+   */
+  void set expression(Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  int get precedence => 7;
+
+  /**
+   * Return the name of the type being cast to.
+   */
+  TypeName get type => _type;
+
+  /**
+   * Set the name of the type being cast to to the given [name].
+   */
+  void set type(TypeName name) {
+    _type = _becomeParentOf(name);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitAsExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_expression, visitor);
+    _safelyVisitChild(_type, visitor);
+  }
+}
+
+/**
+ * An assert statement.
+ *
+ * > assertStatement ::=
+ * >     'assert' '(' [Expression] ')' ';'
+ */
+class AssertStatement extends Statement {
+  /**
+   * The token representing the 'assert' keyword.
+   */
+  Token assertKeyword;
+
+  /**
+   * The left parenthesis.
+   */
+  Token leftParenthesis;
+
+  /**
+   * The condition that is being asserted to be `true`.
+   */
+  Expression _condition;
+
+  /**
+   * The right parenthesis.
+   */
+  Token rightParenthesis;
+
+  /**
+   * The semicolon terminating the statement.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created assert statement.
+   */
+  AssertStatement(this.assertKeyword, this.leftParenthesis,
+      Expression condition, this.rightParenthesis, this.semicolon) {
+    _condition = _becomeParentOf(condition);
+  }
+
+  @override
+  Token get beginToken => assertKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(assertKeyword)
+    ..add(leftParenthesis)
+    ..add(_condition)
+    ..add(rightParenthesis)
+    ..add(semicolon);
+
+  /**
+   * Return the condition that is being asserted to be `true`.
+   */
+  Expression get condition => _condition;
+
+  /**
+   * Set the condition that is being asserted to be `true` to the given
+   * [expression].
+   */
+  void set condition(Expression condition) {
+    _condition = _becomeParentOf(condition);
+  }
+
+  @override
+  Token get endToken => semicolon;
+
+  /**
+   * Return the token representing the 'assert' keyword.
+   */
+  @deprecated // Use "this.assertKeyword"
+  Token get keyword => assertKeyword;
+
+  /**
+   * Set the token representing the 'assert' keyword to the given [token].
+   */
+  @deprecated // Use "this.assertKeyword"
+  set keyword(Token token) {
+    assertKeyword = token;
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitAssertStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_condition, visitor);
+  }
+}
+
+/**
+ * An assignment expression.
+ *
+ * > assignmentExpression ::=
+ * >     [Expression] operator [Expression]
+ */
+class AssignmentExpression extends Expression {
+  /**
+   * The expression used to compute the left hand side.
+   */
+  Expression _leftHandSide;
+
+  /**
+   * The assignment operator being applied.
+   */
+  Token operator;
+
+  /**
+   * The expression used to compute the right hand side.
+   */
+  Expression _rightHandSide;
+
+  /**
+   * The element associated with the operator based on the static type of the
+   * left-hand-side, or `null` if the AST structure has not been resolved, if
+   * the operator is not a compound operator, or if the operator could not be
+   * resolved.
+   */
+  MethodElement staticElement;
+
+  /**
+   * The element associated with the operator based on the propagated type of
+   * the left-hand-side, or `null` if the AST structure has not been resolved,
+   * if the operator is not a compound operator, or if the operator could not be
+   * resolved.
+   */
+  MethodElement propagatedElement;
+
+  /**
+   * Initialize a newly created assignment expression.
+   */
+  AssignmentExpression(
+      Expression leftHandSide, this.operator, Expression rightHandSide) {
+    if (leftHandSide == null || rightHandSide == null) {
+      String message;
+      if (leftHandSide == null) {
+        if (rightHandSide == null) {
+          message = "Both the left-hand and right-hand sides are null";
+        } else {
+          message = "The left-hand size is null";
+        }
+      } else {
+        message = "The right-hand size is null";
+      }
+      AnalysisEngine.instance.logger.logError(
+          message, new CaughtException(new AnalysisException(message), null));
+    }
+    _leftHandSide = _becomeParentOf(leftHandSide);
+    _rightHandSide = _becomeParentOf(rightHandSide);
+  }
+
+  @override
+  Token get beginToken => _leftHandSide.beginToken;
+
+  /**
+   * Return the best element available for this operator. If resolution was able
+   * to find a better element based on type propagation, that element will be
+   * returned. Otherwise, the element found using the result of static analysis
+   * will be returned. If resolution has not been performed, then `null` will be
+   * returned.
+   */
+  MethodElement get bestElement {
+    MethodElement element = propagatedElement;
+    if (element == null) {
+      element = staticElement;
+    }
+    return element;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_leftHandSide)
+    ..add(operator)
+    ..add(_rightHandSide);
+
+  @override
+  Token get endToken => _rightHandSide.endToken;
+
+  /**
+   * Set the expression used to compute the left hand side to the given
+   * [expression].
+   */
+  Expression get leftHandSide => _leftHandSide;
+
+  /**
+   * Return the expression used to compute the left hand side.
+   */
+  void set leftHandSide(Expression expression) {
+    _leftHandSide = _becomeParentOf(expression);
+  }
+
+  @override
+  int get precedence => 1;
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on propagated type information, then return the parameter
+   * element representing the parameter to which the value of the right operand
+   * will be bound. Otherwise, return `null`.
+   */
+  @deprecated // Use "expression.propagatedParameterElement"
+  ParameterElement get propagatedParameterElementForRightHandSide {
+    return _propagatedParameterElementForRightHandSide;
+  }
+
+  /**
+   * Return the expression used to compute the right hand side.
+   */
+  Expression get rightHandSide => _rightHandSide;
+
+  /**
+   * Set the expression used to compute the left hand side to the given
+   * [expression].
+   */
+  void set rightHandSide(Expression expression) {
+    _rightHandSide = _becomeParentOf(expression);
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on static type information, then return the parameter element
+   * representing the parameter to which the value of the right operand will be
+   * bound. Otherwise, return `null`.
+   */
+  @deprecated // Use "expression.staticParameterElement"
+  ParameterElement get staticParameterElementForRightHandSide {
+    return _staticParameterElementForRightHandSide;
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on propagated type information, then return the parameter
+   * element representing the parameter to which the value of the right operand
+   * will be bound. Otherwise, return `null`.
+   */
+  ParameterElement get _propagatedParameterElementForRightHandSide {
+    ExecutableElement executableElement = null;
+    if (propagatedElement != null) {
+      executableElement = propagatedElement;
+    } else {
+      if (_leftHandSide is Identifier) {
+        Identifier identifier = _leftHandSide as Identifier;
+        Element leftElement = identifier.propagatedElement;
+        if (leftElement is ExecutableElement) {
+          executableElement = leftElement;
+        }
+      }
+      if (_leftHandSide is PropertyAccess) {
+        SimpleIdentifier identifier =
+            (_leftHandSide as PropertyAccess).propertyName;
+        Element leftElement = identifier.propagatedElement;
+        if (leftElement is ExecutableElement) {
+          executableElement = leftElement;
+        }
+      }
+    }
+    if (executableElement == null) {
+      return null;
+    }
+    List<ParameterElement> parameters = executableElement.parameters;
+    if (parameters.length < 1) {
+      return null;
+    }
+    return parameters[0];
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on static type information, then return the parameter element
+   * representing the parameter to which the value of the right operand will be
+   * bound. Otherwise, return `null`.
+   */
+  ParameterElement get _staticParameterElementForRightHandSide {
+    ExecutableElement executableElement = null;
+    if (staticElement != null) {
+      executableElement = staticElement;
+    } else {
+      if (_leftHandSide is Identifier) {
+        Element leftElement = (_leftHandSide as Identifier).staticElement;
+        if (leftElement is ExecutableElement) {
+          executableElement = leftElement;
+        }
+      }
+      if (_leftHandSide is PropertyAccess) {
+        Element leftElement =
+            (_leftHandSide as PropertyAccess).propertyName.staticElement;
+        if (leftElement is ExecutableElement) {
+          executableElement = leftElement;
+        }
+      }
+    }
+    if (executableElement == null) {
+      return null;
+    }
+    List<ParameterElement> parameters = executableElement.parameters;
+    if (parameters.length < 1) {
+      return null;
+    }
+    return parameters[0];
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitAssignmentExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_leftHandSide, visitor);
+    _safelyVisitChild(_rightHandSide, visitor);
+  }
+}
+
+/**
+ * An AST visitor that will clone any AST structure that it visits. The cloner
+ * will only clone the structure, it will not preserve any resolution results or
+ * properties associated with the nodes.
+ */
+class AstCloner implements AstVisitor<AstNode> {
+  /**
+   * A flag indicating whether tokens should be cloned while cloning an AST
+   * structure.
+   */
+  final bool cloneTokens;
+
+  /**
+   * Initialize a newly created AST cloner to optionally clone tokens while
+   * cloning AST nodes if [cloneTokens] is `true`.
+   */
+  AstCloner(
+      [this.cloneTokens = false]); // TODO(brianwilkerson) Change this to be a named parameter.
+
+  /**
+   * Return a clone of the given [node].
+   */
+  AstNode cloneNode(AstNode node) {
+    if (node == null) {
+      return null;
+    }
+    return node.accept(this) as AstNode;
+  }
+
+  /**
+   * Return a list containing cloned versions of the nodes in the given list of
+   * [nodes].
+   */
+  List<AstNode> cloneNodeList(NodeList nodes) {
+    int count = nodes.length;
+    List clonedNodes = new List();
+    for (int i = 0; i < count; i++) {
+      clonedNodes.add((nodes[i]).accept(this) as AstNode);
+    }
+    return clonedNodes;
+  }
+
+  /**
+   * Clone the given [token] if tokens are supposed to be cloned.
+   */
+  Token cloneToken(Token token) {
+    if (cloneTokens) {
+      return (token == null ? null : token.copy());
+    } else {
+      return token;
+    }
+  }
+
+  /**
+   * Clone the given [tokens] if tokens are supposed to be cloned.
+   */
+  List<Token> cloneTokenList(List<Token> tokens) {
+    if (cloneTokens) {
+      return tokens.map((Token token) => token.copy()).toList();
+    }
+    return tokens;
+  }
+
+  @override
+  AdjacentStrings visitAdjacentStrings(AdjacentStrings node) =>
+      new AdjacentStrings(cloneNodeList(node.strings));
+
+  @override
+  Annotation visitAnnotation(Annotation node) => new Annotation(
+      cloneToken(node.atSign), cloneNode(node.name), cloneToken(node.period),
+      cloneNode(node.constructorName), cloneNode(node.arguments));
+
+  @override
+  ArgumentList visitArgumentList(ArgumentList node) => new ArgumentList(
+      cloneToken(node.leftParenthesis), cloneNodeList(node.arguments),
+      cloneToken(node.rightParenthesis));
+
+  @override
+  AsExpression visitAsExpression(AsExpression node) => new AsExpression(
+      cloneNode(node.expression), cloneToken(node.asOperator),
+      cloneNode(node.type));
+
+  @override
+  AstNode visitAssertStatement(AssertStatement node) => new AssertStatement(
+      cloneToken(node.assertKeyword), cloneToken(node.leftParenthesis),
+      cloneNode(node.condition), cloneToken(node.rightParenthesis),
+      cloneToken(node.semicolon));
+
+  @override
+  AssignmentExpression visitAssignmentExpression(AssignmentExpression node) =>
+      new AssignmentExpression(cloneNode(node.leftHandSide),
+          cloneToken(node.operator), cloneNode(node.rightHandSide));
+
+  @override
+  AwaitExpression visitAwaitExpression(AwaitExpression node) =>
+      new AwaitExpression(
+          cloneToken(node.awaitKeyword), cloneNode(node.expression));
+
+  @override
+  BinaryExpression visitBinaryExpression(BinaryExpression node) =>
+      new BinaryExpression(cloneNode(node.leftOperand),
+          cloneToken(node.operator), cloneNode(node.rightOperand));
+
+  @override
+  Block visitBlock(Block node) => new Block(cloneToken(node.leftBracket),
+      cloneNodeList(node.statements), cloneToken(node.rightBracket));
+
+  @override
+  BlockFunctionBody visitBlockFunctionBody(
+      BlockFunctionBody node) => new BlockFunctionBody(
+      cloneToken(node.keyword), cloneToken(node.star), cloneNode(node.block));
+
+  @override
+  BooleanLiteral visitBooleanLiteral(BooleanLiteral node) =>
+      new BooleanLiteral(cloneToken(node.literal), node.value);
+
+  @override
+  BreakStatement visitBreakStatement(BreakStatement node) => new BreakStatement(
+      cloneToken(node.breakKeyword), cloneNode(node.label),
+      cloneToken(node.semicolon));
+
+  @override
+  CascadeExpression visitCascadeExpression(CascadeExpression node) =>
+      new CascadeExpression(
+          cloneNode(node.target), cloneNodeList(node.cascadeSections));
+
+  @override
+  CatchClause visitCatchClause(CatchClause node) => new CatchClause(
+      cloneToken(node.onKeyword), cloneNode(node.exceptionType),
+      cloneToken(node.catchKeyword), cloneToken(node.leftParenthesis),
+      cloneNode(node.exceptionParameter), cloneToken(node.comma),
+      cloneNode(node.stackTraceParameter), cloneToken(node.rightParenthesis),
+      cloneNode(node.body));
+
+  @override
+  ClassDeclaration visitClassDeclaration(ClassDeclaration node) {
+    ClassDeclaration copy = new ClassDeclaration(
+        cloneNode(node.documentationComment), cloneNodeList(node.metadata),
+        cloneToken(node.abstractKeyword), cloneToken(node.classKeyword),
+        cloneNode(node.name), cloneNode(node.typeParameters),
+        cloneNode(node.extendsClause), cloneNode(node.withClause),
+        cloneNode(node.implementsClause), cloneToken(node.leftBracket),
+        cloneNodeList(node.members), cloneToken(node.rightBracket));
+    copy.nativeClause = cloneNode(node.nativeClause);
+    return copy;
+  }
+
+  @override
+  ClassTypeAlias visitClassTypeAlias(ClassTypeAlias node) => new ClassTypeAlias(
+      cloneNode(node.documentationComment), cloneNodeList(node.metadata),
+      cloneToken(node.typedefKeyword), cloneNode(node.name),
+      cloneNode(node.typeParameters), cloneToken(node.equals),
+      cloneToken(node.abstractKeyword), cloneNode(node.superclass),
+      cloneNode(node.withClause), cloneNode(node.implementsClause),
+      cloneToken(node.semicolon));
+
+  @override
+  Comment visitComment(Comment node) {
+    if (node.isDocumentation) {
+      return Comment.createDocumentationCommentWithReferences(
+          cloneTokenList(node.tokens), cloneNodeList(node.references));
+    } else if (node.isBlock) {
+      return Comment.createBlockComment(cloneTokenList(node.tokens));
+    }
+    return Comment.createEndOfLineComment(cloneTokenList(node.tokens));
+  }
+
+  @override
+  CommentReference visitCommentReference(CommentReference node) =>
+      new CommentReference(
+          cloneToken(node.newKeyword), cloneNode(node.identifier));
+
+  @override
+  CompilationUnit visitCompilationUnit(CompilationUnit node) {
+    CompilationUnit clone = new CompilationUnit(cloneToken(node.beginToken),
+        cloneNode(node.scriptTag), cloneNodeList(node.directives),
+        cloneNodeList(node.declarations), cloneToken(node.endToken));
+    clone.lineInfo = node.lineInfo;
+    return clone;
+  }
+
+  @override
+  ConditionalExpression visitConditionalExpression(
+      ConditionalExpression node) => new ConditionalExpression(
+      cloneNode(node.condition), cloneToken(node.question),
+      cloneNode(node.thenExpression), cloneToken(node.colon),
+      cloneNode(node.elseExpression));
+
+  @override
+  ConstructorDeclaration visitConstructorDeclaration(
+      ConstructorDeclaration node) => new ConstructorDeclaration(
+      cloneNode(node.documentationComment), cloneNodeList(node.metadata),
+      cloneToken(node.externalKeyword), cloneToken(node.constKeyword),
+      cloneToken(node.factoryKeyword), cloneNode(node.returnType),
+      cloneToken(node.period), cloneNode(node.name), cloneNode(node.parameters),
+      cloneToken(node.separator), cloneNodeList(node.initializers),
+      cloneNode(node.redirectedConstructor), cloneNode(node.body));
+
+  @override
+  ConstructorFieldInitializer visitConstructorFieldInitializer(
+      ConstructorFieldInitializer node) => new ConstructorFieldInitializer(
+      cloneToken(node.thisKeyword), cloneToken(node.period),
+      cloneNode(node.fieldName), cloneToken(node.equals),
+      cloneNode(node.expression));
+
+  @override
+  ConstructorName visitConstructorName(ConstructorName node) =>
+      new ConstructorName(
+          cloneNode(node.type), cloneToken(node.period), cloneNode(node.name));
+
+  @override
+  ContinueStatement visitContinueStatement(ContinueStatement node) =>
+      new ContinueStatement(cloneToken(node.continueKeyword),
+          cloneNode(node.label), cloneToken(node.semicolon));
+
+  @override
+  DeclaredIdentifier visitDeclaredIdentifier(DeclaredIdentifier node) =>
+      new DeclaredIdentifier(cloneNode(node.documentationComment),
+          cloneNodeList(node.metadata), cloneToken(node.keyword),
+          cloneNode(node.type), cloneNode(node.identifier));
+
+  @override
+  DefaultFormalParameter visitDefaultFormalParameter(
+      DefaultFormalParameter node) => new DefaultFormalParameter(
+      cloneNode(node.parameter), node.kind, cloneToken(node.separator),
+      cloneNode(node.defaultValue));
+
+  @override
+  DoStatement visitDoStatement(DoStatement node) => new DoStatement(
+      cloneToken(node.doKeyword), cloneNode(node.body),
+      cloneToken(node.whileKeyword), cloneToken(node.leftParenthesis),
+      cloneNode(node.condition), cloneToken(node.rightParenthesis),
+      cloneToken(node.semicolon));
+
+  @override
+  DoubleLiteral visitDoubleLiteral(DoubleLiteral node) =>
+      new DoubleLiteral(cloneToken(node.literal), node.value);
+
+  @override
+  EmptyFunctionBody visitEmptyFunctionBody(EmptyFunctionBody node) =>
+      new EmptyFunctionBody(cloneToken(node.semicolon));
+
+  @override
+  EmptyStatement visitEmptyStatement(EmptyStatement node) =>
+      new EmptyStatement(cloneToken(node.semicolon));
+
+  @override
+  AstNode visitEnumConstantDeclaration(EnumConstantDeclaration node) =>
+      new EnumConstantDeclaration(cloneNode(node.documentationComment),
+          cloneNodeList(node.metadata), cloneNode(node.name));
+
+  @override
+  EnumDeclaration visitEnumDeclaration(EnumDeclaration node) =>
+      new EnumDeclaration(cloneNode(node.documentationComment),
+          cloneNodeList(node.metadata), cloneToken(node.enumKeyword),
+          cloneNode(node.name), cloneToken(node.leftBracket),
+          cloneNodeList(node.constants), cloneToken(node.rightBracket));
+
+  @override
+  ExportDirective visitExportDirective(ExportDirective node) {
+    ExportDirective directive = new ExportDirective(
+        cloneNode(node.documentationComment), cloneNodeList(node.metadata),
+        cloneToken(node.keyword), cloneNode(node.uri),
+        cloneNodeList(node.combinators), cloneToken(node.semicolon));
+    directive.source = node.source;
+    directive.uriContent = node.uriContent;
+    return directive;
+  }
+
+  @override
+  ExpressionFunctionBody visitExpressionFunctionBody(
+      ExpressionFunctionBody node) => new ExpressionFunctionBody(
+      cloneToken(node.keyword), cloneToken(node.functionDefinition),
+      cloneNode(node.expression), cloneToken(node.semicolon));
+
+  @override
+  ExpressionStatement visitExpressionStatement(ExpressionStatement node) =>
+      new ExpressionStatement(
+          cloneNode(node.expression), cloneToken(node.semicolon));
+
+  @override
+  ExtendsClause visitExtendsClause(ExtendsClause node) => new ExtendsClause(
+      cloneToken(node.extendsKeyword), cloneNode(node.superclass));
+
+  @override
+  FieldDeclaration visitFieldDeclaration(FieldDeclaration node) =>
+      new FieldDeclaration(cloneNode(node.documentationComment),
+          cloneNodeList(node.metadata), cloneToken(node.staticKeyword),
+          cloneNode(node.fields), cloneToken(node.semicolon));
+
+  @override
+  FieldFormalParameter visitFieldFormalParameter(FieldFormalParameter node) =>
+      new FieldFormalParameter(cloneNode(node.documentationComment),
+          cloneNodeList(node.metadata), cloneToken(node.keyword),
+          cloneNode(node.type), cloneToken(node.thisKeyword),
+          cloneToken(node.period), cloneNode(node.identifier),
+          cloneNode(node.parameters));
+
+  @override
+  ForEachStatement visitForEachStatement(ForEachStatement node) {
+    DeclaredIdentifier loopVariable = node.loopVariable;
+    if (loopVariable == null) {
+      return new ForEachStatement.con2(cloneToken(node.awaitKeyword),
+          cloneToken(node.forKeyword), cloneToken(node.leftParenthesis),
+          cloneNode(node.identifier), cloneToken(node.inKeyword),
+          cloneNode(node.iterable), cloneToken(node.rightParenthesis),
+          cloneNode(node.body));
+    }
+    return new ForEachStatement.con1(cloneToken(node.awaitKeyword),
+        cloneToken(node.forKeyword), cloneToken(node.leftParenthesis),
+        cloneNode(loopVariable), cloneToken(node.inKeyword),
+        cloneNode(node.iterable), cloneToken(node.rightParenthesis),
+        cloneNode(node.body));
+  }
+
+  @override
+  FormalParameterList visitFormalParameterList(FormalParameterList node) =>
+      new FormalParameterList(cloneToken(node.leftParenthesis),
+          cloneNodeList(node.parameters), cloneToken(node.leftDelimiter),
+          cloneToken(node.rightDelimiter), cloneToken(node.rightParenthesis));
+
+  @override
+  ForStatement visitForStatement(ForStatement node) => new ForStatement(
+      cloneToken(node.forKeyword), cloneToken(node.leftParenthesis),
+      cloneNode(node.variables), cloneNode(node.initialization),
+      cloneToken(node.leftSeparator), cloneNode(node.condition),
+      cloneToken(node.rightSeparator), cloneNodeList(node.updaters),
+      cloneToken(node.rightParenthesis), cloneNode(node.body));
+
+  @override
+  FunctionDeclaration visitFunctionDeclaration(FunctionDeclaration node) =>
+      new FunctionDeclaration(cloneNode(node.documentationComment),
+          cloneNodeList(node.metadata), cloneToken(node.externalKeyword),
+          cloneNode(node.returnType), cloneToken(node.propertyKeyword),
+          cloneNode(node.name), cloneNode(node.functionExpression));
+
+  @override
+  FunctionDeclarationStatement visitFunctionDeclarationStatement(
+          FunctionDeclarationStatement node) =>
+      new FunctionDeclarationStatement(cloneNode(node.functionDeclaration));
+
+  @override
+  FunctionExpression visitFunctionExpression(FunctionExpression node) =>
+      new FunctionExpression(cloneNode(node.parameters), cloneNode(node.body));
+
+  @override
+  FunctionExpressionInvocation visitFunctionExpressionInvocation(
+      FunctionExpressionInvocation node) => new FunctionExpressionInvocation(
+      cloneNode(node.function), cloneNode(node.argumentList));
+
+  @override
+  FunctionTypeAlias visitFunctionTypeAlias(FunctionTypeAlias node) =>
+      new FunctionTypeAlias(cloneNode(node.documentationComment),
+          cloneNodeList(node.metadata), cloneToken(node.typedefKeyword),
+          cloneNode(node.returnType), cloneNode(node.name),
+          cloneNode(node.typeParameters), cloneNode(node.parameters),
+          cloneToken(node.semicolon));
+
+  @override
+  FunctionTypedFormalParameter visitFunctionTypedFormalParameter(
+      FunctionTypedFormalParameter node) => new FunctionTypedFormalParameter(
+      cloneNode(node.documentationComment), cloneNodeList(node.metadata),
+      cloneNode(node.returnType), cloneNode(node.identifier),
+      cloneNode(node.parameters));
+
+  @override
+  HideCombinator visitHideCombinator(HideCombinator node) => new HideCombinator(
+      cloneToken(node.keyword), cloneNodeList(node.hiddenNames));
+
+  @override
+  IfStatement visitIfStatement(IfStatement node) => new IfStatement(
+      cloneToken(node.ifKeyword), cloneToken(node.leftParenthesis),
+      cloneNode(node.condition), cloneToken(node.rightParenthesis),
+      cloneNode(node.thenStatement), cloneToken(node.elseKeyword),
+      cloneNode(node.elseStatement));
+
+  @override
+  ImplementsClause visitImplementsClause(ImplementsClause node) =>
+      new ImplementsClause(
+          cloneToken(node.implementsKeyword), cloneNodeList(node.interfaces));
+
+  @override
+  ImportDirective visitImportDirective(ImportDirective node) {
+    ImportDirective directive = new ImportDirective(
+        cloneNode(node.documentationComment), cloneNodeList(node.metadata),
+        cloneToken(node.keyword), cloneNode(node.uri),
+        cloneToken(node.deferredKeyword), cloneToken(node.asKeyword),
+        cloneNode(node.prefix), cloneNodeList(node.combinators),
+        cloneToken(node.semicolon));
+    directive.source = node.source;
+    directive.uriContent = node.uriContent;
+    return directive;
+  }
+
+  @override
+  IndexExpression visitIndexExpression(IndexExpression node) {
+    Token period = node.period;
+    if (period == null) {
+      return new IndexExpression.forTarget(cloneNode(node.target),
+          cloneToken(node.leftBracket), cloneNode(node.index),
+          cloneToken(node.rightBracket));
+    } else {
+      return new IndexExpression.forCascade(cloneToken(period),
+          cloneToken(node.leftBracket), cloneNode(node.index),
+          cloneToken(node.rightBracket));
+    }
+  }
+
+  @override
+  InstanceCreationExpression visitInstanceCreationExpression(
+      InstanceCreationExpression node) => new InstanceCreationExpression(
+      cloneToken(node.keyword), cloneNode(node.constructorName),
+      cloneNode(node.argumentList));
+
+  @override
+  IntegerLiteral visitIntegerLiteral(IntegerLiteral node) =>
+      new IntegerLiteral(cloneToken(node.literal), node.value);
+
+  @override
+  InterpolationExpression visitInterpolationExpression(
+      InterpolationExpression node) => new InterpolationExpression(
+      cloneToken(node.leftBracket), cloneNode(node.expression),
+      cloneToken(node.rightBracket));
+
+  @override
+  InterpolationString visitInterpolationString(InterpolationString node) =>
+      new InterpolationString(cloneToken(node.contents), node.value);
+
+  @override
+  IsExpression visitIsExpression(IsExpression node) => new IsExpression(
+      cloneNode(node.expression), cloneToken(node.isOperator),
+      cloneToken(node.notOperator), cloneNode(node.type));
+
+  @override
+  Label visitLabel(Label node) =>
+      new Label(cloneNode(node.label), cloneToken(node.colon));
+
+  @override
+  LabeledStatement visitLabeledStatement(LabeledStatement node) =>
+      new LabeledStatement(
+          cloneNodeList(node.labels), cloneNode(node.statement));
+
+  @override
+  LibraryDirective visitLibraryDirective(LibraryDirective node) =>
+      new LibraryDirective(cloneNode(node.documentationComment),
+          cloneNodeList(node.metadata), cloneToken(node.libraryKeyword),
+          cloneNode(node.name), cloneToken(node.semicolon));
+
+  @override
+  LibraryIdentifier visitLibraryIdentifier(LibraryIdentifier node) =>
+      new LibraryIdentifier(cloneNodeList(node.components));
+
+  @override
+  ListLiteral visitListLiteral(ListLiteral node) => new ListLiteral(
+      cloneToken(node.constKeyword), cloneNode(node.typeArguments),
+      cloneToken(node.leftBracket), cloneNodeList(node.elements),
+      cloneToken(node.rightBracket));
+
+  @override
+  MapLiteral visitMapLiteral(MapLiteral node) => new MapLiteral(
+      cloneToken(node.constKeyword), cloneNode(node.typeArguments),
+      cloneToken(node.leftBracket), cloneNodeList(node.entries),
+      cloneToken(node.rightBracket));
+
+  @override
+  MapLiteralEntry visitMapLiteralEntry(
+      MapLiteralEntry node) => new MapLiteralEntry(
+      cloneNode(node.key), cloneToken(node.separator), cloneNode(node.value));
+
+  @override
+  MethodDeclaration visitMethodDeclaration(MethodDeclaration node) =>
+      new MethodDeclaration(cloneNode(node.documentationComment),
+          cloneNodeList(node.metadata), cloneToken(node.externalKeyword),
+          cloneToken(node.modifierKeyword), cloneNode(node.returnType),
+          cloneToken(node.propertyKeyword), cloneToken(node.operatorKeyword),
+          cloneNode(node.name), cloneNode(node.parameters),
+          cloneNode(node.body));
+
+  @override
+  MethodInvocation visitMethodInvocation(MethodInvocation node) =>
+      new MethodInvocation(cloneNode(node.target), cloneToken(node.operator),
+          cloneNode(node.methodName), cloneNode(node.argumentList));
+
+  @override
+  NamedExpression visitNamedExpression(NamedExpression node) =>
+      new NamedExpression(cloneNode(node.name), cloneNode(node.expression));
+
+  @override
+  AstNode visitNativeClause(NativeClause node) =>
+      new NativeClause(cloneToken(node.nativeKeyword), cloneNode(node.name));
+
+  @override
+  NativeFunctionBody visitNativeFunctionBody(NativeFunctionBody node) =>
+      new NativeFunctionBody(cloneToken(node.nativeKeyword),
+          cloneNode(node.stringLiteral), cloneToken(node.semicolon));
+
+  @override
+  NullLiteral visitNullLiteral(NullLiteral node) =>
+      new NullLiteral(cloneToken(node.literal));
+
+  @override
+  ParenthesizedExpression visitParenthesizedExpression(
+      ParenthesizedExpression node) => new ParenthesizedExpression(
+      cloneToken(node.leftParenthesis), cloneNode(node.expression),
+      cloneToken(node.rightParenthesis));
+
+  @override
+  PartDirective visitPartDirective(PartDirective node) {
+    PartDirective directive = new PartDirective(
+        cloneNode(node.documentationComment), cloneNodeList(node.metadata),
+        cloneToken(node.partKeyword), cloneNode(node.uri),
+        cloneToken(node.semicolon));
+    directive.source = node.source;
+    directive.uriContent = node.uriContent;
+    return directive;
+  }
+
+  @override
+  PartOfDirective visitPartOfDirective(PartOfDirective node) =>
+      new PartOfDirective(cloneNode(node.documentationComment),
+          cloneNodeList(node.metadata), cloneToken(node.partKeyword),
+          cloneToken(node.ofKeyword), cloneNode(node.libraryName),
+          cloneToken(node.semicolon));
+
+  @override
+  PostfixExpression visitPostfixExpression(PostfixExpression node) =>
+      new PostfixExpression(cloneNode(node.operand), cloneToken(node.operator));
+
+  @override
+  PrefixedIdentifier visitPrefixedIdentifier(PrefixedIdentifier node) =>
+      new PrefixedIdentifier(cloneNode(node.prefix), cloneToken(node.period),
+          cloneNode(node.identifier));
+
+  @override
+  PrefixExpression visitPrefixExpression(PrefixExpression node) =>
+      new PrefixExpression(cloneToken(node.operator), cloneNode(node.operand));
+
+  @override
+  PropertyAccess visitPropertyAccess(PropertyAccess node) => new PropertyAccess(
+      cloneNode(node.target), cloneToken(node.operator),
+      cloneNode(node.propertyName));
+
+  @override
+  RedirectingConstructorInvocation visitRedirectingConstructorInvocation(
+          RedirectingConstructorInvocation node) =>
+      new RedirectingConstructorInvocation(cloneToken(node.thisKeyword),
+          cloneToken(node.period), cloneNode(node.constructorName),
+          cloneNode(node.argumentList));
+
+  @override
+  RethrowExpression visitRethrowExpression(RethrowExpression node) =>
+      new RethrowExpression(cloneToken(node.rethrowKeyword));
+
+  @override
+  ReturnStatement visitReturnStatement(ReturnStatement node) =>
+      new ReturnStatement(cloneToken(node.returnKeyword),
+          cloneNode(node.expression), cloneToken(node.semicolon));
+
+  @override
+  ScriptTag visitScriptTag(ScriptTag node) =>
+      new ScriptTag(cloneToken(node.scriptTag));
+
+  @override
+  ShowCombinator visitShowCombinator(ShowCombinator node) => new ShowCombinator(
+      cloneToken(node.keyword), cloneNodeList(node.shownNames));
+
+  @override
+  SimpleFormalParameter visitSimpleFormalParameter(
+      SimpleFormalParameter node) => new SimpleFormalParameter(
+      cloneNode(node.documentationComment), cloneNodeList(node.metadata),
+      cloneToken(node.keyword), cloneNode(node.type),
+      cloneNode(node.identifier));
+
+  @override
+  SimpleIdentifier visitSimpleIdentifier(SimpleIdentifier node) =>
+      new SimpleIdentifier(cloneToken(node.token));
+
+  @override
+  SimpleStringLiteral visitSimpleStringLiteral(SimpleStringLiteral node) =>
+      new SimpleStringLiteral(cloneToken(node.literal), node.value);
+
+  @override
+  StringInterpolation visitStringInterpolation(StringInterpolation node) =>
+      new StringInterpolation(cloneNodeList(node.elements));
+
+  @override
+  SuperConstructorInvocation visitSuperConstructorInvocation(
+      SuperConstructorInvocation node) => new SuperConstructorInvocation(
+      cloneToken(node.superKeyword), cloneToken(node.period),
+      cloneNode(node.constructorName), cloneNode(node.argumentList));
+
+  @override
+  SuperExpression visitSuperExpression(SuperExpression node) =>
+      new SuperExpression(cloneToken(node.superKeyword));
+
+  @override
+  SwitchCase visitSwitchCase(SwitchCase node) => new SwitchCase(
+      cloneNodeList(node.labels), cloneToken(node.keyword),
+      cloneNode(node.expression), cloneToken(node.colon),
+      cloneNodeList(node.statements));
+
+  @override
+  SwitchDefault visitSwitchDefault(SwitchDefault node) => new SwitchDefault(
+      cloneNodeList(node.labels), cloneToken(node.keyword),
+      cloneToken(node.colon), cloneNodeList(node.statements));
+
+  @override
+  SwitchStatement visitSwitchStatement(SwitchStatement node) =>
+      new SwitchStatement(cloneToken(node.switchKeyword),
+          cloneToken(node.leftParenthesis), cloneNode(node.expression),
+          cloneToken(node.rightParenthesis), cloneToken(node.leftBracket),
+          cloneNodeList(node.members), cloneToken(node.rightBracket));
+
+  @override
+  SymbolLiteral visitSymbolLiteral(SymbolLiteral node) => new SymbolLiteral(
+      cloneToken(node.poundSign), cloneTokenList(node.components));
+
+  @override
+  ThisExpression visitThisExpression(ThisExpression node) =>
+      new ThisExpression(cloneToken(node.thisKeyword));
+
+  @override
+  ThrowExpression visitThrowExpression(ThrowExpression node) =>
+      new ThrowExpression(
+          cloneToken(node.throwKeyword), cloneNode(node.expression));
+
+  @override
+  TopLevelVariableDeclaration visitTopLevelVariableDeclaration(
+      TopLevelVariableDeclaration node) => new TopLevelVariableDeclaration(
+      cloneNode(node.documentationComment), cloneNodeList(node.metadata),
+      cloneNode(node.variables), cloneToken(node.semicolon));
+
+  @override
+  TryStatement visitTryStatement(TryStatement node) => new TryStatement(
+      cloneToken(node.tryKeyword), cloneNode(node.body),
+      cloneNodeList(node.catchClauses), cloneToken(node.finallyKeyword),
+      cloneNode(node.finallyBlock));
+
+  @override
+  TypeArgumentList visitTypeArgumentList(TypeArgumentList node) =>
+      new TypeArgumentList(cloneToken(node.leftBracket),
+          cloneNodeList(node.arguments), cloneToken(node.rightBracket));
+
+  @override
+  TypeName visitTypeName(TypeName node) =>
+      new TypeName(cloneNode(node.name), cloneNode(node.typeArguments));
+
+  @override
+  TypeParameter visitTypeParameter(TypeParameter node) => new TypeParameter(
+      cloneNode(node.documentationComment), cloneNodeList(node.metadata),
+      cloneNode(node.name), cloneToken(node.extendsKeyword),
+      cloneNode(node.bound));
+
+  @override
+  TypeParameterList visitTypeParameterList(TypeParameterList node) =>
+      new TypeParameterList(cloneToken(node.leftBracket),
+          cloneNodeList(node.typeParameters), cloneToken(node.rightBracket));
+
+  @override
+  VariableDeclaration visitVariableDeclaration(VariableDeclaration node) =>
+      new VariableDeclaration(cloneNode(node.name), cloneToken(node.equals),
+          cloneNode(node.initializer));
+
+  @override
+  VariableDeclarationList visitVariableDeclarationList(
+      VariableDeclarationList node) => new VariableDeclarationList(null,
+      cloneNodeList(node.metadata), cloneToken(node.keyword),
+      cloneNode(node.type), cloneNodeList(node.variables));
+
+  @override
+  VariableDeclarationStatement visitVariableDeclarationStatement(
+      VariableDeclarationStatement node) => new VariableDeclarationStatement(
+      cloneNode(node.variables), cloneToken(node.semicolon));
+
+  @override
+  WhileStatement visitWhileStatement(WhileStatement node) => new WhileStatement(
+      cloneToken(node.whileKeyword), cloneToken(node.leftParenthesis),
+      cloneNode(node.condition), cloneToken(node.rightParenthesis),
+      cloneNode(node.body));
+
+  @override
+  WithClause visitWithClause(WithClause node) => new WithClause(
+      cloneToken(node.withKeyword), cloneNodeList(node.mixinTypes));
+
+  @override
+  YieldStatement visitYieldStatement(YieldStatement node) => new YieldStatement(
+      cloneToken(node.yieldKeyword), cloneToken(node.star),
+      cloneNode(node.expression), cloneToken(node.semicolon));
+
+  /**
+   * Return a clone of the given [node].
+   */
+  static AstNode clone(AstNode node) {
+    return node.accept(new AstCloner());
+  }
+}
+
+/**
+ * An AstVisitor that compares the structure of two AstNodes to see whether they
+ * are equal.
+ */
+class AstComparator implements AstVisitor<bool> {
+  /**
+   * The AST node with which the node being visited is to be compared. This is
+   * only valid at the beginning of each visit method (until [isEqualNodes] is
+   * invoked).
+   */
+  AstNode _other;
+
+  /**
+   * Return `true` if the [first] node and the [second] node have the same
+   * structure.
+   *
+   * *Note:* This method is only visible for testing purposes and should not be
+   * used by clients.
+   */
+  bool isEqualNodes(AstNode first, AstNode second) {
+    if (first == null) {
+      return second == null;
+    } else if (second == null) {
+      return false;
+    } else if (first.runtimeType != second.runtimeType) {
+      return false;
+    }
+    _other = second;
+    return first.accept(this);
+  }
+
+  /**
+   * Return `true` if the [first] token and the [second] token have the same
+   * structure.
+   *
+   * *Note:* This method is only visible for testing purposes and should not be
+   * used by clients.
+   */
+  bool isEqualTokens(Token first, Token second) {
+    if (first == null) {
+      return second == null;
+    } else if (second == null) {
+      return false;
+    } else if (identical(first, second)) {
+      return true;
+    }
+    return first.offset == second.offset &&
+        first.length == second.length &&
+        first.lexeme == second.lexeme;
+  }
+
+  @override
+  bool visitAdjacentStrings(AdjacentStrings node) {
+    AdjacentStrings other = _other as AdjacentStrings;
+    return _isEqualNodeLists(node.strings, other.strings);
+  }
+
+  @override
+  bool visitAnnotation(Annotation node) {
+    Annotation other = _other as Annotation;
+    return isEqualTokens(node.atSign, other.atSign) &&
+        isEqualNodes(node.name, other.name) &&
+        isEqualTokens(node.period, other.period) &&
+        isEqualNodes(node.constructorName, other.constructorName) &&
+        isEqualNodes(node.arguments, other.arguments);
+  }
+
+  @override
+  bool visitArgumentList(ArgumentList node) {
+    ArgumentList other = _other as ArgumentList;
+    return isEqualTokens(node.leftParenthesis, other.leftParenthesis) &&
+        _isEqualNodeLists(node.arguments, other.arguments) &&
+        isEqualTokens(node.rightParenthesis, other.rightParenthesis);
+  }
+
+  @override
+  bool visitAsExpression(AsExpression node) {
+    AsExpression other = _other as AsExpression;
+    return isEqualNodes(node.expression, other.expression) &&
+        isEqualTokens(node.asOperator, other.asOperator) &&
+        isEqualNodes(node.type, other.type);
+  }
+
+  @override
+  bool visitAssertStatement(AssertStatement node) {
+    AssertStatement other = _other as AssertStatement;
+    return isEqualTokens(node.assertKeyword, other.assertKeyword) &&
+        isEqualTokens(node.leftParenthesis, other.leftParenthesis) &&
+        isEqualNodes(node.condition, other.condition) &&
+        isEqualTokens(node.rightParenthesis, other.rightParenthesis) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitAssignmentExpression(AssignmentExpression node) {
+    AssignmentExpression other = _other as AssignmentExpression;
+    return isEqualNodes(node.leftHandSide, other.leftHandSide) &&
+        isEqualTokens(node.operator, other.operator) &&
+        isEqualNodes(node.rightHandSide, other.rightHandSide);
+  }
+
+  @override
+  bool visitAwaitExpression(AwaitExpression node) {
+    AwaitExpression other = _other as AwaitExpression;
+    return isEqualTokens(node.awaitKeyword, other.awaitKeyword) &&
+        isEqualNodes(node.expression, other.expression);
+  }
+
+  @override
+  bool visitBinaryExpression(BinaryExpression node) {
+    BinaryExpression other = _other as BinaryExpression;
+    return isEqualNodes(node.leftOperand, other.leftOperand) &&
+        isEqualTokens(node.operator, other.operator) &&
+        isEqualNodes(node.rightOperand, other.rightOperand);
+  }
+
+  @override
+  bool visitBlock(Block node) {
+    Block other = _other as Block;
+    return isEqualTokens(node.leftBracket, other.leftBracket) &&
+        _isEqualNodeLists(node.statements, other.statements) &&
+        isEqualTokens(node.rightBracket, other.rightBracket);
+  }
+
+  @override
+  bool visitBlockFunctionBody(BlockFunctionBody node) {
+    BlockFunctionBody other = _other as BlockFunctionBody;
+    return isEqualNodes(node.block, other.block);
+  }
+
+  @override
+  bool visitBooleanLiteral(BooleanLiteral node) {
+    BooleanLiteral other = _other as BooleanLiteral;
+    return isEqualTokens(node.literal, other.literal) &&
+        node.value == other.value;
+  }
+
+  @override
+  bool visitBreakStatement(BreakStatement node) {
+    BreakStatement other = _other as BreakStatement;
+    return isEqualTokens(node.breakKeyword, other.breakKeyword) &&
+        isEqualNodes(node.label, other.label) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitCascadeExpression(CascadeExpression node) {
+    CascadeExpression other = _other as CascadeExpression;
+    return isEqualNodes(node.target, other.target) &&
+        _isEqualNodeLists(node.cascadeSections, other.cascadeSections);
+  }
+
+  @override
+  bool visitCatchClause(CatchClause node) {
+    CatchClause other = _other as CatchClause;
+    return isEqualTokens(node.onKeyword, other.onKeyword) &&
+        isEqualNodes(node.exceptionType, other.exceptionType) &&
+        isEqualTokens(node.catchKeyword, other.catchKeyword) &&
+        isEqualTokens(node.leftParenthesis, other.leftParenthesis) &&
+        isEqualNodes(node.exceptionParameter, other.exceptionParameter) &&
+        isEqualTokens(node.comma, other.comma) &&
+        isEqualNodes(node.stackTraceParameter, other.stackTraceParameter) &&
+        isEqualTokens(node.rightParenthesis, other.rightParenthesis) &&
+        isEqualNodes(node.body, other.body);
+  }
+
+  @override
+  bool visitClassDeclaration(ClassDeclaration node) {
+    ClassDeclaration other = _other as ClassDeclaration;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.abstractKeyword, other.abstractKeyword) &&
+        isEqualTokens(node.classKeyword, other.classKeyword) &&
+        isEqualNodes(node.name, other.name) &&
+        isEqualNodes(node.typeParameters, other.typeParameters) &&
+        isEqualNodes(node.extendsClause, other.extendsClause) &&
+        isEqualNodes(node.withClause, other.withClause) &&
+        isEqualNodes(node.implementsClause, other.implementsClause) &&
+        isEqualTokens(node.leftBracket, other.leftBracket) &&
+        _isEqualNodeLists(node.members, other.members) &&
+        isEqualTokens(node.rightBracket, other.rightBracket);
+  }
+
+  @override
+  bool visitClassTypeAlias(ClassTypeAlias node) {
+    ClassTypeAlias other = _other as ClassTypeAlias;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.typedefKeyword, other.typedefKeyword) &&
+        isEqualNodes(node.name, other.name) &&
+        isEqualNodes(node.typeParameters, other.typeParameters) &&
+        isEqualTokens(node.equals, other.equals) &&
+        isEqualTokens(node.abstractKeyword, other.abstractKeyword) &&
+        isEqualNodes(node.superclass, other.superclass) &&
+        isEqualNodes(node.withClause, other.withClause) &&
+        isEqualNodes(node.implementsClause, other.implementsClause) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitComment(Comment node) {
+    Comment other = _other as Comment;
+    return _isEqualNodeLists(node.references, other.references);
+  }
+
+  @override
+  bool visitCommentReference(CommentReference node) {
+    CommentReference other = _other as CommentReference;
+    return isEqualTokens(node.newKeyword, other.newKeyword) &&
+        isEqualNodes(node.identifier, other.identifier);
+  }
+
+  @override
+  bool visitCompilationUnit(CompilationUnit node) {
+    CompilationUnit other = _other as CompilationUnit;
+    return isEqualTokens(node.beginToken, other.beginToken) &&
+        isEqualNodes(node.scriptTag, other.scriptTag) &&
+        _isEqualNodeLists(node.directives, other.directives) &&
+        _isEqualNodeLists(node.declarations, other.declarations) &&
+        isEqualTokens(node.endToken, other.endToken);
+  }
+
+  @override
+  bool visitConditionalExpression(ConditionalExpression node) {
+    ConditionalExpression other = _other as ConditionalExpression;
+    return isEqualNodes(node.condition, other.condition) &&
+        isEqualTokens(node.question, other.question) &&
+        isEqualNodes(node.thenExpression, other.thenExpression) &&
+        isEqualTokens(node.colon, other.colon) &&
+        isEqualNodes(node.elseExpression, other.elseExpression);
+  }
+
+  @override
+  bool visitConstructorDeclaration(ConstructorDeclaration node) {
+    ConstructorDeclaration other = _other as ConstructorDeclaration;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.externalKeyword, other.externalKeyword) &&
+        isEqualTokens(node.constKeyword, other.constKeyword) &&
+        isEqualTokens(node.factoryKeyword, other.factoryKeyword) &&
+        isEqualNodes(node.returnType, other.returnType) &&
+        isEqualTokens(node.period, other.period) &&
+        isEqualNodes(node.name, other.name) &&
+        isEqualNodes(node.parameters, other.parameters) &&
+        isEqualTokens(node.separator, other.separator) &&
+        _isEqualNodeLists(node.initializers, other.initializers) &&
+        isEqualNodes(node.redirectedConstructor, other.redirectedConstructor) &&
+        isEqualNodes(node.body, other.body);
+  }
+
+  @override
+  bool visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+    ConstructorFieldInitializer other = _other as ConstructorFieldInitializer;
+    return isEqualTokens(node.thisKeyword, other.thisKeyword) &&
+        isEqualTokens(node.period, other.period) &&
+        isEqualNodes(node.fieldName, other.fieldName) &&
+        isEqualTokens(node.equals, other.equals) &&
+        isEqualNodes(node.expression, other.expression);
+  }
+
+  @override
+  bool visitConstructorName(ConstructorName node) {
+    ConstructorName other = _other as ConstructorName;
+    return isEqualNodes(node.type, other.type) &&
+        isEqualTokens(node.period, other.period) &&
+        isEqualNodes(node.name, other.name);
+  }
+
+  @override
+  bool visitContinueStatement(ContinueStatement node) {
+    ContinueStatement other = _other as ContinueStatement;
+    return isEqualTokens(node.continueKeyword, other.continueKeyword) &&
+        isEqualNodes(node.label, other.label) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitDeclaredIdentifier(DeclaredIdentifier node) {
+    DeclaredIdentifier other = _other as DeclaredIdentifier;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.keyword, other.keyword) &&
+        isEqualNodes(node.type, other.type) &&
+        isEqualNodes(node.identifier, other.identifier);
+  }
+
+  @override
+  bool visitDefaultFormalParameter(DefaultFormalParameter node) {
+    DefaultFormalParameter other = _other as DefaultFormalParameter;
+    return isEqualNodes(node.parameter, other.parameter) &&
+        node.kind == other.kind &&
+        isEqualTokens(node.separator, other.separator) &&
+        isEqualNodes(node.defaultValue, other.defaultValue);
+  }
+
+  @override
+  bool visitDoStatement(DoStatement node) {
+    DoStatement other = _other as DoStatement;
+    return isEqualTokens(node.doKeyword, other.doKeyword) &&
+        isEqualNodes(node.body, other.body) &&
+        isEqualTokens(node.whileKeyword, other.whileKeyword) &&
+        isEqualTokens(node.leftParenthesis, other.leftParenthesis) &&
+        isEqualNodes(node.condition, other.condition) &&
+        isEqualTokens(node.rightParenthesis, other.rightParenthesis) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitDoubleLiteral(DoubleLiteral node) {
+    DoubleLiteral other = _other as DoubleLiteral;
+    return isEqualTokens(node.literal, other.literal) &&
+        node.value == other.value;
+  }
+
+  @override
+  bool visitEmptyFunctionBody(EmptyFunctionBody node) {
+    EmptyFunctionBody other = _other as EmptyFunctionBody;
+    return isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitEmptyStatement(EmptyStatement node) {
+    EmptyStatement other = _other as EmptyStatement;
+    return isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitEnumConstantDeclaration(EnumConstantDeclaration node) {
+    EnumConstantDeclaration other = _other as EnumConstantDeclaration;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualNodes(node.name, other.name);
+  }
+
+  @override
+  bool visitEnumDeclaration(EnumDeclaration node) {
+    EnumDeclaration other = _other as EnumDeclaration;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.enumKeyword, other.enumKeyword) &&
+        isEqualNodes(node.name, other.name) &&
+        isEqualTokens(node.leftBracket, other.leftBracket) &&
+        _isEqualNodeLists(node.constants, other.constants) &&
+        isEqualTokens(node.rightBracket, other.rightBracket);
+  }
+
+  @override
+  bool visitExportDirective(ExportDirective node) {
+    ExportDirective other = _other as ExportDirective;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.keyword, other.keyword) &&
+        isEqualNodes(node.uri, other.uri) &&
+        _isEqualNodeLists(node.combinators, other.combinators) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitExpressionFunctionBody(ExpressionFunctionBody node) {
+    ExpressionFunctionBody other = _other as ExpressionFunctionBody;
+    return isEqualTokens(node.functionDefinition, other.functionDefinition) &&
+        isEqualNodes(node.expression, other.expression) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitExpressionStatement(ExpressionStatement node) {
+    ExpressionStatement other = _other as ExpressionStatement;
+    return isEqualNodes(node.expression, other.expression) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitExtendsClause(ExtendsClause node) {
+    ExtendsClause other = _other as ExtendsClause;
+    return isEqualTokens(node.extendsKeyword, other.extendsKeyword) &&
+        isEqualNodes(node.superclass, other.superclass);
+  }
+
+  @override
+  bool visitFieldDeclaration(FieldDeclaration node) {
+    FieldDeclaration other = _other as FieldDeclaration;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.staticKeyword, other.staticKeyword) &&
+        isEqualNodes(node.fields, other.fields) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitFieldFormalParameter(FieldFormalParameter node) {
+    FieldFormalParameter other = _other as FieldFormalParameter;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.keyword, other.keyword) &&
+        isEqualNodes(node.type, other.type) &&
+        isEqualTokens(node.thisKeyword, other.thisKeyword) &&
+        isEqualTokens(node.period, other.period) &&
+        isEqualNodes(node.identifier, other.identifier);
+  }
+
+  @override
+  bool visitForEachStatement(ForEachStatement node) {
+    ForEachStatement other = _other as ForEachStatement;
+    return isEqualTokens(node.forKeyword, other.forKeyword) &&
+        isEqualTokens(node.leftParenthesis, other.leftParenthesis) &&
+        isEqualNodes(node.loopVariable, other.loopVariable) &&
+        isEqualTokens(node.inKeyword, other.inKeyword) &&
+        isEqualNodes(node.iterable, other.iterable) &&
+        isEqualTokens(node.rightParenthesis, other.rightParenthesis) &&
+        isEqualNodes(node.body, other.body);
+  }
+
+  @override
+  bool visitFormalParameterList(FormalParameterList node) {
+    FormalParameterList other = _other as FormalParameterList;
+    return isEqualTokens(node.leftParenthesis, other.leftParenthesis) &&
+        _isEqualNodeLists(node.parameters, other.parameters) &&
+        isEqualTokens(node.leftDelimiter, other.leftDelimiter) &&
+        isEqualTokens(node.rightDelimiter, other.rightDelimiter) &&
+        isEqualTokens(node.rightParenthesis, other.rightParenthesis);
+  }
+
+  @override
+  bool visitForStatement(ForStatement node) {
+    ForStatement other = _other as ForStatement;
+    return isEqualTokens(node.forKeyword, other.forKeyword) &&
+        isEqualTokens(node.leftParenthesis, other.leftParenthesis) &&
+        isEqualNodes(node.variables, other.variables) &&
+        isEqualNodes(node.initialization, other.initialization) &&
+        isEqualTokens(node.leftSeparator, other.leftSeparator) &&
+        isEqualNodes(node.condition, other.condition) &&
+        isEqualTokens(node.rightSeparator, other.rightSeparator) &&
+        _isEqualNodeLists(node.updaters, other.updaters) &&
+        isEqualTokens(node.rightParenthesis, other.rightParenthesis) &&
+        isEqualNodes(node.body, other.body);
+  }
+
+  @override
+  bool visitFunctionDeclaration(FunctionDeclaration node) {
+    FunctionDeclaration other = _other as FunctionDeclaration;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.externalKeyword, other.externalKeyword) &&
+        isEqualNodes(node.returnType, other.returnType) &&
+        isEqualTokens(node.propertyKeyword, other.propertyKeyword) &&
+        isEqualNodes(node.name, other.name) &&
+        isEqualNodes(node.functionExpression, other.functionExpression);
+  }
+
+  @override
+  bool visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
+    FunctionDeclarationStatement other = _other as FunctionDeclarationStatement;
+    return isEqualNodes(node.functionDeclaration, other.functionDeclaration);
+  }
+
+  @override
+  bool visitFunctionExpression(FunctionExpression node) {
+    FunctionExpression other = _other as FunctionExpression;
+    return isEqualNodes(node.parameters, other.parameters) &&
+        isEqualNodes(node.body, other.body);
+  }
+
+  @override
+  bool visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    FunctionExpressionInvocation other = _other as FunctionExpressionInvocation;
+    return isEqualNodes(node.function, other.function) &&
+        isEqualNodes(node.argumentList, other.argumentList);
+  }
+
+  @override
+  bool visitFunctionTypeAlias(FunctionTypeAlias node) {
+    FunctionTypeAlias other = _other as FunctionTypeAlias;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.typedefKeyword, other.typedefKeyword) &&
+        isEqualNodes(node.returnType, other.returnType) &&
+        isEqualNodes(node.name, other.name) &&
+        isEqualNodes(node.typeParameters, other.typeParameters) &&
+        isEqualNodes(node.parameters, other.parameters) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+    FunctionTypedFormalParameter other = _other as FunctionTypedFormalParameter;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualNodes(node.returnType, other.returnType) &&
+        isEqualNodes(node.identifier, other.identifier) &&
+        isEqualNodes(node.parameters, other.parameters);
+  }
+
+  @override
+  bool visitHideCombinator(HideCombinator node) {
+    HideCombinator other = _other as HideCombinator;
+    return isEqualTokens(node.keyword, other.keyword) &&
+        _isEqualNodeLists(node.hiddenNames, other.hiddenNames);
+  }
+
+  @override
+  bool visitIfStatement(IfStatement node) {
+    IfStatement other = _other as IfStatement;
+    return isEqualTokens(node.ifKeyword, other.ifKeyword) &&
+        isEqualTokens(node.leftParenthesis, other.leftParenthesis) &&
+        isEqualNodes(node.condition, other.condition) &&
+        isEqualTokens(node.rightParenthesis, other.rightParenthesis) &&
+        isEqualNodes(node.thenStatement, other.thenStatement) &&
+        isEqualTokens(node.elseKeyword, other.elseKeyword) &&
+        isEqualNodes(node.elseStatement, other.elseStatement);
+  }
+
+  @override
+  bool visitImplementsClause(ImplementsClause node) {
+    ImplementsClause other = _other as ImplementsClause;
+    return isEqualTokens(node.implementsKeyword, other.implementsKeyword) &&
+        _isEqualNodeLists(node.interfaces, other.interfaces);
+  }
+
+  @override
+  bool visitImportDirective(ImportDirective node) {
+    ImportDirective other = _other as ImportDirective;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.keyword, other.keyword) &&
+        isEqualNodes(node.uri, other.uri) &&
+        isEqualTokens(node.deferredKeyword, other.deferredKeyword) &&
+        isEqualTokens(node.asKeyword, other.asKeyword) &&
+        isEqualNodes(node.prefix, other.prefix) &&
+        _isEqualNodeLists(node.combinators, other.combinators) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitIndexExpression(IndexExpression node) {
+    IndexExpression other = _other as IndexExpression;
+    return isEqualNodes(node.target, other.target) &&
+        isEqualTokens(node.leftBracket, other.leftBracket) &&
+        isEqualNodes(node.index, other.index) &&
+        isEqualTokens(node.rightBracket, other.rightBracket);
+  }
+
+  @override
+  bool visitInstanceCreationExpression(InstanceCreationExpression node) {
+    InstanceCreationExpression other = _other as InstanceCreationExpression;
+    return isEqualTokens(node.keyword, other.keyword) &&
+        isEqualNodes(node.constructorName, other.constructorName) &&
+        isEqualNodes(node.argumentList, other.argumentList);
+  }
+
+  @override
+  bool visitIntegerLiteral(IntegerLiteral node) {
+    IntegerLiteral other = _other as IntegerLiteral;
+    return isEqualTokens(node.literal, other.literal) &&
+        (node.value == other.value);
+  }
+
+  @override
+  bool visitInterpolationExpression(InterpolationExpression node) {
+    InterpolationExpression other = _other as InterpolationExpression;
+    return isEqualTokens(node.leftBracket, other.leftBracket) &&
+        isEqualNodes(node.expression, other.expression) &&
+        isEqualTokens(node.rightBracket, other.rightBracket);
+  }
+
+  @override
+  bool visitInterpolationString(InterpolationString node) {
+    InterpolationString other = _other as InterpolationString;
+    return isEqualTokens(node.contents, other.contents) &&
+        node.value == other.value;
+  }
+
+  @override
+  bool visitIsExpression(IsExpression node) {
+    IsExpression other = _other as IsExpression;
+    return isEqualNodes(node.expression, other.expression) &&
+        isEqualTokens(node.isOperator, other.isOperator) &&
+        isEqualTokens(node.notOperator, other.notOperator) &&
+        isEqualNodes(node.type, other.type);
+  }
+
+  @override
+  bool visitLabel(Label node) {
+    Label other = _other as Label;
+    return isEqualNodes(node.label, other.label) &&
+        isEqualTokens(node.colon, other.colon);
+  }
+
+  @override
+  bool visitLabeledStatement(LabeledStatement node) {
+    LabeledStatement other = _other as LabeledStatement;
+    return _isEqualNodeLists(node.labels, other.labels) &&
+        isEqualNodes(node.statement, other.statement);
+  }
+
+  @override
+  bool visitLibraryDirective(LibraryDirective node) {
+    LibraryDirective other = _other as LibraryDirective;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.libraryKeyword, other.libraryKeyword) &&
+        isEqualNodes(node.name, other.name) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitLibraryIdentifier(LibraryIdentifier node) {
+    LibraryIdentifier other = _other as LibraryIdentifier;
+    return _isEqualNodeLists(node.components, other.components);
+  }
+
+  @override
+  bool visitListLiteral(ListLiteral node) {
+    ListLiteral other = _other as ListLiteral;
+    return isEqualTokens(node.constKeyword, other.constKeyword) &&
+        isEqualNodes(node.typeArguments, other.typeArguments) &&
+        isEqualTokens(node.leftBracket, other.leftBracket) &&
+        _isEqualNodeLists(node.elements, other.elements) &&
+        isEqualTokens(node.rightBracket, other.rightBracket);
+  }
+
+  @override
+  bool visitMapLiteral(MapLiteral node) {
+    MapLiteral other = _other as MapLiteral;
+    return isEqualTokens(node.constKeyword, other.constKeyword) &&
+        isEqualNodes(node.typeArguments, other.typeArguments) &&
+        isEqualTokens(node.leftBracket, other.leftBracket) &&
+        _isEqualNodeLists(node.entries, other.entries) &&
+        isEqualTokens(node.rightBracket, other.rightBracket);
+  }
+
+  @override
+  bool visitMapLiteralEntry(MapLiteralEntry node) {
+    MapLiteralEntry other = _other as MapLiteralEntry;
+    return isEqualNodes(node.key, other.key) &&
+        isEqualTokens(node.separator, other.separator) &&
+        isEqualNodes(node.value, other.value);
+  }
+
+  @override
+  bool visitMethodDeclaration(MethodDeclaration node) {
+    MethodDeclaration other = _other as MethodDeclaration;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.externalKeyword, other.externalKeyword) &&
+        isEqualTokens(node.modifierKeyword, other.modifierKeyword) &&
+        isEqualNodes(node.returnType, other.returnType) &&
+        isEqualTokens(node.propertyKeyword, other.propertyKeyword) &&
+        isEqualTokens(node.propertyKeyword, other.propertyKeyword) &&
+        isEqualNodes(node.name, other.name) &&
+        isEqualNodes(node.parameters, other.parameters) &&
+        isEqualNodes(node.body, other.body);
+  }
+
+  @override
+  bool visitMethodInvocation(MethodInvocation node) {
+    MethodInvocation other = _other as MethodInvocation;
+    return isEqualNodes(node.target, other.target) &&
+        isEqualTokens(node.operator, other.operator) &&
+        isEqualNodes(node.methodName, other.methodName) &&
+        isEqualNodes(node.argumentList, other.argumentList);
+  }
+
+  @override
+  bool visitNamedExpression(NamedExpression node) {
+    NamedExpression other = _other as NamedExpression;
+    return isEqualNodes(node.name, other.name) &&
+        isEqualNodes(node.expression, other.expression);
+  }
+
+  @override
+  bool visitNativeClause(NativeClause node) {
+    NativeClause other = _other as NativeClause;
+    return isEqualTokens(node.nativeKeyword, other.nativeKeyword) &&
+        isEqualNodes(node.name, other.name);
+  }
+
+  @override
+  bool visitNativeFunctionBody(NativeFunctionBody node) {
+    NativeFunctionBody other = _other as NativeFunctionBody;
+    return isEqualTokens(node.nativeKeyword, other.nativeKeyword) &&
+        isEqualNodes(node.stringLiteral, other.stringLiteral) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitNullLiteral(NullLiteral node) {
+    NullLiteral other = _other as NullLiteral;
+    return isEqualTokens(node.literal, other.literal);
+  }
+
+  @override
+  bool visitParenthesizedExpression(ParenthesizedExpression node) {
+    ParenthesizedExpression other = _other as ParenthesizedExpression;
+    return isEqualTokens(node.leftParenthesis, other.leftParenthesis) &&
+        isEqualNodes(node.expression, other.expression) &&
+        isEqualTokens(node.rightParenthesis, other.rightParenthesis);
+  }
+
+  @override
+  bool visitPartDirective(PartDirective node) {
+    PartDirective other = _other as PartDirective;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.partKeyword, other.partKeyword) &&
+        isEqualNodes(node.uri, other.uri) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitPartOfDirective(PartOfDirective node) {
+    PartOfDirective other = _other as PartOfDirective;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.partKeyword, other.partKeyword) &&
+        isEqualTokens(node.ofKeyword, other.ofKeyword) &&
+        isEqualNodes(node.libraryName, other.libraryName) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitPostfixExpression(PostfixExpression node) {
+    PostfixExpression other = _other as PostfixExpression;
+    return isEqualNodes(node.operand, other.operand) &&
+        isEqualTokens(node.operator, other.operator);
+  }
+
+  @override
+  bool visitPrefixedIdentifier(PrefixedIdentifier node) {
+    PrefixedIdentifier other = _other as PrefixedIdentifier;
+    return isEqualNodes(node.prefix, other.prefix) &&
+        isEqualTokens(node.period, other.period) &&
+        isEqualNodes(node.identifier, other.identifier);
+  }
+
+  @override
+  bool visitPrefixExpression(PrefixExpression node) {
+    PrefixExpression other = _other as PrefixExpression;
+    return isEqualTokens(node.operator, other.operator) &&
+        isEqualNodes(node.operand, other.operand);
+  }
+
+  @override
+  bool visitPropertyAccess(PropertyAccess node) {
+    PropertyAccess other = _other as PropertyAccess;
+    return isEqualNodes(node.target, other.target) &&
+        isEqualTokens(node.operator, other.operator) &&
+        isEqualNodes(node.propertyName, other.propertyName);
+  }
+
+  @override
+  bool visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    RedirectingConstructorInvocation other =
+        _other as RedirectingConstructorInvocation;
+    return isEqualTokens(node.thisKeyword, other.thisKeyword) &&
+        isEqualTokens(node.period, other.period) &&
+        isEqualNodes(node.constructorName, other.constructorName) &&
+        isEqualNodes(node.argumentList, other.argumentList);
+  }
+
+  @override
+  bool visitRethrowExpression(RethrowExpression node) {
+    RethrowExpression other = _other as RethrowExpression;
+    return isEqualTokens(node.rethrowKeyword, other.rethrowKeyword);
+  }
+
+  @override
+  bool visitReturnStatement(ReturnStatement node) {
+    ReturnStatement other = _other as ReturnStatement;
+    return isEqualTokens(node.returnKeyword, other.returnKeyword) &&
+        isEqualNodes(node.expression, other.expression) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitScriptTag(ScriptTag node) {
+    ScriptTag other = _other as ScriptTag;
+    return isEqualTokens(node.scriptTag, other.scriptTag);
+  }
+
+  @override
+  bool visitShowCombinator(ShowCombinator node) {
+    ShowCombinator other = _other as ShowCombinator;
+    return isEqualTokens(node.keyword, other.keyword) &&
+        _isEqualNodeLists(node.shownNames, other.shownNames);
+  }
+
+  @override
+  bool visitSimpleFormalParameter(SimpleFormalParameter node) {
+    SimpleFormalParameter other = _other as SimpleFormalParameter;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.keyword, other.keyword) &&
+        isEqualNodes(node.type, other.type) &&
+        isEqualNodes(node.identifier, other.identifier);
+  }
+
+  @override
+  bool visitSimpleIdentifier(SimpleIdentifier node) {
+    SimpleIdentifier other = _other as SimpleIdentifier;
+    return isEqualTokens(node.token, other.token);
+  }
+
+  @override
+  bool visitSimpleStringLiteral(SimpleStringLiteral node) {
+    SimpleStringLiteral other = _other as SimpleStringLiteral;
+    return isEqualTokens(node.literal, other.literal) &&
+        (node.value == other.value);
+  }
+
+  @override
+  bool visitStringInterpolation(StringInterpolation node) {
+    StringInterpolation other = _other as StringInterpolation;
+    return _isEqualNodeLists(node.elements, other.elements);
+  }
+
+  @override
+  bool visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    SuperConstructorInvocation other = _other as SuperConstructorInvocation;
+    return isEqualTokens(node.superKeyword, other.superKeyword) &&
+        isEqualTokens(node.period, other.period) &&
+        isEqualNodes(node.constructorName, other.constructorName) &&
+        isEqualNodes(node.argumentList, other.argumentList);
+  }
+
+  @override
+  bool visitSuperExpression(SuperExpression node) {
+    SuperExpression other = _other as SuperExpression;
+    return isEqualTokens(node.superKeyword, other.superKeyword);
+  }
+
+  @override
+  bool visitSwitchCase(SwitchCase node) {
+    SwitchCase other = _other as SwitchCase;
+    return _isEqualNodeLists(node.labels, other.labels) &&
+        isEqualTokens(node.keyword, other.keyword) &&
+        isEqualNodes(node.expression, other.expression) &&
+        isEqualTokens(node.colon, other.colon) &&
+        _isEqualNodeLists(node.statements, other.statements);
+  }
+
+  @override
+  bool visitSwitchDefault(SwitchDefault node) {
+    SwitchDefault other = _other as SwitchDefault;
+    return _isEqualNodeLists(node.labels, other.labels) &&
+        isEqualTokens(node.keyword, other.keyword) &&
+        isEqualTokens(node.colon, other.colon) &&
+        _isEqualNodeLists(node.statements, other.statements);
+  }
+
+  @override
+  bool visitSwitchStatement(SwitchStatement node) {
+    SwitchStatement other = _other as SwitchStatement;
+    return isEqualTokens(node.switchKeyword, other.switchKeyword) &&
+        isEqualTokens(node.leftParenthesis, other.leftParenthesis) &&
+        isEqualNodes(node.expression, other.expression) &&
+        isEqualTokens(node.rightParenthesis, other.rightParenthesis) &&
+        isEqualTokens(node.leftBracket, other.leftBracket) &&
+        _isEqualNodeLists(node.members, other.members) &&
+        isEqualTokens(node.rightBracket, other.rightBracket);
+  }
+
+  @override
+  bool visitSymbolLiteral(SymbolLiteral node) {
+    SymbolLiteral other = _other as SymbolLiteral;
+    return isEqualTokens(node.poundSign, other.poundSign) &&
+        _isEqualTokenLists(node.components, other.components);
+  }
+
+  @override
+  bool visitThisExpression(ThisExpression node) {
+    ThisExpression other = _other as ThisExpression;
+    return isEqualTokens(node.thisKeyword, other.thisKeyword);
+  }
+
+  @override
+  bool visitThrowExpression(ThrowExpression node) {
+    ThrowExpression other = _other as ThrowExpression;
+    return isEqualTokens(node.throwKeyword, other.throwKeyword) &&
+        isEqualNodes(node.expression, other.expression);
+  }
+
+  @override
+  bool visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+    TopLevelVariableDeclaration other = _other as TopLevelVariableDeclaration;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualNodes(node.variables, other.variables) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitTryStatement(TryStatement node) {
+    TryStatement other = _other as TryStatement;
+    return isEqualTokens(node.tryKeyword, other.tryKeyword) &&
+        isEqualNodes(node.body, other.body) &&
+        _isEqualNodeLists(node.catchClauses, other.catchClauses) &&
+        isEqualTokens(node.finallyKeyword, other.finallyKeyword) &&
+        isEqualNodes(node.finallyBlock, other.finallyBlock);
+  }
+
+  @override
+  bool visitTypeArgumentList(TypeArgumentList node) {
+    TypeArgumentList other = _other as TypeArgumentList;
+    return isEqualTokens(node.leftBracket, other.leftBracket) &&
+        _isEqualNodeLists(node.arguments, other.arguments) &&
+        isEqualTokens(node.rightBracket, other.rightBracket);
+  }
+
+  @override
+  bool visitTypeName(TypeName node) {
+    TypeName other = _other as TypeName;
+    return isEqualNodes(node.name, other.name) &&
+        isEqualNodes(node.typeArguments, other.typeArguments);
+  }
+
+  @override
+  bool visitTypeParameter(TypeParameter node) {
+    TypeParameter other = _other as TypeParameter;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualNodes(node.name, other.name) &&
+        isEqualTokens(node.extendsKeyword, other.extendsKeyword) &&
+        isEqualNodes(node.bound, other.bound);
+  }
+
+  @override
+  bool visitTypeParameterList(TypeParameterList node) {
+    TypeParameterList other = _other as TypeParameterList;
+    return isEqualTokens(node.leftBracket, other.leftBracket) &&
+        _isEqualNodeLists(node.typeParameters, other.typeParameters) &&
+        isEqualTokens(node.rightBracket, other.rightBracket);
+  }
+
+  @override
+  bool visitVariableDeclaration(VariableDeclaration node) {
+    VariableDeclaration other = _other as VariableDeclaration;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualNodes(node.name, other.name) &&
+        isEqualTokens(node.equals, other.equals) &&
+        isEqualNodes(node.initializer, other.initializer);
+  }
+
+  @override
+  bool visitVariableDeclarationList(VariableDeclarationList node) {
+    VariableDeclarationList other = _other as VariableDeclarationList;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.keyword, other.keyword) &&
+        isEqualNodes(node.type, other.type) &&
+        _isEqualNodeLists(node.variables, other.variables);
+  }
+
+  @override
+  bool visitVariableDeclarationStatement(VariableDeclarationStatement node) {
+    VariableDeclarationStatement other = _other as VariableDeclarationStatement;
+    return isEqualNodes(node.variables, other.variables) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
+  bool visitWhileStatement(WhileStatement node) {
+    WhileStatement other = _other as WhileStatement;
+    return isEqualTokens(node.whileKeyword, other.whileKeyword) &&
+        isEqualTokens(node.leftParenthesis, other.leftParenthesis) &&
+        isEqualNodes(node.condition, other.condition) &&
+        isEqualTokens(node.rightParenthesis, other.rightParenthesis) &&
+        isEqualNodes(node.body, other.body);
+  }
+
+  @override
+  bool visitWithClause(WithClause node) {
+    WithClause other = _other as WithClause;
+    return isEqualTokens(node.withKeyword, other.withKeyword) &&
+        _isEqualNodeLists(node.mixinTypes, other.mixinTypes);
+  }
+
+  @override
+  bool visitYieldStatement(YieldStatement node) {
+    YieldStatement other = _other as YieldStatement;
+    return isEqualTokens(node.yieldKeyword, other.yieldKeyword) &&
+        isEqualNodes(node.expression, other.expression) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  /**
+   * Return `true` if the [first] and [second] lists of AST nodes have the same
+   * size and corresponding elements are equal.
+   */
+  bool _isEqualNodeLists(NodeList first, NodeList second) {
+    if (first == null) {
+      return second == null;
+    } else if (second == null) {
+      return false;
+    }
+    int size = first.length;
+    if (second.length != size) {
+      return false;
+    }
+    for (int i = 0; i < size; i++) {
+      if (!isEqualNodes(first[i], second[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Return `true` if the [first] and [second] lists of tokens have the same
+   * length and corresponding elements are equal.
+   */
+  bool _isEqualTokenLists(List<Token> first, List<Token> second) {
+    int length = first.length;
+    if (second.length != length) {
+      return false;
+    }
+    for (int i = 0; i < length; i++) {
+      if (!isEqualTokens(first[i], second[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Return `true` if the [first] and [second] nodes are equal.
+   */
+  static bool equalNodes(AstNode first, AstNode second) {
+    AstComparator comparator = new AstComparator();
+    return comparator.isEqualNodes(first, second);
+  }
+}
+
+/**
+ * A node in the AST structure for a Dart program.
+ */
+abstract class AstNode {
+  /**
+   * An empty list of AST nodes.
+   */
+  @deprecated // Use "AstNode.EMPTY_LIST"
+  static const List<AstNode> EMPTY_ARRAY = EMPTY_LIST;
+
+  /**
+   * An empty list of AST nodes.
+   */
+  static const List<AstNode> EMPTY_LIST = const <AstNode>[];
+
+  /**
+   * A comparator that can be used to sort AST nodes in lexical order. In other
+   * words, `compare` will return a negative value if the offset of the first
+   * node is less than the offset of the second node, zero (0) if the nodes have
+   * the same offset, and a positive value if the offset of the first node is
+   * greater than the offset of the second node.
+   */
+  static Comparator<AstNode> LEXICAL_ORDER =
+      (AstNode first, AstNode second) => second.offset - first.offset;
+
+  /**
+   * The parent of the node, or `null` if the node is the root of an AST
+   * structure.
+   */
+  AstNode _parent;
+
+  /**
+   * A table mapping the names of properties to their values, or `null` if this
+   * node does not have any properties associated with it.
+   */
+  Map<String, Object> _propertyMap;
+
+  /**
+   * Return the first token included in this node's source range.
+   */
+  Token get beginToken;
+
+  /**
+   * Iterate through all the entities (either AST nodes or tokens) which make
+   * up the contents of this node, including doc comments but excluding other
+   * comments.
+   */
+  Iterable /*<AstNode | Token>*/ get childEntities;
+
+  /**
+   * Return the offset of the character immediately following the last character
+   * of this node's source range. This is equivalent to
+   * `node.getOffset() + node.getLength()`. For a compilation unit this will be
+   * equal to the length of the unit's source. For synthetic nodes this will be
+   * equivalent to the node's offset (because the length is zero (0) by
+   * definition).
+   */
+  int get end => offset + length;
+
+  /**
+   * Return the last token included in this node's source range.
+   */
+  Token get endToken;
+
+  /**
+   * Return `true` if this node is a synthetic node. A synthetic node is a node
+   * that was introduced by the parser in order to recover from an error in the
+   * code. Synthetic nodes always have a length of zero (`0`).
+   */
+  bool get isSynthetic => false;
+
+  /**
+   * Return the number of characters in the node's source range.
+   */
+  int get length {
+    Token beginToken = this.beginToken;
+    Token endToken = this.endToken;
+    if (beginToken == null || endToken == null) {
+      return -1;
+    }
+    return endToken.offset + endToken.length - beginToken.offset;
+  }
+
+  /**
+   * Return the offset from the beginning of the file to the first character in
+   * the node's source range.
+   */
+  int get offset {
+    Token beginToken = this.beginToken;
+    if (beginToken == null) {
+      return -1;
+    }
+    return beginToken.offset;
+  }
+
+  /**
+   * Return this node's parent node, or `null` if this node is the root of an
+   * AST structure.
+   *
+   * Note that the relationship between an AST node and its parent node may
+   * change over the lifetime of a node.
+   */
+  AstNode get parent => _parent;
+
+  /**
+   * Set the parent of this node to the [newParent].
+   */
+  @deprecated // Never intended for public use.
+  void set parent(AstNode newParent) {
+    _parent = newParent;
+  }
+
+  /**
+   * Return the node at the root of this node's AST structure. Note that this
+   * method's performance is linear with respect to the depth of the node in the
+   * AST structure (O(depth)).
+   */
+  AstNode get root {
+    AstNode root = this;
+    AstNode parent = this.parent;
+    while (parent != null) {
+      root = parent;
+      parent = root.parent;
+    }
+    return root;
+  }
+
+  /**
+   * Use the given [visitor] to visit this node. Return the value returned by
+   * the visitor as a result of visiting this node.
+   */
+  /* <E> E */ accept(AstVisitor /*<E>*/ visitor);
+
+  /**
+   * Make this node the parent of the given [child] node. Return the child node.
+   */
+  @deprecated // Never intended for public use.
+  AstNode becomeParentOf(AstNode child) {
+    return _becomeParentOf(child);
+  }
+
+  /**
+   * Return the most immediate ancestor of this node for which the [predicate]
+   * returns `true`, or `null` if there is no such ancestor. Note that this node
+   * will never be returned.
+   */
+  AstNode getAncestor(Predicate<AstNode> predicate) {
+    // TODO(brianwilkerson) It is a bug that this method can return `this`.
+    AstNode node = this;
+    while (node != null && !predicate(node)) {
+      node = node.parent;
+    }
+    return node;
+  }
+
+  /**
+   * Return the value of the property with the given [name], or `null` if this
+   * node does not have a property with the given name.
+   */
+  Object getProperty(String name) {
+    if (_propertyMap == null) {
+      return null;
+    }
+    return _propertyMap[name];
+  }
+
+  /**
+   * If the given [child] is not `null`, use the given [visitor] to visit it.
+   */
+  @deprecated // Never intended for public use.
+  void safelyVisitChild(AstNode child, AstVisitor visitor) {
+    if (child != null) {
+      child.accept(visitor);
+    }
+  }
+
+  /**
+   * Set the value of the property with the given [name] to the given [value].
+   * If the value is `null`, the property will effectively be removed.
+   */
+  void setProperty(String name, Object value) {
+    if (value == null) {
+      if (_propertyMap != null) {
+        _propertyMap.remove(name);
+        if (_propertyMap.isEmpty) {
+          _propertyMap = null;
+        }
+      }
+    } else {
+      if (_propertyMap == null) {
+        _propertyMap = new HashMap<String, Object>();
+      }
+      _propertyMap[name] = value;
+    }
+  }
+
+  /**
+   * Return a textual description of this node in a form approximating valid
+   * source. The returned string will not be valid source primarily in the case
+   * where the node itself is not well-formed.
+   */
+  String toSource() {
+    PrintStringWriter writer = new PrintStringWriter();
+    accept(new ToSourceVisitor(writer));
+    return writer.toString();
+  }
+
+  @override
+  String toString() => toSource();
+
+  /**
+   * Use the given [visitor] to visit all of the children of this node. The
+   * children will be visited in lexical order.
+   */
+  void visitChildren(AstVisitor visitor);
+
+  /**
+   * Make this node the parent of the given [child] node. Return the child node.
+   */
+  AstNode _becomeParentOf(AstNode child) {
+    if (child != null) {
+      child._parent = this;
+    }
+    return child;
+  }
+
+  /**
+   * If the given [child] is not `null`, use the given [visitor] to visit it.
+   */
+  void _safelyVisitChild(AstNode child, AstVisitor visitor) {
+    if (child != null) {
+      child.accept(visitor);
+    }
+  }
+}
+
+/**
+ * An object that can be used to visit an AST structure.
+ */
+abstract class AstVisitor<R> {
+  R visitAdjacentStrings(AdjacentStrings node);
+
+  R visitAnnotation(Annotation node);
+
+  R visitArgumentList(ArgumentList node);
+
+  R visitAsExpression(AsExpression node);
+
+  R visitAssertStatement(AssertStatement assertStatement);
+
+  R visitAssignmentExpression(AssignmentExpression node);
+
+  R visitAwaitExpression(AwaitExpression node);
+
+  R visitBinaryExpression(BinaryExpression node);
+
+  R visitBlock(Block node);
+
+  R visitBlockFunctionBody(BlockFunctionBody node);
+
+  R visitBooleanLiteral(BooleanLiteral node);
+
+  R visitBreakStatement(BreakStatement node);
+
+  R visitCascadeExpression(CascadeExpression node);
+
+  R visitCatchClause(CatchClause node);
+
+  R visitClassDeclaration(ClassDeclaration node);
+
+  R visitClassTypeAlias(ClassTypeAlias node);
+
+  R visitComment(Comment node);
+
+  R visitCommentReference(CommentReference node);
+
+  R visitCompilationUnit(CompilationUnit node);
+
+  R visitConditionalExpression(ConditionalExpression node);
+
+  R visitConstructorDeclaration(ConstructorDeclaration node);
+
+  R visitConstructorFieldInitializer(ConstructorFieldInitializer node);
+
+  R visitConstructorName(ConstructorName node);
+
+  R visitContinueStatement(ContinueStatement node);
+
+  R visitDeclaredIdentifier(DeclaredIdentifier node);
+
+  R visitDefaultFormalParameter(DefaultFormalParameter node);
+
+  R visitDoStatement(DoStatement node);
+
+  R visitDoubleLiteral(DoubleLiteral node);
+
+  R visitEmptyFunctionBody(EmptyFunctionBody node);
+
+  R visitEmptyStatement(EmptyStatement node);
+
+  R visitEnumConstantDeclaration(EnumConstantDeclaration node);
+
+  R visitEnumDeclaration(EnumDeclaration node);
+
+  R visitExportDirective(ExportDirective node);
+
+  R visitExpressionFunctionBody(ExpressionFunctionBody node);
+
+  R visitExpressionStatement(ExpressionStatement node);
+
+  R visitExtendsClause(ExtendsClause node);
+
+  R visitFieldDeclaration(FieldDeclaration node);
+
+  R visitFieldFormalParameter(FieldFormalParameter node);
+
+  R visitForEachStatement(ForEachStatement node);
+
+  R visitFormalParameterList(FormalParameterList node);
+
+  R visitForStatement(ForStatement node);
+
+  R visitFunctionDeclaration(FunctionDeclaration node);
+
+  R visitFunctionDeclarationStatement(FunctionDeclarationStatement node);
+
+  R visitFunctionExpression(FunctionExpression node);
+
+  R visitFunctionExpressionInvocation(FunctionExpressionInvocation node);
+
+  R visitFunctionTypeAlias(FunctionTypeAlias functionTypeAlias);
+
+  R visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node);
+
+  R visitHideCombinator(HideCombinator node);
+
+  R visitIfStatement(IfStatement node);
+
+  R visitImplementsClause(ImplementsClause node);
+
+  R visitImportDirective(ImportDirective node);
+
+  R visitIndexExpression(IndexExpression node);
+
+  R visitInstanceCreationExpression(InstanceCreationExpression node);
+
+  R visitIntegerLiteral(IntegerLiteral node);
+
+  R visitInterpolationExpression(InterpolationExpression node);
+
+  R visitInterpolationString(InterpolationString node);
+
+  R visitIsExpression(IsExpression node);
+
+  R visitLabel(Label node);
+
+  R visitLabeledStatement(LabeledStatement node);
+
+  R visitLibraryDirective(LibraryDirective node);
+
+  R visitLibraryIdentifier(LibraryIdentifier node);
+
+  R visitListLiteral(ListLiteral node);
+
+  R visitMapLiteral(MapLiteral node);
+
+  R visitMapLiteralEntry(MapLiteralEntry node);
+
+  R visitMethodDeclaration(MethodDeclaration node);
+
+  R visitMethodInvocation(MethodInvocation node);
+
+  R visitNamedExpression(NamedExpression node);
+
+  R visitNativeClause(NativeClause node);
+
+  R visitNativeFunctionBody(NativeFunctionBody node);
+
+  R visitNullLiteral(NullLiteral node);
+
+  R visitParenthesizedExpression(ParenthesizedExpression node);
+
+  R visitPartDirective(PartDirective node);
+
+  R visitPartOfDirective(PartOfDirective node);
+
+  R visitPostfixExpression(PostfixExpression node);
+
+  R visitPrefixedIdentifier(PrefixedIdentifier node);
+
+  R visitPrefixExpression(PrefixExpression node);
+
+  R visitPropertyAccess(PropertyAccess node);
+
+  R visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node);
+
+  R visitRethrowExpression(RethrowExpression node);
+
+  R visitReturnStatement(ReturnStatement node);
+
+  R visitScriptTag(ScriptTag node);
+
+  R visitShowCombinator(ShowCombinator node);
+
+  R visitSimpleFormalParameter(SimpleFormalParameter node);
+
+  R visitSimpleIdentifier(SimpleIdentifier node);
+
+  R visitSimpleStringLiteral(SimpleStringLiteral node);
+
+  R visitStringInterpolation(StringInterpolation node);
+
+  R visitSuperConstructorInvocation(SuperConstructorInvocation node);
+
+  R visitSuperExpression(SuperExpression node);
+
+  R visitSwitchCase(SwitchCase node);
+
+  R visitSwitchDefault(SwitchDefault node);
+
+  R visitSwitchStatement(SwitchStatement node);
+
+  R visitSymbolLiteral(SymbolLiteral node);
+
+  R visitThisExpression(ThisExpression node);
+
+  R visitThrowExpression(ThrowExpression node);
+
+  R visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node);
+
+  R visitTryStatement(TryStatement node);
+
+  R visitTypeArgumentList(TypeArgumentList node);
+
+  R visitTypeName(TypeName node);
+
+  R visitTypeParameter(TypeParameter node);
+
+  R visitTypeParameterList(TypeParameterList node);
+
+  R visitVariableDeclaration(VariableDeclaration node);
+
+  R visitVariableDeclarationList(VariableDeclarationList node);
+
+  R visitVariableDeclarationStatement(VariableDeclarationStatement node);
+
+  R visitWhileStatement(WhileStatement node);
+
+  R visitWithClause(WithClause node);
+
+  R visitYieldStatement(YieldStatement node);
+}
+
+/**
+ * An await expression.
+ *
+ * > awaitExpression ::=
+ * >     'await' [Expression]
+ */
+class AwaitExpression extends Expression {
+  /**
+   * The 'await' keyword.
+   */
+  Token awaitKeyword;
+
+  /**
+   * The expression whose value is being waited on.
+   */
+  Expression _expression;
+
+  /**
+   * Initialize a newly created await expression.
+   */
+  AwaitExpression(this.awaitKeyword, Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get beginToken {
+    if (awaitKeyword != null) {
+      return awaitKeyword;
+    }
+    return _expression.beginToken;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(awaitKeyword)
+    ..add(_expression);
+
+  @override
+  Token get endToken => _expression.endToken;
+
+  /**
+   * Return the expression whose value is being waited on.
+   */
+  Expression get expression => _expression;
+
+  /**
+   * Set the expression whose value is being waited on to the given [expression].
+   */
+  void set expression(Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  int get precedence => 0;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitAwaitExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_expression, visitor);
+  }
+}
+
+/**
+ * A binary (infix) expression.
+ *
+ * > binaryExpression ::=
+ * >     [Expression] [Token] [Expression]
+ */
+class BinaryExpression extends Expression {
+  /**
+   * The expression used to compute the left operand.
+   */
+  Expression _leftOperand;
+
+  /**
+   * The binary operator being applied.
+   */
+  Token operator;
+
+  /**
+   * The expression used to compute the right operand.
+   */
+  Expression _rightOperand;
+
+  /**
+   * The element associated with the operator based on the static type of the
+   * left operand, or `null` if the AST structure has not been resolved, if the
+   * operator is not user definable, or if the operator could not be resolved.
+   */
+  MethodElement staticElement;
+
+  /**
+   * The element associated with the operator based on the propagated type of
+   * the left operand, or `null` if the AST structure has not been resolved, if
+   * the operator is not user definable, or if the operator could not be
+   * resolved.
+   */
+  MethodElement propagatedElement;
+
+  /**
+   * Initialize a newly created binary expression.
+   */
+  BinaryExpression(
+      Expression leftOperand, this.operator, Expression rightOperand) {
+    _leftOperand = _becomeParentOf(leftOperand);
+    _rightOperand = _becomeParentOf(rightOperand);
+  }
+
+  @override
+  Token get beginToken => _leftOperand.beginToken;
+
+  /**
+   * Return the best element available for this operator. If resolution was able
+   * to find a better element based on type propagation, that element will be
+   * returned. Otherwise, the element found using the result of static analysis
+   * will be returned. If resolution has not been performed, then `null` will be
+   * returned.
+   */
+  MethodElement get bestElement {
+    MethodElement element = propagatedElement;
+    if (element == null) {
+      element = staticElement;
+    }
+    return element;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_leftOperand)
+    ..add(operator)
+    ..add(_rightOperand);
+
+  @override
+  Token get endToken => _rightOperand.endToken;
+
+  /**
+   * Return the expression used to compute the left operand.
+   */
+  Expression get leftOperand => _leftOperand;
+
+  /**
+   * Set the expression used to compute the left operand to the given
+   * [expression].
+   */
+  void set leftOperand(Expression expression) {
+    _leftOperand = _becomeParentOf(expression);
+  }
+
+  @override
+  int get precedence => operator.type.precedence;
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on propagated type information, then return the parameter
+   * element representing the parameter to which the value of the right operand
+   * will be bound. Otherwise, return `null`.
+   */
+  @deprecated // Use "expression.propagatedParameterElement"
+  ParameterElement get propagatedParameterElementForRightOperand {
+    return _propagatedParameterElementForRightOperand;
+  }
+
+  /**
+   * Return the expression used to compute the right operand.
+   */
+  Expression get rightOperand => _rightOperand;
+
+  /**
+   * Set the expression used to compute the right operand to the given
+   * [expression].
+   */
+  void set rightOperand(Expression expression) {
+    _rightOperand = _becomeParentOf(expression);
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on static type information, then return the parameter element
+   * representing the parameter to which the value of the right operand will be
+   * bound. Otherwise, return `null`.
+   */
+  @deprecated // Use "expression.staticParameterElement"
+  ParameterElement get staticParameterElementForRightOperand {
+    return _staticParameterElementForRightOperand;
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on propagated type information, then return the parameter
+   * element representing the parameter to which the value of the right operand
+   * will be bound. Otherwise, return `null`.
+   */
+  ParameterElement get _propagatedParameterElementForRightOperand {
+    if (propagatedElement == null) {
+      return null;
+    }
+    List<ParameterElement> parameters = propagatedElement.parameters;
+    if (parameters.length < 1) {
+      return null;
+    }
+    return parameters[0];
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on static type information, then return the parameter element
+   * representing the parameter to which the value of the right operand will be
+   * bound. Otherwise, return `null`.
+   */
+  ParameterElement get _staticParameterElementForRightOperand {
+    if (staticElement == null) {
+      return null;
+    }
+    List<ParameterElement> parameters = staticElement.parameters;
+    if (parameters.length < 1) {
+      return null;
+    }
+    return parameters[0];
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitBinaryExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_leftOperand, visitor);
+    _safelyVisitChild(_rightOperand, visitor);
+  }
+}
+
+/**
+ * A sequence of statements.
+ *
+ * > block ::=
+ * >     '{' statement* '}'
+ */
+class Block extends Statement {
+  /**
+   * The left curly bracket.
+   */
+  Token leftBracket;
+
+  /**
+   * The statements contained in the block.
+   */
+  NodeList<Statement> _statements;
+
+  /**
+   * The right curly bracket.
+   */
+  Token rightBracket;
+
+  /**
+   * Initialize a newly created block of code.
+   */
+  Block(this.leftBracket, List<Statement> statements, this.rightBracket) {
+    _statements = new NodeList<Statement>(this, statements);
+  }
+
+  @override
+  Token get beginToken => leftBracket;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(leftBracket)
+    ..addAll(_statements)
+    ..add(rightBracket);
+
+  @override
+  Token get endToken => rightBracket;
+
+  /**
+   * Return the statements contained in the block.
+   */
+  NodeList<Statement> get statements => _statements;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitBlock(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _statements.accept(visitor);
+  }
+}
+
+/**
+ * A function body that consists of a block of statements.
+ *
+ * > blockFunctionBody ::=
+ * >     ('async' | 'async' '*' | 'sync' '*')? [Block]
+ */
+class BlockFunctionBody extends FunctionBody {
+  /**
+   * The token representing the 'async' or 'sync' keyword, or `null` if there is
+   * no such keyword.
+   */
+  Token keyword;
+
+  /**
+   * The star optionally following the 'async' or 'sync' keyword, or `null` if
+   * there is wither no such keyword or no star.
+   */
+  Token star;
+
+  /**
+   * The block representing the body of the function.
+   */
+  Block _block;
+
+  /**
+   * Initialize a newly created function body consisting of a block of
+   * statements. The [keyword] can be `null` if there is no keyword specified
+   * for the block. The [star] can be `null` if there is no star following the
+   * keyword (and must be `null` if there is no keyword).
+   */
+  BlockFunctionBody(this.keyword, this.star, Block block) {
+    _block = _becomeParentOf(block);
+  }
+
+  @override
+  Token get beginToken => _block.beginToken;
+
+  /**
+   * Return the block representing the body of the function.
+   */
+  Block get block => _block;
+
+  /**
+   * Set the block representing the body of the function to the given [block].
+   */
+  void set block(Block block) {
+    _block = _becomeParentOf(block);
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(keyword)
+    ..add(star)
+    ..add(_block);
+
+  @override
+  Token get endToken => _block.endToken;
+
+  @override
+  bool get isAsynchronous => keyword != null && keyword.lexeme == Parser.ASYNC;
+
+  @override
+  bool get isGenerator => star != null;
+
+  @override
+  bool get isSynchronous => keyword == null || keyword.lexeme != Parser.ASYNC;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitBlockFunctionBody(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_block, visitor);
+  }
+}
+
+/**
+ * A boolean literal expression.
+ *
+ * > booleanLiteral ::=
+ * >     'false' | 'true'
+ */
+class BooleanLiteral extends Literal {
+  /**
+   * The token representing the literal.
+   */
+  Token literal;
+
+  /**
+   * The value of the literal.
+   */
+  bool value = false;
+
+  /**
+   * Initialize a newly created boolean literal.
+   */
+  BooleanLiteral(this.literal, this.value);
+
+  @override
+  Token get beginToken => literal;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..add(literal);
+
+  @override
+  Token get endToken => literal;
+
+  @override
+  bool get isSynthetic => literal.isSynthetic;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitBooleanLiteral(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    // There are no children to visit.
+  }
+}
+
+/**
+ * An AST visitor that will recursively visit all of the nodes in an AST
+ * structure, similar to [GeneralizingAstVisitor]. This visitor uses a
+ * breadth-first ordering rather than the depth-first ordering of
+ * [GeneralizingAstVisitor].
+ *
+ * Subclasses that override a visit method must either invoke the overridden
+ * visit method or explicitly invoke the more general visit method. Failure to
+ * do so will cause the visit methods for superclasses of the node to not be
+ * invoked and will cause the children of the visited node to not be visited.
+ *
+ * In addition, subclasses should <b>not</b> explicitly visit the children of a
+ * node, but should ensure that the method [visitNode] is used to visit the
+ * children (either directly or indirectly). Failure to do will break the order
+ * in which nodes are visited.
+ */
+class BreadthFirstVisitor<R> extends GeneralizingAstVisitor<R> {
+  /**
+   * A queue holding the nodes that have not yet been visited in the order in
+   * which they ought to be visited.
+   */
+  Queue<AstNode> _queue = new Queue<AstNode>();
+
+  /**
+   * A visitor, used to visit the children of the current node, that will add
+   * the nodes it visits to the [_queue].
+   */
+  GeneralizingAstVisitor<Object> _childVisitor;
+
+  /**
+   * Initialize a newly created visitor.
+   */
+  BreadthFirstVisitor() {
+    _childVisitor = new GeneralizingAstVisitor_BreadthFirstVisitor(this);
+  }
+
+  /**
+   * Visit all nodes in the tree starting at the given [root] node, in
+   * breadth-first order.
+   */
+  void visitAllNodes(AstNode root) {
+    _queue.add(root);
+    while (!_queue.isEmpty) {
+      AstNode next = _queue.removeFirst();
+      next.accept(this);
+    }
+  }
+
+  @override
+  R visitNode(AstNode node) {
+    node.visitChildren(_childVisitor);
+    return null;
+  }
+}
+
+/**
+ * A break statement.
+ *
+ * > breakStatement ::=
+ * >     'break' [SimpleIdentifier]? ';'
+ */
+class BreakStatement extends Statement {
+  /**
+   * The token representing the 'break' keyword.
+   */
+  Token breakKeyword;
+
+  /**
+   * The label associated with the statement, or `null` if there is no label.
+   */
+  SimpleIdentifier _label;
+
+  /**
+   * The semicolon terminating the statement.
+   */
+  Token semicolon;
+
+  /**
+   * The AstNode which this break statement is breaking from.  This will be
+   * either a [Statement] (in the case of breaking out of a loop), a
+   * [SwitchMember] (in the case of a labeled break statement whose label
+   * matches a label on a switch case in an enclosing switch statement), or
+   * `null` if the AST has not yet been resolved or if the target could not be
+   * resolved. Note that if the source code has errors, the target might be
+   * invalid (e.g. trying to break to a switch case).
+   */
+  AstNode target;
+
+  /**
+   * Initialize a newly created break statement. The [label] can be `null` if
+   * there is no label associated with the statement.
+   */
+  BreakStatement(this.breakKeyword, SimpleIdentifier label, this.semicolon) {
+    _label = _becomeParentOf(label);
+  }
+
+  @override
+  Token get beginToken => breakKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(breakKeyword)
+    ..add(_label)
+    ..add(semicolon);
+
+  @override
+  Token get endToken => semicolon;
+
+  /**
+   * Return the token representing the 'break' keyword.
+   */
+  @deprecated // Use "this.breakKeyword"
+  Token get keyword => breakKeyword;
+
+  /**
+   * Sethe token representing the 'break' keyword to the given [token].
+   */
+  @deprecated // Use "this.breakKeyword"
+  void set keyword(Token token) {
+    breakKeyword = token;
+  }
+
+  /**
+   * Return the label associated with the statement, or `null` if there is no
+   * label.
+   */
+  SimpleIdentifier get label => _label;
+
+  /**
+   * Set the label associated with the statement to the given [identifier].
+   */
+  void set label(SimpleIdentifier identifier) {
+    _label = _becomeParentOf(identifier);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitBreakStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_label, visitor);
+  }
+}
+
+/**
+ * A sequence of cascaded expressions: expressions that share a common target.
+ * There are three kinds of expressions that can be used in a cascade
+ * expression: [IndexExpression], [MethodInvocation] and [PropertyAccess].
+ *
+ * > cascadeExpression ::=
+ * >     [Expression] cascadeSection*
+ * >
+ * > cascadeSection ::=
+ * >     '..'  (cascadeSelector arguments*) (assignableSelector arguments*)*
+ * >     (assignmentOperator expressionWithoutCascade)?
+ * >
+ * > cascadeSelector ::=
+ * >     '[ ' expression '] '
+ * >   | identifier
+ */
+class CascadeExpression extends Expression {
+  /**
+   * The target of the cascade sections.
+   */
+  Expression _target;
+
+  /**
+   * The cascade sections sharing the common target.
+   */
+  NodeList<Expression> _cascadeSections;
+
+  /**
+   * Initialize a newly created cascade expression. The list of
+   * [cascadeSections] must contain at least one element.
+   */
+  CascadeExpression(Expression target, List<Expression> cascadeSections) {
+    _target = _becomeParentOf(target);
+    _cascadeSections = new NodeList<Expression>(this, cascadeSections);
+  }
+
+  @override
+  Token get beginToken => _target.beginToken;
+
+  /**
+   * Return the cascade sections sharing the common target.
+   */
+  NodeList<Expression> get cascadeSections => _cascadeSections;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_target)
+    ..addAll(_cascadeSections);
+
+  @override
+  Token get endToken => _cascadeSections.endToken;
+
+  @override
+  int get precedence => 2;
+
+  /**
+   * Return the target of the cascade sections.
+   */
+  Expression get target => _target;
+
+  /**
+   * Set the target of the cascade sections to the given [expression].
+   */
+  void set target(Expression target) {
+    _target = _becomeParentOf(target);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitCascadeExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_target, visitor);
+    _cascadeSections.accept(visitor);
+  }
+}
+
+/**
+ * A catch clause within a try statement.
+ *
+ * > onPart ::=
+ * >     catchPart [Block]
+ * >   | 'on' type catchPart? [Block]
+ * >
+ * > catchPart ::=
+ * >     'catch' '(' [SimpleIdentifier] (',' [SimpleIdentifier])? ')'
+ */
+class CatchClause extends AstNode {
+  /**
+   * The token representing the 'on' keyword, or `null` if there is no 'on'
+   * keyword.
+   */
+  Token onKeyword;
+
+  /**
+   * The type of exceptions caught by this catch clause, or `null` if this catch
+   * clause catches every type of exception.
+   */
+  TypeName _exceptionType;
+
+  /**
+   * The token representing the 'catch' keyword, or `null` if there is no
+   * 'catch' keyword.
+   */
+  Token catchKeyword;
+
+  /**
+   * The left parenthesis, or `null` if there is no 'catch' keyword.
+   */
+  Token leftParenthesis;
+
+  /**
+   * The parameter whose value will be the exception that was thrown, or `null`
+   * if there is no 'catch' keyword.
+   */
+  SimpleIdentifier _exceptionParameter;
+
+  /**
+   * The comma separating the exception parameter from the stack trace
+   * parameter, or `null` if there is no stack trace parameter.
+   */
+  Token comma;
+
+  /**
+   * The parameter whose value will be the stack trace associated with the
+   * exception, or `null` if there is no stack trace parameter.
+   */
+  SimpleIdentifier _stackTraceParameter;
+
+  /**
+   * The right parenthesis, or `null` if there is no 'catch' keyword.
+   */
+  Token rightParenthesis;
+
+  /**
+   * The body of the catch block.
+   */
+  Block _body;
+
+  /**
+   * Initialize a newly created catch clause. The [onKeyword] and
+   * [exceptionType] can be `null` if the clause will catch all exceptions. The
+   * [comma] and [stackTraceParameter] can be `null` if the stack trace is not
+   * referencable within the body.
+   */
+  CatchClause(this.onKeyword, TypeName exceptionType, this.catchKeyword,
+      this.leftParenthesis, SimpleIdentifier exceptionParameter, this.comma,
+      SimpleIdentifier stackTraceParameter, this.rightParenthesis, Block body) {
+    _exceptionType = _becomeParentOf(exceptionType);
+    _exceptionParameter = _becomeParentOf(exceptionParameter);
+    _stackTraceParameter = _becomeParentOf(stackTraceParameter);
+    _body = _becomeParentOf(body);
+  }
+
+  @override
+  Token get beginToken {
+    if (onKeyword != null) {
+      return onKeyword;
+    }
+    return catchKeyword;
+  }
+
+  /**
+   * Return the body of the catch block.
+   */
+  Block get body => _body;
+
+  /**
+   * Set the body of the catch block to the given [block].
+   */
+  void set body(Block block) {
+    _body = _becomeParentOf(block);
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(onKeyword)
+    ..add(_exceptionType)
+    ..add(catchKeyword)
+    ..add(leftParenthesis)
+    ..add(_exceptionParameter)
+    ..add(comma)
+    ..add(_stackTraceParameter)
+    ..add(rightParenthesis)
+    ..add(_body);
+
+  @override
+  Token get endToken => _body.endToken;
+
+  /**
+   * Return the parameter whose value will be the exception that was thrown, or
+   * `null` if there is no 'catch' keyword.
+   */
+  SimpleIdentifier get exceptionParameter => _exceptionParameter;
+
+  /**
+   * Set the parameter whose value will be the exception that was thrown to the
+   * given [parameter].
+   */
+  void set exceptionParameter(SimpleIdentifier parameter) {
+    _exceptionParameter = _becomeParentOf(parameter);
+  }
+
+  /**
+   * Return the type of exceptions caught by this catch clause, or `null` if
+   * this catch clause catches every type of exception.
+   */
+  TypeName get exceptionType => _exceptionType;
+
+  /**
+   * Set the type of exceptions caught by this catch clause to the given
+   * [exceptionType].
+   */
+  void set exceptionType(TypeName exceptionType) {
+    _exceptionType = _becomeParentOf(exceptionType);
+  }
+
+  /**
+   * Return the parameter whose value will be the stack trace associated with
+   * the exception, or `null` if there is no stack trace parameter.
+   */
+  SimpleIdentifier get stackTraceParameter => _stackTraceParameter;
+
+  /**
+   * Set the parameter whose value will be the stack trace associated with the
+   * exception to the given [parameter].
+   */
+  void set stackTraceParameter(SimpleIdentifier parameter) {
+    _stackTraceParameter = _becomeParentOf(parameter);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitCatchClause(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_exceptionType, visitor);
+    _safelyVisitChild(_exceptionParameter, visitor);
+    _safelyVisitChild(_stackTraceParameter, visitor);
+    _safelyVisitChild(_body, visitor);
+  }
+}
+
+/**
+ * Helper class to allow iteration of child entities of an AST node.
+ */
+class ChildEntities extends Object with IterableMixin implements Iterable {
+  /**
+   * The list of child entities to be iterated over.
+   */
+  List _entities = [];
+
+  @override
+  Iterator get iterator => _entities.iterator;
+
+  /**
+   * Add an AST node or token as the next child entity, if it is not null.
+   */
+  void add(entity) {
+    if (entity != null) {
+      assert(entity is Token || entity is AstNode);
+      _entities.add(entity);
+    }
+  }
+
+  /**
+   * Add the given items as the next child entities, if [items] is not null.
+   */
+  void addAll(Iterable items) {
+    if (items != null) {
+      _entities.addAll(items);
+    }
+  }
+}
+
+/**
+ * The declaration of a class.
+ *
+ * > classDeclaration ::=
+ * >     'abstract'? 'class' [SimpleIdentifier] [TypeParameterList]?
+ * >     ([ExtendsClause] [WithClause]?)?
+ * >     [ImplementsClause]?
+ * >     '{' [ClassMember]* '}'
+ */
+class ClassDeclaration extends NamedCompilationUnitMember {
+  /**
+   * The 'abstract' keyword, or `null` if the keyword was absent.
+   */
+  Token abstractKeyword;
+
+  /**
+   * The token representing the 'class' keyword.
+   */
+  Token classKeyword;
+
+  /**
+   * The type parameters for the class, or `null` if the class does not have any
+   * type parameters.
+   */
+  TypeParameterList _typeParameters;
+
+  /**
+   * The extends clause for the class, or `null` if the class does not extend
+   * any other class.
+   */
+  ExtendsClause _extendsClause;
+
+  /**
+   * The with clause for the class, or `null` if the class does not have a with
+   * clause.
+   */
+  WithClause _withClause;
+
+  /**
+   * The implements clause for the class, or `null` if the class does not
+   * implement any interfaces.
+   */
+  ImplementsClause _implementsClause;
+
+  /**
+   * The native clause for the class, or `null` if the class does not have a
+   * native clause.
+   */
+  NativeClause _nativeClause;
+
+  /**
+   * The left curly bracket.
+   */
+  Token leftBracket;
+
+  /**
+   * The members defined by the class.
+   */
+  NodeList<ClassMember> _members;
+
+  /**
+   * The right curly bracket.
+   */
+  Token rightBracket;
+
+  /**
+   * Initialize a newly created class declaration. Either or both of the
+   * [comment] and [metadata] can be `null` if the class does not have the
+   * corresponding attribute. The [abstractKeyword] can be `null` if the class
+   * is not abstract. The [typeParameters] can be `null` if the class does not
+   * have any type parameters. Any or all of the [extendsClause], [withClause],
+   * and [implementsClause] can be `null` if the class does not have the
+   * corresponding clause. The list of [members] can be `null` if the class does
+   * not have any members.
+   */
+  ClassDeclaration(Comment comment, List<Annotation> metadata,
+      this.abstractKeyword, this.classKeyword, SimpleIdentifier name,
+      TypeParameterList typeParameters, ExtendsClause extendsClause,
+      WithClause withClause, ImplementsClause implementsClause,
+      this.leftBracket, List<ClassMember> members, this.rightBracket)
+      : super(comment, metadata, name) {
+    _typeParameters = _becomeParentOf(typeParameters);
+    _extendsClause = _becomeParentOf(extendsClause);
+    _withClause = _becomeParentOf(withClause);
+    _implementsClause = _becomeParentOf(implementsClause);
+    _members = new NodeList<ClassMember>(this, members);
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(abstractKeyword)
+    ..add(classKeyword)
+    ..add(_name)
+    ..add(_typeParameters)
+    ..add(_extendsClause)
+    ..add(_withClause)
+    ..add(_implementsClause)
+    ..add(_nativeClause)
+    ..add(leftBracket)
+    ..addAll(members)
+    ..add(rightBracket);
+
+  @override
+  ClassElement get element =>
+      _name != null ? (_name.staticElement as ClassElement) : null;
+
+  @override
+  Token get endToken => rightBracket;
+
+  /**
+   * Return the extends clause for this class, or `null` if the class does not
+   * extend any other class.
+   */
+  ExtendsClause get extendsClause => _extendsClause;
+
+  /**
+   * Set the extends clause for this class to the given [extendsClause].
+   */
+  void set extendsClause(ExtendsClause extendsClause) {
+    _extendsClause = _becomeParentOf(extendsClause);
+  }
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata {
+    if (abstractKeyword != null) {
+      return abstractKeyword;
+    }
+    return classKeyword;
+  }
+
+  /**
+   * Return the implements clause for the class, or `null` if the class does not
+   * implement any interfaces.
+   */
+  ImplementsClause get implementsClause => _implementsClause;
+
+  /**
+   * Set the implements clause for the class to the given [implementsClause].
+   */
+  void set implementsClause(ImplementsClause implementsClause) {
+    _implementsClause = _becomeParentOf(implementsClause);
+  }
+
+  /**
+   * Return `true` if this class is declared to be an abstract class.
+   */
+  bool get isAbstract => abstractKeyword != null;
+
+  /**
+   * Return the members defined by the class.
+   */
+  NodeList<ClassMember> get members => _members;
+
+  /**
+   * Return the native clause for this class, or `null` if the class does not
+   * have a native clause.
+   */
+  NativeClause get nativeClause => _nativeClause;
+
+  /**
+   * Set the native clause for this class to the given [nativeClause].
+   */
+  void set nativeClause(NativeClause nativeClause) {
+    _nativeClause = _becomeParentOf(nativeClause);
+  }
+
+  /**
+   * Return the type parameters for the class, or `null` if the class does not
+   * have any type parameters.
+   */
+  TypeParameterList get typeParameters => _typeParameters;
+
+  /**
+   * Set the type parameters for the class to the given list of [typeParameters].
+   */
+  void set typeParameters(TypeParameterList typeParameters) {
+    _typeParameters = _becomeParentOf(typeParameters);
+  }
+
+  /**
+   * Return the with clause for the class, or `null` if the class does not have
+   * a with clause.
+   */
+  WithClause get withClause => _withClause;
+
+  /**
+   * Set the with clause for the class to the given [withClause].
+   */
+  void set withClause(WithClause withClause) {
+    _withClause = _becomeParentOf(withClause);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitClassDeclaration(this);
+
+  /**
+   * Return the constructor declared in the class with the given [name], or
+   * `null` if there is no such constructor. If the [name] is `null` then the
+   * default constructor will be searched for.
+   */
+  ConstructorDeclaration getConstructor(String name) {
+    for (ClassMember classMember in _members) {
+      if (classMember is ConstructorDeclaration) {
+        ConstructorDeclaration constructor = classMember;
+        SimpleIdentifier constructorName = constructor.name;
+        if (name == null && constructorName == null) {
+          return constructor;
+        }
+        if (constructorName != null && constructorName.name == name) {
+          return constructor;
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the field declared in the class with the given [name], or `null` if
+   * there is no such field.
+   */
+  VariableDeclaration getField(String name) {
+    for (ClassMember classMember in _members) {
+      if (classMember is FieldDeclaration) {
+        FieldDeclaration fieldDeclaration = classMember;
+        NodeList<VariableDeclaration> fields =
+            fieldDeclaration.fields.variables;
+        for (VariableDeclaration field in fields) {
+          SimpleIdentifier fieldName = field.name;
+          if (fieldName != null && name == fieldName.name) {
+            return field;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the method declared in the class with the given [name], or `null` if
+   * there is no such method.
+   */
+  MethodDeclaration getMethod(String name) {
+    for (ClassMember classMember in _members) {
+      if (classMember is MethodDeclaration) {
+        MethodDeclaration method = classMember;
+        SimpleIdentifier methodName = method.name;
+        if (methodName != null && name == methodName.name) {
+          return method;
+        }
+      }
+    }
+    return null;
+  }
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_name, visitor);
+    _safelyVisitChild(_typeParameters, visitor);
+    _safelyVisitChild(_extendsClause, visitor);
+    _safelyVisitChild(_withClause, visitor);
+    _safelyVisitChild(_implementsClause, visitor);
+    _safelyVisitChild(_nativeClause, visitor);
+    members.accept(visitor);
+  }
+}
+
+/**
+ * A node that declares a name within the scope of a class.
+ */
+abstract class ClassMember extends Declaration {
+  /**
+   * Initialize a newly created member of a class. Either or both of the
+   * [comment] and [metadata] can be `null` if the member does not have the
+   * corresponding attribute.
+   */
+  ClassMember(Comment comment, List<Annotation> metadata)
+      : super(comment, metadata);
+}
+
+/**
+ * A class type alias.
+ *
+ * > classTypeAlias ::=
+ * >     [SimpleIdentifier] [TypeParameterList]? '=' 'abstract'? mixinApplication
+ * >
+ * > mixinApplication ::=
+ * >     [TypeName] [WithClause] [ImplementsClause]? ';'
+ */
+class ClassTypeAlias extends TypeAlias {
+  /**
+   * The type parameters for the class, or `null` if the class does not have any
+   * type parameters.
+   */
+  TypeParameterList _typeParameters;
+
+  /**
+   * The token for the '=' separating the name from the definition.
+   */
+  Token equals;
+
+  /**
+   * The token for the 'abstract' keyword, or `null` if this is not defining an
+   * abstract class.
+   */
+  Token abstractKeyword;
+
+  /**
+   * The name of the superclass of the class being declared.
+   */
+  TypeName _superclass;
+
+  /**
+   * The with clause for this class.
+   */
+  WithClause _withClause;
+
+  /**
+   * The implements clause for this class, or `null` if there is no implements
+   * clause.
+   */
+  ImplementsClause _implementsClause;
+
+  /**
+   * Initialize a newly created class type alias. Either or both of the
+   * [comment] and [metadata] can be `null` if the class type alias does not
+   * have the corresponding attribute. The [typeParameters] can be `null` if the
+   * class does not have any type parameters. The [abstractKeyword] can be
+   * `null` if the class is not abstract. The [implementsClause] can be `null`
+   * if the class does not implement any interfaces.
+   */
+  ClassTypeAlias(Comment comment, List<Annotation> metadata, Token keyword,
+      SimpleIdentifier name, TypeParameterList typeParameters, this.equals,
+      this.abstractKeyword, TypeName superclass, WithClause withClause,
+      ImplementsClause implementsClause, Token semicolon)
+      : super(comment, metadata, keyword, name, semicolon) {
+    _typeParameters = _becomeParentOf(typeParameters);
+    _superclass = _becomeParentOf(superclass);
+    _withClause = _becomeParentOf(withClause);
+    _implementsClause = _becomeParentOf(implementsClause);
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(typedefKeyword)
+    ..add(_name)
+    ..add(_typeParameters)
+    ..add(equals)
+    ..add(abstractKeyword)
+    ..add(_superclass)
+    ..add(_withClause)
+    ..add(_implementsClause)
+    ..add(semicolon);
+
+  @override
+  ClassElement get element =>
+      _name != null ? (_name.staticElement as ClassElement) : null;
+
+  /**
+   * Return the implements clause for this class, or `null` if there is no
+   * implements clause.
+   */
+  ImplementsClause get implementsClause => _implementsClause;
+
+  /**
+   * Set the implements clause for this class to the given [implementsClause].
+   */
+  void set implementsClause(ImplementsClause implementsClause) {
+    _implementsClause = _becomeParentOf(implementsClause);
+  }
+
+  /**
+   * Return `true` if this class is declared to be an abstract class.
+   */
+  bool get isAbstract => abstractKeyword != null;
+
+  /**
+   * Return the name of the superclass of the class being declared.
+   */
+  TypeName get superclass => _superclass;
+
+  /**
+   * Set the name of the superclass of the class being declared to the given
+   * [superclass] name.
+   */
+  void set superclass(TypeName superclass) {
+    _superclass = _becomeParentOf(superclass);
+  }
+
+  /**
+   * Return the type parameters for the class, or `null` if the class does not
+   * have any type parameters.
+   */
+  TypeParameterList get typeParameters => _typeParameters;
+
+  /**
+   * Set the type parameters for the class to the given list of [typeParameters].
+   */
+  void set typeParameters(TypeParameterList typeParameters) {
+    _typeParameters = _becomeParentOf(typeParameters);
+  }
+
+  /**
+   * Return the with clause for this class.
+   */
+  WithClause get withClause => _withClause;
+
+  /**
+   * Set the with clause for this class to the given with [withClause].
+   */
+  void set withClause(WithClause withClause) {
+    _withClause = _becomeParentOf(withClause);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitClassTypeAlias(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_name, visitor);
+    _safelyVisitChild(_typeParameters, visitor);
+    _safelyVisitChild(_superclass, visitor);
+    _safelyVisitChild(_withClause, visitor);
+    _safelyVisitChild(_implementsClause, visitor);
+  }
+}
+
+/**
+ * A combinator associated with an import or export directive.
+ *
+ * > combinator ::=
+ * >     [HideCombinator]
+ * >   | [ShowCombinator]
+ */
+abstract class Combinator extends AstNode {
+  /**
+   * The 'hide' or 'show' keyword specifying what kind of processing is to be
+   * done on the names.
+   */
+  Token keyword;
+
+  /**
+   * Initialize a newly created combinator.
+   */
+  Combinator(this.keyword);
+
+  @override
+  Token get beginToken => keyword;
+}
+
+/**
+ * A comment within the source code.
+ *
+ * > comment ::=
+ * >     endOfLineComment
+ * >   | blockComment
+ * >   | documentationComment
+ * >
+ * > endOfLineComment ::=
+ * >     '//' (CHARACTER - EOL)* EOL
+ * >
+ * > blockComment ::=
+ * >     '/ *' CHARACTER* '&#42;/'
+ * >
+ * > documentationComment ::=
+ * >     '/ **' (CHARACTER | [CommentReference])* '&#42;/'
+ * >   | ('///' (CHARACTER - EOL)* EOL)+
+ */
+class Comment extends AstNode {
+  /**
+   * The tokens representing the comment.
+   */
+  final List<Token> tokens;
+
+  /**
+   * The type of the comment.
+   */
+  final CommentType _type;
+
+  /**
+   * The references embedded within the documentation comment. This list will be
+   * empty unless this is a documentation comment that has references embedded
+   * within it.
+   */
+  NodeList<CommentReference> _references;
+
+  /**
+   * Initialize a newly created comment. The list of [tokens] must contain at
+   * least one token. The [type] is the type of the comment. The list of
+   * [references] can be empty if the comment does not contain any embedded
+   * references.
+   */
+  Comment(this.tokens, this._type, List<CommentReference> references) {
+    _references = new NodeList<CommentReference>(this, references);
+  }
+
+  @override
+  Token get beginToken => tokens[0];
+
+  @override
+  Iterable get childEntities => new ChildEntities()..addAll(tokens);
+
+  @override
+  Token get endToken => tokens[tokens.length - 1];
+
+  /**
+   * Return `true` if this is a block comment.
+   */
+  bool get isBlock => _type == CommentType.BLOCK;
+
+  /**
+   * Return `true` if this is a documentation comment.
+   */
+  bool get isDocumentation => _type == CommentType.DOCUMENTATION;
+
+  /**
+   * Return `true` if this is an end-of-line comment.
+   */
+  bool get isEndOfLine => _type == CommentType.END_OF_LINE;
+
+  /**
+   * Return the references embedded within the documentation comment.
+   */
+  NodeList<CommentReference> get references => _references;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitComment(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _references.accept(visitor);
+  }
+
+  /**
+   * Create a block comment consisting of the given [tokens].
+   */
+  static Comment createBlockComment(List<Token> tokens) =>
+      new Comment(tokens, CommentType.BLOCK, null);
+
+  /**
+   * Create a documentation comment consisting of the given [tokens].
+   */
+  static Comment createDocumentationComment(List<Token> tokens) => new Comment(
+      tokens, CommentType.DOCUMENTATION, new List<CommentReference>());
+
+  /**
+   * Create a documentation comment consisting of the given [tokens] and having
+   * the given [references] embedded within it.
+   */
+  static Comment createDocumentationCommentWithReferences(
+          List<Token> tokens, List<CommentReference> references) =>
+      new Comment(tokens, CommentType.DOCUMENTATION, references);
+
+  /**
+   * Create an end-of-line comment consisting of the given [tokens].
+   */
+  static Comment createEndOfLineComment(List<Token> tokens) =>
+      new Comment(tokens, CommentType.END_OF_LINE, null);
+}
+
+/**
+ * A reference to a Dart element that is found within a documentation comment.
+ *
+ * > commentReference ::=
+ * >     '[' 'new'? [Identifier] ']'
+ */
+class CommentReference extends AstNode {
+  /**
+   * The token representing the 'new' keyword, or `null` if there was no 'new'
+   * keyword.
+   */
+  Token newKeyword;
+
+  /**
+   * The identifier being referenced.
+   */
+  Identifier _identifier;
+
+  /**
+   * Initialize a newly created reference to a Dart element. The [newKeyword]
+   * can be `null` if the reference is not to a constructor.
+   */
+  CommentReference(this.newKeyword, Identifier identifier) {
+    _identifier = _becomeParentOf(identifier);
+  }
+
+  @override
+  Token get beginToken => _identifier.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(newKeyword)
+    ..add(_identifier);
+
+  @override
+  Token get endToken => _identifier.endToken;
+
+  /**
+   * Return the identifier being referenced.
+   */
+  Identifier get identifier => _identifier;
+
+  /**
+   * Set the identifier being referenced to the given [identifier].
+   */
+  void set identifier(Identifier identifier) {
+    _identifier = _becomeParentOf(identifier);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitCommentReference(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_identifier, visitor);
+  }
+}
+
+/**
+ * The possible types of comments that are recognized by the parser.
+ */
+class CommentType {
+  /**
+   * A block comment.
+   */
+  static const CommentType BLOCK = const CommentType('BLOCK');
+
+  /**
+   * A documentation comment.
+   */
+  static const CommentType DOCUMENTATION = const CommentType('DOCUMENTATION');
+
+  /**
+   * An end-of-line comment.
+   */
+  static const CommentType END_OF_LINE = const CommentType('END_OF_LINE');
+
+  /**
+   * The name of the comment type.
+   */
+  final String name;
+
+  /**
+   * Initialize a newly created comment type to have the given [name].
+   */
+  const CommentType(this.name);
+
+  @override
+  String toString() => name;
+}
+
+/**
+ * A compilation unit.
+ *
+ * While the grammar restricts the order of the directives and declarations
+ * within a compilation unit, this class does not enforce those restrictions.
+ * In particular, the children of a compilation unit will be visited in lexical
+ * order even if lexical order does not conform to the restrictions of the
+ * grammar.
+ *
+ * > compilationUnit ::=
+ * >     directives declarations
+ * >
+ * > directives ::=
+ * >     [ScriptTag]? [LibraryDirective]? namespaceDirective* [PartDirective]*
+ * >   | [PartOfDirective]
+ * >
+ * > namespaceDirective ::=
+ * >     [ImportDirective]
+ * >   | [ExportDirective]
+ * >
+ * > declarations ::=
+ * >     [CompilationUnitMember]*
+ */
+class CompilationUnit extends AstNode {
+  /**
+   * The first token in the token stream that was parsed to form this
+   * compilation unit.
+   */
+  Token beginToken;
+
+  /**
+   * The script tag at the beginning of the compilation unit, or `null` if there
+   * is no script tag in this compilation unit.
+   */
+  ScriptTag _scriptTag;
+
+  /**
+   * The directives contained in this compilation unit.
+   */
+  NodeList<Directive> _directives;
+
+  /**
+   * The declarations contained in this compilation unit.
+   */
+  NodeList<CompilationUnitMember> _declarations;
+
+  /**
+   * The last token in the token stream that was parsed to form this compilation
+   * unit. This token should always have a type of [TokenType.EOF].
+   */
+  final Token endToken;
+
+  /**
+   * The element associated with this compilation unit, or `null` if the AST
+   * structure has not been resolved.
+   */
+  CompilationUnitElement element;
+
+  /**
+   * The line information for this compilation unit.
+   */
+  LineInfo lineInfo;
+
+  /**
+   * Initialize a newly created compilation unit to have the given directives
+   * and declarations. The [scriptTag] can be `null` if there is no script tag
+   * in the compilation unit. The list of [directives] can be `null` if there
+   * are no directives in the compilation unit. The list of [declarations] can
+   * be `null` if there are no declarations in the compilation unit.
+   */
+  CompilationUnit(this.beginToken, ScriptTag scriptTag,
+      List<Directive> directives, List<CompilationUnitMember> declarations,
+      this.endToken) {
+    _scriptTag = _becomeParentOf(scriptTag);
+    _directives = new NodeList<Directive>(this, directives);
+    _declarations = new NodeList<CompilationUnitMember>(this, declarations);
+  }
+
+  @override
+  Iterable get childEntities {
+    ChildEntities result = new ChildEntities()..add(_scriptTag);
+    if (_directivesAreBeforeDeclarations) {
+      result
+        ..addAll(_directives)
+        ..addAll(_declarations);
+    } else {
+      result.addAll(sortedDirectivesAndDeclarations);
+    }
+    return result;
+  }
+
+  /**
+   * Return the declarations contained in this compilation unit.
+   */
+  NodeList<CompilationUnitMember> get declarations => _declarations;
+
+  /**
+   * Return the directives contained in this compilation unit.
+   */
+  NodeList<Directive> get directives => _directives;
+
+  @override
+  int get length {
+    Token endToken = this.endToken;
+    if (endToken == null) {
+      return 0;
+    }
+    return endToken.offset + endToken.length;
+  }
+
+  @override
+  int get offset => 0;
+
+  /**
+   * Return the script tag at the beginning of the compilation unit, or `null`
+   * if there is no script tag in this compilation unit.
+   */
+  ScriptTag get scriptTag => _scriptTag;
+
+  /**
+   * Set the script tag at the beginning of the compilation unit to the given
+   * [scriptTag].
+   */
+  void set scriptTag(ScriptTag scriptTag) {
+    _scriptTag = _becomeParentOf(scriptTag);
+  }
+
+  /**
+   * Return a list containing all of the directives and declarations in this
+   * compilation unit, sorted in lexical order.
+   */
+  List<AstNode> get sortedDirectivesAndDeclarations {
+    return <AstNode>[]
+      ..addAll(_directives)
+      ..addAll(_declarations)
+      ..sort(AstNode.LEXICAL_ORDER);
+  }
+
+  /**
+   * Return `true` if all of the directives are lexically before any
+   * declarations.
+   */
+  bool get _directivesAreBeforeDeclarations {
+    if (_directives.isEmpty || _declarations.isEmpty) {
+      return true;
+    }
+    Directive lastDirective = _directives[_directives.length - 1];
+    CompilationUnitMember firstDeclaration = _declarations[0];
+    return lastDirective.offset < firstDeclaration.offset;
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitCompilationUnit(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_scriptTag, visitor);
+    if (_directivesAreBeforeDeclarations) {
+      _directives.accept(visitor);
+      _declarations.accept(visitor);
+    } else {
+      for (AstNode child in sortedDirectivesAndDeclarations) {
+        child.accept(visitor);
+      }
+    }
+  }
+}
+
+/**
+ * A node that declares one or more names within the scope of a compilation
+ * unit.
+ *
+ * > compilationUnitMember ::=
+ * >     [ClassDeclaration]
+ * >   | [TypeAlias]
+ * >   | [FunctionDeclaration]
+ * >   | [MethodDeclaration]
+ * >   | [VariableDeclaration]
+ * >   | [VariableDeclaration]
+ */
+abstract class CompilationUnitMember extends Declaration {
+  /**
+   * Initialize a newly created generic compilation unit member. Either or both
+   * of the [comment] and [metadata] can be `null` if the member does not have
+   * the corresponding attribute.
+   */
+  CompilationUnitMember(Comment comment, List<Annotation> metadata)
+      : super(comment, metadata);
+}
+
+/**
+ * A conditional expression.
+ *
+ * > conditionalExpression ::=
+ * >     [Expression] '?' [Expression] ':' [Expression]
+ */
+class ConditionalExpression extends Expression {
+  /**
+   * The condition used to determine which of the expressions is executed next.
+   */
+  Expression _condition;
+
+  /**
+   * The token used to separate the condition from the then expression.
+   */
+  Token question;
+
+  /**
+   * The expression that is executed if the condition evaluates to `true`.
+   */
+  Expression _thenExpression;
+
+  /**
+   * The token used to separate the then expression from the else expression.
+   */
+  Token colon;
+
+  /**
+   * The expression that is executed if the condition evaluates to `false`.
+   */
+  Expression _elseExpression;
+
+  /**
+   * Initialize a newly created conditional expression.
+   */
+  ConditionalExpression(Expression condition, this.question,
+      Expression thenExpression, this.colon, Expression elseExpression) {
+    _condition = _becomeParentOf(condition);
+    _thenExpression = _becomeParentOf(thenExpression);
+    _elseExpression = _becomeParentOf(elseExpression);
+  }
+
+  @override
+  Token get beginToken => _condition.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_condition)
+    ..add(question)
+    ..add(_thenExpression)
+    ..add(colon)
+    ..add(_elseExpression);
+
+  /**
+   * Return the condition used to determine which of the expressions is executed
+   * next.
+   */
+  Expression get condition => _condition;
+
+  /**
+   * Set the condition used to determine which of the expressions is executed
+   * next to the given [expression].
+   */
+  void set condition(Expression expression) {
+    _condition = _becomeParentOf(expression);
+  }
+
+  /**
+   * Return the expression that is executed if the condition evaluates to
+   * `false`.
+   */
+  Expression get elseExpression => _elseExpression;
+
+  /**
+   * Set the expression that is executed if the condition evaluates to `false`
+   * to the given [expression].
+   */
+  void set elseExpression(Expression expression) {
+    _elseExpression = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get endToken => _elseExpression.endToken;
+
+  @override
+  int get precedence => 3;
+
+  /**
+   * Return the expression that is executed if the condition evaluates to
+   * `true`.
+   */
+  Expression get thenExpression => _thenExpression;
+
+  /**
+   * Set the expression that is executed if the condition evaluates to `true` to
+   * the given [expression].
+   */
+  void set thenExpression(Expression expression) {
+    _thenExpression = _becomeParentOf(expression);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitConditionalExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_condition, visitor);
+    _safelyVisitChild(_thenExpression, visitor);
+    _safelyVisitChild(_elseExpression, visitor);
+  }
+}
+
+/**
+ * An object that can be used to evaluate constant expressions to produce their
+ * compile-time value. According to the Dart Language Specification:
+ * <blockquote>
+ * A constant expression is one of the following:
+ * * A literal number.
+ * * A literal boolean.
+ * * A literal string where any interpolated expression is a compile-time
+ *   constant that evaluates to a numeric, string or boolean value or to `null`.
+ * * `null`.
+ * * A reference to a static constant variable.
+ * * An identifier expression that denotes a constant variable, a class or a
+ *   type parameter.
+ * * A constant constructor invocation.
+ * * A constant list literal.
+ * * A constant map literal.
+ * * A simple or qualified identifier denoting a top-level function or a static
+ *   method.
+ * * A parenthesized expression `(e)` where `e` is a constant expression.
+ * * An expression of one of the forms `identical(e1, e2)`, `e1 == e2`,
+ *   `e1 != e2` where `e1` and `e2` are constant expressions that evaluate to a
+ *   numeric, string or boolean value or to `null`.
+ * * An expression of one of the forms `!e`, `e1 && e2` or `e1 || e2`, where
+ *   `e`, `e1` and `e2` are constant expressions that evaluate to a boolean
+ *   value or to `null`.
+ * * An expression of one of the forms `~e`, `e1 ^ e2`, `e1 & e2`, `e1 | e2`,
+ *   `e1 >> e2` or `e1 << e2`, where `e`, `e1` and `e2` are constant expressions
+ *   that evaluate to an integer value or to `null`.
+ * * An expression of one of the forms `-e`, `e1 + e2`, `e1 - e2`, `e1 * e2`,
+ *   `e1 / e2`, `e1 ~/ e2`, `e1 > e2`, `e1 < e2`, `e1 >= e2`, `e1 <= e2` or
+ *   `e1 % e2`, where `e`, `e1` and `e2` are constant expressions that evaluate
+ *   to a numeric value or to `null`.
+ * </blockquote>
+ * The values returned by instances of this class are therefore `null` and
+ * instances of the classes `Boolean`, `BigInteger`, `Double`, `String`, and
+ * `DartObject`.
+ *
+ * In addition, this class defines several values that can be returned to
+ * indicate various conditions encountered during evaluation. These are
+ * documented with the static fields that define those values.
+ */
+class ConstantEvaluator extends GeneralizingAstVisitor<Object> {
+  /**
+   * The value returned for expressions (or non-expression nodes) that are not
+   * compile-time constant expressions.
+   */
+  static Object NOT_A_CONSTANT = new Object();
+
+  @override
+  Object visitAdjacentStrings(AdjacentStrings node) {
+    StringBuffer buffer = new StringBuffer();
+    for (StringLiteral string in node.strings) {
+      Object value = string.accept(this);
+      if (identical(value, NOT_A_CONSTANT)) {
+        return value;
+      }
+      buffer.write(value);
+    }
+    return buffer.toString();
+  }
+
+  @override
+  Object visitBinaryExpression(BinaryExpression node) {
+    Object leftOperand = node.leftOperand.accept(this);
+    if (identical(leftOperand, NOT_A_CONSTANT)) {
+      return leftOperand;
+    }
+    Object rightOperand = node.rightOperand.accept(this);
+    if (identical(rightOperand, NOT_A_CONSTANT)) {
+      return rightOperand;
+    }
+    while (true) {
+      if (node.operator.type == TokenType.AMPERSAND) {
+        // integer or {@code null}
+        if (leftOperand is int && rightOperand is int) {
+          return leftOperand & rightOperand;
+        }
+      } else if (node.operator.type == TokenType.AMPERSAND_AMPERSAND) {
+        // boolean or {@code null}
+        if (leftOperand is bool && rightOperand is bool) {
+          return leftOperand && rightOperand;
+        }
+      } else if (node.operator.type == TokenType.BANG_EQ) {
+        // numeric, string, boolean, or {@code null}
+        if (leftOperand is bool && rightOperand is bool) {
+          return leftOperand != rightOperand;
+        } else if (leftOperand is num && rightOperand is num) {
+          return leftOperand != rightOperand;
+        } else if (leftOperand is String && rightOperand is String) {
+          return leftOperand != rightOperand;
+        }
+      } else if (node.operator.type == TokenType.BAR) {
+        // integer or {@code null}
+        if (leftOperand is int && rightOperand is int) {
+          return leftOperand | rightOperand;
+        }
+      } else if (node.operator.type == TokenType.BAR_BAR) {
+        // boolean or {@code null}
+        if (leftOperand is bool && rightOperand is bool) {
+          return leftOperand || rightOperand;
+        }
+      } else if (node.operator.type == TokenType.CARET) {
+        // integer or {@code null}
+        if (leftOperand is int && rightOperand is int) {
+          return leftOperand ^ rightOperand;
+        }
+      } else if (node.operator.type == TokenType.EQ_EQ) {
+        // numeric, string, boolean, or {@code null}
+        if (leftOperand is bool && rightOperand is bool) {
+          return leftOperand == rightOperand;
+        } else if (leftOperand is num && rightOperand is num) {
+          return leftOperand == rightOperand;
+        } else if (leftOperand is String && rightOperand is String) {
+          return leftOperand == rightOperand;
+        }
+      } else if (node.operator.type == TokenType.GT) {
+        // numeric or {@code null}
+        if (leftOperand is num && rightOperand is num) {
+          return leftOperand.compareTo(rightOperand) > 0;
+        }
+      } else if (node.operator.type == TokenType.GT_EQ) {
+        // numeric or {@code null}
+        if (leftOperand is num && rightOperand is num) {
+          return leftOperand.compareTo(rightOperand) >= 0;
+        }
+      } else if (node.operator.type == TokenType.GT_GT) {
+        // integer or {@code null}
+        if (leftOperand is int && rightOperand is int) {
+          return leftOperand >> rightOperand;
+        }
+      } else if (node.operator.type == TokenType.LT) {
+        // numeric or {@code null}
+        if (leftOperand is num && rightOperand is num) {
+          return leftOperand.compareTo(rightOperand) < 0;
+        }
+      } else if (node.operator.type == TokenType.LT_EQ) {
+        // numeric or {@code null}
+        if (leftOperand is num && rightOperand is num) {
+          return leftOperand.compareTo(rightOperand) <= 0;
+        }
+      } else if (node.operator.type == TokenType.LT_LT) {
+        // integer or {@code null}
+        if (leftOperand is int && rightOperand is int) {
+          return leftOperand << rightOperand;
+        }
+      } else if (node.operator.type == TokenType.MINUS) {
+        // numeric or {@code null}
+        if (leftOperand is num && rightOperand is num) {
+          return leftOperand - rightOperand;
+        }
+      } else if (node.operator.type == TokenType.PERCENT) {
+        // numeric or {@code null}
+        if (leftOperand is num && rightOperand is num) {
+          return leftOperand.remainder(rightOperand);
+        }
+      } else if (node.operator.type == TokenType.PLUS) {
+        // numeric or {@code null}
+        if (leftOperand is num && rightOperand is num) {
+          return leftOperand + rightOperand;
+        }
+      } else if (node.operator.type == TokenType.STAR) {
+        // numeric or {@code null}
+        if (leftOperand is num && rightOperand is num) {
+          return leftOperand * rightOperand;
+        }
+      } else if (node.operator.type == TokenType.SLASH) {
+        // numeric or {@code null}
+        if (leftOperand is num && rightOperand is num) {
+          return leftOperand / rightOperand;
+        }
+      } else if (node.operator.type == TokenType.TILDE_SLASH) {
+        // numeric or {@code null}
+        if (leftOperand is num && rightOperand is num) {
+          return leftOperand ~/ rightOperand;
+        }
+      } else {}
+      break;
+    }
+    // TODO(brianwilkerson) This doesn't handle numeric conversions.
+    return visitExpression(node);
+  }
+
+  @override
+  Object visitBooleanLiteral(BooleanLiteral node) => node.value ? true : false;
+
+  @override
+  Object visitDoubleLiteral(DoubleLiteral node) => node.value;
+
+  @override
+  Object visitIntegerLiteral(IntegerLiteral node) => node.value;
+
+  @override
+  Object visitInterpolationExpression(InterpolationExpression node) {
+    Object value = node.expression.accept(this);
+    if (value == null || value is bool || value is String || value is num) {
+      return value;
+    }
+    return NOT_A_CONSTANT;
+  }
+
+  @override
+  Object visitInterpolationString(InterpolationString node) => node.value;
+
+  @override
+  Object visitListLiteral(ListLiteral node) {
+    List<Object> list = new List<Object>();
+    for (Expression element in node.elements) {
+      Object value = element.accept(this);
+      if (identical(value, NOT_A_CONSTANT)) {
+        return value;
+      }
+      list.add(value);
+    }
+    return list;
+  }
+
+  @override
+  Object visitMapLiteral(MapLiteral node) {
+    HashMap<String, Object> map = new HashMap<String, Object>();
+    for (MapLiteralEntry entry in node.entries) {
+      Object key = entry.key.accept(this);
+      Object value = entry.value.accept(this);
+      if (key is! String || identical(value, NOT_A_CONSTANT)) {
+        return NOT_A_CONSTANT;
+      }
+      map[(key as String)] = value;
+    }
+    return map;
+  }
+
+  @override
+  Object visitMethodInvocation(MethodInvocation node) => visitNode(node);
+
+  @override
+  Object visitNode(AstNode node) => NOT_A_CONSTANT;
+
+  @override
+  Object visitNullLiteral(NullLiteral node) => null;
+
+  @override
+  Object visitParenthesizedExpression(ParenthesizedExpression node) =>
+      node.expression.accept(this);
+
+  @override
+  Object visitPrefixedIdentifier(PrefixedIdentifier node) =>
+      _getConstantValue(null);
+
+  @override
+  Object visitPrefixExpression(PrefixExpression node) {
+    Object operand = node.operand.accept(this);
+    if (identical(operand, NOT_A_CONSTANT)) {
+      return operand;
+    }
+    while (true) {
+      if (node.operator.type == TokenType.BANG) {
+        if (identical(operand, true)) {
+          return false;
+        } else if (identical(operand, false)) {
+          return true;
+        }
+      } else if (node.operator.type == TokenType.TILDE) {
+        if (operand is int) {
+          return ~operand;
+        }
+      } else if (node.operator.type == TokenType.MINUS) {
+        if (operand == null) {
+          return null;
+        } else if (operand is num) {
+          return -operand;
+        }
+      } else {}
+      break;
+    }
+    return NOT_A_CONSTANT;
+  }
+
+  @override
+  Object visitPropertyAccess(PropertyAccess node) => _getConstantValue(null);
+
+  @override
+  Object visitSimpleIdentifier(SimpleIdentifier node) =>
+      _getConstantValue(null);
+
+  @override
+  Object visitSimpleStringLiteral(SimpleStringLiteral node) => node.value;
+
+  @override
+  Object visitStringInterpolation(StringInterpolation node) {
+    StringBuffer buffer = new StringBuffer();
+    for (InterpolationElement element in node.elements) {
+      Object value = element.accept(this);
+      if (identical(value, NOT_A_CONSTANT)) {
+        return value;
+      }
+      buffer.write(value);
+    }
+    return buffer.toString();
+  }
+
+  @override
+  Object visitSymbolLiteral(SymbolLiteral node) {
+    // TODO(brianwilkerson) This isn't optimal because a Symbol is not a String.
+    StringBuffer buffer = new StringBuffer();
+    for (Token component in node.components) {
+      if (buffer.length > 0) {
+        buffer.writeCharCode(0x2E);
+      }
+      buffer.write(component.lexeme);
+    }
+    return buffer.toString();
+  }
+
+  /**
+   * Return the constant value of the static constant represented by the given
+   * [element].
+   */
+  Object _getConstantValue(Element element) {
+    // TODO(brianwilkerson) Implement this
+    if (element is FieldElement) {
+      FieldElement field = element;
+      if (field.isStatic && field.isConst) {
+        //field.getConstantValue();
+      }
+      //    } else if (element instanceof VariableElement) {
+      //      VariableElement variable = (VariableElement) element;
+      //      if (variable.isStatic() && variable.isConst()) {
+      //        //variable.getConstantValue();
+      //      }
+    }
+    return NOT_A_CONSTANT;
+  }
+}
+
+/**
+ * Object representing a "const" instance creation expression, and its
+ * evaluation result.  This is used as the AnalysisTarget for constant
+ * evaluation of instance creation expressions.
+ */
+class ConstantInstanceCreationHandle {
+  /**
+   * The result of evaluating the constant.
+   */
+  EvaluationResultImpl evaluationResult;
+}
+
+/**
+ * A constructor declaration.
+ *
+ * > constructorDeclaration ::=
+ * >     constructorSignature [FunctionBody]?
+ * >   | constructorName formalParameterList ':' 'this' ('.' [SimpleIdentifier])? arguments
+ * >
+ * > constructorSignature ::=
+ * >     'external'? constructorName formalParameterList initializerList?
+ * >   | 'external'? 'factory' factoryName formalParameterList initializerList?
+ * >   | 'external'? 'const'  constructorName formalParameterList initializerList?
+ * >
+ * > constructorName ::=
+ * >     [SimpleIdentifier] ('.' [SimpleIdentifier])?
+ * >
+ * > factoryName ::=
+ * >     [Identifier] ('.' [SimpleIdentifier])?
+ * >
+ * > initializerList ::=
+ * >     ':' [ConstructorInitializer] (',' [ConstructorInitializer])*
+ */
+class ConstructorDeclaration extends ClassMember {
+  /**
+   * The token for the 'external' keyword, or `null` if the constructor is not
+   * external.
+   */
+  Token externalKeyword;
+
+  /**
+   * The token for the 'const' keyword, or `null` if the constructor is not a
+   * const constructor.
+   */
+  Token constKeyword;
+
+  /**
+   * The token for the 'factory' keyword, or `null` if the constructor is not a
+   * factory constructor.
+   */
+  Token factoryKeyword;
+
+  /**
+   * The type of object being created. This can be different than the type in
+   * which the constructor is being declared if the constructor is the
+   * implementation of a factory constructor.
+   */
+  Identifier _returnType;
+
+  /**
+   * The token for the period before the constructor name, or `null` if the
+   * constructor being declared is unnamed.
+   */
+  Token period;
+
+  /**
+   * The name of the constructor, or `null` if the constructor being declared is
+   * unnamed.
+   */
+  SimpleIdentifier _name;
+
+  /**
+   * The parameters associated with the constructor.
+   */
+  FormalParameterList _parameters;
+
+  /**
+   * The token for the separator (colon or equals) before the initializer list
+   * or redirection, or `null` if there are no initializers.
+   */
+  Token separator;
+
+  /**
+   * The initializers associated with the constructor.
+   */
+  NodeList<ConstructorInitializer> _initializers;
+
+  /**
+   * The name of the constructor to which this constructor will be redirected,
+   * or `null` if this is not a redirecting factory constructor.
+   */
+  ConstructorName _redirectedConstructor;
+
+  /**
+   * The body of the constructor, or `null` if the constructor does not have a
+   * body.
+   */
+  FunctionBody _body;
+
+  /**
+   * The element associated with this constructor, or `null` if the AST
+   * structure has not been resolved or if this constructor could not be
+   * resolved.
+   */
+  ConstructorElement element;
+
+  /**
+   * Initialize a newly created constructor declaration. The [externalKeyword]
+   * can be `null` if the constructor is not external. Either or both of the
+   * [comment] and [metadata] can be `null` if the constructor does not have the
+   * corresponding attribute. The [constKeyword] can be `null` if the
+   * constructor cannot be used to create a constant. The [factoryKeyword] can
+   * be `null` if the constructor is not a factory. The [period] and [name] can
+   * both be `null` if the constructor is not a named constructor. The
+   * [separator] can be `null` if the constructor does not have any initializers
+   * and does not redirect to a different constructor. The list of
+   * [initializers] can be `null` if the constructor does not have any
+   * initializers. The [redirectedConstructor] can be `null` if the constructor
+   * does not redirect to a different constructor. The [body] can be `null` if
+   * the constructor does not have a body.
+   */
+  ConstructorDeclaration(Comment comment, List<Annotation> metadata,
+      this.externalKeyword, this.constKeyword, this.factoryKeyword,
+      Identifier returnType, this.period, SimpleIdentifier name,
+      FormalParameterList parameters, this.separator,
+      List<ConstructorInitializer> initializers,
+      ConstructorName redirectedConstructor, FunctionBody body)
+      : super(comment, metadata) {
+    _returnType = _becomeParentOf(returnType);
+    _name = _becomeParentOf(name);
+    _parameters = _becomeParentOf(parameters);
+    _initializers = new NodeList<ConstructorInitializer>(this, initializers);
+    _redirectedConstructor = _becomeParentOf(redirectedConstructor);
+    _body = _becomeParentOf(body);
+  }
+
+  /**
+   * Return the body of the constructor, or `null` if the constructor does not
+   * have a body.
+   */
+  FunctionBody get body => _body;
+
+  /**
+   * Set the body of the constructor to the given [functionBody].
+   */
+  void set body(FunctionBody functionBody) {
+    _body = _becomeParentOf(functionBody);
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(externalKeyword)
+    ..add(constKeyword)
+    ..add(factoryKeyword)
+    ..add(_returnType)
+    ..add(period)
+    ..add(_name)
+    ..add(_parameters)
+    ..add(separator)
+    ..addAll(initializers)
+    ..add(_redirectedConstructor)
+    ..add(_body);
+
+  @override
+  Token get endToken {
+    if (_body != null) {
+      return _body.endToken;
+    } else if (!_initializers.isEmpty) {
+      return _initializers.endToken;
+    }
+    return _parameters.endToken;
+  }
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata {
+    Token leftMost =
+        Token.lexicallyFirst([externalKeyword, constKeyword, factoryKeyword]);
+    if (leftMost != null) {
+      return leftMost;
+    }
+    return _returnType.beginToken;
+  }
+
+  /**
+   * Return the initializers associated with the constructor.
+   */
+  NodeList<ConstructorInitializer> get initializers => _initializers;
+
+  /**
+   * Return the name of the constructor, or `null` if the constructor being
+   * declared is unnamed.
+   */
+  SimpleIdentifier get name => _name;
+
+  /**
+   * Set the name of the constructor to the given [identifier].
+   */
+  void set name(SimpleIdentifier identifier) {
+    _name = _becomeParentOf(identifier);
+  }
+
+  /**
+   * Return the parameters associated with the constructor.
+   */
+  FormalParameterList get parameters => _parameters;
+
+  /**
+   * Set the parameters associated with the constructor to the given list of
+   * [parameters].
+   */
+  void set parameters(FormalParameterList parameters) {
+    _parameters = _becomeParentOf(parameters);
+  }
+
+  /**
+   * Return the name of the constructor to which this constructor will be
+   * redirected, or `null` if this is not a redirecting factory constructor.
+   */
+  ConstructorName get redirectedConstructor => _redirectedConstructor;
+
+  /**
+   * Set the name of the constructor to which this constructor will be
+   * redirected to the given [redirectedConstructor] name.
+   */
+  void set redirectedConstructor(ConstructorName redirectedConstructor) {
+    _redirectedConstructor = _becomeParentOf(redirectedConstructor);
+  }
+
+  /**
+   * Return the type of object being created. This can be different than the
+   * type in which the constructor is being declared if the constructor is the
+   * implementation of a factory constructor.
+   */
+  Identifier get returnType => _returnType;
+
+  /**
+   * Set the type of object being created to the given [typeName].
+   */
+  void set returnType(Identifier typeName) {
+    _returnType = _becomeParentOf(typeName);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitConstructorDeclaration(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_returnType, visitor);
+    _safelyVisitChild(_name, visitor);
+    _safelyVisitChild(_parameters, visitor);
+    _initializers.accept(visitor);
+    _safelyVisitChild(_redirectedConstructor, visitor);
+    _safelyVisitChild(_body, visitor);
+  }
+}
+
+/**
+ * The initialization of a field within a constructor's initialization list.
+ *
+ * > fieldInitializer ::=
+ * >     ('this' '.')? [SimpleIdentifier] '=' [Expression]
+ */
+class ConstructorFieldInitializer extends ConstructorInitializer {
+  /**
+   * The token for the 'this' keyword, or `null` if there is no 'this' keyword.
+   */
+  Token thisKeyword;
+
+  /**
+   * The token for the period after the 'this' keyword, or `null` if there is no
+   * 'this' keyword.
+   */
+  Token period;
+
+  /**
+   * The name of the field being initialized.
+   */
+  SimpleIdentifier _fieldName;
+
+  /**
+   * The token for the equal sign between the field name and the expression.
+   */
+  Token equals;
+
+  /**
+   * The expression computing the value to which the field will be initialized.
+   */
+  Expression _expression;
+
+  /**
+   * Initialize a newly created field initializer to initialize the field with
+   * the given name to the value of the given expression. The [thisKeyword] and
+   * [period] can be `null` if the 'this' keyword was not specified.
+   */
+  ConstructorFieldInitializer(this.thisKeyword, this.period,
+      SimpleIdentifier fieldName, this.equals, Expression expression) {
+    _fieldName = _becomeParentOf(fieldName);
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get beginToken {
+    if (thisKeyword != null) {
+      return thisKeyword;
+    }
+    return _fieldName.beginToken;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(thisKeyword)
+    ..add(period)
+    ..add(_fieldName)
+    ..add(equals)
+    ..add(_expression);
+
+  @override
+  Token get endToken => _expression.endToken;
+
+  /**
+   * Return the expression computing the value to which the field will be
+   * initialized.
+   */
+  Expression get expression => _expression;
+
+  /**
+   * Set the expression computing the value to which the field will be
+   * initialized to the given [expression].
+   */
+  void set expression(Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  /**
+   * Return the name of the field being initialized.
+   */
+  SimpleIdentifier get fieldName => _fieldName;
+
+  /**
+   * Set the name of the field being initialized to the given [identifier].
+   */
+  void set fieldName(SimpleIdentifier identifier) {
+    _fieldName = _becomeParentOf(identifier);
+  }
+
+  /**
+   * Return the token for the 'this' keyword, or `null` if there is no 'this'
+   * keyword.
+   */
+  @deprecated // Use "this.thisKeyword"
+  Token get keyword => thisKeyword;
+
+  /**
+   * Set the token for the 'this' keyword to the given [token].
+   */
+  @deprecated // Use "this.thisKeyword"
+  set keyword(Token token) {
+    thisKeyword = token;
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitConstructorFieldInitializer(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_fieldName, visitor);
+    _safelyVisitChild(_expression, visitor);
+  }
+}
+
+/**
+ * A node that can occur in the initializer list of a constructor declaration.
+ *
+ * > constructorInitializer ::=
+ * >     [SuperConstructorInvocation]
+ * >   | [ConstructorFieldInitializer]
+ */
+abstract class ConstructorInitializer extends AstNode {}
+
+/**
+ * The name of the constructor.
+ *
+ * > constructorName ::=
+ * >     type ('.' identifier)?
+ */
+class ConstructorName extends AstNode {
+  /**
+   * The name of the type defining the constructor.
+   */
+  TypeName _type;
+
+  /**
+   * The token for the period before the constructor name, or `null` if the
+   * specified constructor is the unnamed constructor.
+   */
+  Token period;
+
+  /**
+   * The name of the constructor, or `null` if the specified constructor is the
+   * unnamed constructor.
+   */
+  SimpleIdentifier _name;
+
+  /**
+   * The element associated with this constructor name based on static type
+   * information, or `null` if the AST structure has not been resolved or if
+   * this constructor name could not be resolved.
+   */
+  ConstructorElement staticElement;
+
+  /**
+   * Initialize a newly created constructor name. The [period] and [name] can be
+   * `null` if the constructor being named is the unnamed constructor.
+   */
+  ConstructorName(TypeName type, this.period, SimpleIdentifier name) {
+    _type = _becomeParentOf(type);
+    _name = _becomeParentOf(name);
+  }
+
+  @override
+  Token get beginToken => _type.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_type)
+    ..add(period)
+    ..add(_name);
+
+  @override
+  Token get endToken {
+    if (_name != null) {
+      return _name.endToken;
+    }
+    return _type.endToken;
+  }
+
+  /**
+   * Return the name of the constructor, or `null` if the specified constructor
+   * is the unnamed constructor.
+   */
+  SimpleIdentifier get name => _name;
+
+  /**
+   * Set the name of the constructor to the given [name].
+   */
+  void set name(SimpleIdentifier name) {
+    _name = _becomeParentOf(name);
+  }
+
+  /**
+   * Return the name of the type defining the constructor.
+   */
+  TypeName get type => _type;
+
+  /**
+   * Set the name of the type defining the constructor to the given [type] name.
+   */
+  void set type(TypeName type) {
+    _type = _becomeParentOf(type);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitConstructorName(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_type, visitor);
+    _safelyVisitChild(_name, visitor);
+  }
+}
+
+/**
+ * A continue statement.
+ *
+ * > continueStatement ::=
+ * >     'continue' [SimpleIdentifier]? ';'
+ */
+class ContinueStatement extends Statement {
+  /**
+   * The token representing the 'continue' keyword.
+   */
+  Token continueKeyword;
+
+  /**
+   * The label associated with the statement, or `null` if there is no label.
+   */
+  SimpleIdentifier _label;
+
+  /**
+   * The semicolon terminating the statement.
+   */
+  Token semicolon;
+
+  /**
+   * The AstNode which this continue statement is continuing to.  This will be
+   * either a Statement (in the case of continuing a loop) or a SwitchMember
+   * (in the case of continuing from one switch case to another).  Null if the
+   * AST has not yet been resolved or if the target could not be resolved.
+   * Note that if the source code has errors, the target may be invalid (e.g.
+   * the target may be in an enclosing function).
+   */
+  AstNode target;
+
+  /**
+   * Initialize a newly created continue statement. The [label] can be `null` if
+   * there is no label associated with the statement.
+   */
+  ContinueStatement(
+      this.continueKeyword, SimpleIdentifier label, this.semicolon) {
+    _label = _becomeParentOf(label);
+  }
+
+  @override
+  Token get beginToken => continueKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(continueKeyword)
+    ..add(_label)
+    ..add(semicolon);
+
+  @override
+  Token get endToken => semicolon;
+
+  /**
+   * Return the token for the 'continue' keyword, or `null` if there is no
+   * 'continue' keyword.
+   */
+  @deprecated // Use "this.continueKeyword"
+  Token get keyword => continueKeyword;
+
+  /**
+   * Set the token for the 'continue' keyword to the given [token].
+   */
+  @deprecated // Use "this.continueKeyword"
+  set keyword(Token token) {
+    continueKeyword = token;
+  }
+
+  /**
+   * Return the label associated with the statement, or `null` if there is no
+   * label.
+   */
+  SimpleIdentifier get label => _label;
+
+  /**
+   * Set the label associated with the statement to the given [identifier].
+   */
+  void set label(SimpleIdentifier identifier) {
+    _label = _becomeParentOf(identifier);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitContinueStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_label, visitor);
+  }
+}
+
+/**
+ * A node that represents the declaration of one or more names. Each declared
+ * name is visible within a name scope.
+ */
+abstract class Declaration extends AnnotatedNode {
+  /**
+   * Initialize a newly created declaration. Either or both of the [comment] and
+   * [metadata] can be `null` if the declaration does not have the corresponding
+   * attribute.
+   */
+  Declaration(Comment comment, List<Annotation> metadata)
+      : super(comment, metadata);
+
+  /**
+   * Return the element associated with this declaration, or `null` if either
+   * this node corresponds to a list of declarations or if the AST structure has
+   * not been resolved.
+   */
+  Element get element;
+}
+
+/**
+ * The declaration of a single identifier.
+ *
+ * > declaredIdentifier ::=
+ * >     [Annotation] finalConstVarOrType [SimpleIdentifier]
+ */
+class DeclaredIdentifier extends Declaration {
+  /**
+   * The token representing either the 'final', 'const' or 'var' keyword, or
+   * `null` if no keyword was used.
+   */
+  Token keyword;
+
+  /**
+   * The name of the declared type of the parameter, or `null` if the parameter
+   * does not have a declared type.
+   */
+  TypeName _type;
+
+  /**
+   * The name of the variable being declared.
+   */
+  SimpleIdentifier _identifier;
+
+  /**
+   * Initialize a newly created formal parameter. Either or both of the
+   * [comment] and [metadata] can be `null` if the declaration does not have the
+   * corresponding attribute. The [keyword] can be `null` if a type name is
+   * given. The [type] must be `null` if the keyword is 'var'.
+   */
+  DeclaredIdentifier(Comment comment, List<Annotation> metadata, this.keyword,
+      TypeName type, SimpleIdentifier identifier)
+      : super(comment, metadata) {
+    _type = _becomeParentOf(type);
+    _identifier = _becomeParentOf(identifier);
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(keyword)
+    ..add(_type)
+    ..add(_identifier);
+
+  @override
+  LocalVariableElement get element {
+    if (_identifier == null) {
+      return null;
+    }
+    return _identifier.staticElement as LocalVariableElement;
+  }
+
+  @override
+  Token get endToken => _identifier.endToken;
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata {
+    if (keyword != null) {
+      return keyword;
+    } else if (_type != null) {
+      return _type.beginToken;
+    }
+    return _identifier.beginToken;
+  }
+
+  /**
+   * Return the name of the variable being declared.
+   */
+  SimpleIdentifier get identifier => _identifier;
+
+  /**
+   * Set the name of the variable being declared to the given [identifier].
+   */
+  void set identifier(SimpleIdentifier identifier) {
+    _identifier = _becomeParentOf(identifier);
+  }
+
+  /**
+   * Return `true` if this variable was declared with the 'const' modifier.
+   */
+  bool get isConst => (keyword is KeywordToken) &&
+      (keyword as KeywordToken).keyword == Keyword.CONST;
+
+  /**
+   * Return `true` if this variable was declared with the 'final' modifier.
+   * Variables that are declared with the 'const' modifier will return `false`
+   * even though they are implicitly final.
+   */
+  bool get isFinal => (keyword is KeywordToken) &&
+      (keyword as KeywordToken).keyword == Keyword.FINAL;
+
+  /**
+   * Return the name of the declared type of the parameter, or `null` if the
+   * parameter does not have a declared type.
+   */
+  TypeName get type => _type;
+
+  /**
+   * Set the name of the declared type of the parameter to the given [typeName].
+   */
+  void set type(TypeName typeName) {
+    _type = _becomeParentOf(typeName);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitDeclaredIdentifier(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_type, visitor);
+    _safelyVisitChild(_identifier, visitor);
+  }
+}
+
+/**
+ * A formal parameter with a default value. There are two kinds of parameters
+ * that are both represented by this class: named formal parameters and
+ * positional formal parameters.
+ *
+ * > defaultFormalParameter ::=
+ * >     [NormalFormalParameter] ('=' [Expression])?
+ * >
+ * > defaultNamedParameter ::=
+ * >     [NormalFormalParameter] (':' [Expression])?
+ */
+class DefaultFormalParameter extends FormalParameter {
+  /**
+   * The formal parameter with which the default value is associated.
+   */
+  NormalFormalParameter _parameter;
+
+  /**
+   * The kind of this parameter.
+   */
+  ParameterKind kind;
+
+  /**
+   * The token separating the parameter from the default value, or `null` if
+   * there is no default value.
+   */
+  Token separator;
+
+  /**
+   * The expression computing the default value for the parameter, or `null` if
+   * there is no default value.
+   */
+  Expression _defaultValue;
+
+  /**
+   * Initialize a newly created default formal parameter. The [separator] and
+   * [defaultValue] can be `null` if there is no default value.
+   */
+  DefaultFormalParameter(NormalFormalParameter parameter, this.kind,
+      this.separator, Expression defaultValue) {
+    _parameter = _becomeParentOf(parameter);
+    _defaultValue = _becomeParentOf(defaultValue);
+  }
+
+  @override
+  Token get beginToken => _parameter.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_parameter)
+    ..add(separator)
+    ..add(_defaultValue);
+
+  /**
+   * Return the expression computing the default value for the parameter, or
+   * `null` if there is no default value.
+   */
+  Expression get defaultValue => _defaultValue;
+
+  /**
+   * Set the expression computing the default value for the parameter to the
+   * given [expression].
+   */
+  void set defaultValue(Expression expression) {
+    _defaultValue = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get endToken {
+    if (_defaultValue != null) {
+      return _defaultValue.endToken;
+    }
+    return _parameter.endToken;
+  }
+
+  @override
+  SimpleIdentifier get identifier => _parameter.identifier;
+
+  @override
+  bool get isConst => _parameter != null && _parameter.isConst;
+
+  @override
+  bool get isFinal => _parameter != null && _parameter.isFinal;
+
+  /**
+   * Return the formal parameter with which the default value is associated.
+   */
+  NormalFormalParameter get parameter => _parameter;
+
+  /**
+   * Set the formal parameter with which the default value is associated to the
+   * given [formalParameter].
+   */
+  void set parameter(NormalFormalParameter formalParameter) {
+    _parameter = _becomeParentOf(formalParameter);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitDefaultFormalParameter(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_parameter, visitor);
+    _safelyVisitChild(_defaultValue, visitor);
+  }
+}
+
+/**
+ * A recursive AST visitor that is used to run over [Expression]s to determine
+ * whether the expression is composed by at least one deferred
+ * [PrefixedIdentifier].
+ *
+ * See [PrefixedIdentifier.isDeferred].
+ */
+class DeferredLibraryReferenceDetector extends RecursiveAstVisitor<Object> {
+  /**
+   * A flag indicating whether an identifier from a deferred library has been
+   * found.
+   */
+  bool _result = false;
+
+  /**
+   * Return `true` if the visitor found a [PrefixedIdentifier] that returned
+   * `true` to the [PrefixedIdentifier.isDeferred] query.
+   */
+  bool get result => _result;
+
+  @override
+  Object visitPrefixedIdentifier(PrefixedIdentifier node) {
+    if (!_result) {
+      if (node.isDeferred) {
+        _result = true;
+      }
+    }
+    return null;
+  }
+}
+
+/**
+ * A node that represents a directive.
+ *
+ * > directive ::=
+ * >     [ExportDirective]
+ * >   | [ImportDirective]
+ * >   | [LibraryDirective]
+ * >   | [PartDirective]
+ * >   | [PartOfDirective]
+ */
+abstract class Directive extends AnnotatedNode {
+  /**
+   * The element associated with this directive, or `null` if the AST structure
+   * has not been resolved or if this directive could not be resolved.
+   */
+  Element element;
+
+  /**
+   * Initialize a newly create directive. Either or both of the [comment] and
+   * [metadata] can be `null` if the directive does not have the corresponding
+   * attribute.
+   */
+  Directive(Comment comment, List<Annotation> metadata)
+      : super(comment, metadata);
+
+  /**
+   * Return the token representing the keyword that introduces this directive
+   * ('import', 'export', 'library' or 'part').
+   */
+  Token get keyword;
+}
+
+/**
+ * A do statement.
+ *
+ * > doStatement ::=
+ * >     'do' [Statement] 'while' '(' [Expression] ')' ';'
+ */
+class DoStatement extends Statement {
+  /**
+   * The token representing the 'do' keyword.
+   */
+  Token doKeyword;
+
+  /**
+   * The body of the loop.
+   */
+  Statement _body;
+
+  /**
+   * The token representing the 'while' keyword.
+   */
+  Token whileKeyword;
+
+  /**
+   * The left parenthesis.
+   */
+  Token leftParenthesis;
+
+  /**
+   * The condition that determines when the loop will terminate.
+   */
+  Expression _condition;
+
+  /**
+   * The right parenthesis.
+   */
+  Token rightParenthesis;
+
+  /**
+   * The semicolon terminating the statement.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created do loop.
+   */
+  DoStatement(this.doKeyword, Statement body, this.whileKeyword,
+      this.leftParenthesis, Expression condition, this.rightParenthesis,
+      this.semicolon) {
+    _body = _becomeParentOf(body);
+    _condition = _becomeParentOf(condition);
+  }
+
+  @override
+  Token get beginToken => doKeyword;
+
+  /**
+   * Return the body of the loop.
+   */
+  Statement get body => _body;
+
+  /**
+   * Set the body of the loop to the given [statement].
+   */
+  void set body(Statement statement) {
+    _body = _becomeParentOf(statement);
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(doKeyword)
+    ..add(_body)
+    ..add(whileKeyword)
+    ..add(leftParenthesis)
+    ..add(_condition)
+    ..add(rightParenthesis)
+    ..add(semicolon);
+
+  /**
+   * Return the condition that determines when the loop will terminate.
+   */
+  Expression get condition => _condition;
+
+  /**
+   * Set the condition that determines when the loop will terminate to the given
+   * [expression].
+   */
+  void set condition(Expression expression) {
+    _condition = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get endToken => semicolon;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitDoStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_body, visitor);
+    _safelyVisitChild(_condition, visitor);
+  }
+}
+
+/**
+ * A floating point literal expression.
+ *
+ * > doubleLiteral ::=
+ * >     decimalDigit+ ('.' decimalDigit*)? exponent?
+ * >   | '.' decimalDigit+ exponent?
+ * >
+ * > exponent ::=
+ * >     ('e' | 'E') ('+' | '-')? decimalDigit+
+ */
+class DoubleLiteral extends Literal {
+  /**
+   * The token representing the literal.
+   */
+  Token literal;
+
+  /**
+   * The value of the literal.
+   */
+  double value;
+
+  /**
+   * Initialize a newly created floating point literal.
+   */
+  DoubleLiteral(this.literal, this.value);
+
+  @override
+  Token get beginToken => literal;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..add(literal);
+
+  @override
+  Token get endToken => literal;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitDoubleLiteral(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    // There are no children to visit.
+  }
+}
+
+/**
+ * An object used to locate the [Element] associated with a given [AstNode].
+ */
+class ElementLocator {
+  /**
+   * Return the element associated with the given [node], or `null` if there is
+   * no element associated with the node.
+   */
+  static Element locate(AstNode node) {
+    if (node == null) {
+      return null;
+    }
+    ElementLocator_ElementMapper mapper = new ElementLocator_ElementMapper();
+    return node.accept(mapper);
+  }
+
+  /**
+   * Return the element associated with the given [node], or `null` if there is
+   * no element associated with the node.
+   */
+  static Element locateWithOffset(AstNode node, int offset) {
+    // TODO(brianwilkerson) 'offset' is not used. Figure out what's going on:
+    // whether there's a bug or whether this method is unnecessary.
+    if (node == null) {
+      return null;
+    }
+    // try to get Element from node
+    Element nodeElement = locate(node);
+    if (nodeElement != null) {
+      return nodeElement;
+    }
+    // no Element
+    return null;
+  }
+}
+
+/**
+ * Visitor that maps nodes to elements.
+ */
+class ElementLocator_ElementMapper extends GeneralizingAstVisitor<Element> {
+  @override
+  Element visitAnnotation(Annotation node) => node.element;
+
+  @override
+  Element visitAssignmentExpression(AssignmentExpression node) =>
+      node.bestElement;
+
+  @override
+  Element visitBinaryExpression(BinaryExpression node) => node.bestElement;
+
+  @override
+  Element visitClassDeclaration(ClassDeclaration node) => node.element;
+
+  @override
+  Element visitCompilationUnit(CompilationUnit node) => node.element;
+
+  @override
+  Element visitConstructorDeclaration(ConstructorDeclaration node) =>
+      node.element;
+
+  @override
+  Element visitFunctionDeclaration(FunctionDeclaration node) => node.element;
+
+  @override
+  Element visitIdentifier(Identifier node) {
+    AstNode parent = node.parent;
+    // Type name in Annotation
+    if (parent is Annotation) {
+      Annotation annotation = parent;
+      if (identical(annotation.name, node) &&
+          annotation.constructorName == null) {
+        return annotation.element;
+      }
+    }
+    // Extra work to map Constructor Declarations to their associated
+    // Constructor Elements
+    if (parent is ConstructorDeclaration) {
+      ConstructorDeclaration decl = parent;
+      Identifier returnType = decl.returnType;
+      if (identical(returnType, node)) {
+        SimpleIdentifier name = decl.name;
+        if (name != null) {
+          return name.bestElement;
+        }
+        Element element = node.bestElement;
+        if (element is ClassElement) {
+          return element.unnamedConstructor;
+        }
+      }
+    }
+    if (parent is LibraryIdentifier) {
+      AstNode grandParent = parent.parent;
+      if (grandParent is PartOfDirective) {
+        Element element = grandParent.element;
+        if (element is LibraryElement) {
+          return element.definingCompilationUnit;
+        }
+      }
+    }
+    return node.bestElement;
+  }
+
+  @override
+  Element visitImportDirective(ImportDirective node) => node.element;
+
+  @override
+  Element visitIndexExpression(IndexExpression node) => node.bestElement;
+
+  @override
+  Element visitInstanceCreationExpression(InstanceCreationExpression node) =>
+      node.staticElement;
+
+  @override
+  Element visitLibraryDirective(LibraryDirective node) => node.element;
+
+  @override
+  Element visitMethodDeclaration(MethodDeclaration node) => node.element;
+
+  @override
+  Element visitMethodInvocation(MethodInvocation node) =>
+      node.methodName.bestElement;
+
+  @override
+  Element visitPostfixExpression(PostfixExpression node) => node.bestElement;
+
+  @override
+  Element visitPrefixedIdentifier(PrefixedIdentifier node) => node.bestElement;
+
+  @override
+  Element visitPrefixExpression(PrefixExpression node) => node.bestElement;
+
+  @override
+  Element visitStringLiteral(StringLiteral node) {
+    AstNode parent = node.parent;
+    if (parent is UriBasedDirective) {
+      return parent.uriElement;
+    }
+    return null;
+  }
+
+  @override
+  Element visitVariableDeclaration(VariableDeclaration node) => node.element;
+}
+
+/**
+ * An empty function body, which can only appear in constructors or abstract
+ * methods.
+ *
+ * > emptyFunctionBody ::=
+ * >     ';'
+ */
+class EmptyFunctionBody extends FunctionBody {
+  /**
+   * The token representing the semicolon that marks the end of the function
+   * body.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created function body.
+   */
+  EmptyFunctionBody(this.semicolon);
+
+  @override
+  Token get beginToken => semicolon;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..add(semicolon);
+
+  @override
+  Token get endToken => semicolon;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitEmptyFunctionBody(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    // Empty function bodies have no children.
+  }
+}
+
+/**
+ * An empty statement.
+ *
+ * > emptyStatement ::=
+ * >     ';'
+ */
+class EmptyStatement extends Statement {
+  /**
+   * The semicolon terminating the statement.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created empty statement.
+   */
+  EmptyStatement(this.semicolon);
+
+  @override
+  Token get beginToken => semicolon;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..add(semicolon);
+
+  @override
+  Token get endToken => semicolon;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitEmptyStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    // There are no children to visit.
+  }
+}
+
+/**
+ * The declaration of an enum constant.
+ */
+class EnumConstantDeclaration extends Declaration {
+  /**
+   * The name of the constant.
+   */
+  SimpleIdentifier _name;
+
+  /**
+   * Initialize a newly created enum constant declaration. Either or both of the
+   * [comment] and [metadata] can be `null` if the constant does not have the
+   * corresponding attribute. (Technically, enum constants cannot have metadata,
+   * but we allow it for consistency.)
+   */
+  EnumConstantDeclaration(
+      Comment comment, List<Annotation> metadata, SimpleIdentifier name)
+      : super(comment, metadata) {
+    _name = _becomeParentOf(name);
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities..add(_name);
+
+  @override
+  FieldElement get element =>
+      _name == null ? null : (_name.staticElement as FieldElement);
+
+  @override
+  Token get endToken => _name.endToken;
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata => _name.beginToken;
+
+  /**
+   * Return the name of the constant.
+   */
+  SimpleIdentifier get name => _name;
+
+  /**
+   * Set the name of the constant to the given [name].
+   */
+  void set name(SimpleIdentifier name) {
+    _name = _becomeParentOf(name);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitEnumConstantDeclaration(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_name, visitor);
+  }
+}
+
+/**
+ * The declaration of an enumeration.
+ *
+ * > enumType ::=
+ * >     metadata 'enum' [SimpleIdentifier] '{' [SimpleIdentifier] (',' [SimpleIdentifier])* (',')? '}'
+ */
+class EnumDeclaration extends NamedCompilationUnitMember {
+  /**
+   * The 'enum' keyword.
+   */
+  Token enumKeyword;
+
+  /**
+   * The left curly bracket.
+   */
+  Token leftBracket;
+
+  /**
+   * The enumeration constants being declared.
+   */
+  NodeList<EnumConstantDeclaration> _constants;
+
+  /**
+   * The right curly bracket.
+   */
+  Token rightBracket;
+
+  /**
+   * Initialize a newly created enumeration declaration. Either or both of the
+   * [comment] and [metadata] can be `null` if the declaration does not have the
+   * corresponding attribute. The list of [constants] must contain at least one
+   * value.
+   */
+  EnumDeclaration(Comment comment, List<Annotation> metadata, this.enumKeyword,
+      SimpleIdentifier name, this.leftBracket,
+      List<EnumConstantDeclaration> constants, this.rightBracket)
+      : super(comment, metadata, name) {
+    _constants = new NodeList<EnumConstantDeclaration>(this, constants);
+  }
+
+  @override
+  // TODO(brianwilkerson) Add commas?
+  Iterable get childEntities => super._childEntities
+    ..add(enumKeyword)
+    ..add(_name)
+    ..add(leftBracket)
+    ..addAll(_constants)
+    ..add(rightBracket);
+
+  /**
+   * Return the enumeration constants being declared.
+   */
+  NodeList<EnumConstantDeclaration> get constants => _constants;
+
+  @override
+  ClassElement get element =>
+      _name != null ? (_name.staticElement as ClassElement) : null;
+
+  @override
+  Token get endToken => rightBracket;
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata => enumKeyword;
+
+  /**
+   * Return the token for the 'enum' keyword, or `null` if there is no
+   * 'enum' keyword.
+   */
+  @deprecated // Use "this.enumKeyword"
+  Token get keyword => enumKeyword;
+
+  /**
+   * Set the token for the 'enum' keyword to the given [token].
+   */
+  @deprecated // Use "this.enumKeyword"
+  set keyword(Token token) {
+    enumKeyword = token;
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitEnumDeclaration(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_name, visitor);
+    _constants.accept(visitor);
+  }
+}
+
+/**
+ * Ephemeral identifiers are created as needed to mimic the presence of an empty
+ * identifier.
+ */
+class EphemeralIdentifier extends SimpleIdentifier {
+  EphemeralIdentifier(AstNode parent, int location)
+      : super(new StringToken(TokenType.IDENTIFIER, "", location)) {
+    parent._becomeParentOf(this);
+  }
+}
+
+/**
+ * An export directive.
+ *
+ * > exportDirective ::=
+ * >     [Annotation] 'export' [StringLiteral] [Combinator]* ';'
+ */
+class ExportDirective extends NamespaceDirective {
+  /**
+   * Initialize a newly created export directive. Either or both of the
+   * [comment] and [metadata] can be `null` if the directive does not have the
+   * corresponding attribute. The list of [combinators] can be `null` if there
+   * are no combinators.
+   */
+  ExportDirective(Comment comment, List<Annotation> metadata, Token keyword,
+      StringLiteral libraryUri, List<Combinator> combinators, Token semicolon)
+      : super(comment, metadata, keyword, libraryUri, combinators, semicolon);
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(_uri)
+    ..addAll(combinators)
+    ..add(semicolon);
+
+  @override
+  ExportElement get element => super.element as ExportElement;
+
+  @override
+  LibraryElement get uriElement {
+    if (element != null) {
+      return element.exportedLibrary;
+    }
+    return null;
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitExportDirective(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    combinators.accept(visitor);
+  }
+}
+
+/**
+ * A node that represents an expression.
+ *
+ * > expression ::=
+ * >     [AssignmentExpression]
+ * >   | [ConditionalExpression] cascadeSection*
+ * >   | [ThrowExpression]
+ */
+abstract class Expression extends AstNode {
+  /**
+   * An empty list of expressions.
+   */
+  @deprecated // Use "Expression.EMPTY_LIST"
+  static const List<Expression> EMPTY_ARRAY = EMPTY_LIST;
+
+  /**
+   * An empty list of expressions.
+   */
+  static const List<Expression> EMPTY_LIST = const <Expression>[];
+
+  /**
+   * The static type of this expression, or `null` if the AST structure has not
+   * been resolved.
+   */
+  DartType staticType;
+
+  /**
+   * The propagated type of this expression, or `null` if type propagation has
+   * not been performed on the AST structure.
+   */
+  DartType propagatedType;
+
+  /**
+   * Return the best parameter element information available for this
+   * expression. If type propagation was able to find a better parameter element
+   * than static analysis, that type will be returned. Otherwise, the result of
+   * static analysis will be returned.
+   */
+  ParameterElement get bestParameterElement {
+    ParameterElement propagatedElement = propagatedParameterElement;
+    if (propagatedElement != null) {
+      return propagatedElement;
+    }
+    return staticParameterElement;
+  }
+
+  /**
+   * Return the best type information available for this expression. If type
+   * propagation was able to find a better type than static analysis, that type
+   * will be returned. Otherwise, the result of static analysis will be
+   * returned. If no type analysis has been performed, then the type 'dynamic'
+   * will be returned.
+   */
+  DartType get bestType {
+    if (propagatedType != null) {
+      return propagatedType;
+    } else if (staticType != null) {
+      return staticType;
+    }
+    return DynamicTypeImpl.instance;
+  }
+
+  /**
+   * Return `true` if this expression is syntactically valid for the LHS of an
+   * [AssignmentExpression].
+   */
+  bool get isAssignable => false;
+
+  /**
+   * Return the precedence of this expression. The precedence is a positive
+   * integer value that defines how the source code is parsed into an AST. For
+   * example `a * b + c` is parsed as `(a * b) + c` because the precedence of
+   * `*` is greater than the precedence of `+`.
+   *
+   * Clients should not assume that returned values will stay the same, they
+   * might change as result of specification change. Only relative order should
+   * be used.
+   */
+  int get precedence;
+
+  /**
+   * If this expression is an argument to an invocation, and the AST structure
+   * has been resolved, and the function being invoked is known based on
+   * propagated type information, and this expression corresponds to one of the
+   * parameters of the function being invoked, then return the parameter element
+   * representing the parameter to which the value of this expression will be
+   * bound. Otherwise, return `null`.
+   */
+  ParameterElement get propagatedParameterElement {
+    AstNode parent = this.parent;
+    if (parent is ArgumentList) {
+      return parent._getPropagatedParameterElementFor(this);
+    } else if (parent is IndexExpression) {
+      IndexExpression indexExpression = parent;
+      if (identical(indexExpression.index, this)) {
+        return indexExpression._propagatedParameterElementForIndex;
+      }
+    } else if (parent is BinaryExpression) {
+      BinaryExpression binaryExpression = parent;
+      if (identical(binaryExpression.rightOperand, this)) {
+        return binaryExpression._propagatedParameterElementForRightOperand;
+      }
+    } else if (parent is AssignmentExpression) {
+      AssignmentExpression assignmentExpression = parent;
+      if (identical(assignmentExpression.rightHandSide, this)) {
+        return assignmentExpression._propagatedParameterElementForRightHandSide;
+      }
+    } else if (parent is PrefixExpression) {
+      return parent._propagatedParameterElementForOperand;
+    } else if (parent is PostfixExpression) {
+      return parent._propagatedParameterElementForOperand;
+    }
+    return null;
+  }
+
+  /**
+   * If this expression is an argument to an invocation, and the AST structure
+   * has been resolved, and the function being invoked is known based on static
+   * type information, and this expression corresponds to one of the parameters
+   * of the function being invoked, then return the parameter element
+   * representing the parameter to which the value of this expression will be
+   * bound. Otherwise, return `null`.
+   */
+  ParameterElement get staticParameterElement {
+    AstNode parent = this.parent;
+    if (parent is ArgumentList) {
+      return parent._getStaticParameterElementFor(this);
+    } else if (parent is IndexExpression) {
+      IndexExpression indexExpression = parent;
+      if (identical(indexExpression.index, this)) {
+        return indexExpression._staticParameterElementForIndex;
+      }
+    } else if (parent is BinaryExpression) {
+      BinaryExpression binaryExpression = parent;
+      if (identical(binaryExpression.rightOperand, this)) {
+        return binaryExpression._staticParameterElementForRightOperand;
+      }
+    } else if (parent is AssignmentExpression) {
+      AssignmentExpression assignmentExpression = parent;
+      if (identical(assignmentExpression.rightHandSide, this)) {
+        return assignmentExpression._staticParameterElementForRightHandSide;
+      }
+    } else if (parent is PrefixExpression) {
+      return parent._staticParameterElementForOperand;
+    } else if (parent is PostfixExpression) {
+      return parent._staticParameterElementForOperand;
+    }
+    return null;
+  }
+}
+
+/**
+ * A function body consisting of a single expression.
+ *
+ * > expressionFunctionBody ::=
+ * >     'async'? '=>' [Expression] ';'
+ */
+class ExpressionFunctionBody extends FunctionBody {
+  /**
+   * The token representing the 'async' keyword, or `null` if there is no such
+   * keyword.
+   */
+  Token keyword;
+
+  /**
+   * The token introducing the expression that represents the body of the
+   * function.
+   */
+  Token functionDefinition;
+
+  /**
+   * The expression representing the body of the function.
+   */
+  Expression _expression;
+
+  /**
+   * The semicolon terminating the statement.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created function body consisting of a block of
+   * statements. The [keyword] can be `null` if the function body is not an
+   * async function body.
+   */
+  ExpressionFunctionBody(this.keyword, this.functionDefinition,
+      Expression expression, this.semicolon) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get beginToken {
+    if (keyword != null) {
+      return keyword;
+    }
+    return functionDefinition;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(keyword)
+    ..add(functionDefinition)
+    ..add(_expression)
+    ..add(semicolon);
+
+  @override
+  Token get endToken {
+    if (semicolon != null) {
+      return semicolon;
+    }
+    return _expression.endToken;
+  }
+
+  /**
+   * Return the expression representing the body of the function.
+   */
+  Expression get expression => _expression;
+
+  /**
+   * Set the expression representing the body of the function to the given
+   * [expression].
+   */
+  void set expression(Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  bool get isAsynchronous => keyword != null;
+
+  @override
+  bool get isSynchronous => keyword == null;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitExpressionFunctionBody(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_expression, visitor);
+  }
+}
+
+/**
+ * An expression used as a statement.
+ *
+ * > expressionStatement ::=
+ * >     [Expression]? ';'
+ */
+class ExpressionStatement extends Statement {
+  /**
+   * The expression that comprises the statement.
+   */
+  Expression _expression;
+
+  /**
+   * The semicolon terminating the statement, or `null` if the expression is a
+   * function expression and therefore isn't followed by a semicolon.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created expression statement.
+   */
+  ExpressionStatement(Expression expression, this.semicolon) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get beginToken => _expression.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_expression)
+    ..add(semicolon);
+
+  @override
+  Token get endToken {
+    if (semicolon != null) {
+      return semicolon;
+    }
+    return _expression.endToken;
+  }
+
+  /**
+   * Return the expression that comprises the statement.
+   */
+  Expression get expression => _expression;
+
+  /**
+   * Set the expression that comprises the statement to the given [expression].
+   */
+  void set expression(Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  bool get isSynthetic => _expression.isSynthetic && semicolon.isSynthetic;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitExpressionStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_expression, visitor);
+  }
+}
+
+/**
+ * The "extends" clause in a class declaration.
+ *
+ * > extendsClause ::=
+ * >     'extends' [TypeName]
+ */
+class ExtendsClause extends AstNode {
+  /**
+   * The token representing the 'extends' keyword.
+   */
+  Token extendsKeyword;
+
+  /**
+   * The name of the class that is being extended.
+   */
+  TypeName _superclass;
+
+  /**
+   * Initialize a newly created extends clause.
+   */
+  ExtendsClause(this.extendsKeyword, TypeName superclass) {
+    _superclass = _becomeParentOf(superclass);
+  }
+
+  @override
+  Token get beginToken => extendsKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(extendsKeyword)
+    ..add(_superclass);
+
+  @override
+  Token get endToken => _superclass.endToken;
+
+  /**
+   * Return the token for the 'extends' keyword.
+   */
+  @deprecated // Use "this.extendsKeyword"
+  Token get keyword => extendsKeyword;
+
+  /**
+   * Set the token for the 'extends' keyword to the given [token].
+   */
+  @deprecated // Use "this.extendsKeyword"
+  set keyword(Token token) {
+    extendsKeyword = token;
+  }
+
+  /**
+   * Return the name of the class that is being extended.
+   */
+  TypeName get superclass => _superclass;
+
+  /**
+   * Set the name of the class that is being extended to the given [name].
+   */
+  void set superclass(TypeName name) {
+    _superclass = _becomeParentOf(name);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitExtendsClause(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_superclass, visitor);
+  }
+}
+
+/**
+ * The declaration of one or more fields of the same type.
+ *
+ * > fieldDeclaration ::=
+ * >     'static'? [VariableDeclarationList] ';'
+ */
+class FieldDeclaration extends ClassMember {
+  /**
+   * The token representing the 'static' keyword, or `null` if the fields are
+   * not static.
+   */
+  Token staticKeyword;
+
+  /**
+   * The fields being declared.
+   */
+  VariableDeclarationList _fieldList;
+
+  /**
+   * The semicolon terminating the declaration.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created field declaration. Either or both of the
+   * [comment] and [metadata] can be `null` if the declaration does not have the
+   * corresponding attribute. The [staticKeyword] can be `null` if the field is
+   * not a static field.
+   */
+  FieldDeclaration(Comment comment, List<Annotation> metadata,
+      this.staticKeyword, VariableDeclarationList fieldList, this.semicolon)
+      : super(comment, metadata) {
+    _fieldList = _becomeParentOf(fieldList);
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(staticKeyword)
+    ..add(_fieldList)
+    ..add(semicolon);
+
+  @override
+  Element get element => null;
+
+  @override
+  Token get endToken => semicolon;
+
+  /**
+   * Return the fields being declared.
+   */
+  VariableDeclarationList get fields => _fieldList;
+
+  /**
+   * Set the fields being declared to the given list of [fields].
+   */
+  void set fields(VariableDeclarationList fields) {
+    _fieldList = _becomeParentOf(fields);
+  }
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata {
+    if (staticKeyword != null) {
+      return staticKeyword;
+    }
+    return _fieldList.beginToken;
+  }
+
+  /**
+   * Return `true` if the fields are declared to be static.
+   */
+  bool get isStatic => staticKeyword != null;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitFieldDeclaration(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_fieldList, visitor);
+  }
+}
+
+/**
+ * A field formal parameter.
+ *
+ * > fieldFormalParameter ::=
+ * >     ('final' [TypeName] | 'const' [TypeName] | 'var' | [TypeName])?
+ * >     'this' '.' [SimpleIdentifier] [FormalParameterList]?
+ */
+class FieldFormalParameter extends NormalFormalParameter {
+  /**
+   * The token representing either the 'final', 'const' or 'var' keyword, or
+   * `null` if no keyword was used.
+   */
+  Token keyword;
+
+  /**
+   * The name of the declared type of the parameter, or `null` if the parameter
+   * does not have a declared type.
+   */
+  TypeName _type;
+
+  /**
+   * The token representing the 'this' keyword.
+   */
+  Token thisKeyword;
+
+  /**
+   * The token representing the period.
+   */
+  Token period;
+
+  /**
+   * The parameters of the function-typed parameter, or `null` if this is not a
+   * function-typed field formal parameter.
+   */
+  FormalParameterList _parameters;
+
+  /**
+   * Initialize a newly created formal parameter. Either or both of the
+   * [comment] and [metadata] can be `null` if the parameter does not have the
+   * corresponding attribute. The [keyword] can be `null` if there is a type.
+   * The [type] must be `null` if the keyword is 'var'. The [thisKeyword] and
+   * [period] can be `null` if the keyword 'this' was not provided.  The
+   * [parameters] can be `null` if this is not a function-typed field formal
+   * parameter.
+   */
+  FieldFormalParameter(Comment comment, List<Annotation> metadata, this.keyword,
+      TypeName type, this.thisKeyword, this.period, SimpleIdentifier identifier,
+      FormalParameterList parameters)
+      : super(comment, metadata, identifier) {
+    _type = _becomeParentOf(type);
+    _parameters = _becomeParentOf(parameters);
+  }
+
+  @override
+  Token get beginToken {
+    if (keyword != null) {
+      return keyword;
+    } else if (_type != null) {
+      return _type.beginToken;
+    }
+    return thisKeyword;
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(keyword)
+    ..add(_type)
+    ..add(thisKeyword)
+    ..add(period)
+    ..add(identifier)
+    ..add(_parameters);
+
+  @override
+  Token get endToken {
+    if (_parameters != null) {
+      return _parameters.endToken;
+    }
+    return identifier.endToken;
+  }
+
+  @override
+  bool get isConst => (keyword is KeywordToken) &&
+      (keyword as KeywordToken).keyword == Keyword.CONST;
+
+  @override
+  bool get isFinal => (keyword is KeywordToken) &&
+      (keyword as KeywordToken).keyword == Keyword.FINAL;
+
+  /**
+   * Return the parameters of the function-typed parameter, or `null` if this is
+   * not a function-typed field formal parameter.
+   */
+  FormalParameterList get parameters => _parameters;
+
+  /**
+   * Set the parameters of the function-typed parameter to the given
+   * [parameters].
+   */
+  void set parameters(FormalParameterList parameters) {
+    _parameters = _becomeParentOf(parameters);
+  }
+
+  /**
+   * Return the token representing the 'this' keyword.
+   */
+  @deprecated // Use "this.thisKeyword"
+  Token get thisToken => thisKeyword;
+
+  /**
+   * Set the token representing the 'this' keyword to the given [token].
+   */
+  @deprecated // Use "this.thisKeyword"
+  set thisToken(Token token) {
+    thisKeyword = token;
+  }
+
+  /**
+   * Return the name of the declared type of the parameter, or `null` if the
+   * parameter does not have a declared type. Note that if this is a
+   * function-typed field formal parameter this is the return type of the
+   * function.
+   */
+  TypeName get type => _type;
+
+  /**
+   * Set the name of the declared type of the parameter to the given [typeName].
+   */
+  void set type(TypeName typeName) {
+    _type = _becomeParentOf(typeName);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitFieldFormalParameter(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_type, visitor);
+    _safelyVisitChild(identifier, visitor);
+    _safelyVisitChild(_parameters, visitor);
+  }
+}
+
+/**
+ * A for-each statement.
+ *
+ * > forEachStatement ::=
+ * >     'await'? 'for' '(' [DeclaredIdentifier] 'in' [Expression] ')' [Block]
+ * >   | 'await'? 'for' '(' [SimpleIdentifier] 'in' [Expression] ')' [Block]
+ */
+class ForEachStatement extends Statement {
+  /**
+   * The token representing the 'await' keyword, or `null` if there is no
+   * 'await' keyword.
+   */
+  Token awaitKeyword;
+
+  /**
+   * The token representing the 'for' keyword.
+   */
+  Token forKeyword;
+
+  /**
+   * The left parenthesis.
+   */
+  Token leftParenthesis;
+
+  /**
+   * The declaration of the loop variable, or `null` if the loop variable is a
+   * simple identifier.
+   */
+  DeclaredIdentifier _loopVariable;
+
+  /**
+   * The loop variable, or `null` if the loop variable is declared in the 'for'.
+   */
+  SimpleIdentifier _identifier;
+
+  /**
+   * The token representing the 'in' keyword.
+   */
+  Token inKeyword;
+
+  /**
+   * The expression evaluated to produce the iterator.
+   */
+  Expression _iterable;
+
+  /**
+   * The right parenthesis.
+   */
+  Token rightParenthesis;
+
+  /**
+   * The body of the loop.
+   */
+  Statement _body;
+
+  /**
+   * Initialize a newly created for-each statement. The [awaitKeyword] can be
+   * `null` if this is not an asynchronous for loop.
+   */
+  ForEachStatement.con1(this.awaitKeyword, this.forKeyword,
+      this.leftParenthesis, DeclaredIdentifier loopVariable, this.inKeyword,
+      Expression iterator, this.rightParenthesis, Statement body) {
+    _loopVariable = _becomeParentOf(loopVariable);
+    _iterable = _becomeParentOf(iterator);
+    _body = _becomeParentOf(body);
+  }
+
+  /**
+   * Initialize a newly created for-each statement. The [awaitKeyword] can be
+   * `null` if this is not an asynchronous for loop.
+   */
+  ForEachStatement.con2(this.awaitKeyword, this.forKeyword,
+      this.leftParenthesis, SimpleIdentifier identifier, this.inKeyword,
+      Expression iterator, this.rightParenthesis, Statement body) {
+    _identifier = _becomeParentOf(identifier);
+    _iterable = _becomeParentOf(iterator);
+    _body = _becomeParentOf(body);
+  }
+
+  @override
+  Token get beginToken => forKeyword;
+
+  /**
+   * Return the body of the loop.
+   */
+  Statement get body => _body;
+
+  /**
+   * Set the body of the loop to the given [statement].
+   */
+  void set body(Statement statement) {
+    _body = _becomeParentOf(statement);
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(awaitKeyword)
+    ..add(forKeyword)
+    ..add(leftParenthesis)
+    ..add(_loopVariable)
+    ..add(_identifier)
+    ..add(inKeyword)
+    ..add(_iterable)
+    ..add(rightParenthesis)
+    ..add(_body);
+
+  @override
+  Token get endToken => _body.endToken;
+
+  /**
+   * Return the loop variable, or `null` if the loop variable is declared in the
+   * 'for'.
+   */
+  SimpleIdentifier get identifier => _identifier;
+
+  /**
+   * Set the loop variable to the given [identifier].
+   */
+  void set identifier(SimpleIdentifier identifier) {
+    _identifier = _becomeParentOf(identifier);
+  }
+
+  /**
+   * Return the expression evaluated to produce the iterator.
+   */
+  Expression get iterable => _iterable;
+
+  /**
+   * Set the expression evaluated to produce the iterator to the given
+   * [expression].
+   */
+  void set iterable(Expression expression) {
+    _iterable = _becomeParentOf(expression);
+  }
+
+  /**
+   * Return the expression evaluated to produce the iterator.
+   */
+  @deprecated // Use "this.iterable"
+  Expression get iterator => iterable;
+
+  /**
+   * Return the declaration of the loop variable, or `null` if the loop variable
+   * is a simple identifier.
+   */
+  DeclaredIdentifier get loopVariable => _loopVariable;
+
+  /**
+   * Set the declaration of the loop variable to the given [variable].
+   */
+  void set loopVariable(DeclaredIdentifier variable) {
+    _loopVariable = _becomeParentOf(variable);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitForEachStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_loopVariable, visitor);
+    _safelyVisitChild(_identifier, visitor);
+    _safelyVisitChild(_iterable, visitor);
+    _safelyVisitChild(_body, visitor);
+  }
+}
+
+/**
+ * A node representing a parameter to a function.
+ *
+ * > formalParameter ::=
+ * >     [NormalFormalParameter]
+ * >   | [DefaultFormalParameter]
+ */
+abstract class FormalParameter extends AstNode {
+  /**
+   * Return the element representing this parameter, or `null` if this parameter
+   * has not been resolved.
+   */
+  ParameterElement get element {
+    SimpleIdentifier identifier = this.identifier;
+    if (identifier == null) {
+      return null;
+    }
+    return identifier.staticElement as ParameterElement;
+  }
+
+  /**
+   * Return the name of the parameter being declared.
+   */
+  SimpleIdentifier get identifier;
+
+  /**
+   * Return `true` if this parameter was declared with the 'const' modifier.
+   */
+  bool get isConst;
+
+  /**
+   * Return `true` if this parameter was declared with the 'final' modifier.
+   * Parameters that are declared with the 'const' modifier will return `false`
+   * even though they are implicitly final.
+   */
+  bool get isFinal;
+
+  /**
+   * Return the kind of this parameter.
+   */
+  ParameterKind get kind;
+}
+
+/**
+ * The formal parameter list of a method declaration, function declaration, or
+ * function type alias.
+ *
+ * While the grammar requires all optional formal parameters to follow all of
+ * the normal formal parameters and at most one grouping of optional formal
+ * parameters, this class does not enforce those constraints. All parameters are
+ * flattened into a single list, which can have any or all kinds of parameters
+ * (normal, named, and positional) in any order.
+ *
+ * > formalParameterList ::=
+ * >     '(' ')'
+ * >   | '(' normalFormalParameters (',' optionalFormalParameters)? ')'
+ * >   | '(' optionalFormalParameters ')'
+ * >
+ * > normalFormalParameters ::=
+ * >     [NormalFormalParameter] (',' [NormalFormalParameter])*
+ * >
+ * > optionalFormalParameters ::=
+ * >     optionalPositionalFormalParameters
+ * >   | namedFormalParameters
+ * >
+ * > optionalPositionalFormalParameters ::=
+ * >     '[' [DefaultFormalParameter] (',' [DefaultFormalParameter])* ']'
+ * >
+ * > namedFormalParameters ::=
+ * >     '{' [DefaultFormalParameter] (',' [DefaultFormalParameter])* '}'
+ */
+class FormalParameterList extends AstNode {
+  /**
+   * The left parenthesis.
+   */
+  Token leftParenthesis;
+
+  /**
+   * The parameters associated with the method.
+   */
+  NodeList<FormalParameter> _parameters;
+
+  /**
+   * The left square bracket ('[') or left curly brace ('{') introducing the
+   * optional parameters, or `null` if there are no optional parameters.
+   */
+  Token leftDelimiter;
+
+  /**
+   * The right square bracket (']') or right curly brace ('}') terminating the
+   * optional parameters, or `null` if there are no optional parameters.
+   */
+  Token rightDelimiter;
+
+  /**
+   * The right parenthesis.
+   */
+  Token rightParenthesis;
+
+  /**
+   * Initialize a newly created parameter list. The list of [parameters] can be
+   * `null` if there are no parameters. The [leftDelimiter] and [rightDelimiter]
+   * can be `null` if there are no optional parameters.
+   */
+  FormalParameterList(this.leftParenthesis, List<FormalParameter> parameters,
+      this.leftDelimiter, this.rightDelimiter, this.rightParenthesis) {
+    _parameters = new NodeList<FormalParameter>(this, parameters);
+  }
+
+  @override
+  Token get beginToken => leftParenthesis;
+
+  @override
+  Iterable get childEntities {
+    // TODO(paulberry): include commas.
+    ChildEntities result = new ChildEntities()..add(leftParenthesis);
+    bool leftDelimiterNeeded = leftDelimiter != null;
+    for (FormalParameter parameter in _parameters) {
+      if (leftDelimiterNeeded && leftDelimiter.offset < parameter.offset) {
+        result.add(leftDelimiter);
+        leftDelimiterNeeded = false;
+      }
+      result.add(parameter);
+    }
+    return result
+      ..add(rightDelimiter)
+      ..add(rightParenthesis);
+  }
+
+  @override
+  Token get endToken => rightParenthesis;
+
+  /**
+   * Return a list containing the elements representing the parameters in this
+   * list. The list will contain `null`s if the parameters in this list have not
+   * been resolved.
+   */
+  List<ParameterElement> get parameterElements {
+    int count = _parameters.length;
+    List<ParameterElement> types = new List<ParameterElement>(count);
+    for (int i = 0; i < count; i++) {
+      types[i] = _parameters[i].element;
+    }
+    return types;
+  }
+
+  /**
+   * Return the parameters associated with the method.
+   */
+  NodeList<FormalParameter> get parameters => _parameters;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitFormalParameterList(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _parameters.accept(visitor);
+  }
+}
+
+/**
+ * A for statement.
+ *
+ * > forStatement ::=
+ * >     'for' '(' forLoopParts ')' [Statement]
+ * >
+ * > forLoopParts ::=
+ * >     forInitializerStatement ';' [Expression]? ';' [Expression]?
+ * >
+ * > forInitializerStatement ::=
+ * >     [DefaultFormalParameter]
+ * >   | [Expression]?
+ */
+class ForStatement extends Statement {
+  /**
+   * The token representing the 'for' keyword.
+   */
+  Token forKeyword;
+
+  /**
+   * The left parenthesis.
+   */
+  Token leftParenthesis;
+
+  /**
+   * The declaration of the loop variables, or `null` if there are no variables.
+   * Note that a for statement cannot have both a variable list and an
+   * initialization expression, but can validly have neither.
+   */
+  VariableDeclarationList _variableList;
+
+  /**
+   * The initialization expression, or `null` if there is no initialization
+   * expression. Note that a for statement cannot have both a variable list and
+   * an initialization expression, but can validly have neither.
+   */
+  Expression _initialization;
+
+  /**
+   * The semicolon separating the initializer and the condition.
+   */
+  Token leftSeparator;
+
+  /**
+   * The condition used to determine when to terminate the loop, or `null` if
+   * there is no condition.
+   */
+  Expression _condition;
+
+  /**
+   * The semicolon separating the condition and the updater.
+   */
+  Token rightSeparator;
+
+  /**
+   * The list of expressions run after each execution of the loop body.
+   */
+  NodeList<Expression> _updaters;
+
+  /**
+   * The right parenthesis.
+   */
+  Token rightParenthesis;
+
+  /**
+   * The body of the loop.
+   */
+  Statement _body;
+
+  /**
+   * Initialize a newly created for statement. Either the [variableList] or the
+   * [initialization] must be `null`. Either the [condition] and the list of
+   * [updaters] can be `null` if the loop does not have the corresponding
+   * attribute.
+   */
+  ForStatement(this.forKeyword, this.leftParenthesis,
+      VariableDeclarationList variableList, Expression initialization,
+      this.leftSeparator, Expression condition, this.rightSeparator,
+      List<Expression> updaters, this.rightParenthesis, Statement body) {
+    _variableList = _becomeParentOf(variableList);
+    _initialization = _becomeParentOf(initialization);
+    _condition = _becomeParentOf(condition);
+    _updaters = new NodeList<Expression>(this, updaters);
+    _body = _becomeParentOf(body);
+  }
+
+  @override
+  Token get beginToken => forKeyword;
+
+  /**
+   * Return the body of the loop.
+   */
+  Statement get body => _body;
+
+  /**
+   * Set the body of the loop to the given [statement].
+   */
+  void set body(Statement statement) {
+    _body = _becomeParentOf(statement);
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(forKeyword)
+    ..add(leftParenthesis)
+    ..add(_variableList)
+    ..add(_initialization)
+    ..add(leftSeparator)
+    ..add(_condition)
+    ..add(rightSeparator)
+    ..addAll(_updaters)
+    ..add(rightParenthesis)
+    ..add(_body);
+
+  /**
+   * Return the condition used to determine when to terminate the loop, or
+   * `null` if there is no condition.
+   */
+  Expression get condition => _condition;
+
+  /**
+   * Set the condition used to determine when to terminate the loop to the given
+   * [expression].
+   */
+  void set condition(Expression expression) {
+    _condition = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get endToken => _body.endToken;
+
+  /**
+   * Return the initialization expression, or `null` if there is no
+   * initialization expression.
+   */
+  Expression get initialization => _initialization;
+
+  /**
+   * Set the initialization expression to the given [expression].
+   */
+  void set initialization(Expression initialization) {
+    _initialization = _becomeParentOf(initialization);
+  }
+
+  /**
+   * Return the list of expressions run after each execution of the loop body.
+   */
+  NodeList<Expression> get updaters => _updaters;
+
+  /**
+   * Return the declaration of the loop variables, or `null` if there are no
+   * variables.
+   */
+  VariableDeclarationList get variables => _variableList;
+
+  /**
+   * Set the declaration of the loop variables to the given [variableList].
+   */
+  void set variables(VariableDeclarationList variableList) {
+    _variableList = _becomeParentOf(variableList);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitForStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_variableList, visitor);
+    _safelyVisitChild(_initialization, visitor);
+    _safelyVisitChild(_condition, visitor);
+    _updaters.accept(visitor);
+    _safelyVisitChild(_body, visitor);
+  }
+}
+
+/**
+ * A node representing the body of a function or method.
+ *
+ * > functionBody ::=
+ * >     [BlockFunctionBody]
+ * >   | [EmptyFunctionBody]
+ * >   | [ExpressionFunctionBody]
+ */
+abstract class FunctionBody extends AstNode {
+  /**
+   * Return `true` if this function body is asynchronous.
+   */
+  bool get isAsynchronous => false;
+
+  /**
+   * Return `true` if this function body is a generator.
+   */
+  bool get isGenerator => false;
+
+  /**
+   * Return `true` if this function body is synchronous.
+   */
+  bool get isSynchronous => true;
+
+  /**
+   * Return the token representing the 'async' or 'sync' keyword, or `null` if
+   * there is no such keyword.
+   */
+  Token get keyword => null;
+
+  /**
+   * Return the star following the 'async' or 'sync' keyword, or `null` if there
+   * is no star.
+   */
+  Token get star => null;
+}
+
+/**
+ * A top-level declaration.
+ *
+ * > functionDeclaration ::=
+ * >     'external' functionSignature
+ * >   | functionSignature [FunctionBody]
+ * >
+ * > functionSignature ::=
+ * >     [Type]? ('get' | 'set')? [SimpleIdentifier] [FormalParameterList]
+ */
+class FunctionDeclaration extends NamedCompilationUnitMember {
+  /**
+   * The token representing the 'external' keyword, or `null` if this is not an
+   * external function.
+   */
+  Token externalKeyword;
+
+  /**
+   * The return type of the function, or `null` if no return type was declared.
+   */
+  TypeName _returnType;
+
+  /**
+   * The token representing the 'get' or 'set' keyword, or `null` if this is a
+   * function declaration rather than a property declaration.
+   */
+  Token propertyKeyword;
+
+  /**
+   * The function expression being wrapped.
+   */
+  FunctionExpression _functionExpression;
+
+  /**
+   * Initialize a newly created function declaration. Either or both of the
+   * [comment] and [metadata] can be `null` if the function does not have the
+   * corresponding attribute. The [externalKeyword] can be `null` if the
+   * function is not an external function. The [returnType] can be `null` if no
+   * return type was specified. The [propertyKeyword] can be `null` if the
+   * function is neither a getter or a setter.
+   */
+  FunctionDeclaration(Comment comment, List<Annotation> metadata,
+      this.externalKeyword, TypeName returnType, this.propertyKeyword,
+      SimpleIdentifier name, FunctionExpression functionExpression)
+      : super(comment, metadata, name) {
+    _returnType = _becomeParentOf(returnType);
+    _functionExpression = _becomeParentOf(functionExpression);
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(externalKeyword)
+    ..add(_returnType)
+    ..add(propertyKeyword)
+    ..add(_name)
+    ..add(_functionExpression);
+
+  @override
+  ExecutableElement get element =>
+      _name != null ? (_name.staticElement as ExecutableElement) : null;
+
+  @override
+  Token get endToken => _functionExpression.endToken;
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata {
+    if (externalKeyword != null) {
+      return externalKeyword;
+    } else if (_returnType != null) {
+      return _returnType.beginToken;
+    } else if (propertyKeyword != null) {
+      return propertyKeyword;
+    } else if (_name != null) {
+      return _name.beginToken;
+    }
+    return _functionExpression.beginToken;
+  }
+
+  /**
+   * Return the function expression being wrapped.
+   */
+  FunctionExpression get functionExpression => _functionExpression;
+
+  /**
+   * Set the function expression being wrapped to the given
+   * [functionExpression].
+   */
+  void set functionExpression(FunctionExpression functionExpression) {
+    _functionExpression = _becomeParentOf(functionExpression);
+  }
+
+  /**
+   * Return `true` if this function declares a getter.
+   */
+  bool get isGetter => propertyKeyword != null &&
+      (propertyKeyword as KeywordToken).keyword == Keyword.GET;
+
+  /**
+   * Return `true` if this function declares a setter.
+   */
+  bool get isSetter => propertyKeyword != null &&
+      (propertyKeyword as KeywordToken).keyword == Keyword.SET;
+
+  /**
+   * Return the return type of the function, or `null` if no return type was
+   * declared.
+   */
+  TypeName get returnType => _returnType;
+
+  /**
+   * Set the return type of the function to the given [returnType].
+   */
+  void set returnType(TypeName returnType) {
+    _returnType = _becomeParentOf(returnType);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitFunctionDeclaration(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_returnType, visitor);
+    _safelyVisitChild(_name, visitor);
+    _safelyVisitChild(_functionExpression, visitor);
+  }
+}
+
+/**
+ * A [FunctionDeclaration] used as a statement.
+ */
+class FunctionDeclarationStatement extends Statement {
+  /**
+   * The function declaration being wrapped.
+   */
+  FunctionDeclaration _functionDeclaration;
+
+  /**
+   * Initialize a newly created function declaration statement.
+   */
+  FunctionDeclarationStatement(FunctionDeclaration functionDeclaration) {
+    _functionDeclaration = _becomeParentOf(functionDeclaration);
+  }
+
+  @override
+  Token get beginToken => _functionDeclaration.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..add(_functionDeclaration);
+
+  @override
+  Token get endToken => _functionDeclaration.endToken;
+
+  /**
+   * Return the function declaration being wrapped.
+   */
+  FunctionDeclaration get functionDeclaration => _functionDeclaration;
+
+  /**
+   * Set the function declaration being wrapped to the given
+   * [functionDeclaration].
+   */
+  void set functionDeclaration(FunctionDeclaration functionDeclaration) {
+    _functionDeclaration = _becomeParentOf(functionDeclaration);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitFunctionDeclarationStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_functionDeclaration, visitor);
+  }
+}
+
+/**
+ * A function expression.
+ *
+ * > functionExpression ::=
+ * >     [FormalParameterList] [FunctionBody]
+ */
+class FunctionExpression extends Expression {
+  /**
+   * The parameters associated with the function.
+   */
+  FormalParameterList _parameters;
+
+  /**
+   * The body of the function, or `null` if this is an external function.
+   */
+  FunctionBody _body;
+
+  /**
+   * The element associated with the function, or `null` if the AST structure
+   * has not been resolved.
+   */
+  ExecutableElement element;
+
+  /**
+   * Initialize a newly created function declaration.
+   */
+  FunctionExpression(FormalParameterList parameters, FunctionBody body) {
+    _parameters = _becomeParentOf(parameters);
+    _body = _becomeParentOf(body);
+  }
+
+  @override
+  Token get beginToken {
+    if (_parameters != null) {
+      return _parameters.beginToken;
+    } else if (_body != null) {
+      return _body.beginToken;
+    }
+    // This should never be reached because external functions must be named,
+    // hence either the body or the name should be non-null.
+    throw new IllegalStateException("Non-external functions must have a body");
+  }
+
+  /**
+   * Return the body of the function, or `null` if this is an external function.
+   */
+  FunctionBody get body => _body;
+
+  /**
+   * Set the body of the function to the given [functionBody].
+   */
+  void set body(FunctionBody functionBody) {
+    _body = _becomeParentOf(functionBody);
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_parameters)
+    ..add(_body);
+
+  @override
+  Token get endToken {
+    if (_body != null) {
+      return _body.endToken;
+    } else if (_parameters != null) {
+      return _parameters.endToken;
+    }
+    // This should never be reached because external functions must be named,
+    // hence either the body or the name should be non-null.
+    throw new IllegalStateException("Non-external functions must have a body");
+  }
+
+  /**
+   * Return the parameters associated with the function.
+   */
+  FormalParameterList get parameters => _parameters;
+
+  /**
+   * Set the parameters associated with the function to the given list of
+   * [parameters].
+   */
+  void set parameters(FormalParameterList parameters) {
+    _parameters = _becomeParentOf(parameters);
+  }
+
+  @override
+  int get precedence => 16;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitFunctionExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_parameters, visitor);
+    _safelyVisitChild(_body, visitor);
+  }
+}
+
+/**
+ * The invocation of a function resulting from evaluating an expression.
+ * Invocations of methods and other forms of functions are represented by
+ * [MethodInvocation] nodes. Invocations of getters and setters are represented
+ * by either [PrefixedIdentifier] or [PropertyAccess] nodes.
+ *
+ * > functionExpressionInvoction ::=
+ * >     [Expression] [ArgumentList]
+ */
+class FunctionExpressionInvocation extends Expression {
+  /**
+   * The expression producing the function being invoked.
+   */
+  Expression _function;
+
+  /**
+   * The list of arguments to the function.
+   */
+  ArgumentList _argumentList;
+
+  /**
+   * The element associated with the function being invoked based on static type
+   * information, or `null` if the AST structure has not been resolved or the
+   * function could not be resolved.
+   */
+  ExecutableElement staticElement;
+
+  /**
+   * The element associated with the function being invoked based on propagated
+   * type information, or `null` if the AST structure has not been resolved or
+   * the function could not be resolved.
+   */
+  ExecutableElement propagatedElement;
+
+  /**
+   * Initialize a newly created function expression invocation.
+   */
+  FunctionExpressionInvocation(Expression function, ArgumentList argumentList) {
+    _function = _becomeParentOf(function);
+    _argumentList = _becomeParentOf(argumentList);
+  }
+
+  /**
+   * Return the list of arguments to the method.
+   */
+  ArgumentList get argumentList => _argumentList;
+
+  /**
+   * Set the list of arguments to the method to the given [argumentList].
+   */
+  void set argumentList(ArgumentList argumentList) {
+    _argumentList = _becomeParentOf(argumentList);
+  }
+
+  @override
+  Token get beginToken => _function.beginToken;
+
+  /**
+   * Return the best element available for the function being invoked. If
+   * resolution was able to find a better element based on type propagation,
+   * that element will be returned. Otherwise, the element found using the
+   * result of static analysis will be returned. If resolution has not been
+   * performed, then `null` will be returned.
+   */
+  ExecutableElement get bestElement {
+    ExecutableElement element = propagatedElement;
+    if (element == null) {
+      element = staticElement;
+    }
+    return element;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_function)
+    ..add(_argumentList);
+
+  @override
+  Token get endToken => _argumentList.endToken;
+
+  /**
+   * Return the expression producing the function being invoked.
+   */
+  Expression get function => _function;
+
+  /**
+   * Set the expression producing the function being invoked to the given
+   * [expression].
+   */
+  void set function(Expression expression) {
+    _function = _becomeParentOf(expression);
+  }
+
+  @override
+  int get precedence => 15;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitFunctionExpressionInvocation(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_function, visitor);
+    _safelyVisitChild(_argumentList, visitor);
+  }
+}
+
+/**
+ * A function type alias.
+ *
+ * > functionTypeAlias ::=
+ * >     functionPrefix [TypeParameterList]? [FormalParameterList] ';'
+ * >
+ * > functionPrefix ::=
+ * >     [TypeName]? [SimpleIdentifier]
+ */
+class FunctionTypeAlias extends TypeAlias {
+  /**
+   * The name of the return type of the function type being defined, or `null`
+   * if no return type was given.
+   */
+  TypeName _returnType;
+
+  /**
+   * The type parameters for the function type, or `null` if the function type
+   * does not have any type parameters.
+   */
+  TypeParameterList _typeParameters;
+
+  /**
+   * The parameters associated with the function type.
+   */
+  FormalParameterList _parameters;
+
+  /**
+   * Initialize a newly created function type alias. Either or both of the
+   * [comment] and [metadata] can be `null` if the function does not have the
+   * corresponding attribute. The [returnType] can be `null` if no return type
+   * was specified. The [typeParameters] can be `null` if the function has no
+   * type parameters.
+   */
+  FunctionTypeAlias(Comment comment, List<Annotation> metadata, Token keyword,
+      TypeName returnType, SimpleIdentifier name,
+      TypeParameterList typeParameters, FormalParameterList parameters,
+      Token semicolon)
+      : super(comment, metadata, keyword, name, semicolon) {
+    _returnType = _becomeParentOf(returnType);
+    _typeParameters = _becomeParentOf(typeParameters);
+    _parameters = _becomeParentOf(parameters);
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(typedefKeyword)
+    ..add(_returnType)
+    ..add(_name)
+    ..add(_typeParameters)
+    ..add(_parameters)
+    ..add(semicolon);
+
+  @override
+  FunctionTypeAliasElement get element =>
+      _name != null ? (_name.staticElement as FunctionTypeAliasElement) : null;
+
+  /**
+   * Return the parameters associated with the function type.
+   */
+  FormalParameterList get parameters => _parameters;
+
+  /**
+   * Set the parameters associated with the function type to the given list of
+   * [parameters].
+   */
+  void set parameters(FormalParameterList parameters) {
+    _parameters = _becomeParentOf(parameters);
+  }
+
+  /**
+   * Return the name of the return type of the function type being defined, or
+   * `null` if no return type was given.
+   */
+  TypeName get returnType => _returnType;
+
+  /**
+   * Set the name of the return type of the function type being defined to the
+   * given [typeName].
+   */
+  void set returnType(TypeName typeName) {
+    _returnType = _becomeParentOf(typeName);
+  }
+
+  /**
+   * Return the type parameters for the function type, or `null` if the function
+   * type does not have any type parameters.
+   */
+  TypeParameterList get typeParameters => _typeParameters;
+
+  /**
+   * Set the type parameters for the function type to the given list of
+   * [typeParameters].
+   */
+  void set typeParameters(TypeParameterList typeParameters) {
+    _typeParameters = _becomeParentOf(typeParameters);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitFunctionTypeAlias(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_returnType, visitor);
+    _safelyVisitChild(_name, visitor);
+    _safelyVisitChild(_typeParameters, visitor);
+    _safelyVisitChild(_parameters, visitor);
+  }
+}
+
+/**
+ * A function-typed formal parameter.
+ *
+ * > functionSignature ::=
+ * >     [TypeName]? [SimpleIdentifier] [FormalParameterList]
+ */
+class FunctionTypedFormalParameter extends NormalFormalParameter {
+  /**
+   * The return type of the function, or `null` if the function does not have a
+   * return type.
+   */
+  TypeName _returnType;
+
+  /**
+   * The parameters of the function-typed parameter.
+   */
+  FormalParameterList _parameters;
+
+  /**
+   * Initialize a newly created formal parameter. Either or both of the
+   * [comment] and [metadata] can be `null` if the parameter does not have the
+   * corresponding attribute. The [returnType] can be `null` if no return type
+   * was specified.
+   */
+  FunctionTypedFormalParameter(Comment comment, List<Annotation> metadata,
+      TypeName returnType, SimpleIdentifier identifier,
+      FormalParameterList parameters)
+      : super(comment, metadata, identifier) {
+    _returnType = _becomeParentOf(returnType);
+    _parameters = _becomeParentOf(parameters);
+  }
+
+  @override
+  Token get beginToken {
+    if (_returnType != null) {
+      return _returnType.beginToken;
+    }
+    return identifier.beginToken;
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(_returnType)
+    ..add(identifier)
+    ..add(parameters);
+
+  @override
+  Token get endToken => _parameters.endToken;
+
+  @override
+  bool get isConst => false;
+
+  @override
+  bool get isFinal => false;
+
+  /**
+   * Return the parameters of the function-typed parameter.
+   */
+  FormalParameterList get parameters => _parameters;
+
+  /**
+   * Set the parameters of the function-typed parameter to the given
+   * [parameters].
+   */
+  void set parameters(FormalParameterList parameters) {
+    _parameters = _becomeParentOf(parameters);
+  }
+
+  /**
+   * Return the return type of the function, or `null` if the function does not
+   * have a return type.
+   */
+  TypeName get returnType => _returnType;
+
+  /**
+   * Set the return type of the function to the given [type].
+   */
+  void set returnType(TypeName type) {
+    _returnType = _becomeParentOf(type);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitFunctionTypedFormalParameter(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_returnType, visitor);
+    _safelyVisitChild(identifier, visitor);
+    _safelyVisitChild(_parameters, visitor);
+  }
+}
+
+/**
+ * An AST visitor that will recursively visit all of the nodes in an AST
+ * structure (like instances of the class [RecursiveAstVisitor]). In addition,
+ * when a node of a specific type is visited not only will the visit method for
+ * that specific type of node be invoked, but additional methods for the
+ * superclasses of that node will also be invoked. For example, using an
+ * instance of this class to visit a [Block] will cause the method [visitBlock]
+ * to be invoked but will also cause the methods [visitStatement] and
+ * [visitNode] to be subsequently invoked. This allows visitors to be written
+ * that visit all statements without needing to override the visit method for
+ * each of the specific subclasses of [Statement].
+ *
+ * Subclasses that override a visit method must either invoke the overridden
+ * visit method or explicitly invoke the more general visit method. Failure to
+ * do so will cause the visit methods for superclasses of the node to not be
+ * invoked and will cause the children of the visited node to not be visited.
+ */
+class GeneralizingAstVisitor<R> implements AstVisitor<R> {
+  @override
+  R visitAdjacentStrings(AdjacentStrings node) => visitStringLiteral(node);
+
+  R visitAnnotatedNode(AnnotatedNode node) => visitNode(node);
+
+  @override
+  R visitAnnotation(Annotation node) => visitNode(node);
+
+  @override
+  R visitArgumentList(ArgumentList node) => visitNode(node);
+
+  @override
+  R visitAsExpression(AsExpression node) => visitExpression(node);
+
+  @override
+  R visitAssertStatement(AssertStatement node) => visitStatement(node);
+
+  @override
+  R visitAssignmentExpression(AssignmentExpression node) =>
+      visitExpression(node);
+
+  @override
+  R visitAwaitExpression(AwaitExpression node) => visitExpression(node);
+
+  @override
+  R visitBinaryExpression(BinaryExpression node) => visitExpression(node);
+
+  @override
+  R visitBlock(Block node) => visitStatement(node);
+
+  @override
+  R visitBlockFunctionBody(BlockFunctionBody node) => visitFunctionBody(node);
+
+  @override
+  R visitBooleanLiteral(BooleanLiteral node) => visitLiteral(node);
+
+  @override
+  R visitBreakStatement(BreakStatement node) => visitStatement(node);
+
+  @override
+  R visitCascadeExpression(CascadeExpression node) => visitExpression(node);
+
+  @override
+  R visitCatchClause(CatchClause node) => visitNode(node);
+
+  @override
+  R visitClassDeclaration(ClassDeclaration node) =>
+      visitNamedCompilationUnitMember(node);
+
+  R visitClassMember(ClassMember node) => visitDeclaration(node);
+
+  @override
+  R visitClassTypeAlias(ClassTypeAlias node) => visitTypeAlias(node);
+
+  R visitCombinator(Combinator node) => visitNode(node);
+
+  @override
+  R visitComment(Comment node) => visitNode(node);
+
+  @override
+  R visitCommentReference(CommentReference node) => visitNode(node);
+
+  @override
+  R visitCompilationUnit(CompilationUnit node) => visitNode(node);
+
+  R visitCompilationUnitMember(CompilationUnitMember node) =>
+      visitDeclaration(node);
+
+  @override
+  R visitConditionalExpression(ConditionalExpression node) =>
+      visitExpression(node);
+
+  @override
+  R visitConstructorDeclaration(ConstructorDeclaration node) =>
+      visitClassMember(node);
+
+  @override
+  R visitConstructorFieldInitializer(ConstructorFieldInitializer node) =>
+      visitConstructorInitializer(node);
+
+  R visitConstructorInitializer(ConstructorInitializer node) => visitNode(node);
+
+  @override
+  R visitConstructorName(ConstructorName node) => visitNode(node);
+
+  @override
+  R visitContinueStatement(ContinueStatement node) => visitStatement(node);
+
+  R visitDeclaration(Declaration node) => visitAnnotatedNode(node);
+
+  @override
+  R visitDeclaredIdentifier(DeclaredIdentifier node) => visitDeclaration(node);
+
+  @override
+  R visitDefaultFormalParameter(DefaultFormalParameter node) =>
+      visitFormalParameter(node);
+
+  R visitDirective(Directive node) => visitAnnotatedNode(node);
+
+  @override
+  R visitDoStatement(DoStatement node) => visitStatement(node);
+
+  @override
+  R visitDoubleLiteral(DoubleLiteral node) => visitLiteral(node);
+
+  @override
+  R visitEmptyFunctionBody(EmptyFunctionBody node) => visitFunctionBody(node);
+
+  @override
+  R visitEmptyStatement(EmptyStatement node) => visitStatement(node);
+
+  @override
+  R visitEnumConstantDeclaration(EnumConstantDeclaration node) =>
+      visitDeclaration(node);
+
+  @override
+  R visitEnumDeclaration(EnumDeclaration node) =>
+      visitNamedCompilationUnitMember(node);
+
+  @override
+  R visitExportDirective(ExportDirective node) => visitNamespaceDirective(node);
+
+  R visitExpression(Expression node) => visitNode(node);
+
+  @override
+  R visitExpressionFunctionBody(ExpressionFunctionBody node) =>
+      visitFunctionBody(node);
+
+  @override
+  R visitExpressionStatement(ExpressionStatement node) => visitStatement(node);
+
+  @override
+  R visitExtendsClause(ExtendsClause node) => visitNode(node);
+
+  @override
+  R visitFieldDeclaration(FieldDeclaration node) => visitClassMember(node);
+
+  @override
+  R visitFieldFormalParameter(FieldFormalParameter node) =>
+      visitNormalFormalParameter(node);
+
+  @override
+  R visitForEachStatement(ForEachStatement node) => visitStatement(node);
+
+  R visitFormalParameter(FormalParameter node) => visitNode(node);
+
+  @override
+  R visitFormalParameterList(FormalParameterList node) => visitNode(node);
+
+  @override
+  R visitForStatement(ForStatement node) => visitStatement(node);
+
+  R visitFunctionBody(FunctionBody node) => visitNode(node);
+
+  @override
+  R visitFunctionDeclaration(FunctionDeclaration node) =>
+      visitNamedCompilationUnitMember(node);
+
+  @override
+  R visitFunctionDeclarationStatement(FunctionDeclarationStatement node) =>
+      visitStatement(node);
+
+  @override
+  R visitFunctionExpression(FunctionExpression node) => visitExpression(node);
+
+  @override
+  R visitFunctionExpressionInvocation(FunctionExpressionInvocation node) =>
+      visitExpression(node);
+
+  @override
+  R visitFunctionTypeAlias(FunctionTypeAlias node) => visitTypeAlias(node);
+
+  @override
+  R visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) =>
+      visitNormalFormalParameter(node);
+
+  @override
+  R visitHideCombinator(HideCombinator node) => visitCombinator(node);
+
+  R visitIdentifier(Identifier node) => visitExpression(node);
+
+  @override
+  R visitIfStatement(IfStatement node) => visitStatement(node);
+
+  @override
+  R visitImplementsClause(ImplementsClause node) => visitNode(node);
+
+  @override
+  R visitImportDirective(ImportDirective node) => visitNamespaceDirective(node);
+
+  @override
+  R visitIndexExpression(IndexExpression node) => visitExpression(node);
+
+  @override
+  R visitInstanceCreationExpression(InstanceCreationExpression node) =>
+      visitExpression(node);
+
+  @override
+  R visitIntegerLiteral(IntegerLiteral node) => visitLiteral(node);
+
+  R visitInterpolationElement(InterpolationElement node) => visitNode(node);
+
+  @override
+  R visitInterpolationExpression(InterpolationExpression node) =>
+      visitInterpolationElement(node);
+
+  @override
+  R visitInterpolationString(InterpolationString node) =>
+      visitInterpolationElement(node);
+
+  @override
+  R visitIsExpression(IsExpression node) => visitExpression(node);
+
+  @override
+  R visitLabel(Label node) => visitNode(node);
+
+  @override
+  R visitLabeledStatement(LabeledStatement node) => visitStatement(node);
+
+  @override
+  R visitLibraryDirective(LibraryDirective node) => visitDirective(node);
+
+  @override
+  R visitLibraryIdentifier(LibraryIdentifier node) => visitIdentifier(node);
+
+  @override
+  R visitListLiteral(ListLiteral node) => visitTypedLiteral(node);
+
+  R visitLiteral(Literal node) => visitExpression(node);
+
+  @override
+  R visitMapLiteral(MapLiteral node) => visitTypedLiteral(node);
+
+  @override
+  R visitMapLiteralEntry(MapLiteralEntry node) => visitNode(node);
+
+  @override
+  R visitMethodDeclaration(MethodDeclaration node) => visitClassMember(node);
+
+  @override
+  R visitMethodInvocation(MethodInvocation node) => visitExpression(node);
+
+  R visitNamedCompilationUnitMember(NamedCompilationUnitMember node) =>
+      visitCompilationUnitMember(node);
+
+  @override
+  R visitNamedExpression(NamedExpression node) => visitExpression(node);
+
+  R visitNamespaceDirective(NamespaceDirective node) =>
+      visitUriBasedDirective(node);
+
+  @override
+  R visitNativeClause(NativeClause node) => visitNode(node);
+
+  @override
+  R visitNativeFunctionBody(NativeFunctionBody node) => visitFunctionBody(node);
+
+  R visitNode(AstNode node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  R visitNormalFormalParameter(NormalFormalParameter node) =>
+      visitFormalParameter(node);
+
+  @override
+  R visitNullLiteral(NullLiteral node) => visitLiteral(node);
+
+  @override
+  R visitParenthesizedExpression(ParenthesizedExpression node) =>
+      visitExpression(node);
+
+  @override
+  R visitPartDirective(PartDirective node) => visitUriBasedDirective(node);
+
+  @override
+  R visitPartOfDirective(PartOfDirective node) => visitDirective(node);
+
+  @override
+  R visitPostfixExpression(PostfixExpression node) => visitExpression(node);
+
+  @override
+  R visitPrefixedIdentifier(PrefixedIdentifier node) => visitIdentifier(node);
+
+  @override
+  R visitPrefixExpression(PrefixExpression node) => visitExpression(node);
+
+  @override
+  R visitPropertyAccess(PropertyAccess node) => visitExpression(node);
+
+  @override
+  R visitRedirectingConstructorInvocation(
+          RedirectingConstructorInvocation node) =>
+      visitConstructorInitializer(node);
+
+  @override
+  R visitRethrowExpression(RethrowExpression node) => visitExpression(node);
+
+  @override
+  R visitReturnStatement(ReturnStatement node) => visitStatement(node);
+
+  @override
+  R visitScriptTag(ScriptTag scriptTag) => visitNode(scriptTag);
+
+  @override
+  R visitShowCombinator(ShowCombinator node) => visitCombinator(node);
+
+  @override
+  R visitSimpleFormalParameter(SimpleFormalParameter node) =>
+      visitNormalFormalParameter(node);
+
+  @override
+  R visitSimpleIdentifier(SimpleIdentifier node) => visitIdentifier(node);
+
+  @override
+  R visitSimpleStringLiteral(SimpleStringLiteral node) =>
+      visitSingleStringLiteral(node);
+
+  R visitSingleStringLiteral(SingleStringLiteral node) =>
+      visitStringLiteral(node);
+
+  R visitStatement(Statement node) => visitNode(node);
+
+  @override
+  R visitStringInterpolation(StringInterpolation node) =>
+      visitSingleStringLiteral(node);
+
+  R visitStringLiteral(StringLiteral node) => visitLiteral(node);
+
+  @override
+  R visitSuperConstructorInvocation(SuperConstructorInvocation node) =>
+      visitConstructorInitializer(node);
+
+  @override
+  R visitSuperExpression(SuperExpression node) => visitExpression(node);
+
+  @override
+  R visitSwitchCase(SwitchCase node) => visitSwitchMember(node);
+
+  @override
+  R visitSwitchDefault(SwitchDefault node) => visitSwitchMember(node);
+
+  R visitSwitchMember(SwitchMember node) => visitNode(node);
+
+  @override
+  R visitSwitchStatement(SwitchStatement node) => visitStatement(node);
+
+  @override
+  R visitSymbolLiteral(SymbolLiteral node) => visitLiteral(node);
+
+  @override
+  R visitThisExpression(ThisExpression node) => visitExpression(node);
+
+  @override
+  R visitThrowExpression(ThrowExpression node) => visitExpression(node);
+
+  @override
+  R visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) =>
+      visitCompilationUnitMember(node);
+
+  @override
+  R visitTryStatement(TryStatement node) => visitStatement(node);
+
+  R visitTypeAlias(TypeAlias node) => visitNamedCompilationUnitMember(node);
+
+  @override
+  R visitTypeArgumentList(TypeArgumentList node) => visitNode(node);
+
+  R visitTypedLiteral(TypedLiteral node) => visitLiteral(node);
+
+  @override
+  R visitTypeName(TypeName node) => visitNode(node);
+
+  @override
+  R visitTypeParameter(TypeParameter node) => visitNode(node);
+
+  @override
+  R visitTypeParameterList(TypeParameterList node) => visitNode(node);
+
+  R visitUriBasedDirective(UriBasedDirective node) => visitDirective(node);
+
+  @override
+  R visitVariableDeclaration(VariableDeclaration node) =>
+      visitDeclaration(node);
+
+  @override
+  R visitVariableDeclarationList(VariableDeclarationList node) =>
+      visitNode(node);
+
+  @override
+  R visitVariableDeclarationStatement(VariableDeclarationStatement node) =>
+      visitStatement(node);
+
+  @override
+  R visitWhileStatement(WhileStatement node) => visitStatement(node);
+
+  @override
+  R visitWithClause(WithClause node) => visitNode(node);
+
+  @override
+  R visitYieldStatement(YieldStatement node) => visitStatement(node);
+}
+
+class GeneralizingAstVisitor_BreadthFirstVisitor
+    extends GeneralizingAstVisitor<Object> {
+  final BreadthFirstVisitor BreadthFirstVisitor_this;
+
+  GeneralizingAstVisitor_BreadthFirstVisitor(this.BreadthFirstVisitor_this)
+      : super();
+
+  @override
+  Object visitNode(AstNode node) {
+    BreadthFirstVisitor_this._queue.add(node);
+    return null;
+  }
+}
+
+/**
+ * A combinator that restricts the names being imported to those that are not in
+ * a given list.
+ *
+ * > hideCombinator ::=
+ * >     'hide' [SimpleIdentifier] (',' [SimpleIdentifier])*
+ */
+class HideCombinator extends Combinator {
+  /**
+   * The list of names from the library that are hidden by this combinator.
+   */
+  NodeList<SimpleIdentifier> _hiddenNames;
+
+  /**
+   * Initialize a newly created import show combinator.
+   */
+  HideCombinator(Token keyword, List<SimpleIdentifier> hiddenNames)
+      : super(keyword) {
+    _hiddenNames = new NodeList<SimpleIdentifier>(this, hiddenNames);
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(keyword)
+    ..addAll(_hiddenNames);
+
+  @override
+  Token get endToken => _hiddenNames.endToken;
+
+  /**
+   * Return the list of names from the library that are hidden by this
+   * combinator.
+   */
+  NodeList<SimpleIdentifier> get hiddenNames => _hiddenNames;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitHideCombinator(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _hiddenNames.accept(visitor);
+  }
+}
+
+/**
+ * A node that represents an identifier.
+ *
+ * > identifier ::=
+ * >     [SimpleIdentifier]
+ * >   | [PrefixedIdentifier]
+ */
+abstract class Identifier extends Expression {
+  /**
+   * Return the best element available for this operator. If resolution was able
+   * to find a better element based on type propagation, that element will be
+   * returned. Otherwise, the element found using the result of static analysis
+   * will be returned. If resolution has not been performed, then `null` will be
+   * returned.
+   */
+  Element get bestElement;
+
+  @override
+  bool get isAssignable => true;
+
+  /**
+   * Return the lexical representation of the identifier.
+   */
+  String get name;
+
+  /**
+   * Return the element associated with this identifier based on propagated type
+   * information, or `null` if the AST structure has not been resolved or if
+   * this identifier could not be resolved. One example of the latter case is an
+   * identifier that is not defined within the scope in which it appears.
+   */
+  Element get propagatedElement;
+
+  /**
+   * Return the element associated with this identifier based on static type
+   * information, or `null` if the AST structure has not been resolved or if
+   * this identifier could not be resolved. One example of the latter case is an
+   * identifier that is not defined within the scope in which it appears
+   */
+  Element get staticElement;
+
+  /**
+   * Return `true` if the given [name] is visible only within the library in
+   * which it is declared.
+   */
+  static bool isPrivateName(String name) =>
+      StringUtilities.startsWithChar(name, 0x5F);
+}
+
+/**
+ * An if statement.
+ *
+ * > ifStatement ::=
+ * >     'if' '(' [Expression] ')' [Statement] ('else' [Statement])?
+ */
+class IfStatement extends Statement {
+  /**
+   * The token representing the 'if' keyword.
+   */
+  Token ifKeyword;
+
+  /**
+   * The left parenthesis.
+   */
+  Token leftParenthesis;
+
+  /**
+   * The condition used to determine which of the statements is executed next.
+   */
+  Expression _condition;
+
+  /**
+   * The right parenthesis.
+   */
+  Token rightParenthesis;
+
+  /**
+   * The statement that is executed if the condition evaluates to `true`.
+   */
+  Statement _thenStatement;
+
+  /**
+   * The token representing the 'else' keyword, or `null` if there is no else
+   * statement.
+   */
+  Token elseKeyword;
+
+  /**
+   * The statement that is executed if the condition evaluates to `false`, or
+   * `null` if there is no else statement.
+   */
+  Statement _elseStatement;
+
+  /**
+   * Initialize a newly created if statement. The [elseKeyword] and
+   * [elseStatement] can be `null` if there is no else clause.
+   */
+  IfStatement(this.ifKeyword, this.leftParenthesis, Expression condition,
+      this.rightParenthesis, Statement thenStatement, this.elseKeyword,
+      Statement elseStatement) {
+    _condition = _becomeParentOf(condition);
+    _thenStatement = _becomeParentOf(thenStatement);
+    _elseStatement = _becomeParentOf(elseStatement);
+  }
+
+  @override
+  Token get beginToken => ifKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(ifKeyword)
+    ..add(leftParenthesis)
+    ..add(_condition)
+    ..add(rightParenthesis)
+    ..add(_thenStatement)
+    ..add(elseKeyword)
+    ..add(_elseStatement);
+
+  /**
+   * Return the condition used to determine which of the statements is executed
+   * next.
+   */
+  Expression get condition => _condition;
+
+  /**
+   * Set the condition used to determine which of the statements is executed
+   * next to the given [expression].
+   */
+  void set condition(Expression expression) {
+    _condition = _becomeParentOf(expression);
+  }
+
+  /**
+   * Return the statement that is executed if the condition evaluates to
+   * `false`, or `null` if there is no else statement.
+   */
+  Statement get elseStatement => _elseStatement;
+
+  /**
+   * Set the statement that is executed if the condition evaluates to `false`
+   * to the given [statement].
+   */
+  void set elseStatement(Statement statement) {
+    _elseStatement = _becomeParentOf(statement);
+  }
+
+  @override
+  Token get endToken {
+    if (_elseStatement != null) {
+      return _elseStatement.endToken;
+    }
+    return _thenStatement.endToken;
+  }
+
+  /**
+   * Return the statement that is executed if the condition evaluates to `true`.
+   */
+  Statement get thenStatement => _thenStatement;
+
+  /**
+   * Set the statement that is executed if the condition evaluates to `true` to
+   * the given [statement].
+   */
+  void set thenStatement(Statement statement) {
+    _thenStatement = _becomeParentOf(statement);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitIfStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_condition, visitor);
+    _safelyVisitChild(_thenStatement, visitor);
+    _safelyVisitChild(_elseStatement, visitor);
+  }
+}
+
+/**
+ * The "implements" clause in an class declaration.
+ *
+ * > implementsClause ::=
+ * >     'implements' [TypeName] (',' [TypeName])*
+ */
+class ImplementsClause extends AstNode {
+  /**
+   * The token representing the 'implements' keyword.
+   */
+  Token implementsKeyword;
+
+  /**
+   * The interfaces that are being implemented.
+   */
+  NodeList<TypeName> _interfaces;
+
+  /**
+   * Initialize a newly created implements clause.
+   */
+  ImplementsClause(this.implementsKeyword, List<TypeName> interfaces) {
+    _interfaces = new NodeList<TypeName>(this, interfaces);
+  }
+
+  @override
+  Token get beginToken => implementsKeyword;
+
+  /**
+   * TODO(paulberry): add commas.
+   */
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(implementsKeyword)
+    ..addAll(interfaces);
+
+  @override
+  Token get endToken => _interfaces.endToken;
+
+  /**
+   * Return the list of the interfaces that are being implemented.
+   */
+  NodeList<TypeName> get interfaces => _interfaces;
+
+  /**
+   * Return the token representing the 'implements' keyword.
+   */
+  @deprecated // Use "this.implementsKeyword"
+  Token get keyword => implementsKeyword;
+
+  /**
+   * Set the token representing the 'implements' keyword to the given [token].
+   */
+  @deprecated // Use "this.implementsKeyword"
+  set keyword(Token token) {
+    implementsKeyword = token;
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitImplementsClause(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _interfaces.accept(visitor);
+  }
+}
+
+/**
+ * An import directive.
+ *
+ * > importDirective ::=
+ * >     [Annotation] 'import' [StringLiteral] ('as' identifier)? [Combinator]* ';'
+ * >   | [Annotation] 'import' [StringLiteral] 'deferred' 'as' identifier [Combinator]* ';'
+ */
+class ImportDirective extends NamespaceDirective {
+  static Comparator<ImportDirective> COMPARATOR = (ImportDirective import1,
+      ImportDirective import2) {
+    //
+    // uri
+    //
+    StringLiteral uri1 = import1.uri;
+    StringLiteral uri2 = import2.uri;
+    String uriStr1 = uri1.stringValue;
+    String uriStr2 = uri2.stringValue;
+    if (uriStr1 != null || uriStr2 != null) {
+      if (uriStr1 == null) {
+        return -1;
+      } else if (uriStr2 == null) {
+        return 1;
+      } else {
+        int compare = uriStr1.compareTo(uriStr2);
+        if (compare != 0) {
+          return compare;
+        }
+      }
+    }
+    //
+    // as
+    //
+    SimpleIdentifier prefix1 = import1.prefix;
+    SimpleIdentifier prefix2 = import2.prefix;
+    String prefixStr1 = prefix1 != null ? prefix1.name : null;
+    String prefixStr2 = prefix2 != null ? prefix2.name : null;
+    if (prefixStr1 != null || prefixStr2 != null) {
+      if (prefixStr1 == null) {
+        return -1;
+      } else if (prefixStr2 == null) {
+        return 1;
+      } else {
+        int compare = prefixStr1.compareTo(prefixStr2);
+        if (compare != 0) {
+          return compare;
+        }
+      }
+    }
+    //
+    // hides and shows
+    //
+    NodeList<Combinator> combinators1 = import1.combinators;
+    List<String> allHides1 = new List<String>();
+    List<String> allShows1 = new List<String>();
+    for (Combinator combinator in combinators1) {
+      if (combinator is HideCombinator) {
+        NodeList<SimpleIdentifier> hides = combinator.hiddenNames;
+        for (SimpleIdentifier simpleIdentifier in hides) {
+          allHides1.add(simpleIdentifier.name);
+        }
+      } else {
+        NodeList<SimpleIdentifier> shows =
+            (combinator as ShowCombinator).shownNames;
+        for (SimpleIdentifier simpleIdentifier in shows) {
+          allShows1.add(simpleIdentifier.name);
+        }
+      }
+    }
+    NodeList<Combinator> combinators2 = import2.combinators;
+    List<String> allHides2 = new List<String>();
+    List<String> allShows2 = new List<String>();
+    for (Combinator combinator in combinators2) {
+      if (combinator is HideCombinator) {
+        NodeList<SimpleIdentifier> hides = combinator.hiddenNames;
+        for (SimpleIdentifier simpleIdentifier in hides) {
+          allHides2.add(simpleIdentifier.name);
+        }
+      } else {
+        NodeList<SimpleIdentifier> shows =
+            (combinator as ShowCombinator).shownNames;
+        for (SimpleIdentifier simpleIdentifier in shows) {
+          allShows2.add(simpleIdentifier.name);
+        }
+      }
+    }
+    // test lengths of combinator lists first
+    if (allHides1.length != allHides2.length) {
+      return allHides1.length - allHides2.length;
+    }
+    if (allShows1.length != allShows2.length) {
+      return allShows1.length - allShows2.length;
+    }
+    // next ensure that the lists are equivalent
+    if (!javaCollectionContainsAll(allHides1, allHides2)) {
+      return -1;
+    }
+    if (!javaCollectionContainsAll(allShows1, allShows2)) {
+      return -1;
+    }
+    return 0;
+  };
+
+  /**
+   * The token representing the 'deferred' keyword, or `null` if the imported is
+   * not deferred.
+   */
+  Token deferredKeyword;
+
+  /**
+   * The token representing the 'as' keyword, or `null` if the imported names are
+   * not prefixed.
+   */
+  Token asKeyword;
+
+  /**
+   * The prefix to be used with the imported names, or `null` if the imported
+   * names are not prefixed.
+   */
+  SimpleIdentifier _prefix;
+
+  /**
+   * Initialize a newly created import directive. Either or both of the
+   * [comment] and [metadata] can be `null` if the function does not have the
+   * corresponding attribute. The [deferredKeyword] can be `null` if the import
+   * is not deferred. The [asKeyword] and [prefix] can be `null` if the import
+   * does not specify a prefix. The list of [combinators] can be `null` if there
+   * are no combinators.
+   */
+  ImportDirective(Comment comment, List<Annotation> metadata, Token keyword,
+      StringLiteral libraryUri, this.deferredKeyword, this.asKeyword,
+      SimpleIdentifier prefix, List<Combinator> combinators, Token semicolon)
+      : super(comment, metadata, keyword, libraryUri, combinators, semicolon) {
+    _prefix = _becomeParentOf(prefix);
+  }
+
+  /**
+   * The token representing the 'as' token, or `null` if the imported names are
+   * not prefixed.
+   */
+  @deprecated // Use "this.asKeyword"
+  Token get asToken => asKeyword;
+
+  /**
+   * The token representing the 'as' token to the given token.
+   */
+  @deprecated // Use "this.asKeyword"
+  set asToken(Token token) {
+    asKeyword = token;
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(_uri)
+    ..add(deferredKeyword)
+    ..add(asKeyword)
+    ..add(_prefix)
+    ..addAll(combinators)
+    ..add(semicolon);
+
+  /**
+   * Return the token representing the 'deferred' token, or `null` if the
+   * imported is not deferred.
+   */
+  @deprecated // Use "this.deferredKeyword"
+  Token get deferredToken => deferredKeyword;
+
+  /**
+   * Set the token representing the 'deferred' token to the given token.
+   */
+  @deprecated // Use "this.deferredKeyword"
+  set deferredToken(Token token) {
+    deferredKeyword = token;
+  }
+
+  @override
+  ImportElement get element => super.element as ImportElement;
+
+  /**
+   * Return the prefix to be used with the imported names, or `null` if the
+   * imported names are not prefixed.
+   */
+  SimpleIdentifier get prefix => _prefix;
+
+  /**
+   * Set the prefix to be used with the imported names to the given [identifier].
+   */
+  void set prefix(SimpleIdentifier identifier) {
+    _prefix = _becomeParentOf(identifier);
+  }
+
+  @override
+  LibraryElement get uriElement {
+    ImportElement element = this.element;
+    if (element == null) {
+      return null;
+    }
+    return element.importedLibrary;
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitImportDirective(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_prefix, visitor);
+    combinators.accept(visitor);
+  }
+}
+
+/**
+ * An object that will clone any AST structure that it visits. The cloner will
+ * clone the structure, replacing the specified ASTNode with a new ASTNode,
+ * mapping the old token stream to a new token stream, and preserving resolution
+ * results.
+ */
+class IncrementalAstCloner implements AstVisitor<AstNode> {
+  /**
+   * The node to be replaced during the cloning process.
+   */
+  final AstNode _oldNode;
+
+  /**
+   * The replacement node used during the cloning process.
+   */
+  final AstNode _newNode;
+
+  /**
+   * A mapping of old tokens to new tokens used during the cloning process.
+   */
+  final TokenMap _tokenMap;
+
+  /**
+   * Construct a new instance that will replace the [oldNode] with the [newNode]
+   * in the process of cloning an existing AST structure. The [tokenMap] is a
+   * mapping of old tokens to new tokens.
+   */
+  IncrementalAstCloner(this._oldNode, this._newNode, this._tokenMap);
+
+  @override
+  AdjacentStrings visitAdjacentStrings(AdjacentStrings node) =>
+      new AdjacentStrings(_cloneNodeList(node.strings));
+
+  @override
+  Annotation visitAnnotation(Annotation node) {
+    Annotation copy = new Annotation(_mapToken(node.atSign),
+        _cloneNode(node.name), _mapToken(node.period),
+        _cloneNode(node.constructorName), _cloneNode(node.arguments));
+    copy.element = node.element;
+    return copy;
+  }
+
+  @override
+  ArgumentList visitArgumentList(ArgumentList node) => new ArgumentList(
+      _mapToken(node.leftParenthesis), _cloneNodeList(node.arguments),
+      _mapToken(node.rightParenthesis));
+
+  @override
+  AsExpression visitAsExpression(AsExpression node) {
+    AsExpression copy = new AsExpression(_cloneNode(node.expression),
+        _mapToken(node.asOperator), _cloneNode(node.type));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  AstNode visitAssertStatement(AssertStatement node) => new AssertStatement(
+      _mapToken(node.assertKeyword), _mapToken(node.leftParenthesis),
+      _cloneNode(node.condition), _mapToken(node.rightParenthesis),
+      _mapToken(node.semicolon));
+
+  @override
+  AssignmentExpression visitAssignmentExpression(AssignmentExpression node) {
+    AssignmentExpression copy = new AssignmentExpression(
+        _cloneNode(node.leftHandSide), _mapToken(node.operator),
+        _cloneNode(node.rightHandSide));
+    copy.propagatedElement = node.propagatedElement;
+    copy.propagatedType = node.propagatedType;
+    copy.staticElement = node.staticElement;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  AwaitExpression visitAwaitExpression(AwaitExpression node) =>
+      new AwaitExpression(
+          _mapToken(node.awaitKeyword), _cloneNode(node.expression));
+
+  @override
+  BinaryExpression visitBinaryExpression(BinaryExpression node) {
+    BinaryExpression copy = new BinaryExpression(_cloneNode(node.leftOperand),
+        _mapToken(node.operator), _cloneNode(node.rightOperand));
+    copy.propagatedElement = node.propagatedElement;
+    copy.propagatedType = node.propagatedType;
+    copy.staticElement = node.staticElement;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  Block visitBlock(Block node) => new Block(_mapToken(node.leftBracket),
+      _cloneNodeList(node.statements), _mapToken(node.rightBracket));
+
+  @override
+  BlockFunctionBody visitBlockFunctionBody(
+      BlockFunctionBody node) => new BlockFunctionBody(
+      _mapToken(node.keyword), _mapToken(node.star), _cloneNode(node.block));
+
+  @override
+  BooleanLiteral visitBooleanLiteral(BooleanLiteral node) {
+    BooleanLiteral copy =
+        new BooleanLiteral(_mapToken(node.literal), node.value);
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  BreakStatement visitBreakStatement(BreakStatement node) => new BreakStatement(
+      _mapToken(node.breakKeyword), _cloneNode(node.label),
+      _mapToken(node.semicolon));
+
+  @override
+  CascadeExpression visitCascadeExpression(CascadeExpression node) {
+    CascadeExpression copy = new CascadeExpression(
+        _cloneNode(node.target), _cloneNodeList(node.cascadeSections));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  CatchClause visitCatchClause(CatchClause node) => new CatchClause(
+      _mapToken(node.onKeyword), _cloneNode(node.exceptionType),
+      _mapToken(node.catchKeyword), _mapToken(node.leftParenthesis),
+      _cloneNode(node.exceptionParameter), _mapToken(node.comma),
+      _cloneNode(node.stackTraceParameter), _mapToken(node.rightParenthesis),
+      _cloneNode(node.body));
+
+  @override
+  ClassDeclaration visitClassDeclaration(ClassDeclaration node) {
+    ClassDeclaration copy = new ClassDeclaration(
+        _cloneNode(node.documentationComment), _cloneNodeList(node.metadata),
+        _mapToken(node.abstractKeyword), _mapToken(node.classKeyword),
+        _cloneNode(node.name), _cloneNode(node.typeParameters),
+        _cloneNode(node.extendsClause), _cloneNode(node.withClause),
+        _cloneNode(node.implementsClause), _mapToken(node.leftBracket),
+        _cloneNodeList(node.members), _mapToken(node.rightBracket));
+    copy.nativeClause = _cloneNode(node.nativeClause);
+    return copy;
+  }
+
+  @override
+  ClassTypeAlias visitClassTypeAlias(ClassTypeAlias node) => new ClassTypeAlias(
+      _cloneNode(node.documentationComment), _cloneNodeList(node.metadata),
+      _mapToken(node.typedefKeyword), _cloneNode(node.name),
+      _cloneNode(node.typeParameters), _mapToken(node.equals),
+      _mapToken(node.abstractKeyword), _cloneNode(node.superclass),
+      _cloneNode(node.withClause), _cloneNode(node.implementsClause),
+      _mapToken(node.semicolon));
+
+  @override
+  Comment visitComment(Comment node) {
+    if (node.isDocumentation) {
+      return Comment.createDocumentationCommentWithReferences(
+          _mapTokens(node.tokens), _cloneNodeList(node.references));
+    } else if (node.isBlock) {
+      return Comment.createBlockComment(_mapTokens(node.tokens));
+    }
+    return Comment.createEndOfLineComment(_mapTokens(node.tokens));
+  }
+
+  @override
+  CommentReference visitCommentReference(CommentReference node) =>
+      new CommentReference(
+          _mapToken(node.newKeyword), _cloneNode(node.identifier));
+
+  @override
+  CompilationUnit visitCompilationUnit(CompilationUnit node) {
+    CompilationUnit copy = new CompilationUnit(_mapToken(node.beginToken),
+        _cloneNode(node.scriptTag), _cloneNodeList(node.directives),
+        _cloneNodeList(node.declarations), _mapToken(node.endToken));
+    copy.lineInfo = node.lineInfo;
+    copy.element = node.element;
+    return copy;
+  }
+
+  @override
+  ConditionalExpression visitConditionalExpression(ConditionalExpression node) {
+    ConditionalExpression copy = new ConditionalExpression(
+        _cloneNode(node.condition), _mapToken(node.question),
+        _cloneNode(node.thenExpression), _mapToken(node.colon),
+        _cloneNode(node.elseExpression));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  ConstructorDeclaration visitConstructorDeclaration(
+      ConstructorDeclaration node) {
+    ConstructorDeclaration copy = new ConstructorDeclaration(
+        _cloneNode(node.documentationComment), _cloneNodeList(node.metadata),
+        _mapToken(node.externalKeyword), _mapToken(node.constKeyword),
+        _mapToken(node.factoryKeyword), _cloneNode(node.returnType),
+        _mapToken(node.period), _cloneNode(node.name),
+        _cloneNode(node.parameters), _mapToken(node.separator),
+        _cloneNodeList(node.initializers),
+        _cloneNode(node.redirectedConstructor), _cloneNode(node.body));
+    copy.element = node.element;
+    return copy;
+  }
+
+  @override
+  ConstructorFieldInitializer visitConstructorFieldInitializer(
+      ConstructorFieldInitializer node) => new ConstructorFieldInitializer(
+      _mapToken(node.thisKeyword), _mapToken(node.period),
+      _cloneNode(node.fieldName), _mapToken(node.equals),
+      _cloneNode(node.expression));
+
+  @override
+  ConstructorName visitConstructorName(ConstructorName node) {
+    ConstructorName copy = new ConstructorName(
+        _cloneNode(node.type), _mapToken(node.period), _cloneNode(node.name));
+    copy.staticElement = node.staticElement;
+    return copy;
+  }
+
+  @override
+  ContinueStatement visitContinueStatement(ContinueStatement node) =>
+      new ContinueStatement(_mapToken(node.continueKeyword),
+          _cloneNode(node.label), _mapToken(node.semicolon));
+
+  @override
+  DeclaredIdentifier visitDeclaredIdentifier(DeclaredIdentifier node) =>
+      new DeclaredIdentifier(_cloneNode(node.documentationComment),
+          _cloneNodeList(node.metadata), _mapToken(node.keyword),
+          _cloneNode(node.type), _cloneNode(node.identifier));
+
+  @override
+  DefaultFormalParameter visitDefaultFormalParameter(
+      DefaultFormalParameter node) => new DefaultFormalParameter(
+      _cloneNode(node.parameter), node.kind, _mapToken(node.separator),
+      _cloneNode(node.defaultValue));
+
+  @override
+  DoStatement visitDoStatement(DoStatement node) => new DoStatement(
+      _mapToken(node.doKeyword), _cloneNode(node.body),
+      _mapToken(node.whileKeyword), _mapToken(node.leftParenthesis),
+      _cloneNode(node.condition), _mapToken(node.rightParenthesis),
+      _mapToken(node.semicolon));
+
+  @override
+  DoubleLiteral visitDoubleLiteral(DoubleLiteral node) {
+    DoubleLiteral copy = new DoubleLiteral(_mapToken(node.literal), node.value);
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  EmptyFunctionBody visitEmptyFunctionBody(EmptyFunctionBody node) =>
+      new EmptyFunctionBody(_mapToken(node.semicolon));
+
+  @override
+  EmptyStatement visitEmptyStatement(EmptyStatement node) =>
+      new EmptyStatement(_mapToken(node.semicolon));
+
+  @override
+  AstNode visitEnumConstantDeclaration(EnumConstantDeclaration node) =>
+      new EnumConstantDeclaration(_cloneNode(node.documentationComment),
+          _cloneNodeList(node.metadata), _cloneNode(node.name));
+
+  @override
+  AstNode visitEnumDeclaration(EnumDeclaration node) => new EnumDeclaration(
+      _cloneNode(node.documentationComment), _cloneNodeList(node.metadata),
+      _mapToken(node.enumKeyword), _cloneNode(node.name),
+      _mapToken(node.leftBracket), _cloneNodeList(node.constants),
+      _mapToken(node.rightBracket));
+
+  @override
+  ExportDirective visitExportDirective(ExportDirective node) {
+    ExportDirective copy = new ExportDirective(
+        _cloneNode(node.documentationComment), _cloneNodeList(node.metadata),
+        _mapToken(node.keyword), _cloneNode(node.uri),
+        _cloneNodeList(node.combinators), _mapToken(node.semicolon));
+    copy.element = node.element;
+    return copy;
+  }
+
+  @override
+  ExpressionFunctionBody visitExpressionFunctionBody(
+      ExpressionFunctionBody node) => new ExpressionFunctionBody(
+      _mapToken(node.keyword), _mapToken(node.functionDefinition),
+      _cloneNode(node.expression), _mapToken(node.semicolon));
+
+  @override
+  ExpressionStatement visitExpressionStatement(ExpressionStatement node) =>
+      new ExpressionStatement(
+          _cloneNode(node.expression), _mapToken(node.semicolon));
+
+  @override
+  ExtendsClause visitExtendsClause(ExtendsClause node) => new ExtendsClause(
+      _mapToken(node.extendsKeyword), _cloneNode(node.superclass));
+
+  @override
+  FieldDeclaration visitFieldDeclaration(FieldDeclaration node) =>
+      new FieldDeclaration(_cloneNode(node.documentationComment),
+          _cloneNodeList(node.metadata), _mapToken(node.staticKeyword),
+          _cloneNode(node.fields), _mapToken(node.semicolon));
+
+  @override
+  FieldFormalParameter visitFieldFormalParameter(FieldFormalParameter node) =>
+      new FieldFormalParameter(_cloneNode(node.documentationComment),
+          _cloneNodeList(node.metadata), _mapToken(node.keyword),
+          _cloneNode(node.type), _mapToken(node.thisKeyword),
+          _mapToken(node.period), _cloneNode(node.identifier),
+          _cloneNode(node.parameters));
+
+  @override
+  ForEachStatement visitForEachStatement(ForEachStatement node) {
+    DeclaredIdentifier loopVariable = node.loopVariable;
+    if (loopVariable == null) {
+      return new ForEachStatement.con2(_mapToken(node.awaitKeyword),
+          _mapToken(node.forKeyword), _mapToken(node.leftParenthesis),
+          _cloneNode(node.identifier), _mapToken(node.inKeyword),
+          _cloneNode(node.iterable), _mapToken(node.rightParenthesis),
+          _cloneNode(node.body));
+    }
+    return new ForEachStatement.con1(_mapToken(node.awaitKeyword),
+        _mapToken(node.forKeyword), _mapToken(node.leftParenthesis),
+        _cloneNode(loopVariable), _mapToken(node.inKeyword),
+        _cloneNode(node.iterable), _mapToken(node.rightParenthesis),
+        _cloneNode(node.body));
+  }
+
+  @override
+  FormalParameterList visitFormalParameterList(FormalParameterList node) =>
+      new FormalParameterList(_mapToken(node.leftParenthesis),
+          _cloneNodeList(node.parameters), _mapToken(node.leftDelimiter),
+          _mapToken(node.rightDelimiter), _mapToken(node.rightParenthesis));
+
+  @override
+  ForStatement visitForStatement(ForStatement node) => new ForStatement(
+      _mapToken(node.forKeyword), _mapToken(node.leftParenthesis),
+      _cloneNode(node.variables), _cloneNode(node.initialization),
+      _mapToken(node.leftSeparator), _cloneNode(node.condition),
+      _mapToken(node.rightSeparator), _cloneNodeList(node.updaters),
+      _mapToken(node.rightParenthesis), _cloneNode(node.body));
+
+  @override
+  FunctionDeclaration visitFunctionDeclaration(FunctionDeclaration node) =>
+      new FunctionDeclaration(_cloneNode(node.documentationComment),
+          _cloneNodeList(node.metadata), _mapToken(node.externalKeyword),
+          _cloneNode(node.returnType), _mapToken(node.propertyKeyword),
+          _cloneNode(node.name), _cloneNode(node.functionExpression));
+
+  @override
+  FunctionDeclarationStatement visitFunctionDeclarationStatement(
+          FunctionDeclarationStatement node) =>
+      new FunctionDeclarationStatement(_cloneNode(node.functionDeclaration));
+
+  @override
+  FunctionExpression visitFunctionExpression(FunctionExpression node) {
+    FunctionExpression copy = new FunctionExpression(
+        _cloneNode(node.parameters), _cloneNode(node.body));
+    copy.element = node.element;
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  FunctionExpressionInvocation visitFunctionExpressionInvocation(
+      FunctionExpressionInvocation node) {
+    FunctionExpressionInvocation copy = new FunctionExpressionInvocation(
+        _cloneNode(node.function), _cloneNode(node.argumentList));
+    copy.propagatedElement = node.propagatedElement;
+    copy.propagatedType = node.propagatedType;
+    copy.staticElement = node.staticElement;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  FunctionTypeAlias visitFunctionTypeAlias(FunctionTypeAlias node) =>
+      new FunctionTypeAlias(_cloneNode(node.documentationComment),
+          _cloneNodeList(node.metadata), _mapToken(node.typedefKeyword),
+          _cloneNode(node.returnType), _cloneNode(node.name),
+          _cloneNode(node.typeParameters), _cloneNode(node.parameters),
+          _mapToken(node.semicolon));
+
+  @override
+  FunctionTypedFormalParameter visitFunctionTypedFormalParameter(
+      FunctionTypedFormalParameter node) => new FunctionTypedFormalParameter(
+      _cloneNode(node.documentationComment), _cloneNodeList(node.metadata),
+      _cloneNode(node.returnType), _cloneNode(node.identifier),
+      _cloneNode(node.parameters));
+
+  @override
+  HideCombinator visitHideCombinator(HideCombinator node) => new HideCombinator(
+      _mapToken(node.keyword), _cloneNodeList(node.hiddenNames));
+
+  @override
+  IfStatement visitIfStatement(IfStatement node) => new IfStatement(
+      _mapToken(node.ifKeyword), _mapToken(node.leftParenthesis),
+      _cloneNode(node.condition), _mapToken(node.rightParenthesis),
+      _cloneNode(node.thenStatement), _mapToken(node.elseKeyword),
+      _cloneNode(node.elseStatement));
+
+  @override
+  ImplementsClause visitImplementsClause(ImplementsClause node) =>
+      new ImplementsClause(
+          _mapToken(node.implementsKeyword), _cloneNodeList(node.interfaces));
+
+  @override
+  ImportDirective visitImportDirective(ImportDirective node) =>
+      new ImportDirective(_cloneNode(node.documentationComment),
+          _cloneNodeList(node.metadata), _mapToken(node.keyword),
+          _cloneNode(node.uri), _mapToken(node.deferredKeyword),
+          _mapToken(node.asKeyword), _cloneNode(node.prefix),
+          _cloneNodeList(node.combinators), _mapToken(node.semicolon));
+
+  @override
+  IndexExpression visitIndexExpression(IndexExpression node) {
+    Token period = _mapToken(node.period);
+    IndexExpression copy;
+    if (period == null) {
+      copy = new IndexExpression.forTarget(_cloneNode(node.target),
+          _mapToken(node.leftBracket), _cloneNode(node.index),
+          _mapToken(node.rightBracket));
+    } else {
+      copy = new IndexExpression.forCascade(period, _mapToken(node.leftBracket),
+          _cloneNode(node.index), _mapToken(node.rightBracket));
+    }
+    copy.auxiliaryElements = node.auxiliaryElements;
+    copy.propagatedElement = node.propagatedElement;
+    copy.propagatedType = node.propagatedType;
+    copy.staticElement = node.staticElement;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  InstanceCreationExpression visitInstanceCreationExpression(
+      InstanceCreationExpression node) {
+    InstanceCreationExpression copy = new InstanceCreationExpression(
+        _mapToken(node.keyword), _cloneNode(node.constructorName),
+        _cloneNode(node.argumentList));
+    copy.propagatedType = node.propagatedType;
+    copy.staticElement = node.staticElement;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  IntegerLiteral visitIntegerLiteral(IntegerLiteral node) {
+    IntegerLiteral copy =
+        new IntegerLiteral(_mapToken(node.literal), node.value);
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  InterpolationExpression visitInterpolationExpression(
+      InterpolationExpression node) => new InterpolationExpression(
+      _mapToken(node.leftBracket), _cloneNode(node.expression),
+      _mapToken(node.rightBracket));
+
+  @override
+  InterpolationString visitInterpolationString(InterpolationString node) =>
+      new InterpolationString(_mapToken(node.contents), node.value);
+
+  @override
+  IsExpression visitIsExpression(IsExpression node) {
+    IsExpression copy = new IsExpression(_cloneNode(node.expression),
+        _mapToken(node.isOperator), _mapToken(node.notOperator),
+        _cloneNode(node.type));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  Label visitLabel(Label node) =>
+      new Label(_cloneNode(node.label), _mapToken(node.colon));
+
+  @override
+  LabeledStatement visitLabeledStatement(LabeledStatement node) =>
+      new LabeledStatement(
+          _cloneNodeList(node.labels), _cloneNode(node.statement));
+
+  @override
+  LibraryDirective visitLibraryDirective(LibraryDirective node) =>
+      new LibraryDirective(_cloneNode(node.documentationComment),
+          _cloneNodeList(node.metadata), _mapToken(node.libraryKeyword),
+          _cloneNode(node.name), _mapToken(node.semicolon));
+
+  @override
+  LibraryIdentifier visitLibraryIdentifier(LibraryIdentifier node) {
+    LibraryIdentifier copy =
+        new LibraryIdentifier(_cloneNodeList(node.components));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  ListLiteral visitListLiteral(ListLiteral node) {
+    ListLiteral copy = new ListLiteral(_mapToken(node.constKeyword),
+        _cloneNode(node.typeArguments), _mapToken(node.leftBracket),
+        _cloneNodeList(node.elements), _mapToken(node.rightBracket));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  MapLiteral visitMapLiteral(MapLiteral node) {
+    MapLiteral copy = new MapLiteral(_mapToken(node.constKeyword),
+        _cloneNode(node.typeArguments), _mapToken(node.leftBracket),
+        _cloneNodeList(node.entries), _mapToken(node.rightBracket));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  MapLiteralEntry visitMapLiteralEntry(
+      MapLiteralEntry node) => new MapLiteralEntry(
+      _cloneNode(node.key), _mapToken(node.separator), _cloneNode(node.value));
+
+  @override
+  MethodDeclaration visitMethodDeclaration(MethodDeclaration node) =>
+      new MethodDeclaration(_cloneNode(node.documentationComment),
+          _cloneNodeList(node.metadata), _mapToken(node.externalKeyword),
+          _mapToken(node.modifierKeyword), _cloneNode(node.returnType),
+          _mapToken(node.propertyKeyword), _mapToken(node.operatorKeyword),
+          _cloneNode(node.name), _cloneNode(node.parameters),
+          _cloneNode(node.body));
+
+  @override
+  MethodInvocation visitMethodInvocation(MethodInvocation node) {
+    MethodInvocation copy = new MethodInvocation(_cloneNode(node.target),
+        _mapToken(node.operator), _cloneNode(node.methodName),
+        _cloneNode(node.argumentList));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  NamedExpression visitNamedExpression(NamedExpression node) {
+    NamedExpression copy =
+        new NamedExpression(_cloneNode(node.name), _cloneNode(node.expression));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  AstNode visitNativeClause(NativeClause node) =>
+      new NativeClause(_mapToken(node.nativeKeyword), _cloneNode(node.name));
+
+  @override
+  NativeFunctionBody visitNativeFunctionBody(NativeFunctionBody node) =>
+      new NativeFunctionBody(_mapToken(node.nativeKeyword),
+          _cloneNode(node.stringLiteral), _mapToken(node.semicolon));
+
+  @override
+  NullLiteral visitNullLiteral(NullLiteral node) {
+    NullLiteral copy = new NullLiteral(_mapToken(node.literal));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  ParenthesizedExpression visitParenthesizedExpression(
+      ParenthesizedExpression node) {
+    ParenthesizedExpression copy = new ParenthesizedExpression(
+        _mapToken(node.leftParenthesis), _cloneNode(node.expression),
+        _mapToken(node.rightParenthesis));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  PartDirective visitPartDirective(PartDirective node) {
+    PartDirective copy = new PartDirective(
+        _cloneNode(node.documentationComment), _cloneNodeList(node.metadata),
+        _mapToken(node.partKeyword), _cloneNode(node.uri),
+        _mapToken(node.semicolon));
+    copy.element = node.element;
+    return copy;
+  }
+
+  @override
+  PartOfDirective visitPartOfDirective(PartOfDirective node) {
+    PartOfDirective copy = new PartOfDirective(
+        _cloneNode(node.documentationComment), _cloneNodeList(node.metadata),
+        _mapToken(node.partKeyword), _mapToken(node.ofKeyword),
+        _cloneNode(node.libraryName), _mapToken(node.semicolon));
+    copy.element = node.element;
+    return copy;
+  }
+
+  @override
+  PostfixExpression visitPostfixExpression(PostfixExpression node) {
+    PostfixExpression copy = new PostfixExpression(
+        _cloneNode(node.operand), _mapToken(node.operator));
+    copy.propagatedElement = node.propagatedElement;
+    copy.propagatedType = node.propagatedType;
+    copy.staticElement = node.staticElement;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  PrefixedIdentifier visitPrefixedIdentifier(PrefixedIdentifier node) {
+    PrefixedIdentifier copy = new PrefixedIdentifier(_cloneNode(node.prefix),
+        _mapToken(node.period), _cloneNode(node.identifier));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  PrefixExpression visitPrefixExpression(PrefixExpression node) {
+    PrefixExpression copy = new PrefixExpression(
+        _mapToken(node.operator), _cloneNode(node.operand));
+    copy.propagatedElement = node.propagatedElement;
+    copy.propagatedType = node.propagatedType;
+    copy.staticElement = node.staticElement;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  PropertyAccess visitPropertyAccess(PropertyAccess node) {
+    PropertyAccess copy = new PropertyAccess(_cloneNode(node.target),
+        _mapToken(node.operator), _cloneNode(node.propertyName));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  RedirectingConstructorInvocation visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    RedirectingConstructorInvocation copy =
+        new RedirectingConstructorInvocation(_mapToken(node.thisKeyword),
+            _mapToken(node.period), _cloneNode(node.constructorName),
+            _cloneNode(node.argumentList));
+    copy.staticElement = node.staticElement;
+    return copy;
+  }
+
+  @override
+  RethrowExpression visitRethrowExpression(RethrowExpression node) {
+    RethrowExpression copy =
+        new RethrowExpression(_mapToken(node.rethrowKeyword));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  ReturnStatement visitReturnStatement(ReturnStatement node) =>
+      new ReturnStatement(_mapToken(node.returnKeyword),
+          _cloneNode(node.expression), _mapToken(node.semicolon));
+
+  @override
+  ScriptTag visitScriptTag(ScriptTag node) =>
+      new ScriptTag(_mapToken(node.scriptTag));
+
+  @override
+  ShowCombinator visitShowCombinator(ShowCombinator node) => new ShowCombinator(
+      _mapToken(node.keyword), _cloneNodeList(node.shownNames));
+
+  @override
+  SimpleFormalParameter visitSimpleFormalParameter(
+      SimpleFormalParameter node) => new SimpleFormalParameter(
+      _cloneNode(node.documentationComment), _cloneNodeList(node.metadata),
+      _mapToken(node.keyword), _cloneNode(node.type),
+      _cloneNode(node.identifier));
+
+  @override
+  SimpleIdentifier visitSimpleIdentifier(SimpleIdentifier node) {
+    Token mappedToken = _mapToken(node.token);
+    if (mappedToken == null) {
+      // This only happens for SimpleIdentifiers created by the parser as part
+      // of scanning documentation comments (the tokens for those identifiers
+      // are not in the original token stream and hence do not get copied).
+      // This extra check can be removed if the scanner is changed to scan
+      // documentation comments for the parser.
+      mappedToken = node.token;
+    }
+    SimpleIdentifier copy = new SimpleIdentifier(mappedToken);
+    copy.auxiliaryElements = node.auxiliaryElements;
+    copy.propagatedElement = node.propagatedElement;
+    copy.propagatedType = node.propagatedType;
+    copy.staticElement = node.staticElement;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  SimpleStringLiteral visitSimpleStringLiteral(SimpleStringLiteral node) {
+    SimpleStringLiteral copy =
+        new SimpleStringLiteral(_mapToken(node.literal), node.value);
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  StringInterpolation visitStringInterpolation(StringInterpolation node) {
+    StringInterpolation copy =
+        new StringInterpolation(_cloneNodeList(node.elements));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  SuperConstructorInvocation visitSuperConstructorInvocation(
+      SuperConstructorInvocation node) {
+    SuperConstructorInvocation copy = new SuperConstructorInvocation(
+        _mapToken(node.superKeyword), _mapToken(node.period),
+        _cloneNode(node.constructorName), _cloneNode(node.argumentList));
+    copy.staticElement = node.staticElement;
+    return copy;
+  }
+
+  @override
+  SuperExpression visitSuperExpression(SuperExpression node) {
+    SuperExpression copy = new SuperExpression(_mapToken(node.superKeyword));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  SwitchCase visitSwitchCase(SwitchCase node) => new SwitchCase(
+      _cloneNodeList(node.labels), _mapToken(node.keyword),
+      _cloneNode(node.expression), _mapToken(node.colon),
+      _cloneNodeList(node.statements));
+
+  @override
+  SwitchDefault visitSwitchDefault(SwitchDefault node) => new SwitchDefault(
+      _cloneNodeList(node.labels), _mapToken(node.keyword),
+      _mapToken(node.colon), _cloneNodeList(node.statements));
+
+  @override
+  SwitchStatement visitSwitchStatement(SwitchStatement node) =>
+      new SwitchStatement(_mapToken(node.switchKeyword),
+          _mapToken(node.leftParenthesis), _cloneNode(node.expression),
+          _mapToken(node.rightParenthesis), _mapToken(node.leftBracket),
+          _cloneNodeList(node.members), _mapToken(node.rightBracket));
+
+  @override
+  AstNode visitSymbolLiteral(SymbolLiteral node) {
+    SymbolLiteral copy = new SymbolLiteral(
+        _mapToken(node.poundSign), _mapTokens(node.components));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  ThisExpression visitThisExpression(ThisExpression node) {
+    ThisExpression copy = new ThisExpression(_mapToken(node.thisKeyword));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  ThrowExpression visitThrowExpression(ThrowExpression node) {
+    ThrowExpression copy = new ThrowExpression(
+        _mapToken(node.throwKeyword), _cloneNode(node.expression));
+    copy.propagatedType = node.propagatedType;
+    copy.staticType = node.staticType;
+    return copy;
+  }
+
+  @override
+  TopLevelVariableDeclaration visitTopLevelVariableDeclaration(
+      TopLevelVariableDeclaration node) => new TopLevelVariableDeclaration(
+      _cloneNode(node.documentationComment), _cloneNodeList(node.metadata),
+      _cloneNode(node.variables), _mapToken(node.semicolon));
+
+  @override
+  TryStatement visitTryStatement(TryStatement node) => new TryStatement(
+      _mapToken(node.tryKeyword), _cloneNode(node.body),
+      _cloneNodeList(node.catchClauses), _mapToken(node.finallyKeyword),
+      _cloneNode(node.finallyBlock));
+
+  @override
+  TypeArgumentList visitTypeArgumentList(TypeArgumentList node) =>
+      new TypeArgumentList(_mapToken(node.leftBracket),
+          _cloneNodeList(node.arguments), _mapToken(node.rightBracket));
+
+  @override
+  TypeName visitTypeName(TypeName node) {
+    TypeName copy =
+        new TypeName(_cloneNode(node.name), _cloneNode(node.typeArguments));
+    copy.type = node.type;
+    return copy;
+  }
+
+  @override
+  TypeParameter visitTypeParameter(TypeParameter node) => new TypeParameter(
+      _cloneNode(node.documentationComment), _cloneNodeList(node.metadata),
+      _cloneNode(node.name), _mapToken(node.extendsKeyword),
+      _cloneNode(node.bound));
+
+  @override
+  TypeParameterList visitTypeParameterList(TypeParameterList node) =>
+      new TypeParameterList(_mapToken(node.leftBracket),
+          _cloneNodeList(node.typeParameters), _mapToken(node.rightBracket));
+
+  @override
+  VariableDeclaration visitVariableDeclaration(VariableDeclaration node) =>
+      new VariableDeclaration(_cloneNode(node.name), _mapToken(node.equals),
+          _cloneNode(node.initializer));
+
+  @override
+  VariableDeclarationList visitVariableDeclarationList(
+      VariableDeclarationList node) => new VariableDeclarationList(null,
+      _cloneNodeList(node.metadata), _mapToken(node.keyword),
+      _cloneNode(node.type), _cloneNodeList(node.variables));
+
+  @override
+  VariableDeclarationStatement visitVariableDeclarationStatement(
+      VariableDeclarationStatement node) => new VariableDeclarationStatement(
+      _cloneNode(node.variables), _mapToken(node.semicolon));
+
+  @override
+  WhileStatement visitWhileStatement(WhileStatement node) => new WhileStatement(
+      _mapToken(node.whileKeyword), _mapToken(node.leftParenthesis),
+      _cloneNode(node.condition), _mapToken(node.rightParenthesis),
+      _cloneNode(node.body));
+
+  @override
+  WithClause visitWithClause(WithClause node) => new WithClause(
+      _mapToken(node.withKeyword), _cloneNodeList(node.mixinTypes));
+
+  @override
+  YieldStatement visitYieldStatement(YieldStatement node) => new YieldStatement(
+      _mapToken(node.yieldKeyword), _mapToken(node.star),
+      _cloneNode(node.expression), _mapToken(node.semicolon));
+
+  AstNode _cloneNode(AstNode node) {
+    if (node == null) {
+      return null;
+    }
+    if (identical(node, _oldNode)) {
+      return _newNode;
+    }
+    return node.accept(this) as AstNode;
+  }
+
+  List _cloneNodeList(NodeList nodes) {
+    List clonedNodes = new List();
+    for (AstNode node in nodes) {
+      clonedNodes.add(_cloneNode(node));
+    }
+    return clonedNodes;
+  }
+
+  Token _mapToken(Token oldToken) {
+    if (oldToken == null) {
+      return null;
+    }
+    return _tokenMap.get(oldToken);
+  }
+
+  List<Token> _mapTokens(List<Token> oldTokens) {
+    List<Token> newTokens = new List<Token>(oldTokens.length);
+    for (int index = 0; index < newTokens.length; index++) {
+      newTokens[index] = _mapToken(oldTokens[index]);
+    }
+    return newTokens;
+  }
+}
+
+/**
+ * An index expression.
+ *
+ * > indexExpression ::=
+ * >     [Expression] '[' [Expression] ']'
+ */
+class IndexExpression extends Expression {
+  /**
+   * The expression used to compute the object being indexed, or `null` if this
+   * index expression is part of a cascade expression.
+   */
+  Expression _target;
+
+  /**
+   * The period ("..") before a cascaded index expression, or `null` if this
+   * index expression is not part of a cascade expression.
+   */
+  Token period;
+
+  /**
+   * The left square bracket.
+   */
+  Token leftBracket;
+
+  /**
+   * The expression used to compute the index.
+   */
+  Expression _index;
+
+  /**
+   * The right square bracket.
+   */
+  Token rightBracket;
+
+  /**
+   * The element associated with the operator based on the static type of the
+   * target, or `null` if the AST structure has not been resolved or if the
+   * operator could not be resolved.
+   */
+  MethodElement staticElement;
+
+  /**
+   * The element associated with the operator based on the propagated type of
+   * the target, or `null` if the AST structure has not been resolved or if the
+   * operator could not be resolved.
+   */
+  MethodElement propagatedElement;
+
+  /**
+   * If this expression is both in a getter and setter context, the
+   * [AuxiliaryElements] will be set to hold onto the static and propagated
+   * information. The auxiliary element will hold onto the elements from the
+   * getter context.
+   */
+  AuxiliaryElements auxiliaryElements = null;
+
+  /**
+   * Initialize a newly created index expression.
+   */
+  IndexExpression.forCascade(
+      this.period, this.leftBracket, Expression index, this.rightBracket) {
+    _index = _becomeParentOf(index);
+  }
+
+  /**
+   * Initialize a newly created index expression.
+   */
+  IndexExpression.forTarget(Expression target, this.leftBracket,
+      Expression index, this.rightBracket) {
+    _target = _becomeParentOf(target);
+    _index = _becomeParentOf(index);
+  }
+
+  @override
+  Token get beginToken {
+    if (_target != null) {
+      return _target.beginToken;
+    }
+    return period;
+  }
+
+  /**
+   * Return the best element available for this operator. If resolution was able
+   * to find a better element based on type propagation, that element will be
+   * returned. Otherwise, the element found using the result of static analysis
+   * will be returned. If resolution has not been performed, then `null` will be
+   * returned.
+   */
+  MethodElement get bestElement {
+    MethodElement element = propagatedElement;
+    if (element == null) {
+      element = staticElement;
+    }
+    return element;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_target)
+    ..add(period)
+    ..add(leftBracket)
+    ..add(_index)
+    ..add(rightBracket);
+
+  @override
+  Token get endToken => rightBracket;
+
+  /**
+   * Return the expression used to compute the index.
+   */
+  Expression get index => _index;
+
+  /**
+   * Set the expression used to compute the index to the given [expression].
+   */
+  void set index(Expression expression) {
+    _index = _becomeParentOf(expression);
+  }
+
+  @override
+  bool get isAssignable => true;
+
+  /**
+   * Return `true` if this expression is cascaded. If it is, then the target of
+   * this expression is not stored locally but is stored in the nearest ancestor
+   * that is a [CascadeExpression].
+   */
+  bool get isCascaded => period != null;
+
+  @override
+  int get precedence => 15;
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on propagated type information, then return the parameter
+   * element representing the parameter to which the value of the index
+   * expression will be bound. Otherwise, return `null`.
+   */
+  @deprecated // Use "expression.propagatedParameterElement"
+  ParameterElement get propagatedParameterElementForIndex {
+    return _propagatedParameterElementForIndex;
+  }
+
+  /**
+   * Return the expression used to compute the object being indexed. If this
+   * index expression is not part of a cascade expression, then this is the same
+   * as [target]. If this index expression is part of a cascade expression, then
+   * the target expression stored with the cascade expression is returned.
+   */
+  Expression get realTarget {
+    if (isCascaded) {
+      AstNode ancestor = parent;
+      while (ancestor is! CascadeExpression) {
+        if (ancestor == null) {
+          return _target;
+        }
+        ancestor = ancestor.parent;
+      }
+      return (ancestor as CascadeExpression).target;
+    }
+    return _target;
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on static type information, then return the parameter element
+   * representing the parameter to which the value of the index expression will
+   * be bound. Otherwise, return `null`.
+   */
+  @deprecated // Use "expression.propagatedParameterElement"
+  ParameterElement get staticParameterElementForIndex {
+    return _staticParameterElementForIndex;
+  }
+
+  /**
+   * Return the expression used to compute the object being indexed, or `null`
+   * if this index expression is part of a cascade expression.
+   *
+   * Use [realTarget] to get the target independent of whether this is part of a
+   * cascade expression.
+   */
+  Expression get target => _target;
+
+  /**
+   * Set the expression used to compute the object being indexed to the given
+   * [expression].
+   */
+  void set target(Expression expression) {
+    _target = _becomeParentOf(expression);
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on propagated type information, then return the parameter
+   * element representing the parameter to which the value of the index
+   * expression will be bound. Otherwise, return `null`.
+   */
+  ParameterElement get _propagatedParameterElementForIndex {
+    if (propagatedElement == null) {
+      return null;
+    }
+    List<ParameterElement> parameters = propagatedElement.parameters;
+    if (parameters.length < 1) {
+      return null;
+    }
+    return parameters[0];
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on static type information, then return the parameter element
+   * representing the parameter to which the value of the index expression will
+   * be bound. Otherwise, return `null`.
+   */
+  ParameterElement get _staticParameterElementForIndex {
+    if (staticElement == null) {
+      return null;
+    }
+    List<ParameterElement> parameters = staticElement.parameters;
+    if (parameters.length < 1) {
+      return null;
+    }
+    return parameters[0];
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitIndexExpression(this);
+
+  /**
+   * Return `true` if this expression is computing a right-hand value (that is,
+   * if this expression is in a context where the operator '[]' will be
+   * invoked).
+   *
+   * Note that [inGetterContext] and [inSetterContext] are not opposites, nor
+   * are they mutually exclusive. In other words, it is possible for both
+   * methods to return `true` when invoked on the same node.
+   */
+  bool inGetterContext() {
+    // TODO(brianwilkerson) Convert this to a getter.
+    AstNode parent = this.parent;
+    if (parent is AssignmentExpression) {
+      AssignmentExpression assignment = parent;
+      if (identical(assignment.leftHandSide, this) &&
+          assignment.operator.type == TokenType.EQ) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Return `true` if this expression is computing a left-hand value (that is,
+   * if this expression is in a context where the operator '[]=' will be
+   * invoked).
+   *
+   * Note that [inGetterContext] and [inSetterContext] are not opposites, nor
+   * are they mutually exclusive. In other words, it is possible for both
+   * methods to return `true` when invoked on the same node.
+   */
+  bool inSetterContext() {
+    // TODO(brianwilkerson) Convert this to a getter.
+    AstNode parent = this.parent;
+    if (parent is PrefixExpression) {
+      return parent.operator.type.isIncrementOperator;
+    } else if (parent is PostfixExpression) {
+      return true;
+    } else if (parent is AssignmentExpression) {
+      return identical(parent.leftHandSide, this);
+    }
+    return false;
+  }
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_target, visitor);
+    _safelyVisitChild(_index, visitor);
+  }
+}
+
+/**
+ * An instance creation expression.
+ *
+ * > newExpression ::=
+ * >     ('new' | 'const') [TypeName] ('.' [SimpleIdentifier])? [ArgumentList]
+ */
+class InstanceCreationExpression extends Expression {
+  /**
+   * The 'new' or 'const' keyword used to indicate how an object should be
+   * created.
+   */
+  Token keyword;
+
+  /**
+   * The name of the constructor to be invoked.
+   */
+  ConstructorName _constructorName;
+
+  /**
+   * The list of arguments to the constructor.
+   */
+  ArgumentList _argumentList;
+
+  /**
+   * The element associated with the constructor based on static type
+   * information, or `null` if the AST structure has not been resolved or if the
+   * constructor could not be resolved.
+   */
+  ConstructorElement staticElement;
+
+  /**
+   * The [ConstantInstanceCreationHandle] holding the result of evaluating this
+   * expression, if it is constant.
+   */
+  ConstantInstanceCreationHandle constantHandle;
+
+  /**
+   * Initialize a newly created instance creation expression.
+   */
+  InstanceCreationExpression(this.keyword, ConstructorName constructorName,
+      ArgumentList argumentList) {
+    _constructorName = _becomeParentOf(constructorName);
+    _argumentList = _becomeParentOf(argumentList);
+  }
+
+  /**
+   * Return the list of arguments to the constructor.
+   */
+  ArgumentList get argumentList => _argumentList;
+
+  /**
+   * Set the list of arguments to the constructor to the given [argumentList].
+   */
+  void set argumentList(ArgumentList argumentList) {
+    _argumentList = _becomeParentOf(argumentList);
+  }
+
+  @override
+  Token get beginToken => keyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(keyword)
+    ..add(_constructorName)
+    ..add(_argumentList);
+
+  /**
+   * Return the name of the constructor to be invoked.
+   */
+  ConstructorName get constructorName => _constructorName;
+
+  /**
+   * Set the name of the constructor to be invoked to the given [name].
+   */
+  void set constructorName(ConstructorName name) {
+    _constructorName = _becomeParentOf(name);
+  }
+
+  @override
+  Token get endToken => _argumentList.endToken;
+
+  /**
+   * The result of evaluating this expression, if it is constant.
+   */
+  EvaluationResultImpl get evaluationResult {
+    if (constantHandle != null) {
+      return constantHandle.evaluationResult;
+    }
+    return null;
+  }
+
+  /**
+   * Return `true` if this creation expression is used to invoke a constant
+   * constructor.
+   */
+  bool get isConst => keyword is KeywordToken &&
+      (keyword as KeywordToken).keyword == Keyword.CONST;
+
+  @override
+  int get precedence => 16;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitInstanceCreationExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_constructorName, visitor);
+    _safelyVisitChild(_argumentList, visitor);
+  }
+}
+
+/**
+ * An integer literal expression.
+ *
+ * > integerLiteral ::=
+ * >     decimalIntegerLiteral
+ * >   | hexidecimalIntegerLiteral
+ * >
+ * > decimalIntegerLiteral ::=
+ * >     decimalDigit+
+ * >
+ * > hexidecimalIntegerLiteral ::=
+ * >     '0x' hexidecimalDigit+
+ * >   | '0X' hexidecimalDigit+
+ */
+class IntegerLiteral extends Literal {
+  /**
+   * The token representing the literal.
+   */
+  Token literal;
+
+  /**
+   * The value of the literal.
+   */
+  int value = 0;
+
+  /**
+   * Initialize a newly created integer literal.
+   */
+  IntegerLiteral(this.literal, this.value);
+
+  @override
+  Token get beginToken => literal;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..add(literal);
+
+  @override
+  Token get endToken => literal;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitIntegerLiteral(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    // There are no children to visit.
+  }
+}
+
+/**
+ * A node within a [StringInterpolation].
+ *
+ * > interpolationElement ::=
+ * >     [InterpolationExpression]
+ * >   | [InterpolationString]
+ */
+abstract class InterpolationElement extends AstNode {}
+
+/**
+ * An expression embedded in a string interpolation.
+ *
+ * > interpolationExpression ::=
+ * >     '$' [SimpleIdentifier]
+ * >   | '$' '{' [Expression] '}'
+ */
+class InterpolationExpression extends InterpolationElement {
+  /**
+   * The token used to introduce the interpolation expression; either '$' if the
+   * expression is a simple identifier or '${' if the expression is a full
+   * expression.
+   */
+  Token leftBracket;
+
+  /**
+   * The expression to be evaluated for the value to be converted into a string.
+   */
+  Expression _expression;
+
+  /**
+   * The right curly bracket, or `null` if the expression is an identifier
+   * without brackets.
+   */
+  Token rightBracket;
+
+  /**
+   * Initialize a newly created interpolation expression.
+   */
+  InterpolationExpression(
+      this.leftBracket, Expression expression, this.rightBracket) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get beginToken => leftBracket;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(leftBracket)
+    ..add(_expression)
+    ..add(rightBracket);
+
+  @override
+  Token get endToken {
+    if (rightBracket != null) {
+      return rightBracket;
+    }
+    return _expression.endToken;
+  }
+
+  /**
+   * Return the expression to be evaluated for the value to be converted into a
+   * string.
+   */
+  Expression get expression => _expression;
+
+  /**
+   * Set the expression to be evaluated for the value to be converted into a
+   * string to the given [expression].
+   */
+  void set expression(Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitInterpolationExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_expression, visitor);
+  }
+}
+
+/**
+ * A non-empty substring of an interpolated string.
+ *
+ * > interpolationString ::=
+ * >     characters
+ */
+class InterpolationString extends InterpolationElement {
+  /**
+   * The characters that will be added to the string.
+   */
+  Token contents;
+
+  /**
+   * The value of the literal.
+   */
+  String _value;
+
+  /**
+   * Initialize a newly created string of characters that are part of a string
+   * interpolation.
+   */
+  InterpolationString(this.contents, String value) {
+    _value = value;
+  }
+
+  @override
+  Token get beginToken => contents;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..add(contents);
+
+  /**
+   * Return the offset of the after-last contents character.
+   */
+  int get contentsEnd {
+    String lexeme = contents.lexeme;
+    return offset + new StringLexemeHelper(lexeme, true, true).end;
+  }
+
+  /**
+   * Return the offset of the first contents character.
+   */
+  int get contentsOffset {
+    int offset = contents.offset;
+    String lexeme = contents.lexeme;
+    return offset + new StringLexemeHelper(lexeme, true, true).start;
+  }
+
+  @override
+  Token get endToken => contents;
+
+  /**
+   * Return the value of the literal.
+   */
+  String get value => _value;
+
+  /**
+   * Set the value of the literal to the given [string].
+   */
+  void set value(String string) {
+    _value = string;
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitInterpolationString(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {}
+}
+
+/**
+ * An is expression.
+ *
+ * > isExpression ::=
+ * >     [Expression] 'is' '!'? [TypeName]
+ */
+class IsExpression extends Expression {
+  /**
+   * The expression used to compute the value whose type is being tested.
+   */
+  Expression _expression;
+
+  /**
+   * The is operator.
+   */
+  Token isOperator;
+
+  /**
+   * The not operator, or `null` if the sense of the test is not negated.
+   */
+  Token notOperator;
+
+  /**
+   * The name of the type being tested for.
+   */
+  TypeName _type;
+
+  /**
+   * Initialize a newly created is expression. The [notOperator] can be `null`
+   * if the sense of the test is not negated.
+   */
+  IsExpression(
+      Expression expression, this.isOperator, this.notOperator, TypeName type) {
+    _expression = _becomeParentOf(expression);
+    _type = _becomeParentOf(type);
+  }
+
+  @override
+  Token get beginToken => _expression.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_expression)
+    ..add(isOperator)
+    ..add(notOperator)
+    ..add(_type);
+
+  @override
+  Token get endToken => _type.endToken;
+
+  /**
+   * Return the expression used to compute the value whose type is being tested.
+   */
+  Expression get expression => _expression;
+
+  /**
+   * Set the expression used to compute the value whose type is being tested to
+   * the given [expression].
+   */
+  void set expression(Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  int get precedence => 7;
+
+  /**
+   * Return the name of the type being tested for.
+   */
+  TypeName get type => _type;
+
+  /**
+   * Set the name of the type being tested for to the given [name].
+   */
+  void set type(TypeName name) {
+    _type = _becomeParentOf(name);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitIsExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_expression, visitor);
+    _safelyVisitChild(_type, visitor);
+  }
+}
+
+/**
+ * A label on either a [LabeledStatement] or a [NamedExpression].
+ *
+ * > label ::=
+ * >     [SimpleIdentifier] ':'
+ */
+class Label extends AstNode {
+  /**
+   * The label being associated with the statement.
+   */
+  SimpleIdentifier _label;
+
+  /**
+   * The colon that separates the label from the statement.
+   */
+  Token colon;
+
+  /**
+   * Initialize a newly created label.
+   */
+  Label(SimpleIdentifier label, this.colon) {
+    _label = _becomeParentOf(label);
+  }
+
+  @override
+  Token get beginToken => _label.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_label)
+    ..add(colon);
+
+  @override
+  Token get endToken => colon;
+
+  /**
+   * Return the label being associated with the statement.
+   */
+  SimpleIdentifier get label => _label;
+
+  /**
+   * Set the label being associated with the statement to the given [label].
+   */
+  void set label(SimpleIdentifier label) {
+    _label = _becomeParentOf(label);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitLabel(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_label, visitor);
+  }
+}
+
+/**
+ * A statement that has a label associated with them.
+ *
+ * > labeledStatement ::=
+ * >    [Label]+ [Statement]
+ */
+class LabeledStatement extends Statement {
+  /**
+   * The labels being associated with the statement.
+   */
+  NodeList<Label> _labels;
+
+  /**
+   * The statement with which the labels are being associated.
+   */
+  Statement _statement;
+
+  /**
+   * Initialize a newly created labeled statement.
+   */
+  LabeledStatement(List<Label> labels, Statement statement) {
+    _labels = new NodeList<Label>(this, labels);
+    _statement = _becomeParentOf(statement);
+  }
+
+  @override
+  Token get beginToken {
+    if (!_labels.isEmpty) {
+      return _labels.beginToken;
+    }
+    return _statement.beginToken;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..addAll(_labels)
+    ..add(_statement);
+
+  @override
+  Token get endToken => _statement.endToken;
+
+  /**
+   * Return the labels being associated with the statement.
+   */
+  NodeList<Label> get labels => _labels;
+
+  /**
+   * Return the statement with which the labels are being associated.
+   */
+  Statement get statement => _statement;
+
+  /**
+   * Set the statement with which the labels are being associated to the given
+   * [statement].
+   */
+  void set statement(Statement statement) {
+    _statement = _becomeParentOf(statement);
+  }
+
+  @override
+  Statement get unlabeled => _statement.unlabeled;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitLabeledStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _labels.accept(visitor);
+    _safelyVisitChild(_statement, visitor);
+  }
+}
+
+/**
+ * A library directive.
+ *
+ * > libraryDirective ::=
+ * >     [Annotation] 'library' [Identifier] ';'
+ */
+class LibraryDirective extends Directive {
+  /**
+   * The token representing the 'library' keyword.
+   */
+  Token libraryKeyword;
+
+  /**
+   * The name of the library being defined.
+   */
+  LibraryIdentifier _name;
+
+  /**
+   * The semicolon terminating the directive.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created library directive. Either or both of the
+   * [comment] and [metadata] can be `null` if the directive does not have the
+   * corresponding attribute.
+   */
+  LibraryDirective(Comment comment, List<Annotation> metadata,
+      this.libraryKeyword, LibraryIdentifier name, this.semicolon)
+      : super(comment, metadata) {
+    _name = _becomeParentOf(name);
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(libraryKeyword)
+    ..add(_name)
+    ..add(semicolon);
+
+  @override
+  Token get endToken => semicolon;
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata => libraryKeyword;
+
+  @override
+  Token get keyword => libraryKeyword;
+
+  /**
+   * Return the token representing the 'library' token.
+   */
+  @deprecated // Use "this.libraryKeyword"
+  Token get libraryToken => libraryKeyword;
+
+  /**
+   * Set the token representing the 'library' token to the given [token].
+   */
+  @deprecated // Use "this.libraryKeyword"
+  set libraryToken(Token token) {
+    libraryKeyword = token;
+  }
+
+  /**
+   * Return the name of the library being defined.
+   */
+  LibraryIdentifier get name => _name;
+
+  /**
+   * Set the name of the library being defined to the given [name].
+   */
+  void set name(LibraryIdentifier name) {
+    _name = _becomeParentOf(name);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitLibraryDirective(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_name, visitor);
+  }
+}
+
+/**
+ * The identifier for a library.
+ *
+ * > libraryIdentifier ::=
+ * >     [SimpleIdentifier] ('.' [SimpleIdentifier])*
+ */
+class LibraryIdentifier extends Identifier {
+  /**
+   * The components of the identifier.
+   */
+  NodeList<SimpleIdentifier> _components;
+
+  /**
+   * Initialize a newly created prefixed identifier.
+   */
+  LibraryIdentifier(List<SimpleIdentifier> components) {
+    _components = new NodeList<SimpleIdentifier>(this, components);
+  }
+
+  @override
+  Token get beginToken => _components.beginToken;
+
+  @override
+  Element get bestElement => staticElement;
+
+  /**
+   * TODO(paulberry): add "." tokens.
+   */
+  @override
+  Iterable get childEntities => new ChildEntities()..addAll(_components);
+
+  /**
+   * Return the components of the identifier.
+   */
+  NodeList<SimpleIdentifier> get components => _components;
+
+  @override
+  Token get endToken => _components.endToken;
+
+  @override
+  String get name {
+    StringBuffer buffer = new StringBuffer();
+    bool needsPeriod = false;
+    for (SimpleIdentifier identifier in _components) {
+      if (needsPeriod) {
+        buffer.write(".");
+      } else {
+        needsPeriod = true;
+      }
+      buffer.write(identifier.name);
+    }
+    return buffer.toString();
+  }
+
+  @override
+  int get precedence => 15;
+
+  @override
+  Element get propagatedElement => null;
+
+  @override
+  Element get staticElement => null;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitLibraryIdentifier(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _components.accept(visitor);
+  }
+}
+
+/**
+ * A list literal.
+ *
+ * > listLiteral ::=
+ * >     'const'? ('<' [TypeName] '>')? '[' ([Expression] ','?)? ']'
+ */
+class ListLiteral extends TypedLiteral {
+  /**
+   * The left square bracket.
+   */
+  Token leftBracket;
+
+  /**
+   * The expressions used to compute the elements of the list.
+   */
+  NodeList<Expression> _elements;
+
+  /**
+   * The right square bracket.
+   */
+  Token rightBracket;
+
+  /**
+   * Initialize a newly created list literal. The [constKeyword] can be `null`
+   * if the literal is not a constant. The [typeArguments] can be `null` if no
+   * type arguments were declared. The list of [elements] can be `null` if the
+   * list is empty.
+   */
+  ListLiteral(Token constKeyword, TypeArgumentList typeArguments,
+      this.leftBracket, List<Expression> elements, this.rightBracket)
+      : super(constKeyword, typeArguments) {
+    _elements = new NodeList<Expression>(this, elements);
+  }
+
+  @override
+  Token get beginToken {
+    if (constKeyword != null) {
+      return constKeyword;
+    }
+    TypeArgumentList typeArguments = this.typeArguments;
+    if (typeArguments != null) {
+      return typeArguments.beginToken;
+    }
+    return leftBracket;
+  }
+
+  /**
+   * TODO(paulberry): add commas.
+   */
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(leftBracket)
+    ..addAll(_elements)
+    ..add(rightBracket);
+
+  /**
+   * Return the expressions used to compute the elements of the list.
+   */
+  NodeList<Expression> get elements => _elements;
+
+  @override
+  Token get endToken => rightBracket;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitListLiteral(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _elements.accept(visitor);
+  }
+}
+
+/**
+ * A node that represents a literal expression.
+ *
+ * > literal ::=
+ * >     [BooleanLiteral]
+ * >   | [DoubleLiteral]
+ * >   | [IntegerLiteral]
+ * >   | [ListLiteral]
+ * >   | [MapLiteral]
+ * >   | [NullLiteral]
+ * >   | [StringLiteral]
+ */
+abstract class Literal extends Expression {
+  @override
+  int get precedence => 16;
+}
+
+/**
+ * A literal map.
+ *
+ * > mapLiteral ::=
+ * >     'const'? ('<' [TypeName] (',' [TypeName])* '>')?
+ * >     '{' ([MapLiteralEntry] (',' [MapLiteralEntry])* ','?)? '}'
+ */
+class MapLiteral extends TypedLiteral {
+  /**
+   * The left curly bracket.
+   */
+  Token leftBracket;
+
+  /**
+   * The entries in the map.
+   */
+  NodeList<MapLiteralEntry> _entries;
+
+  /**
+   * The right curly bracket.
+   */
+  Token rightBracket;
+
+  /**
+   * Initialize a newly created map literal. The [constKeyword] can be `null` if
+   * the literal is not a constant. The [typeArguments] can be `null` if no type
+   * arguments were declared. The [entries] can be `null` if the map is empty.
+   */
+  MapLiteral(Token constKeyword, TypeArgumentList typeArguments,
+      this.leftBracket, List<MapLiteralEntry> entries, this.rightBracket)
+      : super(constKeyword, typeArguments) {
+    _entries = new NodeList<MapLiteralEntry>(this, entries);
+  }
+
+  @override
+  Token get beginToken {
+    if (constKeyword != null) {
+      return constKeyword;
+    }
+    TypeArgumentList typeArguments = this.typeArguments;
+    if (typeArguments != null) {
+      return typeArguments.beginToken;
+    }
+    return leftBracket;
+  }
+
+  /**
+   * TODO(paulberry): add commas.
+   */
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(leftBracket)
+    ..addAll(entries)
+    ..add(rightBracket);
+
+  @override
+  Token get endToken => rightBracket;
+
+  /**
+   * Return the entries in the map.
+   */
+  NodeList<MapLiteralEntry> get entries => _entries;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitMapLiteral(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _entries.accept(visitor);
+  }
+}
+
+/**
+ * A single key/value pair in a map literal.
+ *
+ * > mapLiteralEntry ::=
+ * >     [Expression] ':' [Expression]
+ */
+class MapLiteralEntry extends AstNode {
+  /**
+   * The expression computing the key with which the value will be associated.
+   */
+  Expression _key;
+
+  /**
+   * The colon that separates the key from the value.
+   */
+  Token separator;
+
+  /**
+   * The expression computing the value that will be associated with the key.
+   */
+  Expression _value;
+
+  /**
+   * Initialize a newly created map literal entry.
+   */
+  MapLiteralEntry(Expression key, this.separator, Expression value) {
+    _key = _becomeParentOf(key);
+    _value = _becomeParentOf(value);
+  }
+
+  @override
+  Token get beginToken => _key.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_key)
+    ..add(separator)
+    ..add(_value);
+
+  @override
+  Token get endToken => _value.endToken;
+
+  /**
+   * Return the expression computing the key with which the value will be
+   * associated.
+   */
+  Expression get key => _key;
+
+  /**
+   * Set the expression computing the key with which the value will be
+   * associated to the given [string].
+   */
+  void set key(Expression string) {
+    _key = _becomeParentOf(string);
+  }
+
+  /**
+   * Return the expression computing the value that will be associated with the
+   * key.
+   */
+  Expression get value => _value;
+
+  /**
+   * Set the expression computing the value that will be associated with the key
+   * to the given [expression].
+   */
+  void set value(Expression expression) {
+    _value = _becomeParentOf(expression);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitMapLiteralEntry(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_key, visitor);
+    _safelyVisitChild(_value, visitor);
+  }
+}
+
+/**
+ * A method declaration.
+ *
+ * > methodDeclaration ::=
+ * >     methodSignature [FunctionBody]
+ * >
+ * > methodSignature ::=
+ * >     'external'? ('abstract' | 'static')? [Type]? ('get' | 'set')?
+ * >     methodName [FormalParameterList]
+ * >
+ * > methodName ::=
+ * >     [SimpleIdentifier]
+ * >   | 'operator' [SimpleIdentifier]
+ */
+class MethodDeclaration extends ClassMember {
+  /**
+   * The token for the 'external' keyword, or `null` if the constructor is not
+   * external.
+   */
+  Token externalKeyword;
+
+  /**
+   * The token representing the 'abstract' or 'static' keyword, or `null` if
+   * neither modifier was specified.
+   */
+  Token modifierKeyword;
+
+  /**
+   * The return type of the method, or `null` if no return type was declared.
+   */
+  TypeName _returnType;
+
+  /**
+   * The token representing the 'get' or 'set' keyword, or `null` if this is a
+   * method declaration rather than a property declaration.
+   */
+  Token propertyKeyword;
+
+  /**
+   * The token representing the 'operator' keyword, or `null` if this method
+   * does not declare an operator.
+   */
+  Token operatorKeyword;
+
+  /**
+   * The name of the method.
+   */
+  SimpleIdentifier _name;
+
+  /**
+   * The parameters associated with the method, or `null` if this method
+   * declares a getter.
+   */
+  FormalParameterList _parameters;
+
+  /**
+   * The body of the method.
+   */
+  FunctionBody _body;
+
+  /**
+   * Initialize a newly created method declaration. Either or both of the
+   * [comment] and [metadata] can be `null` if the declaration does not have the
+   * corresponding attribute. The [externalKeyword] can be `null` if the method
+   * is not external. The [modifierKeyword] can be `null` if the method is
+   * neither abstract nor static. The [returnType] can be `null` if no return
+   * type was specified. The [propertyKeyword] can be `null` if the method is
+   * neither a getter or a setter. The [operatorKeyword] can be `null` if the
+   * method does not implement an operator. The [parameters] must be `null` if
+   * this method declares a getter.
+   */
+  MethodDeclaration(Comment comment, List<Annotation> metadata,
+      this.externalKeyword, this.modifierKeyword, TypeName returnType,
+      this.propertyKeyword, this.operatorKeyword, SimpleIdentifier name,
+      FormalParameterList parameters, FunctionBody body)
+      : super(comment, metadata) {
+    _returnType = _becomeParentOf(returnType);
+    _name = _becomeParentOf(name);
+    _parameters = _becomeParentOf(parameters);
+    _body = _becomeParentOf(body);
+  }
+
+  /**
+   * Return the body of the method.
+   */
+  FunctionBody get body => _body;
+
+  /**
+   * Set the body of the method to the given [functionBody].
+   */
+  void set body(FunctionBody functionBody) {
+    _body = _becomeParentOf(functionBody);
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(externalKeyword)
+    ..add(modifierKeyword)
+    ..add(_returnType)
+    ..add(propertyKeyword)
+    ..add(operatorKeyword)
+    ..add(_name)
+    ..add(_parameters)
+    ..add(_body);
+
+  /**
+   * Return the element associated with this method, or `null` if the AST
+   * structure has not been resolved. The element can either be a
+   * [MethodElement], if this represents the declaration of a normal method, or
+   * a [PropertyAccessorElement] if this represents the declaration of either a
+   * getter or a setter.
+   */
+  @override
+  ExecutableElement get element =>
+      _name != null ? (_name.staticElement as ExecutableElement) : null;
+
+  @override
+  Token get endToken => _body.endToken;
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata {
+    if (modifierKeyword != null) {
+      return modifierKeyword;
+    } else if (_returnType != null) {
+      return _returnType.beginToken;
+    } else if (propertyKeyword != null) {
+      return propertyKeyword;
+    } else if (operatorKeyword != null) {
+      return operatorKeyword;
+    }
+    return _name.beginToken;
+  }
+
+  /**
+   * Return `true` if this method is declared to be an abstract method.
+   */
+  bool get isAbstract {
+    FunctionBody body = _body;
+    return externalKeyword == null &&
+        (body is EmptyFunctionBody && !body.semicolon.isSynthetic);
+  }
+
+  /**
+   * Return `true` if this method declares a getter.
+   */
+  bool get isGetter => propertyKeyword != null &&
+      (propertyKeyword as KeywordToken).keyword == Keyword.GET;
+
+  /**
+   * Return `true` if this method declares an operator.
+   */
+  bool get isOperator => operatorKeyword != null;
+
+  /**
+   * Return `true` if this method declares a setter.
+   */
+  bool get isSetter => propertyKeyword != null &&
+      (propertyKeyword as KeywordToken).keyword == Keyword.SET;
+
+  /**
+   * Return `true` if this method is declared to be a static method.
+   */
+  bool get isStatic => modifierKeyword != null &&
+      (modifierKeyword as KeywordToken).keyword == Keyword.STATIC;
+
+  /**
+   * Return the name of the method.
+   */
+  SimpleIdentifier get name => _name;
+
+  /**
+   * Set the name of the method to the given [identifier].
+   */
+  void set name(SimpleIdentifier identifier) {
+    _name = _becomeParentOf(identifier);
+  }
+
+  /**
+   * Return the parameters associated with the method, or `null` if this method
+   * declares a getter.
+   */
+  FormalParameterList get parameters => _parameters;
+
+  /**
+   * Set the parameters associated with the method to the given list of
+   * [parameters].
+   */
+  void set parameters(FormalParameterList parameters) {
+    _parameters = _becomeParentOf(parameters);
+  }
+
+  /**
+   * Return the return type of the method, or `null` if no return type was
+   * declared.
+   */
+  TypeName get returnType => _returnType;
+
+  /**
+   * Set the return type of the method to the given [typeName].
+   */
+  void set returnType(TypeName typeName) {
+    _returnType = _becomeParentOf(typeName);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitMethodDeclaration(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_returnType, visitor);
+    _safelyVisitChild(_name, visitor);
+    _safelyVisitChild(_parameters, visitor);
+    _safelyVisitChild(_body, visitor);
+  }
+}
+
+/**
+ * The invocation of either a function or a method. Invocations of functions
+ * resulting from evaluating an expression are represented by
+ * [FunctionExpressionInvocation] nodes. Invocations of getters and setters are
+ * represented by either [PrefixedIdentifier] or [PropertyAccess] nodes.
+ *
+ * > methodInvoction ::=
+ * >     ([Expression] '.')? [SimpleIdentifier] [ArgumentList]
+ */
+class MethodInvocation extends Expression {
+  /**
+   * The expression producing the object on which the method is defined, or
+   * `null` if there is no target (that is, the target is implicitly `this`).
+   */
+  Expression _target;
+
+  /**
+   * The operator that separates the target from the method name, or `null`
+   * if there is no target. In an ordinary method invocation this will be a
+   * period ('.'). In a cascade section this will be the cascade operator
+   * ('..').
+   */
+  Token operator;
+
+  /**
+   * The name of the method being invoked.
+   */
+  SimpleIdentifier _methodName;
+
+  /**
+   * The list of arguments to the method.
+   */
+  ArgumentList _argumentList;
+
+  /**
+   * Initialize a newly created method invocation. The [target] and [operator]
+   * can be `null` if there is no target.
+   */
+  MethodInvocation(Expression target, this.operator,
+      SimpleIdentifier methodName, ArgumentList argumentList) {
+    _target = _becomeParentOf(target);
+    _methodName = _becomeParentOf(methodName);
+    _argumentList = _becomeParentOf(argumentList);
+  }
+
+  /**
+   * Return the list of arguments to the method.
+   */
+  ArgumentList get argumentList => _argumentList;
+
+  /**
+   * Set the list of arguments to the method to the given [argumentList].
+   */
+  void set argumentList(ArgumentList argumentList) {
+    _argumentList = _becomeParentOf(argumentList);
+  }
+
+  @override
+  Token get beginToken {
+    if (_target != null) {
+      return _target.beginToken;
+    } else if (operator != null) {
+      return operator;
+    }
+    return _methodName.beginToken;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_target)
+    ..add(operator)
+    ..add(_methodName)
+    ..add(_argumentList);
+
+  @override
+  Token get endToken => _argumentList.endToken;
+
+  /**
+   * Return `true` if this expression is cascaded. If it is, then the target of
+   * this expression is not stored locally but is stored in the nearest ancestor
+   * that is a [CascadeExpression].
+   */
+  bool get isCascaded =>
+      operator != null && operator.type == TokenType.PERIOD_PERIOD;
+
+  /**
+   * Return the name of the method being invoked.
+   */
+  SimpleIdentifier get methodName => _methodName;
+
+  /**
+   * Set the name of the method being invoked to the given [identifier].
+   */
+  void set methodName(SimpleIdentifier identifier) {
+    _methodName = _becomeParentOf(identifier);
+  }
+
+  /**
+   * The operator that separates the target from the method name, or `null`
+   * if there is no target. In an ordinary method invocation this will be a
+   * period ('.'). In a cascade section this will be the cascade operator
+   * ('..').
+   *
+   * Deprecated: use [operator] instead.
+   */
+  @deprecated
+  Token get period => operator;
+
+  /**
+   * The operator that separates the target from the method name, or `null`
+   * if there is no target. In an ordinary method invocation this will be a
+   * period ('.'). In a cascade section this will be the cascade operator
+   * ('..').
+   *
+   * Deprecated: use [operator] instead.
+   */
+  @deprecated
+  void set period(Token value) {
+    operator = value;
+  }
+
+  @override
+  int get precedence => 15;
+
+  /**
+   * Return the expression used to compute the receiver of the invocation. If
+   * this invocation is not part of a cascade expression, then this is the same
+   * as [target]. If this invocation is part of a cascade expression, then the
+   * target stored with the cascade expression is returned.
+   */
+  Expression get realTarget {
+    if (isCascaded) {
+      AstNode ancestor = parent;
+      while (ancestor is! CascadeExpression) {
+        if (ancestor == null) {
+          return _target;
+        }
+        ancestor = ancestor.parent;
+      }
+      return (ancestor as CascadeExpression).target;
+    }
+    return _target;
+  }
+
+  /**
+   * Return the expression producing the object on which the method is defined,
+   * or `null` if there is no target (that is, the target is implicitly `this`)
+   * or if this method invocation is part of a cascade expression.
+   *
+   * Use [realTarget] to get the target independent of whether this is part of a
+   * cascade expression.
+   */
+  Expression get target => _target;
+
+  /**
+   * Set the expression producing the object on which the method is defined to
+   * the given [expression].
+   */
+  void set target(Expression expression) {
+    _target = _becomeParentOf(expression);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitMethodInvocation(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_target, visitor);
+    _safelyVisitChild(_methodName, visitor);
+    _safelyVisitChild(_argumentList, visitor);
+  }
+}
+
+/**
+ * A node that declares a single name within the scope of a compilation unit.
+ */
+abstract class NamedCompilationUnitMember extends CompilationUnitMember {
+  /**
+   * The name of the member being declared.
+   */
+  SimpleIdentifier _name;
+
+  /**
+   * Initialize a newly created compilation unit member with the given [name].
+   * Either or both of the [comment] and [metadata] can be `null` if the member
+   * does not have the corresponding attribute.
+   */
+  NamedCompilationUnitMember(
+      Comment comment, List<Annotation> metadata, SimpleIdentifier name)
+      : super(comment, metadata) {
+    _name = _becomeParentOf(name);
+  }
+
+  /**
+   * Return the name of the member being declared.
+   */
+  SimpleIdentifier get name => _name;
+
+  /**
+   * Set the name of the member being declared to the given [identifier].
+   */
+  void set name(SimpleIdentifier identifier) {
+    _name = _becomeParentOf(identifier);
+  }
+}
+
+/**
+ * An expression that has a name associated with it. They are used in method
+ * invocations when there are named parameters.
+ *
+ * > namedExpression ::=
+ * >     [Label] [Expression]
+ */
+class NamedExpression extends Expression {
+  /**
+   * The name associated with the expression.
+   */
+  Label _name;
+
+  /**
+   * The expression with which the name is associated.
+   */
+  Expression _expression;
+
+  /**
+   * Initialize a newly created named expression..
+   */
+  NamedExpression(Label name, Expression expression) {
+    _name = _becomeParentOf(name);
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get beginToken => _name.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_name)
+    ..add(_expression);
+
+  /**
+   * Return the element representing the parameter being named by this
+   * expression, or `null` if the AST structure has not been resolved or if
+   * there is no parameter with the same name as this expression.
+   */
+  ParameterElement get element {
+    Element element = _name.label.staticElement;
+    if (element is ParameterElement) {
+      return element;
+    }
+    return null;
+  }
+
+  @override
+  Token get endToken => _expression.endToken;
+
+  /**
+   * Return the expression with which the name is associated.
+   */
+  Expression get expression => _expression;
+
+  /**
+   * Set the expression with which the name is associated to the given
+   * [expression].
+   */
+  void set expression(Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  /**
+   * Return the name associated with the expression.
+   */
+  Label get name => _name;
+
+  /**
+   * Set the name associated with the expression to the given [identifier].
+   */
+  void set name(Label identifier) {
+    _name = _becomeParentOf(identifier);
+  }
+
+  @override
+  int get precedence => 0;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitNamedExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_name, visitor);
+    _safelyVisitChild(_expression, visitor);
+  }
+}
+
+/**
+ * A node that represents a directive that impacts the namespace of a library.
+ *
+ * > directive ::=
+ * >     [ExportDirective]
+ * >   | [ImportDirective]
+ */
+abstract class NamespaceDirective extends UriBasedDirective {
+  /**
+   * The token representing the 'import' or 'export' keyword.
+   */
+  Token keyword;
+
+  /**
+   * The combinators used to control which names are imported or exported.
+   */
+  NodeList<Combinator> _combinators;
+
+  /**
+   * The semicolon terminating the directive.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created namespace directive. Either or both of the
+   * [comment] and [metadata] can be `null` if the directive does not have the
+   * corresponding attribute. The list of [combinators] can be `null` if there
+   * are no combinators.
+   */
+  NamespaceDirective(Comment comment, List<Annotation> metadata, this.keyword,
+      StringLiteral libraryUri, List<Combinator> combinators, this.semicolon)
+      : super(comment, metadata, libraryUri) {
+    _combinators = new NodeList<Combinator>(this, combinators);
+  }
+
+  /**
+   * Return the combinators used to control how names are imported or exported.
+   */
+  NodeList<Combinator> get combinators => _combinators;
+
+  @override
+  Token get endToken => semicolon;
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata => keyword;
+
+  @override
+  LibraryElement get uriElement;
+}
+
+/**
+ * The "native" clause in an class declaration.
+ *
+ * > nativeClause ::=
+ * >     'native' [StringLiteral]
+ */
+class NativeClause extends AstNode {
+  /**
+   * The token representing the 'native' keyword.
+   */
+  Token nativeKeyword;
+
+  /**
+   * The name of the native object that implements the class.
+   */
+  StringLiteral _name;
+
+  /**
+   * Initialize a newly created native clause.
+   */
+  NativeClause(this.nativeKeyword, StringLiteral name) {
+    _name = _becomeParentOf(name);
+  }
+
+  @override
+  Token get beginToken => nativeKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(nativeKeyword)
+    ..add(_name);
+
+  @override
+  Token get endToken => _name.endToken;
+
+  /**
+   * Get the token representing the 'native' keyword.
+   */
+  @deprecated // Use "this.nativeKeyword"
+  Token get keyword => nativeKeyword;
+
+  /**
+   * Set the token representing the 'native' keyword to the given [token].
+   */
+  @deprecated // Use "this.nativeKeyword"
+  set keyword(Token token) {
+    nativeKeyword = token;
+  }
+
+  /**
+   * Return the name of the native object that implements the class.
+   */
+  StringLiteral get name => _name;
+
+  /**
+   * Sets the name of the native object that implements the class to the given
+   * [name].
+   */
+  void set name(StringLiteral name) {
+    _name = _becomeParentOf(name);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitNativeClause(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_name, visitor);
+  }
+}
+
+/**
+ * A function body that consists of a native keyword followed by a string
+ * literal.
+ *
+ * > nativeFunctionBody ::=
+ * >     'native' [SimpleStringLiteral] ';'
+ */
+class NativeFunctionBody extends FunctionBody {
+  /**
+   * The token representing 'native' that marks the start of the function body.
+   */
+  Token nativeKeyword;
+
+  /**
+   * The string literal, after the 'native' token.
+   */
+  StringLiteral _stringLiteral;
+
+  /**
+   * The token representing the semicolon that marks the end of the function
+   * body.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created function body consisting of the 'native' token,
+   * a string literal, and a semicolon.
+   */
+  NativeFunctionBody(
+      this.nativeKeyword, StringLiteral stringLiteral, this.semicolon) {
+    _stringLiteral = _becomeParentOf(stringLiteral);
+  }
+
+  @override
+  Token get beginToken => nativeKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(nativeKeyword)
+    ..add(_stringLiteral)
+    ..add(semicolon);
+
+  @override
+  Token get endToken => semicolon;
+
+  /**
+   * Return the token representing 'native' that marks the start of the function
+   * body.
+   */
+  @deprecated // Use "this.nativeKeyword"
+  Token get nativeToken => nativeKeyword;
+
+  /**
+   * Set the token representing 'native' that marks the start of the function
+   * body to the given [token].
+   */
+  @deprecated // Use "this.nativeKeyword"
+  set nativeToken(Token token) {
+    nativeKeyword = token;
+  }
+
+  /**
+   * Return the string literal representing the string after the 'native' token.
+   */
+  StringLiteral get stringLiteral => _stringLiteral;
+
+  /**
+   * Set the string literal representing the string after the 'native' token to
+   * the given [stringLiteral].
+   */
+  void set stringLiteral(StringLiteral stringLiteral) {
+    _stringLiteral = _becomeParentOf(stringLiteral);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitNativeFunctionBody(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_stringLiteral, visitor);
+  }
+}
+
+/**
+ * A list of AST nodes that have a common parent.
+ */
+class NodeList<E extends AstNode> extends Object with ListMixin<E> {
+  /**
+   * The node that is the parent of each of the elements in the list.
+   */
+  AstNode owner;
+
+  /**
+   * The elements contained in the list.
+   */
+  List<E> _elements = <E>[];
+
+  /**
+   * Initialize a newly created list of nodes such that all of the nodes that
+   * are added to the list will have their parent set to the given [owner]. The
+   * list will initially be populated with the given [elements].
+   */
+  NodeList(this.owner, [List<E> elements]) {
+    addAll(elements);
+  }
+
+  /**
+   * Return the first token included in this node list's source range, or `null`
+   * if the list is empty.
+   */
+  Token get beginToken {
+    if (_elements.length == 0) {
+      return null;
+    }
+    return _elements[0].beginToken;
+  }
+
+  /**
+   * Return the last token included in this node list's source range, or `null`
+   * if the list is empty.
+   */
+  Token get endToken {
+    int length = _elements.length;
+    if (length == 0) {
+      return null;
+    }
+    return _elements[length - 1].endToken;
+  }
+
+  int get length => _elements.length;
+
+  @deprecated // Never intended for public use.
+  void set length(int value) {
+    throw new UnsupportedError("Cannot resize NodeList.");
+  }
+
+  E operator [](int index) {
+    if (index < 0 || index >= _elements.length) {
+      throw new RangeError("Index: $index, Size: ${_elements.length}");
+    }
+    return _elements[index];
+  }
+
+  void operator []=(int index, E node) {
+    if (index < 0 || index >= _elements.length) {
+      throw new RangeError("Index: $index, Size: ${_elements.length}");
+    }
+    owner._becomeParentOf(node);
+    _elements[index] = node;
+  }
+
+  /**
+   * Use the given [visitor] to visit each of the nodes in this list.
+   */
+  accept(AstVisitor visitor) {
+    int length = _elements.length;
+    for (var i = 0; i < length; i++) {
+      _elements[i].accept(visitor);
+    }
+  }
+
+  @override
+  void add(E node) {
+    insert(length, node);
+  }
+
+  @override
+  bool addAll(Iterable<E> nodes) {
+    if (nodes != null && !nodes.isEmpty) {
+      _elements.addAll(nodes);
+      for (E node in nodes) {
+        owner._becomeParentOf(node);
+      }
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  void clear() {
+    _elements = <E>[];
+  }
+
+  @override
+  void insert(int index, E node) {
+    int length = _elements.length;
+    if (index < 0 || index > length) {
+      throw new RangeError("Index: $index, Size: ${_elements.length}");
+    }
+    owner._becomeParentOf(node);
+    if (length == 0) {
+      _elements.add(node);
+    } else {
+      _elements.insert(index, node);
+    }
+  }
+
+  @override
+  E removeAt(int index) {
+    if (index < 0 || index >= _elements.length) {
+      throw new RangeError("Index: $index, Size: ${_elements.length}");
+    }
+    E removedNode = _elements[index];
+    _elements.removeAt(index);
+    return removedNode;
+  }
+
+  /**
+   * Create an empty list with the given [owner].
+   */
+  @deprecated // Use "new NodeList<E>(owner)"
+  static NodeList create(AstNode owner) => new NodeList(owner);
+}
+
+/**
+ * An object used to locate the [AstNode] associated with a source range, given
+ * the AST structure built from the source. More specifically, they will return
+ * the [AstNode] with the shortest length whose source range completely
+ * encompasses the specified range.
+ */
+class NodeLocator extends UnifyingAstVisitor<Object> {
+  /**
+   * The start offset of the range used to identify the node.
+   */
+  int _startOffset = 0;
+
+  /**
+   * The end offset of the range used to identify the node.
+   */
+  int _endOffset = 0;
+
+  /**
+   * The element that was found that corresponds to the given source range, or
+   * `null` if there is no such element.
+   */
+  AstNode _foundNode;
+
+  /**
+   * Initialize a newly created locator to locate an [AstNode] by locating the
+   * node within an AST structure that corresponds to the given [offset] in the
+   * source.
+   */
+  NodeLocator.con1(int offset) : this.con2(offset, offset);
+
+  /**
+   * Initialize a newly created locator to locate an [AstNode] by locating the
+   * node within an AST structure that corresponds to the given range of
+   * characters (between the [startOffset] and [endOffset] in the source.
+   */
+  NodeLocator.con2(this._startOffset, this._endOffset);
+
+  /**
+   * Return the node that was found that corresponds to the given source range
+   * or `null` if there is no such node.
+   */
+  AstNode get foundNode => _foundNode;
+
+  /**
+   * Search within the given AST [node] for an identifier representing an
+   * element in the specified source range. Return the element that was found,
+   * or `null` if no element was found.
+   */
+  AstNode searchWithin(AstNode node) {
+    if (node == null) {
+      return null;
+    }
+    try {
+      node.accept(this);
+    } on NodeLocator_NodeFoundException {
+      // A node with the right source position was found.
+    } catch (exception, stackTrace) {
+      AnalysisEngine.instance.logger.logInformation(
+          "Unable to locate element at offset ($_startOffset - $_endOffset)",
+          new CaughtException(exception, stackTrace));
+      return null;
+    }
+    return _foundNode;
+  }
+
+  @override
+  Object visitNode(AstNode node) {
+    int start = node.offset;
+    int end = start + node.length;
+    if (end < _startOffset) {
+      return null;
+    }
+    if (start > _endOffset) {
+      return null;
+    }
+    try {
+      node.visitChildren(this);
+    } on NodeLocator_NodeFoundException {
+      rethrow;
+    } catch (exception, stackTrace) {
+      // Ignore the exception and proceed in order to visit the rest of the
+      // structure.
+      AnalysisEngine.instance.logger.logInformation(
+          "Exception caught while traversing an AST structure.",
+          new CaughtException(exception, stackTrace));
+    }
+    if (start <= _startOffset && _endOffset <= end) {
+      _foundNode = node;
+      throw new NodeLocator_NodeFoundException();
+    }
+    return null;
+  }
+}
+
+/**
+ * An exception used by [NodeLocator] to cancel visiting after a node has been
+ * found.
+ */
+class NodeLocator_NodeFoundException extends RuntimeException {}
+
+/**
+ * An object that will replace one child node in an AST node with another node.
+ */
+class NodeReplacer implements AstVisitor<bool> {
+  /**
+   * The node being replaced.
+   */
+  final AstNode _oldNode;
+
+  /**
+   * The node that is replacing the old node.
+   */
+  final AstNode _newNode;
+
+  /**
+   * Initialize a newly created node locator to replace the [_oldNode] with the
+   * [_newNode].
+   */
+  NodeReplacer(this._oldNode, this._newNode);
+
+  @override
+  bool visitAdjacentStrings(AdjacentStrings node) {
+    if (_replaceInList(node.strings)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  bool visitAnnotatedNode(AnnotatedNode node) {
+    if (identical(node.documentationComment, _oldNode)) {
+      node.documentationComment = _newNode as Comment;
+      return true;
+    } else if (_replaceInList(node.metadata)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitAnnotation(Annotation node) {
+    if (identical(node.arguments, _oldNode)) {
+      node.arguments = _newNode as ArgumentList;
+      return true;
+    } else if (identical(node.constructorName, _oldNode)) {
+      node.constructorName = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.name, _oldNode)) {
+      node.name = _newNode as Identifier;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitArgumentList(ArgumentList node) {
+    if (_replaceInList(node.arguments)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitAsExpression(AsExpression node) {
+    if (identical(node.expression, _oldNode)) {
+      node.expression = _newNode as Expression;
+      return true;
+    } else if (identical(node.type, _oldNode)) {
+      node.type = _newNode as TypeName;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitAssertStatement(AssertStatement node) {
+    if (identical(node.condition, _oldNode)) {
+      node.condition = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitAssignmentExpression(AssignmentExpression node) {
+    if (identical(node.leftHandSide, _oldNode)) {
+      node.leftHandSide = _newNode as Expression;
+      return true;
+    } else if (identical(node.rightHandSide, _oldNode)) {
+      node.rightHandSide = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitAwaitExpression(AwaitExpression node) {
+    if (identical(node.expression, _oldNode)) {
+      node.expression = _newNode as Expression;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitBinaryExpression(BinaryExpression node) {
+    if (identical(node.leftOperand, _oldNode)) {
+      node.leftOperand = _newNode as Expression;
+      return true;
+    } else if (identical(node.rightOperand, _oldNode)) {
+      node.rightOperand = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitBlock(Block node) {
+    if (_replaceInList(node.statements)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitBlockFunctionBody(BlockFunctionBody node) {
+    if (identical(node.block, _oldNode)) {
+      node.block = _newNode as Block;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitBooleanLiteral(BooleanLiteral node) => visitNode(node);
+
+  @override
+  bool visitBreakStatement(BreakStatement node) {
+    if (identical(node.label, _oldNode)) {
+      node.label = _newNode as SimpleIdentifier;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitCascadeExpression(CascadeExpression node) {
+    if (identical(node.target, _oldNode)) {
+      node.target = _newNode as Expression;
+      return true;
+    } else if (_replaceInList(node.cascadeSections)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitCatchClause(CatchClause node) {
+    if (identical(node.exceptionType, _oldNode)) {
+      node.exceptionType = _newNode as TypeName;
+      return true;
+    } else if (identical(node.exceptionParameter, _oldNode)) {
+      node.exceptionParameter = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.stackTraceParameter, _oldNode)) {
+      node.stackTraceParameter = _newNode as SimpleIdentifier;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitClassDeclaration(ClassDeclaration node) {
+    if (identical(node.name, _oldNode)) {
+      node.name = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.typeParameters, _oldNode)) {
+      node.typeParameters = _newNode as TypeParameterList;
+      return true;
+    } else if (identical(node.extendsClause, _oldNode)) {
+      node.extendsClause = _newNode as ExtendsClause;
+      return true;
+    } else if (identical(node.withClause, _oldNode)) {
+      node.withClause = _newNode as WithClause;
+      return true;
+    } else if (identical(node.implementsClause, _oldNode)) {
+      node.implementsClause = _newNode as ImplementsClause;
+      return true;
+    } else if (identical(node.nativeClause, _oldNode)) {
+      node.nativeClause = _newNode as NativeClause;
+      return true;
+    } else if (_replaceInList(node.members)) {
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitClassTypeAlias(ClassTypeAlias node) {
+    if (identical(node.name, _oldNode)) {
+      node.name = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.typeParameters, _oldNode)) {
+      node.typeParameters = _newNode as TypeParameterList;
+      return true;
+    } else if (identical(node.superclass, _oldNode)) {
+      node.superclass = _newNode as TypeName;
+      return true;
+    } else if (identical(node.withClause, _oldNode)) {
+      node.withClause = _newNode as WithClause;
+      return true;
+    } else if (identical(node.implementsClause, _oldNode)) {
+      node.implementsClause = _newNode as ImplementsClause;
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitComment(Comment node) {
+    if (_replaceInList(node.references)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitCommentReference(CommentReference node) {
+    if (identical(node.identifier, _oldNode)) {
+      node.identifier = _newNode as Identifier;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitCompilationUnit(CompilationUnit node) {
+    if (identical(node.scriptTag, _oldNode)) {
+      node.scriptTag = _newNode as ScriptTag;
+      return true;
+    } else if (_replaceInList(node.directives)) {
+      return true;
+    } else if (_replaceInList(node.declarations)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitConditionalExpression(ConditionalExpression node) {
+    if (identical(node.condition, _oldNode)) {
+      node.condition = _newNode as Expression;
+      return true;
+    } else if (identical(node.thenExpression, _oldNode)) {
+      node.thenExpression = _newNode as Expression;
+      return true;
+    } else if (identical(node.elseExpression, _oldNode)) {
+      node.elseExpression = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitConstructorDeclaration(ConstructorDeclaration node) {
+    if (identical(node.returnType, _oldNode)) {
+      node.returnType = _newNode as Identifier;
+      return true;
+    } else if (identical(node.name, _oldNode)) {
+      node.name = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.parameters, _oldNode)) {
+      node.parameters = _newNode as FormalParameterList;
+      return true;
+    } else if (identical(node.redirectedConstructor, _oldNode)) {
+      node.redirectedConstructor = _newNode as ConstructorName;
+      return true;
+    } else if (identical(node.body, _oldNode)) {
+      node.body = _newNode as FunctionBody;
+      return true;
+    } else if (_replaceInList(node.initializers)) {
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+    if (identical(node.fieldName, _oldNode)) {
+      node.fieldName = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.expression, _oldNode)) {
+      node.expression = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitConstructorName(ConstructorName node) {
+    if (identical(node.type, _oldNode)) {
+      node.type = _newNode as TypeName;
+      return true;
+    } else if (identical(node.name, _oldNode)) {
+      node.name = _newNode as SimpleIdentifier;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitContinueStatement(ContinueStatement node) {
+    if (identical(node.label, _oldNode)) {
+      node.label = _newNode as SimpleIdentifier;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitDeclaredIdentifier(DeclaredIdentifier node) {
+    if (identical(node.type, _oldNode)) {
+      node.type = _newNode as TypeName;
+      return true;
+    } else if (identical(node.identifier, _oldNode)) {
+      node.identifier = _newNode as SimpleIdentifier;
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitDefaultFormalParameter(DefaultFormalParameter node) {
+    if (identical(node.parameter, _oldNode)) {
+      node.parameter = _newNode as NormalFormalParameter;
+      return true;
+    } else if (identical(node.defaultValue, _oldNode)) {
+      node.defaultValue = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitDoStatement(DoStatement node) {
+    if (identical(node.body, _oldNode)) {
+      node.body = _newNode as Statement;
+      return true;
+    } else if (identical(node.condition, _oldNode)) {
+      node.condition = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitDoubleLiteral(DoubleLiteral node) => visitNode(node);
+
+  @override
+  bool visitEmptyFunctionBody(EmptyFunctionBody node) => visitNode(node);
+
+  @override
+  bool visitEmptyStatement(EmptyStatement node) => visitNode(node);
+
+  @override
+  bool visitEnumConstantDeclaration(EnumConstantDeclaration node) {
+    if (identical(node.name, _oldNode)) {
+      node.name = _newNode as SimpleIdentifier;
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitEnumDeclaration(EnumDeclaration node) {
+    if (identical(node.name, _oldNode)) {
+      node.name = _newNode as SimpleIdentifier;
+      return true;
+    } else if (_replaceInList(node.constants)) {
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitExportDirective(ExportDirective node) =>
+      visitNamespaceDirective(node);
+
+  @override
+  bool visitExpressionFunctionBody(ExpressionFunctionBody node) {
+    if (identical(node.expression, _oldNode)) {
+      node.expression = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitExpressionStatement(ExpressionStatement node) {
+    if (identical(node.expression, _oldNode)) {
+      node.expression = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitExtendsClause(ExtendsClause node) {
+    if (identical(node.superclass, _oldNode)) {
+      node.superclass = _newNode as TypeName;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitFieldDeclaration(FieldDeclaration node) {
+    if (identical(node.fields, _oldNode)) {
+      node.fields = _newNode as VariableDeclarationList;
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitFieldFormalParameter(FieldFormalParameter node) {
+    if (identical(node.type, _oldNode)) {
+      node.type = _newNode as TypeName;
+      return true;
+    } else if (identical(node.parameters, _oldNode)) {
+      node.parameters = _newNode as FormalParameterList;
+      return true;
+    }
+    return visitNormalFormalParameter(node);
+  }
+
+  @override
+  bool visitForEachStatement(ForEachStatement node) {
+    if (identical(node.loopVariable, _oldNode)) {
+      node.loopVariable = _newNode as DeclaredIdentifier;
+      return true;
+    } else if (identical(node.identifier, _oldNode)) {
+      node.identifier = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.iterable, _oldNode)) {
+      node.iterable = _newNode as Expression;
+      return true;
+    } else if (identical(node.body, _oldNode)) {
+      node.body = _newNode as Statement;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitFormalParameterList(FormalParameterList node) {
+    if (_replaceInList(node.parameters)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitForStatement(ForStatement node) {
+    if (identical(node.variables, _oldNode)) {
+      node.variables = _newNode as VariableDeclarationList;
+      return true;
+    } else if (identical(node.initialization, _oldNode)) {
+      node.initialization = _newNode as Expression;
+      return true;
+    } else if (identical(node.condition, _oldNode)) {
+      node.condition = _newNode as Expression;
+      return true;
+    } else if (identical(node.body, _oldNode)) {
+      node.body = _newNode as Statement;
+      return true;
+    } else if (_replaceInList(node.updaters)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitFunctionDeclaration(FunctionDeclaration node) {
+    if (identical(node.returnType, _oldNode)) {
+      node.returnType = _newNode as TypeName;
+      return true;
+    } else if (identical(node.name, _oldNode)) {
+      node.name = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.functionExpression, _oldNode)) {
+      node.functionExpression = _newNode as FunctionExpression;
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
+    if (identical(node.functionDeclaration, _oldNode)) {
+      node.functionDeclaration = _newNode as FunctionDeclaration;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitFunctionExpression(FunctionExpression node) {
+    if (identical(node.parameters, _oldNode)) {
+      node.parameters = _newNode as FormalParameterList;
+      return true;
+    } else if (identical(node.body, _oldNode)) {
+      node.body = _newNode as FunctionBody;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    if (identical(node.function, _oldNode)) {
+      node.function = _newNode as Expression;
+      return true;
+    } else if (identical(node.argumentList, _oldNode)) {
+      node.argumentList = _newNode as ArgumentList;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitFunctionTypeAlias(FunctionTypeAlias node) {
+    if (identical(node.returnType, _oldNode)) {
+      node.returnType = _newNode as TypeName;
+      return true;
+    } else if (identical(node.name, _oldNode)) {
+      node.name = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.typeParameters, _oldNode)) {
+      node.typeParameters = _newNode as TypeParameterList;
+      return true;
+    } else if (identical(node.parameters, _oldNode)) {
+      node.parameters = _newNode as FormalParameterList;
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+    if (identical(node.returnType, _oldNode)) {
+      node.returnType = _newNode as TypeName;
+      return true;
+    } else if (identical(node.parameters, _oldNode)) {
+      node.parameters = _newNode as FormalParameterList;
+      return true;
+    }
+    return visitNormalFormalParameter(node);
+  }
+
+  @override
+  bool visitHideCombinator(HideCombinator node) {
+    if (_replaceInList(node.hiddenNames)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitIfStatement(IfStatement node) {
+    if (identical(node.condition, _oldNode)) {
+      node.condition = _newNode as Expression;
+      return true;
+    } else if (identical(node.thenStatement, _oldNode)) {
+      node.thenStatement = _newNode as Statement;
+      return true;
+    } else if (identical(node.elseStatement, _oldNode)) {
+      node.elseStatement = _newNode as Statement;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitImplementsClause(ImplementsClause node) {
+    if (_replaceInList(node.interfaces)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitImportDirective(ImportDirective node) {
+    if (identical(node.prefix, _oldNode)) {
+      node.prefix = _newNode as SimpleIdentifier;
+      return true;
+    }
+    return visitNamespaceDirective(node);
+  }
+
+  @override
+  bool visitIndexExpression(IndexExpression node) {
+    if (identical(node.target, _oldNode)) {
+      node.target = _newNode as Expression;
+      return true;
+    } else if (identical(node.index, _oldNode)) {
+      node.index = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitInstanceCreationExpression(InstanceCreationExpression node) {
+    if (identical(node.constructorName, _oldNode)) {
+      node.constructorName = _newNode as ConstructorName;
+      return true;
+    } else if (identical(node.argumentList, _oldNode)) {
+      node.argumentList = _newNode as ArgumentList;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitIntegerLiteral(IntegerLiteral node) => visitNode(node);
+
+  @override
+  bool visitInterpolationExpression(InterpolationExpression node) {
+    if (identical(node.expression, _oldNode)) {
+      node.expression = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitInterpolationString(InterpolationString node) => visitNode(node);
+
+  @override
+  bool visitIsExpression(IsExpression node) {
+    if (identical(node.expression, _oldNode)) {
+      node.expression = _newNode as Expression;
+      return true;
+    } else if (identical(node.type, _oldNode)) {
+      node.type = _newNode as TypeName;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitLabel(Label node) {
+    if (identical(node.label, _oldNode)) {
+      node.label = _newNode as SimpleIdentifier;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitLabeledStatement(LabeledStatement node) {
+    if (identical(node.statement, _oldNode)) {
+      node.statement = _newNode as Statement;
+      return true;
+    } else if (_replaceInList(node.labels)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitLibraryDirective(LibraryDirective node) {
+    if (identical(node.name, _oldNode)) {
+      node.name = _newNode as LibraryIdentifier;
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitLibraryIdentifier(LibraryIdentifier node) {
+    if (_replaceInList(node.components)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitListLiteral(ListLiteral node) {
+    if (_replaceInList(node.elements)) {
+      return true;
+    }
+    return visitTypedLiteral(node);
+  }
+
+  @override
+  bool visitMapLiteral(MapLiteral node) {
+    if (_replaceInList(node.entries)) {
+      return true;
+    }
+    return visitTypedLiteral(node);
+  }
+
+  @override
+  bool visitMapLiteralEntry(MapLiteralEntry node) {
+    if (identical(node.key, _oldNode)) {
+      node.key = _newNode as Expression;
+      return true;
+    } else if (identical(node.value, _oldNode)) {
+      node.value = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitMethodDeclaration(MethodDeclaration node) {
+    if (identical(node.returnType, _oldNode)) {
+      node.returnType = _newNode as TypeName;
+      return true;
+    } else if (identical(node.name, _oldNode)) {
+      node.name = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.parameters, _oldNode)) {
+      node.parameters = _newNode as FormalParameterList;
+      return true;
+    } else if (identical(node.body, _oldNode)) {
+      node.body = _newNode as FunctionBody;
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitMethodInvocation(MethodInvocation node) {
+    if (identical(node.target, _oldNode)) {
+      node.target = _newNode as Expression;
+      return true;
+    } else if (identical(node.methodName, _oldNode)) {
+      node.methodName = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.argumentList, _oldNode)) {
+      node.argumentList = _newNode as ArgumentList;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitNamedExpression(NamedExpression node) {
+    if (identical(node.name, _oldNode)) {
+      node.name = _newNode as Label;
+      return true;
+    } else if (identical(node.expression, _oldNode)) {
+      node.expression = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  bool visitNamespaceDirective(NamespaceDirective node) {
+    if (_replaceInList(node.combinators)) {
+      return true;
+    }
+    return visitUriBasedDirective(node);
+  }
+
+  @override
+  bool visitNativeClause(NativeClause node) {
+    if (identical(node.name, _oldNode)) {
+      node.name = _newNode as StringLiteral;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitNativeFunctionBody(NativeFunctionBody node) {
+    if (identical(node.stringLiteral, _oldNode)) {
+      node.stringLiteral = _newNode as StringLiteral;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  bool visitNode(AstNode node) {
+    throw new IllegalArgumentException(
+        "The old node is not a child of it's parent");
+  }
+
+  bool visitNormalFormalParameter(NormalFormalParameter node) {
+    if (identical(node.documentationComment, _oldNode)) {
+      node.documentationComment = _newNode as Comment;
+      return true;
+    } else if (identical(node.identifier, _oldNode)) {
+      node.identifier = _newNode as SimpleIdentifier;
+      return true;
+    } else if (_replaceInList(node.metadata)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitNullLiteral(NullLiteral node) => visitNode(node);
+
+  @override
+  bool visitParenthesizedExpression(ParenthesizedExpression node) {
+    if (identical(node.expression, _oldNode)) {
+      node.expression = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitPartDirective(PartDirective node) => visitUriBasedDirective(node);
+
+  @override
+  bool visitPartOfDirective(PartOfDirective node) {
+    if (identical(node.libraryName, _oldNode)) {
+      node.libraryName = _newNode as LibraryIdentifier;
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitPostfixExpression(PostfixExpression node) {
+    if (identical(node.operand, _oldNode)) {
+      node.operand = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitPrefixedIdentifier(PrefixedIdentifier node) {
+    if (identical(node.prefix, _oldNode)) {
+      node.prefix = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.identifier, _oldNode)) {
+      node.identifier = _newNode as SimpleIdentifier;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitPrefixExpression(PrefixExpression node) {
+    if (identical(node.operand, _oldNode)) {
+      node.operand = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitPropertyAccess(PropertyAccess node) {
+    if (identical(node.target, _oldNode)) {
+      node.target = _newNode as Expression;
+      return true;
+    } else if (identical(node.propertyName, _oldNode)) {
+      node.propertyName = _newNode as SimpleIdentifier;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    if (identical(node.constructorName, _oldNode)) {
+      node.constructorName = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.argumentList, _oldNode)) {
+      node.argumentList = _newNode as ArgumentList;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitRethrowExpression(RethrowExpression node) => visitNode(node);
+
+  @override
+  bool visitReturnStatement(ReturnStatement node) {
+    if (identical(node.expression, _oldNode)) {
+      node.expression = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitScriptTag(ScriptTag scriptTag) => visitNode(scriptTag);
+
+  @override
+  bool visitShowCombinator(ShowCombinator node) {
+    if (_replaceInList(node.shownNames)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitSimpleFormalParameter(SimpleFormalParameter node) {
+    if (identical(node.type, _oldNode)) {
+      node.type = _newNode as TypeName;
+      return true;
+    }
+    return visitNormalFormalParameter(node);
+  }
+
+  @override
+  bool visitSimpleIdentifier(SimpleIdentifier node) => visitNode(node);
+
+  @override
+  bool visitSimpleStringLiteral(SimpleStringLiteral node) => visitNode(node);
+
+  @override
+  bool visitStringInterpolation(StringInterpolation node) {
+    if (_replaceInList(node.elements)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    if (identical(node.constructorName, _oldNode)) {
+      node.constructorName = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.argumentList, _oldNode)) {
+      node.argumentList = _newNode as ArgumentList;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitSuperExpression(SuperExpression node) => visitNode(node);
+
+  @override
+  bool visitSwitchCase(SwitchCase node) {
+    if (identical(node.expression, _oldNode)) {
+      node.expression = _newNode as Expression;
+      return true;
+    }
+    return visitSwitchMember(node);
+  }
+
+  @override
+  bool visitSwitchDefault(SwitchDefault node) => visitSwitchMember(node);
+
+  bool visitSwitchMember(SwitchMember node) {
+    if (_replaceInList(node.labels)) {
+      return true;
+    } else if (_replaceInList(node.statements)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitSwitchStatement(SwitchStatement node) {
+    if (identical(node.expression, _oldNode)) {
+      node.expression = _newNode as Expression;
+      return true;
+    } else if (_replaceInList(node.members)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitSymbolLiteral(SymbolLiteral node) => visitNode(node);
+
+  @override
+  bool visitThisExpression(ThisExpression node) => visitNode(node);
+
+  @override
+  bool visitThrowExpression(ThrowExpression node) {
+    if (identical(node.expression, _oldNode)) {
+      node.expression = _newNode as Expression;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+    if (identical(node.variables, _oldNode)) {
+      node.variables = _newNode as VariableDeclarationList;
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitTryStatement(TryStatement node) {
+    if (identical(node.body, _oldNode)) {
+      node.body = _newNode as Block;
+      return true;
+    } else if (identical(node.finallyBlock, _oldNode)) {
+      node.finallyBlock = _newNode as Block;
+      return true;
+    } else if (_replaceInList(node.catchClauses)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitTypeArgumentList(TypeArgumentList node) {
+    if (_replaceInList(node.arguments)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  bool visitTypedLiteral(TypedLiteral node) {
+    if (identical(node.typeArguments, _oldNode)) {
+      node.typeArguments = _newNode as TypeArgumentList;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitTypeName(TypeName node) {
+    if (identical(node.name, _oldNode)) {
+      node.name = _newNode as Identifier;
+      return true;
+    } else if (identical(node.typeArguments, _oldNode)) {
+      node.typeArguments = _newNode as TypeArgumentList;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitTypeParameter(TypeParameter node) {
+    if (identical(node.name, _oldNode)) {
+      node.name = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.bound, _oldNode)) {
+      node.bound = _newNode as TypeName;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitTypeParameterList(TypeParameterList node) {
+    if (_replaceInList(node.typeParameters)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  bool visitUriBasedDirective(UriBasedDirective node) {
+    if (identical(node.uri, _oldNode)) {
+      node.uri = _newNode as StringLiteral;
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitVariableDeclaration(VariableDeclaration node) {
+    if (identical(node.name, _oldNode)) {
+      node.name = _newNode as SimpleIdentifier;
+      return true;
+    } else if (identical(node.initializer, _oldNode)) {
+      node.initializer = _newNode as Expression;
+      return true;
+    }
+    return visitAnnotatedNode(node);
+  }
+
+  @override
+  bool visitVariableDeclarationList(VariableDeclarationList node) {
+    if (identical(node.type, _oldNode)) {
+      node.type = _newNode as TypeName;
+      return true;
+    } else if (_replaceInList(node.variables)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitVariableDeclarationStatement(VariableDeclarationStatement node) {
+    if (identical(node.variables, _oldNode)) {
+      node.variables = _newNode as VariableDeclarationList;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitWhileStatement(WhileStatement node) {
+    if (identical(node.condition, _oldNode)) {
+      node.condition = _newNode as Expression;
+      return true;
+    } else if (identical(node.body, _oldNode)) {
+      node.body = _newNode as Statement;
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitWithClause(WithClause node) {
+    if (_replaceInList(node.mixinTypes)) {
+      return true;
+    }
+    return visitNode(node);
+  }
+
+  @override
+  bool visitYieldStatement(YieldStatement node) {
+    if (identical(node.expression, _oldNode)) {
+      node.expression = _newNode as Expression;
+    }
+    return visitNode(node);
+  }
+
+  bool _replaceInList(NodeList list) {
+    int count = list.length;
+    for (int i = 0; i < count; i++) {
+      if (identical(_oldNode, list[i])) {
+        list[i] = _newNode;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Replace the [oldNode] with the [newNode] in the AST structure containing
+   * the old node. Return `true` if the replacement was successful.
+   *
+   * Throws an [IllegalArgumentException] if either node is `null`, if the old
+   * node does not have a parent node, or if the AST structure has been
+   * corrupted.
+   */
+  static bool replace(AstNode oldNode, AstNode newNode) {
+    if (oldNode == null || newNode == null) {
+      throw new IllegalArgumentException(
+          "The old and new nodes must be non-null");
+    } else if (identical(oldNode, newNode)) {
+      return true;
+    }
+    AstNode parent = oldNode.parent;
+    if (parent == null) {
+      throw new IllegalArgumentException(
+          "The old node is not a child of another node");
+    }
+    NodeReplacer replacer = new NodeReplacer(oldNode, newNode);
+    return parent.accept(replacer);
+  }
+}
+
+/**
+ * A formal parameter that is required (is not optional).
+ *
+ * > normalFormalParameter ::=
+ * >     [FunctionTypedFormalParameter]
+ * >   | [FieldFormalParameter]
+ * >   | [SimpleFormalParameter]
+ */
+abstract class NormalFormalParameter extends FormalParameter {
+  /**
+   * The documentation comment associated with this parameter, or `null` if this
+   * parameter does not have a documentation comment associated with it.
+   */
+  Comment _comment;
+
+  /**
+   * The annotations associated with this parameter.
+   */
+  NodeList<Annotation> _metadata;
+
+  /**
+   * The name of the parameter being declared.
+   */
+  SimpleIdentifier _identifier;
+
+  /**
+   * Initialize a newly created formal parameter. Either or both of the
+   * [comment] and [metadata] can be `null` if the parameter does not have the
+   * corresponding attribute.
+   */
+  NormalFormalParameter(
+      Comment comment, List<Annotation> metadata, SimpleIdentifier identifier) {
+    _comment = _becomeParentOf(comment);
+    _metadata = new NodeList<Annotation>(this, metadata);
+    _identifier = _becomeParentOf(identifier);
+  }
+
+  /**
+   * Return the documentation comment associated with this parameter, or `null`
+   * if this parameter does not have a documentation comment associated with it.
+   */
+  Comment get documentationComment => _comment;
+
+  /**
+   * Set the documentation comment associated with this parameter to the given
+   * [comment].
+   */
+  void set documentationComment(Comment comment) {
+    _comment = _becomeParentOf(comment);
+  }
+
+  @override
+  SimpleIdentifier get identifier => _identifier;
+
+  /**
+   * Set the name of the parameter being declared to the given [identifier].
+   */
+  void set identifier(SimpleIdentifier identifier) {
+    _identifier = _becomeParentOf(identifier);
+  }
+
+  @override
+  ParameterKind get kind {
+    AstNode parent = this.parent;
+    if (parent is DefaultFormalParameter) {
+      return parent.kind;
+    }
+    return ParameterKind.REQUIRED;
+  }
+
+  /**
+   * Return the annotations associated with this parameter.
+   */
+  NodeList<Annotation> get metadata => _metadata;
+
+  /**
+   * Set the metadata associated with this node to the given [metadata].
+   */
+  void set metadata(List<Annotation> metadata) {
+    _metadata.clear();
+    _metadata.addAll(metadata);
+  }
+
+  /**
+   * Return a list containing the comment and annotations associated with this
+   * parameter, sorted in lexical order.
+   */
+  List<AstNode> get sortedCommentAndAnnotations {
+    return <AstNode>[]
+      ..add(_comment)
+      ..addAll(_metadata)
+      ..sort(AstNode.LEXICAL_ORDER);
+  }
+
+  ChildEntities get _childEntities {
+    ChildEntities result = new ChildEntities();
+    if (_commentIsBeforeAnnotations()) {
+      result
+        ..add(_comment)
+        ..addAll(_metadata);
+    } else {
+      result.addAll(sortedCommentAndAnnotations);
+    }
+    return result;
+  }
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    //
+    // Note that subclasses are responsible for visiting the identifier because
+    // they often need to visit other nodes before visiting the identifier.
+    //
+    if (_commentIsBeforeAnnotations()) {
+      _safelyVisitChild(_comment, visitor);
+      _metadata.accept(visitor);
+    } else {
+      for (AstNode child in sortedCommentAndAnnotations) {
+        child.accept(visitor);
+      }
+    }
+  }
+
+  /**
+   * Return `true` if the comment is lexically before any annotations.
+   */
+  bool _commentIsBeforeAnnotations() {
+    if (_comment == null || _metadata.isEmpty) {
+      return true;
+    }
+    Annotation firstAnnotation = _metadata[0];
+    return _comment.offset < firstAnnotation.offset;
+  }
+}
+
+/**
+ * A null literal expression.
+ *
+ * > nullLiteral ::=
+ * >     'null'
+ */
+class NullLiteral extends Literal {
+  /**
+   * The token representing the literal.
+   */
+  Token literal;
+
+  /**
+   * Initialize a newly created null literal.
+   */
+  NullLiteral(this.literal);
+
+  @override
+  Token get beginToken => literal;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..add(literal);
+
+  @override
+  Token get endToken => literal;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitNullLiteral(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    // There are no children to visit.
+  }
+}
+
+/**
+ * A parenthesized expression.
+ *
+ * > parenthesizedExpression ::=
+ * >     '(' [Expression] ')'
+ */
+class ParenthesizedExpression extends Expression {
+  /**
+   * The left parenthesis.
+   */
+  Token leftParenthesis;
+
+  /**
+   * The expression within the parentheses.
+   */
+  Expression _expression;
+
+  /**
+   * The right parenthesis.
+   */
+  Token rightParenthesis;
+
+  /**
+   * Initialize a newly created parenthesized expression.
+   */
+  ParenthesizedExpression(
+      this.leftParenthesis, Expression expression, this.rightParenthesis) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get beginToken => leftParenthesis;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(leftParenthesis)
+    ..add(_expression)
+    ..add(rightParenthesis);
+
+  @override
+  Token get endToken => rightParenthesis;
+
+  /**
+   * Return the expression within the parentheses.
+   */
+  Expression get expression => _expression;
+
+  /**
+   * Set the expression within the parentheses to the given [expression].
+   */
+  void set expression(Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  int get precedence => 15;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitParenthesizedExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_expression, visitor);
+  }
+}
+
+/**
+ * A part directive.
+ *
+ * > partDirective ::=
+ * >     [Annotation] 'part' [StringLiteral] ';'
+ */
+class PartDirective extends UriBasedDirective {
+  /**
+   * The token representing the 'part' keyword.
+   */
+  Token partKeyword;
+
+  /**
+   * The semicolon terminating the directive.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created part directive. Either or both of the [comment]
+   * and [metadata] can be `null` if the directive does not have the
+   * corresponding attribute.
+   */
+  PartDirective(Comment comment, List<Annotation> metadata, this.partKeyword,
+      StringLiteral partUri, this.semicolon)
+      : super(comment, metadata, partUri);
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(partKeyword)
+    ..add(_uri)
+    ..add(semicolon);
+
+  @override
+  Token get endToken => semicolon;
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata => partKeyword;
+
+  @override
+  Token get keyword => partKeyword;
+
+  /**
+   * Return the token representing the 'part' token.
+   */
+  @deprecated // Use "this.partKeyword"
+  Token get partToken => partKeyword;
+
+  /**
+   * Set the token representing the 'part' token to the given [token].
+   */
+  @deprecated // Use "this.partKeyword"
+  set partToken(Token token) {
+    partKeyword = token;
+  }
+
+  @override
+  CompilationUnitElement get uriElement => element as CompilationUnitElement;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitPartDirective(this);
+}
+
+/**
+ * A part-of directive.
+ *
+ * > partOfDirective ::=
+ * >     [Annotation] 'part' 'of' [Identifier] ';'
+ */
+class PartOfDirective extends Directive {
+  /**
+   * The token representing the 'part' keyword.
+   */
+  Token partKeyword;
+
+  /**
+   * The token representing the 'of' keyword.
+   */
+  Token ofKeyword;
+
+  /**
+   * The name of the library that the containing compilation unit is part of.
+   */
+  LibraryIdentifier _libraryName;
+
+  /**
+   * The semicolon terminating the directive.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created part-of directive. Either or both of the
+   * [comment] and [metadata] can be `null` if the directive does not have the
+   * corresponding attribute.
+   */
+  PartOfDirective(Comment comment, List<Annotation> metadata, this.partKeyword,
+      this.ofKeyword, LibraryIdentifier libraryName, this.semicolon)
+      : super(comment, metadata) {
+    _libraryName = _becomeParentOf(libraryName);
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(partKeyword)
+    ..add(ofKeyword)
+    ..add(_libraryName)
+    ..add(semicolon);
+
+  @override
+  Token get endToken => semicolon;
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata => partKeyword;
+
+  @override
+  Token get keyword => partKeyword;
+
+  /**
+   * Return the name of the library that the containing compilation unit is part
+   * of.
+   */
+  LibraryIdentifier get libraryName => _libraryName;
+
+  /**
+   * Set the name of the library that the containing compilation unit is part of
+   * to the given [libraryName].
+   */
+  void set libraryName(LibraryIdentifier libraryName) {
+    _libraryName = _becomeParentOf(libraryName);
+  }
+
+  /**
+   * Return the token representing the 'of' token.
+   */
+  @deprecated // Use "this.ofKeyword"
+  Token get ofToken => ofKeyword;
+
+  /**
+   * Set the token representing the 'of' token to the given [token].
+   */
+  @deprecated // Use "this.ofKeyword"
+  set ofToken(Token token) {
+    ofKeyword = token;
+  }
+
+  /**
+   * Return the token representing the 'part' token.
+   */
+  @deprecated // Use "this.partKeyword"
+  Token get partToken => partKeyword;
+
+  /**
+   * Set the token representing the 'part' token to the given [token].
+   */
+  @deprecated // Use "this.partKeyword"
+  set partToken(Token token) {
+    partKeyword = token;
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitPartOfDirective(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_libraryName, visitor);
+  }
+}
+
+/**
+ * A postfix unary expression.
+ *
+ * > postfixExpression ::=
+ * >     [Expression] [Token]
+ */
+class PostfixExpression extends Expression {
+  /**
+   * The expression computing the operand for the operator.
+   */
+  Expression _operand;
+
+  /**
+   * The postfix operator being applied to the operand.
+   */
+  Token operator;
+
+  /**
+   * The element associated with this the operator based on the propagated type
+   * of the operand, or `null` if the AST structure has not been resolved, if
+   * the operator is not user definable, or if the operator could not be
+   * resolved.
+   */
+  MethodElement propagatedElement;
+
+  /**
+   * The element associated with the operator based on the static type of the
+   * operand, or `null` if the AST structure has not been resolved, if the
+   * operator is not user definable, or if the operator could not be resolved.
+   */
+  MethodElement staticElement;
+
+  /**
+   * Initialize a newly created postfix expression.
+   */
+  PostfixExpression(Expression operand, this.operator) {
+    _operand = _becomeParentOf(operand);
+  }
+
+  @override
+  Token get beginToken => _operand.beginToken;
+
+  /**
+   * Return the best element available for this operator. If resolution was able
+   * to find a better element based on type propagation, that element will be
+   * returned. Otherwise, the element found using the result of static analysis
+   * will be returned. If resolution has not been performed, then `null` will be
+   * returned.
+   */
+  MethodElement get bestElement {
+    MethodElement element = propagatedElement;
+    if (element == null) {
+      element = staticElement;
+    }
+    return element;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_operand)
+    ..add(operator);
+
+  @override
+  Token get endToken => operator;
+
+  /**
+   * Return the expression computing the operand for the operator.
+   */
+  Expression get operand => _operand;
+
+  /**
+   * Set the expression computing the operand for the operator to the given
+   * [expression].
+   */
+  void set operand(Expression expression) {
+    _operand = _becomeParentOf(expression);
+  }
+
+  @override
+  int get precedence => 15;
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on propagated type information, then return the parameter
+   * element representing the parameter to which the value of the operand will
+   * be bound. Otherwise, return `null`.
+   */
+  @deprecated // Use "expression.propagatedParameterElement"
+  ParameterElement get propagatedParameterElementForOperand {
+    return _propagatedParameterElementForOperand;
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on static type information, then return the parameter element
+   * representing the parameter to which the value of the operand will be bound.
+   * Otherwise, return `null`.
+   */
+  @deprecated // Use "expression.propagatedParameterElement"
+  ParameterElement get staticParameterElementForOperand {
+    return _staticParameterElementForOperand;
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on propagated type information, then return the parameter
+   * element representing the parameter to which the value of the operand will
+   * be bound. Otherwise, return `null`.
+   */
+  ParameterElement get _propagatedParameterElementForOperand {
+    if (propagatedElement == null) {
+      return null;
+    }
+    List<ParameterElement> parameters = propagatedElement.parameters;
+    if (parameters.length < 1) {
+      return null;
+    }
+    return parameters[0];
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on static type information, then return the parameter element
+   * representing the parameter to which the value of the operand will be bound.
+   * Otherwise, return `null`.
+   */
+  ParameterElement get _staticParameterElementForOperand {
+    if (staticElement == null) {
+      return null;
+    }
+    List<ParameterElement> parameters = staticElement.parameters;
+    if (parameters.length < 1) {
+      return null;
+    }
+    return parameters[0];
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitPostfixExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_operand, visitor);
+  }
+}
+
+/**
+ * An identifier that is prefixed or an access to an object property where the
+ * target of the property access is a simple identifier.
+ *
+ * > prefixedIdentifier ::=
+ * >     [SimpleIdentifier] '.' [SimpleIdentifier]
+ */
+class PrefixedIdentifier extends Identifier {
+  /**
+   * The prefix associated with the library in which the identifier is defined.
+   */
+  SimpleIdentifier _prefix;
+
+  /**
+   * The period used to separate the prefix from the identifier.
+   */
+  Token period;
+
+  /**
+   * The identifier being prefixed.
+   */
+  SimpleIdentifier _identifier;
+
+  /**
+   * Initialize a newly created prefixed identifier.
+   */
+  PrefixedIdentifier(
+      SimpleIdentifier prefix, this.period, SimpleIdentifier identifier) {
+    _prefix = _becomeParentOf(prefix);
+    _identifier = _becomeParentOf(identifier);
+  }
+
+  @override
+  Token get beginToken => _prefix.beginToken;
+
+  @override
+  Element get bestElement {
+    if (_identifier == null) {
+      return null;
+    }
+    return _identifier.bestElement;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_prefix)
+    ..add(period)
+    ..add(_identifier);
+
+  @override
+  Token get endToken => _identifier.endToken;
+
+  /**
+   * Return the identifier being prefixed.
+   */
+  SimpleIdentifier get identifier => _identifier;
+
+  /**
+   * Set the identifier being prefixed to the given [identifier].
+   */
+  void set identifier(SimpleIdentifier identifier) {
+    _identifier = _becomeParentOf(identifier);
+  }
+
+  /**
+   * Return `true` if this type is a deferred type. If the AST structure has not
+   * been resolved, then return `false`.
+   *
+   * 15.1 Static Types: A type <i>T</i> is deferred iff it is of the form
+   * </i>p.T</i> where <i>p</i> is a deferred prefix.
+   */
+  bool get isDeferred {
+    Element element = _prefix.staticElement;
+    if (element is! PrefixElement) {
+      return false;
+    }
+    PrefixElement prefixElement = element as PrefixElement;
+    List<ImportElement> imports =
+        prefixElement.enclosingElement.getImportsWithPrefix(prefixElement);
+    if (imports.length != 1) {
+      return false;
+    }
+    return imports[0].isDeferred;
+  }
+
+  @override
+  String get name => "${_prefix.name}.${_identifier.name}";
+
+  @override
+  int get precedence => 15;
+
+  /**
+   * Return the prefix associated with the library in which the identifier is
+   * defined.
+   */
+  SimpleIdentifier get prefix => _prefix;
+
+  /**
+   * Set the prefix associated with the library in which the identifier is
+   * defined to the given [identifier].
+   */
+  void set prefix(SimpleIdentifier identifier) {
+    _prefix = _becomeParentOf(identifier);
+  }
+
+  @override
+  Element get propagatedElement {
+    if (_identifier == null) {
+      return null;
+    }
+    return _identifier.propagatedElement;
+  }
+
+  @override
+  Element get staticElement {
+    if (_identifier == null) {
+      return null;
+    }
+    return _identifier.staticElement;
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitPrefixedIdentifier(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_prefix, visitor);
+    _safelyVisitChild(_identifier, visitor);
+  }
+}
+
+/**
+ * A prefix unary expression.
+ *
+ * > prefixExpression ::=
+ * >     [Token] [Expression]
+ */
+class PrefixExpression extends Expression {
+  /**
+   * The prefix operator being applied to the operand.
+   */
+  Token operator;
+
+  /**
+   * The expression computing the operand for the operator.
+   */
+  Expression _operand;
+
+  /**
+   * The element associated with the operator based on the static type of the
+   * operand, or `null` if the AST structure has not been resolved, if the
+   * operator is not user definable, or if the operator could not be resolved.
+   */
+  MethodElement staticElement;
+
+  /**
+   * The element associated with the operator based on the propagated type of
+   * the operand, or `null` if the AST structure has not been resolved, if the
+   * operator is not user definable, or if the operator could not be resolved.
+   */
+  MethodElement propagatedElement;
+
+  /**
+   * Initialize a newly created prefix expression.
+   */
+  PrefixExpression(this.operator, Expression operand) {
+    _operand = _becomeParentOf(operand);
+  }
+
+  @override
+  Token get beginToken => operator;
+
+  /**
+   * Return the best element available for this operator. If resolution was able
+   * to find a better element based on type propagation, that element will be
+   * returned. Otherwise, the element found using the result of static analysis
+   * will be returned. If resolution has not been performed, then `null` will be
+   * returned.
+   */
+  MethodElement get bestElement {
+    MethodElement element = propagatedElement;
+    if (element == null) {
+      element = staticElement;
+    }
+    return element;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(operator)
+    ..add(_operand);
+
+  @override
+  Token get endToken => _operand.endToken;
+
+  /**
+   * Return the expression computing the operand for the operator.
+   */
+  Expression get operand => _operand;
+
+  /**
+   * Set the expression computing the operand for the operator to the given
+   * [expression].
+   */
+  void set operand(Expression expression) {
+    _operand = _becomeParentOf(expression);
+  }
+
+  @override
+  int get precedence => 14;
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on propagated type information, then return the parameter
+   * element representing the parameter to which the value of the operand will
+   * be bound. Otherwise, return `null`.
+   */
+  @deprecated // Use "expression.propagatedParameterElement"
+  ParameterElement get propagatedParameterElementForOperand {
+    return _propagatedParameterElementForOperand;
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on static type information, then return the parameter element
+   * representing the parameter to which the value of the operand will be bound.
+   * Otherwise, return `null`.
+   */
+  @deprecated // Use "expression.propagatedParameterElement"
+  ParameterElement get staticParameterElementForOperand {
+    return _staticParameterElementForOperand;
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on propagated type information, then return the parameter
+   * element representing the parameter to which the value of the operand will
+   * be bound. Otherwise, return `null`.
+   */
+  ParameterElement get _propagatedParameterElementForOperand {
+    if (propagatedElement == null) {
+      return null;
+    }
+    List<ParameterElement> parameters = propagatedElement.parameters;
+    if (parameters.length < 1) {
+      return null;
+    }
+    return parameters[0];
+  }
+
+  /**
+   * If the AST structure has been resolved, and the function being invoked is
+   * known based on static type information, then return the parameter element
+   * representing the parameter to which the value of the operand will be bound.
+   * Otherwise, return `null`.
+   */
+  ParameterElement get _staticParameterElementForOperand {
+    if (staticElement == null) {
+      return null;
+    }
+    List<ParameterElement> parameters = staticElement.parameters;
+    if (parameters.length < 1) {
+      return null;
+    }
+    return parameters[0];
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitPrefixExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_operand, visitor);
+  }
+}
+
+/**
+ * The access of a property of an object.
+ *
+ * Note, however, that accesses to properties of objects can also be represented
+ * as [PrefixedIdentifier] nodes in cases where the target is also a simple
+ * identifier.
+ *
+ * > propertyAccess ::=
+ * >     [Expression] '.' [SimpleIdentifier]
+ */
+class PropertyAccess extends Expression {
+  /**
+   * The expression computing the object defining the property being accessed.
+   */
+  Expression _target;
+
+  /**
+   * The property access operator.
+   */
+  Token operator;
+
+  /**
+   * The name of the property being accessed.
+   */
+  SimpleIdentifier _propertyName;
+
+  /**
+   * Initialize a newly created property access expression.
+   */
+  PropertyAccess(
+      Expression target, this.operator, SimpleIdentifier propertyName) {
+    _target = _becomeParentOf(target);
+    _propertyName = _becomeParentOf(propertyName);
+  }
+
+  @override
+  Token get beginToken {
+    if (_target != null) {
+      return _target.beginToken;
+    }
+    return operator;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_target)
+    ..add(operator)
+    ..add(_propertyName);
+
+  @override
+  Token get endToken => _propertyName.endToken;
+
+  @override
+  bool get isAssignable => true;
+
+  /**
+   * Return `true` if this expression is cascaded. If it is, then the target of
+   * this expression is not stored locally but is stored in the nearest ancestor
+   * that is a [CascadeExpression].
+   */
+  bool get isCascaded =>
+      operator != null && operator.type == TokenType.PERIOD_PERIOD;
+
+  @override
+  int get precedence => 15;
+
+  /**
+   * Return the name of the property being accessed.
+   */
+  SimpleIdentifier get propertyName => _propertyName;
+
+  /**
+   * Set the name of the property being accessed to the given [identifier].
+   */
+  void set propertyName(SimpleIdentifier identifier) {
+    _propertyName = _becomeParentOf(identifier);
+  }
+
+  /**
+   * Return the expression used to compute the receiver of the invocation. If
+   * this invocation is not part of a cascade expression, then this is the same
+   * as [target]. If this invocation is part of a cascade expression, then the
+   * target stored with the cascade expression is returned.
+   */
+  Expression get realTarget {
+    if (isCascaded) {
+      AstNode ancestor = parent;
+      while (ancestor is! CascadeExpression) {
+        if (ancestor == null) {
+          return _target;
+        }
+        ancestor = ancestor.parent;
+      }
+      return (ancestor as CascadeExpression).target;
+    }
+    return _target;
+  }
+
+  /**
+   * Return the expression computing the object defining the property being
+   * accessed, or `null` if this property access is part of a cascade expression.
+   *
+   * Use [realTarget] to get the target independent of whether this is part of a
+   * cascade expression.
+   */
+  Expression get target => _target;
+
+  /**
+   * Set the expression computing the object defining the property being
+   * accessed to the given [expression].
+   */
+  void set target(Expression expression) {
+    _target = _becomeParentOf(expression);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitPropertyAccess(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_target, visitor);
+    _safelyVisitChild(_propertyName, visitor);
+  }
+}
+
+/**
+ * An AST visitor that will recursively visit all of the nodes in an AST
+ * structure. For example, using an instance of this class to visit a [Block]
+ * will also cause all of the statements in the block to be visited.
+ *
+ * Subclasses that override a visit method must either invoke the overridden
+ * visit method or must explicitly ask the visited node to visit its children.
+ * Failure to do so will cause the children of the visited node to not be
+ * visited.
+ */
+class RecursiveAstVisitor<R> implements AstVisitor<R> {
+  @override
+  R visitAdjacentStrings(AdjacentStrings node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitAnnotation(Annotation node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitArgumentList(ArgumentList node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitAsExpression(AsExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitAssertStatement(AssertStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitAssignmentExpression(AssignmentExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitAwaitExpression(AwaitExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitBinaryExpression(BinaryExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitBlock(Block node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitBlockFunctionBody(BlockFunctionBody node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitBooleanLiteral(BooleanLiteral node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitBreakStatement(BreakStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitCascadeExpression(CascadeExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitCatchClause(CatchClause node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitClassDeclaration(ClassDeclaration node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitClassTypeAlias(ClassTypeAlias node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitComment(Comment node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitCommentReference(CommentReference node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitCompilationUnit(CompilationUnit node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitConditionalExpression(ConditionalExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitConstructorDeclaration(ConstructorDeclaration node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitConstructorName(ConstructorName node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitContinueStatement(ContinueStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitDeclaredIdentifier(DeclaredIdentifier node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitDefaultFormalParameter(DefaultFormalParameter node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitDoStatement(DoStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitDoubleLiteral(DoubleLiteral node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitEmptyFunctionBody(EmptyFunctionBody node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitEmptyStatement(EmptyStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitEnumConstantDeclaration(EnumConstantDeclaration node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitEnumDeclaration(EnumDeclaration node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitExportDirective(ExportDirective node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitExpressionFunctionBody(ExpressionFunctionBody node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitExpressionStatement(ExpressionStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitExtendsClause(ExtendsClause node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitFieldDeclaration(FieldDeclaration node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitFieldFormalParameter(FieldFormalParameter node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitForEachStatement(ForEachStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitFormalParameterList(FormalParameterList node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitForStatement(ForStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitFunctionDeclaration(FunctionDeclaration node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitFunctionExpression(FunctionExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitFunctionTypeAlias(FunctionTypeAlias node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitHideCombinator(HideCombinator node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitIfStatement(IfStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitImplementsClause(ImplementsClause node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitImportDirective(ImportDirective node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitIndexExpression(IndexExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitInstanceCreationExpression(InstanceCreationExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitIntegerLiteral(IntegerLiteral node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitInterpolationExpression(InterpolationExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitInterpolationString(InterpolationString node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitIsExpression(IsExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitLabel(Label node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitLabeledStatement(LabeledStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitLibraryDirective(LibraryDirective node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitLibraryIdentifier(LibraryIdentifier node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitListLiteral(ListLiteral node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitMapLiteral(MapLiteral node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitMapLiteralEntry(MapLiteralEntry node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitMethodDeclaration(MethodDeclaration node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitMethodInvocation(MethodInvocation node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitNamedExpression(NamedExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitNativeClause(NativeClause node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitNativeFunctionBody(NativeFunctionBody node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitNullLiteral(NullLiteral node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitParenthesizedExpression(ParenthesizedExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitPartDirective(PartDirective node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitPartOfDirective(PartOfDirective node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitPostfixExpression(PostfixExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitPrefixedIdentifier(PrefixedIdentifier node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitPrefixExpression(PrefixExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitPropertyAccess(PropertyAccess node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitRethrowExpression(RethrowExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitReturnStatement(ReturnStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitScriptTag(ScriptTag node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitShowCombinator(ShowCombinator node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitSimpleFormalParameter(SimpleFormalParameter node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitSimpleIdentifier(SimpleIdentifier node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitSimpleStringLiteral(SimpleStringLiteral node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitStringInterpolation(StringInterpolation node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitSuperExpression(SuperExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitSwitchCase(SwitchCase node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitSwitchDefault(SwitchDefault node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitSwitchStatement(SwitchStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitSymbolLiteral(SymbolLiteral node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitThisExpression(ThisExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitThrowExpression(ThrowExpression node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitTryStatement(TryStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitTypeArgumentList(TypeArgumentList node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitTypeName(TypeName node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitTypeParameter(TypeParameter node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitTypeParameterList(TypeParameterList node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitVariableDeclaration(VariableDeclaration node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitVariableDeclarationList(VariableDeclarationList node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitVariableDeclarationStatement(VariableDeclarationStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitWhileStatement(WhileStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitWithClause(WithClause node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitYieldStatement(YieldStatement node) {
+    node.visitChildren(this);
+    return null;
+  }
+}
+
+/**
+ * The invocation of a constructor in the same class from within a constructor's
+ * initialization list.
+ *
+ * > redirectingConstructorInvocation ::=
+ * >     'this' ('.' identifier)? arguments
+ */
+class RedirectingConstructorInvocation extends ConstructorInitializer {
+  /**
+   * The token for the 'this' keyword.
+   */
+  Token thisKeyword;
+
+  /**
+   * The token for the period before the name of the constructor that is being
+   * invoked, or `null` if the unnamed constructor is being invoked.
+   */
+  Token period;
+
+  /**
+   * The name of the constructor that is being invoked, or `null` if the unnamed
+   * constructor is being invoked.
+   */
+  SimpleIdentifier _constructorName;
+
+  /**
+   * The list of arguments to the constructor.
+   */
+  ArgumentList _argumentList;
+
+  /**
+   * The element associated with the constructor based on static type
+   * information, or `null` if the AST structure has not been resolved or if the
+   * constructor could not be resolved.
+   */
+  ConstructorElement staticElement;
+
+  /**
+   * Initialize a newly created redirecting invocation to invoke the constructor
+   * with the given name with the given arguments. The [constructorName] can be
+   * `null` if the constructor being invoked is the unnamed constructor.
+   */
+  RedirectingConstructorInvocation(this.thisKeyword, this.period,
+      SimpleIdentifier constructorName, ArgumentList argumentList) {
+    _constructorName = _becomeParentOf(constructorName);
+    _argumentList = _becomeParentOf(argumentList);
+  }
+
+  /**
+   * Return the list of arguments to the constructor.
+   */
+  ArgumentList get argumentList => _argumentList;
+
+  /**
+   * Set the list of arguments to the constructor to the given [argumentList].
+   */
+  void set argumentList(ArgumentList argumentList) {
+    _argumentList = _becomeParentOf(argumentList);
+  }
+
+  @override
+  Token get beginToken => thisKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(thisKeyword)
+    ..add(period)
+    ..add(_constructorName)
+    ..add(_argumentList);
+
+  /**
+   * Return the name of the constructor that is being invoked, or `null` if the
+   * unnamed constructor is being invoked.
+   */
+  SimpleIdentifier get constructorName => _constructorName;
+
+  /**
+   * Set the name of the constructor that is being invoked to the given
+   * [identifier].
+   */
+  void set constructorName(SimpleIdentifier identifier) {
+    _constructorName = _becomeParentOf(identifier);
+  }
+
+  @override
+  Token get endToken => _argumentList.endToken;
+
+  /**
+   * Return the token for the 'this' keyword.
+   */
+  @deprecated // Use "this.thisKeyword"
+  Token get keyword => thisKeyword;
+
+  /**
+   * Set the token for the 'this' keyword to the given [token].
+   */
+  @deprecated // Use "this.thisKeyword"
+  set keyword(Token token) {
+    thisKeyword = token;
+  }
+
+  @override
+  accept(AstVisitor visitor) =>
+      visitor.visitRedirectingConstructorInvocation(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_constructorName, visitor);
+    _safelyVisitChild(_argumentList, visitor);
+  }
+}
+
+/**
+ * A rethrow expression.
+ *
+ * > rethrowExpression ::=
+ * >     'rethrow'
+ */
+class RethrowExpression extends Expression {
+  /**
+   * The token representing the 'rethrow' keyword.
+   */
+  Token rethrowKeyword;
+
+  /**
+   * Initialize a newly created rethrow expression.
+   */
+  RethrowExpression(this.rethrowKeyword);
+
+  @override
+  Token get beginToken => rethrowKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..add(rethrowKeyword);
+
+  @override
+  Token get endToken => rethrowKeyword;
+
+  /**
+   * Return the token representing the 'rethrow' keyword.
+   */
+  @deprecated // Use "this.rethrowKeyword"
+  Token get keyword => rethrowKeyword;
+
+  /**
+   * Set the token representing the 'rethrow' keyword to the given [token].
+   */
+  @deprecated // Use "this.rethrowKeyword"
+  set keyword(Token token) {
+    rethrowKeyword = token;
+  }
+
+  @override
+  int get precedence => 0;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitRethrowExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    // There are no children to visit.
+  }
+}
+
+/**
+ * A return statement.
+ *
+ * > returnStatement ::=
+ * >     'return' [Expression]? ';'
+ */
+class ReturnStatement extends Statement {
+  /**
+   * The token representing the 'return' keyword.
+   */
+  Token returnKeyword;
+
+  /**
+   * The expression computing the value to be returned, or `null` if no explicit
+   * value was provided.
+   */
+  Expression _expression;
+
+  /**
+   * The semicolon terminating the statement.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created return statement. The [expression] can be `null`
+   * if no explicit value was provided.
+   */
+  ReturnStatement(this.returnKeyword, Expression expression, this.semicolon) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get beginToken => returnKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(returnKeyword)
+    ..add(_expression)
+    ..add(semicolon);
+
+  @override
+  Token get endToken => semicolon;
+
+  /**
+   * Return the expression computing the value to be returned, or `null` if no
+   * explicit value was provided.
+   */
+  Expression get expression => _expression;
+
+  /**
+   * Set the expression computing the value to be returned to the given
+   * [expression].
+   */
+  void set expression(Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  /**
+   * Return the token representing the 'return' keyword.
+   */
+  @deprecated // Use "this.returnKeyword"
+  Token get keyword => returnKeyword;
+
+  /**
+   * Set the token representing the 'return' keyword to the given [token].
+   */
+  @deprecated // Use "this.returnKeyword"
+  set keyword(Token token) {
+    returnKeyword = token;
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitReturnStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_expression, visitor);
+  }
+}
+
+/**
+ * Traverse the AST from initial child node to successive parents, building a
+ * collection of local variable and parameter names visible to the initial child
+ * node. In case of name shadowing, the first name seen is the most specific one
+ * so names are not redefined.
+ *
+ * Completion test code coverage is 95%. The two basic blocks that are not
+ * executed cannot be executed. They are included for future reference.
+ */
+class ScopedNameFinder extends GeneralizingAstVisitor<Object> {
+  Declaration _declarationNode;
+
+  AstNode _immediateChild;
+
+  Map<String, SimpleIdentifier> _locals =
+      new HashMap<String, SimpleIdentifier>();
+
+  final int _position;
+
+  bool _referenceIsWithinLocalFunction = false;
+
+  ScopedNameFinder(this._position);
+
+  Declaration get declaration => _declarationNode;
+
+  Map<String, SimpleIdentifier> get locals => _locals;
+
+  @override
+  Object visitBlock(Block node) {
+    _checkStatements(node.statements);
+    return super.visitBlock(node);
+  }
+
+  @override
+  Object visitCatchClause(CatchClause node) {
+    _addToScope(node.exceptionParameter);
+    _addToScope(node.stackTraceParameter);
+    return super.visitCatchClause(node);
+  }
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    if (!identical(_immediateChild, node.parameters)) {
+      _addParameters(node.parameters.parameters);
+    }
+    _declarationNode = node;
+    return null;
+  }
+
+  @override
+  Object visitFieldDeclaration(FieldDeclaration node) {
+    _declarationNode = node;
+    return null;
+  }
+
+  @override
+  Object visitForEachStatement(ForEachStatement node) {
+    DeclaredIdentifier loopVariable = node.loopVariable;
+    if (loopVariable != null) {
+      _addToScope(loopVariable.identifier);
+    }
+    return super.visitForEachStatement(node);
+  }
+
+  @override
+  Object visitForStatement(ForStatement node) {
+    if (!identical(_immediateChild, node.variables) && node.variables != null) {
+      _addVariables(node.variables.variables);
+    }
+    return super.visitForStatement(node);
+  }
+
+  @override
+  Object visitFunctionDeclaration(FunctionDeclaration node) {
+    if (node.parent is! FunctionDeclarationStatement) {
+      _declarationNode = node;
+      return null;
+    }
+    return super.visitFunctionDeclaration(node);
+  }
+
+  @override
+  Object visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
+    _referenceIsWithinLocalFunction = true;
+    return super.visitFunctionDeclarationStatement(node);
+  }
+
+  @override
+  Object visitFunctionExpression(FunctionExpression node) {
+    if (node.parameters != null &&
+        !identical(_immediateChild, node.parameters)) {
+      _addParameters(node.parameters.parameters);
+    }
+    return super.visitFunctionExpression(node);
+  }
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    _declarationNode = node;
+    if (node.parameters == null) {
+      return null;
+    }
+    if (!identical(_immediateChild, node.parameters)) {
+      _addParameters(node.parameters.parameters);
+    }
+    return null;
+  }
+
+  @override
+  Object visitNode(AstNode node) {
+    _immediateChild = node;
+    AstNode parent = node.parent;
+    if (parent != null) {
+      parent.accept(this);
+    }
+    return null;
+  }
+
+  @override
+  Object visitSwitchMember(SwitchMember node) {
+    _checkStatements(node.statements);
+    return super.visitSwitchMember(node);
+  }
+
+  @override
+  Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+    _declarationNode = node;
+    return null;
+  }
+
+  @override
+  Object visitTypeAlias(TypeAlias node) {
+    _declarationNode = node;
+    return null;
+  }
+
+  void _addParameters(NodeList<FormalParameter> vars) {
+    for (FormalParameter var2 in vars) {
+      _addToScope(var2.identifier);
+    }
+  }
+
+  void _addToScope(SimpleIdentifier identifier) {
+    if (identifier != null && _isInRange(identifier)) {
+      String name = identifier.name;
+      if (!_locals.containsKey(name)) {
+        _locals[name] = identifier;
+      }
+    }
+  }
+
+  void _addVariables(NodeList<VariableDeclaration> variables) {
+    for (VariableDeclaration variable in variables) {
+      _addToScope(variable.name);
+    }
+  }
+
+  /**
+   * Check the given list of [statements] for any that come before the immediate
+   * child and that define a name that would be visible to the immediate child.
+   */
+  void _checkStatements(List<Statement> statements) {
+    for (Statement statement in statements) {
+      if (identical(statement, _immediateChild)) {
+        return;
+      }
+      if (statement is VariableDeclarationStatement) {
+        _addVariables(statement.variables.variables);
+      } else if (statement is FunctionDeclarationStatement &&
+          !_referenceIsWithinLocalFunction) {
+        _addToScope(statement.functionDeclaration.name);
+      }
+    }
+  }
+
+  bool _isInRange(AstNode node) {
+    if (_position < 0) {
+      // if source position is not set then all nodes are in range
+      return true;
+      // not reached
+    }
+    return node.end < _position;
+  }
+}
+
+/**
+ * A script tag that can optionally occur at the beginning of a compilation unit.
+ *
+ * > scriptTag ::=
+ * >     '#!' (~NEWLINE)* NEWLINE
+ */
+class ScriptTag extends AstNode {
+  /**
+   * The token representing this script tag.
+   */
+  Token scriptTag;
+
+  /**
+   * Initialize a newly created script tag.
+   */
+  ScriptTag(this.scriptTag);
+
+  @override
+  Token get beginToken => scriptTag;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..add(scriptTag);
+
+  @override
+  Token get endToken => scriptTag;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitScriptTag(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    // There are no children to visit.
+  }
+}
+
+/**
+ * A combinator that restricts the names being imported to those in a given list.
+ *
+ * > showCombinator ::=
+ * >     'show' [SimpleIdentifier] (',' [SimpleIdentifier])*
+ */
+class ShowCombinator extends Combinator {
+  /**
+   * The list of names from the library that are made visible by this combinator.
+   */
+  NodeList<SimpleIdentifier> _shownNames;
+
+  /**
+   * Initialize a newly created import show combinator.
+   */
+  ShowCombinator(Token keyword, List<SimpleIdentifier> shownNames)
+      : super(keyword) {
+    _shownNames = new NodeList<SimpleIdentifier>(this, shownNames);
+  }
+
+  /**
+   * TODO(paulberry): add commas.
+   */
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(keyword)
+    ..addAll(_shownNames);
+
+  @override
+  Token get endToken => _shownNames.endToken;
+
+  /**
+   * Return the list of names from the library that are made visible by this
+   * combinator.
+   */
+  NodeList<SimpleIdentifier> get shownNames => _shownNames;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitShowCombinator(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _shownNames.accept(visitor);
+  }
+}
+
+/**
+ * An AST visitor that will do nothing when visiting an AST node. It is intended
+ * to be a superclass for classes that use the visitor pattern primarily as a
+ * dispatch mechanism (and hence don't need to recursively visit a whole
+ * structure) and that only need to visit a small number of node types.
+ */
+class SimpleAstVisitor<R> implements AstVisitor<R> {
+  @override
+  R visitAdjacentStrings(AdjacentStrings node) => null;
+
+  @override
+  R visitAnnotation(Annotation node) => null;
+
+  @override
+  R visitArgumentList(ArgumentList node) => null;
+
+  @override
+  R visitAsExpression(AsExpression node) => null;
+
+  @override
+  R visitAssertStatement(AssertStatement node) => null;
+
+  @override
+  R visitAssignmentExpression(AssignmentExpression node) => null;
+
+  @override
+  R visitAwaitExpression(AwaitExpression node) => null;
+
+  @override
+  R visitBinaryExpression(BinaryExpression node) => null;
+
+  @override
+  R visitBlock(Block node) => null;
+
+  @override
+  R visitBlockFunctionBody(BlockFunctionBody node) => null;
+
+  @override
+  R visitBooleanLiteral(BooleanLiteral node) => null;
+
+  @override
+  R visitBreakStatement(BreakStatement node) => null;
+
+  @override
+  R visitCascadeExpression(CascadeExpression node) => null;
+
+  @override
+  R visitCatchClause(CatchClause node) => null;
+
+  @override
+  R visitClassDeclaration(ClassDeclaration node) => null;
+
+  @override
+  R visitClassTypeAlias(ClassTypeAlias node) => null;
+
+  @override
+  R visitComment(Comment node) => null;
+
+  @override
+  R visitCommentReference(CommentReference node) => null;
+
+  @override
+  R visitCompilationUnit(CompilationUnit node) => null;
+
+  @override
+  R visitConditionalExpression(ConditionalExpression node) => null;
+
+  @override
+  R visitConstructorDeclaration(ConstructorDeclaration node) => null;
+
+  @override
+  R visitConstructorFieldInitializer(ConstructorFieldInitializer node) => null;
+
+  @override
+  R visitConstructorName(ConstructorName node) => null;
+
+  @override
+  R visitContinueStatement(ContinueStatement node) => null;
+
+  @override
+  R visitDeclaredIdentifier(DeclaredIdentifier node) => null;
+
+  @override
+  R visitDefaultFormalParameter(DefaultFormalParameter node) => null;
+
+  @override
+  R visitDoStatement(DoStatement node) => null;
+
+  @override
+  R visitDoubleLiteral(DoubleLiteral node) => null;
+
+  @override
+  R visitEmptyFunctionBody(EmptyFunctionBody node) => null;
+
+  @override
+  R visitEmptyStatement(EmptyStatement node) => null;
+
+  @override
+  R visitEnumConstantDeclaration(EnumConstantDeclaration node) => null;
+
+  @override
+  R visitEnumDeclaration(EnumDeclaration node) => null;
+
+  @override
+  R visitExportDirective(ExportDirective node) => null;
+
+  @override
+  R visitExpressionFunctionBody(ExpressionFunctionBody node) => null;
+
+  @override
+  R visitExpressionStatement(ExpressionStatement node) => null;
+
+  @override
+  R visitExtendsClause(ExtendsClause node) => null;
+
+  @override
+  R visitFieldDeclaration(FieldDeclaration node) => null;
+
+  @override
+  R visitFieldFormalParameter(FieldFormalParameter node) => null;
+
+  @override
+  R visitForEachStatement(ForEachStatement node) => null;
+
+  @override
+  R visitFormalParameterList(FormalParameterList node) => null;
+
+  @override
+  R visitForStatement(ForStatement node) => null;
+
+  @override
+  R visitFunctionDeclaration(FunctionDeclaration node) => null;
+
+  @override
+  R visitFunctionDeclarationStatement(FunctionDeclarationStatement node) =>
+      null;
+
+  @override
+  R visitFunctionExpression(FunctionExpression node) => null;
+
+  @override
+  R visitFunctionExpressionInvocation(FunctionExpressionInvocation node) =>
+      null;
+
+  @override
+  R visitFunctionTypeAlias(FunctionTypeAlias node) => null;
+
+  @override
+  R visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) =>
+      null;
+
+  @override
+  R visitHideCombinator(HideCombinator node) => null;
+
+  @override
+  R visitIfStatement(IfStatement node) => null;
+
+  @override
+  R visitImplementsClause(ImplementsClause node) => null;
+
+  @override
+  R visitImportDirective(ImportDirective node) => null;
+
+  @override
+  R visitIndexExpression(IndexExpression node) => null;
+
+  @override
+  R visitInstanceCreationExpression(InstanceCreationExpression node) => null;
+
+  @override
+  R visitIntegerLiteral(IntegerLiteral node) => null;
+
+  @override
+  R visitInterpolationExpression(InterpolationExpression node) => null;
+
+  @override
+  R visitInterpolationString(InterpolationString node) => null;
+
+  @override
+  R visitIsExpression(IsExpression node) => null;
+
+  @override
+  R visitLabel(Label node) => null;
+
+  @override
+  R visitLabeledStatement(LabeledStatement node) => null;
+
+  @override
+  R visitLibraryDirective(LibraryDirective node) => null;
+
+  @override
+  R visitLibraryIdentifier(LibraryIdentifier node) => null;
+
+  @override
+  R visitListLiteral(ListLiteral node) => null;
+
+  @override
+  R visitMapLiteral(MapLiteral node) => null;
+
+  @override
+  R visitMapLiteralEntry(MapLiteralEntry node) => null;
+
+  @override
+  R visitMethodDeclaration(MethodDeclaration node) => null;
+
+  @override
+  R visitMethodInvocation(MethodInvocation node) => null;
+
+  @override
+  R visitNamedExpression(NamedExpression node) => null;
+
+  @override
+  R visitNativeClause(NativeClause node) => null;
+
+  @override
+  R visitNativeFunctionBody(NativeFunctionBody node) => null;
+
+  @override
+  R visitNullLiteral(NullLiteral node) => null;
+
+  @override
+  R visitParenthesizedExpression(ParenthesizedExpression node) => null;
+
+  @override
+  R visitPartDirective(PartDirective node) => null;
+
+  @override
+  R visitPartOfDirective(PartOfDirective node) => null;
+
+  @override
+  R visitPostfixExpression(PostfixExpression node) => null;
+
+  @override
+  R visitPrefixedIdentifier(PrefixedIdentifier node) => null;
+
+  @override
+  R visitPrefixExpression(PrefixExpression node) => null;
+
+  @override
+  R visitPropertyAccess(PropertyAccess node) => null;
+
+  @override
+  R visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) => null;
+
+  @override
+  R visitRethrowExpression(RethrowExpression node) => null;
+
+  @override
+  R visitReturnStatement(ReturnStatement node) => null;
+
+  @override
+  R visitScriptTag(ScriptTag node) => null;
+
+  @override
+  R visitShowCombinator(ShowCombinator node) => null;
+
+  @override
+  R visitSimpleFormalParameter(SimpleFormalParameter node) => null;
+
+  @override
+  R visitSimpleIdentifier(SimpleIdentifier node) => null;
+
+  @override
+  R visitSimpleStringLiteral(SimpleStringLiteral node) => null;
+
+  @override
+  R visitStringInterpolation(StringInterpolation node) => null;
+
+  @override
+  R visitSuperConstructorInvocation(SuperConstructorInvocation node) => null;
+
+  @override
+  R visitSuperExpression(SuperExpression node) => null;
+
+  @override
+  R visitSwitchCase(SwitchCase node) => null;
+
+  @override
+  R visitSwitchDefault(SwitchDefault node) => null;
+
+  @override
+  R visitSwitchStatement(SwitchStatement node) => null;
+
+  @override
+  R visitSymbolLiteral(SymbolLiteral node) => null;
+
+  @override
+  R visitThisExpression(ThisExpression node) => null;
+
+  @override
+  R visitThrowExpression(ThrowExpression node) => null;
+
+  @override
+  R visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) => null;
+
+  @override
+  R visitTryStatement(TryStatement node) => null;
+
+  @override
+  R visitTypeArgumentList(TypeArgumentList node) => null;
+
+  @override
+  R visitTypeName(TypeName node) => null;
+
+  @override
+  R visitTypeParameter(TypeParameter node) => null;
+
+  @override
+  R visitTypeParameterList(TypeParameterList node) => null;
+
+  @override
+  R visitVariableDeclaration(VariableDeclaration node) => null;
+
+  @override
+  R visitVariableDeclarationList(VariableDeclarationList node) => null;
+
+  @override
+  R visitVariableDeclarationStatement(VariableDeclarationStatement node) =>
+      null;
+
+  @override
+  R visitWhileStatement(WhileStatement node) => null;
+
+  @override
+  R visitWithClause(WithClause node) => null;
+
+  @override
+  R visitYieldStatement(YieldStatement node) => null;
+}
+
+/**
+ * A simple formal parameter.
+ *
+ * > simpleFormalParameter ::=
+ * >     ('final' [TypeName] | 'var' | [TypeName])? [SimpleIdentifier]
+ */
+class SimpleFormalParameter extends NormalFormalParameter {
+  /**
+   * The token representing either the 'final', 'const' or 'var' keyword, or
+   * `null` if no keyword was used.
+   */
+  Token keyword;
+
+  /**
+   * The name of the declared type of the parameter, or `null` if the parameter
+   * does not have a declared type.
+   */
+  TypeName _type;
+
+  /**
+   * Initialize a newly created formal parameter. Either or both of the
+   * [comment] and [metadata] can be `null` if the parameter does not have the
+   * corresponding attribute. The [keyword] can be `null` if a type was
+   * specified. The [type] must be `null` if the keyword is 'var'.
+   */
+  SimpleFormalParameter(Comment comment, List<Annotation> metadata,
+      this.keyword, TypeName type, SimpleIdentifier identifier)
+      : super(comment, metadata, identifier) {
+    _type = _becomeParentOf(type);
+  }
+
+  @override
+  Token get beginToken {
+    NodeList<Annotation> metadata = this.metadata;
+    if (!metadata.isEmpty) {
+      return metadata.beginToken;
+    } else if (keyword != null) {
+      return keyword;
+    } else if (_type != null) {
+      return _type.beginToken;
+    }
+    return identifier.beginToken;
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(keyword)
+    ..add(_type)
+    ..add(identifier);
+
+  @override
+  Token get endToken => identifier.endToken;
+
+  @override
+  bool get isConst => (keyword is KeywordToken) &&
+      (keyword as KeywordToken).keyword == Keyword.CONST;
+
+  @override
+  bool get isFinal => (keyword is KeywordToken) &&
+      (keyword as KeywordToken).keyword == Keyword.FINAL;
+
+  /**
+   * Return the name of the declared type of the parameter, or `null` if the
+   * parameter does not have a declared type.
+   */
+  TypeName get type => _type;
+
+  /**
+   * Set the name of the declared type of the parameter to the given [typeName].
+   */
+  void set type(TypeName typeName) {
+    _type = _becomeParentOf(typeName);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitSimpleFormalParameter(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_type, visitor);
+    _safelyVisitChild(identifier, visitor);
+  }
+}
+
+/**
+ * A simple identifier.
+ *
+ * > simpleIdentifier ::=
+ * >     initialCharacter internalCharacter*
+ * >
+ * > initialCharacter ::= '_' | '$' | letter
+ * >
+ * > internalCharacter ::= '_' | '$' | letter | digit
+ */
+class SimpleIdentifier extends Identifier {
+  /**
+   * The token representing the identifier.
+   */
+  Token token;
+
+  /**
+   * The element associated with this identifier based on static type
+   * information, or `null` if the AST structure has not been resolved or if
+   * this identifier could not be resolved.
+   */
+  Element _staticElement;
+
+  /**
+   * The element associated with this identifier based on propagated type
+   * information, or `null` if the AST structure has not been resolved or if
+   * this identifier could not be resolved.
+   */
+  Element _propagatedElement;
+
+  /**
+   * If this expression is both in a getter and setter context, the
+   * [AuxiliaryElements] will be set to hold onto the static and propagated
+   * information. The auxiliary element will hold onto the elements from the
+   * getter context.
+   */
+  AuxiliaryElements auxiliaryElements = null;
+
+  /**
+   * Initialize a newly created identifier.
+   */
+  SimpleIdentifier(this.token);
+
+  @override
+  Token get beginToken => token;
+
+  @override
+  Element get bestElement {
+    if (_propagatedElement == null) {
+      return _staticElement;
+    }
+    return _propagatedElement;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()..add(token);
+
+  @override
+  Token get endToken => token;
+
+  /**
+   * Returns `true` if this identifier is the "name" part of a prefixed
+   * identifier or a method invocation.
+   */
+  bool get isQualified {
+    AstNode parent = this.parent;
+    if (parent is PrefixedIdentifier) {
+      return identical(parent.identifier, this);
+    }
+    if (parent is PropertyAccess) {
+      return identical(parent.propertyName, this);
+    }
+    if (parent is MethodInvocation) {
+      MethodInvocation invocation = parent;
+      return identical(invocation.methodName, this) &&
+          invocation.realTarget != null;
+    }
+    return false;
+  }
+
+  @override
+  bool get isSynthetic => token.isSynthetic;
+
+  @override
+  String get name => token.lexeme;
+
+  @override
+  int get precedence => 16;
+
+  @override
+  Element get propagatedElement => _propagatedElement;
+
+  /**
+   * Set the element associated with this identifier based on propagated type
+   * information to the given [element].
+   */
+  void set propagatedElement(Element element) {
+    _propagatedElement = _validateElement(element);
+  }
+
+  @override
+  Element get staticElement => _staticElement;
+
+  /**
+   * Set the element associated with this identifier based on static type
+   * information to the given [element].
+   */
+  void set staticElement(Element element) {
+    _staticElement = _validateElement(element);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitSimpleIdentifier(this);
+
+  /**
+   * Return `true` if this identifier is the name being declared in a
+   * declaration.
+   */
+  bool inDeclarationContext() {
+    // TODO(brianwilkerson) Convert this to a getter.
+    AstNode parent = this.parent;
+    if (parent is CatchClause) {
+      CatchClause clause = parent;
+      return identical(this, clause.exceptionParameter) ||
+          identical(this, clause.stackTraceParameter);
+    } else if (parent is ClassDeclaration) {
+      return identical(this, parent.name);
+    } else if (parent is ClassTypeAlias) {
+      return identical(this, parent.name);
+    } else if (parent is ConstructorDeclaration) {
+      return identical(this, parent.name);
+    } else if (parent is DeclaredIdentifier) {
+      return identical(this, parent.identifier);
+    } else if (parent is EnumDeclaration) {
+      return identical(this, parent.name);
+    } else if (parent is EnumConstantDeclaration) {
+      return identical(this, parent.name);
+    } else if (parent is FunctionDeclaration) {
+      return identical(this, parent.name);
+    } else if (parent is FunctionTypeAlias) {
+      return identical(this, parent.name);
+    } else if (parent is ImportDirective) {
+      return identical(this, parent.prefix);
+    } else if (parent is Label) {
+      return identical(this, parent.label) &&
+          (parent.parent is LabeledStatement);
+    } else if (parent is MethodDeclaration) {
+      return identical(this, parent.name);
+    } else if (parent is FunctionTypedFormalParameter ||
+        parent is SimpleFormalParameter) {
+      return identical(this, (parent as NormalFormalParameter).identifier);
+    } else if (parent is TypeParameter) {
+      return identical(this, parent.name);
+    } else if (parent is VariableDeclaration) {
+      return identical(this, parent.name);
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if this expression is computing a right-hand value.
+   *
+   * Note that [inGetterContext] and [inSetterContext] are not opposites, nor
+   * are they mutually exclusive. In other words, it is possible for both
+   * methods to return `true` when invoked on the same node.
+   */
+  bool inGetterContext() {
+    // TODO(brianwilkerson) Convert this to a getter.
+    AstNode parent = this.parent;
+    AstNode target = this;
+    // skip prefix
+    if (parent is PrefixedIdentifier) {
+      PrefixedIdentifier prefixed = parent as PrefixedIdentifier;
+      if (identical(prefixed.prefix, this)) {
+        return true;
+      }
+      parent = prefixed.parent;
+      target = prefixed;
+    } else if (parent is PropertyAccess) {
+      PropertyAccess access = parent as PropertyAccess;
+      if (identical(access.target, this)) {
+        return true;
+      }
+      parent = access.parent;
+      target = access;
+    }
+    // skip label
+    if (parent is Label) {
+      return false;
+    }
+    // analyze usage
+    if (parent is AssignmentExpression) {
+      if (identical(parent.leftHandSide, target) &&
+          parent.operator.type == TokenType.EQ) {
+        return false;
+      }
+    }
+    if (parent is ForEachStatement) {
+      if (identical(parent.identifier, target)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Return `true` if this expression is computing a left-hand value.
+   *
+   * Note that [inGetterContext] and [inSetterContext] are not opposites, nor
+   * are they mutually exclusive. In other words, it is possible for both
+   * methods to return `true` when invoked on the same node.
+   */
+  bool inSetterContext() {
+    // TODO(brianwilkerson) Convert this to a getter.
+    AstNode parent = this.parent;
+    AstNode target = this;
+    // skip prefix
+    if (parent is PrefixedIdentifier) {
+      PrefixedIdentifier prefixed = parent as PrefixedIdentifier;
+      // if this is the prefix, then return false
+      if (identical(prefixed.prefix, this)) {
+        return false;
+      }
+      parent = prefixed.parent;
+      target = prefixed;
+    } else if (parent is PropertyAccess) {
+      PropertyAccess access = parent as PropertyAccess;
+      if (identical(access.target, this)) {
+        return false;
+      }
+      parent = access.parent;
+      target = access;
+    }
+    // analyze usage
+    if (parent is PrefixExpression) {
+      return parent.operator.type.isIncrementOperator;
+    } else if (parent is PostfixExpression) {
+      return true;
+    } else if (parent is AssignmentExpression) {
+      return identical(parent.leftHandSide, target);
+    } else if (parent is ForEachStatement) {
+      return identical(parent.identifier, target);
+    }
+    return false;
+  }
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    // There are no children to visit.
+  }
+
+  /**
+   * Return the given element if it is valid, or report the problem and return
+   * `null` if it is not appropriate.
+   *
+   * The [parent] is the parent of the element, used for reporting when there is
+   * a problem.
+   * The [isValid] is `true` if the element is appropriate.
+   * The [element] is the element to be associated with this identifier.
+   */
+  Element _returnOrReportElement(
+      AstNode parent, bool isValid, Element element) {
+    if (!isValid) {
+      AnalysisEngine.instance.logger.logInformation(
+          "Internal error: attempting to set the name of a ${parent.runtimeType} to a ${element.runtimeType}",
+          new CaughtException(new AnalysisException(), null));
+      return null;
+    }
+    return element;
+  }
+
+  /**
+   * Return the given [element] if it is an appropriate element based on the
+   * parent of this identifier, or `null` if it is not appropriate.
+   */
+  Element _validateElement(Element element) {
+    if (element == null) {
+      return null;
+    }
+    AstNode parent = this.parent;
+    if (parent is ClassDeclaration && identical(parent.name, this)) {
+      return _returnOrReportElement(parent, element is ClassElement, element);
+    } else if (parent is ClassTypeAlias && identical(parent.name, this)) {
+      return _returnOrReportElement(parent, element is ClassElement, element);
+    } else if (parent is DeclaredIdentifier &&
+        identical(parent.identifier, this)) {
+      return _returnOrReportElement(
+          parent, element is LocalVariableElement, element);
+    } else if (parent is FormalParameter &&
+        identical(parent.identifier, this)) {
+      return _returnOrReportElement(
+          parent, element is ParameterElement, element);
+    } else if (parent is FunctionDeclaration && identical(parent.name, this)) {
+      return _returnOrReportElement(
+          parent, element is ExecutableElement, element);
+    } else if (parent is FunctionTypeAlias && identical(parent.name, this)) {
+      return _returnOrReportElement(
+          parent, element is FunctionTypeAliasElement, element);
+    } else if (parent is MethodDeclaration && identical(parent.name, this)) {
+      return _returnOrReportElement(
+          parent, element is ExecutableElement, element);
+    } else if (parent is TypeParameter && identical(parent.name, this)) {
+      return _returnOrReportElement(
+          parent, element is TypeParameterElement, element);
+    } else if (parent is VariableDeclaration && identical(parent.name, this)) {
+      return _returnOrReportElement(
+          parent, element is VariableElement, element);
+    }
+    return element;
+  }
+}
+
+/**
+ * A string literal expression that does not contain any interpolations.
+ *
+ * > simpleStringLiteral ::=
+ * >     rawStringLiteral
+ * >   | basicStringLiteral
+ * >
+ * > rawStringLiteral ::=
+ * >     'r' basicStringLiteral
+ * >
+ * > simpleStringLiteral ::=
+ * >     multiLineStringLiteral
+ * >   | singleLineStringLiteral
+ * >
+ * > multiLineStringLiteral ::=
+ * >     "'''" characters "'''"
+ * >   | '"""' characters '"""'
+ * >
+ * > singleLineStringLiteral ::=
+ * >     "'" characters "'"
+ * >   | '"' characters '"'
+ */
+class SimpleStringLiteral extends SingleStringLiteral {
+  /**
+   * The token representing the literal.
+   */
+  Token literal;
+
+  /**
+   * The value of the literal.
+   */
+  String _value;
+
+  /**
+   * The toolkit specific element associated with this literal, or `null`.
+   */
+  @deprecated // No replacement
+  Element toolkitElement;
+
+  /**
+   * Initialize a newly created simple string literal.
+   */
+  SimpleStringLiteral(this.literal, String value) {
+    _value = StringUtilities.intern(value);
+  }
+
+  @override
+  Token get beginToken => literal;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..add(literal);
+
+  @override
+  int get contentsEnd => offset + _helper.end;
+
+  @override
+  int get contentsOffset => offset + _helper.start;
+
+  @override
+  Token get endToken => literal;
+
+  @override
+  bool get isMultiline => _helper.isMultiline;
+
+  @override
+  bool get isRaw => _helper.isRaw;
+
+  @override
+  bool get isSingleQuoted => _helper.isSingleQuoted;
+
+  @override
+  bool get isSynthetic => literal.isSynthetic;
+
+  /**
+   * Return the value of the literal.
+   */
+  String get value => _value;
+
+  /**
+   * Set the value of the literal to the given [string].
+   */
+  void set value(String string) {
+    _value = StringUtilities.intern(_value);
+  }
+
+  StringLexemeHelper get _helper {
+    return new StringLexemeHelper(literal.lexeme, true, true);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitSimpleStringLiteral(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    // There are no children to visit.
+  }
+
+  @override
+  void _appendStringValue(StringBuffer buffer) {
+    buffer.write(value);
+  }
+}
+
+/**
+ * A single string literal expression.
+ *
+ * > singleStringLiteral ::=
+ * >     [SimpleStringLiteral]
+ * >   | [StringInterpolation]
+ */
+abstract class SingleStringLiteral extends StringLiteral {
+  /**
+   * Return the offset of the after-last contents character.
+   */
+  int get contentsEnd;
+
+  /**
+   * Return the offset of the first contents character.
+   * If the string is multiline, then leading whitespaces are skipped.
+   */
+  int get contentsOffset;
+
+  /**
+   * Return `true` if this string literal is a multi-line string.
+   */
+  bool get isMultiline;
+
+  /**
+   * Return `true` if this string literal is a raw string.
+   */
+  bool get isRaw;
+
+  /**
+   * Return `true` if this string literal uses single qoutes (' or ''').
+   * Return `false` if this string literal uses double qoutes (" or """).
+   */
+  bool get isSingleQuoted;
+}
+
+/**
+ * A node that represents a statement.
+ *
+ * > statement ::=
+ * >     [Block]
+ * >   | [VariableDeclarationStatement]
+ * >   | [ForStatement]
+ * >   | [ForEachStatement]
+ * >   | [WhileStatement]
+ * >   | [DoStatement]
+ * >   | [SwitchStatement]
+ * >   | [IfStatement]
+ * >   | [TryStatement]
+ * >   | [BreakStatement]
+ * >   | [ContinueStatement]
+ * >   | [ReturnStatement]
+ * >   | [ExpressionStatement]
+ * >   | [FunctionDeclarationStatement]
+ */
+abstract class Statement extends AstNode {
+  /**
+   * If this is a labeled statement, return the unlabeled portion of the
+   * statement.  Otherwise return the statement itself.
+   */
+  Statement get unlabeled => this;
+}
+
+/**
+ * A string interpolation literal.
+ *
+ * > stringInterpolation ::=
+ * >     ''' [InterpolationElement]* '''
+ * >   | '"' [InterpolationElement]* '"'
+ */
+class StringInterpolation extends SingleStringLiteral {
+  /**
+   * The elements that will be composed to produce the resulting string.
+   */
+  NodeList<InterpolationElement> _elements;
+
+  /**
+   * Initialize a newly created string interpolation expression.
+   */
+  StringInterpolation(List<InterpolationElement> elements) {
+    _elements = new NodeList<InterpolationElement>(this, elements);
+  }
+
+  @override
+  Token get beginToken => _elements.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..addAll(_elements);
+
+  @override
+  int get contentsEnd {
+    InterpolationString element = _elements.last;
+    return element.contentsEnd;
+  }
+
+  @override
+  int get contentsOffset {
+    InterpolationString element = _elements.first;
+    return element.contentsOffset;
+  }
+
+  /**
+   * Return the elements that will be composed to produce the resulting string.
+   */
+  NodeList<InterpolationElement> get elements => _elements;
+
+  @override
+  Token get endToken => _elements.endToken;
+
+  @override
+  bool get isMultiline => _firstHelper.isMultiline;
+
+  @override
+  bool get isRaw => false;
+
+  @override
+  bool get isSingleQuoted => _firstHelper.isSingleQuoted;
+
+  StringLexemeHelper get _firstHelper {
+    InterpolationString lastString = _elements.first;
+    String lexeme = lastString.contents.lexeme;
+    return new StringLexemeHelper(lexeme, true, false);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitStringInterpolation(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _elements.accept(visitor);
+  }
+
+  @override
+  void _appendStringValue(StringBuffer buffer) {
+    throw new IllegalArgumentException();
+  }
+}
+
+/**
+ * A helper for analyzing string lexemes.
+ */
+class StringLexemeHelper {
+  final String lexeme;
+  final bool isFirst;
+  final bool isLast;
+
+  bool isRaw = false;
+  bool isSingleQuoted = false;
+  bool isMultiline = false;
+  int start = 0;
+  int end;
+
+  StringLexemeHelper(this.lexeme, this.isFirst, this.isLast) {
+    if (isFirst) {
+      isRaw = StringUtilities.startsWithChar(lexeme, 0x72);
+      if (isRaw) {
+        start++;
+      }
+      if (StringUtilities.startsWith3(lexeme, start, 0x27, 0x27, 0x27)) {
+        isSingleQuoted = true;
+        isMultiline = true;
+        start += 3;
+        start = _trimInitialWhitespace(start);
+      } else if (StringUtilities.startsWith3(lexeme, start, 0x22, 0x22, 0x22)) {
+        isSingleQuoted = false;
+        isMultiline = true;
+        start += 3;
+        start = _trimInitialWhitespace(start);
+      } else if (start < lexeme.length && lexeme.codeUnitAt(start) == 0x27) {
+        isSingleQuoted = true;
+        isMultiline = false;
+        start++;
+      } else if (start < lexeme.length && lexeme.codeUnitAt(start) == 0x22) {
+        isSingleQuoted = false;
+        isMultiline = false;
+        start++;
+      }
+    }
+    end = lexeme.length;
+    if (isLast) {
+      if (start + 3 <= end &&
+          (StringUtilities.endsWith3(lexeme, 0x22, 0x22, 0x22) ||
+              StringUtilities.endsWith3(lexeme, 0x27, 0x27, 0x27))) {
+        end -= 3;
+      } else if (start + 1 <= end &&
+          (StringUtilities.endsWithChar(lexeme, 0x22) ||
+              StringUtilities.endsWithChar(lexeme, 0x27))) {
+        end -= 1;
+      }
+    }
+  }
+
+  /**
+   * Given the [lexeme] for a multi-line string whose content begins at the
+   * given [start] index, return the index of the first character that is
+   * included in the value of the string. According to the specification:
+   *
+   * If the first line of a multiline string consists solely of the whitespace
+   * characters defined by the production WHITESPACE 20.1), possibly prefixed
+   * by \, then that line is ignored, including the new line at its end.
+   */
+  int _trimInitialWhitespace(int start) {
+    int length = lexeme.length;
+    int index = start;
+    while (index < length) {
+      int currentChar = lexeme.codeUnitAt(index);
+      if (currentChar == 0x0D) {
+        if (index + 1 < length && lexeme.codeUnitAt(index + 1) == 0x0A) {
+          return index + 2;
+        }
+        return index + 1;
+      } else if (currentChar == 0x0A) {
+        return index + 1;
+      } else if (currentChar == 0x5C) {
+        if (index + 1 >= length) {
+          return start;
+        }
+        currentChar = lexeme.codeUnitAt(index + 1);
+        if (currentChar != 0x0D &&
+            currentChar != 0x0A &&
+            currentChar != 0x09 &&
+            currentChar != 0x20) {
+          return start;
+        }
+      } else if (currentChar != 0x09 && currentChar != 0x20) {
+        return start;
+      }
+      index++;
+    }
+    return start;
+  }
+}
+
+/**
+ * A string literal expression.
+ *
+ * > stringLiteral ::=
+ * >     [SimpleStringLiteral]
+ * >   | [AdjacentStrings]
+ * >   | [StringInterpolation]
+ */
+abstract class StringLiteral extends Literal {
+  /**
+   * Return the value of the string literal, or `null` if the string is not a
+   * constant string without any string interpolation.
+   */
+  String get stringValue {
+    StringBuffer buffer = new StringBuffer();
+    try {
+      _appendStringValue(buffer);
+    } on IllegalArgumentException {
+      return null;
+    }
+    return buffer.toString();
+  }
+
+  /**
+   * Append the value of this string literal to the given [buffer]. Throw an
+   * [IllegalArgumentException] if the string is not a constant string without
+   * any string interpolation.
+   */
+  @deprecated // Use "this.stringValue"
+  void appendStringValue(StringBuffer buffer) => _appendStringValue(buffer);
+
+  /**
+   * Append the value of this string literal to the given [buffer]. Throw an
+   * [IllegalArgumentException] if the string is not a constant string without
+   * any string interpolation.
+   */
+  void _appendStringValue(StringBuffer buffer);
+}
+
+/**
+ * The invocation of a superclass' constructor from within a constructor's
+ * initialization list.
+ *
+ * > superInvocation ::=
+ * >     'super' ('.' [SimpleIdentifier])? [ArgumentList]
+ */
+class SuperConstructorInvocation extends ConstructorInitializer {
+  /**
+   * The token for the 'super' keyword.
+   */
+  Token superKeyword;
+
+  /**
+   * The token for the period before the name of the constructor that is being
+   * invoked, or `null` if the unnamed constructor is being invoked.
+   */
+  Token period;
+
+  /**
+   * The name of the constructor that is being invoked, or `null` if the unnamed
+   * constructor is being invoked.
+   */
+  SimpleIdentifier _constructorName;
+
+  /**
+   * The list of arguments to the constructor.
+   */
+  ArgumentList _argumentList;
+
+  /**
+   * The element associated with the constructor based on static type
+   * information, or `null` if the AST structure has not been resolved or if the
+   * constructor could not be resolved.
+   */
+  ConstructorElement staticElement;
+
+  /**
+   * Initialize a newly created super invocation to invoke the inherited
+   * constructor with the given name with the given arguments. The [period] and
+   * [constructorName] can be `null` if the constructor being invoked is the
+   * unnamed constructor.
+   */
+  SuperConstructorInvocation(this.superKeyword, this.period,
+      SimpleIdentifier constructorName, ArgumentList argumentList) {
+    _constructorName = _becomeParentOf(constructorName);
+    _argumentList = _becomeParentOf(argumentList);
+  }
+
+  /**
+   * Return the list of arguments to the constructor.
+   */
+  ArgumentList get argumentList => _argumentList;
+
+  /**
+   * Set the list of arguments to the constructor to the given [argumentList].
+   */
+  void set argumentList(ArgumentList argumentList) {
+    _argumentList = _becomeParentOf(argumentList);
+  }
+
+  @override
+  Token get beginToken => superKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(superKeyword)
+    ..add(period)
+    ..add(_constructorName)
+    ..add(_argumentList);
+
+  /**
+   * Return the name of the constructor that is being invoked, or `null` if the
+   * unnamed constructor is being invoked.
+   */
+  SimpleIdentifier get constructorName => _constructorName;
+
+  /**
+   * Set the name of the constructor that is being invoked to the given
+   * [identifier].
+   */
+  void set constructorName(SimpleIdentifier identifier) {
+    _constructorName = _becomeParentOf(identifier);
+  }
+
+  @override
+  Token get endToken => _argumentList.endToken;
+
+  /**
+   * Return the token for the 'super' keyword.
+   */
+  @deprecated // Use "this.superKeyword"
+  Token get keyword => superKeyword;
+
+  /**
+   * Set the token for the 'super' keyword to the given [token].
+   */
+  @deprecated // Use "this.superKeyword"
+  set keyword(Token token) {
+    superKeyword = token;
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitSuperConstructorInvocation(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_constructorName, visitor);
+    _safelyVisitChild(_argumentList, visitor);
+  }
+}
+
+/**
+ * A super expression.
+ *
+ * > superExpression ::=
+ * >     'super'
+ */
+class SuperExpression extends Expression {
+  /**
+   * The token representing the 'super' keyword.
+   */
+  Token superKeyword;
+
+  /**
+   * Initialize a newly created super expression.
+   */
+  SuperExpression(this.superKeyword);
+
+  @override
+  Token get beginToken => superKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..add(superKeyword);
+
+  @override
+  Token get endToken => superKeyword;
+
+  /**
+   * Return the token for the 'super' keyword.
+   */
+  @deprecated // Use "this.superKeyword"
+  Token get keyword => superKeyword;
+
+  /**
+   * Set the token for the 'super' keyword to the given [token].
+   */
+  @deprecated // Use "this.superKeyword"
+  set keyword(Token token) {
+    superKeyword = token;
+  }
+
+  @override
+  int get precedence => 16;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitSuperExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    // There are no children to visit.
+  }
+}
+
+/**
+ * A case in a switch statement.
+ *
+ * > switchCase ::=
+ * >     [SimpleIdentifier]* 'case' [Expression] ':' [Statement]*
+ */
+class SwitchCase extends SwitchMember {
+  /**
+   * The expression controlling whether the statements will be executed.
+   */
+  Expression _expression;
+
+  /**
+   * Initialize a newly created switch case. The list of [labels] can be `null`
+   * if there are no labels.
+   */
+  SwitchCase(List<Label> labels, Token keyword, Expression expression,
+      Token colon, List<Statement> statements)
+      : super(labels, keyword, colon, statements) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..addAll(labels)
+    ..add(keyword)
+    ..add(_expression)
+    ..add(colon)
+    ..addAll(statements);
+
+  /**
+   * Return the expression controlling whether the statements will be executed.
+   */
+  Expression get expression => _expression;
+
+  /**
+   * Set the expression controlling whether the statements will be executed to
+   * the given [expression].
+   */
+  void set expression(Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitSwitchCase(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    labels.accept(visitor);
+    _safelyVisitChild(_expression, visitor);
+    statements.accept(visitor);
+  }
+}
+
+/**
+ * The default case in a switch statement.
+ *
+ * > switchDefault ::=
+ * >     [SimpleIdentifier]* 'default' ':' [Statement]*
+ */
+class SwitchDefault extends SwitchMember {
+  /**
+   * Initialize a newly created switch default. The list of [labels] can be
+   * `null` if there are no labels.
+   */
+  SwitchDefault(List<Label> labels, Token keyword, Token colon,
+      List<Statement> statements)
+      : super(labels, keyword, colon, statements);
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..addAll(labels)
+    ..add(keyword)
+    ..add(colon)
+    ..addAll(statements);
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitSwitchDefault(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    labels.accept(visitor);
+    statements.accept(visitor);
+  }
+}
+
+/**
+ * An element within a switch statement.
+ *
+ * > switchMember ::=
+ * >     switchCase
+ * >   | switchDefault
+ */
+abstract class SwitchMember extends AstNode {
+  /**
+   * The labels associated with the switch member.
+   */
+  NodeList<Label> _labels;
+
+  /**
+   * The token representing the 'case' or 'default' keyword.
+   */
+  Token keyword;
+
+  /**
+   * The colon separating the keyword or the expression from the statements.
+   */
+  Token colon;
+
+  /**
+   * The statements that will be executed if this switch member is selected.
+   */
+  NodeList<Statement> _statements;
+
+  /**
+   * Initialize a newly created switch member. The list of [labels] can be
+   * `null` if there are no labels.
+   */
+  SwitchMember(List<Label> labels, this.keyword, this.colon,
+      List<Statement> statements) {
+    _labels = new NodeList<Label>(this, labels);
+    _statements = new NodeList<Statement>(this, statements);
+  }
+
+  @override
+  Token get beginToken {
+    if (!_labels.isEmpty) {
+      return _labels.beginToken;
+    }
+    return keyword;
+  }
+
+  @override
+  Token get endToken {
+    if (!_statements.isEmpty) {
+      return _statements.endToken;
+    }
+    return colon;
+  }
+
+  /**
+   * Return the labels associated with the switch member.
+   */
+  NodeList<Label> get labels => _labels;
+
+  /**
+   * Return the statements that will be executed if this switch member is
+   * selected.
+   */
+  NodeList<Statement> get statements => _statements;
+}
+
+/**
+ * A switch statement.
+ *
+ * > switchStatement ::=
+ * >     'switch' '(' [Expression] ')' '{' [SwitchCase]* [SwitchDefault]? '}'
+ */
+class SwitchStatement extends Statement {
+  /**
+   * The token representing the 'switch' keyword.
+   */
+  Token switchKeyword;
+
+  /**
+   * The left parenthesis.
+   */
+  Token leftParenthesis;
+
+  /**
+   * The expression used to determine which of the switch members will be
+   * selected.
+   */
+  Expression _expression;
+
+  /**
+   * The right parenthesis.
+   */
+  Token rightParenthesis;
+
+  /**
+   * The left curly bracket.
+   */
+  Token leftBracket;
+
+  /**
+   * The switch members that can be selected by the expression.
+   */
+  NodeList<SwitchMember> _members;
+
+  /**
+   * The right curly bracket.
+   */
+  Token rightBracket;
+
+  /**
+   * Initialize a newly created switch statement. The list of [members] can be
+   * `null` if there are no switch members.
+   */
+  SwitchStatement(this.switchKeyword, this.leftParenthesis,
+      Expression expression, this.rightParenthesis, this.leftBracket,
+      List<SwitchMember> members, this.rightBracket) {
+    _expression = _becomeParentOf(expression);
+    _members = new NodeList<SwitchMember>(this, members);
+  }
+
+  @override
+  Token get beginToken => switchKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(switchKeyword)
+    ..add(leftParenthesis)
+    ..add(_expression)
+    ..add(rightParenthesis)
+    ..add(leftBracket)
+    ..addAll(_members)
+    ..add(rightBracket);
+
+  @override
+  Token get endToken => rightBracket;
+
+  /**
+   * Return the expression used to determine which of the switch members will be
+   * selected.
+   */
+  Expression get expression => _expression;
+
+  /**
+   * Set the expression used to determine which of the switch members will be
+   * selected to the given [expression].
+   */
+  void set expression(Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  /**
+   * Return the token representing the 'switch' keyword.
+   */
+  @deprecated // Use "this.switchKeyword"
+  Token get keyword => switchKeyword;
+
+  /**
+   * Set the token representing the 'switch' keyword to the given [token].
+   */
+  @deprecated // Use "this.switchKeyword"
+  set keyword(Token token) {
+    switchKeyword = token;
+  }
+
+  /**
+   * Return the switch members that can be selected by the expression.
+   */
+  NodeList<SwitchMember> get members => _members;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitSwitchStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_expression, visitor);
+    _members.accept(visitor);
+  }
+}
+
+/**
+ * A symbol literal expression.
+ *
+ * > symbolLiteral ::=
+ * >     '#' (operator | (identifier ('.' identifier)*))
+ */
+class SymbolLiteral extends Literal {
+  /**
+   * The token introducing the literal.
+   */
+  Token poundSign;
+
+  /**
+   * The components of the literal.
+   */
+  final List<Token> components;
+
+  /**
+   * Initialize a newly created symbol literal.
+   */
+  SymbolLiteral(this.poundSign, this.components);
+
+  @override
+  Token get beginToken => poundSign;
+
+  /**
+   * TODO(paulberry): add "." tokens.
+   */
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(poundSign)
+    ..addAll(components);
+
+  @override
+  Token get endToken => components[components.length - 1];
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitSymbolLiteral(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    // There are no children to visit.
+  }
+}
+
+/**
+ * A this expression.
+ *
+ * > thisExpression ::=
+ * >     'this'
+ */
+class ThisExpression extends Expression {
+  /**
+   * The token representing the 'this' keyword.
+   */
+  Token thisKeyword;
+
+  /**
+   * Initialize a newly created this expression.
+   */
+  ThisExpression(this.thisKeyword);
+
+  @override
+  Token get beginToken => thisKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()..add(thisKeyword);
+
+  @override
+  Token get endToken => thisKeyword;
+
+  /**
+   * Return the token representing the 'this' keyword.
+   */
+  @deprecated // Use "this.thisKeyword"
+  Token get keyword => thisKeyword;
+
+  /**
+   * Set the token representing the 'this' keyword to the given [token].
+   */
+  @deprecated // Use "this.thisKeyword"
+  set keyword(Token token) {
+    thisKeyword = token;
+  }
+
+  @override
+  int get precedence => 16;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitThisExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    // There are no children to visit.
+  }
+}
+
+/**
+ * A throw expression.
+ *
+ * > throwExpression ::=
+ * >     'throw' [Expression]
+ */
+class ThrowExpression extends Expression {
+  /**
+   * The token representing the 'throw' keyword.
+   */
+  Token throwKeyword;
+
+  /**
+   * The expression computing the exception to be thrown.
+   */
+  Expression _expression;
+
+  /**
+   * Initialize a newly created throw expression.
+   */
+  ThrowExpression(this.throwKeyword, Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get beginToken => throwKeyword;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(throwKeyword)
+    ..add(_expression);
+
+  @override
+  Token get endToken {
+    if (_expression != null) {
+      return _expression.endToken;
+    }
+    return throwKeyword;
+  }
+
+  /**
+   * Return the expression computing the exception to be thrown.
+   */
+  Expression get expression => _expression;
+
+  /**
+   * Set the expression computing the exception to be thrown to the given
+   * [expression].
+   */
+  void set expression(Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  /**
+   * Return the token representing the 'throw' keyword.
+   */
+  @deprecated // Use "this.throwKeyword"
+  Token get keyword => throwKeyword;
+
+  /**
+   * Set the token representing the 'throw' keyword to the given [token].
+   */
+  @deprecated // Use "this.throwKeyword"
+  set keyword(Token token) {
+    throwKeyword = token;
+  }
+
+  @override
+  int get precedence => 0;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitThrowExpression(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_expression, visitor);
+  }
+}
+
+/**
+ * The declaration of one or more top-level variables of the same type.
+ *
+ * > topLevelVariableDeclaration ::=
+ * >     ('final' | 'const') type? staticFinalDeclarationList ';'
+ * >   | variableDeclaration ';'
+ */
+class TopLevelVariableDeclaration extends CompilationUnitMember {
+  /**
+   * The top-level variables being declared.
+   */
+  VariableDeclarationList _variableList;
+
+  /**
+   * The semicolon terminating the declaration.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created top-level variable declaration. Either or both
+   * of the [comment] and [metadata] can be `null` if the variable does not have
+   * the corresponding attribute.
+   */
+  TopLevelVariableDeclaration(Comment comment, List<Annotation> metadata,
+      VariableDeclarationList variableList, this.semicolon)
+      : super(comment, metadata) {
+    _variableList = _becomeParentOf(variableList);
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(_variableList)
+    ..add(semicolon);
+
+  @override
+  Element get element => null;
+
+  @override
+  Token get endToken => semicolon;
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata => _variableList.beginToken;
+
+  /**
+   * Return the top-level variables being declared.
+   */
+  VariableDeclarationList get variables => _variableList;
+
+  /**
+   * Set the top-level variables being declared to the given list of
+   * [variables].
+   */
+  void set variables(VariableDeclarationList variables) {
+    _variableList = _becomeParentOf(variables);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitTopLevelVariableDeclaration(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_variableList, visitor);
+  }
+}
+
+/**
+ * A visitor used to write a source representation of a visited AST node (and
+ * all of it's children) to a writer.
+ */
+class ToSourceVisitor implements AstVisitor<Object> {
+  /**
+   * The writer to which the source is to be written.
+   */
+  final PrintWriter _writer;
+
+  /**
+   * Initialize a newly created visitor to write source code representing the
+   * visited nodes to the given [writer].
+   */
+  ToSourceVisitor(this._writer);
+
+  @override
+  Object visitAdjacentStrings(AdjacentStrings node) {
+    _visitNodeListWithSeparator(node.strings, " ");
+    return null;
+  }
+
+  @override
+  Object visitAnnotation(Annotation node) {
+    _writer.print('@');
+    _visitNode(node.name);
+    _visitNodeWithPrefix(".", node.constructorName);
+    _visitNode(node.arguments);
+    return null;
+  }
+
+  @override
+  Object visitArgumentList(ArgumentList node) {
+    _writer.print('(');
+    _visitNodeListWithSeparator(node.arguments, ", ");
+    _writer.print(')');
+    return null;
+  }
+
+  @override
+  Object visitAsExpression(AsExpression node) {
+    _visitNode(node.expression);
+    _writer.print(" as ");
+    _visitNode(node.type);
+    return null;
+  }
+
+  @override
+  Object visitAssertStatement(AssertStatement node) {
+    _writer.print("assert (");
+    _visitNode(node.condition);
+    _writer.print(");");
+    return null;
+  }
+
+  @override
+  Object visitAssignmentExpression(AssignmentExpression node) {
+    _visitNode(node.leftHandSide);
+    _writer.print(' ');
+    _writer.print(node.operator.lexeme);
+    _writer.print(' ');
+    _visitNode(node.rightHandSide);
+    return null;
+  }
+
+  @override
+  Object visitAwaitExpression(AwaitExpression node) {
+    _writer.print("await ");
+    _visitNode(node.expression);
+    _writer.print(";");
+    return null;
+  }
+
+  @override
+  Object visitBinaryExpression(BinaryExpression node) {
+    _visitNode(node.leftOperand);
+    _writer.print(' ');
+    _writer.print(node.operator.lexeme);
+    _writer.print(' ');
+    _visitNode(node.rightOperand);
+    return null;
+  }
+
+  @override
+  Object visitBlock(Block node) {
+    _writer.print('{');
+    _visitNodeListWithSeparator(node.statements, " ");
+    _writer.print('}');
+    return null;
+  }
+
+  @override
+  Object visitBlockFunctionBody(BlockFunctionBody node) {
+    Token keyword = node.keyword;
+    if (keyword != null) {
+      _writer.print(keyword.lexeme);
+      if (node.star != null) {
+        _writer.print('*');
+      }
+      _writer.print(' ');
+    }
+    _visitNode(node.block);
+    return null;
+  }
+
+  @override
+  Object visitBooleanLiteral(BooleanLiteral node) {
+    _writer.print(node.literal.lexeme);
+    return null;
+  }
+
+  @override
+  Object visitBreakStatement(BreakStatement node) {
+    _writer.print("break");
+    _visitNodeWithPrefix(" ", node.label);
+    _writer.print(";");
+    return null;
+  }
+
+  @override
+  Object visitCascadeExpression(CascadeExpression node) {
+    _visitNode(node.target);
+    _visitNodeList(node.cascadeSections);
+    return null;
+  }
+
+  @override
+  Object visitCatchClause(CatchClause node) {
+    _visitNodeWithPrefix("on ", node.exceptionType);
+    if (node.catchKeyword != null) {
+      if (node.exceptionType != null) {
+        _writer.print(' ');
+      }
+      _writer.print("catch (");
+      _visitNode(node.exceptionParameter);
+      _visitNodeWithPrefix(", ", node.stackTraceParameter);
+      _writer.print(") ");
+    } else {
+      _writer.print(" ");
+    }
+    _visitNode(node.body);
+    return null;
+  }
+
+  @override
+  Object visitClassDeclaration(ClassDeclaration node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _visitTokenWithSuffix(node.abstractKeyword, " ");
+    _writer.print("class ");
+    _visitNode(node.name);
+    _visitNode(node.typeParameters);
+    _visitNodeWithPrefix(" ", node.extendsClause);
+    _visitNodeWithPrefix(" ", node.withClause);
+    _visitNodeWithPrefix(" ", node.implementsClause);
+    _writer.print(" {");
+    _visitNodeListWithSeparator(node.members, " ");
+    _writer.print("}");
+    return null;
+  }
+
+  @override
+  Object visitClassTypeAlias(ClassTypeAlias node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    if (node.abstractKeyword != null) {
+      _writer.print("abstract ");
+    }
+    _writer.print("class ");
+    _visitNode(node.name);
+    _visitNode(node.typeParameters);
+    _writer.print(" = ");
+    _visitNode(node.superclass);
+    _visitNodeWithPrefix(" ", node.withClause);
+    _visitNodeWithPrefix(" ", node.implementsClause);
+    _writer.print(";");
+    return null;
+  }
+
+  @override
+  Object visitComment(Comment node) => null;
+
+  @override
+  Object visitCommentReference(CommentReference node) => null;
+
+  @override
+  Object visitCompilationUnit(CompilationUnit node) {
+    ScriptTag scriptTag = node.scriptTag;
+    NodeList<Directive> directives = node.directives;
+    _visitNode(scriptTag);
+    String prefix = scriptTag == null ? "" : " ";
+    _visitNodeListWithSeparatorAndPrefix(prefix, directives, " ");
+    prefix = scriptTag == null && directives.isEmpty ? "" : " ";
+    _visitNodeListWithSeparatorAndPrefix(prefix, node.declarations, " ");
+    return null;
+  }
+
+  @override
+  Object visitConditionalExpression(ConditionalExpression node) {
+    _visitNode(node.condition);
+    _writer.print(" ? ");
+    _visitNode(node.thenExpression);
+    _writer.print(" : ");
+    _visitNode(node.elseExpression);
+    return null;
+  }
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _visitTokenWithSuffix(node.externalKeyword, " ");
+    _visitTokenWithSuffix(node.constKeyword, " ");
+    _visitTokenWithSuffix(node.factoryKeyword, " ");
+    _visitNode(node.returnType);
+    _visitNodeWithPrefix(".", node.name);
+    _visitNode(node.parameters);
+    _visitNodeListWithSeparatorAndPrefix(" : ", node.initializers, ", ");
+    _visitNodeWithPrefix(" = ", node.redirectedConstructor);
+    _visitFunctionWithPrefix(" ", node.body);
+    return null;
+  }
+
+  @override
+  Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+    _visitTokenWithSuffix(node.thisKeyword, ".");
+    _visitNode(node.fieldName);
+    _writer.print(" = ");
+    _visitNode(node.expression);
+    return null;
+  }
+
+  @override
+  Object visitConstructorName(ConstructorName node) {
+    _visitNode(node.type);
+    _visitNodeWithPrefix(".", node.name);
+    return null;
+  }
+
+  @override
+  Object visitContinueStatement(ContinueStatement node) {
+    _writer.print("continue");
+    _visitNodeWithPrefix(" ", node.label);
+    _writer.print(";");
+    return null;
+  }
+
+  @override
+  Object visitDeclaredIdentifier(DeclaredIdentifier node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _visitTokenWithSuffix(node.keyword, " ");
+    _visitNodeWithSuffix(node.type, " ");
+    _visitNode(node.identifier);
+    return null;
+  }
+
+  @override
+  Object visitDefaultFormalParameter(DefaultFormalParameter node) {
+    _visitNode(node.parameter);
+    if (node.separator != null) {
+      _writer.print(" ");
+      _writer.print(node.separator.lexeme);
+      _visitNodeWithPrefix(" ", node.defaultValue);
+    }
+    return null;
+  }
+
+  @override
+  Object visitDoStatement(DoStatement node) {
+    _writer.print("do ");
+    _visitNode(node.body);
+    _writer.print(" while (");
+    _visitNode(node.condition);
+    _writer.print(");");
+    return null;
+  }
+
+  @override
+  Object visitDoubleLiteral(DoubleLiteral node) {
+    _writer.print(node.literal.lexeme);
+    return null;
+  }
+
+  @override
+  Object visitEmptyFunctionBody(EmptyFunctionBody node) {
+    _writer.print(';');
+    return null;
+  }
+
+  @override
+  Object visitEmptyStatement(EmptyStatement node) {
+    _writer.print(';');
+    return null;
+  }
+
+  @override
+  Object visitEnumConstantDeclaration(EnumConstantDeclaration node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _visitNode(node.name);
+    return null;
+  }
+
+  @override
+  Object visitEnumDeclaration(EnumDeclaration node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _writer.print("enum ");
+    _visitNode(node.name);
+    _writer.print(" {");
+    _visitNodeListWithSeparator(node.constants, ", ");
+    _writer.print("}");
+    return null;
+  }
+
+  @override
+  Object visitExportDirective(ExportDirective node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _writer.print("export ");
+    _visitNode(node.uri);
+    _visitNodeListWithSeparatorAndPrefix(" ", node.combinators, " ");
+    _writer.print(';');
+    return null;
+  }
+
+  @override
+  Object visitExpressionFunctionBody(ExpressionFunctionBody node) {
+    Token keyword = node.keyword;
+    if (keyword != null) {
+      _writer.print(keyword.lexeme);
+      _writer.print(' ');
+    }
+    _writer.print("=> ");
+    _visitNode(node.expression);
+    if (node.semicolon != null) {
+      _writer.print(';');
+    }
+    return null;
+  }
+
+  @override
+  Object visitExpressionStatement(ExpressionStatement node) {
+    _visitNode(node.expression);
+    _writer.print(';');
+    return null;
+  }
+
+  @override
+  Object visitExtendsClause(ExtendsClause node) {
+    _writer.print("extends ");
+    _visitNode(node.superclass);
+    return null;
+  }
+
+  @override
+  Object visitFieldDeclaration(FieldDeclaration node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _visitTokenWithSuffix(node.staticKeyword, " ");
+    _visitNode(node.fields);
+    _writer.print(";");
+    return null;
+  }
+
+  @override
+  Object visitFieldFormalParameter(FieldFormalParameter node) {
+    _visitTokenWithSuffix(node.keyword, " ");
+    _visitNodeWithSuffix(node.type, " ");
+    _writer.print("this.");
+    _visitNode(node.identifier);
+    _visitNode(node.parameters);
+    return null;
+  }
+
+  @override
+  Object visitForEachStatement(ForEachStatement node) {
+    DeclaredIdentifier loopVariable = node.loopVariable;
+    if (node.awaitKeyword != null) {
+      _writer.print("await ");
+    }
+    _writer.print("for (");
+    if (loopVariable == null) {
+      _visitNode(node.identifier);
+    } else {
+      _visitNode(loopVariable);
+    }
+    _writer.print(" in ");
+    _visitNode(node.iterable);
+    _writer.print(") ");
+    _visitNode(node.body);
+    return null;
+  }
+
+  @override
+  Object visitFormalParameterList(FormalParameterList node) {
+    String groupEnd = null;
+    _writer.print('(');
+    NodeList<FormalParameter> parameters = node.parameters;
+    int size = parameters.length;
+    for (int i = 0; i < size; i++) {
+      FormalParameter parameter = parameters[i];
+      if (i > 0) {
+        _writer.print(", ");
+      }
+      if (groupEnd == null && parameter is DefaultFormalParameter) {
+        if (parameter.kind == ParameterKind.NAMED) {
+          groupEnd = "}";
+          _writer.print('{');
+        } else {
+          groupEnd = "]";
+          _writer.print('[');
+        }
+      }
+      parameter.accept(this);
+    }
+    if (groupEnd != null) {
+      _writer.print(groupEnd);
+    }
+    _writer.print(')');
+    return null;
+  }
+
+  @override
+  Object visitForStatement(ForStatement node) {
+    Expression initialization = node.initialization;
+    _writer.print("for (");
+    if (initialization != null) {
+      _visitNode(initialization);
+    } else {
+      _visitNode(node.variables);
+    }
+    _writer.print(";");
+    _visitNodeWithPrefix(" ", node.condition);
+    _writer.print(";");
+    _visitNodeListWithSeparatorAndPrefix(" ", node.updaters, ", ");
+    _writer.print(") ");
+    _visitNode(node.body);
+    return null;
+  }
+
+  @override
+  Object visitFunctionDeclaration(FunctionDeclaration node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _visitNodeWithSuffix(node.returnType, " ");
+    _visitTokenWithSuffix(node.propertyKeyword, " ");
+    _visitNode(node.name);
+    _visitNode(node.functionExpression);
+    return null;
+  }
+
+  @override
+  Object visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
+    _visitNode(node.functionDeclaration);
+    return null;
+  }
+
+  @override
+  Object visitFunctionExpression(FunctionExpression node) {
+    _visitNode(node.parameters);
+    _writer.print(' ');
+    _visitNode(node.body);
+    return null;
+  }
+
+  @override
+  Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    _visitNode(node.function);
+    _visitNode(node.argumentList);
+    return null;
+  }
+
+  @override
+  Object visitFunctionTypeAlias(FunctionTypeAlias node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _writer.print("typedef ");
+    _visitNodeWithSuffix(node.returnType, " ");
+    _visitNode(node.name);
+    _visitNode(node.typeParameters);
+    _visitNode(node.parameters);
+    _writer.print(";");
+    return null;
+  }
+
+  @override
+  Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+    _visitNodeWithSuffix(node.returnType, " ");
+    _visitNode(node.identifier);
+    _visitNode(node.parameters);
+    return null;
+  }
+
+  @override
+  Object visitHideCombinator(HideCombinator node) {
+    _writer.print("hide ");
+    _visitNodeListWithSeparator(node.hiddenNames, ", ");
+    return null;
+  }
+
+  @override
+  Object visitIfStatement(IfStatement node) {
+    _writer.print("if (");
+    _visitNode(node.condition);
+    _writer.print(") ");
+    _visitNode(node.thenStatement);
+    _visitNodeWithPrefix(" else ", node.elseStatement);
+    return null;
+  }
+
+  @override
+  Object visitImplementsClause(ImplementsClause node) {
+    _writer.print("implements ");
+    _visitNodeListWithSeparator(node.interfaces, ", ");
+    return null;
+  }
+
+  @override
+  Object visitImportDirective(ImportDirective node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _writer.print("import ");
+    _visitNode(node.uri);
+    if (node.deferredKeyword != null) {
+      _writer.print(" deferred");
+    }
+    _visitNodeWithPrefix(" as ", node.prefix);
+    _visitNodeListWithSeparatorAndPrefix(" ", node.combinators, " ");
+    _writer.print(';');
+    return null;
+  }
+
+  @override
+  Object visitIndexExpression(IndexExpression node) {
+    if (node.isCascaded) {
+      _writer.print("..");
+    } else {
+      _visitNode(node.target);
+    }
+    _writer.print('[');
+    _visitNode(node.index);
+    _writer.print(']');
+    return null;
+  }
+
+  @override
+  Object visitInstanceCreationExpression(InstanceCreationExpression node) {
+    _visitTokenWithSuffix(node.keyword, " ");
+    _visitNode(node.constructorName);
+    _visitNode(node.argumentList);
+    return null;
+  }
+
+  @override
+  Object visitIntegerLiteral(IntegerLiteral node) {
+    _writer.print(node.literal.lexeme);
+    return null;
+  }
+
+  @override
+  Object visitInterpolationExpression(InterpolationExpression node) {
+    if (node.rightBracket != null) {
+      _writer.print("\${");
+      _visitNode(node.expression);
+      _writer.print("}");
+    } else {
+      _writer.print("\$");
+      _visitNode(node.expression);
+    }
+    return null;
+  }
+
+  @override
+  Object visitInterpolationString(InterpolationString node) {
+    _writer.print(node.contents.lexeme);
+    return null;
+  }
+
+  @override
+  Object visitIsExpression(IsExpression node) {
+    _visitNode(node.expression);
+    if (node.notOperator == null) {
+      _writer.print(" is ");
+    } else {
+      _writer.print(" is! ");
+    }
+    _visitNode(node.type);
+    return null;
+  }
+
+  @override
+  Object visitLabel(Label node) {
+    _visitNode(node.label);
+    _writer.print(":");
+    return null;
+  }
+
+  @override
+  Object visitLabeledStatement(LabeledStatement node) {
+    _visitNodeListWithSeparatorAndSuffix(node.labels, " ", " ");
+    _visitNode(node.statement);
+    return null;
+  }
+
+  @override
+  Object visitLibraryDirective(LibraryDirective node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _writer.print("library ");
+    _visitNode(node.name);
+    _writer.print(';');
+    return null;
+  }
+
+  @override
+  Object visitLibraryIdentifier(LibraryIdentifier node) {
+    _writer.print(node.name);
+    return null;
+  }
+
+  @override
+  Object visitListLiteral(ListLiteral node) {
+    if (node.constKeyword != null) {
+      _writer.print(node.constKeyword.lexeme);
+      _writer.print(' ');
+    }
+    _visitNodeWithSuffix(node.typeArguments, " ");
+    _writer.print("[");
+    _visitNodeListWithSeparator(node.elements, ", ");
+    _writer.print("]");
+    return null;
+  }
+
+  @override
+  Object visitMapLiteral(MapLiteral node) {
+    if (node.constKeyword != null) {
+      _writer.print(node.constKeyword.lexeme);
+      _writer.print(' ');
+    }
+    _visitNodeWithSuffix(node.typeArguments, " ");
+    _writer.print("{");
+    _visitNodeListWithSeparator(node.entries, ", ");
+    _writer.print("}");
+    return null;
+  }
+
+  @override
+  Object visitMapLiteralEntry(MapLiteralEntry node) {
+    _visitNode(node.key);
+    _writer.print(" : ");
+    _visitNode(node.value);
+    return null;
+  }
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _visitTokenWithSuffix(node.externalKeyword, " ");
+    _visitTokenWithSuffix(node.modifierKeyword, " ");
+    _visitNodeWithSuffix(node.returnType, " ");
+    _visitTokenWithSuffix(node.propertyKeyword, " ");
+    _visitTokenWithSuffix(node.operatorKeyword, " ");
+    _visitNode(node.name);
+    if (!node.isGetter) {
+      _visitNode(node.parameters);
+    }
+    _visitFunctionWithPrefix(" ", node.body);
+    return null;
+  }
+
+  @override
+  Object visitMethodInvocation(MethodInvocation node) {
+    if (node.isCascaded) {
+      _writer.print("..");
+    } else {
+      if (node.target != null) {
+        node.target.accept(this);
+        _writer.print(node.operator.lexeme);
+      }
+    }
+    _visitNode(node.methodName);
+    _visitNode(node.argumentList);
+    return null;
+  }
+
+  @override
+  Object visitNamedExpression(NamedExpression node) {
+    _visitNode(node.name);
+    _visitNodeWithPrefix(" ", node.expression);
+    return null;
+  }
+
+  @override
+  Object visitNativeClause(NativeClause node) {
+    _writer.print("native ");
+    _visitNode(node.name);
+    return null;
+  }
+
+  @override
+  Object visitNativeFunctionBody(NativeFunctionBody node) {
+    _writer.print("native ");
+    _visitNode(node.stringLiteral);
+    _writer.print(';');
+    return null;
+  }
+
+  @override
+  Object visitNullLiteral(NullLiteral node) {
+    _writer.print("null");
+    return null;
+  }
+
+  @override
+  Object visitParenthesizedExpression(ParenthesizedExpression node) {
+    _writer.print('(');
+    _visitNode(node.expression);
+    _writer.print(')');
+    return null;
+  }
+
+  @override
+  Object visitPartDirective(PartDirective node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _writer.print("part ");
+    _visitNode(node.uri);
+    _writer.print(';');
+    return null;
+  }
+
+  @override
+  Object visitPartOfDirective(PartOfDirective node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _writer.print("part of ");
+    _visitNode(node.libraryName);
+    _writer.print(';');
+    return null;
+  }
+
+  @override
+  Object visitPostfixExpression(PostfixExpression node) {
+    _visitNode(node.operand);
+    _writer.print(node.operator.lexeme);
+    return null;
+  }
+
+  @override
+  Object visitPrefixedIdentifier(PrefixedIdentifier node) {
+    _visitNode(node.prefix);
+    _writer.print('.');
+    _visitNode(node.identifier);
+    return null;
+  }
+
+  @override
+  Object visitPrefixExpression(PrefixExpression node) {
+    _writer.print(node.operator.lexeme);
+    _visitNode(node.operand);
+    return null;
+  }
+
+  @override
+  Object visitPropertyAccess(PropertyAccess node) {
+    if (node.isCascaded) {
+      _writer.print("..");
+    } else {
+      _visitNode(node.target);
+      _writer.print(node.operator.lexeme);
+    }
+    _visitNode(node.propertyName);
+    return null;
+  }
+
+  @override
+  Object visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    _writer.print("this");
+    _visitNodeWithPrefix(".", node.constructorName);
+    _visitNode(node.argumentList);
+    return null;
+  }
+
+  @override
+  Object visitRethrowExpression(RethrowExpression node) {
+    _writer.print("rethrow");
+    return null;
+  }
+
+  @override
+  Object visitReturnStatement(ReturnStatement node) {
+    Expression expression = node.expression;
+    if (expression == null) {
+      _writer.print("return;");
+    } else {
+      _writer.print("return ");
+      expression.accept(this);
+      _writer.print(";");
+    }
+    return null;
+  }
+
+  @override
+  Object visitScriptTag(ScriptTag node) {
+    _writer.print(node.scriptTag.lexeme);
+    return null;
+  }
+
+  @override
+  Object visitShowCombinator(ShowCombinator node) {
+    _writer.print("show ");
+    _visitNodeListWithSeparator(node.shownNames, ", ");
+    return null;
+  }
+
+  @override
+  Object visitSimpleFormalParameter(SimpleFormalParameter node) {
+    _visitTokenWithSuffix(node.keyword, " ");
+    _visitNodeWithSuffix(node.type, " ");
+    _visitNode(node.identifier);
+    return null;
+  }
+
+  @override
+  Object visitSimpleIdentifier(SimpleIdentifier node) {
+    _writer.print(node.token.lexeme);
+    return null;
+  }
+
+  @override
+  Object visitSimpleStringLiteral(SimpleStringLiteral node) {
+    _writer.print(node.literal.lexeme);
+    return null;
+  }
+
+  @override
+  Object visitStringInterpolation(StringInterpolation node) {
+    _visitNodeList(node.elements);
+    return null;
+  }
+
+  @override
+  Object visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    _writer.print("super");
+    _visitNodeWithPrefix(".", node.constructorName);
+    _visitNode(node.argumentList);
+    return null;
+  }
+
+  @override
+  Object visitSuperExpression(SuperExpression node) {
+    _writer.print("super");
+    return null;
+  }
+
+  @override
+  Object visitSwitchCase(SwitchCase node) {
+    _visitNodeListWithSeparatorAndSuffix(node.labels, " ", " ");
+    _writer.print("case ");
+    _visitNode(node.expression);
+    _writer.print(": ");
+    _visitNodeListWithSeparator(node.statements, " ");
+    return null;
+  }
+
+  @override
+  Object visitSwitchDefault(SwitchDefault node) {
+    _visitNodeListWithSeparatorAndSuffix(node.labels, " ", " ");
+    _writer.print("default: ");
+    _visitNodeListWithSeparator(node.statements, " ");
+    return null;
+  }
+
+  @override
+  Object visitSwitchStatement(SwitchStatement node) {
+    _writer.print("switch (");
+    _visitNode(node.expression);
+    _writer.print(") {");
+    _visitNodeListWithSeparator(node.members, " ");
+    _writer.print("}");
+    return null;
+  }
+
+  @override
+  Object visitSymbolLiteral(SymbolLiteral node) {
+    _writer.print("#");
+    List<Token> components = node.components;
+    for (int i = 0; i < components.length; i++) {
+      if (i > 0) {
+        _writer.print(".");
+      }
+      _writer.print(components[i].lexeme);
+    }
+    return null;
+  }
+
+  @override
+  Object visitThisExpression(ThisExpression node) {
+    _writer.print("this");
+    return null;
+  }
+
+  @override
+  Object visitThrowExpression(ThrowExpression node) {
+    _writer.print("throw ");
+    _visitNode(node.expression);
+    return null;
+  }
+
+  @override
+  Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+    _visitNodeWithSuffix(node.variables, ";");
+    return null;
+  }
+
+  @override
+  Object visitTryStatement(TryStatement node) {
+    _writer.print("try ");
+    _visitNode(node.body);
+    _visitNodeListWithSeparatorAndPrefix(" ", node.catchClauses, " ");
+    _visitNodeWithPrefix(" finally ", node.finallyBlock);
+    return null;
+  }
+
+  @override
+  Object visitTypeArgumentList(TypeArgumentList node) {
+    _writer.print('<');
+    _visitNodeListWithSeparator(node.arguments, ", ");
+    _writer.print('>');
+    return null;
+  }
+
+  @override
+  Object visitTypeName(TypeName node) {
+    _visitNode(node.name);
+    _visitNode(node.typeArguments);
+    return null;
+  }
+
+  @override
+  Object visitTypeParameter(TypeParameter node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _visitNode(node.name);
+    _visitNodeWithPrefix(" extends ", node.bound);
+    return null;
+  }
+
+  @override
+  Object visitTypeParameterList(TypeParameterList node) {
+    _writer.print('<');
+    _visitNodeListWithSeparator(node.typeParameters, ", ");
+    _writer.print('>');
+    return null;
+  }
+
+  @override
+  Object visitVariableDeclaration(VariableDeclaration node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _visitNode(node.name);
+    _visitNodeWithPrefix(" = ", node.initializer);
+    return null;
+  }
+
+  @override
+  Object visitVariableDeclarationList(VariableDeclarationList node) {
+    _visitNodeListWithSeparatorAndSuffix(node.metadata, " ", " ");
+    _visitTokenWithSuffix(node.keyword, " ");
+    _visitNodeWithSuffix(node.type, " ");
+    _visitNodeListWithSeparator(node.variables, ", ");
+    return null;
+  }
+
+  @override
+  Object visitVariableDeclarationStatement(VariableDeclarationStatement node) {
+    _visitNode(node.variables);
+    _writer.print(";");
+    return null;
+  }
+
+  @override
+  Object visitWhileStatement(WhileStatement node) {
+    _writer.print("while (");
+    _visitNode(node.condition);
+    _writer.print(") ");
+    _visitNode(node.body);
+    return null;
+  }
+
+  @override
+  Object visitWithClause(WithClause node) {
+    _writer.print("with ");
+    _visitNodeListWithSeparator(node.mixinTypes, ", ");
+    return null;
+  }
+
+  @override
+  Object visitYieldStatement(YieldStatement node) {
+    if (node.star != null) {
+      _writer.print("yield* ");
+    } else {
+      _writer.print("yield ");
+    }
+    _visitNode(node.expression);
+    _writer.print(";");
+    return null;
+  }
+
+  /**
+   * Visit the given function [body], printing the [prefix] before if the body
+   * is not empty.
+   */
+  void _visitFunctionWithPrefix(String prefix, FunctionBody body) {
+    if (body is! EmptyFunctionBody) {
+      _writer.print(prefix);
+    }
+    _visitNode(body);
+  }
+
+  /**
+   * Safely visit the given [node].
+   */
+  void _visitNode(AstNode node) {
+    if (node != null) {
+      node.accept(this);
+    }
+  }
+
+  /**
+   * Print a list of [nodes] without any separation.
+   */
+  void _visitNodeList(NodeList<AstNode> nodes) {
+    _visitNodeListWithSeparator(nodes, "");
+  }
+
+  /**
+   * Print a list of [nodes], separated by the given [separator].
+   */
+  void _visitNodeListWithSeparator(NodeList<AstNode> nodes, String separator) {
+    if (nodes != null) {
+      int size = nodes.length;
+      for (int i = 0; i < size; i++) {
+        if (i > 0) {
+          _writer.print(separator);
+        }
+        nodes[i].accept(this);
+      }
+    }
+  }
+
+  /**
+   * Print a list of [nodes], prefixed by the given [prefix] if the list is not
+   * empty, and separated by the given [separator].
+   */
+  void _visitNodeListWithSeparatorAndPrefix(
+      String prefix, NodeList<AstNode> nodes, String separator) {
+    if (nodes != null) {
+      int size = nodes.length;
+      if (size > 0) {
+        _writer.print(prefix);
+        for (int i = 0; i < size; i++) {
+          if (i > 0) {
+            _writer.print(separator);
+          }
+          nodes[i].accept(this);
+        }
+      }
+    }
+  }
+
+  /**
+   * Print a list of [nodes], separated by the given [separator], followed by
+   * the given [suffix] if the list is not empty.
+   */
+  void _visitNodeListWithSeparatorAndSuffix(
+      NodeList<AstNode> nodes, String separator, String suffix) {
+    if (nodes != null) {
+      int size = nodes.length;
+      if (size > 0) {
+        for (int i = 0; i < size; i++) {
+          if (i > 0) {
+            _writer.print(separator);
+          }
+          nodes[i].accept(this);
+        }
+        _writer.print(suffix);
+      }
+    }
+  }
+
+  /**
+   * Safely visit the given [node], printing the [prefix] before the node if it
+   * is non-`null`.
+   */
+  void _visitNodeWithPrefix(String prefix, AstNode node) {
+    if (node != null) {
+      _writer.print(prefix);
+      node.accept(this);
+    }
+  }
+
+  /**
+   * Safely visit the given [node], printing the [suffix] after the node if it
+   * is non-`null`.
+   */
+  void _visitNodeWithSuffix(AstNode node, String suffix) {
+    if (node != null) {
+      node.accept(this);
+      _writer.print(suffix);
+    }
+  }
+
+  /**
+   * Safely visit the given [token], printing the [suffix] after the token if it
+   * is non-`null`.
+   */
+  void _visitTokenWithSuffix(Token token, String suffix) {
+    if (token != null) {
+      _writer.print(token.lexeme);
+      _writer.print(suffix);
+    }
+  }
+}
+
+/**
+ * A try statement.
+ *
+ * > tryStatement ::=
+ * >     'try' [Block] ([CatchClause]+ finallyClause? | finallyClause)
+ * >
+ * > finallyClause ::=
+ * >     'finally' [Block]
+ */
+class TryStatement extends Statement {
+  /**
+   * The token representing the 'try' keyword.
+   */
+  Token tryKeyword;
+
+  /**
+   * The body of the statement.
+   */
+  Block _body;
+
+  /**
+   * The catch clauses contained in the try statement.
+   */
+  NodeList<CatchClause> _catchClauses;
+
+  /**
+   * The token representing the 'finally' keyword, or `null` if the statement
+   * does not contain a finally clause.
+   */
+  Token finallyKeyword;
+
+  /**
+   * The finally block contained in the try statement, or `null` if the
+   * statement does not contain a finally clause.
+   */
+  Block _finallyBlock;
+
+  /**
+   * Initialize a newly created try statement. The list of [catchClauses] can be
+   * `null` if there are no catch clauses. The [finallyKeyword] and
+   * [finallyBlock] can be `null` if there is no finally clause.
+   */
+  TryStatement(this.tryKeyword, Block body, List<CatchClause> catchClauses,
+      this.finallyKeyword, Block finallyBlock) {
+    _body = _becomeParentOf(body);
+    _catchClauses = new NodeList<CatchClause>(this, catchClauses);
+    _finallyBlock = _becomeParentOf(finallyBlock);
+  }
+
+  @override
+  Token get beginToken => tryKeyword;
+
+  /**
+   * Return the body of the statement.
+   */
+  Block get body => _body;
+
+  /**
+   * Set the body of the statement to the given [block].
+   */
+  void set body(Block block) {
+    _body = _becomeParentOf(block);
+  }
+
+  /**
+   * Return the catch clauses contained in the try statement.
+   */
+  NodeList<CatchClause> get catchClauses => _catchClauses;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(tryKeyword)
+    ..add(_body)
+    ..addAll(_catchClauses)
+    ..add(finallyKeyword)
+    ..add(_finallyBlock);
+
+  @override
+  Token get endToken {
+    if (_finallyBlock != null) {
+      return _finallyBlock.endToken;
+    } else if (finallyKeyword != null) {
+      return finallyKeyword;
+    } else if (!_catchClauses.isEmpty) {
+      return _catchClauses.endToken;
+    }
+    return _body.endToken;
+  }
+
+  /**
+   * Return the finally block contained in the try statement, or `null` if the
+   * statement does not contain a finally clause.
+   */
+  Block get finallyBlock => _finallyBlock;
+
+  /**
+   * Set the finally block contained in the try statement to the given [block].
+   */
+  void set finallyBlock(Block block) {
+    _finallyBlock = _becomeParentOf(block);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitTryStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_body, visitor);
+    _catchClauses.accept(visitor);
+    _safelyVisitChild(_finallyBlock, visitor);
+  }
+}
+
+/**
+ * The declaration of a type alias.
+ *
+ * > typeAlias ::=
+ * >     'typedef' typeAliasBody
+ * >
+ * > typeAliasBody ::=
+ * >     classTypeAlias
+ * >   | functionTypeAlias
+ */
+abstract class TypeAlias extends NamedCompilationUnitMember {
+  /**
+   * The token representing the 'typedef' keyword.
+   */
+  Token typedefKeyword;
+
+  /**
+   * The semicolon terminating the declaration.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created type alias. Either or both of the [comment] and
+   * [metadata] can be `null` if the declaration does not have the corresponding
+   * attribute.
+   */
+  TypeAlias(Comment comment, List<Annotation> metadata, this.typedefKeyword,
+      SimpleIdentifier name, this.semicolon)
+      : super(comment, metadata, name);
+
+  @override
+  Token get endToken => semicolon;
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata => typedefKeyword;
+
+  /**
+   * Return the token representing the 'typedef' keyword.
+   */
+  @deprecated // Use "this.typedefKeyword"
+  Token get keyword => typedefKeyword;
+
+  /**
+   * Set the token representing the 'typedef' keyword to the given [token].
+   */
+  @deprecated // Use "this.typedefKeyword"
+  set keyword(Token token) {
+    typedefKeyword = token;
+  }
+}
+
+/**
+ * A list of type arguments.
+ *
+ * > typeArguments ::=
+ * >     '<' typeName (',' typeName)* '>'
+ */
+class TypeArgumentList extends AstNode {
+  /**
+   * The left bracket.
+   */
+  Token leftBracket;
+
+  /**
+   * The type arguments associated with the type.
+   */
+  NodeList<TypeName> _arguments;
+
+  /**
+   * The right bracket.
+   */
+  Token rightBracket;
+
+  /**
+   * Initialize a newly created list of type arguments.
+   */
+  TypeArgumentList(
+      this.leftBracket, List<TypeName> arguments, this.rightBracket) {
+    _arguments = new NodeList<TypeName>(this, arguments);
+  }
+
+  /**
+   * Return the type arguments associated with the type.
+   */
+  NodeList<TypeName> get arguments => _arguments;
+
+  @override
+  Token get beginToken => leftBracket;
+
+  /**
+   * TODO(paulberry): Add commas.
+   */
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(leftBracket)
+    ..addAll(_arguments)
+    ..add(rightBracket);
+
+  @override
+  Token get endToken => rightBracket;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitTypeArgumentList(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _arguments.accept(visitor);
+  }
+}
+
+/**
+ * A literal that has a type associated with it.
+ *
+ * > typedLiteral ::=
+ * >     [ListLiteral]
+ * >   | [MapLiteral]
+ */
+abstract class TypedLiteral extends Literal {
+  /**
+   * The token representing the 'const' keyword, or `null` if the literal is not
+   * a constant.
+   */
+  Token constKeyword;
+
+  /**
+   * The type argument associated with this literal, or `null` if no type
+   * arguments were declared.
+   */
+  TypeArgumentList _typeArguments;
+
+  /**
+   * Initialize a newly created typed literal. The [constKeyword] can be `null`\
+   * if the literal is not a constant. The [typeArguments] can be `null` if no
+   * type arguments were declared.
+   */
+  TypedLiteral(this.constKeyword, TypeArgumentList typeArguments) {
+    _typeArguments = _becomeParentOf(typeArguments);
+  }
+
+  /**
+   * Return the type argument associated with this literal, or `null` if no type
+   * arguments were declared.
+   */
+  TypeArgumentList get typeArguments => _typeArguments;
+
+  /**
+   * Set the type argument associated with this literal to the given
+   * [typeArguments].
+   */
+  void set typeArguments(TypeArgumentList typeArguments) {
+    _typeArguments = _becomeParentOf(typeArguments);
+  }
+
+  ChildEntities get _childEntities => new ChildEntities()
+    ..add(constKeyword)
+    ..add(_typeArguments);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_typeArguments, visitor);
+  }
+}
+
+/**
+ * The name of a type, which can optionally include type arguments.
+ *
+ * > typeName ::=
+ * >     [Identifier] typeArguments?
+ */
+class TypeName extends AstNode {
+  /**
+   * The name of the type.
+   */
+  Identifier _name;
+
+  /**
+   * The type arguments associated with the type, or `null` if there are no type
+   * arguments.
+   */
+  TypeArgumentList _typeArguments;
+
+  /**
+   * The type being named, or `null` if the AST structure has not been resolved.
+   */
+  DartType type;
+
+  /**
+   * Initialize a newly created type name. The [typeArguments] can be `null` if
+   * there are no type arguments.
+   */
+  TypeName(Identifier name, TypeArgumentList typeArguments) {
+    _name = _becomeParentOf(name);
+    _typeArguments = _becomeParentOf(typeArguments);
+  }
+
+  @override
+  Token get beginToken => _name.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_name)
+    ..add(_typeArguments);
+
+  @override
+  Token get endToken {
+    if (_typeArguments != null) {
+      return _typeArguments.endToken;
+    }
+    return _name.endToken;
+  }
+
+  /**
+   * Return `true` if this type is a deferred type.
+   *
+   * 15.1 Static Types: A type <i>T</i> is deferred iff it is of the form
+   * </i>p.T</i> where <i>p</i> is a deferred prefix.
+   */
+  bool get isDeferred {
+    Identifier identifier = name;
+    if (identifier is! PrefixedIdentifier) {
+      return false;
+    }
+    return (identifier as PrefixedIdentifier).isDeferred;
+  }
+
+  @override
+  bool get isSynthetic => _name.isSynthetic && _typeArguments == null;
+
+  /**
+   * Return the name of the type.
+   */
+  Identifier get name => _name;
+
+  /**
+   * Set the name of the type to the given [identifier].
+   */
+  void set name(Identifier identifier) {
+    _name = _becomeParentOf(identifier);
+  }
+
+  /**
+   * Return the type arguments associated with the type, or `null` if there are
+   * no type arguments.
+   */
+  TypeArgumentList get typeArguments => _typeArguments;
+
+  /**
+   * Set the type arguments associated with the type to the given
+   * [typeArguments].
+   */
+  void set typeArguments(TypeArgumentList typeArguments) {
+    _typeArguments = _becomeParentOf(typeArguments);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitTypeName(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_name, visitor);
+    _safelyVisitChild(_typeArguments, visitor);
+  }
+}
+
+/**
+ * A type parameter.
+ *
+ * > typeParameter ::=
+ * >     [SimpleIdentifier] ('extends' [TypeName])?
+ */
+class TypeParameter extends Declaration {
+  /**
+   * The name of the type parameter.
+   */
+  SimpleIdentifier _name;
+
+  /**
+   * The token representing the 'extends' keyword, or `null` if there is no
+   * explicit upper bound.
+   */
+  Token extendsKeyword;
+
+  /**
+   * The name of the upper bound for legal arguments, or `null` if there is no
+   * explicit upper bound.
+   */
+  TypeName _bound;
+
+  /**
+   * Initialize a newly created type parameter. Either or both of the [comment]
+   * and [metadata] can be `null` if the parameter does not have the
+   * corresponding attribute. The [extendsKeyword] and [bound] can be `null` if
+   * the parameter does not have an upper bound.
+   */
+  TypeParameter(Comment comment, List<Annotation> metadata,
+      SimpleIdentifier name, this.extendsKeyword, TypeName bound)
+      : super(comment, metadata) {
+    _name = _becomeParentOf(name);
+    _bound = _becomeParentOf(bound);
+  }
+
+  /**
+   * Return the name of the upper bound for legal arguments, or `null` if there
+   * is no explicit upper bound.
+   */
+  TypeName get bound => _bound;
+
+  /**
+   * Set the name of the upper bound for legal arguments to the given
+   * [typeName].
+   */
+  void set bound(TypeName typeName) {
+    _bound = _becomeParentOf(typeName);
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(_name)
+    ..add(extendsKeyword)
+    ..add(_bound);
+
+  @override
+  TypeParameterElement get element =>
+      _name != null ? (_name.staticElement as TypeParameterElement) : null;
+
+  @override
+  Token get endToken {
+    if (_bound == null) {
+      return _name.endToken;
+    }
+    return _bound.endToken;
+  }
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata => _name.beginToken;
+
+  /**
+   * Return the token representing the 'extends' keyword, or `null` if there is
+   * no explicit upper bound.
+   */
+  @deprecated // Use "this.extendsKeyword"
+  Token get keyword => extendsKeyword;
+
+  /**
+   * Set the token representing the 'extends' keyword to the given [token].
+   */
+  @deprecated // Use "this.extendsKeyword"
+  set keyword(Token token) {
+    extendsKeyword = token;
+  }
+
+  /**
+   * Return the name of the type parameter.
+   */
+  SimpleIdentifier get name => _name;
+
+  /**
+   * Set the name of the type parameter to the given [identifier].
+   */
+  void set name(SimpleIdentifier identifier) {
+    _name = _becomeParentOf(identifier);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitTypeParameter(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_name, visitor);
+    _safelyVisitChild(_bound, visitor);
+  }
+}
+
+/**
+ * Type parameters within a declaration.
+ *
+ * > typeParameterList ::=
+ * >     '<' [TypeParameter] (',' [TypeParameter])* '>'
+ */
+class TypeParameterList extends AstNode {
+  /**
+   * The left angle bracket.
+   */
+  final Token leftBracket;
+
+  /**
+   * The type parameters in the list.
+   */
+  NodeList<TypeParameter> _typeParameters;
+
+  /**
+   * The right angle bracket.
+   */
+  final Token rightBracket;
+
+  /**
+   * Initialize a newly created list of type parameters.
+   */
+  TypeParameterList(
+      this.leftBracket, List<TypeParameter> typeParameters, this.rightBracket) {
+    _typeParameters = new NodeList<TypeParameter>(this, typeParameters);
+  }
+
+  @override
+  Token get beginToken => leftBracket;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(leftBracket)
+    ..addAll(_typeParameters)
+    ..add(rightBracket);
+
+  @override
+  Token get endToken => rightBracket;
+
+  /**
+   * Return the type parameters for the type.
+   */
+  NodeList<TypeParameter> get typeParameters => _typeParameters;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitTypeParameterList(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _typeParameters.accept(visitor);
+  }
+}
+
+/**
+ * An AST visitor that will recursively visit all of the nodes in an AST
+ * structure (like instances of the class [RecursiveAstVisitor]). In addition,
+ * every node will also be visited by using a single unified [visitNode] method.
+ *
+ * Subclasses that override a visit method must either invoke the overridden
+ * visit method or explicitly invoke the more general [visitNode] method.
+ * Failure to do so will cause the children of the visited node to not be
+ * visited.
+ */
+class UnifyingAstVisitor<R> implements AstVisitor<R> {
+  @override
+  R visitAdjacentStrings(AdjacentStrings node) => visitNode(node);
+
+  @override
+  R visitAnnotation(Annotation node) => visitNode(node);
+
+  @override
+  R visitArgumentList(ArgumentList node) => visitNode(node);
+
+  @override
+  R visitAsExpression(AsExpression node) => visitNode(node);
+
+  @override
+  R visitAssertStatement(AssertStatement node) => visitNode(node);
+
+  @override
+  R visitAssignmentExpression(AssignmentExpression node) => visitNode(node);
+
+  @override
+  R visitAwaitExpression(AwaitExpression node) => visitNode(node);
+
+  @override
+  R visitBinaryExpression(BinaryExpression node) => visitNode(node);
+
+  @override
+  R visitBlock(Block node) => visitNode(node);
+
+  @override
+  R visitBlockFunctionBody(BlockFunctionBody node) => visitNode(node);
+
+  @override
+  R visitBooleanLiteral(BooleanLiteral node) => visitNode(node);
+
+  @override
+  R visitBreakStatement(BreakStatement node) => visitNode(node);
+
+  @override
+  R visitCascadeExpression(CascadeExpression node) => visitNode(node);
+
+  @override
+  R visitCatchClause(CatchClause node) => visitNode(node);
+
+  @override
+  R visitClassDeclaration(ClassDeclaration node) => visitNode(node);
+
+  @override
+  R visitClassTypeAlias(ClassTypeAlias node) => visitNode(node);
+
+  @override
+  R visitComment(Comment node) => visitNode(node);
+
+  @override
+  R visitCommentReference(CommentReference node) => visitNode(node);
+
+  @override
+  R visitCompilationUnit(CompilationUnit node) => visitNode(node);
+
+  @override
+  R visitConditionalExpression(ConditionalExpression node) => visitNode(node);
+
+  @override
+  R visitConstructorDeclaration(ConstructorDeclaration node) => visitNode(node);
+
+  @override
+  R visitConstructorFieldInitializer(ConstructorFieldInitializer node) =>
+      visitNode(node);
+
+  @override
+  R visitConstructorName(ConstructorName node) => visitNode(node);
+
+  @override
+  R visitContinueStatement(ContinueStatement node) => visitNode(node);
+
+  @override
+  R visitDeclaredIdentifier(DeclaredIdentifier node) => visitNode(node);
+
+  @override
+  R visitDefaultFormalParameter(DefaultFormalParameter node) => visitNode(node);
+
+  @override
+  R visitDoStatement(DoStatement node) => visitNode(node);
+
+  @override
+  R visitDoubleLiteral(DoubleLiteral node) => visitNode(node);
+
+  @override
+  R visitEmptyFunctionBody(EmptyFunctionBody node) => visitNode(node);
+
+  @override
+  R visitEmptyStatement(EmptyStatement node) => visitNode(node);
+
+  @override
+  R visitEnumConstantDeclaration(EnumConstantDeclaration node) =>
+      visitNode(node);
+
+  @override
+  R visitEnumDeclaration(EnumDeclaration node) => visitNode(node);
+
+  @override
+  R visitExportDirective(ExportDirective node) => visitNode(node);
+
+  @override
+  R visitExpressionFunctionBody(ExpressionFunctionBody node) => visitNode(node);
+
+  @override
+  R visitExpressionStatement(ExpressionStatement node) => visitNode(node);
+
+  @override
+  R visitExtendsClause(ExtendsClause node) => visitNode(node);
+
+  @override
+  R visitFieldDeclaration(FieldDeclaration node) => visitNode(node);
+
+  @override
+  R visitFieldFormalParameter(FieldFormalParameter node) => visitNode(node);
+
+  @override
+  R visitForEachStatement(ForEachStatement node) => visitNode(node);
+
+  @override
+  R visitFormalParameterList(FormalParameterList node) => visitNode(node);
+
+  @override
+  R visitForStatement(ForStatement node) => visitNode(node);
+
+  @override
+  R visitFunctionDeclaration(FunctionDeclaration node) => visitNode(node);
+
+  @override
+  R visitFunctionDeclarationStatement(FunctionDeclarationStatement node) =>
+      visitNode(node);
+
+  @override
+  R visitFunctionExpression(FunctionExpression node) => visitNode(node);
+
+  @override
+  R visitFunctionExpressionInvocation(FunctionExpressionInvocation node) =>
+      visitNode(node);
+
+  @override
+  R visitFunctionTypeAlias(FunctionTypeAlias node) => visitNode(node);
+
+  @override
+  R visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) =>
+      visitNode(node);
+
+  @override
+  R visitHideCombinator(HideCombinator node) => visitNode(node);
+
+  @override
+  R visitIfStatement(IfStatement node) => visitNode(node);
+
+  @override
+  R visitImplementsClause(ImplementsClause node) => visitNode(node);
+
+  @override
+  R visitImportDirective(ImportDirective node) => visitNode(node);
+
+  @override
+  R visitIndexExpression(IndexExpression node) => visitNode(node);
+
+  @override
+  R visitInstanceCreationExpression(InstanceCreationExpression node) =>
+      visitNode(node);
+
+  @override
+  R visitIntegerLiteral(IntegerLiteral node) => visitNode(node);
+
+  @override
+  R visitInterpolationExpression(InterpolationExpression node) =>
+      visitNode(node);
+
+  @override
+  R visitInterpolationString(InterpolationString node) => visitNode(node);
+
+  @override
+  R visitIsExpression(IsExpression node) => visitNode(node);
+
+  @override
+  R visitLabel(Label node) => visitNode(node);
+
+  @override
+  R visitLabeledStatement(LabeledStatement node) => visitNode(node);
+
+  @override
+  R visitLibraryDirective(LibraryDirective node) => visitNode(node);
+
+  @override
+  R visitLibraryIdentifier(LibraryIdentifier node) => visitNode(node);
+
+  @override
+  R visitListLiteral(ListLiteral node) => visitNode(node);
+
+  @override
+  R visitMapLiteral(MapLiteral node) => visitNode(node);
+
+  @override
+  R visitMapLiteralEntry(MapLiteralEntry node) => visitNode(node);
+
+  @override
+  R visitMethodDeclaration(MethodDeclaration node) => visitNode(node);
+
+  @override
+  R visitMethodInvocation(MethodInvocation node) => visitNode(node);
+
+  @override
+  R visitNamedExpression(NamedExpression node) => visitNode(node);
+
+  @override
+  R visitNativeClause(NativeClause node) => visitNode(node);
+
+  @override
+  R visitNativeFunctionBody(NativeFunctionBody node) => visitNode(node);
+
+  R visitNode(AstNode node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitNullLiteral(NullLiteral node) => visitNode(node);
+
+  @override
+  R visitParenthesizedExpression(ParenthesizedExpression node) =>
+      visitNode(node);
+
+  @override
+  R visitPartDirective(PartDirective node) => visitNode(node);
+
+  @override
+  R visitPartOfDirective(PartOfDirective node) => visitNode(node);
+
+  @override
+  R visitPostfixExpression(PostfixExpression node) => visitNode(node);
+
+  @override
+  R visitPrefixedIdentifier(PrefixedIdentifier node) => visitNode(node);
+
+  @override
+  R visitPrefixExpression(PrefixExpression node) => visitNode(node);
+
+  @override
+  R visitPropertyAccess(PropertyAccess node) => visitNode(node);
+
+  @override
+  R visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) => visitNode(node);
+
+  @override
+  R visitRethrowExpression(RethrowExpression node) => visitNode(node);
+
+  @override
+  R visitReturnStatement(ReturnStatement node) => visitNode(node);
+
+  @override
+  R visitScriptTag(ScriptTag scriptTag) => visitNode(scriptTag);
+
+  @override
+  R visitShowCombinator(ShowCombinator node) => visitNode(node);
+
+  @override
+  R visitSimpleFormalParameter(SimpleFormalParameter node) => visitNode(node);
+
+  @override
+  R visitSimpleIdentifier(SimpleIdentifier node) => visitNode(node);
+
+  @override
+  R visitSimpleStringLiteral(SimpleStringLiteral node) => visitNode(node);
+
+  @override
+  R visitStringInterpolation(StringInterpolation node) => visitNode(node);
+
+  @override
+  R visitSuperConstructorInvocation(SuperConstructorInvocation node) =>
+      visitNode(node);
+
+  @override
+  R visitSuperExpression(SuperExpression node) => visitNode(node);
+
+  @override
+  R visitSwitchCase(SwitchCase node) => visitNode(node);
+
+  @override
+  R visitSwitchDefault(SwitchDefault node) => visitNode(node);
+
+  @override
+  R visitSwitchStatement(SwitchStatement node) => visitNode(node);
+
+  @override
+  R visitSymbolLiteral(SymbolLiteral node) => visitNode(node);
+
+  @override
+  R visitThisExpression(ThisExpression node) => visitNode(node);
+
+  @override
+  R visitThrowExpression(ThrowExpression node) => visitNode(node);
+
+  @override
+  R visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) =>
+      visitNode(node);
+
+  @override
+  R visitTryStatement(TryStatement node) => visitNode(node);
+
+  @override
+  R visitTypeArgumentList(TypeArgumentList node) => visitNode(node);
+
+  @override
+  R visitTypeName(TypeName node) => visitNode(node);
+
+  @override
+  R visitTypeParameter(TypeParameter node) => visitNode(node);
+
+  @override
+  R visitTypeParameterList(TypeParameterList node) => visitNode(node);
+
+  @override
+  R visitVariableDeclaration(VariableDeclaration node) => visitNode(node);
+
+  @override
+  R visitVariableDeclarationList(VariableDeclarationList node) =>
+      visitNode(node);
+
+  @override
+  R visitVariableDeclarationStatement(VariableDeclarationStatement node) =>
+      visitNode(node);
+
+  @override
+  R visitWhileStatement(WhileStatement node) => visitNode(node);
+
+  @override
+  R visitWithClause(WithClause node) => visitNode(node);
+
+  @override
+  R visitYieldStatement(YieldStatement node) => visitNode(node);
+}
+
+/**
+ * A directive that references a URI.
+ *
+ * > uriBasedDirective ::=
+ * >     [ExportDirective]
+ * >   | [ImportDirective]
+ * >   | [PartDirective]
+ */
+abstract class UriBasedDirective extends Directive {
+  /**
+   * The prefix of a URI using the `dart-ext` scheme to reference a native code
+   * library.
+   */
+  static String _DART_EXT_SCHEME = "dart-ext:";
+
+  /**
+   * The URI referenced by this directive.
+   */
+  StringLiteral _uri;
+
+  /**
+   * The content of the URI.
+   */
+  String uriContent;
+
+  /**
+   * The source to which the URI was resolved.
+   */
+  Source source;
+
+  /**
+   * Initialize a newly create URI-based directive. Either or both of the
+   * [comment] and [metadata] can be `null` if the directive does not have the
+   * corresponding attribute.
+   */
+  UriBasedDirective(
+      Comment comment, List<Annotation> metadata, StringLiteral uri)
+      : super(comment, metadata) {
+    _uri = _becomeParentOf(uri);
+  }
+
+  /**
+   * Return the URI referenced by this directive.
+   */
+  StringLiteral get uri => _uri;
+
+  /**
+   * Set the URI referenced by this directive to the given [uri].
+   */
+  void set uri(StringLiteral uri) {
+    _uri = _becomeParentOf(uri);
+  }
+
+  /**
+   * Return the element associated with the URI of this directive, or `null` if
+   * the AST structure has not been resolved or if the URI could not be
+   * resolved. Examples of the latter case include a directive that contains an
+   * invalid URL or a URL that does not exist.
+   */
+  Element get uriElement;
+
+  /**
+   * Validate this directive, but do not check for existence. Return a code
+   * indicating the problem if there is one, or `null` no problem
+   */
+  UriValidationCode validate() {
+    StringLiteral uriLiteral = uri;
+    if (uriLiteral is StringInterpolation) {
+      return UriValidationCode.URI_WITH_INTERPOLATION;
+    }
+    String uriContent = this.uriContent;
+    if (uriContent == null) {
+      return UriValidationCode.INVALID_URI;
+    }
+    if (this is ImportDirective && uriContent.startsWith(_DART_EXT_SCHEME)) {
+      return UriValidationCode.URI_WITH_DART_EXT_SCHEME;
+    }
+    try {
+      parseUriWithException(Uri.encodeFull(uriContent));
+    } on URISyntaxException {
+      return UriValidationCode.INVALID_URI;
+    }
+    return null;
+  }
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_uri, visitor);
+  }
+}
+
+/**
+ * Validation codes returned by [UriBasedDirective.validate].
+ */
+class UriValidationCode {
+  static const UriValidationCode INVALID_URI =
+      const UriValidationCode('INVALID_URI');
+
+  static const UriValidationCode URI_WITH_INTERPOLATION =
+      const UriValidationCode('URI_WITH_INTERPOLATION');
+
+  static const UriValidationCode URI_WITH_DART_EXT_SCHEME =
+      const UriValidationCode('URI_WITH_DART_EXT_SCHEME');
+
+  /**
+   * The name of the validation code.
+   */
+  final String name;
+
+  /**
+   * Initialize a newly created validation code to have the given [name].
+   */
+  const UriValidationCode(this.name);
+
+  @override
+  String toString() => name;
+}
+
+/**
+ * An identifier that has an initial value associated with it. Instances of this
+ * class are always children of the class [VariableDeclarationList].
+ *
+ * > variableDeclaration ::=
+ * >     [SimpleIdentifier] ('=' [Expression])?
+ *
+ * TODO(paulberry): the grammar does not allow metadata to be associated with
+ * a VariableDeclaration, and currently we don't record comments for it either.
+ * Consider changing the class hierarchy so that [VariableDeclaration] does not
+ * extend [Declaration].
+ */
+class VariableDeclaration extends Declaration {
+  /**
+   * The name of the variable being declared.
+   */
+  SimpleIdentifier _name;
+
+  /**
+   * The equal sign separating the variable name from the initial value, or
+   * `null` if the initial value was not specified.
+   */
+  Token equals;
+
+  /**
+   * The expression used to compute the initial value for the variable, or
+   * `null` if the initial value was not specified.
+   */
+  Expression _initializer;
+
+  /**
+   * Initialize a newly created variable declaration. The [equals] and
+   * [initializer] can be `null` if there is no initializer.
+   */
+  VariableDeclaration(
+      SimpleIdentifier name, this.equals, Expression initializer)
+      : super(null, null) {
+    _name = _becomeParentOf(name);
+    _initializer = _becomeParentOf(initializer);
+  }
+
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(_name)
+    ..add(equals)
+    ..add(_initializer);
+
+  /**
+   * This overridden implementation of getDocumentationComment() looks in the
+   * grandparent node for dartdoc comments if no documentation is specifically
+   * available on the node.
+   */
+  @override
+  Comment get documentationComment {
+    Comment comment = super.documentationComment;
+    if (comment == null) {
+      if (parent != null && parent.parent != null) {
+        AstNode node = parent.parent;
+        if (node is AnnotatedNode) {
+          return node.documentationComment;
+        }
+      }
+    }
+    return comment;
+  }
+
+  @override
+  VariableElement get element =>
+      _name != null ? (_name.staticElement as VariableElement) : null;
+
+  @override
+  Token get endToken {
+    if (_initializer != null) {
+      return _initializer.endToken;
+    }
+    return _name.endToken;
+  }
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata => _name.beginToken;
+
+  /**
+   * Return the expression used to compute the initial value for the variable,
+   * or `null` if the initial value was not specified.
+   */
+  Expression get initializer => _initializer;
+
+  /**
+   * Set the expression used to compute the initial value for the variable to
+   * the given [expression].
+   */
+  void set initializer(Expression expression) {
+    _initializer = _becomeParentOf(expression);
+  }
+
+  /**
+   * Return `true` if this variable was declared with the 'const' modifier.
+   */
+  bool get isConst {
+    AstNode parent = this.parent;
+    return parent is VariableDeclarationList && parent.isConst;
+  }
+
+  /**
+   * Return `true` if this variable was declared with the 'final' modifier.
+   * Variables that are declared with the 'const' modifier will return `false`
+   * even though they are implicitly final.
+   */
+  bool get isFinal {
+    AstNode parent = this.parent;
+    return parent is VariableDeclarationList && parent.isFinal;
+  }
+
+  /**
+   * Return the name of the variable being declared.
+   */
+  SimpleIdentifier get name => _name;
+
+  /**
+   * Set the name of the variable being declared to the given [identifier].
+   */
+  void set name(SimpleIdentifier identifier) {
+    _name = _becomeParentOf(identifier);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitVariableDeclaration(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_name, visitor);
+    _safelyVisitChild(_initializer, visitor);
+  }
+}
+
+/**
+ * The declaration of one or more variables of the same type.
+ *
+ * > variableDeclarationList ::=
+ * >     finalConstVarOrType [VariableDeclaration] (',' [VariableDeclaration])*
+ * >
+ * > finalConstVarOrType ::=
+ * >   | 'final' [TypeName]?
+ * >   | 'const' [TypeName]?
+ * >   | 'var'
+ * >   | [TypeName]
+ */
+class VariableDeclarationList extends AnnotatedNode {
+  /**
+   * The token representing the 'final', 'const' or 'var' keyword, or `null` if
+   * no keyword was included.
+   */
+  Token keyword;
+
+  /**
+   * The type of the variables being declared, or `null` if no type was provided.
+   */
+  TypeName _type;
+
+  /**
+   * A list containing the individual variables being declared.
+   */
+  NodeList<VariableDeclaration> _variables;
+
+  /**
+   * Initialize a newly created variable declaration list. Either or both of the
+   * [comment] and [metadata] can be `null` if the variable list does not have
+   * the corresponding attribute. The [keyword] can be `null` if a type was
+   * specified. The [type] must be `null` if the keyword is 'var'.
+   */
+  VariableDeclarationList(Comment comment, List<Annotation> metadata,
+      this.keyword, TypeName type, List<VariableDeclaration> variables)
+      : super(comment, metadata) {
+    _type = _becomeParentOf(type);
+    _variables = new NodeList<VariableDeclaration>(this, variables);
+  }
+
+  /**
+   * TODO(paulberry): include commas.
+   */
+  @override
+  Iterable get childEntities => super._childEntities
+    ..add(keyword)
+    ..add(_type)
+    ..addAll(_variables);
+
+  @override
+  Token get endToken => _variables.endToken;
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata {
+    if (keyword != null) {
+      return keyword;
+    } else if (_type != null) {
+      return _type.beginToken;
+    }
+    return _variables.beginToken;
+  }
+
+  /**
+   * Return `true` if the variables in this list were declared with the 'const'
+   * modifier.
+   */
+  bool get isConst => keyword is KeywordToken &&
+      (keyword as KeywordToken).keyword == Keyword.CONST;
+
+  /**
+   * Return `true` if the variables in this list were declared with the 'final'
+   * modifier. Variables that are declared with the 'const' modifier will return
+   * `false` even though they are implicitly final. (In other words, this is a
+   * syntactic check rather than a semantic check.)
+   */
+  bool get isFinal => keyword is KeywordToken &&
+      (keyword as KeywordToken).keyword == Keyword.FINAL;
+
+  /**
+   * Return the type of the variables being declared, or `null` if no type was
+   * provided.
+   */
+  TypeName get type => _type;
+
+  /**
+   * Set the type of the variables being declared to the given [typeName].
+   */
+  void set type(TypeName typeName) {
+    _type = _becomeParentOf(typeName);
+  }
+
+  /**
+   * Return a list containing the individual variables being declared.
+   */
+  NodeList<VariableDeclaration> get variables => _variables;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitVariableDeclarationList(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    super.visitChildren(visitor);
+    _safelyVisitChild(_type, visitor);
+    _variables.accept(visitor);
+  }
+}
+
+/**
+ * A list of variables that are being declared in a context where a statement is
+ * required.
+ *
+ * > variableDeclarationStatement ::=
+ * >     [VariableDeclarationList] ';'
+ */
+class VariableDeclarationStatement extends Statement {
+  /**
+   * The variables being declared.
+   */
+  VariableDeclarationList _variableList;
+
+  /**
+   * The semicolon terminating the statement.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created variable declaration statement.
+   */
+  VariableDeclarationStatement(
+      VariableDeclarationList variableList, this.semicolon) {
+    _variableList = _becomeParentOf(variableList);
+  }
+
+  @override
+  Token get beginToken => _variableList.beginToken;
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(_variableList)
+    ..add(semicolon);
+
+  @override
+  Token get endToken => semicolon;
+
+  /**
+   * Return the variables being declared.
+   */
+  VariableDeclarationList get variables => _variableList;
+
+  /**
+   * Set the variables being declared to the given list of [variables].
+   */
+  void set variables(VariableDeclarationList variables) {
+    _variableList = _becomeParentOf(variables);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitVariableDeclarationStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_variableList, visitor);
+  }
+}
+
+/**
+ * A while statement.
+ *
+ * > whileStatement ::=
+ * >     'while' '(' [Expression] ')' [Statement]
+ */
+class WhileStatement extends Statement {
+  /**
+   * The token representing the 'while' keyword.
+   */
+  Token whileKeyword;
+
+  /**
+   * The left parenthesis.
+   */
+  Token leftParenthesis;
+
+  /**
+   * The expression used to determine whether to execute the body of the loop.
+   */
+  Expression _condition;
+
+  /**
+   * The right parenthesis.
+   */
+  Token rightParenthesis;
+
+  /**
+   * The body of the loop.
+   */
+  Statement _body;
+
+  /**
+   * Initialize a newly created while statement.
+   */
+  WhileStatement(this.whileKeyword, this.leftParenthesis, Expression condition,
+      this.rightParenthesis, Statement body) {
+    _condition = _becomeParentOf(condition);
+    _body = _becomeParentOf(body);
+  }
+
+  @override
+  Token get beginToken => whileKeyword;
+
+  /**
+   * Return the body of the loop.
+   */
+  Statement get body => _body;
+
+  /**
+   * Set the body of the loop to the given [statement].
+   */
+  void set body(Statement statement) {
+    _body = _becomeParentOf(statement);
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(whileKeyword)
+    ..add(leftParenthesis)
+    ..add(_condition)
+    ..add(rightParenthesis)
+    ..add(_body);
+
+  /**
+   * Return the expression used to determine whether to execute the body of the
+   * loop.
+   */
+  Expression get condition => _condition;
+
+  /**
+   * Set the expression used to determine whether to execute the body of the
+   * loop to the given [expression].
+   */
+  void set condition(Expression expression) {
+    _condition = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get endToken => _body.endToken;
+
+  /**
+   * Return the token representing the 'while' keyword.
+   */
+  @deprecated // Use "this.whileKeyword"
+  Token get keyword => whileKeyword;
+
+  /**
+   * Set the token representing the 'while' keyword to the given [token].
+   */
+  @deprecated // Use "this.whileKeyword"
+  set keyword(Token token) {
+    whileKeyword = token;
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitWhileStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_condition, visitor);
+    _safelyVisitChild(_body, visitor);
+  }
+}
+
+/**
+ * The with clause in a class declaration.
+ *
+ * > withClause ::=
+ * >     'with' [TypeName] (',' [TypeName])*
+ */
+class WithClause extends AstNode {
+  /**
+   * The token representing the 'with' keyword.
+   */
+  Token withKeyword;
+
+  /**
+   * The names of the mixins that were specified.
+   */
+  NodeList<TypeName> _mixinTypes;
+
+  /**
+   * Initialize a newly created with clause.
+   */
+  WithClause(this.withKeyword, List<TypeName> mixinTypes) {
+    _mixinTypes = new NodeList<TypeName>(this, mixinTypes);
+  }
+
+  @override
+  Token get beginToken => withKeyword;
+
+  /**
+   * TODO(paulberry): add commas.
+   */
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(withKeyword)
+    ..addAll(_mixinTypes);
+
+  @override
+  Token get endToken => _mixinTypes.endToken;
+
+  /**
+   * Set the token representing the 'with' keyword to the given [token].
+   */
+  @deprecated // Use "this.withKeyword"
+  void set mixinKeyword(Token token) {
+    this.withKeyword = token;
+  }
+
+  /**
+   * Return the names of the mixins that were specified.
+   */
+  NodeList<TypeName> get mixinTypes => _mixinTypes;
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitWithClause(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _mixinTypes.accept(visitor);
+  }
+}
+
+/**
+ * A yield statement.
+ *
+ * > yieldStatement ::=
+ * >     'yield' '*'? [Expression] ‘;’
+ */
+class YieldStatement extends Statement {
+  /**
+   * The 'yield' keyword.
+   */
+  Token yieldKeyword;
+
+  /**
+   * The star optionally following the 'yield' keyword.
+   */
+  Token star;
+
+  /**
+   * The expression whose value will be yielded.
+   */
+  Expression _expression;
+
+  /**
+   * The semicolon following the expression.
+   */
+  Token semicolon;
+
+  /**
+   * Initialize a newly created yield expression. The [star] can be `null` if no
+   * star was provided.
+   */
+  YieldStatement(
+      this.yieldKeyword, this.star, Expression expression, this.semicolon) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  Token get beginToken {
+    if (yieldKeyword != null) {
+      return yieldKeyword;
+    }
+    return _expression.beginToken;
+  }
+
+  @override
+  Iterable get childEntities => new ChildEntities()
+    ..add(yieldKeyword)
+    ..add(star)
+    ..add(_expression)
+    ..add(semicolon);
+
+  @override
+  Token get endToken {
+    if (semicolon != null) {
+      return semicolon;
+    }
+    return _expression.endToken;
+  }
+
+  /**
+   * Return the expression whose value will be yielded.
+   */
+  Expression get expression => _expression;
+
+  /**
+   * Set the expression whose value will be yielded to the given [expression].
+   */
+  void set expression(Expression expression) {
+    _expression = _becomeParentOf(expression);
+  }
+
+  @override
+  accept(AstVisitor visitor) => visitor.visitYieldStatement(this);
+
+  @override
+  void visitChildren(AstVisitor visitor) {
+    _safelyVisitChild(_expression, visitor);
+  }
+}
diff --git a/analyzer/lib/src/generated/constant.dart b/analyzer/lib/src/generated/constant.dart
new file mode 100644
index 0000000..7c866c1
--- /dev/null
+++ b/analyzer/lib/src/generated/constant.dart
@@ -0,0 +1,5111 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.constant;
+
+import 'dart:collection';
+
+import 'package:analyzer/src/generated/utilities_general.dart';
+
+import 'ast.dart';
+import 'element.dart';
+import 'engine.dart' show AnalysisEngine, RecordingErrorListener;
+import 'error.dart';
+import 'java_core.dart';
+import 'resolver.dart' show TypeProvider;
+import 'scanner.dart' show Token, TokenType;
+import 'source.dart' show Source;
+import 'utilities_collection.dart';
+import 'utilities_dart.dart' show ParameterKind;
+
+/**
+ * The state of an object representing a boolean value.
+ */
+class BoolState extends InstanceState {
+  /**
+   * An instance representing the boolean value 'false'.
+   */
+  static BoolState FALSE_STATE = new BoolState(false);
+
+  /**
+   * An instance representing the boolean value 'true'.
+   */
+  static BoolState TRUE_STATE = new BoolState(true);
+
+  /**
+   * A state that can be used to represent a boolean whose value is not known.
+   */
+  static BoolState UNKNOWN_VALUE = new BoolState(null);
+
+  /**
+   * The value of this instance.
+   */
+  final bool value;
+
+  /**
+   * Initialize a newly created state to represent the given [value].
+   */
+  BoolState(this.value);
+
+  @override
+  bool get hasExactValue => true;
+
+  @override
+  int get hashCode => value == null ? 0 : (value ? 2 : 3);
+
+  @override
+  bool get isBool => true;
+
+  @override
+  bool get isBoolNumStringOrNull => true;
+
+  @override
+  bool get isUnknown => value == null;
+
+  @override
+  String get typeName => "bool";
+
+  @override
+  bool operator ==(Object object) =>
+      object is BoolState && identical(value, object.value);
+
+  @override
+  BoolState convertToBool() => this;
+
+  @override
+  StringState convertToString() {
+    if (value == null) {
+      return StringState.UNKNOWN_VALUE;
+    }
+    return new StringState(value ? "true" : "false");
+  }
+
+  @override
+  BoolState equalEqual(InstanceState rightOperand) {
+    assertBoolNumStringOrNull(rightOperand);
+    return isIdentical(rightOperand);
+  }
+
+  @override
+  BoolState isIdentical(InstanceState rightOperand) {
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is BoolState) {
+      bool rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return BoolState.from(identical(value, rightValue));
+    } else if (rightOperand is DynamicState) {
+      return UNKNOWN_VALUE;
+    }
+    return FALSE_STATE;
+  }
+
+  @override
+  BoolState logicalAnd(InstanceState rightOperand) {
+    assertBool(rightOperand);
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    return value ? rightOperand.convertToBool() : FALSE_STATE;
+  }
+
+  @override
+  BoolState logicalNot() {
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    return value ? FALSE_STATE : TRUE_STATE;
+  }
+
+  @override
+  BoolState logicalOr(InstanceState rightOperand) {
+    assertBool(rightOperand);
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    return value ? TRUE_STATE : rightOperand.convertToBool();
+  }
+
+  @override
+  String toString() => value == null ? "-unknown-" : (value ? "true" : "false");
+
+  /**
+   * Return the boolean state representing the given boolean [value].
+   */
+  static BoolState from(bool value) =>
+      value ? BoolState.TRUE_STATE : BoolState.FALSE_STATE;
+}
+
+/**
+ * An [AstCloner] that copies the necessary information from the AST to allow
+ * constants to be evaluated.
+ */
+class ConstantAstCloner extends AstCloner {
+  ConstantAstCloner() : super(true);
+
+  @override
+  InstanceCreationExpression visitInstanceCreationExpression(
+      InstanceCreationExpression node) {
+    InstanceCreationExpression expression =
+        super.visitInstanceCreationExpression(node);
+    expression.constantHandle = node.constantHandle;
+    return expression;
+  }
+
+  @override
+  RedirectingConstructorInvocation visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    RedirectingConstructorInvocation invocation =
+        super.visitRedirectingConstructorInvocation(node);
+    invocation.staticElement = node.staticElement;
+    return invocation;
+  }
+
+  @override
+  SimpleIdentifier visitSimpleIdentifier(SimpleIdentifier node) {
+    SimpleIdentifier identifier = super.visitSimpleIdentifier(node);
+    identifier.staticElement = node.staticElement;
+    return identifier;
+  }
+
+  @override
+  SuperConstructorInvocation visitSuperConstructorInvocation(
+      SuperConstructorInvocation node) {
+    SuperConstructorInvocation invocation =
+        super.visitSuperConstructorInvocation(node);
+    invocation.staticElement = node.staticElement;
+    return invocation;
+  }
+}
+
+/**
+ * Instances of the class `ConstantEvaluator` evaluate constant expressions to
+ * produce their compile-time value. According to the Dart Language
+ * Specification:
+ * <blockquote>
+ * A constant expression is one of the following:
+ * * A literal number.
+ * * A literal boolean.
+ * * A literal string where any interpolated expression is a compile-time
+ *   constant that evaluates to a numeric, string or boolean value or to
+ *   <b>null</b>.
+ * * A literal symbol.
+ * * <b>null</b>.
+ * * A qualified reference to a static constant variable.
+ * * An identifier expression that denotes a constant variable, class or type
+ *   alias.
+ * * A constant constructor invocation.
+ * * A constant list literal.
+ * * A constant map literal.
+ * * A simple or qualified identifier denoting a top-level function or a static
+ *   method.
+ * * A parenthesized expression <i>(e)</i> where <i>e</i> is a constant
+ *   expression.
+ * * An expression of the form <i>identical(e<sub>1</sub>, e<sub>2</sub>)</i>
+ *   where <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> are constant
+ *   expressions and <i>identical()</i> is statically bound to the predefined
+ *   dart function <i>identical()</i> discussed above.
+ * * An expression of one of the forms <i>e<sub>1</sub> == e<sub>2</sub></i> or
+ *   <i>e<sub>1</sub> != e<sub>2</sub></i> where <i>e<sub>1</sub></i> and
+ *   <i>e<sub>2</sub></i> are constant expressions that evaluate to a numeric,
+ *   string or boolean value.
+ * * An expression of one of the forms <i>!e</i>, <i>e<sub>1</sub> &amp;&amp;
+ *   e<sub>2</sub></i> or <i>e<sub>1</sub> || e<sub>2</sub></i>, where <i>e</i>,
+ *   <i>e1</sub></i> and <i>e2</sub></i> are constant expressions that evaluate
+ *   to a boolean value.
+ * * An expression of one of the forms <i>~e</i>, <i>e<sub>1</sub> ^
+ *   e<sub>2</sub></i>, <i>e<sub>1</sub> &amp; e<sub>2</sub></i>,
+ *   <i>e<sub>1</sub> | e<sub>2</sub></i>, <i>e<sub>1</sub> &gt;&gt;
+ *   e<sub>2</sub></i> or <i>e<sub>1</sub> &lt;&lt; e<sub>2</sub></i>, where
+ *   <i>e</i>, <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> are constant
+ *   expressions that evaluate to an integer value or to <b>null</b>.
+ * * An expression of one of the forms <i>-e</i>, <i>e<sub>1</sub> +
+ *   e<sub>2</sub></i>, <i>e<sub>1</sub> -e<sub>2</sub></i>, <i>e<sub>1</sub> *
+ *   e<sub>2</sub></i>, <i>e<sub>1</sub> / e<sub>2</sub></i>, <i>e<sub>1</sub>
+ *   ~/ e<sub>2</sub></i>, <i>e<sub>1</sub> &gt; e<sub>2</sub></i>,
+ *   <i>e<sub>1</sub> &lt; e<sub>2</sub></i>, <i>e<sub>1</sub> &gt;=
+ *   e<sub>2</sub></i>, <i>e<sub>1</sub> &lt;= e<sub>2</sub></i> or
+ *   <i>e<sub>1</sub> % e<sub>2</sub></i>, where <i>e</i>, <i>e<sub>1</sub></i>
+ *   and <i>e<sub>2</sub></i> are constant expressions that evaluate to a
+ *   numeric value or to <b>null</b>.
+ * * An expression of the form <i>e<sub>1</sub> ? e<sub>2</sub> :
+ *   e<sub>3</sub></i> where <i>e<sub>1</sub></i>, <i>e<sub>2</sub></i> and
+ *   <i>e<sub>3</sub></i> are constant expressions, and <i>e<sub>1</sub></i>
+ *   evaluates to a boolean value.
+ * </blockquote>
+ */
+class ConstantEvaluator {
+  /**
+   * The source containing the expression(s) that will be evaluated.
+   */
+  final Source _source;
+
+  /**
+   * The type provider used to access the known types.
+   */
+  final TypeProvider _typeProvider;
+
+  /**
+   * Initialize a newly created evaluator to evaluate expressions in the given
+   * [source]. The [typeProvider] is the type provider used to access known
+   * types.
+   */
+  ConstantEvaluator(this._source, this._typeProvider);
+
+  EvaluationResult evaluate(Expression expression) {
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    ErrorReporter errorReporter = new ErrorReporter(errorListener, _source);
+    DartObjectImpl result = expression
+        .accept(new ConstantVisitor.con1(_typeProvider, errorReporter));
+    if (result != null) {
+      return EvaluationResult.forValue(result);
+    }
+    return EvaluationResult.forErrors(errorListener.errors);
+  }
+}
+
+/**
+ * A visitor used to traverse the AST structures of all of the compilation units
+ * being resolved and build tables of the constant variables, constant
+ * constructors, constant constructor invocations, and annotations found in
+ * those compilation units.
+ */
+class ConstantFinder extends RecursiveAstVisitor<Object> {
+  /**
+   * A table mapping constant variable elements to the declarations of those
+   * variables.
+   */
+  final HashMap<PotentiallyConstVariableElement, VariableDeclaration> variableMap =
+      new HashMap<PotentiallyConstVariableElement, VariableDeclaration>();
+
+  /**
+   * A table mapping constant constructors to the declarations of those
+   * constructors.
+   */
+  final HashMap<ConstructorElement, ConstructorDeclaration> constructorMap =
+      new HashMap<ConstructorElement, ConstructorDeclaration>();
+
+  /**
+   * A collection of constant constructor invocations.
+   */
+  final List<InstanceCreationExpression> constructorInvocations =
+      new List<InstanceCreationExpression>();
+
+  /**
+   * A collection of annotations.
+   */
+  final List<Annotation> annotations = <Annotation>[];
+
+  @override
+  Object visitAnnotation(Annotation node) {
+    super.visitAnnotation(node);
+    annotations.add(node);
+    return null;
+  }
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    super.visitConstructorDeclaration(node);
+    if (node.constKeyword != null) {
+      ConstructorElement element = node.element;
+      if (element != null) {
+        constructorMap[element] = node;
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitInstanceCreationExpression(InstanceCreationExpression node) {
+    super.visitInstanceCreationExpression(node);
+    if (node.isConst) {
+      constructorInvocations.add(node);
+    }
+    return null;
+  }
+
+  @override
+  Object visitVariableDeclaration(VariableDeclaration node) {
+    super.visitVariableDeclaration(node);
+    Expression initializer = node.initializer;
+    if (initializer != null && node.isConst) {
+      if (node.element != null) {
+        variableMap[node.element as PotentiallyConstVariableElement] = node;
+      }
+    }
+    return null;
+  }
+}
+
+/**
+ * An object used to compute the values of constant variables and constant
+ * constructor invocations in one or more compilation units. The expected usage
+ * pattern is for the compilation units to be added to this computer using the
+ * method [add] and then for the method [computeValues] to be invoked exactly
+ * once. Any use of an instance after invoking the method [computeValues] will
+ * result in unpredictable behavior.
+ */
+class ConstantValueComputer {
+  /**
+   * Parameter to "fromEnvironment" methods that denotes the default value.
+   */
+  static String _DEFAULT_VALUE_PARAM = "defaultValue";
+
+  /**
+   * Source of RegExp matching declarable operator names.
+   * From sdk/lib/internal/symbol.dart.
+   */
+  static String _OPERATOR_RE =
+      "(?:[\\-+*/%&|^]|\\[\\]=?|==|~/?|<[<=]?|>[>=]?|unary-)";
+
+  /**
+   * Source of RegExp matching any public identifier.
+   * From sdk/lib/internal/symbol.dart.
+   */
+  static String _PUBLIC_IDENTIFIER_RE =
+      "(?!${ConstantValueComputer._RESERVED_WORD_RE}\\b(?!\\\$))[a-zA-Z\$][\\w\$]*";
+
+  /**
+   * Source of RegExp matching Dart reserved words.
+   * From sdk/lib/internal/symbol.dart.
+   */
+  static String _RESERVED_WORD_RE =
+      "(?:assert|break|c(?:a(?:se|tch)|lass|on(?:st|tinue))|d(?:efault|o)|e(?:lse|num|xtends)|f(?:alse|inal(?:ly)?|or)|i[fns]|n(?:ew|ull)|ret(?:hrow|urn)|s(?:uper|witch)|t(?:h(?:is|row)|r(?:ue|y))|v(?:ar|oid)|w(?:hile|ith))";
+
+  /**
+   * RegExp that validates a non-empty non-private symbol.
+   * From sdk/lib/internal/symbol.dart.
+   */
+  static RegExp _PUBLIC_SYMBOL_PATTERN = new RegExp(
+      "^(?:${ConstantValueComputer._OPERATOR_RE}\$|$_PUBLIC_IDENTIFIER_RE(?:=?\$|[.](?!\$)))+?\$");
+
+  /**
+   * The type provider used to access the known types.
+   */
+  TypeProvider typeProvider;
+
+  /**
+   * The object used to find constant variables and constant constructor
+   * invocations in the compilation units that were added.
+   */
+  ConstantFinder _constantFinder = new ConstantFinder();
+
+  /**
+   * A graph in which the nodes are the constants, and the edges are from each
+   * constant to the other constants that are referenced by it.
+   */
+  DirectedGraph<AstNode> referenceGraph = new DirectedGraph<AstNode>();
+
+  /**
+   * A table mapping constant variables to the declarations of those variables.
+   */
+  HashMap<PotentiallyConstVariableElement, VariableDeclaration> _variableDeclarationMap;
+
+  /**
+   * A table mapping constant constructors to the declarations of those
+   * constructors.
+   */
+  HashMap<ConstructorElement, ConstructorDeclaration> constructorDeclarationMap;
+
+  /**
+   * A collection of constant constructor invocations.
+   */
+  List<InstanceCreationExpression> _constructorInvocations;
+
+  /**
+   * A collection of annotations.
+   */
+  List<Annotation> _annotations;
+
+  /**
+   * The set of variables declared on the command line using '-D'.
+   */
+  final DeclaredVariables _declaredVariables;
+
+  /**
+   * Initialize a newly created constant value computer. The [typeProvider] is
+   * the type provider used to access known types. The [declaredVariables] is
+   * the set of variables declared on the command line using '-D'.
+   */
+  ConstantValueComputer(TypeProvider typeProvider, this._declaredVariables) {
+    this.typeProvider = typeProvider;
+  }
+
+  /**
+   * Add the constants in the given compilation [unit] to the list of constants
+   * whose value needs to be computed.
+   */
+  void add(CompilationUnit unit) {
+    unit.accept(_constantFinder);
+  }
+
+  /**
+   * This method is called just before computing the constant value associated
+   * with [constNode]. Unit tests will override this method to introduce
+   * additional error checking.
+   */
+  void beforeComputeValue(AstNode constNode) {}
+
+  /**
+   * This method is called just before getting the constant initializers
+   * associated with the [constructor]. Unit tests will override this method to
+   * introduce additional error checking.
+   */
+  void beforeGetConstantInitializers(ConstructorElement constructor) {}
+
+  /**
+   * This method is called just before getting a parameter's default value. Unit
+   * tests will override this method to introduce additional error checking.
+   */
+  void beforeGetParameterDefault(ParameterElement parameter) {}
+
+  /**
+   * Compute values for all of the constants in the compilation units that were
+   * added.
+   */
+  void computeValues() {
+    _variableDeclarationMap = _constantFinder.variableMap;
+    constructorDeclarationMap = _constantFinder.constructorMap;
+    _constructorInvocations = _constantFinder.constructorInvocations;
+    _annotations = _constantFinder.annotations;
+    _variableDeclarationMap.values.forEach((VariableDeclaration declaration) {
+      ReferenceFinder referenceFinder = new ReferenceFinder(declaration,
+          referenceGraph, _variableDeclarationMap, constructorDeclarationMap);
+      referenceGraph.addNode(declaration);
+      declaration.initializer.accept(referenceFinder);
+    });
+    constructorDeclarationMap.forEach((ConstructorElementImpl element,
+        ConstructorDeclaration declaration) {
+      element.isCycleFree = false;
+      ConstructorElement redirectedConstructor =
+          _getConstRedirectedConstructor(element);
+      if (redirectedConstructor != null) {
+        ConstructorElement redirectedConstructorBase =
+            _getConstructorBase(redirectedConstructor);
+        ConstructorDeclaration redirectedConstructorDeclaration =
+            findConstructorDeclaration(redirectedConstructorBase);
+        referenceGraph.addEdge(declaration, redirectedConstructorDeclaration);
+        return;
+      }
+      ReferenceFinder referenceFinder = new ReferenceFinder(declaration,
+          referenceGraph, _variableDeclarationMap, constructorDeclarationMap);
+      referenceGraph.addNode(declaration);
+      bool superInvocationFound = false;
+      NodeList<ConstructorInitializer> initializers = declaration.initializers;
+      for (ConstructorInitializer initializer in initializers) {
+        if (initializer is SuperConstructorInvocation) {
+          superInvocationFound = true;
+        }
+        initializer.accept(referenceFinder);
+      }
+      if (!superInvocationFound) {
+        // No explicit superconstructor invocation found, so we need to
+        // manually insert a reference to the implicit superconstructor.
+        InterfaceType superclass =
+            (element.returnType as InterfaceType).superclass;
+        if (superclass != null && !superclass.isObject) {
+          ConstructorElement unnamedConstructor =
+              superclass.element.unnamedConstructor;
+          ConstructorDeclaration superConstructorDeclaration =
+              findConstructorDeclaration(unnamedConstructor);
+          if (superConstructorDeclaration != null) {
+            referenceGraph.addEdge(declaration, superConstructorDeclaration);
+          }
+        }
+      }
+      for (FormalParameter parameter in declaration.parameters.parameters) {
+        referenceGraph.addNode(parameter);
+        referenceGraph.addEdge(declaration, parameter);
+        if (parameter is DefaultFormalParameter) {
+          Expression defaultValue = parameter.defaultValue;
+          if (defaultValue != null) {
+            ReferenceFinder parameterReferenceFinder = new ReferenceFinder(
+                parameter, referenceGraph, _variableDeclarationMap,
+                constructorDeclarationMap);
+            defaultValue.accept(parameterReferenceFinder);
+          }
+        }
+      }
+    });
+    for (InstanceCreationExpression expression in _constructorInvocations) {
+      referenceGraph.addNode(expression);
+      ConstructorElement constructor = expression.staticElement;
+      if (constructor == null) {
+        continue;
+      }
+      ConstructorDeclaration declaration =
+          findConstructorDeclaration(constructor);
+      // An instance creation expression depends both on the constructor and
+      // the arguments passed to it.
+      ReferenceFinder referenceFinder = new ReferenceFinder(expression,
+          referenceGraph, _variableDeclarationMap, constructorDeclarationMap);
+      if (declaration != null) {
+        referenceGraph.addEdge(expression, declaration);
+      }
+      expression.argumentList.accept(referenceFinder);
+    }
+    List<List<AstNode>> topologicalSort =
+        referenceGraph.computeTopologicalSort();
+    for (List<AstNode> constantsInCycle in topologicalSort) {
+      if (constantsInCycle.length == 1) {
+        _computeValueFor(constantsInCycle[0]);
+      } else {
+        for (AstNode constant in constantsInCycle) {
+          _generateCycleError(constantsInCycle, constant);
+        }
+      }
+    }
+    // Since no constant can depend on an annotation, we don't waste time
+    // including them in the topological sort.  We just process all the
+    // annotations after all other constants are finished.
+    for (Annotation annotation in _annotations) {
+      _computeValueFor(annotation);
+    }
+  }
+
+  /**
+   * Create the ConstantVisitor used to evaluate constants. Unit tests will
+   * override this method to introduce additional error checking.
+   */
+  ConstantVisitor createConstantVisitor(ErrorReporter errorReporter) =>
+      new ConstantVisitor.con1(typeProvider, errorReporter);
+
+  ConstructorDeclaration findConstructorDeclaration(
+          ConstructorElement constructor) =>
+      constructorDeclarationMap[_getConstructorBase(constructor)];
+
+  /**
+   * Check that the arguments to a call to fromEnvironment() are correct. The
+   * [arguments] are the AST nodes of the arguments. The [argumentValues] are
+   * the values of the unnamed arguments. The [namedArgumentValues] are the
+   * values of the named arguments. The [expectedDefaultValueType] is the
+   * allowed type of the "defaultValue" parameter (if present). Note:
+   * "defaultValue" is always allowed to be null. Return `true` if the arguments
+   * are correct, `false` if there is an error.
+   */
+  bool _checkFromEnvironmentArguments(NodeList<Expression> arguments,
+      List<DartObjectImpl> argumentValues,
+      HashMap<String, DartObjectImpl> namedArgumentValues,
+      InterfaceType expectedDefaultValueType) {
+    int argumentCount = arguments.length;
+    if (argumentCount < 1 || argumentCount > 2) {
+      return false;
+    }
+    if (arguments[0] is NamedExpression) {
+      return false;
+    }
+    if (!identical(argumentValues[0].type, typeProvider.stringType)) {
+      return false;
+    }
+    if (argumentCount == 2) {
+      if (arguments[1] is! NamedExpression) {
+        return false;
+      }
+      if (!((arguments[1] as NamedExpression).name.label.name ==
+          _DEFAULT_VALUE_PARAM)) {
+        return false;
+      }
+      ParameterizedType defaultValueType =
+          namedArgumentValues[_DEFAULT_VALUE_PARAM].type;
+      if (!(identical(defaultValueType, expectedDefaultValueType) ||
+          identical(defaultValueType, typeProvider.nullType))) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Check that the arguments to a call to Symbol() are correct. The [arguments]
+   * are the AST nodes of the arguments. The [argumentValues] are the values of
+   * the unnamed arguments. The [namedArgumentValues] are the values of the
+   * named arguments. Return `true` if the arguments are correct, `false` if
+   * there is an error.
+   */
+  bool _checkSymbolArguments(NodeList<Expression> arguments,
+      List<DartObjectImpl> argumentValues,
+      HashMap<String, DartObjectImpl> namedArgumentValues) {
+    if (arguments.length != 1) {
+      return false;
+    }
+    if (arguments[0] is NamedExpression) {
+      return false;
+    }
+    if (!identical(argumentValues[0].type, typeProvider.stringType)) {
+      return false;
+    }
+    String name = argumentValues[0].stringValue;
+    return isValidPublicSymbol(name);
+  }
+
+  /**
+   * Compute a value for the given [constNode].
+   */
+  void _computeValueFor(AstNode constNode) {
+    beforeComputeValue(constNode);
+    if (constNode is VariableDeclaration) {
+      VariableElement element = constNode.element;
+      RecordingErrorListener errorListener = new RecordingErrorListener();
+      ErrorReporter errorReporter =
+          new ErrorReporter(errorListener, element.source);
+      DartObjectImpl dartObject =
+          (element as PotentiallyConstVariableElement).constantInitializer
+              .accept(createConstantVisitor(errorReporter));
+      if (dartObject != null) {
+        if (!_runtimeTypeMatch(dartObject, element.type)) {
+          errorReporter.reportErrorForElement(
+              CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH, element, [
+            dartObject.type,
+            element.type
+          ]);
+        }
+      }
+      (element as VariableElementImpl).evaluationResult =
+          new EvaluationResultImpl.con2(dartObject, errorListener.errors);
+    } else if (constNode is InstanceCreationExpression) {
+      InstanceCreationExpression expression = constNode;
+      ConstructorElement constructor = expression.staticElement;
+      if (constructor == null) {
+        // Couldn't resolve the constructor so we can't compute a value.
+        // No problem - the error has already been reported.
+        // But we still need to store an evaluation result.
+        expression.constantHandle.evaluationResult =
+            new EvaluationResultImpl.con1(null);
+        return;
+      }
+      RecordingErrorListener errorListener = new RecordingErrorListener();
+      CompilationUnit sourceCompilationUnit =
+          expression.getAncestor((node) => node is CompilationUnit);
+      ErrorReporter errorReporter = new ErrorReporter(
+          errorListener, sourceCompilationUnit.element.source);
+      ConstantVisitor constantVisitor = createConstantVisitor(errorReporter);
+      DartObjectImpl result = _evaluateConstructorCall(constNode,
+          expression.argumentList.arguments, constructor, constantVisitor,
+          errorReporter);
+      expression.constantHandle.evaluationResult =
+          new EvaluationResultImpl.con2(result, errorListener.errors);
+    } else if (constNode is ConstructorDeclaration) {
+      // No evaluation needs to be done; constructor declarations are only in
+      // the dependency graph to ensure that any constants referred to in
+      // initializer lists and parameter defaults are evaluated before
+      // invocations of the constructor.  However we do need to annotate the
+      // element as being free of constant evaluation cycles so that later code
+      // will know that it is safe to evaluate.
+      ConstructorElementImpl constructor = constNode.element;
+      constructor.isCycleFree = true;
+    } else if (constNode is FormalParameter) {
+      if (constNode is DefaultFormalParameter) {
+        DefaultFormalParameter parameter = constNode;
+        ParameterElement element = parameter.element;
+        Expression defaultValue = parameter.defaultValue;
+        if (defaultValue != null) {
+          RecordingErrorListener errorListener = new RecordingErrorListener();
+          ErrorReporter errorReporter =
+              new ErrorReporter(errorListener, element.source);
+          DartObjectImpl dartObject =
+              defaultValue.accept(createConstantVisitor(errorReporter));
+          (element as ParameterElementImpl).evaluationResult =
+              new EvaluationResultImpl.con2(dartObject, errorListener.errors);
+        }
+      }
+    } else if (constNode is Annotation) {
+      ElementAnnotationImpl elementAnnotation = constNode.elementAnnotation;
+      // elementAnnotation is null if the annotation couldn't be resolved, in
+      // which case we skip it.
+      if (elementAnnotation != null) {
+        Element element = elementAnnotation.element;
+        if (element is PropertyAccessorElement &&
+            element.variable is VariableElementImpl) {
+          // The annotation is a reference to a compile-time constant variable.
+          // Just copy the evaluation result.
+          VariableElementImpl variableElement =
+              element.variable as VariableElementImpl;
+          elementAnnotation.evaluationResult = variableElement.evaluationResult;
+        } else if (element is ConstructorElementImpl &&
+            constNode.arguments != null) {
+          RecordingErrorListener errorListener = new RecordingErrorListener();
+          CompilationUnit sourceCompilationUnit =
+              constNode.getAncestor((node) => node is CompilationUnit);
+          ErrorReporter errorReporter = new ErrorReporter(
+              errorListener, sourceCompilationUnit.element.source);
+          ConstantVisitor constantVisitor =
+              createConstantVisitor(errorReporter);
+          DartObjectImpl result = _evaluateConstructorCall(constNode,
+              constNode.arguments.arguments, element, constantVisitor,
+              errorReporter);
+          elementAnnotation.evaluationResult =
+              new EvaluationResultImpl.con2(result, errorListener.errors);
+        } else {
+          // This may happen for invalid code (e.g. failing to pass arguments
+          // to an annotation which references a const constructor).  The error
+          // is detected elsewhere, so just silently ignore it here.
+          elementAnnotation.evaluationResult =
+              new EvaluationResultImpl.con1(null);
+        }
+      }
+    } else {
+      // Should not happen.
+      AnalysisEngine.instance.logger.logError(
+          "Constant value computer trying to compute the value of a node which is not a VariableDeclaration, InstanceCreationExpression, FormalParameter, or ConstructorDeclaration");
+      return;
+    }
+  }
+
+  /**
+   * Evaluate a call to fromEnvironment() on the bool, int, or String class. The
+   * [environmentValue] is the value fetched from the environment. The
+   * [builtInDefaultValue] is the value that should be used as the default if no
+   * "defaultValue" argument appears in [namedArgumentValues]. The
+   * [namedArgumentValues] are the values of the named parameters passed to
+   * fromEnvironment(). Return a [DartObjectImpl] object corresponding to the
+   * evaluated result.
+   */
+  DartObjectImpl _computeValueFromEnvironment(DartObject environmentValue,
+      DartObjectImpl builtInDefaultValue,
+      HashMap<String, DartObjectImpl> namedArgumentValues) {
+    DartObjectImpl value = environmentValue as DartObjectImpl;
+    if (value.isUnknown || value.isNull) {
+      // The name either doesn't exist in the environment or we couldn't parse
+      // the corresponding value.
+      // If the code supplied an explicit default, use it.
+      if (namedArgumentValues.containsKey(_DEFAULT_VALUE_PARAM)) {
+        value = namedArgumentValues[_DEFAULT_VALUE_PARAM];
+      } else if (value.isNull) {
+        // The code didn't supply an explicit default.
+        // The name exists in the environment but we couldn't parse the
+        // corresponding value.
+        // So use the built-in default value, because this is what the VM does.
+        value = builtInDefaultValue;
+      } else {
+        // The code didn't supply an explicit default.
+        // The name doesn't exist in the environment.
+        // The VM would use the built-in default value, but we don't want to do
+        // that for analysis because it's likely to lead to cascading errors.
+        // So just leave [value] in the unknown state.
+      }
+    }
+    return value;
+  }
+
+  DartObjectImpl _evaluateConstructorCall(AstNode node,
+      NodeList<Expression> arguments, ConstructorElement constructor,
+      ConstantVisitor constantVisitor, ErrorReporter errorReporter) {
+    if (!_getConstructorBase(constructor).isCycleFree) {
+      // It's not safe to evaluate this constructor, so bail out.
+      // TODO(paulberry): ensure that a reasonable error message is produced
+      // in this case, as well as other cases involving constant expression
+      // circularities (e.g. "compile-time constant expression depends on
+      // itself")
+      return new DartObjectImpl.validWithUnknownValue(constructor.returnType);
+    }
+    int argumentCount = arguments.length;
+    List<DartObjectImpl> argumentValues =
+        new List<DartObjectImpl>(argumentCount);
+    List<Expression> argumentNodes = new List<Expression>(argumentCount);
+    HashMap<String, DartObjectImpl> namedArgumentValues =
+        new HashMap<String, DartObjectImpl>();
+    HashMap<String, NamedExpression> namedArgumentNodes =
+        new HashMap<String, NamedExpression>();
+    for (int i = 0; i < argumentCount; i++) {
+      Expression argument = arguments[i];
+      if (argument is NamedExpression) {
+        String name = argument.name.label.name;
+        namedArgumentValues[name] =
+            constantVisitor._valueOf(argument.expression);
+        namedArgumentNodes[name] = argument;
+        argumentValues[i] = typeProvider.nullObject;
+      } else {
+        argumentValues[i] = constantVisitor._valueOf(argument);
+        argumentNodes[i] = argument;
+      }
+    }
+    constructor = _followConstantRedirectionChain(constructor);
+    InterfaceType definingClass = constructor.returnType as InterfaceType;
+    if (constructor.isFactory) {
+      // We couldn't find a non-factory constructor.
+      // See if it's because we reached an external const factory constructor
+      // that we can emulate.
+      if (constructor.name == "fromEnvironment") {
+        if (!_checkFromEnvironmentArguments(
+            arguments, argumentValues, namedArgumentValues, definingClass)) {
+          errorReporter.reportErrorForNode(
+              CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node);
+          return null;
+        }
+        String variableName =
+            argumentCount < 1 ? null : argumentValues[0].stringValue;
+        if (identical(definingClass, typeProvider.boolType)) {
+          DartObject valueFromEnvironment;
+          valueFromEnvironment =
+              _declaredVariables.getBool(typeProvider, variableName);
+          return _computeValueFromEnvironment(valueFromEnvironment,
+              new DartObjectImpl(typeProvider.boolType, BoolState.FALSE_STATE),
+              namedArgumentValues);
+        } else if (identical(definingClass, typeProvider.intType)) {
+          DartObject valueFromEnvironment;
+          valueFromEnvironment =
+              _declaredVariables.getInt(typeProvider, variableName);
+          return _computeValueFromEnvironment(valueFromEnvironment,
+              new DartObjectImpl(typeProvider.nullType, NullState.NULL_STATE),
+              namedArgumentValues);
+        } else if (identical(definingClass, typeProvider.stringType)) {
+          DartObject valueFromEnvironment;
+          valueFromEnvironment =
+              _declaredVariables.getString(typeProvider, variableName);
+          return _computeValueFromEnvironment(valueFromEnvironment,
+              new DartObjectImpl(typeProvider.nullType, NullState.NULL_STATE),
+              namedArgumentValues);
+        }
+      } else if (constructor.name == "" &&
+          identical(definingClass, typeProvider.symbolType) &&
+          argumentCount == 1) {
+        if (!_checkSymbolArguments(
+            arguments, argumentValues, namedArgumentValues)) {
+          errorReporter.reportErrorForNode(
+              CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node);
+          return null;
+        }
+        String argumentValue = argumentValues[0].stringValue;
+        return new DartObjectImpl(
+            definingClass, new SymbolState(argumentValue));
+      }
+      // Either it's an external const factory constructor that we can't
+      // emulate, or an error occurred (a cycle, or a const constructor trying
+      // to delegate to a non-const constructor).
+      // In the former case, the best we can do is consider it an unknown value.
+      // In the latter case, the error has already been reported, so considering
+      // it an unknown value will suppress further errors.
+      return new DartObjectImpl.validWithUnknownValue(definingClass);
+    }
+    beforeGetConstantInitializers(constructor);
+    ConstructorElementImpl constructorBase = _getConstructorBase(constructor);
+    List<ConstructorInitializer> initializers =
+        constructorBase.constantInitializers;
+    if (initializers == null) {
+      // This can happen in some cases where there are compile errors in the
+      // code being analyzed (for example if the code is trying to create a
+      // const instance using a non-const constructor, or the node we're
+      // visiting is involved in a cycle).  The error has already been reported,
+      // so consider it an unknown value to suppress further errors.
+      return new DartObjectImpl.validWithUnknownValue(definingClass);
+    }
+    HashMap<String, DartObjectImpl> fieldMap =
+        new HashMap<String, DartObjectImpl>();
+    HashMap<String, DartObjectImpl> parameterMap =
+        new HashMap<String, DartObjectImpl>();
+    List<ParameterElement> parameters = constructor.parameters;
+    int parameterCount = parameters.length;
+    for (int i = 0; i < parameterCount; i++) {
+      ParameterElement parameter = parameters[i];
+      ParameterElement baseParameter = parameter;
+      while (baseParameter is ParameterMember) {
+        baseParameter = (baseParameter as ParameterMember).baseElement;
+      }
+      DartObjectImpl argumentValue = null;
+      AstNode errorTarget = null;
+      if (baseParameter.parameterKind == ParameterKind.NAMED) {
+        argumentValue = namedArgumentValues[baseParameter.name];
+        errorTarget = namedArgumentNodes[baseParameter.name];
+      } else if (i < argumentCount) {
+        argumentValue = argumentValues[i];
+        errorTarget = argumentNodes[i];
+      }
+      if (errorTarget == null) {
+        // No argument node that we can direct error messages to, because we
+        // are handling an optional parameter that wasn't specified.  So just
+        // direct error messages to the constructor call.
+        errorTarget = node;
+      }
+      if (argumentValue == null && baseParameter is ParameterElementImpl) {
+        // The parameter is an optional positional parameter for which no value
+        // was provided, so use the default value.
+        beforeGetParameterDefault(baseParameter);
+        EvaluationResultImpl evaluationResult = baseParameter.evaluationResult;
+        if (evaluationResult == null) {
+          // No default was provided, so the default value is null.
+          argumentValue = typeProvider.nullObject;
+        } else if (evaluationResult.value != null) {
+          argumentValue = evaluationResult.value;
+        }
+      }
+      if (argumentValue != null) {
+        if (!_runtimeTypeMatch(argumentValue, parameter.type)) {
+          errorReporter.reportErrorForNode(
+              CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH,
+              errorTarget, [argumentValue.type, parameter.type]);
+        }
+        if (baseParameter.isInitializingFormal) {
+          FieldElement field = (parameter as FieldFormalParameterElement).field;
+          if (field != null) {
+            DartType fieldType = field.type;
+            if (fieldType != parameter.type) {
+              // We've already checked that the argument can be assigned to the
+              // parameter; we also need to check that it can be assigned to
+              // the field.
+              if (!_runtimeTypeMatch(argumentValue, fieldType)) {
+                errorReporter.reportErrorForNode(
+                    CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH,
+                    errorTarget, [argumentValue.type, fieldType]);
+              }
+            }
+            String fieldName = field.name;
+            fieldMap[fieldName] = argumentValue;
+          }
+        } else {
+          String name = baseParameter.name;
+          parameterMap[name] = argumentValue;
+        }
+      }
+    }
+    ConstantVisitor initializerVisitor =
+        new ConstantVisitor.con2(typeProvider, parameterMap, errorReporter);
+    String superName = null;
+    NodeList<Expression> superArguments = null;
+    for (ConstructorInitializer initializer in initializers) {
+      if (initializer is ConstructorFieldInitializer) {
+        ConstructorFieldInitializer constructorFieldInitializer = initializer;
+        Expression initializerExpression =
+            constructorFieldInitializer.expression;
+        DartObjectImpl evaluationResult =
+            initializerExpression.accept(initializerVisitor);
+        if (evaluationResult != null) {
+          String fieldName = constructorFieldInitializer.fieldName.name;
+          fieldMap[fieldName] = evaluationResult;
+          PropertyAccessorElement getter = definingClass.getGetter(fieldName);
+          if (getter != null) {
+            PropertyInducingElement field = getter.variable;
+            if (!_runtimeTypeMatch(evaluationResult, field.type)) {
+              errorReporter.reportErrorForNode(
+                  CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH,
+                  node, [evaluationResult.type, fieldName, field.type]);
+            }
+          }
+        }
+      } else if (initializer is SuperConstructorInvocation) {
+        SuperConstructorInvocation superConstructorInvocation = initializer;
+        SimpleIdentifier name = superConstructorInvocation.constructorName;
+        if (name != null) {
+          superName = name.name;
+        }
+        superArguments = superConstructorInvocation.argumentList.arguments;
+      } else if (initializer is RedirectingConstructorInvocation) {
+        // This is a redirecting constructor, so just evaluate the constructor
+        // it redirects to.
+        ConstructorElement constructor = initializer.staticElement;
+        if (constructor != null && constructor.isConst) {
+          return _evaluateConstructorCall(node,
+              initializer.argumentList.arguments, constructor,
+              initializerVisitor, errorReporter);
+        }
+      }
+    }
+    // Evaluate explicit or implicit call to super().
+    InterfaceType superclass = definingClass.superclass;
+    if (superclass != null && !superclass.isObject) {
+      ConstructorElement superConstructor =
+          superclass.lookUpConstructor(superName, constructor.library);
+      if (superConstructor != null) {
+        if (superArguments == null) {
+          superArguments = new NodeList<Expression>(null);
+        }
+        _evaluateSuperConstructorCall(node, fieldMap, superConstructor,
+            superArguments, initializerVisitor, errorReporter);
+      }
+    }
+    return new DartObjectImpl(definingClass, new GenericState(fieldMap));
+  }
+
+  void _evaluateSuperConstructorCall(AstNode node,
+      HashMap<String, DartObjectImpl> fieldMap,
+      ConstructorElement superConstructor, NodeList<Expression> superArguments,
+      ConstantVisitor initializerVisitor, ErrorReporter errorReporter) {
+    if (superConstructor != null && superConstructor.isConst) {
+      DartObjectImpl evaluationResult = _evaluateConstructorCall(node,
+          superArguments, superConstructor, initializerVisitor, errorReporter);
+      if (evaluationResult != null) {
+        fieldMap[GenericState.SUPERCLASS_FIELD] = evaluationResult;
+      }
+    }
+  }
+
+  /**
+   * Attempt to follow the chain of factory redirections until a constructor is
+   * reached which is not a const factory constructor. Return the constant
+   * constructor which terminates the chain of factory redirections, if the
+   * chain terminates. If there is a problem (e.g. a redirection can't be found,
+   * or a cycle is encountered), the chain will be followed as far as possible
+   * and then a const factory constructor will be returned.
+   */
+  ConstructorElement _followConstantRedirectionChain(
+      ConstructorElement constructor) {
+    HashSet<ConstructorElement> constructorsVisited =
+        new HashSet<ConstructorElement>();
+    while (true) {
+      ConstructorElement redirectedConstructor =
+          _getConstRedirectedConstructor(constructor);
+      if (redirectedConstructor == null) {
+        break;
+      } else {
+        ConstructorElement constructorBase = _getConstructorBase(constructor);
+        constructorsVisited.add(constructorBase);
+        ConstructorElement redirectedConstructorBase =
+            _getConstructorBase(redirectedConstructor);
+        if (constructorsVisited.contains(redirectedConstructorBase)) {
+          // Cycle in redirecting factory constructors--this is not allowed
+          // and is checked elsewhere--see
+          // [ErrorVerifier.checkForRecursiveFactoryRedirect()]).
+          break;
+        }
+      }
+      constructor = redirectedConstructor;
+    }
+    return constructor;
+  }
+
+  /**
+   * Generate an error indicating that the given [constant] is not a valid
+   * compile-time constant because it references at least one of the constants
+   * in the given [cycle], each of which directly or indirectly references the
+   * constant.
+   */
+  void _generateCycleError(List<AstNode> cycle, AstNode constant) {
+    // TODO(brianwilkerson) Implement this.
+  }
+
+  /**
+   * If [constructor] redirects to another const constructor, return the
+   * const constructor it redirects to.  Otherwise return `null`.
+   */
+  ConstructorElement _getConstRedirectedConstructor(
+      ConstructorElement constructor) {
+    if (!constructor.isFactory) {
+      return null;
+    }
+    if (identical(constructor.enclosingElement.type, typeProvider.symbolType)) {
+      // The dart:core.Symbol has a const factory constructor that redirects
+      // to dart:_internal.Symbol.  That in turn redirects to an external
+      // const constructor, which we won't be able to evaluate.
+      // So stop following the chain of redirections at dart:core.Symbol, and
+      // let [evaluateInstanceCreationExpression] handle it specially.
+      return null;
+    }
+    ConstructorElement redirectedConstructor =
+        constructor.redirectedConstructor;
+    if (redirectedConstructor == null) {
+      // This can happen if constructor is an external factory constructor.
+      return null;
+    }
+    if (!redirectedConstructor.isConst) {
+      // Delegating to a non-const constructor--this is not allowed (and
+      // is checked elsewhere--see
+      // [ErrorVerifier.checkForRedirectToNonConstConstructor()]).
+      return null;
+    }
+    return redirectedConstructor;
+  }
+
+  ConstructorElementImpl _getConstructorBase(ConstructorElement constructor) {
+    while (constructor is ConstructorMember) {
+      constructor = (constructor as ConstructorMember).baseElement;
+    }
+    return constructor;
+  }
+
+  /**
+   * Check if the object [obj] matches the type [type] according to runtime type
+   * checking rules.
+   */
+  bool _runtimeTypeMatch(DartObjectImpl obj, DartType type) {
+    if (obj.isNull) {
+      return true;
+    }
+    if (type.isUndefined) {
+      return false;
+    }
+    return obj.type.isSubtypeOf(type);
+  }
+
+  /**
+   * Determine whether the given string is a valid name for a public symbol
+   * (i.e. whether it is allowed for a call to the Symbol constructor).
+   */
+  static bool isValidPublicSymbol(String name) => name.isEmpty ||
+      name == "void" ||
+      new JavaPatternMatcher(_PUBLIC_SYMBOL_PATTERN, name).matches();
+}
+
+/**
+ * A visitor used to evaluate constant expressions to produce their compile-time
+ * value. According to the Dart Language Specification: <blockquote> A constant
+ * expression is one of the following:
+ *
+ * * A literal number.
+ * * A literal boolean.
+ * * A literal string where any interpolated expression is a compile-time
+ *   constant that evaluates to a numeric, string or boolean value or to
+ *   <b>null</b>.
+ * * A literal symbol.
+ * * <b>null</b>.
+ * * A qualified reference to a static constant variable.
+ * * An identifier expression that denotes a constant variable, class or type
+ *   alias.
+ * * A constant constructor invocation.
+ * * A constant list literal.
+ * * A constant map literal.
+ * * A simple or qualified identifier denoting a top-level function or a static
+ *   method.
+ * * A parenthesized expression <i>(e)</i> where <i>e</i> is a constant
+ *   expression.
+ * * An expression of the form <i>identical(e<sub>1</sub>, e<sub>2</sub>)</i>
+ *   where <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> are constant
+ *   expressions and <i>identical()</i> is statically bound to the predefined
+ *   dart function <i>identical()</i> discussed above.
+ * * An expression of one of the forms <i>e<sub>1</sub> == e<sub>2</sub></i> or
+ *   <i>e<sub>1</sub> != e<sub>2</sub></i> where <i>e<sub>1</sub></i> and
+ *   <i>e<sub>2</sub></i> are constant expressions that evaluate to a numeric,
+ *   string or boolean value.
+ * * An expression of one of the forms <i>!e</i>, <i>e<sub>1</sub> &amp;&amp;
+ *   e<sub>2</sub></i> or <i>e<sub>1</sub> || e<sub>2</sub></i>, where <i>e</i>,
+ *   <i>e1</sub></i> and <i>e2</sub></i> are constant expressions that evaluate
+ *   to a boolean value.
+ * * An expression of one of the forms <i>~e</i>, <i>e<sub>1</sub> ^
+ *   e<sub>2</sub></i>, <i>e<sub>1</sub> &amp; e<sub>2</sub></i>,
+ *   <i>e<sub>1</sub> | e<sub>2</sub></i>, <i>e<sub>1</sub> &gt;&gt;
+ *   e<sub>2</sub></i> or <i>e<sub>1</sub> &lt;&lt; e<sub>2</sub></i>, where
+ *   <i>e</i>, <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> are constant
+ *   expressions that evaluate to an integer value or to <b>null</b>.
+ * * An expression of one of the forms <i>-e</i>, <i>e<sub>1</sub> +
+ *   e<sub>2</sub></i>, <i>e<sub>1</sub> - e<sub>2</sub></i>, <i>e<sub>1</sub> *
+ *   e<sub>2</sub></i>, <i>e<sub>1</sub> / e<sub>2</sub></i>, <i>e<sub>1</sub>
+ *   ~/ e<sub>2</sub></i>, <i>e<sub>1</sub> &gt; e<sub>2</sub></i>,
+ *   <i>e<sub>1</sub> &lt; e<sub>2</sub></i>, <i>e<sub>1</sub> &gt;=
+ *   e<sub>2</sub></i>, <i>e<sub>1</sub> &lt;= e<sub>2</sub></i> or
+ *   <i>e<sub>1</sub> % e<sub>2</sub></i>, where <i>e</i>, <i>e<sub>1</sub></i>
+ *   and <i>e<sub>2</sub></i> are constant expressions that evaluate to a
+ *   numeric value or to <b>null</b>.
+ * * An expression of the form <i>e<sub>1</sub> ? e<sub>2</sub> :
+ *   e<sub>3</sub></i> where <i>e<sub>1</sub></i>, <i>e<sub>2</sub></i> and
+ *   <i>e<sub>3</sub></i> are constant expressions, and <i>e<sub>1</sub></i>
+ *   evaluates to a boolean value.
+ * </blockquote>
+ */
+class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
+  /**
+   * The type provider used to access the known types.
+   */
+  final TypeProvider _typeProvider;
+
+  HashMap<String, DartObjectImpl> _lexicalEnvironment;
+
+  /**
+   * Error reporter that we use to report errors accumulated while computing the
+   * constant.
+   */
+  final ErrorReporter _errorReporter;
+
+  /**
+   * Helper class used to compute constant values.
+   */
+  DartObjectComputer _dartObjectComputer;
+
+  /**
+   * Initialize a newly created constant visitor. The [_typeProvider] is the
+   * type provider used to access known types. The [_errorReporter] is used to
+   * report errors found during evaluation.
+   */
+  ConstantVisitor.con1(this._typeProvider, this._errorReporter) {
+    this._lexicalEnvironment = null;
+    this._dartObjectComputer =
+        new DartObjectComputer(_errorReporter, _typeProvider);
+  }
+
+  /**
+   * Initialize a newly created constant visitor. The [_typeProvider] is the
+   * type provider used to access known types. The [lexicalEnvironment] is a map
+   * containing values which should override identifiers, or `null` if no
+   * overriding is necessary. The [_errorReporter] is used to report errors
+   * found during evaluation.
+   */
+  ConstantVisitor.con2(this._typeProvider,
+      HashMap<String, DartObjectImpl> lexicalEnvironment, this._errorReporter) {
+    this._lexicalEnvironment = lexicalEnvironment;
+    this._dartObjectComputer =
+        new DartObjectComputer(_errorReporter, _typeProvider);
+  }
+
+  /**
+   * This method is called just before retrieving an evaluation result from an
+   * AST node. Unit tests will override it to introduce additional error
+   * checking.
+   */
+  void beforeGetEvaluationResult(AstNode node) {}
+
+  /**
+   * Return `true` if the given [element] represents the `length` getter in
+   * class 'String'.
+   */
+  bool isStringLength(Element element) {
+    if (element is PropertyAccessorElement) {
+      if (element.isGetter && element.name == 'length') {
+        return element.enclosingElement == _typeProvider.stringType.element;
+      }
+    }
+    return false;
+  }
+
+  @override
+  DartObjectImpl visitAdjacentStrings(AdjacentStrings node) {
+    DartObjectImpl result = null;
+    for (StringLiteral string in node.strings) {
+      if (result == null) {
+        result = string.accept(this);
+      } else {
+        result =
+            _dartObjectComputer.concatenate(node, result, string.accept(this));
+      }
+    }
+    return result;
+  }
+
+  @override
+  DartObjectImpl visitBinaryExpression(BinaryExpression node) {
+    DartObjectImpl leftResult = node.leftOperand.accept(this);
+    DartObjectImpl rightResult = node.rightOperand.accept(this);
+    TokenType operatorType = node.operator.type;
+    // 'null' is almost never good operand
+    if (operatorType != TokenType.BANG_EQ && operatorType != TokenType.EQ_EQ) {
+      if (leftResult != null && leftResult.isNull ||
+          rightResult != null && rightResult.isNull) {
+        _error(node, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+        return null;
+      }
+    }
+    // evaluate operator
+    while (true) {
+      if (operatorType == TokenType.AMPERSAND) {
+        return _dartObjectComputer.bitAnd(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.AMPERSAND_AMPERSAND) {
+        return _dartObjectComputer.logicalAnd(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.BANG_EQ) {
+        return _dartObjectComputer.notEqual(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.BAR) {
+        return _dartObjectComputer.bitOr(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.BAR_BAR) {
+        return _dartObjectComputer.logicalOr(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.CARET) {
+        return _dartObjectComputer.bitXor(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.EQ_EQ) {
+        return _dartObjectComputer.equalEqual(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.GT) {
+        return _dartObjectComputer.greaterThan(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.GT_EQ) {
+        return _dartObjectComputer.greaterThanOrEqual(
+            node, leftResult, rightResult);
+      } else if (operatorType == TokenType.GT_GT) {
+        return _dartObjectComputer.shiftRight(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.LT) {
+        return _dartObjectComputer.lessThan(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.LT_EQ) {
+        return _dartObjectComputer.lessThanOrEqual(
+            node, leftResult, rightResult);
+      } else if (operatorType == TokenType.LT_LT) {
+        return _dartObjectComputer.shiftLeft(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.MINUS) {
+        return _dartObjectComputer.minus(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.PERCENT) {
+        return _dartObjectComputer.remainder(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.PLUS) {
+        return _dartObjectComputer.add(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.STAR) {
+        return _dartObjectComputer.times(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.SLASH) {
+        return _dartObjectComputer.divide(node, leftResult, rightResult);
+      } else if (operatorType == TokenType.TILDE_SLASH) {
+        return _dartObjectComputer.integerDivide(node, leftResult, rightResult);
+      } else {
+        // TODO(brianwilkerson) Figure out which error to report.
+        _error(node, null);
+        return null;
+      }
+      break;
+    }
+  }
+
+  @override
+  DartObjectImpl visitBooleanLiteral(BooleanLiteral node) =>
+      new DartObjectImpl(_typeProvider.boolType, BoolState.from(node.value));
+
+  @override
+  DartObjectImpl visitConditionalExpression(ConditionalExpression node) {
+    Expression condition = node.condition;
+    DartObjectImpl conditionResult = condition.accept(this);
+    DartObjectImpl thenResult = node.thenExpression.accept(this);
+    DartObjectImpl elseResult = node.elseExpression.accept(this);
+    if (conditionResult == null) {
+      return conditionResult;
+    } else if (!conditionResult.isBool) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL, condition);
+      return null;
+    } else if (thenResult == null) {
+      return thenResult;
+    } else if (elseResult == null) {
+      return elseResult;
+    }
+    conditionResult =
+        _dartObjectComputer.applyBooleanConversion(condition, conditionResult);
+    if (conditionResult == null) {
+      return conditionResult;
+    }
+    if (conditionResult.isTrue) {
+      return thenResult;
+    } else if (conditionResult.isFalse) {
+      return elseResult;
+    }
+    ParameterizedType thenType = thenResult.type;
+    ParameterizedType elseType = elseResult.type;
+    return new DartObjectImpl.validWithUnknownValue(
+        thenType.getLeastUpperBound(elseType) as InterfaceType);
+  }
+
+  @override
+  DartObjectImpl visitDoubleLiteral(DoubleLiteral node) =>
+      new DartObjectImpl(_typeProvider.doubleType, new DoubleState(node.value));
+
+  @override
+  DartObjectImpl visitInstanceCreationExpression(
+      InstanceCreationExpression node) {
+    if (!node.isConst) {
+      // TODO(brianwilkerson) Figure out which error to report.
+      _error(node, null);
+      return null;
+    }
+    beforeGetEvaluationResult(node);
+    EvaluationResultImpl result = node.evaluationResult;
+    if (result != null) {
+      return result.value;
+    }
+    // TODO(brianwilkerson) Figure out which error to report.
+    _error(node, null);
+    return null;
+  }
+
+  @override
+  DartObjectImpl visitIntegerLiteral(IntegerLiteral node) =>
+      new DartObjectImpl(_typeProvider.intType, new IntState(node.value));
+
+  @override
+  DartObjectImpl visitInterpolationExpression(InterpolationExpression node) {
+    DartObjectImpl result = node.expression.accept(this);
+    if (result != null && !result.isBoolNumStringOrNull) {
+      _error(node, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING);
+      return null;
+    }
+    return _dartObjectComputer.performToString(node, result);
+  }
+
+  @override
+  DartObjectImpl visitInterpolationString(InterpolationString node) =>
+      new DartObjectImpl(_typeProvider.stringType, new StringState(node.value));
+
+  @override
+  DartObjectImpl visitListLiteral(ListLiteral node) {
+    if (node.constKeyword == null) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.MISSING_CONST_IN_LIST_LITERAL, node);
+      return null;
+    }
+    bool errorOccurred = false;
+    List<DartObjectImpl> elements = new List<DartObjectImpl>();
+    for (Expression element in node.elements) {
+      DartObjectImpl elementResult = element.accept(this);
+      if (elementResult == null) {
+        errorOccurred = true;
+      } else {
+        elements.add(elementResult);
+      }
+    }
+    if (errorOccurred) {
+      return null;
+    }
+    DartType elementType = _typeProvider.dynamicType;
+    if (node.typeArguments != null &&
+        node.typeArguments.arguments.length == 1) {
+      DartType type = node.typeArguments.arguments[0].type;
+      if (type != null) {
+        elementType = type;
+      }
+    }
+    InterfaceType listType = _typeProvider.listType.substitute4([elementType]);
+    return new DartObjectImpl(listType, new ListState(elements));
+  }
+
+  @override
+  DartObjectImpl visitMapLiteral(MapLiteral node) {
+    if (node.constKeyword == null) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.MISSING_CONST_IN_MAP_LITERAL, node);
+      return null;
+    }
+    bool errorOccurred = false;
+    HashMap<DartObjectImpl, DartObjectImpl> map =
+        new HashMap<DartObjectImpl, DartObjectImpl>();
+    for (MapLiteralEntry entry in node.entries) {
+      DartObjectImpl keyResult = entry.key.accept(this);
+      DartObjectImpl valueResult = entry.value.accept(this);
+      if (keyResult == null || valueResult == null) {
+        errorOccurred = true;
+      } else {
+        map[keyResult] = valueResult;
+      }
+    }
+    if (errorOccurred) {
+      return null;
+    }
+    DartType keyType = _typeProvider.dynamicType;
+    DartType valueType = _typeProvider.dynamicType;
+    if (node.typeArguments != null &&
+        node.typeArguments.arguments.length == 2) {
+      DartType keyTypeCandidate = node.typeArguments.arguments[0].type;
+      if (keyTypeCandidate != null) {
+        keyType = keyTypeCandidate;
+      }
+      DartType valueTypeCandidate = node.typeArguments.arguments[1].type;
+      if (valueTypeCandidate != null) {
+        valueType = valueTypeCandidate;
+      }
+    }
+    InterfaceType mapType =
+        _typeProvider.mapType.substitute4([keyType, valueType]);
+    return new DartObjectImpl(mapType, new MapState(map));
+  }
+
+  @override
+  DartObjectImpl visitMethodInvocation(MethodInvocation node) {
+    Element element = node.methodName.staticElement;
+    if (element is FunctionElement) {
+      FunctionElement function = element;
+      if (function.name == "identical") {
+        NodeList<Expression> arguments = node.argumentList.arguments;
+        if (arguments.length == 2) {
+          Element enclosingElement = function.enclosingElement;
+          if (enclosingElement is CompilationUnitElement) {
+            LibraryElement library = enclosingElement.library;
+            if (library.isDartCore) {
+              DartObjectImpl leftArgument = arguments[0].accept(this);
+              DartObjectImpl rightArgument = arguments[1].accept(this);
+              return _dartObjectComputer.isIdentical(
+                  node, leftArgument, rightArgument);
+            }
+          }
+        }
+      }
+    }
+    // TODO(brianwilkerson) Figure out which error to report.
+    _error(node, null);
+    return null;
+  }
+
+  @override
+  DartObjectImpl visitNamedExpression(NamedExpression node) =>
+      node.expression.accept(this);
+
+  @override
+  DartObjectImpl visitNode(AstNode node) {
+    // TODO(brianwilkerson) Figure out which error to report.
+    _error(node, null);
+    return null;
+  }
+
+  @override
+  DartObjectImpl visitNullLiteral(NullLiteral node) => _typeProvider.nullObject;
+
+  @override
+  DartObjectImpl visitParenthesizedExpression(ParenthesizedExpression node) =>
+      node.expression.accept(this);
+
+  @override
+  DartObjectImpl visitPrefixedIdentifier(PrefixedIdentifier node) {
+    // String.length
+    {
+      Element element = node.staticElement;
+      if (isStringLength(element)) {
+        DartObjectImpl prefixResult = node.prefix.accept(this);
+        return prefixResult.stringLength(_typeProvider);
+      }
+    }
+    // importPrefix.CONST
+    SimpleIdentifier prefixNode = node.prefix;
+    Element prefixElement = prefixNode.staticElement;
+    if (prefixElement is! PrefixElement) {
+      DartObjectImpl prefixResult = prefixNode.accept(this);
+      if (prefixResult == null) {
+        // The error has already been reported.
+        return null;
+      }
+    }
+    // validate prefixed identifier
+    return _getConstantValue(node, node.staticElement);
+  }
+
+  @override
+  DartObjectImpl visitPrefixExpression(PrefixExpression node) {
+    DartObjectImpl operand = node.operand.accept(this);
+    if (operand != null && operand.isNull) {
+      _error(node, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+      return null;
+    }
+    while (true) {
+      if (node.operator.type == TokenType.BANG) {
+        return _dartObjectComputer.logicalNot(node, operand);
+      } else if (node.operator.type == TokenType.TILDE) {
+        return _dartObjectComputer.bitNot(node, operand);
+      } else if (node.operator.type == TokenType.MINUS) {
+        return _dartObjectComputer.negated(node, operand);
+      } else {
+        // TODO(brianwilkerson) Figure out which error to report.
+        _error(node, null);
+        return null;
+      }
+      break;
+    }
+  }
+
+  @override
+  DartObjectImpl visitPropertyAccess(PropertyAccess node) {
+    Element element = node.propertyName.staticElement;
+    if (isStringLength(element)) {
+      DartObjectImpl prefixResult = node.realTarget.accept(this);
+      return prefixResult.stringLength(_typeProvider);
+    }
+    return _getConstantValue(node, element);
+  }
+
+  @override
+  DartObjectImpl visitSimpleIdentifier(SimpleIdentifier node) {
+    if (_lexicalEnvironment != null &&
+        _lexicalEnvironment.containsKey(node.name)) {
+      return _lexicalEnvironment[node.name];
+    }
+    return _getConstantValue(node, node.staticElement);
+  }
+
+  @override
+  DartObjectImpl visitSimpleStringLiteral(SimpleStringLiteral node) =>
+      new DartObjectImpl(_typeProvider.stringType, new StringState(node.value));
+
+  @override
+  DartObjectImpl visitStringInterpolation(StringInterpolation node) {
+    DartObjectImpl result = null;
+    bool first = true;
+    for (InterpolationElement element in node.elements) {
+      if (first) {
+        result = element.accept(this);
+        first = false;
+      } else {
+        result =
+            _dartObjectComputer.concatenate(node, result, element.accept(this));
+      }
+    }
+    return result;
+  }
+
+  @override
+  DartObjectImpl visitSymbolLiteral(SymbolLiteral node) {
+    StringBuffer buffer = new StringBuffer();
+    List<Token> components = node.components;
+    for (int i = 0; i < components.length; i++) {
+      if (i > 0) {
+        buffer.writeCharCode(0x2E);
+      }
+      buffer.write(components[i].lexeme);
+    }
+    return new DartObjectImpl(
+        _typeProvider.symbolType, new SymbolState(buffer.toString()));
+  }
+
+  /**
+   * Create an error associated with the given [node]. The error will have the
+   * given error [code].
+   */
+  void _error(AstNode node, ErrorCode code) {
+    _errorReporter.reportErrorForNode(
+        code == null ? CompileTimeErrorCode.INVALID_CONSTANT : code, node);
+  }
+
+  /**
+   * Return the constant value of the static constant represented by the given
+   * [element]. The [node] is the node to be used if an error needs to be
+   * reported.
+   */
+  DartObjectImpl _getConstantValue(AstNode node, Element element) {
+    if (element is PropertyAccessorElement) {
+      element = (element as PropertyAccessorElement).variable;
+    }
+    if (element is VariableElementImpl) {
+      VariableElementImpl variableElementImpl = element;
+      beforeGetEvaluationResult(node);
+      EvaluationResultImpl value = variableElementImpl.evaluationResult;
+      if (variableElementImpl.isConst && value != null) {
+        return value.value;
+      }
+    } else if (element is ExecutableElement) {
+      ExecutableElement function = element;
+      if (function.isStatic) {
+        ParameterizedType functionType = function.type;
+        if (functionType == null) {
+          functionType = _typeProvider.functionType;
+        }
+        return new DartObjectImpl(functionType, new FunctionState(function));
+      }
+    } else if (element is ClassElement ||
+        element is FunctionTypeAliasElement ||
+        element is DynamicElementImpl) {
+      return new DartObjectImpl(_typeProvider.typeType, new TypeState(element));
+    }
+    // TODO(brianwilkerson) Figure out which error to report.
+    _error(node, null);
+    return null;
+  }
+
+  /**
+   * Return the value of the given [expression], or a representation of 'null'
+   * if the expression cannot be evaluated.
+   */
+  DartObjectImpl _valueOf(Expression expression) {
+    DartObjectImpl expressionValue = expression.accept(this);
+    if (expressionValue != null) {
+      return expressionValue;
+    }
+    return _typeProvider.nullObject;
+  }
+}
+
+/**
+ * The state of a Dart object.
+ */
+abstract class DartObject {
+  /**
+   * Return the boolean value of this object, or `null` if either the value of
+   * this object is not known or this object is not of type 'bool'.
+   */
+  bool get boolValue;
+
+  /**
+   * Return the floating point value of this object, or `null` if either the
+   * value of this object is not known or this object is not of type 'double'.
+   */
+  double get doubleValue;
+
+  /**
+   * Return `true` if this object's value can be represented exactly.
+   */
+  bool get hasExactValue;
+
+  /**
+   * Return the integer value of this object, or `null` if either the value of
+   * this object is not known or this object is not of type 'int'.
+   */
+  int get intValue;
+
+  /**
+   * Return `true` if this object represents the value 'false'.
+   */
+  bool get isFalse;
+
+  /**
+   * Return `true` if this object represents the value 'null'.
+   */
+  bool get isNull;
+
+  /**
+   * Return `true` if this object represents the value 'true'.
+   */
+  bool get isTrue;
+
+  /**
+   * Return the string value of this object, or `null` if either the value of
+   * this object is not known or this object is not of type 'String'.
+   */
+  String get stringValue;
+
+  /**
+   * Return the run-time type of this object.
+   */
+  ParameterizedType get type;
+
+  /**
+   * Return this object's value if it can be represented exactly, or `null` if
+   * either the value cannot be represented exactly or if the value is `null`.
+   * Clients should use [hasExactValue] to distinguish between these two cases.
+   */
+  Object get value;
+}
+
+/**
+ * A utility class that contains methods for manipulating instances of a Dart
+ * class and for collecting errors during evaluation.
+ */
+class DartObjectComputer {
+  /**
+   * The error reporter that we are using to collect errors.
+   */
+  final ErrorReporter _errorReporter;
+
+  /**
+   * The type provider used to create objects of the appropriate types, and to
+   * identify when an object is of a built-in type.
+   */
+  final TypeProvider _typeProvider;
+
+  DartObjectComputer(this._errorReporter, this._typeProvider);
+
+  DartObjectImpl add(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.add(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+        return null;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the result of applying boolean conversion to the [evaluationResult].
+   * The [node] is the node against which errors should be reported.
+   */
+  DartObjectImpl applyBooleanConversion(
+      AstNode node, DartObjectImpl evaluationResult) {
+    if (evaluationResult != null) {
+      try {
+        return evaluationResult.convertToBool(_typeProvider);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl bitAnd(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.bitAnd(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl bitNot(Expression node, DartObjectImpl evaluationResult) {
+    if (evaluationResult != null) {
+      try {
+        return evaluationResult.bitNot(_typeProvider);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl bitOr(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.bitOr(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl bitXor(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.bitXor(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl concatenate(Expression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.concatenate(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl divide(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.divide(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl equalEqual(Expression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.equalEqual(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl greaterThan(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.greaterThan(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl greaterThanOrEqual(BinaryExpression node,
+      DartObjectImpl leftOperand, DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.greaterThanOrEqual(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl integerDivide(BinaryExpression node,
+      DartObjectImpl leftOperand, DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.integerDivide(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl isIdentical(Expression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.isIdentical(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl lessThan(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.lessThan(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl lessThanOrEqual(BinaryExpression node,
+      DartObjectImpl leftOperand, DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.lessThanOrEqual(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl logicalAnd(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.logicalAnd(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl logicalNot(Expression node, DartObjectImpl evaluationResult) {
+    if (evaluationResult != null) {
+      try {
+        return evaluationResult.logicalNot(_typeProvider);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl logicalOr(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.logicalOr(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl minus(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.minus(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl negated(Expression node, DartObjectImpl evaluationResult) {
+    if (evaluationResult != null) {
+      try {
+        return evaluationResult.negated(_typeProvider);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl notEqual(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.notEqual(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl performToString(
+      AstNode node, DartObjectImpl evaluationResult) {
+    if (evaluationResult != null) {
+      try {
+        return evaluationResult.performToString(_typeProvider);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl remainder(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.remainder(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl shiftLeft(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.shiftLeft(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  DartObjectImpl shiftRight(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.shiftRight(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the result of invoking the 'length' getter on the
+   * [evaluationResult]. The [node] is the node against which errors should be
+   * reported.
+   */
+  EvaluationResultImpl stringLength(
+      Expression node, EvaluationResultImpl evaluationResult) {
+    if (evaluationResult.value != null) {
+      try {
+        return new EvaluationResultImpl.con1(
+            evaluationResult.value.stringLength(_typeProvider));
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return new EvaluationResultImpl.con1(null);
+  }
+
+  DartObjectImpl times(BinaryExpression node, DartObjectImpl leftOperand,
+      DartObjectImpl rightOperand) {
+    if (leftOperand != null && rightOperand != null) {
+      try {
+        return leftOperand.times(_typeProvider, rightOperand);
+      } on EvaluationException catch (exception) {
+        _errorReporter.reportErrorForNode(exception.errorCode, node);
+      }
+    }
+    return null;
+  }
+}
+
+/**
+ * An instance of a Dart class.
+ */
+class DartObjectImpl implements DartObject {
+  /**
+   * An empty list of objects.
+   */
+  static const List<DartObjectImpl> EMPTY_LIST = const <DartObjectImpl>[];
+
+  /**
+   * The run-time type of this object.
+   */
+  final ParameterizedType type;
+
+  /**
+   * The state of the object.
+   */
+  final InstanceState _state;
+
+  /**
+   * Initialize a newly created object to have the given [type] and [_state].
+   */
+  DartObjectImpl(this.type, this._state);
+
+  /**
+   * Create an object to represent an unknown value.
+   */
+  factory DartObjectImpl.validWithUnknownValue(InterfaceType type) {
+    if (type.element.library.isDartCore) {
+      String typeName = type.name;
+      if (typeName == "bool") {
+        return new DartObjectImpl(type, BoolState.UNKNOWN_VALUE);
+      } else if (typeName == "double") {
+        return new DartObjectImpl(type, DoubleState.UNKNOWN_VALUE);
+      } else if (typeName == "int") {
+        return new DartObjectImpl(type, IntState.UNKNOWN_VALUE);
+      } else if (typeName == "String") {
+        return new DartObjectImpl(type, StringState.UNKNOWN_VALUE);
+      }
+    }
+    return new DartObjectImpl(type, GenericState.UNKNOWN_VALUE);
+  }
+
+  @override
+  bool get boolValue {
+    if (_state is BoolState) {
+      return (_state as BoolState).value;
+    }
+    return null;
+  }
+
+  @override
+  double get doubleValue {
+    if (_state is DoubleState) {
+      return (_state as DoubleState).value;
+    }
+    return null;
+  }
+
+  HashMap<String, DartObjectImpl> get fields => _state.fields;
+
+  @override
+  bool get hasExactValue => _state.hasExactValue;
+
+  @override
+  int get hashCode => JenkinsSmiHash.hash2(type.hashCode, _state.hashCode);
+
+  @override
+  int get intValue {
+    if (_state is IntState) {
+      return (_state as IntState).value;
+    }
+    return null;
+  }
+
+  /**
+   * Return `true` if this object represents an object whose type is 'bool'.
+   */
+  bool get isBool => _state.isBool;
+
+  /**
+   * Return `true` if this object represents an object whose type is either
+   * 'bool', 'num', 'String', or 'Null'.
+   */
+  bool get isBoolNumStringOrNull => _state.isBoolNumStringOrNull;
+
+  @override
+  bool get isFalse =>
+      _state is BoolState && identical((_state as BoolState).value, false);
+
+  @override
+  bool get isNull => _state is NullState;
+
+  @override
+  bool get isTrue =>
+      _state is BoolState && identical((_state as BoolState).value, true);
+
+  /**
+   * Return `true` if this object represents an unknown value.
+   */
+  bool get isUnknown => _state.isUnknown;
+
+  /**
+   * Return `true` if this object represents an instance of a user-defined
+   * class.
+   */
+  bool get isUserDefinedObject => _state is GenericState;
+
+  @override
+  String get stringValue {
+    if (_state is StringState) {
+      return (_state as StringState).value;
+    }
+    return null;
+  }
+
+  @override
+  Object get value => _state.value;
+
+  @override
+  bool operator ==(Object object) {
+    if (object is! DartObjectImpl) {
+      return false;
+    }
+    DartObjectImpl dartObject = object as DartObjectImpl;
+    return type == dartObject.type && _state == dartObject._state;
+  }
+
+  /**
+   * Return the result of invoking the '+' operator on this object with the
+   * given [rightOperand]. The [typeProvider] is the type provider used to find
+   * known types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl add(TypeProvider typeProvider, DartObjectImpl rightOperand) {
+    InstanceState result = _state.add(rightOperand._state);
+    if (result is IntState) {
+      return new DartObjectImpl(typeProvider.intType, result);
+    } else if (result is DoubleState) {
+      return new DartObjectImpl(typeProvider.doubleType, result);
+    } else if (result is NumState) {
+      return new DartObjectImpl(typeProvider.numType, result);
+    } else if (result is StringState) {
+      return new DartObjectImpl(typeProvider.stringType, result);
+    }
+    // We should never get here.
+    throw new IllegalStateException("add returned a ${result.runtimeType}");
+  }
+
+  /**
+   * Return the result of invoking the '&' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl bitAnd(
+          TypeProvider typeProvider, DartObjectImpl rightOperand) =>
+      new DartObjectImpl(
+          typeProvider.intType, _state.bitAnd(rightOperand._state));
+
+  /**
+   * Return the result of invoking the '~' operator on this object. The
+   * [typeProvider] is the type provider used to find known types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl bitNot(TypeProvider typeProvider) =>
+      new DartObjectImpl(typeProvider.intType, _state.bitNot());
+
+  /**
+   * Return the result of invoking the '|' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl bitOr(
+          TypeProvider typeProvider, DartObjectImpl rightOperand) =>
+      new DartObjectImpl(
+          typeProvider.intType, _state.bitOr(rightOperand._state));
+
+  /**
+   * Return the result of invoking the '^' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl bitXor(
+          TypeProvider typeProvider, DartObjectImpl rightOperand) =>
+      new DartObjectImpl(
+          typeProvider.intType, _state.bitXor(rightOperand._state));
+
+  /**
+   * Return the result of invoking the ' ' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl concatenate(
+          TypeProvider typeProvider, DartObjectImpl rightOperand) =>
+      new DartObjectImpl(
+          typeProvider.stringType, _state.concatenate(rightOperand._state));
+
+  /**
+   * Return the result of applying boolean conversion to this object. The
+   * [typeProvider] is the type provider used to find known types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl convertToBool(TypeProvider typeProvider) {
+    InterfaceType boolType = typeProvider.boolType;
+    if (identical(type, boolType)) {
+      return this;
+    }
+    return new DartObjectImpl(boolType, _state.convertToBool());
+  }
+
+  /**
+   * Return the result of invoking the '/' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for
+   * an object of this kind.
+   */
+  DartObjectImpl divide(
+      TypeProvider typeProvider, DartObjectImpl rightOperand) {
+    InstanceState result = _state.divide(rightOperand._state);
+    if (result is IntState) {
+      return new DartObjectImpl(typeProvider.intType, result);
+    } else if (result is DoubleState) {
+      return new DartObjectImpl(typeProvider.doubleType, result);
+    } else if (result is NumState) {
+      return new DartObjectImpl(typeProvider.numType, result);
+    }
+    // We should never get here.
+    throw new IllegalStateException("divide returned a ${result.runtimeType}");
+  }
+
+  /**
+   * Return the result of invoking the '==' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl equalEqual(
+      TypeProvider typeProvider, DartObjectImpl rightOperand) {
+    if (type != rightOperand.type) {
+      String typeName = type.name;
+      if (!(typeName == "bool" ||
+          typeName == "double" ||
+          typeName == "int" ||
+          typeName == "num" ||
+          typeName == "String" ||
+          typeName == "Null" ||
+          type.isDynamic)) {
+        throw new EvaluationException(
+            CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING);
+      }
+    }
+    return new DartObjectImpl(
+        typeProvider.boolType, _state.equalEqual(rightOperand._state));
+  }
+
+  /**
+   * Return the result of invoking the '&gt;' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl greaterThan(
+          TypeProvider typeProvider, DartObjectImpl rightOperand) =>
+      new DartObjectImpl(
+          typeProvider.boolType, _state.greaterThan(rightOperand._state));
+
+  /**
+   * Return the result of invoking the '&gt;=' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl greaterThanOrEqual(TypeProvider typeProvider,
+      DartObjectImpl rightOperand) => new DartObjectImpl(
+      typeProvider.boolType, _state.greaterThanOrEqual(rightOperand._state));
+
+  /**
+   * Return the result of invoking the '~/' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl integerDivide(
+          TypeProvider typeProvider, DartObjectImpl rightOperand) =>
+      new DartObjectImpl(
+          typeProvider.intType, _state.integerDivide(rightOperand._state));
+
+  /**
+   * Return the result of invoking the identical function on this object with
+   * the [rightOperand]. The [typeProvider] is the type provider used to find
+   * known types.
+   */
+  DartObjectImpl isIdentical(
+      TypeProvider typeProvider, DartObjectImpl rightOperand) {
+    return new DartObjectImpl(
+        typeProvider.boolType, _state.isIdentical(rightOperand._state));
+  }
+
+  /**
+   * Return the result of invoking the '&lt;' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl lessThan(
+          TypeProvider typeProvider, DartObjectImpl rightOperand) =>
+      new DartObjectImpl(
+          typeProvider.boolType, _state.lessThan(rightOperand._state));
+
+  /**
+   * Return the result of invoking the '&lt;=' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl lessThanOrEqual(
+          TypeProvider typeProvider, DartObjectImpl rightOperand) =>
+      new DartObjectImpl(
+          typeProvider.boolType, _state.lessThanOrEqual(rightOperand._state));
+
+  /**
+   * Return the result of invoking the '&&' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl logicalAnd(
+          TypeProvider typeProvider, DartObjectImpl rightOperand) =>
+      new DartObjectImpl(
+          typeProvider.boolType, _state.logicalAnd(rightOperand._state));
+
+  /**
+   * Return the result of invoking the '!' operator on this object. The
+   * [typeProvider] is the type provider used to find known types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl logicalNot(TypeProvider typeProvider) =>
+      new DartObjectImpl(typeProvider.boolType, _state.logicalNot());
+
+  /**
+   * Return the result of invoking the '||' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl logicalOr(
+          TypeProvider typeProvider, DartObjectImpl rightOperand) =>
+      new DartObjectImpl(
+          typeProvider.boolType, _state.logicalOr(rightOperand._state));
+
+  /**
+   * Return the result of invoking the '-' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl minus(TypeProvider typeProvider, DartObjectImpl rightOperand) {
+    InstanceState result = _state.minus(rightOperand._state);
+    if (result is IntState) {
+      return new DartObjectImpl(typeProvider.intType, result);
+    } else if (result is DoubleState) {
+      return new DartObjectImpl(typeProvider.doubleType, result);
+    } else if (result is NumState) {
+      return new DartObjectImpl(typeProvider.numType, result);
+    }
+    // We should never get here.
+    throw new IllegalStateException("minus returned a ${result.runtimeType}");
+  }
+
+  /**
+   * Return the result of invoking the '-' operator on this object. The
+   * [typeProvider] is the type provider used to find known types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl negated(TypeProvider typeProvider) {
+    InstanceState result = _state.negated();
+    if (result is IntState) {
+      return new DartObjectImpl(typeProvider.intType, result);
+    } else if (result is DoubleState) {
+      return new DartObjectImpl(typeProvider.doubleType, result);
+    } else if (result is NumState) {
+      return new DartObjectImpl(typeProvider.numType, result);
+    }
+    // We should never get here.
+    throw new IllegalStateException("negated returned a ${result.runtimeType}");
+  }
+
+  /**
+   * Return the result of invoking the '!=' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl notEqual(
+      TypeProvider typeProvider, DartObjectImpl rightOperand) {
+    if (type != rightOperand.type) {
+      String typeName = type.name;
+      if (typeName != "bool" &&
+          typeName != "double" &&
+          typeName != "int" &&
+          typeName != "num" &&
+          typeName != "String") {
+        return new DartObjectImpl(typeProvider.boolType, BoolState.TRUE_STATE);
+      }
+    }
+    return new DartObjectImpl(typeProvider.boolType,
+        _state.equalEqual(rightOperand._state).logicalNot());
+  }
+
+  /**
+   * Return the result of converting this object to a 'String'. The
+   * [typeProvider] is the type provider used to find known types.
+   *
+   * Throws an [EvaluationException] if the object cannot be converted to a
+   * 'String'.
+   */
+  DartObjectImpl performToString(TypeProvider typeProvider) {
+    InterfaceType stringType = typeProvider.stringType;
+    if (identical(type, stringType)) {
+      return this;
+    }
+    return new DartObjectImpl(stringType, _state.convertToString());
+  }
+
+  /**
+   * Return the result of invoking the '%' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl remainder(
+      TypeProvider typeProvider, DartObjectImpl rightOperand) {
+    InstanceState result = _state.remainder(rightOperand._state);
+    if (result is IntState) {
+      return new DartObjectImpl(typeProvider.intType, result);
+    } else if (result is DoubleState) {
+      return new DartObjectImpl(typeProvider.doubleType, result);
+    } else if (result is NumState) {
+      return new DartObjectImpl(typeProvider.numType, result);
+    }
+    // We should never get here.
+    throw new IllegalStateException(
+        "remainder returned a ${result.runtimeType}");
+  }
+
+  /**
+   * Return the result of invoking the '&lt;&lt;' operator on this object with
+   * the [rightOperand]. The [typeProvider] is the type provider used to find
+   * known types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl shiftLeft(
+          TypeProvider typeProvider, DartObjectImpl rightOperand) =>
+      new DartObjectImpl(
+          typeProvider.intType, _state.shiftLeft(rightOperand._state));
+
+  /**
+   * Return the result of invoking the '&gt;&gt;' operator on this object with
+   * the [rightOperand]. The [typeProvider] is the type provider used to find
+   * known types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl shiftRight(
+          TypeProvider typeProvider, DartObjectImpl rightOperand) =>
+      new DartObjectImpl(
+          typeProvider.intType, _state.shiftRight(rightOperand._state));
+
+  /**
+   * Return the result of invoking the 'length' getter on this object. The
+   * [typeProvider] is the type provider used to find known types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl stringLength(TypeProvider typeProvider) =>
+      new DartObjectImpl(typeProvider.intType, _state.stringLength());
+
+  /**
+   * Return the result of invoking the '*' operator on this object with the
+   * [rightOperand]. The [typeProvider] is the type provider used to find known
+   * types.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  DartObjectImpl times(TypeProvider typeProvider, DartObjectImpl rightOperand) {
+    InstanceState result = _state.times(rightOperand._state);
+    if (result is IntState) {
+      return new DartObjectImpl(typeProvider.intType, result);
+    } else if (result is DoubleState) {
+      return new DartObjectImpl(typeProvider.doubleType, result);
+    } else if (result is NumState) {
+      return new DartObjectImpl(typeProvider.numType, result);
+    }
+    // We should never get here.
+    throw new IllegalStateException("times returned a ${result.runtimeType}");
+  }
+
+  @override
+  String toString() => "${type.displayName} ($_state)";
+}
+
+/**
+ * An object used to provide access to the values of variables that have been
+ * defined on the command line using the `-D` option.
+ */
+class DeclaredVariables {
+  /**
+   * A table mapping the names of declared variables to their values.
+   */
+  HashMap<String, String> _declaredVariables = new HashMap<String, String>();
+
+  /**
+   * Define a variable with the given [name] to have the given [value].
+   */
+  void define(String name, String value) {
+    _declaredVariables[name] = value;
+  }
+
+  /**
+   * Return the value of the variable with the given [name] interpreted as a
+   * 'boolean' value. If the variable is not defined (or [name] is `null`), a
+   * DartObject representing "unknown" is returned. If the value cannot be
+   * parsed as a boolean, a DartObject representing 'null' is returned. The
+   * [typeProvider] is the type provider used to find the type 'bool'.
+   */
+  DartObject getBool(TypeProvider typeProvider, String name) {
+    String value = _declaredVariables[name];
+    if (value == null) {
+      return new DartObjectImpl(typeProvider.boolType, BoolState.UNKNOWN_VALUE);
+    }
+    if (value == "true") {
+      return new DartObjectImpl(typeProvider.boolType, BoolState.TRUE_STATE);
+    } else if (value == "false") {
+      return new DartObjectImpl(typeProvider.boolType, BoolState.FALSE_STATE);
+    }
+    return new DartObjectImpl(typeProvider.nullType, NullState.NULL_STATE);
+  }
+
+  /**
+   * Return the value of the variable with the given [name] interpreted as an
+   * integer value. If the variable is not defined (or [name] is `null`), a
+   * DartObject representing "unknown" is returned. If the value cannot be
+   * parsed as an integer, a DartObject representing 'null' is returned.
+   */
+  DartObject getInt(TypeProvider typeProvider, String name) {
+    String value = _declaredVariables[name];
+    if (value == null) {
+      return new DartObjectImpl(typeProvider.intType, IntState.UNKNOWN_VALUE);
+    }
+    int bigInteger;
+    try {
+      bigInteger = int.parse(value);
+    } on FormatException {
+      return new DartObjectImpl(typeProvider.nullType, NullState.NULL_STATE);
+    }
+    return new DartObjectImpl(typeProvider.intType, new IntState(bigInteger));
+  }
+
+  /**
+   * Return the value of the variable with the given [name] interpreted as a
+   * String value, or `null` if the variable is not defined. Return the value of
+   * the variable with the given name interpreted as a String value. If the
+   * variable is not defined (or [name] is `null`), a DartObject representing
+   * "unknown" is returned. The [typeProvider] is the type provider used to find
+   * the type 'String'.
+   */
+  DartObject getString(TypeProvider typeProvider, String name) {
+    String value = _declaredVariables[name];
+    if (value == null) {
+      return new DartObjectImpl(
+          typeProvider.stringType, StringState.UNKNOWN_VALUE);
+    }
+    return new DartObjectImpl(typeProvider.stringType, new StringState(value));
+  }
+}
+
+/**
+ * The state of an object representing a double.
+ */
+class DoubleState extends NumState {
+  /**
+   * A state that can be used to represent a double whose value is not known.
+   */
+  static DoubleState UNKNOWN_VALUE = new DoubleState(null);
+
+  /**
+   * The value of this instance.
+   */
+  final double value;
+
+  /**
+   * Initialize a newly created state to represent a double with the given
+   * [value].
+   */
+  DoubleState(this.value);
+
+  @override
+  bool get hasExactValue => true;
+
+  @override
+  int get hashCode => value == null ? 0 : value.hashCode;
+
+  @override
+  bool get isBoolNumStringOrNull => true;
+
+  @override
+  bool get isUnknown => value == null;
+
+  @override
+  String get typeName => "double";
+
+  @override
+  bool operator ==(Object object) =>
+      object is DoubleState && (value == object.value);
+
+  @override
+  NumState add(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new DoubleState(value + rightValue.toDouble());
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new DoubleState(value + rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  StringState convertToString() {
+    if (value == null) {
+      return StringState.UNKNOWN_VALUE;
+    }
+    return new StringState(value.toString());
+  }
+
+  @override
+  NumState divide(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new DoubleState(value / rightValue.toDouble());
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new DoubleState(value / rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  BoolState equalEqual(InstanceState rightOperand) {
+    assertBoolNumStringOrNull(rightOperand);
+    return isIdentical(rightOperand);
+  }
+
+  @override
+  BoolState greaterThan(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value > rightValue.toDouble());
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value > rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  BoolState greaterThanOrEqual(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value >= rightValue.toDouble());
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value >= rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  IntState integerDivide(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return IntState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return IntState.UNKNOWN_VALUE;
+      }
+      double result = value / rightValue.toDouble();
+      return new IntState(result.toInt());
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return IntState.UNKNOWN_VALUE;
+      }
+      double result = value / rightValue;
+      return new IntState(result.toInt());
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return IntState.UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  BoolState isIdentical(InstanceState rightOperand) {
+    if (value == null) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value == rightValue);
+    } else if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value == rightValue.toDouble());
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    return BoolState.FALSE_STATE;
+  }
+
+  @override
+  BoolState lessThan(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value < rightValue.toDouble());
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value < rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  BoolState lessThanOrEqual(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value <= rightValue.toDouble());
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value <= rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  NumState minus(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new DoubleState(value - rightValue.toDouble());
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new DoubleState(value - rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  NumState negated() {
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    return new DoubleState(-(value));
+  }
+
+  @override
+  NumState remainder(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new DoubleState(value % rightValue.toDouble());
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new DoubleState(value % rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  NumState times(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new DoubleState(value * rightValue.toDouble());
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new DoubleState(value * rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  String toString() => value == null ? "-unknown-" : value.toString();
+}
+
+/**
+ * The state of an object representing a Dart object for which there is no type
+ * information.
+ */
+class DynamicState extends InstanceState {
+  /**
+   * The unique instance of this class.
+   */
+  static DynamicState DYNAMIC_STATE = new DynamicState();
+
+  @override
+  bool get isBool => true;
+
+  @override
+  bool get isBoolNumStringOrNull => true;
+
+  @override
+  String get typeName => "dynamic";
+
+  @override
+  NumState add(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return _unknownNum(rightOperand);
+  }
+
+  @override
+  IntState bitAnd(InstanceState rightOperand) {
+    assertIntOrNull(rightOperand);
+    return IntState.UNKNOWN_VALUE;
+  }
+
+  @override
+  IntState bitNot() => IntState.UNKNOWN_VALUE;
+
+  @override
+  IntState bitOr(InstanceState rightOperand) {
+    assertIntOrNull(rightOperand);
+    return IntState.UNKNOWN_VALUE;
+  }
+
+  @override
+  IntState bitXor(InstanceState rightOperand) {
+    assertIntOrNull(rightOperand);
+    return IntState.UNKNOWN_VALUE;
+  }
+
+  @override
+  StringState concatenate(InstanceState rightOperand) {
+    assertString(rightOperand);
+    return StringState.UNKNOWN_VALUE;
+  }
+
+  @override
+  BoolState convertToBool() => BoolState.UNKNOWN_VALUE;
+
+  @override
+  StringState convertToString() => StringState.UNKNOWN_VALUE;
+
+  @override
+  NumState divide(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return _unknownNum(rightOperand);
+  }
+
+  @override
+  BoolState equalEqual(InstanceState rightOperand) {
+    assertBoolNumStringOrNull(rightOperand);
+    return BoolState.UNKNOWN_VALUE;
+  }
+
+  @override
+  BoolState greaterThan(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return BoolState.UNKNOWN_VALUE;
+  }
+
+  @override
+  BoolState greaterThanOrEqual(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return BoolState.UNKNOWN_VALUE;
+  }
+
+  @override
+  IntState integerDivide(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return IntState.UNKNOWN_VALUE;
+  }
+
+  @override
+  BoolState isIdentical(InstanceState rightOperand) {
+    return BoolState.UNKNOWN_VALUE;
+  }
+
+  @override
+  BoolState lessThan(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return BoolState.UNKNOWN_VALUE;
+  }
+
+  @override
+  BoolState lessThanOrEqual(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return BoolState.UNKNOWN_VALUE;
+  }
+
+  @override
+  BoolState logicalAnd(InstanceState rightOperand) {
+    assertBool(rightOperand);
+    return BoolState.UNKNOWN_VALUE;
+  }
+
+  @override
+  BoolState logicalNot() => BoolState.UNKNOWN_VALUE;
+
+  @override
+  BoolState logicalOr(InstanceState rightOperand) {
+    assertBool(rightOperand);
+    return rightOperand.convertToBool();
+  }
+
+  @override
+  NumState minus(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return _unknownNum(rightOperand);
+  }
+
+  @override
+  NumState negated() => NumState.UNKNOWN_VALUE;
+
+  @override
+  NumState remainder(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return _unknownNum(rightOperand);
+  }
+
+  @override
+  IntState shiftLeft(InstanceState rightOperand) {
+    assertIntOrNull(rightOperand);
+    return IntState.UNKNOWN_VALUE;
+  }
+
+  @override
+  IntState shiftRight(InstanceState rightOperand) {
+    assertIntOrNull(rightOperand);
+    return IntState.UNKNOWN_VALUE;
+  }
+
+  @override
+  NumState times(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return _unknownNum(rightOperand);
+  }
+
+  /**
+   * Return an object representing an unknown numeric value whose type is based
+   * on the type of the [rightOperand].
+   */
+  NumState _unknownNum(InstanceState rightOperand) {
+    if (rightOperand is IntState) {
+      return IntState.UNKNOWN_VALUE;
+    } else if (rightOperand is DoubleState) {
+      return DoubleState.UNKNOWN_VALUE;
+    }
+    return NumState.UNKNOWN_VALUE;
+  }
+}
+
+/**
+ * A run-time exception that would be thrown during the evaluation of Dart code.
+ */
+class EvaluationException extends JavaException {
+  /**
+   * The error code associated with the exception.
+   */
+  final ErrorCode errorCode;
+
+  /**
+   * Initialize a newly created exception to have the given [errorCode].
+   */
+  EvaluationException(this.errorCode);
+}
+
+/**
+ * The result of attempting to evaluate an expression.
+ */
+class EvaluationResult {
+  /**
+   * The value of the expression.
+   */
+  final DartObject value;
+
+  /**
+   * The errors that should be reported for the expression(s) that were
+   * evaluated.
+   */
+  final List<AnalysisError> _errors;
+
+  /**
+   * Initialize a newly created result object with the given [value] and set of
+   * [_errors]. Clients should use one of the factory methods: [forErrors] and
+   * [forValue].
+   */
+  EvaluationResult(this.value, this._errors);
+
+  /**
+   * Return a list containing the errors that should be reported for the
+   * expression(s) that were evaluated. If there are no such errors, the list
+   * will be empty. The list can be empty even if the expression is not a valid
+   * compile time constant if the errors would have been reported by other parts
+   * of the analysis engine.
+   */
+  List<AnalysisError> get errors =>
+      _errors == null ? AnalysisError.NO_ERRORS : _errors;
+
+  /**
+   * Return `true` if the expression is a compile-time constant expression that
+   * would not throw an exception when evaluated.
+   */
+  bool get isValid => _errors == null;
+
+  /**
+   * Return an evaluation result representing the result of evaluating an
+   * expression that is not a compile-time constant because of the given
+   * [errors].
+   */
+  static EvaluationResult forErrors(List<AnalysisError> errors) =>
+      new EvaluationResult(null, errors);
+
+  /**
+   * Return an evaluation result representing the result of evaluating an
+   * expression that is a compile-time constant that evaluates to the given
+   * [value].
+   */
+  static EvaluationResult forValue(DartObject value) =>
+      new EvaluationResult(value, null);
+}
+
+/**
+ * The result of attempting to evaluate a expression.
+ */
+class EvaluationResultImpl {
+  /**
+   * The errors encountered while trying to evaluate the compile time constant.
+   * These errors may or may not have prevented the expression from being a
+   * valid compile time constant.
+   */
+  List<AnalysisError> _errors;
+
+  /**
+   * The value of the expression, or `null` if the value couldn't be computed
+   * due to errors.
+   */
+  final DartObjectImpl value;
+
+  EvaluationResultImpl.con1(this.value) {
+    this._errors = new List<AnalysisError>(0);
+  }
+
+  EvaluationResultImpl.con2(this.value, List<AnalysisError> errors) {
+    this._errors = errors;
+  }
+
+  List<AnalysisError> get errors => _errors;
+
+  bool equalValues(TypeProvider typeProvider, EvaluationResultImpl result) {
+    if (this.value != null) {
+      if (result.value == null) {
+        return false;
+      }
+      return value == result.value;
+    } else {
+      return false;
+    }
+  }
+
+  @override
+  String toString() {
+    if (value == null) {
+      return "error";
+    }
+    return value.toString();
+  }
+}
+
+/**
+ * The state of an object representing a function.
+ */
+class FunctionState extends InstanceState {
+  /**
+   * The element representing the function being modeled.
+   */
+  final ExecutableElement _element;
+
+  /**
+   * Initialize a newly created state to represent the function with the given
+   * [element].
+   */
+  FunctionState(this._element);
+
+  @override
+  int get hashCode => _element == null ? 0 : _element.hashCode;
+
+  @override
+  String get typeName => "Function";
+
+  @override
+  bool operator ==(Object object) =>
+      object is FunctionState && (_element == object._element);
+
+  @override
+  StringState convertToString() {
+    if (_element == null) {
+      return StringState.UNKNOWN_VALUE;
+    }
+    return new StringState(_element.name);
+  }
+
+  @override
+  BoolState equalEqual(InstanceState rightOperand) {
+    return isIdentical(rightOperand);
+  }
+
+  @override
+  BoolState isIdentical(InstanceState rightOperand) {
+    if (_element == null) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is FunctionState) {
+      ExecutableElement rightElement = rightOperand._element;
+      if (rightElement == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(_element == rightElement);
+    } else if (rightOperand is DynamicState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    return BoolState.FALSE_STATE;
+  }
+
+  @override
+  String toString() => _element == null ? "-unknown-" : _element.name;
+}
+
+/**
+ * The state of an object representing a Dart object for which there is no more
+ * specific state.
+ */
+class GenericState extends InstanceState {
+  /**
+   * Pseudo-field that we use to represent fields in the superclass.
+   */
+  static String SUPERCLASS_FIELD = "(super)";
+
+  /**
+   * A state that can be used to represent an object whose state is not known.
+   */
+  static GenericState UNKNOWN_VALUE =
+      new GenericState(new HashMap<String, DartObjectImpl>());
+
+  /**
+   * The values of the fields of this instance.
+   */
+  final HashMap<String, DartObjectImpl> _fieldMap;
+
+  /**
+   * Initialize a newly created state to represent a newly created object. The
+   * [fieldMap] contains the values of the fields of the instance.
+   */
+  GenericState(this._fieldMap);
+
+  @override
+  HashMap<String, DartObjectImpl> get fields => _fieldMap;
+
+  @override
+  int get hashCode {
+    int hashCode = 0;
+    for (DartObjectImpl value in _fieldMap.values) {
+      hashCode += value.hashCode;
+    }
+    return hashCode;
+  }
+
+  @override
+  bool get isUnknown => identical(this, UNKNOWN_VALUE);
+
+  @override
+  String get typeName => "user defined type";
+
+  @override
+  bool operator ==(Object object) {
+    if (object is! GenericState) {
+      return false;
+    }
+    GenericState state = object as GenericState;
+    HashSet<String> otherFields =
+        new HashSet<String>.from(state._fieldMap.keys.toSet());
+    for (String fieldName in _fieldMap.keys.toSet()) {
+      if (_fieldMap[fieldName] != state._fieldMap[fieldName]) {
+        return false;
+      }
+      otherFields.remove(fieldName);
+    }
+    for (String fieldName in otherFields) {
+      if (state._fieldMap[fieldName] != _fieldMap[fieldName]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @override
+  StringState convertToString() => StringState.UNKNOWN_VALUE;
+
+  @override
+  BoolState equalEqual(InstanceState rightOperand) {
+    assertBoolNumStringOrNull(rightOperand);
+    return isIdentical(rightOperand);
+  }
+
+  @override
+  BoolState isIdentical(InstanceState rightOperand) {
+    if (rightOperand is DynamicState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    return BoolState.from(this == rightOperand);
+  }
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    List<String> fieldNames = _fieldMap.keys.toList();
+    fieldNames.sort();
+    bool first = true;
+    for (String fieldName in fieldNames) {
+      if (first) {
+        first = false;
+      } else {
+        buffer.write('; ');
+      }
+      buffer.write(fieldName);
+      buffer.write(' = ');
+      buffer.write(_fieldMap[fieldName]);
+    }
+    return buffer.toString();
+  }
+}
+
+/**
+ * The state of an object representing a Dart object.
+ */
+abstract class InstanceState {
+  /**
+   * If this represents a generic dart object, return a map from its field names
+   * to their values. Otherwise return null.
+   */
+  HashMap<String, DartObjectImpl> get fields => null;
+
+  /**
+   * Return `true` if this object's value can be represented exactly.
+   */
+  bool get hasExactValue => false;
+
+  /**
+   * Return `true` if this object represents an object whose type is 'bool'.
+   */
+  bool get isBool => false;
+
+  /**
+   * Return `true` if this object represents an object whose type is either
+   * 'bool', 'num', 'String', or 'Null'.
+   */
+  bool get isBoolNumStringOrNull => false;
+
+  /**
+   * Return `true` if this object represents an unknown value.
+   */
+  bool get isUnknown => false;
+
+  /**
+   * Return the name of the type of this value.
+   */
+  String get typeName;
+
+  /**
+   * Return this object's value if it can be represented exactly, or `null` if
+   * either the value cannot be represented exactly or if the value is `null`.
+   * Clients should use [hasExactValue] to distinguish between these two cases.
+   */
+  Object get value => null;
+
+  /**
+   * Return the result of invoking the '+' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  InstanceState add(InstanceState rightOperand) {
+    if (this is StringState && rightOperand is StringState) {
+      return concatenate(rightOperand);
+    }
+    assertNumOrNull(this);
+    assertNumOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Throw an exception if the given [state] does not represent a boolean value.
+   */
+  void assertBool(InstanceState state) {
+    if (!(state is BoolState || state is DynamicState)) {
+      throw new EvaluationException(CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL);
+    }
+  }
+
+  /**
+   * Throw an exception if the given [state] does not represent a boolean,
+   * numeric, string or null value.
+   */
+  void assertBoolNumStringOrNull(InstanceState state) {
+    if (!(state is BoolState ||
+        state is DoubleState ||
+        state is IntState ||
+        state is NumState ||
+        state is StringState ||
+        state is NullState ||
+        state is DynamicState)) {
+      throw new EvaluationException(
+          CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING);
+    }
+  }
+
+  /**
+   * Throw an exception if the given [state] does not represent an integer or
+   * null value.
+   */
+  void assertIntOrNull(InstanceState state) {
+    if (!(state is IntState ||
+        state is NumState ||
+        state is NullState ||
+        state is DynamicState)) {
+      throw new EvaluationException(CompileTimeErrorCode.CONST_EVAL_TYPE_INT);
+    }
+  }
+
+  /**
+   * Throw an exception if the given [state] does not represent a boolean,
+   * numeric, string or null value.
+   */
+  void assertNumOrNull(InstanceState state) {
+    if (!(state is DoubleState ||
+        state is IntState ||
+        state is NumState ||
+        state is NullState ||
+        state is DynamicState)) {
+      throw new EvaluationException(CompileTimeErrorCode.CONST_EVAL_TYPE_NUM);
+    }
+  }
+
+  /**
+   * Throw an exception if the given [state] does not represent a String value.
+   */
+  void assertString(InstanceState state) {
+    if (!(state is StringState || state is DynamicState)) {
+      throw new EvaluationException(CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL);
+    }
+  }
+
+  /**
+   * Return the result of invoking the '&' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  IntState bitAnd(InstanceState rightOperand) {
+    assertIntOrNull(this);
+    assertIntOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the '~' operator on this object.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  IntState bitNot() {
+    assertIntOrNull(this);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the '|' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  IntState bitOr(InstanceState rightOperand) {
+    assertIntOrNull(this);
+    assertIntOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the '^' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  IntState bitXor(InstanceState rightOperand) {
+    assertIntOrNull(this);
+    assertIntOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the ' ' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  StringState concatenate(InstanceState rightOperand) {
+    assertString(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of applying boolean conversion to this object.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  BoolState convertToBool() => BoolState.FALSE_STATE;
+
+  /**
+   * Return the result of converting this object to a String.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  StringState convertToString();
+
+  /**
+   * Return the result of invoking the '/' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  NumState divide(InstanceState rightOperand) {
+    assertNumOrNull(this);
+    assertNumOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the '==' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  BoolState equalEqual(InstanceState rightOperand);
+
+  /**
+   * Return the result of invoking the '&gt;' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  BoolState greaterThan(InstanceState rightOperand) {
+    assertNumOrNull(this);
+    assertNumOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the '&gt;=' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  BoolState greaterThanOrEqual(InstanceState rightOperand) {
+    assertNumOrNull(this);
+    assertNumOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the '~/' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  IntState integerDivide(InstanceState rightOperand) {
+    assertNumOrNull(this);
+    assertNumOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the identical function on this object with
+   * the [rightOperand].
+   */
+  BoolState isIdentical(InstanceState rightOperand);
+
+  /**
+   * Return the result of invoking the '&lt;' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  BoolState lessThan(InstanceState rightOperand) {
+    assertNumOrNull(this);
+    assertNumOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the '&lt;=' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  BoolState lessThanOrEqual(InstanceState rightOperand) {
+    assertNumOrNull(this);
+    assertNumOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the '&&' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  BoolState logicalAnd(InstanceState rightOperand) {
+    assertBool(this);
+    assertBool(rightOperand);
+    return BoolState.FALSE_STATE;
+  }
+
+  /**
+   * Return the result of invoking the '!' operator on this object.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  BoolState logicalNot() {
+    assertBool(this);
+    return BoolState.TRUE_STATE;
+  }
+
+  /**
+   * Return the result of invoking the '||' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  BoolState logicalOr(InstanceState rightOperand) {
+    assertBool(this);
+    assertBool(rightOperand);
+    return rightOperand.convertToBool();
+  }
+
+  /**
+   * Return the result of invoking the '-' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  NumState minus(InstanceState rightOperand) {
+    assertNumOrNull(this);
+    assertNumOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the '-' operator on this object.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  NumState negated() {
+    assertNumOrNull(this);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the '%' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  NumState remainder(InstanceState rightOperand) {
+    assertNumOrNull(this);
+    assertNumOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the '&lt;&lt;' operator on this object with
+   * the [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  IntState shiftLeft(InstanceState rightOperand) {
+    assertIntOrNull(this);
+    assertIntOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the '&gt;&gt;' operator on this object with
+   * the [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  IntState shiftRight(InstanceState rightOperand) {
+    assertIntOrNull(this);
+    assertIntOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the 'length' getter on this object.
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  IntState stringLength() {
+    assertString(this);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+
+  /**
+   * Return the result of invoking the '*' operator on this object with the
+   * [rightOperand].
+   *
+   * Throws an [EvaluationException] if the operator is not appropriate for an
+   * object of this kind.
+   */
+  NumState times(InstanceState rightOperand) {
+    assertNumOrNull(this);
+    assertNumOrNull(rightOperand);
+    throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT);
+  }
+}
+
+/**
+ * The state of an object representing an int.
+ */
+class IntState extends NumState {
+  /**
+   * A state that can be used to represent an int whose value is not known.
+   */
+  static IntState UNKNOWN_VALUE = new IntState(null);
+
+  /**
+   * The value of this instance.
+   */
+  final int value;
+
+  /**
+   * Initialize a newly created state to represent an int with the given
+   * [value].
+   */
+  IntState(this.value);
+
+  @override
+  bool get hasExactValue => true;
+
+  @override
+  int get hashCode => value == null ? 0 : value.hashCode;
+
+  @override
+  bool get isBoolNumStringOrNull => true;
+
+  @override
+  bool get isUnknown => value == null;
+
+  @override
+  String get typeName => "int";
+
+  @override
+  bool operator ==(Object object) =>
+      object is IntState && (value == object.value);
+
+  @override
+  NumState add(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      if (rightOperand is DoubleState) {
+        return DoubleState.UNKNOWN_VALUE;
+      }
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new IntState(value + rightValue);
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return DoubleState.UNKNOWN_VALUE;
+      }
+      return new DoubleState(value.toDouble() + rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  IntState bitAnd(InstanceState rightOperand) {
+    assertIntOrNull(rightOperand);
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new IntState(value & rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  IntState bitNot() {
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    return new IntState(~value);
+  }
+
+  @override
+  IntState bitOr(InstanceState rightOperand) {
+    assertIntOrNull(rightOperand);
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new IntState(value | rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  IntState bitXor(InstanceState rightOperand) {
+    assertIntOrNull(rightOperand);
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new IntState(value ^ rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  StringState convertToString() {
+    if (value == null) {
+      return StringState.UNKNOWN_VALUE;
+    }
+    return new StringState(value.toString());
+  }
+
+  @override
+  NumState divide(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return DoubleState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return DoubleState.UNKNOWN_VALUE;
+      } else {
+        return new DoubleState(value.toDouble() / rightValue.toDouble());
+      }
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return DoubleState.UNKNOWN_VALUE;
+      }
+      return new DoubleState(value.toDouble() / rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return DoubleState.UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  BoolState equalEqual(InstanceState rightOperand) {
+    assertBoolNumStringOrNull(rightOperand);
+    return isIdentical(rightOperand);
+  }
+
+  @override
+  BoolState greaterThan(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value.compareTo(rightValue) > 0);
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value.toDouble() > rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  BoolState greaterThanOrEqual(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value.compareTo(rightValue) >= 0);
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value.toDouble() >= rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  IntState integerDivide(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      } else if (rightValue == 0) {
+        throw new EvaluationException(
+            CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE);
+      }
+      return new IntState(value ~/ rightValue);
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      double result = value.toDouble() / rightValue;
+      return new IntState(result.toInt());
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  BoolState isIdentical(InstanceState rightOperand) {
+    if (value == null) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value == rightValue);
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(rightValue == value.toDouble());
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    return BoolState.FALSE_STATE;
+  }
+
+  @override
+  BoolState lessThan(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value.compareTo(rightValue) < 0);
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value.toDouble() < rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  BoolState lessThanOrEqual(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value.compareTo(rightValue) <= 0);
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value.toDouble() <= rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  NumState minus(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      if (rightOperand is DoubleState) {
+        return DoubleState.UNKNOWN_VALUE;
+      }
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new IntState(value - rightValue);
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return DoubleState.UNKNOWN_VALUE;
+      }
+      return new DoubleState(value.toDouble() - rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  NumState negated() {
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    return new IntState(-value);
+  }
+
+  @override
+  NumState remainder(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      if (rightOperand is DoubleState) {
+        return DoubleState.UNKNOWN_VALUE;
+      }
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      } else if (rightValue == 0) {
+        return new DoubleState(value.toDouble() % rightValue.toDouble());
+      }
+      return new IntState(value.remainder(rightValue));
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return DoubleState.UNKNOWN_VALUE;
+      }
+      return new DoubleState(value.toDouble() % rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  IntState shiftLeft(InstanceState rightOperand) {
+    assertIntOrNull(rightOperand);
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      } else if (rightValue.bitLength > 31) {
+        return UNKNOWN_VALUE;
+      }
+      return new IntState(value << rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  IntState shiftRight(InstanceState rightOperand) {
+    assertIntOrNull(rightOperand);
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      } else if (rightValue.bitLength > 31) {
+        return UNKNOWN_VALUE;
+      }
+      return new IntState(value >> rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  NumState times(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (value == null) {
+      if (rightOperand is DoubleState) {
+        return DoubleState.UNKNOWN_VALUE;
+      }
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new IntState(value * rightValue);
+    } else if (rightOperand is DoubleState) {
+      double rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return DoubleState.UNKNOWN_VALUE;
+      }
+      return new DoubleState(value.toDouble() * rightValue);
+    } else if (rightOperand is DynamicState || rightOperand is NumState) {
+      return UNKNOWN_VALUE;
+    }
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  String toString() => value == null ? "-unknown-" : value.toString();
+}
+
+/**
+ * The state of an object representing a list.
+ */
+class ListState extends InstanceState {
+  /**
+   * The elements of the list.
+   */
+  final List<DartObjectImpl> _elements;
+
+  /**
+   * Initialize a newly created state to represent a list with the given
+   * [elements].
+   */
+  ListState(this._elements);
+
+  @override
+  bool get hasExactValue {
+    int count = _elements.length;
+    for (int i = 0; i < count; i++) {
+      if (!_elements[i].hasExactValue) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @override
+  int get hashCode {
+    int value = 0;
+    int count = _elements.length;
+    for (int i = 0; i < count; i++) {
+      value = (value << 3) ^ _elements[i].hashCode;
+    }
+    return value;
+  }
+
+  @override
+  String get typeName => "List";
+
+  @override
+  List<Object> get value {
+    int count = _elements.length;
+    List<Object> result = new List<Object>(count);
+    for (int i = 0; i < count; i++) {
+      DartObjectImpl element = _elements[i];
+      if (!element.hasExactValue) {
+        return null;
+      }
+      result[i] = element.value;
+    }
+    return result;
+  }
+
+  @override
+  bool operator ==(Object object) {
+    if (object is! ListState) {
+      return false;
+    }
+    List<DartObjectImpl> otherElements = (object as ListState)._elements;
+    int count = _elements.length;
+    if (otherElements.length != count) {
+      return false;
+    } else if (count == 0) {
+      return true;
+    }
+    for (int i = 0; i < count; i++) {
+      if (_elements[i] != otherElements[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @override
+  StringState convertToString() => StringState.UNKNOWN_VALUE;
+
+  @override
+  BoolState equalEqual(InstanceState rightOperand) {
+    assertBoolNumStringOrNull(rightOperand);
+    return isIdentical(rightOperand);
+  }
+
+  @override
+  BoolState isIdentical(InstanceState rightOperand) {
+    if (rightOperand is DynamicState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    return BoolState.from(this == rightOperand);
+  }
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    buffer.write('[');
+    bool first = true;
+    _elements.forEach((DartObjectImpl element) {
+      if (first) {
+        first = false;
+      } else {
+        buffer.write(', ');
+      }
+      buffer.write(element);
+    });
+    buffer.write(']');
+    return buffer.toString();
+  }
+}
+
+/**
+ * The state of an object representing a map.
+ */
+class MapState extends InstanceState {
+  /**
+   * The entries in the map.
+   */
+  final HashMap<DartObjectImpl, DartObjectImpl> _entries;
+
+  /**
+   * Initialize a newly created state to represent a map with the given
+   * [entries].
+   */
+  MapState(this._entries);
+
+  @override
+  bool get hasExactValue {
+    for (DartObjectImpl key in _entries.keys) {
+      if (!key.hasExactValue || !_entries[key].hasExactValue) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @override
+  int get hashCode {
+    int value = 0;
+    for (DartObjectImpl key in _entries.keys.toSet()) {
+      value = (value << 3) ^ key.hashCode;
+    }
+    return value;
+  }
+
+  @override
+  String get typeName => "Map";
+
+  @override
+  Map<Object, Object> get value {
+    HashMap<Object, Object> result = new HashMap<Object, Object>();
+    for (DartObjectImpl key in _entries.keys) {
+      DartObjectImpl value = _entries[key];
+      if (!key.hasExactValue || !value.hasExactValue) {
+        return null;
+      }
+      result[key.value] = value.value;
+    }
+    return result;
+  }
+
+  @override
+  bool operator ==(Object object) {
+    if (object is! MapState) {
+      return false;
+    }
+    HashMap<DartObjectImpl, DartObjectImpl> otherElements =
+        (object as MapState)._entries;
+    int count = _entries.length;
+    if (otherElements.length != count) {
+      return false;
+    } else if (count == 0) {
+      return true;
+    }
+    for (DartObjectImpl key in _entries.keys) {
+      DartObjectImpl value = _entries[key];
+      DartObjectImpl otherValue = otherElements[key];
+      if (value != otherValue) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @override
+  StringState convertToString() => StringState.UNKNOWN_VALUE;
+
+  @override
+  BoolState equalEqual(InstanceState rightOperand) {
+    assertBoolNumStringOrNull(rightOperand);
+    return isIdentical(rightOperand);
+  }
+
+  @override
+  BoolState isIdentical(InstanceState rightOperand) {
+    if (rightOperand is DynamicState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    return BoolState.from(this == rightOperand);
+  }
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    buffer.write('{');
+    bool first = true;
+    _entries.forEach((DartObjectImpl key, DartObjectImpl value) {
+      if (first) {
+        first = false;
+      } else {
+        buffer.write(', ');
+      }
+      buffer.write(key);
+      buffer.write(' = ');
+      buffer.write(value);
+    });
+    buffer.write('}');
+    return buffer.toString();
+  }
+}
+
+/**
+ * The state of an object representing the value 'null'.
+ */
+class NullState extends InstanceState {
+  /**
+   * An instance representing the boolean value 'null'.
+   */
+  static NullState NULL_STATE = new NullState();
+
+  @override
+  bool get hasExactValue => true;
+
+  @override
+  int get hashCode => 0;
+
+  @override
+  bool get isBoolNumStringOrNull => true;
+
+  @override
+  String get typeName => "Null";
+
+  @override
+  bool operator ==(Object object) => object is NullState;
+
+  @override
+  BoolState convertToBool() {
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  StringState convertToString() => new StringState("null");
+
+  @override
+  BoolState equalEqual(InstanceState rightOperand) {
+    assertBoolNumStringOrNull(rightOperand);
+    return isIdentical(rightOperand);
+  }
+
+  @override
+  BoolState isIdentical(InstanceState rightOperand) {
+    if (rightOperand is DynamicState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    return BoolState.from(rightOperand is NullState);
+  }
+
+  @override
+  BoolState logicalNot() {
+    throw new EvaluationException(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+  }
+
+  @override
+  String toString() => "null";
+}
+
+/**
+ * The state of an object representing a number of an unknown type (a 'num').
+ */
+class NumState extends InstanceState {
+  /**
+   * A state that can be used to represent a number whose value is not known.
+   */
+  static NumState UNKNOWN_VALUE = new NumState();
+
+  @override
+  int get hashCode => 7;
+
+  @override
+  bool get isBoolNumStringOrNull => true;
+
+  @override
+  bool get isUnknown => identical(this, UNKNOWN_VALUE);
+
+  @override
+  String get typeName => "num";
+
+  @override
+  bool operator ==(Object object) => object is NumState;
+
+  @override
+  NumState add(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return UNKNOWN_VALUE;
+  }
+
+  @override
+  StringState convertToString() => StringState.UNKNOWN_VALUE;
+
+  @override
+  NumState divide(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return DoubleState.UNKNOWN_VALUE;
+  }
+
+  @override
+  BoolState equalEqual(InstanceState rightOperand) {
+    assertBoolNumStringOrNull(rightOperand);
+    return BoolState.UNKNOWN_VALUE;
+  }
+
+  @override
+  BoolState greaterThan(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return BoolState.UNKNOWN_VALUE;
+  }
+
+  @override
+  BoolState greaterThanOrEqual(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return BoolState.UNKNOWN_VALUE;
+  }
+
+  @override
+  IntState integerDivide(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    if (rightOperand is IntState) {
+      int rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return IntState.UNKNOWN_VALUE;
+      } else if (rightValue == 0) {
+        throw new EvaluationException(
+            CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE);
+      }
+    } else if (rightOperand is DynamicState) {
+      return IntState.UNKNOWN_VALUE;
+    }
+    return IntState.UNKNOWN_VALUE;
+  }
+
+  @override
+  BoolState isIdentical(InstanceState rightOperand) {
+    return BoolState.UNKNOWN_VALUE;
+  }
+
+  @override
+  BoolState lessThan(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return BoolState.UNKNOWN_VALUE;
+  }
+
+  @override
+  BoolState lessThanOrEqual(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return BoolState.UNKNOWN_VALUE;
+  }
+
+  @override
+  NumState minus(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return UNKNOWN_VALUE;
+  }
+
+  @override
+  NumState negated() => UNKNOWN_VALUE;
+
+  @override
+  NumState remainder(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return UNKNOWN_VALUE;
+  }
+
+  @override
+  NumState times(InstanceState rightOperand) {
+    assertNumOrNull(rightOperand);
+    return UNKNOWN_VALUE;
+  }
+
+  @override
+  String toString() => "-unknown-";
+}
+
+/**
+ * An object used to add reference information for a given variable to the
+ * bi-directional mapping used to order the evaluation of constants.
+ */
+class ReferenceFinder extends RecursiveAstVisitor<Object> {
+  /**
+   * The element representing the construct that will be visited.
+   */
+  final AstNode _source;
+
+  /**
+   * A graph in which the nodes are the constant variables and the edges are
+   * from each variable to the other constant variables that are referenced in
+   * the head's initializer.
+   */
+  final DirectedGraph<AstNode> _referenceGraph;
+
+  /**
+   * A table mapping constant variables to the declarations of those variables.
+   */
+  final HashMap<PotentiallyConstVariableElement, VariableDeclaration> _variableDeclarationMap;
+
+  /**
+   * A table mapping constant constructors to the declarations of those
+   * constructors.
+   */
+  final HashMap<ConstructorElement, ConstructorDeclaration> _constructorDeclarationMap;
+
+  /**
+   * Initialize a newly created reference finder to find references from a given
+   * variable to other variables and to add those references to the given graph.
+   * The [source] is the element representing the variable whose initializer
+   * will be visited. The [referenceGraph] is a graph recording which variables
+   * (heads) reference which other variables (tails) in their initializers. The
+   * [variableDeclarationMap] is a table mapping constant variables to the
+   * declarations of those variables. The [constructorDeclarationMap] is a table
+   * mapping constant constructors to the declarations of those constructors.
+   */
+  ReferenceFinder(this._source, this._referenceGraph,
+      this._variableDeclarationMap, this._constructorDeclarationMap);
+
+  @override
+  Object visitInstanceCreationExpression(InstanceCreationExpression node) {
+    if (node.isConst) {
+      _referenceGraph.addEdge(_source, node);
+    }
+    return null;
+  }
+
+  @override
+  Object visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    super.visitRedirectingConstructorInvocation(node);
+    ConstructorElement target = node.staticElement;
+    if (target != null && target.isConst) {
+      ConstructorDeclaration targetDeclaration =
+          _constructorDeclarationMap[target];
+      if (targetDeclaration != null) {
+        _referenceGraph.addEdge(_source, targetDeclaration);
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitSimpleIdentifier(SimpleIdentifier node) {
+    Element element = node.staticElement;
+    if (element is PropertyAccessorElement) {
+      element = (element as PropertyAccessorElement).variable;
+    }
+    if (element is VariableElement) {
+      if (element.isConst) {
+        VariableDeclaration variableDeclaration =
+            _variableDeclarationMap[element];
+        // The declaration will be null when the variable is not defined in the
+        // compilation units that were used to produce the
+        // variableDeclarationMap.  In such cases, the variable should already
+        // have a value associated with it, but we don't bother to check because
+        // there's nothing we can do about it at this point.
+        if (variableDeclaration != null) {
+          _referenceGraph.addEdge(_source, variableDeclaration);
+        }
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    super.visitSuperConstructorInvocation(node);
+    ConstructorElement constructor = node.staticElement;
+    if (constructor != null && constructor.isConst) {
+      ConstructorDeclaration constructorDeclaration =
+          _constructorDeclarationMap[constructor];
+      // The declaration will be null when the constructor is not defined in the
+      // compilation units that were used to produce the
+      // constructorDeclarationMap.  In such cases, the constructor should
+      // already have its initializer AST's stored in it, but we don't bother
+      // to check because there's nothing we can do about it at this point.
+      if (constructorDeclaration != null) {
+        _referenceGraph.addEdge(_source, constructorDeclaration);
+      }
+    }
+    return null;
+  }
+}
+
+/**
+ * The state of an object representing a string.
+ */
+class StringState extends InstanceState {
+  /**
+   * A state that can be used to represent a double whose value is not known.
+   */
+  static StringState UNKNOWN_VALUE = new StringState(null);
+
+  /**
+   * The value of this instance.
+   */
+  final String value;
+
+  /**
+   * Initialize a newly created state to represent the given [value].
+   */
+  StringState(this.value);
+
+  @override
+  bool get hasExactValue => true;
+
+  @override
+  int get hashCode => value == null ? 0 : value.hashCode;
+
+  @override
+  bool get isBoolNumStringOrNull => true;
+
+  @override
+  bool get isUnknown => value == null;
+
+  @override
+  String get typeName => "String";
+
+  @override
+  bool operator ==(Object object) =>
+      object is StringState && (value == object.value);
+
+  @override
+  StringState concatenate(InstanceState rightOperand) {
+    if (value == null) {
+      return UNKNOWN_VALUE;
+    }
+    if (rightOperand is StringState) {
+      String rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return UNKNOWN_VALUE;
+      }
+      return new StringState("$value$rightValue");
+    } else if (rightOperand is DynamicState) {
+      return UNKNOWN_VALUE;
+    }
+    return super.concatenate(rightOperand);
+  }
+
+  @override
+  StringState convertToString() => this;
+
+  @override
+  BoolState equalEqual(InstanceState rightOperand) {
+    assertBoolNumStringOrNull(rightOperand);
+    return isIdentical(rightOperand);
+  }
+
+  @override
+  BoolState isIdentical(InstanceState rightOperand) {
+    if (value == null) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is StringState) {
+      String rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value == rightValue);
+    } else if (rightOperand is DynamicState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    return BoolState.FALSE_STATE;
+  }
+
+  @override
+  IntState stringLength() {
+    if (value == null) {
+      return IntState.UNKNOWN_VALUE;
+    }
+    return new IntState(value.length);
+  }
+
+  @override
+  String toString() => value == null ? "-unknown-" : "'$value'";
+}
+
+/**
+ * The state of an object representing a symbol.
+ */
+class SymbolState extends InstanceState {
+  /**
+   * The value of this instance.
+   */
+  final String value;
+
+  /**
+   * Initialize a newly created state to represent the given [value].
+   */
+  SymbolState(this.value);
+
+  @override
+  bool get hasExactValue => true;
+
+  @override
+  int get hashCode => value == null ? 0 : value.hashCode;
+
+  @override
+  String get typeName => "Symbol";
+
+  @override
+  bool operator ==(Object object) =>
+      object is SymbolState && (value == object.value);
+
+  @override
+  StringState convertToString() {
+    if (value == null) {
+      return StringState.UNKNOWN_VALUE;
+    }
+    return new StringState(value);
+  }
+
+  @override
+  BoolState equalEqual(InstanceState rightOperand) {
+    assertBoolNumStringOrNull(rightOperand);
+    return isIdentical(rightOperand);
+  }
+
+  @override
+  BoolState isIdentical(InstanceState rightOperand) {
+    if (value == null) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is SymbolState) {
+      String rightValue = rightOperand.value;
+      if (rightValue == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(value == rightValue);
+    } else if (rightOperand is DynamicState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    return BoolState.FALSE_STATE;
+  }
+
+  @override
+  String toString() => value == null ? "-unknown-" : "#$value";
+}
+
+/**
+ * The state of an object representing a type.
+ */
+class TypeState extends InstanceState {
+  /**
+   * The element representing the type being modeled.
+   */
+  final Element _element;
+
+  /**
+   * Initialize a newly created state to represent the given [value].
+   */
+  TypeState(this._element);
+
+  @override
+  int get hashCode => _element == null ? 0 : _element.hashCode;
+
+  @override
+  String get typeName => "Type";
+
+  @override
+  Element get value => _element;
+
+  @override
+  bool operator ==(Object object) =>
+      object is TypeState && (_element == object._element);
+
+  @override
+  StringState convertToString() {
+    if (_element == null) {
+      return StringState.UNKNOWN_VALUE;
+    }
+    return new StringState(_element.name);
+  }
+
+  @override
+  BoolState equalEqual(InstanceState rightOperand) {
+    assertBoolNumStringOrNull(rightOperand);
+    return isIdentical(rightOperand);
+  }
+
+  @override
+  BoolState isIdentical(InstanceState rightOperand) {
+    if (_element == null) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    if (rightOperand is TypeState) {
+      Element rightElement = rightOperand._element;
+      if (rightElement == null) {
+        return BoolState.UNKNOWN_VALUE;
+      }
+      return BoolState.from(_element == rightElement);
+    } else if (rightOperand is DynamicState) {
+      return BoolState.UNKNOWN_VALUE;
+    }
+    return BoolState.FALSE_STATE;
+  }
+
+  @override
+  String toString() => _element == null ? "-unknown-" : _element.name;
+}
diff --git a/analyzer/lib/src/generated/element.dart b/analyzer/lib/src/generated/element.dart
new file mode 100644
index 0000000..f583c37
--- /dev/null
+++ b/analyzer/lib/src/generated/element.dart
@@ -0,0 +1,10336 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.element;
+
+import 'dart:collection';
+
+import 'package:analyzer/src/generated/utilities_general.dart';
+import 'package:analyzer/task/model.dart' show AnalysisTarget;
+
+import 'ast.dart';
+import 'constant.dart' show EvaluationResultImpl;
+import 'engine.dart' show AnalysisContext, AnalysisEngine, AnalysisException;
+import 'html.dart' show XmlAttributeNode, XmlTagNode;
+import 'java_core.dart';
+import 'java_engine.dart';
+import 'resolver.dart';
+import 'scanner.dart' show Keyword;
+import 'sdk.dart' show DartSdk;
+import 'source.dart';
+import 'utilities_collection.dart';
+import 'utilities_dart.dart';
+
+/**
+ * For AST nodes that could be in both the getter and setter contexts
+ * ([IndexExpression]s and [SimpleIdentifier]s), the additional resolved
+ * elements are stored in the AST node, in an [AuxiliaryElements]. Because
+ * resolved elements are either statically resolved or resolved using propagated
+ * type information, this class is a wrapper for a pair of [ExecutableElement]s,
+ * not just a single [ExecutableElement].
+ */
+class AuxiliaryElements {
+  /**
+   * The element based on propagated type information, or `null` if the AST
+   * structure has not been resolved or if the node could not be resolved.
+   */
+  final ExecutableElement propagatedElement;
+
+  /**
+   * The element based on static type information, or `null` if the AST
+   * structure has not been resolved or if the node could not be resolved.
+   */
+  final ExecutableElement staticElement;
+
+  /**
+   * Initialize a newly created pair to have both the [staticElement] and the
+   * [propagatedElement].
+   */
+  AuxiliaryElements(this.staticElement, this.propagatedElement);
+}
+
+/**
+ * A [Type] that represents the type 'bottom'.
+ */
+class BottomTypeImpl extends TypeImpl {
+  /**
+   * The unique instance of this class.
+   */
+  static BottomTypeImpl _INSTANCE = new BottomTypeImpl._();
+
+  /**
+   * Return the unique instance of this class.
+   */
+  static BottomTypeImpl get instance => _INSTANCE;
+
+  /**
+   * Prevent the creation of instances of this class.
+   */
+  BottomTypeImpl._() : super(null, "<bottom>");
+
+  @override
+  int get hashCode => 0;
+
+  @override
+  bool get isBottom => true;
+
+  @override
+  bool operator ==(Object object) => identical(object, this);
+
+  @override
+  bool internalEquals(Object object, Set<ElementPair> visitedElementPairs) =>
+      identical(object, this);
+
+  @override
+  int internalHashCode(List<DartType> visitedTypes) => hashCode;
+
+  @override
+  bool isMoreSpecificThan(DartType type, [Set<Element> thisExpansions,
+      Set<Element> typeExpansions, bool withDynamic = false,
+      Set<Element> visitedElements]) => true;
+
+  @override
+  bool isSubtypeOf(DartType type,
+      [Set<Element> thisExpansions, Set<Element> typeExpansions]) => true;
+
+  @override
+  bool isSupertypeOf(DartType type) => false;
+
+  @override
+  BottomTypeImpl substitute2(
+      List<DartType> argumentTypes, List<DartType> parameterTypes) => this;
+}
+
+/**
+ * An element that represents a class.
+ */
+abstract class ClassElement implements Element {
+  /**
+   * An empty list of class elements.
+   */
+  static const List<ClassElement> EMPTY_LIST = const <ClassElement>[];
+
+  /**
+   * Return a list containing all of the accessors (getters and setters)
+   * declared in this class.
+   */
+  List<PropertyAccessorElement> get accessors;
+
+  /**
+   * Return a list containing all the supertypes defined for this class and its
+   * supertypes. This includes superclasses, mixins and interfaces.
+   */
+  List<InterfaceType> get allSupertypes;
+
+  /**
+   * Return a list containing all of the constructors declared in this class.
+   */
+  List<ConstructorElement> get constructors;
+
+  /**
+   * Return a list containing all of the fields declared in this class.
+   */
+  List<FieldElement> get fields;
+
+  /**
+   * Return `true` if this class or its superclass declares a non-final instance
+   * field.
+   */
+  bool get hasNonFinalField;
+
+  /**
+   * Return `true` if this class has reference to super (so, for example, cannot
+   * be used as a mixin).
+   */
+  bool get hasReferenceToSuper;
+
+  /**
+   * Return `true` if this class declares a static member.
+   */
+  bool get hasStaticMember;
+
+  /**
+   * Return a list containing all of the interfaces that are implemented by this
+   * class.
+   *
+   * <b>Note:</b> Because the element model represents the state of the code, it
+   * is possible for it to be semantically invalid. In particular, it is not
+   * safe to assume that the inheritance structure of a class does not contain a
+   * cycle. Clients that traverse the inheritance structure must explicitly
+   * guard against infinite loops.
+   */
+  List<InterfaceType> get interfaces;
+
+  /**
+   * Return `true` if this class is abstract. A class is abstract if it has an
+   * explicit `abstract` modifier. Note, that this definition of <i>abstract</i>
+   * is different from <i>has unimplemented members</i>.
+   */
+  bool get isAbstract;
+
+  /**
+   * Return `true` if this class is defined by an enum declaration.
+   */
+  bool get isEnum;
+
+  /**
+   * Return `true` if this class [isProxy], or if it inherits the proxy
+   * annotation from a supertype.
+   */
+  bool get isOrInheritsProxy;
+
+  /**
+   * Return `true` if this element has an annotation of the form '@proxy'.
+   */
+  bool get isProxy;
+
+  /**
+   * Return `true` if this class is defined by a typedef construct.
+   */
+  bool get isTypedef;
+
+  /**
+   * Return `true` if this class can validly be used as a mixin when defining
+   * another class. The behavior of this method is defined by the Dart Language
+   * Specification in section 9:
+   * <blockquote>
+   * It is a compile-time error if a declared or derived mixin refers to super.
+   * It is a compile-time error if a declared or derived mixin explicitly
+   * declares a constructor. It is a compile-time error if a mixin is derived
+   * from a class whose superclass is not Object.
+   * </blockquote>
+   */
+  bool get isValidMixin;
+
+  /**
+   * Return a list containing all of the methods declared in this class.
+   */
+  List<MethodElement> get methods;
+
+  /**
+   * Return a list containing all of the mixins that are applied to the class
+   * being extended in order to derive the superclass of this class.
+   *
+   * <b>Note:</b> Because the element model represents the state of the code, it
+   * is possible for it to be semantically invalid. In particular, it is not
+   * safe to assume that the inheritance structure of a class does not contain a
+   * cycle. Clients that traverse the inheritance structure must explicitly
+   * guard against infinite loops.
+   */
+  List<InterfaceType> get mixins;
+
+  /**
+   * Return the resolved [ClassDeclaration] or [EnumDeclaration] node that
+   * declares this [ClassElement].
+   *
+   * This method is expensive, because resolved AST might be evicted from cache,
+   * so parsing and resolving will be performed.
+   */
+  @override
+  NamedCompilationUnitMember get node;
+
+  /**
+   * Return the superclass of this class, or `null` if the class represents the
+   * class 'Object'. All other classes will have a non-`null` superclass. If the
+   * superclass was not explicitly declared then the implicit superclass
+   * 'Object' will be returned.
+   *
+   * <b>Note:</b> Because the element model represents the state of the code, it
+   * is possible for it to be semantically invalid. In particular, it is not
+   * safe to assume that the inheritance structure of a class does not contain a
+   * cycle. Clients that traverse the inheritance structure must explicitly
+   * guard against infinite loops.
+   */
+  InterfaceType get supertype;
+
+  /**
+   * Return the type defined by the class.
+   */
+  InterfaceType get type;
+
+  /**
+   * Return a list containing all of the type parameters declared for this
+   * class.
+   */
+  List<TypeParameterElement> get typeParameters;
+
+  /**
+   * Return the unnamed constructor declared in this class, or `null` if this
+   * class does not declare an unnamed constructor but does declare named
+   * constructors. The returned constructor will be synthetic if this class does
+   * not declare any constructors, in which case it will represent the default
+   * constructor for the class.
+   */
+  ConstructorElement get unnamedConstructor;
+
+  /**
+   * Return the field (synthetic or explicit) defined in this class that has the
+   * given [name], or `null` if this class does not define a field with the
+   * given name.
+   */
+  FieldElement getField(String name);
+
+  /**
+   * Return the element representing the getter with the given [name] that is
+   * declared in this class, or `null` if this class does not declare a getter
+   * with the given name.
+   */
+  PropertyAccessorElement getGetter(String name);
+
+  /**
+   * Return the element representing the method with the given [name] that is
+   * declared in this class, or `null` if this class does not declare a method
+   * with the given name.
+   */
+  MethodElement getMethod(String name);
+
+  /**
+   * Return the named constructor declared in this class with the given [name],
+   * or `null` if this class does not declare a named constructor with the given
+   * name.
+   */
+  ConstructorElement getNamedConstructor(String name);
+
+  /**
+   * Return the element representing the setter with the given [name] that is
+   * declared in this class, or `null` if this class does not declare a setter
+   * with the given name.
+   */
+  PropertyAccessorElement getSetter(String name);
+
+  /**
+   * Determine whether the given [constructor], which exists in the superclass
+   * of this class, is accessible to constructors in this class.
+   */
+  bool isSuperConstructorAccessible(ConstructorElement constructor);
+
+  /**
+   * Return the element representing the method that results from looking up the
+   * given [methodName] in this class with respect to the given [library],
+   * ignoring abstract methods, or `null` if the look up fails. The behavior of
+   * this method is defined by the Dart Language Specification in section
+   * 16.15.1:
+   * <blockquote>
+   * The result of looking up method <i>m</i> in class <i>C</i> with respect to
+   * library <i>L</i> is: If <i>C</i> declares an instance method named <i>m</i>
+   * that is accessible to <i>L</i>, then that method is the result of the
+   * lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the result
+   * of the lookup is the result of looking up method <i>m</i> in <i>S</i> with
+   * respect to <i>L</i>. Otherwise, we say that the lookup has failed.
+   * </blockquote>
+   */
+  MethodElement lookUpConcreteMethod(String methodName, LibraryElement library);
+
+  /**
+   * Return the element representing the getter that results from looking up the
+   * given [getterName] in this class with respect to the given [library], or
+   * `null` if the look up fails. The behavior of this method is defined by the
+   * Dart Language Specification in section 16.15.2:
+   * <blockquote>
+   * The result of looking up getter (respectively setter) <i>m</i> in class
+   * <i>C</i> with respect to library <i>L</i> is: If <i>C</i> declares an
+   * instance getter (respectively setter) named <i>m</i> that is accessible to
+   * <i>L</i>, then that getter (respectively setter) is the result of the
+   * lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the result
+   * of the lookup is the result of looking up getter (respectively setter)
+   * <i>m</i> in <i>S</i> with respect to <i>L</i>. Otherwise, we say that the
+   * lookup has failed.
+   * </blockquote>
+   */
+  PropertyAccessorElement lookUpGetter(
+      String getterName, LibraryElement library);
+
+  /**
+   * Return the element representing the getter that results from looking up the
+   * given [getterName] in the superclass of this class with respect to the
+   * given [library], ignoring abstract getters, or `null` if the look up fails.
+   * The behavior of this method is defined by the Dart Language Specification
+   * in section 16.15.2:
+   * <blockquote>
+   * The result of looking up getter (respectively setter) <i>m</i> in class
+   * <i>C</i> with respect to library <i>L</i> is: If <i>C</i> declares an
+   * instance getter (respectively setter) named <i>m</i> that is accessible to
+   * <i>L</i>, then that getter (respectively setter) is the result of the
+   * lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the result
+   * of the lookup is the result of looking up getter (respectively setter)
+   * <i>m</i> in <i>S</i> with respect to <i>L</i>. Otherwise, we say that the
+   * lookup has failed.
+   * </blockquote>
+   */
+  PropertyAccessorElement lookUpInheritedConcreteGetter(
+      String getterName, LibraryElement library);
+
+  /**
+   * Return the element representing the method that results from looking up the
+   * given [methodName] in the superclass of this class with respect to the
+   * given [library], ignoring abstract methods, or `null` if the look up fails.
+   * The behavior of this method is defined by the Dart Language Specification
+   * in section 16.15.1:
+   * <blockquote>
+   * The result of looking up method <i>m</i> in class <i>C</i> with respect to
+   * library <i>L</i> is:  If <i>C</i> declares an instance method named
+   * <i>m</i> that is accessible to <i>L</i>, then that method is the result of
+   * the lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the
+   * result of the lookup is the result of looking up method <i>m</i> in
+   * <i>S</i> with respect to <i>L</i>. Otherwise, we say that the lookup has
+   * failed.
+   * </blockquote>
+   */
+  MethodElement lookUpInheritedConcreteMethod(
+      String methodName, LibraryElement library);
+
+  /**
+   * Return the element representing the setter that results from looking up the
+   * given [setterName] in the superclass of this class with respect to the
+   * given [library], ignoring abstract setters, or `null` if the look up fails.
+   * The behavior of this method is defined by the Dart Language Specification
+   * in section 16.15.2:
+   * <blockquote>
+   * The result of looking up getter (respectively setter) <i>m</i> in class
+   * <i>C</i> with respect to library <i>L</i> is:  If <i>C</i> declares an
+   * instance getter (respectively setter) named <i>m</i> that is accessible to
+   * <i>L</i>, then that getter (respectively setter) is the result of the
+   * lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the result
+   * of the lookup is the result of looking up getter (respectively setter)
+   * <i>m</i> in <i>S</i> with respect to <i>L</i>. Otherwise, we say that the
+   * lookup has failed.
+   * </blockquote>
+   */
+  PropertyAccessorElement lookUpInheritedConcreteSetter(
+      String setterName, LibraryElement library);
+
+  /**
+   * Return the element representing the method that results from looking up the
+   * given [methodName] in the superclass of this class with respect to the
+   * given [library], or `null` if the look up fails. The behavior of this
+   * method is defined by the Dart Language Specification in section 16.15.1:
+   * <blockquote>
+   * The result of looking up method <i>m</i> in class <i>C</i> with respect to
+   * library <i>L</i> is:  If <i>C</i> declares an instance method named
+   * <i>m</i> that is accessible to <i>L</i>, then that method is the result of
+   * the lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the
+   * result of the lookup is the result of looking up method <i>m</i> in
+   * <i>S</i> with respect to <i>L</i>. Otherwise, we say that the lookup has
+   * failed.
+   * </blockquote>
+   */
+  MethodElement lookUpInheritedMethod(
+      String methodName, LibraryElement library);
+
+  /**
+   * Return the element representing the method that results from looking up the
+   * given [methodName] in this class with respect to the given [library], or
+   * `null` if the look up fails. The behavior of this method is defined by the
+   * Dart Language Specification in section 16.15.1:
+   * <blockquote>
+   * The result of looking up method <i>m</i> in class <i>C</i> with respect to
+   * library <i>L</i> is:  If <i>C</i> declares an instance method named
+   * <i>m</i> that is accessible to <i>L</i>, then that method is the result of
+   * the lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the
+   * result of the lookup is the result of looking up method <i>m</i> in
+   * <i>S</i> with respect to <i>L</i>. Otherwise, we say that the lookup has
+   * failed.
+   * </blockquote>
+   */
+  MethodElement lookUpMethod(String methodName, LibraryElement library);
+
+  /**
+   * Return the element representing the setter that results from looking up the
+   * given [setterName] in this class with respect to the given [library], or
+   * `null` if the look up fails. The behavior of this method is defined by the
+   * Dart Language Specification in section 16.15.2:
+   * <blockquote>
+   * The result of looking up getter (respectively setter) <i>m</i> in class
+   * <i>C</i> with respect to library <i>L</i> is: If <i>C</i> declares an
+   * instance getter (respectively setter) named <i>m</i> that is accessible to
+   * <i>L</i>, then that getter (respectively setter) is the result of the
+   * lookup. Otherwise, if <i>C</i> has a superclass <i>S</i>, then the result
+   * of the lookup is the result of looking up getter (respectively setter)
+   * <i>m</i> in <i>S</i> with respect to <i>L</i>. Otherwise, we say that the
+   * lookup has failed.
+   * </blockquote>
+   */
+  PropertyAccessorElement lookUpSetter(
+      String setterName, LibraryElement library);
+}
+
+/**
+ * A concrete implementation of a [ClassElement].
+ */
+class ClassElementImpl extends ElementImpl implements ClassElement {
+  /**
+   * An empty list of class elements.
+   */
+  @deprecated // Use ClassElement.EMPTY_LIST
+  static const List<ClassElement> EMPTY_ARRAY = const <ClassElement>[];
+
+  /**
+   * A list containing all of the accessors (getters and setters) contained in
+   * this class.
+   */
+  List<PropertyAccessorElement> _accessors = PropertyAccessorElement.EMPTY_LIST;
+
+  /**
+   * A list containing all of the constructors contained in this class.
+   */
+  List<ConstructorElement> _constructors = ConstructorElement.EMPTY_LIST;
+
+  /**
+   * A list containing all of the fields contained in this class.
+   */
+  List<FieldElement> _fields = FieldElement.EMPTY_LIST;
+
+  /**
+   * A list containing all of the mixins that are applied to the class being
+   * extended in order to derive the superclass of this class.
+   */
+  List<InterfaceType> mixins = InterfaceType.EMPTY_LIST;
+
+  /**
+   * A list containing all of the interfaces that are implemented by this class.
+   */
+  List<InterfaceType> interfaces = InterfaceType.EMPTY_LIST;
+
+  /**
+   * A list containing all of the methods contained in this class.
+   */
+  List<MethodElement> _methods = MethodElement.EMPTY_LIST;
+
+  /**
+   * The superclass of the class, or `null` if the class does not have an
+   * explicit superclass.
+   */
+  InterfaceType supertype;
+
+  /**
+   * The type defined by the class.
+   */
+  InterfaceType type;
+
+  /**
+   * A list containing all of the type parameters defined for this class.
+   */
+  List<TypeParameterElement> _typeParameters = TypeParameterElement.EMPTY_LIST;
+
+  /**
+   * The [SourceRange] of the `with` clause, `null` if there is no one.
+   */
+  SourceRange withClauseRange;
+
+  /**
+   * Initialize a newly created class element to have the given [name] at the
+   * given [offset] in the file that contains the declaration of this element.
+   */
+  ClassElementImpl(String name, int offset) : super(name, offset);
+
+  /**
+   * Initialize a newly created class element to have the given [name].
+   */
+  ClassElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  /**
+   * Set whether this class is abstract.
+   */
+  void set abstract(bool isAbstract) {
+    setModifier(Modifier.ABSTRACT, isAbstract);
+  }
+
+  @override
+  List<PropertyAccessorElement> get accessors => _accessors;
+
+  /**
+   * Set the accessors contained in this class to the given [accessors].
+   */
+  void set accessors(List<PropertyAccessorElement> accessors) {
+    for (PropertyAccessorElement accessor in accessors) {
+      (accessor as PropertyAccessorElementImpl).enclosingElement = this;
+    }
+    this._accessors = accessors;
+  }
+
+  @override
+  List<InterfaceType> get allSupertypes {
+    List<InterfaceType> list = new List<InterfaceType>();
+    _collectAllSupertypes(list);
+    return list;
+  }
+
+  @override
+  List<ConstructorElement> get constructors => _constructors;
+
+  /**
+   * Set the constructors contained in this class to the given [constructors].
+   */
+  void set constructors(List<ConstructorElement> constructors) {
+    for (ConstructorElement constructor in constructors) {
+      (constructor as ConstructorElementImpl).enclosingElement = this;
+    }
+    this._constructors = constructors;
+  }
+
+  /**
+   * Set whether this class is defined by an enum declaration.
+   */
+  void set enum2(bool isEnum) {
+    setModifier(Modifier.ENUM, isEnum);
+  }
+
+  @override
+  List<FieldElement> get fields => _fields;
+
+  /**
+   * Set the fields contained in this class to the given [fields].
+   */
+  void set fields(List<FieldElement> fields) {
+    for (FieldElement field in fields) {
+      (field as FieldElementImpl).enclosingElement = this;
+    }
+    this._fields = fields;
+  }
+
+  @override
+  bool get hasNonFinalField {
+    List<ClassElement> classesToVisit = new List<ClassElement>();
+    HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
+    classesToVisit.add(this);
+    while (!classesToVisit.isEmpty) {
+      ClassElement currentElement = classesToVisit.removeAt(0);
+      if (visitedClasses.add(currentElement)) {
+        // check fields
+        for (FieldElement field in currentElement.fields) {
+          if (!field.isFinal &&
+              !field.isConst &&
+              !field.isStatic &&
+              !field.isSynthetic) {
+            return true;
+          }
+        }
+        // check mixins
+        for (InterfaceType mixinType in currentElement.mixins) {
+          ClassElement mixinElement = mixinType.element;
+          classesToVisit.add(mixinElement);
+        }
+        // check super
+        InterfaceType supertype = currentElement.supertype;
+        if (supertype != null) {
+          ClassElement superElement = supertype.element;
+          if (superElement != null) {
+            classesToVisit.add(superElement);
+          }
+        }
+      }
+    }
+    // not found
+    return false;
+  }
+
+  @override
+  bool get hasReferenceToSuper => hasModifier(Modifier.REFERENCES_SUPER);
+
+  /**
+   * Set whether this class references 'super'.
+   */
+  void set hasReferenceToSuper(bool isReferencedSuper) {
+    setModifier(Modifier.REFERENCES_SUPER, isReferencedSuper);
+  }
+
+  @override
+  bool get hasStaticMember {
+    for (MethodElement method in _methods) {
+      if (method.isStatic) {
+        return true;
+      }
+    }
+    for (PropertyAccessorElement accessor in _accessors) {
+      if (accessor.isStatic) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @override
+  bool get isAbstract => hasModifier(Modifier.ABSTRACT);
+
+  @override
+  bool get isEnum => hasModifier(Modifier.ENUM);
+
+  @override
+  bool get isOrInheritsProxy =>
+      _safeIsOrInheritsProxy(this, new HashSet<ClassElement>());
+
+  @override
+  bool get isProxy {
+    for (ElementAnnotation annotation in metadata) {
+      if (annotation.isProxy) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @override
+  bool get isTypedef => hasModifier(Modifier.TYPEDEF);
+
+  @override
+  bool get isValidMixin => hasModifier(Modifier.MIXIN);
+
+  @override
+  ElementKind get kind => ElementKind.CLASS;
+
+  @override
+  List<MethodElement> get methods => _methods;
+
+  /**
+   * Set the methods contained in this class to the given [methods].
+   */
+  void set methods(List<MethodElement> methods) {
+    for (MethodElement method in methods) {
+      (method as MethodElementImpl).enclosingElement = this;
+    }
+    this._methods = methods;
+  }
+
+  bool get mixinErrorsReported => hasModifier(Modifier.MIXIN_ERRORS_REPORTED);
+
+  /**
+   * Set whether an error has reported explaining why this class is an
+   * invalid mixin application.
+   */
+  void set mixinErrorsReported(bool value) {
+    setModifier(Modifier.MIXIN_ERRORS_REPORTED, value);
+  }
+
+  @override
+  NamedCompilationUnitMember get node {
+    if (isEnum) {
+      return getNodeMatching((node) => node is EnumDeclaration);
+    } else {
+      return getNodeMatching(
+          (node) => node is ClassDeclaration || node is ClassTypeAlias);
+    }
+  }
+
+  /**
+   * Set whether this class is defined by a typedef construct.
+   */
+  void set typedef(bool isTypedef) {
+    setModifier(Modifier.TYPEDEF, isTypedef);
+  }
+
+  @override
+  List<TypeParameterElement> get typeParameters => _typeParameters;
+
+  /**
+   * Set the type parameters defined for this class to the given
+   * [typeParameters].
+   */
+  void set typeParameters(List<TypeParameterElement> typeParameters) {
+    for (TypeParameterElement typeParameter in typeParameters) {
+      (typeParameter as TypeParameterElementImpl).enclosingElement = this;
+    }
+    this._typeParameters = typeParameters;
+  }
+
+  @override
+  ConstructorElement get unnamedConstructor {
+    for (ConstructorElement element in constructors) {
+      String name = element.displayName;
+      if (name == null || name.isEmpty) {
+        return element;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Set whether this class is a valid mixin.
+   */
+  void set validMixin(bool isValidMixin) {
+    setModifier(Modifier.MIXIN, isValidMixin);
+  }
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitClassElement(this);
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    String name = displayName;
+    if (name == null) {
+      buffer.write("{unnamed class}");
+    } else {
+      buffer.write(name);
+    }
+    int variableCount = _typeParameters.length;
+    if (variableCount > 0) {
+      buffer.write("<");
+      for (int i = 0; i < variableCount; i++) {
+        if (i > 0) {
+          buffer.write(", ");
+        }
+        (_typeParameters[i] as TypeParameterElementImpl).appendTo(buffer);
+      }
+      buffer.write(">");
+    }
+  }
+
+  @override
+  ElementImpl getChild(String identifier) {
+    //
+    // The casts in this method are safe because the set methods would have
+    // thrown a CCE if any of the elements in the arrays were not of the
+    // expected types.
+    //
+    for (PropertyAccessorElement accessor in _accessors) {
+      if ((accessor as PropertyAccessorElementImpl).identifier == identifier) {
+        return accessor as PropertyAccessorElementImpl;
+      }
+    }
+    for (ConstructorElement constructor in _constructors) {
+      if ((constructor as ConstructorElementImpl).identifier == identifier) {
+        return constructor as ConstructorElementImpl;
+      }
+    }
+    for (FieldElement field in _fields) {
+      if ((field as FieldElementImpl).identifier == identifier) {
+        return field as FieldElementImpl;
+      }
+    }
+    for (MethodElement method in _methods) {
+      if ((method as MethodElementImpl).identifier == identifier) {
+        return method as MethodElementImpl;
+      }
+    }
+    for (TypeParameterElement typeParameter in _typeParameters) {
+      if ((typeParameter as TypeParameterElementImpl).identifier ==
+          identifier) {
+        return typeParameter as TypeParameterElementImpl;
+      }
+    }
+    return null;
+  }
+
+  @override
+  FieldElement getField(String name) {
+    for (FieldElement fieldElement in _fields) {
+      if (name == fieldElement.name) {
+        return fieldElement;
+      }
+    }
+    return null;
+  }
+
+  @override
+  PropertyAccessorElement getGetter(String getterName) {
+    for (PropertyAccessorElement accessor in _accessors) {
+      if (accessor.isGetter && accessor.name == getterName) {
+        return accessor;
+      }
+    }
+    return null;
+  }
+
+  @override
+  MethodElement getMethod(String methodName) {
+    for (MethodElement method in _methods) {
+      if (method.name == methodName) {
+        return method;
+      }
+    }
+    return null;
+  }
+
+  @override
+  ConstructorElement getNamedConstructor(String name) {
+    for (ConstructorElement element in constructors) {
+      String elementName = element.name;
+      if (elementName != null && elementName == name) {
+        return element;
+      }
+    }
+    return null;
+  }
+
+  @override
+  PropertyAccessorElement getSetter(String setterName) {
+    // TODO (jwren) revisit- should we append '=' here or require clients to
+    // include it?
+    // Do we need the check for isSetter below?
+    if (!StringUtilities.endsWithChar(setterName, 0x3D)) {
+      setterName += '=';
+    }
+    for (PropertyAccessorElement accessor in _accessors) {
+      if (accessor.isSetter && accessor.name == setterName) {
+        return accessor;
+      }
+    }
+    return null;
+  }
+
+  @override
+  bool isSuperConstructorAccessible(ConstructorElement constructor) {
+    // If this class has no mixins, then all superclass constructors are
+    // accessible.
+    if (mixins.isEmpty) {
+      return true;
+    }
+    // Otherwise only constructors that lack optional parameters are
+    // accessible (see dartbug.com/19576).
+    for (ParameterElement parameter in constructor.parameters) {
+      if (parameter.parameterKind != ParameterKind.REQUIRED) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @override
+  MethodElement lookUpConcreteMethod(
+          String methodName, LibraryElement library) =>
+      _internalLookUpConcreteMethod(methodName, library, true);
+
+  @override
+  PropertyAccessorElement lookUpGetter(
+          String getterName, LibraryElement library) =>
+      _internalLookUpGetter(getterName, library, true);
+
+  @override
+  PropertyAccessorElement lookUpInheritedConcreteGetter(
+          String getterName, LibraryElement library) =>
+      _internalLookUpConcreteGetter(getterName, library, false);
+
+  @override
+  MethodElement lookUpInheritedConcreteMethod(
+          String methodName, LibraryElement library) =>
+      _internalLookUpConcreteMethod(methodName, library, false);
+
+  @override
+  PropertyAccessorElement lookUpInheritedConcreteSetter(
+          String setterName, LibraryElement library) =>
+      _internalLookUpConcreteSetter(setterName, library, false);
+
+  @override
+  MethodElement lookUpInheritedMethod(
+          String methodName, LibraryElement library) =>
+      _internalLookUpMethod(methodName, library, false);
+
+  @override
+  MethodElement lookUpMethod(String methodName, LibraryElement library) =>
+      _internalLookUpMethod(methodName, library, true);
+
+  @override
+  PropertyAccessorElement lookUpSetter(
+          String setterName, LibraryElement library) =>
+      _internalLookUpSetter(setterName, library, true);
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    super.visitChildren(visitor);
+    safelyVisitChildren(_accessors, visitor);
+    safelyVisitChildren(_constructors, visitor);
+    safelyVisitChildren(_fields, visitor);
+    safelyVisitChildren(_methods, visitor);
+    safelyVisitChildren(_typeParameters, visitor);
+  }
+
+  void _collectAllSupertypes(List<InterfaceType> supertypes) {
+    List<InterfaceType> typesToVisit = new List<InterfaceType>();
+    List<ClassElement> visitedClasses = new List<ClassElement>();
+    typesToVisit.add(this.type);
+    while (!typesToVisit.isEmpty) {
+      InterfaceType currentType = typesToVisit.removeAt(0);
+      ClassElement currentElement = currentType.element;
+      if (!visitedClasses.contains(currentElement)) {
+        visitedClasses.add(currentElement);
+        if (!identical(currentType, this.type)) {
+          supertypes.add(currentType);
+        }
+        InterfaceType supertype = currentType.superclass;
+        if (supertype != null) {
+          typesToVisit.add(supertype);
+        }
+        for (InterfaceType type in currentElement.interfaces) {
+          typesToVisit.add(type);
+        }
+        for (InterfaceType type in currentElement.mixins) {
+          ClassElement element = type.element;
+          if (!visitedClasses.contains(element)) {
+            supertypes.add(type);
+          }
+        }
+      }
+    }
+  }
+
+  PropertyAccessorElement _internalLookUpConcreteGetter(
+      String getterName, LibraryElement library, bool includeThisClass) {
+    PropertyAccessorElement getter =
+        _internalLookUpGetter(getterName, library, includeThisClass);
+    while (getter != null && getter.isAbstract) {
+      Element definingClass = getter.enclosingElement;
+      if (definingClass is! ClassElementImpl) {
+        return null;
+      }
+      getter = (definingClass as ClassElementImpl)._internalLookUpGetter(
+          getterName, library, false);
+    }
+    return getter;
+  }
+
+  MethodElement _internalLookUpConcreteMethod(
+      String methodName, LibraryElement library, bool includeThisClass) {
+    MethodElement method =
+        _internalLookUpMethod(methodName, library, includeThisClass);
+    while (method != null && method.isAbstract) {
+      ClassElement definingClass = method.enclosingElement;
+      if (definingClass == null) {
+        return null;
+      }
+      method = definingClass.lookUpInheritedMethod(methodName, library);
+    }
+    return method;
+  }
+
+  PropertyAccessorElement _internalLookUpConcreteSetter(
+      String setterName, LibraryElement library, bool includeThisClass) {
+    PropertyAccessorElement setter =
+        _internalLookUpSetter(setterName, library, includeThisClass);
+    while (setter != null && setter.isAbstract) {
+      Element definingClass = setter.enclosingElement;
+      if (definingClass is! ClassElementImpl) {
+        return null;
+      }
+      setter = (definingClass as ClassElementImpl)._internalLookUpSetter(
+          setterName, library, false);
+    }
+    return setter;
+  }
+
+  PropertyAccessorElement _internalLookUpGetter(
+      String getterName, LibraryElement library, bool includeThisClass) {
+    HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
+    ClassElement currentElement = this;
+    if (includeThisClass) {
+      PropertyAccessorElement element = currentElement.getGetter(getterName);
+      if (element != null && element.isAccessibleIn(library)) {
+        return element;
+      }
+    }
+    while (currentElement != null && visitedClasses.add(currentElement)) {
+      for (InterfaceType mixin in currentElement.mixins.reversed) {
+        ClassElement mixinElement = mixin.element;
+        if (mixinElement != null) {
+          PropertyAccessorElement element = mixinElement.getGetter(getterName);
+          if (element != null && element.isAccessibleIn(library)) {
+            return element;
+          }
+        }
+      }
+      InterfaceType supertype = currentElement.supertype;
+      if (supertype == null) {
+        return null;
+      }
+      currentElement = supertype.element;
+      PropertyAccessorElement element = currentElement.getGetter(getterName);
+      if (element != null && element.isAccessibleIn(library)) {
+        return element;
+      }
+    }
+    return null;
+  }
+
+  MethodElement _internalLookUpMethod(
+      String methodName, LibraryElement library, bool includeThisClass) {
+    HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
+    ClassElement currentElement = this;
+    if (includeThisClass) {
+      MethodElement element = currentElement.getMethod(methodName);
+      if (element != null && element.isAccessibleIn(library)) {
+        return element;
+      }
+    }
+    while (currentElement != null && visitedClasses.add(currentElement)) {
+      for (InterfaceType mixin in currentElement.mixins.reversed) {
+        ClassElement mixinElement = mixin.element;
+        if (mixinElement != null) {
+          MethodElement element = mixinElement.getMethod(methodName);
+          if (element != null && element.isAccessibleIn(library)) {
+            return element;
+          }
+        }
+      }
+      InterfaceType supertype = currentElement.supertype;
+      if (supertype == null) {
+        return null;
+      }
+      currentElement = supertype.element;
+      MethodElement element = currentElement.getMethod(methodName);
+      if (element != null && element.isAccessibleIn(library)) {
+        return element;
+      }
+    }
+    return null;
+  }
+
+  PropertyAccessorElement _internalLookUpSetter(
+      String setterName, LibraryElement library, bool includeThisClass) {
+    HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
+    ClassElement currentElement = this;
+    if (includeThisClass) {
+      PropertyAccessorElement element = currentElement.getSetter(setterName);
+      if (element != null && element.isAccessibleIn(library)) {
+        return element;
+      }
+    }
+    while (currentElement != null && visitedClasses.add(currentElement)) {
+      for (InterfaceType mixin in currentElement.mixins.reversed) {
+        ClassElement mixinElement = mixin.element;
+        if (mixinElement != null) {
+          PropertyAccessorElement element = mixinElement.getSetter(setterName);
+          if (element != null && element.isAccessibleIn(library)) {
+            return element;
+          }
+        }
+      }
+      InterfaceType supertype = currentElement.supertype;
+      if (supertype == null) {
+        return null;
+      }
+      currentElement = supertype.element;
+      PropertyAccessorElement element = currentElement.getSetter(setterName);
+      if (element != null && element.isAccessibleIn(library)) {
+        return element;
+      }
+    }
+    return null;
+  }
+
+  bool _safeIsOrInheritsProxy(
+      ClassElement classElt, HashSet<ClassElement> visitedClassElts) {
+    if (visitedClassElts.contains(classElt)) {
+      return false;
+    }
+    visitedClassElts.add(classElt);
+    if (classElt.isProxy) {
+      return true;
+    } else if (classElt.supertype != null &&
+        _safeIsOrInheritsProxy(classElt.supertype.element, visitedClassElts)) {
+      return true;
+    }
+    List<InterfaceType> supertypes = classElt.interfaces;
+    for (int i = 0; i < supertypes.length; i++) {
+      if (_safeIsOrInheritsProxy(supertypes[i].element, visitedClassElts)) {
+        return true;
+      }
+    }
+    supertypes = classElt.mixins;
+    for (int i = 0; i < supertypes.length; i++) {
+      if (_safeIsOrInheritsProxy(supertypes[i].element, visitedClassElts)) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
+
+/**
+ * An element that is contained within a [ClassElement].
+ */
+abstract class ClassMemberElement implements Element {
+  /**
+   * Return the type in which this member is defined.
+   */
+  @override
+  ClassElement get enclosingElement;
+
+  /**
+   * Return `true` if this element is a static element. A static element is an
+   * element that is not associated with a particular instance, but rather with
+   * an entire library or class.
+   */
+  bool get isStatic;
+}
+
+/**
+ * An element representing a compilation unit.
+ */
+abstract class CompilationUnitElement implements Element, UriReferencedElement {
+  /**
+   * An empty list of compilation unit elements.
+   */
+  static const List<CompilationUnitElement> EMPTY_LIST =
+      const <CompilationUnitElement>[];
+
+  /**
+   * Return a list containing all of the top-level accessors (getters and
+   * setters) contained in this compilation unit.
+   */
+  List<PropertyAccessorElement> get accessors;
+
+  /**
+   * Return the library in which this compilation unit is defined.
+   */
+  @override
+  LibraryElement get enclosingElement;
+
+  /**
+   * Return a list containing all of the enums contained in this compilation
+   * unit.
+   */
+  List<ClassElement> get enums;
+
+  /**
+   * Return a list containing all of the top-level functions contained in this
+   * compilation unit.
+   */
+  List<FunctionElement> get functions;
+
+  /**
+   * Return a list containing all of the function type aliases contained in this
+   * compilation unit.
+   */
+  List<FunctionTypeAliasElement> get functionTypeAliases;
+
+  /**
+   * Return `true` if this compilation unit defines a top-level function named
+   * `loadLibrary`.
+   */
+  bool get hasLoadLibraryFunction;
+
+  /**
+   * Return the resolved [CompilationUnit] node that declares this element.
+   *
+   * This method is expensive, because resolved AST might be evicted from cache,
+   * so parsing and resolving will be performed.
+   */
+  @override
+  CompilationUnit get node;
+
+  /**
+   * Return a list containing all of the top-level variables contained in this
+   * compilation unit.
+   */
+  List<TopLevelVariableElement> get topLevelVariables;
+
+  /**
+   * Return a list containing all of the classes contained in this compilation
+   * unit.
+   */
+  List<ClassElement> get types;
+
+  /**
+   * Return the element at the given [offset], maybe `null` if no such element.
+   */
+  Element getElementAt(int offset);
+
+  /**
+   * Return the enum defined in this compilation unit that has the given [name],
+   * or `null` if this compilation unit does not define an enum with the given
+   * name.
+   */
+  ClassElement getEnum(String name);
+
+  /**
+   * Return the class defined in this compilation unit that has the given
+   * [name], or `null` if this compilation unit does not define a class with the
+   * given name.
+   */
+  ClassElement getType(String name);
+}
+
+/**
+ * A concrete implementation of a [CompilationUnitElement].
+ */
+class CompilationUnitElementImpl extends UriReferencedElementImpl
+    implements CompilationUnitElement {
+  /**
+   * An empty list of compilation unit elements.
+   */
+  @deprecated // Use CompilationUnitElement.EMPTY_LIST
+  static const List<CompilationUnitElement> EMPTY_ARRAY =
+      const <CompilationUnitElement>[];
+
+  /**
+   * The source that corresponds to this compilation unit.
+   */
+  Source source;
+
+  /**
+   * A list containing all of the top-level accessors (getters and setters)
+   * contained in this compilation unit.
+   */
+  List<PropertyAccessorElement> _accessors = PropertyAccessorElement.EMPTY_LIST;
+
+  /**
+   * A list containing all of the enums contained in this compilation unit.
+   */
+  List<ClassElement> _enums = ClassElement.EMPTY_LIST;
+
+  /**
+   * A list containing all of the top-level functions contained in this
+   * compilation unit.
+   */
+  List<FunctionElement> _functions = FunctionElement.EMPTY_LIST;
+
+  /**
+   * A list containing all of the function type aliases contained in this
+   * compilation unit.
+   */
+  List<FunctionTypeAliasElement> _typeAliases =
+      FunctionTypeAliasElement.EMPTY_LIST;
+
+  /**
+   * A list containing all of the types contained in this compilation unit.
+   */
+  List<ClassElement> _types = ClassElement.EMPTY_LIST;
+
+  /**
+   * A list containing all of the variables contained in this compilation unit.
+   */
+  List<TopLevelVariableElement> _variables = TopLevelVariableElement.EMPTY_LIST;
+
+  /**
+   * A map from offsets to elements of this unit at these offsets.
+   */
+  final Map<int, Element> _offsetToElementMap = new HashMap<int, Element>();
+
+  /**
+   * Initialize a newly created compilation unit element to have the given
+   * [name].
+   */
+  CompilationUnitElementImpl(String name) : super(name, -1);
+
+  @override
+  List<PropertyAccessorElement> get accessors => _accessors;
+
+  /**
+   * Set the top-level accessors (getters and setters) contained in this
+   * compilation unit to the given [accessors].
+   */
+  void set accessors(List<PropertyAccessorElement> accessors) {
+    for (PropertyAccessorElement accessor in accessors) {
+      (accessor as PropertyAccessorElementImpl).enclosingElement = this;
+    }
+    this._accessors = accessors;
+  }
+
+  @override
+  LibraryElement get enclosingElement =>
+      super.enclosingElement as LibraryElement;
+
+  @override
+  List<ClassElement> get enums => _enums;
+
+  /**
+   * Set the enums contained in this compilation unit to the given [enums].
+   */
+  void set enums(List<ClassElement> enums) {
+    for (ClassElement enumDeclaration in enums) {
+      (enumDeclaration as ClassElementImpl).enclosingElement = this;
+    }
+    this._enums = enums;
+  }
+
+  @override
+  List<FunctionElement> get functions => _functions;
+
+  /**
+   * Set the top-level functions contained in this compilation unit to the given
+   * [functions].
+   */
+  void set functions(List<FunctionElement> functions) {
+    for (FunctionElement function in functions) {
+      (function as FunctionElementImpl).enclosingElement = this;
+    }
+    this._functions = functions;
+  }
+
+  @override
+  List<FunctionTypeAliasElement> get functionTypeAliases => _typeAliases;
+
+  @override
+  int get hashCode => source.hashCode;
+
+  @override
+  bool get hasLoadLibraryFunction {
+    for (int i = 0; i < _functions.length; i++) {
+      if (_functions[i].name == FunctionElement.LOAD_LIBRARY_NAME) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @override
+  String get identifier => source.encoding;
+
+  @override
+  ElementKind get kind => ElementKind.COMPILATION_UNIT;
+
+  @override
+  CompilationUnit get node => unit;
+
+  @override
+  List<TopLevelVariableElement> get topLevelVariables => _variables;
+
+  /**
+   * Set the top-level variables contained in this compilation unit to the given
+   * [variables].
+   */
+  void set topLevelVariables(List<TopLevelVariableElement> variables) {
+    for (TopLevelVariableElement field in variables) {
+      (field as TopLevelVariableElementImpl).enclosingElement = this;
+    }
+    this._variables = variables;
+  }
+
+  /**
+   * Set the function type aliases contained in this compilation unit to the
+   * given [typeAliases].
+   */
+  void set typeAliases(List<FunctionTypeAliasElement> typeAliases) {
+    for (FunctionTypeAliasElement typeAlias in typeAliases) {
+      (typeAlias as FunctionTypeAliasElementImpl).enclosingElement = this;
+    }
+    this._typeAliases = typeAliases;
+  }
+
+  @override
+  List<ClassElement> get types => _types;
+
+  /**
+   * Set the types contained in this compilation unit to the given [types].
+   */
+  void set types(List<ClassElement> types) {
+    for (ClassElement type in types) {
+      (type as ClassElementImpl).enclosingElement = this;
+    }
+    this._types = types;
+  }
+
+  @override
+  bool operator ==(Object object) =>
+      object is CompilationUnitElementImpl && source == object.source;
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitCompilationUnitElement(this);
+
+  /**
+   * This method is invoked after this unit was incrementally resolved.
+   */
+  void afterIncrementalResolution() {
+    _offsetToElementMap.clear();
+  }
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    if (source == null) {
+      buffer.write("{compilation unit}");
+    } else {
+      buffer.write(source.fullName);
+    }
+  }
+
+  @override
+  ElementImpl getChild(String identifier) {
+    //
+    // The casts in this method are safe because the set methods would have
+    // thrown a CCE if any of the elements in the arrays were not of the
+    // expected types.
+    //
+    for (PropertyAccessorElement accessor in _accessors) {
+      if ((accessor as PropertyAccessorElementImpl).identifier == identifier) {
+        return accessor as PropertyAccessorElementImpl;
+      }
+    }
+    for (VariableElement variable in _variables) {
+      if ((variable as VariableElementImpl).identifier == identifier) {
+        return variable as VariableElementImpl;
+      }
+    }
+    for (ExecutableElement function in _functions) {
+      if ((function as ExecutableElementImpl).identifier == identifier) {
+        return function as ExecutableElementImpl;
+      }
+    }
+    for (FunctionTypeAliasElement typeAlias in _typeAliases) {
+      if ((typeAlias as FunctionTypeAliasElementImpl).identifier ==
+          identifier) {
+        return typeAlias as FunctionTypeAliasElementImpl;
+      }
+    }
+    for (ClassElement type in _types) {
+      if ((type as ClassElementImpl).identifier == identifier) {
+        return type as ClassElementImpl;
+      }
+    }
+    for (ClassElement type in _enums) {
+      if ((type as ClassElementImpl).identifier == identifier) {
+        return type as ClassElementImpl;
+      }
+    }
+    return null;
+  }
+
+  @override
+  Element getElementAt(int offset) {
+    if (_offsetToElementMap.isEmpty) {
+      accept(new _BuildOffsetToElementMap(_offsetToElementMap));
+    }
+    return _offsetToElementMap[offset];
+  }
+
+  @override
+  ClassElement getEnum(String enumName) {
+    for (ClassElement enumDeclaration in _enums) {
+      if (enumDeclaration.name == enumName) {
+        return enumDeclaration;
+      }
+    }
+    return null;
+  }
+
+  @override
+  ClassElement getType(String className) {
+    for (ClassElement type in _types) {
+      if (type.name == className) {
+        return type;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Replace the given [from] top-level variable with [to] in this compilation unit.
+   */
+  void replaceTopLevelVariable(
+      TopLevelVariableElement from, TopLevelVariableElement to) {
+    int index = _variables.indexOf(from);
+    _variables[index] = to;
+  }
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    super.visitChildren(visitor);
+    safelyVisitChildren(_accessors, visitor);
+    safelyVisitChildren(_enums, visitor);
+    safelyVisitChildren(_functions, visitor);
+    safelyVisitChildren(_typeAliases, visitor);
+    safelyVisitChildren(_types, visitor);
+    safelyVisitChildren(_variables, visitor);
+  }
+}
+
+/**
+ * A [FieldElement] for a 'const' field that has an initializer.
+ */
+class ConstFieldElementImpl extends FieldElementImpl with ConstVariableElement {
+  /**
+   * The result of evaluating this variable's initializer.
+   */
+  EvaluationResultImpl _result;
+
+  /**
+   * Initialize a newly created field element to have the given [name].
+   */
+  ConstFieldElementImpl.con1(Identifier name) : super.forNode(name);
+
+  /**
+   * Initialize a newly created synthetic field element to have the given
+   * [name] and [offset].
+   */
+  ConstFieldElementImpl.con2(String name, int offset) : super(name, offset);
+
+  @override
+  EvaluationResultImpl get evaluationResult => _result;
+
+  @override
+  void set evaluationResult(EvaluationResultImpl result) {
+    this._result = result;
+  }
+}
+
+/**
+ * A [LocalVariableElement] for a local 'const' variable that has an
+ * initializer.
+ */
+class ConstLocalVariableElementImpl extends LocalVariableElementImpl
+    with ConstVariableElement {
+  /**
+   * The result of evaluating this variable's initializer.
+   */
+  EvaluationResultImpl _result;
+
+  /**
+   * Initialize a newly created local variable element to have the given [name]
+   * and [offset].
+   */
+  ConstLocalVariableElementImpl(String name, int offset) : super(name, offset);
+
+  /**
+   * Initialize a newly created local variable element to have the given [name].
+   */
+  ConstLocalVariableElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  @override
+  EvaluationResultImpl get evaluationResult => _result;
+
+  @override
+  void set evaluationResult(EvaluationResultImpl result) {
+    this._result = result;
+  }
+}
+
+/**
+ * An element representing a constructor or a factory method defined within a
+ * class.
+ */
+abstract class ConstructorElement
+    implements ClassMemberElement, ExecutableElement {
+  /**
+   * An empty list of constructor elements.
+   */
+  static const List<ConstructorElement> EMPTY_LIST =
+      const <ConstructorElement>[];
+
+  /**
+   * Return `true` if this constructor is a const constructor.
+   */
+  bool get isConst;
+
+  /**
+   * Return `true` if this constructor can be used as a default constructor -
+   * unnamed and has no required parameters.
+   */
+  bool get isDefaultConstructor;
+
+  /**
+   * Return `true` if this constructor represents a factory constructor.
+   */
+  bool get isFactory;
+
+  /**
+   * Return the offset of the character immediately following the last character
+   * of this constructor's name, or `null` if not named.
+   */
+  int get nameEnd;
+
+  /**
+   * Return the resolved [ConstructorDeclaration] node that declares this
+   * [ConstructorElement] .
+   *
+   * This method is expensive, because resolved AST might be evicted from cache,
+   * so parsing and resolving will be performed.
+   */
+  @override
+  ConstructorDeclaration get node;
+
+  /**
+   * Return the offset of the `.` before this constructor name, or `null` if
+   * not named.
+   */
+  int get periodOffset;
+
+  /**
+   * Return the constructor to which this constructor is redirecting, or `null`
+   * if this constructor does not redirect to another constructor or if the
+   * library containing this constructor has not yet been resolved.
+   */
+  ConstructorElement get redirectedConstructor;
+}
+
+/**
+ * A concrete implementation of a [ConstructorElement].
+ */
+class ConstructorElementImpl extends ExecutableElementImpl
+    implements ConstructorElement {
+  /**
+   * An empty list of constructor elements.
+   */
+  @deprecated // Use ConstructorElement.EMPTY_LIST
+  static const List<ConstructorElement> EMPTY_ARRAY =
+      const <ConstructorElement>[];
+
+  /**
+   * The constructor to which this constructor is redirecting.
+   */
+  ConstructorElement redirectedConstructor;
+
+  /**
+   * The initializers for this constructor (used for evaluating constant
+   * instance creation expressions).
+   */
+  List<ConstructorInitializer> constantInitializers;
+
+  /**
+   * The offset of the `.` before this constructor name or `null` if not named.
+   */
+  int periodOffset;
+
+  /**
+   * Return the offset of the character immediately following the last character
+   * of this constructor's name, or `null` if not named.
+   */
+  int nameEnd;
+
+  /**
+   * True if this constructor has been found by constant evaluation to be free
+   * of redirect cycles, and is thus safe to evaluate.
+   */
+  bool isCycleFree = false;
+
+  /**
+   * Initialize a newly created constructor element to have the given [name] and
+   * [offset].
+   */
+  ConstructorElementImpl(String name, int offset) : super(name, offset);
+
+  /**
+   * Initialize a newly created constructor element to have the given [name].
+   */
+  ConstructorElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  /**
+   * Set whether this constructor represents a 'const' constructor.
+   */
+  void set const2(bool isConst) {
+    setModifier(Modifier.CONST, isConst);
+  }
+
+  @override
+  ClassElement get enclosingElement => super.enclosingElement as ClassElement;
+
+  /**
+   * Set whether this constructor represents a factory method.
+   */
+  void set factory(bool isFactory) {
+    setModifier(Modifier.FACTORY, isFactory);
+  }
+
+  @override
+  bool get isConst => hasModifier(Modifier.CONST);
+
+  @override
+  bool get isDefaultConstructor {
+    // unnamed
+    String name = this.name;
+    if (name != null && name.length != 0) {
+      return false;
+    }
+    // no required parameters
+    for (ParameterElement parameter in parameters) {
+      if (parameter.parameterKind == ParameterKind.REQUIRED) {
+        return false;
+      }
+    }
+    // OK, can be used as default constructor
+    return true;
+  }
+
+  @override
+  bool get isFactory => hasModifier(Modifier.FACTORY);
+
+  @override
+  bool get isStatic => false;
+
+  @override
+  ElementKind get kind => ElementKind.CONSTRUCTOR;
+
+  @override
+  ConstructorDeclaration get node =>
+      getNodeMatching((node) => node is ConstructorDeclaration);
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitConstructorElement(this);
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    if (enclosingElement == null) {
+      String message;
+      String name = displayName;
+      if (name != null && !name.isEmpty) {
+        message =
+            'Found constructor element named $name with no enclosing element';
+      } else {
+        message = 'Found unnamed constructor element with no enclosing element';
+      }
+      AnalysisEngine.instance.logger.logError(message);
+      buffer.write('<unknown class>');
+    } else {
+      buffer.write(enclosingElement.displayName);
+    }
+    String name = displayName;
+    if (name != null && !name.isEmpty) {
+      buffer.write(".");
+      buffer.write(name);
+    }
+    super.appendTo(buffer);
+  }
+}
+
+/**
+ * A constructor element defined in a parameterized type where the values of the
+ * type parameters are known.
+ */
+class ConstructorMember extends ExecutableMember implements ConstructorElement {
+  /**
+   * Initialize a newly created element to represent a constructor, based on the
+   * [baseElement], defined by the [definingType].
+   */
+  ConstructorMember(ConstructorElement baseElement, InterfaceType definingType)
+      : super(baseElement, definingType);
+
+  @override
+  ConstructorElement get baseElement => super.baseElement as ConstructorElement;
+
+  @override
+  InterfaceType get definingType => super.definingType as InterfaceType;
+
+  @override
+  ClassElement get enclosingElement => baseElement.enclosingElement;
+
+  @override
+  bool get isConst => baseElement.isConst;
+
+  @override
+  bool get isDefaultConstructor => baseElement.isDefaultConstructor;
+
+  @override
+  bool get isFactory => baseElement.isFactory;
+
+  @override
+  int get nameEnd => baseElement.nameEnd;
+
+  @override
+  ConstructorDeclaration get node => baseElement.node;
+
+  @override
+  int get periodOffset => baseElement.periodOffset;
+
+  @override
+  ConstructorElement get redirectedConstructor =>
+      from(baseElement.redirectedConstructor, definingType);
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitConstructorElement(this);
+
+  @override
+  String toString() {
+    ConstructorElement baseElement = this.baseElement;
+    List<ParameterElement> parameters = this.parameters;
+    FunctionType type = this.type;
+    StringBuffer buffer = new StringBuffer();
+    buffer.write(baseElement.enclosingElement.displayName);
+    String name = displayName;
+    if (name != null && !name.isEmpty) {
+      buffer.write(".");
+      buffer.write(name);
+    }
+    buffer.write("(");
+    int parameterCount = parameters.length;
+    for (int i = 0; i < parameterCount; i++) {
+      if (i > 0) {
+        buffer.write(", ");
+      }
+      buffer.write(parameters[i]);
+    }
+    buffer.write(")");
+    if (type != null) {
+      buffer.write(Element.RIGHT_ARROW);
+      buffer.write(type.returnType);
+    }
+    return buffer.toString();
+  }
+
+  /**
+   * If the given [constructor]'s type is different when any type parameters
+   * from the defining type's declaration are replaced with the actual type
+   * arguments from the [definingType], create a constructor member representing
+   * the given constructor. Return the member that was created, or the original
+   * constructor if no member was created.
+   */
+  static ConstructorElement from(
+      ConstructorElement constructor, InterfaceType definingType) {
+    if (constructor == null || definingType.typeArguments.length == 0) {
+      return constructor;
+    }
+    FunctionType baseType = constructor.type;
+    if (baseType == null) {
+      // TODO(brianwilkerson) We need to understand when this can happen.
+      return constructor;
+    }
+    List<DartType> argumentTypes = definingType.typeArguments;
+    List<DartType> parameterTypes = definingType.element.type.typeArguments;
+    FunctionType substitutedType =
+        baseType.substitute2(argumentTypes, parameterTypes);
+    if (baseType == substitutedType) {
+      return constructor;
+    }
+    // TODO(brianwilkerson) Consider caching the substituted type in the
+    // instance. It would use more memory but speed up some operations.
+    // We need to see how often the type is being re-computed.
+    return new ConstructorMember(constructor, definingType);
+  }
+}
+
+/**
+ * A [TopLevelVariableElement] for a top-level 'const' variable that has an
+ * initializer.
+ */
+class ConstTopLevelVariableElementImpl extends TopLevelVariableElementImpl
+    with ConstVariableElement {
+  /**
+   * The result of evaluating this variable's initializer.
+   */
+  EvaluationResultImpl _result;
+
+  /**
+   * Initialize a newly created top-level variable element to have the given
+   * [name].
+   */
+  ConstTopLevelVariableElementImpl(Identifier name) : super.forNode(name);
+
+  @override
+  EvaluationResultImpl get evaluationResult => _result;
+
+  @override
+  void set evaluationResult(EvaluationResultImpl result) {
+    this._result = result;
+  }
+}
+
+/**
+ * Mixin used by elements that represent constant variables and have
+ * initializers.
+ *
+ * Note that in correct Dart code, all constant variables must have
+ * initializers.  However, analyzer also needs to handle incorrect Dart code,
+ * in which case there might be some constant variables that lack initializers.
+ * This interface is only used for constant variables that have initializers.
+ *
+ * This class is not intended to be part of the public API for analyzer.
+ */
+abstract class ConstVariableElement implements PotentiallyConstVariableElement {
+  /**
+   * If this element represents a constant variable, and it has an initializer,
+   * a copy of the initializer for the constant.  Otherwise `null`.
+   *
+   * Note that in correct Dart code, all constant variables must have
+   * initializers.  However, analyzer also needs to handle incorrect Dart code,
+   * in which case there might be some constant variables that lack
+   * initializers.
+   */
+  Expression constantInitializer;
+}
+
+/**
+ * The type associated with elements in the element model.
+ */
+abstract class DartType {
+  /**
+   * An empty list of types.
+   */
+  static const List<DartType> EMPTY_LIST = const <DartType>[];
+
+  /**
+   * Return the name of this type as it should appear when presented to users in
+   * contexts such as error messages.
+   */
+  String get displayName;
+
+  /**
+   * Return the element representing the declaration of this type, or `null` if
+   * the type has not, or cannot, be associated with an element. The former case
+   * will occur if the element model is not yet complete; the latter case will
+   * occur if this object represents an undefined type.
+   */
+  Element get element;
+
+  /**
+   * Return `true` if this type represents the bottom type.
+   */
+  bool get isBottom;
+
+  /**
+   * Return `true` if this type represents the type 'Function' defined in the
+   * dart:core library.
+   */
+  bool get isDartCoreFunction;
+
+  /**
+   * Return `true` if this type represents the type 'dynamic'.
+   */
+  bool get isDynamic;
+
+  /**
+   * Return `true` if this type represents the type 'Object'.
+   */
+  bool get isObject;
+
+  /**
+   * Return `true` if this type represents a typename that couldn't be resolved.
+   */
+  bool get isUndefined;
+
+  /**
+   * Return `true` if this type represents the type 'void'.
+   */
+  bool get isVoid;
+
+  /**
+   * Return the name of this type, or `null` if the type does not have a name,
+   * such as when the type represents the type of an unnamed function.
+   */
+  String get name;
+
+  /**
+   * Return the least upper bound of this type and the given [type], or `null`
+   * if there is no least upper bound.
+   */
+  DartType getLeastUpperBound(DartType type);
+
+  /**
+   * Return `true` if this type is assignable to the given [type]. A type
+   * <i>T</i> may be assigned to a type <i>S</i>, written <i>T</i> &hArr;
+   * <i>S</i>, iff either <i>T</i> <: <i>S</i> or <i>S</i> <: <i>T</i>.
+   */
+  bool isAssignableTo(DartType type);
+
+  /**
+   * Return `true` if this type is more specific than the given [type].
+   */
+  bool isMoreSpecificThan(DartType type);
+
+  /**
+   * Return `true` if this type is a subtype of the given [type].
+   */
+  bool isSubtypeOf(DartType type);
+
+  /**
+   * Return `true` if this type is a supertype of the given [type]. A type
+   * <i>S</i> is a supertype of <i>T</i>, written <i>S</i> :> <i>T</i>, iff
+   * <i>T</i> is a subtype of <i>S</i>.
+   */
+  bool isSupertypeOf(DartType type);
+
+  /**
+   * Return the type resulting from substituting the given [argumentTypes] for
+   * the given [parameterTypes] in this type. The specification defines this
+   * operation in section 2:
+   * <blockquote>
+   * The notation <i>[x<sub>1</sub>, ..., x<sub>n</sub>/y<sub>1</sub>, ...,
+   * y<sub>n</sub>]E</i> denotes a copy of <i>E</i> in which all occurrences of
+   * <i>y<sub>i</sub>, 1 <= i <= n</i> have been replaced with
+   * <i>x<sub>i</sub></i>.
+   * </blockquote>
+   * Note that, contrary to the specification, this method will not create a
+   * copy of this type if no substitutions were required, but will return this
+   * type directly.
+   *
+   * Note too that the current implementation of this method is only guaranteed
+   * to work when the argument types are type variables.
+   */
+  DartType substitute2(
+      List<DartType> argumentTypes, List<DartType> parameterTypes);
+}
+
+/**
+ * A [FieldFormalParameterElementImpl] for parameters that have an initializer.
+ */
+class DefaultFieldFormalParameterElementImpl
+    extends FieldFormalParameterElementImpl {
+  /**
+   * The result of evaluating this variable's initializer.
+   */
+  EvaluationResultImpl _result;
+
+  /**
+   * Initialize a newly created parameter element to have the given [name].
+   */
+  DefaultFieldFormalParameterElementImpl(Identifier name) : super(name);
+
+  @override
+  EvaluationResultImpl get evaluationResult => _result;
+
+  @override
+  void set evaluationResult(EvaluationResultImpl result) {
+    this._result = result;
+  }
+}
+
+/**
+ * A [ParameterElement] for parameters that have an initializer.
+ */
+class DefaultParameterElementImpl extends ParameterElementImpl {
+  /**
+   * The result of evaluating this variable's initializer.
+   */
+  EvaluationResultImpl _result;
+
+  /**
+   * Initialize a newly created parameter element to have the given [name].
+   */
+  DefaultParameterElementImpl(Identifier name) : super.forNode(name);
+
+  @override
+  EvaluationResultImpl get evaluationResult => _result;
+
+  @override
+  void set evaluationResult(EvaluationResultImpl result) {
+    this._result = result;
+  }
+
+  @override
+  DefaultFormalParameter get node =>
+      getNodeMatching((node) => node is DefaultFormalParameter);
+}
+
+/**
+ * The synthetic element representing the declaration of the type `dynamic`.
+ */
+class DynamicElementImpl extends ElementImpl {
+  /**
+   * Return the unique instance of this class.
+   */
+  static DynamicElementImpl get instance =>
+      DynamicTypeImpl.instance.element as DynamicElementImpl;
+
+  /**
+   * The type defined by this element.
+   */
+  DynamicTypeImpl type;
+
+  /**
+   * Initialize a newly created instance of this class. Instances of this class
+   * should <b>not</b> be created except as part of creating the type associated
+   * with this element. The single instance of this class should be accessed
+   * through the method [getInstance].
+   */
+  DynamicElementImpl() : super(Keyword.DYNAMIC.syntax, -1) {
+    setModifier(Modifier.SYNTHETIC, true);
+  }
+
+  @override
+  ElementKind get kind => ElementKind.DYNAMIC;
+
+  @override
+  accept(ElementVisitor visitor) => null;
+}
+
+/**
+ * The [Type] representing the type `dynamic`.
+ */
+class DynamicTypeImpl extends TypeImpl {
+  /**
+   * The unique instance of this class.
+   */
+  static DynamicTypeImpl _INSTANCE = new DynamicTypeImpl._();
+
+  /**
+   * Return the unique instance of this class.
+   */
+  static DynamicTypeImpl get instance => _INSTANCE;
+
+  /**
+   * Prevent the creation of instances of this class.
+   */
+  DynamicTypeImpl._()
+      : super(new DynamicElementImpl(), Keyword.DYNAMIC.syntax) {
+    (element as DynamicElementImpl).type = this;
+  }
+
+  @override
+  int get hashCode => 1;
+
+  @override
+  bool get isDynamic => true;
+
+  @override
+  bool operator ==(Object object) => identical(object, this);
+
+  @override
+  bool internalEquals(Object object, Set<ElementPair> visitedElementPairs) =>
+      identical(object, this);
+
+  @override
+  int internalHashCode(List<DartType> visitedTypes) => hashCode;
+
+  @override
+  bool isMoreSpecificThan(DartType type, [Set<Element> thisExpansions,
+      Set<Element> typeExpansions, bool withDynamic = false,
+      Set<Element> visitedElements]) {
+    // T is S
+    if (identical(this, type)) {
+      return true;
+    }
+    // else
+    return withDynamic;
+  }
+
+  @override
+  bool isSubtypeOf(DartType type,
+      [Set<Element> thisExpansions, Set<Element> typeExpansions]) => true;
+
+  @override
+  bool isSupertypeOf(DartType type) => true;
+
+  @override
+  DartType substitute2(
+      List<DartType> argumentTypes, List<DartType> parameterTypes) {
+    int length = parameterTypes.length;
+    for (int i = 0; i < length; i++) {
+      if (parameterTypes[i] == this) {
+        return argumentTypes[i];
+      }
+    }
+    return this;
+  }
+}
+
+/**
+ * The base class for all of the elements in the element model. Generally
+ * speaking, the element model is a semantic model of the program that
+ * represents things that are declared with a name and hence can be referenced
+ * elsewhere in the code.
+ *
+ * There are two exceptions to the general case. First, there are elements in
+ * the element model that are created for the convenience of various kinds of
+ * analysis but that do not have any corresponding declaration within the source
+ * code. Such elements are marked as being <i>synthetic</i>. Examples of
+ * synthetic elements include
+ * * default constructors in classes that do not define any explicit
+ *   constructors,
+ * * getters and setters that are induced by explicit field declarations,
+ * * fields that are induced by explicit declarations of getters and setters,
+ *   and
+ * * functions representing the initialization expression for a variable.
+ *
+ * Second, there are elements in the element model that do not have a name.
+ * These correspond to unnamed functions and exist in order to more accurately
+ * represent the semantic structure of the program.
+ */
+abstract class Element implements AnalysisTarget {
+  /**
+   * An Unicode right arrow.
+   */
+  static final String RIGHT_ARROW = " \u2192 ";
+
+  /**
+   * A comparator that can be used to sort elements by their name offset.
+   * Elements with a smaller offset will be sorted to be before elements with a
+   * larger name offset.
+   */
+  static final Comparator<Element> SORT_BY_OFFSET = (Element firstElement,
+          Element secondElement) =>
+      firstElement.nameOffset - secondElement.nameOffset;
+
+  /**
+   * Return the analysis context in which this element is defined.
+   */
+  AnalysisContext get context;
+
+  /**
+   * Return the display name of this element, or `null` if this element does not
+   * have a name.
+   *
+   * In most cases the name and the display name are the same. Differences
+   * though are cases such as setters where the name of some setter `set f(x)`
+   * is `f=`, instead of `f`.
+   */
+  String get displayName;
+
+  /**
+   * Return the element that either physically or logically encloses this
+   * element. This will be `null` if this element is a library because libraries
+   * are the top-level elements in the model.
+   */
+  Element get enclosingElement;
+
+  /**
+   * The unique integer identifier of this element.
+   */
+  int get id;
+
+  /**
+   * Return `true` if this element has an annotation of the form '@deprecated'
+   * or '@Deprecated('..')'.
+   */
+  bool get isDeprecated;
+
+  /**
+   * Return `true` if this element has an annotation of the form '@override'.
+   */
+  bool get isOverride;
+
+  /**
+   * Return `true` if this element is private. Private elements are visible only
+   * within the library in which they are declared.
+   */
+  bool get isPrivate;
+
+  /**
+   * Return `true` if this element is public. Public elements are visible within
+   * any library that imports the library in which they are declared.
+   */
+  bool get isPublic;
+
+  /**
+   * Return `true` if this element is synthetic. A synthetic element is an
+   * element that is not represented in the source code explicitly, but is
+   * implied by the source code, such as the default constructor for a class
+   * that does not explicitly define any constructors.
+   */
+  bool get isSynthetic;
+
+  /**
+   * Return the kind of element that this is.
+   */
+  ElementKind get kind;
+
+  /**
+   * Return the library that contains this element. This will be the element
+   * itself if it is a library element. This will be `null` if this element is
+   * an HTML file because HTML files are not contained in libraries.
+   */
+  LibraryElement get library;
+
+  /**
+   * Return an object representing the location of this element in the element
+   * model. The object can be used to locate this element at a later time.
+   */
+  ElementLocation get location;
+
+  /**
+   * Return a list containing all of the metadata associated with this element.
+   * The array will be empty if the element does not have any metadata or if the
+   * library containing this element has not yet been resolved.
+   */
+  List<ElementAnnotation> get metadata;
+
+  /**
+   * Return the name of this element, or `null` if this element does not have a
+   * name.
+   */
+  String get name;
+
+  /**
+   * Return the offset of the name of this element in the file that contains the
+   * declaration of this element, or `-1` if this element is synthetic, does not
+   * have a name, or otherwise does not have an offset.
+   */
+  int get nameOffset;
+
+  /**
+   * Return the resolved [AstNode] node that declares this element, or `null` if
+   * this element is synthetic or isn't contained in a compilation unit, such as
+   * a [LibraryElement].
+   *
+   * This method is expensive, because resolved AST might be evicted from cache,
+   * so parsing and resolving will be performed.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  AstNode get node;
+
+  /**
+   * Return the source that contains this element, or `null` if this element is
+   * not contained in a source.
+   */
+  Source get source;
+
+  /**
+   * Return the resolved [CompilationUnit] that declares this element, or `null`
+   * if this element is synthetic.
+   *
+   * This method is expensive, because resolved AST might have been already
+   * evicted from cache, so parsing and resolving will be performed.
+   */
+  CompilationUnit get unit;
+
+  /**
+   * Use the given [visitor] to visit this element. Return the value returned by
+   * the visitor as a result of visiting this element.
+   */
+  accept(ElementVisitor visitor);
+
+  /**
+   * Return the documentation comment for this element as it appears in the
+   * original source (complete with the beginning and ending delimiters), or
+   * `null` if this element does not have a documentation comment associated
+   * with it. This can be a long-running operation if the information needed to
+   * access the comment is not cached.
+   *
+   * Throws [AnalysisException] if the documentation comment could not be
+   * determined because the analysis could not be performed
+   */
+  String computeDocumentationComment();
+
+  /**
+   * Return the most immediate ancestor of this element for which the
+   * [predicate] returns `true`, or `null` if there is no such ancestor. Note
+   * that this element will never be returned.
+   */
+  Element getAncestor(Predicate<Element> predicate);
+
+  /**
+   * Return a display name for the given element that includes the path to the
+   * compilation unit in which the type is defined. If [shortName] is `null`
+   * then [getDisplayName] will be used as the name of this element. Otherwise
+   * the provided name will be used.
+   */
+  // TODO(brianwilkerson) Make the parameter optional.
+  String getExtendedDisplayName(String shortName);
+
+  /**
+   * Return `true` if this element, assuming that it is within scope, is
+   * accessible to code in the given [library]. This is defined by the Dart
+   * Language Specification in section 3.2:
+   * <blockquote>
+   * A declaration <i>m</i> is accessible to library <i>L</i> if <i>m</i> is
+   * declared in <i>L</i> or if <i>m</i> is public.
+   * </blockquote>
+   */
+  bool isAccessibleIn(LibraryElement library);
+
+  /**
+   * Use the given [visitor] to visit all of the children of this element. There
+   * is no guarantee of the order in which the children will be visited.
+   */
+  void visitChildren(ElementVisitor visitor);
+}
+
+/**
+ * A single annotation associated with an element.
+ */
+abstract class ElementAnnotation {
+  /**
+   * An empty list of annotations.
+   */
+  static const List<ElementAnnotation> EMPTY_LIST = const <ElementAnnotation>[];
+
+  /**
+   * Return the element representing the field, variable, or const constructor
+   * being used as an annotation.
+   */
+  Element get element;
+
+  /**
+   * Return `true` if this annotation marks the associated element as being
+   * deprecated.
+   */
+  bool get isDeprecated;
+
+  /**
+   * Return `true` if this annotation marks the associated method as being
+   * expected to override an inherited method.
+   */
+  bool get isOverride;
+
+  /**
+   * Return `true` if this annotation marks the associated class as implementing
+   * a proxy object.
+   */
+  bool get isProxy;
+}
+
+/**
+ * A concrete implementation of an [ElementAnnotation].
+ */
+class ElementAnnotationImpl implements ElementAnnotation {
+  /**
+   * An empty list of annotations.
+   */
+  @deprecated // Use ElementAnnotation.EMPTY_LIST
+  static const List<ElementAnnotationImpl> EMPTY_ARRAY =
+      const <ElementAnnotationImpl>[];
+
+  /**
+   * The name of the class used to mark an element as being deprecated.
+   */
+  static String _DEPRECATED_CLASS_NAME = "Deprecated";
+
+  /**
+   * The name of the top-level variable used to mark an element as being
+   * deprecated.
+   */
+  static String _DEPRECATED_VARIABLE_NAME = "deprecated";
+
+  /**
+   * The name of the top-level variable used to mark a method as being expected
+   * to override an inherited method.
+   */
+  static String _OVERRIDE_VARIABLE_NAME = "override";
+
+  /**
+   * The name of the top-level variable used to mark a class as implementing a
+   * proxy object.
+   */
+  static String PROXY_VARIABLE_NAME = "proxy";
+
+  /**
+   * The element representing the field, variable, or constructor being used as
+   * an annotation.
+   */
+  final Element element;
+
+  /**
+   * The result of evaluating this annotation as a compile-time constant
+   * expression, or `null` if the compilation unit containing the variable has
+   * not been resolved.
+   */
+  EvaluationResultImpl evaluationResult;
+
+  /**
+   * Initialize a newly created annotation. The given [element] is the element
+   * representing the field, variable, or constructor being used as an
+   * annotation.
+   */
+  ElementAnnotationImpl(this.element);
+
+  @override
+  bool get isDeprecated {
+    if (element != null) {
+      LibraryElement library = element.library;
+      if (library != null && library.isDartCore) {
+        if (element is ConstructorElement) {
+          ConstructorElement constructorElement = element as ConstructorElement;
+          if (constructorElement.enclosingElement.name ==
+              _DEPRECATED_CLASS_NAME) {
+            return true;
+          }
+        } else if (element is PropertyAccessorElement &&
+            element.name == _DEPRECATED_VARIABLE_NAME) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  @override
+  bool get isOverride {
+    if (element != null) {
+      LibraryElement library = element.library;
+      if (library != null && library.isDartCore) {
+        if (element is PropertyAccessorElement &&
+            element.name == _OVERRIDE_VARIABLE_NAME) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  @override
+  bool get isProxy {
+    if (element != null) {
+      LibraryElement library = element.library;
+      if (library != null && library.isDartCore) {
+        if (element is PropertyAccessorElement &&
+            element.name == PROXY_VARIABLE_NAME) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  @override
+  String toString() => '@$element';
+}
+
+/**
+ * A base class for concrete implementations of an [Element].
+ */
+abstract class ElementImpl implements Element {
+  static int _NEXT_ID = 0;
+
+  final int id = _NEXT_ID++;
+
+  /**
+   * The enclosing element of this element, or `null` if this element is at the
+   * root of the element structure.
+   */
+  ElementImpl _enclosingElement;
+
+  /**
+   * The name of this element.
+   */
+  String _name;
+
+  /**
+   * The offset of the name of this element in the file that contains the
+   * declaration of this element.
+   */
+  int _nameOffset = 0;
+
+  /**
+   * A bit-encoded form of the modifiers associated with this element.
+   */
+  int _modifiers = 0;
+
+  /**
+   * A list containing all of the metadata associated with this element.
+   */
+  List<ElementAnnotation> metadata = ElementAnnotation.EMPTY_LIST;
+
+  /**
+   * A cached copy of the calculated hashCode for this element.
+   */
+  int _cachedHashCode;
+
+  /**
+   * A cached copy of the calculated location for this element.
+   */
+  ElementLocation _cachedLocation;
+
+  /**
+   * Initialize a newly created element to have the given [name] at the given
+   * [_nameOffset].
+   */
+  ElementImpl(String name, this._nameOffset) {
+    this._name = StringUtilities.intern(name);
+  }
+
+  /**
+   * Initialize a newly created element to have the given [name].
+   */
+  ElementImpl.forNode(Identifier name)
+      : this(name == null ? "" : name.name, name == null ? -1 : name.offset);
+
+  @override
+  AnalysisContext get context {
+    if (_enclosingElement == null) {
+      return null;
+    }
+    return _enclosingElement.context;
+  }
+
+  @override
+  String get displayName => _name;
+
+  @override
+  Element get enclosingElement => _enclosingElement;
+
+  /**
+   * Set the enclosing element of this element to the given [element].
+   */
+  void set enclosingElement(Element element) {
+    _enclosingElement = element as ElementImpl;
+    _cachedLocation = null;
+    _cachedHashCode = null;
+  }
+
+  @override
+  int get hashCode {
+    // TODO: We might want to re-visit this optimization in the future.
+    // We cache the hash code value as this is a very frequently called method.
+    if (_cachedHashCode == null) {
+      int hashIdentifier = identifier.hashCode;
+      Element enclosing = enclosingElement;
+      if (enclosing != null) {
+        _cachedHashCode = hashIdentifier + enclosing.hashCode;
+      } else {
+        _cachedHashCode = hashIdentifier;
+      }
+    }
+    return _cachedHashCode;
+  }
+
+  /**
+   * Return an identifier that uniquely identifies this element among the
+   * children of this element's parent.
+   */
+  String get identifier => name;
+
+  @override
+  bool get isDeprecated {
+    for (ElementAnnotation annotation in metadata) {
+      if (annotation.isDeprecated) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @override
+  bool get isOverride {
+    for (ElementAnnotation annotation in metadata) {
+      if (annotation.isOverride) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @override
+  bool get isPrivate {
+    String name = displayName;
+    if (name == null) {
+      return true;
+    }
+    return Identifier.isPrivateName(name);
+  }
+
+  @override
+  bool get isPublic => !isPrivate;
+
+  @override
+  bool get isSynthetic => hasModifier(Modifier.SYNTHETIC);
+
+  @override
+  LibraryElement get library =>
+      getAncestor((element) => element is LibraryElement);
+
+  @override
+  ElementLocation get location {
+    if (_cachedLocation == null) {
+      _cachedLocation = new ElementLocationImpl.con1(this);
+    }
+    return _cachedLocation;
+  }
+
+  @override
+  String get name => _name;
+
+  void set name(String name) {
+    this._name = name;
+    _cachedLocation = null;
+    _cachedHashCode = null;
+  }
+
+  /**
+   * The offset of the name of this element in the file that contains the
+   * declaration of this element.
+   */
+  int get nameOffset => _nameOffset;
+
+  /**
+   * Sets the offset of the name of this element in the file that contains the
+   * declaration of this element.
+   */
+  void set nameOffset(int offset) {
+    _nameOffset = offset;
+    _cachedHashCode = null;
+    _cachedLocation = null;
+  }
+
+  @override
+  AstNode get node => getNodeMatching((node) => node is AstNode);
+
+  @override
+  Source get source {
+    if (_enclosingElement == null) {
+      return null;
+    }
+    return _enclosingElement.source;
+  }
+
+  /**
+   * Set whether this element is synthetic.
+   */
+  void set synthetic(bool isSynthetic) {
+    setModifier(Modifier.SYNTHETIC, isSynthetic);
+  }
+
+  @override
+  CompilationUnit get unit => context.resolveCompilationUnit(source, library);
+
+  @override
+  bool operator ==(Object object) {
+    if (identical(this, object)) {
+      return true;
+    }
+    if (object == null || hashCode != object.hashCode) {
+      return false;
+    }
+    return object.runtimeType == runtimeType &&
+        (object as Element).location == location;
+  }
+
+  /**
+   * Append a textual representation of this element to the given [buffer].
+   */
+  void appendTo(StringBuffer buffer) {
+    if (_name == null) {
+      buffer.write("<unnamed ");
+      buffer.write(runtimeType.toString());
+      buffer.write(">");
+    } else {
+      buffer.write(_name);
+    }
+  }
+
+  @override
+  String computeDocumentationComment() {
+    AnalysisContext context = this.context;
+    if (context == null) {
+      return null;
+    }
+    return context.computeDocumentationComment(this);
+  }
+
+  /**
+   * Set this element as the enclosing element for given [element].
+   */
+  void encloseElement(ElementImpl element) {
+    element.enclosingElement = this;
+  }
+
+  @override
+  Element getAncestor(Predicate<Element> predicate) {
+    Element ancestor = _enclosingElement;
+    while (ancestor != null && !predicate(ancestor)) {
+      ancestor = ancestor.enclosingElement;
+    }
+    return ancestor;
+  }
+
+  /**
+   * Return the child of this element that is uniquely identified by the given
+   * [identifier], or `null` if there is no such child.
+   */
+  ElementImpl getChild(String identifier) => null;
+
+  @override
+  String getExtendedDisplayName(String shortName) {
+    if (shortName == null) {
+      shortName = displayName;
+    }
+    Source source = this.source;
+    if (source != null) {
+      return "$shortName (${source.fullName})";
+    }
+    return shortName;
+  }
+
+  /**
+   * Return the resolved [AstNode] of the given type enclosing [getNameOffset].
+   */
+  AstNode getNodeMatching(Predicate<AstNode> predicate) {
+    CompilationUnit unit = this.unit;
+    if (unit == null) {
+      return null;
+    }
+    int offset = nameOffset;
+    AstNode node = new NodeLocator.con1(offset).searchWithin(unit);
+    if (node == null) {
+      return null;
+    }
+    return node.getAncestor(predicate);
+  }
+
+  /**
+   * Return `true` if this element has the given [modifier] associated with it.
+   */
+  bool hasModifier(Modifier modifier) =>
+      BooleanArray.getEnum(_modifiers, modifier);
+
+  @override
+  bool isAccessibleIn(LibraryElement library) {
+    if (Identifier.isPrivateName(_name)) {
+      return library == this.library;
+    }
+    return true;
+  }
+
+  /**
+   * If the given [child] is not `null`, use the given [visitor] to visit it.
+   */
+  void safelyVisitChild(Element child, ElementVisitor visitor) {
+    if (child != null) {
+      child.accept(visitor);
+    }
+  }
+
+  /**
+   * Use the given [visitor] to visit all of the [children] in the given array.
+   */
+  void safelyVisitChildren(List<Element> children, ElementVisitor visitor) {
+    if (children != null) {
+      for (Element child in children) {
+        child.accept(visitor);
+      }
+    }
+  }
+
+  /**
+   * Set whether the given [modifier] is associated with this element to
+   * correspond to the given [value].
+   */
+  void setModifier(Modifier modifier, bool value) {
+    _modifiers = BooleanArray.setEnum(_modifiers, modifier, value);
+  }
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    appendTo(buffer);
+    return buffer.toString();
+  }
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    // There are no children to visit
+  }
+}
+
+/**
+ * The enumeration `ElementKind` defines the various kinds of elements in the
+ * element model.
+ */
+class ElementKind extends Enum<ElementKind> {
+  static const ElementKind CLASS = const ElementKind('CLASS', 0, "class");
+
+  static const ElementKind COMPILATION_UNIT =
+      const ElementKind('COMPILATION_UNIT', 1, "compilation unit");
+
+  static const ElementKind CONSTRUCTOR =
+      const ElementKind('CONSTRUCTOR', 2, "constructor");
+
+  static const ElementKind DYNAMIC =
+      const ElementKind('DYNAMIC', 3, "<dynamic>");
+
+  static const ElementKind EMBEDDED_HTML_SCRIPT =
+      const ElementKind('EMBEDDED_HTML_SCRIPT', 4, "embedded html script");
+
+  static const ElementKind ERROR = const ElementKind('ERROR', 5, "<error>");
+
+  static const ElementKind EXPORT =
+      const ElementKind('EXPORT', 6, "export directive");
+
+  static const ElementKind EXTERNAL_HTML_SCRIPT =
+      const ElementKind('EXTERNAL_HTML_SCRIPT', 7, "external html script");
+
+  static const ElementKind FIELD = const ElementKind('FIELD', 8, "field");
+
+  static const ElementKind FUNCTION =
+      const ElementKind('FUNCTION', 9, "function");
+
+  static const ElementKind GETTER = const ElementKind('GETTER', 10, "getter");
+
+  static const ElementKind HTML = const ElementKind('HTML', 11, "html");
+
+  static const ElementKind IMPORT =
+      const ElementKind('IMPORT', 12, "import directive");
+
+  static const ElementKind LABEL = const ElementKind('LABEL', 13, "label");
+
+  static const ElementKind LIBRARY =
+      const ElementKind('LIBRARY', 14, "library");
+
+  static const ElementKind LOCAL_VARIABLE =
+      const ElementKind('LOCAL_VARIABLE', 15, "local variable");
+
+  static const ElementKind METHOD = const ElementKind('METHOD', 16, "method");
+
+  static const ElementKind NAME = const ElementKind('NAME', 17, "<name>");
+
+  static const ElementKind PARAMETER =
+      const ElementKind('PARAMETER', 18, "parameter");
+
+  static const ElementKind PREFIX =
+      const ElementKind('PREFIX', 19, "import prefix");
+
+  static const ElementKind SETTER = const ElementKind('SETTER', 20, "setter");
+
+  static const ElementKind TOP_LEVEL_VARIABLE =
+      const ElementKind('TOP_LEVEL_VARIABLE', 21, "top level variable");
+
+  static const ElementKind FUNCTION_TYPE_ALIAS =
+      const ElementKind('FUNCTION_TYPE_ALIAS', 22, "function type alias");
+
+  static const ElementKind TYPE_PARAMETER =
+      const ElementKind('TYPE_PARAMETER', 23, "type parameter");
+
+  static const ElementKind UNIVERSE =
+      const ElementKind('UNIVERSE', 24, "<universe>");
+
+  static const List<ElementKind> values = const [
+    CLASS,
+    COMPILATION_UNIT,
+    CONSTRUCTOR,
+    DYNAMIC,
+    EMBEDDED_HTML_SCRIPT,
+    ERROR,
+    EXPORT,
+    EXTERNAL_HTML_SCRIPT,
+    FIELD,
+    FUNCTION,
+    GETTER,
+    HTML,
+    IMPORT,
+    LABEL,
+    LIBRARY,
+    LOCAL_VARIABLE,
+    METHOD,
+    NAME,
+    PARAMETER,
+    PREFIX,
+    SETTER,
+    TOP_LEVEL_VARIABLE,
+    FUNCTION_TYPE_ALIAS,
+    TYPE_PARAMETER,
+    UNIVERSE
+  ];
+
+  /**
+   * The name displayed in the UI for this kind of element.
+   */
+  final String displayName;
+
+  /**
+   * Initialize a newly created element kind to have the given [displayName].
+   */
+  const ElementKind(String name, int ordinal, this.displayName)
+      : super(name, ordinal);
+
+  /**
+   * Return the kind of the given [element], or [ERROR] if the element is
+   * `null`. This is a utility method that can reduce the need for null checks
+   * in other places.
+   */
+  static ElementKind of(Element element) {
+    if (element == null) {
+      return ERROR;
+    }
+    return element.kind;
+  }
+}
+
+/**
+ * The location of an element within the element model.
+ */
+abstract class ElementLocation {
+  /**
+   * Return the path to the element whose location is represented by this
+   * object. Clients must not modify the returned array.
+   */
+  List<String> get components;
+
+  /**
+   * Return an encoded representation of this location that can be used to
+   * create a location that is equal to this location.
+   */
+  String get encoding;
+}
+
+/**
+ * A concrete implementation of an [ElementLocation].
+ */
+class ElementLocationImpl implements ElementLocation {
+  /**
+   * The character used to separate components in the encoded form.
+   */
+  static int _SEPARATOR_CHAR = 0x3B;
+
+  /**
+   * The path to the element whose location is represented by this object.
+   */
+  List<String> _components;
+
+  /**
+   * The object managing [indexKeyId] and [indexLocationId].
+   */
+  Object indexOwner;
+
+  /**
+   * A cached id of this location in index.
+   */
+  int indexKeyId;
+
+  /**
+   * A cached id of this location in index.
+   */
+  int indexLocationId;
+
+  /**
+   * Initialize a newly created location to represent the given [element].
+   */
+  ElementLocationImpl.con1(Element element) {
+    List<String> components = new List<String>();
+    Element ancestor = element;
+    while (ancestor != null) {
+      components.insert(0, (ancestor as ElementImpl).identifier);
+      ancestor = ancestor.enclosingElement;
+    }
+    this._components = components;
+  }
+
+  /**
+   * Initialize a newly created location from the given [encoding].
+   */
+  ElementLocationImpl.con2(String encoding) {
+    this._components = _decode(encoding);
+  }
+
+  /**
+   * Initialize a newly created location from the given [components].
+   */
+  ElementLocationImpl.con3(List<String> components) {
+    this._components = components;
+  }
+
+  @override
+  List<String> get components => _components;
+
+  @override
+  String get encoding {
+    StringBuffer buffer = new StringBuffer();
+    int length = _components.length;
+    for (int i = 0; i < length; i++) {
+      if (i > 0) {
+        buffer.writeCharCode(_SEPARATOR_CHAR);
+      }
+      _encode(buffer, _components[i]);
+    }
+    return buffer.toString();
+  }
+
+  @override
+  int get hashCode {
+    int result = 1;
+    for (int i = 0; i < _components.length; i++) {
+      String component = _components[i];
+      result = 31 * result + component.hashCode;
+    }
+    return result;
+  }
+
+  @override
+  bool operator ==(Object object) {
+    if (identical(this, object)) {
+      return true;
+    }
+    if (object is! ElementLocationImpl) {
+      return false;
+    }
+    ElementLocationImpl location = object as ElementLocationImpl;
+    List<String> otherComponents = location._components;
+    int length = _components.length;
+    if (otherComponents.length != length) {
+      return false;
+    }
+    for (int i = 0; i < length; i++) {
+      if (_components[i] != otherComponents[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @override
+  String toString() => encoding;
+
+  /**
+   * Decode the [encoding] of a location into a list of components and return
+   * the components.
+   */
+  List<String> _decode(String encoding) {
+    List<String> components = new List<String>();
+    StringBuffer buffer = new StringBuffer();
+    int index = 0;
+    int length = encoding.length;
+    while (index < length) {
+      int currentChar = encoding.codeUnitAt(index);
+      if (currentChar == _SEPARATOR_CHAR) {
+        if (index + 1 < length &&
+            encoding.codeUnitAt(index + 1) == _SEPARATOR_CHAR) {
+          buffer.writeCharCode(_SEPARATOR_CHAR);
+          index += 2;
+        } else {
+          components.add(buffer.toString());
+          buffer = new StringBuffer();
+          index++;
+        }
+      } else {
+        buffer.writeCharCode(currentChar);
+        index++;
+      }
+    }
+    components.add(buffer.toString());
+    return components;
+  }
+
+  /**
+   * Append an encoded form of the given [component] to the given [buffer].
+   */
+  void _encode(StringBuffer buffer, String component) {
+    int length = component.length;
+    for (int i = 0; i < length; i++) {
+      int currentChar = component.codeUnitAt(i);
+      if (currentChar == _SEPARATOR_CHAR) {
+        buffer.writeCharCode(_SEPARATOR_CHAR);
+      }
+      buffer.writeCharCode(currentChar);
+    }
+  }
+}
+
+/**
+ * A pair of [Element]s. [Object.==] and
+ * [Object.hashCode] so this class can be used in hashed data structures.
+ */
+class ElementPair {
+  /**
+   * The first [Element].
+   */
+  final Element _first;
+
+  /**
+   * The second [Element].
+   */
+  final Element _second;
+
+  /**
+   * A cached copy of the calculated hashCode for this element.
+   */
+  int _cachedHashCode;
+
+  /**
+   * Initialize a newly created pair of elements consisting of the [_first] and
+   * [_second] elements.
+   */
+  ElementPair(this._first, this._second) {
+    _cachedHashCode = JenkinsSmiHash.hash2(_first.hashCode, _second.hashCode);
+  }
+
+  /**
+   * Return the first element.
+   */
+  Element get firstElt => _first;
+
+  @override
+  int get hashCode {
+    return _cachedHashCode;
+  }
+
+  /**
+   * Return the second element
+   */
+  Element get secondElt => _second;
+
+  @override
+  bool operator ==(Object object) {
+    if (identical(object, this)) {
+      return true;
+    }
+    return object is ElementPair &&
+        _first == object._first &&
+        _second == object._second;
+  }
+}
+
+/**
+ * An object that can be used to visit an element structure.
+ */
+abstract class ElementVisitor<R> {
+  R visitClassElement(ClassElement element);
+
+  R visitCompilationUnitElement(CompilationUnitElement element);
+
+  R visitConstructorElement(ConstructorElement element);
+
+  R visitEmbeddedHtmlScriptElement(EmbeddedHtmlScriptElement element);
+
+  R visitExportElement(ExportElement element);
+
+  R visitExternalHtmlScriptElement(ExternalHtmlScriptElement element);
+
+  R visitFieldElement(FieldElement element);
+
+  R visitFieldFormalParameterElement(FieldFormalParameterElement element);
+
+  R visitFunctionElement(FunctionElement element);
+
+  R visitFunctionTypeAliasElement(FunctionTypeAliasElement element);
+
+  R visitHtmlElement(HtmlElement element);
+
+  R visitImportElement(ImportElement element);
+
+  R visitLabelElement(LabelElement element);
+
+  R visitLibraryElement(LibraryElement element);
+
+  R visitLocalVariableElement(LocalVariableElement element);
+
+  R visitMethodElement(MethodElement element);
+
+  R visitMultiplyDefinedElement(MultiplyDefinedElement element);
+
+  R visitParameterElement(ParameterElement element);
+
+  R visitPrefixElement(PrefixElement element);
+
+  R visitPropertyAccessorElement(PropertyAccessorElement element);
+
+  R visitTopLevelVariableElement(TopLevelVariableElement element);
+
+  R visitTypeParameterElement(TypeParameterElement element);
+}
+
+/**
+ * A script tag in an HTML file having content that defines a Dart library.
+ */
+abstract class EmbeddedHtmlScriptElement implements HtmlScriptElement {
+  /**
+   * Return the library element defined by the content of the script tag.
+   */
+  LibraryElement get scriptLibrary;
+}
+
+/**
+ * A concrete implementation of an [EmbeddedHtmlScriptElement].
+ */
+class EmbeddedHtmlScriptElementImpl extends HtmlScriptElementImpl
+    implements EmbeddedHtmlScriptElement {
+  /**
+   * The library defined by the script tag's content.
+   */
+  LibraryElement _scriptLibrary;
+
+  /**
+   * Initialize a newly created script element to represent the given [node].
+   */
+  EmbeddedHtmlScriptElementImpl(XmlTagNode node) : super(node);
+
+  @override
+  ElementKind get kind => ElementKind.EMBEDDED_HTML_SCRIPT;
+
+  @override
+  LibraryElement get scriptLibrary => _scriptLibrary;
+
+  /**
+   * Set the script library defined by the script tag's content to the given
+   * [library].
+   */
+  void set scriptLibrary(LibraryElementImpl library) {
+    library.enclosingElement = this;
+    _scriptLibrary = library;
+  }
+
+  @override
+  accept(ElementVisitor visitor) =>
+      visitor.visitEmbeddedHtmlScriptElement(this);
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    safelyVisitChild(_scriptLibrary, visitor);
+  }
+}
+
+/**
+ * An element representing an executable object, including functions, methods,
+ * constructors, getters, and setters.
+ */
+abstract class ExecutableElement implements Element {
+  /**
+   * An empty list of executable elements.
+   */
+  static const List<ExecutableElement> EMPTY_LIST = const <ExecutableElement>[];
+
+  /**
+   * Return a list containing all of the functions defined within this
+   * executable element.
+   */
+  List<FunctionElement> get functions;
+
+  /**
+   * Return `true` if this executable element is abstract.
+   * Executable elements are abstract if they are not external and have no body.
+   */
+  bool get isAbstract;
+
+  /**
+   * Return `true` if this executable element has body marked as being
+   * asynchronous.
+   */
+  bool get isAsynchronous;
+
+  /**
+   * Return `true` if this executable element has a body marked as being a
+   * generator.
+   */
+  bool get isGenerator;
+
+  /**
+   * Return `true` if this executable element is an operator. The test may be
+   * based on the name of the executable element, in which case the result will
+   * be correct when the name is legal.
+   */
+  bool get isOperator;
+
+  /**
+   * Return `true` if this element is a static element. A static element is an
+   * element that is not associated with a particular instance, but rather with
+   * an entire library or class.
+   */
+  bool get isStatic;
+
+  /**
+   * Return `true` if this executable element has a body marked as being
+   * synchronous.
+   */
+  bool get isSynchronous;
+
+  /**
+   * Return a list containing all of the labels defined within this executable
+   * element.
+   */
+  List<LabelElement> get labels;
+
+  /**
+   * Return a list containing all of the local variables defined within this
+   * executable element.
+   */
+  List<LocalVariableElement> get localVariables;
+
+  /**
+   * Return a list containing all of the parameters defined by this executable
+   * element.
+   */
+  List<ParameterElement> get parameters;
+
+  /**
+   * Return the return type defined by this executable element.
+   */
+  DartType get returnType;
+
+  /**
+   * Return the type of function defined by this executable element.
+   */
+  FunctionType get type;
+}
+
+/**
+ * A base class for concrete implementations of an [ExecutableElement].
+ */
+abstract class ExecutableElementImpl extends ElementImpl
+    implements ExecutableElement {
+  /**
+   * An empty list of executable elements.
+   */
+  @deprecated // Use ExecutableElement.EMPTY_LIST
+  static const List<ExecutableElement> EMPTY_ARRAY =
+      const <ExecutableElement>[];
+
+  /**
+   * A list containing all of the functions defined within this executable
+   * element.
+   */
+  List<FunctionElement> _functions = FunctionElement.EMPTY_LIST;
+
+  /**
+   * A list containing all of the labels defined within this executable element.
+   */
+  List<LabelElement> _labels = LabelElement.EMPTY_LIST;
+
+  /**
+   * A list containing all of the local variables defined within this executable
+   * element.
+   */
+  List<LocalVariableElement> _localVariables = LocalVariableElement.EMPTY_LIST;
+
+  /**
+   * A list containing all of the parameters defined by this executable element.
+   */
+  List<ParameterElement> _parameters = ParameterElement.EMPTY_LIST;
+
+  /**
+   * The return type defined by this executable element.
+   */
+  DartType returnType;
+
+  /**
+   * The type of function defined by this executable element.
+   */
+  FunctionType type;
+
+  /**
+   * Initialize a newly created executable element to have the given [name] and
+   * [offset].
+   */
+  ExecutableElementImpl(String name, int offset) : super(name, offset);
+
+  /**
+   * Initialize a newly created executable element to have the given [name].
+   */
+  ExecutableElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  /**
+   * Set whether this method's body is asynchronous.
+   */
+  void set asynchronous(bool isAsynchronous) {
+    setModifier(Modifier.ASYNCHRONOUS, isAsynchronous);
+  }
+
+  @override
+  List<FunctionElement> get functions => _functions;
+
+  /**
+   * Set the functions defined within this executable element to the given
+   * [functions].
+   */
+  void set functions(List<FunctionElement> functions) {
+    for (FunctionElement function in functions) {
+      (function as FunctionElementImpl).enclosingElement = this;
+    }
+    this._functions = functions;
+  }
+
+  /**
+   * Set whether this method's body is a generator.
+   */
+  void set generator(bool isGenerator) {
+    setModifier(Modifier.GENERATOR, isGenerator);
+  }
+
+  @override
+  bool get isAbstract => hasModifier(Modifier.ABSTRACT);
+
+  @override
+  bool get isAsynchronous => hasModifier(Modifier.ASYNCHRONOUS);
+
+  @override
+  bool get isGenerator => hasModifier(Modifier.GENERATOR);
+
+  @override
+  bool get isOperator => false;
+
+  @override
+  bool get isSynchronous => !hasModifier(Modifier.ASYNCHRONOUS);
+
+  @override
+  List<LabelElement> get labels => _labels;
+
+  /**
+   * Set the labels defined within this executable element to the given
+   * [labels].
+   */
+  void set labels(List<LabelElement> labels) {
+    for (LabelElement label in labels) {
+      (label as LabelElementImpl).enclosingElement = this;
+    }
+    this._labels = labels;
+  }
+
+  @override
+  List<LocalVariableElement> get localVariables => _localVariables;
+
+  /**
+   * Set the local variables defined within this executable element to the given
+   * [variables].
+   */
+  void set localVariables(List<LocalVariableElement> variables) {
+    for (LocalVariableElement variable in variables) {
+      (variable as LocalVariableElementImpl).enclosingElement = this;
+    }
+    this._localVariables = variables;
+  }
+
+  @override
+  List<ParameterElement> get parameters => _parameters;
+
+  /**
+   * Set the parameters defined by this executable element to the given
+   * [parameters].
+   */
+  void set parameters(List<ParameterElement> parameters) {
+    for (ParameterElement parameter in parameters) {
+      (parameter as ParameterElementImpl).enclosingElement = this;
+    }
+    this._parameters = parameters;
+  }
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    if (this.kind != ElementKind.GETTER) {
+      buffer.write("(");
+      String closing = null;
+      ParameterKind kind = ParameterKind.REQUIRED;
+      int parameterCount = _parameters.length;
+      for (int i = 0; i < parameterCount; i++) {
+        if (i > 0) {
+          buffer.write(", ");
+        }
+        ParameterElementImpl parameter = _parameters[i] as ParameterElementImpl;
+        ParameterKind parameterKind = parameter.parameterKind;
+        if (parameterKind != kind) {
+          if (closing != null) {
+            buffer.write(closing);
+          }
+          if (parameterKind == ParameterKind.POSITIONAL) {
+            buffer.write("[");
+            closing = "]";
+          } else if (parameterKind == ParameterKind.NAMED) {
+            buffer.write("{");
+            closing = "}";
+          } else {
+            closing = null;
+          }
+        }
+        kind = parameterKind;
+        parameter.appendToWithoutDelimiters(buffer);
+      }
+      if (closing != null) {
+        buffer.write(closing);
+      }
+      buffer.write(")");
+    }
+    if (type != null) {
+      buffer.write(Element.RIGHT_ARROW);
+      buffer.write(type.returnType);
+    }
+  }
+
+  @override
+  ElementImpl getChild(String identifier) {
+    for (ExecutableElement function in _functions) {
+      if ((function as ExecutableElementImpl).identifier == identifier) {
+        return function as ExecutableElementImpl;
+      }
+    }
+    for (LabelElement label in _labels) {
+      if ((label as LabelElementImpl).identifier == identifier) {
+        return label as LabelElementImpl;
+      }
+    }
+    for (VariableElement variable in _localVariables) {
+      if ((variable as VariableElementImpl).identifier == identifier) {
+        return variable as VariableElementImpl;
+      }
+    }
+    for (ParameterElement parameter in _parameters) {
+      if ((parameter as ParameterElementImpl).identifier == identifier) {
+        return parameter as ParameterElementImpl;
+      }
+    }
+    return null;
+  }
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    super.visitChildren(visitor);
+    safelyVisitChildren(_functions, visitor);
+    safelyVisitChildren(_labels, visitor);
+    safelyVisitChildren(_localVariables, visitor);
+    safelyVisitChildren(_parameters, visitor);
+  }
+}
+
+/**
+ * An executable element defined in a parameterized type where the values of the
+ * type parameters are known.
+ */
+abstract class ExecutableMember extends Member implements ExecutableElement {
+  /**
+   * Initialize a newly created element to represent a constructor, based on the
+   * [baseElement], defined by the [definingType].
+   */
+  ExecutableMember(ExecutableElement baseElement, InterfaceType definingType)
+      : super(baseElement, definingType);
+
+  @override
+  ExecutableElement get baseElement => super.baseElement as ExecutableElement;
+
+  @override
+  List<FunctionElement> get functions {
+    //
+    // Elements within this element should have type parameters substituted,
+    // just like this element.
+    //
+    throw new UnsupportedOperationException();
+//    return getBaseElement().getFunctions();
+  }
+
+  @override
+  bool get isAbstract => baseElement.isAbstract;
+
+  @override
+  bool get isAsynchronous => baseElement.isAsynchronous;
+
+  @override
+  bool get isGenerator => baseElement.isGenerator;
+
+  @override
+  bool get isOperator => baseElement.isOperator;
+
+  @override
+  bool get isStatic => baseElement.isStatic;
+
+  @override
+  bool get isSynchronous => baseElement.isSynchronous;
+
+  @override
+  List<LabelElement> get labels => baseElement.labels;
+
+  @override
+  List<LocalVariableElement> get localVariables {
+    //
+    // Elements within this element should have type parameters substituted,
+    // just like this element.
+    //
+    throw new UnsupportedOperationException();
+//    return getBaseElement().getLocalVariables();
+  }
+
+  @override
+  List<ParameterElement> get parameters {
+    List<ParameterElement> baseParameters = baseElement.parameters;
+    int parameterCount = baseParameters.length;
+    if (parameterCount == 0) {
+      return baseParameters;
+    }
+    List<ParameterElement> parameterizedParameters =
+        new List<ParameterElement>(parameterCount);
+    for (int i = 0; i < parameterCount; i++) {
+      parameterizedParameters[i] =
+          ParameterMember.from(baseParameters[i], definingType);
+    }
+    return parameterizedParameters;
+  }
+
+  @override
+  DartType get returnType => substituteFor(baseElement.returnType);
+
+  @override
+  FunctionType get type => substituteFor(baseElement.type);
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    // TODO(brianwilkerson) We need to finish implementing the accessors used
+    // below so that we can safely invoke them.
+    super.visitChildren(visitor);
+    safelyVisitChildren(baseElement.functions, visitor);
+    safelyVisitChildren(labels, visitor);
+    safelyVisitChildren(baseElement.localVariables, visitor);
+    safelyVisitChildren(parameters, visitor);
+  }
+}
+
+/**
+ * An export directive within a library.
+ */
+abstract class ExportElement implements Element, UriReferencedElement {
+  /**
+   * An empty list of export elements.
+   */
+  @deprecated // Use ExportElement.EMPTY_LIST
+  static const List<ExportElement> EMPTY_ARRAY = const <ExportElement>[];
+
+  /**
+   * An empty list of export elements.
+   */
+  static const List<ExportElement> EMPTY_LIST = const <ExportElement>[];
+
+  /**
+   * Return a list containing the combinators that were specified as part of the
+   * export directive in the order in which they were specified.
+   */
+  List<NamespaceCombinator> get combinators;
+
+  /**
+   * Return the library that is exported from this library by this export
+   * directive.
+   */
+  LibraryElement get exportedLibrary;
+}
+
+/**
+ * A concrete implementation of an [ExportElement].
+ */
+class ExportElementImpl extends UriReferencedElementImpl
+    implements ExportElement {
+  /**
+   * The library that is exported from this library by this export directive.
+   */
+  LibraryElement exportedLibrary;
+
+  /**
+   * The combinators that were specified as part of the export directive in the
+   * order in which they were specified.
+   */
+  List<NamespaceCombinator> combinators = NamespaceCombinator.EMPTY_LIST;
+
+  /**
+   * Initialize a newly created export element at the given [offset].
+   */
+  ExportElementImpl(int offset) : super(null, offset);
+
+  @override
+  String get identifier => exportedLibrary.name;
+
+  @override
+  ElementKind get kind => ElementKind.EXPORT;
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitExportElement(this);
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    buffer.write("export ");
+    (exportedLibrary as LibraryElementImpl).appendTo(buffer);
+  }
+}
+
+/**
+ * A script tag in an HTML file having a `source` attribute that references a
+ * Dart library source file.
+ */
+abstract class ExternalHtmlScriptElement implements HtmlScriptElement {
+  /**
+   * Return the source referenced by this element, or `null` if this element
+   * does not reference a Dart library source file.
+   */
+  Source get scriptSource;
+}
+
+/**
+ * A concrete implementation of an [ExternalHtmlScriptElement].
+ */
+class ExternalHtmlScriptElementImpl extends HtmlScriptElementImpl
+    implements ExternalHtmlScriptElement {
+  /**
+   * The source specified in the `source` attribute or `null` if unspecified.
+   */
+  Source scriptSource;
+
+  /**
+   * Initialize a newly created script element to correspond to the given
+   * [node].
+   */
+  ExternalHtmlScriptElementImpl(XmlTagNode node) : super(node);
+
+  @override
+  ElementKind get kind => ElementKind.EXTERNAL_HTML_SCRIPT;
+
+  @override
+  accept(ElementVisitor visitor) =>
+      visitor.visitExternalHtmlScriptElement(this);
+}
+
+/**
+ * A field defined within a type.
+ */
+abstract class FieldElement
+    implements ClassMemberElement, PropertyInducingElement {
+  /**
+   * An empty list of field elements.
+   */
+  static const List<FieldElement> EMPTY_LIST = const <FieldElement>[];
+
+  /**
+   * Return {@code true} if this element is an enum constant.
+   */
+  bool get isEnumConstant;
+
+  /**
+   * Return the resolved [VariableDeclaration] or [EnumConstantDeclaration]
+   * node that declares this [FieldElement].
+   *
+   * This method is expensive, because resolved AST might be evicted from cache,
+   * so parsing and resolving will be performed.
+   */
+  @override
+  AstNode get node;
+}
+
+/**
+ * A concrete implementation of a [FieldElement].
+ */
+class FieldElementImpl extends PropertyInducingElementImpl
+    with PotentiallyConstVariableElement implements FieldElement {
+  /**
+   * An empty list of field elements.
+   */
+  @deprecated // Use FieldElement.EMPTY_LIST
+  static const List<FieldElement> EMPTY_ARRAY = const <FieldElement>[];
+
+  /**
+   * Initialize a newly created synthetic field element to have the given [name]
+   * at the given [offset].
+   */
+  FieldElementImpl(String name, int offset) : super(name, offset);
+
+  /**
+   * Initialize a newly created field element to have the given [name].
+   */
+  FieldElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  @override
+  ClassElement get enclosingElement => super.enclosingElement as ClassElement;
+
+  @override
+  bool get isEnumConstant =>
+      enclosingElement != null ? enclosingElement.isEnum : false;
+
+  @override
+  bool get isStatic => hasModifier(Modifier.STATIC);
+
+  @override
+  ElementKind get kind => ElementKind.FIELD;
+
+  @override
+  AstNode get node {
+    if (isEnumConstant) {
+      return getNodeMatching((node) => node is EnumConstantDeclaration);
+    } else {
+      return getNodeMatching((node) => node is VariableDeclaration);
+    }
+  }
+
+  /**
+   * Set whether this field is static.
+   */
+  void set static(bool isStatic) {
+    setModifier(Modifier.STATIC, isStatic);
+  }
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitFieldElement(this);
+}
+
+/**
+ * A field formal parameter defined within a constructor element.
+ */
+abstract class FieldFormalParameterElement implements ParameterElement {
+  /**
+   * Return the field element associated with this field formal parameter, or
+   * `null` if the parameter references a field that doesn't exist.
+   */
+  FieldElement get field;
+}
+
+/**
+ * A [ParameterElementImpl] that has the additional information of the
+ * [FieldElement] associated with the parameter.
+ */
+class FieldFormalParameterElementImpl extends ParameterElementImpl
+    implements FieldFormalParameterElement {
+  /**
+   * The field associated with this field formal parameter.
+   */
+  FieldElement field;
+
+  /**
+   * Initialize a newly created parameter element to have the given [name].
+   */
+  FieldFormalParameterElementImpl(Identifier name) : super.forNode(name);
+
+  @override
+  bool get isInitializingFormal => true;
+
+  @override
+  accept(ElementVisitor visitor) =>
+      visitor.visitFieldFormalParameterElement(this);
+}
+
+/**
+ * A parameter element defined in a parameterized type where the values of the
+ * type parameters are known.
+ */
+class FieldFormalParameterMember extends ParameterMember
+    implements FieldFormalParameterElement {
+  /**
+   * Initialize a newly created element to represent a constructor, based on the
+   * [baseElement], defined by the [definingType].
+   */
+  FieldFormalParameterMember(
+      FieldFormalParameterElement baseElement, ParameterizedType definingType)
+      : super(baseElement, definingType);
+
+  @override
+  FieldElement get field {
+    FieldElement field = (baseElement as FieldFormalParameterElement).field;
+    if (field is FieldElement) {
+      return FieldMember.from(field, definingType);
+    }
+    return field;
+  }
+
+  @override
+  accept(ElementVisitor visitor) =>
+      visitor.visitFieldFormalParameterElement(this);
+}
+
+/**
+ * A field element defined in a parameterized type where the values of the type
+ * parameters are known.
+ */
+class FieldMember extends VariableMember implements FieldElement {
+  /**
+   * Initialize a newly created element to represent a constructor, based on the
+   * [baseElement], defined by the [definingType].
+   */
+  FieldMember(FieldElement baseElement, InterfaceType definingType)
+      : super(baseElement, definingType);
+
+  @override
+  FieldElement get baseElement => super.baseElement as FieldElement;
+
+  @override
+  InterfaceType get definingType => super.definingType as InterfaceType;
+
+  @override
+  ClassElement get enclosingElement => baseElement.enclosingElement;
+
+  @override
+  PropertyAccessorElement get getter =>
+      PropertyAccessorMember.from(baseElement.getter, definingType);
+
+  @override
+  bool get isEnumConstant => baseElement.isEnumConstant;
+
+  @override
+  bool get isStatic => baseElement.isStatic;
+
+  @override
+  VariableDeclaration get node => baseElement.node;
+
+  @override
+  DartType get propagatedType => substituteFor(baseElement.propagatedType);
+
+  @override
+  PropertyAccessorElement get setter =>
+      PropertyAccessorMember.from(baseElement.setter, definingType);
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitFieldElement(this);
+
+  @override
+  String toString() => '$type $displayName';
+
+  /**
+   * If the given [field]'s type is different when any type parameters from the
+   * defining type's declaration are replaced with the actual type arguments
+   * from the [definingType], create a field member representing the given
+   * field. Return the member that was created, or the base field if no member
+   * was created.
+   */
+  static FieldElement from(FieldElement field, InterfaceType definingType) {
+    if (!_isChangedByTypeSubstitution(field, definingType)) {
+      return field;
+    }
+    // TODO(brianwilkerson) Consider caching the substituted type in the
+    // instance. It would use more memory but speed up some operations.
+    // We need to see how often the type is being re-computed.
+    return new FieldMember(field, definingType);
+  }
+
+  /**
+   * Determine whether the given [field]'s type is changed when type parameters
+   * from the [definingType]'s declaration are replaced with the actual type
+   * arguments from the defining type.
+   */
+  static bool _isChangedByTypeSubstitution(
+      FieldElement field, InterfaceType definingType) {
+    List<DartType> argumentTypes = definingType.typeArguments;
+    if (field != null && argumentTypes.length != 0) {
+      DartType baseType = field.type;
+      List<DartType> parameterTypes = definingType.element.type.typeArguments;
+      if (baseType != null) {
+        DartType substitutedType =
+            baseType.substitute2(argumentTypes, parameterTypes);
+        if (baseType != substitutedType) {
+          return true;
+        }
+      }
+      // If the field has a propagated type, then we need to check whether the
+      // propagated type needs substitution.
+      DartType basePropagatedType = field.propagatedType;
+      if (basePropagatedType != null) {
+        DartType substitutedPropagatedType =
+            basePropagatedType.substitute2(argumentTypes, parameterTypes);
+        if (basePropagatedType != substitutedPropagatedType) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+}
+
+/**
+ * A (non-method) function. This can be either a top-level function, a local
+ * function, a closure, or the initialization expression for a field or
+ * variable.
+ */
+abstract class FunctionElement implements ExecutableElement, LocalElement {
+  /**
+   * An empty list of function elements.
+   */
+  static const List<FunctionElement> EMPTY_LIST = const <FunctionElement>[];
+
+  /**
+   * The name of the method that can be implemented by a class to allow its
+   * instances to be invoked as if they were a function.
+   */
+  static final String CALL_METHOD_NAME = "call";
+
+  /**
+   * The name of the synthetic function defined for libraries that are deferred.
+   */
+  static final String LOAD_LIBRARY_NAME = "loadLibrary";
+
+  /**
+   * The name of the function used as an entry point.
+   */
+  static const String MAIN_FUNCTION_NAME = "main";
+
+  /**
+   * The name of the method that will be invoked if an attempt is made to invoke
+   * an undefined method on an object.
+   */
+  static final String NO_SUCH_METHOD_METHOD_NAME = "noSuchMethod";
+
+  /**
+   * Return `true` if the function is an entry point, i.e. a top-level function
+   * and has the name `main`.
+   */
+  bool get isEntryPoint;
+
+  /**
+   * Return the resolved function declaration node that declares this element.
+   *
+   * This method is expensive, because resolved AST might be evicted from cache,
+   * so parsing and resolving will be performed.
+   */
+  @override
+  FunctionDeclaration get node;
+}
+
+/**
+ * A concrete implementation of a [FunctionElement].
+ */
+class FunctionElementImpl extends ExecutableElementImpl
+    implements FunctionElement {
+  /**
+   * An empty list of function elements.
+   */
+  @deprecated // Use FunctionElement.EMPTY_LIST
+  static const List<FunctionElement> EMPTY_ARRAY = const <FunctionElement>[];
+
+  /**
+   * The offset to the beginning of the visible range for this element.
+   */
+  int _visibleRangeOffset = 0;
+
+  /**
+   * The length of the visible range for this element, or `-1` if this element
+   * does not have a visible range.
+   */
+  int _visibleRangeLength = -1;
+
+  /**
+   * Initialize a newly created function element to have the given [name] and
+   * [offset].
+   */
+  FunctionElementImpl(String name, int offset) : super(name, offset);
+
+  /**
+   * Initialize a newly created function element to have the given [name].
+   */
+  FunctionElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  /**
+   * Initialize a newly created function element to have no name and the given
+   * [offset]. This is used for function expressions, that have no name.
+   */
+  FunctionElementImpl.forOffset(int nameOffset) : super("", nameOffset);
+
+  @override
+  String get identifier {
+    String identifier = super.identifier;
+    if (!isStatic) {
+      identifier += "@$nameOffset";
+    }
+    return identifier;
+  }
+
+  @override
+  bool get isEntryPoint {
+    return isStatic && displayName == FunctionElement.MAIN_FUNCTION_NAME;
+  }
+
+  @override
+  bool get isStatic => enclosingElement is CompilationUnitElement;
+
+  @override
+  ElementKind get kind => ElementKind.FUNCTION;
+
+  @override
+  FunctionDeclaration get node =>
+      getNodeMatching((node) => node is FunctionDeclaration);
+
+  @override
+  SourceRange get visibleRange {
+    if (_visibleRangeLength < 0) {
+      return null;
+    }
+    return new SourceRange(_visibleRangeOffset, _visibleRangeLength);
+  }
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitFunctionElement(this);
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    String name = displayName;
+    if (name != null) {
+      buffer.write(name);
+    }
+    super.appendTo(buffer);
+  }
+
+  /**
+   * Set the visible range for this element to the range starting at the given
+   * [offset] with the given [length].
+   */
+  void setVisibleRange(int offset, int length) {
+    _visibleRangeOffset = offset;
+    _visibleRangeLength = length;
+  }
+}
+
+/**
+ * The type of a function, method, constructor, getter, or setter. Function
+ * types come in three variations:
+ *
+ * * The types of functions that only have required parameters. These have the
+ *   general form <i>(T<sub>1</sub>, &hellip;, T<sub>n</sub>) &rarr; T</i>.
+ * * The types of functions with optional positional parameters. These have the
+ *   general form <i>(T<sub>1</sub>, &hellip;, T<sub>n</sub>, [T<sub>n+1</sub>
+ *   &hellip;, T<sub>n+k</sub>]) &rarr; T</i>.
+ * * The types of functions with named parameters. These have the general form
+ *   <i>(T<sub>1</sub>, &hellip;, T<sub>n</sub>, {T<sub>x1</sub> x1, &hellip;,
+ *   T<sub>xk</sub> xk}) &rarr; T</i>.
+ */
+abstract class FunctionType implements ParameterizedType {
+  /**
+   * Return a map from the names of named parameters to the types of the named
+   * parameters of this type of function. The entries in the map will be
+   * iterated in the same order as the order in which the named parameters were
+   * defined. If there were no named parameters declared then the map will be
+   * empty.
+   */
+  Map<String, DartType> get namedParameterTypes;
+
+  /**
+   * Return a list containing the types of the normal parameters of this type of
+   * function. The parameter types are in the same order as they appear in the
+   * declaration of the function.
+   */
+  List<DartType> get normalParameterTypes;
+
+  /**
+   * Return a map from the names of optional (positional) parameters to the
+   * types of the optional parameters of this type of function. The entries in
+   * the map will be iterated in the same order as the order in which the
+   * optional parameters were defined. If there were no optional parameters
+   * declared then the map will be empty.
+   */
+  List<DartType> get optionalParameterTypes;
+
+  /**
+   * Return a list containing the parameters elements of this type of function.
+   * The parameter types are in the same order as they appear in the declaration
+   * of the function.
+   */
+  List<ParameterElement> get parameters;
+
+  /**
+   * Return the type of object returned by this type of function.
+   */
+  DartType get returnType;
+
+  /**
+   * Return `true` if this type is a subtype of the given [type].
+   *
+   * A function type <i>(T<sub>1</sub>, &hellip;, T<sub>n</sub>) &rarr; T</i> is
+   * a subtype of the function type <i>(S<sub>1</sub>, &hellip;, S<sub>n</sub>)
+   * &rarr; S</i>, if all of the following conditions are met:
+   *
+   * * Either
+   *   * <i>S</i> is void, or
+   *   * <i>T &hArr; S</i>.
+   *
+   * * For all <i>i</i>, 1 <= <i>i</i> <= <i>n</i>, <i>T<sub>i</sub> &hArr;
+   *   S<sub>i</sub></i>.
+   *
+   * A function type <i>(T<sub>1</sub>, &hellip;, T<sub>n</sub>,
+   * [T<sub>n+1</sub>, &hellip;, T<sub>n+k</sub>]) &rarr; T</i> is a subtype of
+   * the function type <i>(S<sub>1</sub>, &hellip;, S<sub>n</sub>,
+   * [S<sub>n+1</sub>, &hellip;, S<sub>n+m</sub>]) &rarr; S</i>, if all of the
+   * following conditions are met:
+   *
+   * * Either
+   *   * <i>S</i> is void, or
+   *   * <i>T &hArr; S</i>.
+   *
+   * * <i>k</i> >= <i>m</i> and for all <i>i</i>, 1 <= <i>i</i> <= <i>n+m</i>,
+   *   <i>T<sub>i</sub> &hArr; S<sub>i</sub></i>.
+   *
+   * A function type <i>(T<sub>1</sub>, &hellip;, T<sub>n</sub>,
+   * {T<sub>x1</sub> x1, &hellip;, T<sub>xk</sub> xk}) &rarr; T</i> is a subtype
+   * of the function type <i>(S<sub>1</sub>, &hellip;, S<sub>n</sub>,
+   * {S<sub>y1</sub> y1, &hellip;, S<sub>ym</sub> ym}) &rarr; S</i>, if all of
+   * the following conditions are met:
+   * * Either
+   *   * <i>S</i> is void,
+   *   * or <i>T &hArr; S</i>.
+   *
+   * * For all <i>i</i>, 1 <= <i>i</i> <= <i>n</i>, <i>T<sub>i</sub> &hArr;
+   *   S<sub>i</sub></i>.
+   * * <i>k</i> >= <i>m</i> and <i>y<sub>i</sub></i> in <i>{x<sub>1</sub>,
+   *   &hellip;, x<sub>k</sub>}</i>, 1 <= <i>i</i> <= <i>m</i>.
+   * * For all <i>y<sub>i</sub></i> in <i>{y<sub>1</sub>, &hellip;,
+   *   y<sub>m</sub>}</i>, <i>y<sub>i</sub> = x<sub>j</sub> => Tj &hArr; Si</i>.
+   *
+   * In addition, the following subtype rules apply:
+   *
+   * <i>(T<sub>1</sub>, &hellip;, T<sub>n</sub>, []) &rarr; T <: (T<sub>1</sub>,
+   * &hellip;, T<sub>n</sub>) &rarr; T.</i><br>
+   * <i>(T<sub>1</sub>, &hellip;, T<sub>n</sub>) &rarr; T <: (T<sub>1</sub>,
+   * &hellip;, T<sub>n</sub>, {}) &rarr; T.</i><br>
+   * <i>(T<sub>1</sub>, &hellip;, T<sub>n</sub>, {}) &rarr; T <: (T<sub>1</sub>,
+   * &hellip;, T<sub>n</sub>) &rarr; T.</i><br>
+   * <i>(T<sub>1</sub>, &hellip;, T<sub>n</sub>) &rarr; T <: (T<sub>1</sub>,
+   * &hellip;, T<sub>n</sub>, []) &rarr; T.</i>
+   *
+   * All functions implement the class `Function`. However not all function
+   * types are a subtype of `Function`. If an interface type <i>I</i> includes a
+   * method named `call()`, and the type of `call()` is the function type
+   * <i>F</i>, then <i>I</i> is considered to be a subtype of <i>F</i>.
+   */
+  @override
+  bool isSubtypeOf(DartType type);
+
+  @override
+  FunctionType substitute2(
+      List<DartType> argumentTypes, List<DartType> parameterTypes);
+
+  /**
+   * Return the type resulting from substituting the given [argumentTypes] for
+   * this type's parameters. This is fully equivalent to
+   * `substitute(argumentTypes, getTypeArguments())`.
+   */
+  FunctionType substitute3(List<DartType> argumentTypes);
+}
+
+/**
+ * A function type alias (`typedef`).
+ */
+abstract class FunctionTypeAliasElement implements Element {
+  /**
+   * An empty array of type alias elements.
+   */
+  static List<FunctionTypeAliasElement> EMPTY_LIST =
+      new List<FunctionTypeAliasElement>(0);
+
+  /**
+   * Return the compilation unit in which this type alias is defined.
+   */
+  @override
+  CompilationUnitElement get enclosingElement;
+
+  /**
+   * Return the resolved function type alias node that declares this element.
+   *
+   * This method is expensive, because resolved AST might be evicted from cache,
+   * so parsing and resolving will be performed.
+   */
+  @override
+  FunctionTypeAlias get node;
+
+  /**
+   * Return a list containing all of the parameters defined by this type alias.
+   */
+  List<ParameterElement> get parameters;
+
+  /**
+   * Return the return type defined by this type alias.
+   */
+  DartType get returnType;
+
+  /**
+   * Return the type of function defined by this type alias.
+   */
+  FunctionType get type;
+
+  /**
+   * Return a list containing all of the type parameters defined for this type.
+   */
+  List<TypeParameterElement> get typeParameters;
+}
+
+/**
+ * A concrete implementation of a [FunctionTypeAliasElement].
+ */
+class FunctionTypeAliasElementImpl extends ElementImpl
+    implements FunctionTypeAliasElement {
+  /**
+   * An empty array of type alias elements.
+   */
+  @deprecated // Use FunctionTypeAliasElement.EMPTY_LIST
+  static List<FunctionTypeAliasElement> EMPTY_ARRAY =
+      new List<FunctionTypeAliasElement>(0);
+
+  /**
+   * A list containing all of the parameters defined by this type alias.
+   */
+  List<ParameterElement> _parameters = ParameterElement.EMPTY_LIST;
+
+  /**
+   * The return type defined by this type alias.
+   */
+  DartType returnType;
+
+  /**
+   * The type of function defined by this type alias.
+   */
+  FunctionType type;
+
+  /**
+   * A list containing all of the type parameters defined for this type.
+   */
+  List<TypeParameterElement> _typeParameters = TypeParameterElement.EMPTY_LIST;
+
+  /**
+   * Initialize a newly created type alias element to have the given name.
+   *
+   * [name] the name of this element
+   * [nameOffset] the offset of the name of this element in the file that
+   *    contains the declaration of this element
+   */
+  FunctionTypeAliasElementImpl(String name, int nameOffset)
+      : super(name, nameOffset);
+
+  /**
+   * Initialize a newly created type alias element to have the given [name].
+   */
+  FunctionTypeAliasElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  @override
+  CompilationUnitElement get enclosingElement =>
+      super.enclosingElement as CompilationUnitElement;
+
+  @override
+  ElementKind get kind => ElementKind.FUNCTION_TYPE_ALIAS;
+
+  @override
+  FunctionTypeAlias get node =>
+      getNodeMatching((node) => node is FunctionTypeAlias);
+
+  @override
+  List<ParameterElement> get parameters => _parameters;
+
+  /**
+   * Set the parameters defined by this type alias to the given [parameters].
+   */
+  void set parameters(List<ParameterElement> parameters) {
+    if (parameters != null) {
+      for (ParameterElement parameter in parameters) {
+        (parameter as ParameterElementImpl).enclosingElement = this;
+      }
+    }
+    this._parameters = parameters;
+  }
+
+  @override
+  List<TypeParameterElement> get typeParameters => _typeParameters;
+
+  /**
+   * Set the type parameters defined for this type to the given
+   * [typeParameters].
+   */
+  void set typeParameters(List<TypeParameterElement> typeParameters) {
+    for (TypeParameterElement typeParameter in typeParameters) {
+      (typeParameter as TypeParameterElementImpl).enclosingElement = this;
+    }
+    this._typeParameters = typeParameters;
+  }
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitFunctionTypeAliasElement(this);
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    buffer.write("typedef ");
+    buffer.write(displayName);
+    int typeParameterCount = _typeParameters.length;
+    if (typeParameterCount > 0) {
+      buffer.write("<");
+      for (int i = 0; i < typeParameterCount; i++) {
+        if (i > 0) {
+          buffer.write(", ");
+        }
+        (_typeParameters[i] as TypeParameterElementImpl).appendTo(buffer);
+      }
+      buffer.write(">");
+    }
+    buffer.write("(");
+    int parameterCount = _parameters.length;
+    for (int i = 0; i < parameterCount; i++) {
+      if (i > 0) {
+        buffer.write(", ");
+      }
+      (_parameters[i] as ParameterElementImpl).appendTo(buffer);
+    }
+    buffer.write(")");
+    if (type != null) {
+      buffer.write(Element.RIGHT_ARROW);
+      buffer.write(type.returnType);
+    } else if (returnType != null) {
+      buffer.write(Element.RIGHT_ARROW);
+      buffer.write(returnType);
+    }
+  }
+
+  @override
+  ElementImpl getChild(String identifier) {
+    for (VariableElement parameter in _parameters) {
+      if ((parameter as VariableElementImpl).identifier == identifier) {
+        return parameter as VariableElementImpl;
+      }
+    }
+    for (TypeParameterElement typeParameter in _typeParameters) {
+      if ((typeParameter as TypeParameterElementImpl).identifier ==
+          identifier) {
+        return typeParameter as TypeParameterElementImpl;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Set the parameters defined by this type alias to the given [parameters]
+   * without becoming the parent of the parameters. This should only be used by
+   * the [TypeResolverVisitor] when creating a synthetic type alias.
+   */
+  void shareParameters(List<ParameterElement> parameters) {
+    this._parameters = parameters;
+  }
+
+  /**
+   * Set the type parameters defined for this type to the given [typeParameters]
+   * without becoming the parent of the parameters. This should only be used by
+   * the [TypeResolverVisitor] when creating a synthetic type alias.
+   */
+  void shareTypeParameters(List<TypeParameterElement> typeParameters) {
+    this._typeParameters = typeParameters;
+  }
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    super.visitChildren(visitor);
+    safelyVisitChildren(_parameters, visitor);
+    safelyVisitChildren(_typeParameters, visitor);
+  }
+}
+
+/**
+ * The type of a function, method, constructor, getter, or setter.
+ */
+class FunctionTypeImpl extends TypeImpl implements FunctionType {
+  /**
+   * A list containing the actual types of the type arguments.
+   */
+  List<DartType> typeArguments = DartType.EMPTY_LIST;
+
+  /**
+   * Initialize a newly created function type to be declared by the given
+   * [element].
+   */
+  FunctionTypeImpl.con1(ExecutableElement element) : super(element, null);
+
+  /**
+   * Initialize a newly created function type to be declared by the given
+   * [element].
+   */
+  FunctionTypeImpl.con2(FunctionTypeAliasElement element)
+      : super(element, element == null ? null : element.name);
+
+  /**
+   * Return the base parameter elements of this function element.
+   */
+  List<ParameterElement> get baseParameters {
+    Element element = this.element;
+    if (element is ExecutableElement) {
+      return element.parameters;
+    } else {
+      return (element as FunctionTypeAliasElement).parameters;
+    }
+  }
+
+  /**
+   * Return the return type defined by this function's element.
+   */
+  DartType get baseReturnType {
+    Element element = this.element;
+    if (element is ExecutableElement) {
+      return element.returnType;
+    } else {
+      return (element as FunctionTypeAliasElement).returnType;
+    }
+  }
+
+  @override
+  String get displayName {
+    String name = this.name;
+    if (name == null || name.length == 0) {
+      // Function types have an empty name when they are defined implicitly by
+      // either a closure or as part of a parameter declaration.
+      List<DartType> normalParameterTypes = this.normalParameterTypes;
+      List<DartType> optionalParameterTypes = this.optionalParameterTypes;
+      Map<String, DartType> namedParameterTypes = this.namedParameterTypes;
+      DartType returnType = this.returnType;
+      StringBuffer buffer = new StringBuffer();
+      buffer.write("(");
+      bool needsComma = false;
+      if (normalParameterTypes.length > 0) {
+        for (DartType type in normalParameterTypes) {
+          if (needsComma) {
+            buffer.write(", ");
+          } else {
+            needsComma = true;
+          }
+          buffer.write(type.displayName);
+        }
+      }
+      if (optionalParameterTypes.length > 0) {
+        if (needsComma) {
+          buffer.write(", ");
+          needsComma = false;
+        }
+        buffer.write("[");
+        for (DartType type in optionalParameterTypes) {
+          if (needsComma) {
+            buffer.write(", ");
+          } else {
+            needsComma = true;
+          }
+          buffer.write(type.displayName);
+        }
+        buffer.write("]");
+        needsComma = true;
+      }
+      if (namedParameterTypes.length > 0) {
+        if (needsComma) {
+          buffer.write(", ");
+          needsComma = false;
+        }
+        buffer.write("{");
+        namedParameterTypes.forEach((String name, DartType type) {
+          if (needsComma) {
+            buffer.write(", ");
+          } else {
+            needsComma = true;
+          }
+          buffer.write(name);
+          buffer.write(": ");
+          buffer.write(type.displayName);
+        });
+        buffer.write("}");
+        needsComma = true;
+      }
+      buffer.write(")");
+      buffer.write(Element.RIGHT_ARROW);
+      if (returnType == null) {
+        buffer.write("null");
+      } else {
+        buffer.write(returnType.displayName);
+      }
+      name = buffer.toString();
+    }
+    return name;
+  }
+
+  @override
+  int get hashCode => internalHashCode(<DartType>[]);
+
+  @override
+  Map<String, DartType> get namedParameterTypes {
+    LinkedHashMap<String, DartType> namedParameterTypes =
+        new LinkedHashMap<String, DartType>();
+    List<ParameterElement> parameters = baseParameters;
+    if (parameters.length == 0) {
+      return namedParameterTypes;
+    }
+    List<DartType> typeParameters =
+        TypeParameterTypeImpl.getTypes(this.typeParameters);
+    for (ParameterElement parameter in parameters) {
+      if (parameter.parameterKind == ParameterKind.NAMED) {
+        DartType type = parameter.type;
+        if (typeArguments.length != 0 &&
+            typeArguments.length == typeParameters.length) {
+          type = type.substitute2(typeArguments, typeParameters);
+        }
+        namedParameterTypes[parameter.name] = type;
+      }
+    }
+    return namedParameterTypes;
+  }
+
+  @override
+  List<DartType> get normalParameterTypes {
+    List<ParameterElement> parameters = baseParameters;
+    if (parameters.length == 0) {
+      return DartType.EMPTY_LIST;
+    }
+    List<DartType> typeParameters =
+        TypeParameterTypeImpl.getTypes(this.typeParameters);
+    List<DartType> types = new List<DartType>();
+    for (ParameterElement parameter in parameters) {
+      if (parameter.parameterKind == ParameterKind.REQUIRED) {
+        DartType type = parameter.type;
+        if (typeArguments.length != 0 &&
+            typeArguments.length == typeParameters.length) {
+          type = type.substitute2(typeArguments, typeParameters);
+        }
+        types.add(type);
+      }
+    }
+    return types;
+  }
+
+  @override
+  List<DartType> get optionalParameterTypes {
+    List<ParameterElement> parameters = baseParameters;
+    if (parameters.length == 0) {
+      return DartType.EMPTY_LIST;
+    }
+    List<DartType> typeParameters =
+        TypeParameterTypeImpl.getTypes(this.typeParameters);
+    List<DartType> types = new List<DartType>();
+    for (ParameterElement parameter in parameters) {
+      if (parameter.parameterKind == ParameterKind.POSITIONAL) {
+        DartType type = parameter.type;
+        if (typeArguments.length != 0 &&
+            typeArguments.length == typeParameters.length) {
+          type = type.substitute2(typeArguments, typeParameters);
+        }
+        types.add(type);
+      }
+    }
+    return types;
+  }
+
+  @override
+  List<ParameterElement> get parameters {
+    List<ParameterElement> baseParameters = this.baseParameters;
+    // no parameters, quick return
+    int parameterCount = baseParameters.length;
+    if (parameterCount == 0) {
+      return baseParameters;
+    }
+    // create specialized parameters
+    List<ParameterElement> specializedParameters =
+        new List<ParameterElement>(parameterCount);
+    for (int i = 0; i < parameterCount; i++) {
+      specializedParameters[i] = ParameterMember.from(baseParameters[i], this);
+    }
+    return specializedParameters;
+  }
+
+  @override
+  DartType get returnType {
+    DartType baseReturnType = this.baseReturnType;
+    if (baseReturnType == null) {
+      // TODO(brianwilkerson) This is a patch. The return type should never be
+      // null and we need to understand why it is and fix it.
+      return DynamicTypeImpl.instance;
+    }
+    // If there are no arguments to substitute, or if the arguments size doesn't
+    // match the parameter size, return the base return type.
+    if (typeArguments.length == 0 ||
+        typeArguments.length != typeParameters.length) {
+      return baseReturnType;
+    }
+    return baseReturnType.substitute2(
+        typeArguments, TypeParameterTypeImpl.getTypes(typeParameters));
+  }
+
+  @override
+  List<TypeParameterElement> get typeParameters {
+    Element element = this.element;
+    if (element is FunctionTypeAliasElement) {
+      return element.typeParameters;
+    }
+    ClassElement definingClass =
+        element.getAncestor((element) => element is ClassElement);
+    if (definingClass != null) {
+      return definingClass.typeParameters;
+    }
+    return TypeParameterElement.EMPTY_LIST;
+  }
+
+  @override
+  bool operator ==(Object object) =>
+      internalEquals(object, new HashSet<ElementPair>());
+
+  @override
+  void appendTo(StringBuffer buffer, Set<DartType> visitedTypes) {
+    if (!visitedTypes.add(this)) {
+      buffer.write(name == null ? '...' : name);
+      return;
+    }
+    List<DartType> normalParameterTypes = this.normalParameterTypes;
+    List<DartType> optionalParameterTypes = this.optionalParameterTypes;
+    Map<String, DartType> namedParameterTypes = this.namedParameterTypes;
+    DartType returnType = this.returnType;
+    buffer.write("(");
+    bool needsComma = false;
+    if (normalParameterTypes.length > 0) {
+      for (DartType type in normalParameterTypes) {
+        if (needsComma) {
+          buffer.write(", ");
+        } else {
+          needsComma = true;
+        }
+        (type as TypeImpl).appendTo(buffer, visitedTypes);
+      }
+    }
+    if (optionalParameterTypes.length > 0) {
+      if (needsComma) {
+        buffer.write(", ");
+        needsComma = false;
+      }
+      buffer.write("[");
+      for (DartType type in optionalParameterTypes) {
+        if (needsComma) {
+          buffer.write(", ");
+        } else {
+          needsComma = true;
+        }
+        (type as TypeImpl).appendTo(buffer, visitedTypes);
+      }
+      buffer.write("]");
+      needsComma = true;
+    }
+    if (namedParameterTypes.length > 0) {
+      if (needsComma) {
+        buffer.write(", ");
+        needsComma = false;
+      }
+      buffer.write("{");
+      namedParameterTypes.forEach((String name, DartType type) {
+        if (needsComma) {
+          buffer.write(", ");
+        } else {
+          needsComma = true;
+        }
+        buffer.write(name);
+        buffer.write(": ");
+        (type as TypeImpl).appendTo(buffer, visitedTypes);
+      });
+      buffer.write("}");
+      needsComma = true;
+    }
+    buffer.write(")");
+    buffer.write(Element.RIGHT_ARROW);
+    if (returnType == null) {
+      buffer.write("null");
+    } else {
+      (returnType as TypeImpl).appendTo(buffer, visitedTypes);
+    }
+  }
+
+  @override
+  bool internalEquals(Object object, Set<ElementPair> visitedElementPairs) {
+    if (object is! FunctionTypeImpl) {
+      return false;
+    }
+    FunctionTypeImpl otherType = object as FunctionTypeImpl;
+    // If the visitedTypePairs already has the pair (this, type),
+    // use the elements to determine equality
+    ElementPair elementPair = new ElementPair(element, otherType.element);
+    if (!visitedElementPairs.add(elementPair)) {
+      return elementPair.firstElt == elementPair.secondElt;
+    }
+    // Compute the result
+    bool result = TypeImpl.equalArrays(normalParameterTypes,
+            otherType.normalParameterTypes, visitedElementPairs) &&
+        TypeImpl.equalArrays(optionalParameterTypes,
+            otherType.optionalParameterTypes, visitedElementPairs) &&
+        _equals(namedParameterTypes, otherType.namedParameterTypes,
+            visitedElementPairs) &&
+        (returnType as TypeImpl).internalEquals(
+            otherType.returnType, visitedElementPairs);
+    // Remove the pair from our visited pairs list
+    visitedElementPairs.remove(elementPair);
+    // Return the result
+    return result;
+  }
+
+  @override
+  int internalHashCode(List<DartType> visitedTypes) {
+    if (element == null) {
+      return 0;
+    } else if (visitedTypes.contains(this)) {
+      return 3;
+    }
+    visitedTypes.add(this);
+    // Reference the arrays of parameters
+    List<DartType> normalParameterTypes = this.normalParameterTypes;
+    List<DartType> optionalParameterTypes = this.optionalParameterTypes;
+    Iterable<DartType> namedParameterTypes = this.namedParameterTypes.values;
+    // Generate the hashCode
+    int code = (returnType as TypeImpl).internalHashCode(visitedTypes);
+    for (int i = 0; i < normalParameterTypes.length; i++) {
+      code = (code << 1) +
+          (normalParameterTypes[i] as TypeImpl).internalHashCode(visitedTypes);
+    }
+    for (int i = 0; i < optionalParameterTypes.length; i++) {
+      code = (code << 1) +
+          (optionalParameterTypes[i] as TypeImpl)
+              .internalHashCode(visitedTypes);
+    }
+    for (DartType type in namedParameterTypes) {
+      code = (code << 1) + (type as TypeImpl).internalHashCode(visitedTypes);
+    }
+    return code;
+  }
+
+  @override
+  bool isAssignableTo(DartType type,
+      [Set<Element> thisExpansions, Set<Element> typeExpansions]) {
+    // A function type T may be assigned to a function type S, written T <=> S,
+    // iff T <: S.
+    return isSubtypeOf(type, thisExpansions, typeExpansions);
+  }
+
+  @override
+  bool isMoreSpecificThan(DartType type, [Set<Element> thisExpansions,
+      Set<Element> typeExpansions, bool withDynamic = false,
+      Set<Element> visitedElements]) {
+    // Note: visitedElements is only used for breaking recursion in the type
+    // hierarchy; we don't use it when recursing into the function type.
+
+    // trivial base cases
+    if (type == null) {
+      return false;
+    } else if (identical(this, type) ||
+        type.isDynamic ||
+        type.isDartCoreFunction ||
+        type.isObject) {
+      return true;
+    } else if (type is! FunctionType) {
+      return false;
+    } else if (this == type) {
+      return true;
+    }
+    FunctionType t = this;
+    FunctionType s = type as FunctionType;
+    if (thisExpansions == null) {
+      thisExpansions = new HashSet<Element>();
+    } else if (thisExpansions.contains(this.element)) {
+      // [this] contains a reference to itself, which is illegal (and is
+      // checked elsewhere).  To avoid cascading errors, consider T to be a
+      // subtype of S.
+      return true;
+    }
+    if (typeExpansions == null) {
+      typeExpansions = new HashSet<Element>();
+    } else if (typeExpansions.contains(type.element)) {
+      // [type] contains a reference to itself, which is illegal (and is
+      // checked elsewhere).  To avoid cascading errors, consider T to be a
+      // subtype of S.
+      return true;
+    }
+    thisExpansions.add(this.element);
+    typeExpansions.add(type.element);
+    try {
+      List<DartType> tTypes = t.normalParameterTypes;
+      List<DartType> tOpTypes = t.optionalParameterTypes;
+      List<DartType> sTypes = s.normalParameterTypes;
+      List<DartType> sOpTypes = s.optionalParameterTypes;
+      // If one function has positional and the other has named parameters,
+      // return false.
+      if ((sOpTypes.length > 0 && t.namedParameterTypes.length > 0) ||
+          (tOpTypes.length > 0 && s.namedParameterTypes.length > 0)) {
+        return false;
+      }
+      // named parameters case
+      if (t.namedParameterTypes.length > 0) {
+        // check that the number of required parameters are equal, and check that
+        // every t_i is more specific than every s_i
+        if (t.normalParameterTypes.length != s.normalParameterTypes.length) {
+          return false;
+        } else if (t.normalParameterTypes.length > 0) {
+          for (int i = 0; i < tTypes.length; i++) {
+            if (!(tTypes[i] as TypeImpl).isMoreSpecificThan(
+                sTypes[i], thisExpansions, typeExpansions, withDynamic)) {
+              return false;
+            }
+          }
+        }
+        Map<String, DartType> namedTypesT = t.namedParameterTypes;
+        Map<String, DartType> namedTypesS = s.namedParameterTypes;
+        // if k >= m is false, return false: the passed function type has more
+        // named parameter types than this
+        if (namedTypesT.length < namedTypesS.length) {
+          return false;
+        }
+        // Loop through each element in S verifying that T has a matching
+        // parameter name and that the corresponding type is more specific then
+        // the type in S.
+        for (String keyS in namedTypesS.keys) {
+          DartType typeT = namedTypesT[keyS];
+          if (typeT == null) {
+            return false;
+          }
+          if (!(typeT as TypeImpl).isMoreSpecificThan(
+              namedTypesS[keyS], thisExpansions, typeExpansions, withDynamic)) {
+            return false;
+          }
+        }
+      } else if (s.namedParameterTypes.length > 0) {
+        return false;
+      } else {
+        // positional parameter case
+        int tArgLength = tTypes.length + tOpTypes.length;
+        int sArgLength = sTypes.length + sOpTypes.length;
+        // Check that the total number of parameters in t is greater than or equal
+        // to the number of parameters in s and that the number of required
+        // parameters in s is greater than or equal to the number of required
+        // parameters in t.
+        if (tArgLength < sArgLength || sTypes.length < tTypes.length) {
+          return false;
+        }
+        if (tOpTypes.length == 0 && sOpTypes.length == 0) {
+          // No positional arguments, don't copy contents to new array
+          for (int i = 0; i < sTypes.length; i++) {
+            if (!(tTypes[i] as TypeImpl).isMoreSpecificThan(
+                sTypes[i], thisExpansions, typeExpansions, withDynamic)) {
+              return false;
+            }
+          }
+        } else {
+          // Else, we do have positional parameters, copy required and positional
+          // parameter types into arrays to do the compare (for loop below).
+          List<DartType> tAllTypes = new List<DartType>(sArgLength);
+          for (int i = 0; i < tTypes.length; i++) {
+            tAllTypes[i] = tTypes[i];
+          }
+          for (int i = tTypes.length, j = 0; i < sArgLength; i++, j++) {
+            tAllTypes[i] = tOpTypes[j];
+          }
+          List<DartType> sAllTypes = new List<DartType>(sArgLength);
+          for (int i = 0; i < sTypes.length; i++) {
+            sAllTypes[i] = sTypes[i];
+          }
+          for (int i = sTypes.length, j = 0; i < sArgLength; i++, j++) {
+            sAllTypes[i] = sOpTypes[j];
+          }
+          for (int i = 0; i < sAllTypes.length; i++) {
+            if (!(tAllTypes[i] as TypeImpl).isMoreSpecificThan(
+                sAllTypes[i], thisExpansions, typeExpansions, withDynamic)) {
+              return false;
+            }
+          }
+        }
+      }
+      DartType tRetType = t.returnType;
+      DartType sRetType = s.returnType;
+      return sRetType.isVoid ||
+          (tRetType as TypeImpl).isMoreSpecificThan(
+              sRetType, thisExpansions, typeExpansions, withDynamic);
+    } finally {
+      thisExpansions.remove(this.element);
+      typeExpansions.remove(type.element);
+    }
+  }
+
+  @override
+  bool isSubtypeOf(DartType type,
+      [Set<Element> thisExpansions, Set<Element> typeExpansions]) {
+    // trivial base cases
+    if (type == null) {
+      return false;
+    } else if (identical(this, type) ||
+        type.isDynamic ||
+        type.isDartCoreFunction ||
+        type.isObject) {
+      return true;
+    } else if (type is! FunctionType) {
+      return false;
+    } else if (this == type) {
+      return true;
+    }
+    FunctionType t = this;
+    FunctionType s = type as FunctionType;
+    if (thisExpansions == null) {
+      thisExpansions = new HashSet<Element>();
+    } else if (thisExpansions.contains(this.element)) {
+      // [this] contains a reference to itself, which is illegal (and is
+      // checked elsewhere).  To avoid cascading errors, consider T to be a
+      // subtype of S.
+      return true;
+    }
+    if (typeExpansions == null) {
+      typeExpansions = new HashSet<Element>();
+    } else if (typeExpansions.contains(type.element)) {
+      // [type] contains a reference to itself, which is illegal (and is
+      // checked elsewhere).  To avoid cascading errors, consider T to be a
+      // subtype of S.
+      return true;
+    }
+    thisExpansions.add(this.element);
+    typeExpansions.add(type.element);
+    try {
+      List<DartType> tTypes = t.normalParameterTypes;
+      List<DartType> tOpTypes = t.optionalParameterTypes;
+      List<DartType> sTypes = s.normalParameterTypes;
+      List<DartType> sOpTypes = s.optionalParameterTypes;
+      // If one function has positional and the other has named parameters,
+      // return false.
+      if ((sOpTypes.length > 0 && t.namedParameterTypes.length > 0) ||
+          (tOpTypes.length > 0 && s.namedParameterTypes.length > 0)) {
+        return false;
+      }
+      // named parameters case
+      if (t.namedParameterTypes.length > 0) {
+        // check that the number of required parameters are equal,
+        // and check that every t_i is assignable to every s_i
+        if (t.normalParameterTypes.length != s.normalParameterTypes.length) {
+          return false;
+        } else if (t.normalParameterTypes.length > 0) {
+          for (int i = 0; i < tTypes.length; i++) {
+            if (!(tTypes[i] as TypeImpl).isAssignableTo(
+                sTypes[i], thisExpansions, typeExpansions)) {
+              return false;
+            }
+          }
+        }
+        Map<String, DartType> namedTypesT = t.namedParameterTypes;
+        Map<String, DartType> namedTypesS = s.namedParameterTypes;
+        // if k >= m is false, return false: the passed function type has more
+        // named parameter types than this
+        if (namedTypesT.length < namedTypesS.length) {
+          return false;
+        }
+        // Loop through each element in S verifying that T has a matching
+        // parameter name and that the corresponding type is assignable to the
+        // type in S.
+        for (String keyS in namedTypesS.keys) {
+          DartType typeT = namedTypesT[keyS];
+          if (typeT == null) {
+            return false;
+          }
+          if (!(typeT as TypeImpl).isAssignableTo(
+              namedTypesS[keyS], thisExpansions, typeExpansions)) {
+            return false;
+          }
+        }
+      } else if (s.namedParameterTypes.length > 0) {
+        return false;
+      } else {
+        // positional parameter case
+        int tArgLength = tTypes.length + tOpTypes.length;
+        int sArgLength = sTypes.length + sOpTypes.length;
+        // Check that the total number of parameters in t is greater than or
+        // equal to the number of parameters in s and that the number of
+        // required parameters in s is greater than or equal to the number of
+        // required parameters in t.
+        if (tArgLength < sArgLength || sTypes.length < tTypes.length) {
+          return false;
+        }
+        if (tOpTypes.length == 0 && sOpTypes.length == 0) {
+          // No positional arguments, don't copy contents to new array
+          for (int i = 0; i < sTypes.length; i++) {
+            if (!(tTypes[i] as TypeImpl).isAssignableTo(
+                sTypes[i], thisExpansions, typeExpansions)) {
+              return false;
+            }
+          }
+        } else {
+          // Else, we do have positional parameters, copy required and
+          // positional parameter types into arrays to do the compare (for loop
+          // below).
+          List<DartType> tAllTypes = new List<DartType>(sArgLength);
+          for (int i = 0; i < tTypes.length; i++) {
+            tAllTypes[i] = tTypes[i];
+          }
+          for (int i = tTypes.length, j = 0; i < sArgLength; i++, j++) {
+            tAllTypes[i] = tOpTypes[j];
+          }
+          List<DartType> sAllTypes = new List<DartType>(sArgLength);
+          for (int i = 0; i < sTypes.length; i++) {
+            sAllTypes[i] = sTypes[i];
+          }
+          for (int i = sTypes.length, j = 0; i < sArgLength; i++, j++) {
+            sAllTypes[i] = sOpTypes[j];
+          }
+          for (int i = 0; i < sAllTypes.length; i++) {
+            if (!(tAllTypes[i] as TypeImpl).isAssignableTo(
+                sAllTypes[i], thisExpansions, typeExpansions)) {
+              return false;
+            }
+          }
+        }
+      }
+      DartType tRetType = t.returnType;
+      DartType sRetType = s.returnType;
+      return sRetType.isVoid ||
+          (tRetType as TypeImpl).isAssignableTo(
+              sRetType, thisExpansions, typeExpansions);
+    } finally {
+      thisExpansions.remove(this.element);
+      typeExpansions.remove(type.element);
+    }
+  }
+
+  @override
+  FunctionTypeImpl substitute2(
+      List<DartType> argumentTypes, List<DartType> parameterTypes) {
+    if (argumentTypes.length != parameterTypes.length) {
+      throw new IllegalArgumentException(
+          "argumentTypes.length (${argumentTypes.length}) != parameterTypes.length (${parameterTypes.length})");
+    }
+    if (argumentTypes.length == 0) {
+      return this;
+    }
+    Element element = this.element;
+    FunctionTypeImpl newType = (element is ExecutableElement)
+        ? new FunctionTypeImpl.con1(element)
+        : new FunctionTypeImpl.con2(element as FunctionTypeAliasElement);
+    newType.typeArguments =
+        TypeImpl.substitute(typeArguments, argumentTypes, parameterTypes);
+    return newType;
+  }
+
+  @override
+  FunctionTypeImpl substitute3(List<DartType> argumentTypes) =>
+      substitute2(argumentTypes, typeArguments);
+
+  /**
+   * Return `true` if all of the name/type pairs in the first map ([firstTypes])
+   * are equal to the corresponding name/type pairs in the second map
+   * ([secondTypes]). The maps are expected to iterate over their entries in the
+   * same order in which those entries were added to the map. The set of
+   * [visitedElementPairs] is used to prevent infinite recursion in the case of
+   * cyclic type structures.
+   */
+  static bool _equals(Map<String, DartType> firstTypes,
+      Map<String, DartType> secondTypes, Set<ElementPair> visitedElementPairs) {
+    if (secondTypes.length != firstTypes.length) {
+      return false;
+    }
+    Iterator<String> firstKeys = firstTypes.keys.iterator;
+    Iterator<String> secondKeys = secondTypes.keys.iterator;
+    while (firstKeys.moveNext() && secondKeys.moveNext()) {
+      String firstKey = firstKeys.current;
+      String secondKey = secondKeys.current;
+      TypeImpl firstType = firstTypes[firstKey];
+      TypeImpl secondType = secondTypes[secondKey];
+      if (firstKey != secondKey ||
+          !firstType.internalEquals(secondType, visitedElementPairs)) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
+
+/**
+ * An element visitor that will recursively visit all of the elements in an
+ * element model (like instances of the class [RecursiveElementVisitor]). In
+ * addition, when an element of a specific type is visited not only will the
+ * visit method for that specific type of element be invoked, but additional
+ * methods for the supertypes of that element will also be invoked. For example,
+ * using an instance of this class to visit a [MethodElement] will cause the
+ * method [visitMethodElement] to be invoked but will also cause the methods
+ * [visitExecutableElement] and [visitElement] to be subsequently invoked. This
+ * allows visitors to be written that visit all executable elements without
+ * needing to override the visit method for each of the specific subclasses of
+ * [ExecutableElement].
+ *
+ * Note, however, that unlike many visitors, element visitors visit objects
+ * based on the interfaces implemented by those elements. Because interfaces
+ * form a graph structure rather than a tree structure the way classes do, and
+ * because it is generally undesirable for an object to be visited more than
+ * once, this class flattens the interface graph into a pseudo-tree. In
+ * particular, this class treats elements as if the element types were
+ * structured in the following way:
+ *
+ * <pre>
+ * Element
+ *   ClassElement
+ *   CompilationUnitElement
+ *   ExecutableElement
+ *       ConstructorElement
+ *       LocalElement
+ *           FunctionElement
+ *       MethodElement
+ *       PropertyAccessorElement
+ *   ExportElement
+ *   HtmlElement
+ *   ImportElement
+ *   LabelElement
+ *   LibraryElement
+ *   MultiplyDefinedElement
+ *   PrefixElement
+ *   TypeAliasElement
+ *   TypeParameterElement
+ *   UndefinedElement
+ *   VariableElement
+ *       PropertyInducingElement
+ *           FieldElement
+ *           TopLevelVariableElement
+ *       LocalElement
+ *           LocalVariableElement
+ *           ParameterElement
+ *               FieldFormalParameterElement
+ * </pre>
+ *
+ * Subclasses that override a visit method must either invoke the overridden
+ * visit method or explicitly invoke the more general visit method. Failure to
+ * do so will cause the visit methods for superclasses of the element to not be
+ * invoked and will cause the children of the visited node to not be visited.
+ */
+class GeneralizingElementVisitor<R> implements ElementVisitor<R> {
+  @override
+  R visitClassElement(ClassElement element) => visitElement(element);
+
+  @override
+  R visitCompilationUnitElement(CompilationUnitElement element) =>
+      visitElement(element);
+
+  @override
+  R visitConstructorElement(ConstructorElement element) =>
+      visitExecutableElement(element);
+
+  R visitElement(Element element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitEmbeddedHtmlScriptElement(EmbeddedHtmlScriptElement element) =>
+      visitHtmlScriptElement(element);
+
+  R visitExecutableElement(ExecutableElement element) => visitElement(element);
+
+  @override
+  R visitExportElement(ExportElement element) => visitElement(element);
+
+  @override
+  R visitExternalHtmlScriptElement(ExternalHtmlScriptElement element) =>
+      visitHtmlScriptElement(element);
+
+  @override
+  R visitFieldElement(FieldElement element) =>
+      visitPropertyInducingElement(element);
+
+  @override
+  R visitFieldFormalParameterElement(FieldFormalParameterElement element) =>
+      visitParameterElement(element);
+
+  @override
+  R visitFunctionElement(FunctionElement element) => visitLocalElement(element);
+
+  @override
+  R visitFunctionTypeAliasElement(FunctionTypeAliasElement element) =>
+      visitElement(element);
+
+  @override
+  R visitHtmlElement(HtmlElement element) => visitElement(element);
+
+  R visitHtmlScriptElement(HtmlScriptElement element) => visitElement(element);
+
+  @override
+  R visitImportElement(ImportElement element) => visitElement(element);
+
+  @override
+  R visitLabelElement(LabelElement element) => visitElement(element);
+
+  @override
+  R visitLibraryElement(LibraryElement element) => visitElement(element);
+
+  R visitLocalElement(LocalElement element) {
+    if (element is LocalVariableElement) {
+      return visitVariableElement(element);
+    } else if (element is ParameterElement) {
+      return visitVariableElement(element);
+    } else if (element is FunctionElement) {
+      return visitExecutableElement(element);
+    }
+    return null;
+  }
+
+  @override
+  R visitLocalVariableElement(LocalVariableElement element) =>
+      visitLocalElement(element);
+
+  @override
+  R visitMethodElement(MethodElement element) =>
+      visitExecutableElement(element);
+
+  @override
+  R visitMultiplyDefinedElement(MultiplyDefinedElement element) =>
+      visitElement(element);
+
+  @override
+  R visitParameterElement(ParameterElement element) =>
+      visitLocalElement(element);
+
+  @override
+  R visitPrefixElement(PrefixElement element) => visitElement(element);
+
+  @override
+  R visitPropertyAccessorElement(PropertyAccessorElement element) =>
+      visitExecutableElement(element);
+
+  R visitPropertyInducingElement(PropertyInducingElement element) =>
+      visitVariableElement(element);
+
+  @override
+  R visitTopLevelVariableElement(TopLevelVariableElement element) =>
+      visitPropertyInducingElement(element);
+
+  @override
+  R visitTypeParameterElement(TypeParameterElement element) =>
+      visitElement(element);
+
+  R visitVariableElement(VariableElement element) => visitElement(element);
+}
+
+/**
+ * A combinator that causes some of the names in a namespace to be hidden when
+ * being imported.
+ */
+abstract class HideElementCombinator implements NamespaceCombinator {
+  /**
+   * Return a list containing the names that are not to be made visible in the
+   * importing library even if they are defined in the imported library.
+   */
+  List<String> get hiddenNames;
+}
+
+/**
+ * A concrete implementation of a [HideElementCombinator].
+ */
+class HideElementCombinatorImpl implements HideElementCombinator {
+  /**
+   * The names that are not to be made visible in the importing library even if
+   * they are defined in the imported library.
+   */
+  List<String> hiddenNames = StringUtilities.EMPTY_ARRAY;
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    buffer.write("show ");
+    int count = hiddenNames.length;
+    for (int i = 0; i < count; i++) {
+      if (i > 0) {
+        buffer.write(", ");
+      }
+      buffer.write(hiddenNames[i]);
+    }
+    return buffer.toString();
+  }
+}
+
+/**
+ * An HTML file.
+ */
+abstract class HtmlElement implements Element {
+  /**
+   * An empty list of HTML file elements.
+   */
+  static const List<HtmlElement> EMPTY_LIST = const <HtmlElement>[];
+
+  /**
+   * Return a list containing all of the script elements contained in the HTML
+   * file. This includes scripts with libraries that are defined by the content
+   * of a script tag as well as libraries that are referenced in the `source`
+   * attribute of a script tag.
+   */
+  List<HtmlScriptElement> get scripts;
+}
+
+/**
+ * A concrete implementation of an [HtmlElement].
+ */
+class HtmlElementImpl extends ElementImpl implements HtmlElement {
+  /**
+   * An empty list of HTML file elements.
+   */
+  @deprecated // Use HtmlElement.EMPTY_LIST
+  static const List<HtmlElement> EMPTY_ARRAY = const <HtmlElement>[];
+
+  /**
+   * The analysis context in which this library is defined.
+   */
+  final AnalysisContext context;
+
+  /**
+   * The scripts contained in or referenced from script tags in the HTML file.
+   */
+  List<HtmlScriptElement> _scripts = HtmlScriptElement.EMPTY_LIST;
+
+  /**
+   * The source that corresponds to this HTML file.
+   */
+  Source source;
+
+  /**
+   * Initialize a newly created HTML element in the given [context] to have the
+   * given [name].
+   */
+  HtmlElementImpl(this.context, String name) : super(name, -1);
+
+  @override
+  int get hashCode => source.hashCode;
+
+  @override
+  String get identifier => source.encoding;
+
+  @override
+  ElementKind get kind => ElementKind.HTML;
+
+  @override
+  List<HtmlScriptElement> get scripts => _scripts;
+
+  /**
+   * Set the scripts contained in the HTML file to the given [scripts].
+   */
+  void set scripts(List<HtmlScriptElement> scripts) {
+    if (scripts.length == 0) {
+      this._scripts = HtmlScriptElement.EMPTY_LIST;
+      return;
+    }
+    for (HtmlScriptElement script in scripts) {
+      (script as HtmlScriptElementImpl).enclosingElement = this;
+    }
+    this._scripts = scripts;
+  }
+
+  @override
+  bool operator ==(Object object) {
+    if (identical(object, this)) {
+      return true;
+    }
+    return object is HtmlElementImpl && source == object.source;
+  }
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitHtmlElement(this);
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    if (source == null) {
+      buffer.write("{HTML file}");
+    } else {
+      buffer.write(source.fullName);
+    }
+  }
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    super.visitChildren(visitor);
+    safelyVisitChildren(_scripts, visitor);
+  }
+}
+
+/**
+ * A script tag in an HTML file.
+ *
+ * See [EmbeddedHtmlScriptElement], and [ExternalHtmlScriptElement].
+ */
+abstract class HtmlScriptElement implements Element {
+  /**
+   * An empty list of HTML script elements.
+   */
+  static const List<HtmlScriptElement> EMPTY_LIST = const <HtmlScriptElement>[];
+}
+
+/**
+ * A concrete implementation of an [HtmlScriptElement].
+ */
+abstract class HtmlScriptElementImpl extends ElementImpl
+    implements HtmlScriptElement {
+  /**
+   * An empty list of HTML script elements.
+   */
+  @deprecated // Use HtmlScriptElement.EMPTY_LIST
+  static const List<HtmlScriptElement> EMPTY_ARRAY =
+      const <HtmlScriptElement>[];
+
+  /**
+   * Initialize a newly created script element corresponding to the given
+   * [node].
+   */
+  HtmlScriptElementImpl(XmlTagNode node)
+      : super(node.tag, node.tagToken.offset);
+}
+
+/**
+ * A single import directive within a library.
+ */
+abstract class ImportElement implements Element, UriReferencedElement {
+  /**
+   * An empty list of import elements.
+   */
+  @deprecated // Use ImportElement.EMPTY_LIST
+  static const List<ImportElement> EMPTY_ARRAY = const <ImportElement>[];
+
+  /**
+   * An empty list of import elements.
+   */
+  static const List<ImportElement> EMPTY_LIST = const <ImportElement>[];
+
+  /**
+   * Return a list containing the combinators that were specified as part of the
+   * import directive in the order in which they were specified.
+   */
+  List<NamespaceCombinator> get combinators;
+
+  /**
+   * Return the library that is imported into this library by this import
+   * directive.
+   */
+  LibraryElement get importedLibrary;
+
+  /**
+   * Return `true` if this import is for a deferred library.
+   */
+  bool get isDeferred;
+
+  /**
+   * Return the prefix that was specified as part of the import directive, or
+   * `null` if there was no prefix specified.
+   */
+  PrefixElement get prefix;
+
+  /**
+   * Return the offset of the prefix of this import in the file that contains
+   * this import directive, or `-1` if this import is synthetic, does not have a
+   * prefix, or otherwise does not have an offset.
+   */
+  int get prefixOffset;
+}
+
+/**
+ * A concrete implementation of an [ImportElement].
+ */
+class ImportElementImpl extends UriReferencedElementImpl
+    implements ImportElement {
+  /**
+   * The offset of the prefix of this import in the file that contains the this
+   * import directive, or `-1` if this import is synthetic.
+   */
+  int prefixOffset = 0;
+
+  /**
+   * The library that is imported into this library by this import directive.
+   */
+  LibraryElement importedLibrary;
+
+  /**
+   * The combinators that were specified as part of the import directive in the
+   * order in which they were specified.
+   */
+  List<NamespaceCombinator> combinators = NamespaceCombinator.EMPTY_LIST;
+
+  /**
+   * The prefix that was specified as part of the import directive, or `null` if
+   * there was no prefix specified.
+   */
+  PrefixElement prefix;
+
+  /**
+   * Initialize a newly created import element at the given [offset].
+   * The offset may be `-1` if the import is synthetic.
+   */
+  ImportElementImpl(int offset) : super(null, offset);
+
+  /**
+   * Set whether this import is for a deferred library.
+   */
+  void set deferred(bool isDeferred) {
+    setModifier(Modifier.DEFERRED, isDeferred);
+  }
+
+  @override
+  String get identifier =>
+      "${(importedLibrary as LibraryElementImpl).identifier}@$nameOffset";
+
+  @override
+  bool get isDeferred => hasModifier(Modifier.DEFERRED);
+
+  @override
+  ElementKind get kind => ElementKind.IMPORT;
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitImportElement(this);
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    buffer.write("import ");
+    (importedLibrary as LibraryElementImpl).appendTo(buffer);
+  }
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    super.visitChildren(visitor);
+    safelyVisitChild(prefix, visitor);
+  }
+}
+
+/**
+ * The type introduced by either a class or an interface, or a reference to such
+ * a type.
+ */
+abstract class InterfaceType implements ParameterizedType {
+  /**
+   * An empty list of types.
+   */
+  @deprecated // Use InterfaceType.EMPTY_LIST
+  static const List<InterfaceType> EMPTY_ARRAY = const <InterfaceType>[];
+
+  /**
+   * An empty list of types.
+   */
+  static const List<InterfaceType> EMPTY_LIST = const <InterfaceType>[];
+
+  /**
+   * Return a list containing all of the accessors (getters and setters)
+   * declared in this type.
+   */
+  List<PropertyAccessorElement> get accessors;
+
+  @override
+  ClassElement get element;
+
+  /**
+   * Return a list containing all of the interfaces that are implemented by this
+   * interface. Note that this is <b>not</b>, in general, equivalent to getting
+   * the interfaces from this type's element because the types returned by this
+   * method will have had their type parameters replaced.
+   */
+  List<InterfaceType> get interfaces;
+
+  /**
+   * Return a list containing all of the methods declared in this type.
+   */
+  List<MethodElement> get methods;
+
+  /**
+   * Return a list containing all of the mixins that are applied to the class
+   * being extended in order to derive the superclass of this class. Note that
+   * this is <b>not</b>, in general, equivalent to getting the mixins from this
+   * type's element because the types returned by this method will have had
+   * their type parameters replaced.
+   */
+  List<InterfaceType> get mixins;
+
+  /**
+   * Return the type representing the superclass of this type, or null if this
+   * type represents the class 'Object'. Note that this is <b>not</b>, in
+   * general, equivalent to getting the superclass from this type's element
+   * because the type returned by this method will have had it's type parameters
+   * replaced.
+   */
+  InterfaceType get superclass;
+
+  /**
+   * Return the element representing the getter with the given [name] that is
+   * declared in this class, or `null` if this class does not declare a getter
+   * with the given name.
+   */
+  PropertyAccessorElement getGetter(String name);
+
+  /**
+   * Return the least upper bound of this type and the given [type], or `null`
+   * if there is no least upper bound.
+   *
+   * Given two interfaces <i>I</i> and <i>J</i>, let <i>S<sub>I</sub></i> be the
+   * set of superinterfaces of <i>I<i>, let <i>S<sub>J</sub></i> be the set of
+   * superinterfaces of <i>J</i> and let <i>S = (I &cup; S<sub>I</sub>) &cap;
+   * (J &cup; S<sub>J</sub>)</i>. Furthermore, we define <i>S<sub>n</sub> =
+   * {T | T &isin; S &and; depth(T) = n}</i> for any finite <i>n</i>, where
+   * <i>depth(T)</i> is the number of steps in the longest inheritance path from
+   * <i>T</i> to <i>Object</i>. Let <i>q</i> be the largest number such that
+   * <i>S<sub>q</sub></i> has cardinality one. The least upper bound of <i>I</i>
+   * and <i>J</i> is the sole element of <i>S<sub>q</sub></i>.
+   */
+  @override
+  DartType getLeastUpperBound(DartType type);
+
+  /**
+   * Return the element representing the method with the given [name] that is
+   * declared in this class, or `null` if this class does not declare a method
+   * with the given name.
+   */
+  MethodElement getMethod(String name);
+
+  /**
+   * Return the element representing the setter with the given [name] that is
+   * declared in this class, or `null` if this class does not declare a setter
+   * with the given name.
+   */
+  PropertyAccessorElement getSetter(String name);
+
+  /**
+   * Return `true` if this type is a direct supertype of the given [type]. The
+   * implicit interface of class <i>I</i> is a direct supertype of the implicit
+   * interface of class <i>J</i> iff:
+   *
+   * * <i>I</i> is Object, and <i>J</i> has no extends clause.
+   * * <i>I</i> is listed in the extends clause of <i>J</i>.
+   * * <i>I</i> is listed in the implements clause of <i>J</i>.
+   * * <i>I</i> is listed in the with clause of <i>J</i>.
+   * * <i>J</i> is a mixin application of the mixin of <i>I</i>.
+   */
+  bool isDirectSupertypeOf(InterfaceType type);
+
+  /**
+   * Return `true` if this type is more specific than the given [type]. An
+   * interface type <i>T</i> is more specific than an interface type <i>S</i>,
+   * written <i>T &laquo; S</i>, if one of the following conditions is met:
+   *
+   * * Reflexivity: <i>T</i> is <i>S</i>.
+   * * <i>T</i> is bottom.
+   * * <i>S</i> is dynamic.
+   * * Direct supertype: <i>S</i> is a direct supertype of <i>T</i>.
+   * * <i>T</i> is a type parameter and <i>S</i> is the upper bound of <i>T</i>.
+   * * Covariance: <i>T</i> is of the form <i>I&lt;T<sub>1</sub>, &hellip;,
+   *   T<sub>n</sub>&gt;</i> and S</i> is of the form <i>I&lt;S<sub>1</sub>,
+   *   &hellip;, S<sub>n</sub>&gt;</i> and <i>T<sub>i</sub> &laquo;
+   *   S<sub>i</sub></i>, <i>1 <= i <= n</i>.
+   * * Transitivity: <i>T &laquo; U</i> and <i>U &laquo; S</i>.
+   */
+  @override
+  bool isMoreSpecificThan(DartType type);
+
+  /**
+   * Return `true` if this type is a subtype of the given [type]. An interface
+   * type <i>T</i> is a subtype of an interface type <i>S</i>, written <i>T</i>
+   * <: <i>S</i>, iff <i>[bottom/dynamic]T</i> &laquo; <i>S</i> (<i>T</i> is
+   * more specific than <i>S</i>). If an interface type <i>I</i> includes a
+   * method named <i>call()</i>, and the type of <i>call()</i> is the function
+   * type <i>F</i>, then <i>I</i> is considered to be a subtype of <i>F</i>.
+   */
+  @override
+  bool isSubtypeOf(DartType type);
+
+  /**
+   * Return the element representing the constructor that results from looking
+   * up the constructor with the given [name] in this class with respect to the
+   * given [library], or `null` if the look up fails. The behavior of this
+   * method is defined by the Dart Language Specification in section 12.11.1:
+   * <blockquote>
+   * If <i>e</i> is of the form <b>new</b> <i>T.id()</i> then let <i>q<i> be the
+   * constructor <i>T.id</i>, otherwise let <i>q<i> be the constructor <i>T<i>.
+   * Otherwise, if <i>q</i> is not defined or not accessible, a
+   * NoSuchMethodException is thrown.
+   * </blockquote>
+   */
+  ConstructorElement lookUpConstructor(String name, LibraryElement library);
+
+  /**
+   * Return the element representing the getter that results from looking up the
+   * getter with the given [name] in this class with respect to the given
+   * [library], or `null` if the look up fails. The behavior of this method is
+   * defined by the Dart Language Specification in section 12.15.1:
+   * <blockquote>
+   * The result of looking up getter (respectively setter) <i>m</i> in class
+   * <i>C</i> with respect to library <i>L</i> is:
+   * * If <i>C</i> declares an instance getter (respectively setter) named
+   *   <i>m</i> that is accessible to <i>L</i>, then that getter (respectively
+   *   setter) is the result of the lookup. Otherwise, if <i>C</i> has a
+   *   superclass <i>S</i>, then the result of the lookup is the result of
+   *   looking up getter (respectively setter) <i>m</i> in <i>S</i> with respect
+   *   to <i>L</i>. Otherwise, we say that the lookup has failed.
+   * </blockquote>
+   */
+  PropertyAccessorElement lookUpGetter(String name, LibraryElement library);
+
+  /**
+   * Return the element representing the getter that results from looking up the
+   * getter with the given [name] in the superclass of this class with respect
+   * to the given [library], or `null` if the look up fails. The behavior of
+   * this method is defined by the Dart Language Specification in section
+   * 12.15.1:
+   * <blockquote>
+   * The result of looking up getter (respectively setter) <i>m</i> in class
+   * <i>C</i> with respect to library <i>L</i> is:
+   * * If <i>C</i> declares an instance getter (respectively setter) named
+   *   <i>m</i> that is accessible to <i>L</i>, then that getter (respectively
+   *   setter) is the result of the lookup. Otherwise, if <i>C</i> has a
+   *   superclass <i>S</i>, then the result of the lookup is the result of
+   *   looking up getter (respectively setter) <i>m</i> in <i>S</i> with respect
+   *   to <i>L</i>. Otherwise, we say that the lookup has failed.
+   * </blockquote>
+   */
+  PropertyAccessorElement lookUpGetterInSuperclass(
+      String name, LibraryElement library);
+
+  /**
+   * Return the element representing the method that results from looking up the
+   * method with the given [name] in this class with respect to the given
+   * [library], or `null` if the look up fails. The behavior of this method is
+   * defined by the Dart Language Specification in section 12.15.1:
+   * <blockquote>
+   * The result of looking up method <i>m</i> in class <i>C</i> with respect to
+   * library <i>L</i> is:
+   * * If <i>C</i> declares an instance method named <i>m</i> that is accessible
+   *   to <i>L</i>, then that method is the result of the lookup. Otherwise, if
+   *   <i>C</i> has a superclass <i>S</i>, then the result of the lookup is the
+   *   result of looking up method <i>m</i> in <i>S</i> with respect to <i>L</i>
+   *   Otherwise, we say that the lookup has failed.
+   * </blockquote>
+   */
+  MethodElement lookUpMethod(String name, LibraryElement library);
+
+  /**
+   * Return the element representing the method that results from looking up the
+   * method with the given [name] in the superclass of this class with respect
+   * to the given [library], or `null` if the look up fails. The behavior of
+   * this method is defined by the Dart Language Specification in section
+   * 12.15.1:
+   * <blockquote>
+   * The result of looking up method <i>m</i> in class <i>C</i> with respect to
+   * library <i>L</i> is:
+   * * If <i>C</i> declares an instance method named <i>m</i> that is accessible
+   *   to <i>L</i>, then that method is the result of the lookup. Otherwise, if
+   * <i>C</i> has a superclass <i>S</i>, then the result of the lookup is the
+   * result of looking up method <i>m</i> in <i>S</i> with respect to <i>L</i>.
+   * Otherwise, we say that the lookup has failed.
+   * </blockquote>
+   */
+  MethodElement lookUpMethodInSuperclass(String name, LibraryElement library);
+
+  /**
+   * Return the element representing the setter that results from looking up the
+   * setter with the given [name] in this class with respect to the given
+   * [library], or `null` if the look up fails. The behavior of this method is
+   * defined by the Dart Language Specification in section 12.16:
+   * <blockquote>
+   * The result of looking up getter (respectively setter) <i>m</i> in class
+   * <i>C</i> with respect to library <i>L</i> is:
+   * * If <i>C</i> declares an instance getter (respectively setter) named
+   *   <i>m</i> that is accessible to <i>L</i>, then that getter (respectively
+   *   setter) is the result of the lookup. Otherwise, if <i>C</i> has a
+   *   superclass <i>S</i>, then the result of the lookup is the result of
+   *   looking up getter (respectively setter) <i>m</i> in <i>S</i> with respect
+   *   to <i>L</i>. Otherwise, we say that the lookup has failed.
+   * </blockquote>
+   */
+  PropertyAccessorElement lookUpSetter(String name, LibraryElement library);
+
+  /**
+   * Return the element representing the setter that results from looking up the
+   * setter with the given [name] in the superclass of this class with respect
+   * to the given [library], or `null` if the look up fails. The behavior of
+   * this method is defined by the Dart Language Specification in section 12.16:
+   * <blockquote>
+   * The result of looking up getter (respectively setter) <i>m</i> in class
+   * <i>C</i> with respect to library <i>L</i> is:
+   * * If <i>C</i> declares an instance getter (respectively setter) named
+   *   <i>m</i> that is accessible to <i>L</i>, then that getter (respectively
+   *   setter) is the result of the lookup. Otherwise, if <i>C</i> has a
+   *   superclass <i>S</i>, then the result of the lookup is the result of
+   *   looking up getter (respectively setter) <i>m</i> in <i>S</i> with respect
+   *   to <i>L</i>. Otherwise, we say that the lookup has failed.
+   * </blockquote>
+   */
+  PropertyAccessorElement lookUpSetterInSuperclass(
+      String name, LibraryElement library);
+
+  @override
+  InterfaceType substitute2(
+      List<DartType> argumentTypes, List<DartType> parameterTypes);
+
+  /**
+   * Return the type resulting from substituting the given arguments for this
+   * type's parameters. This is fully equivalent to `substitute2(argumentTypes,
+   * getTypeArguments())`.
+   */
+  InterfaceType substitute4(List<DartType> argumentTypes);
+
+  /**
+   * Returns a "smart" version of the "least upper bound" of the given types.
+   *
+   * If these types have the same element and differ only in terms of the type
+   * arguments, attempts to find a compatible set of type arguments.
+   *
+   * Otherwise, calls [DartType.getLeastUpperBound].
+   */
+  static InterfaceType getSmartLeastUpperBound(
+      InterfaceType first, InterfaceType second) {
+    if (first.element == second.element) {
+      return _leastUpperBound(first, second);
+    }
+    return first.getLeastUpperBound(second);
+  }
+
+  /**
+   * Return the "least upper bound" of the given types under the assumption that
+   * the types have the same element and differ only in terms of the type
+   * arguments.
+   *
+   * The resulting type is composed by comparing the corresponding type
+   * arguments, keeping those that are the same, and using 'dynamic' for those
+   * that are different.
+   */
+  static InterfaceType _leastUpperBound(
+      InterfaceType firstType, InterfaceType secondType) {
+    ClassElement firstElement = firstType.element;
+    ClassElement secondElement = secondType.element;
+    if (firstElement != secondElement) {
+      throw new IllegalArgumentException('The same elements expected, but '
+          '$firstElement and $secondElement are given.');
+    }
+    if (firstType == secondType) {
+      return firstType;
+    }
+    List<DartType> firstArguments = firstType.typeArguments;
+    List<DartType> secondArguments = secondType.typeArguments;
+    int argumentCount = firstArguments.length;
+    if (argumentCount == 0) {
+      return firstType;
+    }
+    List<DartType> lubArguments = new List<DartType>(argumentCount);
+    for (int i = 0; i < argumentCount; i++) {
+      //
+      // Ideally we would take the least upper bound of the two argument types,
+      // but this can cause an infinite recursion (such as when finding the
+      // least upper bound of String and num).
+      //
+      if (firstArguments[i] == secondArguments[i]) {
+        lubArguments[i] = firstArguments[i];
+      }
+      if (lubArguments[i] == null) {
+        lubArguments[i] = DynamicTypeImpl.instance;
+      }
+    }
+    InterfaceTypeImpl lub = new InterfaceTypeImpl.con1(firstElement);
+    lub.typeArguments = lubArguments;
+    return lub;
+  }
+}
+
+/**
+ * A concrete implementation of an [InterfaceType].
+ */
+class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
+  /**
+   * A list containing the actual types of the type arguments.
+   */
+  List<DartType> typeArguments = DartType.EMPTY_LIST;
+
+  /**
+   * Initialize a newly created type to be declared by the given [element].
+   */
+  InterfaceTypeImpl.con1(ClassElement element)
+      : super(element, element.displayName);
+
+  /**
+   * Initialize a newly created type to have the given [name]. This constructor
+   * should only be used in cases where there is no declaration of the type.
+   */
+  InterfaceTypeImpl.con2(String name) : super(null, name);
+
+  @override
+  List<PropertyAccessorElement> get accessors {
+    List<PropertyAccessorElement> accessors = element.accessors;
+    List<PropertyAccessorElement> members =
+        new List<PropertyAccessorElement>(accessors.length);
+    for (int i = 0; i < accessors.length; i++) {
+      members[i] = PropertyAccessorMember.from(accessors[i], this);
+    }
+    return members;
+  }
+
+  @override
+  String get displayName {
+    String name = this.name;
+    List<DartType> typeArguments = this.typeArguments;
+    bool allDynamic = true;
+    for (DartType type in typeArguments) {
+      if (type != null && !type.isDynamic) {
+        allDynamic = false;
+        break;
+      }
+    }
+    // If there is at least one non-dynamic type, then list them out
+    if (!allDynamic) {
+      StringBuffer buffer = new StringBuffer();
+      buffer.write(name);
+      buffer.write("<");
+      for (int i = 0; i < typeArguments.length; i++) {
+        if (i != 0) {
+          buffer.write(", ");
+        }
+        DartType typeArg = typeArguments[i];
+        buffer.write(typeArg.displayName);
+      }
+      buffer.write(">");
+      name = buffer.toString();
+    }
+    return name;
+  }
+
+  @override
+  ClassElement get element => super.element as ClassElement;
+
+  @override
+  int get hashCode {
+    ClassElement element = this.element;
+    if (element == null) {
+      return 0;
+    }
+    return element.hashCode;
+  }
+
+  @override
+  List<InterfaceType> get interfaces {
+    ClassElement classElement = element;
+    List<InterfaceType> interfaces = classElement.interfaces;
+    List<TypeParameterElement> typeParameters = classElement.typeParameters;
+    List<DartType> parameterTypes = classElement.type.typeArguments;
+    if (typeParameters.length == 0) {
+      return interfaces;
+    }
+    int count = interfaces.length;
+    List<InterfaceType> typedInterfaces = new List<InterfaceType>(count);
+    for (int i = 0; i < count; i++) {
+      typedInterfaces[i] =
+          interfaces[i].substitute2(typeArguments, parameterTypes);
+    }
+    return typedInterfaces;
+  }
+
+  @override
+  bool get isDartCoreFunction {
+    ClassElement element = this.element;
+    if (element == null) {
+      return false;
+    }
+    return element.name == "Function" && element.library.isDartCore;
+  }
+
+  @override
+  bool get isObject => element.supertype == null;
+
+  @override
+  List<MethodElement> get methods {
+    List<MethodElement> methods = element.methods;
+    List<MethodElement> members = new List<MethodElement>(methods.length);
+    for (int i = 0; i < methods.length; i++) {
+      members[i] = MethodMember.from(methods[i], this);
+    }
+    return members;
+  }
+
+  @override
+  List<InterfaceType> get mixins {
+    ClassElement classElement = element;
+    List<InterfaceType> mixins = classElement.mixins;
+    List<TypeParameterElement> typeParameters = classElement.typeParameters;
+    List<DartType> parameterTypes = classElement.type.typeArguments;
+    if (typeParameters.length == 0) {
+      return mixins;
+    }
+    int count = mixins.length;
+    List<InterfaceType> typedMixins = new List<InterfaceType>(count);
+    for (int i = 0; i < count; i++) {
+      typedMixins[i] = mixins[i].substitute2(typeArguments, parameterTypes);
+    }
+    return typedMixins;
+  }
+
+  @override
+  InterfaceType get superclass {
+    ClassElement classElement = element;
+    InterfaceType supertype = classElement.supertype;
+    if (supertype == null) {
+      return null;
+    }
+    List<DartType> typeParameters = classElement.type.typeArguments;
+    if (typeArguments.length == 0 ||
+        typeArguments.length != typeParameters.length) {
+      return supertype;
+    }
+    return supertype.substitute2(typeArguments, typeParameters);
+  }
+
+  @override
+  List<TypeParameterElement> get typeParameters => element.typeParameters;
+
+  @override
+  bool operator ==(Object object) {
+    if (identical(object, this)) {
+      return true;
+    }
+    return internalEquals(object, new HashSet<ElementPair>());
+  }
+
+  @override
+  void appendTo(StringBuffer buffer, Set<DartType> visitedTypes) {
+    if (!visitedTypes.add(this)) {
+      buffer.write(name == null ? '...' : name);
+      return;
+    }
+    buffer.write(name);
+    int argumentCount = typeArguments.length;
+    if (argumentCount > 0) {
+      buffer.write("<");
+      for (int i = 0; i < argumentCount; i++) {
+        if (i > 0) {
+          buffer.write(", ");
+        }
+        (typeArguments[i] as TypeImpl).appendTo(buffer, visitedTypes);
+      }
+      buffer.write(">");
+    }
+  }
+
+  @override
+  PropertyAccessorElement getGetter(String getterName) => PropertyAccessorMember
+      .from((element as ClassElementImpl).getGetter(getterName), this);
+
+  @override
+  DartType getLeastUpperBound(DartType type) {
+    // quick check for self
+    if (identical(type, this)) {
+      return this;
+    }
+    // dynamic
+    DartType dynamicType = DynamicTypeImpl.instance;
+    if (identical(this, dynamicType) || identical(type, dynamicType)) {
+      return dynamicType;
+    }
+    // TODO (jwren) opportunity here for a better, faster algorithm if this
+    // turns out to be a bottle-neck
+    if (type is! InterfaceType) {
+      return null;
+    }
+    // new names to match up with the spec
+    InterfaceType i = this;
+    InterfaceType j = type as InterfaceType;
+    // compute set of supertypes
+    Set<InterfaceType> si = computeSuperinterfaceSet(i);
+    Set<InterfaceType> sj = computeSuperinterfaceSet(j);
+    // union si with i and sj with j
+    si.add(i);
+    sj.add(j);
+    // compute intersection, reference as set 's'
+    List<InterfaceType> s = _intersection(si, sj);
+    // for each element in Set s, compute the largest inheritance path to Object
+    List<int> depths = new List<int>.filled(s.length, 0);
+    int maxDepth = 0;
+    for (int n = 0; n < s.length; n++) {
+      depths[n] = computeLongestInheritancePathToObject(s[n]);
+      if (depths[n] > maxDepth) {
+        maxDepth = depths[n];
+      }
+    }
+    // ensure that the currently computed maxDepth is unique,
+    // otherwise, decrement and test for uniqueness again
+    for (; maxDepth >= 0; maxDepth--) {
+      int indexOfLeastUpperBound = -1;
+      int numberOfTypesAtMaxDepth = 0;
+      for (int m = 0; m < depths.length; m++) {
+        if (depths[m] == maxDepth) {
+          numberOfTypesAtMaxDepth++;
+          indexOfLeastUpperBound = m;
+        }
+      }
+      if (numberOfTypesAtMaxDepth == 1) {
+        return s[indexOfLeastUpperBound];
+      }
+    }
+    // illegal state, log and return null- Object at maxDepth == 0 should always
+    // return itself as the least upper bound.
+    // TODO (jwren) log the error state
+    return null;
+  }
+
+  @override
+  MethodElement getMethod(String methodName) => MethodMember.from(
+      (element as ClassElementImpl).getMethod(methodName), this);
+
+  @override
+  PropertyAccessorElement getSetter(String setterName) => PropertyAccessorMember
+      .from((element as ClassElementImpl).getSetter(setterName), this);
+
+  @override
+  bool internalEquals(Object object, Set<ElementPair> visitedElementPairs) {
+    if (identical(object, this)) {
+      return true;
+    }
+    if (object is! InterfaceTypeImpl) {
+      return false;
+    }
+    InterfaceTypeImpl otherType = object as InterfaceTypeImpl;
+    return (element == otherType.element) &&
+        TypeImpl.equalArrays(
+            typeArguments, otherType.typeArguments, visitedElementPairs);
+  }
+
+  @override
+  int internalHashCode(List<DartType> visitedTypes) => hashCode;
+
+  @override
+  bool isDirectSupertypeOf(InterfaceType type) {
+    InterfaceType i = this;
+    InterfaceType j = type;
+    ClassElement jElement = j.element;
+    InterfaceType supertype = jElement.supertype;
+    //
+    // If J has no direct supertype then it is Object, and Object has no direct
+    // supertypes.
+    //
+    if (supertype == null) {
+      return false;
+    }
+    //
+    // I is listed in the extends clause of J.
+    //
+    List<DartType> jArgs = j.typeArguments;
+    List<DartType> jVars = jElement.type.typeArguments;
+    supertype = supertype.substitute2(jArgs, jVars);
+    if (supertype == i) {
+      return true;
+    }
+    //
+    // I is listed in the implements clause of J.
+    //
+    for (InterfaceType interfaceType in jElement.interfaces) {
+      interfaceType = interfaceType.substitute2(jArgs, jVars);
+      if (interfaceType == i) {
+        return true;
+      }
+    }
+    //
+    // I is listed in the with clause of J.
+    //
+    for (InterfaceType mixinType in jElement.mixins) {
+      mixinType = mixinType.substitute2(jArgs, jVars);
+      if (mixinType == i) {
+        return true;
+      }
+    }
+    //
+    // J is a mixin application of the mixin of I.
+    //
+    // TODO(brianwilkerson) Determine whether this needs to be implemented or
+    // whether it is covered by the case above.
+    return false;
+  }
+
+  @override
+  bool isMoreSpecificThan(DartType type, [Set<Element> thisExpansions,
+      Set<Element> typeExpansions, bool withDynamic = false,
+      Set<Element> visitedElements]) {
+    //
+    // S is dynamic.
+    // The test to determine whether S is dynamic is done here because dynamic
+    // is not an instance of InterfaceType.
+    //
+    if (type.isDynamic) {
+      return true;
+    }
+    //
+    // A type T is more specific than a type S, written T << S,
+    // if one of the following conditions is met:
+    //
+    // Reflexivity: T is S.
+    //
+    if (this == type) {
+      return true;
+    }
+    if (type is InterfaceType) {
+      //
+      // T is bottom. (This case is handled by the class BottomTypeImpl.)
+      //
+      // Direct supertype: S is a direct supertype of T.
+      //
+      if (type.isDirectSupertypeOf(this)) {
+        return true;
+      }
+      //
+      // Covariance: T is of the form I<T1, ..., Tn> and S is of the form
+      // I<S1, ..., Sn> and Ti << Si, 1 <= i <= n.
+      //
+      ClassElement tElement = this.element;
+      ClassElement sElement = type.element;
+      if (tElement == sElement) {
+        List<DartType> tArguments = typeArguments;
+        List<DartType> sArguments = type.typeArguments;
+        if (tArguments.length != sArguments.length) {
+          return false;
+        }
+        for (int i = 0; i < tArguments.length; i++) {
+          if (!(tArguments[i] as TypeImpl).isMoreSpecificThan(
+              sArguments[i], thisExpansions, typeExpansions, withDynamic)) {
+            return false;
+          }
+        }
+        return true;
+      }
+    }
+    //
+    // Transitivity: T << U and U << S.
+    //
+    // First check for infinite loops
+    if (element == null) {
+      return false;
+    }
+    if (visitedElements == null) {
+      visitedElements = new HashSet<ClassElement>();
+    } else if (visitedElements.contains(element)) {
+      return false;
+    }
+    visitedElements.add(element);
+    try {
+      // Iterate over all of the types U that are more specific than T because
+      // they are direct supertypes of T and return true if any of them are more
+      // specific than S.
+      InterfaceTypeImpl supertype = superclass;
+      if (supertype != null &&
+          supertype.isMoreSpecificThan(type, thisExpansions, typeExpansions,
+              withDynamic, visitedElements)) {
+        return true;
+      }
+      for (InterfaceTypeImpl interfaceType in interfaces) {
+        if (interfaceType.isMoreSpecificThan(type, thisExpansions,
+            typeExpansions, withDynamic, visitedElements)) {
+          return true;
+        }
+      }
+      for (InterfaceTypeImpl mixinType in mixins) {
+        if (mixinType.isMoreSpecificThan(type, thisExpansions, typeExpansions,
+            withDynamic, visitedElements)) {
+          return true;
+        }
+      }
+      // If a type I includes an instance method named `call`, and the type of
+      // `call` is the function type F, then I is considered to be more specific
+      // than F.
+      MethodElement callMethod = getMethod('call');
+      if (callMethod != null && !callMethod.isStatic) {
+        FunctionTypeImpl callType = callMethod.type;
+        if (callType.isMoreSpecificThan(type, thisExpansions, typeExpansions,
+            withDynamic, visitedElements)) {
+          return true;
+        }
+      }
+      return false;
+    } finally {
+      visitedElements.remove(element);
+    }
+  }
+
+  @override
+  ConstructorElement lookUpConstructor(
+      String constructorName, LibraryElement library) {
+    // prepare base ConstructorElement
+    ConstructorElement constructorElement;
+    if (constructorName == null) {
+      constructorElement = element.unnamedConstructor;
+    } else {
+      constructorElement = element.getNamedConstructor(constructorName);
+    }
+    // not found or not accessible
+    if (constructorElement == null ||
+        !constructorElement.isAccessibleIn(library)) {
+      return null;
+    }
+    // return member
+    return ConstructorMember.from(constructorElement, this);
+  }
+
+  @override
+  PropertyAccessorElement lookUpGetter(
+      String getterName, LibraryElement library) {
+    PropertyAccessorElement element = getGetter(getterName);
+    if (element != null && element.isAccessibleIn(library)) {
+      return element;
+    }
+    return lookUpGetterInSuperclass(getterName, library);
+  }
+
+  @override
+  PropertyAccessorElement lookUpGetterInSuperclass(
+      String getterName, LibraryElement library) {
+    for (InterfaceType mixin in mixins.reversed) {
+      PropertyAccessorElement element = mixin.getGetter(getterName);
+      if (element != null && element.isAccessibleIn(library)) {
+        return element;
+      }
+    }
+    HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
+    InterfaceType supertype = superclass;
+    ClassElement supertypeElement =
+        supertype == null ? null : supertype.element;
+    while (supertype != null && !visitedClasses.contains(supertypeElement)) {
+      visitedClasses.add(supertypeElement);
+      PropertyAccessorElement element = supertype.getGetter(getterName);
+      if (element != null && element.isAccessibleIn(library)) {
+        return element;
+      }
+      for (InterfaceType mixin in supertype.mixins.reversed) {
+        element = mixin.getGetter(getterName);
+        if (element != null && element.isAccessibleIn(library)) {
+          return element;
+        }
+      }
+      supertype = supertype.superclass;
+      supertypeElement = supertype == null ? null : supertype.element;
+    }
+    return null;
+  }
+
+  @override
+  MethodElement lookUpMethod(String methodName, LibraryElement library) {
+    MethodElement element = getMethod(methodName);
+    if (element != null && element.isAccessibleIn(library)) {
+      return element;
+    }
+    return lookUpMethodInSuperclass(methodName, library);
+  }
+
+  @override
+  MethodElement lookUpMethodInSuperclass(
+      String methodName, LibraryElement library) {
+    for (InterfaceType mixin in mixins.reversed) {
+      MethodElement element = mixin.getMethod(methodName);
+      if (element != null && element.isAccessibleIn(library)) {
+        return element;
+      }
+    }
+    HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
+    InterfaceType supertype = superclass;
+    ClassElement supertypeElement =
+        supertype == null ? null : supertype.element;
+    while (supertype != null && !visitedClasses.contains(supertypeElement)) {
+      visitedClasses.add(supertypeElement);
+      MethodElement element = supertype.getMethod(methodName);
+      if (element != null && element.isAccessibleIn(library)) {
+        return element;
+      }
+      for (InterfaceType mixin in supertype.mixins.reversed) {
+        element = mixin.getMethod(methodName);
+        if (element != null && element.isAccessibleIn(library)) {
+          return element;
+        }
+      }
+      supertype = supertype.superclass;
+      supertypeElement = supertype == null ? null : supertype.element;
+    }
+    return null;
+  }
+
+  @override
+  PropertyAccessorElement lookUpSetter(
+      String setterName, LibraryElement library) {
+    PropertyAccessorElement element = getSetter(setterName);
+    if (element != null && element.isAccessibleIn(library)) {
+      return element;
+    }
+    return lookUpSetterInSuperclass(setterName, library);
+  }
+
+  @override
+  PropertyAccessorElement lookUpSetterInSuperclass(
+      String setterName, LibraryElement library) {
+    for (InterfaceType mixin in mixins.reversed) {
+      PropertyAccessorElement element = mixin.getSetter(setterName);
+      if (element != null && element.isAccessibleIn(library)) {
+        return element;
+      }
+    }
+    HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
+    InterfaceType supertype = superclass;
+    ClassElement supertypeElement =
+        supertype == null ? null : supertype.element;
+    while (supertype != null && !visitedClasses.contains(supertypeElement)) {
+      visitedClasses.add(supertypeElement);
+      PropertyAccessorElement element = supertype.getSetter(setterName);
+      if (element != null && element.isAccessibleIn(library)) {
+        return element;
+      }
+      for (InterfaceType mixin in supertype.mixins.reversed) {
+        element = mixin.getSetter(setterName);
+        if (element != null && element.isAccessibleIn(library)) {
+          return element;
+        }
+      }
+      supertype = supertype.superclass;
+      supertypeElement = supertype == null ? null : supertype.element;
+    }
+    return null;
+  }
+
+  @override
+  InterfaceTypeImpl substitute2(
+      List<DartType> argumentTypes, List<DartType> parameterTypes) {
+    if (argumentTypes.length != parameterTypes.length) {
+      throw new IllegalArgumentException(
+          "argumentTypes.length (${argumentTypes.length}) != parameterTypes.length (${parameterTypes.length})");
+    }
+    if (argumentTypes.length == 0 || typeArguments.length == 0) {
+      return this;
+    }
+    List<DartType> newTypeArguments =
+        TypeImpl.substitute(typeArguments, argumentTypes, parameterTypes);
+    if (JavaArrays.equals(newTypeArguments, typeArguments)) {
+      return this;
+    }
+    InterfaceTypeImpl newType = new InterfaceTypeImpl.con1(element);
+    newType.typeArguments = newTypeArguments;
+    return newType;
+  }
+
+  @override
+  InterfaceTypeImpl substitute4(List<DartType> argumentTypes) =>
+      substitute2(argumentTypes, typeArguments);
+
+  /**
+   * Return the length of the longest inheritance path from the given [type] to
+   * Object.
+   *
+   * See [InterfaceType.getLeastUpperBound].
+   */
+  static int computeLongestInheritancePathToObject(InterfaceType type) =>
+      _computeLongestInheritancePathToObject(
+          type, 0, new HashSet<ClassElement>());
+
+  /**
+   * Returns the set of all superinterfaces of the given [type].
+   *
+   * See [getLeastUpperBound].
+   */
+  static Set<InterfaceType> computeSuperinterfaceSet(InterfaceType type) =>
+      _computeSuperinterfaceSet(type, new HashSet<InterfaceType>());
+
+  /**
+   * Return the length of the longest inheritance path from a subtype of the
+   * given [type] to Object, where the given [depth] is the length of the
+   * longest path from the subtype to this type. The set of [visitedTypes] is
+   * used to prevent infinite recursion in the case of a cyclic type structure.
+   *
+   * See [computeLongestInheritancePathToObject], and [getLeastUpperBound].
+   */
+  static int _computeLongestInheritancePathToObject(
+      InterfaceType type, int depth, HashSet<ClassElement> visitedTypes) {
+    ClassElement classElement = type.element;
+    // Object case
+    if (classElement.supertype == null || visitedTypes.contains(classElement)) {
+      return depth;
+    }
+    int longestPath = 1;
+    try {
+      visitedTypes.add(classElement);
+      List<InterfaceType> superinterfaces = classElement.interfaces;
+      int pathLength;
+      if (superinterfaces.length > 0) {
+        // loop through each of the superinterfaces recursively calling this
+        // method and keeping track of the longest path to return
+        for (InterfaceType superinterface in superinterfaces) {
+          pathLength = _computeLongestInheritancePathToObject(
+              superinterface, depth + 1, visitedTypes);
+          if (pathLength > longestPath) {
+            longestPath = pathLength;
+          }
+        }
+      }
+      // finally, perform this same check on the super type
+      // TODO(brianwilkerson) Does this also need to add in the number of mixin
+      // classes?
+      InterfaceType supertype = classElement.supertype;
+      pathLength = _computeLongestInheritancePathToObject(
+          supertype, depth + 1, visitedTypes);
+      if (pathLength > longestPath) {
+        longestPath = pathLength;
+      }
+    } finally {
+      visitedTypes.remove(classElement);
+    }
+    return longestPath;
+  }
+
+  /**
+   * Add all of the superinterfaces of the given [type] to the given [set].
+   * Return the [set] as a convenience.
+   *
+   * See [computeSuperinterfaceSet], and [getLeastUpperBound].
+   */
+  static Set<InterfaceType> _computeSuperinterfaceSet(
+      InterfaceType type, HashSet<InterfaceType> set) {
+    Element element = type.element;
+    if (element != null) {
+      List<InterfaceType> superinterfaces = type.interfaces;
+      for (InterfaceType superinterface in superinterfaces) {
+        if (set.add(superinterface)) {
+          _computeSuperinterfaceSet(superinterface, set);
+        }
+      }
+      InterfaceType supertype = type.superclass;
+      if (supertype != null) {
+        if (set.add(supertype)) {
+          _computeSuperinterfaceSet(supertype, set);
+        }
+      }
+    }
+    return set;
+  }
+
+  /**
+   * Return the intersection of the [first] and [second] sets of types, where
+   * intersection is based on the equality of the types themselves.
+   */
+  static List<InterfaceType> _intersection(
+      Set<InterfaceType> first, Set<InterfaceType> second) {
+    Set<InterfaceType> result = new HashSet<InterfaceType>.from(first);
+    result.retainAll(second);
+    return new List.from(result);
+  }
+}
+
+/**
+ * A label associated with a statement.
+ */
+abstract class LabelElement implements Element {
+  /**
+   * An empty list of label elements.
+   */
+  static const List<LabelElement> EMPTY_LIST = const <LabelElement>[];
+
+  /**
+   * Return the executable element in which this label is defined.
+   */
+  @override
+  ExecutableElement get enclosingElement;
+}
+
+/**
+ * A concrete implementation of a [LabelElement].
+ */
+class LabelElementImpl extends ElementImpl implements LabelElement {
+  /**
+   * An empty list of label elements.
+   */
+  @deprecated // Use LabelElement.EMPTY_LIST
+  static const List<LabelElement> EMPTY_ARRAY = const <LabelElement>[];
+
+  /**
+   * A flag indicating whether this label is associated with a `switch`
+   * statement.
+   */
+  // TODO(brianwilkerson) Make this a modifier.
+  final bool _onSwitchStatement;
+
+  /**
+   * A flag indicating whether this label is associated with a `switch` member
+   * (`case` or `default`).
+   */
+  // TODO(brianwilkerson) Make this a modifier.
+  final bool _onSwitchMember;
+
+  /**
+   * Initialize a newly created label element to have the given [name].
+   * [onSwitchStatement] should be `true` if this label is associated with a
+   * `switch` statement and [onSwitchMember] should be `true` if this label is
+   * associated with a `switch` member.
+   */
+  LabelElementImpl(
+      Identifier name, this._onSwitchStatement, this._onSwitchMember)
+      : super.forNode(name);
+
+  @override
+  ExecutableElement get enclosingElement =>
+      super.enclosingElement as ExecutableElement;
+
+  /**
+   * Return `true` if this label is associated with a `switch` member (`case` or
+   * `default`).
+   */
+  bool get isOnSwitchMember => _onSwitchMember;
+
+  /**
+   * Return `true` if this label is associated with a `switch` statement.
+   */
+  bool get isOnSwitchStatement => _onSwitchStatement;
+
+  @override
+  ElementKind get kind => ElementKind.LABEL;
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitLabelElement(this);
+}
+
+/**
+ * A library.
+ */
+abstract class LibraryElement implements Element {
+  /**
+   * An empty list of library elements.
+   */
+  static const List<LibraryElement> EMPTY_LIST = const <LibraryElement>[];
+
+  /**
+   * Return the compilation unit that defines this library.
+   */
+  CompilationUnitElement get definingCompilationUnit;
+
+  /**
+   * Return the entry point for this library, or `null` if this library does not
+   * have an entry point. The entry point is defined to be a zero argument
+   * top-level function whose name is `main`.
+   */
+  FunctionElement get entryPoint;
+
+  /**
+   * Return a list containing all of the libraries that are exported from this
+   * library.
+   */
+  List<LibraryElement> get exportedLibraries;
+
+  /**
+   * The export [Namespace] of this library, `null` if it has not been
+   * computed yet.
+   */
+  Namespace get exportNamespace;
+
+  /**
+   * Return a list containing all of the exports defined in this library.
+   */
+  List<ExportElement> get exports;
+
+  /**
+   * Return `true` if the defining compilation unit of this library contains at
+   * least one import directive whose URI uses the "dart-ext" scheme.
+   */
+  bool get hasExtUri;
+
+  /**
+   * Return `true` if this library defines a top-level function named
+   * `loadLibrary`.
+   */
+  bool get hasLoadLibraryFunction;
+
+  /**
+   * Return a list containing all of the libraries that are imported into this
+   * library. This includes all of the libraries that are imported using a
+   * prefix (also available through the prefixes returned by [getPrefixes]) and
+   * those that are imported without a prefix.
+   */
+  List<LibraryElement> get importedLibraries;
+
+  /**
+   * Return a list containing all of the imports defined in this library.
+   */
+  List<ImportElement> get imports;
+
+  /**
+   * Return `true` if this library is an application that can be run in the
+   * browser.
+   */
+  bool get isBrowserApplication;
+
+  /**
+   * Return `true` if this library is the dart:core library.
+   */
+  bool get isDartCore;
+
+  /**
+   * Return `true` if this library is part of the SDK.
+   */
+  bool get isInSdk;
+
+  /**
+   * Return the element representing the synthetic function `loadLibrary` that
+   * is implicitly defined for this library if the library is imported using a
+   * deferred import.
+   */
+  FunctionElement get loadLibraryFunction;
+
+  /**
+   * Return a list containing all of the compilation units that are included in
+   * this library using a `part` directive. This does not include the defining
+   * compilation unit that contains the `part` directives.
+   */
+  List<CompilationUnitElement> get parts;
+
+  /**
+   * Return a list containing elements for each of the prefixes used to `import`
+   * libraries into this library. Each prefix can be used in more than one
+   * `import` directive.
+   */
+  List<PrefixElement> get prefixes;
+
+  /**
+   * The public [Namespace] of this library, `null` if it has not been
+   * computed yet.
+   */
+  Namespace get publicNamespace;
+
+  /**
+   * Return a list containing all of the compilation units this library consists
+   * of. This includes the defining compilation unit and units included using
+   * the `part` directive.
+   */
+  List<CompilationUnitElement> get units;
+
+  /**
+   * Return a list containing all directly and indirectly imported libraries.
+   */
+  List<LibraryElement> get visibleLibraries;
+
+  /**
+   * Return a list containing all of the imports that share the given [prefix],
+   * or an empty array if there are no such imports.
+   */
+  List<ImportElement> getImportsWithPrefix(PrefixElement prefix);
+
+  /**
+   * Return the class defined in this library that has the given [name], or
+   * `null` if this library does not define a class with the given name.
+   */
+  ClassElement getType(String className);
+
+  /**
+   * Return `true` if this library is up to date with respect to the given
+   * [timeStamp]. If any transitively referenced Source is newer than the time
+   * stamp, this method returns false.
+   */
+  bool isUpToDate(int timeStamp);
+}
+
+/**
+ * A concrete implementation of a [LibraryElement].
+ */
+class LibraryElementImpl extends ElementImpl implements LibraryElement {
+  /**
+   * An empty list of library elements.
+   */
+  @deprecated // Use LibraryElement.EMPTY_LIST
+  static const List<LibraryElement> EMPTY_ARRAY = const <LibraryElement>[];
+
+  /**
+   * The analysis context in which this library is defined.
+   */
+  final AnalysisContext context;
+
+  /**
+   * The compilation unit that defines this library.
+   */
+  CompilationUnitElement _definingCompilationUnit;
+
+  /**
+   * The entry point for this library, or `null` if this library does not have
+   * an entry point.
+   */
+  FunctionElement entryPoint;
+
+  /**
+   * A list containing specifications of all of the imports defined in this
+   * library.
+   */
+  List<ImportElement> _imports = ImportElement.EMPTY_LIST;
+
+  /**
+   * A list containing specifications of all of the exports defined in this
+   * library.
+   */
+  List<ExportElement> _exports = ExportElement.EMPTY_LIST;
+
+  /**
+   * A list containing all of the compilation units that are included in this
+   * library using a `part` directive.
+   */
+  List<CompilationUnitElement> _parts = CompilationUnitElement.EMPTY_LIST;
+
+  /**
+   * The element representing the synthetic function `loadLibrary` that is
+   * defined for this library, or `null` if the element has not yet been created.
+   */
+  FunctionElement _loadLibraryFunction;
+
+  /**
+   * The export [Namespace] of this library, `null` if it has not been
+   * computed yet.
+   */
+  @override
+  Namespace exportNamespace;
+
+  /**
+   * The public [Namespace] of this library, `null` if it has not been
+   * computed yet.
+   */
+  @override
+  Namespace publicNamespace;
+
+  /**
+   * Initialize a newly created library element in the given [context] to have
+   * the given [name] and [offset].
+   */
+  LibraryElementImpl(this.context, String name, int offset)
+      : super(name, offset);
+
+  /**
+   * Initialize a newly created library element in the given [context] to have
+   * the given [name].
+   */
+  LibraryElementImpl.forNode(this.context, LibraryIdentifier name)
+      : super.forNode(name);
+
+  @override
+  CompilationUnitElement get definingCompilationUnit =>
+      _definingCompilationUnit;
+
+  /**
+   * Set the compilation unit that defines this library to the given compilation
+   * [unit].
+   */
+  void set definingCompilationUnit(CompilationUnitElement unit) {
+    (unit as CompilationUnitElementImpl).enclosingElement = this;
+    this._definingCompilationUnit = unit;
+  }
+
+  @override
+  List<LibraryElement> get exportedLibraries {
+    HashSet<LibraryElement> libraries = new HashSet<LibraryElement>();
+    for (ExportElement element in _exports) {
+      LibraryElement library = element.exportedLibrary;
+      if (library != null) {
+        libraries.add(library);
+      }
+    }
+    return new List.from(libraries);
+  }
+
+  @override
+  List<ExportElement> get exports => _exports;
+
+  /**
+   * Set the specifications of all of the exports defined in this library to the
+   * given list of [exports].
+   */
+  void set exports(List<ExportElement> exports) {
+    for (ExportElement exportElement in exports) {
+      (exportElement as ExportElementImpl).enclosingElement = this;
+    }
+    this._exports = exports;
+  }
+
+  @override
+  bool get hasExtUri => hasModifier(Modifier.HAS_EXT_URI);
+
+  /**
+   * Set whether this library has an import of a "dart-ext" URI.
+   */
+  void set hasExtUri(bool hasExtUri) {
+    setModifier(Modifier.HAS_EXT_URI, hasExtUri);
+  }
+
+  @override
+  int get hashCode => _definingCompilationUnit.hashCode;
+
+  @override
+  bool get hasLoadLibraryFunction {
+    if (_definingCompilationUnit.hasLoadLibraryFunction) {
+      return true;
+    }
+    for (int i = 0; i < _parts.length; i++) {
+      if (_parts[i].hasLoadLibraryFunction) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @override
+  String get identifier => _definingCompilationUnit.source.encoding;
+
+  @override
+  List<LibraryElement> get importedLibraries {
+    HashSet<LibraryElement> libraries = new HashSet<LibraryElement>();
+    for (ImportElement element in _imports) {
+      LibraryElement library = element.importedLibrary;
+      if (library != null) {
+        libraries.add(library);
+      }
+    }
+    return new List.from(libraries);
+  }
+
+  @override
+  List<ImportElement> get imports => _imports;
+
+  /**
+   * Set the specifications of all of the imports defined in this library to the
+   * given list of [imports].
+   */
+  void set imports(List<ImportElement> imports) {
+    for (ImportElement importElement in imports) {
+      (importElement as ImportElementImpl).enclosingElement = this;
+      PrefixElementImpl prefix = importElement.prefix as PrefixElementImpl;
+      if (prefix != null) {
+        prefix.enclosingElement = this;
+      }
+    }
+    this._imports = imports;
+  }
+
+  @override
+  bool get isBrowserApplication =>
+      entryPoint != null && isOrImportsBrowserLibrary;
+
+  @override
+  bool get isDartCore => name == "dart.core";
+
+  @override
+  bool get isInSdk =>
+      StringUtilities.startsWith5(name, 0, 0x64, 0x61, 0x72, 0x74, 0x2E);
+
+  /**
+   * Return `true` if the receiver directly or indirectly imports the
+   * 'dart:html' libraries.
+   */
+  bool get isOrImportsBrowserLibrary {
+    List<LibraryElement> visited = new List<LibraryElement>();
+    Source htmlLibSource = context.sourceFactory.forUri(DartSdk.DART_HTML);
+    visited.add(this);
+    for (int index = 0; index < visited.length; index++) {
+      LibraryElement library = visited[index];
+      Source source = library.definingCompilationUnit.source;
+      if (source == htmlLibSource) {
+        return true;
+      }
+      for (LibraryElement importedLibrary in library.importedLibraries) {
+        if (!visited.contains(importedLibrary)) {
+          visited.add(importedLibrary);
+        }
+      }
+      for (LibraryElement exportedLibrary in library.exportedLibraries) {
+        if (!visited.contains(exportedLibrary)) {
+          visited.add(exportedLibrary);
+        }
+      }
+    }
+    return false;
+  }
+
+  @override
+  ElementKind get kind => ElementKind.LIBRARY;
+
+  @override
+  LibraryElement get library => this;
+
+  @override
+  FunctionElement get loadLibraryFunction {
+    if (_loadLibraryFunction == null) {
+      FunctionElementImpl function =
+          new FunctionElementImpl(FunctionElement.LOAD_LIBRARY_NAME, -1);
+      function.synthetic = true;
+      function.enclosingElement = this;
+      function.returnType = loadLibraryReturnType;
+      function.type = new FunctionTypeImpl.con1(function);
+      _loadLibraryFunction = function;
+    }
+    return _loadLibraryFunction;
+  }
+
+  /**
+   * Return the object representing the type 'Future' from the 'dart:async'
+   * library, or the type 'void' if the type 'Future' cannot be accessed.
+   */
+  DartType get loadLibraryReturnType {
+    try {
+      Source asyncSource = context.sourceFactory.forUri(DartSdk.DART_ASYNC);
+      if (asyncSource == null) {
+        AnalysisEngine.instance.logger
+            .logError("Could not create a source for dart:async");
+        return VoidTypeImpl.instance;
+      }
+      LibraryElement asyncElement = context.computeLibraryElement(asyncSource);
+      if (asyncElement == null) {
+        AnalysisEngine.instance.logger
+            .logError("Could not build the element model for dart:async");
+        return VoidTypeImpl.instance;
+      }
+      ClassElement futureElement = asyncElement.getType("Future");
+      if (futureElement == null) {
+        AnalysisEngine.instance.logger
+            .logError("Could not find type Future in dart:async");
+        return VoidTypeImpl.instance;
+      }
+      InterfaceType futureType = futureElement.type;
+      return futureType.substitute4(<DartType>[DynamicTypeImpl.instance]);
+    } on AnalysisException catch (exception, stackTrace) {
+      AnalysisEngine.instance.logger.logError(
+          "Could not build the element model for dart:async",
+          new CaughtException(exception, stackTrace));
+      return VoidTypeImpl.instance;
+    }
+  }
+
+  @override
+  List<CompilationUnitElement> get parts => _parts;
+
+  /**
+   * Set the compilation units that are included in this library using a `part`
+   * directive to the given list of [parts].
+   */
+  void set parts(List<CompilationUnitElement> parts) {
+    for (CompilationUnitElement compilationUnit in parts) {
+      (compilationUnit as CompilationUnitElementImpl).enclosingElement = this;
+    }
+    this._parts = parts;
+  }
+
+  @override
+  List<PrefixElement> get prefixes {
+    HashSet<PrefixElement> prefixes = new HashSet<PrefixElement>();
+    for (ImportElement element in _imports) {
+      PrefixElement prefix = element.prefix;
+      if (prefix != null) {
+        prefixes.add(prefix);
+      }
+    }
+    return new List.from(prefixes);
+  }
+
+  @override
+  Source get source {
+    if (_definingCompilationUnit == null) {
+      return null;
+    }
+    return _definingCompilationUnit.source;
+  }
+
+  @override
+  List<CompilationUnitElement> get units {
+    List<CompilationUnitElement> units = new List<CompilationUnitElement>();
+    units.add(_definingCompilationUnit);
+    units.addAll(_parts);
+    return units;
+  }
+
+  @override
+  List<LibraryElement> get visibleLibraries {
+    Set<LibraryElement> visibleLibraries = new Set();
+    _addVisibleLibraries(visibleLibraries, false);
+    return new List.from(visibleLibraries);
+  }
+
+  @override
+  bool operator ==(Object object) => object is LibraryElementImpl &&
+      _definingCompilationUnit == object.definingCompilationUnit;
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitLibraryElement(this);
+
+  @override
+  ElementImpl getChild(String identifier) {
+    if ((_definingCompilationUnit as CompilationUnitElementImpl).identifier ==
+        identifier) {
+      return _definingCompilationUnit as CompilationUnitElementImpl;
+    }
+    for (CompilationUnitElement part in _parts) {
+      if ((part as CompilationUnitElementImpl).identifier == identifier) {
+        return part as CompilationUnitElementImpl;
+      }
+    }
+    for (ImportElement importElement in _imports) {
+      if ((importElement as ImportElementImpl).identifier == identifier) {
+        return importElement as ImportElementImpl;
+      }
+    }
+    for (ExportElement exportElement in _exports) {
+      if ((exportElement as ExportElementImpl).identifier == identifier) {
+        return exportElement as ExportElementImpl;
+      }
+    }
+    return null;
+  }
+
+  @override
+  List<ImportElement> getImportsWithPrefix(PrefixElement prefixElement) {
+    int count = _imports.length;
+    List<ImportElement> importList = new List<ImportElement>();
+    for (int i = 0; i < count; i++) {
+      if (identical(_imports[i].prefix, prefixElement)) {
+        importList.add(_imports[i]);
+      }
+    }
+    return importList;
+  }
+
+  @override
+  ClassElement getType(String className) {
+    ClassElement type = _definingCompilationUnit.getType(className);
+    if (type != null) {
+      return type;
+    }
+    for (CompilationUnitElement part in _parts) {
+      type = part.getType(className);
+      if (type != null) {
+        return type;
+      }
+    }
+    return null;
+  }
+
+  @override
+  bool isUpToDate(int timeStamp) {
+    Set<LibraryElement> visitedLibraries = new Set();
+    return _safeIsUpToDate(this, timeStamp, visitedLibraries);
+  }
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    super.visitChildren(visitor);
+    safelyVisitChild(_definingCompilationUnit, visitor);
+    safelyVisitChildren(_exports, visitor);
+    safelyVisitChildren(_imports, visitor);
+    safelyVisitChildren(_parts, visitor);
+  }
+
+  /**
+   * Recursively fills set of visible libraries for
+   * [getVisibleElementsLibraries].
+   */
+  void _addVisibleLibraries(
+      Set<LibraryElement> visibleLibraries, bool includeExports) {
+    // maybe already processed
+    if (!visibleLibraries.add(this)) {
+      return;
+    }
+    // add imported libraries
+    for (ImportElement importElement in _imports) {
+      LibraryElement importedLibrary = importElement.importedLibrary;
+      if (importedLibrary != null) {
+        (importedLibrary as LibraryElementImpl)._addVisibleLibraries(
+            visibleLibraries, true);
+      }
+    }
+    // add exported libraries
+    if (includeExports) {
+      for (ExportElement exportElement in _exports) {
+        LibraryElement exportedLibrary = exportElement.exportedLibrary;
+        if (exportedLibrary != null) {
+          (exportedLibrary as LibraryElementImpl)._addVisibleLibraries(
+              visibleLibraries, true);
+        }
+      }
+    }
+  }
+
+  /**
+   * Return `true` if the given [library] is up to date with respect to the
+   * given [timeStamp]. The set of [visitedLibraries] is used to prevent
+   * infinite recusion in the case of mutually dependent libraries.
+   */
+  static bool _safeIsUpToDate(LibraryElement library, int timeStamp,
+      Set<LibraryElement> visitedLibraries) {
+    if (!visitedLibraries.contains(library)) {
+      visitedLibraries.add(library);
+      AnalysisContext context = library.context;
+      // Check the defining compilation unit.
+      if (timeStamp <
+          context
+              .getModificationStamp(library.definingCompilationUnit.source)) {
+        return false;
+      }
+      // Check the parted compilation units.
+      for (CompilationUnitElement element in library.parts) {
+        if (timeStamp < context.getModificationStamp(element.source)) {
+          return false;
+        }
+      }
+      // Check the imported libraries.
+      for (LibraryElement importedLibrary in library.importedLibraries) {
+        if (!_safeIsUpToDate(importedLibrary, timeStamp, visitedLibraries)) {
+          return false;
+        }
+      }
+      // Check the exported libraries.
+      for (LibraryElement exportedLibrary in library.exportedLibraries) {
+        if (!_safeIsUpToDate(exportedLibrary, timeStamp, visitedLibraries)) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+}
+
+/**
+ * An element that can be (but are not required to be) defined within a method
+ * or function (an [ExecutableElement]).
+ */
+abstract class LocalElement implements Element {
+  /**
+   * Return a source range that covers the approximate portion of the source in
+   * which the name of this element is visible, or `null` if there is no single
+   * range of characters within which the element name is visible.
+   *
+   * * For a local variable, this includes everything from the end of the
+   *   variable's initializer to the end of the block that encloses the variable
+   *   declaration.
+   * * For a parameter, this includes the body of the method or function that
+   *   declares the parameter.
+   * * For a local function, this includes everything from the beginning of the
+   *   function's body to the end of the block that encloses the function
+   *   declaration.
+   * * For top-level functions, `null` will be returned because they are
+   *   potentially visible in multiple sources.
+   */
+  SourceRange get visibleRange;
+}
+
+/**
+ * A local variable.
+ */
+abstract class LocalVariableElement implements LocalElement, VariableElement {
+  /**
+   * An empty list of field elements.
+   */
+  static const List<LocalVariableElement> EMPTY_LIST =
+      const <LocalVariableElement>[];
+
+  /**
+   * Return the resolved [VariableDeclaration] node that declares this
+   * [LocalVariableElement].
+   *
+   * This method is expensive, because resolved AST might be evicted from cache,
+   * so parsing and resolving will be performed.
+   */
+  @override
+  VariableDeclaration get node;
+}
+
+/**
+ * A concrete implementation of a [LocalVariableElement].
+ */
+class LocalVariableElementImpl extends VariableElementImpl
+    with PotentiallyConstVariableElement implements LocalVariableElement {
+  /**
+   * An empty list of field elements.
+   */
+  @deprecated // Use LocalVariableElement.EMPTY_LIST
+  static const List<LocalVariableElement> EMPTY_ARRAY =
+      const <LocalVariableElement>[];
+
+  /**
+   * The offset to the beginning of the visible range for this element.
+   */
+  int _visibleRangeOffset = 0;
+
+  /**
+   * The length of the visible range for this element, or `-1` if this element
+   * does not have a visible range.
+   */
+  int _visibleRangeLength = -1;
+
+  /**
+   * Initialize a newly created method element to have the given [name] and
+   * [offset].
+   */
+  LocalVariableElementImpl(String name, int offset) : super(name, offset);
+
+  /**
+   * Initialize a newly created local variable element to have the given [name].
+   */
+  LocalVariableElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  @override
+  String get identifier {
+    int enclosingOffset =
+        enclosingElement != null ? enclosingElement.nameOffset : 0;
+    int delta = nameOffset - enclosingOffset;
+    return '${super.identifier}@$delta';
+  }
+
+  @override
+  bool get isPotentiallyMutatedInClosure =>
+      hasModifier(Modifier.POTENTIALLY_MUTATED_IN_CONTEXT);
+
+  @override
+  bool get isPotentiallyMutatedInScope =>
+      hasModifier(Modifier.POTENTIALLY_MUTATED_IN_SCOPE);
+
+  @override
+  ElementKind get kind => ElementKind.LOCAL_VARIABLE;
+
+  @override
+  VariableDeclaration get node =>
+      getNodeMatching((node) => node is VariableDeclaration);
+
+  @override
+  SourceRange get visibleRange {
+    if (_visibleRangeLength < 0) {
+      return null;
+    }
+    return new SourceRange(_visibleRangeOffset, _visibleRangeLength);
+  }
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitLocalVariableElement(this);
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    buffer.write(type);
+    buffer.write(" ");
+    buffer.write(displayName);
+  }
+
+  /**
+   * Specifies that this variable is potentially mutated somewhere in closure.
+   */
+  void markPotentiallyMutatedInClosure() {
+    setModifier(Modifier.POTENTIALLY_MUTATED_IN_CONTEXT, true);
+  }
+
+  /**
+   * Specifies that this variable is potentially mutated somewhere in its scope.
+   */
+  void markPotentiallyMutatedInScope() {
+    setModifier(Modifier.POTENTIALLY_MUTATED_IN_SCOPE, true);
+  }
+
+  /**
+   * Set the visible range for this element to the range starting at the given
+   * [offset] with the given [length].
+   */
+  void setVisibleRange(int offset, int length) {
+    _visibleRangeOffset = offset;
+    _visibleRangeLength = length;
+  }
+}
+
+/**
+ * An element defined in a parameterized type where the values of the type
+ * parameters are known.
+ */
+abstract class Member implements Element {
+  /**
+   * The element on which the parameterized element was created.
+   */
+  final Element _baseElement;
+
+  /**
+   * The type in which the element is defined.
+   */
+  final ParameterizedType _definingType;
+
+  /**
+   * Initialize a newly created element to represent a constructor, based on the
+   * [baseElement], defined by the [definingType].
+   */
+  Member(this._baseElement, this._definingType);
+
+  /**
+   * Return the element on which the parameterized element was created.
+   */
+  Element get baseElement => _baseElement;
+
+  @override
+  AnalysisContext get context => _baseElement.context;
+
+  /**
+   * Return the type in which the element is defined.
+   */
+  ParameterizedType get definingType => _definingType;
+
+  @override
+  String get displayName => _baseElement.displayName;
+
+  int get id => _baseElement.id;
+
+  @override
+  bool get isDeprecated => _baseElement.isDeprecated;
+
+  @override
+  bool get isOverride => _baseElement.isOverride;
+
+  @override
+  bool get isPrivate => _baseElement.isPrivate;
+
+  @override
+  bool get isPublic => _baseElement.isPublic;
+
+  @override
+  bool get isSynthetic => _baseElement.isSynthetic;
+
+  @override
+  ElementKind get kind => _baseElement.kind;
+
+  @override
+  LibraryElement get library => _baseElement.library;
+
+  @override
+  ElementLocation get location => _baseElement.location;
+
+  @override
+  List<ElementAnnotation> get metadata => _baseElement.metadata;
+
+  @override
+  String get name => _baseElement.name;
+
+  @override
+  int get nameOffset => _baseElement.nameOffset;
+
+  @override
+  AstNode get node => _baseElement.node;
+
+  @override
+  Source get source => _baseElement.source;
+
+  @override
+  CompilationUnit get unit => _baseElement.unit;
+
+  @override
+  String computeDocumentationComment() =>
+      _baseElement.computeDocumentationComment();
+
+  @override
+  Element getAncestor(Predicate<Element> predicate) =>
+      baseElement.getAncestor(predicate);
+
+  @override
+  String getExtendedDisplayName(String shortName) =>
+      _baseElement.getExtendedDisplayName(shortName);
+
+  @override
+  bool isAccessibleIn(LibraryElement library) =>
+      _baseElement.isAccessibleIn(library);
+
+  /**
+   * If the given [child] is not `null`, use the given [visitor] to visit it.
+   */
+  void safelyVisitChild(Element child, ElementVisitor visitor) {
+    // TODO(brianwilkerson) Make this private
+    if (child != null) {
+      child.accept(visitor);
+    }
+  }
+
+  /**
+   * Use the given [visitor] to visit all of the [children].
+   */
+  void safelyVisitChildren(List<Element> children, ElementVisitor visitor) {
+    // TODO(brianwilkerson) Make this private
+    if (children != null) {
+      for (Element child in children) {
+        child.accept(visitor);
+      }
+    }
+  }
+
+  /**
+   * Return the type that results from replacing the type parameters in the
+   * given [type] with the type arguments associated with this member.
+   */
+  DartType substituteFor(DartType type) {
+    if (type == null) {
+      return null;
+    }
+    List<DartType> argumentTypes = _definingType.typeArguments;
+    List<DartType> parameterTypes =
+        TypeParameterTypeImpl.getTypes(_definingType.typeParameters);
+    return type.substitute2(argumentTypes, parameterTypes);
+  }
+
+  /**
+   * Return the list of types that results from replacing the type parameters in
+   * the given [types] with the type arguments associated with this member.
+   */
+  List<InterfaceType> substituteFor2(List<InterfaceType> types) {
+    int count = types.length;
+    List<InterfaceType> substitutedTypes = new List<InterfaceType>(count);
+    for (int i = 0; i < count; i++) {
+      substitutedTypes[i] = substituteFor(types[i]);
+    }
+    return substitutedTypes;
+  }
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    // There are no children to visit
+  }
+}
+
+/**
+ * An element that represents a method defined within a type.
+ */
+abstract class MethodElement implements ClassMemberElement, ExecutableElement {
+  /**
+   * An empty list of method elements.
+   */
+  static const List<MethodElement> EMPTY_LIST = const <MethodElement>[];
+
+  /**
+   * Return the resolved [MethodDeclaration] node that declares this
+   * [MethodElement].
+   *
+   * This method is expensive, because resolved AST might be evicted from cache,
+   * so parsing and resolving will be performed.
+   */
+  @override
+  MethodDeclaration get node;
+}
+
+/**
+ * A concrete implementation of a [MethodElement].
+ */
+class MethodElementImpl extends ExecutableElementImpl implements MethodElement {
+  /**
+   * An empty list of method elements.
+   */
+  @deprecated // Use MethodElement.EMPTY_LIST
+  static const List<MethodElement> EMPTY_ARRAY = const <MethodElement>[];
+
+  /**
+   * Initialize a newly created method element to have the given [name] at the
+   * given [offset].
+   */
+  MethodElementImpl(String name, int offset) : super(name, offset);
+
+  /**
+   * Initialize a newly created method element to have the given [name].
+   */
+  MethodElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  /**
+   * Set whether this method is abstract.
+   */
+  void set abstract(bool isAbstract) {
+    setModifier(Modifier.ABSTRACT, isAbstract);
+  }
+
+  @override
+  String get displayName {
+    String displayName = super.displayName;
+    if ("unary-" == displayName) {
+      return "-";
+    }
+    return displayName;
+  }
+
+  @override
+  ClassElement get enclosingElement => super.enclosingElement as ClassElement;
+
+  @override
+  bool get isOperator {
+    String name = displayName;
+    if (name.isEmpty) {
+      return false;
+    }
+    int first = name.codeUnitAt(0);
+    return !((0x61 <= first && first <= 0x7A) ||
+        (0x41 <= first && first <= 0x5A) ||
+        first == 0x5F ||
+        first == 0x24);
+  }
+
+  @override
+  bool get isStatic => hasModifier(Modifier.STATIC);
+
+  @override
+  ElementKind get kind => ElementKind.METHOD;
+
+  @override
+  String get name {
+    String name = super.name;
+    if (isOperator && name == "-") {
+      if (parameters.length == 0) {
+        return "unary-";
+      }
+    }
+    return super.name;
+  }
+
+  @override
+  MethodDeclaration get node =>
+      getNodeMatching((node) => node is MethodDeclaration);
+
+  /**
+   * Set whether this method is static.
+   */
+  void set static(bool isStatic) {
+    setModifier(Modifier.STATIC, isStatic);
+  }
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitMethodElement(this);
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    buffer.write(displayName);
+    super.appendTo(buffer);
+  }
+}
+
+/**
+ * A method element defined in a parameterized type where the values of the type
+ * parameters are known.
+ */
+class MethodMember extends ExecutableMember implements MethodElement {
+  /**
+   * Initialize a newly created element to represent a constructor, based on the
+   * [baseElement], defined by the [definingType].
+   */
+  MethodMember(MethodElement baseElement, InterfaceType definingType)
+      : super(baseElement, definingType);
+
+  @override
+  MethodElement get baseElement => super.baseElement as MethodElement;
+
+  @override
+  ClassElement get enclosingElement => baseElement.enclosingElement;
+
+  @override
+  MethodDeclaration get node => baseElement.node;
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitMethodElement(this);
+
+  @override
+  String toString() {
+    MethodElement baseElement = this.baseElement;
+    List<ParameterElement> parameters = this.parameters;
+    FunctionType type = this.type;
+    StringBuffer buffer = new StringBuffer();
+    buffer.write(baseElement.enclosingElement.displayName);
+    buffer.write(".");
+    buffer.write(baseElement.displayName);
+    buffer.write("(");
+    int parameterCount = parameters.length;
+    for (int i = 0; i < parameterCount; i++) {
+      if (i > 0) {
+        buffer.write(", ");
+      }
+      buffer.write(parameters[i]);
+    }
+    buffer.write(")");
+    if (type != null) {
+      buffer.write(Element.RIGHT_ARROW);
+      buffer.write(type.returnType);
+    }
+    return buffer.toString();
+  }
+
+  /**
+   * If the given [method]'s type is different when any type parameters from the
+   * defining type's declaration are replaced with the actual type arguments
+   * from the [definingType], create a method member representing the given
+   * method. Return the member that was created, or the base method if no member
+   * was created.
+   */
+  static MethodElement from(MethodElement method, InterfaceType definingType) {
+    if (method == null || definingType.typeArguments.length == 0) {
+      return method;
+    }
+    FunctionType baseType = method.type;
+    List<DartType> argumentTypes = definingType.typeArguments;
+    List<DartType> parameterTypes = definingType.element.type.typeArguments;
+    FunctionType substitutedType =
+        baseType.substitute2(argumentTypes, parameterTypes);
+    if (baseType == substitutedType) {
+      return method;
+    }
+    // TODO(brianwilkerson) Consider caching the substituted type in the
+    // instance. It would use more memory but speed up some operations.
+    // We need to see how often the type is being re-computed.
+    return new MethodMember(method, definingType);
+  }
+}
+
+/**
+ * The enumeration `Modifier` defines constants for all of the modifiers defined
+ * by the Dart language and for a few additional flags that are useful.
+ */
+class Modifier extends Enum<Modifier> {
+  /**
+   * Indicates that the modifier 'abstract' was applied to the element.
+   */
+  static const Modifier ABSTRACT = const Modifier('ABSTRACT', 0);
+
+  /**
+   * Indicates that an executable element has a body marked as being
+   * asynchronous.
+   */
+  static const Modifier ASYNCHRONOUS = const Modifier('ASYNCHRONOUS', 1);
+
+  /**
+   * Indicates that the modifier 'const' was applied to the element.
+   */
+  static const Modifier CONST = const Modifier('CONST', 2);
+
+  /**
+   * Indicates that the import element represents a deferred library.
+   */
+  static const Modifier DEFERRED = const Modifier('DEFERRED', 3);
+
+  /**
+   * Indicates that a class element was defined by an enum declaration.
+   */
+  static const Modifier ENUM = const Modifier('ENUM', 4);
+
+  /**
+   * Indicates that the modifier 'factory' was applied to the element.
+   */
+  static const Modifier FACTORY = const Modifier('FACTORY', 5);
+
+  /**
+   * Indicates that the modifier 'final' was applied to the element.
+   */
+  static const Modifier FINAL = const Modifier('FINAL', 6);
+
+  /**
+   * Indicates that an executable element has a body marked as being a
+   * generator.
+   */
+  static const Modifier GENERATOR = const Modifier('GENERATOR', 7);
+
+  /**
+   * Indicates that the pseudo-modifier 'get' was applied to the element.
+   */
+  static const Modifier GETTER = const Modifier('GETTER', 8);
+
+  /**
+   * A flag used for libraries indicating that the defining compilation unit
+   * contains at least one import directive whose URI uses the "dart-ext"
+   * scheme.
+   */
+  static const Modifier HAS_EXT_URI = const Modifier('HAS_EXT_URI', 9);
+
+  /**
+   * Indicates that a class can validly be used as a mixin.
+   */
+  static const Modifier MIXIN = const Modifier('MIXIN', 10);
+
+  /**
+   * Indicates that an error has reported explaining why this class is an
+   * invalid mixin application.
+   */
+  static const Modifier MIXIN_ERRORS_REPORTED =
+      const Modifier('MIXIN_ERRORS_REPORTED', 11);
+
+  /**
+   * Indicates that the value of a parameter or local variable might be mutated
+   * within the context.
+   */
+  static const Modifier POTENTIALLY_MUTATED_IN_CONTEXT =
+      const Modifier('POTENTIALLY_MUTATED_IN_CONTEXT', 12);
+
+  /**
+   * Indicates that the value of a parameter or local variable might be mutated
+   * within the scope.
+   */
+  static const Modifier POTENTIALLY_MUTATED_IN_SCOPE =
+      const Modifier('POTENTIALLY_MUTATED_IN_SCOPE', 13);
+
+  /**
+   * Indicates that a class contains an explicit reference to 'super'.
+   */
+  static const Modifier REFERENCES_SUPER =
+      const Modifier('REFERENCES_SUPER', 14);
+
+  /**
+   * Indicates that the pseudo-modifier 'set' was applied to the element.
+   */
+  static const Modifier SETTER = const Modifier('SETTER', 15);
+
+  /**
+   * Indicates that the modifier 'static' was applied to the element.
+   */
+  static const Modifier STATIC = const Modifier('STATIC', 16);
+
+  /**
+   * Indicates that the element does not appear in the source code but was
+   * implicitly created. For example, if a class does not define any
+   * constructors, an implicit zero-argument constructor will be created and it
+   * will be marked as being synthetic.
+   */
+  static const Modifier SYNTHETIC = const Modifier('SYNTHETIC', 17);
+
+  /**
+   * Indicates that a class was defined using an alias.
+   * TODO(brianwilkerson) This should be renamed to 'ALIAS'.
+   */
+  static const Modifier TYPEDEF = const Modifier('TYPEDEF', 18);
+
+  static const List<Modifier> values = const [
+    ABSTRACT,
+    ASYNCHRONOUS,
+    CONST,
+    DEFERRED,
+    ENUM,
+    FACTORY,
+    FINAL,
+    GENERATOR,
+    GETTER,
+    HAS_EXT_URI,
+    MIXIN,
+    MIXIN_ERRORS_REPORTED,
+    POTENTIALLY_MUTATED_IN_CONTEXT,
+    POTENTIALLY_MUTATED_IN_SCOPE,
+    REFERENCES_SUPER,
+    SETTER,
+    STATIC,
+    SYNTHETIC,
+    TYPEDEF
+  ];
+
+  const Modifier(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * A pseudo-element that represents multiple elements defined within a single
+ * scope that have the same name. This situation is not allowed by the language,
+ * so objects implementing this interface always represent an error. As a
+ * result, most of the normal operations on elements do not make sense and will
+ * return useless results.
+ */
+abstract class MultiplyDefinedElement implements Element {
+  /**
+   * Return a list containing all of the elements that were defined within the
+   * scope to have the same name.
+   */
+  List<Element> get conflictingElements;
+
+  /**
+   * Return the type of this element as the dynamic type.
+   */
+  DartType get type;
+}
+
+/**
+ * A concrete implementation of a [MultiplyDefinedElement].
+ */
+class MultiplyDefinedElementImpl implements MultiplyDefinedElement {
+  /**
+   * The unique integer identifier of this element.
+   */
+  final int id = ElementImpl._NEXT_ID++;
+
+  /**
+   * The analysis context in which the multiply defined elements are defined.
+   */
+  final AnalysisContext context;
+
+  /**
+   * The name of the conflicting elements.
+   */
+  String _name;
+
+  /**
+   * A list containing all of the elements that conflict.
+   */
+  final List<Element> conflictingElements;
+
+  /**
+   * Initialize a newly created element in the given [context] to represent a
+   * list of [conflictingElements].
+   */
+  MultiplyDefinedElementImpl(this.context, this.conflictingElements) {
+    _name = conflictingElements[0].name;
+  }
+
+  @override
+  String get displayName => _name;
+
+  @override
+  Element get enclosingElement => null;
+
+  @override
+  bool get isDeprecated => false;
+
+  @override
+  bool get isOverride => false;
+
+  @override
+  bool get isPrivate {
+    String name = displayName;
+    if (name == null) {
+      return false;
+    }
+    return Identifier.isPrivateName(name);
+  }
+
+  @override
+  bool get isPublic => !isPrivate;
+
+  @override
+  bool get isSynthetic => true;
+
+  @override
+  ElementKind get kind => ElementKind.ERROR;
+
+  @override
+  LibraryElement get library => null;
+
+  @override
+  ElementLocation get location => null;
+
+  @override
+  List<ElementAnnotation> get metadata => ElementAnnotation.EMPTY_LIST;
+
+  @override
+  String get name => _name;
+
+  @override
+  int get nameOffset => -1;
+
+  @override
+  AstNode get node => null;
+
+  @override
+  Source get source => null;
+
+  @override
+  DartType get type => DynamicTypeImpl.instance;
+
+  @override
+  CompilationUnit get unit => null;
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitMultiplyDefinedElement(this);
+
+  @override
+  String computeDocumentationComment() => null;
+
+  @override
+  Element getAncestor(Predicate<Element> predicate) => null;
+
+  @override
+  String getExtendedDisplayName(String shortName) {
+    if (shortName != null) {
+      return shortName;
+    }
+    return displayName;
+  }
+
+  @override
+  bool isAccessibleIn(LibraryElement library) {
+    for (Element element in conflictingElements) {
+      if (element.isAccessibleIn(library)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    buffer.write("[");
+    int count = conflictingElements.length;
+    for (int i = 0; i < count; i++) {
+      if (i > 0) {
+        buffer.write(", ");
+      }
+      (conflictingElements[i] as ElementImpl).appendTo(buffer);
+    }
+    buffer.write("]");
+    return buffer.toString();
+  }
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    // There are no children to visit
+  }
+
+  /**
+   * Return an element in the given [context] that represents the fact that the
+   * [firstElement] and [secondElement] conflict. (If the elements are the same,
+   * then one of the two will be returned directly.)
+   */
+  static Element fromElements(
+      AnalysisContext context, Element firstElement, Element secondElement) {
+    List<Element> conflictingElements =
+        _computeConflictingElements(firstElement, secondElement);
+    int length = conflictingElements.length;
+    if (length == 0) {
+      return null;
+    } else if (length == 1) {
+      return conflictingElements[0];
+    }
+    return new MultiplyDefinedElementImpl(context, conflictingElements);
+  }
+
+  /**
+   * Add the given [element] to the list of [elements]. If the element is a
+   * multiply-defined element, add all of the conflicting elements that it
+   * represents.
+   */
+  static void _add(HashSet<Element> elements, Element element) {
+    if (element is MultiplyDefinedElementImpl) {
+      for (Element conflictingElement in element.conflictingElements) {
+        elements.add(conflictingElement);
+      }
+    } else {
+      elements.add(element);
+    }
+  }
+
+  /**
+   * Use the given elements to construct a list of conflicting elements. If
+   * either the [firstElement] or [secondElement] are multiply-defined elements
+   * then the conflicting elements they represent will be included in the array.
+   * Otherwise, the element itself will be included.
+   */
+  static List<Element> _computeConflictingElements(
+      Element firstElement, Element secondElement) {
+    HashSet<Element> elements = new HashSet<Element>();
+    _add(elements, firstElement);
+    _add(elements, secondElement);
+    return new List.from(elements);
+  }
+}
+
+/**
+ * An [ExecutableElement], with the additional information of a list of
+ * [ExecutableElement]s from which this element was composed.
+ */
+abstract class MultiplyInheritedExecutableElement implements ExecutableElement {
+  /**
+   * Return a list containing all of the executable elements defined within this
+   * executable element.
+   */
+  List<ExecutableElement> get inheritedElements;
+}
+
+/**
+ * A [MethodElementImpl], with the additional information of a list of
+ * [ExecutableElement]s from which this element was composed.
+ */
+class MultiplyInheritedMethodElementImpl extends MethodElementImpl
+    implements MultiplyInheritedExecutableElement {
+  /**
+   * A list the array of executable elements that were used to compose this
+   * element.
+   */
+  List<ExecutableElement> _elements = MethodElement.EMPTY_LIST;
+
+  MultiplyInheritedMethodElementImpl(Identifier name) : super.forNode(name) {
+    synthetic = true;
+  }
+
+  @override
+  List<ExecutableElement> get inheritedElements => _elements;
+
+  void set inheritedElements(List<ExecutableElement> elements) {
+    this._elements = elements;
+  }
+}
+
+/**
+ * A [PropertyAccessorElementImpl], with the additional information of a list of
+ * [ExecutableElement]s from which this element was composed.
+ */
+class MultiplyInheritedPropertyAccessorElementImpl
+    extends PropertyAccessorElementImpl
+    implements MultiplyInheritedExecutableElement {
+  /**
+   * A list the array of executable elements that were used to compose this
+   * element.
+   */
+  List<ExecutableElement> _elements = PropertyAccessorElement.EMPTY_LIST;
+
+  MultiplyInheritedPropertyAccessorElementImpl(Identifier name)
+      : super.forNode(name) {
+    synthetic = true;
+  }
+
+  @override
+  List<ExecutableElement> get inheritedElements => _elements;
+
+  void set inheritedElements(List<ExecutableElement> elements) {
+    this._elements = elements;
+  }
+}
+
+/**
+ * An object that controls how namespaces are combined.
+ */
+abstract class NamespaceCombinator {
+  /**
+   * An empty list of namespace combinators.
+   */
+  @deprecated // Use NamespaceCombinator.EMPTY_LIST
+  static const List<NamespaceCombinator> EMPTY_ARRAY =
+      const <NamespaceCombinator>[];
+
+  /**
+   * An empty list of namespace combinators.
+   */
+  static const List<NamespaceCombinator> EMPTY_LIST =
+      const <NamespaceCombinator>[];
+}
+
+/**
+ * A parameter defined within an executable element.
+ */
+abstract class ParameterElement implements LocalElement, VariableElement {
+  /**
+   * An empty list of parameter elements.
+   */
+  static const List<ParameterElement> EMPTY_LIST = const <ParameterElement>[];
+
+  /**
+   * Return the Dart code of the default value, or `null` if no default value.
+   */
+  String get defaultValueCode;
+
+  /**
+   * Return `true` if this parameter is an initializing formal parameter.
+   */
+  bool get isInitializingFormal;
+
+  @override
+  FormalParameter get node;
+
+  /**
+   * Return the kind of this parameter.
+   */
+  ParameterKind get parameterKind;
+
+  /**
+   * Return a list containing all of the parameters defined by this parameter.
+   * A parameter will only define other parameters if it is a function typed
+   * parameter.
+   */
+  List<ParameterElement> get parameters;
+}
+
+/**
+ * A concrete implementation of a [ParameterElement].
+ */
+class ParameterElementImpl extends VariableElementImpl
+    implements ParameterElement {
+  /**
+   * An empty list of parameter elements.
+   */
+  @deprecated // Use ParameterElement.EMPTY_LIST
+  static const List<ParameterElement> EMPTY_ARRAY = const <ParameterElement>[];
+
+  /**
+   * A list containing all of the parameters defined by this parameter element.
+   * There will only be parameters if this parameter is a function typed
+   * parameter.
+   */
+  List<ParameterElement> _parameters = ParameterElement.EMPTY_LIST;
+
+  /**
+   * The kind of this parameter.
+   */
+  ParameterKind parameterKind;
+
+  /**
+   * The Dart code of the default value.
+   */
+  String _defaultValueCode;
+
+  /**
+   * The offset to the beginning of the visible range for this element.
+   */
+  int _visibleRangeOffset = 0;
+
+  /**
+   * The length of the visible range for this element, or `-1` if this element
+   * does not have a visible range.
+   */
+  int _visibleRangeLength = -1;
+
+  /**
+   * Initialize a newly created parameter element to have the given [name] and
+   * [offset].
+   */
+  ParameterElementImpl(String name, int nameOffset) : super(name, nameOffset);
+
+  /**
+   * Initialize a newly created parameter element to have the given [name].
+   */
+  ParameterElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  @override
+  String get defaultValueCode => _defaultValueCode;
+
+  /**
+   * Set Dart code of the default value.
+   */
+  void set defaultValueCode(String defaultValueCode) {
+    this._defaultValueCode = StringUtilities.intern(defaultValueCode);
+  }
+
+  @override
+  bool get isInitializingFormal => false;
+
+  @override
+  bool get isPotentiallyMutatedInClosure =>
+      hasModifier(Modifier.POTENTIALLY_MUTATED_IN_CONTEXT);
+
+  @override
+  bool get isPotentiallyMutatedInScope =>
+      hasModifier(Modifier.POTENTIALLY_MUTATED_IN_SCOPE);
+
+  @override
+  ElementKind get kind => ElementKind.PARAMETER;
+
+  @override
+  FormalParameter get node =>
+      getNodeMatching((node) => node is FormalParameter);
+
+  @override
+  List<ParameterElement> get parameters => _parameters;
+
+  /**
+   * Set the parameters defined by this executable element to the given
+   * [parameters].
+   */
+  void set parameters(List<ParameterElement> parameters) {
+    for (ParameterElement parameter in parameters) {
+      (parameter as ParameterElementImpl).enclosingElement = this;
+    }
+    this._parameters = parameters;
+  }
+
+  @override
+  SourceRange get visibleRange {
+    if (_visibleRangeLength < 0) {
+      return null;
+    }
+    return new SourceRange(_visibleRangeOffset, _visibleRangeLength);
+  }
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitParameterElement(this);
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    String left = "";
+    String right = "";
+    while (true) {
+      if (parameterKind == ParameterKind.NAMED) {
+        left = "{";
+        right = "}";
+      } else if (parameterKind == ParameterKind.POSITIONAL) {
+        left = "[";
+        right = "]";
+      } else if (parameterKind == ParameterKind.REQUIRED) {}
+      break;
+    }
+    buffer.write(left);
+    appendToWithoutDelimiters(buffer);
+    buffer.write(right);
+  }
+
+  /**
+   * Append the type and name of this parameter to the given [buffer].
+   */
+  void appendToWithoutDelimiters(StringBuffer buffer) {
+    buffer.write(type);
+    buffer.write(" ");
+    buffer.write(displayName);
+    if (_defaultValueCode != null) {
+      if (parameterKind == ParameterKind.NAMED) {
+        buffer.write(": ");
+      }
+      if (parameterKind == ParameterKind.POSITIONAL) {
+        buffer.write(" = ");
+      }
+      buffer.write(_defaultValueCode);
+    }
+  }
+
+  @override
+  ElementImpl getChild(String identifier) {
+    for (ParameterElement parameter in _parameters) {
+      if ((parameter as ParameterElementImpl).identifier == identifier) {
+        return parameter as ParameterElementImpl;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Specifies that this variable is potentially mutated somewhere in closure.
+   */
+  void markPotentiallyMutatedInClosure() {
+    setModifier(Modifier.POTENTIALLY_MUTATED_IN_CONTEXT, true);
+  }
+
+  /**
+   * Specifies that this variable is potentially mutated somewhere in its scope.
+   */
+  void markPotentiallyMutatedInScope() {
+    setModifier(Modifier.POTENTIALLY_MUTATED_IN_SCOPE, true);
+  }
+
+  /**
+   * Set the visible range for this element to the range starting at the given
+   * [offset] with the given [length].
+   */
+  void setVisibleRange(int offset, int length) {
+    _visibleRangeOffset = offset;
+    _visibleRangeLength = length;
+  }
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    super.visitChildren(visitor);
+    safelyVisitChildren(_parameters, visitor);
+  }
+}
+
+/**
+ * A type with type parameters, such as a class or function type alias.
+ */
+abstract class ParameterizedType implements DartType {
+  /**
+   * Return a list containing the actual types of the type arguments. If this
+   * type's element does not have type parameters, then the array should be
+   * empty (although it is possible for type arguments to be erroneously
+   * declared). If the element has type parameters and the actual type does not
+   * explicitly include argument values, then the type "dynamic" will be
+   * automatically provided.
+   */
+  List<DartType> get typeArguments;
+
+  /**
+   * Return a list containing all of the type parameters declared for this type.
+   */
+  List<TypeParameterElement> get typeParameters;
+}
+
+/**
+ * A parameter element defined in a parameterized type where the values of the
+ * type parameters are known.
+ */
+class ParameterMember extends VariableMember implements ParameterElement {
+  /**
+   * Initialize a newly created element to represent a constructor, based on the
+   * [baseElement], defined by the [definingType].
+   */
+  ParameterMember(ParameterElement baseElement, ParameterizedType definingType)
+      : super(baseElement, definingType);
+
+  @override
+  ParameterElement get baseElement => super.baseElement as ParameterElement;
+
+  @override
+  String get defaultValueCode => baseElement.defaultValueCode;
+
+  @override
+  Element get enclosingElement => baseElement.enclosingElement;
+
+  @override
+  bool get isInitializingFormal => baseElement.isInitializingFormal;
+
+  @override
+  FormalParameter get node => baseElement.node;
+
+  @override
+  ParameterKind get parameterKind => baseElement.parameterKind;
+
+  @override
+  List<ParameterElement> get parameters {
+    List<ParameterElement> baseParameters = baseElement.parameters;
+    int parameterCount = baseParameters.length;
+    if (parameterCount == 0) {
+      return baseParameters;
+    }
+    List<ParameterElement> parameterizedParameters =
+        new List<ParameterElement>(parameterCount);
+    for (int i = 0; i < parameterCount; i++) {
+      parameterizedParameters[i] =
+          ParameterMember.from(baseParameters[i], definingType);
+    }
+    return parameterizedParameters;
+  }
+
+  @override
+  SourceRange get visibleRange => baseElement.visibleRange;
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitParameterElement(this);
+
+  @override
+  Element getAncestor(Predicate<Element> predicate) {
+    Element element = baseElement.getAncestor(predicate);
+    ParameterizedType definingType = this.definingType;
+    if (definingType is InterfaceType) {
+      InterfaceType definingInterfaceType = definingType;
+      if (element is ConstructorElement) {
+        return ConstructorMember.from(element, definingInterfaceType);
+      } else if (element is MethodElement) {
+        return MethodMember.from(element, definingInterfaceType);
+      } else if (element is PropertyAccessorElement) {
+        return PropertyAccessorMember.from(element, definingInterfaceType);
+      }
+    }
+    return element;
+  }
+
+  @override
+  String toString() {
+    ParameterElement baseElement = this.baseElement;
+    String left = "";
+    String right = "";
+    while (true) {
+      if (baseElement.parameterKind == ParameterKind.NAMED) {
+        left = "{";
+        right = "}";
+      } else if (baseElement.parameterKind == ParameterKind.POSITIONAL) {
+        left = "[";
+        right = "]";
+      } else if (baseElement.parameterKind == ParameterKind.REQUIRED) {}
+      break;
+    }
+    return '$left$type ${baseElement.displayName}$right';
+  }
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    super.visitChildren(visitor);
+    safelyVisitChildren(parameters, visitor);
+  }
+
+  /**
+   * If the given [parameter]'s type is different when any type parameters from
+   * the defining type's declaration are replaced with the actual type
+   * arguments from the [definingType], create a parameter member representing
+   * the given parameter. Return the member that was created, or the base
+   * parameter if no member was created.
+   */
+  static ParameterElement from(
+      ParameterElement parameter, ParameterizedType definingType) {
+    if (parameter == null || definingType.typeArguments.length == 0) {
+      return parameter;
+    }
+    // Check if parameter type depends on defining type type arguments.
+    // It is possible that we did not resolve field formal parameter yet,
+    // so skip this check for it.
+    bool isFieldFormal = parameter is FieldFormalParameterElement;
+    if (!isFieldFormal) {
+      DartType baseType = parameter.type;
+      List<DartType> argumentTypes = definingType.typeArguments;
+      List<DartType> parameterTypes =
+          TypeParameterTypeImpl.getTypes(definingType.typeParameters);
+      DartType substitutedType =
+          baseType.substitute2(argumentTypes, parameterTypes);
+      if (baseType == substitutedType) {
+        return parameter;
+      }
+    }
+    // TODO(brianwilkerson) Consider caching the substituted type in the
+    // instance. It would use more memory but speed up some operations.
+    // We need to see how often the type is being re-computed.
+    if (isFieldFormal) {
+      return new FieldFormalParameterMember(
+          parameter as FieldFormalParameterElement, definingType);
+    }
+    return new ParameterMember(parameter, definingType);
+  }
+}
+
+/**
+ * Interface used by elements that might represent constant variables.
+ *
+ * This class may be used as a mixin in the case where [constInitializer] is
+ * known to return null.
+ *
+ * This class is not intended to be part of the public API for analyzer.
+ */
+abstract class PotentiallyConstVariableElement {
+  /**
+   * If this element represents a constant variable, and it has an initializer,
+   * a copy of the initializer for the constant.  Otherwise `null`.
+   *
+   * Note that in correct Dart code, all constant variables must have
+   * initializers.  However, analyzer also needs to handle incorrect Dart code,
+   * in which case there might be some constant variables that lack
+   * initializers.
+   */
+  Expression get constantInitializer => null;
+}
+
+/**
+ * A prefix used to import one or more libraries into another library.
+ */
+abstract class PrefixElement implements Element {
+  /**
+   * An empty list of prefix elements.
+   */
+  static const List<PrefixElement> EMPTY_LIST = const <PrefixElement>[];
+
+  /**
+   * Return the library into which other libraries are imported using this
+   * prefix.
+   */
+  @override
+  LibraryElement get enclosingElement;
+
+  /**
+   * Return a list containing all of the libraries that are imported using this
+   * prefix.
+   */
+  List<LibraryElement> get importedLibraries;
+}
+
+/**
+ * A concrete implementation of a [PrefixElement].
+ */
+class PrefixElementImpl extends ElementImpl implements PrefixElement {
+  /**
+   * An empty list of prefix elements.
+   */
+  @deprecated // Use PrefixElement.EMPTY_LIST
+  static const List<PrefixElement> EMPTY_ARRAY = const <PrefixElement>[];
+
+  /**
+   * A list containing all of the libraries that are imported using this prefix.
+   */
+  List<LibraryElement> _importedLibraries = LibraryElement.EMPTY_LIST;
+
+  /**
+   * Initialize a newly created method element to have the given [name] and
+   * [offset].
+   */
+  PrefixElementImpl(String name, int nameOffset) : super(name, nameOffset);
+
+  /**
+   * Initialize a newly created prefix element to have the given [name].
+   */
+  PrefixElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  @override
+  LibraryElement get enclosingElement =>
+      super.enclosingElement as LibraryElement;
+
+  @override
+  String get identifier => "_${super.identifier}";
+
+  @override
+  List<LibraryElement> get importedLibraries => _importedLibraries;
+
+  /**
+   * Set the libraries that are imported using this prefix to the given
+   * [libraries].
+   */
+  void set importedLibraries(List<LibraryElement> libraries) {
+    for (LibraryElement library in libraries) {
+      (library as LibraryElementImpl).enclosingElement = this;
+    }
+    _importedLibraries = libraries;
+  }
+
+  @override
+  ElementKind get kind => ElementKind.PREFIX;
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitPrefixElement(this);
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    buffer.write("as ");
+    super.appendTo(buffer);
+  }
+}
+
+/**
+ * A getter or a setter. Note that explicitly defined property accessors
+ * implicitly define a synthetic field. Symmetrically, synthetic accessors are
+ * implicitly created for explicitly defined fields. The following rules apply:
+ *
+ * * Every explicit field is represented by a non-synthetic [FieldElement].
+ * * Every explicit field induces a getter and possibly a setter, both of which
+ *   are represented by synthetic [PropertyAccessorElement]s.
+ * * Every explicit getter or setter is represented by a non-synthetic
+ *   [PropertyAccessorElement].
+ * * Every explicit getter or setter (or pair thereof if they have the same
+ *   name) induces a field that is represented by a synthetic [FieldElement].
+ */
+abstract class PropertyAccessorElement implements ExecutableElement {
+  /**
+   * An empty list of property accessor elements.
+   */
+  static const List<PropertyAccessorElement> EMPTY_LIST =
+      const <PropertyAccessorElement>[];
+
+  /**
+   * Return the accessor representing the getter that corresponds to (has the
+   * same name as) this setter, or `null` if this accessor is not a setter or if
+   * there is no corresponding getter.
+   */
+  PropertyAccessorElement get correspondingGetter;
+
+  /**
+   * Return the accessor representing the setter that corresponds to (has the
+   * same name as) this getter, or `null` if this accessor is not a getter or if
+   * there is no corresponding setter.
+   */
+  PropertyAccessorElement get correspondingSetter;
+
+  /**
+   * Return `true` if this accessor represents a getter.
+   */
+  bool get isGetter;
+
+  /**
+   * Return `true` if this accessor represents a setter.
+   */
+  bool get isSetter;
+
+  /**
+   * Return the field or top-level variable associated with this accessor. If
+   * this accessor was explicitly defined (is not synthetic) then the variable
+   * associated with it will be synthetic.
+   */
+  PropertyInducingElement get variable;
+}
+
+/**
+ * A concrete implementation of a [PropertyAccessorElement].
+ */
+class PropertyAccessorElementImpl extends ExecutableElementImpl
+    implements PropertyAccessorElement {
+  /**
+   * An empty list of property accessor elements.
+   */
+  @deprecated // Use PropertyAccessorElement.EMPTY_LIST
+  static const List<PropertyAccessorElement> EMPTY_ARRAY =
+      const <PropertyAccessorElement>[];
+
+  /**
+   * The variable associated with this accessor.
+   */
+  PropertyInducingElement variable;
+
+  /**
+   * Initialize a newly created property accessor element to have the given
+   * [name].
+   */
+  PropertyAccessorElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  /**
+   * Initialize a newly created synthetic property accessor element to be
+   * associated with the given [variable].
+   */
+  PropertyAccessorElementImpl.forVariable(PropertyInducingElementImpl variable)
+      : super(variable.name, variable.nameOffset) {
+    this.variable = variable;
+    static = variable.isStatic;
+    synthetic = true;
+  }
+
+  /**
+   * Set whether this accessor is abstract.
+   */
+  void set abstract(bool isAbstract) {
+    setModifier(Modifier.ABSTRACT, isAbstract);
+  }
+
+  @override
+  PropertyAccessorElement get correspondingGetter {
+    if (isGetter || variable == null) {
+      return null;
+    }
+    return variable.getter;
+  }
+
+  @override
+  PropertyAccessorElement get correspondingSetter {
+    if (isSetter || variable == null) {
+      return null;
+    }
+    return variable.setter;
+  }
+
+  /**
+   * Set whether this accessor is a getter.
+   */
+  void set getter(bool isGetter) {
+    setModifier(Modifier.GETTER, isGetter);
+  }
+
+  @override
+  int get hashCode => JenkinsSmiHash.hash2(super.hashCode, isGetter ? 1 : 2);
+
+  @override
+  String get identifier {
+    String name = displayName;
+    String suffix = isGetter ? "?" : "=";
+    return "$name$suffix";
+  }
+
+  @override
+  bool get isGetter => hasModifier(Modifier.GETTER);
+
+  @override
+  bool get isSetter => hasModifier(Modifier.SETTER);
+
+  @override
+  bool get isStatic => hasModifier(Modifier.STATIC);
+
+  @override
+  ElementKind get kind {
+    if (isGetter) {
+      return ElementKind.GETTER;
+    }
+    return ElementKind.SETTER;
+  }
+
+  @override
+  String get name {
+    if (isSetter) {
+      return "${super.name}=";
+    }
+    return super.name;
+  }
+
+  @override
+  AstNode get node {
+    if (isSynthetic) {
+      return null;
+    }
+    if (enclosingElement is ClassElement) {
+      return getNodeMatching((node) => node is MethodDeclaration);
+    }
+    if (enclosingElement is CompilationUnitElement) {
+      return getNodeMatching((node) => node is FunctionDeclaration);
+    }
+    return null;
+  }
+
+  /**
+   * Set whether this accessor is a setter.
+   */
+  void set setter(bool isSetter) {
+    setModifier(Modifier.SETTER, isSetter);
+  }
+
+  /**
+   * Set whether this accessor is static.
+   */
+  void set static(bool isStatic) {
+    setModifier(Modifier.STATIC, isStatic);
+  }
+
+  @override
+  bool operator ==(Object object) => super == object &&
+      isGetter == (object as PropertyAccessorElement).isGetter;
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitPropertyAccessorElement(this);
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    buffer.write(isGetter ? "get " : "set ");
+    buffer.write(variable.displayName);
+    super.appendTo(buffer);
+  }
+}
+
+/**
+ * A property accessor element defined in a parameterized type where the values
+ * of the type parameters are known.
+ */
+class PropertyAccessorMember extends ExecutableMember
+    implements PropertyAccessorElement {
+  /**
+   * Initialize a newly created element to represent a constructor, based on the
+   * [baseElement], defined by the [definingType].
+   */
+  PropertyAccessorMember(
+      PropertyAccessorElement baseElement, InterfaceType definingType)
+      : super(baseElement, definingType);
+
+  @override
+  PropertyAccessorElement get baseElement =>
+      super.baseElement as PropertyAccessorElement;
+
+  @override
+  PropertyAccessorElement get correspondingGetter =>
+      from(baseElement.correspondingGetter, definingType);
+
+  @override
+  PropertyAccessorElement get correspondingSetter =>
+      from(baseElement.correspondingSetter, definingType);
+
+  @override
+  InterfaceType get definingType => super.definingType as InterfaceType;
+
+  @override
+  Element get enclosingElement => baseElement.enclosingElement;
+
+  @override
+  bool get isGetter => baseElement.isGetter;
+
+  @override
+  bool get isSetter => baseElement.isSetter;
+
+  @override
+  PropertyInducingElement get variable {
+    PropertyInducingElement variable = baseElement.variable;
+    if (variable is FieldElement) {
+      return FieldMember.from(variable, definingType);
+    }
+    return variable;
+  }
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitPropertyAccessorElement(this);
+
+  @override
+  String toString() {
+    PropertyAccessorElement baseElement = this.baseElement;
+    List<ParameterElement> parameters = this.parameters;
+    FunctionType type = this.type;
+    StringBuffer builder = new StringBuffer();
+    if (isGetter) {
+      builder.write("get ");
+    } else {
+      builder.write("set ");
+    }
+    builder.write(baseElement.enclosingElement.displayName);
+    builder.write(".");
+    builder.write(baseElement.displayName);
+    builder.write("(");
+    int parameterCount = parameters.length;
+    for (int i = 0; i < parameterCount; i++) {
+      if (i > 0) {
+        builder.write(", ");
+      }
+      builder.write(parameters[i]);
+    }
+    builder.write(")");
+    if (type != null) {
+      builder.write(Element.RIGHT_ARROW);
+      builder.write(type.returnType);
+    }
+    return builder.toString();
+  }
+
+  /**
+   * If the given [accessor]'s type is different when any type parameters from
+   * the defining type's declaration are replaced with the actual type
+   * arguments from the [definingType], create an accessor member representing
+   * the given accessor. Return the member that was created, or the base
+   * accessor if no member was created.
+   */
+  static PropertyAccessorElement from(
+      PropertyAccessorElement accessor, InterfaceType definingType) {
+    if (!_isChangedByTypeSubstitution(accessor, definingType)) {
+      return accessor;
+    }
+    // TODO(brianwilkerson) Consider caching the substituted type in the
+    // instance. It would use more memory but speed up some operations.
+    // We need to see how often the type is being re-computed.
+    return new PropertyAccessorMember(accessor, definingType);
+  }
+
+  /**
+   * Determine whether the given property [accessor]'s type is changed when type
+   * parameters from the defining type's declaration are replaced with the
+   * actual type arguments from the [definingType].
+   */
+  static bool _isChangedByTypeSubstitution(
+      PropertyAccessorElement accessor, InterfaceType definingType) {
+    List<DartType> argumentTypes = definingType.typeArguments;
+    if (accessor != null && argumentTypes.length != 0) {
+      FunctionType baseType = accessor.type;
+      if (baseType == null) {
+        AnalysisEngine.instance.logger.logInformation(
+            'Type of $accessor is null in PropertyAccessorMember._isChangedByTypeSubstitution');
+        return false;
+      }
+      List<DartType> parameterTypes = definingType.element.type.typeArguments;
+      FunctionType substitutedType =
+          baseType.substitute2(argumentTypes, parameterTypes);
+      if (baseType != substitutedType) {
+        return true;
+      }
+      // If this property accessor is based on a field, that field might have a
+      // propagated type. In which case we need to check whether the propagated
+      // type of the field needs substitution.
+      PropertyInducingElement field = accessor.variable;
+      if (!field.isSynthetic) {
+        DartType baseFieldType = field.propagatedType;
+        if (baseFieldType != null) {
+          DartType substitutedFieldType =
+              baseFieldType.substitute2(argumentTypes, parameterTypes);
+          if (baseFieldType != substitutedFieldType) {
+            return true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+}
+
+/**
+ * A variable that has an associated getter and possibly a setter. Note that
+ * explicitly defined variables implicitly define a synthetic getter and that
+ * non-`final` explicitly defined variables implicitly define a synthetic
+ * setter. Symmetrically, synthetic fields are implicitly created for explicitly
+ * defined getters and setters. The following rules apply:
+ *
+ * * Every explicit variable is represented by a non-synthetic
+ *   [PropertyInducingElement].
+ * * Every explicit variable induces a getter and possibly a setter, both of
+ *   which are represented by synthetic [PropertyAccessorElement]s.
+ * * Every explicit getter or setter is represented by a non-synthetic
+ *   [PropertyAccessorElement].
+ * * Every explicit getter or setter (or pair thereof if they have the same
+ *   name) induces a variable that is represented by a synthetic
+ *   [PropertyInducingElement].
+ */
+abstract class PropertyInducingElement implements VariableElement {
+  /**
+   * An empty list of elements.
+   */
+  static const List<PropertyInducingElement> EMPTY_LIST =
+      const <PropertyInducingElement>[];
+
+  /**
+   * Return the getter associated with this variable. If this variable was
+   * explicitly defined (is not synthetic) then the getter associated with it
+   * will be synthetic.
+   */
+  PropertyAccessorElement get getter;
+
+  /**
+   * Return `true` if this element is a static element. A static element is an
+   * element that is not associated with a particular instance, but rather with
+   * an entire library or class.
+   */
+  bool get isStatic;
+
+  /**
+   * Return the propagated type of this variable, or `null` if type propagation
+   * has not been performed, for example because the variable is not final.
+   */
+  DartType get propagatedType;
+
+  /**
+   * Return the setter associated with this variable, or `null` if the variable
+   * is effectively `final` and therefore does not have a setter associated with
+   * it. (This can happen either because the variable is explicitly defined as
+   * being `final` or because the variable is induced by an explicit getter that
+   * does not have a corresponding setter.) If this variable was explicitly
+   * defined (is not synthetic) then the setter associated with it will be
+   * synthetic.
+   */
+  PropertyAccessorElement get setter;
+}
+
+/**
+ * A concrete implementation of a [PropertyInducingElement].
+ */
+abstract class PropertyInducingElementImpl extends VariableElementImpl
+    implements PropertyInducingElement {
+  /**
+   * An empty list of elements.
+   */
+  @deprecated // Use PropertyInducingElement.EMPTY_LIST
+  static const List<PropertyInducingElement> EMPTY_ARRAY =
+      const <PropertyInducingElement>[];
+
+  /**
+   * The getter associated with this element.
+   */
+  PropertyAccessorElement getter;
+
+  /**
+   * The setter associated with this element, or `null` if the element is
+   * effectively `final` and therefore does not have a setter associated with
+   * it.
+   */
+  PropertyAccessorElement setter;
+
+  /**
+   * The propagated type of this variable, or `null` if type propagation has not
+   * been performed.
+   */
+  DartType propagatedType;
+
+  /**
+   * Initialize a newly created synthetic element to have the given [name] and
+   * [offset].
+   */
+  PropertyInducingElementImpl(String name, int offset) : super(name, offset);
+
+  /**
+   * Initialize a newly created element to have the given [name].
+   */
+  PropertyInducingElementImpl.forNode(Identifier name) : super.forNode(name);
+}
+
+/**
+ * A visitor that will recursively visit all of the element in an element model.
+ * For example, using an instance of this class to visit a
+ * [CompilationUnitElement] will also cause all of the types in the compilation
+ * unit to be visited.
+ *
+ * Subclasses that override a visit method must either invoke the overridden
+ * visit method or must explicitly ask the visited element to visit its
+ * children. Failure to do so will cause the children of the visited element to
+ * not be visited.
+ */
+class RecursiveElementVisitor<R> implements ElementVisitor<R> {
+  @override
+  R visitClassElement(ClassElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitCompilationUnitElement(CompilationUnitElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitConstructorElement(ConstructorElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitEmbeddedHtmlScriptElement(EmbeddedHtmlScriptElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitExportElement(ExportElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitExternalHtmlScriptElement(ExternalHtmlScriptElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitFieldElement(FieldElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitFieldFormalParameterElement(FieldFormalParameterElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitFunctionElement(FunctionElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitFunctionTypeAliasElement(FunctionTypeAliasElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitHtmlElement(HtmlElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitImportElement(ImportElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitLabelElement(LabelElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitLibraryElement(LibraryElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitLocalVariableElement(LocalVariableElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitMethodElement(MethodElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitMultiplyDefinedElement(MultiplyDefinedElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitParameterElement(ParameterElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitPrefixElement(PrefixElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitPropertyAccessorElement(PropertyAccessorElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitTopLevelVariableElement(TopLevelVariableElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitTypeParameterElement(TypeParameterElement element) {
+    element.visitChildren(this);
+    return null;
+  }
+}
+
+/**
+ * A combinator that cause some of the names in a namespace to be visible (and
+ * the rest hidden) when being imported.
+ */
+abstract class ShowElementCombinator implements NamespaceCombinator {
+  /**
+   * Return the offset of the character immediately following the last character
+   * of this node.
+   */
+  int get end;
+
+  /**
+   * Return the offset of the 'show' keyword of this element.
+   */
+  int get offset;
+
+  /**
+   * Return a list containing the names that are to be made visible in the
+   * importing library if they are defined in the imported library.
+   */
+  List<String> get shownNames;
+}
+
+/**
+ * A concrete implementation of a [ShowElementCombinator].
+ */
+class ShowElementCombinatorImpl implements ShowElementCombinator {
+  /**
+   * The names that are to be made visible in the importing library if they are
+   * defined in the imported library.
+   */
+  List<String> shownNames = StringUtilities.EMPTY_ARRAY;
+
+  /**
+   * The offset of the character immediately following the last character of
+   * this node.
+   */
+  int end = -1;
+
+  /**
+   * The offset of the 'show' keyword of this element.
+   */
+  int offset = 0;
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    buffer.write("show ");
+    int count = shownNames.length;
+    for (int i = 0; i < count; i++) {
+      if (i > 0) {
+        buffer.write(", ");
+      }
+      buffer.write(shownNames[i]);
+    }
+    return buffer.toString();
+  }
+}
+
+/**
+ * A visitor that will do nothing when visiting an element. It is intended to be
+ * a superclass for classes that use the visitor pattern primarily as a dispatch
+ * mechanism (and hence don't need to recursively visit a whole structure) and
+ * that only need to visit a small number of element types.
+ */
+class SimpleElementVisitor<R> implements ElementVisitor<R> {
+  @override
+  R visitClassElement(ClassElement element) => null;
+
+  @override
+  R visitCompilationUnitElement(CompilationUnitElement element) => null;
+
+  @override
+  R visitConstructorElement(ConstructorElement element) => null;
+
+  @override
+  R visitEmbeddedHtmlScriptElement(EmbeddedHtmlScriptElement element) => null;
+
+  @override
+  R visitExportElement(ExportElement element) => null;
+
+  @override
+  R visitExternalHtmlScriptElement(ExternalHtmlScriptElement element) => null;
+
+  @override
+  R visitFieldElement(FieldElement element) => null;
+
+  @override
+  R visitFieldFormalParameterElement(FieldFormalParameterElement element) =>
+      null;
+
+  @override
+  R visitFunctionElement(FunctionElement element) => null;
+
+  @override
+  R visitFunctionTypeAliasElement(FunctionTypeAliasElement element) => null;
+
+  @override
+  R visitHtmlElement(HtmlElement element) => null;
+
+  @override
+  R visitImportElement(ImportElement element) => null;
+
+  @override
+  R visitLabelElement(LabelElement element) => null;
+
+  @override
+  R visitLibraryElement(LibraryElement element) => null;
+
+  @override
+  R visitLocalVariableElement(LocalVariableElement element) => null;
+
+  @override
+  R visitMethodElement(MethodElement element) => null;
+
+  @override
+  R visitMultiplyDefinedElement(MultiplyDefinedElement element) => null;
+
+  @override
+  R visitParameterElement(ParameterElement element) => null;
+
+  @override
+  R visitPrefixElement(PrefixElement element) => null;
+
+  @override
+  R visitPropertyAccessorElement(PropertyAccessorElement element) => null;
+
+  @override
+  R visitTopLevelVariableElement(TopLevelVariableElement element) => null;
+
+  @override
+  R visitTypeParameterElement(TypeParameterElement element) => null;
+}
+
+/**
+ * A top-level variable.
+ */
+abstract class TopLevelVariableElement implements PropertyInducingElement {
+  /**
+   * An empty list of top-level variable elements.
+   */
+  static const List<TopLevelVariableElement> EMPTY_LIST =
+      const <TopLevelVariableElement>[];
+
+  @override
+  VariableDeclaration get node;
+}
+
+/**
+ * A concrete implementation of a [TopLevelVariableElement].
+ */
+class TopLevelVariableElementImpl extends PropertyInducingElementImpl
+    with PotentiallyConstVariableElement implements TopLevelVariableElement {
+  /**
+   * An empty list of top-level variable elements.
+   */
+  @deprecated // Use TopLevelVariableElement.EMPTY_LIST
+  static const List<TopLevelVariableElement> EMPTY_ARRAY =
+      const <TopLevelVariableElement>[];
+
+  /**
+   * Initialize a newly created synthetic top-level variable element to have the
+   * given [name] and [offset].
+   */
+  TopLevelVariableElementImpl(String name, int offset) : super(name, offset);
+
+  /**
+   * Initialize a newly created top-level variable element to have the given
+   * [name].
+   */
+  TopLevelVariableElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  @override
+  bool get isStatic => true;
+
+  @override
+  ElementKind get kind => ElementKind.TOP_LEVEL_VARIABLE;
+
+  @override
+  VariableDeclaration get node =>
+      getNodeMatching((node) => node is VariableDeclaration);
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitTopLevelVariableElement(this);
+}
+
+/**
+ * The abstract class `TypeImpl` implements the behavior common to objects
+ * representing the declared type of elements in the element model.
+ */
+abstract class TypeImpl implements DartType {
+  /**
+   * An empty list of types.
+   */
+  @deprecated // Use DartType.EMPTY_LIST
+  static const List<DartType> EMPTY_ARRAY = const <DartType>[];
+
+  /**
+   * The element representing the declaration of this type, or `null` if the
+   * type has not, or cannot, be associated with an element.
+   */
+  final Element _element;
+
+  /**
+   * The name of this type, or `null` if the type does not have a name.
+   */
+  final String name;
+
+  /**
+   * Initialize a newly created type to be declared by the given [element] and
+   * to have the given [name].
+   */
+  TypeImpl(this._element, this.name);
+
+  @override
+  String get displayName => name;
+
+  @override
+  Element get element => _element;
+
+  @override
+  bool get isBottom => false;
+
+  @override
+  bool get isDartCoreFunction => false;
+
+  @override
+  bool get isDynamic => false;
+
+  @override
+  bool get isObject => false;
+
+  @override
+  bool get isUndefined => false;
+
+  @override
+  bool get isVoid => false;
+
+  /**
+   * Append a textual representation of this type to the given [buffer]. The set
+   * of [visitedTypes] is used to prevent infinite recusion.
+   */
+  void appendTo(StringBuffer buffer, Set<DartType> visitedTypes) {
+    if (!visitedTypes.add(this)) {
+      buffer.write(name == null ? '...' : name);
+      return;
+    }
+    if (name == null) {
+      buffer.write("<unnamed type>");
+    } else {
+      buffer.write(name);
+    }
+  }
+
+  @override
+  DartType getLeastUpperBound(DartType type) => null;
+
+  bool internalEquals(Object object, Set<ElementPair> visitedElementPairs);
+
+  int internalHashCode(List<DartType> visitedTypes);
+
+  /**
+   * Return `true` if this type is assignable to the given [type] (written in
+   * the spec as "T <=> S", where T=[this] and S=[type]).
+   *
+   * The sets [thisExpansions] and [typeExpansions], if given, are the sets of
+   * function type aliases that have been expanded so far in the process of
+   * reaching [this] and [type], respectively.  These are used to avoid
+   * infinite regress when analyzing invalid code; since the language spec
+   * forbids a typedef from referring to itself directly or indirectly, we can
+   * use these as sets of function type aliases that don't need to be expanded.
+   */
+  @override
+  bool isAssignableTo(TypeImpl type,
+      [Set<Element> thisExpansions, Set<Element> typeExpansions]) {
+    // An interface type T may be assigned to a type S, written T <=> S, iff
+    // either T <: S or S <: T.
+    return isSubtypeOf(type, thisExpansions, typeExpansions) ||
+        type.isSubtypeOf(this, typeExpansions, thisExpansions);
+  }
+
+  /**
+   * Return `true` if this type is more specific than the given [type] (written
+   * in the spec as "T << S", where T=[this] and S=[type]).
+   *
+   * The sets [thisExpansions] and [typeExpansions], if given, are the sets of
+   * function type aliases that have been expanded so far in the process of
+   * reaching [this] and [type], respectively.  These are used to avoid
+   * infinite regress when analyzing invalid code; since the language spec
+   * forbids a typedef from referring to itself directly or indirectly, we can
+   * use these as sets of function type aliases that don't need to be expanded.
+   *
+   * If [withDynamic] is `true`, then "dynamic" should be considered as a
+   * subtype of any type (as though "dynamic" had been replaced with bottom).
+   *
+   * The set [visitedElements], if given, is the set of classes and type
+   * parameters that have been visited so far while examining the class
+   * hierarchy of [this].  This is used to avoid infinite regress when
+   * analyzing invalid code; since the language spec forbids loops in the class
+   * hierarchy, we can use this as a set of classes that don't need to be
+   * examined when walking the class hierarchy.
+   */
+  @override
+  bool isMoreSpecificThan(DartType type, [Set<Element> thisExpansions,
+      Set<Element> typeExpansions, bool withDynamic = false,
+      Set<Element> visitedElements]);
+
+  /**
+   * Return `true` if this type is a subtype of the given [type] (written in
+   * the spec as "T <: S", where T=[this] and S=[type]).
+   *
+   * The sets [thisExpansions] and [typeExpansions], if given, are the sets of
+   * function type aliases that have been expanded so far in the process of
+   * reaching [this] and [type], respectively.  These are used to avoid
+   * infinite regress when analyzing invalid code; since the language spec
+   * forbids a typedef from referring to itself directly or indirectly, we can
+   * use these as sets of function type aliases that don't need to be expanded.
+   */
+  @override
+  bool isSubtypeOf(DartType type,
+      [Set<Element> thisExpansions, Set<Element> typeExpansions]) {
+    // For non-function types, T <: S iff [_|_/dynamic]T << S.
+    return isMoreSpecificThan(type, thisExpansions, typeExpansions, true);
+  }
+
+  @override
+  bool isSupertypeOf(DartType type) => type.isSubtypeOf(this);
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    appendTo(buffer, new HashSet<DartType>());
+    return buffer.toString();
+  }
+
+  /**
+   * Return `true` if corresponding elements of the [first] and [second] lists
+   * of type arguments are all equal. Use the set of [visitedElementPairs] to
+   * prevent infinite loops when the types are recursively defined.
+   */
+  static bool equalArrays(List<DartType> first, List<DartType> second,
+      Set<ElementPair> visitedElementPairs) {
+    if (first.length != second.length) {
+      return false;
+    }
+    for (int i = 0; i < first.length; i++) {
+      if (first[i] == null) {
+        AnalysisEngine.instance.logger
+            .logInformation('Found null type argument in TypeImpl.equalArrays');
+        return second[i] == null;
+      } else if (second[i] == null) {
+        AnalysisEngine.instance.logger
+            .logInformation('Found null type argument in TypeImpl.equalArrays');
+        return false;
+      }
+      if (!(first[i] as TypeImpl).internalEquals(
+          second[i], visitedElementPairs)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Return a list containing the results of using the given [argumentTypes] and
+   * [parameterTypes] to perform a substitution on all of the given [types].
+   */
+  static List<DartType> substitute(List<DartType> types,
+      List<DartType> argumentTypes, List<DartType> parameterTypes) {
+    int length = types.length;
+    if (length == 0) {
+      return types;
+    }
+    List<DartType> newTypes = new List<DartType>(length);
+    for (int i = 0; i < length; i++) {
+      newTypes[i] = types[i].substitute2(argumentTypes, parameterTypes);
+    }
+    return newTypes;
+  }
+}
+
+/**
+ * A type parameter.
+ */
+abstract class TypeParameterElement implements Element {
+  /**
+   * An empty list of type parameter elements.
+   */
+  static const List<TypeParameterElement> EMPTY_LIST =
+      const <TypeParameterElement>[];
+
+  /**
+   * Return the type representing the bound associated with this parameter, or
+   * `null` if this parameter does not have an explicit bound.
+   */
+  DartType get bound;
+
+  /**
+   * Return the type defined by this type parameter.
+   */
+  TypeParameterType get type;
+}
+
+/**
+ * A concrete implementation of a [TypeParameterElement].
+ */
+class TypeParameterElementImpl extends ElementImpl
+    implements TypeParameterElement {
+  /**
+   * An empty list of type parameter elements.
+   */
+  @deprecated // Use TypeParameterElement.EMPTY_LIST
+  static const List<TypeParameterElement> EMPTY_ARRAY =
+      const <TypeParameterElement>[];
+
+  /**
+   * The type defined by this type parameter.
+   */
+  TypeParameterType type;
+
+  /**
+   * The type representing the bound associated with this parameter, or `null`
+   * if this parameter does not have an explicit bound.
+   */
+  DartType bound;
+
+  /**
+   * Initialize a newly created method element to have the given [name] and
+   * [offset].
+   */
+  TypeParameterElementImpl(String name, int offset) : super(name, offset);
+
+  /**
+   * Initialize a newly created type parameter element to have the given [name].
+   */
+  TypeParameterElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  @override
+  ElementKind get kind => ElementKind.TYPE_PARAMETER;
+
+  @override
+  accept(ElementVisitor visitor) => visitor.visitTypeParameterElement(this);
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    buffer.write(displayName);
+    if (bound != null) {
+      buffer.write(" extends ");
+      buffer.write(bound);
+    }
+  }
+}
+
+/**
+ * The type introduced by a type parameter.
+ */
+abstract class TypeParameterType implements DartType {
+  /**
+   * An empty list of type parameter types.
+   */
+  static const List<TypeParameterType> EMPTY_LIST = const <TypeParameterType>[];
+
+  @override
+  TypeParameterElement get element;
+}
+
+/**
+ * A concrete implementation of a [TypeParameterType].
+ */
+class TypeParameterTypeImpl extends TypeImpl implements TypeParameterType {
+  /**
+   * An empty list of type parameter types.
+   */
+  @deprecated // Use TypeParameterType.EMPTY_LIST
+  static const List<TypeParameterType> EMPTY_ARRAY =
+      const <TypeParameterType>[];
+
+  /**
+   * Initialize a newly created type parameter type to be declared by the given
+   * [element] and to have the given name.
+   */
+  TypeParameterTypeImpl(TypeParameterElement element)
+      : super(element, element.name);
+
+  @override
+  TypeParameterElement get element => super.element as TypeParameterElement;
+
+  @override
+  int get hashCode => element.hashCode;
+
+  @override
+  bool operator ==(Object object) =>
+      object is TypeParameterTypeImpl && (element == object.element);
+
+  @override
+  bool internalEquals(Object object, Set<ElementPair> visitedElementPairs) =>
+      this == object;
+
+  @override
+  int internalHashCode(List<DartType> visitedTypes) => hashCode;
+
+  @override
+  bool isMoreSpecificThan(DartType s, [Set<Element> thisExpansions,
+      Set<Element> typeExpansions, bool withDynamic = false,
+      Set<Element> visitedElements]) {
+    //
+    // A type T is more specific than a type S, written T << S,
+    // if one of the following conditions is met:
+    //
+    // Reflexivity: T is S.
+    //
+    if (this == s) {
+      return true;
+    }
+    // S is dynamic.
+    //
+    if (s.isDynamic) {
+      return true;
+    }
+    //
+    // T is a type parameter and S is the upper bound of T.
+    //
+    TypeImpl bound = element.bound;
+    if (s == bound) {
+      return true;
+    }
+    //
+    // T is a type parameter and S is Object.
+    //
+    if (s.isObject) {
+      return true;
+    }
+    // We need upper bound to continue.
+    if (bound == null) {
+      return false;
+    }
+    //
+    // Transitivity: T << U and U << S.
+    //
+    // First check for infinite loops
+    if (element == null) {
+      return false;
+    }
+    if (visitedElements == null) {
+      visitedElements = new HashSet<Element>();
+    } else if (visitedElements.contains(element)) {
+      return false;
+    }
+    visitedElements.add(element);
+    try {
+      return bound.isMoreSpecificThan(
+          s, thisExpansions, typeExpansions, withDynamic, visitedElements);
+    } finally {
+      visitedElements.remove(element);
+    }
+  }
+
+  @override
+  bool isSubtypeOf(DartType type,
+          [Set<Element> thisExpansions, Set<Element> typeExpansions]) =>
+      isMoreSpecificThan(type, thisExpansions, typeExpansions, true);
+
+  @override
+  DartType substitute2(
+      List<DartType> argumentTypes, List<DartType> parameterTypes) {
+    int length = parameterTypes.length;
+    for (int i = 0; i < length; i++) {
+      if (parameterTypes[i] == this) {
+        return argumentTypes[i];
+      }
+    }
+    return this;
+  }
+
+  /**
+   * Return a list containing the type parameter types defined by the given
+   * array of type parameter elements ([typeParameters]).
+   */
+  static List<TypeParameterType> getTypes(
+      List<TypeParameterElement> typeParameters) {
+    int count = typeParameters.length;
+    if (count == 0) {
+      return TypeParameterType.EMPTY_LIST;
+    }
+    List<TypeParameterType> types = new List<TypeParameterType>(count);
+    for (int i = 0; i < count; i++) {
+      types[i] = typeParameters[i].type;
+    }
+    return types;
+  }
+}
+
+/**
+ * A pseudo-elements that represents names that are undefined. This situation is
+ * not allowed by the language, so objects implementing this interface always
+ * represent an error. As a result, most of the normal operations on elements do
+ * not make sense and will return useless results.
+ */
+abstract class UndefinedElement implements Element {}
+
+/**
+ * The unique instance of the class `UndefinedTypeImpl` implements the type of
+ * typenames that couldn't be resolved.
+ *
+ * This class behaves like DynamicTypeImpl in almost every respect, to reduce
+ * cascading errors.
+ */
+class UndefinedTypeImpl extends TypeImpl {
+  /**
+   * The unique instance of this class.
+   */
+  static UndefinedTypeImpl _INSTANCE = new UndefinedTypeImpl._();
+
+  /**
+   * Return the unique instance of this class.
+   */
+  static UndefinedTypeImpl get instance => _INSTANCE;
+
+  /**
+   * Prevent the creation of instances of this class.
+   */
+  UndefinedTypeImpl._()
+      : super(DynamicElementImpl.instance, Keyword.DYNAMIC.syntax);
+
+  @override
+  int get hashCode => 1;
+
+  @override
+  bool get isDynamic => true;
+
+  @override
+  bool get isUndefined => true;
+
+  @override
+  bool operator ==(Object object) => identical(object, this);
+
+  @override
+  bool internalEquals(Object object, Set<ElementPair> visitedElementPairs) =>
+      identical(object, this);
+
+  @override
+  int internalHashCode(List<DartType> visitedTypes) => hashCode;
+
+  @override
+  bool isMoreSpecificThan(DartType type, [Set<Element> thisExpansions,
+      Set<Element> typeExpansions, bool withDynamic = false,
+      Set<Element> visitedElements]) {
+    // T is S
+    if (identical(this, type)) {
+      return true;
+    }
+    // else
+    return withDynamic;
+  }
+
+  @override
+  bool isSubtypeOf(DartType type,
+      [Set<Element> thisExpansions, Set<Element> typeExpansions]) => true;
+
+  @override
+  bool isSupertypeOf(DartType type) => true;
+
+  @override
+  DartType substitute2(
+      List<DartType> argumentTypes, List<DartType> parameterTypes) {
+    int length = parameterTypes.length;
+    for (int i = 0; i < length; i++) {
+      if (parameterTypes[i] == this) {
+        return argumentTypes[i];
+      }
+    }
+    return this;
+  }
+}
+
+/**
+ * An element included into a library using some URI.
+ */
+abstract class UriReferencedElement implements Element {
+  /**
+   * Return the URI that is used to include this element into the enclosing
+   * library, or `null` if this is the defining compilation unit of a library.
+   */
+  String get uri;
+
+  /**
+   * Return the offset of the character immediately following the last character
+   * of this node's URI, or `-1` for synthetic import.
+   */
+  int get uriEnd;
+
+  /**
+   * Return the offset of the URI in the file, or `-1` if this element is
+   * synthetic.
+   */
+  int get uriOffset;
+}
+
+/**
+ * A concrete implementation of a [UriReferencedElement].
+ */
+abstract class UriReferencedElementImpl extends ElementImpl
+    implements UriReferencedElement {
+  /**
+   * The offset of the URI in the file, may be `-1` if synthetic.
+   */
+  int uriOffset = -1;
+
+  /**
+   * The offset of the character immediately following the last character of
+   * this node's URI, may be `-1` if synthetic.
+   */
+  int uriEnd = -1;
+
+  /**
+   * The URI that is specified by this directive.
+   */
+  String uri;
+
+  /**
+   * Initialize a newly created import element to heve the given [name] and
+   * [offset]. The offset may be `-1` if the element is synthetic.
+   */
+  UriReferencedElementImpl(String name, int offset) : super(name, offset);
+}
+
+/**
+ * A variable. There are concrete subclasses for different kinds of variables.
+ */
+abstract class VariableElement implements Element {
+  /**
+   * An empty list of variable elements.
+   */
+  static const List<VariableElement> EMPTY_LIST = const <VariableElement>[];
+
+  /**
+   * Return a synthetic function representing this variable's initializer, or
+   * `null` if this variable does not have an initializer. The function will
+   * have no parameters. The return type of the function will be the
+   * compile-time type of the initialization expression.
+   */
+  FunctionElement get initializer;
+
+  /**
+   * Return `true` if this variable was declared with the 'const' modifier.
+   */
+  bool get isConst;
+
+  /**
+   * Return `true` if this variable was declared with the 'final' modifier.
+   * Variables that are declared with the 'const' modifier will return `false`
+   * even though they are implicitly final.
+   */
+  bool get isFinal;
+
+  /**
+   * Return `true` if this variable is potentially mutated somewhere in a
+   * closure. This information is only available for local variables (including
+   * parameters) and only after the compilation unit containing the variable has
+   * been resolved.
+   */
+  bool get isPotentiallyMutatedInClosure;
+
+  /**
+   * Return `true` if this variable is potentially mutated somewhere in its
+   * scope. This information is only available for local variables (including
+   * parameters) and only after the compilation unit containing the variable has
+   * been resolved.
+   */
+  bool get isPotentiallyMutatedInScope;
+
+  /**
+   * Return the declared type of this variable, or `null` if the variable did
+   * not have a declared type (such as if it was declared using the keyword
+   * 'var').
+   */
+  DartType get type;
+}
+
+/**
+ * A concrete implementation of a [VariableElement].
+ */
+abstract class VariableElementImpl extends ElementImpl
+    implements VariableElement {
+  /**
+   * An empty list of variable elements.
+   */
+  @deprecated // Use VariableElement.EMPTY_LIST
+  static const List<VariableElement> EMPTY_ARRAY = const <VariableElement>[];
+
+  /**
+   * The declared type of this variable.
+   */
+  DartType type;
+
+  /**
+   * A synthetic function representing this variable's initializer, or `null` if
+   * this variable does not have an initializer.
+   */
+  FunctionElement _initializer;
+
+  /**
+   * Initialize a newly created variable element to have the given [name] and
+   * [offset].
+   */
+  VariableElementImpl(String name, int offset) : super(name, offset);
+
+  /**
+   * Initialize a newly created variable element to have the given [name].
+   */
+  VariableElementImpl.forNode(Identifier name) : super.forNode(name);
+
+  /**
+   * Set whether this variable is const.
+   */
+  void set const3(bool isConst) {
+    setModifier(Modifier.CONST, isConst);
+  }
+
+  /**
+   * Return the result of evaluating this variable's initializer as a
+   * compile-time constant expression, or `null` if this variable is not a
+   * 'const' variable, if it does not have an initializer, or if the compilation
+   * unit containing the variable has not been resolved.
+   */
+  EvaluationResultImpl get evaluationResult => null;
+
+  /**
+   * Set the result of evaluating this variable's initializer as a compile-time
+   * constant expression to the given [result].
+   */
+  void set evaluationResult(EvaluationResultImpl result) {
+    throw new IllegalStateException(
+        "Invalid attempt to set a compile-time constant result");
+  }
+
+  /**
+   * Set whether this variable is final.
+   */
+  void set final2(bool isFinal) {
+    setModifier(Modifier.FINAL, isFinal);
+  }
+
+  @override
+  FunctionElement get initializer => _initializer;
+
+  /**
+   * Set the function representing this variable's initializer to the given
+   * [function].
+   */
+  void set initializer(FunctionElement function) {
+    if (function != null) {
+      (function as FunctionElementImpl).enclosingElement = this;
+    }
+    this._initializer = function;
+  }
+
+  @override
+  bool get isConst => hasModifier(Modifier.CONST);
+
+  @override
+  bool get isFinal => hasModifier(Modifier.FINAL);
+
+  @override
+  bool get isPotentiallyMutatedInClosure => false;
+
+  @override
+  bool get isPotentiallyMutatedInScope => false;
+
+  @override
+  void appendTo(StringBuffer buffer) {
+    buffer.write(type);
+    buffer.write(" ");
+    buffer.write(displayName);
+  }
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    super.visitChildren(visitor);
+    safelyVisitChild(_initializer, visitor);
+  }
+}
+
+/**
+ * A variable element defined in a parameterized type where the values of the
+ * type parameters are known.
+ */
+abstract class VariableMember extends Member implements VariableElement {
+  /**
+   * Initialize a newly created element to represent a constructor, based on the
+   * [baseElement], defined by the [definingType].
+   */
+  VariableMember(VariableElement baseElement, ParameterizedType definingType)
+      : super(baseElement, definingType);
+
+  @override
+  VariableElement get baseElement => super.baseElement as VariableElement;
+
+  @override
+  FunctionElement get initializer {
+    //
+    // Elements within this element should have type parameters substituted,
+    // just like this element.
+    //
+    throw new UnsupportedOperationException();
+    //    return getBaseElement().getInitializer();
+  }
+
+  @override
+  bool get isConst => baseElement.isConst;
+
+  @override
+  bool get isFinal => baseElement.isFinal;
+
+  @override
+  bool get isPotentiallyMutatedInClosure =>
+      baseElement.isPotentiallyMutatedInClosure;
+
+  @override
+  bool get isPotentiallyMutatedInScope =>
+      baseElement.isPotentiallyMutatedInScope;
+
+  @override
+  DartType get type => substituteFor(baseElement.type);
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    // TODO(brianwilkerson) We need to finish implementing the accessors used
+    // below so that we can safely invoke them.
+    super.visitChildren(visitor);
+    safelyVisitChild(baseElement.initializer, visitor);
+  }
+}
+
+/**
+ * The type `void`.
+ */
+abstract class VoidType implements DartType {
+  @override
+  VoidType substitute2(
+      List<DartType> argumentTypes, List<DartType> parameterTypes);
+}
+
+/**
+ * A concrete implementation of a [VoidType].
+ */
+class VoidTypeImpl extends TypeImpl implements VoidType {
+  /**
+   * The unique instance of this class.
+   */
+  static VoidTypeImpl _INSTANCE = new VoidTypeImpl();
+
+  /**
+   * Return the unique instance of this class.
+   */
+  static VoidTypeImpl get instance => _INSTANCE;
+
+  /**
+   * Prevent the creation of instances of this class.
+   */
+  VoidTypeImpl() : super(null, Keyword.VOID.syntax);
+
+  @override
+  int get hashCode => 2;
+
+  @override
+  bool get isVoid => true;
+
+  @override
+  bool operator ==(Object object) => identical(object, this);
+
+  @override
+  bool internalEquals(Object object, Set<ElementPair> visitedElementPairs) =>
+      identical(object, this);
+
+  @override
+  int internalHashCode(List<DartType> visitedTypes) => hashCode;
+
+  @override
+  bool isMoreSpecificThan(DartType type, [Set<Element> thisExpansions,
+      Set<Element> typeExpansions, bool withDynamic = false,
+      Set<Element> visitedElements]) => isSubtypeOf(type);
+
+  @override
+  bool isSubtypeOf(DartType type,
+      [Set<Element> thisExpansions, Set<Element> typeExpansions]) {
+    // The only subtype relations that pertain to void are therefore:
+    // void <: void (by reflexivity)
+    // bottom <: void (as bottom is a subtype of all types).
+    // void <: dynamic (as dynamic is a supertype of all types)
+    return identical(type, this) || type.isDynamic;
+  }
+
+  @override
+  VoidTypeImpl substitute2(
+      List<DartType> argumentTypes, List<DartType> parameterTypes) => this;
+}
+
+/**
+ * A visitor that visit all the elements recursively and fill the given [map].
+ */
+class _BuildOffsetToElementMap extends GeneralizingElementVisitor {
+  final Map<int, Element> map;
+
+  _BuildOffsetToElementMap(this.map);
+
+  @override
+  void visitElement(Element element) {
+    int offset = element.nameOffset;
+    if (offset != -1) {
+      map[offset] = element;
+    }
+    super.visitElement(element);
+  }
+}
diff --git a/analyzer/lib/src/generated/element_handle.dart b/analyzer/lib/src/generated/element_handle.dart
new file mode 100644
index 0000000..07c7f3a
--- /dev/null
+++ b/analyzer/lib/src/generated/element_handle.dart
@@ -0,0 +1,1103 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.element_handle;
+
+import 'ast.dart';
+import 'element.dart';
+import 'engine.dart';
+import 'java_core.dart';
+import 'java_engine.dart';
+import 'resolver.dart';
+import 'source.dart';
+import 'utilities_dart.dart';
+
+/**
+ * Instances of the class `ClassElementHandle` implement a handle to a `ClassElement`.
+ */
+class ClassElementHandle extends ElementHandle implements ClassElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  ClassElementHandle(ClassElement element) : super(element);
+
+  @override
+  List<PropertyAccessorElement> get accessors => actualElement.accessors;
+
+  @override
+  ClassElement get actualElement => super.actualElement as ClassElement;
+
+  @override
+  List<InterfaceType> get allSupertypes => actualElement.allSupertypes;
+
+  @override
+  List<ConstructorElement> get constructors => actualElement.constructors;
+
+  @override
+  List<FieldElement> get fields => actualElement.fields;
+
+  @override
+  bool get hasNonFinalField => actualElement.hasNonFinalField;
+
+  @override
+  bool get hasReferenceToSuper => actualElement.hasReferenceToSuper;
+
+  @override
+  bool get hasStaticMember => actualElement.hasStaticMember;
+
+  @override
+  List<InterfaceType> get interfaces => actualElement.interfaces;
+
+  @override
+  bool get isAbstract => actualElement.isAbstract;
+
+  @override
+  bool get isEnum => actualElement.isEnum;
+
+  @override
+  bool get isOrInheritsProxy => actualElement.isOrInheritsProxy;
+
+  @override
+  bool get isProxy => actualElement.isProxy;
+
+  @override
+  bool get isTypedef => actualElement.isTypedef;
+
+  @override
+  bool get isValidMixin => actualElement.isValidMixin;
+
+  @override
+  ElementKind get kind => ElementKind.CLASS;
+
+  @override
+  List<MethodElement> get methods => actualElement.methods;
+
+  @override
+  List<InterfaceType> get mixins => actualElement.mixins;
+
+  @override
+  InterfaceType get supertype => actualElement.supertype;
+
+  @override
+  InterfaceType get type => actualElement.type;
+
+  @override
+  List<TypeParameterElement> get typeParameters => actualElement.typeParameters;
+
+  @override
+  ConstructorElement get unnamedConstructor => actualElement.unnamedConstructor;
+
+  @override
+  FieldElement getField(String fieldName) => actualElement.getField(fieldName);
+
+  @override
+  PropertyAccessorElement getGetter(String getterName) =>
+      actualElement.getGetter(getterName);
+
+  @override
+  MethodElement getMethod(String methodName) =>
+      actualElement.getMethod(methodName);
+
+  @override
+  ConstructorElement getNamedConstructor(String name) =>
+      actualElement.getNamedConstructor(name);
+
+  @override
+  PropertyAccessorElement getSetter(String setterName) =>
+      actualElement.getSetter(setterName);
+
+  @override
+  bool isSuperConstructorAccessible(ConstructorElement constructor) =>
+      actualElement.isSuperConstructorAccessible(constructor);
+
+  @override
+  MethodElement lookUpConcreteMethod(
+          String methodName, LibraryElement library) =>
+      actualElement.lookUpConcreteMethod(methodName, library);
+
+  @override
+  PropertyAccessorElement lookUpGetter(
+          String getterName, LibraryElement library) =>
+      actualElement.lookUpGetter(getterName, library);
+
+  @override
+  PropertyAccessorElement lookUpInheritedConcreteGetter(
+          String methodName, LibraryElement library) =>
+      actualElement.lookUpInheritedConcreteGetter(methodName, library);
+
+  @override
+  MethodElement lookUpInheritedConcreteMethod(
+          String methodName, LibraryElement library) =>
+      actualElement.lookUpInheritedConcreteMethod(methodName, library);
+
+  @override
+  PropertyAccessorElement lookUpInheritedConcreteSetter(
+          String methodName, LibraryElement library) =>
+      actualElement.lookUpInheritedConcreteSetter(methodName, library);
+
+  @override
+  MethodElement lookUpInheritedMethod(
+          String methodName, LibraryElement library) =>
+      actualElement.lookUpInheritedMethod(methodName, library);
+
+  @override
+  MethodElement lookUpMethod(String methodName, LibraryElement library) =>
+      actualElement.lookUpMethod(methodName, library);
+
+  @override
+  PropertyAccessorElement lookUpSetter(
+          String setterName, LibraryElement library) =>
+      actualElement.lookUpSetter(setterName, library);
+}
+
+/**
+ * Instances of the class `CompilationUnitElementHandle` implements a handle to a
+ * [CompilationUnitElement].
+ */
+class CompilationUnitElementHandle extends ElementHandle
+    implements CompilationUnitElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  CompilationUnitElementHandle(CompilationUnitElement element) : super(element);
+
+  @override
+  List<PropertyAccessorElement> get accessors => actualElement.accessors;
+
+  @override
+  CompilationUnitElement get actualElement =>
+      super.actualElement as CompilationUnitElement;
+
+  @override
+  LibraryElement get enclosingElement =>
+      super.enclosingElement as LibraryElement;
+
+  @override
+  List<ClassElement> get enums => actualElement.enums;
+
+  @override
+  List<FunctionElement> get functions => actualElement.functions;
+
+  @override
+  List<FunctionTypeAliasElement> get functionTypeAliases =>
+      actualElement.functionTypeAliases;
+
+  @override
+  bool get hasLoadLibraryFunction => actualElement.hasLoadLibraryFunction;
+
+  @override
+  ElementKind get kind => ElementKind.COMPILATION_UNIT;
+
+  @override
+  CompilationUnit get node => actualElement.node;
+
+  @override
+  Source get source => actualElement.source;
+
+  @override
+  List<TopLevelVariableElement> get topLevelVariables =>
+      actualElement.topLevelVariables;
+
+  @override
+  List<ClassElement> get types => actualElement.types;
+
+  @override
+  String get uri => actualElement.uri;
+
+  @override
+  int get uriEnd => actualElement.uriEnd;
+
+  @override
+  int get uriOffset => actualElement.uriOffset;
+
+  @override
+  Element getElementAt(int offset) {
+    return actualElement.getElementAt(offset);
+  }
+
+  @override
+  ClassElement getEnum(String enumName) => actualElement.getEnum(enumName);
+
+  @override
+  ClassElement getType(String className) => actualElement.getType(className);
+}
+
+/**
+ * Instances of the class `ConstructorElementHandle` implement a handle to a
+ * `ConstructorElement`.
+ */
+class ConstructorElementHandle extends ExecutableElementHandle
+    implements ConstructorElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  ConstructorElementHandle(ConstructorElement element) : super(element);
+
+  @override
+  ConstructorElement get actualElement =>
+      super.actualElement as ConstructorElement;
+
+  @override
+  ClassElement get enclosingElement => actualElement.enclosingElement;
+
+  @override
+  bool get isConst => actualElement.isConst;
+
+  @override
+  bool get isDefaultConstructor => actualElement.isDefaultConstructor;
+
+  @override
+  bool get isFactory => actualElement.isFactory;
+
+  @override
+  ElementKind get kind => ElementKind.CONSTRUCTOR;
+
+  @override
+  int get nameEnd => actualElement.nameEnd;
+
+  @override
+  ConstructorDeclaration get node => actualElement.node;
+
+  @override
+  int get periodOffset => actualElement.periodOffset;
+
+  @override
+  ConstructorElement get redirectedConstructor =>
+      actualElement.redirectedConstructor;
+}
+
+/**
+ * The abstract class `ElementHandle` implements the behavior common to objects that implement
+ * a handle to an [Element].
+ */
+abstract class ElementHandle implements Element {
+  /**
+   * The unique integer identifier of this element.
+   */
+  final int id = 0;
+
+  /**
+   * The context in which the element is defined.
+   */
+  AnalysisContext _context;
+
+  /**
+   * The location of this element, used to reconstitute the element if it has been garbage
+   * collected.
+   */
+  ElementLocation _location;
+
+  /**
+   * A reference to the element being referenced by this handle, or `null` if the element has
+   * been garbage collected.
+   */
+  WeakReference<Element> _elementReference;
+
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  ElementHandle(Element element) {
+    _context = element.context;
+    _location = element.location;
+    _elementReference = new WeakReference<Element>(element);
+  }
+
+  /**
+   * Return the element being represented by this handle, reconstituting the element if the
+   * reference has been set to `null`.
+   *
+   * @return the element being represented by this handle
+   */
+  Element get actualElement {
+    Element element = _elementReference.get();
+    if (element == null) {
+      element = _context.getElement(_location);
+      _elementReference = new WeakReference<Element>(element);
+    }
+    return element;
+  }
+
+  @override
+  AnalysisContext get context => _context;
+
+  @override
+  String get displayName => actualElement.displayName;
+
+  @override
+  Element get enclosingElement => actualElement.enclosingElement;
+
+  @override
+  int get hashCode => _location.hashCode;
+
+  @override
+  bool get isDeprecated => actualElement.isDeprecated;
+
+  @override
+  bool get isOverride => actualElement.isOverride;
+
+  @override
+  bool get isPrivate => actualElement.isPrivate;
+
+  @override
+  bool get isPublic => actualElement.isPublic;
+
+  @override
+  bool get isSynthetic => actualElement.isSynthetic;
+
+  @override
+  LibraryElement get library =>
+      getAncestor((element) => element is LibraryElement);
+
+  @override
+  ElementLocation get location => _location;
+
+  @override
+  List<ElementAnnotation> get metadata => actualElement.metadata;
+
+  @override
+  String get name => actualElement.name;
+
+  @override
+  int get nameOffset => actualElement.nameOffset;
+
+  @override
+  AstNode get node => actualElement.node;
+
+  @override
+  Source get source => actualElement.source;
+
+  @override
+  CompilationUnit get unit => actualElement.unit;
+
+  @override
+  bool operator ==(Object object) =>
+      object is Element && object.location == _location;
+
+  @override
+  accept(ElementVisitor visitor) => actualElement.accept(visitor);
+
+  @override
+  String computeDocumentationComment() =>
+      actualElement.computeDocumentationComment();
+
+  @override
+  Element getAncestor(Predicate<Element> predicate) =>
+      actualElement.getAncestor(predicate);
+
+  @override
+  String getExtendedDisplayName(String shortName) =>
+      actualElement.getExtendedDisplayName(shortName);
+
+  @override
+  bool isAccessibleIn(LibraryElement library) =>
+      actualElement.isAccessibleIn(library);
+
+  @override
+  void visitChildren(ElementVisitor visitor) {
+    actualElement.visitChildren(visitor);
+  }
+
+  /**
+   * Return a handle on the given element. If the element is already a handle, then it will be
+   * returned directly, otherwise a handle of the appropriate class will be constructed.
+   *
+   * @param element the element for which a handle is to be constructed
+   * @return a handle on the given element
+   */
+  static Element forElement(Element element) {
+    if (element is ElementHandle) {
+      return element;
+    }
+    while (true) {
+      if (element.kind == ElementKind.CLASS) {
+        return new ClassElementHandle(element as ClassElement);
+      } else if (element.kind == ElementKind.COMPILATION_UNIT) {
+        return new CompilationUnitElementHandle(
+            element as CompilationUnitElement);
+      } else if (element.kind == ElementKind.CONSTRUCTOR) {
+        return new ConstructorElementHandle(element as ConstructorElement);
+      } else if (element.kind == ElementKind.EXPORT) {
+        return new ExportElementHandle(element as ExportElement);
+      } else if (element.kind == ElementKind.FIELD) {
+        return new FieldElementHandle(element as FieldElement);
+      } else if (element.kind == ElementKind.FUNCTION) {
+        return new FunctionElementHandle(element as FunctionElement);
+      } else if (element.kind == ElementKind.GETTER) {
+        return new PropertyAccessorElementHandle(
+            element as PropertyAccessorElement);
+      } else if (element.kind == ElementKind.IMPORT) {
+        return new ImportElementHandle(element as ImportElement);
+      } else if (element.kind == ElementKind.LABEL) {
+        return new LabelElementHandle(element as LabelElement);
+      } else if (element.kind == ElementKind.LIBRARY) {
+        return new LibraryElementHandle(element as LibraryElement);
+      } else if (element.kind == ElementKind.LOCAL_VARIABLE) {
+        return new LocalVariableElementHandle(element as LocalVariableElement);
+      } else if (element.kind == ElementKind.METHOD) {
+        return new MethodElementHandle(element as MethodElement);
+      } else if (element.kind == ElementKind.PARAMETER) {
+        return new ParameterElementHandle(element as ParameterElement);
+      } else if (element.kind == ElementKind.PREFIX) {
+        return new PrefixElementHandle(element as PrefixElement);
+      } else if (element.kind == ElementKind.SETTER) {
+        return new PropertyAccessorElementHandle(
+            element as PropertyAccessorElement);
+      } else if (element.kind == ElementKind.TOP_LEVEL_VARIABLE) {
+        return new TopLevelVariableElementHandle(
+            element as TopLevelVariableElement);
+      } else if (element.kind == ElementKind.FUNCTION_TYPE_ALIAS) {
+        return new FunctionTypeAliasElementHandle(
+            element as FunctionTypeAliasElement);
+      } else if (element.kind == ElementKind.TYPE_PARAMETER) {
+        return new TypeParameterElementHandle(element as TypeParameterElement);
+      } else {
+        throw new UnsupportedOperationException();
+      }
+      break;
+    }
+  }
+
+  /**
+   * Return an array of the same size as the given array where each element of the returned array is
+   * a handle for the corresponding element of the given array.
+   *
+   * @param elements the elements for which handles are to be created
+   * @return an array of handles to the given elements
+   */
+  static List<Element> forElements(List<Element> elements) {
+    int length = elements.length;
+    List<Element> handles = new List<Element>.from(elements);
+    for (int i = 0; i < length; i++) {
+      handles[i] = forElement(elements[i]);
+    }
+    return handles;
+  }
+}
+
+/**
+ * The abstract class `ExecutableElementHandle` implements the behavior common to objects that
+ * implement a handle to an [ExecutableElement].
+ */
+abstract class ExecutableElementHandle extends ElementHandle
+    implements ExecutableElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  ExecutableElementHandle(ExecutableElement element) : super(element);
+
+  @override
+  ExecutableElement get actualElement =>
+      super.actualElement as ExecutableElement;
+
+  @override
+  List<FunctionElement> get functions => actualElement.functions;
+
+  @override
+  bool get isAbstract => actualElement.isAbstract;
+
+  @override
+  bool get isAsynchronous => actualElement.isAsynchronous;
+
+  @override
+  bool get isGenerator => actualElement.isGenerator;
+
+  @override
+  bool get isOperator => actualElement.isOperator;
+
+  @override
+  bool get isStatic => actualElement.isStatic;
+
+  @override
+  bool get isSynchronous => actualElement.isSynchronous;
+
+  @override
+  List<LabelElement> get labels => actualElement.labels;
+
+  @override
+  List<LocalVariableElement> get localVariables => actualElement.localVariables;
+
+  @override
+  List<ParameterElement> get parameters => actualElement.parameters;
+
+  @override
+  DartType get returnType => actualElement.returnType;
+
+  @override
+  FunctionType get type => actualElement.type;
+}
+
+/**
+ * Instances of the class `ExportElementHandle` implement a handle to an `ExportElement`
+ * .
+ */
+class ExportElementHandle extends ElementHandle implements ExportElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  ExportElementHandle(ExportElement element) : super(element);
+
+  @override
+  ExportElement get actualElement => super.actualElement as ExportElement;
+
+  @override
+  List<NamespaceCombinator> get combinators => actualElement.combinators;
+
+  @override
+  LibraryElement get exportedLibrary => actualElement.exportedLibrary;
+
+  @override
+  ElementKind get kind => ElementKind.EXPORT;
+
+  @override
+  String get uri => actualElement.uri;
+
+  @override
+  int get uriEnd => actualElement.uriEnd;
+
+  @override
+  int get uriOffset => actualElement.uriOffset;
+}
+
+/**
+ * Instances of the class `FieldElementHandle` implement a handle to a `FieldElement`.
+ */
+class FieldElementHandle extends PropertyInducingElementHandle
+    implements FieldElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  FieldElementHandle(FieldElement element) : super(element);
+
+  @override
+  FieldElement get actualElement => super.actualElement as FieldElement;
+
+  @override
+  ClassElement get enclosingElement => actualElement.enclosingElement;
+
+  @override
+  bool get isEnumConstant => actualElement.isEnumConstant;
+
+  @override
+  bool get isStatic => actualElement.isStatic;
+
+  @override
+  ElementKind get kind => ElementKind.FIELD;
+
+  @override
+  VariableDeclaration get node => actualElement.node;
+}
+
+/**
+ * Instances of the class `FunctionElementHandle` implement a handle to a
+ * `FunctionElement`.
+ */
+class FunctionElementHandle extends ExecutableElementHandle
+    implements FunctionElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  FunctionElementHandle(FunctionElement element) : super(element);
+
+  @override
+  FunctionElement get actualElement => super.actualElement as FunctionElement;
+
+  @override
+  bool get isEntryPoint => actualElement.isEntryPoint;
+
+  @override
+  ElementKind get kind => ElementKind.FUNCTION;
+
+  @override
+  FunctionDeclaration get node => actualElement.node;
+
+  @override
+  SourceRange get visibleRange => actualElement.visibleRange;
+}
+
+/**
+ * Instances of the class `FunctionTypeAliasElementHandle` implement a handle to a
+ * `FunctionTypeAliasElement`.
+ */
+class FunctionTypeAliasElementHandle extends ElementHandle
+    implements FunctionTypeAliasElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  FunctionTypeAliasElementHandle(FunctionTypeAliasElement element)
+      : super(element);
+
+  @override
+  FunctionTypeAliasElement get actualElement =>
+      super.actualElement as FunctionTypeAliasElement;
+
+  @override
+  CompilationUnitElement get enclosingElement =>
+      super.enclosingElement as CompilationUnitElement;
+
+  @override
+  ElementKind get kind => ElementKind.FUNCTION_TYPE_ALIAS;
+
+  @override
+  FunctionTypeAlias get node => actualElement.node;
+
+  @override
+  List<ParameterElement> get parameters => actualElement.parameters;
+
+  @override
+  DartType get returnType => actualElement.returnType;
+
+  @override
+  FunctionType get type => actualElement.type;
+
+  @override
+  List<TypeParameterElement> get typeParameters => actualElement.typeParameters;
+}
+
+/**
+ * Instances of the class `ImportElementHandle` implement a handle to an `ImportElement`
+ * .
+ */
+class ImportElementHandle extends ElementHandle implements ImportElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  ImportElementHandle(ImportElement element) : super(element);
+
+  @override
+  ImportElement get actualElement => super.actualElement as ImportElement;
+
+  @override
+  List<NamespaceCombinator> get combinators => actualElement.combinators;
+
+  @override
+  LibraryElement get importedLibrary => actualElement.importedLibrary;
+
+  @override
+  bool get isDeferred => actualElement.isDeferred;
+
+  @override
+  ElementKind get kind => ElementKind.IMPORT;
+
+  @override
+  PrefixElement get prefix => actualElement.prefix;
+
+  @override
+  int get prefixOffset => actualElement.prefixOffset;
+
+  @override
+  String get uri => actualElement.uri;
+
+  @override
+  int get uriEnd => actualElement.uriEnd;
+
+  @override
+  int get uriOffset => actualElement.uriOffset;
+}
+
+/**
+ * Instances of the class `LabelElementHandle` implement a handle to a `LabelElement`.
+ */
+class LabelElementHandle extends ElementHandle implements LabelElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  LabelElementHandle(LabelElement element) : super(element);
+
+  @override
+  ExecutableElement get enclosingElement =>
+      super.enclosingElement as ExecutableElement;
+
+  @override
+  ElementKind get kind => ElementKind.LABEL;
+}
+
+/**
+ * Instances of the class `LibraryElementHandle` implement a handle to a
+ * `LibraryElement`.
+ */
+class LibraryElementHandle extends ElementHandle implements LibraryElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  LibraryElementHandle(LibraryElement element) : super(element);
+
+  @override
+  LibraryElement get actualElement => super.actualElement as LibraryElement;
+
+  @override
+  CompilationUnitElement get definingCompilationUnit =>
+      actualElement.definingCompilationUnit;
+
+  @override
+  FunctionElement get entryPoint => actualElement.entryPoint;
+
+  @override
+  List<LibraryElement> get exportedLibraries => actualElement.exportedLibraries;
+
+  @override
+  Namespace get exportNamespace => actualElement.exportNamespace;
+
+  @override
+  List<ExportElement> get exports => actualElement.exports;
+
+  @override
+  bool get hasExtUri => actualElement.hasExtUri;
+
+  @override
+  bool get hasLoadLibraryFunction => actualElement.hasLoadLibraryFunction;
+
+  @override
+  List<LibraryElement> get importedLibraries => actualElement.importedLibraries;
+
+  @override
+  List<ImportElement> get imports => actualElement.imports;
+
+  @override
+  bool get isBrowserApplication => actualElement.isBrowserApplication;
+
+  @override
+  bool get isDartCore => actualElement.isDartCore;
+
+  @override
+  bool get isInSdk => actualElement.isInSdk;
+
+  @override
+  ElementKind get kind => ElementKind.LIBRARY;
+
+  @override
+  FunctionElement get loadLibraryFunction => actualElement.loadLibraryFunction;
+
+  @override
+  List<CompilationUnitElement> get parts => actualElement.parts;
+
+  @override
+  List<PrefixElement> get prefixes => actualElement.prefixes;
+
+  @override
+  Namespace get publicNamespace => actualElement.publicNamespace;
+
+  @override
+  List<CompilationUnitElement> get units => actualElement.units;
+
+  @override
+  List<LibraryElement> get visibleLibraries => actualElement.visibleLibraries;
+
+  @override
+  List<ImportElement> getImportsWithPrefix(PrefixElement prefixElement) =>
+      actualElement.getImportsWithPrefix(prefixElement);
+
+  @override
+  ClassElement getType(String className) => actualElement.getType(className);
+
+  @override
+  bool isUpToDate(int timeStamp) => actualElement.isUpToDate(timeStamp);
+}
+
+/**
+ * Instances of the class `LocalVariableElementHandle` implement a handle to a
+ * `LocalVariableElement`.
+ */
+class LocalVariableElementHandle extends VariableElementHandle
+    implements LocalVariableElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  LocalVariableElementHandle(LocalVariableElement element) : super(element);
+
+  @override
+  LocalVariableElement get actualElement =>
+      super.actualElement as LocalVariableElement;
+
+  @override
+  ElementKind get kind => ElementKind.LOCAL_VARIABLE;
+
+  @override
+  VariableDeclaration get node => actualElement.node;
+
+  @override
+  SourceRange get visibleRange => actualElement.visibleRange;
+}
+
+/**
+ * Instances of the class `MethodElementHandle` implement a handle to a `MethodElement`.
+ */
+class MethodElementHandle extends ExecutableElementHandle
+    implements MethodElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  MethodElementHandle(MethodElement element) : super(element);
+
+  @override
+  MethodElement get actualElement => super.actualElement as MethodElement;
+
+  @override
+  ClassElement get enclosingElement => super.enclosingElement as ClassElement;
+
+  @override
+  bool get isStatic => actualElement.isStatic;
+
+  @override
+  ElementKind get kind => ElementKind.METHOD;
+
+  @override
+  MethodDeclaration get node => actualElement.node;
+}
+
+/**
+ * Instances of the class `ParameterElementHandle` implement a handle to a
+ * `ParameterElement`.
+ */
+class ParameterElementHandle extends VariableElementHandle
+    implements ParameterElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  ParameterElementHandle(ParameterElement element) : super(element);
+
+  @override
+  ParameterElement get actualElement => super.actualElement as ParameterElement;
+
+  @override
+  String get defaultValueCode => actualElement.defaultValueCode;
+
+  @override
+  bool get isInitializingFormal => actualElement.isInitializingFormal;
+
+  @override
+  ElementKind get kind => ElementKind.PARAMETER;
+
+  @override
+  ParameterKind get parameterKind => actualElement.parameterKind;
+
+  @override
+  List<ParameterElement> get parameters => actualElement.parameters;
+
+  @override
+  SourceRange get visibleRange => actualElement.visibleRange;
+}
+
+/**
+ * Instances of the class `PrefixElementHandle` implement a handle to a `PrefixElement`.
+ */
+class PrefixElementHandle extends ElementHandle implements PrefixElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  PrefixElementHandle(PrefixElement element) : super(element);
+
+  @override
+  PrefixElement get actualElement => super.actualElement as PrefixElement;
+
+  @override
+  LibraryElement get enclosingElement =>
+      super.enclosingElement as LibraryElement;
+
+  @override
+  List<LibraryElement> get importedLibraries => actualElement.importedLibraries;
+
+  @override
+  ElementKind get kind => ElementKind.PREFIX;
+}
+
+/**
+ * Instances of the class `PropertyAccessorElementHandle` implement a handle to a
+ * `PropertyAccessorElement`.
+ */
+class PropertyAccessorElementHandle extends ExecutableElementHandle
+    implements PropertyAccessorElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  PropertyAccessorElementHandle(PropertyAccessorElement element)
+      : super(element);
+
+  @override
+  PropertyAccessorElement get actualElement =>
+      super.actualElement as PropertyAccessorElement;
+
+  @override
+  PropertyAccessorElement get correspondingGetter =>
+      actualElement.correspondingGetter;
+
+  @override
+  PropertyAccessorElement get correspondingSetter =>
+      actualElement.correspondingSetter;
+
+  @override
+  bool get isGetter => actualElement.isGetter;
+
+  @override
+  bool get isSetter => actualElement.isSetter;
+
+  @override
+  ElementKind get kind {
+    if (isGetter) {
+      return ElementKind.GETTER;
+    } else {
+      return ElementKind.SETTER;
+    }
+  }
+
+  @override
+  PropertyInducingElement get variable => actualElement.variable;
+}
+
+/**
+ * The abstract class `PropertyInducingElementHandle` implements the behavior common to
+ * objects that implement a handle to an `PropertyInducingElement`.
+ */
+abstract class PropertyInducingElementHandle extends VariableElementHandle
+    implements PropertyInducingElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  PropertyInducingElementHandle(PropertyInducingElement element)
+      : super(element);
+
+  @override
+  PropertyInducingElement get actualElement =>
+      super.actualElement as PropertyInducingElement;
+
+  @override
+  PropertyAccessorElement get getter => actualElement.getter;
+
+  @override
+  bool get isStatic => actualElement.isStatic;
+
+  @override
+  DartType get propagatedType => actualElement.propagatedType;
+
+  @override
+  PropertyAccessorElement get setter => actualElement.setter;
+}
+
+/**
+ * Instances of the class `TopLevelVariableElementHandle` implement a handle to a
+ * `TopLevelVariableElement`.
+ */
+class TopLevelVariableElementHandle extends PropertyInducingElementHandle
+    implements TopLevelVariableElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  TopLevelVariableElementHandle(TopLevelVariableElement element)
+      : super(element);
+
+  @override
+  ElementKind get kind => ElementKind.TOP_LEVEL_VARIABLE;
+}
+
+/**
+ * Instances of the class `TypeParameterElementHandle` implement a handle to a
+ * [TypeParameterElement].
+ */
+class TypeParameterElementHandle extends ElementHandle
+    implements TypeParameterElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  TypeParameterElementHandle(TypeParameterElement element) : super(element);
+
+  @override
+  TypeParameterElement get actualElement =>
+      super.actualElement as TypeParameterElement;
+
+  @override
+  DartType get bound => actualElement.bound;
+
+  @override
+  ElementKind get kind => ElementKind.TYPE_PARAMETER;
+
+  @override
+  TypeParameterType get type => actualElement.type;
+}
+
+/**
+ * The abstract class `VariableElementHandle` implements the behavior common to objects that
+ * implement a handle to an `VariableElement`.
+ */
+abstract class VariableElementHandle extends ElementHandle
+    implements VariableElement {
+  /**
+   * Initialize a newly created element handle to represent the given element.
+   *
+   * @param element the element being represented
+   */
+  VariableElementHandle(VariableElement element) : super(element);
+
+  @override
+  VariableElement get actualElement => super.actualElement as VariableElement;
+
+  @override
+  FunctionElement get initializer => actualElement.initializer;
+
+  @override
+  bool get isConst => actualElement.isConst;
+
+  @override
+  bool get isFinal => actualElement.isFinal;
+
+  @override
+  bool get isPotentiallyMutatedInClosure =>
+      actualElement.isPotentiallyMutatedInClosure;
+
+  @override
+  bool get isPotentiallyMutatedInScope =>
+      actualElement.isPotentiallyMutatedInScope;
+
+  @override
+  DartType get type => actualElement.type;
+}
+/**
+ * TODO(scheglov) invalid implementation
+ */
+class WeakReference<T> {
+  final T value;
+  WeakReference(this.value);
+  T get() => value;
+}
diff --git a/analyzer/lib/src/generated/element_resolver.dart b/analyzer/lib/src/generated/element_resolver.dart
new file mode 100644
index 0000000..33d1617
--- /dev/null
+++ b/analyzer/lib/src/generated/element_resolver.dart
@@ -0,0 +1,2785 @@
+// 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 engine.resolver.element_resolver;
+
+import 'dart:collection';
+
+import 'ast.dart';
+import 'element.dart';
+import 'engine.dart';
+import 'error.dart';
+import 'resolver.dart';
+import 'scanner.dart' as sc;
+import 'utilities_dart.dart';
+
+/**
+ * An object used by instances of [ResolverVisitor] to resolve references within
+ * the AST structure to the elements being referenced. The requirements for the
+ * element resolver are:
+ *
+ * 1. Every [SimpleIdentifier] should be resolved to the element to which it
+ *    refers. Specifically:
+ *    * An identifier within the declaration of that name should resolve to the
+ *      element being declared.
+ *    * An identifier denoting a prefix should resolve to the element
+ *      representing the import that defines the prefix (an [ImportElement]).
+ *    * An identifier denoting a variable should resolve to the element
+ *      representing the variable (a [VariableElement]).
+ *    * An identifier denoting a parameter should resolve to the element
+ *      representing the parameter (a [ParameterElement]).
+ *    * An identifier denoting a field should resolve to the element
+ *      representing the getter or setter being invoked (a
+ *      [PropertyAccessorElement]).
+ *    * An identifier denoting the name of a method or function being invoked
+ *      should resolve to the element representing the method or function (an
+ *      [ExecutableElement]).
+ *    * An identifier denoting a label should resolve to the element
+ *      representing the label (a [LabelElement]).
+ *    The identifiers within directives are exceptions to this rule and are
+ *    covered below.
+ * 2. Every node containing a token representing an operator that can be
+ *    overridden ( [BinaryExpression], [PrefixExpression], [PostfixExpression])
+ *    should resolve to the element representing the method invoked by that
+ *    operator (a [MethodElement]).
+ * 3. Every [FunctionExpressionInvocation] should resolve to the element
+ *    representing the function being invoked (a [FunctionElement]). This will
+ *    be the same element as that to which the name is resolved if the function
+ *    has a name, but is provided for those cases where an unnamed function is
+ *    being invoked.
+ * 4. Every [LibraryDirective] and [PartOfDirective] should resolve to the
+ *    element representing the library being specified by the directive (a
+ *    [LibraryElement]) unless, in the case of a part-of directive, the
+ *    specified library does not exist.
+ * 5. Every [ImportDirective] and [ExportDirective] should resolve to the
+ *    element representing the library being specified by the directive unless
+ *    the specified library does not exist (an [ImportElement] or
+ *    [ExportElement]).
+ * 6. The identifier representing the prefix in an [ImportDirective] should
+ *    resolve to the element representing the prefix (a [PrefixElement]).
+ * 7. The identifiers in the hide and show combinators in [ImportDirective]s
+ *    and [ExportDirective]s should resolve to the elements that are being
+ *    hidden or shown, respectively, unless those names are not defined in the
+ *    specified library (or the specified library does not exist).
+ * 8. Every [PartDirective] should resolve to the element representing the
+ *    compilation unit being specified by the string unless the specified
+ *    compilation unit does not exist (a [CompilationUnitElement]).
+ *
+ * Note that AST nodes that would represent elements that are not defined are
+ * not resolved to anything. This includes such things as references to
+ * undeclared variables (which is an error) and names in hide and show
+ * combinators that are not defined in the imported library (which is not an
+ * error).
+ */
+class ElementResolver extends SimpleAstVisitor<Object> {
+  /**
+   * The resolver driving this participant.
+   */
+  final ResolverVisitor _resolver;
+
+  /**
+   * The element for the library containing the compilation unit being visited.
+   */
+  LibraryElement _definingLibrary;
+
+  /**
+   * A flag indicating whether we should generate hints.
+   */
+  bool _enableHints = false;
+
+  /**
+   * A flag indicating whether we should strictly follow the specification when
+   * generating warnings on "call" methods (fixes dartbug.com/21938).
+   */
+  bool _enableStrictCallChecks = false;
+
+  /**
+   * The type representing the type 'dynamic'.
+   */
+  DartType _dynamicType;
+
+  /**
+   * The type representing the type 'type'.
+   */
+  DartType _typeType;
+
+  /**
+   * A utility class for the resolver to answer the question of "what are my
+   * subtypes?".
+   */
+  SubtypeManager _subtypeManager;
+
+  /**
+   * The object keeping track of which elements have had their types promoted.
+   */
+  TypePromotionManager _promoteManager;
+
+  /**
+   * Initialize a newly created visitor to work for the given [_resolver] to
+   * resolve the nodes in a compilation unit.
+   */
+  ElementResolver(this._resolver) {
+    this._definingLibrary = _resolver.definingLibrary;
+    AnalysisOptions options = _definingLibrary.context.analysisOptions;
+    _enableHints = options.hint;
+    _enableStrictCallChecks = options.enableStrictCallChecks;
+    _dynamicType = _resolver.typeProvider.dynamicType;
+    _typeType = _resolver.typeProvider.typeType;
+    _subtypeManager = new SubtypeManager();
+    _promoteManager = _resolver.promoteManager;
+  }
+
+  /**
+   * Return `true` iff the current enclosing function is a constant constructor
+   * declaration.
+   */
+  bool get isInConstConstructor {
+    ExecutableElement function = _resolver.enclosingFunction;
+    if (function is ConstructorElement) {
+      return function.isConst;
+    }
+    return false;
+  }
+
+  @override
+  Object visitAssignmentExpression(AssignmentExpression node) {
+    sc.Token operator = node.operator;
+    sc.TokenType operatorType = operator.type;
+    if (operatorType != sc.TokenType.EQ &&
+        operatorType != sc.TokenType.QUESTION_QUESTION_EQ) {
+      operatorType = _operatorFromCompoundAssignment(operatorType);
+      Expression leftHandSide = node.leftHandSide;
+      if (leftHandSide != null) {
+        String methodName = operatorType.lexeme;
+        DartType staticType = _getStaticType(leftHandSide);
+        MethodElement staticMethod =
+            _lookUpMethod(leftHandSide, staticType, methodName);
+        node.staticElement = staticMethod;
+        DartType propagatedType = _getPropagatedType(leftHandSide);
+        MethodElement propagatedMethod =
+            _lookUpMethod(leftHandSide, propagatedType, methodName);
+        node.propagatedElement = propagatedMethod;
+        if (_shouldReportMissingMember(staticType, staticMethod)) {
+          _recordUndefinedToken(staticType.element,
+              StaticTypeWarningCode.UNDEFINED_METHOD, operator, [
+            methodName,
+            staticType.displayName
+          ]);
+        } else if (_enableHints &&
+            _shouldReportMissingMember(propagatedType, propagatedMethod) &&
+            !_memberFoundInSubclass(
+                propagatedType.element, methodName, true, false)) {
+          _recordUndefinedToken(propagatedType.element,
+              HintCode.UNDEFINED_METHOD, operator, [
+            methodName,
+            propagatedType.displayName
+          ]);
+        }
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitBinaryExpression(BinaryExpression node) {
+    sc.Token operator = node.operator;
+    if (operator.isUserDefinableOperator) {
+      _resolveBinaryExpression(node, operator.lexeme);
+    } else if (operator.type == sc.TokenType.BANG_EQ) {
+      _resolveBinaryExpression(node, sc.TokenType.EQ_EQ.lexeme);
+    }
+    return null;
+  }
+
+  @override
+  Object visitBreakStatement(BreakStatement node) {
+    node.target = _lookupBreakOrContinueTarget(node, node.label, false);
+    return null;
+  }
+
+  @override
+  Object visitClassDeclaration(ClassDeclaration node) {
+    _setMetadata(node.element, node);
+    return null;
+  }
+
+  @override
+  Object visitClassTypeAlias(ClassTypeAlias node) {
+    _setMetadata(node.element, node);
+    return null;
+  }
+
+  @override
+  Object visitCommentReference(CommentReference node) {
+    Identifier identifier = node.identifier;
+    if (identifier is SimpleIdentifier) {
+      SimpleIdentifier simpleIdentifier = identifier;
+      Element element = _resolveSimpleIdentifier(simpleIdentifier);
+      if (element == null) {
+        //
+        // This might be a reference to an imported name that is missing the
+        // prefix.
+        //
+        element = _findImportWithoutPrefix(simpleIdentifier);
+        if (element is MultiplyDefinedElement) {
+          // TODO(brianwilkerson) Report this error?
+          element = null;
+        }
+      }
+      if (element == null) {
+        // TODO(brianwilkerson) Report this error?
+        //        resolver.reportError(
+        //            StaticWarningCode.UNDEFINED_IDENTIFIER,
+        //            simpleIdentifier,
+        //            simpleIdentifier.getName());
+      } else {
+        if (element.library == null || element.library != _definingLibrary) {
+          // TODO(brianwilkerson) Report this error?
+        }
+        simpleIdentifier.staticElement = element;
+        if (node.newKeyword != null) {
+          if (element is ClassElement) {
+            ConstructorElement constructor = element.unnamedConstructor;
+            if (constructor == null) {
+              // TODO(brianwilkerson) Report this error.
+            } else {
+              simpleIdentifier.staticElement = constructor;
+            }
+          } else {
+            // TODO(brianwilkerson) Report this error.
+          }
+        }
+      }
+    } else if (identifier is PrefixedIdentifier) {
+      PrefixedIdentifier prefixedIdentifier = identifier;
+      SimpleIdentifier prefix = prefixedIdentifier.prefix;
+      SimpleIdentifier name = prefixedIdentifier.identifier;
+      Element element = _resolveSimpleIdentifier(prefix);
+      if (element == null) {
+//        resolver.reportError(StaticWarningCode.UNDEFINED_IDENTIFIER, prefix, prefix.getName());
+      } else {
+        if (element is PrefixElement) {
+          prefix.staticElement = element;
+          // TODO(brianwilkerson) Report this error?
+          element = _resolver.nameScope.lookup(identifier, _definingLibrary);
+          name.staticElement = element;
+          return null;
+        }
+        LibraryElement library = element.library;
+        if (library == null) {
+          // TODO(brianwilkerson) We need to understand how the library could
+          // ever be null.
+          AnalysisEngine.instance.logger
+              .logError("Found element with null library: ${element.name}");
+        } else if (library != _definingLibrary) {
+          // TODO(brianwilkerson) Report this error.
+        }
+        name.staticElement = element;
+        if (node.newKeyword == null) {
+          if (element is ClassElement) {
+            Element memberElement =
+                _lookupGetterOrMethod(element.type, name.name);
+            if (memberElement == null) {
+              memberElement = element.getNamedConstructor(name.name);
+              if (memberElement == null) {
+                memberElement = _lookUpSetter(prefix, element.type, name.name);
+              }
+            }
+            if (memberElement == null) {
+//              reportGetterOrSetterNotFound(prefixedIdentifier, name, element.getDisplayName());
+            } else {
+              name.staticElement = memberElement;
+            }
+          } else {
+            // TODO(brianwilkerson) Report this error.
+          }
+        } else {
+          if (element is ClassElement) {
+            ConstructorElement constructor =
+                element.getNamedConstructor(name.name);
+            if (constructor == null) {
+              // TODO(brianwilkerson) Report this error.
+            } else {
+              name.staticElement = constructor;
+            }
+          } else {
+            // TODO(brianwilkerson) Report this error.
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    super.visitConstructorDeclaration(node);
+    ConstructorElement element = node.element;
+    if (element is ConstructorElementImpl) {
+      ConstructorElementImpl constructorElement = element;
+      ConstructorName redirectedNode = node.redirectedConstructor;
+      if (redirectedNode != null) {
+        // set redirected factory constructor
+        ConstructorElement redirectedElement = redirectedNode.staticElement;
+        constructorElement.redirectedConstructor = redirectedElement;
+      } else {
+        // set redirected generative constructor
+        for (ConstructorInitializer initializer in node.initializers) {
+          if (initializer is RedirectingConstructorInvocation) {
+            ConstructorElement redirectedElement = initializer.staticElement;
+            constructorElement.redirectedConstructor = redirectedElement;
+          }
+        }
+      }
+      _setMetadata(constructorElement, node);
+    }
+    return null;
+  }
+
+  @override
+  Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+    SimpleIdentifier fieldName = node.fieldName;
+    ClassElement enclosingClass = _resolver.enclosingClass;
+    FieldElement fieldElement = enclosingClass.getField(fieldName.name);
+    fieldName.staticElement = fieldElement;
+    return null;
+  }
+
+  @override
+  Object visitConstructorName(ConstructorName node) {
+    DartType type = node.type.type;
+    if (type != null && type.isDynamic) {
+      return null;
+    } else if (type is! InterfaceType) {
+// TODO(brianwilkerson) Report these errors.
+//      ASTNode parent = node.getParent();
+//      if (parent instanceof InstanceCreationExpression) {
+//        if (((InstanceCreationExpression) parent).isConst()) {
+//          // CompileTimeErrorCode.CONST_WITH_NON_TYPE
+//        } else {
+//          // StaticWarningCode.NEW_WITH_NON_TYPE
+//        }
+//      } else {
+//        // This is part of a redirecting factory constructor; not sure which error code to use
+//      }
+      return null;
+    }
+    // look up ConstructorElement
+    ConstructorElement constructor;
+    SimpleIdentifier name = node.name;
+    InterfaceType interfaceType = type as InterfaceType;
+    if (name == null) {
+      constructor = interfaceType.lookUpConstructor(null, _definingLibrary);
+    } else {
+      constructor =
+          interfaceType.lookUpConstructor(name.name, _definingLibrary);
+      name.staticElement = constructor;
+    }
+    node.staticElement = constructor;
+    return null;
+  }
+
+  @override
+  Object visitContinueStatement(ContinueStatement node) {
+    node.target = _lookupBreakOrContinueTarget(node, node.label, true);
+    return null;
+  }
+
+  @override
+  Object visitDeclaredIdentifier(DeclaredIdentifier node) {
+    _setMetadata(node.element, node);
+    return null;
+  }
+
+  @override
+  Object visitEnumDeclaration(EnumDeclaration node) {
+    _setMetadata(node.element, node);
+    return null;
+  }
+
+  @override
+  Object visitExportDirective(ExportDirective node) {
+    ExportElement exportElement = node.element;
+    if (exportElement != null) {
+      // The element is null when the URI is invalid
+      // TODO(brianwilkerson) Figure out whether the element can ever be
+      // something other than an ExportElement
+      _resolveCombinators(exportElement.exportedLibrary, node.combinators);
+      _setMetadata(exportElement, node);
+    }
+    return null;
+  }
+
+  @override
+  Object visitFieldFormalParameter(FieldFormalParameter node) {
+    _setMetadataForParameter(node.element, node);
+    return super.visitFieldFormalParameter(node);
+  }
+
+  @override
+  Object visitFunctionDeclaration(FunctionDeclaration node) {
+    _setMetadata(node.element, node);
+    return null;
+  }
+
+  @override
+  Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    // TODO(brianwilkerson) Can we ever resolve the function being invoked?
+    Expression expression = node.function;
+    if (expression is FunctionExpression) {
+      FunctionExpression functionExpression = expression;
+      ExecutableElement functionElement = functionExpression.element;
+      ArgumentList argumentList = node.argumentList;
+      List<ParameterElement> parameters =
+          _resolveArgumentsToFunction(false, argumentList, functionElement);
+      if (parameters != null) {
+        argumentList.correspondingStaticParameters = parameters;
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitFunctionTypeAlias(FunctionTypeAlias node) {
+    _setMetadata(node.element, node);
+    return null;
+  }
+
+  @override
+  Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+    _setMetadataForParameter(node.element, node);
+    return null;
+  }
+
+  @override
+  Object visitImportDirective(ImportDirective node) {
+    SimpleIdentifier prefixNode = node.prefix;
+    if (prefixNode != null) {
+      String prefixName = prefixNode.name;
+      for (PrefixElement prefixElement in _definingLibrary.prefixes) {
+        if (prefixElement.displayName == prefixName) {
+          prefixNode.staticElement = prefixElement;
+          break;
+        }
+      }
+    }
+    ImportElement importElement = node.element;
+    if (importElement != null) {
+      // The element is null when the URI is invalid
+      LibraryElement library = importElement.importedLibrary;
+      if (library != null) {
+        _resolveCombinators(library, node.combinators);
+      }
+      _setMetadata(importElement, node);
+    }
+    return null;
+  }
+
+  @override
+  Object visitIndexExpression(IndexExpression node) {
+    Expression target = node.realTarget;
+    DartType staticType = _getStaticType(target);
+    DartType propagatedType = _getPropagatedType(target);
+    String getterMethodName = sc.TokenType.INDEX.lexeme;
+    String setterMethodName = sc.TokenType.INDEX_EQ.lexeme;
+    bool isInGetterContext = node.inGetterContext();
+    bool isInSetterContext = node.inSetterContext();
+    if (isInGetterContext && isInSetterContext) {
+      // lookup setter
+      MethodElement setterStaticMethod =
+          _lookUpMethod(target, staticType, setterMethodName);
+      MethodElement setterPropagatedMethod =
+          _lookUpMethod(target, propagatedType, setterMethodName);
+      // set setter element
+      node.staticElement = setterStaticMethod;
+      node.propagatedElement = setterPropagatedMethod;
+      // generate undefined method warning
+      _checkForUndefinedIndexOperator(node, target, getterMethodName,
+          setterStaticMethod, setterPropagatedMethod, staticType,
+          propagatedType);
+      // lookup getter method
+      MethodElement getterStaticMethod =
+          _lookUpMethod(target, staticType, getterMethodName);
+      MethodElement getterPropagatedMethod =
+          _lookUpMethod(target, propagatedType, getterMethodName);
+      // set getter element
+      AuxiliaryElements auxiliaryElements =
+          new AuxiliaryElements(getterStaticMethod, getterPropagatedMethod);
+      node.auxiliaryElements = auxiliaryElements;
+      // generate undefined method warning
+      _checkForUndefinedIndexOperator(node, target, getterMethodName,
+          getterStaticMethod, getterPropagatedMethod, staticType,
+          propagatedType);
+    } else if (isInGetterContext) {
+      // lookup getter method
+      MethodElement staticMethod =
+          _lookUpMethod(target, staticType, getterMethodName);
+      MethodElement propagatedMethod =
+          _lookUpMethod(target, propagatedType, getterMethodName);
+      // set getter element
+      node.staticElement = staticMethod;
+      node.propagatedElement = propagatedMethod;
+      // generate undefined method warning
+      _checkForUndefinedIndexOperator(node, target, getterMethodName,
+          staticMethod, propagatedMethod, staticType, propagatedType);
+    } else if (isInSetterContext) {
+      // lookup setter method
+      MethodElement staticMethod =
+          _lookUpMethod(target, staticType, setterMethodName);
+      MethodElement propagatedMethod =
+          _lookUpMethod(target, propagatedType, setterMethodName);
+      // set setter element
+      node.staticElement = staticMethod;
+      node.propagatedElement = propagatedMethod;
+      // generate undefined method warning
+      _checkForUndefinedIndexOperator(node, target, setterMethodName,
+          staticMethod, propagatedMethod, staticType, propagatedType);
+    }
+    return null;
+  }
+
+  @override
+  Object visitInstanceCreationExpression(InstanceCreationExpression node) {
+    ConstructorElement invokedConstructor = node.constructorName.staticElement;
+    node.staticElement = invokedConstructor;
+    ArgumentList argumentList = node.argumentList;
+    List<ParameterElement> parameters = _resolveArgumentsToFunction(
+        node.isConst, argumentList, invokedConstructor);
+    if (parameters != null) {
+      argumentList.correspondingStaticParameters = parameters;
+    }
+    return null;
+  }
+
+  @override
+  Object visitLibraryDirective(LibraryDirective node) {
+    _setMetadata(node.element, node);
+    return null;
+  }
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    _setMetadata(node.element, node);
+    return null;
+  }
+
+  @override
+  Object visitMethodInvocation(MethodInvocation node) {
+    SimpleIdentifier methodName = node.methodName;
+    //
+    // Synthetic identifiers have been already reported during parsing.
+    //
+    if (methodName.isSynthetic) {
+      return null;
+    }
+    //
+    // We have a method invocation of one of two forms: 'e.m(a1, ..., an)' or
+    // 'm(a1, ..., an)'. The first step is to figure out which executable is
+    // being invoked, using both the static and the propagated type information.
+    //
+    Expression target = node.realTarget;
+    if (target is SuperExpression && !_isSuperInValidContext(target)) {
+      return null;
+    }
+    Element staticElement;
+    Element propagatedElement;
+    DartType staticType = null;
+    DartType propagatedType = null;
+    if (target == null) {
+      staticElement = _resolveInvokedElement(methodName);
+      propagatedElement = null;
+    } else if (methodName.name == FunctionElement.LOAD_LIBRARY_NAME &&
+        _isDeferredPrefix(target)) {
+      LibraryElement importedLibrary = _getImportedLibrary(target);
+      methodName.staticElement = importedLibrary.loadLibraryFunction;
+      return null;
+    } else {
+      staticType = _getStaticType(target);
+      propagatedType = _getPropagatedType(target);
+      //
+      // If this method invocation is of the form 'C.m' where 'C' is a class,
+      // then we don't call resolveInvokedElement(...) which walks up the class
+      // hierarchy, instead we just look for the member in the type only.  This
+      // does not apply to conditional method invocation (i.e. 'C?.m(...)').
+      //
+      bool isConditional = node.operator.type == sc.TokenType.QUESTION_PERIOD;
+      ClassElementImpl typeReference = getTypeReference(target, isConditional);
+      if (typeReference != null) {
+        staticElement =
+            propagatedElement = _resolveElement(typeReference, methodName);
+      } else {
+        staticElement =
+            _resolveInvokedElementWithTarget(target, staticType, methodName);
+        propagatedElement = _resolveInvokedElementWithTarget(
+            target, propagatedType, methodName);
+      }
+    }
+    staticElement = _convertSetterToGetter(staticElement);
+    propagatedElement = _convertSetterToGetter(propagatedElement);
+    //
+    // Record the results.
+    //
+    methodName.staticElement = staticElement;
+    methodName.propagatedElement = propagatedElement;
+    ArgumentList argumentList = node.argumentList;
+    if (staticElement != null) {
+      List<ParameterElement> parameters =
+          _computeCorrespondingParameters(argumentList, staticElement);
+      if (parameters != null) {
+        argumentList.correspondingStaticParameters = parameters;
+      }
+    }
+    if (propagatedElement != null) {
+      List<ParameterElement> parameters =
+          _computeCorrespondingParameters(argumentList, propagatedElement);
+      if (parameters != null) {
+        argumentList.correspondingPropagatedParameters = parameters;
+      }
+    }
+    //
+    // Then check for error conditions.
+    //
+    ErrorCode errorCode = _checkForInvocationError(target, true, staticElement);
+    bool generatedWithTypePropagation = false;
+    if (_enableHints && errorCode == null && staticElement == null) {
+      // The method lookup may have failed because there were multiple
+      // incompatible choices. In this case we don't want to generate a hint.
+      errorCode = _checkForInvocationError(target, false, propagatedElement);
+      if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_METHOD)) {
+        ClassElement classElementContext = null;
+        if (target == null) {
+          classElementContext = _resolver.enclosingClass;
+        } else {
+          DartType type = target.bestType;
+          if (type != null) {
+            if (type.element is ClassElement) {
+              classElementContext = type.element as ClassElement;
+            }
+          }
+        }
+        if (classElementContext != null) {
+          _subtypeManager.ensureLibraryVisited(_definingLibrary);
+          HashSet<ClassElement> subtypeElements =
+              _subtypeManager.computeAllSubtypes(classElementContext);
+          for (ClassElement subtypeElement in subtypeElements) {
+            if (subtypeElement.getMethod(methodName.name) != null) {
+              errorCode = null;
+            }
+          }
+        }
+      }
+      generatedWithTypePropagation = true;
+    }
+    if (errorCode == null) {
+      return null;
+    }
+    if (identical(
+        errorCode, StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION)) {
+      _resolver.reportErrorForNode(
+          StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION, methodName,
+          [methodName.name]);
+    } else if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_FUNCTION)) {
+      _resolver.reportErrorForNode(StaticTypeWarningCode.UNDEFINED_FUNCTION,
+          methodName, [methodName.name]);
+    } else if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_METHOD)) {
+      String targetTypeName;
+      if (target == null) {
+        ClassElement enclosingClass = _resolver.enclosingClass;
+        targetTypeName = enclosingClass.displayName;
+        ErrorCode proxyErrorCode = (generatedWithTypePropagation
+            ? HintCode.UNDEFINED_METHOD
+            : StaticTypeWarningCode.UNDEFINED_METHOD);
+        _recordUndefinedNode(_resolver.enclosingClass, proxyErrorCode,
+            methodName, [methodName.name, targetTypeName]);
+      } else {
+        // ignore Function "call"
+        // (if we are about to create a hint using type propagation,
+        // then we can use type propagation here as well)
+        DartType targetType = null;
+        if (!generatedWithTypePropagation) {
+          targetType = _getStaticType(target);
+        } else {
+          // choose the best type
+          targetType = _getPropagatedType(target);
+          if (targetType == null) {
+            targetType = _getStaticType(target);
+          }
+        }
+        if (!_enableStrictCallChecks &&
+            targetType != null &&
+            targetType.isDartCoreFunction &&
+            methodName.name == FunctionElement.CALL_METHOD_NAME) {
+          // TODO(brianwilkerson) Can we ever resolve the function being
+          // invoked?
+//          resolveArgumentsToParameters(node.getArgumentList(), invokedFunction);
+          return null;
+        }
+        targetTypeName = targetType == null ? null : targetType.displayName;
+        ErrorCode proxyErrorCode = (generatedWithTypePropagation
+            ? HintCode.UNDEFINED_METHOD
+            : StaticTypeWarningCode.UNDEFINED_METHOD);
+        _recordUndefinedNode(targetType.element, proxyErrorCode, methodName, [
+          methodName.name,
+          targetTypeName
+        ]);
+      }
+    } else if (identical(
+        errorCode, StaticTypeWarningCode.UNDEFINED_SUPER_METHOD)) {
+      // Generate the type name.
+      // The error code will never be generated via type propagation
+      DartType targetType = _getStaticType(target);
+      if (targetType is InterfaceType && !targetType.isObject) {
+        targetType = (targetType as InterfaceType).superclass;
+      }
+      String targetTypeName = targetType == null ? null : targetType.name;
+      _resolver.reportErrorForNode(StaticTypeWarningCode.UNDEFINED_SUPER_METHOD,
+          methodName, [methodName.name, targetTypeName]);
+    }
+    return null;
+  }
+
+  @override
+  Object visitPartDirective(PartDirective node) {
+    _setMetadata(node.element, node);
+    return null;
+  }
+
+  @override
+  Object visitPartOfDirective(PartOfDirective node) {
+    _setMetadata(node.element, node);
+    return null;
+  }
+
+  @override
+  Object visitPostfixExpression(PostfixExpression node) {
+    Expression operand = node.operand;
+    String methodName = _getPostfixOperator(node);
+    DartType staticType = _getStaticType(operand);
+    MethodElement staticMethod = _lookUpMethod(operand, staticType, methodName);
+    node.staticElement = staticMethod;
+    DartType propagatedType = _getPropagatedType(operand);
+    MethodElement propagatedMethod =
+        _lookUpMethod(operand, propagatedType, methodName);
+    node.propagatedElement = propagatedMethod;
+    if (_shouldReportMissingMember(staticType, staticMethod)) {
+      if (operand is SuperExpression) {
+        _recordUndefinedToken(staticType.element,
+            StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR, node.operator, [
+          methodName,
+          staticType.displayName
+        ]);
+      } else {
+        _recordUndefinedToken(staticType.element,
+            StaticTypeWarningCode.UNDEFINED_OPERATOR, node.operator, [
+          methodName,
+          staticType.displayName
+        ]);
+      }
+    } else if (_enableHints &&
+        _shouldReportMissingMember(propagatedType, propagatedMethod) &&
+        !_memberFoundInSubclass(
+            propagatedType.element, methodName, true, false)) {
+      _recordUndefinedToken(propagatedType.element, HintCode.UNDEFINED_OPERATOR,
+          node.operator, [methodName, propagatedType.displayName]);
+    }
+    return null;
+  }
+
+  @override
+  Object visitPrefixedIdentifier(PrefixedIdentifier node) {
+    SimpleIdentifier prefix = node.prefix;
+    SimpleIdentifier identifier = node.identifier;
+    //
+    // First, check the "lib.loadLibrary" case
+    //
+    if (identifier.name == FunctionElement.LOAD_LIBRARY_NAME &&
+        _isDeferredPrefix(prefix)) {
+      LibraryElement importedLibrary = _getImportedLibrary(prefix);
+      identifier.staticElement = importedLibrary.loadLibraryFunction;
+      return null;
+    }
+    //
+    // Check to see whether the prefix is really a prefix.
+    //
+    Element prefixElement = prefix.staticElement;
+    if (prefixElement is PrefixElement) {
+      Element element = _resolver.nameScope.lookup(node, _definingLibrary);
+      if (element == null && identifier.inSetterContext()) {
+        element = _resolver.nameScope.lookup(
+            new SyntheticIdentifier("${node.name}=", node), _definingLibrary);
+      }
+      if (element == null) {
+        if (identifier.inSetterContext()) {
+          _resolver.reportErrorForNode(StaticWarningCode.UNDEFINED_SETTER,
+              identifier, [identifier.name, prefixElement.name]);
+        } else if (node.parent is Annotation) {
+          Annotation annotation = node.parent as Annotation;
+          _resolver.reportErrorForNode(
+              CompileTimeErrorCode.INVALID_ANNOTATION, annotation);
+          return null;
+        } else {
+          _resolver.reportErrorForNode(StaticWarningCode.UNDEFINED_GETTER,
+              identifier, [identifier.name, prefixElement.name]);
+        }
+        return null;
+      }
+      if (element is PropertyAccessorElement && identifier.inSetterContext()) {
+        PropertyInducingElement variable =
+            (element as PropertyAccessorElement).variable;
+        if (variable != null) {
+          PropertyAccessorElement setter = variable.setter;
+          if (setter != null) {
+            element = setter;
+          }
+        }
+      }
+      // TODO(brianwilkerson) The prefix needs to be resolved to the element for
+      // the import that defines the prefix, not the prefix's element.
+      identifier.staticElement = element;
+      // Validate annotation element.
+      if (node.parent is Annotation) {
+        Annotation annotation = node.parent as Annotation;
+        _resolveAnnotationElement(annotation);
+        return null;
+      }
+      return null;
+    }
+    // May be annotation, resolve invocation of "const" constructor.
+    if (node.parent is Annotation) {
+      Annotation annotation = node.parent as Annotation;
+      _resolveAnnotationElement(annotation);
+    }
+    //
+    // Otherwise, the prefix is really an expression that happens to be a simple
+    // identifier and this is really equivalent to a property access node.
+    //
+    _resolvePropertyAccess(prefix, identifier, false);
+    return null;
+  }
+
+  @override
+  Object visitPrefixExpression(PrefixExpression node) {
+    sc.Token operator = node.operator;
+    sc.TokenType operatorType = operator.type;
+    if (operatorType.isUserDefinableOperator ||
+        operatorType == sc.TokenType.PLUS_PLUS ||
+        operatorType == sc.TokenType.MINUS_MINUS) {
+      Expression operand = node.operand;
+      String methodName = _getPrefixOperator(node);
+      DartType staticType = _getStaticType(operand);
+      MethodElement staticMethod =
+          _lookUpMethod(operand, staticType, methodName);
+      node.staticElement = staticMethod;
+      DartType propagatedType = _getPropagatedType(operand);
+      MethodElement propagatedMethod =
+          _lookUpMethod(operand, propagatedType, methodName);
+      node.propagatedElement = propagatedMethod;
+      if (_shouldReportMissingMember(staticType, staticMethod)) {
+        if (operand is SuperExpression) {
+          _recordUndefinedToken(staticType.element,
+              StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR, operator, [
+            methodName,
+            staticType.displayName
+          ]);
+        } else {
+          _recordUndefinedToken(staticType.element,
+              StaticTypeWarningCode.UNDEFINED_OPERATOR, operator, [
+            methodName,
+            staticType.displayName
+          ]);
+        }
+      } else if (_enableHints &&
+          _shouldReportMissingMember(propagatedType, propagatedMethod) &&
+          !_memberFoundInSubclass(
+              propagatedType.element, methodName, true, false)) {
+        _recordUndefinedToken(propagatedType.element,
+            HintCode.UNDEFINED_OPERATOR, operator, [
+          methodName,
+          propagatedType.displayName
+        ]);
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitPropertyAccess(PropertyAccess node) {
+    Expression target = node.realTarget;
+    if (target is SuperExpression && !_isSuperInValidContext(target)) {
+      return null;
+    }
+    SimpleIdentifier propertyName = node.propertyName;
+    _resolvePropertyAccess(target, propertyName,
+        node.operator.type == sc.TokenType.QUESTION_PERIOD);
+    return null;
+  }
+
+  @override
+  Object visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    ClassElement enclosingClass = _resolver.enclosingClass;
+    if (enclosingClass == null) {
+      // TODO(brianwilkerson) Report this error.
+      return null;
+    }
+    SimpleIdentifier name = node.constructorName;
+    ConstructorElement element;
+    if (name == null) {
+      element = enclosingClass.unnamedConstructor;
+    } else {
+      element = enclosingClass.getNamedConstructor(name.name);
+    }
+    if (element == null) {
+      // TODO(brianwilkerson) Report this error and decide what element to
+      // associate with the node.
+      return null;
+    }
+    if (name != null) {
+      name.staticElement = element;
+    }
+    node.staticElement = element;
+    ArgumentList argumentList = node.argumentList;
+    List<ParameterElement> parameters =
+        _resolveArgumentsToFunction(false, argumentList, element);
+    if (parameters != null) {
+      argumentList.correspondingStaticParameters = parameters;
+    }
+    return null;
+  }
+
+  @override
+  Object visitSimpleFormalParameter(SimpleFormalParameter node) {
+    _setMetadataForParameter(node.element, node);
+    return null;
+  }
+
+  @override
+  Object visitSimpleIdentifier(SimpleIdentifier node) {
+    //
+    // Synthetic identifiers have been already reported during parsing.
+    //
+    if (node.isSynthetic) {
+      return null;
+    }
+    //
+    // We ignore identifiers that have already been resolved, such as
+    // identifiers representing the name in a declaration.
+    //
+    if (node.staticElement != null) {
+      return null;
+    }
+    //
+    // The name dynamic denotes a Type object even though dynamic is not a
+    // class.
+    //
+    if (node.name == _dynamicType.name) {
+      node.staticElement = _dynamicType.element;
+      node.staticType = _typeType;
+      return null;
+    }
+    //
+    // Otherwise, the node should be resolved.
+    //
+    Element element = _resolveSimpleIdentifier(node);
+    ClassElement enclosingClass = _resolver.enclosingClass;
+    if (_isFactoryConstructorReturnType(node) &&
+        !identical(element, enclosingClass)) {
+      _resolver.reportErrorForNode(
+          CompileTimeErrorCode.INVALID_FACTORY_NAME_NOT_A_CLASS, node);
+    } else if (_isConstructorReturnType(node) &&
+        !identical(element, enclosingClass)) {
+      _resolver.reportErrorForNode(
+          CompileTimeErrorCode.INVALID_CONSTRUCTOR_NAME, node);
+      element = null;
+    } else if (element == null ||
+        (element is PrefixElement && !_isValidAsPrefix(node))) {
+      // TODO(brianwilkerson) Recover from this error.
+      if (_isConstructorReturnType(node)) {
+        _resolver.reportErrorForNode(
+            CompileTimeErrorCode.INVALID_CONSTRUCTOR_NAME, node);
+      } else if (node.parent is Annotation) {
+        Annotation annotation = node.parent as Annotation;
+        _resolver.reportErrorForNode(
+            CompileTimeErrorCode.INVALID_ANNOTATION, annotation);
+      } else {
+        _recordUndefinedNode(_resolver.enclosingClass,
+            StaticWarningCode.UNDEFINED_IDENTIFIER, node, [node.name]);
+      }
+    }
+    node.staticElement = element;
+    if (node.inSetterContext() &&
+        node.inGetterContext() &&
+        enclosingClass != null) {
+      InterfaceType enclosingType = enclosingClass.type;
+      AuxiliaryElements auxiliaryElements = new AuxiliaryElements(
+          _lookUpGetter(null, enclosingType, node.name), null);
+      node.auxiliaryElements = auxiliaryElements;
+    }
+    //
+    // Validate annotation element.
+    //
+    if (node.parent is Annotation) {
+      Annotation annotation = node.parent as Annotation;
+      _resolveAnnotationElement(annotation);
+    }
+    return null;
+  }
+
+  @override
+  Object visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    ClassElementImpl enclosingClass = _resolver.enclosingClass;
+    if (enclosingClass == null) {
+      // TODO(brianwilkerson) Report this error.
+      return null;
+    }
+    InterfaceType superType = enclosingClass.supertype;
+    if (superType == null) {
+      // TODO(brianwilkerson) Report this error.
+      return null;
+    }
+    SimpleIdentifier name = node.constructorName;
+    String superName = name != null ? name.name : null;
+    ConstructorElement element =
+        superType.lookUpConstructor(superName, _definingLibrary);
+    if (element == null ||
+        (!enclosingClass.mixinErrorsReported &&
+            !enclosingClass.isSuperConstructorAccessible(element))) {
+      if (name != null) {
+        _resolver.reportErrorForNode(
+            CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER, node, [
+          superType.displayName,
+          name
+        ]);
+      } else {
+        _resolver.reportErrorForNode(
+            CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT,
+            node, [superType.displayName]);
+      }
+      return null;
+    } else {
+      if (element.isFactory) {
+        _resolver.reportErrorForNode(
+            CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR, node, [element]);
+      }
+    }
+    if (name != null) {
+      name.staticElement = element;
+    }
+    node.staticElement = element;
+    ArgumentList argumentList = node.argumentList;
+    List<ParameterElement> parameters = _resolveArgumentsToFunction(
+        isInConstConstructor, argumentList, element);
+    if (parameters != null) {
+      argumentList.correspondingStaticParameters = parameters;
+    }
+    return null;
+  }
+
+  @override
+  Object visitSuperExpression(SuperExpression node) {
+    if (!_isSuperInValidContext(node)) {
+      _resolver.reportErrorForNode(
+          CompileTimeErrorCode.SUPER_IN_INVALID_CONTEXT, node);
+    }
+    return super.visitSuperExpression(node);
+  }
+
+  @override
+  Object visitTypeParameter(TypeParameter node) {
+    _setMetadata(node.element, node);
+    return null;
+  }
+
+  @override
+  Object visitVariableDeclaration(VariableDeclaration node) {
+    _setMetadata(node.element, node);
+    return null;
+  }
+
+  /**
+   * Generate annotation elements for each of the annotations in the
+   * [annotationList] and add them to the given list of [annotations].
+   */
+  void _addAnnotations(List<ElementAnnotationImpl> annotationList,
+      NodeList<Annotation> annotations) {
+    int annotationCount = annotations.length;
+    for (int i = 0; i < annotationCount; i++) {
+      Annotation annotation = annotations[i];
+      Element resolvedElement = annotation.element;
+      if (resolvedElement != null) {
+        ElementAnnotationImpl elementAnnotation =
+            new ElementAnnotationImpl(resolvedElement);
+        annotation.elementAnnotation = elementAnnotation;
+        annotationList.add(elementAnnotation);
+      }
+    }
+  }
+
+  /**
+   * Given that we have found code to invoke the given [element], return the
+   * error code that should be reported, or `null` if no error should be
+   * reported. The [target] is the target of the invocation, or `null` if there
+   * was no target. The flag [useStaticContext] should be `true` if the
+   * invocation is in a static constant (does not have access to instance state.
+   */
+  ErrorCode _checkForInvocationError(
+      Expression target, bool useStaticContext, Element element) {
+    // Prefix is not declared, instead "prefix.id" are declared.
+    if (element is PrefixElement) {
+      element = null;
+    }
+    if (element is PropertyAccessorElement) {
+      //
+      // This is really a function expression invocation.
+      //
+      // TODO(brianwilkerson) Consider the possibility of re-writing the AST.
+      FunctionType getterType = element.type;
+      if (getterType != null) {
+        DartType returnType = getterType.returnType;
+        if (!_isExecutableType(returnType)) {
+          return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION;
+        }
+      }
+    } else if (element is ExecutableElement) {
+      return null;
+    } else if (element is MultiplyDefinedElement) {
+      // The error has already been reported
+      return null;
+    } else if (element == null && target is SuperExpression) {
+      // TODO(jwren) We should split the UNDEFINED_METHOD into two error codes,
+      // this one, and a code that describes the situation where the method was
+      // found, but it was not accessible from the current library.
+      return StaticTypeWarningCode.UNDEFINED_SUPER_METHOD;
+    } else {
+      //
+      // This is really a function expression invocation.
+      //
+      // TODO(brianwilkerson) Consider the possibility of re-writing the AST.
+      if (element is PropertyInducingElement) {
+        PropertyAccessorElement getter = element.getter;
+        FunctionType getterType = getter.type;
+        if (getterType != null) {
+          DartType returnType = getterType.returnType;
+          if (!_isExecutableType(returnType)) {
+            return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION;
+          }
+        }
+      } else if (element is VariableElement) {
+        DartType variableType = element.type;
+        if (!_isExecutableType(variableType)) {
+          return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION;
+        }
+      } else {
+        if (target == null) {
+          ClassElement enclosingClass = _resolver.enclosingClass;
+          if (enclosingClass == null) {
+            return StaticTypeWarningCode.UNDEFINED_FUNCTION;
+          } else if (element == null) {
+            // Proxy-conditional warning, based on state of
+            // resolver.getEnclosingClass()
+            return StaticTypeWarningCode.UNDEFINED_METHOD;
+          } else {
+            return StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION;
+          }
+        } else {
+          DartType targetType;
+          if (useStaticContext) {
+            targetType = _getStaticType(target);
+          } else {
+            // Compute and use the propagated type, if it is null, then it may
+            // be the case that static type is some type, in which the static
+            // type should be used.
+            targetType = target.bestType;
+          }
+          if (targetType == null) {
+            return StaticTypeWarningCode.UNDEFINED_FUNCTION;
+          } else if (!targetType.isDynamic && !targetType.isBottom) {
+            // Proxy-conditional warning, based on state of
+            // targetType.getElement()
+            return StaticTypeWarningCode.UNDEFINED_METHOD;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Check that the given index [expression] was resolved, otherwise a
+   * [StaticTypeWarningCode.UNDEFINED_OPERATOR] is generated. The [target] is
+   * the target of the expression. The [methodName] is the name of the operator
+   * associated with the context of using of the given index expression.
+   */
+  bool _checkForUndefinedIndexOperator(IndexExpression expression,
+      Expression target, String methodName, MethodElement staticMethod,
+      MethodElement propagatedMethod, DartType staticType,
+      DartType propagatedType) {
+    bool shouldReportMissingMember_static =
+        _shouldReportMissingMember(staticType, staticMethod);
+    bool shouldReportMissingMember_propagated =
+        !shouldReportMissingMember_static &&
+            _enableHints &&
+            _shouldReportMissingMember(propagatedType, propagatedMethod) &&
+            !_memberFoundInSubclass(
+                propagatedType.element, methodName, true, false);
+    if (shouldReportMissingMember_static ||
+        shouldReportMissingMember_propagated) {
+      sc.Token leftBracket = expression.leftBracket;
+      sc.Token rightBracket = expression.rightBracket;
+      ErrorCode errorCode;
+      if (shouldReportMissingMember_static) {
+        if (target is SuperExpression) {
+          errorCode = StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR;
+        } else {
+          errorCode = StaticTypeWarningCode.UNDEFINED_OPERATOR;
+        }
+      } else {
+        errorCode = HintCode.UNDEFINED_OPERATOR;
+      }
+      DartType type =
+          shouldReportMissingMember_static ? staticType : propagatedType;
+      if (leftBracket == null || rightBracket == null) {
+        _recordUndefinedNode(type.element, errorCode, expression, [
+          methodName,
+          type.displayName
+        ]);
+      } else {
+        int offset = leftBracket.offset;
+        int length = rightBracket.offset - offset + 1;
+        _recordUndefinedOffset(type.element, errorCode, offset, length, [
+          methodName,
+          type.displayName
+        ]);
+      }
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Given an [argumentList] and the executable [element] that  will be invoked
+   * using those arguments, compute the list of parameters that correspond to
+   * the list of arguments. Return the parameters that correspond to the
+   * arguments, or `null` if no correspondence could be computed.
+   */
+  List<ParameterElement> _computeCorrespondingParameters(
+      ArgumentList argumentList, Element element) {
+    if (element is PropertyAccessorElement) {
+      //
+      // This is an invocation of the call method defined on the value returned
+      // by the getter.
+      //
+      FunctionType getterType = element.type;
+      if (getterType != null) {
+        DartType getterReturnType = getterType.returnType;
+        if (getterReturnType is InterfaceType) {
+          MethodElement callMethod = getterReturnType.lookUpMethod(
+              FunctionElement.CALL_METHOD_NAME, _definingLibrary);
+          if (callMethod != null) {
+            return _resolveArgumentsToFunction(false, argumentList, callMethod);
+          }
+        } else if (getterReturnType is FunctionType) {
+          List<ParameterElement> parameters = getterReturnType.parameters;
+          return _resolveArgumentsToParameters(false, argumentList, parameters);
+        }
+      }
+    } else if (element is ExecutableElement) {
+      return _resolveArgumentsToFunction(false, argumentList, element);
+    } else if (element is VariableElement) {
+      VariableElement variable = element;
+      DartType type = _promoteManager.getStaticType(variable);
+      if (type is FunctionType) {
+        FunctionType functionType = type;
+        List<ParameterElement> parameters = functionType.parameters;
+        return _resolveArgumentsToParameters(false, argumentList, parameters);
+      } else if (type is InterfaceType) {
+        // "call" invocation
+        MethodElement callMethod = type.lookUpMethod(
+            FunctionElement.CALL_METHOD_NAME, _definingLibrary);
+        if (callMethod != null) {
+          List<ParameterElement> parameters = callMethod.parameters;
+          return _resolveArgumentsToParameters(false, argumentList, parameters);
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * If the given [element] is a setter, return the getter associated with it.
+   * Otherwise, return the element unchanged.
+   */
+  Element _convertSetterToGetter(Element element) {
+    // TODO(brianwilkerson) Determine whether and why the element could ever be
+    // a setter.
+    if (element is PropertyAccessorElement) {
+      return element.variable.getter;
+    }
+    return element;
+  }
+
+  /**
+   * Return `true` if the given [element] is not a proxy. See
+   * [ClassElement.isOrInheritsProxy].
+   */
+  bool _doesntHaveProxy(Element element) =>
+      !(element is ClassElement && element.isOrInheritsProxy);
+
+  /**
+   * Look for any declarations of the given [identifier] that are imported using
+   * a prefix. Return the element that was found, or `null` if the name is not
+   * imported using a prefix.
+   */
+  Element _findImportWithoutPrefix(SimpleIdentifier identifier) {
+    Element element = null;
+    Scope nameScope = _resolver.nameScope;
+    for (ImportElement importElement in _definingLibrary.imports) {
+      PrefixElement prefixElement = importElement.prefix;
+      if (prefixElement != null) {
+        Identifier prefixedIdentifier = new SyntheticIdentifier(
+            "${prefixElement.name}.${identifier.name}", identifier);
+        Element importedElement =
+            nameScope.lookup(prefixedIdentifier, _definingLibrary);
+        if (importedElement != null) {
+          if (element == null) {
+            element = importedElement;
+          } else {
+            element = MultiplyDefinedElementImpl.fromElements(
+                _definingLibrary.context, element, importedElement);
+          }
+        }
+      }
+    }
+    return element;
+  }
+
+  /**
+   * Assuming that the given [expression] is a prefix for a deferred import,
+   * return the library that is being imported.
+   */
+  LibraryElement _getImportedLibrary(Expression expression) {
+    PrefixElement prefixElement =
+        (expression as SimpleIdentifier).staticElement as PrefixElement;
+    List<ImportElement> imports =
+        prefixElement.enclosingElement.getImportsWithPrefix(prefixElement);
+    return imports[0].importedLibrary;
+  }
+
+  /**
+   * Return the name of the method invoked by the given postfix [expression].
+   */
+  String _getPostfixOperator(PostfixExpression expression) =>
+      (expression.operator.type == sc.TokenType.PLUS_PLUS)
+          ? sc.TokenType.PLUS.lexeme
+          : sc.TokenType.MINUS.lexeme;
+
+  /**
+   * Return the name of the method invoked by the given postfix [expression].
+   */
+  String _getPrefixOperator(PrefixExpression expression) {
+    sc.Token operator = expression.operator;
+    sc.TokenType operatorType = operator.type;
+    if (operatorType == sc.TokenType.PLUS_PLUS) {
+      return sc.TokenType.PLUS.lexeme;
+    } else if (operatorType == sc.TokenType.MINUS_MINUS) {
+      return sc.TokenType.MINUS.lexeme;
+    } else if (operatorType == sc.TokenType.MINUS) {
+      return "unary-";
+    } else {
+      return operator.lexeme;
+    }
+  }
+
+  /**
+   * Return the propagated type of the given [expression] that is to be used for
+   * type analysis.
+   */
+  DartType _getPropagatedType(Expression expression) {
+    DartType propagatedType = _resolveTypeParameter(expression.propagatedType);
+    if (propagatedType is FunctionType) {
+      //
+      // All function types are subtypes of 'Function', which is itself a
+      // subclass of 'Object'.
+      //
+      propagatedType = _resolver.typeProvider.functionType;
+    }
+    return propagatedType;
+  }
+
+  /**
+   * Return the static type of the given [expression] that is to be used for
+   * type analysis.
+   */
+  DartType _getStaticType(Expression expression) {
+    if (expression is NullLiteral) {
+      return _resolver.typeProvider.bottomType;
+    }
+    DartType staticType = _resolveTypeParameter(expression.staticType);
+    if (staticType is FunctionType) {
+      //
+      // All function types are subtypes of 'Function', which is itself a
+      // subclass of 'Object'.
+      //
+      staticType = _resolver.typeProvider.functionType;
+    }
+    return staticType;
+  }
+
+  /**
+   * Return `true` if the given [expression] is a prefix for a deferred import.
+   */
+  bool _isDeferredPrefix(Expression expression) {
+    if (expression is! SimpleIdentifier) {
+      return false;
+    }
+    Element element = (expression as SimpleIdentifier).staticElement;
+    if (element is! PrefixElement) {
+      return false;
+    }
+    PrefixElement prefixElement = element as PrefixElement;
+    List<ImportElement> imports =
+        prefixElement.enclosingElement.getImportsWithPrefix(prefixElement);
+    if (imports.length != 1) {
+      return false;
+    }
+    return imports[0].isDeferred;
+  }
+
+  /**
+   * Return `true` if the given [type] represents an object that could be
+   * invoked using the call operator '()'.
+   */
+  bool _isExecutableType(DartType type) {
+    if (type.isDynamic || type is FunctionType) {
+      return true;
+    } else if (!_enableStrictCallChecks &&
+        (type.isDartCoreFunction || type.isObject)) {
+      return true;
+    } else if (type is InterfaceType) {
+      ClassElement classElement = type.element;
+      // 16078 from Gilad: If the type is a Functor with the @proxy annotation,
+      // treat it as an executable type.
+      // example code: NonErrorResolverTest.
+      // test_invocationOfNonFunction_proxyOnFunctionClass()
+      if (classElement.isProxy &&
+          type.isSubtypeOf(_resolver.typeProvider.functionType)) {
+        return true;
+      }
+      MethodElement methodElement = classElement.lookUpMethod(
+          FunctionElement.CALL_METHOD_NAME, _definingLibrary);
+      return methodElement != null;
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the given [element] is a static element.
+   */
+  bool _isStatic(Element element) {
+    if (element is ExecutableElement) {
+      return element.isStatic;
+    } else if (element is PropertyInducingElement) {
+      return element.isStatic;
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the given [node] can validly be resolved to a prefix:
+   * * it is the prefix in an import directive, or
+   * * it is the prefix in a prefixed identifier.
+   */
+  bool _isValidAsPrefix(SimpleIdentifier node) {
+    AstNode parent = node.parent;
+    if (parent is ImportDirective) {
+      return identical(parent.prefix, node);
+    } else if (parent is PrefixedIdentifier) {
+      return true;
+    } else if (parent is MethodInvocation) {
+      return identical(parent.target, node);
+    }
+    return false;
+  }
+
+  /**
+   * Return the target of a break or continue statement, and update the static
+   * element of its label (if any). The [parentNode] is the AST node of the
+   * break or continue statement. The [labelNode] is the label contained in that
+   * statement (if any). The flag [isContinue] is `true` if the node being
+   * visited is a continue statement.
+   */
+  AstNode _lookupBreakOrContinueTarget(
+      AstNode parentNode, SimpleIdentifier labelNode, bool isContinue) {
+    if (labelNode == null) {
+      return _resolver.implicitLabelScope.getTarget(isContinue);
+    } else {
+      LabelScope labelScope = _resolver.labelScope;
+      if (labelScope == null) {
+        // There are no labels in scope, so by definition the label is
+        // undefined.
+        _resolver.reportErrorForNode(
+            CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, [labelNode.name]);
+        return null;
+      }
+      LabelScope definingScope = labelScope.lookup(labelNode.name);
+      if (definingScope == null) {
+        // No definition of the given label name could be found in any
+        // enclosing scope.
+        _resolver.reportErrorForNode(
+            CompileTimeErrorCode.LABEL_UNDEFINED, labelNode, [labelNode.name]);
+        return null;
+      }
+      // The target has been found.
+      labelNode.staticElement = definingScope.element;
+      ExecutableElement labelContainer = definingScope.element
+          .getAncestor((element) => element is ExecutableElement);
+      if (!identical(labelContainer, _resolver.enclosingFunction)) {
+        _resolver.reportErrorForNode(CompileTimeErrorCode.LABEL_IN_OUTER_SCOPE,
+            labelNode, [labelNode.name]);
+      }
+      return definingScope.node;
+    }
+  }
+
+  /**
+   * Look up the getter with the given [getterName] in the given [type]. Return
+   * the element representing the getter that was found, or `null` if there is
+   * no getter with the given name. The [target] is the target of the
+   * invocation, or `null` if there is no target.
+   */
+  PropertyAccessorElement _lookUpGetter(
+      Expression target, DartType type, String getterName) {
+    type = _resolveTypeParameter(type);
+    if (type is InterfaceType) {
+      InterfaceType interfaceType = type;
+      PropertyAccessorElement accessor;
+      if (target is SuperExpression) {
+        accessor = interfaceType.lookUpGetterInSuperclass(
+            getterName, _definingLibrary);
+      } else {
+        accessor = interfaceType.lookUpGetter(getterName, _definingLibrary);
+      }
+      if (accessor != null) {
+        return accessor;
+      }
+      return _lookUpGetterInInterfaces(
+          interfaceType, false, getterName, new HashSet<ClassElement>());
+    }
+    return null;
+  }
+
+  /**
+   * Look up the getter with the given [getterName] in the interfaces
+   * implemented by the given [targetType], either directly or indirectly.
+   * Return the element representing the getter that was found, or `null` if
+   * there is no getter with the given name. The flag [includeTargetType] should
+   * be `true` if the search should include the target type. The
+   * [visitedInterfaces] is a set containing all of the interfaces that have
+   * been examined, used to prevent infinite recursion and to optimize the
+   * search.
+   */
+  PropertyAccessorElement _lookUpGetterInInterfaces(InterfaceType targetType,
+      bool includeTargetType, String getterName,
+      HashSet<ClassElement> visitedInterfaces) {
+    // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the
+    // specification (titled "Inheritance and Overriding" under "Interfaces")
+    // describes a much more complex scheme for finding the inherited member.
+    // We need to follow that scheme. The code below should cover the 80% case.
+    ClassElement targetClass = targetType.element;
+    if (visitedInterfaces.contains(targetClass)) {
+      return null;
+    }
+    visitedInterfaces.add(targetClass);
+    if (includeTargetType) {
+      PropertyAccessorElement getter = targetType.getGetter(getterName);
+      if (getter != null && getter.isAccessibleIn(_definingLibrary)) {
+        return getter;
+      }
+    }
+    for (InterfaceType interfaceType in targetType.interfaces) {
+      PropertyAccessorElement getter = _lookUpGetterInInterfaces(
+          interfaceType, true, getterName, visitedInterfaces);
+      if (getter != null) {
+        return getter;
+      }
+    }
+    for (InterfaceType mixinType in targetType.mixins.reversed) {
+      PropertyAccessorElement getter = _lookUpGetterInInterfaces(
+          mixinType, true, getterName, visitedInterfaces);
+      if (getter != null) {
+        return getter;
+      }
+    }
+    InterfaceType superclass = targetType.superclass;
+    if (superclass == null) {
+      return null;
+    }
+    return _lookUpGetterInInterfaces(
+        superclass, true, getterName, visitedInterfaces);
+  }
+
+  /**
+   * Look up the method or getter with the given [memberName] in the given
+   * [type]. Return the element representing the method or getter that was
+   * found, or `null` if there is no method or getter with the given name.
+   */
+  ExecutableElement _lookupGetterOrMethod(DartType type, String memberName) {
+    type = _resolveTypeParameter(type);
+    if (type is InterfaceType) {
+      InterfaceType interfaceType = type;
+      ExecutableElement member =
+          interfaceType.lookUpMethod(memberName, _definingLibrary);
+      if (member != null) {
+        return member;
+      }
+      member = interfaceType.lookUpGetter(memberName, _definingLibrary);
+      if (member != null) {
+        return member;
+      }
+      return _lookUpGetterOrMethodInInterfaces(
+          interfaceType, false, memberName, new HashSet<ClassElement>());
+    }
+    return null;
+  }
+
+  /**
+   * Look up the method or getter with the given [memberName] in the interfaces
+   * implemented by the given [targetType], either directly or indirectly.
+   * Return the element representing the method or getter that was found, or
+   * `null` if there is no method or getter with the given name. The flag
+   * [includeTargetType] should be `true` if the search should include the
+   * target type. The [visitedInterfaces] is a set containing all of the
+   * interfaces that have been examined, used to prevent infinite recursion and
+   * to optimize the search.
+   */
+  ExecutableElement _lookUpGetterOrMethodInInterfaces(InterfaceType targetType,
+      bool includeTargetType, String memberName,
+      HashSet<ClassElement> visitedInterfaces) {
+    // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the
+    // specification (titled "Inheritance and Overriding" under "Interfaces")
+    // describes a much more complex scheme for finding the inherited member.
+    // We need to follow that scheme. The code below should cover the 80% case.
+    ClassElement targetClass = targetType.element;
+    if (visitedInterfaces.contains(targetClass)) {
+      return null;
+    }
+    visitedInterfaces.add(targetClass);
+    if (includeTargetType) {
+      ExecutableElement member = targetType.getMethod(memberName);
+      if (member != null) {
+        return member;
+      }
+      member = targetType.getGetter(memberName);
+      if (member != null) {
+        return member;
+      }
+    }
+    for (InterfaceType interfaceType in targetType.interfaces) {
+      ExecutableElement member = _lookUpGetterOrMethodInInterfaces(
+          interfaceType, true, memberName, visitedInterfaces);
+      if (member != null) {
+        return member;
+      }
+    }
+    for (InterfaceType mixinType in targetType.mixins.reversed) {
+      ExecutableElement member = _lookUpGetterOrMethodInInterfaces(
+          mixinType, true, memberName, visitedInterfaces);
+      if (member != null) {
+        return member;
+      }
+    }
+    InterfaceType superclass = targetType.superclass;
+    if (superclass == null) {
+      return null;
+    }
+    return _lookUpGetterOrMethodInInterfaces(
+        superclass, true, memberName, visitedInterfaces);
+  }
+
+  /**
+   * Look up the method with the given [methodName] in the given [type]. Return
+   * the element representing the method that was found, or `null` if there is
+   * no method with the given name. The [target] is the target of the
+   * invocation, or `null` if there is no target.
+   */
+  MethodElement _lookUpMethod(
+      Expression target, DartType type, String methodName) {
+    type = _resolveTypeParameter(type);
+    if (type is InterfaceType) {
+      InterfaceType interfaceType = type;
+      MethodElement method;
+      if (target is SuperExpression) {
+        method = interfaceType.lookUpMethodInSuperclass(
+            methodName, _definingLibrary);
+      } else {
+        method = interfaceType.lookUpMethod(methodName, _definingLibrary);
+      }
+      if (method != null) {
+        return method;
+      }
+      return _lookUpMethodInInterfaces(
+          interfaceType, false, methodName, new HashSet<ClassElement>());
+    }
+    return null;
+  }
+
+  /**
+   * Look up the method with the given [methodName] in the interfaces
+   * implemented by the given [targetType], either directly or indirectly.
+   * Return the element representing the method that was found, or `null` if
+   * there is no method with the given name. The flag [includeTargetType] should
+   * be `true` if the search should include the target type. The
+   * [visitedInterfaces] is a set containing all of the interfaces that have
+   * been examined, used to prevent infinite recursion and to optimize the
+   * search.
+   */
+  MethodElement _lookUpMethodInInterfaces(InterfaceType targetType,
+      bool includeTargetType, String methodName,
+      HashSet<ClassElement> visitedInterfaces) {
+    // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the
+    // specification (titled "Inheritance and Overriding" under "Interfaces")
+    // describes a much more complex scheme for finding the inherited member.
+    // We need to follow that scheme. The code below should cover the 80% case.
+    ClassElement targetClass = targetType.element;
+    if (visitedInterfaces.contains(targetClass)) {
+      return null;
+    }
+    visitedInterfaces.add(targetClass);
+    if (includeTargetType) {
+      MethodElement method = targetType.getMethod(methodName);
+      if (method != null && method.isAccessibleIn(_definingLibrary)) {
+        return method;
+      }
+    }
+    for (InterfaceType interfaceType in targetType.interfaces) {
+      MethodElement method = _lookUpMethodInInterfaces(
+          interfaceType, true, methodName, visitedInterfaces);
+      if (method != null) {
+        return method;
+      }
+    }
+    for (InterfaceType mixinType in targetType.mixins.reversed) {
+      MethodElement method = _lookUpMethodInInterfaces(
+          mixinType, true, methodName, visitedInterfaces);
+      if (method != null) {
+        return method;
+      }
+    }
+    InterfaceType superclass = targetType.superclass;
+    if (superclass == null) {
+      return null;
+    }
+    return _lookUpMethodInInterfaces(
+        superclass, true, methodName, visitedInterfaces);
+  }
+
+  /**
+   * Look up the setter with the given [setterName] in the given [type]. Return
+   * the element representing the setter that was found, or `null` if there is
+   * no setter with the given name. The [target] is the target of the
+   * invocation, or `null` if there is no target.
+   */
+  PropertyAccessorElement _lookUpSetter(
+      Expression target, DartType type, String setterName) {
+    type = _resolveTypeParameter(type);
+    if (type is InterfaceType) {
+      InterfaceType interfaceType = type;
+      PropertyAccessorElement accessor;
+      if (target is SuperExpression) {
+        accessor = interfaceType.lookUpSetterInSuperclass(
+            setterName, _definingLibrary);
+      } else {
+        accessor = interfaceType.lookUpSetter(setterName, _definingLibrary);
+      }
+      if (accessor != null) {
+        return accessor;
+      }
+      return _lookUpSetterInInterfaces(
+          interfaceType, false, setterName, new HashSet<ClassElement>());
+    }
+    return null;
+  }
+
+  /**
+   * Look up the setter with the given [setterName] in the interfaces
+   * implemented by the given [targetType], either directly or indirectly.
+   * Return the element representing the setter that was found, or `null` if
+   * there is no setter with the given name. The [targetType] is the type in
+   * which the setter might be defined. The flag [includeTargetType] should be
+   * `true` if the search should include the target type. The
+   * [visitedInterfaces] is a set containing all of the interfaces that have
+   * been examined, used to prevent infinite recursion and to optimize the
+   * search.
+   */
+  PropertyAccessorElement _lookUpSetterInInterfaces(InterfaceType targetType,
+      bool includeTargetType, String setterName,
+      HashSet<ClassElement> visitedInterfaces) {
+    // TODO(brianwilkerson) This isn't correct. Section 8.1.1 of the
+    // specification (titled "Inheritance and Overriding" under "Interfaces")
+    // describes a much more complex scheme for finding the inherited member.
+    // We need to follow that scheme. The code below should cover the 80% case.
+    ClassElement targetClass = targetType.element;
+    if (visitedInterfaces.contains(targetClass)) {
+      return null;
+    }
+    visitedInterfaces.add(targetClass);
+    if (includeTargetType) {
+      PropertyAccessorElement setter = targetType.getSetter(setterName);
+      if (setter != null && setter.isAccessibleIn(_definingLibrary)) {
+        return setter;
+      }
+    }
+    for (InterfaceType interfaceType in targetType.interfaces) {
+      PropertyAccessorElement setter = _lookUpSetterInInterfaces(
+          interfaceType, true, setterName, visitedInterfaces);
+      if (setter != null) {
+        return setter;
+      }
+    }
+    for (InterfaceType mixinType in targetType.mixins.reversed) {
+      PropertyAccessorElement setter = _lookUpSetterInInterfaces(
+          mixinType, true, setterName, visitedInterfaces);
+      if (setter != null) {
+        return setter;
+      }
+    }
+    InterfaceType superclass = targetType.superclass;
+    if (superclass == null) {
+      return null;
+    }
+    return _lookUpSetterInInterfaces(
+        superclass, true, setterName, visitedInterfaces);
+  }
+
+  /**
+   * Given some class [element], this method uses [_subtypeManager] to find the
+   * set of all subtypes; the subtypes are then searched for a member (method,
+   * getter, or setter), that has the given [memberName]. The flag [asMethod]
+   * should be `true` if the methods should be searched for in the subtypes. The
+   * flag [asAccessor] should be `true` if the accessors (getters and setters)
+   * should be searched for in the subtypes.
+   */
+  bool _memberFoundInSubclass(
+      Element element, String memberName, bool asMethod, bool asAccessor) {
+    if (element is ClassElement) {
+      _subtypeManager.ensureLibraryVisited(_definingLibrary);
+      HashSet<ClassElement> subtypeElements =
+          _subtypeManager.computeAllSubtypes(element);
+      for (ClassElement subtypeElement in subtypeElements) {
+        if (asMethod && subtypeElement.getMethod(memberName) != null) {
+          return true;
+        } else if (asAccessor &&
+            (subtypeElement.getGetter(memberName) != null ||
+                subtypeElement.getSetter(memberName) != null)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Return the binary operator that is invoked by the given compound assignment
+   * [operator].
+   */
+  sc.TokenType _operatorFromCompoundAssignment(sc.TokenType operator) {
+    while (true) {
+      if (operator == sc.TokenType.AMPERSAND_EQ) {
+        return sc.TokenType.AMPERSAND;
+      } else if (operator == sc.TokenType.BAR_EQ) {
+        return sc.TokenType.BAR;
+      } else if (operator == sc.TokenType.CARET_EQ) {
+        return sc.TokenType.CARET;
+      } else if (operator == sc.TokenType.GT_GT_EQ) {
+        return sc.TokenType.GT_GT;
+      } else if (operator == sc.TokenType.LT_LT_EQ) {
+        return sc.TokenType.LT_LT;
+      } else if (operator == sc.TokenType.MINUS_EQ) {
+        return sc.TokenType.MINUS;
+      } else if (operator == sc.TokenType.PERCENT_EQ) {
+        return sc.TokenType.PERCENT;
+      } else if (operator == sc.TokenType.PLUS_EQ) {
+        return sc.TokenType.PLUS;
+      } else if (operator == sc.TokenType.SLASH_EQ) {
+        return sc.TokenType.SLASH;
+      } else if (operator == sc.TokenType.STAR_EQ) {
+        return sc.TokenType.STAR;
+      } else if (operator == sc.TokenType.TILDE_SLASH_EQ) {
+        return sc.TokenType.TILDE_SLASH;
+      } else {
+        // Internal error: Unmapped assignment operator.
+        AnalysisEngine.instance.logger.logError(
+            "Failed to map ${operator.lexeme} to it's corresponding operator");
+        return operator;
+      }
+      break;
+    }
+  }
+
+  /**
+   * Record that the given [node] is undefined, causing an error to be reported
+   * if appropriate. The [declaringElement] is the element inside which no
+   * declaration was found. If this element is a proxy, no error will be
+   * reported. If null, then an error will always be reported. The [errorCode]
+   * is the error code to report. The [arguments] are the arguments to the error
+   * message.
+   */
+  void _recordUndefinedNode(Element declaringElement, ErrorCode errorCode,
+      AstNode node, List<Object> arguments) {
+    if (_doesntHaveProxy(declaringElement)) {
+      _resolver.reportErrorForNode(errorCode, node, arguments);
+    }
+  }
+
+  /**
+   * Record that the given [offset]/[length] is undefined, causing an error to
+   * be reported if appropriate. The [declaringElement] is the element inside
+   * which no declaration was found. If this element is a proxy, no error will
+   * be reported. If null, then an error will always be reported. The
+   * [errorCode] is the error code to report. The [arguments] are arguments to
+   * the error message.
+   */
+  void _recordUndefinedOffset(Element declaringElement, ErrorCode errorCode,
+      int offset, int length, List<Object> arguments) {
+    if (_doesntHaveProxy(declaringElement)) {
+      _resolver.reportErrorForOffset(errorCode, offset, length, arguments);
+    }
+  }
+
+  /**
+   * Record that the given [token] is undefined, causing an error to be reported
+   * if appropriate. The [declaringElement] is the element inside which no
+   * declaration was found. If this element is a proxy, no error will be
+   * reported. If null, then an error will always be reported. The [errorCode]
+   * is the error code to report. The [arguments] are arguments to the error
+   * message.
+   */
+  void _recordUndefinedToken(Element declaringElement, ErrorCode errorCode,
+      sc.Token token, List<Object> arguments) {
+    if (_doesntHaveProxy(declaringElement)) {
+      _resolver.reportErrorForToken(errorCode, token, arguments);
+    }
+  }
+
+  void _resolveAnnotationConstructorInvocationArguments(
+      Annotation annotation, ConstructorElement constructor) {
+    ArgumentList argumentList = annotation.arguments;
+    // error will be reported in ConstantVerifier
+    if (argumentList == null) {
+      return;
+    }
+    // resolve arguments to parameters
+    List<ParameterElement> parameters =
+        _resolveArgumentsToFunction(true, argumentList, constructor);
+    if (parameters != null) {
+      argumentList.correspondingStaticParameters = parameters;
+    }
+  }
+
+  /**
+   * Continues resolution of the given [annotation].
+   */
+  void _resolveAnnotationElement(Annotation annotation) {
+    SimpleIdentifier nameNode1;
+    SimpleIdentifier nameNode2;
+    {
+      Identifier annName = annotation.name;
+      if (annName is PrefixedIdentifier) {
+        PrefixedIdentifier prefixed = annName;
+        nameNode1 = prefixed.prefix;
+        nameNode2 = prefixed.identifier;
+      } else {
+        nameNode1 = annName as SimpleIdentifier;
+        nameNode2 = null;
+      }
+    }
+    SimpleIdentifier nameNode3 = annotation.constructorName;
+    ConstructorElement constructor = null;
+    //
+    // CONST or Class(args)
+    //
+    if (nameNode1 != null && nameNode2 == null && nameNode3 == null) {
+      Element element1 = nameNode1.staticElement;
+      // CONST
+      if (element1 is PropertyAccessorElement) {
+        _resolveAnnotationElementGetter(annotation, element1);
+        return;
+      }
+      // Class(args)
+      if (element1 is ClassElement) {
+        ClassElement classElement = element1;
+        constructor = new InterfaceTypeImpl.con1(classElement)
+            .lookUpConstructor(null, _definingLibrary);
+      }
+    }
+    //
+    // prefix.CONST or prefix.Class() or Class.CONST or Class.constructor(args)
+    //
+    if (nameNode1 != null && nameNode2 != null && nameNode3 == null) {
+      Element element1 = nameNode1.staticElement;
+      Element element2 = nameNode2.staticElement;
+      // Class.CONST - not resolved yet
+      if (element1 is ClassElement) {
+        ClassElement classElement = element1;
+        element2 = classElement.lookUpGetter(nameNode2.name, _definingLibrary);
+      }
+      // prefix.CONST or Class.CONST
+      if (element2 is PropertyAccessorElement) {
+        nameNode2.staticElement = element2;
+        annotation.element = element2;
+        _resolveAnnotationElementGetter(annotation, element2);
+        return;
+      }
+      // prefix.Class()
+      if (element2 is ClassElement) {
+        constructor = element2.unnamedConstructor;
+      }
+      // Class.constructor(args)
+      if (element1 is ClassElement) {
+        ClassElement classElement = element1;
+        constructor = new InterfaceTypeImpl.con1(classElement)
+            .lookUpConstructor(nameNode2.name, _definingLibrary);
+        nameNode2.staticElement = constructor;
+      }
+    }
+    //
+    // prefix.Class.CONST or prefix.Class.constructor(args)
+    //
+    if (nameNode1 != null && nameNode2 != null && nameNode3 != null) {
+      Element element2 = nameNode2.staticElement;
+      // element2 should be ClassElement
+      if (element2 is ClassElement) {
+        ClassElement classElement = element2;
+        String name3 = nameNode3.name;
+        // prefix.Class.CONST
+        PropertyAccessorElement getter =
+            classElement.lookUpGetter(name3, _definingLibrary);
+        if (getter != null) {
+          nameNode3.staticElement = getter;
+          annotation.element = element2;
+          _resolveAnnotationElementGetter(annotation, getter);
+          return;
+        }
+        // prefix.Class.constructor(args)
+        constructor = new InterfaceTypeImpl.con1(classElement)
+            .lookUpConstructor(name3, _definingLibrary);
+        nameNode3.staticElement = constructor;
+      }
+    }
+    // we need constructor
+    if (constructor == null) {
+      _resolver.reportErrorForNode(
+          CompileTimeErrorCode.INVALID_ANNOTATION, annotation);
+      return;
+    }
+    // record element
+    annotation.element = constructor;
+    // resolve arguments
+    _resolveAnnotationConstructorInvocationArguments(annotation, constructor);
+  }
+
+  void _resolveAnnotationElementGetter(
+      Annotation annotation, PropertyAccessorElement accessorElement) {
+    // accessor should be synthetic
+    if (!accessorElement.isSynthetic) {
+      _resolver.reportErrorForNode(
+          CompileTimeErrorCode.INVALID_ANNOTATION, annotation);
+      return;
+    }
+    // variable should be constant
+    VariableElement variableElement = accessorElement.variable;
+    if (!variableElement.isConst) {
+      _resolver.reportErrorForNode(
+          CompileTimeErrorCode.INVALID_ANNOTATION, annotation);
+    }
+    // OK
+    return;
+  }
+
+  /**
+   * Given an [argumentList] and the [executableElement] that will be invoked
+   * using those argument, compute the list of parameters that correspond to the
+   * list of arguments. An error will be reported if any of the arguments cannot
+   * be matched to a parameter. The flag [reportError] should be `true` if a
+   * compile-time error should be reported; or `false` if a compile-time warning
+   * should be reported. Return the parameters that correspond to the arguments,
+   * or `null` if no correspondence could be computed.
+   */
+  List<ParameterElement> _resolveArgumentsToFunction(bool reportError,
+      ArgumentList argumentList, ExecutableElement executableElement) {
+    if (executableElement == null) {
+      return null;
+    }
+    List<ParameterElement> parameters = executableElement.parameters;
+    return _resolveArgumentsToParameters(reportError, argumentList, parameters);
+  }
+
+  /**
+   * Given an [argumentList] and the [parameters] related to the element that
+   * will be invoked using those arguments, compute the list of parameters that
+   * correspond to the list of arguments. An error will be reported if any of
+   * the arguments cannot be matched to a parameter. The flag [reportError]
+   * should be `true` if a compile-time error should be reported; or `false` if
+   * a compile-time warning should be reported. Return the parameters that
+   * correspond to the arguments.
+   */
+  List<ParameterElement> _resolveArgumentsToParameters(bool reportError,
+      ArgumentList argumentList, List<ParameterElement> parameters) {
+    List<ParameterElement> requiredParameters = new List<ParameterElement>();
+    List<ParameterElement> positionalParameters = new List<ParameterElement>();
+    HashMap<String, ParameterElement> namedParameters =
+        new HashMap<String, ParameterElement>();
+    for (ParameterElement parameter in parameters) {
+      ParameterKind kind = parameter.parameterKind;
+      if (kind == ParameterKind.REQUIRED) {
+        requiredParameters.add(parameter);
+      } else if (kind == ParameterKind.POSITIONAL) {
+        positionalParameters.add(parameter);
+      } else {
+        namedParameters[parameter.name] = parameter;
+      }
+    }
+    List<ParameterElement> unnamedParameters =
+        new List<ParameterElement>.from(requiredParameters);
+    unnamedParameters.addAll(positionalParameters);
+    int unnamedParameterCount = unnamedParameters.length;
+    int unnamedIndex = 0;
+    NodeList<Expression> arguments = argumentList.arguments;
+    int argumentCount = arguments.length;
+    List<ParameterElement> resolvedParameters =
+        new List<ParameterElement>(argumentCount);
+    int positionalArgumentCount = 0;
+    HashSet<String> usedNames = new HashSet<String>();
+    bool noBlankArguments = true;
+    for (int i = 0; i < argumentCount; i++) {
+      Expression argument = arguments[i];
+      if (argument is NamedExpression) {
+        SimpleIdentifier nameNode = argument.name.label;
+        String name = nameNode.name;
+        ParameterElement element = namedParameters[name];
+        if (element == null) {
+          ErrorCode errorCode = (reportError
+              ? CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER
+              : StaticWarningCode.UNDEFINED_NAMED_PARAMETER);
+          _resolver.reportErrorForNode(errorCode, nameNode, [name]);
+        } else {
+          resolvedParameters[i] = element;
+          nameNode.staticElement = element;
+        }
+        if (!usedNames.add(name)) {
+          _resolver.reportErrorForNode(
+              CompileTimeErrorCode.DUPLICATE_NAMED_ARGUMENT, nameNode, [name]);
+        }
+      } else {
+        if (argument is SimpleIdentifier && argument.name.isEmpty) {
+          noBlankArguments = false;
+        }
+        positionalArgumentCount++;
+        if (unnamedIndex < unnamedParameterCount) {
+          resolvedParameters[i] = unnamedParameters[unnamedIndex++];
+        }
+      }
+    }
+    if (positionalArgumentCount < requiredParameters.length &&
+        noBlankArguments) {
+      ErrorCode errorCode = (reportError
+          ? CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS
+          : StaticWarningCode.NOT_ENOUGH_REQUIRED_ARGUMENTS);
+      _resolver.reportErrorForNode(errorCode, argumentList, [
+        requiredParameters.length,
+        positionalArgumentCount
+      ]);
+    } else if (positionalArgumentCount > unnamedParameterCount &&
+        noBlankArguments) {
+      ErrorCode errorCode = (reportError
+          ? CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS
+          : StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS);
+      _resolver.reportErrorForNode(errorCode, argumentList, [
+        unnamedParameterCount,
+        positionalArgumentCount
+      ]);
+    }
+    return resolvedParameters;
+  }
+
+  void _resolveBinaryExpression(BinaryExpression node, String methodName) {
+    Expression leftOperand = node.leftOperand;
+    if (leftOperand != null) {
+      DartType staticType = _getStaticType(leftOperand);
+      MethodElement staticMethod =
+          _lookUpMethod(leftOperand, staticType, methodName);
+      node.staticElement = staticMethod;
+      DartType propagatedType = _getPropagatedType(leftOperand);
+      MethodElement propagatedMethod =
+          _lookUpMethod(leftOperand, propagatedType, methodName);
+      node.propagatedElement = propagatedMethod;
+      if (_shouldReportMissingMember(staticType, staticMethod)) {
+        if (leftOperand is SuperExpression) {
+          _recordUndefinedToken(staticType.element,
+              StaticTypeWarningCode.UNDEFINED_SUPER_OPERATOR, node.operator, [
+            methodName,
+            staticType.displayName
+          ]);
+        } else {
+          _recordUndefinedToken(staticType.element,
+              StaticTypeWarningCode.UNDEFINED_OPERATOR, node.operator, [
+            methodName,
+            staticType.displayName
+          ]);
+        }
+      } else if (_enableHints &&
+          _shouldReportMissingMember(propagatedType, propagatedMethod) &&
+          !_memberFoundInSubclass(
+              propagatedType.element, methodName, true, false)) {
+        _recordUndefinedToken(propagatedType.element,
+            HintCode.UNDEFINED_OPERATOR, node.operator, [
+          methodName,
+          propagatedType.displayName
+        ]);
+      }
+    }
+  }
+
+  /**
+   * Resolve the names in the given [combinators] in the scope of the given
+   * [library].
+   */
+  void _resolveCombinators(
+      LibraryElement library, NodeList<Combinator> combinators) {
+    if (library == null) {
+      //
+      // The library will be null if the directive containing the combinators
+      // has a URI that is not valid.
+      //
+      return;
+    }
+    Namespace namespace =
+        new NamespaceBuilder().createExportNamespaceForLibrary(library);
+    for (Combinator combinator in combinators) {
+      NodeList<SimpleIdentifier> names;
+      if (combinator is HideCombinator) {
+        names = combinator.hiddenNames;
+      } else {
+        names = (combinator as ShowCombinator).shownNames;
+      }
+      for (SimpleIdentifier name in names) {
+        String nameStr = name.name;
+        Element element = namespace.get(nameStr);
+        if (element == null) {
+          element = namespace.get("$nameStr=");
+        }
+        if (element != null) {
+          // Ensure that the name always resolves to a top-level variable
+          // rather than a getter or setter
+          if (element is PropertyAccessorElement) {
+            element = (element as PropertyAccessorElement).variable;
+          }
+          name.staticElement = element;
+        }
+      }
+    }
+  }
+
+  /**
+   * Given that we are accessing a property of the given [classElement] with the
+   * given [propertyName], return the element that represents the property.
+   */
+  Element _resolveElement(
+      ClassElementImpl classElement, SimpleIdentifier propertyName) {
+    String name = propertyName.name;
+    Element element = null;
+    if (propertyName.inSetterContext()) {
+      element = classElement.getSetter(name);
+    }
+    if (element == null) {
+      element = classElement.getGetter(name);
+    }
+    if (element == null) {
+      element = classElement.getMethod(name);
+    }
+    if (element != null && element.isAccessibleIn(_definingLibrary)) {
+      return element;
+    }
+    return null;
+  }
+
+  /**
+   * Given an invocation of the form 'm(a1, ..., an)', resolve 'm' to the
+   * element being invoked. If the returned element is a method, then the method
+   * will be invoked. If the returned element is a getter, the getter will be
+   * invoked without arguments and the result of that invocation will then be
+   * invoked with the arguments. The [methodName] is the name of the method
+   * being invoked ('m').
+   */
+  Element _resolveInvokedElement(SimpleIdentifier methodName) {
+    //
+    // Look first in the lexical scope.
+    //
+    Element element = _resolver.nameScope.lookup(methodName, _definingLibrary);
+    if (element == null) {
+      //
+      // If it isn't defined in the lexical scope, and the invocation is within
+      // a class, then look in the inheritance scope.
+      //
+      ClassElement enclosingClass = _resolver.enclosingClass;
+      if (enclosingClass != null) {
+        InterfaceType enclosingType = enclosingClass.type;
+        element = _lookUpMethod(null, enclosingType, methodName.name);
+        if (element == null) {
+          //
+          // If there's no method, then it's possible that 'm' is a getter that
+          // returns a function.
+          //
+          element = _lookUpGetter(null, enclosingType, methodName.name);
+        }
+      }
+    }
+    // TODO(brianwilkerson) Report this error.
+    return element;
+  }
+
+  /**
+   * Given an invocation of the form 'e.m(a1, ..., an)', resolve 'e.m' to the
+   * element being invoked. If the returned element is a method, then the method
+   * will be invoked. If the returned element is a getter, the getter will be
+   * invoked without arguments and the result of that invocation will then be
+   * invoked with the arguments. The [target] is the target of the invocation
+   * ('e'). The [targetType] is the type of the target. The [methodName] is th
+   *  name of the method being invoked ('m').
+   */
+  Element _resolveInvokedElementWithTarget(
+      Expression target, DartType targetType, SimpleIdentifier methodName) {
+    if (targetType is InterfaceType) {
+      Element element = _lookUpMethod(target, targetType, methodName.name);
+      if (element == null) {
+        //
+        // If there's no method, then it's possible that 'm' is a getter that
+        // returns a function.
+        //
+        // TODO (collinsn): need to add union type support here too, in the
+        // style of [lookUpMethod].
+        element = _lookUpGetter(target, targetType, methodName.name);
+      }
+      return element;
+    } else if (target is SimpleIdentifier) {
+      Element targetElement = target.staticElement;
+      if (targetElement is PrefixElement) {
+        //
+        // Look to see whether the name of the method is really part of a
+        // prefixed identifier for an imported top-level function or top-level
+        // getter that returns a function.
+        //
+        String name = "${target.name}.$methodName";
+        Identifier functionName = new SyntheticIdentifier(name, methodName);
+        Element element =
+            _resolver.nameScope.lookup(functionName, _definingLibrary);
+        if (element != null) {
+          // TODO(brianwilkerson) This isn't a method invocation, it's a
+          // function invocation where the function name is a prefixed
+          // identifier. Consider re-writing the AST.
+          return element;
+        }
+      }
+    }
+    // TODO(brianwilkerson) Report this error.
+    return null;
+  }
+
+  /**
+   * Given that we are accessing a property of the given [targetType] with the
+   * given [propertyName], return the element that represents the property. The
+   * [target] is the target of the invocation ('e').
+   */
+  ExecutableElement _resolveProperty(
+      Expression target, DartType targetType, SimpleIdentifier propertyName) {
+    ExecutableElement memberElement = null;
+    if (propertyName.inSetterContext()) {
+      memberElement = _lookUpSetter(target, targetType, propertyName.name);
+    }
+    if (memberElement == null) {
+      memberElement = _lookUpGetter(target, targetType, propertyName.name);
+    }
+    if (memberElement == null) {
+      memberElement = _lookUpMethod(target, targetType, propertyName.name);
+    }
+    return memberElement;
+  }
+
+  void _resolvePropertyAccess(
+      Expression target, SimpleIdentifier propertyName, bool isConditional) {
+    DartType staticType = _getStaticType(target);
+    DartType propagatedType = _getPropagatedType(target);
+    Element staticElement = null;
+    Element propagatedElement = null;
+    //
+    // If this property access is of the form 'C.m' where 'C' is a class,
+    // then we don't call resolveProperty(...) which walks up the class
+    // hierarchy, instead we just look for the member in the type only.  This
+    // does not apply to conditional property accesses (i.e. 'C?.m').
+    //
+    ClassElementImpl typeReference = getTypeReference(target, isConditional);
+    if (typeReference != null) {
+      // TODO(brianwilkerson) Why are we setting the propagated element here?
+      // It looks wrong.
+      staticElement =
+          propagatedElement = _resolveElement(typeReference, propertyName);
+    } else {
+      staticElement = _resolveProperty(target, staticType, propertyName);
+      propagatedElement =
+          _resolveProperty(target, propagatedType, propertyName);
+    }
+    // May be part of annotation, record property element only if exists.
+    // Error was already reported in validateAnnotationElement().
+    if (target.parent.parent is Annotation) {
+      if (staticElement != null) {
+        propertyName.staticElement = staticElement;
+      }
+      return;
+    }
+    propertyName.staticElement = staticElement;
+    propertyName.propagatedElement = propagatedElement;
+    bool shouldReportMissingMember_static =
+        _shouldReportMissingMember(staticType, staticElement);
+    bool shouldReportMissingMember_propagated =
+        !shouldReportMissingMember_static &&
+            _enableHints &&
+            _shouldReportMissingMember(propagatedType, propagatedElement) &&
+            !_memberFoundInSubclass(
+                propagatedType.element, propertyName.name, false, true);
+    if (shouldReportMissingMember_static ||
+        shouldReportMissingMember_propagated) {
+      DartType staticOrPropagatedType =
+          shouldReportMissingMember_static ? staticType : propagatedType;
+      Element staticOrPropagatedEnclosingElt = staticOrPropagatedType.element;
+      bool isStaticProperty = _isStatic(staticOrPropagatedEnclosingElt);
+      DartType displayType = staticOrPropagatedType != null
+          ? staticOrPropagatedType
+          : propagatedType != null ? propagatedType : staticType;
+      // Special getter cases.
+      if (propertyName.inGetterContext()) {
+        if (!isStaticProperty &&
+            staticOrPropagatedEnclosingElt is ClassElement) {
+          ClassElement classElement = staticOrPropagatedEnclosingElt;
+          InterfaceType targetType = classElement.type;
+          if (!_enableStrictCallChecks &&
+              targetType != null &&
+              targetType.isDartCoreFunction &&
+              propertyName.name == FunctionElement.CALL_METHOD_NAME) {
+            // TODO(brianwilkerson) Can we ever resolve the function being
+            // invoked?
+//            resolveArgumentsToParameters(node.getArgumentList(), invokedFunction);
+            return;
+          } else if (classElement.isEnum && propertyName.name == "_name") {
+            _resolver.reportErrorForNode(
+                CompileTimeErrorCode.ACCESS_PRIVATE_ENUM_FIELD, propertyName,
+                [propertyName.name]);
+            return;
+          }
+        }
+      }
+      Element declaringElement =
+          staticType.isVoid ? null : staticOrPropagatedEnclosingElt;
+      if (propertyName.inSetterContext()) {
+        ErrorCode errorCode;
+        if (shouldReportMissingMember_static) {
+          if (target is SuperExpression) {
+            if (isStaticProperty && !staticType.isVoid) {
+              errorCode = StaticWarningCode.UNDEFINED_SUPER_SETTER;
+            } else {
+              errorCode = StaticTypeWarningCode.UNDEFINED_SUPER_SETTER;
+            }
+          } else {
+            if (isStaticProperty && !staticType.isVoid) {
+              errorCode = StaticWarningCode.UNDEFINED_SETTER;
+            } else {
+              errorCode = StaticTypeWarningCode.UNDEFINED_SETTER;
+            }
+          }
+        } else {
+          errorCode = HintCode.UNDEFINED_SETTER;
+        }
+        _recordUndefinedNode(declaringElement, errorCode, propertyName, [
+          propertyName.name,
+          displayType.displayName
+        ]);
+      } else if (propertyName.inGetterContext()) {
+        ErrorCode errorCode;
+        if (shouldReportMissingMember_static) {
+          if (target is SuperExpression) {
+            if (isStaticProperty && !staticType.isVoid) {
+              errorCode = StaticWarningCode.UNDEFINED_SUPER_GETTER;
+            } else {
+              errorCode = StaticTypeWarningCode.UNDEFINED_SUPER_GETTER;
+            }
+          } else {
+            if (isStaticProperty && !staticType.isVoid) {
+              errorCode = StaticWarningCode.UNDEFINED_GETTER;
+            } else {
+              errorCode = StaticTypeWarningCode.UNDEFINED_GETTER;
+            }
+          }
+        } else {
+          errorCode = HintCode.UNDEFINED_GETTER;
+        }
+        _recordUndefinedNode(declaringElement, errorCode, propertyName, [
+          propertyName.name,
+          displayType.displayName
+        ]);
+      } else {
+        _recordUndefinedNode(declaringElement,
+            StaticWarningCode.UNDEFINED_IDENTIFIER, propertyName,
+            [propertyName.name]);
+      }
+    }
+  }
+
+  /**
+   * Resolve the given simple [identifier] if possible. Return the element to
+   * which it could be resolved, or `null` if it could not be resolved. This
+   * does not record the results of the resolution.
+   */
+  Element _resolveSimpleIdentifier(SimpleIdentifier identifier) {
+    Element element = _resolver.nameScope.lookup(identifier, _definingLibrary);
+    if (element is PropertyAccessorElement && identifier.inSetterContext()) {
+      PropertyInducingElement variable =
+          (element as PropertyAccessorElement).variable;
+      if (variable != null) {
+        PropertyAccessorElement setter = variable.setter;
+        if (setter == null) {
+          //
+          // Check to see whether there might be a locally defined getter and
+          // an inherited setter.
+          //
+          ClassElement enclosingClass = _resolver.enclosingClass;
+          if (enclosingClass != null) {
+            setter = _lookUpSetter(null, enclosingClass.type, identifier.name);
+          }
+        }
+        if (setter != null) {
+          element = setter;
+        }
+      }
+    } else if (element == null &&
+        (identifier.inSetterContext() ||
+            identifier.parent is CommentReference)) {
+      element = _resolver.nameScope.lookup(
+          new SyntheticIdentifier("${identifier.name}=", identifier),
+          _definingLibrary);
+    }
+    ClassElement enclosingClass = _resolver.enclosingClass;
+    if (element == null && enclosingClass != null) {
+      InterfaceType enclosingType = enclosingClass.type;
+      if (element == null &&
+          (identifier.inSetterContext() ||
+              identifier.parent is CommentReference)) {
+        element = _lookUpSetter(null, enclosingType, identifier.name);
+      }
+      if (element == null && identifier.inGetterContext()) {
+        element = _lookUpGetter(null, enclosingType, identifier.name);
+      }
+      if (element == null) {
+        element = _lookUpMethod(null, enclosingType, identifier.name);
+      }
+    }
+    return element;
+  }
+
+  /**
+   * If the given [type] is a type parameter, resolve it to the type that should
+   * be used when looking up members. Otherwise, return the original type.
+   */
+  DartType _resolveTypeParameter(DartType type) {
+    if (type is TypeParameterType) {
+      DartType bound = type.element.bound;
+      if (bound == null) {
+        return _resolver.typeProvider.objectType;
+      }
+      return bound;
+    }
+    return type;
+  }
+
+  /**
+   * Given a [node] that can have annotations associated with it and the
+   * [element] to which that node has been resolved, create the annotations in
+   * the element model representing the annotations on the node.
+   */
+  void _setMetadata(Element element, AnnotatedNode node) {
+    if (element is! ElementImpl) {
+      return;
+    }
+    List<ElementAnnotationImpl> annotationList =
+        new List<ElementAnnotationImpl>();
+    _addAnnotations(annotationList, node.metadata);
+    if (node is VariableDeclaration && node.parent is VariableDeclarationList) {
+      VariableDeclarationList list = node.parent as VariableDeclarationList;
+      _addAnnotations(annotationList, list.metadata);
+      if (list.parent is FieldDeclaration) {
+        FieldDeclaration fieldDeclaration = list.parent as FieldDeclaration;
+        _addAnnotations(annotationList, fieldDeclaration.metadata);
+      } else if (list.parent is TopLevelVariableDeclaration) {
+        TopLevelVariableDeclaration variableDeclaration =
+            list.parent as TopLevelVariableDeclaration;
+        _addAnnotations(annotationList, variableDeclaration.metadata);
+      }
+    }
+    if (!annotationList.isEmpty) {
+      (element as ElementImpl).metadata = annotationList;
+    }
+  }
+
+  /**
+   * Given a [node] that can have annotations associated with it and the
+   * [element] to which that node has been resolved, create the annotations in
+   * the element model representing the annotations on the node.
+   */
+  void _setMetadataForParameter(Element element, NormalFormalParameter node) {
+    if (element is! ElementImpl) {
+      return;
+    }
+    List<ElementAnnotationImpl> annotationList =
+        new List<ElementAnnotationImpl>();
+    _addAnnotations(annotationList, node.metadata);
+    if (!annotationList.isEmpty) {
+      (element as ElementImpl).metadata = annotationList;
+    }
+  }
+
+  /**
+   * Return `true` if we should report an error as a result of looking up a
+   * [member] in the given [type] and not finding any member.
+   */
+  bool _shouldReportMissingMember(DartType type, Element member) {
+    if (member != null || type == null || type.isDynamic || type.isBottom) {
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Checks whether the given [expression] is a reference to a class. If it is
+   * then the element representing the class is returned, otherwise `null` is
+   * returned.  [isConditional] indicates whether [expression] is to the left
+   * of a '?.' opertator.
+   */
+  static ClassElementImpl getTypeReference(
+      Expression expression, bool isConditional) {
+    if (!isConditional && expression is Identifier) {
+      Element staticElement = expression.staticElement;
+      if (staticElement is ClassElementImpl) {
+        return staticElement;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return `true` if the given [identifier] is the return type of a constructor
+   * declaration.
+   */
+  static bool _isConstructorReturnType(SimpleIdentifier identifier) {
+    AstNode parent = identifier.parent;
+    if (parent is ConstructorDeclaration) {
+      return identical(parent.returnType, identifier);
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the given [identifier] is the return type of a factory
+   * constructor.
+   */
+  static bool _isFactoryConstructorReturnType(SimpleIdentifier identifier) {
+    AstNode parent = identifier.parent;
+    if (parent is ConstructorDeclaration) {
+      ConstructorDeclaration constructor = parent;
+      return identical(constructor.returnType, identifier) &&
+          constructor.factoryKeyword != null;
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the given 'super' [expression] is used in a valid context.
+   */
+  static bool _isSuperInValidContext(SuperExpression expression) {
+    for (AstNode node = expression; node != null; node = node.parent) {
+      if (node is CompilationUnit) {
+        return false;
+      }
+      if (node is ConstructorDeclaration) {
+        return node.factoryKeyword == null;
+      }
+      if (node is ConstructorFieldInitializer) {
+        return false;
+      }
+      if (node is MethodDeclaration) {
+        return !node.isStatic;
+      }
+    }
+    return false;
+  }
+}
+
+/**
+ * An identifier that can be used to look up names in the lexical scope when
+ * there is no identifier in the AST structure. There is no identifier in the
+ * AST when the parser could not distinguish between a method invocation and an
+ * invocation of a top-level function imported with a prefix.
+ */
+class SyntheticIdentifier extends Identifier {
+  /**
+   * The name of the synthetic identifier.
+   */
+  final String name;
+
+  /**
+   * The identifier to be highlighted in case of an error
+   */
+  final Identifier targetIdentifier;
+
+  /**
+   * Initialize a newly created synthetic identifier to have the given [name]
+   * and [targetIdentifier].
+   */
+  SyntheticIdentifier(this.name, this.targetIdentifier);
+
+  @override
+  sc.Token get beginToken => null;
+
+  @override
+  Element get bestElement => null;
+
+  @override
+  Iterable get childEntities {
+    // Should never be called, since a SyntheticIdentifier never appears in the
+    // AST--it is just used for lookup.
+    assert(false);
+    return new ChildEntities();
+  }
+
+  @override
+  sc.Token get endToken => null;
+
+  @override
+  int get length => targetIdentifier.length;
+
+  @override
+  int get offset => targetIdentifier.offset;
+
+  @override
+  int get precedence => 16;
+
+  @override
+  Element get propagatedElement => null;
+
+  @override
+  Element get staticElement => null;
+
+  @override
+  accept(AstVisitor visitor) => null;
+
+  @override
+  void visitChildren(AstVisitor visitor) {}
+}
diff --git a/analyzer/lib/src/generated/engine.dart b/analyzer/lib/src/generated/engine.dart
new file mode 100644
index 0000000..50b8bca
--- /dev/null
+++ b/analyzer/lib/src/generated/engine.dart
@@ -0,0 +1,11745 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine;
+
+import "dart:math" as math;
+import 'dart:async';
+import 'dart:collection';
+
+import 'package:analyzer/src/cancelable_future.dart';
+import 'package:analyzer/src/context/cache.dart' as cache;
+import 'package:analyzer/src/context/context.dart' as cache;
+import 'package:analyzer/src/generated/incremental_resolution_validator.dart';
+import 'package:analyzer/src/plugin/engine_plugin.dart';
+import 'package:analyzer/src/services/lint.dart';
+import 'package:analyzer/src/task/manager.dart';
+import 'package:analyzer/src/task/task_dart.dart';
+import 'package:analyzer/task/model.dart';
+
+import '../../instrumentation/instrumentation.dart';
+import 'ast.dart';
+import 'constant.dart';
+import 'element.dart';
+import 'error.dart';
+import 'error_verifier.dart';
+import 'html.dart' as ht;
+import 'incremental_resolver.dart'
+    show IncrementalResolver, PoorMansIncrementalResolver;
+import 'incremental_scanner.dart';
+import 'java_core.dart';
+import 'java_engine.dart';
+import 'parser.dart' show Parser, IncrementalParser;
+import 'resolver.dart';
+import 'scanner.dart';
+import 'sdk.dart' show DartSdk;
+import 'source.dart';
+import 'utilities_collection.dart';
+import 'utilities_general.dart';
+
+/**
+ * Used by [AnalysisOptions] to allow function bodies to be analyzed in some
+ * sources but not others.
+ */
+typedef bool AnalyzeFunctionBodiesPredicate(Source source);
+
+/**
+ * Type of callback functions used by PendingFuture.  Functions of this type
+ * should perform a computation based on the data in [sourceEntry] and return
+ * it.  If the computation can't be performed yet because more analysis is
+ * needed, null should be returned.
+ *
+ * The function may also throw an exception, in which case the corresponding
+ * future will be completed with failure.
+ *
+ * Since this function is called while the state of analysis is being updated,
+ * it should be free of side effects so that it doesn't cause reentrant
+ * changes to the analysis state.
+ */
+typedef T PendingFutureComputer<T>(SourceEntry sourceEntry);
+
+/**
+ * An LRU cache of information related to analysis.
+ */
+class AnalysisCache {
+  /**
+   * A flag used to control whether trace information should be produced when
+   * the content of the cache is modified.
+   */
+  static bool _TRACE_CHANGES = false;
+
+  /**
+   * A list containing the partitions of which this cache is comprised.
+   */
+  final List<CachePartition> _partitions;
+
+  /**
+   * Initialize a newly created cache to have the given [_partitions]. The
+   * partitions will be searched in the order in which they appear in the list,
+   * so the most specific partition (usually an [SdkCachePartition]) should be
+   * first and the most general (usually a [UniversalCachePartition]) last.
+   */
+  AnalysisCache(this._partitions);
+
+  /**
+   * Return the number of entries in this cache that have an AST associated with
+   * them.
+   */
+  int get astSize => _partitions[_partitions.length - 1].astSize;
+
+  /**
+   * Return information about each of the partitions in this cache.
+   */
+  List<AnalysisContextStatistics_PartitionData> get partitionData {
+    int count = _partitions.length;
+    List<AnalysisContextStatistics_PartitionData> data =
+        new List<AnalysisContextStatistics_PartitionData>(count);
+    for (int i = 0; i < count; i++) {
+      CachePartition partition = _partitions[i];
+      data[i] = new AnalysisContextStatisticsImpl_PartitionDataImpl(
+          partition.astSize, partition.map.length);
+    }
+    return data;
+  }
+
+  /**
+   * Record that the AST associated with the given [source] was just read from
+   * the cache.
+   */
+  void accessedAst(Source source) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(source)) {
+        _partitions[i].accessedAst(source);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Return the entry associated with the given [source].
+   */
+  SourceEntry get(Source source) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(source)) {
+        return _partitions[i].get(source);
+      }
+    }
+    //
+    // We should never get to this point because the last partition should
+    // always be a universal partition, except in the case of the SDK context,
+    // in which case the source should always be part of the SDK.
+    //
+    return null;
+  }
+
+  /**
+   * Return context that owns the given [source].
+   */
+  InternalAnalysisContext getContextFor(Source source) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(source)) {
+        return _partitions[i].context;
+      }
+    }
+    //
+    // We should never get to this point because the last partition should
+    // always be a universal partition, except in the case of the SDK context,
+    // in which case the source should always be part of the SDK.
+    //
+    AnalysisEngine.instance.logger.logInformation(
+        "Could not find context for ${source.fullName}",
+        new CaughtException(new AnalysisException(), null));
+    return null;
+  }
+
+  /**
+   * Return an iterator returning all of the map entries mapping sources to
+   * cache entries.
+   */
+  MapIterator<Source, SourceEntry> iterator() {
+    int count = _partitions.length;
+    List<Map<Source, SourceEntry>> maps = new List<Map>(count);
+    for (int i = 0; i < count; i++) {
+      maps[i] = _partitions[i].map;
+    }
+    return new MultipleMapIterator<Source, SourceEntry>(maps);
+  }
+
+  /**
+   * Associate the given [entry] with the given [source].
+   */
+  void put(Source source, SourceEntry entry) {
+    entry.fixExceptionState();
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(source)) {
+        if (_TRACE_CHANGES) {
+          try {
+            SourceEntry oldEntry = _partitions[i].get(source);
+            if (oldEntry == null) {
+              AnalysisEngine.instance.logger.logInformation(
+                  "Added a cache entry for '${source.fullName}'.");
+            } else {
+              AnalysisEngine.instance.logger.logInformation(
+                  "Modified the cache entry for ${source.fullName}'. Diff = ${entry.getDiff(oldEntry)}");
+            }
+          } catch (exception) {
+            // Ignored
+            JavaSystem.currentTimeMillis();
+          }
+        }
+        _partitions[i].put(source, entry);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Remove all information related to the given [source] from this cache.
+   */
+  void remove(Source source) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(source)) {
+        if (_TRACE_CHANGES) {
+          try {
+            AnalysisEngine.instance.logger.logInformation(
+                "Removed the cache entry for ${source.fullName}'.");
+          } catch (exception) {
+            // Ignored
+            JavaSystem.currentTimeMillis();
+          }
+        }
+        _partitions[i].remove(source);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Record that the AST associated with the given [source] was just removed
+   * from the cache.
+   */
+  void removedAst(Source source) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(source)) {
+        _partitions[i].removedAst(source);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Return the number of sources that are mapped to cache entries.
+   */
+  int size() {
+    int size = 0;
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      size += _partitions[i].size();
+    }
+    return size;
+  }
+
+  /**
+   * Record that the AST associated with the given [source] was just stored to
+   * the cache.
+   */
+  void storedAst(Source source) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(source)) {
+        _partitions[i].storedAst(source);
+        return;
+      }
+    }
+  }
+}
+
+/**
+ * A context in which a single analysis can be performed and incrementally
+ * maintained. The context includes such information as the version of the SDK
+ * being analyzed against as well as the package-root used to resolve 'package:'
+ * URI's. (Both of which are known indirectly through the [SourceFactory].)
+ *
+ * An analysis context also represents the state of the analysis, which includes
+ * knowing which sources have been included in the analysis (either directly or
+ * indirectly) and the results of the analysis. Sources must be added and
+ * removed from the context using the method [applyChanges], which is also used
+ * to notify the context when sources have been modified and, consequently,
+ * previously known results might have been invalidated.
+ *
+ * There are two ways to access the results of the analysis. The most common is
+ * to use one of the 'get' methods to access the results. The 'get' methods have
+ * the advantage that they will always return quickly, but have the disadvantage
+ * that if the results are not currently available they will return either
+ * nothing or in some cases an incomplete result. The second way to access
+ * results is by using one of the 'compute' methods. The 'compute' methods will
+ * always attempt to compute the requested results but might block the caller
+ * for a significant period of time.
+ *
+ * When results have been invalidated, have never been computed (as is the case
+ * for newly added sources), or have been removed from the cache, they are
+ * <b>not</b> automatically recreated. They will only be recreated if one of the
+ * 'compute' methods is invoked.
+ *
+ * However, this is not always acceptable. Some clients need to keep the
+ * analysis results up-to-date. For such clients there is a mechanism that
+ * allows them to incrementally perform needed analysis and get notified of the
+ * consequent changes to the analysis results. This mechanism is realized by the
+ * method [performAnalysisTask].
+ *
+ * Analysis engine allows for having more than one context. This can be used,
+ * for example, to perform one analysis based on the state of files on disk and
+ * a separate analysis based on the state of those files in open editors. It can
+ * also be used to perform an analysis based on a proposed future state, such as
+ * the state after a refactoring.
+ */
+abstract class AnalysisContext {
+  /**
+   * An empty list of contexts.
+   */
+  static const List<AnalysisContext> EMPTY_LIST = const <AnalysisContext>[];
+
+  /**
+   * Return the set of analysis options controlling the behavior of this
+   * context. Clients should not modify the returned set of options. The options
+   * should only be set by invoking the method [setAnalysisOptions].
+   */
+  AnalysisOptions get analysisOptions;
+
+  /**
+   * Set the set of analysis options controlling the behavior of this context to
+   * the given [options]. Clients can safely assume that all necessary analysis
+   * results have been invalidated.
+   */
+  void set analysisOptions(AnalysisOptions options);
+
+  /**
+   * Set the order in which sources will be analyzed by [performAnalysisTask] to
+   * match the order of the sources in the given list of [sources]. If a source
+   * that needs to be analyzed is not contained in the list, then it will be
+   * treated as if it were at the end of the list. If the list is empty (or
+   * `null`) then no sources will be given priority over other sources.
+   *
+   * Changes made to the list after this method returns will <b>not</b> be
+   * reflected in the priority order.
+   */
+  void set analysisPriorityOrder(List<Source> sources);
+
+  /**
+   * Return the set of declared variables used when computing constant values.
+   */
+  DeclaredVariables get declaredVariables;
+
+  /**
+   * Return a list containing all of the sources known to this context that
+   * represent HTML files. The contents of the list can be incomplete.
+   */
+  List<Source> get htmlSources;
+
+  /**
+   * Returns `true` if this context was disposed using [dispose].
+   */
+  bool get isDisposed;
+
+  /**
+   * Return a list containing all of the sources known to this context that
+   * represent the defining compilation unit of a library that can be run within
+   * a browser. The sources that are returned represent libraries that have a
+   * 'main' method and are either referenced by an HTML file or import, directly
+   * or indirectly, a client-only library. The contents of the list can be
+   * incomplete.
+   */
+  List<Source> get launchableClientLibrarySources;
+
+  /**
+   * Return a list containing all of the sources known to this context that
+   * represent the defining compilation unit of a library that can be run
+   * outside of a browser. The contents of the list can be incomplete.
+   */
+  List<Source> get launchableServerLibrarySources;
+
+  /**
+   * Return a list containing all of the sources known to this context that
+   * represent the defining compilation unit of a library. The contents of the
+   * list can be incomplete.
+   */
+  List<Source> get librarySources;
+
+  /**
+   * Return a client-provided name used to identify this context, or `null` if
+   * the client has not provided a name.
+   */
+  String get name;
+
+  /**
+   * Set the client-provided name used to identify this context to the given
+   * [name].
+   */
+  set name(String name);
+
+  /**
+   * The stream that is notified when sources have been added or removed,
+   * or the source's content has changed.
+   */
+  Stream<SourcesChangedEvent> get onSourcesChanged;
+
+  /**
+   * Return a list containing all of the sources known to this context whose
+   * state is neither valid or flushed. These sources are not safe to update
+   * during refactoring, because we might not know all the references in them.
+   */
+  List<Source> get refactoringUnsafeSources;
+
+  /**
+   * Return the source factory used to create the sources that can be analyzed
+   * in this context.
+   */
+  SourceFactory get sourceFactory;
+
+  /**
+   * Set the source factory used to create the sources that can be analyzed in
+   * this context to the given source [factory]. Clients can safely assume that
+   * all analysis results have been invalidated.
+   */
+  void set sourceFactory(SourceFactory factory);
+
+  /**
+   * Return a list containing all of the sources known to this context.
+   */
+  List<Source> get sources;
+
+  /**
+   * Return a type provider for this context or throw [AnalysisException] if
+   * either `dart:core` or `dart:async` cannot be resolved.
+   */
+  TypeProvider get typeProvider;
+
+  /**
+   * Add the given [listener] to the list of objects that are to be notified
+   * when various analysis results are produced in this context.
+   */
+  void addListener(AnalysisListener listener);
+
+  /**
+   * Apply the given [delta] to change the level of analysis that will be
+   * performed for the sources known to this context.
+   */
+  void applyAnalysisDelta(AnalysisDelta delta);
+
+  /**
+   * Apply the changes specified by the given [changeSet] to this context. Any
+   * analysis results that have been invalidated by these changes will be
+   * removed.
+   */
+  void applyChanges(ChangeSet changeSet);
+
+  /**
+   * Return the documentation comment for the given [element] as it appears in
+   * the original source (complete with the beginning and ending delimiters) for
+   * block documentation comments, or lines starting with `"///"` and separated
+   * with `"\n"` characters for end-of-line documentation comments, or `null` if
+   * the element does not have a documentation comment associated with it. This
+   * can be a long-running operation if the information needed to access the
+   * comment is not cached.
+   *
+   * Throws an [AnalysisException] if the documentation comment could not be
+   * determined because the analysis could not be performed.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  String computeDocumentationComment(Element element);
+
+  /**
+   * Return a list containing all of the errors associated with the given
+   * [source]. If the errors are not already known then the source will be
+   * analyzed in order to determine the errors associated with it.
+   *
+   * Throws an [AnalysisException] if the errors could not be determined because
+   * the analysis could not be performed.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   *
+   * See [getErrors].
+   */
+  List<AnalysisError> computeErrors(Source source);
+
+  /**
+   * Return the element model corresponding to the HTML file defined by the
+   * given [source]. If the element model does not yet exist it will be created.
+   * The process of creating an element model for an HTML file can be
+   * long-running, depending on the size of the file and the number of libraries
+   * that are defined in it (via script tags) that also need to have a model
+   * built for them.
+   *
+   * Throws AnalysisException if the element model could not be determined
+   * because the analysis could not be performed.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   *
+   * See [getHtmlElement].
+   */
+  HtmlElement computeHtmlElement(Source source);
+
+  /**
+   * Return the kind of the given [source], computing it's kind if it is not
+   * already known. Return [SourceKind.UNKNOWN] if the source is not contained
+   * in this context.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   *
+   * See [getKindOf].
+   */
+  SourceKind computeKindOf(Source source);
+
+  /**
+   * Return the element model corresponding to the library defined by the given
+   * [source]. If the element model does not yet exist it will be created. The
+   * process of creating an element model for a library can long-running,
+   * depending on the size of the library and the number of libraries that are
+   * imported into it that also need to have a model built for them.
+   *
+   * Throws an [AnalysisException] if the element model could not be determined
+   * because the analysis could not be performed.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   *
+   * See [getLibraryElement].
+   */
+  LibraryElement computeLibraryElement(Source source);
+
+  /**
+   * Return the line information for the given [source], or `null` if the source
+   * is not of a recognized kind (neither a Dart nor HTML file). If the line
+   * information was not previously known it will be created. The line
+   * information is used to map offsets from the beginning of the source to line
+   * and column pairs.
+   *
+   * Throws an [AnalysisException] if the line information could not be
+   * determined because the analysis could not be performed.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   *
+   * See [getLineInfo].
+   */
+  LineInfo computeLineInfo(Source source);
+
+  /**
+   * Return a future which will be completed with the fully resolved AST for a
+   * single compilation unit within the given library, once that AST is up to
+   * date.
+   *
+   * If the resolved AST can't be computed for some reason, the future will be
+   * completed with an error.  One possible error is AnalysisNotScheduledError,
+   * which means that the resolved AST can't be computed because the given
+   * source file is not scheduled to be analyzed within the context of the
+   * given library.
+   */
+  CancelableFuture<CompilationUnit> computeResolvedCompilationUnitAsync(
+      Source source, Source librarySource);
+
+  /**
+   * Notifies the context that the client is going to stop using this context.
+   */
+  void dispose();
+
+  /**
+   * Return `true` if the given [source] exists.
+   *
+   * This method should be used rather than the method [Source.exists] because
+   * contexts can have local overrides of the content of a source that the
+   * source is not aware of and a source with local content is considered to
+   * exist even if there is no file on disk.
+   */
+  bool exists(Source source);
+
+  /**
+   * Return the element model corresponding to the compilation unit defined by
+   * the given [unitSource] in the library defined by the given [librarySource],
+   * or `null` if the element model does not currently exist or if the library
+   * cannot be analyzed for some reason.
+   */
+  CompilationUnitElement getCompilationUnitElement(
+      Source unitSource, Source librarySource);
+
+  /**
+   * Return the contents and timestamp of the given [source].
+   *
+   * This method should be used rather than the method [Source.getContents]
+   * because contexts can have local overrides of the content of a source that
+   * the source is not aware of.
+   */
+  TimestampedData<String> getContents(Source source);
+
+  /**
+   * Return the element referenced by the given [location], or `null` if the
+   * element is not immediately available or if there is no element with the
+   * given location. The latter condition can occur, for example, if the
+   * location describes an element from a different context or if the element
+   * has been removed from this context as a result of some change since it was
+   * originally obtained.
+   */
+  Element getElement(ElementLocation location);
+
+  /**
+   * Return an analysis error info containing the list of all of the errors and
+   * the line info associated with the given [source]. The list of errors will
+   * be empty if the source is not known to this context or if there are no
+   * errors in the source. The errors contained in the list can be incomplete.
+   *
+   * See [computeErrors].
+   */
+  AnalysisErrorInfo getErrors(Source source);
+
+  /**
+   * Return the element model corresponding to the HTML file defined by the
+   * given [source], or `null` if the source does not represent an HTML file,
+   * the element representing the file has not yet been created, or the analysis
+   * of the HTML file failed for some reason.
+   *
+   * See [computeHtmlElement].
+   */
+  HtmlElement getHtmlElement(Source source);
+
+  /**
+   * Return the sources for the HTML files that reference the compilation unit
+   * with the given [source]. If the source does not represent a Dart source or
+   * is not known to this context, the returned list will be empty. The contents
+   * of the list can be incomplete.
+   */
+  List<Source> getHtmlFilesReferencing(Source source);
+
+  /**
+   * Return the kind of the given [source], or `null` if the kind is not known
+   * to this context.
+   *
+   * See [computeKindOf].
+   */
+  SourceKind getKindOf(Source source);
+
+  /**
+   * Return the sources for the defining compilation units of any libraries of
+   * which the given [source] is a part. The list will normally contain a single
+   * library because most Dart sources are only included in a single library,
+   * but it is possible to have a part that is contained in multiple identically
+   * named libraries. If the source represents the defining compilation unit of
+   * a library, then the returned list will contain the given source as its only
+   * element. If the source does not represent a Dart source or is not known to
+   * this context, the returned list will be empty. The contents of the list can
+   * be incomplete.
+   */
+  List<Source> getLibrariesContaining(Source source);
+
+  /**
+   * Return the sources for the defining compilation units of any libraries that
+   * depend on the library defined by the given [librarySource]. One library
+   * depends on another if it either imports or exports that library.
+   */
+  List<Source> getLibrariesDependingOn(Source librarySource);
+
+  /**
+   * Return the sources for the defining compilation units of any libraries that
+   * are referenced from the HTML file defined by the given [htmlSource].
+   */
+  List<Source> getLibrariesReferencedFromHtml(Source htmlSource);
+
+  /**
+   * Return the element model corresponding to the library defined by the given
+   * [source], or `null` if the element model does not currently exist or if the
+   * library cannot be analyzed for some reason.
+   */
+  LibraryElement getLibraryElement(Source source);
+
+  /**
+   * Return the line information for the given [source], or `null` if the line
+   * information is not known. The line information is used to map offsets from
+   * the beginning of the source to line and column pairs.
+   *
+   * See [computeLineInfo].
+   */
+  LineInfo getLineInfo(Source source);
+
+  /**
+   * Return the modification stamp for the [source], or a negative value if the
+   * source does not exist. A modification stamp is a non-negative integer with
+   * the property that if the contents of the source have not been modified
+   * since the last time the modification stamp was accessed then the same value
+   * will be returned, but if the contents of the source have been modified one
+   * or more times (even if the net change is zero) the stamps will be different.
+   *
+   * This method should be used rather than the method
+   * [Source.getModificationStamp] because contexts can have local overrides of
+   * the content of a source that the source is not aware of.
+   */
+  int getModificationStamp(Source source);
+
+  /**
+   * Return a fully resolved AST for the compilation unit defined by the given
+   * [unitSource] within the given [library], or `null` if the resolved AST is
+   * not already computed.
+   *
+   * See [resolveCompilationUnit].
+   */
+  CompilationUnit getResolvedCompilationUnit(
+      Source unitSource, LibraryElement library);
+
+  /**
+   * Return a fully resolved AST for the compilation unit defined by the given
+   * [unitSource] within the library defined by the given [librarySource], or
+   * `null` if the resolved AST is not already computed.
+   *
+   * See [resolveCompilationUnit2].
+   */
+  CompilationUnit getResolvedCompilationUnit2(
+      Source unitSource, Source librarySource);
+
+  /**
+   * Return the fully resolved HTML unit defined by the given [htmlSource], or
+   * `null` if the resolved unit is not already computed.
+   *
+   * See [resolveHtmlUnit].
+   */
+  ht.HtmlUnit getResolvedHtmlUnit(Source htmlSource);
+
+  /**
+   * Return a list of the sources being analyzed in this context whose full path
+   * is equal to the given [path].
+   */
+  List<Source> getSourcesWithFullName(String path);
+
+  /**
+   * Return `true` if the given [librarySource] is known to be the defining
+   * compilation unit of a library that can be run on a client (references
+   * 'dart:html', either directly or indirectly).
+   *
+   * <b>Note:</b> In addition to the expected case of returning `false` if the
+   * source is known to be a library that cannot be run on a client, this method
+   * will also return `false` if the source is not known to be a library or if
+   * we do not know whether it can be run on a client.
+   */
+  bool isClientLibrary(Source librarySource);
+
+  /**
+   * Return `true` if the given [librarySource] is known to be the defining
+   * compilation unit of a library that can be run on the server (does not
+   * reference 'dart:html', either directly or indirectly).
+   *
+   * <b>Note:</b> In addition to the expected case of returning `false` if the
+   * source is known to be a library that cannot be run on the server, this
+   * method will also return `false` if the source is not known to be a library
+   * or if we do not know whether it can be run on the server.
+   */
+  bool isServerLibrary(Source librarySource);
+
+  /**
+   * Parse the content of the given [source] to produce an AST structure. The
+   * resulting AST structure may or may not be resolved, and may have a slightly
+   * different structure depending upon whether it is resolved.
+   *
+   * Throws an [AnalysisException] if the analysis could not be performed
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  CompilationUnit parseCompilationUnit(Source source);
+
+  /**
+   * Parse a single HTML [source] to produce an AST structure. The resulting
+   * HTML AST structure may or may not be resolved, and may have a slightly
+   * different structure depending upon whether it is resolved.
+   *
+   * Throws an [AnalysisException] if the analysis could not be performed
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  ht.HtmlUnit parseHtmlUnit(Source source);
+
+  /**
+   * Perform the next unit of work required to keep the analysis results
+   * up-to-date and return information about the consequent changes to the
+   * analysis results. This method can be long running.
+   */
+  AnalysisResult performAnalysisTask();
+
+  /**
+   * Remove the given [listener] from the list of objects that are to be
+   * notified when various analysis results are produced in this context.
+   */
+  void removeListener(AnalysisListener listener);
+
+  /**
+   * Return a fully resolved AST for the compilation unit defined by the given
+   * [unitSource] within the given [library].
+   *
+   * Throws an [AnalysisException] if the analysis could not be performed.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   *
+   * See [getResolvedCompilationUnit].
+   */
+  CompilationUnit resolveCompilationUnit(
+      Source unitSource, LibraryElement library);
+
+  /**
+   * Return a fully resolved AST for the compilation unit defined by the given
+   * [unitSource] within the library defined by the given [librarySource].
+   *
+   * Throws an [AnalysisException] if the analysis could not be performed.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   *
+   * See [getResolvedCompilationUnit2].
+   */
+  CompilationUnit resolveCompilationUnit2(
+      Source unitSource, Source librarySource);
+
+  /**
+   * Parse and resolve a single [htmlSource] within the given context to produce
+   * a fully resolved AST.
+   *
+   * Throws an [AnalysisException] if the analysis could not be performed.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  ht.HtmlUnit resolveHtmlUnit(Source htmlSource);
+
+  /**
+   * Set the contents of the given [source] to the given [contents] and mark the
+   * source as having changed. The additional [offset] and [length] information
+   * is used by the context to determine what reanalysis is necessary.
+   */
+  void setChangedContents(
+      Source source, String contents, int offset, int oldLength, int newLength);
+
+  /**
+   * Set the contents of the given [source] to the given [contents] and mark the
+   * source as having changed. This has the effect of overriding the default
+   * contents of the source. If the contents are `null` the override is removed
+   * so that the default contents will be returned.
+   */
+  void setContents(Source source, String contents);
+}
+
+/**
+ * An [AnalysisContext].
+ */
+class AnalysisContextImpl implements InternalAnalysisContext {
+  /**
+   * The difference between the maximum cache size and the maximum priority
+   * order size. The priority list must be capped so that it is less than the
+   * cache size. Failure to do so can result in an infinite loop in
+   * performAnalysisTask() because re-caching one AST structure can cause
+   * another priority source's AST structure to be flushed.
+   */
+  static int _PRIORITY_ORDER_SIZE_DELTA = 4;
+
+  /**
+   * A flag indicating whether trace output should be produced as analysis tasks
+   * are performed. Used for debugging.
+   */
+  static bool _TRACE_PERFORM_TASK = false;
+
+  /**
+   * The next context identifier.
+   */
+  static int _NEXT_ID = 0;
+
+  /**
+   * The unique identifier of this context.
+   */
+  final int _id = _NEXT_ID++;
+
+  /**
+   * A client-provided name used to identify this context, or `null` if the
+   * client has not provided a name.
+   */
+  String name;
+
+  /**
+   * The set of analysis options controlling the behavior of this context.
+   */
+  AnalysisOptionsImpl _options = new AnalysisOptionsImpl();
+
+  /**
+   * A flag indicating whether errors related to implicitly analyzed sources
+   * should be generated and reported.
+   */
+  bool _generateImplicitErrors = true;
+
+  /**
+   * A flag indicating whether errors related to sources in the SDK should be
+   * generated and reported.
+   */
+  bool _generateSdkErrors = true;
+
+  /**
+   * A flag indicating whether this context is disposed.
+   */
+  bool _disposed = false;
+
+  /**
+   * A cache of content used to override the default content of a source.
+   */
+  ContentCache _contentCache = new ContentCache();
+
+  /**
+   * The source factory used to create the sources that can be analyzed in this
+   * context.
+   */
+  SourceFactory _sourceFactory;
+
+  /**
+   * The set of declared variables used when computing constant values.
+   */
+  DeclaredVariables _declaredVariables = new DeclaredVariables();
+
+  /**
+   * A source representing the core library.
+   */
+  Source _coreLibrarySource;
+
+  /**
+   * A source representing the async library.
+   */
+  Source _asyncLibrarySource;
+
+  /**
+   * The partition that contains analysis results that are not shared with other
+   * contexts.
+   */
+  CachePartition _privatePartition;
+
+  /**
+   * A table mapping the sources known to the context to the information known
+   * about the source.
+   */
+  AnalysisCache _cache;
+
+  /**
+   * A list containing sources for which data should not be flushed.
+   */
+  List<Source> _priorityOrder = Source.EMPTY_ARRAY;
+
+  /**
+   * A map from all sources for which there are futures pending to a list of
+   * the corresponding PendingFuture objects.  These sources will be analyzed
+   * in the same way as priority sources, except with higher priority.
+   *
+   * TODO(paulberry): since the size of this map is not constrained (as it is
+   * for _priorityOrder), we run the risk of creating an analysis loop if
+   * re-caching one AST structure causes the AST structure for another source
+   * with pending futures to be flushed.  However, this is unlikely to happen
+   * in practice since sources are removed from this hash set as soon as their
+   * futures have completed.
+   */
+  HashMap<Source, List<PendingFuture>> _pendingFutureSources =
+      new HashMap<Source, List<PendingFuture>>();
+
+  /**
+   * A list containing sources whose AST structure is needed in order to resolve
+   * the next library to be resolved.
+   */
+  HashSet<Source> _neededForResolution = null;
+
+  /**
+   * A table mapping sources to the change notices that are waiting to be
+   * returned related to that source.
+   */
+  HashMap<Source, ChangeNoticeImpl> _pendingNotices =
+      new HashMap<Source, ChangeNoticeImpl>();
+
+  /**
+   * The object used to record the results of performing an analysis task.
+   */
+  AnalysisContextImpl_AnalysisTaskResultRecorder _resultRecorder;
+
+  /**
+   * Cached information used in incremental analysis or `null` if none.
+   */
+  IncrementalAnalysisCache _incrementalAnalysisCache;
+
+  /**
+   * The [TypeProvider] for this context, `null` if not yet created.
+   */
+  TypeProvider _typeProvider;
+
+  /**
+   * The object used to manage the list of sources that need to be analyzed.
+   */
+  WorkManager _workManager = new WorkManager();
+
+  /**
+   * The [Stopwatch] of the current "perform tasks cycle".
+   */
+  Stopwatch _performAnalysisTaskStopwatch;
+
+  /**
+   * The controller for sending [SourcesChangedEvent]s.
+   */
+  StreamController<SourcesChangedEvent> _onSourcesChangedController;
+
+  /**
+   * The listeners that are to be notified when various analysis results are
+   * produced in this context.
+   */
+  List<AnalysisListener> _listeners = new List<AnalysisListener>();
+
+  /**
+   * The most recently incrementally resolved source, or `null` when it was
+   * already validated, or the most recent change was not incrementally resolved.
+   */
+  Source incrementalResolutionValidation_lastUnitSource;
+
+  /**
+   * The most recently incrementally resolved library source, or `null` when it
+   * was already validated, or the most recent change was not incrementally
+   * resolved.
+   */
+  Source incrementalResolutionValidation_lastLibrarySource;
+
+  /**
+   * The result of incremental resolution result of
+   * [incrementalResolutionValidation_lastSource].
+   */
+  CompilationUnit incrementalResolutionValidation_lastUnit;
+
+  /**
+   * A factory to override how the [ResolverVisitor] is created.
+   */
+  ResolverVisitorFactory resolverVisitorFactory;
+
+  /**
+   * A factory to override how the [TypeResolverVisitor] is created.
+   */
+  TypeResolverVisitorFactory typeResolverVisitorFactory;
+
+  /**
+   * A factory to override how [LibraryResolver] is created.
+   */
+  LibraryResolverFactory libraryResolverFactory;
+
+  /**
+   * Initialize a newly created analysis context.
+   */
+  AnalysisContextImpl() {
+    _resultRecorder = new AnalysisContextImpl_AnalysisTaskResultRecorder(this);
+    _privatePartition = new UniversalCachePartition(this,
+        AnalysisOptionsImpl.DEFAULT_CACHE_SIZE,
+        new AnalysisContextImpl_ContextRetentionPolicy(this));
+    _cache = createCacheFromSourceFactory(null);
+    _onSourcesChangedController =
+        new StreamController<SourcesChangedEvent>.broadcast();
+  }
+
+  @override
+  AnalysisOptions get analysisOptions => _options;
+
+  @override
+  void set analysisOptions(AnalysisOptions options) {
+    bool needsRecompute = this._options.analyzeFunctionBodiesPredicate !=
+            options.analyzeFunctionBodiesPredicate ||
+        this._options.generateImplicitErrors !=
+            options.generateImplicitErrors ||
+        this._options.generateSdkErrors != options.generateSdkErrors ||
+        this._options.dart2jsHint != options.dart2jsHint ||
+        (this._options.hint && !options.hint) ||
+        this._options.preserveComments != options.preserveComments ||
+        this._options.enableNullAwareOperators !=
+            options.enableNullAwareOperators ||
+        this._options.enableStrictCallChecks != options.enableStrictCallChecks;
+    int cacheSize = options.cacheSize;
+    if (this._options.cacheSize != cacheSize) {
+      this._options.cacheSize = cacheSize;
+      //cache.setMaxCacheSize(cacheSize);
+      _privatePartition.maxCacheSize = cacheSize;
+      //
+      // Cap the size of the priority list to being less than the cache size.
+      // Failure to do so can result in an infinite loop in
+      // performAnalysisTask() because re-caching one AST structure
+      // can cause another priority source's AST structure to be flushed.
+      //
+      // TODO(brianwilkerson) Remove this constraint when the new task model is
+      // implemented.
+      //
+      int maxPriorityOrderSize = cacheSize - _PRIORITY_ORDER_SIZE_DELTA;
+      if (_priorityOrder.length > maxPriorityOrderSize) {
+        _priorityOrder = _priorityOrder.sublist(0, maxPriorityOrderSize);
+      }
+    }
+    this._options.analyzeFunctionBodiesPredicate =
+        options.analyzeFunctionBodiesPredicate;
+    this._options.generateImplicitErrors = options.generateImplicitErrors;
+    this._options.generateSdkErrors = options.generateSdkErrors;
+    this._options.dart2jsHint = options.dart2jsHint;
+    this._options.enableNullAwareOperators = options.enableNullAwareOperators;
+    this._options.enableStrictCallChecks = options.enableStrictCallChecks;
+    this._options.hint = options.hint;
+    this._options.incremental = options.incremental;
+    this._options.incrementalApi = options.incrementalApi;
+    this._options.incrementalValidation = options.incrementalValidation;
+    this._options.lint = options.lint;
+    this._options.preserveComments = options.preserveComments;
+    _generateImplicitErrors = options.generateImplicitErrors;
+    _generateSdkErrors = options.generateSdkErrors;
+    if (needsRecompute) {
+      _invalidateAllLocalResolutionInformation(false);
+    }
+  }
+
+  @override
+  void set analysisPriorityOrder(List<Source> sources) {
+    if (sources == null || sources.isEmpty) {
+      _priorityOrder = Source.EMPTY_ARRAY;
+    } else {
+      while (sources.remove(null)) {
+        // Nothing else to do.
+      }
+      if (sources.isEmpty) {
+        _priorityOrder = Source.EMPTY_ARRAY;
+      }
+      //
+      // Cap the size of the priority list to being less than the cache size.
+      // Failure to do so can result in an infinite loop in
+      // performAnalysisTask() because re-caching one AST structure
+      // can cause another priority source's AST structure to be flushed.
+      //
+      int count = math.min(
+          sources.length, _options.cacheSize - _PRIORITY_ORDER_SIZE_DELTA);
+      _priorityOrder = new List<Source>(count);
+      for (int i = 0; i < count; i++) {
+        _priorityOrder[i] = sources[i];
+      }
+      // Ensure entries for every priority source.
+      for (var source in _priorityOrder) {
+        SourceEntry entry = _getReadableSourceEntry(source);
+        if (entry == null) {
+          _createSourceEntry(source, false);
+        }
+      }
+    }
+  }
+
+  @override
+  set contentCache(ContentCache value) {
+    _contentCache = value;
+  }
+
+  @override
+  DeclaredVariables get declaredVariables => _declaredVariables;
+
+  @override
+  List<AnalysisTarget> get explicitTargets {
+    List<AnalysisTarget> targets = <AnalysisTarget>[];
+    MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      if (iterator.value.explicitlyAdded) {
+        targets.add(iterator.key);
+      }
+    }
+    return targets;
+  }
+
+  @override
+  List<Source> get htmlSources => _getSources(SourceKind.HTML);
+
+  @override
+  bool get isDisposed => _disposed;
+
+  @override
+  List<Source> get launchableClientLibrarySources {
+    // TODO(brianwilkerson) This needs to filter out libraries that do not
+    // reference dart:html, either directly or indirectly.
+    List<Source> sources = new List<Source>();
+    MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      Source source = iterator.key;
+      SourceEntry sourceEntry = iterator.value;
+      if (sourceEntry.kind == SourceKind.LIBRARY && !source.isInSystemLibrary) {
+//          DartEntry dartEntry = (DartEntry) sourceEntry;
+//          if (dartEntry.getValue(DartEntry.IS_LAUNCHABLE) && dartEntry.getValue(DartEntry.IS_CLIENT)) {
+        sources.add(source);
+//          }
+      }
+    }
+    return sources;
+  }
+
+  @override
+  List<Source> get launchableServerLibrarySources {
+    // TODO(brianwilkerson) This needs to filter out libraries that reference
+    // dart:html, either directly or indirectly.
+    List<Source> sources = new List<Source>();
+    MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      Source source = iterator.key;
+      SourceEntry sourceEntry = iterator.value;
+      if (sourceEntry.kind == SourceKind.LIBRARY && !source.isInSystemLibrary) {
+//          DartEntry dartEntry = (DartEntry) sourceEntry;
+//          if (dartEntry.getValue(DartEntry.IS_LAUNCHABLE) && !dartEntry.getValue(DartEntry.IS_CLIENT)) {
+        sources.add(source);
+//          }
+      }
+    }
+    return sources;
+  }
+
+  @override
+  List<Source> get librarySources => _getSources(SourceKind.LIBRARY);
+
+  /**
+   * Look through the cache for a task that needs to be performed. Return the
+   * task that was found, or `null` if there is no more work to be done.
+   */
+  AnalysisTask get nextAnalysisTask {
+    bool hintsEnabled = _options.hint;
+    bool lintsEnabled = _options.lint;
+    bool hasBlockedTask = false;
+    //
+    // Look for incremental analysis
+    //
+    if (_incrementalAnalysisCache != null &&
+        _incrementalAnalysisCache.hasWork) {
+      AnalysisTask task =
+          new IncrementalAnalysisTask(this, _incrementalAnalysisCache);
+      _incrementalAnalysisCache = null;
+      return task;
+    }
+    //
+    // Look for a source that needs to be analyzed because it has futures
+    // pending.
+    //
+    if (_pendingFutureSources.isNotEmpty) {
+      List<Source> sourcesToRemove = <Source>[];
+      AnalysisTask task;
+      for (Source source in _pendingFutureSources.keys) {
+        SourceEntry sourceEntry = _cache.get(source);
+        List<PendingFuture> pendingFutures = _pendingFutureSources[source];
+        for (int i = 0; i < pendingFutures.length;) {
+          if (pendingFutures[i].evaluate(sourceEntry)) {
+            pendingFutures.removeAt(i);
+          } else {
+            i++;
+          }
+        }
+        if (pendingFutures.isEmpty) {
+          sourcesToRemove.add(source);
+          continue;
+        }
+        AnalysisContextImpl_TaskData taskData = _getNextAnalysisTaskForSource(
+            source, sourceEntry, true, hintsEnabled, lintsEnabled);
+        task = taskData.task;
+        if (task != null) {
+          break;
+        } else if (taskData.isBlocked) {
+          hasBlockedTask = true;
+        } else {
+          // There is no more work to do for this task, so forcibly complete
+          // all its pending futures.
+          for (PendingFuture pendingFuture in pendingFutures) {
+            pendingFuture.forciblyComplete();
+          }
+          sourcesToRemove.add(source);
+        }
+      }
+      for (Source source in sourcesToRemove) {
+        _pendingFutureSources.remove(source);
+      }
+      if (task != null) {
+        return task;
+      }
+    }
+    //
+    // Look for a priority source that needs to be analyzed.
+    //
+    int priorityCount = _priorityOrder.length;
+    for (int i = 0; i < priorityCount; i++) {
+      Source source = _priorityOrder[i];
+      AnalysisContextImpl_TaskData taskData = _getNextAnalysisTaskForSource(
+          source, _cache.get(source), true, hintsEnabled, lintsEnabled);
+      AnalysisTask task = taskData.task;
+      if (task != null) {
+        return task;
+      } else if (taskData.isBlocked) {
+        hasBlockedTask = true;
+      }
+    }
+    if (_neededForResolution != null) {
+      List<Source> sourcesToRemove = new List<Source>();
+      for (Source source in _neededForResolution) {
+        SourceEntry sourceEntry = _cache.get(source);
+        if (sourceEntry is DartEntry) {
+          DartEntry dartEntry = sourceEntry;
+          if (!dartEntry.hasResolvableCompilationUnit) {
+            if (dartEntry.getState(DartEntry.PARSED_UNIT) == CacheState.ERROR) {
+              sourcesToRemove.add(source);
+            } else {
+              AnalysisContextImpl_TaskData taskData =
+                  _createParseDartTask(source, dartEntry);
+              AnalysisTask task = taskData.task;
+              if (task != null) {
+                return task;
+              } else if (taskData.isBlocked) {
+                hasBlockedTask = true;
+              }
+            }
+          }
+        }
+      }
+      int count = sourcesToRemove.length;
+      for (int i = 0; i < count; i++) {
+        _neededForResolution.remove(sourcesToRemove[i]);
+      }
+    }
+    //
+    // Look for a non-priority source that needs to be analyzed.
+    //
+    List<Source> sourcesToRemove = new List<Source>();
+    WorkManager_WorkIterator sources = _workManager.iterator();
+    try {
+      while (sources.hasNext) {
+        Source source = sources.next();
+        AnalysisContextImpl_TaskData taskData = _getNextAnalysisTaskForSource(
+            source, _cache.get(source), false, hintsEnabled, lintsEnabled);
+        AnalysisTask task = taskData.task;
+        if (task != null) {
+          return task;
+        } else if (taskData.isBlocked) {
+          hasBlockedTask = true;
+        } else {
+          sourcesToRemove.add(source);
+        }
+      }
+    } finally {
+      int count = sourcesToRemove.length;
+      for (int i = 0; i < count; i++) {
+        _workManager.remove(sourcesToRemove[i]);
+      }
+    }
+    if (hasBlockedTask) {
+      // All of the analysis work is blocked waiting for an asynchronous task
+      // to complete.
+      return WaitForAsyncTask.instance;
+    }
+    return null;
+  }
+
+  @override
+  Stream<SourcesChangedEvent> get onSourcesChanged =>
+      _onSourcesChangedController.stream;
+
+  /**
+   * Make _pendingFutureSources available to unit tests.
+   */
+  HashMap<Source, List<PendingFuture>> get pendingFutureSources_forTesting =>
+      _pendingFutureSources;
+
+  @override
+  List<Source> get prioritySources => _priorityOrder;
+
+  @override
+  List<AnalysisTarget> get priorityTargets => prioritySources;
+
+  @override
+  List<Source> get refactoringUnsafeSources {
+    List<Source> sources = new List<Source>();
+    MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      SourceEntry sourceEntry = iterator.value;
+      if (sourceEntry is DartEntry) {
+        Source source = iterator.key;
+        if (!source.isInSystemLibrary && !sourceEntry.isRefactoringSafe) {
+          sources.add(source);
+        }
+      }
+    }
+    return sources;
+  }
+
+  @override
+  SourceFactory get sourceFactory => _sourceFactory;
+
+  @override
+  void set sourceFactory(SourceFactory factory) {
+    if (identical(_sourceFactory, factory)) {
+      return;
+    } else if (factory.context != null) {
+      throw new IllegalStateException(
+          "Source factories cannot be shared between contexts");
+    }
+    if (_sourceFactory != null) {
+      _sourceFactory.context = null;
+    }
+    factory.context = this;
+    _sourceFactory = factory;
+    _coreLibrarySource = _sourceFactory.forUri(DartSdk.DART_CORE);
+    _asyncLibrarySource = _sourceFactory.forUri(DartSdk.DART_ASYNC);
+    _cache = createCacheFromSourceFactory(factory);
+    _invalidateAllLocalResolutionInformation(true);
+  }
+
+  @override
+  List<Source> get sources {
+    List<Source> sources = new List<Source>();
+    MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      sources.add(iterator.key);
+    }
+    return sources;
+  }
+
+  /**
+   * Return a list of the sources that would be processed by
+   * [performAnalysisTask]. This method duplicates, and must therefore be kept
+   * in sync with, [getNextAnalysisTask]. This method is intended to be used for
+   * testing purposes only.
+   */
+  List<Source> get sourcesNeedingProcessing {
+    HashSet<Source> sources = new HashSet<Source>();
+    bool hintsEnabled = _options.hint;
+    bool lintsEnabled = _options.lint;
+
+    //
+    // Look for priority sources that need to be analyzed.
+    //
+    for (Source source in _priorityOrder) {
+      _getSourcesNeedingProcessing(source, _cache.get(source), true,
+          hintsEnabled, lintsEnabled, sources);
+    }
+    //
+    // Look for non-priority sources that need to be analyzed.
+    //
+    WorkManager_WorkIterator iterator = _workManager.iterator();
+    while (iterator.hasNext) {
+      Source source = iterator.next();
+      _getSourcesNeedingProcessing(source, _cache.get(source), false,
+          hintsEnabled, lintsEnabled, sources);
+    }
+    return new List<Source>.from(sources);
+  }
+
+  @override
+  AnalysisContextStatistics get statistics {
+    AnalysisContextStatisticsImpl statistics =
+        new AnalysisContextStatisticsImpl();
+    visitCacheItems(statistics._internalPutCacheItem);
+    statistics.partitionData = _cache.partitionData;
+    return statistics;
+  }
+
+  IncrementalAnalysisCache get test_incrementalAnalysisCache {
+    return _incrementalAnalysisCache;
+  }
+
+  set test_incrementalAnalysisCache(IncrementalAnalysisCache value) {
+    _incrementalAnalysisCache = value;
+  }
+
+  List<Source> get test_priorityOrder => _priorityOrder;
+
+  @override
+  TypeProvider get typeProvider {
+    if (_typeProvider != null) {
+      return _typeProvider;
+    }
+    Source coreSource = sourceFactory.forUri(DartSdk.DART_CORE);
+    if (coreSource == null) {
+      throw new AnalysisException("Could not create a source for dart:core");
+    }
+    LibraryElement coreElement = computeLibraryElement(coreSource);
+    if (coreElement == null) {
+      throw new AnalysisException("Could not create an element for dart:core");
+    }
+    Source asyncSource = sourceFactory.forUri(DartSdk.DART_ASYNC);
+    if (asyncSource == null) {
+      throw new AnalysisException("Could not create a source for dart:async");
+    }
+    LibraryElement asyncElement = computeLibraryElement(asyncSource);
+    if (asyncElement == null) {
+      throw new AnalysisException("Could not create an element for dart:async");
+    }
+    _typeProvider = new TypeProviderImpl(coreElement, asyncElement);
+    return _typeProvider;
+  }
+
+  /**
+   * Sets the [TypeProvider] for this context.
+   */
+  void set typeProvider(TypeProvider typeProvider) {
+    _typeProvider = typeProvider;
+  }
+
+  @override
+  void addListener(AnalysisListener listener) {
+    if (!_listeners.contains(listener)) {
+      _listeners.add(listener);
+    }
+  }
+
+  @override
+  void addSourceInfo(Source source, SourceEntry info) {
+    // This implementation assumes that the access to the cache does not need to
+    // be synchronized because no other object can have access to this context
+    // while this method is being invoked.
+    _cache.put(source, info);
+  }
+
+  @override
+  void applyAnalysisDelta(AnalysisDelta delta) {
+    ChangeSet changeSet = new ChangeSet();
+    delta.analysisLevels.forEach((Source source, AnalysisLevel level) {
+      if (level == AnalysisLevel.NONE) {
+        changeSet.removedSource(source);
+      } else {
+        changeSet.addedSource(source);
+      }
+    });
+    applyChanges(changeSet);
+  }
+
+  @override
+  void applyChanges(ChangeSet changeSet) {
+    if (changeSet.isEmpty) {
+      return;
+    }
+    //
+    // First, compute the list of sources that have been removed.
+    //
+    List<Source> removedSources =
+        new List<Source>.from(changeSet.removedSources);
+    for (SourceContainer container in changeSet.removedContainers) {
+      _addSourcesInContainer(removedSources, container);
+    }
+    //
+    // Then determine which cached results are no longer valid.
+    //
+    for (Source source in changeSet.addedSources) {
+      _sourceAvailable(source);
+    }
+    for (Source source in changeSet.changedSources) {
+      if (_contentCache.getContents(source) != null) {
+        // This source is overridden in the content cache, so the change will
+        // have no effect. Just ignore it to avoid wasting time doing
+        // re-analysis.
+        continue;
+      }
+      _sourceChanged(source);
+    }
+    changeSet.changedContents.forEach((Source key, String value) {
+      _contentsChanged(key, value, false);
+    });
+    changeSet.changedRanges
+        .forEach((Source source, ChangeSet_ContentChange change) {
+      _contentRangeChanged(source, change.contents, change.offset,
+          change.oldLength, change.newLength);
+    });
+    for (Source source in changeSet.deletedSources) {
+      _sourceDeleted(source);
+    }
+    for (Source source in removedSources) {
+      _sourceRemoved(source);
+    }
+    _onSourcesChangedController.add(new SourcesChangedEvent(changeSet));
+  }
+
+  @override
+  String computeDocumentationComment(Element element) {
+    if (element == null) {
+      return null;
+    }
+    Source source = element.source;
+    if (source == null) {
+      return null;
+    }
+    CompilationUnit unit = parseCompilationUnit(source);
+    if (unit == null) {
+      return null;
+    }
+    NodeLocator locator = new NodeLocator.con1(element.nameOffset);
+    AstNode nameNode = locator.searchWithin(unit);
+    while (nameNode != null) {
+      if (nameNode is AnnotatedNode) {
+        Comment comment = nameNode.documentationComment;
+        if (comment == null) {
+          return null;
+        }
+        StringBuffer buffer = new StringBuffer();
+        List<Token> tokens = comment.tokens;
+        for (int i = 0; i < tokens.length; i++) {
+          if (i > 0) {
+            buffer.write("\n");
+          }
+          buffer.write(tokens[i].lexeme);
+        }
+        return buffer.toString();
+      }
+      nameNode = nameNode.parent;
+    }
+    return null;
+  }
+
+  @override
+  List<AnalysisError> computeErrors(Source source) {
+    bool enableHints = _options.hint;
+    bool enableLints = _options.lint;
+
+    SourceEntry sourceEntry = _getReadableSourceEntry(source);
+    if (sourceEntry is DartEntry) {
+      List<AnalysisError> errors = new List<AnalysisError>();
+      try {
+        DartEntry dartEntry = sourceEntry;
+        ListUtilities.addAll(
+            errors, _getDartScanData(source, dartEntry, DartEntry.SCAN_ERRORS));
+        dartEntry = _getReadableDartEntry(source);
+        ListUtilities.addAll(errors,
+            _getDartParseData(source, dartEntry, DartEntry.PARSE_ERRORS));
+        dartEntry = _getReadableDartEntry(source);
+        if (dartEntry.getValue(DartEntry.SOURCE_KIND) == SourceKind.LIBRARY) {
+          ListUtilities.addAll(errors, _getDartResolutionData(
+              source, source, dartEntry, DartEntry.RESOLUTION_ERRORS));
+          dartEntry = _getReadableDartEntry(source);
+          ListUtilities.addAll(errors, _getDartVerificationData(
+              source, source, dartEntry, DartEntry.VERIFICATION_ERRORS));
+          if (enableHints) {
+            dartEntry = _getReadableDartEntry(source);
+            ListUtilities.addAll(errors,
+                _getDartHintData(source, source, dartEntry, DartEntry.HINTS));
+          }
+          if (enableLints) {
+            dartEntry = _getReadableDartEntry(source);
+            ListUtilities.addAll(errors,
+                _getDartLintData(source, source, dartEntry, DartEntry.LINTS));
+          }
+        } else {
+          List<Source> libraries = getLibrariesContaining(source);
+          for (Source librarySource in libraries) {
+            ListUtilities.addAll(errors, _getDartResolutionData(
+                source, librarySource, dartEntry, DartEntry.RESOLUTION_ERRORS));
+            dartEntry = _getReadableDartEntry(source);
+            ListUtilities.addAll(errors, _getDartVerificationData(source,
+                librarySource, dartEntry, DartEntry.VERIFICATION_ERRORS));
+            if (enableHints) {
+              dartEntry = _getReadableDartEntry(source);
+              ListUtilities.addAll(errors, _getDartHintData(
+                  source, librarySource, dartEntry, DartEntry.HINTS));
+            }
+            if (enableLints) {
+              dartEntry = _getReadableDartEntry(source);
+              ListUtilities.addAll(errors, _getDartLintData(
+                  source, librarySource, dartEntry, DartEntry.LINTS));
+            }
+          }
+        }
+      } on ObsoleteSourceAnalysisException catch (exception, stackTrace) {
+        AnalysisEngine.instance.logger.logInformation(
+            "Could not compute errors",
+            new CaughtException(exception, stackTrace));
+      }
+      if (errors.isEmpty) {
+        return AnalysisError.NO_ERRORS;
+      }
+      return errors;
+    } else if (sourceEntry is HtmlEntry) {
+      HtmlEntry htmlEntry = sourceEntry;
+      try {
+        return _getHtmlResolutionData2(
+            source, htmlEntry, HtmlEntry.RESOLUTION_ERRORS);
+      } on ObsoleteSourceAnalysisException catch (exception, stackTrace) {
+        AnalysisEngine.instance.logger.logInformation(
+            "Could not compute errors",
+            new CaughtException(exception, stackTrace));
+      }
+    }
+    return AnalysisError.NO_ERRORS;
+  }
+
+  @override
+  List<Source> computeExportedLibraries(Source source) => _getDartParseData2(
+      source, DartEntry.EXPORTED_LIBRARIES, Source.EMPTY_ARRAY);
+
+  @override
+  HtmlElement computeHtmlElement(Source source) =>
+      _getHtmlResolutionData(source, HtmlEntry.ELEMENT, null);
+
+  @override
+  List<Source> computeImportedLibraries(Source source) => _getDartParseData2(
+      source, DartEntry.IMPORTED_LIBRARIES, Source.EMPTY_ARRAY);
+
+  @override
+  SourceKind computeKindOf(Source source) {
+    SourceEntry sourceEntry = _getReadableSourceEntry(source);
+    if (sourceEntry == null) {
+      return SourceKind.UNKNOWN;
+    } else if (sourceEntry is DartEntry) {
+      try {
+        return _getDartParseData(source, sourceEntry, DartEntry.SOURCE_KIND);
+      } on AnalysisException {
+        return SourceKind.UNKNOWN;
+      }
+    }
+    return sourceEntry.kind;
+  }
+
+  @override
+  LibraryElement computeLibraryElement(Source source) =>
+      _getDartResolutionData2(source, source, DartEntry.ELEMENT, null);
+
+  @override
+  LineInfo computeLineInfo(Source source) {
+    SourceEntry sourceEntry = _getReadableSourceEntry(source);
+    try {
+      if (sourceEntry is HtmlEntry) {
+        return _getHtmlParseData(source, SourceEntry.LINE_INFO, null);
+      } else if (sourceEntry is DartEntry) {
+        return _getDartScanData2(source, SourceEntry.LINE_INFO, null);
+      }
+    } on ObsoleteSourceAnalysisException catch (exception, stackTrace) {
+      AnalysisEngine.instance.logger.logInformation(
+          "Could not compute ${SourceEntry.LINE_INFO}",
+          new CaughtException(exception, stackTrace));
+    }
+    return null;
+  }
+
+  @override
+  CompilationUnit computeResolvableCompilationUnit(Source source) {
+    DartEntry dartEntry = _getReadableDartEntry(source);
+    if (dartEntry == null) {
+      throw new AnalysisException(
+          "computeResolvableCompilationUnit for non-Dart: ${source.fullName}");
+    }
+    dartEntry = _cacheDartParseData(source, dartEntry, DartEntry.PARSED_UNIT);
+    CompilationUnit unit = dartEntry.resolvableCompilationUnit;
+    if (unit == null) {
+      throw new AnalysisException(
+          "Internal error: computeResolvableCompilationUnit could not parse ${source.fullName}",
+          new CaughtException(dartEntry.exception, null));
+    }
+    return unit;
+  }
+
+  @override
+  CancelableFuture<CompilationUnit> computeResolvedCompilationUnitAsync(
+      Source unitSource, Source librarySource) {
+    return new _AnalysisFutureHelper<CompilationUnit>(this).computeAsync(
+        unitSource, (SourceEntry sourceEntry) {
+      if (sourceEntry is DartEntry) {
+        if (sourceEntry.getStateInLibrary(
+                DartEntry.RESOLVED_UNIT, librarySource) ==
+            CacheState.ERROR) {
+          throw sourceEntry.exception;
+        }
+        return sourceEntry.getValueInLibrary(
+            DartEntry.RESOLVED_UNIT, librarySource);
+      }
+      throw new AnalysisNotScheduledError();
+    });
+  }
+
+  /**
+   * Create an analysis cache based on the given source [factory].
+   */
+  AnalysisCache createCacheFromSourceFactory(SourceFactory factory) {
+    if (factory == null) {
+      return new AnalysisCache(<CachePartition>[_privatePartition]);
+    }
+    DartSdk sdk = factory.dartSdk;
+    if (sdk == null) {
+      return new AnalysisCache(<CachePartition>[_privatePartition]);
+    }
+    return new AnalysisCache(<CachePartition>[
+      AnalysisEngine.instance.partitionManager.forSdk(sdk),
+      _privatePartition
+    ]);
+  }
+
+  @override
+  void dispose() {
+    _disposed = true;
+    for (List<PendingFuture> pendingFutures in _pendingFutureSources.values) {
+      for (PendingFuture pendingFuture in pendingFutures) {
+        pendingFuture.forciblyComplete();
+      }
+    }
+    _pendingFutureSources.clear();
+  }
+
+  @override
+  List<CompilationUnit> ensureResolvedDartUnits(Source unitSource) {
+    SourceEntry sourceEntry = _cache.get(unitSource);
+    if (sourceEntry is! DartEntry) {
+      return null;
+    }
+    DartEntry dartEntry = sourceEntry;
+    // Check every library.
+    List<CompilationUnit> units = <CompilationUnit>[];
+    List<Source> containingLibraries = dartEntry.containingLibraries;
+    for (Source librarySource in containingLibraries) {
+      CompilationUnit unit =
+          dartEntry.getValueInLibrary(DartEntry.RESOLVED_UNIT, librarySource);
+      if (unit == null) {
+        units = null;
+        break;
+      }
+      units.add(unit);
+    }
+    // Invalidate the flushed RESOLVED_UNIT to force it eventually.
+    if (units == null) {
+      bool shouldBeScheduled = false;
+      for (Source librarySource in containingLibraries) {
+        if (dartEntry.getStateInLibrary(
+                DartEntry.RESOLVED_UNIT, librarySource) ==
+            CacheState.FLUSHED) {
+          dartEntry.setStateInLibrary(
+              DartEntry.RESOLVED_UNIT, librarySource, CacheState.INVALID);
+          shouldBeScheduled = true;
+        }
+      }
+      if (shouldBeScheduled) {
+        _workManager.add(unitSource, SourcePriority.UNKNOWN);
+      }
+      // We cannot provide resolved units right now,
+      // but the future analysis will.
+      return null;
+    }
+    // done
+    return units;
+  }
+
+  @override
+  bool exists(Source source) {
+    if (source == null) {
+      return false;
+    }
+    if (_contentCache.getContents(source) != null) {
+      return true;
+    }
+    return source.exists();
+  }
+
+  Element findElementById(int id) {
+    _ElementByIdFinder finder = new _ElementByIdFinder(id);
+    try {
+      MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+      while (iterator.moveNext()) {
+        SourceEntry sourceEntry = iterator.value;
+        if (sourceEntry.kind == SourceKind.LIBRARY) {
+          DartEntry dartEntry = sourceEntry;
+          LibraryElement library = dartEntry.getValue(DartEntry.ELEMENT);
+          if (library != null) {
+            library.accept(finder);
+          }
+        }
+      }
+    } on _ElementByIdFinderException {
+      return finder.result;
+    }
+    return null;
+  }
+
+  @override
+  cache.CacheEntry getCacheEntry(AnalysisTarget target) {
+    return null;
+  }
+
+  @override
+  CompilationUnitElement getCompilationUnitElement(
+      Source unitSource, Source librarySource) {
+    LibraryElement libraryElement = getLibraryElement(librarySource);
+    if (libraryElement != null) {
+      // try defining unit
+      CompilationUnitElement definingUnit =
+          libraryElement.definingCompilationUnit;
+      if (definingUnit.source == unitSource) {
+        return definingUnit;
+      }
+      // try parts
+      for (CompilationUnitElement partUnit in libraryElement.parts) {
+        if (partUnit.source == unitSource) {
+          return partUnit;
+        }
+      }
+    }
+    return null;
+  }
+
+  @override
+  TimestampedData<String> getContents(Source source) {
+    String contents = _contentCache.getContents(source);
+    if (contents != null) {
+      return new TimestampedData<String>(
+          _contentCache.getModificationStamp(source), contents);
+    }
+    return source.contents;
+  }
+
+  @override
+  InternalAnalysisContext getContextFor(Source source) {
+    InternalAnalysisContext context = _cache.getContextFor(source);
+    return context == null ? this : context;
+  }
+
+  @override
+  Element getElement(ElementLocation location) {
+    // TODO(brianwilkerson) This should not be a "get" method.
+    try {
+      List<String> components = location.components;
+      Source source = _computeSourceFromEncoding(components[0]);
+      String sourceName = source.shortName;
+      if (AnalysisEngine.isDartFileName(sourceName)) {
+        ElementImpl element = computeLibraryElement(source) as ElementImpl;
+        for (int i = 1; i < components.length; i++) {
+          if (element == null) {
+            return null;
+          }
+          element = element.getChild(components[i]);
+        }
+        return element;
+      }
+      if (AnalysisEngine.isHtmlFileName(sourceName)) {
+        return computeHtmlElement(source);
+      }
+    } catch (exception) {
+      // If the location cannot be decoded for some reason then the underlying
+      // cause should have been logged already and we can fall though to return
+      // null.
+    }
+    return null;
+  }
+
+  @override
+  AnalysisErrorInfo getErrors(Source source) {
+    SourceEntry sourceEntry = getReadableSourceEntryOrNull(source);
+    if (sourceEntry is DartEntry) {
+      DartEntry dartEntry = sourceEntry;
+      return new AnalysisErrorInfoImpl(
+          dartEntry.allErrors, dartEntry.getValue(SourceEntry.LINE_INFO));
+    } else if (sourceEntry is HtmlEntry) {
+      HtmlEntry htmlEntry = sourceEntry;
+      return new AnalysisErrorInfoImpl(
+          htmlEntry.allErrors, htmlEntry.getValue(SourceEntry.LINE_INFO));
+    }
+    return new AnalysisErrorInfoImpl(AnalysisError.NO_ERRORS, null);
+  }
+
+  @override
+  HtmlElement getHtmlElement(Source source) {
+    SourceEntry sourceEntry = getReadableSourceEntryOrNull(source);
+    if (sourceEntry is HtmlEntry) {
+      return sourceEntry.getValue(HtmlEntry.ELEMENT);
+    }
+    return null;
+  }
+
+  @override
+  List<Source> getHtmlFilesReferencing(Source source) {
+    SourceKind sourceKind = getKindOf(source);
+    if (sourceKind == null) {
+      return Source.EMPTY_ARRAY;
+    }
+    List<Source> htmlSources = new List<Source>();
+    while (true) {
+      if (sourceKind == SourceKind.PART) {
+        List<Source> librarySources = getLibrariesContaining(source);
+        MapIterator<Source, SourceEntry> partIterator = _cache.iterator();
+        while (partIterator.moveNext()) {
+          SourceEntry sourceEntry = partIterator.value;
+          if (sourceEntry.kind == SourceKind.HTML) {
+            List<Source> referencedLibraries = (sourceEntry as HtmlEntry)
+                .getValue(HtmlEntry.REFERENCED_LIBRARIES);
+            if (_containsAny(referencedLibraries, librarySources)) {
+              htmlSources.add(partIterator.key);
+            }
+          }
+        }
+      } else {
+        MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+        while (iterator.moveNext()) {
+          SourceEntry sourceEntry = iterator.value;
+          if (sourceEntry.kind == SourceKind.HTML) {
+            List<Source> referencedLibraries = (sourceEntry as HtmlEntry)
+                .getValue(HtmlEntry.REFERENCED_LIBRARIES);
+            if (_contains(referencedLibraries, source)) {
+              htmlSources.add(iterator.key);
+            }
+          }
+        }
+      }
+      break;
+    }
+    if (htmlSources.isEmpty) {
+      return Source.EMPTY_ARRAY;
+    }
+    return htmlSources;
+  }
+
+  @override
+  SourceKind getKindOf(Source source) {
+    SourceEntry sourceEntry = getReadableSourceEntryOrNull(source);
+    if (sourceEntry == null) {
+      return SourceKind.UNKNOWN;
+    }
+    return sourceEntry.kind;
+  }
+
+  @override
+  List<Source> getLibrariesContaining(Source source) {
+    SourceEntry sourceEntry = getReadableSourceEntryOrNull(source);
+    if (sourceEntry is DartEntry) {
+      return sourceEntry.containingLibraries;
+    }
+    return Source.EMPTY_ARRAY;
+  }
+
+  @override
+  List<Source> getLibrariesDependingOn(Source librarySource) {
+    List<Source> dependentLibraries = new List<Source>();
+    MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      SourceEntry sourceEntry = iterator.value;
+      if (sourceEntry.kind == SourceKind.LIBRARY) {
+        if (_contains(
+            (sourceEntry as DartEntry).getValue(DartEntry.EXPORTED_LIBRARIES),
+            librarySource)) {
+          dependentLibraries.add(iterator.key);
+        }
+        if (_contains(
+            (sourceEntry as DartEntry).getValue(DartEntry.IMPORTED_LIBRARIES),
+            librarySource)) {
+          dependentLibraries.add(iterator.key);
+        }
+      }
+    }
+    if (dependentLibraries.isEmpty) {
+      return Source.EMPTY_ARRAY;
+    }
+    return dependentLibraries;
+  }
+
+  @override
+  List<Source> getLibrariesReferencedFromHtml(Source htmlSource) {
+    SourceEntry sourceEntry = getReadableSourceEntryOrNull(htmlSource);
+    if (sourceEntry is HtmlEntry) {
+      HtmlEntry htmlEntry = sourceEntry;
+      return htmlEntry.getValue(HtmlEntry.REFERENCED_LIBRARIES);
+    }
+    return Source.EMPTY_ARRAY;
+  }
+
+  @override
+  LibraryElement getLibraryElement(Source source) {
+    SourceEntry sourceEntry = getReadableSourceEntryOrNull(source);
+    if (sourceEntry is DartEntry) {
+      return sourceEntry.getValue(DartEntry.ELEMENT);
+    }
+    return null;
+  }
+
+  @override
+  LineInfo getLineInfo(Source source) {
+    SourceEntry sourceEntry = getReadableSourceEntryOrNull(source);
+    if (sourceEntry != null) {
+      return sourceEntry.getValue(SourceEntry.LINE_INFO);
+    }
+    return null;
+  }
+
+  @override
+  int getModificationStamp(Source source) {
+    int stamp = _contentCache.getModificationStamp(source);
+    if (stamp != null) {
+      return stamp;
+    }
+    return source.modificationStamp;
+  }
+
+  @override
+  Namespace getPublicNamespace(LibraryElement library) {
+    // TODO(brianwilkerson) Rename this to not start with 'get'.
+    // Note that this is not part of the API of the interface.
+    Source source = library.definingCompilationUnit.source;
+    DartEntry dartEntry = _getReadableDartEntry(source);
+    if (dartEntry == null) {
+      return null;
+    }
+    Namespace namespace = null;
+    if (identical(dartEntry.getValue(DartEntry.ELEMENT), library)) {
+      namespace = dartEntry.getValue(DartEntry.PUBLIC_NAMESPACE);
+    }
+    if (namespace == null) {
+      NamespaceBuilder builder = new NamespaceBuilder();
+      namespace = builder.createPublicNamespaceForLibrary(library);
+      if (dartEntry == null) {
+        AnalysisEngine.instance.logger.logError(
+            "Could not compute the public namespace for ${library.source.fullName}",
+            new CaughtException(new AnalysisException(
+                    "A Dart file became a non-Dart file: ${source.fullName}"),
+                null));
+        return null;
+      }
+      if (identical(dartEntry.getValue(DartEntry.ELEMENT), library)) {
+        dartEntry.setValue(DartEntry.PUBLIC_NAMESPACE, namespace);
+      }
+    }
+    return namespace;
+  }
+
+  /**
+   * Return the cache entry associated with the given [source], or `null` if
+   * there is no entry associated with the source.
+   */
+  SourceEntry getReadableSourceEntryOrNull(Source source) => _cache.get(source);
+
+  @override
+  CompilationUnit getResolvedCompilationUnit(
+      Source unitSource, LibraryElement library) {
+    if (library == null) {
+      return null;
+    }
+    return getResolvedCompilationUnit2(unitSource, library.source);
+  }
+
+  @override
+  CompilationUnit getResolvedCompilationUnit2(
+      Source unitSource, Source librarySource) {
+    SourceEntry sourceEntry = getReadableSourceEntryOrNull(unitSource);
+    if (sourceEntry is DartEntry) {
+      return sourceEntry.getValueInLibrary(
+          DartEntry.RESOLVED_UNIT, librarySource);
+    }
+    return null;
+  }
+
+  @override
+  ht.HtmlUnit getResolvedHtmlUnit(Source htmlSource) {
+    SourceEntry sourceEntry = getReadableSourceEntryOrNull(htmlSource);
+    if (sourceEntry is HtmlEntry) {
+      HtmlEntry htmlEntry = sourceEntry;
+      return htmlEntry.getValue(HtmlEntry.RESOLVED_UNIT);
+    }
+    return null;
+  }
+
+  @override
+  List<Source> getSourcesWithFullName(String path) {
+    List<Source> sources = <Source>[];
+    MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      if (iterator.key.fullName == path) {
+        sources.add(iterator.key);
+      }
+    }
+    return sources;
+  }
+
+  @override
+  bool handleContentsChanged(
+      Source source, String originalContents, String newContents, bool notify) {
+    SourceEntry sourceEntry = _cache.get(source);
+    if (sourceEntry == null) {
+      return false;
+    }
+    bool changed = newContents != originalContents;
+    if (newContents != null) {
+      if (newContents != originalContents) {
+        _incrementalAnalysisCache =
+            IncrementalAnalysisCache.clear(_incrementalAnalysisCache, source);
+        if (!analysisOptions.incremental ||
+            !_tryPoorMansIncrementalResolution(source, newContents)) {
+          _sourceChanged(source);
+        }
+        sourceEntry.modificationTime =
+            _contentCache.getModificationStamp(source);
+        sourceEntry.setValue(SourceEntry.CONTENT, newContents);
+      } else {
+        sourceEntry.modificationTime =
+            _contentCache.getModificationStamp(source);
+      }
+    } else if (originalContents != null) {
+      _incrementalAnalysisCache =
+          IncrementalAnalysisCache.clear(_incrementalAnalysisCache, source);
+      changed = newContents != originalContents;
+      // We are removing the overlay for the file, check if the file's
+      // contents is the same as it was in the overlay.
+      try {
+        TimestampedData<String> fileContents = getContents(source);
+        String fileContentsData = fileContents.data;
+        if (fileContentsData == originalContents) {
+          sourceEntry.setValue(SourceEntry.CONTENT, fileContentsData);
+          sourceEntry.modificationTime = fileContents.modificationTime;
+          changed = false;
+        }
+      } catch (e) {}
+      // If not the same content (e.g. the file is being closed without save),
+      // then force analysis.
+      if (changed) {
+        _sourceChanged(source);
+      }
+    }
+    if (notify && changed) {
+      _onSourcesChangedController
+          .add(new SourcesChangedEvent.changedContent(source, newContents));
+    }
+    return changed;
+  }
+
+  /**
+   * Invalidates hints in the given [librarySource] and included parts.
+   */
+  void invalidateLibraryHints(Source librarySource) {
+    SourceEntry sourceEntry = _cache.get(librarySource);
+    if (sourceEntry is! DartEntry) {
+      return;
+    }
+    DartEntry dartEntry = sourceEntry;
+    // Prepare sources to invalidate hints in.
+    List<Source> sources = <Source>[librarySource];
+    sources.addAll(dartEntry.getValue(DartEntry.INCLUDED_PARTS));
+    // Invalidate hints.
+    for (Source source in sources) {
+      DartEntry dartEntry = _cache.get(source);
+      if (dartEntry.getStateInLibrary(DartEntry.HINTS, librarySource) ==
+          CacheState.VALID) {
+        dartEntry.setStateInLibrary(
+            DartEntry.HINTS, librarySource, CacheState.INVALID);
+      }
+    }
+  }
+
+  @override
+  bool isClientLibrary(Source librarySource) {
+    SourceEntry sourceEntry = _getReadableSourceEntry(librarySource);
+    if (sourceEntry is DartEntry) {
+      DartEntry dartEntry = sourceEntry;
+      return dartEntry.getValue(DartEntry.IS_CLIENT) &&
+          dartEntry.getValue(DartEntry.IS_LAUNCHABLE);
+    }
+    return false;
+  }
+
+  @override
+  bool isServerLibrary(Source librarySource) {
+    SourceEntry sourceEntry = _getReadableSourceEntry(librarySource);
+    if (sourceEntry is DartEntry) {
+      DartEntry dartEntry = sourceEntry;
+      return !dartEntry.getValue(DartEntry.IS_CLIENT) &&
+          dartEntry.getValue(DartEntry.IS_LAUNCHABLE);
+    }
+    return false;
+  }
+
+  @override
+  CompilationUnit parseCompilationUnit(Source source) =>
+      _getDartParseData2(source, DartEntry.PARSED_UNIT, null);
+
+  @override
+  ht.HtmlUnit parseHtmlUnit(Source source) =>
+      _getHtmlParseData(source, HtmlEntry.PARSED_UNIT, null);
+
+  @override
+  AnalysisResult performAnalysisTask() {
+    if (_TRACE_PERFORM_TASK) {
+      print("----------------------------------------");
+    }
+    return PerformanceStatistics.performAnaysis.makeCurrentWhile(() {
+      int getStart = JavaSystem.currentTimeMillis();
+      AnalysisTask task = PerformanceStatistics.nextTask
+          .makeCurrentWhile(() => nextAnalysisTask);
+      int getEnd = JavaSystem.currentTimeMillis();
+      if (task == null && _validateCacheConsistency()) {
+        task = nextAnalysisTask;
+      }
+      if (task == null) {
+        _validateLastIncrementalResolutionResult();
+        if (_performAnalysisTaskStopwatch != null) {
+          AnalysisEngine.instance.instrumentationService.logPerformance(
+              AnalysisPerformanceKind.FULL, _performAnalysisTaskStopwatch,
+              'context_id=$_id');
+          _performAnalysisTaskStopwatch = null;
+        }
+        return new AnalysisResult(
+            _getChangeNotices(true), getEnd - getStart, null, -1);
+      }
+      if (_performAnalysisTaskStopwatch == null) {
+        _performAnalysisTaskStopwatch = new Stopwatch()..start();
+      }
+      String taskDescription = task.toString();
+      _notifyAboutToPerformTask(taskDescription);
+      if (_TRACE_PERFORM_TASK) {
+        print(taskDescription);
+      }
+      int performStart = JavaSystem.currentTimeMillis();
+      try {
+        task.perform(_resultRecorder);
+      } on ObsoleteSourceAnalysisException catch (exception, stackTrace) {
+        AnalysisEngine.instance.logger.logInformation(
+            "Could not perform analysis task: $taskDescription",
+            new CaughtException(exception, stackTrace));
+      } on AnalysisException catch (exception, stackTrace) {
+        if (exception.cause is! JavaIOException) {
+          AnalysisEngine.instance.logger.logError(
+              "Internal error while performing the task: $task",
+              new CaughtException(exception, stackTrace));
+        }
+      }
+      int performEnd = JavaSystem.currentTimeMillis();
+      List<ChangeNotice> notices = _getChangeNotices(false);
+      int noticeCount = notices.length;
+      for (int i = 0; i < noticeCount; i++) {
+        ChangeNotice notice = notices[i];
+        Source source = notice.source;
+        // TODO(brianwilkerson) Figure out whether the compilation unit is
+        // always resolved, or whether we need to decide whether to invoke the
+        // "parsed" or "resolved" method. This might be better done when
+        // recording task results in order to reduce the chance of errors.
+//        if (notice.getCompilationUnit() != null) {
+//          notifyResolvedDart(source, notice.getCompilationUnit());
+//        } else if (notice.getHtmlUnit() != null) {
+//          notifyResolvedHtml(source, notice.getHtmlUnit());
+//        }
+        _notifyErrors(source, notice.errors, notice.lineInfo);
+      }
+      return new AnalysisResult(notices, getEnd - getStart,
+          task.runtimeType.toString(), performEnd - performStart);
+    });
+  }
+
+  @override
+  void recordLibraryElements(Map<Source, LibraryElement> elementMap) {
+    Source htmlSource = _sourceFactory.forUri(DartSdk.DART_HTML);
+    elementMap.forEach((Source librarySource, LibraryElement library) {
+      //
+      // Cache the element in the library's info.
+      //
+      DartEntry dartEntry = _getReadableDartEntry(librarySource);
+      if (dartEntry != null) {
+        _recordElementData(dartEntry, library, library.source, htmlSource);
+        dartEntry.setState(SourceEntry.CONTENT, CacheState.FLUSHED);
+        dartEntry.setValue(SourceEntry.LINE_INFO, new LineInfo(<int>[0]));
+        // DartEntry.ELEMENT - set in recordElementData
+        dartEntry.setValue(DartEntry.EXPORTED_LIBRARIES, Source.EMPTY_ARRAY);
+        dartEntry.setValue(DartEntry.IMPORTED_LIBRARIES, Source.EMPTY_ARRAY);
+        dartEntry.setValue(DartEntry.INCLUDED_PARTS, Source.EMPTY_ARRAY);
+        // DartEntry.IS_CLIENT - set in recordElementData
+        // DartEntry.IS_LAUNCHABLE - set in recordElementData
+        dartEntry.setValue(DartEntry.PARSE_ERRORS, AnalysisError.NO_ERRORS);
+        dartEntry.setState(DartEntry.PARSED_UNIT, CacheState.FLUSHED);
+        dartEntry.setState(DartEntry.PUBLIC_NAMESPACE, CacheState.FLUSHED);
+        dartEntry.setValue(DartEntry.SCAN_ERRORS, AnalysisError.NO_ERRORS);
+        dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.LIBRARY);
+        dartEntry.setState(DartEntry.TOKEN_STREAM, CacheState.FLUSHED);
+        dartEntry.setValueInLibrary(DartEntry.RESOLUTION_ERRORS, librarySource,
+            AnalysisError.NO_ERRORS);
+        dartEntry.setStateInLibrary(
+            DartEntry.RESOLVED_UNIT, librarySource, CacheState.FLUSHED);
+        dartEntry.setValueInLibrary(DartEntry.VERIFICATION_ERRORS,
+            librarySource, AnalysisError.NO_ERRORS);
+        dartEntry.setValueInLibrary(
+            DartEntry.HINTS, librarySource, AnalysisError.NO_ERRORS);
+        dartEntry.setValueInLibrary(
+            DartEntry.LINTS, librarySource, AnalysisError.NO_ERRORS);
+      }
+    });
+  }
+
+  /**
+   * Record the results produced by performing a [task] and return the cache
+   * entry associated with the results.
+   */
+  DartEntry recordResolveDartLibraryCycleTaskResults(
+      ResolveDartLibraryCycleTask task) {
+    LibraryResolver2 resolver = task.libraryResolver;
+    CaughtException thrownException = task.exception;
+    Source unitSource = task.unitSource;
+    DartEntry unitEntry = _getReadableDartEntry(unitSource);
+    if (resolver != null) {
+      //
+      // The resolver should only be null if an exception was thrown before (or
+      // while) it was being created.
+      //
+      List<ResolvableLibrary> resolvedLibraries = resolver.resolvedLibraries;
+      if (resolvedLibraries == null) {
+        //
+        // The resolved libraries should only be null if an exception was thrown
+        // during resolution.
+        //
+        if (thrownException == null) {
+          var message = "In recordResolveDartLibraryCycleTaskResults, "
+              "resolvedLibraries was null and there was no thrown exception";
+          unitEntry.recordResolutionError(
+              new CaughtException(new AnalysisException(message), null));
+        } else {
+          unitEntry.recordResolutionError(thrownException);
+        }
+        _cache.remove(unitSource);
+        if (thrownException != null) {
+          throw new AnalysisException('<rethrow>', thrownException);
+        }
+        return unitEntry;
+      }
+      Source htmlSource = sourceFactory.forUri(DartSdk.DART_HTML);
+      RecordingErrorListener errorListener = resolver.errorListener;
+      for (ResolvableLibrary library in resolvedLibraries) {
+        Source librarySource = library.librarySource;
+        for (Source source in library.compilationUnitSources) {
+          CompilationUnit unit = library.getAST(source);
+          List<AnalysisError> errors = errorListener.getErrorsForSource(source);
+          LineInfo lineInfo = getLineInfo(source);
+          DartEntry dartEntry = _cache.get(source);
+          if (thrownException == null) {
+            dartEntry.setState(DartEntry.PARSED_UNIT, CacheState.FLUSHED);
+            dartEntry.setValueInLibrary(
+                DartEntry.RESOLVED_UNIT, librarySource, unit);
+            dartEntry.setValueInLibrary(
+                DartEntry.RESOLUTION_ERRORS, librarySource, errors);
+            if (source == librarySource) {
+              _recordElementData(
+                  dartEntry, library.libraryElement, librarySource, htmlSource);
+            }
+            _cache.storedAst(source);
+          } else {
+            dartEntry.recordResolutionErrorInLibrary(
+                librarySource, thrownException);
+          }
+          if (source != librarySource) {
+            _workManager.add(source, SourcePriority.PRIORITY_PART);
+          }
+          ChangeNoticeImpl notice = _getNotice(source);
+          notice.resolvedDartUnit = unit;
+          notice.setErrors(dartEntry.allErrors, lineInfo);
+        }
+      }
+    }
+    if (thrownException != null) {
+      throw new AnalysisException('<rethrow>', thrownException);
+    }
+    return unitEntry;
+  }
+
+  /**
+   * Record the results produced by performing a [task] and return the cache
+   * entry associated with the results.
+   */
+  DartEntry recordResolveDartLibraryTaskResults(ResolveDartLibraryTask task) {
+    LibraryResolver resolver = task.libraryResolver;
+    CaughtException thrownException = task.exception;
+    Source unitSource = task.unitSource;
+    DartEntry unitEntry = _getReadableDartEntry(unitSource);
+    if (resolver != null) {
+      //
+      // The resolver should only be null if an exception was thrown before (or
+      // while) it was being created.
+      //
+      Set<Library> resolvedLibraries = resolver.resolvedLibraries;
+      if (resolvedLibraries == null) {
+        //
+        // The resolved libraries should only be null if an exception was thrown
+        // during resolution.
+        //
+        if (thrownException == null) {
+          String message = "In recordResolveDartLibraryTaskResults, "
+              "resolvedLibraries was null and there was no thrown exception";
+          unitEntry.recordResolutionError(
+              new CaughtException(new AnalysisException(message), null));
+        } else {
+          unitEntry.recordResolutionError(thrownException);
+        }
+        _cache.remove(unitSource);
+        if (thrownException != null) {
+          throw new AnalysisException('<rethrow>', thrownException);
+        }
+        return unitEntry;
+      }
+      Source htmlSource = sourceFactory.forUri(DartSdk.DART_HTML);
+      RecordingErrorListener errorListener = resolver.errorListener;
+      for (Library library in resolvedLibraries) {
+        Source librarySource = library.librarySource;
+        for (Source source in library.compilationUnitSources) {
+          CompilationUnit unit = library.getAST(source);
+          List<AnalysisError> errors = errorListener.getErrorsForSource(source);
+          LineInfo lineInfo = getLineInfo(source);
+          DartEntry dartEntry = _cache.get(source);
+          if (thrownException == null) {
+            dartEntry.setValue(SourceEntry.LINE_INFO, lineInfo);
+            dartEntry.setState(DartEntry.PARSED_UNIT, CacheState.FLUSHED);
+            dartEntry.setValueInLibrary(
+                DartEntry.RESOLVED_UNIT, librarySource, unit);
+            dartEntry.setValueInLibrary(
+                DartEntry.RESOLUTION_ERRORS, librarySource, errors);
+            if (source == librarySource) {
+              _recordElementData(
+                  dartEntry, library.libraryElement, librarySource, htmlSource);
+            }
+            _cache.storedAst(source);
+          } else {
+            dartEntry.recordResolutionErrorInLibrary(
+                librarySource, thrownException);
+            _cache.remove(source);
+          }
+          if (source != librarySource) {
+            _workManager.add(source, SourcePriority.PRIORITY_PART);
+          }
+          ChangeNoticeImpl notice = _getNotice(source);
+          notice.resolvedDartUnit = unit;
+          notice.setErrors(dartEntry.allErrors, lineInfo);
+        }
+      }
+    }
+    if (thrownException != null) {
+      throw new AnalysisException('<rethrow>', thrownException);
+    }
+    return unitEntry;
+  }
+
+  @override
+  void removeListener(AnalysisListener listener) {
+    _listeners.remove(listener);
+  }
+
+  @override
+  CompilationUnit resolveCompilationUnit(
+      Source unitSource, LibraryElement library) {
+    if (library == null) {
+      return null;
+    }
+    return resolveCompilationUnit2(unitSource, library.source);
+  }
+
+  @override
+  CompilationUnit resolveCompilationUnit2(
+      Source unitSource, Source librarySource) => _getDartResolutionData2(
+          unitSource, librarySource, DartEntry.RESOLVED_UNIT, null);
+
+  @override
+  ht.HtmlUnit resolveHtmlUnit(Source htmlSource) {
+    computeHtmlElement(htmlSource);
+    return parseHtmlUnit(htmlSource);
+  }
+
+  @override
+  void setChangedContents(Source source, String contents, int offset,
+      int oldLength, int newLength) {
+    if (_contentRangeChanged(source, contents, offset, oldLength, newLength)) {
+      _onSourcesChangedController.add(new SourcesChangedEvent.changedRange(
+          source, contents, offset, oldLength, newLength));
+    }
+  }
+
+  @override
+  void setContents(Source source, String contents) {
+    _contentsChanged(source, contents, true);
+  }
+
+  @override
+  void visitCacheItems(void callback(Source source, SourceEntry dartEntry,
+      DataDescriptor rowDesc, CacheState state)) {
+    bool hintsEnabled = _options.hint;
+    bool lintsEnabled = _options.lint;
+    MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      Source source = iterator.key;
+      SourceEntry sourceEntry = iterator.value;
+      for (DataDescriptor descriptor in sourceEntry.descriptors) {
+        if (descriptor == DartEntry.SOURCE_KIND) {
+          // The source kind is always valid, so the state isn't interesting.
+          continue;
+        } else if (descriptor == DartEntry.CONTAINING_LIBRARIES) {
+          // The list of containing libraries is always valid, so the state
+          // isn't interesting.
+          continue;
+        } else if (descriptor == DartEntry.PUBLIC_NAMESPACE) {
+          // The public namespace isn't computed by performAnalysisTask()
+          // and therefore isn't interesting.
+          continue;
+        } else if (descriptor == HtmlEntry.HINTS) {
+          // We are not currently recording any hints related to HTML.
+          continue;
+        }
+        callback(
+            source, sourceEntry, descriptor, sourceEntry.getState(descriptor));
+      }
+      if (sourceEntry is DartEntry) {
+        // get library-specific values
+        List<Source> librarySources = getLibrariesContaining(source);
+        for (Source librarySource in librarySources) {
+          for (DataDescriptor descriptor in sourceEntry.libraryDescriptors) {
+            if (descriptor == DartEntry.BUILT_ELEMENT ||
+                descriptor == DartEntry.BUILT_UNIT) {
+              // These values are not currently being computed, so their state
+              // is not interesting.
+              continue;
+            } else if (!sourceEntry.explicitlyAdded &&
+                !_generateImplicitErrors &&
+                (descriptor == DartEntry.VERIFICATION_ERRORS ||
+                    descriptor == DartEntry.HINTS ||
+                    descriptor == DartEntry.LINTS)) {
+              continue;
+            } else if (source.isInSystemLibrary &&
+                !_generateSdkErrors &&
+                (descriptor == DartEntry.VERIFICATION_ERRORS ||
+                    descriptor == DartEntry.HINTS ||
+                    descriptor == DartEntry.LINTS)) {
+              continue;
+            } else if (!hintsEnabled && descriptor == DartEntry.HINTS) {
+              continue;
+            } else if (!lintsEnabled && descriptor == DartEntry.LINTS) {
+              continue;
+            }
+            callback(librarySource, sourceEntry, descriptor,
+                sourceEntry.getStateInLibrary(descriptor, librarySource));
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Visit all entries of the content cache.
+   */
+  void visitContentCache(ContentCacheVisitor visitor) {
+    _contentCache.accept(visitor);
+  }
+
+  /**
+   * Record that we have accessed the AST structure associated with the given
+   * [source]. At the moment, there is no differentiation between the parsed and
+   * resolved forms of the AST.
+   */
+  void _accessedAst(Source source) {
+    _cache.accessedAst(source);
+  }
+
+  /**
+   * Add all of the sources contained in the given source [container] to the
+   * given list of [sources].
+   */
+  void _addSourcesInContainer(List<Source> sources, SourceContainer container) {
+    MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      Source source = iterator.key;
+      if (container.contains(source)) {
+        sources.add(source);
+      }
+    }
+  }
+
+  /**
+   * Given the [unitSource] of a Dart file and the [librarySource] of the
+   * library that contains it, return a cache entry in which the state of the
+   * data represented by the given [descriptor] is either [CacheState.VALID] or
+   * [CacheState.ERROR]. This method assumes that the data can be produced by
+   * generating hints for the library if the data is not already cached. The
+   * [dartEntry] is the cache entry associated with the Dart file.
+   *
+   * Throws an [AnalysisException] if data could not be returned because the
+   * source could not be parsed.
+   */
+  DartEntry _cacheDartHintData(Source unitSource, Source librarySource,
+      DartEntry dartEntry, DataDescriptor descriptor) {
+    //
+    // Check to see whether we already have the information being requested.
+    //
+    CacheState state = dartEntry.getStateInLibrary(descriptor, librarySource);
+    while (state != CacheState.ERROR && state != CacheState.VALID) {
+      //
+      // If not, compute the information.
+      // Unless the modification date of the source continues to change,
+      // this loop will eventually terminate.
+      //
+      DartEntry libraryEntry = _getReadableDartEntry(librarySource);
+      libraryEntry = _cacheDartResolutionData(
+          librarySource, librarySource, libraryEntry, DartEntry.ELEMENT);
+      LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT);
+      CompilationUnitElement definingUnit =
+          libraryElement.definingCompilationUnit;
+      List<CompilationUnitElement> parts = libraryElement.parts;
+      List<TimestampedData<CompilationUnit>> units =
+          new List<TimestampedData>(parts.length + 1);
+      units[0] = _getResolvedUnit(definingUnit, librarySource);
+      if (units[0] == null) {
+        Source source = definingUnit.source;
+        units[0] = new TimestampedData<CompilationUnit>(
+            getModificationStamp(source),
+            resolveCompilationUnit(source, libraryElement));
+      }
+      for (int i = 0; i < parts.length; i++) {
+        units[i + 1] = _getResolvedUnit(parts[i], librarySource);
+        if (units[i + 1] == null) {
+          Source source = parts[i].source;
+          units[i + 1] = new TimestampedData<CompilationUnit>(
+              getModificationStamp(source),
+              resolveCompilationUnit(source, libraryElement));
+        }
+      }
+      dartEntry = new GenerateDartHintsTask(
+              this, units, getLibraryElement(librarySource))
+          .perform(_resultRecorder) as DartEntry;
+      state = dartEntry.getStateInLibrary(descriptor, librarySource);
+    }
+    return dartEntry;
+  }
+
+  /**
+   * Given a source for a Dart file and the library that contains it, return a
+   * cache entry in which the state of the data represented by the given
+   * descriptor is either [CacheState.VALID] or [CacheState.ERROR]. This method
+   * assumes that the data can be produced by generating lints for the library
+   * if the data is not already cached.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  DartEntry _cacheDartLintData(Source unitSource, Source librarySource,
+      DartEntry dartEntry, DataDescriptor descriptor) {
+    //
+    // Check to see whether we already have the information being requested.
+    //
+    CacheState state = dartEntry.getStateInLibrary(descriptor, librarySource);
+    while (state != CacheState.ERROR && state != CacheState.VALID) {
+      //
+      // If not, compute the information.
+      // Unless the modification date of the source continues to change,
+      // this loop will eventually terminate.
+      //
+      DartEntry libraryEntry = _getReadableDartEntry(librarySource);
+      libraryEntry = _cacheDartResolutionData(
+          librarySource, librarySource, libraryEntry, DartEntry.ELEMENT);
+      LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT);
+      CompilationUnitElement definingUnit =
+          libraryElement.definingCompilationUnit;
+      List<CompilationUnitElement> parts = libraryElement.parts;
+      List<TimestampedData<CompilationUnit>> units =
+          new List<TimestampedData>(parts.length + 1);
+      units[0] = _getResolvedUnit(definingUnit, librarySource);
+      if (units[0] == null) {
+        Source source = definingUnit.source;
+        units[0] = new TimestampedData<CompilationUnit>(
+            getModificationStamp(source),
+            resolveCompilationUnit(source, libraryElement));
+      }
+      for (int i = 0; i < parts.length; i++) {
+        units[i + 1] = _getResolvedUnit(parts[i], librarySource);
+        if (units[i + 1] == null) {
+          Source source = parts[i].source;
+          units[i + 1] = new TimestampedData<CompilationUnit>(
+              getModificationStamp(source),
+              resolveCompilationUnit(source, libraryElement));
+        }
+      }
+      //TODO(pquitslund): revisit if we need all units or whether one will do
+      dartEntry = new GenerateDartLintsTask(
+              this, units, getLibraryElement(librarySource))
+          .perform(_resultRecorder) as DartEntry;
+      state = dartEntry.getStateInLibrary(descriptor, librarySource);
+    }
+    return dartEntry;
+  }
+
+  /**
+   * Given a source for a Dart file, return a cache entry in which the state of
+   * the data represented by the given descriptor is either [CacheState.VALID]
+   * or [CacheState.ERROR]. This method assumes that the data can be produced by
+   * parsing the source if it is not already cached.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  DartEntry _cacheDartParseData(
+      Source source, DartEntry dartEntry, DataDescriptor descriptor) {
+    if (identical(descriptor, DartEntry.PARSED_UNIT)) {
+      if (dartEntry.hasResolvableCompilationUnit) {
+        return dartEntry;
+      }
+    }
+    //
+    // Check to see whether we already have the information being requested.
+    //
+    CacheState state = dartEntry.getState(descriptor);
+    while (state != CacheState.ERROR && state != CacheState.VALID) {
+      //
+      // If not, compute the information. Unless the modification date of the
+      // source continues to change, this loop will eventually terminate.
+      //
+      dartEntry = _cacheDartScanData(source, dartEntry, DartEntry.TOKEN_STREAM);
+      dartEntry = new ParseDartTask(this, source,
+              dartEntry.getValue(DartEntry.TOKEN_STREAM),
+              dartEntry.getValue(SourceEntry.LINE_INFO))
+          .perform(_resultRecorder) as DartEntry;
+      state = dartEntry.getState(descriptor);
+    }
+    return dartEntry;
+  }
+
+  /**
+   * Given a source for a Dart file and the library that contains it, return a
+   * cache entry in which the state of the data represented by the given
+   * descriptor is either [CacheState.VALID] or [CacheState.ERROR]. This method
+   * assumes that the data can be produced by resolving the source in the
+   * context of the library if it is not already cached.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  DartEntry _cacheDartResolutionData(Source unitSource, Source librarySource,
+      DartEntry dartEntry, DataDescriptor descriptor) {
+    //
+    // Check to see whether we already have the information being requested.
+    //
+    CacheState state = (identical(descriptor, DartEntry.ELEMENT))
+        ? dartEntry.getState(descriptor)
+        : dartEntry.getStateInLibrary(descriptor, librarySource);
+    while (state != CacheState.ERROR && state != CacheState.VALID) {
+      //
+      // If not, compute the information. Unless the modification date of the
+      // source continues to change, this loop will eventually terminate.
+      //
+      // TODO(brianwilkerson) As an optimization, if we already have the
+      // element model for the library we can use ResolveDartUnitTask to produce
+      // the resolved AST structure much faster.
+      dartEntry = new ResolveDartLibraryTask(this, unitSource, librarySource)
+          .perform(_resultRecorder) as DartEntry;
+      state = (identical(descriptor, DartEntry.ELEMENT))
+          ? dartEntry.getState(descriptor)
+          : dartEntry.getStateInLibrary(descriptor, librarySource);
+    }
+    return dartEntry;
+  }
+
+  /**
+   * Given a source for a Dart file, return a cache entry in which the state of
+   * the data represented by the given descriptor is either [CacheState.VALID]
+   * or [CacheState.ERROR]. This method assumes that the data can be produced by
+   * scanning the source if it is not already cached.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  DartEntry _cacheDartScanData(
+      Source source, DartEntry dartEntry, DataDescriptor descriptor) {
+    //
+    // Check to see whether we already have the information being requested.
+    //
+    CacheState state = dartEntry.getState(descriptor);
+    while (state != CacheState.ERROR && state != CacheState.VALID) {
+      //
+      // If not, compute the information. Unless the modification date of the
+      // source continues to change, this loop will eventually terminate.
+      //
+      try {
+        if (dartEntry.getState(SourceEntry.CONTENT) != CacheState.VALID) {
+          dartEntry = new GetContentTask(this, source)
+              .perform(_resultRecorder) as DartEntry;
+        }
+        dartEntry = new ScanDartTask(
+                this, source, dartEntry.getValue(SourceEntry.CONTENT))
+            .perform(_resultRecorder) as DartEntry;
+      } on AnalysisException catch (exception) {
+        throw exception;
+      } catch (exception, stackTrace) {
+        throw new AnalysisException(
+            "Exception", new CaughtException(exception, stackTrace));
+      }
+      state = dartEntry.getState(descriptor);
+    }
+    return dartEntry;
+  }
+
+  /**
+   * Given a source for a Dart file and the library that contains it, return a
+   * cache entry in which the state of the data represented by the given
+   * descriptor is either [CacheState.VALID] or [CacheState.ERROR]. This method
+   * assumes that the data can be produced by verifying the source in the given
+   * library if the data is not already cached.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  DartEntry _cacheDartVerificationData(Source unitSource, Source librarySource,
+      DartEntry dartEntry, DataDescriptor descriptor) {
+    //
+    // Check to see whether we already have the information being requested.
+    //
+    CacheState state = dartEntry.getStateInLibrary(descriptor, librarySource);
+    while (state != CacheState.ERROR && state != CacheState.VALID) {
+      //
+      // If not, compute the information. Unless the modification date of the
+      // source continues to change, this loop will eventually terminate.
+      //
+      LibraryElement library = computeLibraryElement(librarySource);
+      CompilationUnit unit = resolveCompilationUnit(unitSource, library);
+      if (unit == null) {
+        throw new AnalysisException(
+            "Could not resolve compilation unit ${unitSource.fullName} in ${librarySource.fullName}");
+      }
+      dartEntry = new GenerateDartErrorsTask(this, unitSource, unit, library)
+          .perform(_resultRecorder) as DartEntry;
+      state = dartEntry.getStateInLibrary(descriptor, librarySource);
+    }
+    return dartEntry;
+  }
+
+  /**
+   * Given a source for an HTML file, return a cache entry in which all of the
+   * data represented by the state of the given descriptors is either
+   * [CacheState.VALID] or [CacheState.ERROR]. This method assumes that the data
+   * can be produced by parsing the source if it is not already cached.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  HtmlEntry _cacheHtmlParseData(
+      Source source, HtmlEntry htmlEntry, DataDescriptor descriptor) {
+    if (identical(descriptor, HtmlEntry.PARSED_UNIT)) {
+      ht.HtmlUnit unit = htmlEntry.anyParsedUnit;
+      if (unit != null) {
+        return htmlEntry;
+      }
+    }
+    //
+    // Check to see whether we already have the information being requested.
+    //
+    CacheState state = htmlEntry.getState(descriptor);
+    while (state != CacheState.ERROR && state != CacheState.VALID) {
+      //
+      // If not, compute the information. Unless the modification date of the
+      // source continues to change, this loop will eventually terminate.
+      //
+      try {
+        if (htmlEntry.getState(SourceEntry.CONTENT) != CacheState.VALID) {
+          htmlEntry = new GetContentTask(this, source)
+              .perform(_resultRecorder) as HtmlEntry;
+        }
+        htmlEntry = new ParseHtmlTask(
+                this, source, htmlEntry.getValue(SourceEntry.CONTENT))
+            .perform(_resultRecorder) as HtmlEntry;
+      } on AnalysisException catch (exception) {
+        throw exception;
+      } catch (exception, stackTrace) {
+        throw new AnalysisException(
+            "Exception", new CaughtException(exception, stackTrace));
+      }
+      state = htmlEntry.getState(descriptor);
+    }
+    return htmlEntry;
+  }
+
+  /**
+   * Given a source for an HTML file, return a cache entry in which the state of
+   * the data represented by the given descriptor is either [CacheState.VALID]
+   * or [CacheState.ERROR]. This method assumes that the data can be produced by
+   * resolving the source if it is not already cached.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  HtmlEntry _cacheHtmlResolutionData(
+      Source source, HtmlEntry htmlEntry, DataDescriptor descriptor) {
+    //
+    // Check to see whether we already have the information being requested.
+    //
+    CacheState state = htmlEntry.getState(descriptor);
+    while (state != CacheState.ERROR && state != CacheState.VALID) {
+      //
+      // If not, compute the information. Unless the modification date of the
+      // source continues to change, this loop will eventually terminate.
+      //
+      htmlEntry = _cacheHtmlParseData(source, htmlEntry, HtmlEntry.PARSED_UNIT);
+      htmlEntry = new ResolveHtmlTask(this, source, htmlEntry.modificationTime,
+              htmlEntry.getValue(HtmlEntry.PARSED_UNIT))
+          .perform(_resultRecorder) as HtmlEntry;
+      state = htmlEntry.getState(descriptor);
+    }
+    return htmlEntry;
+  }
+
+  /**
+   * Remove the given [pendingFuture] from [_pendingFutureSources], since the
+   * client has indicated its computation is not needed anymore.
+   */
+  void _cancelFuture(PendingFuture pendingFuture) {
+    List<PendingFuture> pendingFutures =
+        _pendingFutureSources[pendingFuture.source];
+    if (pendingFutures != null) {
+      pendingFutures.remove(pendingFuture);
+      if (pendingFutures.isEmpty) {
+        _pendingFutureSources.remove(pendingFuture.source);
+      }
+    }
+  }
+
+  /**
+   * Compute the transitive closure of all libraries that depend on the given
+   * [library] by adding such libraries to the given collection of
+   * [librariesToInvalidate].
+   */
+  void _computeAllLibrariesDependingOn(
+      Source library, HashSet<Source> librariesToInvalidate) {
+    if (librariesToInvalidate.add(library)) {
+      for (Source dependentLibrary in getLibrariesDependingOn(library)) {
+        _computeAllLibrariesDependingOn(
+            dependentLibrary, librariesToInvalidate);
+      }
+    }
+  }
+
+  /**
+   * Return the priority that should be used when the source associated with
+   * the given [dartEntry] is added to the work manager.
+   */
+  SourcePriority _computePriority(DartEntry dartEntry) {
+    SourceKind kind = dartEntry.kind;
+    if (kind == SourceKind.LIBRARY) {
+      return SourcePriority.LIBRARY;
+    } else if (kind == SourceKind.PART) {
+      return SourcePriority.NORMAL_PART;
+    }
+    return SourcePriority.UNKNOWN;
+  }
+
+  /**
+   * Given the encoded form of a source ([encoding]), use the source factory to
+   * reconstitute the original source.
+   */
+  Source _computeSourceFromEncoding(String encoding) =>
+      _sourceFactory.fromEncoding(encoding);
+
+  /**
+   * Return `true` if the given list of [sources] contains the given
+   * [targetSource].
+   */
+  bool _contains(List<Source> sources, Source targetSource) {
+    for (Source source in sources) {
+      if (source == targetSource) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the given list of [sources] contains any of the given
+   * [targetSources].
+   */
+  bool _containsAny(List<Source> sources, List<Source> targetSources) {
+    for (Source targetSource in targetSources) {
+      if (_contains(sources, targetSource)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Set the contents of the given [source] to the given [contents] and mark the
+   * source as having changed. The additional [offset], [oldLength] and
+   * [newLength] information is used by the context to determine what reanalysis
+   * is necessary. The method [setChangedContents] triggers a source changed
+   * event where as this method does not.
+   */
+  bool _contentRangeChanged(Source source, String contents, int offset,
+      int oldLength, int newLength) {
+    bool changed = false;
+    String originalContents = _contentCache.setContents(source, contents);
+    if (contents != null) {
+      if (contents != originalContents) {
+        if (_options.incremental) {
+          _incrementalAnalysisCache = IncrementalAnalysisCache.update(
+              _incrementalAnalysisCache, source, originalContents, contents,
+              offset, oldLength, newLength, _getReadableSourceEntry(source));
+        }
+        _sourceChanged(source);
+        changed = true;
+        SourceEntry sourceEntry = _cache.get(source);
+        if (sourceEntry != null) {
+          sourceEntry.modificationTime =
+              _contentCache.getModificationStamp(source);
+          sourceEntry.setValue(SourceEntry.CONTENT, contents);
+        }
+      }
+    } else if (originalContents != null) {
+      _incrementalAnalysisCache =
+          IncrementalAnalysisCache.clear(_incrementalAnalysisCache, source);
+      _sourceChanged(source);
+      changed = true;
+    }
+    return changed;
+  }
+
+  /**
+   * Set the contents of the given [source] to the given [contents] and mark the
+   * source as having changed. This has the effect of overriding the default
+   * contents of the source. If the contents are `null` the override is removed
+   * so that the default contents will be returned. If [notify] is true, a
+   * source changed event is triggered.
+   */
+  void _contentsChanged(Source source, String contents, bool notify) {
+    String originalContents = _contentCache.setContents(source, contents);
+    handleContentsChanged(source, originalContents, contents, notify);
+  }
+
+  /**
+   * Create a [GenerateDartErrorsTask] for the given [unitSource], marking the
+   * verification errors as being in-process. The compilation unit and the
+   * library can be the same if the compilation unit is the defining compilation
+   * unit of the library.
+   */
+  AnalysisContextImpl_TaskData _createGenerateDartErrorsTask(Source unitSource,
+      DartEntry unitEntry, Source librarySource, DartEntry libraryEntry) {
+    if (unitEntry.getStateInLibrary(DartEntry.RESOLVED_UNIT, librarySource) !=
+            CacheState.VALID ||
+        libraryEntry.getState(DartEntry.ELEMENT) != CacheState.VALID) {
+      return _createResolveDartLibraryTask(librarySource, libraryEntry);
+    }
+    CompilationUnit unit =
+        unitEntry.getValueInLibrary(DartEntry.RESOLVED_UNIT, librarySource);
+    if (unit == null) {
+      CaughtException exception = new CaughtException(new AnalysisException(
+              "Entry has VALID state for RESOLVED_UNIT but null value for ${unitSource.fullName} in ${librarySource.fullName}"),
+          null);
+      AnalysisEngine.instance.logger.logInformation(
+          exception.toString(), exception);
+      unitEntry.recordResolutionError(exception);
+      return new AnalysisContextImpl_TaskData(null, false);
+    }
+    LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT);
+    return new AnalysisContextImpl_TaskData(
+        new GenerateDartErrorsTask(this, unitSource, unit, libraryElement),
+        false);
+  }
+
+  /**
+   * Create a [GenerateDartHintsTask] for the given [source], marking the hints
+   * as being in-process.
+   */
+  AnalysisContextImpl_TaskData _createGenerateDartHintsTask(Source source,
+      DartEntry dartEntry, Source librarySource, DartEntry libraryEntry) {
+    if (libraryEntry.getState(DartEntry.ELEMENT) != CacheState.VALID) {
+      return _createResolveDartLibraryTask(librarySource, libraryEntry);
+    }
+    LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT);
+    CompilationUnitElement definingUnit =
+        libraryElement.definingCompilationUnit;
+    List<CompilationUnitElement> parts = libraryElement.parts;
+    List<TimestampedData<CompilationUnit>> units =
+        new List<TimestampedData>(parts.length + 1);
+    units[0] = _getResolvedUnit(definingUnit, librarySource);
+    if (units[0] == null) {
+      // TODO(brianwilkerson) We should return a ResolveDartUnitTask
+      // (unless there are multiple ASTs that need to be resolved).
+      return _createResolveDartLibraryTask(librarySource, libraryEntry);
+    }
+    for (int i = 0; i < parts.length; i++) {
+      units[i + 1] = _getResolvedUnit(parts[i], librarySource);
+      if (units[i + 1] == null) {
+        // TODO(brianwilkerson) We should return a ResolveDartUnitTask
+        // (unless there are multiple ASTs that need to be resolved).
+        return _createResolveDartLibraryTask(librarySource, libraryEntry);
+      }
+    }
+    return new AnalysisContextImpl_TaskData(
+        new GenerateDartHintsTask(this, units, libraryElement), false);
+  }
+
+  /**
+   * Create a [GenerateDartLintsTask] for the given [source], marking the lints
+   * as being in-process.
+   */
+  AnalysisContextImpl_TaskData _createGenerateDartLintsTask(Source source,
+      DartEntry dartEntry, Source librarySource, DartEntry libraryEntry) {
+    if (libraryEntry.getState(DartEntry.ELEMENT) != CacheState.VALID) {
+      return _createResolveDartLibraryTask(librarySource, libraryEntry);
+    }
+    LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT);
+    CompilationUnitElement definingUnit =
+        libraryElement.definingCompilationUnit;
+    List<CompilationUnitElement> parts = libraryElement.parts;
+    List<TimestampedData<CompilationUnit>> units =
+        new List<TimestampedData>(parts.length + 1);
+    units[0] = _getResolvedUnit(definingUnit, librarySource);
+    if (units[0] == null) {
+      // TODO(brianwilkerson) We should return a ResolveDartUnitTask
+      // (unless there are multiple ASTs that need to be resolved).
+      return _createResolveDartLibraryTask(librarySource, libraryEntry);
+    }
+    for (int i = 0; i < parts.length; i++) {
+      units[i + 1] = _getResolvedUnit(parts[i], librarySource);
+      if (units[i + 1] == null) {
+        // TODO(brianwilkerson) We should return a ResolveDartUnitTask
+        // (unless there are multiple ASTs that need to be resolved).
+        return _createResolveDartLibraryTask(librarySource, libraryEntry);
+      }
+    }
+    //TODO(pquitslund): revisit if we need all units or whether one will do
+    return new AnalysisContextImpl_TaskData(
+        new GenerateDartLintsTask(this, units, libraryElement), false);
+  }
+
+  /**
+   * Create a [GetContentTask] for the given [source], marking the content as
+   * being in-process.
+   */
+  AnalysisContextImpl_TaskData _createGetContentTask(
+      Source source, SourceEntry sourceEntry) {
+    return new AnalysisContextImpl_TaskData(
+        new GetContentTask(this, source), false);
+  }
+
+  /**
+   * Create a [ParseDartTask] for the given [source].
+   */
+  AnalysisContextImpl_TaskData _createParseDartTask(
+      Source source, DartEntry dartEntry) {
+    if (dartEntry.getState(DartEntry.TOKEN_STREAM) != CacheState.VALID ||
+        dartEntry.getState(SourceEntry.LINE_INFO) != CacheState.VALID) {
+      return _createScanDartTask(source, dartEntry);
+    }
+    Token tokenStream = dartEntry.getValue(DartEntry.TOKEN_STREAM);
+    dartEntry.setState(DartEntry.TOKEN_STREAM, CacheState.FLUSHED);
+    return new AnalysisContextImpl_TaskData(new ParseDartTask(this, source,
+        tokenStream, dartEntry.getValue(SourceEntry.LINE_INFO)), false);
+  }
+
+  /**
+   * Create a [ParseHtmlTask] for the given [source].
+   */
+  AnalysisContextImpl_TaskData _createParseHtmlTask(
+      Source source, HtmlEntry htmlEntry) {
+    if (htmlEntry.getState(SourceEntry.CONTENT) != CacheState.VALID) {
+      return _createGetContentTask(source, htmlEntry);
+    }
+    String content = htmlEntry.getValue(SourceEntry.CONTENT);
+    htmlEntry.setState(SourceEntry.CONTENT, CacheState.FLUSHED);
+    return new AnalysisContextImpl_TaskData(
+        new ParseHtmlTask(this, source, content), false);
+  }
+
+  /**
+   * Create a [ResolveDartLibraryTask] for the given [source], marking ? as
+   * being in-process.
+   */
+  AnalysisContextImpl_TaskData _createResolveDartLibraryTask(
+      Source source, DartEntry dartEntry) {
+    try {
+      AnalysisContextImpl_CycleBuilder builder =
+          new AnalysisContextImpl_CycleBuilder(this);
+      PerformanceStatistics.cycles.makeCurrentWhile(() {
+        builder.computeCycleContaining(source);
+      });
+      AnalysisContextImpl_TaskData taskData = builder.taskData;
+      if (taskData != null) {
+        return taskData;
+      }
+      return new AnalysisContextImpl_TaskData(new ResolveDartLibraryCycleTask(
+          this, source, source, builder.librariesInCycle), false);
+    } on AnalysisException catch (exception, stackTrace) {
+      dartEntry
+          .recordResolutionError(new CaughtException(exception, stackTrace));
+      AnalysisEngine.instance.logger.logError(
+          "Internal error trying to create a ResolveDartLibraryTask",
+          new CaughtException(exception, stackTrace));
+    }
+    return new AnalysisContextImpl_TaskData(null, false);
+  }
+
+  /**
+   * Create a [ResolveHtmlTask] for the given [source], marking the resolved
+   * unit as being in-process.
+   */
+  AnalysisContextImpl_TaskData _createResolveHtmlTask(
+      Source source, HtmlEntry htmlEntry) {
+    if (htmlEntry.getState(HtmlEntry.PARSED_UNIT) != CacheState.VALID) {
+      return _createParseHtmlTask(source, htmlEntry);
+    }
+    return new AnalysisContextImpl_TaskData(new ResolveHtmlTask(this, source,
+        htmlEntry.modificationTime,
+        htmlEntry.getValue(HtmlEntry.PARSED_UNIT)), false);
+  }
+
+  /**
+   * Create a [ScanDartTask] for the given [source], marking the scan errors as
+   * being in-process.
+   */
+  AnalysisContextImpl_TaskData _createScanDartTask(
+      Source source, DartEntry dartEntry) {
+    if (dartEntry.getState(SourceEntry.CONTENT) != CacheState.VALID) {
+      return _createGetContentTask(source, dartEntry);
+    }
+    String content = dartEntry.getValue(SourceEntry.CONTENT);
+    dartEntry.setState(SourceEntry.CONTENT, CacheState.FLUSHED);
+    return new AnalysisContextImpl_TaskData(
+        new ScanDartTask(this, source, content), false);
+  }
+
+  /**
+   * Create a source entry for the given [source]. Return the source entry that
+   * was created, or `null` if the source should not be tracked by this context.
+   */
+  SourceEntry _createSourceEntry(Source source, bool explicitlyAdded) {
+    String name = source.shortName;
+    if (AnalysisEngine.isHtmlFileName(name)) {
+      HtmlEntry htmlEntry = new HtmlEntry();
+      htmlEntry.modificationTime = getModificationStamp(source);
+      htmlEntry.explicitlyAdded = explicitlyAdded;
+      _cache.put(source, htmlEntry);
+      return htmlEntry;
+    } else {
+      DartEntry dartEntry = new DartEntry();
+      dartEntry.modificationTime = getModificationStamp(source);
+      dartEntry.explicitlyAdded = explicitlyAdded;
+      _cache.put(source, dartEntry);
+      return dartEntry;
+    }
+  }
+
+  /**
+   * Return a list containing all of the change notices that are waiting to be
+   * returned. If there are no notices, then return either `null` or an empty
+   * list, depending on the value of [nullIfEmpty].
+   */
+  List<ChangeNotice> _getChangeNotices(bool nullIfEmpty) {
+    if (_pendingNotices.isEmpty) {
+      if (nullIfEmpty) {
+        return null;
+      }
+      return ChangeNoticeImpl.EMPTY_ARRAY;
+    }
+    List<ChangeNotice> notices = new List.from(_pendingNotices.values);
+    _pendingNotices.clear();
+    return notices;
+  }
+
+  /**
+   * Given a source for a Dart file and the library that contains it, return the
+   * data represented by the given descriptor that is associated with that
+   * source. This method assumes that the data can be produced by generating
+   * hints for the library if it is not already cached.
+   *
+   * Throws an [AnalysisException] if data could not be returned because the
+   * source could not be resolved.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  Object _getDartHintData(Source unitSource, Source librarySource,
+      DartEntry dartEntry, DataDescriptor descriptor) {
+    dartEntry =
+        _cacheDartHintData(unitSource, librarySource, dartEntry, descriptor);
+    if (identical(descriptor, DartEntry.ELEMENT)) {
+      return dartEntry.getValue(descriptor);
+    }
+    return dartEntry.getValueInLibrary(descriptor, librarySource);
+  }
+
+  /**
+   * Given a source for a Dart file and the library that contains it, return the
+   * data represented by the given descriptor that is associated with that
+   * source. This method assumes that the data can be produced by generating
+   * lints for the library if it is not already cached.
+   *
+   * Throws an [AnalysisException] if data could not be returned because the
+   * source could not be resolved.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  Object _getDartLintData(Source unitSource, Source librarySource,
+      DartEntry dartEntry, DataDescriptor descriptor) {
+    dartEntry =
+        _cacheDartLintData(unitSource, librarySource, dartEntry, descriptor);
+    if (identical(descriptor, DartEntry.ELEMENT)) {
+      return dartEntry.getValue(descriptor);
+    }
+    return dartEntry.getValueInLibrary(descriptor, librarySource);
+  }
+
+  /**
+   * Given a source for a Dart file, return the data represented by the given
+   * descriptor that is associated with that source. This method assumes that
+   * the data can be produced by parsing the source if it is not already cached.
+   *
+   * Throws an [AnalysisException] if data could not be returned because the
+   * source could not be parsed.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  Object _getDartParseData(
+      Source source, DartEntry dartEntry, DataDescriptor descriptor) {
+    dartEntry = _cacheDartParseData(source, dartEntry, descriptor);
+    if (identical(descriptor, DartEntry.PARSED_UNIT)) {
+      _accessedAst(source);
+      return dartEntry.anyParsedCompilationUnit;
+    }
+    return dartEntry.getValue(descriptor);
+  }
+
+  /**
+   * Given a source for a Dart file, return the data represented by the given
+   * descriptor that is associated with that source, or the given default value
+   * if the source is not a Dart file. This method assumes that the data can be
+   * produced by parsing the source if it is not already cached.
+   *
+   * Throws an [AnalysisException] if data could not be returned because the
+   * source could not be parsed.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  Object _getDartParseData2(
+      Source source, DataDescriptor descriptor, Object defaultValue) {
+    DartEntry dartEntry = _getReadableDartEntry(source);
+    if (dartEntry == null) {
+      return defaultValue;
+    }
+    try {
+      return _getDartParseData(source, dartEntry, descriptor);
+    } on ObsoleteSourceAnalysisException catch (exception, stackTrace) {
+      AnalysisEngine.instance.logger.logInformation(
+          "Could not compute $descriptor",
+          new CaughtException(exception, stackTrace));
+      return defaultValue;
+    }
+  }
+
+  /**
+   * Given a source for a Dart file and the library that contains it, return the
+   * data represented by the given descriptor that is associated with that
+   * source. This method assumes that the data can be produced by resolving the
+   * source in the context of the library if it is not already cached.
+   *
+   * Throws an [AnalysisException] if data could not be returned because the
+   * source could not be resolved.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  Object _getDartResolutionData(Source unitSource, Source librarySource,
+      DartEntry dartEntry, DataDescriptor descriptor) {
+    dartEntry = _cacheDartResolutionData(
+        unitSource, librarySource, dartEntry, descriptor);
+    if (identical(descriptor, DartEntry.ELEMENT)) {
+      return dartEntry.getValue(descriptor);
+    } else if (identical(descriptor, DartEntry.RESOLVED_UNIT)) {
+      _accessedAst(unitSource);
+    }
+    return dartEntry.getValueInLibrary(descriptor, librarySource);
+  }
+
+  /**
+   * Given a source for a Dart file and the library that contains it, return the
+   * data represented by the given descriptor that is associated with that
+   * source, or the given default value if the source is not a Dart file. This
+   * method assumes that the data can be produced by resolving the source in the
+   * context of the library if it is not already cached.
+   *
+   * Throws an [AnalysisException] if data could not be returned because the
+   * source could not be resolved.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  Object _getDartResolutionData2(Source unitSource, Source librarySource,
+      DataDescriptor descriptor, Object defaultValue) {
+    DartEntry dartEntry = _getReadableDartEntry(unitSource);
+    if (dartEntry == null) {
+      return defaultValue;
+    }
+    try {
+      return _getDartResolutionData(
+          unitSource, librarySource, dartEntry, descriptor);
+    } on ObsoleteSourceAnalysisException catch (exception, stackTrace) {
+      AnalysisEngine.instance.logger.logInformation(
+          "Could not compute $descriptor",
+          new CaughtException(exception, stackTrace));
+      return defaultValue;
+    }
+  }
+
+  /**
+   * Given a source for a Dart file, return the data represented by the given
+   * descriptor that is associated with that source. This method assumes that
+   * the data can be produced by scanning the source if it is not already
+   * cached.
+   *
+   * Throws an [AnalysisException] if data could not be returned because the
+   * source could not be scanned.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  Object _getDartScanData(
+      Source source, DartEntry dartEntry, DataDescriptor descriptor) {
+    dartEntry = _cacheDartScanData(source, dartEntry, descriptor);
+    return dartEntry.getValue(descriptor);
+  }
+
+  /**
+   * Given a source for a Dart file, return the data represented by the given
+   * descriptor that is associated with that source, or the given default value
+   * if the source is not a Dart file. This method assumes that the data can be
+   * produced by scanning the source if it is not already cached.
+   *
+   * Throws an [AnalysisException] if data could not be returned because the
+   * source could not be scanned.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  Object _getDartScanData2(
+      Source source, DataDescriptor descriptor, Object defaultValue) {
+    DartEntry dartEntry = _getReadableDartEntry(source);
+    if (dartEntry == null) {
+      return defaultValue;
+    }
+    try {
+      return _getDartScanData(source, dartEntry, descriptor);
+    } on ObsoleteSourceAnalysisException catch (exception, stackTrace) {
+      AnalysisEngine.instance.logger.logInformation(
+          "Could not compute $descriptor",
+          new CaughtException(exception, stackTrace));
+      return defaultValue;
+    }
+  }
+
+  /**
+   * Given a source for a Dart file and the library that contains it, return the
+   * data represented by the given descriptor that is associated with that
+   * source. This method assumes that the data can be produced by verifying the
+   * source within the given library if it is not already cached.
+   *
+   * Throws an [AnalysisException] if data could not be returned because the
+   * source could not be resolved.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  Object _getDartVerificationData(Source unitSource, Source librarySource,
+      DartEntry dartEntry, DataDescriptor descriptor) {
+    dartEntry = _cacheDartVerificationData(
+        unitSource, librarySource, dartEntry, descriptor);
+    return dartEntry.getValueInLibrary(descriptor, librarySource);
+  }
+
+  /**
+   * Given a source for an HTML file, return the data represented by the given
+   * descriptor that is associated with that source, or the given default value
+   * if the source is not an HTML file. This method assumes that the data can be
+   * produced by parsing the source if it is not already cached.
+   *
+   * Throws an [AnalysisException] if data could not be returned because the
+   * source could not be parsed.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  Object _getHtmlParseData(
+      Source source, DataDescriptor descriptor, Object defaultValue) {
+    HtmlEntry htmlEntry = _getReadableHtmlEntry(source);
+    if (htmlEntry == null) {
+      return defaultValue;
+    }
+    htmlEntry = _cacheHtmlParseData(source, htmlEntry, descriptor);
+    if (identical(descriptor, HtmlEntry.PARSED_UNIT)) {
+      _accessedAst(source);
+      return htmlEntry.anyParsedUnit;
+    }
+    return htmlEntry.getValue(descriptor);
+  }
+
+  /**
+   * Given a source for an HTML file, return the data represented by the given
+   * descriptor that is associated with that source, or the given default value
+   * if the source is not an HTML file. This method assumes that the data can be
+   * produced by resolving the source if it is not already cached.
+   *
+   * Throws an [AnalysisException] if data could not be returned because the
+   * source could not be resolved.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  Object _getHtmlResolutionData(
+      Source source, DataDescriptor descriptor, Object defaultValue) {
+    HtmlEntry htmlEntry = _getReadableHtmlEntry(source);
+    if (htmlEntry == null) {
+      return defaultValue;
+    }
+    try {
+      return _getHtmlResolutionData2(source, htmlEntry, descriptor);
+    } on ObsoleteSourceAnalysisException catch (exception, stackTrace) {
+      AnalysisEngine.instance.logger.logInformation(
+          "Could not compute $descriptor",
+          new CaughtException(exception, stackTrace));
+      return defaultValue;
+    }
+  }
+
+  /**
+   * Given a source for an HTML file, return the data represented by the given
+   * descriptor that is associated with that source. This method assumes that
+   * the data can be produced by resolving the source if it is not already
+   * cached.
+   *
+   * Throws an [AnalysisException] if data could not be returned because the
+   * source could not be resolved.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment.
+   */
+  Object _getHtmlResolutionData2(
+      Source source, HtmlEntry htmlEntry, DataDescriptor descriptor) {
+    htmlEntry = _cacheHtmlResolutionData(source, htmlEntry, descriptor);
+    if (identical(descriptor, HtmlEntry.RESOLVED_UNIT)) {
+      _accessedAst(source);
+    }
+    return htmlEntry.getValue(descriptor);
+  }
+
+  /**
+   * Look at the given [source] to see whether a task needs to be performed
+   * related to it. Return the task that should be performed, or `null` if there
+   * is no more work to be done for the source.
+   */
+  AnalysisContextImpl_TaskData _getNextAnalysisTaskForSource(Source source,
+      SourceEntry sourceEntry, bool isPriority, bool hintsEnabled,
+      bool lintsEnabled) {
+    // Refuse to generate tasks for html based files that are above 1500 KB
+    if (_isTooBigHtmlSourceEntry(source, sourceEntry)) {
+      // TODO (jwren) we still need to report an error of some kind back to the
+      // client.
+      return new AnalysisContextImpl_TaskData(null, false);
+    }
+    if (sourceEntry == null) {
+      return new AnalysisContextImpl_TaskData(null, false);
+    }
+    CacheState contentState = sourceEntry.getState(SourceEntry.CONTENT);
+    if (contentState == CacheState.INVALID) {
+      return _createGetContentTask(source, sourceEntry);
+    } else if (contentState == CacheState.IN_PROCESS) {
+      // We are already in the process of getting the content.
+      // There's nothing else we can do with this source until that's complete.
+      return new AnalysisContextImpl_TaskData(null, true);
+    } else if (contentState == CacheState.ERROR) {
+      // We have done all of the analysis we can for this source because we
+      // cannot get its content.
+      return new AnalysisContextImpl_TaskData(null, false);
+    }
+    if (sourceEntry is DartEntry) {
+      DartEntry dartEntry = sourceEntry;
+      CacheState scanErrorsState = dartEntry.getState(DartEntry.SCAN_ERRORS);
+      if (scanErrorsState == CacheState.INVALID ||
+          (isPriority && scanErrorsState == CacheState.FLUSHED)) {
+        return _createScanDartTask(source, dartEntry);
+      }
+      CacheState parseErrorsState = dartEntry.getState(DartEntry.PARSE_ERRORS);
+      if (parseErrorsState == CacheState.INVALID ||
+          (isPriority && parseErrorsState == CacheState.FLUSHED)) {
+        return _createParseDartTask(source, dartEntry);
+      }
+      if (isPriority && parseErrorsState != CacheState.ERROR) {
+        if (!dartEntry.hasResolvableCompilationUnit) {
+          return _createParseDartTask(source, dartEntry);
+        }
+      }
+      SourceKind kind = dartEntry.getValue(DartEntry.SOURCE_KIND);
+      if (kind == SourceKind.UNKNOWN) {
+        return _createParseDartTask(source, dartEntry);
+      } else if (kind == SourceKind.LIBRARY) {
+        CacheState elementState = dartEntry.getState(DartEntry.ELEMENT);
+        if (elementState == CacheState.INVALID) {
+          return _createResolveDartLibraryTask(source, dartEntry);
+        }
+      }
+      List<Source> librariesContaining = dartEntry.containingLibraries;
+      for (Source librarySource in librariesContaining) {
+        SourceEntry librarySourceEntry = _cache.get(librarySource);
+        if (librarySourceEntry is DartEntry) {
+          DartEntry libraryEntry = librarySourceEntry;
+          CacheState elementState = libraryEntry.getState(DartEntry.ELEMENT);
+          if (elementState == CacheState.INVALID ||
+              (isPriority && elementState == CacheState.FLUSHED)) {
+//            return createResolveDartLibraryTask(librarySource, (DartEntry) libraryEntry);
+            return new AnalysisContextImpl_TaskData(
+                new ResolveDartLibraryTask(this, source, librarySource), false);
+          }
+          CacheState resolvedUnitState = dartEntry.getStateInLibrary(
+              DartEntry.RESOLVED_UNIT, librarySource);
+          if (resolvedUnitState == CacheState.INVALID ||
+              (isPriority && resolvedUnitState == CacheState.FLUSHED)) {
+            //
+            // The commented out lines below are an optimization that doesn't
+            // quite work yet. The problem is that if the source was not
+            // resolved because it wasn't part of any library, then there won't
+            // be any elements in the element model that we can use to resolve
+            // it.
+            //
+//            LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT);
+//            if (libraryElement != null) {
+//              return new ResolveDartUnitTask(this, source, libraryElement);
+//            }
+            // Possibly replace with:
+//             return createResolveDartLibraryTask(librarySource, (DartEntry) libraryEntry);
+            return new AnalysisContextImpl_TaskData(
+                new ResolveDartLibraryTask(this, source, librarySource), false);
+          }
+          if (_shouldErrorsBeAnalyzed(source, dartEntry)) {
+            CacheState verificationErrorsState = dartEntry.getStateInLibrary(
+                DartEntry.VERIFICATION_ERRORS, librarySource);
+            if (verificationErrorsState == CacheState.INVALID ||
+                (isPriority && verificationErrorsState == CacheState.FLUSHED)) {
+              return _createGenerateDartErrorsTask(
+                  source, dartEntry, librarySource, libraryEntry);
+            }
+            if (hintsEnabled) {
+              CacheState hintsState =
+                  dartEntry.getStateInLibrary(DartEntry.HINTS, librarySource);
+              if (hintsState == CacheState.INVALID ||
+                  (isPriority && hintsState == CacheState.FLUSHED)) {
+                return _createGenerateDartHintsTask(
+                    source, dartEntry, librarySource, libraryEntry);
+              }
+            }
+            if (lintsEnabled) {
+              CacheState lintsState =
+                  dartEntry.getStateInLibrary(DartEntry.LINTS, librarySource);
+              if (lintsState == CacheState.INVALID ||
+                  (isPriority && lintsState == CacheState.FLUSHED)) {
+                return _createGenerateDartLintsTask(
+                    source, dartEntry, librarySource, libraryEntry);
+              }
+            }
+          }
+        }
+      }
+    } else if (sourceEntry is HtmlEntry) {
+      HtmlEntry htmlEntry = sourceEntry;
+      CacheState parseErrorsState = htmlEntry.getState(HtmlEntry.PARSE_ERRORS);
+      if (parseErrorsState == CacheState.INVALID ||
+          (isPriority && parseErrorsState == CacheState.FLUSHED)) {
+        return _createParseHtmlTask(source, htmlEntry);
+      }
+      if (isPriority && parseErrorsState != CacheState.ERROR) {
+        ht.HtmlUnit parsedUnit = htmlEntry.anyParsedUnit;
+        if (parsedUnit == null) {
+          return _createParseHtmlTask(source, htmlEntry);
+        }
+      }
+      CacheState resolvedUnitState =
+          htmlEntry.getState(HtmlEntry.RESOLVED_UNIT);
+      if (resolvedUnitState == CacheState.INVALID ||
+          (isPriority && resolvedUnitState == CacheState.FLUSHED)) {
+        return _createResolveHtmlTask(source, htmlEntry);
+      }
+    }
+    return new AnalysisContextImpl_TaskData(null, false);
+  }
+
+  /**
+   * Return a change notice for the given [source], creating one if one does not
+   * already exist.
+   */
+  ChangeNoticeImpl _getNotice(Source source) {
+    ChangeNoticeImpl notice = _pendingNotices[source];
+    if (notice == null) {
+      notice = new ChangeNoticeImpl(source);
+      _pendingNotices[source] = notice;
+    }
+    return notice;
+  }
+
+  /**
+   * Return the cache entry associated with the given [source], or `null` if the
+   * source is not a Dart file.
+   *
+   * @param source the source for which a cache entry is being sought
+   * @return the source cache entry associated with the given source
+   */
+  DartEntry _getReadableDartEntry(Source source) {
+    SourceEntry sourceEntry = _cache.get(source);
+    if (sourceEntry == null) {
+      sourceEntry = _createSourceEntry(source, false);
+    }
+    if (sourceEntry is DartEntry) {
+      return sourceEntry;
+    }
+    return null;
+  }
+
+  /**
+   * Return the cache entry associated with the given [source], or `null` if the
+   * source is not an HTML file.
+   */
+  HtmlEntry _getReadableHtmlEntry(Source source) {
+    SourceEntry sourceEntry = _cache.get(source);
+    if (sourceEntry == null) {
+      sourceEntry = _createSourceEntry(source, false);
+    }
+    if (sourceEntry is HtmlEntry) {
+      return sourceEntry;
+    }
+    return null;
+  }
+
+  /**
+   * Return the cache entry associated with the given [source], creating it if
+   * necessary.
+   */
+  SourceEntry _getReadableSourceEntry(Source source) {
+    SourceEntry sourceEntry = _cache.get(source);
+    if (sourceEntry == null) {
+      sourceEntry = _createSourceEntry(source, false);
+    }
+    return sourceEntry;
+  }
+
+  /**
+   * Return a resolved compilation unit corresponding to the given [element] in
+   * the library defined by the given [librarySource], or `null` if the
+   * information is not cached.
+   */
+  TimestampedData<CompilationUnit> _getResolvedUnit(
+      CompilationUnitElement element, Source librarySource) {
+    SourceEntry sourceEntry = _cache.get(element.source);
+    if (sourceEntry is DartEntry) {
+      DartEntry dartEntry = sourceEntry;
+      if (dartEntry.getStateInLibrary(DartEntry.RESOLVED_UNIT, librarySource) ==
+          CacheState.VALID) {
+        return new TimestampedData<CompilationUnit>(dartEntry.modificationTime,
+            dartEntry.getValueInLibrary(
+                DartEntry.RESOLVED_UNIT, librarySource));
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return a list containing all of the sources known to this context that have
+   * the given [kind].
+   */
+  List<Source> _getSources(SourceKind kind) {
+    List<Source> sources = new List<Source>();
+    MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      if (iterator.value.kind == kind) {
+        sources.add(iterator.key);
+      }
+    }
+    return sources;
+  }
+
+  /**
+   * Look at the given [source] to see whether a task needs to be performed
+   * related to it. If so, add the source to the set of sources that need to be
+   * processed. This method duplicates, and must therefore be kept in sync with,
+   * [_getNextAnalysisTaskForSource]. This method is intended to be used for
+   * testing purposes only.
+   */
+  void _getSourcesNeedingProcessing(Source source, SourceEntry sourceEntry,
+      bool isPriority, bool hintsEnabled, bool lintsEnabled,
+      HashSet<Source> sources) {
+    if (sourceEntry is DartEntry) {
+      DartEntry dartEntry = sourceEntry;
+      CacheState scanErrorsState = dartEntry.getState(DartEntry.SCAN_ERRORS);
+      if (scanErrorsState == CacheState.INVALID ||
+          (isPriority && scanErrorsState == CacheState.FLUSHED)) {
+        sources.add(source);
+        return;
+      }
+      CacheState parseErrorsState = dartEntry.getState(DartEntry.PARSE_ERRORS);
+      if (parseErrorsState == CacheState.INVALID ||
+          (isPriority && parseErrorsState == CacheState.FLUSHED)) {
+        sources.add(source);
+        return;
+      }
+      if (isPriority) {
+        if (!dartEntry.hasResolvableCompilationUnit) {
+          sources.add(source);
+          return;
+        }
+      }
+      for (Source librarySource in getLibrariesContaining(source)) {
+        SourceEntry libraryEntry = _cache.get(librarySource);
+        if (libraryEntry is DartEntry) {
+          CacheState elementState = libraryEntry.getState(DartEntry.ELEMENT);
+          if (elementState == CacheState.INVALID ||
+              (isPriority && elementState == CacheState.FLUSHED)) {
+            sources.add(source);
+            return;
+          }
+          CacheState resolvedUnitState = dartEntry.getStateInLibrary(
+              DartEntry.RESOLVED_UNIT, librarySource);
+          if (resolvedUnitState == CacheState.INVALID ||
+              (isPriority && resolvedUnitState == CacheState.FLUSHED)) {
+            LibraryElement libraryElement =
+                libraryEntry.getValue(DartEntry.ELEMENT);
+            if (libraryElement != null) {
+              sources.add(source);
+              return;
+            }
+          }
+          if (_shouldErrorsBeAnalyzed(source, dartEntry)) {
+            CacheState verificationErrorsState = dartEntry.getStateInLibrary(
+                DartEntry.VERIFICATION_ERRORS, librarySource);
+            if (verificationErrorsState == CacheState.INVALID ||
+                (isPriority && verificationErrorsState == CacheState.FLUSHED)) {
+              LibraryElement libraryElement =
+                  libraryEntry.getValue(DartEntry.ELEMENT);
+              if (libraryElement != null) {
+                sources.add(source);
+                return;
+              }
+            }
+            if (hintsEnabled) {
+              CacheState hintsState =
+                  dartEntry.getStateInLibrary(DartEntry.HINTS, librarySource);
+              if (hintsState == CacheState.INVALID ||
+                  (isPriority && hintsState == CacheState.FLUSHED)) {
+                LibraryElement libraryElement =
+                    libraryEntry.getValue(DartEntry.ELEMENT);
+                if (libraryElement != null) {
+                  sources.add(source);
+                  return;
+                }
+              }
+            }
+            if (lintsEnabled) {
+              CacheState lintsState =
+                  dartEntry.getStateInLibrary(DartEntry.LINTS, librarySource);
+              if (lintsState == CacheState.INVALID ||
+                  (isPriority && lintsState == CacheState.FLUSHED)) {
+                LibraryElement libraryElement =
+                    libraryEntry.getValue(DartEntry.ELEMENT);
+                if (libraryElement != null) {
+                  sources.add(source);
+                  return;
+                }
+              }
+            }
+          }
+        }
+      }
+    } else if (sourceEntry is HtmlEntry) {
+      HtmlEntry htmlEntry = sourceEntry;
+      CacheState parsedUnitState = htmlEntry.getState(HtmlEntry.PARSED_UNIT);
+      if (parsedUnitState == CacheState.INVALID ||
+          (isPriority && parsedUnitState == CacheState.FLUSHED)) {
+        sources.add(source);
+        return;
+      }
+      CacheState resolvedUnitState =
+          htmlEntry.getState(HtmlEntry.RESOLVED_UNIT);
+      if (resolvedUnitState == CacheState.INVALID ||
+          (isPriority && resolvedUnitState == CacheState.FLUSHED)) {
+        sources.add(source);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Invalidate all of the resolution results computed by this context. The flag
+   * [invalidateUris] should be `true` if the cached results of converting URIs
+   * to source files should also be invalidated.
+   */
+  void _invalidateAllLocalResolutionInformation(bool invalidateUris) {
+    HashMap<Source, List<Source>> oldPartMap =
+        new HashMap<Source, List<Source>>();
+    MapIterator<Source, SourceEntry> iterator = _privatePartition.iterator();
+    while (iterator.moveNext()) {
+      Source source = iterator.key;
+      SourceEntry sourceEntry = iterator.value;
+      if (sourceEntry is HtmlEntry) {
+        HtmlEntry htmlEntry = sourceEntry;
+        htmlEntry.invalidateAllResolutionInformation(invalidateUris);
+        iterator.value = htmlEntry;
+        _workManager.add(source, SourcePriority.HTML);
+      } else if (sourceEntry is DartEntry) {
+        DartEntry dartEntry = sourceEntry;
+        oldPartMap[source] = dartEntry.getValue(DartEntry.INCLUDED_PARTS);
+        dartEntry.invalidateAllResolutionInformation(invalidateUris);
+        iterator.value = dartEntry;
+        _workManager.add(source, _computePriority(dartEntry));
+      }
+    }
+    _removeFromPartsUsingMap(oldPartMap);
+  }
+
+  /**
+   * In response to a change to at least one of the compilation units in the
+   * library defined by the given [librarySource], invalidate any results that
+   * are dependent on the result of resolving that library.
+   *
+   * <b>Note:</b> Any cache entries that were accessed before this method was
+   * invoked must be re-accessed after this method returns.
+   */
+  void _invalidateLibraryResolution(Source librarySource) {
+    // TODO(brianwilkerson) This could be optimized. There's no need to flush
+    // all of these entries if the public namespace hasn't changed, which will
+    // be a fairly common case. The question is whether we can afford the time
+    // to compute the namespace to look for differences.
+    DartEntry libraryEntry = _getReadableDartEntry(librarySource);
+    if (libraryEntry != null) {
+      List<Source> includedParts =
+          libraryEntry.getValue(DartEntry.INCLUDED_PARTS);
+      libraryEntry.invalidateAllResolutionInformation(false);
+      _workManager.add(librarySource, SourcePriority.LIBRARY);
+      for (Source partSource in includedParts) {
+        SourceEntry partEntry = _cache.get(partSource);
+        if (partEntry is DartEntry) {
+          partEntry.invalidateAllResolutionInformation(false);
+        }
+      }
+    }
+  }
+
+  /**
+   * Return `true` if the given [library] is, or depends on, 'dart:html'. The
+   * [visitedLibraries] is a collection of the libraries that have been visited,
+   * used to prevent infinite recursion.
+   */
+  bool _isClient(LibraryElement library, Source htmlSource,
+      HashSet<LibraryElement> visitedLibraries) {
+    if (visitedLibraries.contains(library)) {
+      return false;
+    }
+    if (library.source == htmlSource) {
+      return true;
+    }
+    visitedLibraries.add(library);
+    for (LibraryElement imported in library.importedLibraries) {
+      if (_isClient(imported, htmlSource, visitedLibraries)) {
+        return true;
+      }
+    }
+    for (LibraryElement exported in library.exportedLibraries) {
+      if (_isClient(exported, htmlSource, visitedLibraries)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool _isTooBigHtmlSourceEntry(Source source, SourceEntry sourceEntry) =>
+      false;
+
+  /**
+   * Log the given debugging [message].
+   */
+  void _logInformation(String message) {
+    AnalysisEngine.instance.logger.logInformation(message);
+  }
+
+  /**
+   * Notify all of the analysis listeners that a task is about to be performed.
+   */
+  void _notifyAboutToPerformTask(String taskDescription) {
+    int count = _listeners.length;
+    for (int i = 0; i < count; i++) {
+      _listeners[i].aboutToPerformTask(this, taskDescription);
+    }
+  }
+
+  /**
+   * Notify all of the analysis listeners that the errors associated with the
+   * given [source] has been updated to the given [errors].
+   */
+  void _notifyErrors(
+      Source source, List<AnalysisError> errors, LineInfo lineInfo) {
+    int count = _listeners.length;
+    for (int i = 0; i < count; i++) {
+      _listeners[i].computedErrors(this, source, errors, lineInfo);
+    }
+  }
+
+  /**
+   * Given that the given [source] (with the corresponding [sourceEntry]) has
+   * been invalidated, invalidate all of the libraries that depend on it.
+   */
+  void _propagateInvalidation(Source source, SourceEntry sourceEntry) {
+    if (sourceEntry is HtmlEntry) {
+      HtmlEntry htmlEntry = sourceEntry;
+      htmlEntry.modificationTime = getModificationStamp(source);
+      htmlEntry.invalidateAllInformation();
+      _cache.removedAst(source);
+      _workManager.add(source, SourcePriority.HTML);
+    } else if (sourceEntry is DartEntry) {
+      List<Source> containingLibraries = getLibrariesContaining(source);
+      List<Source> dependentLibraries = getLibrariesDependingOn(source);
+      HashSet<Source> librariesToInvalidate = new HashSet<Source>();
+      for (Source containingLibrary in containingLibraries) {
+        _computeAllLibrariesDependingOn(
+            containingLibrary, librariesToInvalidate);
+      }
+      for (Source dependentLibrary in dependentLibraries) {
+        _computeAllLibrariesDependingOn(
+            dependentLibrary, librariesToInvalidate);
+      }
+      for (Source library in librariesToInvalidate) {
+        _invalidateLibraryResolution(library);
+      }
+      DartEntry dartEntry = _cache.get(source);
+      _removeFromParts(source, dartEntry);
+      dartEntry.modificationTime = getModificationStamp(source);
+      dartEntry.invalidateAllInformation();
+      _cache.removedAst(source);
+      _workManager.add(source, SourcePriority.UNKNOWN);
+    }
+    // reset unit in the notification, it is out of date now
+    ChangeNoticeImpl notice = _pendingNotices[source];
+    if (notice != null) {
+      notice.resolvedDartUnit = null;
+      notice.resolvedHtmlUnit = null;
+    }
+  }
+
+  /**
+   * Record the results produced by performing a [task] and return the cache
+   * entry associated with the results.
+   */
+  DartEntry _recordBuildUnitElementTask(BuildUnitElementTask task) {
+    Source source = task.source;
+    Source library = task.library;
+    DartEntry dartEntry = _cache.get(source);
+    CaughtException thrownException = task.exception;
+    if (thrownException != null) {
+      dartEntry.recordBuildElementErrorInLibrary(library, thrownException);
+      throw new AnalysisException('<rethrow>', thrownException);
+    }
+    dartEntry.setValueInLibrary(DartEntry.BUILT_UNIT, library, task.unit);
+    dartEntry.setValueInLibrary(
+        DartEntry.BUILT_ELEMENT, library, task.unitElement);
+    ChangeNoticeImpl notice = _getNotice(source);
+    LineInfo lineInfo = dartEntry.getValue(SourceEntry.LINE_INFO);
+    notice.setErrors(dartEntry.allErrors, lineInfo);
+    return dartEntry;
+  }
+
+//  /**
+//   * Notify all of the analysis listeners that the given source is no longer included in the set of
+//   * sources that are being analyzed.
+//   *
+//   * @param source the source that is no longer being analyzed
+//   */
+//  void _notifyExcludedSource(Source source) {
+//    int count = _listeners.length;
+//    for (int i = 0; i < count; i++) {
+//      _listeners[i].excludedSource(this, source);
+//    }
+//  }
+
+//  /**
+//   * Notify all of the analysis listeners that the given source is now included in the set of
+//   * sources that are being analyzed.
+//   *
+//   * @param source the source that is now being analyzed
+//   */
+//  void _notifyIncludedSource(Source source) {
+//    int count = _listeners.length;
+//    for (int i = 0; i < count; i++) {
+//      _listeners[i].includedSource(this, source);
+//    }
+//  }
+
+//  /**
+//   * Notify all of the analysis listeners that the given Dart source was parsed.
+//   *
+//   * @param source the source that was parsed
+//   * @param unit the result of parsing the source
+//   */
+//  void _notifyParsedDart(Source source, CompilationUnit unit) {
+//    int count = _listeners.length;
+//    for (int i = 0; i < count; i++) {
+//      _listeners[i].parsedDart(this, source, unit);
+//    }
+//  }
+
+//  /**
+//   * Notify all of the analysis listeners that the given HTML source was parsed.
+//   *
+//   * @param source the source that was parsed
+//   * @param unit the result of parsing the source
+//   */
+//  void _notifyParsedHtml(Source source, ht.HtmlUnit unit) {
+//    int count = _listeners.length;
+//    for (int i = 0; i < count; i++) {
+//      _listeners[i].parsedHtml(this, source, unit);
+//    }
+//  }
+
+//  /**
+//   * Notify all of the analysis listeners that the given Dart source was resolved.
+//   *
+//   * @param source the source that was resolved
+//   * @param unit the result of resolving the source
+//   */
+//  void _notifyResolvedDart(Source source, CompilationUnit unit) {
+//    int count = _listeners.length;
+//    for (int i = 0; i < count; i++) {
+//      _listeners[i].resolvedDart(this, source, unit);
+//    }
+//  }
+
+//  /**
+//   * Notify all of the analysis listeners that the given HTML source was resolved.
+//   *
+//   * @param source the source that was resolved
+//   * @param unit the result of resolving the source
+//   */
+//  void _notifyResolvedHtml(Source source, ht.HtmlUnit unit) {
+//    int count = _listeners.length;
+//    for (int i = 0; i < count; i++) {
+//      _listeners[i].resolvedHtml(this, source, unit);
+//    }
+//  }
+
+  /**
+   * Given a [dartEntry] and a [library] element, record the library element and
+   * other information gleaned from the element in the cache entry.
+   */
+  void _recordElementData(DartEntry dartEntry, LibraryElement library,
+      Source librarySource, Source htmlSource) {
+    dartEntry.setValue(DartEntry.ELEMENT, library);
+    dartEntry.setValue(DartEntry.IS_LAUNCHABLE, library.entryPoint != null);
+    dartEntry.setValue(DartEntry.IS_CLIENT,
+        _isClient(library, htmlSource, new HashSet<LibraryElement>()));
+  }
+
+  /**
+   * Record the results produced by performing a [task] and return the cache
+   * entry associated with the results.
+   */
+  DartEntry _recordGenerateDartErrorsTask(GenerateDartErrorsTask task) {
+    Source source = task.source;
+    DartEntry dartEntry = _cache.get(source);
+    Source librarySource = task.libraryElement.source;
+    CaughtException thrownException = task.exception;
+    if (thrownException != null) {
+      dartEntry.recordVerificationErrorInLibrary(
+          librarySource, thrownException);
+      throw new AnalysisException('<rethrow>', thrownException);
+    }
+    dartEntry.setValueInLibrary(
+        DartEntry.VERIFICATION_ERRORS, librarySource, task.errors);
+    ChangeNoticeImpl notice = _getNotice(source);
+    LineInfo lineInfo = dartEntry.getValue(SourceEntry.LINE_INFO);
+    notice.setErrors(dartEntry.allErrors, lineInfo);
+    return dartEntry;
+  }
+
+  /**
+   * Record the results produced by performing a [task] and return the cache
+   * entry associated with the results.
+   */
+  DartEntry _recordGenerateDartHintsTask(GenerateDartHintsTask task) {
+    Source librarySource = task.libraryElement.source;
+    CaughtException thrownException = task.exception;
+    DartEntry libraryEntry = null;
+    HashMap<Source, List<AnalysisError>> hintMap = task.hintMap;
+    if (hintMap == null) {
+      // We don't have any information about which sources to mark as invalid
+      // other than the library source.
+      DartEntry libraryEntry = _cache.get(librarySource);
+      if (thrownException == null) {
+        String message = "GenerateDartHintsTask returned a null hint map "
+            "without throwing an exception: ${librarySource.fullName}";
+        thrownException =
+            new CaughtException(new AnalysisException(message), null);
+      }
+      libraryEntry.recordHintErrorInLibrary(librarySource, thrownException);
+      throw new AnalysisException('<rethrow>', thrownException);
+    }
+    hintMap.forEach((Source unitSource, List<AnalysisError> hints) {
+      DartEntry dartEntry = _cache.get(unitSource);
+      if (unitSource == librarySource) {
+        libraryEntry = dartEntry;
+      }
+      if (thrownException == null) {
+        dartEntry.setValueInLibrary(DartEntry.HINTS, librarySource, hints);
+        ChangeNoticeImpl notice = _getNotice(unitSource);
+        LineInfo lineInfo = dartEntry.getValue(SourceEntry.LINE_INFO);
+        notice.setErrors(dartEntry.allErrors, lineInfo);
+      } else {
+        dartEntry.recordHintErrorInLibrary(librarySource, thrownException);
+      }
+    });
+    if (thrownException != null) {
+      throw new AnalysisException('<rethrow>', thrownException);
+    }
+    return libraryEntry;
+  }
+
+  /**
+   * Record the results produced by performing a [task] and return the cache
+   * entry associated with the results.
+   */
+  DartEntry _recordGenerateDartLintsTask(GenerateDartLintsTask task) {
+    Source librarySource = task.libraryElement.source;
+    CaughtException thrownException = task.exception;
+    DartEntry libraryEntry = null;
+    HashMap<Source, List<AnalysisError>> lintMap = task.lintMap;
+    if (lintMap == null) {
+      // We don't have any information about which sources to mark as invalid
+      // other than the library source.
+      DartEntry libraryEntry = _cache.get(librarySource);
+      if (thrownException == null) {
+        String message = "GenerateDartLintsTask returned a null lint map "
+            "without throwing an exception: ${librarySource.fullName}";
+        thrownException =
+            new CaughtException(new AnalysisException(message), null);
+      }
+      libraryEntry.recordLintErrorInLibrary(librarySource, thrownException);
+      throw new AnalysisException('<rethrow>', thrownException);
+    }
+    lintMap.forEach((Source unitSource, List<AnalysisError> lints) {
+      DartEntry dartEntry = _cache.get(unitSource);
+      if (unitSource == librarySource) {
+        libraryEntry = dartEntry;
+      }
+      if (thrownException == null) {
+        dartEntry.setValueInLibrary(DartEntry.LINTS, librarySource, lints);
+        ChangeNoticeImpl notice = _getNotice(unitSource);
+        LineInfo lineInfo = dartEntry.getValue(SourceEntry.LINE_INFO);
+        notice.setErrors(dartEntry.allErrors, lineInfo);
+      } else {
+        dartEntry.recordLintErrorInLibrary(librarySource, thrownException);
+      }
+    });
+    if (thrownException != null) {
+      throw new AnalysisException('<rethrow>', thrownException);
+    }
+    return libraryEntry;
+  }
+
+  /**
+   * Record the results produced by performing a [task] and return the cache
+   * entry associated with the results.
+   */
+  SourceEntry _recordGetContentsTask(GetContentTask task) {
+    if (!task.isComplete) {
+      return null;
+    }
+    Source source = task.source;
+    SourceEntry sourceEntry = _cache.get(source);
+    CaughtException thrownException = task.exception;
+    if (thrownException != null) {
+      sourceEntry.recordContentError(thrownException);
+      {
+        sourceEntry.setValue(SourceEntry.CONTENT_ERRORS, task.errors);
+        ChangeNoticeImpl notice = _getNotice(source);
+        notice.setErrors(sourceEntry.allErrors, null);
+      }
+      _workManager.remove(source);
+      throw new AnalysisException('<rethrow>', thrownException);
+    }
+    sourceEntry.modificationTime = task.modificationTime;
+    sourceEntry.setValue(SourceEntry.CONTENT, task.content);
+    return sourceEntry;
+  }
+
+  /**
+   * Record the results produced by performing a [task] and return the cache
+   * entry associated with the results.
+   */
+  DartEntry _recordIncrementalAnalysisTaskResults(
+      IncrementalAnalysisTask task) {
+    CompilationUnit unit = task.compilationUnit;
+    if (unit != null) {
+      ChangeNoticeImpl notice = _getNotice(task.source);
+      notice.resolvedDartUnit = unit;
+      _incrementalAnalysisCache =
+          IncrementalAnalysisCache.cacheResult(task.cache, unit);
+    }
+    return null;
+  }
+
+  /**
+   * Record the results produced by performing a [task] and return the cache
+   * entry associated with the results.
+   */
+  DartEntry _recordParseDartTaskResults(ParseDartTask task) {
+    Source source = task.source;
+    DartEntry dartEntry = _cache.get(source);
+    _removeFromParts(source, dartEntry);
+    CaughtException thrownException = task.exception;
+    if (thrownException != null) {
+      _removeFromParts(source, dartEntry);
+      dartEntry.recordParseError(thrownException);
+      _cache.removedAst(source);
+      throw new AnalysisException('<rethrow>', thrownException);
+    }
+    if (task.hasNonPartOfDirective) {
+      dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.LIBRARY);
+      dartEntry.containingLibrary = source;
+      _workManager.add(source, SourcePriority.LIBRARY);
+    } else if (task.hasPartOfDirective) {
+      dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.PART);
+      dartEntry.removeContainingLibrary(source);
+      _workManager.add(source, SourcePriority.NORMAL_PART);
+    } else {
+      // The file contains no directives.
+      List<Source> containingLibraries = dartEntry.containingLibraries;
+      if (containingLibraries.length > 1 ||
+          (containingLibraries.length == 1 &&
+              containingLibraries[0] != source)) {
+        dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.PART);
+        dartEntry.removeContainingLibrary(source);
+        _workManager.add(source, SourcePriority.NORMAL_PART);
+      } else {
+        dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.LIBRARY);
+        dartEntry.containingLibrary = source;
+        _workManager.add(source, SourcePriority.LIBRARY);
+      }
+    }
+    List<Source> newParts = task.includedSources;
+    for (int i = 0; i < newParts.length; i++) {
+      Source partSource = newParts[i];
+      DartEntry partEntry = _getReadableDartEntry(partSource);
+      if (partEntry != null && !identical(partEntry, dartEntry)) {
+        // TODO(brianwilkerson) Change the kind of the "part" if it was marked
+        // as a library and it has no directives.
+        partEntry.addContainingLibrary(source);
+      }
+    }
+    dartEntry.setValue(DartEntry.PARSED_UNIT, task.compilationUnit);
+    dartEntry.setValue(DartEntry.PARSE_ERRORS, task.errors);
+    dartEntry.setValue(DartEntry.EXPORTED_LIBRARIES, task.exportedSources);
+    dartEntry.setValue(DartEntry.IMPORTED_LIBRARIES, task.importedSources);
+    dartEntry.setValue(DartEntry.INCLUDED_PARTS, newParts);
+    _cache.storedAst(source);
+    ChangeNoticeImpl notice = _getNotice(source);
+    if (notice.resolvedDartUnit == null) {
+      notice.parsedDartUnit = task.compilationUnit;
+    }
+    notice.setErrors(dartEntry.allErrors, task.lineInfo);
+    // Verify that the incrementally parsed and resolved unit in the incremental
+    // cache is structurally equivalent to the fully parsed unit
+    _incrementalAnalysisCache = IncrementalAnalysisCache.verifyStructure(
+        _incrementalAnalysisCache, source, task.compilationUnit);
+    return dartEntry;
+  }
+
+  /**
+   * Record the results produced by performing a [task] and return the cache
+   * entry associated with the results.
+   */
+  HtmlEntry _recordParseHtmlTaskResults(ParseHtmlTask task) {
+    Source source = task.source;
+    HtmlEntry htmlEntry = _cache.get(source);
+    CaughtException thrownException = task.exception;
+    if (thrownException != null) {
+      htmlEntry.recordParseError(thrownException);
+      _cache.removedAst(source);
+      throw new AnalysisException('<rethrow>', thrownException);
+    }
+    LineInfo lineInfo = task.lineInfo;
+    htmlEntry.setValue(SourceEntry.LINE_INFO, lineInfo);
+    htmlEntry.setValue(HtmlEntry.PARSED_UNIT, task.htmlUnit);
+    htmlEntry.setValue(HtmlEntry.PARSE_ERRORS, task.errors);
+    htmlEntry.setValue(
+        HtmlEntry.REFERENCED_LIBRARIES, task.referencedLibraries);
+    _cache.storedAst(source);
+    ChangeNoticeImpl notice = _getNotice(source);
+    notice.setErrors(htmlEntry.allErrors, lineInfo);
+    return htmlEntry;
+  }
+
+  /**
+   * Record the results produced by performing a [task] and return the cache
+   * entry associated with the results.
+   */
+  DartEntry _recordResolveDartUnitTaskResults(ResolveDartUnitTask task) {
+    Source unitSource = task.source;
+    DartEntry dartEntry = _cache.get(unitSource);
+    Source librarySource = task.librarySource;
+    CaughtException thrownException = task.exception;
+    if (thrownException != null) {
+      dartEntry.recordResolutionErrorInLibrary(librarySource, thrownException);
+      _cache.removedAst(unitSource);
+      throw new AnalysisException('<rethrow>', thrownException);
+    }
+    dartEntry.setValueInLibrary(
+        DartEntry.RESOLVED_UNIT, librarySource, task.resolvedUnit);
+    _cache.storedAst(unitSource);
+    return dartEntry;
+  }
+
+  /**
+   * Record the results produced by performing a [task] and return the cache
+   * entry associated with the results.
+   */
+  HtmlEntry _recordResolveHtmlTaskResults(ResolveHtmlTask task) {
+    Source source = task.source;
+    HtmlEntry htmlEntry = _cache.get(source);
+    CaughtException thrownException = task.exception;
+    if (thrownException != null) {
+      htmlEntry.recordResolutionError(thrownException);
+      _cache.removedAst(source);
+      throw new AnalysisException('<rethrow>', thrownException);
+    }
+    htmlEntry.setState(HtmlEntry.PARSED_UNIT, CacheState.FLUSHED);
+    htmlEntry.setValue(HtmlEntry.RESOLVED_UNIT, task.resolvedUnit);
+    htmlEntry.setValue(HtmlEntry.ELEMENT, task.element);
+    htmlEntry.setValue(HtmlEntry.RESOLUTION_ERRORS, task.resolutionErrors);
+    _cache.storedAst(source);
+    ChangeNoticeImpl notice = _getNotice(source);
+    notice.resolvedHtmlUnit = task.resolvedUnit;
+    LineInfo lineInfo = htmlEntry.getValue(SourceEntry.LINE_INFO);
+    notice.setErrors(htmlEntry.allErrors, lineInfo);
+    return htmlEntry;
+  }
+
+  /**
+   * Record the results produced by performing a [task] and return the cache
+   * entry associated with the results.
+   */
+  DartEntry _recordScanDartTaskResults(ScanDartTask task) {
+    Source source = task.source;
+    DartEntry dartEntry = _cache.get(source);
+    CaughtException thrownException = task.exception;
+    if (thrownException != null) {
+      _removeFromParts(source, dartEntry);
+      dartEntry.recordScanError(thrownException);
+      _cache.removedAst(source);
+      throw new AnalysisException('<rethrow>', thrownException);
+    }
+    LineInfo lineInfo = task.lineInfo;
+    dartEntry.setValue(SourceEntry.LINE_INFO, lineInfo);
+    dartEntry.setValue(DartEntry.TOKEN_STREAM, task.tokenStream);
+    dartEntry.setValue(DartEntry.SCAN_ERRORS, task.errors);
+    _cache.storedAst(source);
+    ChangeNoticeImpl notice = _getNotice(source);
+    notice.setErrors(dartEntry.allErrors, lineInfo);
+    return dartEntry;
+  }
+
+  /**
+   * Remove the given [librarySource] from the list of containing libraries for
+   * all of the parts referenced by the given [dartEntry].
+   */
+  void _removeFromParts(Source librarySource, DartEntry dartEntry) {
+    List<Source> oldParts = dartEntry.getValue(DartEntry.INCLUDED_PARTS);
+    for (int i = 0; i < oldParts.length; i++) {
+      Source partSource = oldParts[i];
+      DartEntry partEntry = _getReadableDartEntry(partSource);
+      if (partEntry != null && !identical(partEntry, dartEntry)) {
+        partEntry.removeContainingLibrary(librarySource);
+        if (partEntry.containingLibraries.length == 0 && !exists(partSource)) {
+          _cache.remove(partSource);
+        }
+      }
+    }
+  }
+
+  /**
+   * Remove the given libraries that are keys in the given map from the list of
+   * containing libraries for each of the parts in the corresponding value.
+   */
+  void _removeFromPartsUsingMap(HashMap<Source, List<Source>> oldPartMap) {
+    oldPartMap.forEach((Source librarySource, List<Source> oldParts) {
+      for (int i = 0; i < oldParts.length; i++) {
+        Source partSource = oldParts[i];
+        if (partSource != librarySource) {
+          DartEntry partEntry = _getReadableDartEntry(partSource);
+          if (partEntry != null) {
+            partEntry.removeContainingLibrary(librarySource);
+            if (partEntry.containingLibraries.length == 0 &&
+                !exists(partSource)) {
+              _cache.remove(partSource);
+            }
+          }
+        }
+      }
+    });
+  }
+
+  /**
+   * Remove the given [source] from the priority order if it is in the list.
+   */
+  void _removeFromPriorityOrder(Source source) {
+    int count = _priorityOrder.length;
+    List<Source> newOrder = new List<Source>();
+    for (int i = 0; i < count; i++) {
+      if (_priorityOrder[i] != source) {
+        newOrder.add(_priorityOrder[i]);
+      }
+    }
+    if (newOrder.length < count) {
+      analysisPriorityOrder = newOrder;
+    }
+  }
+
+  /**
+   * Return `true` if errors should be produced for the given [source]. The
+   * [dartEntry] associated with the source is passed in for efficiency.
+   */
+  bool _shouldErrorsBeAnalyzed(Source source, DartEntry dartEntry) {
+    if (source.isInSystemLibrary) {
+      return _generateSdkErrors;
+    } else if (!dartEntry.explicitlyAdded) {
+      return _generateImplicitErrors;
+    } else {
+      return true;
+    }
+  }
+
+  /**
+   * Create an entry for the newly added [source] and invalidate any sources
+   * that referenced the source before it existed.
+   */
+  void _sourceAvailable(Source source) {
+    SourceEntry sourceEntry = _cache.get(source);
+    if (sourceEntry == null) {
+      sourceEntry = _createSourceEntry(source, true);
+    } else {
+      _propagateInvalidation(source, sourceEntry);
+      sourceEntry = _cache.get(source);
+    }
+    if (sourceEntry is HtmlEntry) {
+      _workManager.add(source, SourcePriority.HTML);
+    } else if (sourceEntry is DartEntry) {
+      _workManager.add(source, _computePriority(sourceEntry));
+    }
+  }
+
+  /**
+   * Invalidate the [source] that was changed and any sources that referenced
+   * the source before it existed.
+   */
+  void _sourceChanged(Source source) {
+    SourceEntry sourceEntry = _cache.get(source);
+    // If the source is removed, we don't care about it.
+    if (sourceEntry == null) {
+      return;
+    }
+    // Check if the content of the source is the same as it was the last time.
+    String sourceContent = sourceEntry.getValue(SourceEntry.CONTENT);
+    if (sourceContent != null) {
+      sourceEntry.setState(SourceEntry.CONTENT, CacheState.FLUSHED);
+      try {
+        TimestampedData<String> fileContents = getContents(source);
+        if (fileContents.data == sourceContent) {
+          return;
+        }
+      } catch (e) {}
+    }
+    // We have to invalidate the cache.
+    _propagateInvalidation(source, sourceEntry);
+  }
+
+  /**
+   * Record that the give [source] has been deleted.
+   */
+  void _sourceDeleted(Source source) {
+    SourceEntry sourceEntry = _cache.get(source);
+    if (sourceEntry is HtmlEntry) {
+      HtmlEntry htmlEntry = sourceEntry;
+      htmlEntry.recordContentError(new CaughtException(
+          new AnalysisException("This source was marked as being deleted"),
+          null));
+    } else if (sourceEntry is DartEntry) {
+      DartEntry dartEntry = sourceEntry;
+      HashSet<Source> libraries = new HashSet<Source>();
+      for (Source librarySource in getLibrariesContaining(source)) {
+        libraries.add(librarySource);
+        for (Source dependentLibrary
+            in getLibrariesDependingOn(librarySource)) {
+          libraries.add(dependentLibrary);
+        }
+      }
+      for (Source librarySource in libraries) {
+        _invalidateLibraryResolution(librarySource);
+      }
+      dartEntry.recordContentError(new CaughtException(
+          new AnalysisException("This source was marked as being deleted"),
+          null));
+    }
+    _workManager.remove(source);
+    _removeFromPriorityOrder(source);
+  }
+
+  /**
+   * Record that the given [source] has been removed.
+   */
+  void _sourceRemoved(Source source) {
+    SourceEntry sourceEntry = _cache.get(source);
+    if (sourceEntry is HtmlEntry) {} else if (sourceEntry is DartEntry) {
+      HashSet<Source> libraries = new HashSet<Source>();
+      for (Source librarySource in getLibrariesContaining(source)) {
+        libraries.add(librarySource);
+        for (Source dependentLibrary
+            in getLibrariesDependingOn(librarySource)) {
+          libraries.add(dependentLibrary);
+        }
+      }
+      for (Source librarySource in libraries) {
+        _invalidateLibraryResolution(librarySource);
+      }
+    }
+    _cache.remove(source);
+    _workManager.remove(source);
+    _removeFromPriorityOrder(source);
+  }
+
+  /**
+   * TODO(scheglov) A hackish, limited incremental resolution implementation.
+   */
+  bool _tryPoorMansIncrementalResolution(Source unitSource, String newCode) {
+    return PerformanceStatistics.incrementalAnalysis.makeCurrentWhile(() {
+      incrementalResolutionValidation_lastUnitSource = null;
+      incrementalResolutionValidation_lastLibrarySource = null;
+      incrementalResolutionValidation_lastUnit = null;
+      // prepare the entry
+      DartEntry dartEntry = _cache.get(unitSource);
+      if (dartEntry == null) {
+        return false;
+      }
+      // prepare the (only) library source
+      List<Source> librarySources = getLibrariesContaining(unitSource);
+      if (librarySources.length != 1) {
+        return false;
+      }
+      Source librarySource = librarySources[0];
+      // prepare the library element
+      LibraryElement libraryElement = getLibraryElement(librarySource);
+      if (libraryElement == null) {
+        return false;
+      }
+      // prepare the existing unit
+      CompilationUnit oldUnit =
+          getResolvedCompilationUnit2(unitSource, librarySource);
+      if (oldUnit == null) {
+        return false;
+      }
+      // do resolution
+      Stopwatch perfCounter = new Stopwatch()..start();
+      PoorMansIncrementalResolver resolver = new PoorMansIncrementalResolver(
+          typeProvider, unitSource, dartEntry, oldUnit,
+          analysisOptions.incrementalApi, analysisOptions);
+      bool success = resolver.resolve(newCode);
+      AnalysisEngine.instance.instrumentationService.logPerformance(
+          AnalysisPerformanceKind.INCREMENTAL, perfCounter,
+          'success=$success,context_id=$_id,code_length=${newCode.length}');
+      if (!success) {
+        return false;
+      }
+      // if validation, remember the result, but throw it away
+      if (analysisOptions.incrementalValidation) {
+        incrementalResolutionValidation_lastUnitSource = oldUnit.element.source;
+        incrementalResolutionValidation_lastLibrarySource =
+            oldUnit.element.library.source;
+        incrementalResolutionValidation_lastUnit = oldUnit;
+        return false;
+      }
+      // prepare notice
+      {
+        LineInfo lineInfo = getLineInfo(unitSource);
+        ChangeNoticeImpl notice = _getNotice(unitSource);
+        notice.resolvedDartUnit = oldUnit;
+        notice.setErrors(dartEntry.allErrors, lineInfo);
+      }
+      // OK
+      return true;
+    });
+  }
+
+  /**
+   * Check the cache for any invalid entries (entries whose modification time
+   * does not match the modification time of the source associated with the
+   * entry). Invalid entries will be marked as invalid so that the source will
+   * be re-analyzed. Return `true` if at least one entry was invalid.
+   */
+  bool _validateCacheConsistency() {
+    int consistencyCheckStart = JavaSystem.nanoTime();
+    List<Source> changedSources = new List<Source>();
+    List<Source> missingSources = new List<Source>();
+    MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+    while (iterator.moveNext()) {
+      Source source = iterator.key;
+      SourceEntry sourceEntry = iterator.value;
+      int sourceTime = getModificationStamp(source);
+      if (sourceTime != sourceEntry.modificationTime) {
+        changedSources.add(source);
+      }
+      if (sourceEntry.exception != null) {
+        if (!exists(source)) {
+          missingSources.add(source);
+        }
+      }
+    }
+    int count = changedSources.length;
+    for (int i = 0; i < count; i++) {
+      _sourceChanged(changedSources[i]);
+    }
+    int removalCount = 0;
+    for (Source source in missingSources) {
+      if (getLibrariesContaining(source).isEmpty &&
+          getLibrariesDependingOn(source).isEmpty) {
+        _cache.remove(source);
+        removalCount++;
+      }
+    }
+    int consistencyCheckEnd = JavaSystem.nanoTime();
+    if (changedSources.length > 0 || missingSources.length > 0) {
+      StringBuffer buffer = new StringBuffer();
+      buffer.write("Consistency check took ");
+      buffer.write((consistencyCheckEnd - consistencyCheckStart) / 1000000.0);
+      buffer.writeln(" ms and found");
+      buffer.write("  ");
+      buffer.write(changedSources.length);
+      buffer.writeln(" inconsistent entries");
+      buffer.write("  ");
+      buffer.write(missingSources.length);
+      buffer.write(" missing sources (");
+      buffer.write(removalCount);
+      buffer.writeln(" removed");
+      for (Source source in missingSources) {
+        buffer.write("    ");
+        buffer.writeln(source.fullName);
+      }
+      _logInformation(buffer.toString());
+    }
+    return changedSources.length > 0;
+  }
+
+  void _validateLastIncrementalResolutionResult() {
+    if (incrementalResolutionValidation_lastUnitSource == null ||
+        incrementalResolutionValidation_lastLibrarySource == null ||
+        incrementalResolutionValidation_lastUnit == null) {
+      return;
+    }
+    CompilationUnit fullUnit = getResolvedCompilationUnit2(
+        incrementalResolutionValidation_lastUnitSource,
+        incrementalResolutionValidation_lastLibrarySource);
+    if (fullUnit != null) {
+      try {
+        assertSameResolution(
+            incrementalResolutionValidation_lastUnit, fullUnit);
+      } on IncrementalResolutionMismatch catch (mismatch, stack) {
+        String failure = mismatch.message;
+        String message =
+            'Incremental resolution mismatch:\n$failure\nat\n$stack';
+        AnalysisEngine.instance.logger.logError(message);
+      }
+    }
+    incrementalResolutionValidation_lastUnitSource = null;
+    incrementalResolutionValidation_lastLibrarySource = null;
+    incrementalResolutionValidation_lastUnit = null;
+  }
+}
+
+/**
+ * An object used by an analysis context to record the results of a task.
+ */
+class AnalysisContextImpl_AnalysisTaskResultRecorder
+    implements AnalysisTaskVisitor<SourceEntry> {
+  final AnalysisContextImpl AnalysisContextImpl_this;
+
+  AnalysisContextImpl_AnalysisTaskResultRecorder(this.AnalysisContextImpl_this);
+
+  @override
+  DartEntry visitBuildUnitElementTask(BuildUnitElementTask task) =>
+      AnalysisContextImpl_this._recordBuildUnitElementTask(task);
+
+  @override
+  DartEntry visitGenerateDartErrorsTask(GenerateDartErrorsTask task) =>
+      AnalysisContextImpl_this._recordGenerateDartErrorsTask(task);
+
+  @override
+  DartEntry visitGenerateDartHintsTask(GenerateDartHintsTask task) =>
+      AnalysisContextImpl_this._recordGenerateDartHintsTask(task);
+
+  @override
+  DartEntry visitGenerateDartLintsTask(GenerateDartLintsTask task) =>
+      AnalysisContextImpl_this._recordGenerateDartLintsTask(task);
+
+  @override
+  SourceEntry visitGetContentTask(GetContentTask task) =>
+      AnalysisContextImpl_this._recordGetContentsTask(task);
+
+  @override
+  DartEntry visitIncrementalAnalysisTask(IncrementalAnalysisTask task) =>
+      AnalysisContextImpl_this._recordIncrementalAnalysisTaskResults(task);
+
+  @override
+  DartEntry visitParseDartTask(ParseDartTask task) =>
+      AnalysisContextImpl_this._recordParseDartTaskResults(task);
+
+  @override
+  HtmlEntry visitParseHtmlTask(ParseHtmlTask task) =>
+      AnalysisContextImpl_this._recordParseHtmlTaskResults(task);
+
+  @override
+  DartEntry visitResolveDartLibraryCycleTask(
+          ResolveDartLibraryCycleTask task) =>
+      AnalysisContextImpl_this.recordResolveDartLibraryCycleTaskResults(task);
+
+  @override
+  DartEntry visitResolveDartLibraryTask(ResolveDartLibraryTask task) =>
+      AnalysisContextImpl_this.recordResolveDartLibraryTaskResults(task);
+
+  @override
+  DartEntry visitResolveDartUnitTask(ResolveDartUnitTask task) =>
+      AnalysisContextImpl_this._recordResolveDartUnitTaskResults(task);
+
+  @override
+  HtmlEntry visitResolveHtmlTask(ResolveHtmlTask task) =>
+      AnalysisContextImpl_this._recordResolveHtmlTaskResults(task);
+
+  @override
+  DartEntry visitScanDartTask(ScanDartTask task) =>
+      AnalysisContextImpl_this._recordScanDartTaskResults(task);
+}
+
+class AnalysisContextImpl_ContextRetentionPolicy
+    implements CacheRetentionPolicy {
+  final AnalysisContextImpl AnalysisContextImpl_this;
+
+  AnalysisContextImpl_ContextRetentionPolicy(this.AnalysisContextImpl_this);
+
+  @override
+  RetentionPriority getAstPriority(Source source, SourceEntry sourceEntry) {
+    int priorityCount = AnalysisContextImpl_this._priorityOrder.length;
+    for (int i = 0; i < priorityCount; i++) {
+      if (source == AnalysisContextImpl_this._priorityOrder[i]) {
+        return RetentionPriority.HIGH;
+      }
+    }
+    if (AnalysisContextImpl_this._neededForResolution != null &&
+        AnalysisContextImpl_this._neededForResolution.contains(source)) {
+      return RetentionPriority.HIGH;
+    }
+    if (sourceEntry is DartEntry) {
+      DartEntry dartEntry = sourceEntry;
+      if (_astIsNeeded(dartEntry)) {
+        return RetentionPriority.MEDIUM;
+      }
+    }
+    return RetentionPriority.LOW;
+  }
+
+  bool _astIsNeeded(DartEntry dartEntry) =>
+      dartEntry.hasInvalidData(DartEntry.HINTS) ||
+          dartEntry.hasInvalidData(DartEntry.LINTS) ||
+          dartEntry.hasInvalidData(DartEntry.VERIFICATION_ERRORS) ||
+          dartEntry.hasInvalidData(DartEntry.RESOLUTION_ERRORS);
+}
+
+/**
+ * An object used to construct a list of the libraries that must be resolved
+ * together in order to resolve any one of the libraries.
+ */
+class AnalysisContextImpl_CycleBuilder {
+  final AnalysisContextImpl AnalysisContextImpl_this;
+
+  /**
+   * A table mapping the sources of the defining compilation units of libraries
+   * to the representation of the library that has the information needed to
+   * resolve the library.
+   */
+  HashMap<Source, ResolvableLibrary> _libraryMap =
+      new HashMap<Source, ResolvableLibrary>();
+
+  /**
+   * The dependency graph used to compute the libraries in the cycle.
+   */
+  DirectedGraph<ResolvableLibrary> _dependencyGraph;
+
+  /**
+   * A list containing the libraries that are ready to be resolved.
+   */
+  List<ResolvableLibrary> _librariesInCycle;
+
+  /**
+   * The analysis task that needs to be performed before the cycle of libraries
+   * can be resolved, or `null` if the libraries are ready to be resolved.
+   */
+  AnalysisContextImpl_TaskData _taskData;
+
+  /**
+   * Initialize a newly created cycle builder.
+   */
+  AnalysisContextImpl_CycleBuilder(this.AnalysisContextImpl_this) : super();
+
+  /**
+   * Return a list containing the libraries that are ready to be resolved
+   * (assuming that [getTaskData] returns `null`).
+   */
+  List<ResolvableLibrary> get librariesInCycle => _librariesInCycle;
+
+  /**
+   * Return a representation of an analysis task that needs to be performed
+   * before the cycle of libraries can be resolved, or `null` if the libraries
+   * are ready to be resolved.
+   */
+  AnalysisContextImpl_TaskData get taskData => _taskData;
+
+  /**
+   * Compute a list of the libraries that need to be resolved together in orde
+   *  to resolve the given [librarySource].
+   */
+  void computeCycleContaining(Source librarySource) {
+    //
+    // Create the object representing the library being resolved.
+    //
+    ResolvableLibrary targetLibrary = _createLibrary(librarySource);
+    //
+    // Compute the set of libraries that need to be resolved together.
+    //
+    _dependencyGraph = new DirectedGraph<ResolvableLibrary>();
+    _computeLibraryDependencies(targetLibrary);
+    if (_taskData != null) {
+      return;
+    }
+    _librariesInCycle = _dependencyGraph.findCycleContaining(targetLibrary);
+    //
+    // Ensure that all of the data needed to resolve them has been computed.
+    //
+    _ensureImportsAndExports();
+    if (_taskData != null) {
+      // At least one imported library needs to be resolved before the target
+      // library.
+      AnalysisTask task = _taskData.task;
+      if (task is ResolveDartLibraryTask) {
+        AnalysisContextImpl_this._workManager.addFirst(
+            task.librarySource, SourcePriority.LIBRARY);
+      }
+      return;
+    }
+    _computePartsInCycle(librarySource);
+    if (_taskData != null) {
+      // At least one part needs to be parsed.
+      return;
+    }
+    // All of the AST's necessary to perform a resolution of the library cycle
+    // have been gathered, so it is no longer necessary to retain them in the
+    // cache.
+    AnalysisContextImpl_this._neededForResolution = null;
+  }
+
+  bool _addDependency(ResolvableLibrary dependant, Source dependency,
+      List<ResolvableLibrary> dependencyList) {
+    if (dependant.librarySource == dependency) {
+      // Don't add a dependency of a library on itself; there's no point.
+      return true;
+    }
+    ResolvableLibrary importedLibrary = _libraryMap[dependency];
+    if (importedLibrary == null) {
+      importedLibrary = _createLibraryOrNull(dependency);
+      if (importedLibrary != null) {
+        _computeLibraryDependencies(importedLibrary);
+        if (_taskData != null) {
+          return false;
+        }
+      }
+    }
+    if (importedLibrary != null) {
+      if (dependencyList != null) {
+        dependencyList.add(importedLibrary);
+      }
+      _dependencyGraph.addEdge(dependant, importedLibrary);
+    }
+    return true;
+  }
+
+  /**
+   * Recursively traverse the libraries reachable from the given [library],
+   * creating instances of the class [Library] to represent them, and record the
+   * references in the library objects.
+   *
+   * Throws an [AnalysisException] if some portion of the library graph could
+   * not be traversed.
+   */
+  void _computeLibraryDependencies(ResolvableLibrary library) {
+    Source librarySource = library.librarySource;
+    DartEntry dartEntry =
+        AnalysisContextImpl_this._getReadableDartEntry(librarySource);
+    List<Source> importedSources =
+        _getSources(librarySource, dartEntry, DartEntry.IMPORTED_LIBRARIES);
+    if (_taskData != null) {
+      return;
+    }
+    List<Source> exportedSources =
+        _getSources(librarySource, dartEntry, DartEntry.EXPORTED_LIBRARIES);
+    if (_taskData != null) {
+      return;
+    }
+    _computeLibraryDependenciesFromDirectives(
+        library, importedSources, exportedSources);
+  }
+
+  /**
+   * Recursively traverse the libraries reachable from the given [library],
+   * creating instances of the class [Library] to represent them, and record the
+   * references in the library objects. The [importedSources] is a list
+   * containing the sources that are imported into the given library. The
+   * [exportedSources] is a list containing the sources that are exported from
+   * the given library.
+   */
+  void _computeLibraryDependenciesFromDirectives(ResolvableLibrary library,
+      List<Source> importedSources, List<Source> exportedSources) {
+    int importCount = importedSources.length;
+    List<ResolvableLibrary> importedLibraries = new List<ResolvableLibrary>();
+    bool explicitlyImportsCore = false;
+    bool importsAsync = false;
+    for (int i = 0; i < importCount; i++) {
+      Source importedSource = importedSources[i];
+      if (importedSource == AnalysisContextImpl_this._coreLibrarySource) {
+        explicitlyImportsCore = true;
+      } else if (importedSource ==
+          AnalysisContextImpl_this._asyncLibrarySource) {
+        importsAsync = true;
+      }
+      if (!_addDependency(library, importedSource, importedLibraries)) {
+        return;
+      }
+    }
+    library.explicitlyImportsCore = explicitlyImportsCore;
+    if (!explicitlyImportsCore) {
+      if (!_addDependency(library, AnalysisContextImpl_this._coreLibrarySource,
+          importedLibraries)) {
+        return;
+      }
+    }
+    if (!importsAsync) {
+      // Add a dependency on async to ensure that the Future element will be
+      // built before we generate errors and warnings for async methods.  Also
+      // include it in importedLibraries, so that it will be picked up by
+      // LibraryResolver2._buildLibraryMap().
+      // TODO(paulberry): this is a bit of a hack, since the async library
+      // isn't actually being imported.  Also, it's not clear whether it should
+      // be necessary: in theory, dart:core already (indirectly) imports
+      // dart:async, so if core has been built, async should have been built
+      // too.  However, removing this code causes unit test failures.
+      if (!_addDependency(library, AnalysisContextImpl_this._asyncLibrarySource,
+          importedLibraries)) {
+        return;
+      }
+    }
+    library.importedLibraries = importedLibraries;
+    int exportCount = exportedSources.length;
+    if (exportCount > 0) {
+      List<ResolvableLibrary> exportedLibraries = new List<ResolvableLibrary>();
+      for (int i = 0; i < exportCount; i++) {
+        Source exportedSource = exportedSources[i];
+        if (!_addDependency(library, exportedSource, exportedLibraries)) {
+          return;
+        }
+      }
+      library.exportedLibraries = exportedLibraries;
+    }
+  }
+
+  /**
+   * Gather the resolvable AST structures for each of the compilation units in
+   * each of the libraries in the cycle. This is done in two phases: first we
+   * ensure that we have cached an AST structure for each compilation unit, then
+   * we gather them. We split the work this way because getting the AST
+   * structures can change the state of the cache in such a way that we would
+   * have more work to do if any compilation unit didn't have a resolvable AST
+   * structure.
+   */
+  void _computePartsInCycle(Source librarySource) {
+    int count = _librariesInCycle.length;
+    List<CycleBuilder_LibraryPair> libraryData =
+        new List<CycleBuilder_LibraryPair>();
+    for (int i = 0; i < count; i++) {
+      ResolvableLibrary library = _librariesInCycle[i];
+      libraryData.add(new CycleBuilder_LibraryPair(
+          library, _ensurePartsInLibrary(library)));
+    }
+    AnalysisContextImpl_this._neededForResolution = _gatherSources(libraryData);
+    if (AnalysisContextImpl._TRACE_PERFORM_TASK) {
+      print(
+          "  preserve resolution data for ${AnalysisContextImpl_this._neededForResolution.length} sources while resolving ${librarySource.fullName}");
+    }
+    if (_taskData != null) {
+      return;
+    }
+    for (int i = 0; i < count; i++) {
+      _computePartsInLibrary(libraryData[i]);
+    }
+  }
+
+  /**
+   * Gather the resolvable compilation units for each of the compilation units
+   * in the library represented by the [libraryPair].
+   */
+  void _computePartsInLibrary(CycleBuilder_LibraryPair libraryPair) {
+    ResolvableLibrary library = libraryPair.library;
+    List<CycleBuilder_SourceEntryPair> entryPairs = libraryPair.entryPairs;
+    int count = entryPairs.length;
+    List<ResolvableCompilationUnit> units =
+        new List<ResolvableCompilationUnit>(count);
+    for (int i = 0; i < count; i++) {
+      CycleBuilder_SourceEntryPair entryPair = entryPairs[i];
+      Source source = entryPair.source;
+      DartEntry dartEntry = entryPair.entry;
+      units[i] = new ResolvableCompilationUnit(
+          source, dartEntry.resolvableCompilationUnit);
+    }
+    library.resolvableCompilationUnits = units;
+  }
+
+  /**
+   * Create an object to represent the information about the library defined by
+   * the compilation unit with the given [librarySource].
+   */
+  ResolvableLibrary _createLibrary(Source librarySource) {
+    ResolvableLibrary library = new ResolvableLibrary(librarySource);
+    SourceEntry sourceEntry =
+        AnalysisContextImpl_this._cache.get(librarySource);
+    if (sourceEntry is DartEntry) {
+      LibraryElementImpl libraryElement =
+          sourceEntry.getValue(DartEntry.ELEMENT) as LibraryElementImpl;
+      if (libraryElement != null) {
+        library.libraryElement = libraryElement;
+      }
+    }
+    _libraryMap[librarySource] = library;
+    return library;
+  }
+
+  /**
+   * Create an object to represent the information about the library defined by
+   * the compilation unit with the given [librarySource].
+   */
+  ResolvableLibrary _createLibraryOrNull(Source librarySource) {
+    ResolvableLibrary library = new ResolvableLibrary(librarySource);
+    SourceEntry sourceEntry =
+        AnalysisContextImpl_this._cache.get(librarySource);
+    if (sourceEntry is DartEntry) {
+      LibraryElementImpl libraryElement =
+          sourceEntry.getValue(DartEntry.ELEMENT) as LibraryElementImpl;
+      if (libraryElement != null) {
+        library.libraryElement = libraryElement;
+      }
+    }
+    _libraryMap[librarySource] = library;
+    return library;
+  }
+
+  /**
+   * Ensure that the given [library] has an element model built for it. If
+   * another task needs to be executed first in order to build the element
+   * model, that task is placed in [taskData].
+   */
+  void _ensureElementModel(ResolvableLibrary library) {
+    Source librarySource = library.librarySource;
+    DartEntry libraryEntry =
+        AnalysisContextImpl_this._getReadableDartEntry(librarySource);
+    if (libraryEntry != null &&
+        libraryEntry.getState(DartEntry.PARSED_UNIT) != CacheState.ERROR) {
+      AnalysisContextImpl_this._workManager.addFirst(
+          librarySource, SourcePriority.LIBRARY);
+      if (_taskData == null) {
+        _taskData = AnalysisContextImpl_this._createResolveDartLibraryTask(
+            librarySource, libraryEntry);
+      }
+    }
+  }
+
+  /**
+   * Ensure that all of the libraries that are exported by the given [library]
+   * (but are not themselves in the cycle) have element models built for them.
+   * If another task needs to be executed first in order to build the element
+   * model, that task is placed in [taskData].
+   */
+  void _ensureExports(
+      ResolvableLibrary library, HashSet<Source> visitedLibraries) {
+    List<ResolvableLibrary> dependencies = library.exports;
+    int dependencyCount = dependencies.length;
+    for (int i = 0; i < dependencyCount; i++) {
+      ResolvableLibrary dependency = dependencies[i];
+      if (!_librariesInCycle.contains(dependency) &&
+          visitedLibraries.add(dependency.librarySource)) {
+        if (dependency.libraryElement == null) {
+          _ensureElementModel(dependency);
+        } else {
+          _ensureExports(dependency, visitedLibraries);
+        }
+        if (_taskData != null) {
+          return;
+        }
+      }
+    }
+  }
+
+  /**
+   * Ensure that all of the libraries that are exported by the given [library]
+   * (but are not themselves in the cycle) have element models built for them.
+   * If another task needs to be executed first in order to build the element
+   * model, that task is placed in [taskData].
+   */
+  void _ensureImports(ResolvableLibrary library) {
+    List<ResolvableLibrary> dependencies = library.imports;
+    int dependencyCount = dependencies.length;
+    for (int i = 0; i < dependencyCount; i++) {
+      ResolvableLibrary dependency = dependencies[i];
+      if (!_librariesInCycle.contains(dependency) &&
+          dependency.libraryElement == null) {
+        _ensureElementModel(dependency);
+        if (_taskData != null) {
+          return;
+        }
+      }
+    }
+  }
+
+  /**
+   * Ensure that all of the libraries that are either imported or exported by
+   * libraries in the cycle (but are not themselves in the cycle) have element
+   * models built for them.
+   */
+  void _ensureImportsAndExports() {
+    HashSet<Source> visitedLibraries = new HashSet<Source>();
+    int libraryCount = _librariesInCycle.length;
+    for (int i = 0; i < libraryCount; i++) {
+      ResolvableLibrary library = _librariesInCycle[i];
+      _ensureImports(library);
+      if (_taskData != null) {
+        return;
+      }
+      _ensureExports(library, visitedLibraries);
+      if (_taskData != null) {
+        return;
+      }
+    }
+  }
+
+  /**
+   * Ensure that there is a resolvable compilation unit available for all of the
+   * compilation units in the given [library].
+   */
+  List<CycleBuilder_SourceEntryPair> _ensurePartsInLibrary(
+      ResolvableLibrary library) {
+    List<CycleBuilder_SourceEntryPair> pairs =
+        new List<CycleBuilder_SourceEntryPair>();
+    Source librarySource = library.librarySource;
+    DartEntry libraryEntry =
+        AnalysisContextImpl_this._getReadableDartEntry(librarySource);
+    if (libraryEntry == null) {
+      throw new AnalysisException(
+          "Cannot find entry for ${librarySource.fullName}");
+    } else if (libraryEntry.getState(DartEntry.PARSED_UNIT) ==
+        CacheState.ERROR) {
+      String message =
+          "Cannot compute parsed unit for ${librarySource.fullName}";
+      CaughtException exception = libraryEntry.exception;
+      if (exception == null) {
+        throw new AnalysisException(message);
+      }
+      throw new AnalysisException(
+          message, new CaughtException(exception, null));
+    }
+    _ensureResolvableCompilationUnit(librarySource, libraryEntry);
+    pairs.add(new CycleBuilder_SourceEntryPair(librarySource, libraryEntry));
+    List<Source> partSources =
+        _getSources(librarySource, libraryEntry, DartEntry.INCLUDED_PARTS);
+    int count = partSources.length;
+    for (int i = 0; i < count; i++) {
+      Source partSource = partSources[i];
+      DartEntry partEntry =
+          AnalysisContextImpl_this._getReadableDartEntry(partSource);
+      if (partEntry != null &&
+          partEntry.getState(DartEntry.PARSED_UNIT) != CacheState.ERROR) {
+        _ensureResolvableCompilationUnit(partSource, partEntry);
+        pairs.add(new CycleBuilder_SourceEntryPair(partSource, partEntry));
+      }
+    }
+    return pairs;
+  }
+
+  /**
+   * Ensure that there is a resolvable compilation unit available for the given
+   * [source].
+   */
+  void _ensureResolvableCompilationUnit(Source source, DartEntry dartEntry) {
+    // The entry will be null if the source represents a non-Dart file.
+    if (dartEntry != null && !dartEntry.hasResolvableCompilationUnit) {
+      if (_taskData == null) {
+        _taskData =
+            AnalysisContextImpl_this._createParseDartTask(source, dartEntry);
+      }
+    }
+  }
+
+  HashSet<Source> _gatherSources(List<CycleBuilder_LibraryPair> libraryData) {
+    int libraryCount = libraryData.length;
+    HashSet<Source> sources = new HashSet<Source>();
+    for (int i = 0; i < libraryCount; i++) {
+      List<CycleBuilder_SourceEntryPair> entryPairs = libraryData[i].entryPairs;
+      int entryCount = entryPairs.length;
+      for (int j = 0; j < entryCount; j++) {
+        sources.add(entryPairs[j].source);
+      }
+    }
+    return sources;
+  }
+
+  /**
+   * Return the sources described by the given [descriptor].
+   */
+  List<Source> _getSources(Source source, DartEntry dartEntry,
+      DataDescriptor<List<Source>> descriptor) {
+    if (dartEntry == null) {
+      return Source.EMPTY_ARRAY;
+    }
+    CacheState exportState = dartEntry.getState(descriptor);
+    if (exportState == CacheState.ERROR) {
+      return Source.EMPTY_ARRAY;
+    } else if (exportState != CacheState.VALID) {
+      if (_taskData == null) {
+        _taskData =
+            AnalysisContextImpl_this._createParseDartTask(source, dartEntry);
+      }
+      return Source.EMPTY_ARRAY;
+    }
+    return dartEntry.getValue(descriptor);
+  }
+}
+
+/**
+ * Information about the next task to be performed. Each data has an implicit
+ * associated source: the source that might need to be analyzed. There are
+ * essentially three states that can be represented:
+ *
+ * * If [getTask] returns a non-`null` value, then that is the task that should
+ *   be executed to further analyze the associated source.
+ * * Otherwise, if [isBlocked] returns `true`, then there is no work that can be
+ *   done, but analysis for the associated source is not complete.
+ * * Otherwise, [getDependentSource] should return a source that needs to be
+ *   analyzed before the analysis of the associated source can be completed.
+ */
+class AnalysisContextImpl_TaskData {
+  /**
+   * The task that is to be performed.
+   */
+  final AnalysisTask task;
+
+  /**
+   * A flag indicating whether the associated source is blocked waiting for its
+   * contents to be loaded.
+   */
+  final bool _blocked;
+
+  /**
+   * Initialize a newly created data holder.
+   */
+  AnalysisContextImpl_TaskData(this.task, this._blocked);
+
+  /**
+   * Return `true` if the associated source is blocked waiting for its contents
+   * to be loaded.
+   */
+  bool get isBlocked => _blocked;
+
+  @override
+  String toString() {
+    if (task == null) {
+      return "blocked: $_blocked";
+    }
+    return task.toString();
+  }
+}
+
+/**
+ * Statistics and information about a single [AnalysisContext].
+ */
+abstract class AnalysisContextStatistics {
+  /**
+   * Return the statistics for each kind of cached data.
+   */
+  List<AnalysisContextStatistics_CacheRow> get cacheRows;
+
+  /**
+   * Return the exceptions that caused some entries to have a state of
+   * [CacheState.ERROR].
+   */
+  List<CaughtException> get exceptions;
+
+  /**
+   * Return information about each of the partitions in the cache.
+   */
+  List<AnalysisContextStatistics_PartitionData> get partitionData;
+
+  /**
+   * Return a list containing all of the sources in the cache.
+   */
+  List<Source> get sources;
+}
+
+/**
+ * Information about single piece of data in the cache.
+ */
+abstract class AnalysisContextStatistics_CacheRow {
+  /**
+   * List of possible states which can be queried.
+   */
+  static const List<CacheState> STATES = const <CacheState>[
+    CacheState.ERROR,
+    CacheState.FLUSHED,
+    CacheState.IN_PROCESS,
+    CacheState.INVALID,
+    CacheState.VALID
+  ];
+
+  /**
+   * Return the number of entries whose state is [CacheState.ERROR].
+   */
+  int get errorCount;
+
+  /**
+   * Return the number of entries whose state is [CacheState.FLUSHED].
+   */
+  int get flushedCount;
+
+  /**
+   * Return the number of entries whose state is [CacheState.IN_PROCESS].
+   */
+  int get inProcessCount;
+
+  /**
+   * Return the number of entries whose state is [CacheState.INVALID].
+   */
+  int get invalidCount;
+
+  /**
+   * Return the name of the data represented by this object.
+   */
+  String get name;
+
+  /**
+   * Return the number of entries whose state is [CacheState.VALID].
+   */
+  int get validCount;
+
+  /**
+   * Return the number of entries whose state is [state].
+   */
+  int getCount(CacheState state);
+}
+
+/**
+ * Information about a single partition in the cache.
+ */
+abstract class AnalysisContextStatistics_PartitionData {
+  /**
+   * Return the number of entries in the partition that have an AST structure in
+   * one state or another.
+   */
+  int get astCount;
+
+  /**
+   * Return the total number of entries in the partition.
+   */
+  int get totalCount;
+}
+
+/**
+ * Implementation of the [AnalysisContextStatistics].
+ */
+class AnalysisContextStatisticsImpl implements AnalysisContextStatistics {
+  Map<String, AnalysisContextStatistics_CacheRow> _dataMap =
+      new HashMap<String, AnalysisContextStatistics_CacheRow>();
+
+  List<Source> _sources = new List<Source>();
+
+  HashSet<CaughtException> _exceptions = new HashSet<CaughtException>();
+
+  List<AnalysisContextStatistics_PartitionData> _partitionData;
+
+  @override
+  List<AnalysisContextStatistics_CacheRow> get cacheRows =>
+      _dataMap.values.toList();
+
+  @override
+  List<CaughtException> get exceptions => new List.from(_exceptions);
+
+  @override
+  List<AnalysisContextStatistics_PartitionData> get partitionData =>
+      _partitionData;
+
+  /**
+   * Set the partition data returned by this object to the given data.
+   */
+  void set partitionData(List<AnalysisContextStatistics_PartitionData> data) {
+    _partitionData = data;
+  }
+
+  @override
+  List<Source> get sources => _sources;
+
+  void addSource(Source source) {
+    _sources.add(source);
+  }
+
+  void _internalPutCacheItem(Source source, SourceEntry dartEntry,
+      DataDescriptor rowDesc, CacheState state) {
+    String rowName = rowDesc.toString();
+    AnalysisContextStatisticsImpl_CacheRowImpl row =
+        _dataMap[rowName] as AnalysisContextStatisticsImpl_CacheRowImpl;
+    if (row == null) {
+      row = new AnalysisContextStatisticsImpl_CacheRowImpl(rowName);
+      _dataMap[rowName] = row;
+    }
+    row._incState(state);
+    if (state == CacheState.ERROR) {
+      CaughtException exception = dartEntry.exception;
+      if (exception != null) {
+        _exceptions.add(exception);
+      }
+    }
+  }
+}
+
+class AnalysisContextStatisticsImpl_CacheRowImpl
+    implements AnalysisContextStatistics_CacheRow {
+  final String name;
+
+  Map<CacheState, int> _counts = <CacheState, int>{};
+
+  AnalysisContextStatisticsImpl_CacheRowImpl(this.name);
+
+  @override
+  int get errorCount => getCount(CacheState.ERROR);
+
+  @override
+  int get flushedCount => getCount(CacheState.FLUSHED);
+
+  @override
+  int get hashCode => name.hashCode;
+
+  @override
+  int get inProcessCount => getCount(CacheState.IN_PROCESS);
+
+  @override
+  int get invalidCount => getCount(CacheState.INVALID);
+
+  @override
+  int get validCount => getCount(CacheState.VALID);
+
+  @override
+  bool operator ==(Object obj) =>
+      obj is AnalysisContextStatisticsImpl_CacheRowImpl && obj.name == name;
+
+  @override
+  int getCount(CacheState state) {
+    int count = _counts[state];
+    if (count != null) {
+      return count;
+    } else {
+      return 0;
+    }
+  }
+
+  void _incState(CacheState state) {
+    if (_counts[state] == null) {
+      _counts[state] = 1;
+    } else {
+      _counts[state]++;
+    }
+  }
+}
+
+class AnalysisContextStatisticsImpl_PartitionDataImpl
+    implements AnalysisContextStatistics_PartitionData {
+  final int astCount;
+
+  final int totalCount;
+
+  AnalysisContextStatisticsImpl_PartitionDataImpl(
+      this.astCount, this.totalCount);
+}
+
+/**
+ * A representation of changes to the types of analysis that should be
+ * performed.
+ */
+class AnalysisDelta {
+  /**
+   * A mapping from source to what type of analysis should be performed on that
+   * source.
+   */
+  HashMap<Source, AnalysisLevel> _analysisMap =
+      new HashMap<Source, AnalysisLevel>();
+
+  /**
+   * Return a collection of the sources that have been added. This is equivalent
+   * to calling [getAnalysisLevels] and collecting all sources that do not have
+   * an analysis level of [AnalysisLevel.NONE].
+   */
+  List<Source> get addedSources {
+    List<Source> result = new List<Source>();
+    _analysisMap.forEach((Source source, AnalysisLevel level) {
+      if (level != AnalysisLevel.NONE) {
+        result.add(source);
+      }
+    });
+    return result;
+  }
+
+  /**
+   * Return a mapping of sources to the level of analysis that should be
+   * performed.
+   */
+  Map<Source, AnalysisLevel> get analysisLevels => _analysisMap;
+
+  /**
+   * Record that the given [source] should be analyzed at the given [level].
+   */
+  void setAnalysisLevel(Source source, AnalysisLevel level) {
+    _analysisMap[source] = level;
+  }
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    bool needsSeparator = _appendSources(buffer, false, AnalysisLevel.ALL);
+    needsSeparator =
+        _appendSources(buffer, needsSeparator, AnalysisLevel.RESOLVED);
+    _appendSources(buffer, needsSeparator, AnalysisLevel.NONE);
+    return buffer.toString();
+  }
+
+  /**
+   * Appendto the given [buffer] all sources with the given analysis [level],
+   * prefixed with a label and a separator if [needsSeparator] is `true`.
+   */
+  bool _appendSources(
+      StringBuffer buffer, bool needsSeparator, AnalysisLevel level) {
+    bool first = true;
+    _analysisMap.forEach((Source source, AnalysisLevel sourceLevel) {
+      if (sourceLevel == level) {
+        if (first) {
+          first = false;
+          if (needsSeparator) {
+            buffer.write("; ");
+          }
+          buffer.write(level);
+          buffer.write(" ");
+        } else {
+          buffer.write(", ");
+        }
+        buffer.write(source.fullName);
+      }
+    });
+    return needsSeparator || !first;
+  }
+}
+
+/**
+ * The entry point for the functionality provided by the analysis engine. There
+ * is a single instance of this class.
+ */
+class AnalysisEngine {
+  /**
+   * The suffix used for Dart source files.
+   */
+  static const String SUFFIX_DART = "dart";
+
+  /**
+   * The short suffix used for HTML files.
+   */
+  static const String SUFFIX_HTM = "htm";
+
+  /**
+   * The long suffix used for HTML files.
+   */
+  static const String SUFFIX_HTML = "html";
+
+  /**
+   * The unique instance of this class.
+   */
+  static final AnalysisEngine instance = new AnalysisEngine._();
+
+  /**
+   * The logger that should receive information about errors within the analysis
+   * engine.
+   */
+  Logger _logger = Logger.NULL;
+
+  /**
+   * The plugin that defines the extension points and extensions that are
+   * inherently defined by the analysis engine.
+   */
+  final EnginePlugin enginePlugin = new EnginePlugin();
+
+  /**
+   * The instrumentation service that is to be used by this analysis engine.
+   */
+  InstrumentationService _instrumentationService =
+      InstrumentationService.NULL_SERVICE;
+
+  /**
+   * The partition manager being used to manage the shared partitions.
+   */
+  final PartitionManager partitionManager = new PartitionManager();
+
+  /**
+   * The partition manager being used to manage the shared partitions.
+   */
+  final cache.PartitionManager partitionManager_new =
+      new cache.PartitionManager();
+
+  /**
+   * A flag indicating whether the (new) task model should be used to perform
+   * analysis.
+   */
+  bool useTaskModel = false;
+
+  /**
+   * The task manager used to manage the tasks used to analyze code.
+   */
+  TaskManager _taskManager;
+
+  AnalysisEngine._();
+
+  /**
+   * Return the instrumentation service that is to be used by this analysis
+   * engine.
+   */
+  InstrumentationService get instrumentationService => _instrumentationService;
+
+  /**
+   * Set the instrumentation service that is to be used by this analysis engine
+   * to the given [service].
+   */
+  void set instrumentationService(InstrumentationService service) {
+    if (service == null) {
+      _instrumentationService = InstrumentationService.NULL_SERVICE;
+    } else {
+      _instrumentationService = service;
+    }
+  }
+
+  /**
+   * Return the logger that should receive information about errors within the
+   * analysis engine.
+   */
+  Logger get logger => _logger;
+
+  /**
+   * Set the logger that should receive information about errors within the
+   * analysis engine to the given [logger].
+   */
+  void set logger(Logger logger) {
+    this._logger = logger == null ? Logger.NULL : logger;
+  }
+
+  /**
+   * Return the task manager used to manage the tasks used to analyze code.
+   */
+  TaskManager get taskManager {
+    if (_taskManager == null) {
+      _taskManager = new TaskManager();
+      _taskManager.addTaskDescriptors(enginePlugin.taskDescriptors);
+    }
+    return _taskManager;
+  }
+
+  /**
+   * Clear any caches holding on to analysis results so that a full re-analysis
+   * will be performed the next time an analysis context is created.
+   */
+  void clearCaches() {
+    partitionManager.clearCache();
+  }
+
+  /**
+   * Create and return a new context in which analysis can be performed.
+   */
+  AnalysisContext createAnalysisContext() {
+    return new AnalysisContextImpl();
+  }
+
+  /**
+   * Return `true` if the given [fileName] is assumed to contain Dart source
+   * code.
+   */
+  static bool isDartFileName(String fileName) {
+    if (fileName == null) {
+      return false;
+    }
+    return javaStringEqualsIgnoreCase(
+        FileNameUtilities.getExtension(fileName), SUFFIX_DART);
+  }
+
+  /**
+   * Return `true` if the given [fileName] is assumed to contain HTML.
+   */
+  static bool isHtmlFileName(String fileName) {
+    if (fileName == null) {
+      return false;
+    }
+    String extension = FileNameUtilities.getExtension(fileName);
+    return javaStringEqualsIgnoreCase(extension, SUFFIX_HTML) ||
+        javaStringEqualsIgnoreCase(extension, SUFFIX_HTM);
+  }
+}
+
+/**
+ * The analysis errors and line information for the errors.
+ */
+abstract class AnalysisErrorInfo {
+  /**
+   * Return the errors that as a result of the analysis, or `null` if there were
+   * no errors.
+   */
+  List<AnalysisError> get errors;
+
+  /**
+   * Return the line information associated with the errors, or `null` if there
+   * were no errors.
+   */
+  LineInfo get lineInfo;
+}
+
+/**
+ * The analysis errors and line info associated with a source.
+ */
+class AnalysisErrorInfoImpl implements AnalysisErrorInfo {
+  /**
+   * The analysis errors associated with a source, or `null` if there are no
+   * errors.
+   */
+  final List<AnalysisError> errors;
+
+  /**
+   * The line information associated with the errors, or `null` if there are no
+   * errors.
+   */
+  final LineInfo lineInfo;
+
+  /**
+   * Initialize an newly created error info with the given [errors] and
+   * [lineInfo].
+   */
+  AnalysisErrorInfoImpl(this.errors, this.lineInfo);
+}
+
+/**
+ * The levels at which a source can be analyzed.
+ */
+class AnalysisLevel extends Enum<AnalysisLevel> {
+  /**
+   * Indicates a source should be fully analyzed.
+   */
+  static const AnalysisLevel ALL = const AnalysisLevel('ALL', 0);
+
+  /**
+   * Indicates a source should be resolved and that errors, warnings and hints are needed.
+   */
+  static const AnalysisLevel ERRORS = const AnalysisLevel('ERRORS', 1);
+
+  /**
+   * Indicates a source should be resolved, but that errors, warnings and hints are not needed.
+   */
+  static const AnalysisLevel RESOLVED = const AnalysisLevel('RESOLVED', 2);
+
+  /**
+   * Indicates a source is not of interest to the client.
+   */
+  static const AnalysisLevel NONE = const AnalysisLevel('NONE', 3);
+
+  static const List<AnalysisLevel> values = const [ALL, ERRORS, RESOLVED, NONE];
+
+  const AnalysisLevel(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * An object that is listening for results being produced by an analysis
+ * context.
+ */
+abstract class AnalysisListener {
+  /**
+   * Reports that a task, described by the given [taskDescription] is about to
+   * be performed by the given [context].
+   */
+  void aboutToPerformTask(AnalysisContext context, String taskDescription);
+
+  /**
+   * Reports that the [errors] associated with the given [source] in the given
+   * [context] has been updated to the given errors. The [lineInfo] is the line
+   * information associated with the source.
+   */
+  void computedErrors(AnalysisContext context, Source source,
+      List<AnalysisError> errors, LineInfo lineInfo);
+
+  /**
+   * Reports that the given [source] is no longer included in the set of sources
+   * that are being analyzed by the given analysis [context].
+   */
+  void excludedSource(AnalysisContext context, Source source);
+
+  /**
+   * Reports that the given [source] is now included in the set of sources that
+   * are being analyzed by the given analysis [context].
+   */
+  void includedSource(AnalysisContext context, Source source);
+
+  /**
+   * Reports that the given Dart [source] was parsed in the given [context],
+   * producing the given [unit].
+   */
+  void parsedDart(AnalysisContext context, Source source, CompilationUnit unit);
+
+  /**
+   * Reports that the given HTML [source] was parsed in the given [context].
+   */
+  void parsedHtml(AnalysisContext context, Source source, ht.HtmlUnit unit);
+
+  /**
+   * Reports that the given Dart [source] was resolved in the given [context].
+   */
+  void resolvedDart(
+      AnalysisContext context, Source source, CompilationUnit unit);
+
+  /**
+   * Reports that the given HTML [source] was resolved in the given [context].
+   */
+  void resolvedHtml(AnalysisContext context, Source source, ht.HtmlUnit unit);
+}
+
+/**
+ * Futures returned by [AnalysisContext] for pending analysis results will
+ * complete with this error if it is determined that analysis results will
+ * never become available (e.g. because the requested source is not subject to
+ * analysis, or because the requested source is a part file which is not a part
+ * of any known library).
+ */
+class AnalysisNotScheduledError implements Exception {}
+
+/**
+ * A set of analysis options used to control the behavior of an analysis
+ * context.
+ */
+abstract class AnalysisOptions {
+  /**
+   * If analysis is to parse and analyze all function bodies, return `true`.
+   * If analysis is to skip all function bodies, return `false`.  If analysis
+   * is to parse and analyze function bodies in some sources and not in others,
+   * throw an exception.
+   *
+   * This getter is deprecated; consider using [analyzeFunctionBodiesPredicate]
+   * instead.
+   */
+  @deprecated // Use this.analyzeFunctionBodiesPredicate
+  bool get analyzeFunctionBodies;
+
+  /**
+   * Function that returns `true` if analysis is to parse and analyze function
+   * bodies for a given source.
+   */
+  AnalyzeFunctionBodiesPredicate get analyzeFunctionBodiesPredicate;
+
+  /**
+   * Return the maximum number of sources for which AST structures should be
+   * kept in the cache.
+   */
+  int get cacheSize;
+
+  /**
+   * Return `true` if analysis is to generate dart2js related hint results.
+   */
+  bool get dart2jsHint;
+
+  /**
+   * Return `true` if analysis is to include the new async support.
+   */
+  @deprecated // Always true
+  bool get enableAsync;
+
+  /**
+   * Return `true` if analysis is to include the new deferred loading support.
+   */
+  @deprecated // Always true
+  bool get enableDeferredLoading;
+
+  /**
+   * Return `true` if analysis is to include the new enum support.
+   */
+  @deprecated // Always true
+  bool get enableEnum;
+
+  /**
+   * Return `true` to enable null-aware operators (DEP 9).
+   */
+  bool get enableNullAwareOperators;
+
+  /**
+   * Return `true` to strictly follow the specification when generating
+   * warnings on "call" methods (fixes dartbug.com/21938).
+   */
+  bool get enableStrictCallChecks;
+
+  /**
+   * Return `true` if errors, warnings and hints should be generated for sources
+   * that are implicitly being analyzed. The default value is `true`.
+   */
+  bool get generateImplicitErrors;
+
+  /**
+   * Return `true` if errors, warnings and hints should be generated for sources
+   * in the SDK. The default value is `false`.
+   */
+  bool get generateSdkErrors;
+
+  /**
+   * Return `true` if analysis is to generate hint results (e.g. type inference
+   * based information and pub best practices).
+   */
+  bool get hint;
+
+  /**
+   * Return `true` if incremental analysis should be used.
+   */
+  bool get incremental;
+
+  /**
+   * A flag indicating whether incremental analysis should be used for API
+   * changes.
+   */
+  bool get incrementalApi;
+
+  /**
+   * A flag indicating whether validation should be performed after incremental
+   * analysis.
+   */
+  bool get incrementalValidation;
+
+  /**
+   * Return `true` if analysis is to generate lint warnings.
+   */
+  bool get lint;
+
+  /**
+   * Return `true` if analysis is to parse comments.
+   */
+  bool get preserveComments;
+}
+
+/**
+ * A set of analysis options used to control the behavior of an analysis
+ * context.
+ */
+class AnalysisOptionsImpl implements AnalysisOptions {
+  /**
+   * The maximum number of sources for which data should be kept in the cache.
+   */
+  static const int DEFAULT_CACHE_SIZE = 64;
+
+  /**
+   * The default value for enabling deferred loading.
+   */
+  @deprecated
+  static bool DEFAULT_ENABLE_DEFERRED_LOADING = true;
+
+  /**
+   * The default value for enabling enum support.
+   */
+  @deprecated
+  static bool DEFAULT_ENABLE_ENUM = false;
+
+  /**
+   * A predicate indicating whether analysis is to parse and analyze function
+   * bodies.
+   */
+  AnalyzeFunctionBodiesPredicate _analyzeFunctionBodiesPredicate =
+      _analyzeAllFunctionBodies;
+
+  /**
+   * The maximum number of sources for which AST structures should be kept in
+   * the cache.
+   */
+  int cacheSize = DEFAULT_CACHE_SIZE;
+
+  /**
+   * A flag indicating whether analysis is to generate dart2js related hint
+   * results.
+   */
+  bool dart2jsHint = true;
+
+  /**
+   * A flag indicating whether null-aware operators should be parsed (DEP 9).
+   */
+  bool enableNullAwareOperators = false;
+
+  /**
+   * A flag indicating whether analysis is to strictly follow the specification
+   * when generating warnings on "call" methods (fixes dartbug.com/21938).
+   */
+  bool enableStrictCallChecks = false;
+
+  /**
+   * A flag indicating whether errors, warnings and hints should be generated
+   * for sources that are implicitly being analyzed.
+   */
+  bool generateImplicitErrors = true;
+
+  /**
+   * A flag indicating whether errors, warnings and hints should be generated
+   * for sources in the SDK.
+   */
+  bool generateSdkErrors = false;
+
+  /**
+   * A flag indicating whether analysis is to generate hint results (e.g. type
+   * inference based information and pub best practices).
+   */
+  bool hint = true;
+
+  /**
+   * A flag indicating whether incremental analysis should be used.
+   */
+  bool incremental = false;
+
+  /**
+   * A flag indicating whether incremental analysis should be used for API
+   * changes.
+   */
+  bool incrementalApi = false;
+
+  /**
+   * A flag indicating whether validation should be performed after incremental
+   * analysis.
+   */
+  bool incrementalValidation = false;
+
+  /**
+   * A flag indicating whether analysis is to generate lint warnings.
+   */
+  bool lint = false;
+
+  /**
+   * A flag indicating whether analysis is to parse comments.
+   */
+  bool preserveComments = true;
+
+  /**
+   * Initialize a newly created set of analysis options to have their default
+   * values.
+   */
+  AnalysisOptionsImpl();
+
+  /**
+   * Initialize a newly created set of analysis options to have the same values
+   * as those in the given set of analysis [options].
+   */
+  AnalysisOptionsImpl.con1(AnalysisOptions options) {
+    analyzeFunctionBodiesPredicate = options.analyzeFunctionBodiesPredicate;
+    cacheSize = options.cacheSize;
+    dart2jsHint = options.dart2jsHint;
+    enableNullAwareOperators = options.enableNullAwareOperators;
+    enableStrictCallChecks = options.enableStrictCallChecks;
+    generateImplicitErrors = options.generateImplicitErrors;
+    generateSdkErrors = options.generateSdkErrors;
+    hint = options.hint;
+    incremental = options.incremental;
+    incrementalApi = options.incrementalApi;
+    incrementalValidation = options.incrementalValidation;
+    lint = options.lint;
+    preserveComments = options.preserveComments;
+  }
+
+  bool get analyzeFunctionBodies {
+    if (identical(analyzeFunctionBodiesPredicate, _analyzeAllFunctionBodies)) {
+      return true;
+    } else if (identical(
+        analyzeFunctionBodiesPredicate, _analyzeNoFunctionBodies)) {
+      return false;
+    } else {
+      throw new StateError('analyzeFunctionBodiesPredicate in use');
+    }
+  }
+
+  set analyzeFunctionBodies(bool value) {
+    if (value) {
+      analyzeFunctionBodiesPredicate = _analyzeAllFunctionBodies;
+    } else {
+      analyzeFunctionBodiesPredicate = _analyzeNoFunctionBodies;
+    }
+  }
+
+  @override
+  AnalyzeFunctionBodiesPredicate get analyzeFunctionBodiesPredicate =>
+      _analyzeFunctionBodiesPredicate;
+
+  set analyzeFunctionBodiesPredicate(AnalyzeFunctionBodiesPredicate value) {
+    if (value == null) {
+      throw new ArgumentError.notNull('analyzeFunctionBodiesPredicate');
+    }
+    _analyzeFunctionBodiesPredicate = value;
+  }
+
+  @deprecated
+  @override
+  bool get enableAsync => true;
+
+  @deprecated
+  void set enableAsync(bool enable) {
+    // Async support cannot be disabled
+  }
+
+  @deprecated
+  @override
+  bool get enableDeferredLoading => true;
+
+  @deprecated
+  void set enableDeferredLoading(bool enable) {
+    // Deferred loading support cannot be disabled
+  }
+
+  @deprecated
+  @override
+  bool get enableEnum => true;
+
+  @deprecated
+  void set enableEnum(bool enable) {
+    // Enum support cannot be disabled
+  }
+
+  /**
+   * Predicate used for [analyzeFunctionBodiesPredicate] when
+   * [analyzeFunctionBodies] is set to `true`.
+   */
+  static bool _analyzeAllFunctionBodies(Source _) => true;
+
+  /**
+   * Predicate used for [analyzeFunctionBodiesPredicate] when
+   * [analyzeFunctionBodies] is set to `false`.
+   */
+  static bool _analyzeNoFunctionBodies(Source _) => false;
+}
+
+/**
+ *
+ */
+class AnalysisResult {
+  /**
+   * The change notices associated with this result, or `null` if there were no
+   * changes and there is no more work to be done.
+   */
+  final List<ChangeNotice> _notices;
+
+  /**
+   * The number of milliseconds required to determine which task was to be
+   * performed.
+   */
+  final int getTime;
+
+  /**
+   * The name of the class of the task that was performed.
+   */
+  final String taskClassName;
+
+  /**
+   * The number of milliseconds required to perform the task.
+   */
+  final int performTime;
+
+  /**
+   * Initialize a newly created analysis result to have the given values. The
+   * [notices] is the change notices associated with this result. The [getTime]
+   * is the number of milliseconds required to determine which task was to be
+   * performed. The [taskClassName] is the name of the class of the task that
+   * was performed. The [performTime] is the number of milliseconds required to
+   * perform the task.
+   */
+  AnalysisResult(
+      this._notices, this.getTime, this.taskClassName, this.performTime);
+
+  /**
+   * Return the change notices associated with this result, or `null` if there
+   * were no changes and there is no more work to be done.
+   */
+  List<ChangeNotice> get changeNotices => _notices;
+
+  /**
+   * Return `true` if there is more to be performed after the task that was
+   * performed.
+   */
+  bool get hasMoreWork => _notices != null;
+}
+
+/**
+ * An analysis task.
+ */
+abstract class AnalysisTask {
+  /**
+   * The context in which the task is to be performed.
+   */
+  final InternalAnalysisContext context;
+
+  /**
+   * The exception that was thrown while performing this task, or `null` if the
+   * task completed successfully.
+   */
+  CaughtException _thrownException;
+
+  /**
+   * Initialize a newly created task to perform analysis within the given
+   * [context].
+   */
+  AnalysisTask(this.context);
+
+  /**
+   * Return the exception that was thrown while performing this task, or `null`
+   * if the task completed successfully.
+   */
+  CaughtException get exception => _thrownException;
+
+  /**
+   * Return a textual description of this task.
+   */
+  String get taskDescription;
+
+  /**
+   * Use the given [visitor] to visit this task. Throws an [AnalysisException]
+   * if the visitor throws the exception.
+   */
+  accept(AnalysisTaskVisitor visitor);
+
+  /**
+   * Perform this analysis task, protected by an exception handler. Throws an
+   * [AnalysisException] if an exception occurs while performing the task.
+   */
+  void internalPerform();
+
+  /**
+   * Perform this analysis task and use the given [visitor] to visit this task
+   * after it has completed. Throws an [AnalysisException] if the visitor throws
+   * the exception.
+   */
+  Object perform(AnalysisTaskVisitor visitor) {
+    try {
+      _safelyPerform();
+    } on AnalysisException catch (exception, stackTrace) {
+      _thrownException = new CaughtException(exception, stackTrace);
+      AnalysisEngine.instance.logger.logInformation(
+          "Task failed: $taskDescription",
+          new CaughtException(exception, stackTrace));
+    }
+    return PerformanceStatistics.analysisTaskVisitor
+        .makeCurrentWhile(() => accept(visitor));
+  }
+
+  @override
+  String toString() => taskDescription;
+
+  /**
+   * Perform this analysis task, ensuring that all exceptions are wrapped in an
+   * [AnalysisException]. Throws an [AnalysisException] if any exception occurs
+   * while performing the task
+   */
+  void _safelyPerform() {
+    try {
+      String contextName = context.name;
+      if (contextName == null) {
+        contextName = 'unnamed';
+      }
+      AnalysisEngine.instance.instrumentationService.logAnalysisTask(
+          contextName, taskDescription);
+      internalPerform();
+    } on AnalysisException {
+      rethrow;
+    } catch (exception, stackTrace) {
+      throw new AnalysisException(
+          exception.toString(), new CaughtException(exception, stackTrace));
+    }
+  }
+}
+
+/**
+ * An object used to visit tasks. While tasks are not structured in any
+ * interesting way, this class provides the ability to dispatch to an
+ * appropriate method.
+ */
+abstract class AnalysisTaskVisitor<E> {
+  /**
+   * Visit the given [task], returning the result of the visit. This method will
+   * throw an AnalysisException if the visitor throws an exception.
+   */
+  E visitBuildUnitElementTask(BuildUnitElementTask task);
+
+  /**
+   * Visit the given [task], returning the result of the visit. This method will
+   * throw an AnalysisException if the visitor throws an exception.
+   */
+  E visitGenerateDartErrorsTask(GenerateDartErrorsTask task);
+
+  /**
+   * Visit the given [task], returning the result of the visit. This method will
+   * throw an AnalysisException if the visitor throws an exception.
+   */
+  E visitGenerateDartHintsTask(GenerateDartHintsTask task);
+
+  /**
+   * Visit the given [task], returning the result of the visit. This method will
+   * throw an AnalysisException if the visitor throws an exception.
+   */
+  E visitGenerateDartLintsTask(GenerateDartLintsTask task);
+
+  /**
+   * Visit the given [task], returning the result of the visit. This method will
+   * throw an AnalysisException if the visitor throws an exception.
+   */
+  E visitGetContentTask(GetContentTask task);
+
+  /**
+   * Visit the given [task], returning the result of the visit. This method will
+   * throw an AnalysisException if the visitor throws an exception.
+   */
+  E visitIncrementalAnalysisTask(
+      IncrementalAnalysisTask incrementalAnalysisTask);
+
+  /**
+   * Visit the given [task], returning the result of the visit. This method will
+   * throw an AnalysisException if the visitor throws an exception.
+   */
+  E visitParseDartTask(ParseDartTask task);
+
+  /**
+   * Visit the given [task], returning the result of the visit. This method will
+   * throw an AnalysisException if the visitor throws an exception.
+   */
+  E visitParseHtmlTask(ParseHtmlTask task);
+
+  /**
+   * Visit the given [task], returning the result of the visit. This method will
+   * throw an AnalysisException if the visitor throws an exception.
+   */
+  E visitResolveDartLibraryCycleTask(ResolveDartLibraryCycleTask task);
+
+  /**
+   * Visit the given [task], returning the result of the visit. This method will
+   * throw an AnalysisException if the visitor throws an exception.
+   */
+  E visitResolveDartLibraryTask(ResolveDartLibraryTask task);
+
+  /**
+   * Visit the given [task], returning the result of the visit. This method will
+   * throw an AnalysisException if the visitor throws an exception.
+   */
+  E visitResolveDartUnitTask(ResolveDartUnitTask task);
+
+  /**
+   * Visit the given [task], returning the result of the visit. This method will
+   * throw an AnalysisException if the visitor throws an exception.
+   */
+  E visitResolveHtmlTask(ResolveHtmlTask task);
+
+  /**
+   * Visit the given [task], returning the result of the visit. This method will
+   * throw an AnalysisException if the visitor throws an exception.
+   */
+  E visitScanDartTask(ScanDartTask task);
+}
+
+/**
+ * A `CachedResult` is a single analysis result that is stored in a
+ * [SourceEntry].
+ */
+class CachedResult<E> {
+  /**
+   * The state of the cached value.
+   */
+  CacheState state;
+
+  /**
+   * The value being cached, or `null` if there is no value (for example, when
+   * the [state] is [CacheState.INVALID].
+   */
+  E value;
+
+  /**
+   * Initialize a newly created result holder to represent the value of data
+   * described by the given [descriptor].
+   */
+  CachedResult(DataDescriptor descriptor) {
+    state = CacheState.INVALID;
+    value = descriptor.defaultValue;
+  }
+}
+
+/**
+ * A single partition in an LRU cache of information related to analysis.
+ */
+abstract class CachePartition {
+  /**
+   * The context that owns this partition. Multiple contexts can reference a
+   * partition, but only one context can own it.
+   */
+  final InternalAnalysisContext context;
+
+  /**
+   * The maximum number of sources for which AST structures should be kept in
+   * the cache.
+   */
+  int _maxCacheSize = 0;
+
+  /**
+   * The policy used to determine which pieces of data to remove from the cache.
+   */
+  final CacheRetentionPolicy _retentionPolicy;
+
+  /**
+   * A table mapping the sources belonging to this partition to the information
+   * known about those sources.
+   */
+  HashMap<Source, SourceEntry> _sourceMap = new HashMap<Source, SourceEntry>();
+
+  /**
+   * A list containing the most recently accessed sources with the most recently
+   * used at the end of the list. When more sources are added than the maximum
+   * allowed then the least recently used source will be removed and will have
+   * it's cached AST structure flushed.
+   */
+  List<Source> _recentlyUsed;
+
+  /**
+   * Initialize a newly created cache to maintain at most [maxCacheSize] AST
+   * structures in the cache. The cache is owned by the give [context], and the
+   * [retentionPolicy] will be used to determine which pieces of data to remove
+   * from the cache.
+   */
+  CachePartition(this.context, int maxCacheSize, this._retentionPolicy) {
+    this._maxCacheSize = maxCacheSize;
+    _recentlyUsed = new List<Source>();
+  }
+
+  /**
+   * Return the number of entries in this partition that have an AST associated
+   * with them.
+   */
+  int get astSize {
+    int astSize = 0;
+    int count = _recentlyUsed.length;
+    for (int i = 0; i < count; i++) {
+      Source source = _recentlyUsed[i];
+      SourceEntry sourceEntry = _sourceMap[source];
+      if (sourceEntry is DartEntry) {
+        if (sourceEntry.anyParsedCompilationUnit != null) {
+          astSize++;
+        }
+      } else if (sourceEntry is HtmlEntry) {
+        if (sourceEntry.anyParsedUnit != null) {
+          astSize++;
+        }
+      }
+    }
+    return astSize;
+  }
+
+  /**
+   * Return a table mapping the sources known to the context to the information
+   * known about the source.
+   *
+   * <b>Note:</b> This method is only visible for use by [AnalysisCache] and
+   * should not be used for any other purpose.
+   */
+  Map<Source, SourceEntry> get map => _sourceMap;
+
+  /**
+   * Set the maximum size of the cache to the given [size].
+   */
+  void set maxCacheSize(int size) {
+    _maxCacheSize = size;
+    while (_recentlyUsed.length > _maxCacheSize) {
+      if (!_flushAstFromCache()) {
+        break;
+      }
+    }
+  }
+
+  /**
+   * Record that the AST associated with the given source was just read from the
+   * cache.
+   */
+  void accessedAst(Source source) {
+    if (_recentlyUsed.remove(source)) {
+      _recentlyUsed.add(source);
+      return;
+    }
+    while (_recentlyUsed.length >= _maxCacheSize) {
+      if (!_flushAstFromCache()) {
+        break;
+      }
+    }
+    _recentlyUsed.add(source);
+  }
+
+  /**
+   * Return `true` if the given [source] is contained in this partition.
+   */
+  bool contains(Source source);
+
+  /**
+   * Return the entry associated with the given [source].
+   */
+  SourceEntry get(Source source) => _sourceMap[source];
+
+  /**
+   * Return an iterator returning all of the map entries mapping sources to
+   * cache entries.
+   */
+  MapIterator<Source, SourceEntry> iterator() =>
+      new SingleMapIterator<Source, SourceEntry>(_sourceMap);
+
+  /**
+   * Associate the given [entry] with the given [source].
+   */
+  void put(Source source, SourceEntry entry) {
+    entry.fixExceptionState();
+    _sourceMap[source] = entry;
+  }
+
+  /**
+   * Remove all information related to the given [source] from this cache.
+   */
+  void remove(Source source) {
+    _recentlyUsed.remove(source);
+    _sourceMap.remove(source);
+  }
+
+  /**
+   * Record that the AST associated with the given [source] was just removed
+   * from the cache.
+   */
+  void removedAst(Source source) {
+    _recentlyUsed.remove(source);
+  }
+
+  /**
+   * Return the number of sources that are mapped to cache entries.
+   */
+  int size() => _sourceMap.length;
+
+  /**
+   * Record that the AST associated with the given [source] was just stored to
+   * the cache.
+   */
+  void storedAst(Source source) {
+    if (_recentlyUsed.contains(source)) {
+      return;
+    }
+    while (_recentlyUsed.length >= _maxCacheSize) {
+      if (!_flushAstFromCache()) {
+        break;
+      }
+    }
+    _recentlyUsed.add(source);
+  }
+
+  /**
+   * Attempt to flush one AST structure from the cache. Return `true` if a
+   * structure was flushed.
+   */
+  bool _flushAstFromCache() {
+    Source removedSource = _removeAstToFlush();
+    if (removedSource == null) {
+      return false;
+    }
+    SourceEntry sourceEntry = _sourceMap[removedSource];
+    if (sourceEntry is HtmlEntry) {
+      HtmlEntry htmlEntry = sourceEntry;
+      htmlEntry.flushAstStructures();
+    } else if (sourceEntry is DartEntry) {
+      DartEntry dartEntry = sourceEntry;
+      dartEntry.flushAstStructures();
+    }
+    return true;
+  }
+
+  /**
+   * Remove and return one source from the list of recently used sources whose
+   * AST structure can be flushed from the cache. The source that will be
+   * returned will be the source that has been unreferenced for the longest
+   * period of time but that is not a priority for analysis.
+   */
+  Source _removeAstToFlush() {
+    int sourceToRemove = -1;
+    for (int i = 0; i < _recentlyUsed.length; i++) {
+      Source source = _recentlyUsed[i];
+      RetentionPriority priority =
+          _retentionPolicy.getAstPriority(source, _sourceMap[source]);
+      if (priority == RetentionPriority.LOW) {
+        return _recentlyUsed.removeAt(i);
+      } else if (priority == RetentionPriority.MEDIUM && sourceToRemove < 0) {
+        sourceToRemove = i;
+      }
+    }
+    if (sourceToRemove < 0) {
+      // This happens if the retention policy returns a priority of HIGH for all
+      // of the sources that have been recently used. This is the case, for
+      // example, when the list of priority sources is bigger than the current
+      // cache size.
+      return null;
+    }
+    return _recentlyUsed.removeAt(sourceToRemove);
+  }
+}
+
+/**
+ * An object used to determine how important it is for data to be retained in
+ * the analysis cache.
+ */
+abstract class CacheRetentionPolicy {
+  /**
+   * Return the priority of retaining the AST structure for the given [source].
+   */
+  RetentionPriority getAstPriority(Source source, SourceEntry sourceEntry);
+}
+
+/**
+ * The possible states of cached data.
+ */
+class CacheState extends Enum<CacheState> {
+  /**
+   * The data is not in the cache and the last time an attempt was made to
+   * compute the data an exception occurred, making it pointless to attempt to
+   * compute the data again.
+   *
+   * Valid Transitions:
+   * * [INVALID] if a source was modified that might cause the data to be
+   *   computable
+   */
+  static const CacheState ERROR = const CacheState('ERROR', 0);
+
+  /**
+   * The data is not in the cache because it was flushed from the cache in order
+   * to control memory usage. If the data is recomputed, results do not need to
+   * be reported.
+   *
+   * Valid Transitions:
+   * * [IN_PROCESS] if the data is being recomputed
+   * * [INVALID] if a source was modified that causes the data to need to be
+   *   recomputed
+   */
+  static const CacheState FLUSHED = const CacheState('FLUSHED', 1);
+
+  /**
+   * The data might or might not be in the cache but is in the process of being
+   * recomputed.
+   *
+   * Valid Transitions:
+   * * [ERROR] if an exception occurred while trying to compute the data
+   * * [VALID] if the data was successfully computed and stored in the cache
+   */
+  static const CacheState IN_PROCESS = const CacheState('IN_PROCESS', 2);
+
+  /**
+   * The data is not in the cache and needs to be recomputed so that results can
+   * be reported.
+   *
+   * Valid Transitions:
+   * * [IN_PROCESS] if an attempt is being made to recompute the data
+   */
+  static const CacheState INVALID = const CacheState('INVALID', 3);
+
+  /**
+   * The data is in the cache and up-to-date.
+   *
+   * Valid Transitions:
+   * * [FLUSHED] if the data is removed in order to manage memory usage
+   * * [INVALID] if a source was modified in such a way as to invalidate the
+   *   previous data
+   */
+  static const CacheState VALID = const CacheState('VALID', 4);
+
+  static const List<CacheState> values = const [
+    ERROR,
+    FLUSHED,
+    IN_PROCESS,
+    INVALID,
+    VALID
+  ];
+
+  const CacheState(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * An object that represents a change to the analysis results associated with a
+ * given source.
+ */
+abstract class ChangeNotice implements AnalysisErrorInfo {
+  /**
+   * The parsed, but maybe not resolved Dart AST that changed as a result of
+   * the analysis, or `null` if the AST was not changed.
+   */
+  CompilationUnit get parsedDartUnit;
+
+  /**
+   * The fully resolved Dart AST that changed as a result of the analysis, or
+   * `null` if the AST was not changed.
+   */
+  CompilationUnit get resolvedDartUnit;
+
+  /**
+   * The fully resolved HTML AST that changed as a result of the analysis, or
+   * `null` if the AST was not changed.
+   */
+  ht.HtmlUnit get resolvedHtmlUnit;
+
+  /**
+   * Return the source for which the result is being reported.
+   */
+  Source get source;
+}
+
+/**
+ * An implementation of a [ChangeNotice].
+ */
+class ChangeNoticeImpl implements ChangeNotice {
+  /**
+   * An empty list of change notices.
+   */
+  static const List<ChangeNoticeImpl> EMPTY_ARRAY = const <ChangeNoticeImpl>[];
+
+  /**
+   * The source for which the result is being reported.
+   */
+  final Source source;
+
+  /**
+   * The parsed, but maybe not resolved Dart AST that changed as a result of
+   * the analysis, or `null` if the AST was not changed.
+   */
+  CompilationUnit parsedDartUnit;
+
+  /**
+   * The fully resolved Dart AST that changed as a result of the analysis, or
+   * `null` if the AST was not changed.
+   */
+  CompilationUnit resolvedDartUnit;
+
+  /**
+   * The fully resolved HTML AST that changed as a result of the analysis, or
+   * `null` if the AST was not changed.
+   */
+  ht.HtmlUnit resolvedHtmlUnit;
+
+  /**
+   * The errors that changed as a result of the analysis, or `null` if errors
+   * were not changed.
+   */
+  List<AnalysisError> _errors;
+
+  /**
+   * The line information associated with the source, or `null` if errors were
+   * not changed.
+   */
+  LineInfo _lineInfo;
+
+  /**
+   * Initialize a newly created notice associated with the given source.
+   *
+   * @param source the source for which the change is being reported
+   */
+  ChangeNoticeImpl(this.source);
+
+  @override
+  List<AnalysisError> get errors => _errors;
+
+  @override
+  LineInfo get lineInfo => _lineInfo;
+
+  /**
+   * Set the errors that changed as a result of the analysis to the given
+   * [errors] and set the line information to the given [lineInfo].
+   */
+  void setErrors(List<AnalysisError> errors, LineInfo lineInfo) {
+    this._errors = errors;
+    this._lineInfo = lineInfo;
+    if (lineInfo == null) {
+      AnalysisEngine.instance.logger.logInformation("No line info: $source",
+          new CaughtException(new AnalysisException(), null));
+    }
+  }
+
+  @override
+  String toString() => "Changes for ${source.fullName}";
+}
+
+/**
+ * An indication of which sources have been added, changed, removed, or deleted.
+ * In the case of a changed source, there are multiple ways of indicating the
+ * nature of the change.
+ *
+ * No source should be added to the change set more than once, either with the
+ * same or a different kind of change. It does not make sense, for example, for
+ * a source to be both added and removed, and it is redundant for a source to be
+ * marked as changed in its entirety and changed in some specific range.
+ */
+class ChangeSet {
+  /**
+   * A list containing the sources that have been added.
+   */
+  final List<Source> addedSources = new List<Source>();
+
+  /**
+   * A list containing the sources that have been changed.
+   */
+  final List<Source> changedSources = new List<Source>();
+
+  /**
+   * A table mapping the sources whose content has been changed to the current
+   * content of those sources.
+   */
+  HashMap<Source, String> _changedContent = new HashMap<Source, String>();
+
+  /**
+   * A table mapping the sources whose content has been changed within a single
+   * range to the current content of those sources and information about the
+   * affected range.
+   */
+  final HashMap<Source, ChangeSet_ContentChange> changedRanges =
+      new HashMap<Source, ChangeSet_ContentChange>();
+
+  /**
+   * A list containing the sources that have been removed.
+   */
+  final List<Source> removedSources = new List<Source>();
+
+  /**
+   * A list containing the source containers specifying additional sources that
+   * have been removed.
+   */
+  final List<SourceContainer> removedContainers = new List<SourceContainer>();
+
+  /**
+   * A list containing the sources that have been deleted.
+   */
+  final List<Source> deletedSources = new List<Source>();
+
+  /**
+   * Return a table mapping the sources whose content has been changed to the
+   * current content of those sources.
+   */
+  Map<Source, String> get changedContents => _changedContent;
+
+  /**
+   * Return `true` if this change set does not contain any changes.
+   */
+  bool get isEmpty => addedSources.isEmpty &&
+      changedSources.isEmpty &&
+      _changedContent.isEmpty &&
+      changedRanges.isEmpty &&
+      removedSources.isEmpty &&
+      removedContainers.isEmpty &&
+      deletedSources.isEmpty;
+
+  /**
+   * Record that the specified [source] has been added and that its content is
+   * the default contents of the source.
+   */
+  void addedSource(Source source) {
+    addedSources.add(source);
+  }
+
+  /**
+   * Record that the specified [source] has been changed and that its content is
+   * the given [contents].
+   */
+  void changedContent(Source source, String contents) {
+    _changedContent[source] = contents;
+  }
+
+  /**
+   * Record that the specified [source] has been changed and that its content is
+   * the given [contents]. The [offset] is the offset into the current contents.
+   * The [oldLength] is the number of characters in the original contents that
+   * were replaced. The [newLength] is the number of characters in the
+   * replacement text.
+   */
+  void changedRange(Source source, String contents, int offset, int oldLength,
+      int newLength) {
+    changedRanges[source] =
+        new ChangeSet_ContentChange(contents, offset, oldLength, newLength);
+  }
+
+  /**
+   * Record that the specified [source] has been changed. If the content of the
+   * source was previously overridden, this has no effect (the content remains
+   * overridden). To cancel (or change) the override, use [changedContent]
+   * instead.
+   */
+  void changedSource(Source source) {
+    changedSources.add(source);
+  }
+
+  /**
+   * Record that the specified [source] has been deleted.
+   */
+  void deletedSource(Source source) {
+    deletedSources.add(source);
+  }
+
+  /**
+   * Record that the specified source [container] has been removed.
+   */
+  void removedContainer(SourceContainer container) {
+    if (container != null) {
+      removedContainers.add(container);
+    }
+  }
+
+  /**
+   * Record that the specified [source] has been removed.
+   */
+  void removedSource(Source source) {
+    if (source != null) {
+      removedSources.add(source);
+    }
+  }
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    bool needsSeparator =
+        _appendSources(buffer, addedSources, false, "addedSources");
+    needsSeparator = _appendSources(
+        buffer, changedSources, needsSeparator, "changedSources");
+    needsSeparator = _appendSources2(
+        buffer, _changedContent, needsSeparator, "changedContent");
+    needsSeparator =
+        _appendSources2(buffer, changedRanges, needsSeparator, "changedRanges");
+    needsSeparator = _appendSources(
+        buffer, deletedSources, needsSeparator, "deletedSources");
+    needsSeparator = _appendSources(
+        buffer, removedSources, needsSeparator, "removedSources");
+    int count = removedContainers.length;
+    if (count > 0) {
+      if (removedSources.isEmpty) {
+        if (needsSeparator) {
+          buffer.write("; ");
+        }
+        buffer.write("removed: from ");
+        buffer.write(count);
+        buffer.write(" containers");
+      } else {
+        buffer.write(", and more from ");
+        buffer.write(count);
+        buffer.write(" containers");
+      }
+    }
+    return buffer.toString();
+  }
+
+  /**
+   * Append the given [sources] to the given [buffer], prefixed with the given
+   * [label] and a separator if [needsSeparator] is `true`. Return `true` if
+   * future lists of sources will need a separator.
+   */
+  bool _appendSources(StringBuffer buffer, List<Source> sources,
+      bool needsSeparator, String label) {
+    if (sources.isEmpty) {
+      return needsSeparator;
+    }
+    if (needsSeparator) {
+      buffer.write("; ");
+    }
+    buffer.write(label);
+    String prefix = " ";
+    for (Source source in sources) {
+      buffer.write(prefix);
+      buffer.write(source.fullName);
+      prefix = ", ";
+    }
+    return true;
+  }
+
+  /**
+   * Append the given [sources] to the given [builder], prefixed with the given
+   * [label] and a separator if [needsSeparator] is `true`. Return `true` if
+   * future lists of sources will need a separator.
+   */
+  bool _appendSources2(StringBuffer buffer, HashMap<Source, dynamic> sources,
+      bool needsSeparator, String label) {
+    if (sources.isEmpty) {
+      return needsSeparator;
+    }
+    if (needsSeparator) {
+      buffer.write("; ");
+    }
+    buffer.write(label);
+    String prefix = " ";
+    for (Source source in sources.keys.toSet()) {
+      buffer.write(prefix);
+      buffer.write(source.fullName);
+      prefix = ", ";
+    }
+    return true;
+  }
+}
+
+/**
+ * A change to the content of a source.
+ */
+class ChangeSet_ContentChange {
+  /**
+   * The new contents of the source.
+   */
+  final String contents;
+
+  /**
+   * The offset into the current contents.
+   */
+  final int offset;
+
+  /**
+   * The number of characters in the original contents that were replaced
+   */
+  final int oldLength;
+
+  /**
+   * The number of characters in the replacement text.
+   */
+  final int newLength;
+
+  /**
+   * Initialize a newly created change object to represent a change to the
+   * content of a source. The [contents] is the new contents of the source. The
+   * [offse] ist the offset into the current contents. The [oldLength] is the
+   * number of characters in the original contents that were replaced. The
+   * [newLength] is the number of characters in the replacement text.
+   */
+  ChangeSet_ContentChange(
+      this.contents, this.offset, this.oldLength, this.newLength);
+}
+
+/**
+ * A pair containing a library and a list of the (source, entry) pairs for
+ * compilation units in the library.
+ */
+class CycleBuilder_LibraryPair {
+  /**
+   * The library containing the compilation units.
+   */
+  ResolvableLibrary library;
+
+  /**
+   * The (source, entry) pairs representing the compilation units in the
+   * library.
+   */
+  List<CycleBuilder_SourceEntryPair> entryPairs;
+
+  /**
+   * Initialize a newly created pair from the given [library] and [entryPairs].
+   */
+  CycleBuilder_LibraryPair(ResolvableLibrary library,
+      List<CycleBuilder_SourceEntryPair> entryPairs) {
+    this.library = library;
+    this.entryPairs = entryPairs;
+  }
+}
+
+/**
+ * A pair containing a source and the cache entry associated with that source.
+ * They are used to reduce the number of times an entry must be looked up in the
+ * cache.
+ */
+class CycleBuilder_SourceEntryPair {
+  /**
+   * The source associated with the entry.
+   */
+  Source source;
+
+  /**
+   * The entry associated with the source.
+   */
+  DartEntry entry;
+
+  /**
+   * Initialize a newly created pair from the given [source] and [entry].
+   */
+  CycleBuilder_SourceEntryPair(Source source, DartEntry entry) {
+    this.source = source;
+    this.entry = entry;
+  }
+}
+
+/**
+ * The information cached by an analysis context about an individual Dart file.
+ */
+class DartEntry extends SourceEntry {
+  /**
+   * The data descriptor representing the element model representing a single
+   * compilation unit. This model is incomplete and should not be used except as
+   * input to another task.
+   */
+  static final DataDescriptor<List<AnalysisError>> BUILT_ELEMENT =
+      new DataDescriptor<List<AnalysisError>>("DartEntry.BUILT_ELEMENT");
+
+  /**
+   * The data descriptor representing the AST structure after the element model
+   * has been built (and declarations are resolved) but before other resolution
+   * has been performed.
+   */
+  static final DataDescriptor<CompilationUnit> BUILT_UNIT =
+      new DataDescriptor<CompilationUnit>("DartEntry.BUILT_UNIT");
+
+  /**
+   * The data descriptor representing the list of libraries that contain this
+   * compilation unit.
+   */
+  static final DataDescriptor<List<Source>> CONTAINING_LIBRARIES =
+      new DataDescriptor<List<Source>>(
+          "DartEntry.CONTAINING_LIBRARIES", Source.EMPTY_ARRAY);
+
+  /**
+   * The data descriptor representing the library element for the library. This
+   * data is only available for Dart files that are the defining compilation
+   * unit of a library.
+   */
+  static final DataDescriptor<LibraryElement> ELEMENT =
+      new DataDescriptor<LibraryElement>("DartEntry.ELEMENT");
+
+  /**
+   * The data descriptor representing the list of exported libraries. This data
+   * is only available for Dart files that are the defining compilation unit of
+   * a library.
+   */
+  static final DataDescriptor<List<Source>> EXPORTED_LIBRARIES =
+      new DataDescriptor<List<Source>>(
+          "DartEntry.EXPORTED_LIBRARIES", Source.EMPTY_ARRAY);
+
+  /**
+   * The data descriptor representing the hints resulting from auditing the
+   * source.
+   */
+  static final DataDescriptor<List<AnalysisError>> HINTS =
+      new DataDescriptor<List<AnalysisError>>(
+          "DartEntry.HINTS", AnalysisError.NO_ERRORS);
+
+  /**
+   * The data descriptor representing the list of imported libraries. This data
+   * is only available for Dart files that are the defining compilation unit of
+   * a library.
+   */
+  static final DataDescriptor<List<Source>> IMPORTED_LIBRARIES =
+      new DataDescriptor<List<Source>>(
+          "DartEntry.IMPORTED_LIBRARIES", Source.EMPTY_ARRAY);
+
+  /**
+   * The data descriptor representing the list of included parts. This data is
+   * only available for Dart files that are the defining compilation unit of a
+   * library.
+   */
+  static final DataDescriptor<List<Source>> INCLUDED_PARTS =
+      new DataDescriptor<List<Source>>(
+          "DartEntry.INCLUDED_PARTS", Source.EMPTY_ARRAY);
+
+  /**
+   * The data descriptor representing the client flag. This data is only
+   * available for Dart files that are the defining compilation unit of a
+   * library.
+   */
+  static final DataDescriptor<bool> IS_CLIENT =
+      new DataDescriptor<bool>("DartEntry.IS_CLIENT", false);
+
+  /**
+   * The data descriptor representing the launchable flag. This data is only
+   * available for Dart files that are the defining compilation unit of a
+   * library.
+   */
+  static final DataDescriptor<bool> IS_LAUNCHABLE =
+      new DataDescriptor<bool>("DartEntry.IS_LAUNCHABLE", false);
+
+  /**
+   * The data descriptor representing lint warnings resulting from auditing the
+   * source.
+   */
+  static final DataDescriptor<List<AnalysisError>> LINTS =
+      new DataDescriptor<List<AnalysisError>>(
+          "DartEntry.LINTS", AnalysisError.NO_ERRORS);
+
+  /**
+   * The data descriptor representing the errors resulting from parsing the
+   * source.
+   */
+  static final DataDescriptor<List<AnalysisError>> PARSE_ERRORS =
+      new DataDescriptor<List<AnalysisError>>(
+          "DartEntry.PARSE_ERRORS", AnalysisError.NO_ERRORS);
+
+  /**
+   * The data descriptor representing the parsed AST structure.
+   */
+  static final DataDescriptor<CompilationUnit> PARSED_UNIT =
+      new DataDescriptor<CompilationUnit>("DartEntry.PARSED_UNIT");
+
+  /**
+   * The data descriptor representing the public namespace of the library. This
+   * data is only available for Dart files that are the defining compilation
+   * unit of a library.
+   */
+  static final DataDescriptor<Namespace> PUBLIC_NAMESPACE =
+      new DataDescriptor<Namespace>("DartEntry.PUBLIC_NAMESPACE");
+
+  /**
+   * The data descriptor representing the errors resulting from resolving the
+   * source.
+   */
+  static final DataDescriptor<List<AnalysisError>> RESOLUTION_ERRORS =
+      new DataDescriptor<List<AnalysisError>>(
+          "DartEntry.RESOLUTION_ERRORS", AnalysisError.NO_ERRORS);
+
+  /**
+   * The data descriptor representing the resolved AST structure.
+   */
+  static final DataDescriptor<CompilationUnit> RESOLVED_UNIT =
+      new DataDescriptor<CompilationUnit>("DartEntry.RESOLVED_UNIT");
+
+  /**
+   * The data descriptor representing the errors resulting from scanning the
+   * source.
+   */
+  static final DataDescriptor<List<AnalysisError>> SCAN_ERRORS =
+      new DataDescriptor<List<AnalysisError>>(
+          "DartEntry.SCAN_ERRORS", AnalysisError.NO_ERRORS);
+
+  /**
+   * The data descriptor representing the source kind.
+   */
+  static final DataDescriptor<SourceKind> SOURCE_KIND =
+      new DataDescriptor<SourceKind>(
+          "DartEntry.SOURCE_KIND", SourceKind.UNKNOWN);
+
+  /**
+   * The data descriptor representing the token stream.
+   */
+  static final DataDescriptor<Token> TOKEN_STREAM =
+      new DataDescriptor<Token>("DartEntry.TOKEN_STREAM");
+
+  /**
+   * The data descriptor representing the errors resulting from verifying the
+   * source.
+   */
+  static final DataDescriptor<List<AnalysisError>> VERIFICATION_ERRORS =
+      new DataDescriptor<List<AnalysisError>>(
+          "DartEntry.VERIFICATION_ERRORS", AnalysisError.NO_ERRORS);
+
+  /**
+   * The list of libraries that contain this compilation unit. The list will be
+   * empty if there are no known libraries that contain this compilation unit.
+   */
+  List<Source> _containingLibraries = new List<Source>();
+
+  /**
+   * The information known as a result of resolving this compilation unit as
+   * part of the library that contains this unit. This field will never be
+   * `null`.
+   */
+  ResolutionState _resolutionState = new ResolutionState();
+
+  /**
+   * Return all of the errors associated with the compilation unit that are
+   * currently cached.
+   */
+  List<AnalysisError> get allErrors {
+    List<AnalysisError> errors = new List<AnalysisError>();
+    errors.addAll(super.allErrors);
+    errors.addAll(getValue(SCAN_ERRORS));
+    errors.addAll(getValue(PARSE_ERRORS));
+    ResolutionState state = _resolutionState;
+    while (state != null) {
+      errors.addAll(state.getValue(RESOLUTION_ERRORS));
+      errors.addAll(state.getValue(VERIFICATION_ERRORS));
+      errors.addAll(state.getValue(HINTS));
+      errors.addAll(state.getValue(LINTS));
+      state = state._nextState;
+    }
+    if (errors.length == 0) {
+      return AnalysisError.NO_ERRORS;
+    }
+    return errors;
+  }
+
+  /**
+   * Return a valid parsed compilation unit, either an unresolved AST structure
+   * or the result of resolving the AST structure in the context of some
+   * library, or `null` if there is no parsed compilation unit available.
+   */
+  CompilationUnit get anyParsedCompilationUnit {
+    if (getState(PARSED_UNIT) == CacheState.VALID) {
+      return getValue(PARSED_UNIT);
+    }
+    ResolutionState state = _resolutionState;
+    while (state != null) {
+      if (state.getState(BUILT_UNIT) == CacheState.VALID) {
+        return state.getValue(BUILT_UNIT);
+      }
+      state = state._nextState;
+    }
+
+    return anyResolvedCompilationUnit;
+  }
+
+  /**
+   * Return the result of resolving the compilation unit as part of any library,
+   * or `null` if there is no cached resolved compilation unit.
+   */
+  CompilationUnit get anyResolvedCompilationUnit {
+    ResolutionState state = _resolutionState;
+    while (state != null) {
+      if (state.getState(RESOLVED_UNIT) == CacheState.VALID) {
+        return state.getValue(RESOLVED_UNIT);
+      }
+      state = state._nextState;
+    }
+    return null;
+  }
+
+  /**
+   * The libraries that are known to contain this part.
+   */
+  List<Source> get containingLibraries => _containingLibraries;
+
+  /**
+   * Set the list of libraries that contain this compilation unit to contain
+   * only the given [librarySource]. This method should only be invoked on
+   * entries that represent a library.
+   */
+  void set containingLibrary(Source librarySource) {
+    _containingLibraries.clear();
+    _containingLibraries.add(librarySource);
+  }
+
+  @override
+  List<DataDescriptor> get descriptors {
+    List<DataDescriptor> result = super.descriptors;
+    result.addAll(<DataDescriptor>[
+      DartEntry.SOURCE_KIND,
+      DartEntry.CONTAINING_LIBRARIES,
+      DartEntry.PARSE_ERRORS,
+      DartEntry.PARSED_UNIT,
+      DartEntry.SCAN_ERRORS,
+      DartEntry.SOURCE_KIND,
+      DartEntry.TOKEN_STREAM
+    ]);
+    SourceKind kind = getValue(DartEntry.SOURCE_KIND);
+    if (kind == SourceKind.LIBRARY) {
+      result.addAll(<DataDescriptor>[
+        DartEntry.ELEMENT,
+        DartEntry.EXPORTED_LIBRARIES,
+        DartEntry.IMPORTED_LIBRARIES,
+        DartEntry.INCLUDED_PARTS,
+        DartEntry.IS_CLIENT,
+        DartEntry.IS_LAUNCHABLE,
+        DartEntry.PUBLIC_NAMESPACE
+      ]);
+    }
+    return result;
+  }
+
+  /**
+   * Return `true` if this entry has an AST structure that can be resolved, even
+   * if it needs to be copied. Returning `true` implies that the method
+   * [resolvableCompilationUnit] will return a non-`null` result.
+   */
+  bool get hasResolvableCompilationUnit {
+    if (getState(PARSED_UNIT) == CacheState.VALID) {
+      return true;
+    }
+    ResolutionState state = _resolutionState;
+    while (state != null) {
+      if (state.getState(BUILT_UNIT) == CacheState.VALID ||
+          state.getState(RESOLVED_UNIT) == CacheState.VALID) {
+        return true;
+      }
+      state = state._nextState;
+    }
+
+    return false;
+  }
+
+  /**
+   * Return `true` if this data is safe to use in refactoring.
+   */
+  bool get isRefactoringSafe {
+    ResolutionState state = _resolutionState;
+    while (state != null) {
+      CacheState resolvedState = state.getState(RESOLVED_UNIT);
+      if (resolvedState != CacheState.VALID &&
+          resolvedState != CacheState.FLUSHED) {
+        return false;
+      }
+      state = state._nextState;
+    }
+    return true;
+  }
+
+  @override
+  SourceKind get kind => getValue(SOURCE_KIND);
+
+  /**
+   * The library sources containing the receiver's source.
+   */
+  List<Source> get librariesContaining {
+    ResolutionState state = _resolutionState;
+    List<Source> result = new List<Source>();
+    while (state != null) {
+      if (state._librarySource != null) {
+        result.add(state._librarySource);
+      }
+      state = state._nextState;
+    }
+    return result;
+  }
+
+  /**
+   * Get a list of all the library-dependent descriptors for which values may
+   * be stored in this SourceEntry.
+   */
+  List<DataDescriptor> get libraryDescriptors {
+    return <DataDescriptor>[
+      DartEntry.BUILT_ELEMENT,
+      DartEntry.BUILT_UNIT,
+      DartEntry.RESOLUTION_ERRORS,
+      DartEntry.RESOLVED_UNIT,
+      DartEntry.VERIFICATION_ERRORS,
+      DartEntry.HINTS,
+      DartEntry.LINTS
+    ];
+  }
+
+  /**
+   * A compilation unit that has not been accessed by any other client and can
+   * therefore safely be modified by the reconciler, or `null` if the source has
+   * not been parsed.
+   */
+  CompilationUnit get resolvableCompilationUnit {
+    if (getState(PARSED_UNIT) == CacheState.VALID) {
+      CompilationUnit unit = getValue(PARSED_UNIT);
+      setState(PARSED_UNIT, CacheState.FLUSHED);
+      return unit;
+    }
+    ResolutionState state = _resolutionState;
+    while (state != null) {
+      if (state.getState(BUILT_UNIT) == CacheState.VALID) {
+        // TODO(brianwilkerson) We're cloning the structure to remove any
+        // previous resolution data, but I'm not sure that's necessary.
+        return state.getValue(BUILT_UNIT).accept(new AstCloner());
+      }
+      if (state.getState(RESOLVED_UNIT) == CacheState.VALID) {
+        return state.getValue(RESOLVED_UNIT).accept(new AstCloner());
+      }
+      state = state._nextState;
+    }
+    return null;
+  }
+
+  /**
+   * Add the given [librarySource] to the list of libraries that contain this
+   * part. This method should only be invoked on entries that represent a part.
+   */
+  void addContainingLibrary(Source librarySource) {
+    _containingLibraries.add(librarySource);
+  }
+
+  /**
+   * Flush any AST structures being maintained by this entry.
+   */
+  void flushAstStructures() {
+    _flush(TOKEN_STREAM);
+    _flush(PARSED_UNIT);
+    _resolutionState.flushAstStructures();
+  }
+
+  /**
+   * Return the state of the data represented by the given [descriptor] in the
+   * context of the given [librarySource].
+   */
+  CacheState getStateInLibrary(
+      DataDescriptor descriptor, Source librarySource) {
+    if (!_isValidLibraryDescriptor(descriptor)) {
+      throw new ArgumentError("Invalid descriptor: $descriptor");
+    }
+    ResolutionState state = _resolutionState;
+    while (state != null) {
+      if (librarySource == state._librarySource) {
+        return state.getState(descriptor);
+      }
+      state = state._nextState;
+    }
+    return CacheState.INVALID;
+  }
+
+  /**
+   * Return the value of the data represented by the given [descriptor] in the
+   * context of the given [librarySource], or `null` if the data represented by
+   * the descriptor is not in the cache.
+   */
+  Object getValueInLibrary(DataDescriptor descriptor, Source librarySource) {
+    if (!_isValidLibraryDescriptor(descriptor)) {
+      throw new ArgumentError("Invalid descriptor: $descriptor");
+    }
+    ResolutionState state = _resolutionState;
+    while (state != null) {
+      if (librarySource == state._librarySource) {
+        return state.getValue(descriptor);
+      }
+      state = state._nextState;
+    }
+    return descriptor.defaultValue;
+  }
+
+  /**
+   * Return `true` if the data represented by the given [descriptor] is marked
+   * as being invalid. If the descriptor represents library-specific data then
+   * this method will return `true` if the data associated with any library it
+   * marked as invalid.
+   */
+  bool hasInvalidData(DataDescriptor descriptor) {
+    if (_isValidDescriptor(descriptor)) {
+      return getState(descriptor) == CacheState.INVALID;
+    } else if (_isValidLibraryDescriptor(descriptor)) {
+      ResolutionState state = _resolutionState;
+      while (state != null) {
+        if (state.getState(descriptor) == CacheState.INVALID) {
+          return true;
+        }
+        state = state._nextState;
+      }
+    }
+    return false;
+  }
+
+  @override
+  void invalidateAllInformation() {
+    super.invalidateAllInformation();
+    setState(SCAN_ERRORS, CacheState.INVALID);
+    setState(TOKEN_STREAM, CacheState.INVALID);
+    setState(SOURCE_KIND, CacheState.INVALID);
+    setState(PARSE_ERRORS, CacheState.INVALID);
+    setState(PARSED_UNIT, CacheState.INVALID);
+    _discardCachedResolutionInformation(true);
+  }
+
+  /**
+   * Invalidate all of the resolution information associated with the
+   * compilation unit. The flag [invalidateUris] should be `true` if the cached
+   * results of converting URIs to source files should also be invalidated.
+   */
+  void invalidateAllResolutionInformation(bool invalidateUris) {
+    if (getState(PARSED_UNIT) == CacheState.FLUSHED) {
+      ResolutionState state = _resolutionState;
+      while (state != null) {
+        if (state.getState(BUILT_UNIT) == CacheState.VALID) {
+          CompilationUnit unit = state.getValue(BUILT_UNIT);
+          setValue(PARSED_UNIT, unit.accept(new AstCloner()));
+          break;
+        } else if (state.getState(RESOLVED_UNIT) == CacheState.VALID) {
+          CompilationUnit unit = state.getValue(RESOLVED_UNIT);
+          setValue(PARSED_UNIT, unit.accept(new AstCloner()));
+          break;
+        }
+        state = state._nextState;
+      }
+    }
+    _discardCachedResolutionInformation(invalidateUris);
+  }
+
+  /**
+   * Invalidate all of the parse and resolution information associated with
+   * this source.
+   */
+  void invalidateParseInformation() {
+    setState(SOURCE_KIND, CacheState.INVALID);
+    setState(PARSE_ERRORS, CacheState.INVALID);
+    setState(PARSED_UNIT, CacheState.INVALID);
+    _containingLibraries.clear();
+    _discardCachedResolutionInformation(true);
+  }
+
+  /**
+   * Record that an [exception] occurred while attempting to build the element
+   * model for the source represented by this entry in the context of the given
+   * [library]. This will set the state of all resolution-based information as
+   * being in error, but will not change the state of any parse results.
+   */
+  void recordBuildElementErrorInLibrary(
+      Source librarySource, CaughtException exception) {
+    setStateInLibrary(BUILT_ELEMENT, librarySource, CacheState.ERROR);
+    setStateInLibrary(BUILT_UNIT, librarySource, CacheState.ERROR);
+    recordResolutionErrorInLibrary(librarySource, exception);
+  }
+
+  @override
+  void recordContentError(CaughtException exception) {
+    super.recordContentError(exception);
+    recordScanError(exception);
+  }
+
+  /**
+   * Record that an error occurred while attempting to generate hints for the
+   * source represented by this entry. This will set the state of all
+   * verification information as being in error. The [librarySource] is the
+   * source of the library in which hints were being generated. The [exception]
+   * is the exception that shows where the error occurred.
+   */
+  void recordHintErrorInLibrary(
+      Source librarySource, CaughtException exception) {
+    this.exception = exception;
+    ResolutionState state = _getOrCreateResolutionState(librarySource);
+    state.recordHintError();
+  }
+
+  /**
+   * Record that an error occurred while attempting to generate lints for the
+   * source represented by this entry. This will set the state of all
+   * verification information as being in error. The [librarySource] is the
+   * source of the library in which lints were being generated. The [exception]
+   * is the exception that shows where the error occurred.
+   */
+  void recordLintErrorInLibrary(
+      Source librarySource, CaughtException exception) {
+    this.exception = exception;
+    ResolutionState state = _getOrCreateResolutionState(librarySource);
+    state.recordLintError();
+  }
+
+  /**
+   * Record that an [exception] occurred while attempting to scan or parse the
+   * entry represented by this entry. This will set the state of all information,
+   * including any resolution-based information, as being in error.
+   */
+  void recordParseError(CaughtException exception) {
+    setState(SOURCE_KIND, CacheState.ERROR);
+    setState(PARSE_ERRORS, CacheState.ERROR);
+    setState(PARSED_UNIT, CacheState.ERROR);
+    setState(EXPORTED_LIBRARIES, CacheState.ERROR);
+    setState(IMPORTED_LIBRARIES, CacheState.ERROR);
+    setState(INCLUDED_PARTS, CacheState.ERROR);
+    recordResolutionError(exception);
+  }
+
+  /**
+   * Record that an [exception] occurred while attempting to resolve the source
+   * represented by this entry. This will set the state of all resolution-based
+   * information as being in error, but will not change the state of any parse
+   * results.
+   */
+  void recordResolutionError(CaughtException exception) {
+    this.exception = exception;
+    setState(ELEMENT, CacheState.ERROR);
+    setState(IS_CLIENT, CacheState.ERROR);
+    setState(IS_LAUNCHABLE, CacheState.ERROR);
+    setState(PUBLIC_NAMESPACE, CacheState.ERROR);
+    _resolutionState.recordResolutionErrorsInAllLibraries();
+  }
+
+  /**
+   * Record that an error occurred while attempting to resolve the source
+   * represented by this entry. This will set the state of all resolution-based
+   * information as being in error, but will not change the state of any parse
+   * results. The [librarySource] is the source of the library in which
+   * resolution was being performed. The [exception] is the exception that shows
+   * where the error occurred.
+   */
+  void recordResolutionErrorInLibrary(
+      Source librarySource, CaughtException exception) {
+    this.exception = exception;
+    setState(ELEMENT, CacheState.ERROR);
+    setState(IS_CLIENT, CacheState.ERROR);
+    setState(IS_LAUNCHABLE, CacheState.ERROR);
+    setState(PUBLIC_NAMESPACE, CacheState.ERROR);
+    ResolutionState state = _getOrCreateResolutionState(librarySource);
+    state.recordResolutionError();
+  }
+
+  /**
+   * Record that an [exception] occurred while attempting to scan or parse the
+   * entry represented by this entry. This will set the state of all
+   * information, including any resolution-based information, as being in error.
+   */
+  @override
+  void recordScanError(CaughtException exception) {
+    super.recordScanError(exception);
+    setState(SCAN_ERRORS, CacheState.ERROR);
+    setState(TOKEN_STREAM, CacheState.ERROR);
+    recordParseError(exception);
+  }
+
+  /**
+   * Record that an [exception] occurred while attempting to generate errors and
+   * warnings for the source represented by this entry. This will set the state
+   * of all verification information as being in error. The [librarySource] is
+   * the source of the library in which verification was being performed. The
+   * [exception] is the exception that shows where the error occurred.
+   */
+  void recordVerificationErrorInLibrary(
+      Source librarySource, CaughtException exception) {
+    this.exception = exception;
+    ResolutionState state = _getOrCreateResolutionState(librarySource);
+    state.recordVerificationError();
+  }
+
+  /**
+   * Remove the given [library] from the list of libraries that contain this
+   * part. This method should only be invoked on entries that represent a part.
+   */
+  void removeContainingLibrary(Source library) {
+    _containingLibraries.remove(library);
+  }
+
+  /**
+   * Remove any resolution information associated with this compilation unit
+   * being part of the given [library], presumably because it is no longer part
+   * of the library.
+   */
+  void removeResolution(Source library) {
+    if (library != null) {
+      if (library == _resolutionState._librarySource) {
+        if (_resolutionState._nextState == null) {
+          _resolutionState.invalidateAllResolutionInformation();
+        } else {
+          _resolutionState = _resolutionState._nextState;
+        }
+      } else {
+        ResolutionState priorState = _resolutionState;
+        ResolutionState state = _resolutionState._nextState;
+        while (state != null) {
+          if (library == state._librarySource) {
+            priorState._nextState = state._nextState;
+            break;
+          }
+          priorState = state;
+          state = state._nextState;
+        }
+      }
+    }
+  }
+
+  /**
+   * Set the state of the data represented by the given [descriptor] in the
+   * context of the given [library] to the given [state].
+   */
+  void setStateInLibrary(
+      DataDescriptor descriptor, Source library, CacheState state) {
+    if (!_isValidLibraryDescriptor(descriptor)) {
+      throw new ArgumentError("Invalid descriptor: $descriptor");
+    }
+    ResolutionState resolutionState = _getOrCreateResolutionState(library);
+    resolutionState.setState(descriptor, state);
+  }
+
+  /**
+   * Set the value of the data represented by the given [descriptor] in the
+   * context of the given [library] to the given [value], and set the state of
+   * that data to [CacheState.VALID].
+   */
+  void setValueInLibrary(
+      DataDescriptor descriptor, Source library, Object value) {
+    if (!_isValidLibraryDescriptor(descriptor)) {
+      throw new ArgumentError("Invalid descriptor: $descriptor");
+    }
+    ResolutionState state = _getOrCreateResolutionState(library);
+    state.setValue(descriptor, value);
+  }
+
+  /**
+   * Invalidate all of the resolution information associated with the
+   * compilation unit. The flag [invalidateUris] should be `true` if the cached
+   * results of converting URIs to source files should also be invalidated.
+   */
+  void _discardCachedResolutionInformation(bool invalidateUris) {
+    setState(ELEMENT, CacheState.INVALID);
+    setState(IS_CLIENT, CacheState.INVALID);
+    setState(IS_LAUNCHABLE, CacheState.INVALID);
+    setState(PUBLIC_NAMESPACE, CacheState.INVALID);
+    _resolutionState.invalidateAllResolutionInformation();
+    if (invalidateUris) {
+      setState(EXPORTED_LIBRARIES, CacheState.INVALID);
+      setState(IMPORTED_LIBRARIES, CacheState.INVALID);
+      setState(INCLUDED_PARTS, CacheState.INVALID);
+    }
+  }
+
+  /**
+   * Return a resolution state for the specified [library], creating one as
+   * necessary.
+   */
+  ResolutionState _getOrCreateResolutionState(Source library) {
+    ResolutionState state = _resolutionState;
+    if (state._librarySource == null) {
+      state._librarySource = library;
+      return state;
+    }
+    while (state._librarySource != library) {
+      if (state._nextState == null) {
+        ResolutionState newState = new ResolutionState();
+        newState._librarySource = library;
+        state._nextState = newState;
+        return newState;
+      }
+      state = state._nextState;
+    }
+    return state;
+  }
+
+  @override
+  bool _isValidDescriptor(DataDescriptor descriptor) {
+    return descriptor == CONTAINING_LIBRARIES ||
+        descriptor == ELEMENT ||
+        descriptor == EXPORTED_LIBRARIES ||
+        descriptor == IMPORTED_LIBRARIES ||
+        descriptor == INCLUDED_PARTS ||
+        descriptor == IS_CLIENT ||
+        descriptor == IS_LAUNCHABLE ||
+        descriptor == PARSED_UNIT ||
+        descriptor == PARSE_ERRORS ||
+        descriptor == PUBLIC_NAMESPACE ||
+        descriptor == SCAN_ERRORS ||
+        descriptor == SOURCE_KIND ||
+        descriptor == TOKEN_STREAM ||
+        super._isValidDescriptor(descriptor);
+  }
+
+  /**
+   * Return `true` if the [descriptor] is valid for this entry when the data is
+   * relative to a library.
+   */
+  bool _isValidLibraryDescriptor(DataDescriptor descriptor) {
+    return descriptor == BUILT_ELEMENT ||
+        descriptor == BUILT_UNIT ||
+        descriptor == HINTS ||
+        descriptor == LINTS ||
+        descriptor == RESOLUTION_ERRORS ||
+        descriptor == RESOLVED_UNIT ||
+        descriptor == VERIFICATION_ERRORS;
+  }
+
+  @override
+  bool _writeDiffOn(StringBuffer buffer, SourceEntry oldEntry) {
+    bool needsSeparator = super._writeDiffOn(buffer, oldEntry);
+    if (oldEntry is! DartEntry) {
+      if (needsSeparator) {
+        buffer.write("; ");
+      }
+      buffer.write("entry type changed; was ");
+      buffer.write(oldEntry.runtimeType.toString());
+      return true;
+    }
+    needsSeparator = _writeStateDiffOn(buffer, needsSeparator, "tokenStream",
+        DartEntry.TOKEN_STREAM, oldEntry);
+    needsSeparator = _writeStateDiffOn(
+        buffer, needsSeparator, "scanErrors", DartEntry.SCAN_ERRORS, oldEntry);
+    needsSeparator = _writeStateDiffOn(
+        buffer, needsSeparator, "sourceKind", DartEntry.SOURCE_KIND, oldEntry);
+    needsSeparator = _writeStateDiffOn(
+        buffer, needsSeparator, "parsedUnit", DartEntry.PARSED_UNIT, oldEntry);
+    needsSeparator = _writeStateDiffOn(buffer, needsSeparator, "parseErrors",
+        DartEntry.PARSE_ERRORS, oldEntry);
+    needsSeparator = _writeStateDiffOn(buffer, needsSeparator,
+        "importedLibraries", DartEntry.IMPORTED_LIBRARIES, oldEntry);
+    needsSeparator = _writeStateDiffOn(buffer, needsSeparator,
+        "exportedLibraries", DartEntry.EXPORTED_LIBRARIES, oldEntry);
+    needsSeparator = _writeStateDiffOn(buffer, needsSeparator, "includedParts",
+        DartEntry.INCLUDED_PARTS, oldEntry);
+    needsSeparator = _writeStateDiffOn(
+        buffer, needsSeparator, "element", DartEntry.ELEMENT, oldEntry);
+    needsSeparator = _writeStateDiffOn(buffer, needsSeparator,
+        "publicNamespace", DartEntry.PUBLIC_NAMESPACE, oldEntry);
+    needsSeparator = _writeStateDiffOn(
+        buffer, needsSeparator, "clientServer", DartEntry.IS_CLIENT, oldEntry);
+    needsSeparator = _writeStateDiffOn(buffer, needsSeparator, "launchable",
+        DartEntry.IS_LAUNCHABLE, oldEntry);
+    // TODO(brianwilkerson) Add better support for containingLibraries.
+    // It would be nice to be able to report on size-preserving changes.
+    int oldLibraryCount = (oldEntry as DartEntry)._containingLibraries.length;
+    int libraryCount = _containingLibraries.length;
+    if (oldLibraryCount != libraryCount) {
+      if (needsSeparator) {
+        buffer.write("; ");
+      }
+      buffer.write("containingLibraryCount = ");
+      buffer.write(oldLibraryCount);
+      buffer.write(" -> ");
+      buffer.write(libraryCount);
+      needsSeparator = true;
+    }
+    //
+    // Report change to the per-library state.
+    //
+    HashMap<Source, ResolutionState> oldStateMap =
+        new HashMap<Source, ResolutionState>();
+    ResolutionState state = (oldEntry as DartEntry)._resolutionState;
+    while (state != null) {
+      Source librarySource = state._librarySource;
+      if (librarySource != null) {
+        oldStateMap[librarySource] = state;
+      }
+      state = state._nextState;
+    }
+    state = _resolutionState;
+    while (state != null) {
+      Source librarySource = state._librarySource;
+      if (librarySource != null) {
+        ResolutionState oldState = oldStateMap.remove(librarySource);
+        if (oldState == null) {
+          if (needsSeparator) {
+            buffer.write("; ");
+          }
+          buffer.write("added resolution for ");
+          buffer.write(librarySource.fullName);
+          needsSeparator = true;
+        } else {
+          needsSeparator = oldState._writeDiffOn(
+              buffer, needsSeparator, oldEntry as DartEntry);
+        }
+      }
+      state = state._nextState;
+    }
+    for (Source librarySource in oldStateMap.keys.toSet()) {
+      if (needsSeparator) {
+        buffer.write("; ");
+      }
+      buffer.write("removed resolution for ");
+      buffer.write(librarySource.fullName);
+      needsSeparator = true;
+    }
+    return needsSeparator;
+  }
+
+  @override
+  void _writeOn(StringBuffer buffer) {
+    buffer.write("Dart: ");
+    super._writeOn(buffer);
+    _writeStateOn(buffer, "tokenStream", TOKEN_STREAM);
+    _writeStateOn(buffer, "scanErrors", SCAN_ERRORS);
+    _writeStateOn(buffer, "sourceKind", SOURCE_KIND);
+    _writeStateOn(buffer, "parsedUnit", PARSED_UNIT);
+    _writeStateOn(buffer, "parseErrors", PARSE_ERRORS);
+    _writeStateOn(buffer, "exportedLibraries", EXPORTED_LIBRARIES);
+    _writeStateOn(buffer, "importedLibraries", IMPORTED_LIBRARIES);
+    _writeStateOn(buffer, "includedParts", INCLUDED_PARTS);
+    _writeStateOn(buffer, "element", ELEMENT);
+    _writeStateOn(buffer, "publicNamespace", PUBLIC_NAMESPACE);
+    _writeStateOn(buffer, "clientServer", IS_CLIENT);
+    _writeStateOn(buffer, "launchable", IS_LAUNCHABLE);
+    _resolutionState._writeOn(buffer);
+  }
+}
+
+/**
+ * An immutable constant representing data that can be stored in the cache.
+ */
+class DataDescriptor<E> {
+  /**
+   * The next artificial hash code.
+   */
+  static int _NEXT_HASH_CODE = 0;
+
+  /**
+   * The artifitial hash code for this object.
+   */
+  final int _hashCode = _NEXT_HASH_CODE++;
+
+  /**
+   * The name of the descriptor, used for debugging purposes.
+   */
+  final String _name;
+
+  /**
+   * The default value used when the data does not exist.
+   */
+  final E defaultValue;
+
+  /**
+   * Initialize a newly created descriptor to have the given [name] and
+   * [defaultValue].
+   */
+  DataDescriptor(this._name, [this.defaultValue = null]);
+
+  @override
+  int get hashCode => _hashCode;
+
+  @override
+  String toString() => _name;
+}
+
+/**
+ * A retention policy that will keep AST's in the cache if there is analysis
+ * information that needs to be computed for a source, where the computation is
+ * dependent on having the AST.
+ */
+class DefaultRetentionPolicy implements CacheRetentionPolicy {
+  /**
+   * An instance of this class that can be shared.
+   */
+  static DefaultRetentionPolicy POLICY = new DefaultRetentionPolicy();
+
+  /**
+   * Return `true` if there is analysis information in the given [dartEntry]
+   * that needs to be computed, where the computation is dependent on having the
+   * AST.
+   */
+  bool astIsNeeded(DartEntry dartEntry) =>
+      dartEntry.hasInvalidData(DartEntry.HINTS) ||
+          dartEntry.hasInvalidData(DartEntry.LINTS) ||
+          dartEntry.hasInvalidData(DartEntry.VERIFICATION_ERRORS) ||
+          dartEntry.hasInvalidData(DartEntry.RESOLUTION_ERRORS);
+
+  @override
+  RetentionPriority getAstPriority(Source source, SourceEntry sourceEntry) {
+    if (sourceEntry is DartEntry) {
+      DartEntry dartEntry = sourceEntry;
+      if (astIsNeeded(dartEntry)) {
+        return RetentionPriority.MEDIUM;
+      }
+    }
+    return RetentionPriority.LOW;
+  }
+}
+
+/**
+ * Instances of the class `GenerateDartErrorsTask` generate errors and warnings for a single
+ * Dart source.
+ */
+class GenerateDartErrorsTask extends AnalysisTask {
+  /**
+   * The source for which errors and warnings are to be produced.
+   */
+  final Source source;
+
+  /**
+   * The compilation unit used to resolve the dependencies.
+   */
+  final CompilationUnit _unit;
+
+  /**
+   * The element model for the library containing the source.
+   */
+  final LibraryElement libraryElement;
+
+  /**
+   * The errors that were generated for the source.
+   */
+  List<AnalysisError> _errors;
+
+  /**
+   * Initialize a newly created task to perform analysis within the given context.
+   *
+   * @param context the context in which the task is to be performed
+   * @param source the source for which errors and warnings are to be produced
+   * @param unit the compilation unit used to resolve the dependencies
+   * @param libraryElement the element model for the library containing the source
+   */
+  GenerateDartErrorsTask(InternalAnalysisContext context, this.source,
+      this._unit, this.libraryElement)
+      : super(context);
+
+  /**
+   * Return the errors that were generated for the source.
+   *
+   * @return the errors that were generated for the source
+   */
+  List<AnalysisError> get errors => _errors;
+
+  @override
+  String get taskDescription =>
+      "generate errors and warnings for ${source.fullName}";
+
+  @override
+  accept(AnalysisTaskVisitor visitor) =>
+      visitor.visitGenerateDartErrorsTask(this);
+
+  @override
+  void internalPerform() {
+    PerformanceStatistics.errors.makeCurrentWhile(() {
+      RecordingErrorListener errorListener = new RecordingErrorListener();
+      ErrorReporter errorReporter = new ErrorReporter(errorListener, source);
+      TypeProvider typeProvider = context.typeProvider;
+      //
+      // Validate the directives
+      //
+      validateDirectives(context, source, _unit, errorListener);
+      //
+      // Use the ConstantVerifier to verify the use of constants.
+      // This needs to happen before using the ErrorVerifier because some error
+      // codes need the computed constant values.
+      //
+      // TODO(paulberry): as a temporary workaround for issue 21572,
+      // ConstantVerifier is being run right after ConstantValueComputer, so we
+      // don't need to run it here.  Once issue 21572 is fixed, re-enable the
+      // call to ConstantVerifier.
+//      ConstantVerifier constantVerifier = new ConstantVerifier(errorReporter, libraryElement, typeProvider);
+//      _unit.accept(constantVerifier);
+      //
+      // Use the ErrorVerifier to compute the rest of the errors.
+      //
+      ErrorVerifier errorVerifier = new ErrorVerifier(errorReporter,
+          libraryElement, typeProvider, new InheritanceManager(libraryElement));
+      _unit.accept(errorVerifier);
+      _errors = errorListener.getErrorsForSource(source);
+    });
+  }
+
+  /**
+   * Check each directive in the given compilation unit to see if the referenced source exists and
+   * report an error if it does not.
+   *
+   * @param context the context in which the library exists
+   * @param librarySource the source representing the library containing the directives
+   * @param unit the compilation unit containing the directives to be validated
+   * @param errorListener the error listener to which errors should be reported
+   */
+  static void validateDirectives(AnalysisContext context, Source librarySource,
+      CompilationUnit unit, AnalysisErrorListener errorListener) {
+    for (Directive directive in unit.directives) {
+      if (directive is UriBasedDirective) {
+        validateReferencedSource(
+            context, librarySource, directive, errorListener);
+      }
+    }
+  }
+
+  /**
+   * Check the given directive to see if the referenced source exists and report an error if it does
+   * not.
+   *
+   * @param context the context in which the library exists
+   * @param librarySource the source representing the library containing the directive
+   * @param directive the directive to be verified
+   * @param errorListener the error listener to which errors should be reported
+   */
+  static void validateReferencedSource(AnalysisContext context,
+      Source librarySource, UriBasedDirective directive,
+      AnalysisErrorListener errorListener) {
+    Source source = directive.source;
+    if (source != null) {
+      if (context.exists(source)) {
+        return;
+      }
+    } else {
+      // Don't report errors already reported by ParseDartTask.resolveDirective
+      if (directive.validate() != null) {
+        return;
+      }
+    }
+    StringLiteral uriLiteral = directive.uri;
+    errorListener.onError(new AnalysisError.con2(librarySource,
+        uriLiteral.offset, uriLiteral.length,
+        CompileTimeErrorCode.URI_DOES_NOT_EXIST, [directive.uriContent]));
+  }
+}
+
+/**
+ * Instances of the class `GenerateDartHintsTask` generate hints for a single Dart library.
+ */
+class GenerateDartHintsTask extends AnalysisTask {
+  /**
+   * The compilation units that comprise the library, with the defining compilation unit appearing
+   * first in the list.
+   */
+  final List<TimestampedData<CompilationUnit>> _units;
+
+  /**
+   * The element model for the library being analyzed.
+   */
+  final LibraryElement libraryElement;
+
+  /**
+   * A table mapping the sources that were analyzed to the hints that were
+   * generated for the sources.
+   */
+  HashMap<Source, List<AnalysisError>> _hintMap;
+
+  /**
+   * Initialize a newly created task to perform analysis within the given context.
+   *
+   * @param context the context in which the task is to be performed
+   * @param units the compilation units that comprise the library, with the defining compilation
+   *          unit appearing first in the list
+   * @param libraryElement the element model for the library being analyzed
+   */
+  GenerateDartHintsTask(
+      InternalAnalysisContext context, this._units, this.libraryElement)
+      : super(context);
+
+  /**
+   * Return a table mapping the sources that were analyzed to the hints that were generated for the
+   * sources, or `null` if the task has not been performed or if the analysis did not complete
+   * normally.
+   *
+   * @return a table mapping the sources that were analyzed to the hints that were generated for the
+   *         sources
+   */
+  HashMap<Source, List<AnalysisError>> get hintMap => _hintMap;
+
+  @override
+  String get taskDescription {
+    Source librarySource = libraryElement.source;
+    if (librarySource == null) {
+      return "generate Dart hints for library without source";
+    }
+    return "generate Dart hints for ${librarySource.fullName}";
+  }
+
+  @override
+  accept(AnalysisTaskVisitor visitor) =>
+      visitor.visitGenerateDartHintsTask(this);
+
+  @override
+  void internalPerform() {
+    //
+    // Gather the compilation units.
+    //
+    int unitCount = _units.length;
+    List<CompilationUnit> compilationUnits =
+        new List<CompilationUnit>(unitCount);
+    for (int i = 0; i < unitCount; i++) {
+      compilationUnits[i] = _units[i].data;
+    }
+    //
+    // Analyze all of the units.
+    //
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    HintGenerator hintGenerator =
+        new HintGenerator(compilationUnits, context, errorListener);
+    hintGenerator.generateForLibrary();
+    //
+    // Store the results.
+    //
+    _hintMap = new HashMap<Source, List<AnalysisError>>();
+    for (int i = 0; i < unitCount; i++) {
+      Source source = _units[i].data.element.source;
+      _hintMap[source] = errorListener.getErrorsForSource(source);
+    }
+  }
+}
+
+/// Generates lint feedback for a single Dart library.
+class GenerateDartLintsTask extends AnalysisTask {
+
+  ///The compilation units that comprise the library, with the defining
+  ///compilation unit appearing first in the list.
+  final List<TimestampedData<CompilationUnit>> _units;
+
+  /// The element model for the library being analyzed.
+  final LibraryElement libraryElement;
+
+  /// A mapping of analyzed sources to their associated lint warnings.
+  /// May be [null] if the task has not been performed or if analysis did not
+  /// complete normally.
+  HashMap<Source, List<AnalysisError>> lintMap;
+
+  /// Initialize a newly created task to perform lint checking over these
+  /// [_units] belonging to this [libraryElement] within the given [context].
+  GenerateDartLintsTask(context, this._units, this.libraryElement)
+      : super(context);
+
+  @override
+  String get taskDescription {
+    Source librarySource = libraryElement.source;
+    return (librarySource == null)
+        ? "generate Dart lints for library without source"
+        : "generate Dart lints for ${librarySource.fullName}";
+  }
+
+  @override
+  accept(AnalysisTaskVisitor visitor) =>
+      visitor.visitGenerateDartLintsTask(this);
+
+  @override
+  void internalPerform() {
+    Iterable<CompilationUnit> compilationUnits =
+        _units.map((TimestampedData<CompilationUnit> unit) => unit.data);
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    LintGenerator lintGenerator =
+        new LintGenerator(compilationUnits, errorListener);
+    lintGenerator.generate();
+
+    lintMap = new HashMap<Source, List<AnalysisError>>();
+    compilationUnits.forEach((CompilationUnit unit) {
+      Source source = unit.element.source;
+      lintMap[source] = errorListener.getErrorsForSource(source);
+    });
+  }
+}
+
+/**
+ * Instances of the class `GetContentTask` get the contents of a source.
+ */
+class GetContentTask extends AnalysisTask {
+  /**
+   * The source to be read.
+   */
+  final Source source;
+
+  /**
+   * A flag indicating whether this task is complete.
+   */
+  bool _complete = false;
+
+  /**
+   * The contents of the source.
+   */
+  String _content;
+
+  /**
+   * The errors that were produced by getting the source content.
+   */
+  final List<AnalysisError> errors = <AnalysisError>[];
+
+  /**
+   * The time at which the contents of the source were last modified.
+   */
+  int _modificationTime = -1;
+
+  /**
+   * Initialize a newly created task to perform analysis within the given context.
+   *
+   * @param context the context in which the task is to be performed
+   * @param source the source to be parsed
+   * @param contentData the time-stamped contents of the source
+   */
+  GetContentTask(InternalAnalysisContext context, this.source)
+      : super(context) {
+    if (source == null) {
+      throw new IllegalArgumentException("Cannot get contents of null source");
+    }
+  }
+
+  /**
+   * Return the contents of the source, or `null` if the task has not completed or if there
+   * was an exception while getting the contents.
+   *
+   * @return the contents of the source
+   */
+  String get content => _content;
+
+  /**
+   * Return `true` if this task is complete. Unlike most tasks, this task is allowed to be
+   * visited more than once in order to support asynchronous IO. If the task is not complete when it
+   * is visited synchronously as part of the [AnalysisTask.perform]
+   * method, it will be visited again, using the same visitor, when the IO operation has been
+   * performed.
+   *
+   * @return `true` if this task is complete
+   */
+  bool get isComplete => _complete;
+
+  /**
+   * Return the time at which the contents of the source that was parsed were last modified, or a
+   * negative value if the task has not yet been performed or if an exception occurred.
+   *
+   * @return the time at which the contents of the source that was parsed were last modified
+   */
+  int get modificationTime => _modificationTime;
+
+  @override
+  String get taskDescription => "get contents of ${source.fullName}";
+
+  @override
+  accept(AnalysisTaskVisitor visitor) => visitor.visitGetContentTask(this);
+
+  @override
+  void internalPerform() {
+    _complete = true;
+    try {
+      TimestampedData<String> data = context.getContents(source);
+      _content = data.data;
+      _modificationTime = data.modificationTime;
+      AnalysisEngine.instance.instrumentationService.logFileRead(
+          source.fullName, _modificationTime, _content);
+    } catch (exception, stackTrace) {
+      errors.add(new AnalysisError.con1(
+          source, ScannerErrorCode.UNABLE_GET_CONTENT, [exception]));
+      throw new AnalysisException("Could not get contents of $source",
+          new CaughtException(exception, stackTrace));
+    }
+  }
+}
+
+/**
+ * The information cached by an analysis context about an individual HTML file.
+ */
+class HtmlEntry extends SourceEntry {
+  /**
+   * The data descriptor representing the HTML element.
+   */
+  static final DataDescriptor<HtmlElement> ELEMENT =
+      new DataDescriptor<HtmlElement>("HtmlEntry.ELEMENT");
+
+  /**
+   * The data descriptor representing the hints resulting from auditing the
+   * source.
+   */
+  static final DataDescriptor<List<AnalysisError>> HINTS =
+      new DataDescriptor<List<AnalysisError>>(
+          "HtmlEntry.HINTS", AnalysisError.NO_ERRORS);
+
+  /**
+   * The data descriptor representing the errors resulting from parsing the
+   * source.
+   */
+  static final DataDescriptor<List<AnalysisError>> PARSE_ERRORS =
+      new DataDescriptor<List<AnalysisError>>(
+          "HtmlEntry.PARSE_ERRORS", AnalysisError.NO_ERRORS);
+
+  /**
+   * The data descriptor representing the parsed AST structure.
+   */
+  static final DataDescriptor<ht.HtmlUnit> PARSED_UNIT =
+      new DataDescriptor<ht.HtmlUnit>("HtmlEntry.PARSED_UNIT");
+
+  /**
+   * The data descriptor representing the resolved AST structure.
+   */
+  static final DataDescriptor<ht.HtmlUnit> RESOLVED_UNIT =
+      new DataDescriptor<ht.HtmlUnit>("HtmlEntry.RESOLVED_UNIT");
+
+  /**
+   * The data descriptor representing the list of referenced libraries.
+   */
+  static final DataDescriptor<List<Source>> REFERENCED_LIBRARIES =
+      new DataDescriptor<List<Source>>(
+          "HtmlEntry.REFERENCED_LIBRARIES", Source.EMPTY_ARRAY);
+
+  /**
+   * The data descriptor representing the errors resulting from resolving the
+   * source.
+   */
+  static final DataDescriptor<List<AnalysisError>> RESOLUTION_ERRORS =
+      new DataDescriptor<List<AnalysisError>>(
+          "HtmlEntry.RESOLUTION_ERRORS", AnalysisError.NO_ERRORS);
+
+  /**
+   * Return all of the errors associated with the HTML file that are currently
+   * cached.
+   */
+  List<AnalysisError> get allErrors {
+    List<AnalysisError> errors = new List<AnalysisError>();
+    errors.addAll(super.allErrors);
+    errors.addAll(getValue(PARSE_ERRORS));
+    errors.addAll(getValue(RESOLUTION_ERRORS));
+    errors.addAll(getValue(HINTS));
+    if (errors.length == 0) {
+      return AnalysisError.NO_ERRORS;
+    }
+    return errors;
+  }
+
+  /**
+   * Return a valid parsed unit, either an unresolved AST structure or the
+   * result of resolving the AST structure, or `null` if there is no parsed unit
+   * available.
+   */
+  ht.HtmlUnit get anyParsedUnit {
+    if (getState(PARSED_UNIT) == CacheState.VALID) {
+      return getValue(PARSED_UNIT);
+    }
+    if (getState(RESOLVED_UNIT) == CacheState.VALID) {
+      return getValue(RESOLVED_UNIT);
+    }
+    return null;
+  }
+
+  @override
+  List<DataDescriptor> get descriptors {
+    List<DataDescriptor> result = super.descriptors;
+    result.addAll([
+      HtmlEntry.ELEMENT,
+      HtmlEntry.PARSE_ERRORS,
+      HtmlEntry.PARSED_UNIT,
+      HtmlEntry.RESOLUTION_ERRORS,
+      HtmlEntry.RESOLVED_UNIT,
+      HtmlEntry.HINTS
+    ]);
+    return result;
+  }
+
+  @override
+  SourceKind get kind => SourceKind.HTML;
+
+  /**
+   * Flush any AST structures being maintained by this entry.
+   */
+  void flushAstStructures() {
+    _flush(PARSED_UNIT);
+    _flush(RESOLVED_UNIT);
+  }
+
+  @override
+  void invalidateAllInformation() {
+    super.invalidateAllInformation();
+    setState(PARSE_ERRORS, CacheState.INVALID);
+    setState(PARSED_UNIT, CacheState.INVALID);
+    setState(RESOLVED_UNIT, CacheState.INVALID);
+    invalidateAllResolutionInformation(true);
+  }
+
+  /**
+   * Invalidate all of the resolution information associated with the HTML file.
+   * If [invalidateUris] is `true`, the cached results of converting URIs to
+   * source files should also be invalidated.
+   */
+  void invalidateAllResolutionInformation(bool invalidateUris) {
+    setState(RESOLVED_UNIT, CacheState.INVALID);
+    setState(ELEMENT, CacheState.INVALID);
+    setState(RESOLUTION_ERRORS, CacheState.INVALID);
+    setState(HINTS, CacheState.INVALID);
+    if (invalidateUris) {
+      setState(REFERENCED_LIBRARIES, CacheState.INVALID);
+    }
+  }
+
+  /**
+   * Invalidate all of the parse and resolution information associated with
+   * this source.
+   */
+  void invalidateParseInformation() {
+    setState(PARSE_ERRORS, CacheState.INVALID);
+    setState(PARSED_UNIT, CacheState.INVALID);
+    invalidateAllResolutionInformation(true);
+  }
+
+  @override
+  void recordContentError(CaughtException exception) {
+    super.recordContentError(exception);
+    recordParseError(exception);
+  }
+
+  /**
+   * Record that an [exception] was encountered while attempting to parse the
+   * source associated with this entry.
+   */
+  void recordParseError(CaughtException exception) {
+    // If the scanning and parsing of HTML are separated,
+    // the following line can be removed.
+    recordScanError(exception);
+    setState(PARSE_ERRORS, CacheState.ERROR);
+    setState(PARSED_UNIT, CacheState.ERROR);
+    setState(REFERENCED_LIBRARIES, CacheState.ERROR);
+    recordResolutionError(exception);
+  }
+
+  /**
+   * Record that an [exception] was encountered while attempting to resolve the
+   * source associated with this entry.
+   */
+  void recordResolutionError(CaughtException exception) {
+    this.exception = exception;
+    setState(RESOLVED_UNIT, CacheState.ERROR);
+    setState(ELEMENT, CacheState.ERROR);
+    setState(RESOLUTION_ERRORS, CacheState.ERROR);
+    setState(HINTS, CacheState.ERROR);
+  }
+
+  @override
+  bool _isValidDescriptor(DataDescriptor descriptor) {
+    return descriptor == ELEMENT ||
+        descriptor == HINTS ||
+        descriptor == PARSED_UNIT ||
+        descriptor == PARSE_ERRORS ||
+        descriptor == REFERENCED_LIBRARIES ||
+        descriptor == RESOLUTION_ERRORS ||
+        descriptor == RESOLVED_UNIT ||
+        super._isValidDescriptor(descriptor);
+  }
+
+  @override
+  bool _writeDiffOn(StringBuffer buffer, SourceEntry oldEntry) {
+    bool needsSeparator = super._writeDiffOn(buffer, oldEntry);
+    if (oldEntry is! HtmlEntry) {
+      if (needsSeparator) {
+        buffer.write("; ");
+      }
+      buffer.write("entry type changed; was ");
+      buffer.write(oldEntry.runtimeType);
+      return true;
+    }
+    needsSeparator = _writeStateDiffOn(buffer, needsSeparator, "parseErrors",
+        HtmlEntry.PARSE_ERRORS, oldEntry);
+    needsSeparator = _writeStateDiffOn(
+        buffer, needsSeparator, "parsedUnit", HtmlEntry.PARSED_UNIT, oldEntry);
+    needsSeparator = _writeStateDiffOn(buffer, needsSeparator, "resolvedUnit",
+        HtmlEntry.RESOLVED_UNIT, oldEntry);
+    needsSeparator = _writeStateDiffOn(buffer, needsSeparator,
+        "resolutionErrors", HtmlEntry.RESOLUTION_ERRORS, oldEntry);
+    needsSeparator = _writeStateDiffOn(buffer, needsSeparator,
+        "referencedLibraries", HtmlEntry.REFERENCED_LIBRARIES, oldEntry);
+    needsSeparator = _writeStateDiffOn(
+        buffer, needsSeparator, "element", HtmlEntry.ELEMENT, oldEntry);
+    return needsSeparator;
+  }
+
+  @override
+  void _writeOn(StringBuffer buffer) {
+    buffer.write("Html: ");
+    super._writeOn(buffer);
+    _writeStateOn(buffer, "parseErrors", PARSE_ERRORS);
+    _writeStateOn(buffer, "parsedUnit", PARSED_UNIT);
+    _writeStateOn(buffer, "resolvedUnit", RESOLVED_UNIT);
+    _writeStateOn(buffer, "resolutionErrors", RESOLUTION_ERRORS);
+    _writeStateOn(buffer, "referencedLibraries", REFERENCED_LIBRARIES);
+    _writeStateOn(buffer, "element", ELEMENT);
+  }
+}
+
+/**
+ * Instances of the class `IncrementalAnalysisCache` hold information used to perform
+ * incremental analysis.
+ *
+ * See [AnalysisContextImpl.setChangedContents].
+ */
+class IncrementalAnalysisCache {
+  final Source librarySource;
+
+  final Source source;
+
+  final String oldContents;
+
+  final CompilationUnit resolvedUnit;
+
+  String _newContents;
+
+  int _offset = 0;
+
+  int _oldLength = 0;
+
+  int _newLength = 0;
+
+  IncrementalAnalysisCache(this.librarySource, this.source, this.resolvedUnit,
+      this.oldContents, String newContents, int offset, int oldLength,
+      int newLength) {
+    this._newContents = newContents;
+    this._offset = offset;
+    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 get hasWork => _oldLength > 0 || _newLength > 0;
+
+  /**
+   * Return the current contents for the receiver's source.
+   *
+   * @return the contents (not `null`)
+   */
+  String get newContents => _newContents;
+
+  /**
+   * Return the number of characters in the replacement text.
+   *
+   * @return the replacement length (zero or greater)
+   */
+  int get newLength => _newLength;
+
+  /**
+   * Return the character position of the first changed character.
+   *
+   * @return the offset (zero or greater)
+   */
+  int get offset => _offset;
+
+  /**
+   * Return the number of characters that were replaced.
+   *
+   * @return the replaced length (zero or greater)
+   */
+  int get oldLength => _oldLength;
+
+  /**
+   * 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
+   * @param source the source being updated (not `null`)
+   * @return the cache used for incremental analysis or `null` if incremental analysis cannot
+   *         be performed
+   */
+  static IncrementalAnalysisCache clear(
+      IncrementalAnalysisCache cache, Source source) {
+    if (cache == null || cache.source == source) {
+      return null;
+    }
+    return cache;
+  }
+
+  /**
+   * Determine if incremental analysis can be performed from the given information.
+   *
+   * @param cache the prior cache or `null` if none
+   * @param source the source being updated (not `null`)
+   * @param oldContents the original source contents prior to this update (may be `null`)
+   * @param newContents the new contents after this incremental change (not `null`)
+   * @param offset the offset at which the change occurred
+   * @param oldLength the length of the text being replaced
+   * @param newLength the length of the replacement text
+   * @param sourceEntry the cached entry for the given source or `null` if none
+   * @return the cache used for incremental analysis or `null` if incremental analysis cannot
+   *         be performed
+   */
+  static IncrementalAnalysisCache update(IncrementalAnalysisCache cache,
+      Source source, String oldContents, String newContents, int offset,
+      int oldLength, int newLength, SourceEntry sourceEntry) {
+    // Determine the cache resolved unit
+    Source librarySource = null;
+    CompilationUnit unit = null;
+    if (sourceEntry is DartEntry) {
+      DartEntry dartEntry = sourceEntry;
+      List<Source> librarySources = dartEntry.librariesContaining;
+      if (librarySources.length == 1) {
+        librarySource = librarySources[0];
+        if (librarySource != null) {
+          unit = dartEntry.getValueInLibrary(
+              DartEntry.RESOLVED_UNIT, librarySource);
+        }
+      }
+    }
+    // Create a new cache if there is not an existing cache or the source is
+    // different or a new resolved compilation unit is available.
+    if (cache == null || cache.source != source || unit != null) {
+      if (unit == null) {
+        return null;
+      }
+      if (oldContents == null) {
+        if (oldLength != 0) {
+          return null;
+        }
+        oldContents =
+            "${newContents.substring(0, offset)}${newContents.substring(offset + newLength)}";
+      }
+      return new IncrementalAnalysisCache(librarySource, source, unit,
+          oldContents, newContents, offset, oldLength, newLength);
+    }
+    // Update the existing cache if the change is contiguous
+    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;
+    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.equalNodes(cache.resolvedUnit, unit)) {
+        return null;
+      }
+    }
+    return cache;
+  }
+}
+
+/**
+ * Instances of the class `IncrementalAnalysisTask` incrementally update existing analysis.
+ */
+class IncrementalAnalysisTask extends AnalysisTask {
+  /**
+   * The information used to perform incremental analysis.
+   */
+  final IncrementalAnalysisCache cache;
+
+  /**
+   * The compilation unit that was produced by incrementally updating the existing unit.
+   */
+  CompilationUnit _updatedUnit;
+
+  /**
+   * Initialize a newly created task to perform analysis within the given context.
+   *
+   * @param context the context in which the task is to be performed
+   * @param cache the incremental analysis cache used to perform the analysis
+   */
+  IncrementalAnalysisTask(InternalAnalysisContext context, this.cache)
+      : super(context);
+
+  /**
+   * Return the compilation unit that was produced by incrementally updating the existing
+   * compilation unit, or `null` if the task has not yet been performed, could not be
+   * performed, or if an exception occurred.
+   *
+   * @return the compilation unit
+   */
+  CompilationUnit get compilationUnit => _updatedUnit;
+
+  /**
+   * Return the source that is to be incrementally analyzed.
+   *
+   * @return the source
+   */
+  Source get source => cache != null ? cache.source : null;
+
+  @override
+  String get taskDescription =>
+      "incremental analysis ${cache != null ? cache.source : "null"}";
+
+  /**
+   * Return the type provider used for incremental resolution.
+   *
+   * @return the type provider (or `null` if an exception occurs)
+   */
+  TypeProvider get typeProvider {
+    try {
+      return context.typeProvider;
+    } on AnalysisException {
+      return null;
+    }
+  }
+
+  @override
+  accept(AnalysisTaskVisitor visitor) =>
+      visitor.visitIncrementalAnalysisTask(this);
+
+  @override
+  void internalPerform() {
+    if (cache == null) {
+      return;
+    }
+    // Only handle small changes
+    if (cache.oldLength > 0 || cache.newLength > 30) {
+      return;
+    }
+    // Produce an updated token stream
+    CharacterReader reader = new CharSequenceReader(cache.newContents);
+    BooleanErrorListener errorListener = new BooleanErrorListener();
+    IncrementalScanner scanner = new IncrementalScanner(
+        cache.source, reader, errorListener, context.analysisOptions);
+    scanner.rescan(cache.resolvedUnit.beginToken, cache.offset, cache.oldLength,
+        cache.newLength);
+    if (errorListener.errorReported) {
+      return;
+    }
+    // Produce an updated AST
+    IncrementalParser parser = new IncrementalParser(
+        cache.source, scanner.tokenMap, AnalysisErrorListener.NULL_LISTENER);
+    _updatedUnit = parser.reparse(cache.resolvedUnit, scanner.leftToken,
+        scanner.rightToken, cache.offset, cache.offset + cache.oldLength);
+    // Update the resolution
+    TypeProvider typeProvider = this.typeProvider;
+    if (_updatedUnit != null && typeProvider != null) {
+      CompilationUnitElement element = _updatedUnit.element;
+      if (element != null) {
+        LibraryElement library = element.library;
+        if (library != null) {
+          IncrementalResolver resolver = new IncrementalResolver(
+              element, cache.offset, cache.oldLength, cache.newLength);
+          resolver.resolve(parser.updatedNode);
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Additional behavior for an analysis context that is required by internal
+ * users of the context.
+ */
+abstract class InternalAnalysisContext implements AnalysisContext {
+  /**
+   * Allow the client to supply its own content cache.  This will take the
+   * place of the content cache created by default, allowing clients to share
+   * the content cache between contexts.
+   */
+  set contentCache(ContentCache value);
+
+  /**
+   * Return a list of the explicit targets being analyzed by this context.
+   */
+  List<AnalysisTarget> get explicitTargets;
+
+  /**
+   * A factory to override how [LibraryResolver] is created.
+   */
+  LibraryResolverFactory get libraryResolverFactory;
+
+  /**
+   * Return a list containing all of the sources that have been marked as
+   * priority sources. Clients must not modify the returned list.
+   */
+  List<Source> get prioritySources;
+
+  /**
+   * Return a list of the priority targets being analyzed by this context.
+   */
+  List<AnalysisTarget> get priorityTargets;
+
+  /**
+   * A factory to override how [ResolverVisitor] is created.
+   */
+  ResolverVisitorFactory get resolverVisitorFactory;
+
+  /**
+   * Returns a statistics about this context.
+   */
+  AnalysisContextStatistics get statistics;
+
+  /**
+   * Sets the [TypeProvider] for this context.
+   */
+  void set typeProvider(TypeProvider typeProvider);
+
+  /**
+   * A factory to override how [TypeResolverVisitor] is created.
+   */
+  TypeResolverVisitorFactory get typeResolverVisitorFactory;
+
+  /**
+   * Add the given [source] with the given [information] to this context.
+   */
+  void addSourceInfo(Source source, SourceEntry information);
+
+  /**
+   * Return a list containing the sources of the libraries that are exported by
+   * the library with the given [source]. The list will be empty if the given
+   * source is invalid, if the given source does not represent a library, or if
+   * the library does not export any other libraries.
+   *
+   * Throws an [AnalysisException] if the exported libraries could not be
+   * computed.
+   */
+  List<Source> computeExportedLibraries(Source source);
+
+  /**
+   * Return a list containing the sources of the libraries that are imported by
+   * the library with the given [source]. The list will be empty if the given
+   * source is invalid, if the given source does not represent a library, or if
+   * the library does not import any other libraries.
+   *
+   * Throws an [AnalysisException] if the imported libraries could not be
+   * computed.
+   */
+  List<Source> computeImportedLibraries(Source source);
+
+  /**
+   * Return an AST structure corresponding to the given [source], but ensure
+   * that the structure has not already been resolved and will not be resolved
+   * by any other threads or in any other library.
+   *
+   * Throws an [AnalysisException] if the analysis could not be performed.
+   *
+   * <b>Note:</b> This method cannot be used in an async environment
+   */
+  CompilationUnit computeResolvableCompilationUnit(Source source);
+
+  /**
+   * Return all the resolved [CompilationUnit]s for the given [source] if not
+   * flushed, otherwise return `null` and ensures that the [CompilationUnit]s
+   * will be eventually returned to the client from [performAnalysisTask].
+   */
+  List<CompilationUnit> ensureResolvedDartUnits(Source source);
+
+  /**
+   * Return the cache entry associated with the given [target].
+   */
+  cache.CacheEntry getCacheEntry(AnalysisTarget target);
+
+  /**
+   * Return context that owns the given [source].
+   */
+  InternalAnalysisContext getContextFor(Source source);
+
+  /**
+   * Return a namespace containing mappings for all of the public names defined
+   * by the given [library].
+   */
+  Namespace getPublicNamespace(LibraryElement library);
+
+  /**
+   * Respond to a change which has been made to the given [source] file.
+   * [originalContents] is the former contents of the file, and [newContents]
+   * is the updated contents.  If [notify] is true, a source changed event is
+   * triggered.
+   *
+   * Normally it should not be necessary for clients to call this function,
+   * since it will be automatically invoked in response to a call to
+   * [applyChanges] or [setContents].  However, if this analysis context is
+   * sharing its content cache with other contexts, then the client must
+   * manually update the content cache and call this function for each context.
+   *
+   * Return `true` if the change was significant to this context (i.e. [source]
+   * is either implicitly or explicitly analyzed by this context, and a change
+   * actually occurred).
+   */
+  bool handleContentsChanged(
+      Source source, String originalContents, String newContents, bool notify);
+
+  /**
+   * Given an [elementMap] mapping the source for the libraries represented by
+   * the corresponding elements to the elements representing the libraries,
+   * record those mappings.
+   */
+  void recordLibraryElements(Map<Source, LibraryElement> elementMap);
+
+  /**
+   * Call the given callback function for eache cache item in the context.
+   */
+  void visitCacheItems(void callback(Source source, SourceEntry dartEntry,
+      DataDescriptor rowDesc, CacheState state));
+}
+
+/**
+ * An object that can be used to receive information about errors within the
+ * analysis engine. Implementations usually write this information to a file,
+ * but can also record the information for later use (such as during testing) or
+ * even ignore the information.
+ */
+abstract class Logger {
+  /**
+   * A logger that ignores all logging.
+   */
+  static final Logger NULL = new NullLogger();
+
+  /**
+   * Log the given message as an error. The [message] is expected to be an
+   * explanation of why the error occurred or what it means. The [exception] is
+   * expected to be the reason for the error. At least one argument must be
+   * provided.
+   */
+  void logError(String message, [CaughtException exception]);
+
+  /**
+   * Log the given [exception] as one representing an error. The [message] is an
+   * explanation of why the error occurred or what it means.
+   */
+  @deprecated
+  void logError2(String message, Object exception);
+
+  /**
+   * Log the given informational message. The [message] is expected to be an
+   * explanation of why the error occurred or what it means. The [exception] is
+   * expected to be the reason for the error.
+   */
+  void logInformation(String message, [CaughtException exception]);
+
+  /**
+   * Log the given [exception] as one representing an informational message. The
+   * [message] is an explanation of why the error occurred or what it means.
+   */
+  @deprecated
+  void logInformation2(String message, Object exception);
+}
+
+/**
+ * An implementation of [Logger] that does nothing.
+ */
+class NullLogger implements Logger {
+  @override
+  void logError(String message, [CaughtException exception]) {}
+
+  @override
+  void logError2(String message, Object exception) {}
+
+  @override
+  void logInformation(String message, [CaughtException exception]) {}
+
+  @override
+  void logInformation2(String message, Object exception) {}
+}
+
+/**
+ * An exception created when an analysis attempt fails because a source was
+ * deleted between the time the analysis started and the time the results of the
+ * analysis were ready to be recorded.
+ */
+class ObsoleteSourceAnalysisException extends AnalysisException {
+  /**
+   * The source that was removed while it was being analyzed.
+   */
+  Source _source;
+
+  /**
+   * Initialize a newly created exception to represent the removal of the given
+   * [source].
+   */
+  ObsoleteSourceAnalysisException(Source source) : super(
+          "The source '${source.fullName}' was removed while it was being analyzed") {
+    this._source = source;
+  }
+
+  /**
+   * Return the source that was removed while it was being analyzed.
+   */
+  Source get source => _source;
+}
+
+/**
+ * Instances of the class `ParseDartTask` parse a specific source as a Dart file.
+ */
+class ParseDartTask extends AnalysisTask {
+  /**
+   * The source to be parsed.
+   */
+  final Source source;
+
+  /**
+   * The head of the token stream used for parsing.
+   */
+  final Token _tokenStream;
+
+  /**
+   * The line information associated with the source.
+   */
+  final LineInfo lineInfo;
+
+  /**
+   * The compilation unit that was produced by parsing the source.
+   */
+  CompilationUnit _unit;
+
+  /**
+   * A flag indicating whether the source contains a 'part of' directive.
+   */
+  bool _containsPartOfDirective = false;
+
+  /**
+   * A flag indicating whether the source contains any directive other than a 'part of' directive.
+   */
+  bool _containsNonPartOfDirective = false;
+
+  /**
+   * A set containing the sources referenced by 'export' directives.
+   */
+  HashSet<Source> _exportedSources = new HashSet<Source>();
+
+  /**
+   * A set containing the sources referenced by 'import' directives.
+   */
+  HashSet<Source> _importedSources = new HashSet<Source>();
+
+  /**
+   * A set containing the sources referenced by 'part' directives.
+   */
+  HashSet<Source> _includedSources = new HashSet<Source>();
+
+  /**
+   * The errors that were produced by scanning and parsing the source.
+   */
+  List<AnalysisError> _errors = AnalysisError.NO_ERRORS;
+
+  /**
+   * Initialize a newly created task to perform analysis within the given context.
+   *
+   * @param context the context in which the task is to be performed
+   * @param source the source to be parsed
+   * @param tokenStream the head of the token stream used for parsing
+   * @param lineInfo the line information associated with the source
+   */
+  ParseDartTask(InternalAnalysisContext context, this.source, this._tokenStream,
+      this.lineInfo)
+      : super(context);
+
+  /**
+   * Return the compilation unit that was produced by parsing the source, or `null` if the
+   * task has not yet been performed or if an exception occurred.
+   *
+   * @return the compilation unit that was produced by parsing the source
+   */
+  CompilationUnit get compilationUnit => _unit;
+
+  /**
+   * Return the errors that were produced by scanning and parsing the source, or an empty list if
+   * the task has not yet been performed or if an exception occurred.
+   *
+   * @return the errors that were produced by scanning and parsing the source
+   */
+  List<AnalysisError> get errors => _errors;
+
+  /**
+   * Return a list containing the sources referenced by 'export' directives, or an empty list if
+   * the task has not yet been performed or if an exception occurred.
+   *
+   * @return an list containing the sources referenced by 'export' directives
+   */
+  List<Source> get exportedSources => _toArray(_exportedSources);
+
+  /**
+   * Return `true` if the source contains any directive other than a 'part of' directive, or
+   * `false` if the task has not yet been performed or if an exception occurred.
+   *
+   * @return `true` if the source contains any directive other than a 'part of' directive
+   */
+  bool get hasNonPartOfDirective => _containsNonPartOfDirective;
+
+  /**
+   * Return `true` if the source contains a 'part of' directive, or `false` if the task
+   * has not yet been performed or if an exception occurred.
+   *
+   * @return `true` if the source contains a 'part of' directive
+   */
+  bool get hasPartOfDirective => _containsPartOfDirective;
+
+  /**
+   * Return a list containing the sources referenced by 'import' directives, or an empty list if
+   * the task has not yet been performed or if an exception occurred.
+   *
+   * @return a list containing the sources referenced by 'import' directives
+   */
+  List<Source> get importedSources => _toArray(_importedSources);
+
+  /**
+   * Return a list containing the sources referenced by 'part' directives, or an empty list if
+   * the task has not yet been performed or if an exception occurred.
+   *
+   * @return a list containing the sources referenced by 'part' directives
+   */
+  List<Source> get includedSources => _toArray(_includedSources);
+
+  @override
+  String get taskDescription {
+    if (source == null) {
+      return "parse as dart null source";
+    }
+    return "parse as dart ${source.fullName}";
+  }
+
+  @override
+  accept(AnalysisTaskVisitor visitor) => visitor.visitParseDartTask(this);
+
+  @override
+  void internalPerform() {
+    //
+    // Then parse the token stream.
+    //
+    PerformanceStatistics.parse.makeCurrentWhile(() {
+      RecordingErrorListener errorListener = new RecordingErrorListener();
+      Parser parser = new Parser(source, errorListener);
+      AnalysisOptions options = context.analysisOptions;
+      parser.parseFunctionBodies =
+          options.analyzeFunctionBodiesPredicate(source);
+      _unit = parser.parseCompilationUnit(_tokenStream);
+      _unit.lineInfo = lineInfo;
+      AnalysisContext analysisContext = context;
+      for (Directive directive in _unit.directives) {
+        if (directive is PartOfDirective) {
+          _containsPartOfDirective = true;
+        } else {
+          _containsNonPartOfDirective = true;
+          if (directive is UriBasedDirective) {
+            Source referencedSource = resolveDirective(
+                analysisContext, source, directive, errorListener);
+            if (referencedSource != null) {
+              if (directive is ExportDirective) {
+                _exportedSources.add(referencedSource);
+              } else if (directive is ImportDirective) {
+                _importedSources.add(referencedSource);
+              } else if (directive is PartDirective) {
+                if (referencedSource != source) {
+                  _includedSources.add(referencedSource);
+                }
+              } else {
+                throw new AnalysisException(
+                    "$runtimeType failed to handle a ${directive.runtimeType}");
+              }
+            }
+          }
+        }
+      }
+      _errors = errorListener.getErrorsForSource(source);
+    });
+  }
+
+  /**
+   * Efficiently convert the given set of [sources] to a list.
+   */
+  List<Source> _toArray(HashSet<Source> sources) {
+    int size = sources.length;
+    if (size == 0) {
+      return Source.EMPTY_ARRAY;
+    }
+    return new List.from(sources);
+  }
+
+  /**
+   * Return the result of resolving the URI of the given URI-based directive against the URI of the
+   * given library, or `null` if the URI is not valid.
+   *
+   * @param context the context in which the resolution is to be performed
+   * @param librarySource the source representing the library containing the directive
+   * @param directive the directive which URI should be resolved
+   * @param errorListener the error listener to which errors should be reported
+   * @return the result of resolving the URI against the URI of the library
+   */
+  static Source resolveDirective(AnalysisContext context, Source librarySource,
+      UriBasedDirective directive, AnalysisErrorListener errorListener) {
+    StringLiteral uriLiteral = directive.uri;
+    String uriContent = uriLiteral.stringValue;
+    if (uriContent != null) {
+      uriContent = uriContent.trim();
+      directive.uriContent = uriContent;
+    }
+    UriValidationCode code = directive.validate();
+    if (code == null) {
+      String encodedUriContent = Uri.encodeFull(uriContent);
+      try {
+        Source source =
+            context.sourceFactory.resolveUri(librarySource, encodedUriContent);
+        directive.source = source;
+        return source;
+      } on JavaIOException {
+        code = UriValidationCode.INVALID_URI;
+      }
+    }
+    if (code == UriValidationCode.URI_WITH_DART_EXT_SCHEME) {
+      return null;
+    }
+    if (code == UriValidationCode.URI_WITH_INTERPOLATION) {
+      errorListener.onError(new AnalysisError.con2(librarySource,
+          uriLiteral.offset, uriLiteral.length,
+          CompileTimeErrorCode.URI_WITH_INTERPOLATION));
+      return null;
+    }
+    if (code == UriValidationCode.INVALID_URI) {
+      errorListener.onError(new AnalysisError.con2(librarySource,
+          uriLiteral.offset, uriLiteral.length,
+          CompileTimeErrorCode.INVALID_URI, [uriContent]));
+      return null;
+    }
+    throw new RuntimeException(
+        message: "Failed to handle validation code: $code");
+  }
+}
+
+/**
+ * Instances of the class `ParseHtmlTask` parse a specific source as an HTML file.
+ */
+class ParseHtmlTask extends AnalysisTask {
+  /**
+   * The name of the 'src' attribute in a HTML tag.
+   */
+  static String _ATTRIBUTE_SRC = "src";
+
+  /**
+   * The name of the 'script' tag in an HTML file.
+   */
+  static String _TAG_SCRIPT = "script";
+
+  /**
+   * The source to be parsed.
+   */
+  final Source source;
+
+  /**
+   * The contents of the source.
+   */
+  final String _content;
+
+  /**
+   * The line information that was produced.
+   */
+  LineInfo _lineInfo;
+
+  /**
+   * The HTML unit that was produced by parsing the source.
+   */
+  ht.HtmlUnit _unit;
+
+  /**
+   * The errors that were produced by scanning and parsing the source.
+   */
+  List<AnalysisError> _errors = AnalysisError.NO_ERRORS;
+
+  /**
+   * A list containing the sources of the libraries that are referenced within the HTML.
+   */
+  List<Source> _referencedLibraries = Source.EMPTY_ARRAY;
+
+  /**
+   * Initialize a newly created task to perform analysis within the given context.
+   *
+   * @param context the context in which the task is to be performed
+   * @param source the source to be parsed
+   * @param content the contents of the source
+   */
+  ParseHtmlTask(InternalAnalysisContext context, this.source, this._content)
+      : super(context);
+
+  /**
+   * Return the errors that were produced by scanning and parsing the source, or `null` if the
+   * task has not yet been performed or if an exception occurred.
+   *
+   * @return the errors that were produced by scanning and parsing the source
+   */
+  List<AnalysisError> get errors => _errors;
+
+  /**
+   * Return the HTML unit that was produced by parsing the source.
+   *
+   * @return the HTML unit that was produced by parsing the source
+   */
+  ht.HtmlUnit get htmlUnit => _unit;
+
+  /**
+   * Return the sources of libraries that are referenced in the specified HTML file.
+   *
+   * @return the sources of libraries that are referenced in the HTML file
+   */
+  List<Source> get librarySources {
+    List<Source> libraries = new List<Source>();
+    _unit.accept(new ParseHtmlTask_getLibrarySources(this, libraries));
+    if (libraries.isEmpty) {
+      return Source.EMPTY_ARRAY;
+    }
+    return libraries;
+  }
+
+  /**
+   * Return the line information that was produced, or `null` if the task has not yet been
+   * performed or if an exception occurred.
+   *
+   * @return the line information that was produced
+   */
+  LineInfo get lineInfo => _lineInfo;
+
+  /**
+   * Return a list containing the sources of the libraries that are referenced within the HTML.
+   *
+   * @return the sources of the libraries that are referenced within the HTML
+   */
+  List<Source> get referencedLibraries => _referencedLibraries;
+
+  @override
+  String get taskDescription {
+    if (source == null) {
+      return "parse as html null source";
+    }
+    return "parse as html ${source.fullName}";
+  }
+
+  @override
+  accept(AnalysisTaskVisitor visitor) => visitor.visitParseHtmlTask(this);
+
+  @override
+  void internalPerform() {
+    try {
+      ht.AbstractScanner scanner = new ht.StringScanner(source, _content);
+      scanner.passThroughElements = <String>[_TAG_SCRIPT];
+      ht.Token token = scanner.tokenize();
+      _lineInfo = new LineInfo(scanner.lineStarts);
+      RecordingErrorListener errorListener = new RecordingErrorListener();
+      _unit = new ht.HtmlParser(source, errorListener, context.analysisOptions)
+          .parse(token, _lineInfo);
+      _unit.accept(new RecursiveXmlVisitor_ParseHtmlTask_internalPerform(
+          this, errorListener));
+      _errors = errorListener.getErrorsForSource(source);
+      _referencedLibraries = librarySources;
+    } catch (exception, stackTrace) {
+      throw new AnalysisException(
+          "Exception", new CaughtException(exception, stackTrace));
+    }
+  }
+
+  /**
+   * Resolves directives in the given [CompilationUnit].
+   */
+  void _resolveScriptDirectives(
+      CompilationUnit script, AnalysisErrorListener errorListener) {
+    if (script == null) {
+      return;
+    }
+    AnalysisContext analysisContext = context;
+    for (Directive directive in script.directives) {
+      if (directive is UriBasedDirective) {
+        ParseDartTask.resolveDirective(
+            analysisContext, source, directive, errorListener);
+      }
+    }
+  }
+}
+
+class ParseHtmlTask_getLibrarySources extends ht.RecursiveXmlVisitor<Object> {
+  final ParseHtmlTask _task;
+
+  List<Source> libraries;
+
+  ParseHtmlTask_getLibrarySources(this._task, this.libraries) : super();
+
+  @override
+  Object visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) {
+    ht.XmlAttributeNode scriptAttribute = null;
+    for (ht.XmlAttributeNode attribute in node.attributes) {
+      if (javaStringEqualsIgnoreCase(
+          attribute.name, ParseHtmlTask._ATTRIBUTE_SRC)) {
+        scriptAttribute = attribute;
+      }
+    }
+    if (scriptAttribute != null) {
+      try {
+        Uri uri = Uri.parse(scriptAttribute.text);
+        String fileName = uri.path;
+        Source librarySource =
+            _task.context.sourceFactory.resolveUri(_task.source, fileName);
+        if (_task.context.exists(librarySource)) {
+          libraries.add(librarySource);
+        }
+      } on FormatException {
+        // ignored - invalid URI reported during resolution phase
+      }
+    }
+    return super.visitHtmlScriptTagNode(node);
+  }
+}
+
+/**
+ * An object that manages the partitions that can be shared between analysis
+ * contexts.
+ */
+class PartitionManager {
+  /**
+   * The default cache size for a Dart SDK partition.
+   */
+  static int _DEFAULT_SDK_CACHE_SIZE = 256;
+
+  /**
+   * A table mapping SDK's to the partitions used for those SDK's.
+   */
+  HashMap<DartSdk, SdkCachePartition> _sdkPartitions =
+      new HashMap<DartSdk, SdkCachePartition>();
+
+  /**
+   * Clear any cached data being maintained by this manager.
+   */
+  void clearCache() {
+    _sdkPartitions.clear();
+  }
+
+  /**
+   * Return the partition being used for the given [sdk], creating the partition
+   * if necessary.
+   */
+  SdkCachePartition forSdk(DartSdk sdk) {
+    // Call sdk.context now, because when it creates a new
+    // InternalAnalysisContext instance, it calls forSdk() again, so creates an
+    // SdkCachePartition instance.
+    // So, if we initialize context after "partition == null", we end up
+    // with two SdkCachePartition instances.
+    InternalAnalysisContext sdkContext = sdk.context;
+    // Check cache for an existing partition.
+    SdkCachePartition partition = _sdkPartitions[sdk];
+    if (partition == null) {
+      partition = new SdkCachePartition(sdkContext, _DEFAULT_SDK_CACHE_SIZE);
+      _sdkPartitions[sdk] = partition;
+    }
+    return partition;
+  }
+}
+
+/**
+ * Representation of a pending computation which is based on the results of
+ * analysis that may or may not have been completed.
+ */
+class PendingFuture<T> {
+  /**
+   * The context in which this computation runs.
+   */
+  final AnalysisContextImpl _context;
+
+  /**
+   * The source used by this computation to compute its value.
+   */
+  final Source source;
+
+  /**
+   * The function which implements the computation.
+   */
+  final PendingFutureComputer<T> _computeValue;
+
+  /**
+   * The completer that should be completed once the computation has succeeded.
+   */
+  CancelableCompleter<T> _completer;
+
+  PendingFuture(this._context, this.source, this._computeValue) {
+    _completer = new CancelableCompleter<T>(_onCancel);
+  }
+
+  /**
+   * Retrieve the future which will be completed when this object is
+   * successfully evaluated.
+   */
+  CancelableFuture<T> get future => _completer.future;
+
+  /**
+   * Execute [_computeValue], passing it the given [sourceEntry], and complete
+   * the pending future if it's appropriate to do so.  If the pending future is
+   * completed by this call, true is returned; otherwise false is returned.
+   *
+   * Once this function has returned true, it should not be called again.
+   *
+   * Other than completing the future, this method is free of side effects.
+   * Note that any code the client has attached to the future will be executed
+   * in a microtask, so there is no danger of side effects occurring due to
+   * client callbacks.
+   */
+  bool evaluate(SourceEntry sourceEntry) {
+    assert(!_completer.isCompleted);
+    try {
+      T result = _computeValue(sourceEntry);
+      if (result == null) {
+        return false;
+      } else {
+        _completer.complete(result);
+        return true;
+      }
+    } catch (exception, stackTrace) {
+      _completer.completeError(exception, stackTrace);
+      return true;
+    }
+  }
+
+  /**
+   * No further analysis updates are expected which affect this future, so
+   * complete it with an AnalysisNotScheduledError in order to avoid
+   * deadlocking the client.
+   */
+  void forciblyComplete() {
+    try {
+      throw new AnalysisNotScheduledError();
+    } catch (exception, stackTrace) {
+      _completer.completeError(exception, stackTrace);
+    }
+  }
+
+  void _onCancel() {
+    _context._cancelFuture(this);
+  }
+}
+
+/**
+ * Container with global [AnalysisContext] performance statistics.
+ */
+class PerformanceStatistics {
+  /**
+   * The [PerformanceTag] for time spent in reading files.
+   */
+  static PerformanceTag io = new PerformanceTag('io');
+
+  /**
+   * The [PerformanceTag] for time spent in scanning.
+   */
+  static PerformanceTag scan = new PerformanceTag('scan');
+
+  /**
+   * The [PerformanceTag] for time spent in parsing.
+   */
+  static PerformanceTag parse = new PerformanceTag('parse');
+
+  /**
+   * The [PerformanceTag] for time spent in resolving.
+   */
+  static PerformanceTag resolve = new PerformanceTag('resolve');
+
+  /**
+   * The [PerformanceTag] for time spent in error verifier.
+   */
+  static PerformanceTag errors = new PerformanceTag('errors');
+
+  /**
+   * The [PerformanceTag] for time spent in hints generator.
+   */
+  static PerformanceTag hints = new PerformanceTag('hints');
+
+  /**
+   * The [PerformanceTag] for time spent in linting.
+   */
+  static PerformanceTag lint = new PerformanceTag('lint');
+
+  /**
+   * The [PerformanceTag] for time spent computing cycles.
+   */
+  static PerformanceTag cycles = new PerformanceTag('cycles');
+
+  /**
+   * The [PerformanceTag] for time spent in other phases of analysis.
+   */
+  static PerformanceTag performAnaysis = new PerformanceTag('performAnaysis');
+
+  /**
+   * The [PerformanceTag] for time spent in the analysis task visitor after
+   * tasks are complete.
+   */
+  static PerformanceTag analysisTaskVisitor =
+      new PerformanceTag('analysisTaskVisitor');
+
+  /**
+   * The [PerformanceTag] for time spent in the getter
+   * AnalysisContextImpl.nextAnalysisTask.
+   */
+  static var nextTask = new PerformanceTag('nextAnalysisTask');
+
+  /**
+   * The [PerformanceTag] for time spent during otherwise not accounted parts
+   * incremental of analysis.
+   */
+  static PerformanceTag incrementalAnalysis =
+      new PerformanceTag('incrementalAnalysis');
+}
+
+/**
+ * An error listener that will record the errors that are reported to it in a
+ * way that is appropriate for caching those errors within an analysis context.
+ */
+class RecordingErrorListener implements AnalysisErrorListener {
+  /**
+   * A map of sets containing the errors that were collected, keyed by each
+   * source.
+   */
+  Map<Source, HashSet<AnalysisError>> _errors =
+      new HashMap<Source, HashSet<AnalysisError>>();
+
+  /**
+   * Return the errors collected by the listener.
+   */
+  List<AnalysisError> get errors {
+    int numEntries = _errors.length;
+    if (numEntries == 0) {
+      return AnalysisError.NO_ERRORS;
+    }
+    List<AnalysisError> resultList = new List<AnalysisError>();
+    for (HashSet<AnalysisError> errors in _errors.values) {
+      resultList.addAll(errors);
+    }
+    return resultList;
+  }
+
+  /**
+   * Add all of the errors recorded by the given [listener] to this listener.
+   */
+  void addAll(RecordingErrorListener listener) {
+    for (AnalysisError error in listener.errors) {
+      onError(error);
+    }
+  }
+
+  /**
+   * Return the errors collected by the listener for the given [source].
+   */
+  List<AnalysisError> getErrorsForSource(Source source) {
+    HashSet<AnalysisError> errorsForSource = _errors[source];
+    if (errorsForSource == null) {
+      return AnalysisError.NO_ERRORS;
+    } else {
+      return new List.from(errorsForSource);
+    }
+  }
+
+  @override
+  void onError(AnalysisError error) {
+    Source source = error.source;
+    HashSet<AnalysisError> errorsForSource = _errors[source];
+    if (_errors[source] == null) {
+      errorsForSource = new HashSet<AnalysisError>();
+      _errors[source] = errorsForSource;
+    }
+    errorsForSource.add(error);
+  }
+}
+
+class RecursiveXmlVisitor_ParseHtmlTask_internalPerform
+    extends ht.RecursiveXmlVisitor<Object> {
+  final ParseHtmlTask ParseHtmlTask_this;
+
+  RecordingErrorListener errorListener;
+
+  RecursiveXmlVisitor_ParseHtmlTask_internalPerform(
+      this.ParseHtmlTask_this, this.errorListener)
+      : super();
+
+  @override
+  Object visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) {
+    ParseHtmlTask_this._resolveScriptDirectives(node.script, errorListener);
+    return null;
+  }
+}
+
+class RecursiveXmlVisitor_ResolveHtmlTask_internalPerform
+    extends ht.RecursiveXmlVisitor<Object> {
+  final ResolveHtmlTask ResolveHtmlTask_this;
+
+  RecordingErrorListener errorListener;
+
+  RecursiveXmlVisitor_ResolveHtmlTask_internalPerform(
+      this.ResolveHtmlTask_this, this.errorListener)
+      : super();
+
+  @override
+  Object visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) {
+    CompilationUnit script = node.script;
+    if (script != null) {
+      GenerateDartErrorsTask.validateDirectives(ResolveHtmlTask_this.context,
+          ResolveHtmlTask_this.source, script, errorListener);
+    }
+    return null;
+  }
+}
+
+/**
+ * An visitor that removes any resolution information from an AST structure when
+ * used to visit that structure.
+ */
+class ResolutionEraser extends GeneralizingAstVisitor<Object> {
+  @override
+  Object visitAssignmentExpression(AssignmentExpression node) {
+    node.staticElement = null;
+    node.propagatedElement = null;
+    return super.visitAssignmentExpression(node);
+  }
+
+  @override
+  Object visitBinaryExpression(BinaryExpression node) {
+    node.staticElement = null;
+    node.propagatedElement = null;
+    return super.visitBinaryExpression(node);
+  }
+
+  @override
+  Object visitBreakStatement(BreakStatement node) {
+    node.target = null;
+    return super.visitBreakStatement(node);
+  }
+
+  @override
+  Object visitCompilationUnit(CompilationUnit node) {
+    node.element = null;
+    return super.visitCompilationUnit(node);
+  }
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    node.element = null;
+    return super.visitConstructorDeclaration(node);
+  }
+
+  @override
+  Object visitConstructorName(ConstructorName node) {
+    node.staticElement = null;
+    return super.visitConstructorName(node);
+  }
+
+  @override
+  Object visitContinueStatement(ContinueStatement node) {
+    node.target = null;
+    return super.visitContinueStatement(node);
+  }
+
+  @override
+  Object visitDirective(Directive node) {
+    node.element = null;
+    return super.visitDirective(node);
+  }
+
+  @override
+  Object visitExpression(Expression node) {
+    node.staticType = null;
+    node.propagatedType = null;
+    return super.visitExpression(node);
+  }
+
+  @override
+  Object visitFunctionExpression(FunctionExpression node) {
+    node.element = null;
+    return super.visitFunctionExpression(node);
+  }
+
+  @override
+  Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    node.staticElement = null;
+    node.propagatedElement = null;
+    return super.visitFunctionExpressionInvocation(node);
+  }
+
+  @override
+  Object visitIndexExpression(IndexExpression node) {
+    node.staticElement = null;
+    node.propagatedElement = null;
+    return super.visitIndexExpression(node);
+  }
+
+  @override
+  Object visitInstanceCreationExpression(InstanceCreationExpression node) {
+    node.staticElement = null;
+    return super.visitInstanceCreationExpression(node);
+  }
+
+  @override
+  Object visitPostfixExpression(PostfixExpression node) {
+    node.staticElement = null;
+    node.propagatedElement = null;
+    return super.visitPostfixExpression(node);
+  }
+
+  @override
+  Object visitPrefixExpression(PrefixExpression node) {
+    node.staticElement = null;
+    node.propagatedElement = null;
+    return super.visitPrefixExpression(node);
+  }
+
+  @override
+  Object visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    node.staticElement = null;
+    return super.visitRedirectingConstructorInvocation(node);
+  }
+
+  @override
+  Object visitSimpleIdentifier(SimpleIdentifier node) {
+    node.staticElement = null;
+    node.propagatedElement = null;
+    return super.visitSimpleIdentifier(node);
+  }
+
+  @override
+  Object visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    node.staticElement = null;
+    return super.visitSuperConstructorInvocation(node);
+  }
+
+  /**
+   * Remove any resolution information from the given AST structure.
+   */
+  static void erase(AstNode node) {
+    node.accept(new ResolutionEraser());
+  }
+}
+
+/**
+ * The information produced by resolving a compilation unit as part of a
+ * specific library.
+ */
+class ResolutionState {
+  /**
+   * The next resolution state or `null` if none.
+   */
+  ResolutionState _nextState;
+
+  /**
+   * The source for the defining compilation unit of the library that contains
+   * this unit. If this unit is the defining compilation unit for it's library,
+   * then this will be the source for this unit.
+   */
+  Source _librarySource;
+
+  /**
+   * A table mapping descriptors to the cached results for those descriptors.
+   * If there is no entry for a given descriptor then the state is implicitly
+   * [CacheState.INVALID] and the value is implicitly the default value.
+   */
+  Map<DataDescriptor, CachedResult> resultMap =
+      new HashMap<DataDescriptor, CachedResult>();
+
+  /**
+   * Flush any AST structures being maintained by this state.
+   */
+  void flushAstStructures() {
+    _flush(DartEntry.BUILT_UNIT);
+    _flush(DartEntry.RESOLVED_UNIT);
+    if (_nextState != null) {
+      _nextState.flushAstStructures();
+    }
+  }
+
+  /**
+   * Return the state of the data represented by the given [descriptor].
+   */
+  CacheState getState(DataDescriptor descriptor) {
+    CachedResult result = resultMap[descriptor];
+    if (result == null) {
+      return CacheState.INVALID;
+    }
+    return result.state;
+  }
+
+  /**
+   * Return the value of the data represented by the given [descriptor], or
+   * `null` if the data represented by the descriptor is not valid.
+   */
+  /*<V>*/ dynamic /*V*/ getValue(DataDescriptor /*<V>*/ descriptor) {
+    CachedResult result = resultMap[descriptor];
+    if (result == null) {
+      return descriptor.defaultValue;
+    }
+    return result.value;
+  }
+
+  /**
+   * Return `true` if the state of any data value is [CacheState.ERROR].
+   */
+  bool hasErrorState() {
+    for (CachedResult result in resultMap.values) {
+      if (result.state == CacheState.ERROR) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Invalidate all of the resolution information associated with the compilation unit.
+   */
+  void invalidateAllResolutionInformation() {
+    _nextState = null;
+    _librarySource = null;
+    setState(DartEntry.BUILT_UNIT, CacheState.INVALID);
+    setState(DartEntry.BUILT_ELEMENT, CacheState.INVALID);
+    setState(DartEntry.HINTS, CacheState.INVALID);
+    setState(DartEntry.LINTS, CacheState.INVALID);
+    setState(DartEntry.RESOLVED_UNIT, CacheState.INVALID);
+    setState(DartEntry.RESOLUTION_ERRORS, CacheState.INVALID);
+    setState(DartEntry.VERIFICATION_ERRORS, CacheState.INVALID);
+  }
+
+  /**
+   * Record that an exception occurred while attempting to build the element
+   * model for the source associated with this state.
+   */
+  void recordBuildElementError() {
+    setState(DartEntry.BUILT_UNIT, CacheState.ERROR);
+    setState(DartEntry.BUILT_ELEMENT, CacheState.ERROR);
+    recordResolutionError();
+  }
+
+  /**
+   * Record that an exception occurred while attempting to generate hints for
+   * the source associated with this entry. This will set the state of all
+   * verification information as being in error.
+   */
+  void recordHintError() {
+    setState(DartEntry.HINTS, CacheState.ERROR);
+  }
+
+  /**
+   * Record that an exception occurred while attempting to generate lints for
+   * the source associated with this entry. This will set the state of all
+   * verification information as being in error.
+   */
+  void recordLintError() {
+    setState(DartEntry.LINTS, CacheState.ERROR);
+  }
+
+  /**
+   * Record that an exception occurred while attempting to resolve the source
+   * associated with this state.
+   */
+  void recordResolutionError() {
+    setState(DartEntry.RESOLVED_UNIT, CacheState.ERROR);
+    setState(DartEntry.RESOLUTION_ERRORS, CacheState.ERROR);
+    recordVerificationError();
+  }
+
+  /**
+   * Record that an exception occurred while attempting to scan or parse the
+   * source associated with this entry. This will set the state of all
+   * resolution-based information as being in error.
+   */
+  void recordResolutionErrorsInAllLibraries() {
+    recordBuildElementError();
+    if (_nextState != null) {
+      _nextState.recordResolutionErrorsInAllLibraries();
+    }
+  }
+
+  /**
+   * Record that an exception occurred while attempting to generate errors and
+   * warnings for the source associated with this entry. This will set the state
+   * of all verification information as being in error.
+   */
+  void recordVerificationError() {
+    setState(DartEntry.VERIFICATION_ERRORS, CacheState.ERROR);
+    recordHintError();
+  }
+
+  /**
+   * Set the state of the data represented by the given [descriptor] to the
+   * given [state].
+   */
+  void setState(DataDescriptor descriptor, CacheState state) {
+    if (state == CacheState.VALID) {
+      throw new ArgumentError("use setValue() to set the state to VALID");
+    }
+    if (state == CacheState.INVALID) {
+      resultMap.remove(descriptor);
+    } else {
+      CachedResult result =
+          resultMap.putIfAbsent(descriptor, () => new CachedResult(descriptor));
+      result.state = state;
+      if (state != CacheState.IN_PROCESS) {
+        //
+        // If the state is in-process, we can leave the current value in the
+        // cache for any 'get' methods to access.
+        //
+        result.value = descriptor.defaultValue;
+      }
+    }
+  }
+
+  /**
+   * Set the value of the data represented by the given [descriptor] to the
+   * given [value].
+   */
+  void setValue(DataDescriptor /*<V>*/ descriptor, dynamic /*V*/ value) {
+    CachedResult result =
+        resultMap.putIfAbsent(descriptor, () => new CachedResult(descriptor));
+    SourceEntry.countTransition(descriptor, result);
+    result.state = CacheState.VALID;
+    result.value = value == null ? descriptor.defaultValue : value;
+  }
+
+  /**
+   * Flush the value of the data described by the [descriptor].
+   */
+  void _flush(DataDescriptor descriptor) {
+    CachedResult result = resultMap[descriptor];
+    if (result != null && result.state == CacheState.VALID) {
+      result.state = CacheState.FLUSHED;
+      result.value = descriptor.defaultValue;
+    }
+  }
+
+  /**
+   * Write a textual representation of the difference between the old entry and
+   * this entry to the given string [buffer]. A separator will be written before
+   * the first difference if [needsSeparator] is `true`. The [oldEntry] is the
+   * entry that was replaced by this entry. Return `true` is a separator is
+   * needed before writing any subsequent differences.
+   */
+  bool _writeDiffOn(
+      StringBuffer buffer, bool needsSeparator, DartEntry oldEntry) {
+    needsSeparator = _writeStateDiffOn(buffer, needsSeparator, "resolvedUnit",
+        DartEntry.RESOLVED_UNIT, oldEntry);
+    needsSeparator = _writeStateDiffOn(buffer, needsSeparator,
+        "resolutionErrors", DartEntry.RESOLUTION_ERRORS, oldEntry);
+    needsSeparator = _writeStateDiffOn(buffer, needsSeparator,
+        "verificationErrors", DartEntry.VERIFICATION_ERRORS, oldEntry);
+    needsSeparator = _writeStateDiffOn(
+        buffer, needsSeparator, "hints", DartEntry.HINTS, oldEntry);
+    needsSeparator = _writeStateDiffOn(
+        buffer, needsSeparator, "lints", DartEntry.LINTS, oldEntry);
+    return needsSeparator;
+  }
+
+  /**
+   * Write a textual representation of this state to the given [buffer]. The
+   * result will only be used for debugging purposes.
+   */
+  void _writeOn(StringBuffer buffer) {
+    if (_librarySource != null) {
+      _writeStateOn(buffer, "builtElement", DartEntry.BUILT_ELEMENT);
+      _writeStateOn(buffer, "builtUnit", DartEntry.BUILT_UNIT);
+      _writeStateOn(buffer, "resolvedUnit", DartEntry.RESOLVED_UNIT);
+      _writeStateOn(buffer, "resolutionErrors", DartEntry.RESOLUTION_ERRORS);
+      _writeStateOn(
+          buffer, "verificationErrors", DartEntry.VERIFICATION_ERRORS);
+      _writeStateOn(buffer, "hints", DartEntry.HINTS);
+      _writeStateOn(buffer, "lints", DartEntry.LINTS);
+      if (_nextState != null) {
+        _nextState._writeOn(buffer);
+      }
+    }
+  }
+
+  /**
+   * Write a textual representation of the difference between the state of the
+   * value described by the given [descriptor] between the [oldEntry] and this
+   * entry to the given [buffer]. Return `true` if some difference was written.
+   */
+  bool _writeStateDiffOn(StringBuffer buffer, bool needsSeparator, String label,
+      DataDescriptor descriptor, SourceEntry oldEntry) {
+    CacheState oldState = oldEntry.getState(descriptor);
+    CacheState newState = getState(descriptor);
+    if (oldState != newState) {
+      if (needsSeparator) {
+        buffer.write("; ");
+      }
+      buffer.write(label);
+      buffer.write(" = ");
+      buffer.write(oldState);
+      buffer.write(" -> ");
+      buffer.write(newState);
+      return true;
+    }
+    return needsSeparator;
+  }
+
+  /**
+   * Write a textual representation of the state of the value described by the
+   * given [descriptor] to the given bugger, prefixed by the given [label] to
+   * the given [buffer].
+   */
+  void _writeStateOn(
+      StringBuffer buffer, String label, DataDescriptor descriptor) {
+    CachedResult result = resultMap[descriptor];
+    buffer.write("; ");
+    buffer.write(label);
+    buffer.write(" = ");
+    buffer.write(result == null ? CacheState.INVALID : result.state);
+  }
+}
+
+/**
+ * A compilation unit that is not referenced by any other objects. It is used by
+ * the [LibraryResolver] to resolve a library.
+ */
+class ResolvableCompilationUnit {
+  /**
+   * The source of the compilation unit.
+   */
+  final Source source;
+
+  /**
+   * The compilation unit.
+   */
+  final CompilationUnit compilationUnit;
+
+  /**
+   * Initialize a newly created holder to hold the given [source] and
+   * [compilationUnit].
+   */
+  ResolvableCompilationUnit(this.source, this.compilationUnit);
+}
+
+/**
+ * Instances of the class `ResolveDartLibraryTask` resolve a specific Dart library.
+ */
+class ResolveDartLibraryCycleTask extends AnalysisTask {
+  /**
+   * The source representing the file whose compilation unit is to be returned. TODO(brianwilkerson)
+   * This should probably be removed, but is being left in for now to ease the transition.
+   */
+  final Source unitSource;
+
+  /**
+   * The source representing the library to be resolved.
+   */
+  final Source librarySource;
+
+  /**
+   * The libraries that are part of the cycle containing the library to be resolved.
+   */
+  final List<ResolvableLibrary> _librariesInCycle;
+
+  /**
+   * The library resolver holding information about the libraries that were resolved.
+   */
+  LibraryResolver2 _resolver;
+
+  /**
+   * Initialize a newly created task to perform analysis within the given context.
+   *
+   * @param context the context in which the task is to be performed
+   * @param unitSource the source representing the file whose compilation unit is to be returned
+   * @param librarySource the source representing the library to be resolved
+   * @param librariesInCycle the libraries that are part of the cycle containing the library to be
+   *          resolved
+   */
+  ResolveDartLibraryCycleTask(InternalAnalysisContext context, this.unitSource,
+      this.librarySource, this._librariesInCycle)
+      : super(context);
+
+  /**
+   * Return the library resolver holding information about the libraries that were resolved.
+   *
+   * @return the library resolver holding information about the libraries that were resolved
+   */
+  LibraryResolver2 get libraryResolver => _resolver;
+
+  @override
+  String get taskDescription {
+    if (librarySource == null) {
+      return "resolve library null source";
+    }
+    return "resolve library ${librarySource.fullName}";
+  }
+
+  @override
+  accept(AnalysisTaskVisitor visitor) =>
+      visitor.visitResolveDartLibraryCycleTask(this);
+
+  @override
+  void internalPerform() {
+    _resolver = new LibraryResolver2(context);
+    _resolver.resolveLibrary(librarySource, _librariesInCycle);
+  }
+}
+
+/**
+ * Instances of the class `ResolveDartLibraryTask` resolve a specific Dart library.
+ */
+class ResolveDartLibraryTask extends AnalysisTask {
+  /**
+   * The source representing the file whose compilation unit is to be returned.
+   */
+  final Source unitSource;
+
+  /**
+   * The source representing the library to be resolved.
+   */
+  final Source librarySource;
+
+  /**
+   * The library resolver holding information about the libraries that were resolved.
+   */
+  LibraryResolver _resolver;
+
+  /**
+   * Initialize a newly created task to perform analysis within the given context.
+   *
+   * @param context the context in which the task is to be performed
+   * @param unitSource the source representing the file whose compilation unit is to be returned
+   * @param librarySource the source representing the library to be resolved
+   */
+  ResolveDartLibraryTask(
+      InternalAnalysisContext context, this.unitSource, this.librarySource)
+      : super(context);
+
+  /**
+   * Return the library resolver holding information about the libraries that were resolved.
+   *
+   * @return the library resolver holding information about the libraries that were resolved
+   */
+  LibraryResolver get libraryResolver => _resolver;
+
+  @override
+  String get taskDescription {
+    if (librarySource == null) {
+      return "resolve library null source";
+    }
+    return "resolve library ${librarySource.fullName}";
+  }
+
+  @override
+  accept(AnalysisTaskVisitor visitor) =>
+      visitor.visitResolveDartLibraryTask(this);
+
+  @override
+  void internalPerform() {
+    LibraryResolverFactory resolverFactory = context.libraryResolverFactory;
+    _resolver = resolverFactory == null
+        ? new LibraryResolver(context)
+        : resolverFactory(context);
+    _resolver.resolveLibrary(librarySource, true);
+  }
+}
+
+/**
+ * Instances of the class `ResolveDartUnitTask` resolve a single Dart file based on a existing
+ * element model.
+ */
+class ResolveDartUnitTask extends AnalysisTask {
+  /**
+   * The source that is to be resolved.
+   */
+  final Source source;
+
+  /**
+   * The element model for the library containing the source.
+   */
+  final LibraryElement _libraryElement;
+
+  /**
+   * The compilation unit that was resolved by this task.
+   */
+  CompilationUnit _resolvedUnit;
+
+  /**
+   * Initialize a newly created task to perform analysis within the given context.
+   *
+   * @param context the context in which the task is to be performed
+   * @param source the source to be parsed
+   * @param libraryElement the element model for the library containing the source
+   */
+  ResolveDartUnitTask(
+      InternalAnalysisContext context, this.source, this._libraryElement)
+      : super(context);
+
+  /**
+   * Return the source for the library containing the source that is to be resolved.
+   *
+   * @return the source for the library containing the source that is to be resolved
+   */
+  Source get librarySource => _libraryElement.source;
+
+  /**
+   * Return the compilation unit that was resolved by this task.
+   *
+   * @return the compilation unit that was resolved by this task
+   */
+  CompilationUnit get resolvedUnit => _resolvedUnit;
+
+  @override
+  String get taskDescription {
+    Source librarySource = _libraryElement.source;
+    if (librarySource == null) {
+      return "resolve unit null source";
+    }
+    return "resolve unit ${librarySource.fullName}";
+  }
+
+  @override
+  accept(AnalysisTaskVisitor visitor) => visitor.visitResolveDartUnitTask(this);
+
+  @override
+  void internalPerform() {
+    TypeProvider typeProvider = _libraryElement.context.typeProvider;
+    CompilationUnit unit = context.computeResolvableCompilationUnit(source);
+    if (unit == null) {
+      throw new AnalysisException(
+          "Internal error: computeResolvableCompilationUnit returned a value without a parsed Dart unit");
+    }
+    //
+    // Resolve names in declarations.
+    //
+    new DeclarationResolver().resolve(unit, _find(_libraryElement, source));
+    //
+    // Resolve the type names.
+    //
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    TypeResolverVisitor typeResolverVisitor = new TypeResolverVisitor.con2(
+        _libraryElement, source, typeProvider, errorListener);
+    unit.accept(typeResolverVisitor);
+    //
+    // Resolve the rest of the structure
+    //
+    InheritanceManager inheritanceManager =
+        new InheritanceManager(_libraryElement);
+    ResolverVisitor resolverVisitor = new ResolverVisitor.con2(_libraryElement,
+        source, typeProvider, inheritanceManager, errorListener);
+    unit.accept(resolverVisitor);
+    //
+    // Perform additional error checking.
+    //
+    PerformanceStatistics.errors.makeCurrentWhile(() {
+      ErrorReporter errorReporter = new ErrorReporter(errorListener, source);
+      ErrorVerifier errorVerifier = new ErrorVerifier(
+          errorReporter, _libraryElement, typeProvider, inheritanceManager);
+      unit.accept(errorVerifier);
+      // TODO(paulberry): as a temporary workaround for issue 21572,
+      // ConstantVerifier is being run right after ConstantValueComputer, so we
+      // don't need to run it here.  Once issue 21572 is fixed, re-enable the
+      // call to ConstantVerifier.
+//       ConstantVerifier constantVerifier = new ConstantVerifier(errorReporter, _libraryElement, typeProvider);
+//       unit.accept(constantVerifier);
+    });
+    //
+    // Capture the results.
+    //
+    _resolvedUnit = unit;
+  }
+
+  /**
+   * Search the compilation units that are part of the given library and return the element
+   * representing the compilation unit with the given source. Return `null` if there is no
+   * such compilation unit.
+   *
+   * @param libraryElement the element representing the library being searched through
+   * @param unitSource the source for the compilation unit whose element is to be returned
+   * @return the element representing the compilation unit
+   */
+  CompilationUnitElement _find(
+      LibraryElement libraryElement, Source unitSource) {
+    CompilationUnitElement element = libraryElement.definingCompilationUnit;
+    if (element.source == unitSource) {
+      return element;
+    }
+    for (CompilationUnitElement partElement in libraryElement.parts) {
+      if (partElement.source == unitSource) {
+        return partElement;
+      }
+    }
+    return null;
+  }
+}
+
+/**
+ * Instances of the class `ResolveHtmlTask` resolve a specific source as an HTML file.
+ */
+class ResolveHtmlTask extends AnalysisTask {
+  /**
+   * The source to be resolved.
+   */
+  final Source source;
+
+  /**
+   * The time at which the contents of the source were last modified.
+   */
+  final int modificationTime;
+
+  /**
+   * The HTML unit to be resolved.
+   */
+  final ht.HtmlUnit _unit;
+
+  /**
+   * The [HtmlUnit] that was resolved by this task.
+   */
+  ht.HtmlUnit _resolvedUnit;
+
+  /**
+   * The element produced by resolving the source.
+   */
+  HtmlElement _element = null;
+
+  /**
+   * The resolution errors that were discovered while resolving the source.
+   */
+  List<AnalysisError> _resolutionErrors = AnalysisError.NO_ERRORS;
+
+  /**
+   * Initialize a newly created task to perform analysis within the given context.
+   *
+   * @param context the context in which the task is to be performed
+   * @param source the source to be resolved
+   * @param modificationTime the time at which the contents of the source were last modified
+   * @param unit the HTML unit to be resolved
+   */
+  ResolveHtmlTask(InternalAnalysisContext context, this.source,
+      this.modificationTime, this._unit)
+      : super(context);
+
+  HtmlElement get element => _element;
+
+  List<AnalysisError> get resolutionErrors => _resolutionErrors;
+
+  /**
+   * Return the [HtmlUnit] that was resolved by this task.
+   *
+   * @return the [HtmlUnit] that was resolved by this task
+   */
+  ht.HtmlUnit get resolvedUnit => _resolvedUnit;
+
+  @override
+  String get taskDescription {
+    if (source == null) {
+      return "resolve as html null source";
+    }
+    return "resolve as html ${source.fullName}";
+  }
+
+  @override
+  accept(AnalysisTaskVisitor visitor) => visitor.visitResolveHtmlTask(this);
+
+  @override
+  void internalPerform() {
+    //
+    // Build the standard HTML element.
+    //
+    HtmlUnitBuilder builder = new HtmlUnitBuilder(context);
+    _element = builder.buildHtmlElement(source, _unit);
+    RecordingErrorListener errorListener = builder.errorListener;
+    //
+    // Validate the directives
+    //
+    _unit.accept(new RecursiveXmlVisitor_ResolveHtmlTask_internalPerform(
+        this, errorListener));
+    //
+    // Record all resolution errors.
+    //
+    _resolutionErrors = errorListener.getErrorsForSource(source);
+    //
+    // Remember the resolved unit.
+    //
+    _resolvedUnit = _unit;
+  }
+}
+
+/**
+ * The priority of data in the cache in terms of the desirability of retaining
+ * some specified data about a specified source.
+ */
+class RetentionPriority extends Enum<RetentionPriority> {
+  /**
+   * A priority indicating that a given piece of data can be removed from the
+   * cache without reservation.
+   */
+  static const RetentionPriority LOW = const RetentionPriority('LOW', 0);
+
+  /**
+   * A priority indicating that a given piece of data should not be removed from
+   * the cache unless there are no sources for which the corresponding data has
+   * a lower priority. Currently used for data that is needed in order to finish
+   * some outstanding analysis task.
+   */
+  static const RetentionPriority MEDIUM = const RetentionPriority('MEDIUM', 1);
+
+  /**
+   * A priority indicating that a given piece of data should not be removed from
+   * the cache. Currently used for data related to a priority source.
+   */
+  static const RetentionPriority HIGH = const RetentionPriority('HIGH', 2);
+
+  static const List<RetentionPriority> values = const [LOW, MEDIUM, HIGH];
+
+  const RetentionPriority(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * Instances of the class `ScanDartTask` scan a specific source as a Dart file.
+ */
+class ScanDartTask extends AnalysisTask {
+  /**
+   * The source to be scanned.
+   */
+  final Source source;
+
+  /**
+   * The contents of the source.
+   */
+  final String _content;
+
+  /**
+   * The token stream that was produced by scanning the source.
+   */
+  Token _tokenStream;
+
+  /**
+   * The line information that was produced.
+   */
+  LineInfo _lineInfo;
+
+  /**
+   * The errors that were produced by scanning the source.
+   */
+  List<AnalysisError> _errors = AnalysisError.NO_ERRORS;
+
+  /**
+   * Initialize a newly created task to perform analysis within the given context.
+   *
+   * @param context the context in which the task is to be performed
+   * @param source the source to be parsed
+   * @param content the contents of the source
+   */
+  ScanDartTask(InternalAnalysisContext context, this.source, this._content)
+      : super(context);
+
+  /**
+   * Return the errors that were produced by scanning the source, or `null` if the task has
+   * not yet been performed or if an exception occurred.
+   *
+   * @return the errors that were produced by scanning the source
+   */
+  List<AnalysisError> get errors => _errors;
+
+  /**
+   * Return the line information that was produced, or `null` if the task has not yet been
+   * performed or if an exception occurred.
+   *
+   * @return the line information that was produced
+   */
+  LineInfo get lineInfo => _lineInfo;
+
+  @override
+  String get taskDescription {
+    if (source == null) {
+      return "scan as dart null source";
+    }
+    return "scan as dart ${source.fullName}";
+  }
+
+  /**
+   * Return the token stream that was produced by scanning the source, or `null` if the task
+   * has not yet been performed or if an exception occurred.
+   *
+   * @return the token stream that was produced by scanning the source
+   */
+  Token get tokenStream => _tokenStream;
+
+  @override
+  accept(AnalysisTaskVisitor visitor) => visitor.visitScanDartTask(this);
+
+  @override
+  void internalPerform() {
+    PerformanceStatistics.scan.makeCurrentWhile(() {
+      RecordingErrorListener errorListener = new RecordingErrorListener();
+      try {
+        Scanner scanner = new Scanner(
+            source, new CharSequenceReader(_content), errorListener);
+        scanner.preserveComments = context.analysisOptions.preserveComments;
+        scanner.enableNullAwareOperators =
+            context.analysisOptions.enableNullAwareOperators;
+        _tokenStream = scanner.tokenize();
+        _lineInfo = new LineInfo(scanner.lineStarts);
+        _errors = errorListener.getErrorsForSource(source);
+      } catch (exception, stackTrace) {
+        throw new AnalysisException(
+            "Exception", new CaughtException(exception, stackTrace));
+      }
+    });
+  }
+}
+
+/**
+ * An [AnalysisContext] that only contains sources for a Dart SDK.
+ */
+class SdkAnalysisContext extends AnalysisContextImpl {
+  @override
+  AnalysisCache createCacheFromSourceFactory(SourceFactory factory) {
+    if (factory == null) {
+      return super.createCacheFromSourceFactory(factory);
+    }
+    DartSdk sdk = factory.dartSdk;
+    if (sdk == null) {
+      throw new IllegalArgumentException(
+          "The source factory for an SDK analysis context must have a DartUriResolver");
+    }
+    return new AnalysisCache(
+        <CachePartition>[AnalysisEngine.instance.partitionManager.forSdk(sdk)]);
+  }
+}
+
+/**
+ * A cache partition that contains all of the sources in the SDK.
+ */
+class SdkCachePartition extends CachePartition {
+  /**
+   * Initialize a newly created partition. The [context] is the context that
+   * owns this partition. The [maxCacheSize] is the maximum number of sources
+   * for which AST structures should be kept in the cache.
+   */
+  SdkCachePartition(InternalAnalysisContext context, int maxCacheSize)
+      : super(context, maxCacheSize, DefaultRetentionPolicy.POLICY);
+
+  @override
+  bool contains(Source source) => source.isInSystemLibrary;
+}
+
+/**
+ * The information cached by an analysis context about an individual source, no
+ * matter what kind of source it is.
+ */
+abstract class SourceEntry {
+  /**
+   * The data descriptor representing the contents of the source.
+   */
+  static final DataDescriptor<String> CONTENT =
+      new DataDescriptor<String>("SourceEntry.CONTENT");
+
+  /**
+   * The data descriptor representing the errors resulting from reading the
+   * source content.
+   */
+  static final DataDescriptor<List<AnalysisError>> CONTENT_ERRORS =
+      new DataDescriptor<List<AnalysisError>>(
+          "SourceEntry.CONTENT_ERRORS", AnalysisError.NO_ERRORS);
+
+  /**
+   * The data descriptor representing the line information.
+   */
+  static final DataDescriptor<LineInfo> LINE_INFO =
+      new DataDescriptor<LineInfo>("SourceEntry.LINE_INFO");
+
+  /**
+   * The index of the flag indicating whether the source was explicitly added to
+   * the context or whether the source was implicitly added because it was
+   * referenced by another source.
+   */
+  static int _EXPLICITLY_ADDED_FLAG = 0;
+
+  /**
+   * A table mapping data descriptors to a count of the number of times a value
+   * was set when in a given state.
+   */
+  static final Map<DataDescriptor, Map<CacheState, int>> transitionMap =
+      new HashMap<DataDescriptor, Map<CacheState, int>>();
+
+  /**
+   * The most recent time at which the state of the source matched the state
+   * represented by this entry.
+   */
+  int modificationTime = 0;
+
+  /**
+   * The exception that caused one or more values to have a state of
+   * [CacheState.ERROR].
+   */
+  CaughtException exception;
+
+  /**
+   * A bit-encoding of boolean flags associated with this element.
+   */
+  int _flags = 0;
+
+  /**
+   * A table mapping data descriptors to the cached results for those
+   * descriptors.
+   */
+  Map<DataDescriptor, CachedResult> resultMap =
+      new HashMap<DataDescriptor, CachedResult>();
+
+  /**
+   * Return all of the errors associated with this entry.
+   */
+  List<AnalysisError> get allErrors {
+    return getValue(CONTENT_ERRORS);
+  }
+
+  /**
+   * Get a list of all the library-independent descriptors for which values may
+   * be stored in this SourceEntry.
+   */
+  List<DataDescriptor> get descriptors {
+    return <DataDescriptor>[CONTENT, CONTENT_ERRORS, LINE_INFO];
+  }
+
+  /**
+   * Return `true` if the source was explicitly added to the context or `false`
+   * if the source was implicitly added because it was referenced by another
+   * source.
+   */
+  bool get explicitlyAdded => _getFlag(_EXPLICITLY_ADDED_FLAG);
+
+  /**
+   * Set whether the source was explicitly added to the context to match the
+   * [explicitlyAdded] flag.
+   */
+  void set explicitlyAdded(bool explicitlyAdded) {
+    _setFlag(_EXPLICITLY_ADDED_FLAG, explicitlyAdded);
+  }
+
+  /**
+   * Return the kind of the source, or `null` if the kind is not currently
+   * cached.
+   */
+  SourceKind get kind;
+
+  /**
+   * Fix the state of the [exception] to match the current state of the entry.
+   */
+  void fixExceptionState() {
+    if (hasErrorState()) {
+      if (exception == null) {
+        //
+        // This code should never be reached, but is a fail-safe in case an
+        // exception is not recorded when it should be.
+        //
+        String message = "State set to ERROR without setting an exception";
+        exception = new CaughtException(new AnalysisException(message), null);
+      }
+    } else {
+      exception = null;
+    }
+  }
+
+  /**
+   * Return a textual representation of the difference between the [oldEntry]
+   * and this entry. The difference is represented as a sequence of fields whose
+   * value would change if the old entry were converted into the new entry.
+   */
+  String getDiff(SourceEntry oldEntry) {
+    StringBuffer buffer = new StringBuffer();
+    _writeDiffOn(buffer, oldEntry);
+    return buffer.toString();
+  }
+
+  /**
+   * Return the state of the data represented by the given [descriptor].
+   */
+  CacheState getState(DataDescriptor descriptor) {
+    if (!_isValidDescriptor(descriptor)) {
+      throw new ArgumentError("Invalid descriptor: $descriptor");
+    }
+    CachedResult result = resultMap[descriptor];
+    if (result == null) {
+      return CacheState.INVALID;
+    }
+    return result.state;
+  }
+
+  /**
+   * Return the value of the data represented by the given [descriptor], or
+   * `null` if the data represented by the descriptor is not valid.
+   */
+  /*<V>*/ dynamic /*V*/ getValue(DataDescriptor /*<V>*/ descriptor) {
+    if (!_isValidDescriptor(descriptor)) {
+      throw new ArgumentError("Invalid descriptor: $descriptor");
+    }
+    CachedResult result = resultMap[descriptor];
+    if (result == null) {
+      return descriptor.defaultValue;
+    }
+    return result.value;
+  }
+
+  /**
+   * Return `true` if the state of any data value is [CacheState.ERROR].
+   */
+  bool hasErrorState() {
+    for (CachedResult result in resultMap.values) {
+      if (result.state == CacheState.ERROR) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Invalidate all of the information associated with this source.
+   */
+  void invalidateAllInformation() {
+    setState(CONTENT, CacheState.INVALID);
+    setState(CONTENT_ERRORS, CacheState.INVALID);
+    setState(LINE_INFO, CacheState.INVALID);
+  }
+
+  /**
+   * Record that an [exception] occurred while attempting to get the contents of
+   * the source represented by this entry. This will set the state of all
+   * information, including any resolution-based information, as being in error.
+   */
+  void recordContentError(CaughtException exception) {
+    setState(CONTENT, CacheState.ERROR);
+    recordScanError(exception);
+  }
+
+  /**
+   * Record that an [exception] occurred while attempting to scan or parse the
+   * entry represented by this entry. This will set the state of all
+   * information, including any resolution-based information, as being in error.
+   */
+  void recordScanError(CaughtException exception) {
+    this.exception = exception;
+    setState(LINE_INFO, CacheState.ERROR);
+  }
+
+  /**
+   * Set the state of the data represented by the given [descriptor] to the
+   * given [state].
+   */
+  void setState(DataDescriptor descriptor, CacheState state) {
+    if (!_isValidDescriptor(descriptor)) {
+      throw new ArgumentError("Invalid descriptor: $descriptor");
+    }
+    if (state == CacheState.VALID) {
+      throw new ArgumentError("use setValue() to set the state to VALID");
+    }
+    _validateStateChange(descriptor, state);
+    if (state == CacheState.INVALID) {
+      resultMap.remove(descriptor);
+    } else {
+      CachedResult result =
+          resultMap.putIfAbsent(descriptor, () => new CachedResult(descriptor));
+      result.state = state;
+      if (state != CacheState.IN_PROCESS) {
+        //
+        // If the state is in-process, we can leave the current value in the
+        // cache for any 'get' methods to access.
+        //
+        result.value = descriptor.defaultValue;
+      }
+    }
+  }
+
+  /**
+   * Set the value of the data represented by the given [descriptor] to the
+   * given [value].
+   */
+  void setValue(DataDescriptor /*<V>*/ descriptor, dynamic /*V*/ value) {
+    if (!_isValidDescriptor(descriptor)) {
+      throw new ArgumentError("Invalid descriptor: $descriptor");
+    }
+    _validateStateChange(descriptor, CacheState.VALID);
+    CachedResult result =
+        resultMap.putIfAbsent(descriptor, () => new CachedResult(descriptor));
+    countTransition(descriptor, result);
+    result.state = CacheState.VALID;
+    result.value = value == null ? descriptor.defaultValue : value;
+  }
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    _writeOn(buffer);
+    return buffer.toString();
+  }
+
+  /**
+   * Flush the value of the data described by the [descriptor].
+   */
+  void _flush(DataDescriptor descriptor) {
+    CachedResult result = resultMap[descriptor];
+    if (result != null && result.state == CacheState.VALID) {
+      _validateStateChange(descriptor, CacheState.FLUSHED);
+      result.state = CacheState.FLUSHED;
+      result.value = descriptor.defaultValue;
+    }
+  }
+
+  /**
+   * Return the value of the flag with the given [index].
+   */
+  bool _getFlag(int index) => BooleanArray.get(_flags, index);
+
+  /**
+   * Return `true` if the [descriptor] is valid for this entry.
+   */
+  bool _isValidDescriptor(DataDescriptor descriptor) {
+    return descriptor == CONTENT ||
+        descriptor == CONTENT_ERRORS ||
+        descriptor == LINE_INFO;
+  }
+
+  /**
+   * Set the value of the flag with the given [index] to the given [value].
+   */
+  void _setFlag(int index, bool value) {
+    _flags = BooleanArray.set(_flags, index, value);
+  }
+
+  /**
+   * If the state of the value described by the given [descriptor] is changing
+   * from ERROR to anything else, capture the information. This is an attempt to
+   * discover the underlying cause of a long-standing bug.
+   */
+  void _validateStateChange(DataDescriptor descriptor, CacheState newState) {
+    // TODO(brianwilkerson) Decide whether we still want to capture this data.
+//    if (descriptor != CONTENT) {
+//      return;
+//    }
+//    CachedResult result = resultMap[CONTENT];
+//    if (result != null && result.state == CacheState.ERROR) {
+//      String message =
+//          "contentState changing from ${result.state} to $newState";
+//      InstrumentationBuilder builder =
+//          Instrumentation.builder2("SourceEntry-validateStateChange");
+//      builder.data3("message", message);
+//      //builder.data("source", source.getFullName());
+//      builder.record(new CaughtException(new AnalysisException(message), null));
+//      builder.log();
+//    }
+  }
+
+  /**
+   * Write a textual representation of the difference between the [oldEntry] and
+   * this entry to the given string [buffer]. Return `true` if some difference
+   * was written.
+   */
+  bool _writeDiffOn(StringBuffer buffer, SourceEntry oldEntry) {
+    bool needsSeparator = false;
+    CaughtException oldException = oldEntry.exception;
+    if (!identical(oldException, exception)) {
+      buffer.write("exception = ");
+      buffer.write(oldException.runtimeType);
+      buffer.write(" -> ");
+      buffer.write(exception.runtimeType);
+      needsSeparator = true;
+    }
+    int oldModificationTime = oldEntry.modificationTime;
+    if (oldModificationTime != modificationTime) {
+      if (needsSeparator) {
+        buffer.write("; ");
+      }
+      buffer.write("time = ");
+      buffer.write(oldModificationTime);
+      buffer.write(" -> ");
+      buffer.write(modificationTime);
+      needsSeparator = true;
+    }
+    needsSeparator =
+        _writeStateDiffOn(buffer, needsSeparator, "content", CONTENT, oldEntry);
+    needsSeparator = _writeStateDiffOn(
+        buffer, needsSeparator, "contentErrors", CONTENT_ERRORS, oldEntry);
+    needsSeparator = _writeStateDiffOn(
+        buffer, needsSeparator, "lineInfo", LINE_INFO, oldEntry);
+    return needsSeparator;
+  }
+
+  /**
+   * Write a textual representation of this entry to the given [buffer]. The
+   * result should only be used for debugging purposes.
+   */
+  void _writeOn(StringBuffer buffer) {
+    buffer.write("time = ");
+    buffer.write(modificationTime);
+    _writeStateOn(buffer, "content", CONTENT);
+    _writeStateOn(buffer, "contentErrors", CONTENT_ERRORS);
+    _writeStateOn(buffer, "lineInfo", LINE_INFO);
+  }
+
+  /**
+   * Write a textual representation of the difference between the state of the
+   * value described by the given [descriptor] between the [oldEntry] and this
+   * entry to the given [buffer]. Return `true` if some difference was written.
+   */
+  bool _writeStateDiffOn(StringBuffer buffer, bool needsSeparator, String label,
+      DataDescriptor descriptor, SourceEntry oldEntry) {
+    CacheState oldState = oldEntry.getState(descriptor);
+    CacheState newState = getState(descriptor);
+    if (oldState != newState) {
+      if (needsSeparator) {
+        buffer.write("; ");
+      }
+      buffer.write(label);
+      buffer.write(" = ");
+      buffer.write(oldState);
+      buffer.write(" -> ");
+      buffer.write(newState);
+      return true;
+    }
+    return needsSeparator;
+  }
+
+  /**
+   * Write a textual representation of the state of the value described by the
+   * given [descriptor] to the given bugger, prefixed by the given [label] to
+   * the given [buffer].
+   */
+  void _writeStateOn(
+      StringBuffer buffer, String label, DataDescriptor descriptor) {
+    CachedResult result = resultMap[descriptor];
+    buffer.write("; ");
+    buffer.write(label);
+    buffer.write(" = ");
+    buffer.write(result == null ? CacheState.INVALID : result.state);
+  }
+
+  /**
+   * Increment the count of the number of times that data represented by the
+   * given [descriptor] was transitioned from the current state (as found in the
+   * given [result] to a valid state.
+   */
+  static void countTransition(DataDescriptor descriptor, CachedResult result) {
+    Map<CacheState, int> countMap = transitionMap.putIfAbsent(
+        descriptor, () => new HashMap<CacheState, int>());
+    int count = countMap[result.state];
+    countMap[result.state] = count == null ? 1 : count + 1;
+  }
+}
+
+/**
+ * The priority levels used to return sources in an optimal order. A smaller
+ * ordinal value equates to a higher priority.
+ */
+class SourcePriority extends Enum<SourcePriority> {
+  /**
+   * Used for a Dart source that is known to be a part contained in a library
+   * that was recently resolved. These parts are given a higher priority because
+   * there is a high probability that their AST structure is still in the cache
+   * and therefore would not need to be re-created.
+   */
+  static const SourcePriority PRIORITY_PART =
+      const SourcePriority('PRIORITY_PART', 0);
+
+  /**
+   * Used for a Dart source that is known to be a library.
+   */
+  static const SourcePriority LIBRARY = const SourcePriority('LIBRARY', 1);
+
+  /**
+   * Used for a Dart source whose kind is unknown.
+   */
+  static const SourcePriority UNKNOWN = const SourcePriority('UNKNOWN', 2);
+
+  /**
+   * Used for a Dart source that is known to be a part but whose library has not
+   * yet been resolved.
+   */
+  static const SourcePriority NORMAL_PART =
+      const SourcePriority('NORMAL_PART', 3);
+
+  /**
+   * Used for an HTML source.
+   */
+  static const SourcePriority HTML = const SourcePriority('HTML', 4);
+
+  static const List<SourcePriority> values = const [
+    PRIORITY_PART,
+    LIBRARY,
+    UNKNOWN,
+    NORMAL_PART,
+    HTML
+  ];
+
+  const SourcePriority(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * [SourcesChangedEvent] indicates which sources have been added, removed,
+ * or whose contents have changed.
+ */
+class SourcesChangedEvent {
+  /**
+   * The internal representation of what has changed. Clients should not access
+   * this field directly.
+   */
+  final ChangeSet _changeSet;
+
+  /**
+   * Construct an instance representing the given changes.
+   */
+  SourcesChangedEvent(ChangeSet changeSet) : _changeSet = changeSet;
+
+  /**
+   * Construct an instance representing a source content change.
+   */
+  factory SourcesChangedEvent.changedContent(Source source, String contents) {
+    ChangeSet changeSet = new ChangeSet();
+    changeSet.changedContent(source, contents);
+    return new SourcesChangedEvent(changeSet);
+  }
+
+  /**
+   * Construct an instance representing a source content change.
+   */
+  factory SourcesChangedEvent.changedRange(Source source, String contents,
+      int offset, int oldLength, int newLength) {
+    ChangeSet changeSet = new ChangeSet();
+    changeSet.changedRange(source, contents, offset, oldLength, newLength);
+    return new SourcesChangedEvent(changeSet);
+  }
+
+  /**
+   * Return the collection of sources for which content has changed.
+   */
+  Iterable<Source> get changedSources {
+    List<Source> changedSources = new List.from(_changeSet.changedSources);
+    changedSources.addAll(_changeSet.changedContents.keys);
+    changedSources.addAll(_changeSet.changedRanges.keys);
+    return changedSources;
+  }
+
+  /**
+   * Return `true` if any sources were added.
+   */
+  bool get wereSourcesAdded => _changeSet.addedSources.length > 0;
+
+  /**
+   * Return `true` if any sources were removed or deleted.
+   */
+  bool get wereSourcesRemovedOrDeleted =>
+      _changeSet.removedSources.length > 0 ||
+          _changeSet.removedContainers.length > 0 ||
+          _changeSet.deletedSources.length > 0;
+}
+
+/**
+ * Analysis data for which we have a modification time.
+ */
+class TimestampedData<E> {
+  /**
+   * The modification time of the source from which the data was created.
+   */
+  final int modificationTime;
+
+  /**
+   * The data that was created from the source.
+   */
+  final E data;
+
+  /**
+   * Initialize a newly created holder to associate the given [data] with the
+   * given [modificationTime].
+   */
+  TimestampedData(this.modificationTime, this.data);
+}
+
+/**
+ * A cache partition that contains all sources not contained in other
+ * partitions.
+ */
+class UniversalCachePartition extends CachePartition {
+  /**
+   * Initialize a newly created partition. The [context] is the context that
+   * owns this partition. The [maxCacheSize] is the maximum number of sources
+   * for which AST structures should be kept in the cache. The [retentionPolicy]
+   * is the policy used to determine which pieces of data to remove from the
+   * cache.
+   */
+  UniversalCachePartition(InternalAnalysisContext context, int maxCacheSize,
+      CacheRetentionPolicy retentionPolicy)
+      : super(context, maxCacheSize, retentionPolicy);
+
+  @override
+  bool contains(Source source) => true;
+}
+
+/**
+ * The unique instances of the class `WaitForAsyncTask` represents a state in which there is
+ * no analysis work that can be done until some asynchronous task (such as IO) has completed, but
+ * where analysis is not yet complete.
+ */
+class WaitForAsyncTask extends AnalysisTask {
+  /**
+   * The unique instance of this class.
+   */
+  static WaitForAsyncTask _UniqueInstance = new WaitForAsyncTask();
+
+  /**
+   * Return the unique instance of this class.
+   *
+   * @return the unique instance of this class
+   */
+  static WaitForAsyncTask get instance => _UniqueInstance;
+
+  /**
+   * Prevent the creation of instances of this class.
+   */
+  WaitForAsyncTask() : super(null);
+
+  @override
+  String get taskDescription => "Waiting for async analysis";
+
+  @override
+  accept(AnalysisTaskVisitor visitor) => null;
+
+  @override
+  void internalPerform() {
+    // There is no work to be done.
+  }
+}
+
+/**
+ * An object that manages a list of sources that need to have analysis work
+ * performed on them.
+ */
+class WorkManager {
+  /**
+   * A list containing the various queues is priority order.
+   */
+  List<List<Source>> _workQueues;
+
+  /**
+   * Initialize a newly created manager to have no work queued up.
+   */
+  WorkManager() {
+    int queueCount = SourcePriority.values.length;
+    _workQueues = new List<List>(queueCount);
+    for (int i = 0; i < queueCount; i++) {
+      _workQueues[i] = new List<Source>();
+    }
+  }
+
+  /**
+   * Record that the given [source] needs to be analyzed. The [priority] level
+   * is used to control when the source will be analyzed with respect to other
+   * sources. If the source was previously added then it's priority is updated.
+   * If it was previously added with the same priority then it's position in the
+   * queue is unchanged.
+   */
+  void add(Source source, SourcePriority priority) {
+    int queueCount = _workQueues.length;
+    int ordinal = priority.ordinal;
+    for (int i = 0; i < queueCount; i++) {
+      List<Source> queue = _workQueues[i];
+      if (i == ordinal) {
+        if (!queue.contains(source)) {
+          queue.add(source);
+        }
+      } else {
+        queue.remove(source);
+      }
+    }
+  }
+
+  /**
+   * Record that the given [source] needs to be analyzed. The [priority] level
+   * is used to control when the source will be analyzed with respect to other
+   * sources. If the source was previously added then it's priority is updated.
+   * In either case, it will be analyzed before other sources of the same
+   * priority.
+   */
+  void addFirst(Source source, SourcePriority priority) {
+    int queueCount = _workQueues.length;
+    int ordinal = priority.ordinal;
+    for (int i = 0; i < queueCount; i++) {
+      List<Source> queue = _workQueues[i];
+      if (i == ordinal) {
+        queue.remove(source);
+        queue.insert(0, source);
+      } else {
+        queue.remove(source);
+      }
+    }
+  }
+
+  /**
+   * Return an iterator that can be used to access the sources to be analyzed in
+   * the order in which they should be analyzed.
+   *
+   * <b>Note:</b> As with other iterators, no sources can be added or removed
+   * from this work manager while the iterator is being used. Unlike some
+   * implementations, however, the iterator will not detect when this
+   * requirement has been violated; it might work correctly, it might return the
+   * wrong source, or it might throw an exception.
+   */
+  WorkManager_WorkIterator iterator() => new WorkManager_WorkIterator(this);
+
+  /**
+   * Record that the given source is fully analyzed.
+   */
+  void remove(Source source) {
+    int queueCount = _workQueues.length;
+    for (int i = 0; i < queueCount; i++) {
+      _workQueues[i].remove(source);
+    }
+  }
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    List<SourcePriority> priorities = SourcePriority.values;
+    bool needsSeparator = false;
+    int queueCount = _workQueues.length;
+    for (int i = 0; i < queueCount; i++) {
+      List<Source> queue = _workQueues[i];
+      if (!queue.isEmpty) {
+        if (needsSeparator) {
+          buffer.write("; ");
+        }
+        buffer.write(priorities[i]);
+        buffer.write(": ");
+        int queueSize = queue.length;
+        for (int j = 0; j < queueSize; j++) {
+          if (j > 0) {
+            buffer.write(", ");
+          }
+          buffer.write(queue[j].fullName);
+        }
+        needsSeparator = true;
+      }
+    }
+    return buffer.toString();
+  }
+}
+
+/**
+ * An iterator that returns the sources in a work manager in the order in which
+ * they are to be analyzed.
+ */
+class WorkManager_WorkIterator {
+  final WorkManager _manager;
+
+  /**
+   * The index of the work queue through which we are currently iterating.
+   */
+  int _queueIndex = 0;
+
+  /**
+   * The index of the next element of the work queue to be returned.
+   */
+  int _index = -1;
+
+  /**
+   * Initialize a newly created iterator to be ready to return the first element
+   * in the iteration.
+   */
+  WorkManager_WorkIterator(this._manager) {
+    _advance();
+  }
+
+  /**
+   * Return `true` if there is another [Source] available for processing.
+   */
+  bool get hasNext => _queueIndex < _manager._workQueues.length;
+
+  /**
+   * Return the next [Source] available for processing and advance so that the
+   * returned source will not be returned again.
+   */
+  Source next() {
+    if (!hasNext) {
+      throw new NoSuchElementException();
+    }
+    Source source = _manager._workQueues[_queueIndex][_index];
+    _advance();
+    return source;
+  }
+
+  /**
+   * Increment the [index] and [queueIndex] so that they are either indicating
+   * the next source to be returned or are indicating that there are no more
+   * sources to be returned.
+   */
+  void _advance() {
+    _index++;
+    if (_index >= _manager._workQueues[_queueIndex].length) {
+      _index = 0;
+      _queueIndex++;
+      while (_queueIndex < _manager._workQueues.length &&
+          _manager._workQueues[_queueIndex].isEmpty) {
+        _queueIndex++;
+      }
+    }
+  }
+}
+
+/**
+ * A helper class used to create futures for AnalysisContextImpl. Using a helper
+ * class allows us to preserve the generic parameter T.
+ */
+class _AnalysisFutureHelper<T> {
+  final AnalysisContextImpl _context;
+
+  _AnalysisFutureHelper(this._context);
+
+  /**
+   * Return a future that will be completed with the result of calling
+   * [computeValue].  If [computeValue] returns non-null, the future will be
+   * completed immediately with the resulting value.  If it returns null, then
+   * it will be re-executed in the future, after the next time the cached
+   * information for [source] has changed.  If [computeValue] throws an
+   * exception, the future will fail with that exception.
+   *
+   * If the [computeValue] still returns null after there is no further
+   * analysis to be done for [source], then the future will be completed with
+   * the error AnalysisNotScheduledError.
+   *
+   * Since [computeValue] will be called while the state of analysis is being
+   * updated, it should be free of side effects so that it doesn't cause
+   * reentrant changes to the analysis state.
+   */
+  CancelableFuture<T> computeAsync(
+      Source source, T computeValue(SourceEntry sourceEntry)) {
+    if (_context.isDisposed) {
+      // No further analysis is expected, so return a future that completes
+      // immediately with AnalysisNotScheduledError.
+      return new CancelableFuture.error(new AnalysisNotScheduledError());
+    }
+    SourceEntry sourceEntry = _context.getReadableSourceEntryOrNull(source);
+    if (sourceEntry == null) {
+      return new CancelableFuture.error(new AnalysisNotScheduledError());
+    }
+    PendingFuture pendingFuture =
+        new PendingFuture<T>(_context, source, computeValue);
+    if (!pendingFuture.evaluate(sourceEntry)) {
+      _context._pendingFutureSources
+          .putIfAbsent(source, () => <PendingFuture>[])
+          .add(pendingFuture);
+    }
+    return pendingFuture.future;
+  }
+}
+
+class _ElementByIdFinder extends GeneralizingElementVisitor {
+  final int _id;
+  Element result;
+
+  _ElementByIdFinder(this._id);
+
+  @override
+  visitElement(Element element) {
+    if (element.id == _id) {
+      result = element;
+      throw new _ElementByIdFinderException();
+    }
+    super.visitElement(element);
+  }
+}
+
+class _ElementByIdFinderException {}
diff --git a/analyzer/lib/src/generated/error.dart b/analyzer/lib/src/generated/error.dart
new file mode 100644
index 0000000..9a83ed4
--- /dev/null
+++ b/analyzer/lib/src/generated/error.dart
@@ -0,0 +1,4901 @@
+// 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 engine.error;
+
+import 'dart:collection';
+
+import 'ast.dart' show AstNode;
+import 'element.dart';
+import 'java_core.dart';
+import 'scanner.dart' show Token;
+import 'source.dart';
+
+/**
+ * An error discovered during the analysis of some Dart code.
+ *
+ * See [AnalysisErrorListener].
+ */
+class AnalysisError {
+  /**
+   * An empty array of errors used when no errors are expected.
+   */
+  static const List<AnalysisError> NO_ERRORS = const <AnalysisError>[];
+
+  /**
+   * A [Comparator] that sorts by the name of the file that the [AnalysisError]
+   * was found.
+   */
+  static Comparator<AnalysisError> FILE_COMPARATOR = (AnalysisError o1,
+      AnalysisError o2) => o1.source.shortName.compareTo(o2.source.shortName);
+
+  /**
+   * A [Comparator] that sorts error codes first by their severity (errors
+   * first, warnings second), and then by the the error code type.
+   */
+  static Comparator<AnalysisError> ERROR_CODE_COMPARATOR = (AnalysisError o1,
+      AnalysisError o2) {
+    ErrorCode errorCode1 = o1.errorCode;
+    ErrorCode errorCode2 = o2.errorCode;
+    ErrorSeverity errorSeverity1 = errorCode1.errorSeverity;
+    ErrorSeverity errorSeverity2 = errorCode2.errorSeverity;
+    ErrorType errorType1 = errorCode1.type;
+    ErrorType errorType2 = errorCode2.type;
+    if (errorSeverity1 == errorSeverity2) {
+      return errorType1.compareTo(errorType2);
+    } else {
+      return errorSeverity2.compareTo(errorSeverity1);
+    }
+  };
+
+  /**
+   * The error code associated with the error.
+   */
+  final ErrorCode errorCode;
+
+  /**
+   * The localized error message.
+   */
+  String _message;
+
+  /**
+   * The correction to be displayed for this error, or `null` if there is no
+   * correction information for this error.
+   */
+  String _correction;
+
+  /**
+   * The source in which the error occurred, or `null` if unknown.
+   */
+  Source source;
+
+  /**
+   * The character offset from the beginning of the source (zero based) where
+   * the error occurred.
+   */
+  int offset = 0;
+
+  /**
+   * The number of characters from the offset to the end of the source which
+   * encompasses the compilation error.
+   */
+  int _length = 0;
+
+  /**
+   * A flag indicating whether this error can be shown to be a non-issue because
+   * of the result of type propagation.
+   */
+  bool isStaticOnly = false;
+
+  /**
+   * Initialize a newly created analysis error for the specified [source]. The
+   * error will have the given [errorCode] and the list of [arguments] will be
+   * used to complete the message. The error has no location information.
+   */
+  AnalysisError.con1(this.source, this.errorCode, [List<Object> arguments]) {
+    this._message = formatList(errorCode.message, arguments);
+  }
+
+  /**
+   * Initialize a newly created analysis error for the specified [source] at the
+   * given [offset] with the given [length]. The error will have the given
+   * [errorCode] and the list of [arguments] will be used to complete the
+   * message.
+   */
+  AnalysisError.con2(this.source, this.offset, int length, this.errorCode,
+      [List<Object> arguments]) {
+    this._length = length;
+    this._message = formatList(errorCode.message, arguments);
+    String correctionTemplate = errorCode.correction;
+    if (correctionTemplate != null) {
+      this._correction = formatList(correctionTemplate, arguments);
+    }
+  }
+
+  /**
+   * Return the template used to create the correction to be displayed for this
+   * error, or `null` if there is no correction information for this error. The
+   * correction should indicate how the user can fix the error.
+   */
+  String get correction => _correction;
+
+  @override
+  int get hashCode {
+    int hashCode = offset;
+    hashCode ^= (_message != null) ? _message.hashCode : 0;
+    hashCode ^= (source != null) ? source.hashCode : 0;
+    return hashCode;
+  }
+
+  /**
+   * Return the length of the error location, that is, the number of characters
+   * from the offset to the end of the source which encompasses the compilation
+   * error.
+   */
+  int get length => _length;
+
+  /**
+   * Return the message to be displayed for this error. The message should
+   * indicate what is wrong and why it is wrong.
+   */
+  String get message => _message;
+
+  @override
+  bool operator ==(Object obj) {
+    if (identical(obj, this)) {
+      return true;
+    }
+    // prepare other AnalysisError
+    if (obj is! AnalysisError) {
+      return false;
+    }
+    AnalysisError other = obj as AnalysisError;
+    // Quick checks.
+    if (!identical(errorCode, other.errorCode)) {
+      return false;
+    }
+    if (offset != other.offset || _length != other._length) {
+      return false;
+    }
+    if (isStaticOnly != other.isStaticOnly) {
+      return false;
+    }
+    // Deep checks.
+    if (_message != other._message) {
+      return false;
+    }
+    if (source != other.source) {
+      return false;
+    }
+    // OK
+    return true;
+  }
+
+  /**
+   * Return the value of the given [property], or `null` if the given property
+   * is not defined for this error.
+   */
+  Object getProperty(ErrorProperty property) => null;
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    buffer.write((source != null) ? source.fullName : "<unknown source>");
+    buffer.write("(");
+    buffer.write(offset);
+    buffer.write("..");
+    buffer.write(offset + _length - 1);
+    buffer.write("): ");
+    //buffer.write("(" + lineNumber + ":" + columnNumber + "): ");
+    buffer.write(_message);
+    return buffer.toString();
+  }
+}
+
+/**
+ * An object that listen for [AnalysisError]s being produced by the analysis
+ * engine.
+ */
+abstract class AnalysisErrorListener {
+  /**
+   * An error listener that ignores errors that are reported to it.
+   */
+  static final AnalysisErrorListener NULL_LISTENER =
+      new AnalysisErrorListener_NULL_LISTENER();
+
+  /**
+   * This method is invoked when an [error] has been found by the analysis
+   * engine.
+   */
+  void onError(AnalysisError error);
+}
+
+/**
+ * An [AnalysisErrorListener] that ignores error.
+ */
+class AnalysisErrorListener_NULL_LISTENER implements AnalysisErrorListener {
+  @override
+  void onError(AnalysisError event) {
+    // Ignore errors
+  }
+}
+
+/**
+ * An [AnalysisError] that can have arbitrary properties associated with it.
+ */
+class AnalysisErrorWithProperties extends AnalysisError {
+  /**
+   * The properties associated with this error.
+   */
+  HashMap<ErrorProperty, Object> _propertyMap =
+      new HashMap<ErrorProperty, Object>();
+
+  /**
+   * Initialize a newly created analysis error for the specified [source]. The
+   * error will have the given [errorCode] and the list of [arguments] will be
+   * used to complete the message. The error has no location information.
+   */
+  AnalysisErrorWithProperties.con1(Source source, ErrorCode errorCode,
+      [List<Object> arguments])
+      : super.con1(source, errorCode, arguments);
+
+  /**
+   * Initialize a newly created analysis error for the specified [source] at the
+   * given [offset] with the given [length]. The error will have the given
+   * [errorCode] and the list of [arguments] will be used to complete the
+   * message.
+   */
+  AnalysisErrorWithProperties.con2(
+      Source source, int offset, int length, ErrorCode errorCode,
+      [List<Object> arguments])
+      : super.con2(source, offset, length, errorCode, arguments);
+
+  @override
+  Object getProperty(ErrorProperty property) => _propertyMap[property];
+
+  /**
+   * Set the value of the given [property] to the given [value]. Using a value
+   * of `null` will effectively remove the property from this error.
+   */
+  void setProperty(ErrorProperty property, Object value) {
+    _propertyMap[property] = value;
+  }
+}
+
+/**
+ * An [AnalysisErrorListener] that keeps track of whether any error has been
+ * reported to it.
+ */
+class BooleanErrorListener implements AnalysisErrorListener {
+  /**
+   * A flag indicating whether an error has been reported to this listener.
+   */
+  bool _errorReported = false;
+
+  /**
+   * Return `true` if an error has been reported to this listener.
+   */
+  bool get errorReported => _errorReported;
+
+  @override
+  void onError(AnalysisError error) {
+    _errorReported = true;
+  }
+}
+
+/**
+ * The error codes used for compile time errors caused by constant evaluation
+ * that would throw an exception when run in checked mode. The client of the
+ * analysis engine is responsible for determining how these errors should be
+ * presented to the user (for example, a command-line compiler might elect to
+ * treat these errors differently depending whether it is compiling it "checked"
+ * mode).
+ */
+class CheckedModeCompileTimeErrorCode extends ErrorCode {
+  // TODO(paulberry): improve the text of these error messages so that it's
+  // clear to the user that the error is coming from constant evaluation (and
+  // hence the constant needs to be a subtype of the annotated type) as opposed
+  // to static type analysis (which only requires that the two types be
+  // assignable).  Also consider populating the "correction" field for these
+  // errors.
+
+  /**
+   * 12.11.2 Const: It is a compile-time error if evaluation of a constant
+   * object results in an uncaught exception being thrown.
+   */
+  static const CheckedModeCompileTimeErrorCode CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH =
+      const CheckedModeCompileTimeErrorCode(
+          'CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH',
+          "The object type '{0}' cannot be assigned to the field '{1}', which has type '{2}'");
+
+  /**
+   * 12.11.2 Const: It is a compile-time error if evaluation of a constant
+   * object results in an uncaught exception being thrown.
+   */
+  static const CheckedModeCompileTimeErrorCode CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH =
+      const CheckedModeCompileTimeErrorCode(
+          'CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH',
+          "The object type '{0}' cannot be assigned to a parameter of type '{1}'");
+
+  /**
+   * 7.6.1 Generative Constructors: In checked mode, it is a dynamic type error
+   * if o is not <b>null</b> and the interface of the class of <i>o</i> is not a
+   * subtype of the static type of the field <i>v</i>.
+   *
+   * 12.11.2 Const: It is a compile-time error if evaluation of a constant
+   * object results in an uncaught exception being thrown.
+   *
+   * Parameters:
+   * 0: the name of the type of the initializer expression
+   * 1: the name of the type of the field
+   */
+  static const CheckedModeCompileTimeErrorCode CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE =
+      const CheckedModeCompileTimeErrorCode(
+          'CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE',
+          "The initializer type '{0}' cannot be assigned to the field type '{1}'");
+
+  /**
+   * 12.6 Lists: A run-time list literal &lt;<i>E</i>&gt; [<i>e<sub>1</sub></i>
+   * ... <i>e<sub>n</sub></i>] is evaluated as follows:
+   * * The operator []= is invoked on <i>a</i> with first argument <i>i</i> and
+   *   second argument <i>o<sub>i+1</sub></i><i>, 1 &lt;= i &lt;= n</i>
+   *
+   * 12.14.2 Binding Actuals to Formals: Let <i>T<sub>i</sub></i> be the static
+   * type of <i>a<sub>i</sub></i>, let <i>S<sub>i</sub></i> be the type of
+   * <i>p<sub>i</sub>, 1 &lt;= i &lt;= n+k</i> and let <i>S<sub>q</sub></i> be
+   * the type of the named parameter <i>q</i> of <i>f</i>. It is a static
+   * warning if <i>T<sub>j</sub></i> may not be assigned to <i>S<sub>j</sub>,
+   * 1 &lt;= j &lt;= m</i>.
+   */
+  static const CheckedModeCompileTimeErrorCode LIST_ELEMENT_TYPE_NOT_ASSIGNABLE =
+      const CheckedModeCompileTimeErrorCode('LIST_ELEMENT_TYPE_NOT_ASSIGNABLE',
+          "The element type '{0}' cannot be assigned to the list type '{1}'");
+
+  /**
+   * 12.7 Map: A run-time map literal &lt;<i>K</i>, <i>V</i>&gt;
+   * [<i>k<sub>1</sub></i> : <i>e<sub>1</sub></i> ... <i>k<sub>n</sub></i> :
+   * <i>e<sub>n</sub></i>] is evaluated as follows:
+   * * The operator []= is invoked on <i>m</i> with first argument
+   *   <i>k<sub>i</sub></i> and second argument <i>e<sub>i</sub></i><i>, 1 &lt;=
+   *   i &lt;= n</i>
+   *
+   * 12.14.2 Binding Actuals to Formals: Let <i>T<sub>i</sub></i> be the static
+   * type of <i>a<sub>i</sub></i>, let <i>S<sub>i</sub></i> be the type of
+   * <i>p<sub>i</sub>, 1 &lt;= i &lt;= n+k</i> and let <i>S<sub>q</sub></i> be
+   * the type of the named parameter <i>q</i> of <i>f</i>. It is a static
+   * warning if <i>T<sub>j</sub></i> may not be assigned to <i>S<sub>j</sub>, 1
+   * &lt;= j &lt;= m</i>.
+   */
+  static const CheckedModeCompileTimeErrorCode MAP_KEY_TYPE_NOT_ASSIGNABLE =
+      const CheckedModeCompileTimeErrorCode('MAP_KEY_TYPE_NOT_ASSIGNABLE',
+          "The element type '{0}' cannot be assigned to the map key type '{1}'");
+
+  /**
+   * 12.7 Map: A run-time map literal &lt;<i>K</i>, <i>V</i>&gt;
+   * [<i>k<sub>1</sub></i> : <i>e<sub>1</sub></i> ... <i>k<sub>n</sub></i> :
+   * <i>e<sub>n</sub></i>] is evaluated as follows:
+   * * The operator []= is invoked on <i>m</i> with first argument
+   *   <i>k<sub>i</sub></i> and second argument <i>e<sub>i</sub></i><i>, 1 &lt;=
+   *   i &lt;= n</i>
+   *
+   * 12.14.2 Binding Actuals to Formals: Let <i>T<sub>i</sub></i> be the static
+   * type of <i>a<sub>i</sub></i>, let <i>S<sub>i</sub></i> be the type of
+   * <i>p<sub>i</sub>, 1 &lt;= i &lt;= n+k</i> and let <i>S<sub>q</sub></i> be
+   * the type of the named parameter <i>q</i> of <i>f</i>. It is a static
+   * warning if <i>T<sub>j</sub></i> may not be assigned to <i>S<sub>j</sub>, 1
+   * &lt;= j &lt;= m</i>.
+   */
+  static const CheckedModeCompileTimeErrorCode MAP_VALUE_TYPE_NOT_ASSIGNABLE =
+      const CheckedModeCompileTimeErrorCode('MAP_VALUE_TYPE_NOT_ASSIGNABLE',
+          "The element type '{0}' cannot be assigned to the map value type '{1}'");
+
+  /**
+   * 12.11.2 Const: It is a compile-time error if evaluation of a constant
+   * object results in an uncaught exception being thrown.
+   */
+  static const CheckedModeCompileTimeErrorCode VARIABLE_TYPE_MISMATCH =
+      const CheckedModeCompileTimeErrorCode('VARIABLE_TYPE_MISMATCH',
+          "The object type '{0}' cannot be assigned to a variable of type '{1}'");
+
+  /**
+   * Initialize a newly created error code to have the given [name]. The message
+   * associated with the error will be created from the given [message]
+   * template. The correction associated with the error will be created from the
+   * given [correction] template.
+   */
+  const CheckedModeCompileTimeErrorCode(String name, String message,
+      [String correction])
+      : super(name, message, correction);
+
+  @override
+  ErrorSeverity get errorSeverity =>
+      ErrorType.CHECKED_MODE_COMPILE_TIME_ERROR.severity;
+
+  @override
+  ErrorType get type => ErrorType.CHECKED_MODE_COMPILE_TIME_ERROR;
+}
+
+/**
+ * The error codes used for compile time errors. The convention for this class
+ * is for the name of the error code to indicate the problem that caused the
+ * error to be generated and for the error message to explain what is wrong and,
+ * when appropriate, how the problem can be corrected.
+ */
+class CompileTimeErrorCode extends ErrorCode {
+  /**
+   * Enum proposal: It is also a compile-time error to explicitly instantiate an
+   * enum via 'new' or 'const' or to access its private fields.
+   */
+  static const CompileTimeErrorCode ACCESS_PRIVATE_ENUM_FIELD =
+      const CompileTimeErrorCode('ACCESS_PRIVATE_ENUM_FIELD',
+          "The private fields of an enum cannot be accessed, even within the same library");
+
+  /**
+   * 14.2 Exports: It is a compile-time error if a name <i>N</i> is re-exported
+   * by a library <i>L</i> and <i>N</i> is introduced into the export namespace
+   * of <i>L</i> by more than one export, unless each all exports refer to same
+   * declaration for the name N.
+   *
+   * Parameters:
+   * 0: the name of the ambiguous element
+   * 1: the name of the first library that the type is found
+   * 2: the name of the second library that the type is found
+   */
+  static const CompileTimeErrorCode AMBIGUOUS_EXPORT =
+      const CompileTimeErrorCode('AMBIGUOUS_EXPORT',
+          "The name '{0}' is defined in the libraries '{1}' and '{2}'");
+
+  /**
+   * 12.33 Argument Definition Test: It is a compile time error if <i>v</i> does
+   * not denote a formal parameter.
+   *
+   * Parameters:
+   * 0: the name of the identifier in the argument definition test that is not a
+   *    parameter
+   */
+  static const CompileTimeErrorCode ARGUMENT_DEFINITION_TEST_NON_PARAMETER =
+      const CompileTimeErrorCode(
+          'ARGUMENT_DEFINITION_TEST_NON_PARAMETER', "'{0}' is not a parameter");
+
+  /**
+   * ?? Asynchronous For-in: It is a compile-time error if an asynchronous
+   * for-in statement appears inside a synchronous function.
+   */
+  static const CompileTimeErrorCode ASYNC_FOR_IN_WRONG_CONTEXT =
+      const CompileTimeErrorCode('ASYNC_FOR_IN_WRONG_CONTEXT',
+          "The asynchronous for-in can only be used in a function marked with async or async*");
+
+  /**
+   * ??: It is a compile-time error if the function immediately enclosing a is
+   * not declared asynchronous.
+   */
+  static const CompileTimeErrorCode AWAIT_IN_WRONG_CONTEXT =
+      const CompileTimeErrorCode('AWAIT_IN_WRONG_CONTEXT',
+          "The await expression can only be used in a function marked as async or async*");
+
+  /**
+   * 12.30 Identifier Reference: It is a compile-time error to use a built-in
+   * identifier other than dynamic as a type annotation.
+   */
+  static const CompileTimeErrorCode BUILT_IN_IDENTIFIER_AS_TYPE =
+      const CompileTimeErrorCode('BUILT_IN_IDENTIFIER_AS_TYPE',
+          "The built-in identifier '{0}' cannot be as a type");
+
+  /**
+   * 12.30 Identifier Reference: It is a compile-time error if a built-in
+   * identifier is used as the declared name of a class, type parameter or type
+   * alias.
+   */
+  static const CompileTimeErrorCode BUILT_IN_IDENTIFIER_AS_TYPE_NAME =
+      const CompileTimeErrorCode('BUILT_IN_IDENTIFIER_AS_TYPE_NAME',
+          "The built-in identifier '{0}' cannot be used as a type name");
+
+  /**
+   * 12.30 Identifier Reference: It is a compile-time error if a built-in
+   * identifier is used as the declared name of a class, type parameter or type
+   * alias.
+   */
+  static const CompileTimeErrorCode BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME =
+      const CompileTimeErrorCode('BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME',
+          "The built-in identifier '{0}' cannot be used as a type alias name");
+
+  /**
+   * 12.30 Identifier Reference: It is a compile-time error if a built-in
+   * identifier is used as the declared name of a class, type parameter or type
+   * alias.
+   */
+  static const CompileTimeErrorCode BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME =
+      const CompileTimeErrorCode('BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME',
+          "The built-in identifier '{0}' cannot be used as a type parameter name");
+
+  /**
+   * 13.9 Switch: It is a compile-time error if the class <i>C</i> implements
+   * the operator <i>==</i>.
+   */
+  static const CompileTimeErrorCode CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS =
+      const CompileTimeErrorCode('CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS',
+          "The switch case expression type '{0}' cannot override the == operator");
+
+  /**
+   * 12.1 Constants: It is a compile-time error if evaluation of a compile-time
+   * constant would raise
+   * an exception.
+   */
+  static const CompileTimeErrorCode COMPILE_TIME_CONSTANT_RAISES_EXCEPTION =
+      const CompileTimeErrorCode('COMPILE_TIME_CONSTANT_RAISES_EXCEPTION', "");
+
+  /**
+   * 7.2 Getters: It is a compile-time error if a class has both a getter and a
+   * method with the same name. This restriction holds regardless of whether the
+   * getter is defined explicitly or implicitly, or whether the getter or the
+   * method are inherited or not.
+   */
+  static const CompileTimeErrorCode CONFLICTING_GETTER_AND_METHOD =
+      const CompileTimeErrorCode('CONFLICTING_GETTER_AND_METHOD',
+          "Class '{0}' cannot have both getter '{1}.{2}' and method with the same name");
+
+  /**
+   * 7.2 Getters: It is a compile-time error if a class has both a getter and a
+   * method with the same name. This restriction holds regardless of whether the
+   * getter is defined explicitly or implicitly, or whether the getter or the
+   * method are inherited or not.
+   */
+  static const CompileTimeErrorCode CONFLICTING_METHOD_AND_GETTER =
+      const CompileTimeErrorCode('CONFLICTING_METHOD_AND_GETTER',
+          "Class '{0}' cannot have both method '{1}.{2}' and getter with the same name");
+
+  /**
+   * 7.6 Constructors: A constructor name always begins with the name of its
+   * immediately enclosing class, and may optionally be followed by a dot and an
+   * identifier <i>id</i>. It is a compile-time error if <i>id</i> is the name
+   * of a member declared in the immediately enclosing class.
+   */
+  static const CompileTimeErrorCode CONFLICTING_CONSTRUCTOR_NAME_AND_FIELD =
+      const CompileTimeErrorCode('CONFLICTING_CONSTRUCTOR_NAME_AND_FIELD',
+          "'{0}' cannot be used to name a constructor and a field in this class");
+
+  /**
+   * 7.6 Constructors: A constructor name always begins with the name of its
+   * immediately enclosing class, and may optionally be followed by a dot and an
+   * identifier <i>id</i>. It is a compile-time error if <i>id</i> is the name
+   * of a member declared in the immediately enclosing class.
+   */
+  static const CompileTimeErrorCode CONFLICTING_CONSTRUCTOR_NAME_AND_METHOD =
+      const CompileTimeErrorCode('CONFLICTING_CONSTRUCTOR_NAME_AND_METHOD',
+          "'{0}' cannot be used to name a constructor and a method in this class");
+
+  /**
+   * 7. Classes: It is a compile time error if a generic class declares a type
+   * variable with the same name as the class or any of its members or
+   * constructors.
+   */
+  static const CompileTimeErrorCode CONFLICTING_TYPE_VARIABLE_AND_CLASS =
+      const CompileTimeErrorCode('CONFLICTING_TYPE_VARIABLE_AND_CLASS',
+          "'{0}' cannot be used to name a type varaible in a class with the same name");
+
+  /**
+   * 7. Classes: It is a compile time error if a generic class declares a type
+   * variable with the same name as the class or any of its members or
+   * constructors.
+   */
+  static const CompileTimeErrorCode CONFLICTING_TYPE_VARIABLE_AND_MEMBER =
+      const CompileTimeErrorCode('CONFLICTING_TYPE_VARIABLE_AND_MEMBER',
+          "'{0}' cannot be used to name a type varaible and member in this class");
+
+  /**
+   * 12.11.2 Const: It is a compile-time error if evaluation of a constant
+   * object results in an uncaught exception being thrown.
+   */
+  static const CompileTimeErrorCode CONST_CONSTRUCTOR_THROWS_EXCEPTION =
+      const CompileTimeErrorCode('CONST_CONSTRUCTOR_THROWS_EXCEPTION',
+          "'const' constructors cannot throw exceptions");
+
+  /**
+   * 10.6.3 Constant Constructors: It is a compile-time error if a constant
+   * constructor is declared by a class C if any instance variable declared in C
+   * is initialized with an expression that is not a constant expression.
+   */
+  static const CompileTimeErrorCode CONST_CONSTRUCTOR_WITH_FIELD_INITIALIZED_BY_NON_CONST =
+      const CompileTimeErrorCode(
+          'CONST_CONSTRUCTOR_WITH_FIELD_INITIALIZED_BY_NON_CONST',
+          "Can't define the 'const' constructor because the field '{0}' is initialized with a non-constant value");
+
+  /**
+   * 7.6.3 Constant Constructors: The superinitializer that appears, explicitly
+   * or implicitly, in the initializer list of a constant constructor must
+   * specify a constant constructor of the superclass of the immediately
+   * enclosing class or a compile-time error occurs.
+   *
+   * 9 Mixins: For each generative constructor named ... an implicitly declared
+   * constructor named ... is declared.
+   */
+  static const CompileTimeErrorCode CONST_CONSTRUCTOR_WITH_MIXIN =
+      const CompileTimeErrorCode('CONST_CONSTRUCTOR_WITH_MIXIN',
+          "Constant constructor cannot be declared for a class with a mixin");
+
+  /**
+   * 7.6.3 Constant Constructors: The superinitializer that appears, explicitly
+   * or implicitly, in the initializer list of a constant constructor must
+   * specify a constant constructor of the superclass of the immediately
+   * enclosing class or a compile-time error occurs.
+   */
+  static const CompileTimeErrorCode CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER =
+      const CompileTimeErrorCode('CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER',
+          "Constant constructor cannot call non-constant super constructor of '{0}'");
+
+  /**
+   * 7.6.3 Constant Constructors: It is a compile-time error if a constant
+   * constructor is declared by a class that has a non-final instance variable.
+   *
+   * The above refers to both locally declared and inherited instance variables.
+   */
+  static const CompileTimeErrorCode CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD =
+      const CompileTimeErrorCode('CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD',
+          "Cannot define the 'const' constructor for a class with non-final fields");
+
+  /**
+   * 12.12.2 Const: It is a compile-time error if <i>T</i> is a deferred type.
+   */
+  static const CompileTimeErrorCode CONST_DEFERRED_CLASS =
+      const CompileTimeErrorCode('CONST_DEFERRED_CLASS',
+          "Deferred classes cannot be created with 'const'");
+
+  /**
+   * 6.2 Formal Parameters: It is a compile-time error if a formal parameter is
+   * declared as a constant variable.
+   */
+  static const CompileTimeErrorCode CONST_FORMAL_PARAMETER =
+      const CompileTimeErrorCode(
+          'CONST_FORMAL_PARAMETER', "Parameters cannot be 'const'");
+
+  /**
+   * 5 Variables: A constant variable must be initialized to a compile-time
+   * constant or a compile-time error occurs.
+   */
+  static const CompileTimeErrorCode CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE =
+      const CompileTimeErrorCode('CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE',
+          "'const' variables must be constant value");
+
+  /**
+   * 5 Variables: A constant variable must be initialized to a compile-time
+   * constant or a compile-time error occurs.
+   *
+   * 12.1 Constants: A qualified reference to a static constant variable that is
+   * not qualified by a deferred prefix.
+   */
+  static const CompileTimeErrorCode CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE_FROM_DEFERRED_LIBRARY =
+      const CompileTimeErrorCode(
+          'CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE_FROM_DEFERRED_LIBRARY',
+          "Constant values from a deferred library cannot be used to initialized a 'const' variable");
+
+  /**
+   * 7.5 Instance Variables: It is a compile-time error if an instance variable
+   * is declared to be constant.
+   */
+  static const CompileTimeErrorCode CONST_INSTANCE_FIELD =
+      const CompileTimeErrorCode('CONST_INSTANCE_FIELD',
+          "Only static fields can be declared as 'const'");
+
+  /**
+   * 12.8 Maps: It is a compile-time error if the key of an entry in a constant
+   * map literal is an instance of a class that implements the operator
+   * <i>==</i> unless the key is a string or integer.
+   */
+  static const CompileTimeErrorCode CONST_MAP_KEY_EXPRESSION_TYPE_IMPLEMENTS_EQUALS =
+      const CompileTimeErrorCode(
+          'CONST_MAP_KEY_EXPRESSION_TYPE_IMPLEMENTS_EQUALS',
+          "The constant map entry key expression type '{0}' cannot override the == operator");
+
+  /**
+   * 5 Variables: A constant variable must be initialized to a compile-time
+   * constant (12.1) or a compile-time error occurs.
+   *
+   * Parameters:
+   * 0: the name of the uninitialized final variable
+   */
+  static const CompileTimeErrorCode CONST_NOT_INITIALIZED =
+      const CompileTimeErrorCode('CONST_NOT_INITIALIZED',
+          "The const variable '{0}' must be initialized");
+
+  /**
+   * 12.11.2 Const: An expression of one of the forms !e, e1 && e2 or e1 || e2,
+   * where e, e1 and e2 are constant expressions that evaluate to a boolean
+   * value.
+   */
+  static const CompileTimeErrorCode CONST_EVAL_TYPE_BOOL =
+      const CompileTimeErrorCode('CONST_EVAL_TYPE_BOOL',
+          "In constant expressions, operand(s) of this operator must be of type 'bool'");
+
+  /**
+   * 12.11.2 Const: An expression of one of the forms e1 == e2 or e1 != e2 where
+   * e1 and e2 are constant expressions that evaluate to a numeric, string or
+   * boolean value or to null.
+   */
+  static const CompileTimeErrorCode CONST_EVAL_TYPE_BOOL_NUM_STRING =
+      const CompileTimeErrorCode('CONST_EVAL_TYPE_BOOL_NUM_STRING',
+          "In constant expressions, operands of this operator must be of type 'bool', 'num', 'String' or 'null'");
+
+  /**
+   * 12.11.2 Const: An expression of one of the forms ~e, e1 ^ e2, e1 & e2,
+   * e1 | e2, e1 >> e2 or e1 << e2, where e, e1 and e2 are constant expressions
+   * that evaluate to an integer value or to null.
+   */
+  static const CompileTimeErrorCode CONST_EVAL_TYPE_INT =
+      const CompileTimeErrorCode('CONST_EVAL_TYPE_INT',
+          "In constant expressions, operand(s) of this operator must be of type 'int'");
+
+  /**
+   * 12.11.2 Const: An expression of one of the forms e, e1 + e2, e1 - e2, e1 *
+   * e2, e1 / e2, e1 ~/ e2, e1 > e2, e1 < e2, e1 >= e2, e1 <= e2 or e1 % e2,
+   * where e, e1 and e2 are constant expressions that evaluate to a numeric
+   * value or to null.
+   */
+  static const CompileTimeErrorCode CONST_EVAL_TYPE_NUM =
+      const CompileTimeErrorCode('CONST_EVAL_TYPE_NUM',
+          "In constant expressions, operand(s) of this operator must be of type 'num'");
+
+  /**
+   * 12.11.2 Const: It is a compile-time error if evaluation of a constant
+   * object results in an uncaught exception being thrown.
+   */
+  static const CompileTimeErrorCode CONST_EVAL_THROWS_EXCEPTION =
+      const CompileTimeErrorCode('CONST_EVAL_THROWS_EXCEPTION',
+          "Evaluation of this constant expression causes exception");
+
+  /**
+   * 12.11.2 Const: It is a compile-time error if evaluation of a constant
+   * object results in an uncaught exception being thrown.
+   */
+  static const CompileTimeErrorCode CONST_EVAL_THROWS_IDBZE =
+      const CompileTimeErrorCode('CONST_EVAL_THROWS_IDBZE',
+          "Evaluation of this constant expression throws IntegerDivisionByZeroException");
+
+  /**
+   * 12.11.2 Const: If <i>T</i> is a parameterized type <i>S&lt;U<sub>1</sub>,
+   * &hellip;, U<sub>m</sub>&gt;</i>, let <i>R = S</i>; It is a compile time
+   * error if <i>S</i> is not a generic type with <i>m</i> type parameters.
+   *
+   * Parameters:
+   * 0: the name of the type being referenced (<i>S</i>)
+   * 1: the number of type parameters that were declared
+   * 2: the number of type arguments provided
+   *
+   * See [CompileTimeErrorCode.NEW_WITH_INVALID_TYPE_PARAMETERS], and
+   * [StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS].
+   */
+  static const CompileTimeErrorCode CONST_WITH_INVALID_TYPE_PARAMETERS =
+      const CompileTimeErrorCode('CONST_WITH_INVALID_TYPE_PARAMETERS',
+          "The type '{0}' is declared with {1} type parameters, but {2} type arguments were given");
+
+  /**
+   * 12.11.2 Const: If <i>e</i> is of the form <i>const T(a<sub>1</sub>,
+   * &hellip;, a<sub>n</sub>, x<sub>n+1</sub>: a<sub>n+1</sub>, &hellip;,
+   * x<sub>n+k</sub>: a<sub>n+k</sub>)</i> it is a compile-time error if the
+   * type <i>T</i> does not declare a constant constructor with the same name as
+   * the declaration of <i>T</i>.
+   */
+  static const CompileTimeErrorCode CONST_WITH_NON_CONST =
+      const CompileTimeErrorCode('CONST_WITH_NON_CONST',
+          "The constructor being called is not a 'const' constructor");
+
+  /**
+   * 12.11.2 Const: In all of the above cases, it is a compile-time error if
+   * <i>a<sub>i</sub>, 1 &lt;= i &lt;= n + k</i>, is not a compile-time constant
+   * expression.
+   */
+  static const CompileTimeErrorCode CONST_WITH_NON_CONSTANT_ARGUMENT =
+      const CompileTimeErrorCode('CONST_WITH_NON_CONSTANT_ARGUMENT',
+          "Arguments of a constant creation must be constant expressions");
+
+  /**
+   * 12.11.2 Const: It is a compile-time error if <i>T</i> is not a class
+   * accessible in the current scope, optionally followed by type arguments.
+   *
+   * 12.11.2 Const: If <i>e</i> is of the form <i>const T.id(a<sub>1</sub>,
+   * &hellip;, a<sub>n</sub>, x<sub>n+1</sub>: a<sub>n+1</sub>, &hellip;
+   * x<sub>n+k</sub>: a<sub>n+k</sub>)</i> it is a compile-time error if
+   * <i>T</i> is not a class accessible in the current scope, optionally
+   * followed by type arguments.
+   *
+   * Parameters:
+   * 0: the name of the non-type element
+   */
+  static const CompileTimeErrorCode CONST_WITH_NON_TYPE =
+      const CompileTimeErrorCode(
+          'CONST_WITH_NON_TYPE', "The name '{0}' is not a class");
+
+  /**
+   * 12.11.2 Const: It is a compile-time error if <i>T</i> includes any type
+   * parameters.
+   */
+  static const CompileTimeErrorCode CONST_WITH_TYPE_PARAMETERS =
+      const CompileTimeErrorCode('CONST_WITH_TYPE_PARAMETERS',
+          "The constant creation cannot use a type parameter");
+
+  /**
+   * 12.11.2 Const: It is a compile-time error if <i>T.id</i> is not the name of
+   * a constant constructor declared by the type <i>T</i>.
+   *
+   * Parameters:
+   * 0: the name of the type
+   * 1: the name of the requested constant constructor
+   */
+  static const CompileTimeErrorCode CONST_WITH_UNDEFINED_CONSTRUCTOR =
+      const CompileTimeErrorCode('CONST_WITH_UNDEFINED_CONSTRUCTOR',
+          "The class '{0}' does not have a constant constructor '{1}'");
+
+  /**
+   * 12.11.2 Const: It is a compile-time error if <i>T.id</i> is not the name of
+   * a constant constructor declared by the type <i>T</i>.
+   *
+   * Parameters:
+   * 0: the name of the type
+   */
+  static const CompileTimeErrorCode CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT =
+      const CompileTimeErrorCode('CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT',
+          "The class '{0}' does not have a default constant constructor");
+
+  /**
+   * 15.3.1 Typedef: It is a compile-time error if any default values are
+   * specified in the signature of a function type alias.
+   */
+  static const CompileTimeErrorCode DEFAULT_VALUE_IN_FUNCTION_TYPE_ALIAS =
+      const CompileTimeErrorCode('DEFAULT_VALUE_IN_FUNCTION_TYPE_ALIAS',
+          "Default values aren't allowed in typedefs");
+
+  /**
+   * 6.2.1 Required Formals: By means of a function signature that names the
+   * parameter and describes its type as a function type. It is a compile-time
+   * error if any default values are specified in the signature of such a
+   * function type.
+   */
+  static const CompileTimeErrorCode DEFAULT_VALUE_IN_FUNCTION_TYPED_PARAMETER =
+      const CompileTimeErrorCode('DEFAULT_VALUE_IN_FUNCTION_TYPED_PARAMETER',
+          "Default values aren't allowed in function type parameters");
+
+  /**
+   * 7.6.2 Factories: It is a compile-time error if <i>k</i> explicitly
+   * specifies a default value for an optional parameter.
+   */
+  static const CompileTimeErrorCode DEFAULT_VALUE_IN_REDIRECTING_FACTORY_CONSTRUCTOR =
+      const CompileTimeErrorCode(
+          'DEFAULT_VALUE_IN_REDIRECTING_FACTORY_CONSTRUCTOR',
+          "Default values aren't allowed in factory constructors that redirect to another constructor");
+
+  /**
+   * 3.1 Scoping: It is a compile-time error if there is more than one entity
+   * with the same name declared in the same scope.
+   */
+  static const CompileTimeErrorCode DUPLICATE_CONSTRUCTOR_DEFAULT =
+      const CompileTimeErrorCode('DUPLICATE_CONSTRUCTOR_DEFAULT',
+          "The default constructor is already defined");
+
+  /**
+   * 3.1 Scoping: It is a compile-time error if there is more than one entity
+   * with the same name declared in the same scope.
+   *
+   * Parameters:
+   * 0: the name of the duplicate entity
+   */
+  static const CompileTimeErrorCode DUPLICATE_CONSTRUCTOR_NAME =
+      const CompileTimeErrorCode('DUPLICATE_CONSTRUCTOR_NAME',
+          "The constructor with name '{0}' is already defined");
+
+  /**
+   * 3.1 Scoping: It is a compile-time error if there is more than one entity
+   * with the same name declared in the same scope.
+   *
+   * 7 Classes: It is a compile-time error if a class declares two members of
+   * the same name.
+   *
+   * 7 Classes: It is a compile-time error if a class has an instance member and
+   * a static member with the same name.
+   *
+   * Parameters:
+   * 0: the name of the duplicate entity
+   */
+  static const CompileTimeErrorCode DUPLICATE_DEFINITION =
+      const CompileTimeErrorCode(
+          'DUPLICATE_DEFINITION', "The name '{0}' is already defined");
+
+  /**
+   * 7. Classes: It is a compile-time error if a class has an instance member
+   * and a static member with the same name.
+   *
+   * This covers the additional duplicate definition cases where inheritance has
+   * to be considered.
+   *
+   * Parameters:
+   * 0: the name of the class that has conflicting instance/static members
+   * 1: the name of the conflicting members
+   *
+   * See [DUPLICATE_DEFINITION].
+   */
+  static const CompileTimeErrorCode DUPLICATE_DEFINITION_INHERITANCE =
+      const CompileTimeErrorCode('DUPLICATE_DEFINITION_INHERITANCE',
+          "The name '{0}' is already defined in '{1}'");
+
+  /**
+   * 12.14.2 Binding Actuals to Formals: It is a compile-time error if
+   * <i>q<sub>i</sub> = q<sub>j</sub></i> for any <i>i != j</i> [where
+   * <i>q<sub>i</sub></i> is the label for a named argument].
+   */
+  static const CompileTimeErrorCode DUPLICATE_NAMED_ARGUMENT =
+      const CompileTimeErrorCode('DUPLICATE_NAMED_ARGUMENT',
+          "The argument for the named parameter '{0}' was already specified");
+
+  /**
+   * SDK implementation libraries can be exported only by other SDK libraries.
+   *
+   * Parameters:
+   * 0: the uri pointing to a library
+   */
+  static const CompileTimeErrorCode EXPORT_INTERNAL_LIBRARY =
+      const CompileTimeErrorCode('EXPORT_INTERNAL_LIBRARY',
+          "The library '{0}' is internal and cannot be exported");
+
+  /**
+   * 14.2 Exports: It is a compile-time error if the compilation unit found at
+   * the specified URI is not a library declaration.
+   *
+   * Parameters:
+   * 0: the uri pointing to a non-library declaration
+   */
+  static const CompileTimeErrorCode EXPORT_OF_NON_LIBRARY =
+      const CompileTimeErrorCode('EXPORT_OF_NON_LIBRARY',
+          "The exported library '{0}' must not have a part-of directive");
+
+  /**
+   * Enum proposal: It is a compile-time error to subclass, mix-in or implement
+   * an enum.
+   */
+  static const CompileTimeErrorCode EXTENDS_ENUM = const CompileTimeErrorCode(
+      'EXTENDS_ENUM', "Classes cannot extend an enum");
+
+  /**
+   * 7.9 Superclasses: It is a compile-time error if the extends clause of a
+   * class <i>C</i> includes a type expression that does not denote a class
+   * available in the lexical scope of <i>C</i>.
+   *
+   * Parameters:
+   * 0: the name of the superclass that was not found
+   */
+  static const CompileTimeErrorCode EXTENDS_NON_CLASS =
+      const CompileTimeErrorCode(
+          'EXTENDS_NON_CLASS', "Classes can only extend other classes");
+
+  /**
+   * 12.2 Null: It is a compile-time error for a class to attempt to extend or
+   * implement Null.
+   *
+   * 12.3 Numbers: It is a compile-time error for a class to attempt to extend
+   * or implement int.
+   *
+   * 12.3 Numbers: It is a compile-time error for a class to attempt to extend
+   * or implement double.
+   *
+   * 12.3 Numbers: It is a compile-time error for any type other than the types
+   * int and double to
+   * attempt to extend or implement num.
+   *
+   * 12.4 Booleans: It is a compile-time error for a class to attempt to extend
+   * or implement bool.
+   *
+   * 12.5 Strings: It is a compile-time error for a class to attempt to extend
+   * or implement String.
+   *
+   * Parameters:
+   * 0: the name of the type that cannot be extended
+   *
+   * See [IMPLEMENTS_DISALLOWED_CLASS].
+   */
+  static const CompileTimeErrorCode EXTENDS_DISALLOWED_CLASS =
+      const CompileTimeErrorCode(
+          'EXTENDS_DISALLOWED_CLASS', "Classes cannot extend '{0}'");
+
+  /**
+   * 7.9 Superclasses: It is a compile-time error if the extends clause of a
+   * class <i>C</i> includes a deferred type expression.
+   *
+   * Parameters:
+   * 0: the name of the type that cannot be extended
+   *
+   * See [IMPLEMENTS_DEFERRED_CLASS], and [MIXIN_DEFERRED_CLASS].
+   */
+  static const CompileTimeErrorCode EXTENDS_DEFERRED_CLASS =
+      const CompileTimeErrorCode('EXTENDS_DEFERRED_CLASS',
+          "This class cannot extend the deferred class '{0}'");
+
+  /**
+   * 12.14.2 Binding Actuals to Formals: It is a static warning if <i>m &lt;
+   * h</i> or if <i>m &gt; n</i>.
+   *
+   * 12.11.2 Const: It is a compile-time error if evaluation of a constant
+   * object results in an uncaught exception being thrown.
+   *
+   * Parameters:
+   * 0: the maximum number of positional arguments
+   * 1: the actual number of positional arguments given
+   */
+  static const CompileTimeErrorCode EXTRA_POSITIONAL_ARGUMENTS =
+      const CompileTimeErrorCode('EXTRA_POSITIONAL_ARGUMENTS',
+          "{0} positional arguments expected, but {1} found");
+
+  /**
+   * 7.6.1 Generative Constructors: Let <i>k</i> be a generative constructor. It
+   * is a compile time error if more than one initializer corresponding to a
+   * given instance variable appears in <i>k</i>'s list.
+   */
+  static const CompileTimeErrorCode FIELD_INITIALIZED_BY_MULTIPLE_INITIALIZERS =
+      const CompileTimeErrorCode('FIELD_INITIALIZED_BY_MULTIPLE_INITIALIZERS',
+          "The field '{0}' cannot be initialized twice in the same constructor");
+
+  /**
+   * 7.6.1 Generative Constructors: Let <i>k</i> be a generative constructor. It
+   * is a compile time error if <i>k</i>'s initializer list contains an
+   * initializer for a variable that is initialized by means of an initializing
+   * formal of <i>k</i>.
+   */
+  static const CompileTimeErrorCode FIELD_INITIALIZED_IN_PARAMETER_AND_INITIALIZER =
+      const CompileTimeErrorCode(
+          'FIELD_INITIALIZED_IN_PARAMETER_AND_INITIALIZER',
+          "Fields cannot be initialized in both the parameter list and the initializers");
+
+  /**
+   * 5 Variables: It is a compile-time error if a final instance variable that
+   * has is initialized by means of an initializing formal of a constructor is
+   * also initialized elsewhere in the same constructor.
+   *
+   * Parameters:
+   * 0: the name of the field in question
+   */
+  static const CompileTimeErrorCode FINAL_INITIALIZED_MULTIPLE_TIMES =
+      const CompileTimeErrorCode('FINAL_INITIALIZED_MULTIPLE_TIMES',
+          "'{0}' is a final field and so can only be set once");
+
+  /**
+   * 7.6.1 Generative Constructors: It is a compile-time error if an
+   * initializing formal is used by a function other than a non-redirecting
+   * generative constructor.
+   */
+  static const CompileTimeErrorCode FIELD_INITIALIZER_FACTORY_CONSTRUCTOR =
+      const CompileTimeErrorCode('FIELD_INITIALIZER_FACTORY_CONSTRUCTOR',
+          "Initializing formal fields cannot be used in factory constructors");
+
+  /**
+   * 7.6.1 Generative Constructors: It is a compile-time error if an
+   * initializing formal is used by a function other than a non-redirecting
+   * generative constructor.
+   */
+  static const CompileTimeErrorCode FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR =
+      const CompileTimeErrorCode('FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR',
+          "Initializing formal fields can only be used in constructors");
+
+  /**
+   * 7.6.1 Generative Constructors: A generative constructor may be redirecting,
+   * in which case its only action is to invoke another generative constructor.
+   *
+   * 7.6.1 Generative Constructors: It is a compile-time error if an
+   * initializing formal is used by a function other than a non-redirecting
+   * generative constructor.
+   */
+  static const CompileTimeErrorCode FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR =
+      const CompileTimeErrorCode('FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR',
+          "The redirecting constructor cannot have a field initializer");
+
+  /**
+   * 7.2 Getters: It is a compile-time error if a class has both a getter and a
+   * method with the same name.
+   *
+   * Parameters:
+   * 0: the conflicting name of the getter and method
+   */
+  static const CompileTimeErrorCode GETTER_AND_METHOD_WITH_SAME_NAME =
+      const CompileTimeErrorCode('GETTER_AND_METHOD_WITH_SAME_NAME',
+          "'{0}' cannot be used to name a getter, there is already a method with the same name");
+
+  /**
+   * 7.10 Superinterfaces: It is a compile-time error if the implements clause
+   * of a class <i>C</i> specifies a malformed type or deferred type as a
+   * superinterface.
+   *
+   * Parameters:
+   * 0: the name of the type that cannot be extended
+   *
+   * See [EXTENDS_DEFERRED_CLASS], and [MIXIN_DEFERRED_CLASS].
+   */
+  static const CompileTimeErrorCode IMPLEMENTS_DEFERRED_CLASS =
+      const CompileTimeErrorCode('IMPLEMENTS_DEFERRED_CLASS',
+          "This class cannot implement the deferred class '{0}'");
+
+  /**
+   * 12.2 Null: It is a compile-time error for a class to attempt to extend or
+   * implement Null.
+   *
+   * 12.3 Numbers: It is a compile-time error for a class to attempt to extend
+   * or implement int.
+   *
+   * 12.3 Numbers: It is a compile-time error for a class to attempt to extend
+   * or implement double.
+   *
+   * 12.3 Numbers: It is a compile-time error for any type other than the types
+   * int and double to
+   * attempt to extend or implement num.
+   *
+   * 12.4 Booleans: It is a compile-time error for a class to attempt to extend
+   * or implement bool.
+   *
+   * 12.5 Strings: It is a compile-time error for a class to attempt to extend
+   * or implement String.
+   *
+   * Parameters:
+   * 0: the name of the type that cannot be implemented
+   *
+   * See [EXTENDS_DISALLOWED_CLASS].
+   */
+  static const CompileTimeErrorCode IMPLEMENTS_DISALLOWED_CLASS =
+      const CompileTimeErrorCode(
+          'IMPLEMENTS_DISALLOWED_CLASS', "Classes cannot implement '{0}'");
+
+  /**
+   * 7.10 Superinterfaces: It is a compile-time error if the implements clause
+   * of a class includes type dynamic.
+   */
+  static const CompileTimeErrorCode IMPLEMENTS_DYNAMIC =
+      const CompileTimeErrorCode(
+          'IMPLEMENTS_DYNAMIC', "Classes cannot implement 'dynamic'");
+
+  /**
+   * Enum proposal: It is a compile-time error to subclass, mix-in or implement
+   * an enum.
+   */
+  static const CompileTimeErrorCode IMPLEMENTS_ENUM =
+      const CompileTimeErrorCode(
+          'IMPLEMENTS_ENUM', "Classes cannot implement an enum");
+
+  /**
+   * 7.10 Superinterfaces: It is a compile-time error if the implements clause
+   * of a class <i>C</i> includes a type expression that does not denote a class
+   * available in the lexical scope of <i>C</i>.
+   *
+   * Parameters:
+   * 0: the name of the interface that was not found
+   */
+  static const CompileTimeErrorCode IMPLEMENTS_NON_CLASS =
+      const CompileTimeErrorCode(
+          'IMPLEMENTS_NON_CLASS', "Classes can only implement other classes");
+
+  /**
+   * 7.10 Superinterfaces: It is a compile-time error if a type <i>T</i> appears
+   * more than once in the implements clause of a class.
+   *
+   * Parameters:
+   * 0: the name of the class that is implemented more than once
+   */
+  static const CompileTimeErrorCode IMPLEMENTS_REPEATED =
+      const CompileTimeErrorCode(
+          'IMPLEMENTS_REPEATED', "'{0}' can only be implemented once");
+
+  /**
+   * 7.10 Superinterfaces: It is a compile-time error if the superclass of a
+   * class <i>C</i> appears in the implements clause of <i>C</i>.
+   *
+   * Parameters:
+   * 0: the name of the class that appears in both "extends" and "implements"
+   *    clauses
+   */
+  static const CompileTimeErrorCode IMPLEMENTS_SUPER_CLASS =
+      const CompileTimeErrorCode('IMPLEMENTS_SUPER_CLASS',
+          "'{0}' cannot be used in both 'extends' and 'implements' clauses");
+
+  /**
+   * 7.6.1 Generative Constructors: Note that <b>this</b> is not in scope on the
+   * right hand side of an initializer.
+   *
+   * 12.10 This: It is a compile-time error if this appears in a top-level
+   * function or variable initializer, in a factory constructor, or in a static
+   * method or variable initializer, or in the initializer of an instance
+   * variable.
+   *
+   * Parameters:
+   * 0: the name of the type in question
+   */
+  static const CompileTimeErrorCode IMPLICIT_THIS_REFERENCE_IN_INITIALIZER =
+      const CompileTimeErrorCode('IMPLICIT_THIS_REFERENCE_IN_INITIALIZER',
+          "Only static members can be accessed in initializers");
+
+  /**
+   * SDK implementation libraries can be imported only by other SDK libraries.
+   *
+   * Parameters:
+   * 0: the uri pointing to a library
+   */
+  static const CompileTimeErrorCode IMPORT_INTERNAL_LIBRARY =
+      const CompileTimeErrorCode('IMPORT_INTERNAL_LIBRARY',
+          "The library '{0}' is internal and cannot be imported");
+
+  /**
+   * 14.1 Imports: It is a compile-time error if the specified URI of an
+   * immediate import does not refer to a library declaration.
+   *
+   * Parameters:
+   * 0: the uri pointing to a non-library declaration
+   *
+   * See [StaticWarningCode.IMPORT_OF_NON_LIBRARY].
+   */
+  static const CompileTimeErrorCode IMPORT_OF_NON_LIBRARY =
+      const CompileTimeErrorCode('IMPORT_OF_NON_LIBRARY',
+          "The imported library '{0}' must not have a part-of directive");
+
+  /**
+   * 13.9 Switch: It is a compile-time error if values of the expressions
+   * <i>e<sub>k</sub></i> are not instances of the same class <i>C</i>, for all
+   * <i>1 &lt;= k &lt;= n</i>.
+   *
+   * Parameters:
+   * 0: the expression source code that is the unexpected type
+   * 1: the name of the expected type
+   */
+  static const CompileTimeErrorCode INCONSISTENT_CASE_EXPRESSION_TYPES =
+      const CompileTimeErrorCode('INCONSISTENT_CASE_EXPRESSION_TYPES',
+          "Case expressions must have the same types, '{0}' is not a '{1}'");
+
+  /**
+   * 7.6.1 Generative Constructors: Let <i>k</i> be a generative constructor. It
+   * is a compile-time error if <i>k</i>'s initializer list contains an
+   * initializer for a variable that is not an instance variable declared in the
+   * immediately surrounding class.
+   *
+   * Parameters:
+   * 0: the name of the initializing formal that is not an instance variable in
+   *    the immediately enclosing class
+   *
+   * See [INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD].
+   */
+  static const CompileTimeErrorCode INITIALIZER_FOR_NON_EXISTENT_FIELD =
+      const CompileTimeErrorCode('INITIALIZER_FOR_NON_EXISTENT_FIELD',
+          "'{0}' is not a variable in the enclosing class");
+
+  /**
+   * 7.6.1 Generative Constructors: Let <i>k</i> be a generative constructor. It
+   * is a compile-time error if <i>k</i>'s initializer list contains an
+   * initializer for a variable that is not an instance variable declared in the
+   * immediately surrounding class.
+   *
+   * Parameters:
+   * 0: the name of the initializing formal that is a static variable in the
+   *    immediately enclosing class
+   *
+   * See [INITIALIZING_FORMAL_FOR_STATIC_FIELD].
+   */
+  static const CompileTimeErrorCode INITIALIZER_FOR_STATIC_FIELD =
+      const CompileTimeErrorCode('INITIALIZER_FOR_STATIC_FIELD',
+          "'{0}' is a static variable in the enclosing class, variables initialized in a constructor cannot be static");
+
+  /**
+   * 7.6.1 Generative Constructors: An initializing formal has the form
+   * <i>this.id</i>. It is a compile-time error if <i>id</i> is not the name of
+   * an instance variable of the immediately enclosing class.
+   *
+   * Parameters:
+   * 0: the name of the initializing formal that is not an instance variable in
+   *    the immediately enclosing class
+   *
+   * See [INITIALIZING_FORMAL_FOR_STATIC_FIELD], and
+   * [INITIALIZER_FOR_NON_EXISTENT_FIELD].
+   */
+  static const CompileTimeErrorCode INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD =
+      const CompileTimeErrorCode('INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD',
+          "'{0}' is not a variable in the enclosing class");
+
+  /**
+   * 7.6.1 Generative Constructors: An initializing formal has the form
+   * <i>this.id</i>. It is a compile-time error if <i>id</i> is not the name of
+   * an instance variable of the immediately enclosing class.
+   *
+   * Parameters:
+   * 0: the name of the initializing formal that is a static variable in the
+   *    immediately enclosing class
+   *
+   * See [INITIALIZER_FOR_STATIC_FIELD].
+   */
+  static const CompileTimeErrorCode INITIALIZING_FORMAL_FOR_STATIC_FIELD =
+      const CompileTimeErrorCode('INITIALIZING_FORMAL_FOR_STATIC_FIELD',
+          "'{0}' is a static field in the enclosing class, fields initialized in a constructor cannot be static");
+
+  /**
+   * 12.30 Identifier Reference: Otherwise, e is equivalent to the property
+   * extraction <b>this</b>.<i>id</i>.
+   */
+  static const CompileTimeErrorCode INSTANCE_MEMBER_ACCESS_FROM_FACTORY =
+      const CompileTimeErrorCode('INSTANCE_MEMBER_ACCESS_FROM_FACTORY',
+          "Instance members cannot be accessed from a factory constructor");
+
+  /**
+   * 12.30 Identifier Reference: Otherwise, e is equivalent to the property
+   * extraction <b>this</b>.<i>id</i>.
+   */
+  static const CompileTimeErrorCode INSTANCE_MEMBER_ACCESS_FROM_STATIC =
+      const CompileTimeErrorCode('INSTANCE_MEMBER_ACCESS_FROM_STATIC',
+          "Instance members cannot be accessed from a static method");
+
+  /**
+   * Enum proposal: It is also a compile-time error to explicitly instantiate an
+   * enum via 'new' or 'const' or to access its private fields.
+   */
+  static const CompileTimeErrorCode INSTANTIATE_ENUM =
+      const CompileTimeErrorCode(
+          'INSTANTIATE_ENUM', "Enums cannot be instantiated");
+
+  /**
+   * 11 Metadata: Metadata consists of a series of annotations, each of which
+   * begin with the character @, followed by a constant expression that must be
+   * either a reference to a compile-time constant variable, or a call to a
+   * constant constructor.
+   */
+  static const CompileTimeErrorCode INVALID_ANNOTATION = const CompileTimeErrorCode(
+      'INVALID_ANNOTATION',
+      "Annotation can be only constant variable or constant constructor invocation");
+
+  /**
+   * 11 Metadata: Metadata consists of a series of annotations, each of which
+   * begin with the character @, followed by a constant expression that must be
+   * either a reference to a compile-time constant variable, or a call to a
+   * constant constructor.
+   *
+   * 12.1 Constants: A qualified reference to a static constant variable that is
+   * not qualified by a deferred prefix.
+   */
+  static const CompileTimeErrorCode INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY =
+      const CompileTimeErrorCode('INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY',
+          "Constant values from a deferred library cannot be used as annotations");
+
+  /**
+   * 15.31 Identifier Reference: It is a compile-time error if any of the
+   * identifiers async, await or yield is used as an identifier in a function
+   * body marked with either async, async* or sync*.
+   */
+  static const CompileTimeErrorCode INVALID_IDENTIFIER_IN_ASYNC =
+      const CompileTimeErrorCode('INVALID_IDENTIFIER_IN_ASYNC',
+          "The identifier '{0}' cannot be used in a function marked with async, async* or sync*");
+
+  /**
+   * 9. Functions: It is a compile-time error if an async, async* or sync*
+   * modifier is attached to the body of a setter or constructor.
+   */
+  static const CompileTimeErrorCode INVALID_MODIFIER_ON_CONSTRUCTOR =
+      const CompileTimeErrorCode('INVALID_MODIFIER_ON_CONSTRUCTOR',
+          "The modifier '{0}' cannot be applied to the body of a constructor");
+
+  /**
+   * 9. Functions: It is a compile-time error if an async, async* or sync*
+   * modifier is attached to the body of a setter or constructor.
+   */
+  static const CompileTimeErrorCode INVALID_MODIFIER_ON_SETTER =
+      const CompileTimeErrorCode('INVALID_MODIFIER_ON_SETTER',
+          "The modifier '{0}' cannot be applied to the body of a setter");
+
+  /**
+   * TODO(brianwilkerson) Remove this when we have decided on how to report
+   * errors in compile-time constants. Until then, this acts as a placeholder
+   * for more informative errors.
+   *
+   * See TODOs in ConstantVisitor
+   */
+  static const CompileTimeErrorCode INVALID_CONSTANT =
+      const CompileTimeErrorCode('INVALID_CONSTANT', "Invalid constant value");
+
+  /**
+   * 7.6 Constructors: It is a compile-time error if the name of a constructor
+   * is not a constructor name.
+   */
+  static const CompileTimeErrorCode INVALID_CONSTRUCTOR_NAME =
+      const CompileTimeErrorCode(
+          'INVALID_CONSTRUCTOR_NAME', "Invalid constructor name");
+
+  /**
+   * 7.6.2 Factories: It is a compile-time error if <i>M</i> is not the name of
+   * the immediately enclosing class.
+   */
+  static const CompileTimeErrorCode INVALID_FACTORY_NAME_NOT_A_CLASS =
+      const CompileTimeErrorCode('INVALID_FACTORY_NAME_NOT_A_CLASS',
+          "The name of the immediately enclosing class expected");
+
+  /**
+   * 12.10 This: It is a compile-time error if this appears in a top-level
+   * function or variable initializer, in a factory constructor, or in a static
+   * method or variable initializer, or in the initializer of an instance
+   * variable.
+   */
+  static const CompileTimeErrorCode INVALID_REFERENCE_TO_THIS =
+      const CompileTimeErrorCode('INVALID_REFERENCE_TO_THIS',
+          "Invalid reference to 'this' expression");
+
+  /**
+   * 12.6 Lists: It is a compile time error if the type argument of a constant
+   * list literal includes a type parameter.
+   *
+   * Parameters:
+   * 0: the name of the type parameter
+   */
+  static const CompileTimeErrorCode INVALID_TYPE_ARGUMENT_IN_CONST_LIST =
+      const CompileTimeErrorCode('INVALID_TYPE_ARGUMENT_IN_CONST_LIST',
+          "Constant list literals cannot include a type parameter as a type argument, such as '{0}'");
+
+  /**
+   * 12.7 Maps: It is a compile time error if the type arguments of a constant
+   * map literal include a type parameter.
+   *
+   * Parameters:
+   * 0: the name of the type parameter
+   */
+  static const CompileTimeErrorCode INVALID_TYPE_ARGUMENT_IN_CONST_MAP =
+      const CompileTimeErrorCode('INVALID_TYPE_ARGUMENT_IN_CONST_MAP',
+          "Constant map literals cannot include a type parameter as a type argument, such as '{0}'");
+
+  /**
+   * 14.2 Exports: It is a compile-time error if the compilation unit found at
+   * the specified URI is not a library declaration.
+   *
+   * 14.1 Imports: It is a compile-time error if the compilation unit found at
+   * the specified URI is not a library declaration.
+   *
+   * 14.3 Parts: It is a compile time error if the contents of the URI are not a
+   * valid part declaration.
+   *
+   * Parameters:
+   * 0: the URI that is invalid
+   *
+   * See [URI_DOES_NOT_EXIST].
+   */
+  static const CompileTimeErrorCode INVALID_URI =
+      const CompileTimeErrorCode('INVALID_URI', "Invalid URI syntax: '{0}'");
+
+  /**
+   * 13.13 Break: It is a compile-time error if no such statement
+   * <i>s<sub>E</sub></i> exists within the innermost function in which
+   * <i>s<sub>b</sub></i> occurs.
+   *
+   * 13.14 Continue: It is a compile-time error if no such statement or case
+   * clause <i>s<sub>E</sub></i> exists within the innermost function in which
+   * <i>s<sub>c</sub></i> occurs.
+   *
+   * Parameters:
+   * 0: the name of the unresolvable label
+   */
+  static const CompileTimeErrorCode LABEL_IN_OUTER_SCOPE =
+      const CompileTimeErrorCode('LABEL_IN_OUTER_SCOPE',
+          "Cannot reference label '{0}' declared in an outer method");
+
+  /**
+   * 13.13 Break: It is a compile-time error if no such statement
+   * <i>s<sub>E</sub></i> exists within the innermost function in which
+   * <i>s<sub>b</sub></i> occurs.
+   *
+   * 13.14 Continue: It is a compile-time error if no such statement or case
+   * clause <i>s<sub>E</sub></i> exists within the innermost function in which
+   * <i>s<sub>c</sub></i> occurs.
+   *
+   * Parameters:
+   * 0: the name of the unresolvable label
+   */
+  static const CompileTimeErrorCode LABEL_UNDEFINED =
+      const CompileTimeErrorCode(
+          'LABEL_UNDEFINED', "Cannot reference undefined label '{0}'");
+
+  /**
+   * 7 Classes: It is a compile time error if a class <i>C</i> declares a member
+   * with the same name as <i>C</i>.
+   */
+  static const CompileTimeErrorCode MEMBER_WITH_CLASS_NAME =
+      const CompileTimeErrorCode('MEMBER_WITH_CLASS_NAME',
+          "Class members cannot have the same name as the enclosing class");
+
+  /**
+   * 7.2 Getters: It is a compile-time error if a class has both a getter and a
+   * method with the same name.
+   *
+   * Parameters:
+   * 0: the conflicting name of the getter and method
+   */
+  static const CompileTimeErrorCode METHOD_AND_GETTER_WITH_SAME_NAME =
+      const CompileTimeErrorCode('METHOD_AND_GETTER_WITH_SAME_NAME',
+          "'{0}' cannot be used to name a method, there is already a getter with the same name");
+
+  /**
+   * 12.1 Constants: A constant expression is ... a constant list literal.
+   */
+  static const CompileTimeErrorCode MISSING_CONST_IN_LIST_LITERAL =
+      const CompileTimeErrorCode('MISSING_CONST_IN_LIST_LITERAL',
+          "List literals must be prefixed with 'const' when used as a constant expression");
+
+  /**
+   * 12.1 Constants: A constant expression is ... a constant map literal.
+   */
+  static const CompileTimeErrorCode MISSING_CONST_IN_MAP_LITERAL =
+      const CompileTimeErrorCode('MISSING_CONST_IN_MAP_LITERAL',
+          "Map literals must be prefixed with 'const' when used as a constant expression");
+
+  /**
+   * Enum proposal: It is a static warning if all of the following conditions
+   * hold:
+   * * The switch statement does not have a 'default' clause.
+   * * The static type of <i>e</i> is an enumerated typed with elements
+   *   <i>id<sub>1</sub></i>, &hellip;, <i>id<sub>n</sub></i>.
+   * * The sets {<i>e<sub>1</sub></i>, &hellip;, <i>e<sub>k</sub></i>} and
+   *   {<i>id<sub>1</sub></i>, &hellip;, <i>id<sub>n</sub></i>} are not the
+   *   same.
+   *
+   * Parameters:
+   * 0: the name of the constant that is missing
+   */
+  static const CompileTimeErrorCode MISSING_ENUM_CONSTANT_IN_SWITCH =
+      const CompileTimeErrorCode('MISSING_ENUM_CONSTANT_IN_SWITCH',
+          "Missing case clause for '{0}'",
+          "Add a case clause for the missing constant or add a default clause.");
+
+  /**
+   * 9 Mixins: It is a compile-time error if a declared or derived mixin
+   * explicitly declares a constructor.
+   *
+   * Parameters:
+   * 0: the name of the mixin that is invalid
+   */
+  static const CompileTimeErrorCode MIXIN_DECLARES_CONSTRUCTOR =
+      const CompileTimeErrorCode('MIXIN_DECLARES_CONSTRUCTOR',
+          "The class '{0}' cannot be used as a mixin because it declares a constructor");
+
+  /**
+   * 9.1 Mixin Application: It is a compile-time error if the with clause of a
+   * mixin application <i>C</i> includes a deferred type expression.
+   *
+   * Parameters:
+   * 0: the name of the type that cannot be extended
+   *
+   * See [EXTENDS_DEFERRED_CLASS], and [IMPLEMENTS_DEFERRED_CLASS].
+   */
+  static const CompileTimeErrorCode MIXIN_DEFERRED_CLASS =
+      const CompileTimeErrorCode('MIXIN_DEFERRED_CLASS',
+          "This class cannot mixin the deferred class '{0}'");
+
+  /**
+   * Not yet in the spec, but consistent with VM behavior.  It is a
+   * compile-time error if all of the constructors of a mixin's base class have
+   * at least one optional parameter (since only constructors that lack
+   * optional parameters can be forwarded to the mixin).  See
+   * https://code.google.com/p/dart/issues/detail?id=15101#c4
+   */
+  static const CompileTimeErrorCode MIXIN_HAS_NO_CONSTRUCTORS =
+      const CompileTimeErrorCode('MIXIN_HAS_NO_CONSTRUCTORS',
+          "This mixin application is invalid because all of the constructors "
+          "in the base class '{0}' have optional parameters.");
+
+  /**
+   * 9 Mixins: It is a compile-time error if a mixin is derived from a class
+   * whose superclass is not Object.
+   *
+   * Parameters:
+   * 0: the name of the mixin that is invalid
+   */
+  static const CompileTimeErrorCode MIXIN_INHERITS_FROM_NOT_OBJECT =
+      const CompileTimeErrorCode('MIXIN_INHERITS_FROM_NOT_OBJECT',
+          "The class '{0}' cannot be used as a mixin because it extends a class other than Object");
+
+  /**
+   * 12.2 Null: It is a compile-time error for a class to attempt to extend or
+   * implement Null.
+   *
+   * 12.3 Numbers: It is a compile-time error for a class to attempt to extend
+   * or implement int.
+   *
+   * 12.3 Numbers: It is a compile-time error for a class to attempt to extend
+   * or implement double.
+   *
+   * 12.3 Numbers: It is a compile-time error for any type other than the types
+   * int and double to attempt to extend or implement num.
+   *
+   * 12.4 Booleans: It is a compile-time error for a class to attempt to extend
+   * or implement bool.
+   *
+   * 12.5 Strings: It is a compile-time error for a class to attempt to extend
+   * or implement String.
+   *
+   * Parameters:
+   * 0: the name of the type that cannot be extended
+   *
+   * See [IMPLEMENTS_DISALLOWED_CLASS].
+   */
+  static const CompileTimeErrorCode MIXIN_OF_DISALLOWED_CLASS =
+      const CompileTimeErrorCode(
+          'MIXIN_OF_DISALLOWED_CLASS', "Classes cannot mixin '{0}'");
+
+  /**
+   * Enum proposal: It is a compile-time error to subclass, mix-in or implement
+   * an enum.
+   */
+  static const CompileTimeErrorCode MIXIN_OF_ENUM = const CompileTimeErrorCode(
+      'MIXIN_OF_ENUM', "Classes cannot mixin an enum");
+
+  /**
+   * 9.1 Mixin Application: It is a compile-time error if <i>M</i> does not
+   * denote a class or mixin available in the immediately enclosing scope.
+   */
+  static const CompileTimeErrorCode MIXIN_OF_NON_CLASS =
+      const CompileTimeErrorCode(
+          'MIXIN_OF_NON_CLASS', "Classes can only mixin other classes");
+
+  /**
+   * 9 Mixins: It is a compile-time error if a declared or derived mixin refers
+   * to super.
+   */
+  static const CompileTimeErrorCode MIXIN_REFERENCES_SUPER =
+      const CompileTimeErrorCode('MIXIN_REFERENCES_SUPER',
+          "The class '{0}' cannot be used as a mixin because it references 'super'");
+
+  /**
+   * 9.1 Mixin Application: It is a compile-time error if <i>S</i> does not
+   * denote a class available in the immediately enclosing scope.
+   */
+  static const CompileTimeErrorCode MIXIN_WITH_NON_CLASS_SUPERCLASS =
+      const CompileTimeErrorCode('MIXIN_WITH_NON_CLASS_SUPERCLASS',
+          "Mixin can only be applied to class");
+
+  /**
+   * 7.6.1 Generative Constructors: A generative constructor may be redirecting,
+   * in which case its only action is to invoke another generative constructor.
+   */
+  static const CompileTimeErrorCode MULTIPLE_REDIRECTING_CONSTRUCTOR_INVOCATIONS =
+      const CompileTimeErrorCode('MULTIPLE_REDIRECTING_CONSTRUCTOR_INVOCATIONS',
+          "Constructor may have at most one 'this' redirection");
+
+  /**
+   * 7.6.1 Generative Constructors: Let <i>k</i> be a generative constructor.
+   * Then <i>k</i> may include at most one superinitializer in its initializer
+   * list or a compile time error occurs.
+   */
+  static const CompileTimeErrorCode MULTIPLE_SUPER_INITIALIZERS =
+      const CompileTimeErrorCode('MULTIPLE_SUPER_INITIALIZERS',
+          "Constructor may have at most one 'super' initializer");
+
+  /**
+   * 11 Metadata: Metadata consists of a series of annotations, each of which
+   * begin with the character @, followed by a constant expression that must be
+   * either a reference to a compile-time constant variable, or a call to a
+   * constant constructor.
+   */
+  static const CompileTimeErrorCode NO_ANNOTATION_CONSTRUCTOR_ARGUMENTS =
+      const CompileTimeErrorCode('NO_ANNOTATION_CONSTRUCTOR_ARGUMENTS',
+          "Annotation creation must have arguments");
+
+  /**
+   * 7.6.1 Generative Constructors: If no superinitializer is provided, an
+   * implicit superinitializer of the form <b>super</b>() is added at the end of
+   * <i>k</i>'s initializer list, unless the enclosing class is class
+   * <i>Object</i>.
+   *
+   * 7.6.1 Generative constructors. It is a compile-time error if class <i>S</i>
+   * does not declare a generative constructor named <i>S</i> (respectively
+   * <i>S.id</i>)
+   */
+  static const CompileTimeErrorCode NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT =
+      const CompileTimeErrorCode('NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT',
+          "The class '{0}' does not have a default constructor");
+
+  /**
+   * 7.6 Constructors: Iff no constructor is specified for a class <i>C</i>, it
+   * implicitly has a default constructor C() : <b>super<b>() {}, unless
+   * <i>C</i> is class <i>Object</i>.
+   *
+   * 7.6.1 Generative constructors. It is a compile-time error if class <i>S</i>
+   * does not declare a generative constructor named <i>S</i> (respectively
+   * <i>S.id</i>)
+   */
+  static const CompileTimeErrorCode NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT =
+      const CompileTimeErrorCode('NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT',
+          "The class '{0}' does not have a default constructor");
+
+  /**
+   * 13.2 Expression Statements: It is a compile-time error if a non-constant
+   * map literal that has no explicit type arguments appears in a place where a
+   * statement is expected.
+   */
+  static const CompileTimeErrorCode NON_CONST_MAP_AS_EXPRESSION_STATEMENT =
+      const CompileTimeErrorCode('NON_CONST_MAP_AS_EXPRESSION_STATEMENT',
+          "A non-constant map literal without type arguments cannot be used as an expression statement");
+
+  /**
+   * 13.9 Switch: Given a switch statement of the form <i>switch (e) {
+   * label<sub>11</sub> &hellip; label<sub>1j1</sub> case e<sub>1</sub>:
+   * s<sub>1</sub> &hellip; label<sub>n1</sub> &hellip; label<sub>njn</sub> case
+   * e<sub>n</sub>: s<sub>n</sub> default: s<sub>n+1</sub>}</i> or the form
+   * <i>switch (e) { label<sub>11</sub> &hellip; label<sub>1j1</sub> case
+   * e<sub>1</sub>: s<sub>1</sub> &hellip; label<sub>n1</sub> &hellip;
+   * label<sub>njn</sub> case e<sub>n</sub>: s<sub>n</sub>}</i>, it is a
+   * compile-time error if the expressions <i>e<sub>k</sub></i> are not
+   * compile-time constants, for all <i>1 &lt;= k &lt;= n</i>.
+   */
+  static const CompileTimeErrorCode NON_CONSTANT_CASE_EXPRESSION =
+      const CompileTimeErrorCode(
+          'NON_CONSTANT_CASE_EXPRESSION', "Case expressions must be constant");
+
+  /**
+   * 13.9 Switch: Given a switch statement of the form <i>switch (e) {
+   * label<sub>11</sub> &hellip; label<sub>1j1</sub> case e<sub>1</sub>:
+   * s<sub>1</sub> &hellip; label<sub>n1</sub> &hellip; label<sub>njn</sub> case
+   * e<sub>n</sub>: s<sub>n</sub> default: s<sub>n+1</sub>}</i> or the form
+   * <i>switch (e) { label<sub>11</sub> &hellip; label<sub>1j1</sub> case
+   * e<sub>1</sub>: s<sub>1</sub> &hellip; label<sub>n1</sub> &hellip;
+   * label<sub>njn</sub> case e<sub>n</sub>: s<sub>n</sub>}</i>, it is a
+   * compile-time error if the expressions <i>e<sub>k</sub></i> are not
+   * compile-time constants, for all <i>1 &lt;= k &lt;= n</i>.
+   *
+   * 12.1 Constants: A qualified reference to a static constant variable that is
+   * not qualified by a deferred prefix.
+   */
+  static const CompileTimeErrorCode NON_CONSTANT_CASE_EXPRESSION_FROM_DEFERRED_LIBRARY =
+      const CompileTimeErrorCode(
+          'NON_CONSTANT_CASE_EXPRESSION_FROM_DEFERRED_LIBRARY',
+          "Constant values from a deferred library cannot be used as a case expression");
+
+  /**
+   * 6.2.2 Optional Formals: It is a compile-time error if the default value of
+   * an optional parameter is not a compile-time constant.
+   */
+  static const CompileTimeErrorCode NON_CONSTANT_DEFAULT_VALUE =
+      const CompileTimeErrorCode('NON_CONSTANT_DEFAULT_VALUE',
+          "Default values of an optional parameter must be constant");
+
+  /**
+   * 6.2.2 Optional Formals: It is a compile-time error if the default value of
+   * an optional parameter is not a compile-time constant.
+   *
+   * 12.1 Constants: A qualified reference to a static constant variable that is
+   * not qualified by a deferred prefix.
+   */
+  static const CompileTimeErrorCode NON_CONSTANT_DEFAULT_VALUE_FROM_DEFERRED_LIBRARY =
+      const CompileTimeErrorCode(
+          'NON_CONSTANT_DEFAULT_VALUE_FROM_DEFERRED_LIBRARY',
+          "Constant values from a deferred library cannot be used as a default parameter value");
+
+  /**
+   * 12.6 Lists: It is a compile time error if an element of a constant list
+   * literal is not a compile-time constant.
+   */
+  static const CompileTimeErrorCode NON_CONSTANT_LIST_ELEMENT =
+      const CompileTimeErrorCode('NON_CONSTANT_LIST_ELEMENT',
+          "'const' lists must have all constant values");
+
+  /**
+   * 12.6 Lists: It is a compile time error if an element of a constant list
+   * literal is not a compile-time constant.
+   *
+   * 12.1 Constants: A qualified reference to a static constant variable that is
+   * not qualified by a deferred prefix.
+   */
+  static const CompileTimeErrorCode NON_CONSTANT_LIST_ELEMENT_FROM_DEFERRED_LIBRARY =
+      const CompileTimeErrorCode(
+          'NON_CONSTANT_LIST_ELEMENT_FROM_DEFERRED_LIBRARY',
+          "Constant values from a deferred library cannot be used as values in a 'const' list");
+
+  /**
+   * 12.7 Maps: It is a compile time error if either a key or a value of an
+   * entry in a constant map literal is not a compile-time constant.
+   */
+  static const CompileTimeErrorCode NON_CONSTANT_MAP_KEY =
+      const CompileTimeErrorCode(
+          'NON_CONSTANT_MAP_KEY', "The keys in a map must be constant");
+
+  /**
+   * 12.7 Maps: It is a compile time error if either a key or a value of an
+   * entry in a constant map literal is not a compile-time constant.
+   *
+   * 12.1 Constants: A qualified reference to a static constant variable that is
+   * not qualified by a deferred prefix.
+   */
+  static const CompileTimeErrorCode NON_CONSTANT_MAP_KEY_FROM_DEFERRED_LIBRARY =
+      const CompileTimeErrorCode('NON_CONSTANT_MAP_KEY_FROM_DEFERRED_LIBRARY',
+          "Constant values from a deferred library cannot be used as keys in a map");
+
+  /**
+   * 12.7 Maps: It is a compile time error if either a key or a value of an
+   * entry in a constant map literal is not a compile-time constant.
+   */
+  static const CompileTimeErrorCode NON_CONSTANT_MAP_VALUE =
+      const CompileTimeErrorCode('NON_CONSTANT_MAP_VALUE',
+          "The values in a 'const' map must be constant");
+
+  /**
+   * 12.7 Maps: It is a compile time error if either a key or a value of an
+   * entry in a constant map literal is not a compile-time constant.
+   *
+   * 12.1 Constants: A qualified reference to a static constant variable that is
+   * not qualified by a deferred prefix.
+   */
+  static const CompileTimeErrorCode NON_CONSTANT_MAP_VALUE_FROM_DEFERRED_LIBRARY =
+      const CompileTimeErrorCode('NON_CONSTANT_MAP_VALUE_FROM_DEFERRED_LIBRARY',
+          "Constant values from a deferred library cannot be used as values in a 'const' map");
+
+  /**
+   * 11 Metadata: Metadata consists of a series of annotations, each of which
+   * begin with the character @, followed by a constant expression that must be
+   * either a reference to a compile-time constant variable, or a call to a
+   * constant constructor.
+   *
+   * "From deferred library" case is covered by
+   * [CompileTimeErrorCode.INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY].
+   */
+  static const CompileTimeErrorCode NON_CONSTANT_ANNOTATION_CONSTRUCTOR =
+      const CompileTimeErrorCode('NON_CONSTANT_ANNOTATION_CONSTRUCTOR',
+          "Annotation creation can use only 'const' constructor");
+
+  /**
+   * 7.6.3 Constant Constructors: Any expression that appears within the
+   * initializer list of a constant constructor must be a potentially constant
+   * expression, or a compile-time error occurs.
+   */
+  static const CompileTimeErrorCode NON_CONSTANT_VALUE_IN_INITIALIZER =
+      const CompileTimeErrorCode('NON_CONSTANT_VALUE_IN_INITIALIZER',
+          "Initializer expressions in constant constructors must be constants");
+
+  /**
+   * 7.6.3 Constant Constructors: Any expression that appears within the
+   * initializer list of a constant constructor must be a potentially constant
+   * expression, or a compile-time error occurs.
+   *
+   * 12.1 Constants: A qualified reference to a static constant variable that is
+   * not qualified by a deferred prefix.
+   */
+  static const CompileTimeErrorCode NON_CONSTANT_VALUE_IN_INITIALIZER_FROM_DEFERRED_LIBRARY =
+      const CompileTimeErrorCode(
+          'NON_CONSTANT_VALUE_IN_INITIALIZER_FROM_DEFERRED_LIBRARY',
+          "Constant values from a deferred library cannot be used as constant initializers");
+
+  /**
+   * 12.14.2 Binding Actuals to Formals: It is a static warning if <i>m < h</i>
+   * or if <i>m > n</i>.
+   *
+   * 12.11.2 Const: It is a compile-time error if evaluation of a constant
+   * object results in an uncaught exception being thrown.
+   *
+   * Parameters:
+   * 0: the expected number of required arguments
+   * 1: the actual number of positional arguments given
+   */
+  static const CompileTimeErrorCode NOT_ENOUGH_REQUIRED_ARGUMENTS =
+      const CompileTimeErrorCode('NOT_ENOUGH_REQUIRED_ARGUMENTS',
+          "{0} required argument(s) expected, but {1} found");
+
+  /**
+   * 7.6.1 Generative Constructors: Let <i>C</i> be the class in which the
+   * superinitializer appears and let <i>S</i> be the superclass of <i>C</i>.
+   * Let <i>k</i> be a generative constructor. It is a compile-time error if
+   * class <i>S</i> does not declare a generative constructor named <i>S</i>
+   * (respectively <i>S.id</i>)
+   */
+  static const CompileTimeErrorCode NON_GENERATIVE_CONSTRUCTOR =
+      const CompileTimeErrorCode('NON_GENERATIVE_CONSTRUCTOR',
+          "The generative constructor '{0}' expected, but factory found");
+
+  /**
+   * 7.9 Superclasses: It is a compile-time error to specify an extends clause
+   * for class Object.
+   */
+  static const CompileTimeErrorCode OBJECT_CANNOT_EXTEND_ANOTHER_CLASS =
+      const CompileTimeErrorCode('OBJECT_CANNOT_EXTEND_ANOTHER_CLASS', "");
+
+  /**
+   * 7.1.1 Operators: It is a compile-time error to declare an optional
+   * parameter in an operator.
+   */
+  static const CompileTimeErrorCode OPTIONAL_PARAMETER_IN_OPERATOR =
+      const CompileTimeErrorCode('OPTIONAL_PARAMETER_IN_OPERATOR',
+          "Optional parameters are not allowed when defining an operator");
+
+  /**
+   * 14.3 Parts: It is a compile time error if the contents of the URI are not a
+   * valid part declaration.
+   *
+   * Parameters:
+   * 0: the uri pointing to a non-library declaration
+   */
+  static const CompileTimeErrorCode PART_OF_NON_PART =
+      const CompileTimeErrorCode('PART_OF_NON_PART',
+          "The included part '{0}' must have a part-of directive");
+
+  /**
+   * 14.1 Imports: It is a compile-time error if the current library declares a
+   * top-level member named <i>p</i>.
+   */
+  static const CompileTimeErrorCode PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER =
+      const CompileTimeErrorCode('PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER',
+          "The name '{0}' is already used as an import prefix and cannot be used to name a top-level element");
+
+  /**
+   * 6.2.2 Optional Formals: It is a compile-time error if the name of a named
+   * optional parameter begins with an '_' character.
+   */
+  static const CompileTimeErrorCode PRIVATE_OPTIONAL_PARAMETER =
+      const CompileTimeErrorCode('PRIVATE_OPTIONAL_PARAMETER',
+          "Named optional parameters cannot start with an underscore");
+
+  /**
+   * 12.1 Constants: It is a compile-time error if the value of a compile-time
+   * constant expression depends on itself.
+   */
+  static const CompileTimeErrorCode RECURSIVE_COMPILE_TIME_CONSTANT =
+      const CompileTimeErrorCode('RECURSIVE_COMPILE_TIME_CONSTANT', "");
+
+  /**
+   * 7.6.1 Generative Constructors: A generative constructor may be redirecting,
+   * in which case its only action is to invoke another generative constructor.
+   *
+   * TODO(scheglov) review this later, there are no explicit "it is a
+   * compile-time error" in specification. But it was added to the co19 and
+   * there is same error for factories.
+   *
+   * https://code.google.com/p/dart/issues/detail?id=954
+   */
+  static const CompileTimeErrorCode RECURSIVE_CONSTRUCTOR_REDIRECT =
+      const CompileTimeErrorCode('RECURSIVE_CONSTRUCTOR_REDIRECT',
+          "Cycle in redirecting generative constructors");
+
+  /**
+   * 7.6.2 Factories: It is a compile-time error if a redirecting factory
+   * constructor redirects to itself, either directly or indirectly via a
+   * sequence of redirections.
+   */
+  static const CompileTimeErrorCode RECURSIVE_FACTORY_REDIRECT =
+      const CompileTimeErrorCode('RECURSIVE_FACTORY_REDIRECT',
+          "Cycle in redirecting factory constructors");
+
+  /**
+   * 7.10 Superinterfaces: It is a compile-time error if the interface of a
+   * class <i>C</i> is a superinterface of itself.
+   *
+   * 8.1 Superinterfaces: It is a compile-time error if an interface is a
+   * superinterface of itself.
+   *
+   * 7.9 Superclasses: It is a compile-time error if a class <i>C</i> is a
+   * superclass of itself.
+   *
+   * Parameters:
+   * 0: the name of the class that implements itself recursively
+   * 1: a string representation of the implements loop
+   */
+  static const CompileTimeErrorCode RECURSIVE_INTERFACE_INHERITANCE =
+      const CompileTimeErrorCode('RECURSIVE_INTERFACE_INHERITANCE',
+          "'{0}' cannot be a superinterface of itself: {1}");
+
+  /**
+   * 7.10 Superinterfaces: It is a compile-time error if the interface of a
+   * class <i>C</i> is a superinterface of itself.
+   *
+   * 8.1 Superinterfaces: It is a compile-time error if an interface is a
+   * superinterface of itself.
+   *
+   * 7.9 Superclasses: It is a compile-time error if a class <i>C</i> is a
+   * superclass of itself.
+   *
+   * Parameters:
+   * 0: the name of the class that implements itself recursively
+   */
+  static const CompileTimeErrorCode RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS =
+      const CompileTimeErrorCode(
+          'RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS',
+          "'{0}' cannot extend itself");
+
+  /**
+   * 7.10 Superinterfaces: It is a compile-time error if the interface of a
+   * class <i>C</i> is a superinterface of itself.
+   *
+   * 8.1 Superinterfaces: It is a compile-time error if an interface is a
+   * superinterface of itself.
+   *
+   * 7.9 Superclasses: It is a compile-time error if a class <i>C</i> is a
+   * superclass of itself.
+   *
+   * Parameters:
+   * 0: the name of the class that implements itself recursively
+   */
+  static const CompileTimeErrorCode RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS =
+      const CompileTimeErrorCode(
+          'RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS',
+          "'{0}' cannot implement itself");
+
+  /**
+   * 7.10 Superinterfaces: It is a compile-time error if the interface of a
+   * class <i>C</i> is a superinterface of itself.
+   *
+   * 8.1 Superinterfaces: It is a compile-time error if an interface is a
+   * superinterface of itself.
+   *
+   * 7.9 Superclasses: It is a compile-time error if a class <i>C</i> is a
+   * superclass of itself.
+   *
+   * Parameters:
+   * 0: the name of the class that implements itself recursively
+   */
+  static const CompileTimeErrorCode RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_WITH =
+      const CompileTimeErrorCode(
+          'RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_WITH',
+          "'{0}' cannot use itself as a mixin");
+
+  /**
+   * 7.6.2 Factories: It is a compile-time error if <i>k</i> is prefixed with
+   * the const modifier but <i>k'</i> is not a constant constructor.
+   */
+  static const CompileTimeErrorCode REDIRECT_TO_MISSING_CONSTRUCTOR =
+      const CompileTimeErrorCode('REDIRECT_TO_MISSING_CONSTRUCTOR',
+          "The constructor '{0}' could not be found in '{1}'");
+
+  /**
+   * 7.6.2 Factories: It is a compile-time error if <i>k</i> is prefixed with
+   * the const modifier but <i>k'</i> is not a constant constructor.
+   */
+  static const CompileTimeErrorCode REDIRECT_TO_NON_CLASS =
+      const CompileTimeErrorCode('REDIRECT_TO_NON_CLASS',
+          "The name '{0}' is not a type and cannot be used in a redirected constructor");
+
+  /**
+   * 7.6.2 Factories: It is a compile-time error if <i>k</i> is prefixed with
+   * the const modifier but <i>k'</i> is not a constant constructor.
+   */
+  static const CompileTimeErrorCode REDIRECT_TO_NON_CONST_CONSTRUCTOR =
+      const CompileTimeErrorCode('REDIRECT_TO_NON_CONST_CONSTRUCTOR',
+          "Constant factory constructor cannot delegate to a non-constant constructor");
+
+  /**
+   * 7.6.1 Generative constructors: A generative constructor may be
+   * <i>redirecting</i>, in which case its only action is to invoke another
+   * generative constructor.
+   */
+  static const CompileTimeErrorCode REDIRECT_GENERATIVE_TO_MISSING_CONSTRUCTOR =
+      const CompileTimeErrorCode('REDIRECT_GENERATIVE_TO_MISSING_CONSTRUCTOR',
+          "The constructor '{0}' could not be found in '{1}'");
+
+  /**
+   * 7.6.1 Generative constructors: A generative constructor may be
+   * <i>redirecting</i>, in which case its only action is to invoke another
+   * generative constructor.
+   */
+  static const CompileTimeErrorCode REDIRECT_GENERATIVE_TO_NON_GENERATIVE_CONSTRUCTOR =
+      const CompileTimeErrorCode(
+          'REDIRECT_GENERATIVE_TO_NON_GENERATIVE_CONSTRUCTOR',
+          "Generative constructor cannot redirect to a factory constructor");
+
+  /**
+   * 5 Variables: A local variable may only be referenced at a source code
+   * location that is after its initializer, if any, is complete, or a
+   * compile-time error occurs.
+   */
+  static const CompileTimeErrorCode REFERENCED_BEFORE_DECLARATION =
+      const CompileTimeErrorCode('REFERENCED_BEFORE_DECLARATION',
+          "Local variables cannot be referenced before they are declared");
+
+  /**
+   * 12.8.1 Rethrow: It is a compile-time error if an expression of the form
+   * <i>rethrow;</i> is not enclosed within a on-catch clause.
+   */
+  static const CompileTimeErrorCode RETHROW_OUTSIDE_CATCH =
+      const CompileTimeErrorCode(
+          'RETHROW_OUTSIDE_CATCH', "rethrow must be inside of a catch clause");
+
+  /**
+   * 13.12 Return: It is a compile-time error if a return statement of the form
+   * <i>return e;</i> appears in a generative constructor.
+   */
+  static const CompileTimeErrorCode RETURN_IN_GENERATIVE_CONSTRUCTOR =
+      const CompileTimeErrorCode('RETURN_IN_GENERATIVE_CONSTRUCTOR',
+          "Constructors cannot return a value");
+
+  /**
+   * 13.12 Return: It is a compile-time error if a return statement of the form
+   * <i>return e;</i> appears in a generator function.
+   */
+  static const CompileTimeErrorCode RETURN_IN_GENERATOR =
+      const CompileTimeErrorCode('RETURN_IN_GENERATOR',
+          "Cannot return a value from a generator function (one marked with either 'async*' or 'sync*')");
+
+  /**
+   * 14.1 Imports: It is a compile-time error if a prefix used in a deferred
+   * import is used in another import clause.
+   */
+  static const CompileTimeErrorCode SHARED_DEFERRED_PREFIX =
+      const CompileTimeErrorCode('SHARED_DEFERRED_PREFIX',
+          "The prefix of a deferred import cannot be used in other import directives");
+
+  /**
+   * 12.15.4 Super Invocation: A super method invocation <i>i</i> has the form
+   * <i>super.m(a<sub>1</sub>, &hellip;, a<sub>n</sub>, x<sub>n+1</sub>:
+   * a<sub>n+1</sub>, &hellip; x<sub>n+k</sub>: a<sub>n+k</sub>)</i>. It is a
+   * compile-time error if a super method invocation occurs in a top-level
+   * function or variable initializer, in an instance variable initializer or
+   * initializer list, in class Object, in a factory constructor, or in a static
+   * method or variable initializer.
+   */
+  static const CompileTimeErrorCode SUPER_IN_INVALID_CONTEXT =
+      const CompileTimeErrorCode(
+          'SUPER_IN_INVALID_CONTEXT', "Invalid context for 'super' invocation");
+
+  /**
+   * 7.6.1 Generative Constructors: A generative constructor may be redirecting,
+   * in which case its only action is to invoke another generative constructor.
+   */
+  static const CompileTimeErrorCode SUPER_IN_REDIRECTING_CONSTRUCTOR =
+      const CompileTimeErrorCode('SUPER_IN_REDIRECTING_CONSTRUCTOR',
+          "The redirecting constructor cannot have a 'super' initializer");
+
+  /**
+   * 7.6.1 Generative Constructors: Let <i>k</i> be a generative constructor. It
+   * is a compile-time error if a generative constructor of class Object
+   * includes a superinitializer.
+   */
+  static const CompileTimeErrorCode SUPER_INITIALIZER_IN_OBJECT =
+      const CompileTimeErrorCode('SUPER_INITIALIZER_IN_OBJECT', "");
+
+  /**
+   * 12.11 Instance Creation: It is a static type warning if any of the type
+   * arguments to a constructor of a generic type <i>G</i> invoked by a new
+   * expression or a constant object expression are not subtypes of the bounds
+   * of the corresponding formal type parameters of <i>G</i>.
+   *
+   * 12.11.1 New: If T is malformed a dynamic error occurs. In checked mode, if
+   * T is mal-bounded a dynamic error occurs.
+   *
+   * 12.1 Constants: It is a compile-time error if evaluation of a compile-time
+   * constant would raise an exception.
+   *
+   * Parameters:
+   * 0: the name of the type used in the instance creation that should be
+   *    limited by the bound as specified in the class declaration
+   * 1: the name of the bounding type
+   *
+   * See [StaticTypeWarningCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS].
+   */
+  static const CompileTimeErrorCode TYPE_ARGUMENT_NOT_MATCHING_BOUNDS =
+      const CompileTimeErrorCode(
+          'TYPE_ARGUMENT_NOT_MATCHING_BOUNDS', "'{0}' does not extend '{1}'");
+
+  /**
+   * 15.3.1 Typedef: Any self reference, either directly, or recursively via
+   * another typedef, is a compile time error.
+   */
+  static const CompileTimeErrorCode TYPE_ALIAS_CANNOT_REFERENCE_ITSELF =
+      const CompileTimeErrorCode('TYPE_ALIAS_CANNOT_REFERENCE_ITSELF',
+          "Type alias cannot reference itself directly or recursively via another typedef");
+
+  /**
+   * 12.11.2 Const: It is a compile-time error if <i>T</i> is not a class
+   * accessible in the current scope, optionally followed by type arguments.
+   */
+  static const CompileTimeErrorCode UNDEFINED_CLASS =
+      const CompileTimeErrorCode('UNDEFINED_CLASS', "Undefined class '{0}'");
+
+  /**
+   * 7.6.1 Generative Constructors: Let <i>C</i> be the class in which the
+   * superinitializer appears and let <i>S</i> be the superclass of <i>C</i>.
+   * Let <i>k</i> be a generative constructor. It is a compile-time error if
+   * class <i>S</i> does not declare a generative constructor named <i>S</i>
+   * (respectively <i>S.id</i>)
+   */
+  static const CompileTimeErrorCode UNDEFINED_CONSTRUCTOR_IN_INITIALIZER =
+      const CompileTimeErrorCode('UNDEFINED_CONSTRUCTOR_IN_INITIALIZER',
+          "The class '{0}' does not have a generative constructor '{1}'");
+
+  /**
+   * 7.6.1 Generative Constructors: Let <i>C</i> be the class in which the
+   * superinitializer appears and let <i>S</i> be the superclass of <i>C</i>.
+   * Let <i>k</i> be a generative constructor. It is a compile-time error if
+   * class <i>S</i> does not declare a generative constructor named <i>S</i>
+   * (respectively <i>S.id</i>)
+   */
+  static const CompileTimeErrorCode UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT =
+      const CompileTimeErrorCode('UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT',
+          "The class '{0}' does not have a default generative constructor");
+
+  /**
+   * 12.14.2 Binding Actuals to Formals: Furthermore, each <i>q<sub>i</sub></i>,
+   * <i>1<=i<=l</i>, must have a corresponding named parameter in the set
+   * {<i>p<sub>n+1</sub></i> ... <i>p<sub>n+k</sub></i>} or a static warning
+   * occurs.
+   *
+   * 12.11.2 Const: It is a compile-time error if evaluation of a constant
+   * object results in an uncaught exception being thrown.
+   *
+   * Parameters:
+   * 0: the name of the requested named parameter
+   */
+  static const CompileTimeErrorCode UNDEFINED_NAMED_PARAMETER =
+      const CompileTimeErrorCode('UNDEFINED_NAMED_PARAMETER',
+          "The named parameter '{0}' is not defined");
+
+  /**
+   * 14.2 Exports: It is a compile-time error if the compilation unit found at
+   * the specified URI is not a library declaration.
+   *
+   * 14.1 Imports: It is a compile-time error if the compilation unit found at
+   * the specified URI is not a library declaration.
+   *
+   * 14.3 Parts: It is a compile time error if the contents of the URI are not a
+   * valid part declaration.
+   *
+   * Parameters:
+   * 0: the URI pointing to a non-existent file
+   *
+   * See [INVALID_URI].
+   */
+  static const CompileTimeErrorCode URI_DOES_NOT_EXIST =
+      const CompileTimeErrorCode(
+          'URI_DOES_NOT_EXIST', "Target of URI does not exist: '{0}'");
+
+  /**
+   * 14.1 Imports: It is a compile-time error if <i>x</i> is not a compile-time
+   * constant, or if <i>x</i> involves string interpolation.
+   *
+   * 14.3 Parts: It is a compile-time error if <i>s</i> is not a compile-time
+   * constant, or if <i>s</i> involves string interpolation.
+   *
+   * 14.5 URIs: It is a compile-time error if the string literal <i>x</i> that
+   * describes a URI is not a compile-time constant, or if <i>x</i> involves
+   * string interpolation.
+   */
+  static const CompileTimeErrorCode URI_WITH_INTERPOLATION =
+      const CompileTimeErrorCode(
+          'URI_WITH_INTERPOLATION', "URIs cannot use string interpolation");
+
+  /**
+   * 7.1.1 Operators: It is a compile-time error if the arity of the
+   * user-declared operator []= is not 2. It is a compile time error if the
+   * arity of a user-declared operator with one of the names: &lt;, &gt;, &lt;=,
+   * &gt;=, ==, +, /, ~/, *, %, |, ^, &, &lt;&lt;, &gt;&gt;, [] is not 1. It is
+   * a compile time error if the arity of the user-declared operator - is not 0
+   * or 1. It is a compile time error if the arity of the user-declared operator
+   * ~ is not 0.
+   *
+   * Parameters:
+   * 0: the name of the declared operator
+   * 1: the number of parameters expected
+   * 2: the number of parameters found in the operator declaration
+   */
+  static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR =
+      const CompileTimeErrorCode('WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR',
+          "Operator '{0}' should declare exactly {1} parameter(s), but {2} found");
+
+  /**
+   * 7.1.1 Operators: It is a compile time error if the arity of the
+   * user-declared operator - is not 0 or 1.
+   *
+   * Parameters:
+   * 0: the number of parameters found in the operator declaration
+   */
+  static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS =
+      const CompileTimeErrorCode(
+          'WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS',
+          "Operator '-' should declare 0 or 1 parameter, but {0} found");
+
+  /**
+   * 7.3 Setters: It is a compile-time error if a setter's formal parameter list
+   * does not include exactly one required formal parameter <i>p</i>.
+   */
+  static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER =
+      const CompileTimeErrorCode('WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER',
+          "Setters should declare exactly one required parameter");
+
+  /**
+   * ?? Yield: It is a compile-time error if a yield statement appears in a
+   * function that is not a generator function.
+   */
+  static const CompileTimeErrorCode YIELD_EACH_IN_NON_GENERATOR =
+      const CompileTimeErrorCode('YIELD_EACH_IN_NON_GENERATOR',
+          "Yield-each statements must be in a generator function (one marked with either 'async*' or 'sync*')");
+
+  /**
+   * ?? Yield: It is a compile-time error if a yield statement appears in a
+   * function that is not a generator function.
+   */
+  static const CompileTimeErrorCode YIELD_IN_NON_GENERATOR =
+      const CompileTimeErrorCode('YIELD_IN_NON_GENERATOR',
+          "Yield statements must be in a generator function (one marked with either 'async*' or 'sync*')");
+
+  /**
+   * Initialize a newly created error code to have the given [name]. The message
+   * associated with the error will be created from the given [message]
+   * template. The correction associated with the error will be created from the
+   * given [correction] template.
+   */
+  const CompileTimeErrorCode(String name, String message, [String correction])
+      : super(name, message, correction);
+
+  @override
+  ErrorSeverity get errorSeverity => ErrorType.COMPILE_TIME_ERROR.severity;
+
+  @override
+  ErrorType get type => ErrorType.COMPILE_TIME_ERROR;
+}
+
+/**
+ * An error code associated with an [AnalysisError].
+ *
+ * Generally, we want to provide messages that consist of three sentences. From
+ * the user's perspective these sentences should explain:
+ * 1. what is wrong,
+ * 2. why is it wrong, and
+ * 3. how do I fix it.
+ * However, we combine the first two in the [message] and the last in the
+ * [correction].
+ */
+abstract class ErrorCode {
+  /**
+   * An empty list of error codes.
+   */
+  static const List<ErrorCode> EMPTY_LIST = const <ErrorCode>[];
+
+  /**
+   * The name of the error code.
+   */
+  final String name;
+
+  /**
+   * The template used to create the message to be displayed for this error. The
+   * message should indicate what is wrong and why it is wrong.
+   */
+  final String message;
+
+  /**
+   * The template used to create the correction to be displayed for this error,
+   * or `null` if there is no correction information for this error. The
+   * correction should indicate how the user can fix the error.
+   */
+  final String correction;
+
+  /**
+   * Initialize a newly created error code to have the given [name]. The message
+   * associated with the error will be created from the given [message]
+   * template. The correction associated with the error will be created from the
+   * given [correction] template.
+   */
+  const ErrorCode(this.name, this.message, [this.correction]);
+
+  /**
+   * The severity of the error.
+   */
+  ErrorSeverity get errorSeverity;
+
+  /**
+   * The type of the error.
+   */
+  ErrorType get type;
+
+  /**
+   * The unique name of this error code.
+   */
+  String get uniqueName => "$runtimeType.$name";
+}
+
+/**
+ * The properties that can be associated with an [AnalysisError].
+ */
+class ErrorProperty extends Enum<ErrorProperty> {
+  /**
+   * A property whose value is a list of [FieldElement]s that are final, but
+   * not initialized by a constructor.
+   */
+  static const ErrorProperty NOT_INITIALIZED_FIELDS =
+      const ErrorProperty('NOT_INITIALIZED_FIELDS', 0);
+
+  /**
+   * A property whose value is the name of the library that is used by all
+   * of the "part of" directives, so should be used in the "library" directive.
+   * Is `null` if there is no a single name used by all of the parts.
+   */
+  static const ErrorProperty PARTS_LIBRARY_NAME =
+      const ErrorProperty('PARTS_LIBRARY_NAME', 1);
+
+  /**
+   * A property whose value is a list of [ExecutableElement] that should
+   * be but are not implemented by a concrete class.
+   */
+  static const ErrorProperty UNIMPLEMENTED_METHODS =
+      const ErrorProperty('UNIMPLEMENTED_METHODS', 2);
+
+  static const List<ErrorProperty> values = const [
+    NOT_INITIALIZED_FIELDS,
+    PARTS_LIBRARY_NAME,
+    UNIMPLEMENTED_METHODS
+  ];
+
+  const ErrorProperty(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * An object used to create analysis errors and report then to an error
+ * listener.
+ */
+class ErrorReporter {
+  /**
+   * The error listener to which errors will be reported.
+   */
+  final AnalysisErrorListener _errorListener;
+
+  /**
+   * The default source to be used when reporting errors.
+   */
+  final Source _defaultSource;
+
+  /**
+   * The source to be used when reporting errors.
+   */
+  Source _source;
+
+  /**
+   * Initialize a newly created error reporter that will report errors to the
+   * given [_errorListener]. Errors will be reported against the
+   * [_defaultSource] unless another source is provided later.
+   */
+  ErrorReporter(this._errorListener, this._defaultSource) {
+    if (_errorListener == null) {
+      throw new IllegalArgumentException("An error listener must be provided");
+    } else if (_defaultSource == null) {
+      throw new IllegalArgumentException("A default source must be provided");
+    }
+    this._source = _defaultSource;
+  }
+
+  Source get source => _source;
+
+  /**
+   * Set the source to be used when reporting errors to the given [source].
+   * Setting the source to `null` will cause the default source to be used.
+   */
+  void set source(Source source) {
+    this._source = source == null ? _defaultSource : source;
+  }
+
+  /**
+   * Creates an error with properties with the given [errorCode] and
+   * [arguments]. The [node] is used to compute the location of the error.
+   */
+  AnalysisErrorWithProperties newErrorWithProperties(
+          ErrorCode errorCode, AstNode node, List<Object> arguments) =>
+      new AnalysisErrorWithProperties.con2(
+          _source, node.offset, node.length, errorCode, arguments);
+
+  /**
+   * Report the given [error].
+   */
+  void reportError(AnalysisError error) {
+    _errorListener.onError(error);
+  }
+
+  /**
+   * Report an error with the given [errorCode] and [arguments]. The [element]
+   * is used to compute the location of the error.
+   */
+  void reportErrorForElement(
+      ErrorCode errorCode, Element element, List<Object> arguments) {
+    String displayName = element.displayName;
+    int length = 0;
+    if (displayName != null) {
+      length = displayName.length;
+    } else if (element is ImportElement) {
+      length = 6; // 'import'.length
+    } else if (element is ExportElement) {
+      length = 6; // 'export'.length
+    }
+    reportErrorForOffset(errorCode, element.nameOffset, length, arguments);
+  }
+
+  /**
+   * Report an error with the given [errorCode] and [arguments].
+   * The [node] is used to compute the location of the error.
+   *
+   * If the arguments contain the names of two or more types, the method
+   * [reportTypeErrorForNode] should be used and the types
+   * themselves (rather than their names) should be passed as arguments.
+   */
+  void reportErrorForNode(ErrorCode errorCode, AstNode node,
+      [List<Object> arguments]) {
+    reportErrorForOffset(errorCode, node.offset, node.length, arguments);
+  }
+
+  /**
+   * Report an error with the given [errorCode] and [arguments]. The location of
+   * the error is specified by the given [offset] and [length].
+   */
+  void reportErrorForOffset(ErrorCode errorCode, int offset, int length,
+      [List<Object> arguments]) {
+    _errorListener.onError(
+        new AnalysisError.con2(_source, offset, length, errorCode, arguments));
+  }
+
+  /**
+   * Report an error with the given [errorCode] and [arguments]. The [token] is
+   * used to compute the location of the error.
+   */
+  void reportErrorForToken(ErrorCode errorCode, Token token,
+      [List<Object> arguments]) {
+    reportErrorForOffset(errorCode, token.offset, token.length, arguments);
+  }
+
+  /**
+   * Report an error with the given [errorCode] and [arguments]. The [node] is
+   * used to compute the location of the error. The arguments are expected to
+   * contain two or more types. Convert the types into strings by using the
+   * display names of the types, unless there are two or more types with the
+   * same names, in which case the extended display names of the types will be
+   * used in order to clarify the message.
+   *
+   * If there are not two or more types in the argument list, the method
+   * [reportErrorForNode] should be used instead.
+   */
+  void reportTypeErrorForNode(
+      ErrorCode errorCode, AstNode node, List<Object> arguments) {
+    _convertTypeNames(arguments);
+    reportErrorForOffset(errorCode, node.offset, node.length, arguments);
+  }
+
+  /**
+   * Given an array of [arguments] that is expected to contain two or more
+   * types, convert the types into strings by using the display names of the
+   * types, unless there are two or more types with the same names, in which
+   * case the extended display names of the types will be used in order to
+   * clarify the message.
+   */
+  void _convertTypeNames(List<Object> arguments) {
+    if (_hasEqualTypeNames(arguments)) {
+      int count = arguments.length;
+      for (int i = 0; i < count; i++) {
+        Object argument = arguments[i];
+        if (argument is DartType) {
+          DartType type = argument;
+          Element element = type.element;
+          if (element == null) {
+            arguments[i] = type.displayName;
+          } else {
+            arguments[i] = element.getExtendedDisplayName(type.displayName);
+          }
+        }
+      }
+    } else {
+      int count = arguments.length;
+      for (int i = 0; i < count; i++) {
+        Object argument = arguments[i];
+        if (argument is DartType) {
+          arguments[i] = argument.displayName;
+        }
+      }
+    }
+  }
+
+  /**
+   * Return `true` if the given array of [arguments] contains two or more types
+   * with the same display name.
+   */
+  bool _hasEqualTypeNames(List<Object> arguments) {
+    int count = arguments.length;
+    HashSet<String> typeNames = new HashSet<String>();
+    for (int i = 0; i < count; i++) {
+      if (arguments[i] is DartType &&
+          !typeNames.add((arguments[i] as DartType).displayName)) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
+
+/**
+ * The severity of an [ErrorCode].
+ */
+class ErrorSeverity extends Enum<ErrorSeverity> {
+  /**
+   * The severity representing a non-error. This is never used for any error
+   * code, but is useful for clients.
+   */
+  static const ErrorSeverity NONE = const ErrorSeverity('NONE', 0, " ", "none");
+
+  /**
+   * The severity representing an informational level analysis issue.
+   */
+  static const ErrorSeverity INFO = const ErrorSeverity('INFO', 1, "I", "info");
+
+  /**
+   * The severity representing a warning. Warnings can become errors if the `-Werror` command
+   * line flag is specified.
+   */
+  static const ErrorSeverity WARNING =
+      const ErrorSeverity('WARNING', 2, "W", "warning");
+
+  /**
+   * The severity representing an error.
+   */
+  static const ErrorSeverity ERROR =
+      const ErrorSeverity('ERROR', 3, "E", "error");
+
+  static const List<ErrorSeverity> values = const [NONE, INFO, WARNING, ERROR];
+
+  /**
+   * The name of the severity used when producing machine output.
+   */
+  final String machineCode;
+
+  /**
+   * The name of the severity used when producing readable output.
+   */
+  final String displayName;
+
+  /**
+   * Initialize a newly created severity with the given names.
+   *
+   * Parameters:
+   * 0: the name of the severity used when producing machine output
+   * 1: the name of the severity used when producing readable output
+   */
+  const ErrorSeverity(
+      String name, int ordinal, this.machineCode, this.displayName)
+      : super(name, ordinal);
+
+  /**
+   * Return the severity constant that represents the greatest severity.
+   */
+  ErrorSeverity max(ErrorSeverity severity) =>
+      this.ordinal >= severity.ordinal ? this : severity;
+}
+
+/**
+ * The type of an [ErrorCode].
+ */
+class ErrorType extends Enum<ErrorType> {
+  /**
+   * Task (todo) comments in user code.
+   */
+  static const ErrorType TODO = const ErrorType('TODO', 0, ErrorSeverity.INFO);
+
+  /**
+   * Extra analysis run over the code to follow best practices, which are not in
+   * the Dart Language Specification.
+   */
+  static const ErrorType HINT = const ErrorType('HINT', 1, ErrorSeverity.INFO);
+
+  /**
+   * Compile-time errors are errors that preclude execution. A compile time
+   * error must be reported by a Dart compiler before the erroneous code is
+   * executed.
+   */
+  static const ErrorType COMPILE_TIME_ERROR =
+      const ErrorType('COMPILE_TIME_ERROR', 2, ErrorSeverity.ERROR);
+
+  /**
+   * Checked mode compile-time errors are errors that preclude execution in
+   * checked mode.
+   */
+  static const ErrorType CHECKED_MODE_COMPILE_TIME_ERROR = const ErrorType(
+      'CHECKED_MODE_COMPILE_TIME_ERROR', 3, ErrorSeverity.ERROR);
+
+  /**
+   * Static warnings are those warnings reported by the static checker. They
+   * have no effect on execution. Static warnings must be provided by Dart
+   * compilers used during development.
+   */
+  static const ErrorType STATIC_WARNING =
+      const ErrorType('STATIC_WARNING', 4, ErrorSeverity.WARNING);
+
+  /**
+   * Many, but not all, static warnings relate to types, in which case they are
+   * known as static type warnings.
+   */
+  static const ErrorType STATIC_TYPE_WARNING =
+      const ErrorType('STATIC_TYPE_WARNING', 5, ErrorSeverity.WARNING);
+
+  /**
+   * Syntactic errors are errors produced as a result of input that does not
+   * conform to the grammar.
+   */
+  static const ErrorType SYNTACTIC_ERROR =
+      const ErrorType('SYNTACTIC_ERROR', 6, ErrorSeverity.ERROR);
+
+  /**
+   * Lint warnings describe style and best practice recommendations that can be
+   * used to formalize a project's style guidelines.
+   */
+  static const ErrorType LINT = const ErrorType('LINT', 7, ErrorSeverity.INFO);
+
+  static const List<ErrorType> values = const [
+    TODO,
+    HINT,
+    COMPILE_TIME_ERROR,
+    CHECKED_MODE_COMPILE_TIME_ERROR,
+    STATIC_WARNING,
+    STATIC_TYPE_WARNING,
+    SYNTACTIC_ERROR,
+    LINT
+  ];
+
+  /**
+   * The severity of this type of error.
+   */
+  final ErrorSeverity severity;
+
+  /**
+   * Initialize a newly created error type to have the given [name] and
+   * [severity].
+   */
+  const ErrorType(String name, int ordinal, this.severity)
+      : super(name, ordinal);
+
+  String get displayName => name.toLowerCase().replaceAll('_', ' ');
+}
+
+/**
+ * The hints and coding recommendations for best practices which are not
+ * mentioned in the Dart Language Specification.
+ */
+class HintCode extends ErrorCode {
+  /**
+   * This hint is generated anywhere where the
+   * [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE] would have been generated,
+   * if we used propagated information for the warnings.
+   *
+   * Parameters:
+   * 0: the name of the actual argument type
+   * 1: the name of the expected type
+   */
+  static const HintCode ARGUMENT_TYPE_NOT_ASSIGNABLE = const HintCode(
+      'ARGUMENT_TYPE_NOT_ASSIGNABLE',
+      "The argument type '{0}' cannot be assigned to the parameter type '{1}'");
+
+  /**
+   * Dead code is code that is never reached, this can happen for instance if a
+   * statement follows a return statement.
+   */
+  static const HintCode DEAD_CODE = const HintCode('DEAD_CODE', "Dead code");
+
+  /**
+   * Dead code is code that is never reached. This case covers cases where the
+   * user has catch clauses after `catch (e)` or `on Object catch (e)`.
+   */
+  static const HintCode DEAD_CODE_CATCH_FOLLOWING_CATCH = const HintCode(
+      'DEAD_CODE_CATCH_FOLLOWING_CATCH',
+      "Dead code, catch clauses after a 'catch (e)' or an 'on Object catch (e)' are never reached");
+
+  /**
+   * Dead code is code that is never reached. This case covers cases where the
+   * user has an on-catch clause such as `on A catch (e)`, where a supertype of
+   * `A` was already caught.
+   *
+   * Parameters:
+   * 0: name of the subtype
+   * 1: name of the supertype
+   */
+  static const HintCode DEAD_CODE_ON_CATCH_SUBTYPE = const HintCode(
+      'DEAD_CODE_ON_CATCH_SUBTYPE',
+      "Dead code, this on-catch block will never be executed since '{0}' is a subtype of '{1}'");
+
+  /**
+   * Deprecated members should not be invoked or used.
+   *
+   * Parameters:
+   * 0: the name of the member
+   */
+  static const HintCode DEPRECATED_MEMBER_USE =
+      const HintCode('DEPRECATED_MEMBER_USE', "'{0}' is deprecated");
+
+  /**
+   * Duplicate imports.
+   */
+  static const HintCode DUPLICATE_IMPORT =
+      const HintCode('DUPLICATE_IMPORT', "Duplicate import");
+
+  /**
+   * Hint to use the ~/ operator.
+   */
+  static const HintCode DIVISION_OPTIMIZATION = const HintCode(
+      'DIVISION_OPTIMIZATION',
+      "The operator x ~/ y is more efficient than (x / y).toInt()");
+
+  /**
+   * Hint for the `x is double` type checks.
+   */
+  static const HintCode IS_DOUBLE = const HintCode('IS_DOUBLE',
+      "When compiled to JS, this test might return true when the left hand side is an int");
+
+  /**
+   * Hint for the `x is int` type checks.
+   */
+  static const HintCode IS_INT = const HintCode('IS_INT',
+      "When compiled to JS, this test might return true when the left hand side is a double");
+
+  /**
+   * Hint for the `x is! double` type checks.
+   */
+  static const HintCode IS_NOT_DOUBLE = const HintCode('IS_NOT_DOUBLE',
+      "When compiled to JS, this test might return false when the left hand side is an int");
+
+  /**
+   * Hint for the `x is! int` type checks.
+   */
+  static const HintCode IS_NOT_INT = const HintCode('IS_NOT_INT',
+      "When compiled to JS, this test might return false when the left hand side is a double");
+
+  /**
+   * Deferred libraries shouldn't define a top level function 'loadLibrary'.
+   */
+  static const HintCode IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION = const HintCode(
+      'IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION',
+      "The library '{0}' defines a top-level function named 'loadLibrary' which is hidden by deferring this library");
+
+  /**
+   * This hint is generated anywhere where the
+   * [StaticTypeWarningCode.INVALID_ASSIGNMENT] would have been generated, if we
+   * used propagated information for the warnings.
+   *
+   * Parameters:
+   * 0: the name of the right hand side type
+   * 1: the name of the left hand side type
+   */
+  static const HintCode INVALID_ASSIGNMENT = const HintCode(
+      'INVALID_ASSIGNMENT',
+      "A value of type '{0}' cannot be assigned to a variable of type '{1}'");
+
+  /**
+   * Generate a hint for methods or functions that have a return type, but do
+   * not have a non-void return statement on all branches. At the end of methods
+   * or functions with no return, Dart implicitly returns `null`, avoiding these
+   * implicit returns is considered a best practice.
+   *
+   * Parameters:
+   * 0: the name of the declared return type
+   */
+  static const HintCode MISSING_RETURN = const HintCode('MISSING_RETURN',
+      "This function declares a return type of '{0}', but does not end with a return statement",
+      "Either add a return statement or change the return type to 'void'");
+
+  /**
+   * A getter with the override annotation does not override an existing getter.
+   */
+  static const HintCode OVERRIDE_ON_NON_OVERRIDING_GETTER = const HintCode(
+      'OVERRIDE_ON_NON_OVERRIDING_GETTER',
+      "Getter does not override an inherited getter");
+
+  /**
+   * A method with the override annotation does not override an existing method.
+   */
+  static const HintCode OVERRIDE_ON_NON_OVERRIDING_METHOD = const HintCode(
+      'OVERRIDE_ON_NON_OVERRIDING_METHOD',
+      "Method does not override an inherited method");
+
+  /**
+   * A setter with the override annotation does not override an existing setter.
+   */
+  static const HintCode OVERRIDE_ON_NON_OVERRIDING_SETTER = const HintCode(
+      'OVERRIDE_ON_NON_OVERRIDING_SETTER',
+      "Setter does not override an inherited setter");
+
+  /**
+   * Hint for classes that override equals, but not hashCode.
+   *
+   * Parameters:
+   * 0: the name of the current class
+   */
+  static const HintCode OVERRIDE_EQUALS_BUT_NOT_HASH_CODE = const HintCode(
+      'OVERRIDE_EQUALS_BUT_NOT_HASH_CODE',
+      "The class '{0}' overrides 'operator==', but not 'get hashCode'");
+
+  /**
+   * Type checks of the type `x is! Null` should be done with `x != null`.
+   */
+  static const HintCode TYPE_CHECK_IS_NOT_NULL = const HintCode(
+      'TYPE_CHECK_IS_NOT_NULL',
+      "Tests for non-null should be done with '!= null'");
+
+  /**
+   * Type checks of the type `x is Null` should be done with `x == null`.
+   */
+  static const HintCode TYPE_CHECK_IS_NULL = const HintCode(
+      'TYPE_CHECK_IS_NULL', "Tests for null should be done with '== null'");
+
+  /**
+   * This hint is generated anywhere where the
+   * [StaticTypeWarningCode.UNDEFINED_GETTER] or
+   * [StaticWarningCode.UNDEFINED_GETTER] would have been generated, if we used
+   * propagated information for the warnings.
+   *
+   * Parameters:
+   * 0: the name of the getter
+   * 1: the name of the enclosing type where the getter is being looked for
+   */
+  static const HintCode UNDEFINED_GETTER = const HintCode('UNDEFINED_GETTER',
+      "The getter '{0}' is not defined for the class '{1}'");
+
+  /**
+   * This hint is generated anywhere where the
+   * [StaticTypeWarningCode.UNDEFINED_METHOD] would have been generated, if we
+   * used propagated information for the warnings.
+   *
+   * Parameters:
+   * 0: the name of the method that is undefined
+   * 1: the resolved type name that the method lookup is happening on
+   */
+  static const HintCode UNDEFINED_METHOD = const HintCode('UNDEFINED_METHOD',
+      "The method '{0}' is not defined for the class '{1}'");
+
+  /**
+   * This hint is generated anywhere where the
+   * [StaticTypeWarningCode.UNDEFINED_OPERATOR] would have been generated, if we
+   * used propagated information for the warnings.
+   *
+   * Parameters:
+   * 0: the name of the operator
+   * 1: the name of the enclosing type where the operator is being looked for
+   */
+  static const HintCode UNDEFINED_OPERATOR = const HintCode(
+      'UNDEFINED_OPERATOR',
+      "The operator '{0}' is not defined for the class '{1}'");
+
+  /**
+   * This hint is generated anywhere where the
+   * [StaticTypeWarningCode.UNDEFINED_SETTER] or
+   * [StaticWarningCode.UNDEFINED_SETTER] would have been generated, if we used
+   * propagated information for the warnings.
+   *
+   * Parameters:
+   * 0: the name of the setter
+   * 1: the name of the enclosing type where the setter is being looked for
+   */
+  static const HintCode UNDEFINED_SETTER = const HintCode('UNDEFINED_SETTER',
+      "The setter '{0}' is not defined for the class '{1}'");
+
+  /**
+   * Unnecessary cast.
+   */
+  static const HintCode UNNECESSARY_CAST =
+      const HintCode('UNNECESSARY_CAST', "Unnecessary cast");
+
+  /**
+   * Unnecessary type checks, the result is always true.
+   */
+  static const HintCode UNNECESSARY_TYPE_CHECK_FALSE = const HintCode(
+      'UNNECESSARY_TYPE_CHECK_FALSE',
+      "Unnecessary type check, the result is always false");
+
+  /**
+   * Unnecessary type checks, the result is always false.
+   */
+  static const HintCode UNNECESSARY_TYPE_CHECK_TRUE = const HintCode(
+      'UNNECESSARY_TYPE_CHECK_TRUE',
+      "Unnecessary type check, the result is always true");
+
+  /**
+   * See [Modifier.IS_USED_IN_LIBRARY].
+   */
+  static const HintCode UNUSED_ELEMENT =
+      const HintCode('UNUSED_ELEMENT', "The {0} '{1}' is not used");
+
+  /**
+   * Unused fields are fields which are never read.
+   */
+  static const HintCode UNUSED_FIELD = const HintCode(
+      'UNUSED_FIELD', "The value of the field '{0}' is not used");
+
+  /**
+   * Unused imports are imports which are never used.
+   */
+  static const HintCode UNUSED_IMPORT =
+      const HintCode('UNUSED_IMPORT', "Unused import");
+
+  /**
+   * Unused catch exception variables.
+   */
+  static const HintCode UNUSED_CATCH_CLAUSE = const HintCode(
+      'UNUSED_CATCH_CLAUSE',
+      "The exception variable '{0}' is not used, so the 'catch' clause can be removed");
+
+  /**
+   * Unused catch stack trace variables.
+   */
+  static const HintCode UNUSED_CATCH_STACK = const HintCode(
+      'UNUSED_CATCH_STACK',
+      "The stack trace variable '{0}' is not used and can be removed");
+
+  /**
+   * Unused local variables are local varaibles which are never read.
+   */
+  static const HintCode UNUSED_LOCAL_VARIABLE = const HintCode(
+      'UNUSED_LOCAL_VARIABLE',
+      "The value of the local variable '{0}' is not used");
+
+  /**
+   * Hint for cases where the source expects a method or function to return a
+   * non-void result, but the method or function signature returns void.
+   *
+   * Parameters:
+   * 0: the name of the method or function that returns void
+   */
+  static const HintCode USE_OF_VOID_RESULT = const HintCode(
+      'USE_OF_VOID_RESULT',
+      "The result of '{0}' is being used, even though it is declared to be 'void'");
+
+  /**
+   * It is a bad practice for a source file in a package "lib" directory
+   * hierarchy to traverse outside that directory hierarchy. For example, a
+   * source file in the "lib" directory should not contain a directive such as
+   * `import '../web/some.dart'` which references a file outside the lib
+   * directory.
+   */
+  static const HintCode FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE =
+      const HintCode('FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE',
+          "A file in the 'lib' directory hierarchy should not reference a file outside that hierarchy");
+
+  /**
+   * It is a bad practice for a source file ouside a package "lib" directory
+   * hierarchy to traverse into that directory hierarchy. For example, a source
+   * file in the "web" directory should not contain a directive such as
+   * `import '../lib/some.dart'` which references a file inside the lib
+   * directory.
+   */
+  static const HintCode FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE =
+      const HintCode('FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE',
+          "A file outside the 'lib' directory hierarchy should not reference a file inside that hierarchy. Use a package: reference instead.");
+
+  /**
+   * It is a bad practice for a package import to reference anything outside the
+   * given package, or more generally, it is bad practice for a package import
+   * to contain a "..". For example, a source file should not contain a
+   * directive such as `import 'package:foo/../some.dart'`.
+   */
+  static const HintCode PACKAGE_IMPORT_CONTAINS_DOT_DOT = const HintCode(
+      'PACKAGE_IMPORT_CONTAINS_DOT_DOT',
+      "A package import should not contain '..'");
+
+  /**
+   * Initialize a newly created error code to have the given [name]. The message
+   * associated with the error will be created from the given [message]
+   * template. The correction associated with the error will be created from the
+   * given [correction] template.
+   */
+  const HintCode(String name, String message, [String correction])
+      : super(name, message, correction);
+
+  @override
+  ErrorSeverity get errorSeverity => ErrorType.HINT.severity;
+
+  @override
+  ErrorType get type => ErrorType.HINT;
+}
+
+/**
+ * The error codes used for warnings in HTML files. The convention for this
+ * class is for the name of the error code to indicate the problem that caused
+ * the error to be generated and for the error message to explain what is wrong
+ * and, when appropriate, how the problem can be corrected.
+ */
+class HtmlWarningCode extends ErrorCode {
+  /**
+   * An error code indicating that the value of the 'src' attribute of a Dart
+   * script tag is not a valid URI.
+   *
+   * Parameters:
+   * 0: the URI that is invalid
+   */
+  static const HtmlWarningCode INVALID_URI =
+      const HtmlWarningCode('INVALID_URI', "Invalid URI syntax: '{0}'");
+
+  /**
+   * An error code indicating that the value of the 'src' attribute of a Dart
+   * script tag references a file that does not exist.
+   *
+   * Parameters:
+   * 0: the URI pointing to a non-existent file
+   */
+  static const HtmlWarningCode URI_DOES_NOT_EXIST = const HtmlWarningCode(
+      'URI_DOES_NOT_EXIST', "Target of URI does not exist: '{0}'");
+
+  /**
+   * Initialize a newly created error code to have the given [name]. The message
+   * associated with the error will be created from the given [message]
+   * template. The correction associated with the error will be created from the
+   * given [correction] template.
+   */
+  const HtmlWarningCode(String name, String message, [String correction])
+      : super(name, message, correction);
+
+  @override
+  ErrorSeverity get errorSeverity => ErrorSeverity.WARNING;
+
+  @override
+  ErrorType get type => ErrorType.STATIC_WARNING;
+}
+
+/**
+ * Defines style and best practice recommendations.
+ *
+ * Unlike [HintCode]s, which are akin to traditional static warnings from a
+ * compiler, lint recommendations focus on matters of style and practices that
+ * might aggregated to define a project's style guide.
+ */
+class LintCode extends ErrorCode {
+  const LintCode(String name, String message, [String correction])
+      : super(name, message, correction);
+
+  @override
+  ErrorSeverity get errorSeverity => ErrorSeverity.INFO;
+
+  @override
+  ErrorType get type => ErrorType.LINT;
+}
+
+/**
+ * The error codes used for static type warnings. The convention for this class
+ * is for the name of the error code to indicate the problem that caused the
+ * error to be generated and for the error message to explain what is wrong and,
+ * when appropriate, how the problem can be corrected.
+ */
+class StaticTypeWarningCode extends ErrorCode {
+  /**
+   * 12.7 Lists: A fresh instance (7.6.1) <i>a</i>, of size <i>n</i>, whose
+   * class implements the built-in class <i>List&lt;E></i> is allocated.
+   *
+   * Parameters:
+   * 0: the number of provided type arguments
+   */
+  static const StaticTypeWarningCode EXPECTED_ONE_LIST_TYPE_ARGUMENTS =
+      const StaticTypeWarningCode('EXPECTED_ONE_LIST_TYPE_ARGUMENTS',
+          "List literal requires exactly one type arguments or none, but {0} found");
+
+  /**
+   * 12.8 Maps: A fresh instance (7.6.1) <i>m</i>, of size <i>n</i>, whose class
+   * implements the built-in class <i>Map&lt;K, V></i> is allocated.
+   *
+   * Parameters:
+   * 0: the number of provided type arguments
+   */
+  static const StaticTypeWarningCode EXPECTED_TWO_MAP_TYPE_ARGUMENTS =
+      const StaticTypeWarningCode('EXPECTED_TWO_MAP_TYPE_ARGUMENTS',
+          "Map literal requires exactly two type arguments or none, but {0} found");
+
+  /**
+   * 9 Functions: It is a static warning if the declared return type of a
+   * function marked async* may not be assigned to Stream.
+   */
+  static const StaticTypeWarningCode ILLEGAL_ASYNC_GENERATOR_RETURN_TYPE =
+      const StaticTypeWarningCode('ILLEGAL_ASYNC_GENERATOR_RETURN_TYPE',
+          "Functions marked 'async*' must have a return type assignable to 'Stream'");
+
+  /**
+   * 9 Functions: It is a static warning if the declared return type of a
+   * function marked async may not be assigned to Future.
+   */
+  static const StaticTypeWarningCode ILLEGAL_ASYNC_RETURN_TYPE =
+      const StaticTypeWarningCode('ILLEGAL_ASYNC_RETURN_TYPE',
+          "Functions marked 'async' must have a return type assignable to 'Future'");
+
+  /**
+   * 9 Functions: It is a static warning if the declared return type of a
+   * function marked sync* may not be assigned to Iterable.
+   */
+  static const StaticTypeWarningCode ILLEGAL_SYNC_GENERATOR_RETURN_TYPE =
+      const StaticTypeWarningCode('ILLEGAL_SYNC_GENERATOR_RETURN_TYPE',
+          "Functions marked 'sync*' must have a return type assignable to 'Iterable'");
+
+  /**
+   * 12.18 Assignment: Let <i>T</i> be the static type of <i>e<sub>1</sub></i>.
+   * It is a static type warning if <i>T</i> does not have an accessible
+   * instance setter named <i>v=</i>.
+   *
+   * See [UNDEFINED_SETTER].
+   */
+  static const StaticTypeWarningCode INACCESSIBLE_SETTER =
+      const StaticTypeWarningCode('INACCESSIBLE_SETTER', "");
+
+  /**
+   * 8.1.1 Inheritance and Overriding: However, if the above rules would cause
+   * multiple members <i>m<sub>1</sub>, &hellip;, m<sub>k</sub></i> with the
+   * same name <i>n</i> that would be inherited (because identically named
+   * members existed in several superinterfaces) then at most one member is
+   * inherited.
+   *
+   * If the static types <i>T<sub>1</sub>, &hellip;, T<sub>k</sub></i> of the
+   * members <i>m<sub>1</sub>, &hellip;, m<sub>k</sub></i> are not identical,
+   * then there must be a member <i>m<sub>x</sub></i> such that <i>T<sub>x</sub>
+   * &lt;: T<sub>i</sub>, 1 &lt;= x &lt;= k</i> for all <i>i, 1 &lt;= i &lt;=
+   * k</i>, or a static type warning occurs. The member that is inherited is
+   * <i>m<sub>x</sub></i>, if it exists; otherwise:
+   * * Let <i>numberOfPositionals</i>(<i>f</i>) denote the number of positional
+   *   parameters of a function <i>f</i>, and let
+   *   <i>numberOfRequiredParams</i>(<i>f</i>) denote the number of required
+   *   parameters of a function <i>f</i>. Furthermore, let <i>s</i> denote the
+   *   set of all named parameters of the <i>m<sub>1</sub>, &hellip;,
+   *   m<sub>k</sub></i>. Then let
+   * * <i>h = max(numberOfPositionals(m<sub>i</sub>)),</i>
+   * * <i>r = min(numberOfRequiredParams(m<sub>i</sub>)), for all <i>i</i>, 1 <=
+   *   i <= k.</i> If <i>r <= h</i> then <i>I</i> has a method named <i>n</i>,
+   *   with <i>r</i> required parameters of type <b>dynamic</b>, <i>h</i>
+   *   positional parameters of type <b>dynamic</b>, named parameters <i>s</i>
+   *   of type <b>dynamic</b> and return type <b>dynamic</b>.
+   * * Otherwise none of the members <i>m<sub>1</sub>, &hellip;,
+   *   m<sub>k</sub></i> is inherited.
+   */
+  static const StaticTypeWarningCode INCONSISTENT_METHOD_INHERITANCE =
+      const StaticTypeWarningCode('INCONSISTENT_METHOD_INHERITANCE',
+          "'{0}' is inherited by at least two interfaces inconsistently, from {1}");
+
+  /**
+   * 12.15.1 Ordinary Invocation: It is a static type warning if <i>T</i> does
+   * not have an accessible (3.2) instance member named <i>m</i>.
+   *
+   * Parameters:
+   * 0: the name of the static member
+   *
+   * See [UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER].
+   */
+  static const StaticTypeWarningCode INSTANCE_ACCESS_TO_STATIC_MEMBER =
+      const StaticTypeWarningCode('INSTANCE_ACCESS_TO_STATIC_MEMBER',
+          "Static member '{0}' cannot be accessed using instance access");
+
+  /**
+   * 12.18 Assignment: It is a static type warning if the static type of
+   * <i>e</i> may not be assigned to the static type of <i>v</i>. The static
+   * type of the expression <i>v = e</i> is the static type of <i>e</i>.
+   *
+   * 12.18 Assignment: It is a static type warning if the static type of
+   * <i>e</i> may not be assigned to the static type of <i>C.v</i>. The static
+   * type of the expression <i>C.v = e</i> is the static type of <i>e</i>.
+   *
+   * 12.18 Assignment: Let <i>T</i> be the static type of <i>e<sub>1</sub></i>.
+   * It is a static type warning if the static type of <i>e<sub>2</sub></i> may
+   * not be assigned to <i>T</i>.
+   *
+   * Parameters:
+   * 0: the name of the right hand side type
+   * 1: the name of the left hand side type
+   */
+  static const StaticTypeWarningCode INVALID_ASSIGNMENT =
+      const StaticTypeWarningCode('INVALID_ASSIGNMENT',
+          "A value of type '{0}' cannot be assigned to a variable of type '{1}'");
+
+  /**
+   * 12.15.1 Ordinary Invocation: An ordinary method invocation <i>i</i> has the
+   * form <i>o.m(a<sub>1</sub>, &hellip;, a<sub>n</sub>, x<sub>n+1</sub>:
+   * a<sub>n+1</sub>, &hellip; x<sub>n+k</sub>: a<sub>n+k</sub>)</i>.
+   *
+   * Let <i>T</i> be the static type of <i>o</i>. It is a static type warning if
+   * <i>T</i> does not have an accessible instance member named <i>m</i>. If
+   * <i>T.m</i> exists, it is a static warning if the type <i>F</i> of
+   * <i>T.m</i> may not be assigned to a function type. If <i>T.m</i> does not
+   * exist, or if <i>F</i> is not a function type, the static type of <i>i</i>
+   * is dynamic.
+   *
+   * 12.15.3 Static Invocation: It is a static type warning if the type <i>F</i>
+   * of <i>C.m</i> may not be assigned to a function type.
+   *
+   * 12.15.4 Super Invocation: A super method invocation <i>i</i> has the form
+   * <i>super.m(a<sub>1</sub>, &hellip;, a<sub>n</sub>, x<sub>n+1</sub>:
+   * a<sub>n+1</sub>, &hellip; x<sub>n+k</sub>: a<sub>n+k</sub>)</i>. If
+   * <i>S.m</i> exists, it is a static warning if the type <i>F</i> of
+   * <i>S.m</i> may not be assigned to a function type.
+   *
+   * Parameters:
+   * 0: the name of the identifier that is not a function type
+   */
+  static const StaticTypeWarningCode INVOCATION_OF_NON_FUNCTION =
+      const StaticTypeWarningCode(
+          'INVOCATION_OF_NON_FUNCTION', "'{0}' is not a method");
+
+  /**
+   * 12.14.4 Function Expression Invocation: A function expression invocation
+   * <i>i</i> has the form <i>e<sub>f</sub>(a<sub>1</sub>, &hellip;,
+   * a<sub>n</sub>, x<sub>n+1</sub>: a<sub>n+1</sub>, &hellip;, x<sub>n+k</sub>:
+   * a<sub>n+k</sub>)</i>, where <i>e<sub>f</sub></i> is an expression.
+   *
+   * It is a static type warning if the static type <i>F</i> of
+   * <i>e<sub>f</sub></i> may not be assigned to a function type.
+   */
+  static const StaticTypeWarningCode INVOCATION_OF_NON_FUNCTION_EXPRESSION =
+      const StaticTypeWarningCode('INVOCATION_OF_NON_FUNCTION_EXPRESSION',
+          "Cannot invoke a non-function");
+
+  /**
+   * 12.20 Conditional: It is a static type warning if the type of
+   * <i>e<sub>1</sub></i> may not be assigned to bool.
+   *
+   * 13.5 If: It is a static type warning if the type of the expression <i>b</i>
+   * may not be assigned to bool.
+   *
+   * 13.7 While: It is a static type warning if the type of <i>e</i> may not be
+   * assigned to bool.
+   *
+   * 13.8 Do: It is a static type warning if the type of <i>e</i> cannot be
+   * assigned to bool.
+   */
+  static const StaticTypeWarningCode NON_BOOL_CONDITION =
+      const StaticTypeWarningCode(
+          'NON_BOOL_CONDITION', "Conditions must have a static type of 'bool'");
+
+  /**
+   * 13.15 Assert: It is a static type warning if the type of <i>e</i> may not
+   * be assigned to either bool or () &rarr; bool
+   */
+  static const StaticTypeWarningCode NON_BOOL_EXPRESSION =
+      const StaticTypeWarningCode('NON_BOOL_EXPRESSION',
+          "Assertions must be on either a 'bool' or '() -> bool'");
+
+  /**
+   * 12.28 Unary Expressions: The expression !<i>e</i> is equivalent to the
+   * expression <i>e</i>?<b>false<b> : <b>true</b>.
+   *
+   * 12.20 Conditional: It is a static type warning if the type of
+   * <i>e<sub>1</sub></i> may not be assigned to bool.
+   */
+  static const StaticTypeWarningCode NON_BOOL_NEGATION_EXPRESSION =
+      const StaticTypeWarningCode('NON_BOOL_NEGATION_EXPRESSION',
+          "Negation argument must have a static type of 'bool'");
+
+  /**
+   * 12.21 Logical Boolean Expressions: It is a static type warning if the
+   * static types of both of <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> may
+   * not be assigned to bool.
+   *
+   * Parameters:
+   * 0: the lexeme of the logical operator
+   */
+  static const StaticTypeWarningCode NON_BOOL_OPERAND =
+      const StaticTypeWarningCode('NON_BOOL_OPERAND',
+          "The operands of the '{0}' operator must be assignable to 'bool'");
+
+  /**
+   * 15.8 Parameterized Types: It is a static type warning if <i>A<sub>i</sub>,
+   * 1 &lt;= i &lt;= n</i> does not denote a type in the enclosing lexical scope.
+   */
+  static const StaticTypeWarningCode NON_TYPE_AS_TYPE_ARGUMENT =
+      const StaticTypeWarningCode('NON_TYPE_AS_TYPE_ARGUMENT',
+          "The name '{0}' is not a type and cannot be used as a parameterized type");
+
+  /**
+   * 13.11 Return: It is a static type warning if the type of <i>e</i> may not
+   * be assigned to the declared return type of the immediately enclosing
+   * function.
+   *
+   * Parameters:
+   * 0: the return type as declared in the return statement
+   * 1: the expected return type as defined by the method
+   * 2: the name of the method
+   */
+  static const StaticTypeWarningCode RETURN_OF_INVALID_TYPE =
+      const StaticTypeWarningCode('RETURN_OF_INVALID_TYPE',
+          "The return type '{0}' is not a '{1}', as defined by the method '{2}'");
+
+  /**
+   * 12.11 Instance Creation: It is a static type warning if any of the type
+   * arguments to a constructor of a generic type <i>G</i> invoked by a new
+   * expression or a constant object expression are not subtypes of the bounds
+   * of the corresponding formal type parameters of <i>G</i>.
+   *
+   * 15.8 Parameterized Types: If <i>S</i> is the static type of a member
+   * <i>m</i> of <i>G</i>, then the static type of the member <i>m</i> of
+   * <i>G&lt;A<sub>1</sub>, &hellip;, A<sub>n</sub>&gt;</i> is <i>[A<sub>1</sub>,
+   * &hellip;, A<sub>n</sub>/T<sub>1</sub>, &hellip;, T<sub>n</sub>]S</i> where
+   * <i>T<sub>1</sub>, &hellip;, T<sub>n</sub></i> are the formal type
+   * parameters of <i>G</i>. Let <i>B<sub>i</sub></i> be the bounds of
+   * <i>T<sub>i</sub>, 1 &lt;= i &lt;= n</i>. It is a static type warning if
+   * <i>A<sub>i</sub></i> is not a subtype of <i>[A<sub>1</sub>, &hellip;,
+   * A<sub>n</sub>/T<sub>1</sub>, &hellip;, T<sub>n</sub>]B<sub>i</sub>, 1 &lt;=
+   * i &lt;= n</i>.
+   *
+   * 7.6.2 Factories: It is a static type warning if any of the type arguments
+   * to <i>k'</i> are not subtypes of the bounds of the corresponding formal
+   * type parameters of type.
+   *
+   * Parameters:
+   * 0: the name of the type used in the instance creation that should be
+   *    limited by the bound as specified in the class declaration
+   * 1: the name of the bounding type
+   *
+   * See [TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND].
+   */
+  static const StaticTypeWarningCode TYPE_ARGUMENT_NOT_MATCHING_BOUNDS =
+      const StaticTypeWarningCode(
+          'TYPE_ARGUMENT_NOT_MATCHING_BOUNDS', "'{0}' does not extend '{1}'");
+
+  /**
+   * 10 Generics: It is a static type warning if a type parameter is a supertype
+   * of its upper bound.
+   *
+   * Parameters:
+   * 0: the name of the type parameter
+   *
+   * See [TYPE_ARGUMENT_NOT_MATCHING_BOUNDS].
+   */
+  static const StaticTypeWarningCode TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND =
+      const StaticTypeWarningCode('TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND',
+          "'{0}' cannot be a supertype of its upper bound");
+
+  /**
+   * 12.17 Getter Invocation: It is a static warning if there is no class
+   * <i>C</i> in the enclosing lexical scope of <i>i</i>, or if <i>C</i> does
+   * not declare, implicitly or explicitly, a getter named <i>m</i>.
+   *
+   * Parameters:
+   * 0: the name of the enumeration constant that is not defined
+   * 1: the name of the enumeration used to access the constant
+   */
+  static const StaticTypeWarningCode UNDEFINED_ENUM_CONSTANT =
+      const StaticTypeWarningCode('UNDEFINED_ENUM_CONSTANT',
+          "There is no constant named '{0}' in '{1}'");
+
+  /**
+   * 12.15.3 Unqualified Invocation: If there exists a lexically visible
+   * declaration named <i>id</i>, let <i>f<sub>id</sub></i> be the innermost
+   * such declaration. Then: [skip]. Otherwise, <i>f<sub>id</sub></i> is
+   * considered equivalent to the ordinary method invocation
+   * <b>this</b>.<i>id</i>(<i>a<sub>1</sub></i>, ..., <i>a<sub>n</sub></i>,
+   * <i>x<sub>n+1</sub></i> : <i>a<sub>n+1</sub></i>, ...,
+   * <i>x<sub>n+k</sub></i> : <i>a<sub>n+k</sub></i>).
+   *
+   * Parameters:
+   * 0: the name of the method that is undefined
+   */
+  static const StaticTypeWarningCode UNDEFINED_FUNCTION =
+      const StaticTypeWarningCode(
+          'UNDEFINED_FUNCTION', "The function '{0}' is not defined");
+
+  /**
+   * 12.17 Getter Invocation: Let <i>T</i> be the static type of <i>e</i>. It is
+   * a static type warning if <i>T</i> does not have a getter named <i>m</i>.
+   *
+   * Parameters:
+   * 0: the name of the getter
+   * 1: the name of the enclosing type where the getter is being looked for
+   */
+  static const StaticTypeWarningCode UNDEFINED_GETTER =
+      const StaticTypeWarningCode('UNDEFINED_GETTER',
+          "The getter '{0}' is not defined for the class '{1}'");
+
+  /**
+   * 12.15.1 Ordinary Invocation: Let <i>T</i> be the static type of <i>o</i>.
+   * It is a static type warning if <i>T</i> does not have an accessible
+   * instance member named <i>m</i>.
+   *
+   * Parameters:
+   * 0: the name of the method that is undefined
+   * 1: the resolved type name that the method lookup is happening on
+   */
+  static const StaticTypeWarningCode UNDEFINED_METHOD =
+      const StaticTypeWarningCode('UNDEFINED_METHOD',
+          "The method '{0}' is not defined for the class '{1}'");
+
+  /**
+   * 12.18 Assignment: Evaluation of an assignment of the form
+   * <i>e<sub>1</sub></i>[<i>e<sub>2</sub></i>] = <i>e<sub>3</sub></i> is
+   * equivalent to the evaluation of the expression (a, i, e){a.[]=(i, e);
+   * return e;} (<i>e<sub>1</sub></i>, <i>e<sub>2</sub></i>,
+   * <i>e<sub>2</sub></i>).
+   *
+   * 12.29 Assignable Expressions: An assignable expression of the form
+   * <i>e<sub>1</sub></i>[<i>e<sub>2</sub></i>] is evaluated as a method
+   * invocation of the operator method [] on <i>e<sub>1</sub></i> with argument
+   * <i>e<sub>2</sub></i>.
+   *
+   * 12.15.1 Ordinary Invocation: Let <i>T</i> be the static type of <i>o</i>.
+   * It is a static type warning if <i>T</i> does not have an accessible
+   * instance member named <i>m</i>.
+   *
+   * Parameters:
+   * 0: the name of the operator
+   * 1: the name of the enclosing type where the operator is being looked for
+   */
+  static const StaticTypeWarningCode UNDEFINED_OPERATOR =
+      const StaticTypeWarningCode('UNDEFINED_OPERATOR',
+          "The operator '{0}' is not defined for the class '{1}'");
+
+  /**
+   * 12.18 Assignment: Let <i>T</i> be the static type of <i>e<sub>1</sub></i>.
+   * It is a static type warning if <i>T</i> does not have an accessible
+   * instance setter named <i>v=</i>.
+   *
+   * Parameters:
+   * 0: the name of the setter
+   * 1: the name of the enclosing type where the setter is being looked for
+   *
+   * See [INACCESSIBLE_SETTER].
+   */
+  static const StaticTypeWarningCode UNDEFINED_SETTER =
+      const StaticTypeWarningCode('UNDEFINED_SETTER',
+          "The setter '{0}' is not defined for the class '{1}'");
+
+  /**
+   * 12.17 Getter Invocation: Let <i>T</i> be the static type of <i>e</i>. It is
+   * a static type warning if <i>T</i> does not have a getter named <i>m</i>.
+   *
+   * Parameters:
+   * 0: the name of the getter
+   * 1: the name of the enclosing type where the getter is being looked for
+   */
+  static const StaticTypeWarningCode UNDEFINED_SUPER_GETTER =
+      const StaticTypeWarningCode('UNDEFINED_SUPER_GETTER',
+          "The getter '{0}' is not defined in a superclass of '{1}'");
+
+  /**
+   * 12.15.4 Super Invocation: A super method invocation <i>i</i> has the form
+   * <i>super.m(a<sub>1</sub>, &hellip;, a<sub>n</sub>, x<sub>n+1</sub>:
+   * a<sub>n+1</sub>, &hellip; x<sub>n+k</sub>: a<sub>n+k</sub>)</i>. It is a
+   * static type warning if <i>S</i> does not have an accessible instance member
+   * named <i>m</i>.
+   *
+   * Parameters:
+   * 0: the name of the method that is undefined
+   * 1: the resolved type name that the method lookup is happening on
+   */
+  static const StaticTypeWarningCode UNDEFINED_SUPER_METHOD =
+      const StaticTypeWarningCode('UNDEFINED_SUPER_METHOD',
+          "The method '{0}' is not defined in a superclass of '{1}'");
+
+  /**
+   * 12.18 Assignment: Evaluation of an assignment of the form
+   * <i>e<sub>1</sub></i>[<i>e<sub>2</sub></i>] = <i>e<sub>3</sub></i> is
+   * equivalent to the evaluation of the expression (a, i, e){a.[]=(i, e);
+   * return e;} (<i>e<sub>1</sub></i>, <i>e<sub>2</sub></i>,
+   * <i>e<sub>2</sub></i>).
+   *
+   * 12.29 Assignable Expressions: An assignable expression of the form
+   * <i>e<sub>1</sub></i>[<i>e<sub>2</sub></i>] is evaluated as a method
+   * invocation of the operator method [] on <i>e<sub>1</sub></i> with argument
+   * <i>e<sub>2</sub></i>.
+   *
+   * 12.15.1 Ordinary Invocation: Let <i>T</i> be the static type of <i>o</i>.
+   * It is a static type warning if <i>T</i> does not have an accessible
+   * instance member named <i>m</i>.
+   *
+   * Parameters:
+   * 0: the name of the operator
+   * 1: the name of the enclosing type where the operator is being looked for
+   */
+  static const StaticTypeWarningCode UNDEFINED_SUPER_OPERATOR =
+      const StaticTypeWarningCode('UNDEFINED_SUPER_OPERATOR',
+          "The operator '{0}' is not defined in a superclass of '{1}'");
+
+  /**
+   * 12.18 Assignment: Let <i>T</i> be the static type of <i>e<sub>1</sub></i>.
+   * It is a static type warning if <i>T</i> does not have an accessible
+   * instance setter named <i>v=</i>.
+   *
+   * Parameters:
+   * 0: the name of the setter
+   * 1: the name of the enclosing type where the setter is being looked for
+   *
+   * See [INACCESSIBLE_SETTER].
+   */
+  static const StaticTypeWarningCode UNDEFINED_SUPER_SETTER =
+      const StaticTypeWarningCode('UNDEFINED_SUPER_SETTER',
+          "The setter '{0}' is not defined in a superclass of '{1}'");
+
+  /**
+   * 12.15.1 Ordinary Invocation: It is a static type warning if <i>T</i> does
+   * not have an accessible (3.2) instance member named <i>m</i>.
+   *
+   * This is a specialization of [INSTANCE_ACCESS_TO_STATIC_MEMBER] that is used
+   * when we are able to find the name defined in a supertype. It exists to
+   * provide a more informative error message.
+   */
+  static const StaticTypeWarningCode UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER =
+      const StaticTypeWarningCode(
+          'UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER',
+          "Static members from supertypes must be qualified by the name of the defining type");
+
+  /**
+   * 15.8 Parameterized Types: It is a static type warning if <i>G</i> is not a
+   * generic type with exactly <i>n</i> type parameters.
+   *
+   * Parameters:
+   * 0: the name of the type being referenced (<i>G</i>)
+   * 1: the number of type parameters that were declared
+   * 2: the number of type arguments provided
+   *
+   * See [CompileTimeErrorCode.CONST_WITH_INVALID_TYPE_PARAMETERS], and
+   * [CompileTimeErrorCode.NEW_WITH_INVALID_TYPE_PARAMETERS].
+   */
+  static const StaticTypeWarningCode WRONG_NUMBER_OF_TYPE_ARGUMENTS =
+      const StaticTypeWarningCode('WRONG_NUMBER_OF_TYPE_ARGUMENTS',
+          "The type '{0}' is declared with {1} type parameters, but {2} type arguments were given");
+
+  /**
+   * 17.16.1 Yield: Let T be the static type of e [the expression to the right
+   * of "yield"] and let f be the immediately enclosing function.  It is a
+   * static type warning if either:
+   *
+   * - the body of f is marked async* and the type Stream<T> may not be
+   *   assigned to the declared return type of f.
+   *
+   * - the body of f is marked sync* and the type Iterable<T> may not be
+   *   assigned to the declared return type of f.
+   *
+   * 17.16.2 Yield-Each: Let T be the static type of e [the expression to the
+   * right of "yield*"] and let f be the immediately enclosing function.  It is
+   * a static type warning if T may not be assigned to the declared return type
+   * of f.  If f is synchronous it is a static type warning if T may not be
+   * assigned to Iterable.  If f is asynchronous it is a static type warning if
+   * T may not be assigned to Stream.
+   */
+  static const StaticTypeWarningCode YIELD_OF_INVALID_TYPE =
+      const StaticTypeWarningCode('YIELD_OF_INVALID_TYPE',
+          "The type '{0}' implied by the 'yield' expression must be assignable to '{1}'");
+
+  /**
+   * Initialize a newly created error code to have the given [name]. The message
+   * associated with the error will be created from the given [message]
+   * template. The correction associated with the error will be created from the
+   * given [correction] template.
+   */
+  const StaticTypeWarningCode(String name, String message, [String correction])
+      : super(name, message, correction);
+
+  @override
+  ErrorSeverity get errorSeverity => ErrorType.STATIC_TYPE_WARNING.severity;
+
+  @override
+  ErrorType get type => ErrorType.STATIC_TYPE_WARNING;
+}
+
+/**
+ * The error codes used for static warnings. The convention for this class is
+ * for the name of the error code to indicate the problem that caused the error
+ * to be generated and for the error message to explain what is wrong and, when
+ * appropriate, how the problem can be corrected.
+ */
+class StaticWarningCode extends ErrorCode {
+  /**
+   * 14.1 Imports: If a name <i>N</i> is referenced by a library <i>L</i> and
+   * <i>N</i> is introduced into the top level scope <i>L</i> by more than one
+   * import then:
+   * 1. A static warning occurs.
+   * 2. If <i>N</i> is referenced as a function, getter or setter, a
+   *    <i>NoSuchMethodError</i> is raised.
+   * 3. If <i>N</i> is referenced as a type, it is treated as a malformed type.
+   *
+   * Parameters:
+   * 0: the name of the ambiguous type
+   * 1: the name of the first library that the type is found
+   * 2: the name of the second library that the type is found
+   */
+  static const StaticWarningCode AMBIGUOUS_IMPORT = const StaticWarningCode(
+      'AMBIGUOUS_IMPORT', "The name '{0}' is defined in the libraries {1}",
+      "Consider using 'as prefix' for one of the import directives "
+      "or hiding the name from all but one of the imports.");
+
+  /**
+   * 12.11.1 New: It is a static warning if the static type of <i>a<sub>i</sub>,
+   * 1 &lt;= i &lt;= n+ k</i> may not be assigned to the type of the
+   * corresponding formal parameter of the constructor <i>T.id</i> (respectively
+   * <i>T</i>).
+   *
+   * 12.11.2 Const: It is a static warning if the static type of
+   * <i>a<sub>i</sub>, 1 &lt;= i &lt;= n+ k</i> may not be assigned to the type
+   * of the corresponding formal parameter of the constructor <i>T.id</i>
+   * (respectively <i>T</i>).
+   *
+   * 12.14.2 Binding Actuals to Formals: Let <i>T<sub>i</sub></i> be the static
+   * type of <i>a<sub>i</sub></i>, let <i>S<sub>i</sub></i> be the type of
+   * <i>p<sub>i</sub>, 1 &lt;= i &lt;= n+k</i> and let <i>S<sub>q</sub></i> be
+   * the type of the named parameter <i>q</i> of <i>f</i>. It is a static
+   * warning if <i>T<sub>j</sub></i> may not be assigned to <i>S<sub>j</sub>, 1
+   * &lt;= j &lt;= m</i>.
+   *
+   * 12.14.2 Binding Actuals to Formals: Furthermore, each <i>q<sub>i</sub>, 1
+   * &lt;= i &lt;= l</i>, must have a corresponding named parameter in the set
+   * <i>{p<sub>n+1</sub>, &hellip; p<sub>n+k</sub>}</i> or a static warning
+   * occurs. It is a static warning if <i>T<sub>m+j</sub></i> may not be
+   * assigned to <i>S<sub>r</sub></i>, where <i>r = q<sub>j</sub>, 1 &lt;= j
+   * &lt;= l</i>.
+   *
+   * Parameters:
+   * 0: the name of the actual argument type
+   * 1: the name of the expected type
+   */
+  static const StaticWarningCode ARGUMENT_TYPE_NOT_ASSIGNABLE =
+      const StaticWarningCode('ARGUMENT_TYPE_NOT_ASSIGNABLE',
+          "The argument type '{0}' cannot be assigned to the parameter type '{1}'");
+
+  /**
+   * 5 Variables: Attempting to assign to a final variable elsewhere will cause
+   * a NoSuchMethodError to be thrown, because no setter is defined for it. The
+   * assignment will also give rise to a static warning for the same reason.
+   *
+   * A constant variable is always implicitly final.
+   */
+  static const StaticWarningCode ASSIGNMENT_TO_CONST = const StaticWarningCode(
+      'ASSIGNMENT_TO_CONST', "Constant variables cannot be assigned a value");
+
+  /**
+   * 5 Variables: Attempting to assign to a final variable elsewhere will cause
+   * a NoSuchMethodError to be thrown, because no setter is defined for it. The
+   * assignment will also give rise to a static warning for the same reason.
+   */
+  static const StaticWarningCode ASSIGNMENT_TO_FINAL = const StaticWarningCode(
+      'ASSIGNMENT_TO_FINAL', "'{0}' cannot be used as a setter, it is final");
+
+  /**
+   * 5 Variables: Attempting to assign to a final variable elsewhere will cause
+   * a NoSuchMethodError to be thrown, because no setter is defined for it. The
+   * assignment will also give rise to a static warning for the same reason.
+   */
+  static const StaticWarningCode ASSIGNMENT_TO_FINAL_NO_SETTER =
+      const StaticWarningCode('ASSIGNMENT_TO_FINAL_NO_SETTER',
+          "No setter named '{0}' in class '{1}'");
+
+  /**
+   * 12.18 Assignment: It is as static warning if an assignment of the form
+   * <i>v = e</i> occurs inside a top level or static function (be it function,
+   * method, getter, or setter) or variable initializer and there is neither a
+   * local variable declaration with name <i>v</i> nor setter declaration with
+   * name <i>v=</i> in the lexical scope enclosing the assignment.
+   */
+  static const StaticWarningCode ASSIGNMENT_TO_FUNCTION =
+      const StaticWarningCode(
+          'ASSIGNMENT_TO_FUNCTION', "Functions cannot be assigned a value");
+
+  /**
+   * 12.18 Assignment: Let <i>T</i> be the static type of <i>e<sub>1</sub></i>
+   * It is a static type warning if <i>T</i> does not have an accessible
+   * instance setter named <i>v=</i>.
+   */
+  static const StaticWarningCode ASSIGNMENT_TO_METHOD = const StaticWarningCode(
+      'ASSIGNMENT_TO_METHOD', "Methods cannot be assigned a value");
+
+  /**
+   * 12.18 Assignment: It is as static warning if an assignment of the form
+   * <i>v = e</i> occurs inside a top level or static function (be it function,
+   * method, getter, or setter) or variable initializer and there is neither a
+   * local variable declaration with name <i>v</i> nor setter declaration with
+   * name <i>v=</i> in the lexical scope enclosing the assignment.
+   */
+  static const StaticWarningCode ASSIGNMENT_TO_TYPE = const StaticWarningCode(
+      'ASSIGNMENT_TO_TYPE', "Types cannot be assigned a value");
+
+  /**
+   * 13.9 Switch: It is a static warning if the last statement of the statement
+   * sequence <i>s<sub>k</sub></i> is not a break, continue, return or throw
+   * statement.
+   */
+  static const StaticWarningCode CASE_BLOCK_NOT_TERMINATED =
+      const StaticWarningCode('CASE_BLOCK_NOT_TERMINATED',
+          "The last statement of the 'case' should be 'break', 'continue', 'return' or 'throw'");
+
+  /**
+   * 12.32 Type Cast: It is a static warning if <i>T</i> does not denote a type
+   * available in the current lexical scope.
+   */
+  static const StaticWarningCode CAST_TO_NON_TYPE = const StaticWarningCode(
+      'CAST_TO_NON_TYPE',
+      "The name '{0}' is not a type and cannot be used in an 'as' expression");
+
+  /**
+   * 7.4 Abstract Instance Members: It is a static warning if an abstract member
+   * is declared or inherited in a concrete class.
+   */
+  static const StaticWarningCode CONCRETE_CLASS_WITH_ABSTRACT_MEMBER =
+      const StaticWarningCode('CONCRETE_CLASS_WITH_ABSTRACT_MEMBER',
+          "'{0}' must have a method body because '{1}' is not abstract");
+
+  /**
+   * 14.1 Imports: If a name <i>N</i> is referenced by a library <i>L</i> and
+   * <i>N</i> would be introduced into the top level scope of <i>L</i> by an
+   * import from a library whose URI begins with <i>dart:</i> and an import from
+   * a library whose URI does not begin with <i>dart:</i>:
+   * * The import from <i>dart:</i> is implicitly extended by a hide N clause.
+   * * A static warning is issued.
+   *
+   * Parameters:
+   * 0: the ambiguous name
+   * 1: the name of the dart: library in which the element is found
+   * 1: the name of the non-dart: library in which the element is found
+   */
+  static const StaticWarningCode CONFLICTING_DART_IMPORT =
+      const StaticWarningCode('CONFLICTING_DART_IMPORT',
+          "Element '{0}' from SDK library '{1}' is implicitly hidden by '{2}'");
+
+  /**
+   * 7.2 Getters: It is a static warning if a class <i>C</i> declares an
+   * instance getter named <i>v</i> and an accessible static member named
+   * <i>v</i> or <i>v=</i> is declared in a superclass of <i>C</i>.
+   *
+   * Parameters:
+   * 0: the name of the super class declaring a static member
+   */
+  static const StaticWarningCode CONFLICTING_INSTANCE_GETTER_AND_SUPERCLASS_MEMBER =
+      const StaticWarningCode(
+          'CONFLICTING_INSTANCE_GETTER_AND_SUPERCLASS_MEMBER',
+          "Superclass '{0}' declares static member with the same name");
+
+  /**
+   * 7.1 Instance Methods: It is a static warning if a class <i>C</i> declares
+   * an instance method named <i>n</i> and has a setter named <i>n=</i>.
+   */
+  static const StaticWarningCode CONFLICTING_INSTANCE_METHOD_SETTER =
+      const StaticWarningCode('CONFLICTING_INSTANCE_METHOD_SETTER',
+          "Class '{0}' declares instance method '{1}', but also has a setter with the same name from '{2}'");
+
+  /**
+   * 7.1 Instance Methods: It is a static warning if a class <i>C</i> declares
+   * an instance method named <i>n</i> and has a setter named <i>n=</i>.
+   */
+  static const StaticWarningCode CONFLICTING_INSTANCE_METHOD_SETTER2 =
+      const StaticWarningCode('CONFLICTING_INSTANCE_METHOD_SETTER2',
+          "Class '{0}' declares the setter '{1}', but also has an instance method in the same class");
+
+  /**
+   * 7.3 Setters: It is a static warning if a class <i>C</i> declares an
+   * instance setter named <i>v=</i> and an accessible static member named
+   * <i>v=</i> or <i>v</i> is declared in a superclass of <i>C</i>.
+   *
+   * Parameters:
+   * 0: the name of the super class declaring a static member
+   */
+  static const StaticWarningCode CONFLICTING_INSTANCE_SETTER_AND_SUPERCLASS_MEMBER =
+      const StaticWarningCode(
+          'CONFLICTING_INSTANCE_SETTER_AND_SUPERCLASS_MEMBER',
+          "Superclass '{0}' declares static member with the same name");
+
+  /**
+   * 7.2 Getters: It is a static warning if a class declares a static getter
+   * named <i>v</i> and also has a non-static setter named <i>v=</i>.
+   */
+  static const StaticWarningCode CONFLICTING_STATIC_GETTER_AND_INSTANCE_SETTER =
+      const StaticWarningCode('CONFLICTING_STATIC_GETTER_AND_INSTANCE_SETTER',
+          "Class '{0}' declares non-static setter with the same name");
+
+  /**
+   * 7.3 Setters: It is a static warning if a class declares a static setter
+   * named <i>v=</i> and also has a non-static member named <i>v</i>.
+   */
+  static const StaticWarningCode CONFLICTING_STATIC_SETTER_AND_INSTANCE_MEMBER =
+      const StaticWarningCode('CONFLICTING_STATIC_SETTER_AND_INSTANCE_MEMBER',
+          "Class '{0}' declares non-static member with the same name");
+
+  /**
+   * 12.11.2 Const: Given an instance creation expression of the form <i>const
+   * q(a<sub>1</sub>, &hellip; a<sub>n</sub>)</i> it is a static warning if
+   * <i>q</i> is the constructor of an abstract class but <i>q</i> is not a
+   * factory constructor.
+   */
+  static const StaticWarningCode CONST_WITH_ABSTRACT_CLASS =
+      const StaticWarningCode('CONST_WITH_ABSTRACT_CLASS',
+          "Abstract classes cannot be created with a 'const' expression");
+
+  /**
+   * 12.7 Maps: It is a static warning if the values of any two keys in a map
+   * literal are equal.
+   */
+  static const StaticWarningCode EQUAL_KEYS_IN_MAP = const StaticWarningCode(
+      'EQUAL_KEYS_IN_MAP', "Keys in a map cannot be equal");
+
+  /**
+   * 14.2 Exports: It is a static warning to export two different libraries with
+   * the same name.
+   *
+   * Parameters:
+   * 0: the uri pointing to a first library
+   * 1: the uri pointing to a second library
+   * 2:e the shared name of the exported libraries
+   */
+  static const StaticWarningCode EXPORT_DUPLICATED_LIBRARY_NAMED =
+      const StaticWarningCode('EXPORT_DUPLICATED_LIBRARY_NAMED',
+          "The exported libraries '{0}' and '{1}' cannot have the same name '{2}'");
+
+  /**
+   * 14.2 Exports: It is a static warning to export two different libraries with
+   * the same name.
+   *
+   * Parameters:
+   * 0: the uri pointing to a first library
+   * 1: the uri pointing to a second library
+   */
+  static const StaticWarningCode EXPORT_DUPLICATED_LIBRARY_UNNAMED =
+      const StaticWarningCode('EXPORT_DUPLICATED_LIBRARY_UNNAMED',
+          "The exported libraries '{0}' and '{1}' cannot both be unnamed");
+
+  /**
+   * 12.14.2 Binding Actuals to Formals: It is a static warning if <i>m &lt;
+   * h</i> or if <i>m &gt; n</i>.
+   *
+   * Parameters:
+   * 0: the maximum number of positional arguments
+   * 1: the actual number of positional arguments given
+   *
+   * See [NOT_ENOUGH_REQUIRED_ARGUMENTS].
+   */
+  static const StaticWarningCode EXTRA_POSITIONAL_ARGUMENTS =
+      const StaticWarningCode('EXTRA_POSITIONAL_ARGUMENTS',
+          "{0} positional arguments expected, but {1} found");
+
+  /**
+   * 5. Variables: It is a static warning if a final instance variable that has
+   * been initialized at its point of declaration is also initialized in a
+   * constructor.
+   */
+  static const StaticWarningCode FIELD_INITIALIZED_IN_INITIALIZER_AND_DECLARATION =
+      const StaticWarningCode(
+          'FIELD_INITIALIZED_IN_INITIALIZER_AND_DECLARATION',
+          "Values cannot be set in the constructor if they are final, and have already been set");
+
+  /**
+   * 5. Variables: It is a static warning if a final instance variable that has
+   * been initialized at its point of declaration is also initialized in a
+   * constructor.
+   *
+   * Parameters:
+   * 0: the name of the field in question
+   */
+  static const StaticWarningCode FINAL_INITIALIZED_IN_DECLARATION_AND_CONSTRUCTOR =
+      const StaticWarningCode(
+          'FINAL_INITIALIZED_IN_DECLARATION_AND_CONSTRUCTOR',
+          "'{0}' is final and was given a value when it was declared, so it cannot be set to a new value");
+
+  /**
+   * 7.6.1 Generative Constructors: Execution of an initializer of the form
+   * <b>this</b>.<i>v</i> = <i>e</i> proceeds as follows: First, the expression
+   * <i>e</i> is evaluated to an object <i>o</i>. Then, the instance variable
+   * <i>v</i> of the object denoted by this is bound to <i>o</i>.
+   *
+   * 12.14.2 Binding Actuals to Formals: Let <i>T<sub>i</sub></i> be the static
+   * type of <i>a<sub>i</sub></i>, let <i>S<sub>i</sub></i> be the type of
+   * <i>p<sub>i</sub>, 1 &lt;= i &lt;= n+k</i> and let <i>S<sub>q</sub></i> be
+   * the type of the named parameter <i>q</i> of <i>f</i>. It is a static
+   * warning if <i>T<sub>j</sub></i> may not be assigned to <i>S<sub>j</sub>, 1
+   * &lt;= j &lt;= m</i>.
+   *
+   * Parameters:
+   * 0: the name of the type of the initializer expression
+   * 1: the name of the type of the field
+   */
+  static const StaticWarningCode FIELD_INITIALIZER_NOT_ASSIGNABLE =
+      const StaticWarningCode('FIELD_INITIALIZER_NOT_ASSIGNABLE',
+          "The initializer type '{0}' cannot be assigned to the field type '{1}'");
+
+  /**
+   * 7.6.1 Generative Constructors: An initializing formal has the form
+   * <i>this.id</i>. It is a static warning if the static type of <i>id</i> is
+   * not assignable to <i>T<sub>id</sub></i>.
+   *
+   * Parameters:
+   * 0: the name of the type of the field formal parameter
+   * 1: the name of the type of the field
+   */
+  static const StaticWarningCode FIELD_INITIALIZING_FORMAL_NOT_ASSIGNABLE =
+      const StaticWarningCode('FIELD_INITIALIZING_FORMAL_NOT_ASSIGNABLE',
+          "The parameter type '{0}' is incompatable with the field type '{1}'");
+
+  /**
+   * 5 Variables: It is a static warning if a library, static or local variable
+   * <i>v</i> is final and <i>v</i> is not initialized at its point of
+   * declaration.
+   *
+   * Parameters:
+   * 0: the name of the uninitialized final variable
+   */
+  static const StaticWarningCode FINAL_NOT_INITIALIZED =
+      const StaticWarningCode('FINAL_NOT_INITIALIZED',
+          "The final variable '{0}' must be initialized");
+
+  /**
+   * 7.6.1 Generative Constructors: Each final instance variable <i>f</i>
+   * declared in the immediately enclosing class must have an initializer in
+   * <i>k</i>'s initializer list unless it has already been initialized by one
+   * of the following means:
+   * * Initialization at the declaration of <i>f</i>.
+   * * Initialization by means of an initializing formal of <i>k</i>.
+   * or a static warning occurs.
+   *
+   * Parameters:
+   * 0: the name of the uninitialized final variable
+   */
+  static const StaticWarningCode FINAL_NOT_INITIALIZED_CONSTRUCTOR_1 =
+      const StaticWarningCode('FINAL_NOT_INITIALIZED_CONSTRUCTOR_1',
+          "The final variable '{0}' must be initialized");
+
+  /**
+   * 7.6.1 Generative Constructors: Each final instance variable <i>f</i>
+   * declared in the immediately enclosing class must have an initializer in
+   * <i>k</i>'s initializer list unless it has already been initialized by one
+   * of the following means:
+   * * Initialization at the declaration of <i>f</i>.
+   * * Initialization by means of an initializing formal of <i>k</i>.
+   * or a static warning occurs.
+   *
+   * Parameters:
+   * 0: the name of the uninitialized final variable
+   * 1: the name of the uninitialized final variable
+   */
+  static const StaticWarningCode FINAL_NOT_INITIALIZED_CONSTRUCTOR_2 =
+      const StaticWarningCode('FINAL_NOT_INITIALIZED_CONSTRUCTOR_2',
+          "The final variables '{0}' and '{1}' must be initialized");
+
+  /**
+   * 7.6.1 Generative Constructors: Each final instance variable <i>f</i>
+   * declared in the immediately enclosing class must have an initializer in
+   * <i>k</i>'s initializer list unless it has already been initialized by one
+   * of the following means:
+   * * Initialization at the declaration of <i>f</i>.
+   * * Initialization by means of an initializing formal of <i>k</i>.
+   * or a static warning occurs.
+   *
+   * Parameters:
+   * 0: the name of the uninitialized final variable
+   * 1: the name of the uninitialized final variable
+   * 2: the number of additional not initialized variables that aren't listed
+   */
+  static const StaticWarningCode FINAL_NOT_INITIALIZED_CONSTRUCTOR_3_PLUS =
+      const StaticWarningCode('FINAL_NOT_INITIALIZED_CONSTRUCTOR_3',
+          "The final variables '{0}', '{1}' and '{2}' more must be initialized");
+
+  /**
+   * 15.5 Function Types: It is a static warning if a concrete class implements
+   * Function and does not have a concrete method named call().
+   */
+  static const StaticWarningCode FUNCTION_WITHOUT_CALL = const StaticWarningCode(
+      'FUNCTION_WITHOUT_CALL',
+      "Concrete classes that implement Function must implement the method call()");
+
+  /**
+   * 14.1 Imports: It is a static warning to import two different libraries with
+   * the same name.
+   *
+   * Parameters:
+   * 0: the uri pointing to a first library
+   * 1: the uri pointing to a second library
+   * 2: the shared name of the imported libraries
+   */
+  static const StaticWarningCode IMPORT_DUPLICATED_LIBRARY_NAMED =
+      const StaticWarningCode('IMPORT_DUPLICATED_LIBRARY_NAMED',
+          "The imported libraries '{0}' and '{1}' cannot have the same name '{2}'");
+
+  /**
+   * 14.1 Imports: It is a static warning to import two different libraries with
+   * the same name.
+   *
+   * Parameters:
+   * 0: the uri pointing to a first library
+   * 1: the uri pointing to a second library
+   */
+  static const StaticWarningCode IMPORT_DUPLICATED_LIBRARY_UNNAMED =
+      const StaticWarningCode('IMPORT_DUPLICATED_LIBRARY_UNNAMED',
+          "The imported libraries '{0}' and '{1}' cannot both be unnamed");
+
+  /**
+   * 14.1 Imports: It is a static warning if the specified URI of a deferred
+   * import does not refer to a library declaration.
+   *
+   * Parameters:
+   * 0: the uri pointing to a non-library declaration
+   *
+   * See [CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY].
+   */
+  static const StaticWarningCode IMPORT_OF_NON_LIBRARY =
+      const StaticWarningCode('IMPORT_OF_NON_LIBRARY',
+          "The imported library '{0}' must not have a part-of directive");
+
+  /**
+   * 8.1.1 Inheritance and Overriding: However, if the above rules would cause
+   * multiple members <i>m<sub>1</sub>, &hellip;, m<sub>k</sub></i> with the
+   * same name <i>n</i> that would be inherited (because identically named
+   * members existed in several superinterfaces) then at most one member is
+   * inherited.
+   *
+   * If some but not all of the <i>m<sub>i</sub>, 1 &lt;= i &lt;= k</i> are
+   * getters none of the <i>m<sub>i</sub></i> are inherited, and a static
+   * warning is issued.
+   */
+  static const StaticWarningCode INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD =
+      const StaticWarningCode(
+          'INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD',
+          "'{0}' is inherited as a getter and also a method");
+
+  /**
+   * 7.1 Instance Methods: It is a static warning if a class <i>C</i> declares
+   * an instance method named <i>n</i> and an accessible static member named
+   * <i>n</i> is declared in a superclass of <i>C</i>.
+   *
+   * Parameters:
+   * 0: the name of the member with the name conflict
+   * 1: the name of the enclosing class that has the static member
+   */
+  static const StaticWarningCode INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC =
+      const StaticWarningCode(
+          'INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC',
+          "'{0}' collides with a static member in the superclass '{1}'");
+
+  /**
+   * 7.2 Getters: It is a static warning if a getter <i>m1</i> overrides a
+   * getter <i>m2</i> and the type of <i>m1</i> is not a subtype of the type of
+   * <i>m2</i>.
+   *
+   * Parameters:
+   * 0: the name of the actual return type
+   * 1: the name of the expected return type, not assignable to the actual
+   *    return type
+   * 2: the name of the class where the overridden getter is declared
+   *
+   * See [INVALID_METHOD_OVERRIDE_RETURN_TYPE].
+   */
+  static const StaticWarningCode INVALID_GETTER_OVERRIDE_RETURN_TYPE =
+      const StaticWarningCode('INVALID_GETTER_OVERRIDE_RETURN_TYPE',
+          "The return type '{0}' is not assignable to '{1}' as required by the getter it is overriding from '{2}'");
+
+  /**
+   * 7.1 Instance Methods: It is a static warning if an instance method
+   * <i>m1</i> overrides an instance method <i>m2</i> and the type of <i>m1</i>
+   * is not a subtype of the type of <i>m2</i>.
+   *
+   * Parameters:
+   * 0: the name of the actual parameter type
+   * 1: the name of the expected parameter type, not assignable to the actual
+   *    parameter type
+   * 2: the name of the class where the overridden method is declared
+   */
+  static const StaticWarningCode INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE =
+      const StaticWarningCode('INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE',
+          "The parameter type '{0}' is not assignable to '{1}' as required by the method it is overriding from '{2}'");
+
+  /**
+   * 7.1 Instance Methods: It is a static warning if an instance method
+   * <i>m1</i> overrides an instance method <i>m2</i> and the type of <i>m1</i>
+   * is not a subtype of the type of <i>m2</i>.
+   *
+   * Parameters:
+   * 0: the name of the actual parameter type
+   * 1: the name of the expected parameter type, not assignable to the actual
+   *    parameter type
+   * 2: the name of the class where the overridden method is declared
+   * See [INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE].
+   */
+  static const StaticWarningCode INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE =
+      const StaticWarningCode('INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE',
+          "The parameter type '{0}' is not assignable to '{1}' as required by the method it is overriding from '{2}'");
+
+  /**
+   * 7.1 Instance Methods: It is a static warning if an instance method
+   * <i>m1</i> overrides an instance method <i>m2</i> and the type of <i>m1</i>
+   * is not a subtype of the type of <i>m2</i>.
+   *
+   * Parameters:
+   * 0: the name of the actual parameter type
+   * 1: the name of the expected parameter type, not assignable to the actual
+   *    parameter type
+   * 2: the name of the class where the overridden method is declared
+   */
+  static const StaticWarningCode INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE =
+      const StaticWarningCode('INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE',
+          "The parameter type '{0}' is not assignable to '{1}' as required by the method it is overriding from '{2}'");
+
+  /**
+   * 7.1 Instance Methods: It is a static warning if an instance method
+   * <i>m1</i> overrides an instance method <i>m2</i> and the type of <i>m1</i>
+   * is not a subtype of the type of <i>m2</i>.
+   *
+   * Parameters:
+   * 0: the name of the actual return type
+   * 1: the name of the expected return type, not assignable to the actual
+   *    return type
+   * 2: the name of the class where the overridden method is declared
+   *
+   * See [INVALID_GETTER_OVERRIDE_RETURN_TYPE].
+   */
+  static const StaticWarningCode INVALID_METHOD_OVERRIDE_RETURN_TYPE =
+      const StaticWarningCode('INVALID_METHOD_OVERRIDE_RETURN_TYPE',
+          "The return type '{0}' is not assignable to '{1}' as required by the method it is overriding from '{2}'");
+
+  /**
+   * 7.1 Instance Methods: It is a static warning if an instance method
+   * <i>m1</i> overrides an instance member <i>m2</i>, the signature of
+   * <i>m2</i> explicitly specifies a default value for a formal parameter
+   * <i>p</i> and the signature of <i>m1</i> specifies a different default value
+   * for <i>p</i>.
+   */
+  static const StaticWarningCode INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES_NAMED =
+      const StaticWarningCode('INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES_NAMED',
+          "Parameters cannot override default values, this method overrides '{0}.{1}' where '{2}' has a different value");
+
+  /**
+   * 7.1 Instance Methods: It is a static warning if an instance method
+   * <i>m1</i> overrides an instance member <i>m2</i>, the signature of
+   * <i>m2</i> explicitly specifies a default value for a formal parameter
+   * <i>p</i> and the signature of <i>m1</i> specifies a different default value
+   * for <i>p</i>.
+   */
+  static const StaticWarningCode INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES_POSITIONAL =
+      const StaticWarningCode(
+          'INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES_POSITIONAL',
+          "Parameters cannot override default values, this method overrides '{0}.{1}' where this positional parameter has a different value");
+
+  /**
+   * 7.1 Instance Methods: It is a static warning if an instance method
+   * <i>m1</i> overrides an instance member <i>m2</i> and <i>m1</i> does not
+   * declare all the named parameters declared by <i>m2</i>.
+   *
+   * Parameters:
+   * 0: the number of named parameters in the overridden member
+   * 1: the name of the class from the overridden method
+   */
+  static const StaticWarningCode INVALID_OVERRIDE_NAMED = const StaticWarningCode(
+      'INVALID_OVERRIDE_NAMED',
+      "Missing the named parameter '{0}' to match the overridden method from '{1}'");
+
+  /**
+   * 7.1 Instance Methods: It is a static warning if an instance method
+   * <i>m1</i> overrides an instance member <i>m2</i> and <i>m1</i> has fewer
+   * positional parameters than <i>m2</i>.
+   *
+   * Parameters:
+   * 0: the number of positional parameters in the overridden member
+   * 1: the name of the class from the overridden method
+   */
+  static const StaticWarningCode INVALID_OVERRIDE_POSITIONAL =
+      const StaticWarningCode('INVALID_OVERRIDE_POSITIONAL',
+          "Must have at least {0} parameters to match the overridden method from '{1}'");
+
+  /**
+   * 7.1 Instance Methods: It is a static warning if an instance method
+   * <i>m1</i> overrides an instance member <i>m2</i> and <i>m1</i> has a
+   * greater number of required parameters than <i>m2</i>.
+   *
+   * Parameters:
+   * 0: the number of required parameters in the overridden member
+   * 1: the name of the class from the overridden method
+   */
+  static const StaticWarningCode INVALID_OVERRIDE_REQUIRED =
+      const StaticWarningCode('INVALID_OVERRIDE_REQUIRED',
+          "Must have {0} required parameters or less to match the overridden method from '{1}'");
+
+  /**
+   * 7.3 Setters: It is a static warning if a setter <i>m1</i> overrides a
+   * setter <i>m2</i> and the type of <i>m1</i> is not a subtype of the type of
+   * <i>m2</i>.
+   *
+   * Parameters:
+   * 0: the name of the actual parameter type
+   * 1: the name of the expected parameter type, not assignable to the actual
+   * parameter type
+   * 2: the name of the class where the overridden setter is declared
+   *
+   * See [INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE].
+   */
+  static const StaticWarningCode INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE =
+      const StaticWarningCode('INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE',
+          "The parameter type '{0}' is not assignable to '{1}' as required by the setter it is overriding from '{2}'");
+
+  /**
+   * 12.6 Lists: A run-time list literal &lt;<i>E</i>&gt; [<i>e<sub>1</sub></i>
+   * &hellip; <i>e<sub>n</sub></i>] is evaluated as follows:
+   * * The operator []= is invoked on <i>a</i> with first argument <i>i</i> and
+   *   second argument <i>o<sub>i+1</sub></i><i>, 1 &lt;= i &lt;= n</i>
+   *
+   * 12.14.2 Binding Actuals to Formals: Let <i>T<sub>i</sub></i> be the static
+   * type of <i>a<sub>i</sub></i>, let <i>S<sub>i</sub></i> be the type of
+   * <i>p<sub>i</sub>, 1 &lt;= i &lt;= n+k</i> and let <i>S<sub>q</sub></i> be
+   * the type of the named parameter <i>q</i> of <i>f</i>. It is a static
+   * warning if <i>T<sub>j</sub></i> may not be assigned to <i>S<sub>j</sub>, 1
+   * &lt;= j &lt;= m</i>.
+   */
+  static const StaticWarningCode LIST_ELEMENT_TYPE_NOT_ASSIGNABLE =
+      const StaticWarningCode('LIST_ELEMENT_TYPE_NOT_ASSIGNABLE',
+          "The element type '{0}' cannot be assigned to the list type '{1}'");
+
+  /**
+   * 12.7 Map: A run-time map literal &lt;<i>K</i>, <i>V</i>&gt;
+   * [<i>k<sub>1</sub></i> : <i>e<sub>1</sub></i> &hellip; <i>k<sub>n</sub></i>
+   * : <i>e<sub>n</sub></i>] is evaluated as follows:
+   * * The operator []= is invoked on <i>m</i> with first argument
+   *   <i>k<sub>i</sub></i> and second argument <i>e<sub>i</sub></i><i>, 1 &lt;=
+   *   i &lt;= n</i>
+   *
+   * 12.14.2 Binding Actuals to Formals: Let <i>T<sub>i</sub></i> be the static
+   * type of <i>a<sub>i</sub></i>, let <i>S<sub>i</sub></i> be the type of
+   * <i>p<sub>i</sub>, 1 &lt;= i &lt;= n+k</i> and let <i>S<sub>q</sub></i> be
+   * the type of the named parameter <i>q</i> of <i>f</i>. It is a static
+   * warning if <i>T<sub>j</sub></i> may not be assigned to <i>S<sub>j</sub>, 1
+   * &lt;= j &lt;= m</i>.
+   */
+  static const StaticWarningCode MAP_KEY_TYPE_NOT_ASSIGNABLE =
+      const StaticWarningCode('MAP_KEY_TYPE_NOT_ASSIGNABLE',
+          "The element type '{0}' cannot be assigned to the map key type '{1}'");
+
+  /**
+   * 12.7 Map: A run-time map literal &lt;<i>K</i>, <i>V</i>&gt;
+   * [<i>k<sub>1</sub></i> : <i>e<sub>1</sub></i> &hellip; <i>k<sub>n</sub></i>
+   * : <i>e<sub>n</sub></i>] is evaluated as follows:
+   * * The operator []= is invoked on <i>m</i> with first argument
+   *   <i>k<sub>i</sub></i> and second argument <i>e<sub>i</sub></i><i>, 1 &lt;=
+   *   i &lt;= n</i>
+   *
+   * 12.14.2 Binding Actuals to Formals: Let <i>T<sub>i</sub></i> be the static
+   * type of <i>a<sub>i</sub></i>, let <i>S<sub>i</sub></i> be the type of
+   * <i>p<sub>i</sub>, 1 &lt;= i &lt;= n+k</i> and let <i>S<sub>q</sub></i> be
+   * the type of the named parameter <i>q</i> of <i>f</i>. It is a static
+   * warning if <i>T<sub>j</sub></i> may not be assigned to <i>S<sub>j</sub>, 1
+   * &lt;= j &lt;= m</i>.
+   */
+  static const StaticWarningCode MAP_VALUE_TYPE_NOT_ASSIGNABLE =
+      const StaticWarningCode('MAP_VALUE_TYPE_NOT_ASSIGNABLE',
+          "The element type '{0}' cannot be assigned to the map value type '{1}'");
+
+  /**
+   * 7.3 Setters: It is a static warning if a class has a setter named <i>v=</i>
+   * with argument type <i>T</i> and a getter named <i>v</i> with return type
+   * <i>S</i>, and <i>T</i> may not be assigned to <i>S</i>.
+   */
+  static const StaticWarningCode MISMATCHED_GETTER_AND_SETTER_TYPES =
+      const StaticWarningCode('MISMATCHED_GETTER_AND_SETTER_TYPES',
+          "The parameter type for setter '{0}' is '{1}' which is not assignable to its getter (of type '{2}')");
+
+  /**
+   * 7.3 Setters: It is a static warning if a class has a setter named <i>v=</i>
+   * with argument type <i>T</i> and a getter named <i>v</i> with return type
+   * <i>S</i>, and <i>T</i> may not be assigned to <i>S</i>.
+   */
+  static const StaticWarningCode MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE =
+      const StaticWarningCode(
+          'MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE',
+          "The parameter type for setter '{0}' is '{1}' which is not assignable to its getter (of type '{2}'), from superclass '{3}'");
+
+  /**
+   * 13.12 Return: It is a static warning if a function contains both one or
+   * more return statements of the form <i>return;</i> and one or more return
+   * statements of the form <i>return e;</i>.
+   */
+  static const StaticWarningCode MIXED_RETURN_TYPES = const StaticWarningCode(
+      'MIXED_RETURN_TYPES',
+      "Methods and functions cannot use return both with and without values");
+
+  /**
+   * 12.11.1 New: It is a static warning if <i>q</i> is a constructor of an
+   * abstract class and <i>q</i> is not a factory constructor.
+   */
+  static const StaticWarningCode NEW_WITH_ABSTRACT_CLASS =
+      const StaticWarningCode('NEW_WITH_ABSTRACT_CLASS',
+          "Abstract classes cannot be created with a 'new' expression");
+
+  /**
+   * 15.8 Parameterized Types: Any use of a malbounded type gives rise to a
+   * static warning.
+   *
+   * Parameters:
+   * 0: the name of the type being referenced (<i>S</i>)
+   * 1: the number of type parameters that were declared
+   * 2: the number of type arguments provided
+   *
+   * See [CompileTimeErrorCode.CONST_WITH_INVALID_TYPE_PARAMETERS], and
+   * [StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS].
+   */
+  static const StaticWarningCode NEW_WITH_INVALID_TYPE_PARAMETERS =
+      const StaticWarningCode('NEW_WITH_INVALID_TYPE_PARAMETERS',
+          "The type '{0}' is declared with {1} type parameters, but {2} type arguments were given");
+
+  /**
+   * 12.11.1 New: It is a static warning if <i>T</i> is not a class accessible
+   * in the current scope, optionally followed by type arguments.
+   *
+   * Parameters:
+   * 0: the name of the non-type element
+   */
+  static const StaticWarningCode NEW_WITH_NON_TYPE = const StaticWarningCode(
+      'NEW_WITH_NON_TYPE', "The name '{0}' is not a class");
+
+  /**
+   * 12.11.1 New: If <i>T</i> is a class or parameterized type accessible in the
+   * current scope then:
+   * 1. If <i>e</i> is of the form <i>new T.id(a<sub>1</sub>, &hellip;,
+   *    a<sub>n</sub>, x<sub>n+1</sub>: a<sub>n+1</sub>, &hellip;,
+   *    x<sub>n+k</sub>: a<sub>n+k</sub>)</i> it is a static warning if
+   *    <i>T.id</i> is not the name of a constructor declared by the type
+   *    <i>T</i>.
+   * If <i>e</i> of the form <i>new T(a<sub>1</sub>, &hellip;, a<sub>n</sub>,
+   * x<sub>n+1</sub>: a<sub>n+1</sub>, &hellip;, x<sub>n+k</sub>:
+   * a<sub>n+kM/sub>)</i> it is a static warning if the type <i>T</i> does not
+   * declare a constructor with the same name as the declaration of <i>T</i>.
+   */
+  static const StaticWarningCode NEW_WITH_UNDEFINED_CONSTRUCTOR =
+      const StaticWarningCode('NEW_WITH_UNDEFINED_CONSTRUCTOR',
+          "The class '{0}' does not have a constructor '{1}'");
+
+  /**
+   * 12.11.1 New: If <i>T</i> is a class or parameterized type accessible in the
+   * current scope then:
+   * 1. If <i>e</i> is of the form <i>new T.id(a<sub>1</sub>, &hellip;,
+   * a<sub>n</sub>, x<sub>n+1</sub>: a<sub>n+1</sub>, &hellip;, x<sub>n+k</sub>:
+   * a<sub>n+k</sub>)</i> it is a static warning if <i>T.id</i> is not the name
+   * of a constructor declared by the type <i>T</i>. If <i>e</i> of the form
+   * <i>new T(a<sub>1</sub>, &hellip;, a<sub>n</sub>, x<sub>n+1</sub>:
+   * a<sub>n+1</sub>, &hellip;, x<sub>n+k</sub>: a<sub>n+kM/sub>)</i> it is a
+   * static warning if the type <i>T</i> does not declare a constructor with the
+   * same name as the declaration of <i>T</i>.
+   */
+  static const StaticWarningCode NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT =
+      const StaticWarningCode('NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT',
+          "The class '{0}' does not have a default constructor");
+
+  /**
+   * 7.9.1 Inheritance and Overriding: It is a static warning if a non-abstract
+   * class inherits an abstract method.
+   *
+   * 7.10 Superinterfaces: Let <i>C</i> be a concrete class that does not
+   * declare its own <i>noSuchMethod()</i> method. It is a static warning if the
+   * implicit interface of <i>C</i> includes an instance member <i>m</i> of type
+   * <i>F</i> and <i>C</i> does not declare or inherit a corresponding instance
+   * member <i>m</i> of type <i>F'</i> such that <i>F' <: F</i>.
+   *
+   * 7.4 Abstract Instance Members: It is a static warning if an abstract member
+   * is declared or inherited in a concrete class unless that member overrides a
+   * concrete one.
+   *
+   * Parameters:
+   * 0: the name of the first member
+   * 1: the name of the second member
+   * 2: the name of the third member
+   * 3: the name of the fourth member
+   * 4: the number of additional missing members that aren't listed
+   */
+  static const StaticWarningCode NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_PLUS =
+      const StaticWarningCode(
+          'NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_PLUS',
+          "Missing concrete implementation of {0}, {1}, {2}, {3} and {4} more");
+
+  /**
+   * 7.9.1 Inheritance and Overriding: It is a static warning if a non-abstract
+   * class inherits an abstract method.
+   *
+   * 7.10 Superinterfaces: Let <i>C</i> be a concrete class that does not
+   * declare its own <i>noSuchMethod()</i> method. It is a static warning if the
+   * implicit interface of <i>C</i> includes an instance member <i>m</i> of type
+   * <i>F</i> and <i>C</i> does not declare or inherit a corresponding instance
+   * member <i>m</i> of type <i>F'</i> such that <i>F' <: F</i>.
+   *
+   * 7.4 Abstract Instance Members: It is a static warning if an abstract member
+   * is declared or inherited in a concrete class unless that member overrides a
+   * concrete one.
+   *
+   * Parameters:
+   * 0: the name of the first member
+   * 1: the name of the second member
+   * 2: the name of the third member
+   * 3: the name of the fourth member
+   */
+  static const StaticWarningCode NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR =
+      const StaticWarningCode(
+          'NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR',
+          "Missing concrete implementation of {0}, {1}, {2} and {3}");
+
+  /**
+   * 7.9.1 Inheritance and Overriding: It is a static warning if a non-abstract
+   * class inherits an abstract method.
+   *
+   * 7.10 Superinterfaces: Let <i>C</i> be a concrete class that does not
+   * declare its own <i>noSuchMethod()</i> method. It is a static warning if the
+   * implicit interface of <i>C</i> includes an instance member <i>m</i> of type
+   * <i>F</i> and <i>C</i> does not declare or inherit a corresponding instance
+   * member <i>m</i> of type <i>F'</i> such that <i>F' <: F</i>.
+   *
+   * 7.4 Abstract Instance Members: It is a static warning if an abstract member
+   * is declared or inherited in a concrete class unless that member overrides a
+   * concrete one.
+   *
+   * Parameters:
+   * 0: the name of the member
+   */
+  static const StaticWarningCode NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE =
+      const StaticWarningCode('NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE',
+          "Missing concrete implementation of {0}");
+
+  /**
+   * 7.9.1 Inheritance and Overriding: It is a static warning if a non-abstract
+   * class inherits an abstract method.
+   *
+   * 7.10 Superinterfaces: Let <i>C</i> be a concrete class that does not
+   * declare its own <i>noSuchMethod()</i> method. It is a static warning if the
+   * implicit interface of <i>C</i> includes an instance member <i>m</i> of type
+   * <i>F</i> and <i>C</i> does not declare or inherit a corresponding instance
+   * member <i>m</i> of type <i>F'</i> such that <i>F' <: F</i>.
+   *
+   * 7.4 Abstract Instance Members: It is a static warning if an abstract member
+   * is declared or inherited in a concrete class unless that member overrides a
+   * concrete one.
+   *
+   * Parameters:
+   * 0: the name of the first member
+   * 1: the name of the second member
+   * 2: the name of the third member
+   */
+  static const StaticWarningCode NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_THREE =
+      const StaticWarningCode(
+          'NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_THREE',
+          "Missing concrete implementation of {0}, {1} and {2}");
+
+  /**
+   * 7.9.1 Inheritance and Overriding: It is a static warning if a non-abstract
+   * class inherits an abstract method.
+   *
+   * 7.10 Superinterfaces: Let <i>C</i> be a concrete class that does not
+   * declare its own <i>noSuchMethod()</i> method. It is a static warning if the
+   * implicit interface of <i>C</i> includes an instance member <i>m</i> of type
+   * <i>F</i> and <i>C</i> does not declare or inherit a corresponding instance
+   * member <i>m</i> of type <i>F'</i> such that <i>F' <: F</i>.
+   *
+   * 7.4 Abstract Instance Members: It is a static warning if an abstract member
+   * is declared or inherited in a concrete class unless that member overrides a
+   * concrete one.
+   *
+   * Parameters:
+   * 0: the name of the first member
+   * 1: the name of the second member
+   */
+  static const StaticWarningCode NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_TWO =
+      const StaticWarningCode('NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_TWO',
+          "Missing concrete implementation of {0} and {1}");
+
+  /**
+   * 13.11 Try: An on-catch clause of the form <i>on T catch (p<sub>1</sub>,
+   * p<sub>2</sub>) s</i> or <i>on T s</i> matches an object <i>o</i> if the
+   * type of <i>o</i> is a subtype of <i>T</i>. It is a static warning if
+   * <i>T</i> does not denote a type available in the lexical scope of the
+   * catch clause.
+   *
+   * Parameters:
+   * 0: the name of the non-type element
+   */
+  static const StaticWarningCode NON_TYPE_IN_CATCH_CLAUSE =
+      const StaticWarningCode('NON_TYPE_IN_CATCH_CLAUSE',
+          "The name '{0}' is not a type and cannot be used in an on-catch clause");
+
+  /**
+   * 7.1.1 Operators: It is a static warning if the return type of the
+   * user-declared operator []= is explicitly declared and not void.
+   */
+  static const StaticWarningCode NON_VOID_RETURN_FOR_OPERATOR =
+      const StaticWarningCode('NON_VOID_RETURN_FOR_OPERATOR',
+          "The return type of the operator []= must be 'void'");
+
+  /**
+   * 7.3 Setters: It is a static warning if a setter declares a return type
+   * other than void.
+   */
+  static const StaticWarningCode NON_VOID_RETURN_FOR_SETTER =
+      const StaticWarningCode('NON_VOID_RETURN_FOR_SETTER',
+          "The return type of the setter must be 'void'");
+
+  /**
+   * 15.1 Static Types: A type <i>T</i> is malformed iff:
+   * * <i>T</i> has the form <i>id</i> or the form <i>prefix.id</i>, and in the
+   *   enclosing lexical scope, the name <i>id</i> (respectively
+   *   <i>prefix.id</i>) does not denote a type.
+   * * <i>T</i> denotes a type parameter in the enclosing lexical scope, but
+   * occurs in the signature or body of a static member.
+   * * <i>T</i> is a parameterized type of the form <i>G&lt;S<sub>1</sub>, ..,
+   * S<sub>n</sub>&gt;</i>,
+   *
+   * Any use of a malformed type gives rise to a static warning.
+   *
+   * Parameters:
+   * 0: the name that is not a type
+   */
+  static const StaticWarningCode NOT_A_TYPE =
+      const StaticWarningCode('NOT_A_TYPE', "{0} is not a type");
+
+  /**
+   * 12.14.2 Binding Actuals to Formals: It is a static warning if <i>m &lt;
+   * h</i> or if <i>m &gt; n</i>.
+   *
+   * Parameters:
+   * 0: the expected number of required arguments
+   * 1: the actual number of positional arguments given
+   *
+   * See [EXTRA_POSITIONAL_ARGUMENTS].
+   */
+  static const StaticWarningCode NOT_ENOUGH_REQUIRED_ARGUMENTS =
+      const StaticWarningCode('NOT_ENOUGH_REQUIRED_ARGUMENTS',
+          "{0} required argument(s) expected, but {1} found");
+
+  /**
+   * 14.3 Parts: It is a static warning if the referenced part declaration
+   * <i>p</i> names a library other than the current library as the library to
+   * which <i>p</i> belongs.
+   *
+   * Parameters:
+   * 0: the name of expected library name
+   * 1: the non-matching actual library name from the "part of" declaration
+   */
+  static const StaticWarningCode PART_OF_DIFFERENT_LIBRARY =
+      const StaticWarningCode('PART_OF_DIFFERENT_LIBRARY',
+          "Expected this library to be part of '{0}', not '{1}'");
+
+  /**
+   * 7.6.2 Factories: It is a static warning if the function type of <i>k'</i>
+   * is not a subtype of the type of <i>k</i>.
+   *
+   * Parameters:
+   * 0: the name of the redirected constructor
+   * 1: the name of the redirecting constructor
+   */
+  static const StaticWarningCode REDIRECT_TO_INVALID_FUNCTION_TYPE =
+      const StaticWarningCode('REDIRECT_TO_INVALID_FUNCTION_TYPE',
+          "The redirected constructor '{0}' has incompatible parameters with '{1}'");
+
+  /**
+   * 7.6.2 Factories: It is a static warning if the function type of <i>k'</i>
+   * is not a subtype of the type of <i>k</i>.
+   *
+   * Parameters:
+   * 0: the name of the redirected constructor return type
+   * 1: the name of the redirecting constructor return type
+   */
+  static const StaticWarningCode REDIRECT_TO_INVALID_RETURN_TYPE =
+      const StaticWarningCode('REDIRECT_TO_INVALID_RETURN_TYPE',
+          "The return type '{0}' of the redirected constructor is not assignable to '{1}'");
+
+  /**
+   * 7.6.2 Factories: It is a static warning if type does not denote a class
+   * accessible in the current scope; if type does denote such a class <i>C</i>
+   * it is a static warning if the referenced constructor (be it <i>type</i> or
+   * <i>type.id</i>) is not a constructor of <i>C</i>.
+   */
+  static const StaticWarningCode REDIRECT_TO_MISSING_CONSTRUCTOR =
+      const StaticWarningCode('REDIRECT_TO_MISSING_CONSTRUCTOR',
+          "The constructor '{0}' could not be found in '{1}'");
+
+  /**
+   * 7.6.2 Factories: It is a static warning if type does not denote a class
+   * accessible in the current scope; if type does denote such a class <i>C</i>
+   * it is a static warning if the referenced constructor (be it <i>type</i> or
+   * <i>type.id</i>) is not a constructor of <i>C</i>.
+   */
+  static const StaticWarningCode REDIRECT_TO_NON_CLASS = const StaticWarningCode(
+      'REDIRECT_TO_NON_CLASS',
+      "The name '{0}' is not a type and cannot be used in a redirected constructor");
+
+  /**
+   * 13.12 Return: Let <i>f</i> be the function immediately enclosing a return
+   * statement of the form <i>return;</i> It is a static warning if both of the
+   * following conditions hold:
+   * * <i>f</i> is not a generative constructor.
+   * * The return type of <i>f</i> may not be assigned to void.
+   */
+  static const StaticWarningCode RETURN_WITHOUT_VALUE = const StaticWarningCode(
+      'RETURN_WITHOUT_VALUE', "Missing return value after 'return'");
+
+  /**
+   * 12.16.3 Static Invocation: It is a static warning if <i>C</i> does not
+   * declare a static method or getter <i>m</i>.
+   *
+   * Parameters:
+   * 0: the name of the instance member
+   */
+  static const StaticWarningCode STATIC_ACCESS_TO_INSTANCE_MEMBER =
+      const StaticWarningCode('STATIC_ACCESS_TO_INSTANCE_MEMBER',
+          "Instance member '{0}' cannot be accessed using static access");
+
+  /**
+   * 13.9 Switch: It is a static warning if the type of <i>e</i> may not be
+   * assigned to the type of <i>e<sub>k</sub></i>.
+   */
+  static const StaticWarningCode SWITCH_EXPRESSION_NOT_ASSIGNABLE =
+      const StaticWarningCode('SWITCH_EXPRESSION_NOT_ASSIGNABLE',
+          "Type '{0}' of the switch expression is not assignable to the type '{1}' of case expressions");
+
+  /**
+   * 15.1 Static Types: It is a static warning to use a deferred type in a type
+   * annotation.
+   *
+   * Parameters:
+   * 0: the name of the type that is deferred and being used in a type
+   *    annotation
+   */
+  static const StaticWarningCode TYPE_ANNOTATION_DEFERRED_CLASS =
+      const StaticWarningCode('TYPE_ANNOTATION_DEFERRED_CLASS',
+          "The deferred type '{0}' cannot be used in a declaration, cast or type test");
+
+  /**
+   * 12.31 Type Test: It is a static warning if <i>T</i> does not denote a type
+   * available in the current lexical scope.
+   */
+  static const StaticWarningCode TYPE_TEST_WITH_NON_TYPE = const StaticWarningCode(
+      'TYPE_TEST_WITH_NON_TYPE',
+      "The name '{0}' is not a type and cannot be used in an 'is' expression");
+
+  /**
+   * 12.31 Type Test: It is a static warning if <i>T</i> does not denote a type
+   * available in the current lexical scope.
+   */
+  static const StaticWarningCode TYPE_TEST_WITH_UNDEFINED_NAME =
+      const StaticWarningCode('TYPE_TEST_WITH_UNDEFINED_NAME',
+          "The name '{0}' is not defined and cannot be used in an 'is' expression");
+
+  /**
+   * 10 Generics: However, a type parameter is considered to be a malformed type
+   * when referenced by a static member.
+   *
+   * 15.1 Static Types: Any use of a malformed type gives rise to a static
+   * warning. A malformed type is then interpreted as dynamic by the static type
+   * checker and the runtime.
+   */
+  static const StaticWarningCode TYPE_PARAMETER_REFERENCED_BY_STATIC =
+      const StaticWarningCode('TYPE_PARAMETER_REFERENCED_BY_STATIC',
+          "Static members cannot reference type parameters");
+
+  /**
+   * 12.16.3 Static Invocation: A static method invocation <i>i</i> has the form
+   * <i>C.m(a<sub>1</sub>, &hellip;, a<sub>n</sub>, x<sub>n+1</sub>:
+   * a<sub>n+1</sub>, &hellip; x<sub>n+k</sub>: a<sub>n+k</sub>)</i>. It is a
+   * static warning if <i>C</i> does not denote a class in the current scope.
+   *
+   * Parameters:
+   * 0: the name of the undefined class
+   */
+  static const StaticWarningCode UNDEFINED_CLASS =
+      const StaticWarningCode('UNDEFINED_CLASS', "Undefined class '{0}'");
+
+  /**
+   * Same as [UNDEFINED_CLASS], but to catch using "boolean" instead of "bool".
+   */
+  static const StaticWarningCode UNDEFINED_CLASS_BOOLEAN =
+      const StaticWarningCode('UNDEFINED_CLASS_BOOLEAN',
+          "Undefined class 'boolean'; did you mean 'bool'?");
+
+  /**
+   * 12.17 Getter Invocation: It is a static warning if there is no class
+   * <i>C</i> in the enclosing lexical scope of <i>i</i>, or if <i>C</i> does
+   * not declare, implicitly or explicitly, a getter named <i>m</i>.
+   *
+   * Parameters:
+   * 0: the name of the getter
+   * 1: the name of the enclosing type where the getter is being looked for
+   */
+  static const StaticWarningCode UNDEFINED_GETTER = const StaticWarningCode(
+      'UNDEFINED_GETTER',
+      "The getter '{0}' is not defined for the class '{1}'");
+
+  /**
+   * 12.30 Identifier Reference: It is as static warning if an identifier
+   * expression of the form <i>id</i> occurs inside a top level or static
+   * function (be it function, method, getter, or setter) or variable
+   * initializer and there is no declaration <i>d</i> with name <i>id</i> in the
+   * lexical scope enclosing the expression.
+   *
+   * Parameters:
+   * 0: the name of the identifier
+   */
+  static const StaticWarningCode UNDEFINED_IDENTIFIER =
+      const StaticWarningCode('UNDEFINED_IDENTIFIER', "Undefined name '{0}'");
+
+  /**
+   * 12.14.2 Binding Actuals to Formals: Furthermore, each <i>q<sub>i</sub></i>,
+   * <i>1<=i<=l</i>, must have a corresponding named parameter in the set
+   * {<i>p<sub>n+1</sub></i> &hellip; <i>p<sub>n+k</sub></i>} or a static
+   * warning occurs.
+   *
+   * Parameters:
+   * 0: the name of the requested named parameter
+   */
+  static const StaticWarningCode UNDEFINED_NAMED_PARAMETER =
+      const StaticWarningCode('UNDEFINED_NAMED_PARAMETER',
+          "The named parameter '{0}' is not defined");
+
+  /**
+   * 12.18 Assignment: It is as static warning if an assignment of the form
+   * <i>v = e</i> occurs inside a top level or static function (be it function,
+   * method, getter, or setter) or variable initializer and there is no
+   * declaration <i>d</i> with name <i>v=</i> in the lexical scope enclosing the
+   * assignment.
+   *
+   * 12.18 Assignment: It is a static warning if there is no class <i>C</i> in
+   * the enclosing lexical scope of the assignment, or if <i>C</i> does not
+   * declare, implicitly or explicitly, a setter <i>v=</i>.
+   *
+   * Parameters:
+   * 0: the name of the getter
+   * 1: the name of the enclosing type where the setter is being looked for
+   */
+  static const StaticWarningCode UNDEFINED_SETTER = const StaticWarningCode(
+      'UNDEFINED_SETTER',
+      "The setter '{0}' is not defined for the class '{1}'");
+
+  /**
+   * 12.16.3 Static Invocation: It is a static warning if <i>C</i> does not
+   * declare a static method or getter <i>m</i>.
+   *
+   * Parameters:
+   * 0: the name of the method
+   * 1: the name of the enclosing type where the method is being looked for
+   */
+  static const StaticWarningCode UNDEFINED_STATIC_METHOD_OR_GETTER =
+      const StaticWarningCode('UNDEFINED_STATIC_METHOD_OR_GETTER',
+          "The static method, getter or setter '{0}' is not defined for the class '{1}'");
+
+  /**
+   * 12.17 Getter Invocation: It is a static warning if there is no class
+   * <i>C</i> in the enclosing lexical scope of <i>i</i>, or if <i>C</i> does
+   * not declare, implicitly or explicitly, a getter named <i>m</i>.
+   *
+   * Parameters:
+   * 0: the name of the getter
+   * 1: the name of the enclosing type where the getter is being looked for
+   */
+  static const StaticWarningCode UNDEFINED_SUPER_GETTER =
+      const StaticWarningCode('UNDEFINED_SUPER_GETTER',
+          "The getter '{0}' is not defined in a superclass of '{1}'");
+
+  /**
+   * 12.18 Assignment: It is as static warning if an assignment of the form
+   * <i>v = e</i> occurs inside a top level or static function (be it function,
+   * method, getter, or setter) or variable initializer and there is no
+   * declaration <i>d</i> with name <i>v=</i> in the lexical scope enclosing the
+   * assignment.
+   *
+   * 12.18 Assignment: It is a static warning if there is no class <i>C</i> in
+   * the enclosing lexical scope of the assignment, or if <i>C</i> does not
+   * declare, implicitly or explicitly, a setter <i>v=</i>.
+   *
+   * Parameters:
+   * 0: the name of the getter
+   * 1: the name of the enclosing type where the setter is being looked for
+   */
+  static const StaticWarningCode UNDEFINED_SUPER_SETTER =
+      const StaticWarningCode('UNDEFINED_SUPER_SETTER',
+          "The setter '{0}' is not defined in a superclass of '{1}'");
+
+  /**
+   * 7.2 Getters: It is a static warning if the return type of a getter is void.
+   */
+  static const StaticWarningCode VOID_RETURN_FOR_GETTER =
+      const StaticWarningCode('VOID_RETURN_FOR_GETTER',
+          "The return type of the getter must not be 'void'");
+
+  /**
+   * Initialize a newly created error code to have the given [name]. The message
+   * associated with the error will be created from the given [message]
+   * template. The correction associated with the error will be created from the
+   * given [correction] template.
+   */
+  const StaticWarningCode(String name, String message, [String correction])
+      : super(name, message, correction);
+
+  @override
+  ErrorSeverity get errorSeverity => ErrorType.STATIC_WARNING.severity;
+
+  @override
+  ErrorType get type => ErrorType.STATIC_WARNING;
+}
+
+/**
+ * The error code indicating a marker in code for work that needs to be finished
+ * or revisited.
+ */
+class TodoCode extends ErrorCode {
+  /**
+   * The single enum of TodoCode.
+   */
+  static const TodoCode TODO = const TodoCode('TODO');
+
+  /**
+   * This matches the two common Dart task styles
+   *
+   * * TODO:
+   * * TODO(username):
+   *
+   * As well as
+   * * TODO
+   *
+   * But not
+   * * todo
+   * * TODOS
+   */
+  static RegExp TODO_REGEX =
+      new RegExp("([\\s/\\*])((TODO[^\\w\\d][^\\r\\n]*)|(TODO:?\$))");
+
+  /**
+   * Initialize a newly created error code to have the given [name].
+   */
+  const TodoCode(String name) : super(name, "{0}");
+
+  @override
+  ErrorSeverity get errorSeverity => ErrorSeverity.INFO;
+
+  @override
+  ErrorType get type => ErrorType.TODO;
+}
diff --git a/analyzer/lib/src/generated/error_verifier.dart b/analyzer/lib/src/generated/error_verifier.dart
new file mode 100644
index 0000000..ebfbb3b
--- /dev/null
+++ b/analyzer/lib/src/generated/error_verifier.dart
@@ -0,0 +1,5980 @@
+// 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 engine.resolver.error_verifier;
+
+import "dart:math" as math;
+import 'dart:collection';
+
+import 'package:analyzer/src/generated/static_type_analyzer.dart';
+
+import 'ast.dart';
+import 'constant.dart';
+import 'element.dart';
+import 'element_resolver.dart';
+import 'error.dart';
+import 'java_engine.dart';
+import 'parser.dart' show Parser, ParserErrorCode;
+import 'resolver.dart';
+import 'scanner.dart' as sc;
+import 'sdk.dart' show DartSdk, SdkLibrary;
+import 'utilities_dart.dart';
+
+/**
+ * A visitor used to traverse an AST structure looking for additional errors and
+ * warnings not covered by the parser and resolver.
+ */
+class ErrorVerifier extends RecursiveAstVisitor<Object> {
+  /**
+   * Static final string with value `"getter "` used in the construction of the
+   * [StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE], and
+   * similar, error code messages.
+   *
+   * See [_checkForNonAbstractClassInheritsAbstractMember].
+   */
+  static String _GETTER_SPACE = "getter ";
+
+  /**
+   * Static final string with value `"setter "` used in the construction of the
+   * [StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE], and
+   * similar, error code messages.
+   *
+   * See [_checkForNonAbstractClassInheritsAbstractMember].
+   */
+  static String _SETTER_SPACE = "setter ";
+
+  /**
+   * The error reporter by which errors will be reported.
+   */
+  final ErrorReporter _errorReporter;
+
+  /**
+   * The current library that is being analyzed.
+   */
+  final LibraryElement _currentLibrary;
+
+  /**
+   * The type representing the type 'bool'.
+   */
+  InterfaceType _boolType;
+
+  /**
+   * The type representing the type 'int'.
+   */
+  InterfaceType _intType;
+
+  /**
+   * The object providing access to the types defined by the language.
+   */
+  final TypeProvider _typeProvider;
+
+  /**
+   * The manager for the inheritance mappings.
+   */
+  final InheritanceManager _inheritanceManager;
+
+  /**
+   * A flag indicating whether the visitor is currently within a constructor
+   * declaration that is 'const'.
+   *
+   * See [visitConstructorDeclaration].
+   */
+  bool _isEnclosingConstructorConst = false;
+
+  /**
+   * A flag indicating whether we are currently within a function body marked as
+   * being asynchronous.
+   */
+  bool _inAsync = false;
+
+  /**
+   * A flag indicating whether we are currently within a function body marked a
+   *  being a generator.
+   */
+  bool _inGenerator = false;
+
+  /**
+   * A flag indicating whether the visitor is currently within a catch clause.
+   *
+   * See [visitCatchClause].
+   */
+  bool _isInCatchClause = false;
+
+  /**
+   * A flag indicating whether the visitor is currently within a comment.
+   */
+  bool _isInComment = false;
+
+  /**
+   * A flag indicating whether the visitor is currently within an instance
+   * creation expression.
+   */
+  bool _isInConstInstanceCreation = false;
+
+  /**
+   * A flag indicating whether the visitor is currently within a native class
+   * declaration.
+   */
+  bool _isInNativeClass = false;
+
+  /**
+   * A flag indicating whether the visitor is currently within a static variable
+   * declaration.
+   */
+  bool _isInStaticVariableDeclaration = false;
+
+  /**
+   * A flag indicating whether the visitor is currently within an instance
+   * variable declaration.
+   */
+  bool _isInInstanceVariableDeclaration = false;
+
+  /**
+   * A flag indicating whether the visitor is currently within an instance
+   * variable initializer.
+   */
+  bool _isInInstanceVariableInitializer = false;
+
+  /**
+   * A flag indicating whether the visitor is currently within a constructor
+   * initializer.
+   */
+  bool _isInConstructorInitializer = false;
+
+  /**
+   * This is set to `true` iff the visitor is currently within a function typed
+   * formal parameter.
+   */
+  bool _isInFunctionTypedFormalParameter = false;
+
+  /**
+   * A flag indicating whether the visitor is currently within a static method.
+   * By "method" here getter, setter and operator declarations are also implied
+   * since they are all represented with a [MethodDeclaration] in the AST
+   * structure.
+   */
+  bool _isInStaticMethod = false;
+
+  /**
+   * A flag indicating whether the visitor is currently within a factory
+   * constructor.
+   */
+  bool _isInFactory = false;
+
+  /**
+   * A flag indicating whether the visitor is currently within code in the SDK.
+   */
+  bool _isInSystemLibrary = false;
+
+  /**
+   * A flag indicating whether the current library contains at least one import
+   * directive with a URI that uses the "dart-ext" scheme.
+   */
+  bool _hasExtUri = false;
+
+  /**
+   * This is set to `false` on the entry of every [BlockFunctionBody], and is
+   * restored to the enclosing value on exit. The value is used in
+   * [_checkForMixedReturns] to prevent both
+   * [StaticWarningCode.MIXED_RETURN_TYPES] and
+   * [StaticWarningCode.RETURN_WITHOUT_VALUE] from being generated in the same
+   * function body.
+   */
+  bool _hasReturnWithoutValue = false;
+
+  /**
+   * The class containing the AST nodes being visited, or `null` if we are not
+   * in the scope of a class.
+   */
+  ClassElement _enclosingClass;
+
+  /**
+   * The method or function that we are currently visiting, or `null` if we are
+   * not inside a method or function.
+   */
+  ExecutableElement _enclosingFunction;
+
+  /**
+   * The return statements found in the method or function that we are currently
+   * visiting that have a return value.
+   */
+  List<ReturnStatement> _returnsWith = new List<ReturnStatement>();
+
+  /**
+   * The return statements found in the method or function that we are currently
+   * visiting that do not have a return value.
+   */
+  List<ReturnStatement> _returnsWithout = new List<ReturnStatement>();
+
+  /**
+   * This map is initialized when visiting the contents of a class declaration.
+   * If the visitor is not in an enclosing class declaration, then the map is
+   * set to `null`.
+   *
+   * When set the map maps the set of [FieldElement]s in the class to an
+   * [INIT_STATE.NOT_INIT] or [INIT_STATE.INIT_IN_DECLARATION]. The `checkFor*`
+   * methods, specifically [_checkForAllFinalInitializedErrorCodes], can make a
+   * copy of the map to compute error code states. The `checkFor*` methods
+   * should only ever make a copy, or read from this map after it has been set
+   * in [visitClassDeclaration].
+   *
+   * See [visitClassDeclaration], and [_checkForAllFinalInitializedErrorCodes].
+   */
+  HashMap<FieldElement, INIT_STATE> _initialFieldElementsMap;
+
+  /**
+   * A table mapping name of the library to the export directive which export
+   * this library.
+   */
+  HashMap<String, LibraryElement> _nameToExportElement =
+      new HashMap<String, LibraryElement>();
+
+  /**
+   * A table mapping name of the library to the import directive which import
+   * this library.
+   */
+  HashMap<String, LibraryElement> _nameToImportElement =
+      new HashMap<String, LibraryElement>();
+
+  /**
+   * A table mapping names to the exported elements.
+   */
+  HashMap<String, Element> _exportedElements = new HashMap<String, Element>();
+
+  /**
+   * A set of the names of the variable initializers we are visiting now.
+   */
+  HashSet<String> _namesForReferenceToDeclaredVariableInInitializer =
+      new HashSet<String>();
+
+  /**
+   * A list of types used by the [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]
+   * and [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS] error codes.
+   */
+  List<InterfaceType> _DISALLOWED_TYPES_TO_EXTEND_OR_IMPLEMENT;
+
+  /**
+   * Initialize a newly created error verifier.
+   */
+  ErrorVerifier(this._errorReporter, this._currentLibrary, this._typeProvider,
+      this._inheritanceManager) {
+    this._isInSystemLibrary = _currentLibrary.source.isInSystemLibrary;
+    this._hasExtUri = _currentLibrary.hasExtUri;
+    _isEnclosingConstructorConst = false;
+    _isInCatchClause = false;
+    _isInStaticVariableDeclaration = false;
+    _isInInstanceVariableDeclaration = false;
+    _isInInstanceVariableInitializer = false;
+    _isInConstructorInitializer = false;
+    _isInStaticMethod = false;
+    _boolType = _typeProvider.boolType;
+    _intType = _typeProvider.intType;
+    _DISALLOWED_TYPES_TO_EXTEND_OR_IMPLEMENT = _typeProvider.nonSubtypableTypes;
+  }
+
+  @override
+  Object visitAnnotation(Annotation node) {
+    _checkForInvalidAnnotationFromDeferredLibrary(node);
+    return super.visitAnnotation(node);
+  }
+
+  @override
+  Object visitArgumentList(ArgumentList node) {
+    _checkForArgumentTypesNotAssignableInList(node);
+    return super.visitArgumentList(node);
+  }
+
+  @override
+  Object visitAsExpression(AsExpression node) {
+    _checkForTypeAnnotationDeferredClass(node.type);
+    return super.visitAsExpression(node);
+  }
+
+  @override
+  Object visitAssertStatement(AssertStatement node) {
+    _checkForNonBoolExpression(node);
+    return super.visitAssertStatement(node);
+  }
+
+  @override
+  Object visitAssignmentExpression(AssignmentExpression node) {
+    sc.TokenType operatorType = node.operator.type;
+    Expression lhs = node.leftHandSide;
+    Expression rhs = node.rightHandSide;
+    if (operatorType == sc.TokenType.EQ ||
+        operatorType == sc.TokenType.QUESTION_QUESTION_EQ) {
+      _checkForInvalidAssignment(lhs, rhs);
+    } else {
+      _checkForInvalidCompoundAssignment(node, lhs, rhs);
+      _checkForArgumentTypeNotAssignableForArgument(rhs);
+    }
+    _checkForAssignmentToFinal(lhs);
+    return super.visitAssignmentExpression(node);
+  }
+
+  @override
+  Object visitAwaitExpression(AwaitExpression node) {
+    if (!_inAsync) {
+      _errorReporter.reportErrorForToken(
+          CompileTimeErrorCode.AWAIT_IN_WRONG_CONTEXT, node.awaitKeyword);
+    }
+    return super.visitAwaitExpression(node);
+  }
+
+  @override
+  Object visitBinaryExpression(BinaryExpression node) {
+    sc.Token operator = node.operator;
+    sc.TokenType type = operator.type;
+    if (type == sc.TokenType.AMPERSAND_AMPERSAND ||
+        type == sc.TokenType.BAR_BAR) {
+      String lexeme = operator.lexeme;
+      _checkForAssignability(node.leftOperand, _boolType,
+          StaticTypeWarningCode.NON_BOOL_OPERAND, [lexeme]);
+      _checkForAssignability(node.rightOperand, _boolType,
+          StaticTypeWarningCode.NON_BOOL_OPERAND, [lexeme]);
+    } else {
+      _checkForArgumentTypeNotAssignableForArgument(node.rightOperand);
+    }
+    return super.visitBinaryExpression(node);
+  }
+
+  @override
+  Object visitBlockFunctionBody(BlockFunctionBody node) {
+    bool wasInAsync = _inAsync;
+    bool wasInGenerator = _inGenerator;
+    bool previousHasReturnWithoutValue = _hasReturnWithoutValue;
+    _hasReturnWithoutValue = false;
+    List<ReturnStatement> previousReturnsWith = _returnsWith;
+    List<ReturnStatement> previousReturnsWithout = _returnsWithout;
+    try {
+      _inAsync = node.isAsynchronous;
+      _inGenerator = node.isGenerator;
+      _returnsWith = new List<ReturnStatement>();
+      _returnsWithout = new List<ReturnStatement>();
+      super.visitBlockFunctionBody(node);
+      _checkForMixedReturns(node);
+    } finally {
+      _inAsync = wasInAsync;
+      _inGenerator = wasInGenerator;
+      _returnsWith = previousReturnsWith;
+      _returnsWithout = previousReturnsWithout;
+      _hasReturnWithoutValue = previousHasReturnWithoutValue;
+    }
+    return null;
+  }
+
+  @override
+  Object visitBreakStatement(BreakStatement node) {
+    SimpleIdentifier labelNode = node.label;
+    if (labelNode != null) {
+      Element labelElement = labelNode.staticElement;
+      if (labelElement is LabelElementImpl && labelElement.isOnSwitchMember) {
+        _errorReporter.reportErrorForNode(
+            ResolverErrorCode.BREAK_LABEL_ON_SWITCH_MEMBER, labelNode);
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitCatchClause(CatchClause node) {
+    bool previousIsInCatchClause = _isInCatchClause;
+    try {
+      _isInCatchClause = true;
+      _checkForTypeAnnotationDeferredClass(node.exceptionType);
+      return super.visitCatchClause(node);
+    } finally {
+      _isInCatchClause = previousIsInCatchClause;
+    }
+  }
+
+  @override
+  Object visitClassDeclaration(ClassDeclaration node) {
+    ClassElement outerClass = _enclosingClass;
+    try {
+      _isInNativeClass = node.nativeClause != null;
+      _enclosingClass = node.element;
+      ExtendsClause extendsClause = node.extendsClause;
+      ImplementsClause implementsClause = node.implementsClause;
+      WithClause withClause = node.withClause;
+      _checkForBuiltInIdentifierAsName(
+          node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME);
+      _checkForMemberWithClassName();
+      _checkForNoDefaultSuperConstructorImplicit(node);
+      _checkForConflictingTypeVariableErrorCodes(node);
+      // Only do error checks on the clause nodes if there is a non-null clause
+      if (implementsClause != null ||
+          extendsClause != null ||
+          withClause != null) {
+        // Only check for all of the inheritance logic around clauses if there
+        // isn't an error code such as "Cannot extend double" already on the
+        // class.
+        if (!_checkForImplementsDisallowedClass(implementsClause) &&
+            !_checkForExtendsDisallowedClass(extendsClause) &&
+            !_checkForAllMixinErrorCodes(withClause)) {
+          _checkForExtendsDeferredClass(extendsClause);
+          _checkForImplementsDeferredClass(implementsClause);
+          _checkForNonAbstractClassInheritsAbstractMember(node.name);
+          _checkForInconsistentMethodInheritance();
+          _checkForRecursiveInterfaceInheritance(_enclosingClass);
+          _checkForConflictingGetterAndMethod();
+          _checkForConflictingInstanceGetterAndSuperclassMember();
+          _checkImplementsSuperClass(node);
+          _checkImplementsFunctionWithoutCall(node);
+        }
+      }
+      visitClassDeclarationIncrementally(node);
+      _checkForFinalNotInitializedInClass(node);
+      _checkForDuplicateDefinitionInheritance();
+      _checkForConflictingInstanceMethodSetter(node);
+      return super.visitClassDeclaration(node);
+    } finally {
+      _isInNativeClass = false;
+      _initialFieldElementsMap = null;
+      _enclosingClass = outerClass;
+    }
+  }
+
+  /**
+   * Implementation of this method should be synchronized with
+   * [visitClassDeclaration].
+   */
+  void visitClassDeclarationIncrementally(ClassDeclaration node) {
+    _isInNativeClass = node.nativeClause != null;
+    _enclosingClass = node.element;
+    // initialize initialFieldElementsMap
+    if (_enclosingClass != null) {
+      List<FieldElement> fieldElements = _enclosingClass.fields;
+      _initialFieldElementsMap = new HashMap<FieldElement, INIT_STATE>();
+      for (FieldElement fieldElement in fieldElements) {
+        if (!fieldElement.isSynthetic) {
+          _initialFieldElementsMap[fieldElement] = fieldElement.initializer ==
+              null ? INIT_STATE.NOT_INIT : INIT_STATE.INIT_IN_DECLARATION;
+        }
+      }
+    }
+  }
+
+  @override
+  Object visitClassTypeAlias(ClassTypeAlias node) {
+    _checkForBuiltInIdentifierAsName(
+        node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME);
+    ClassElement outerClassElement = _enclosingClass;
+    try {
+      _enclosingClass = node.element;
+      ImplementsClause implementsClause = node.implementsClause;
+      // Only check for all of the inheritance logic around clauses if there
+      // isn't an error code such as "Cannot extend double" already on the
+      // class.
+      if (!_checkForExtendsDisallowedClassInTypeAlias(node) &&
+          !_checkForImplementsDisallowedClass(implementsClause) &&
+          !_checkForAllMixinErrorCodes(node.withClause)) {
+        _checkForExtendsDeferredClassInTypeAlias(node);
+        _checkForImplementsDeferredClass(implementsClause);
+        _checkForRecursiveInterfaceInheritance(_enclosingClass);
+        _checkForNonAbstractClassInheritsAbstractMember(node.name);
+      }
+    } finally {
+      _enclosingClass = outerClassElement;
+    }
+    return super.visitClassTypeAlias(node);
+  }
+
+  @override
+  Object visitComment(Comment node) {
+    _isInComment = true;
+    try {
+      return super.visitComment(node);
+    } finally {
+      _isInComment = false;
+    }
+  }
+
+  @override
+  Object visitCompilationUnit(CompilationUnit node) {
+    _checkForDeferredPrefixCollisions(node);
+    return super.visitCompilationUnit(node);
+  }
+
+  @override
+  Object visitConditionalExpression(ConditionalExpression node) {
+    _checkForNonBoolCondition(node.condition);
+    return super.visitConditionalExpression(node);
+  }
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    ExecutableElement outerFunction = _enclosingFunction;
+    try {
+      ConstructorElement constructorElement = node.element;
+      _enclosingFunction = constructorElement;
+      _isEnclosingConstructorConst = node.constKeyword != null;
+      _isInFactory = node.factoryKeyword != null;
+      _checkForInvalidModifierOnBody(
+          node.body, CompileTimeErrorCode.INVALID_MODIFIER_ON_CONSTRUCTOR);
+      _checkForConstConstructorWithNonFinalField(node, constructorElement);
+      _checkForConstConstructorWithNonConstSuper(node);
+      _checkForConflictingConstructorNameAndMember(node, constructorElement);
+      _checkForAllFinalInitializedErrorCodes(node);
+      _checkForRedirectingConstructorErrorCodes(node);
+      _checkForMultipleSuperInitializers(node);
+      _checkForRecursiveConstructorRedirect(node, constructorElement);
+      if (!_checkForRecursiveFactoryRedirect(node, constructorElement)) {
+        _checkForAllRedirectConstructorErrorCodes(node);
+      }
+      _checkForUndefinedConstructorInInitializerImplicit(node);
+      _checkForRedirectToNonConstConstructor(node, constructorElement);
+      _checkForReturnInGenerativeConstructor(node);
+      return super.visitConstructorDeclaration(node);
+    } finally {
+      _isEnclosingConstructorConst = false;
+      _isInFactory = false;
+      _enclosingFunction = outerFunction;
+    }
+  }
+
+  @override
+  Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+    _isInConstructorInitializer = true;
+    try {
+      SimpleIdentifier fieldName = node.fieldName;
+      Element staticElement = fieldName.staticElement;
+      _checkForInvalidField(node, fieldName, staticElement);
+      _checkForFieldInitializerNotAssignable(node, staticElement);
+      return super.visitConstructorFieldInitializer(node);
+    } finally {
+      _isInConstructorInitializer = false;
+    }
+  }
+
+  @override
+  Object visitContinueStatement(ContinueStatement node) {
+    SimpleIdentifier labelNode = node.label;
+    if (labelNode != null) {
+      Element labelElement = labelNode.staticElement;
+      if (labelElement is LabelElementImpl &&
+          labelElement.isOnSwitchStatement) {
+        _errorReporter.reportErrorForNode(
+            ResolverErrorCode.CONTINUE_LABEL_ON_SWITCH, labelNode);
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitDefaultFormalParameter(DefaultFormalParameter node) {
+    _checkForInvalidAssignment(node.identifier, node.defaultValue);
+    _checkForDefaultValueInFunctionTypedParameter(node);
+    return super.visitDefaultFormalParameter(node);
+  }
+
+  @override
+  Object visitDoStatement(DoStatement node) {
+    _checkForNonBoolCondition(node.condition);
+    return super.visitDoStatement(node);
+  }
+
+  @override
+  Object visitExportDirective(ExportDirective node) {
+    ExportElement exportElement = node.element;
+    if (exportElement != null) {
+      LibraryElement exportedLibrary = exportElement.exportedLibrary;
+      _checkForAmbiguousExport(node, exportElement, exportedLibrary);
+      _checkForExportDuplicateLibraryName(node, exportElement, exportedLibrary);
+      _checkForExportInternalLibrary(node, exportElement);
+    }
+    return super.visitExportDirective(node);
+  }
+
+  @override
+  Object visitExpressionFunctionBody(ExpressionFunctionBody node) {
+    bool wasInAsync = _inAsync;
+    bool wasInGenerator = _inGenerator;
+    try {
+      _inAsync = node.isAsynchronous;
+      _inGenerator = node.isGenerator;
+      FunctionType functionType =
+          _enclosingFunction == null ? null : _enclosingFunction.type;
+      DartType expectedReturnType = functionType == null
+          ? DynamicTypeImpl.instance
+          : functionType.returnType;
+      _checkForReturnOfInvalidType(node.expression, expectedReturnType);
+      return super.visitExpressionFunctionBody(node);
+    } finally {
+      _inAsync = wasInAsync;
+      _inGenerator = wasInGenerator;
+    }
+  }
+
+  @override
+  Object visitFieldDeclaration(FieldDeclaration node) {
+    _isInStaticVariableDeclaration = node.isStatic;
+    _isInInstanceVariableDeclaration = !_isInStaticVariableDeclaration;
+    if (_isInInstanceVariableDeclaration) {
+      VariableDeclarationList variables = node.fields;
+      if (variables.isConst) {
+        _errorReporter.reportErrorForToken(
+            CompileTimeErrorCode.CONST_INSTANCE_FIELD, variables.keyword);
+      }
+    }
+    try {
+      _checkForAllInvalidOverrideErrorCodesForField(node);
+      return super.visitFieldDeclaration(node);
+    } finally {
+      _isInStaticVariableDeclaration = false;
+      _isInInstanceVariableDeclaration = false;
+    }
+  }
+
+  @override
+  Object visitFieldFormalParameter(FieldFormalParameter node) {
+    _checkForValidField(node);
+    _checkForConstFormalParameter(node);
+    _checkForPrivateOptionalParameter(node);
+    _checkForFieldInitializingFormalRedirectingConstructor(node);
+    _checkForTypeAnnotationDeferredClass(node.type);
+    return super.visitFieldFormalParameter(node);
+  }
+
+  @override
+  Object visitFunctionDeclaration(FunctionDeclaration node) {
+    ExecutableElement outerFunction = _enclosingFunction;
+    try {
+      SimpleIdentifier identifier = node.name;
+      String methodName = "";
+      if (identifier != null) {
+        methodName = identifier.name;
+      }
+      _enclosingFunction = node.element;
+      TypeName returnType = node.returnType;
+      if (node.isSetter || node.isGetter) {
+        _checkForMismatchedAccessorTypes(node, methodName);
+        if (node.isSetter) {
+          FunctionExpression functionExpression = node.functionExpression;
+          if (functionExpression != null) {
+            _checkForWrongNumberOfParametersForSetter(
+                identifier, functionExpression.parameters);
+          }
+          _checkForNonVoidReturnTypeForSetter(returnType);
+        }
+      }
+      if (node.isSetter) {
+        _checkForInvalidModifierOnBody(node.functionExpression.body,
+            CompileTimeErrorCode.INVALID_MODIFIER_ON_SETTER);
+      }
+      _checkForTypeAnnotationDeferredClass(returnType);
+      _checkForIllegalReturnType(returnType);
+      return super.visitFunctionDeclaration(node);
+    } finally {
+      _enclosingFunction = outerFunction;
+    }
+  }
+
+  @override
+  Object visitFunctionExpression(FunctionExpression node) {
+    // If this function expression is wrapped in a function declaration, don't
+    // change the enclosingFunction field.
+    if (node.parent is! FunctionDeclaration) {
+      ExecutableElement outerFunction = _enclosingFunction;
+      try {
+        _enclosingFunction = node.element;
+        return super.visitFunctionExpression(node);
+      } finally {
+        _enclosingFunction = outerFunction;
+      }
+    } else {
+      return super.visitFunctionExpression(node);
+    }
+  }
+
+  @override
+  Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    Expression functionExpression = node.function;
+    DartType expressionType = functionExpression.staticType;
+    if (!_isFunctionType(expressionType)) {
+      _errorReporter.reportErrorForNode(
+          StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION_EXPRESSION,
+          functionExpression);
+    }
+    return super.visitFunctionExpressionInvocation(node);
+  }
+
+  @override
+  Object visitFunctionTypeAlias(FunctionTypeAlias node) {
+    _checkForBuiltInIdentifierAsName(
+        node.name, CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME);
+    _checkForDefaultValueInFunctionTypeAlias(node);
+    _checkForTypeAliasCannotReferenceItself_function(node);
+    return super.visitFunctionTypeAlias(node);
+  }
+
+  @override
+  Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+    bool old = _isInFunctionTypedFormalParameter;
+    _isInFunctionTypedFormalParameter = true;
+    try {
+      _checkForTypeAnnotationDeferredClass(node.returnType);
+      return super.visitFunctionTypedFormalParameter(node);
+    } finally {
+      _isInFunctionTypedFormalParameter = old;
+    }
+  }
+
+  @override
+  Object visitIfStatement(IfStatement node) {
+    _checkForNonBoolCondition(node.condition);
+    return super.visitIfStatement(node);
+  }
+
+  @override
+  Object visitImportDirective(ImportDirective node) {
+    ImportElement importElement = node.element;
+    if (importElement != null) {
+      _checkForImportDuplicateLibraryName(node, importElement);
+      _checkForImportInternalLibrary(node, importElement);
+    }
+    return super.visitImportDirective(node);
+  }
+
+  @override
+  Object visitIndexExpression(IndexExpression node) {
+    _checkForArgumentTypeNotAssignableForArgument(node.index);
+    return super.visitIndexExpression(node);
+  }
+
+  @override
+  Object visitInstanceCreationExpression(InstanceCreationExpression node) {
+    bool wasInConstInstanceCreation = _isInConstInstanceCreation;
+    _isInConstInstanceCreation = node.isConst;
+    try {
+      ConstructorName constructorName = node.constructorName;
+      TypeName typeName = constructorName.type;
+      DartType type = typeName.type;
+      if (type is InterfaceType) {
+        InterfaceType interfaceType = type;
+        _checkForConstOrNewWithAbstractClass(node, typeName, interfaceType);
+        _checkForConstOrNewWithEnum(node, typeName, interfaceType);
+        if (_isInConstInstanceCreation) {
+          _checkForConstWithNonConst(node);
+          _checkForConstWithUndefinedConstructor(
+              node, constructorName, typeName);
+          _checkForConstWithTypeParameters(typeName);
+          _checkForConstDeferredClass(node, constructorName, typeName);
+        } else {
+          _checkForNewWithUndefinedConstructor(node, constructorName, typeName);
+        }
+      }
+      return super.visitInstanceCreationExpression(node);
+    } finally {
+      _isInConstInstanceCreation = wasInConstInstanceCreation;
+    }
+  }
+
+  @override
+  Object visitIsExpression(IsExpression node) {
+    _checkForTypeAnnotationDeferredClass(node.type);
+    return super.visitIsExpression(node);
+  }
+
+  @override
+  Object visitListLiteral(ListLiteral node) {
+    TypeArgumentList typeArguments = node.typeArguments;
+    if (typeArguments != null) {
+      if (node.constKeyword != null) {
+        NodeList<TypeName> arguments = typeArguments.arguments;
+        if (arguments.length != 0) {
+          _checkForInvalidTypeArgumentInConstTypedLiteral(arguments,
+              CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_LIST);
+        }
+      }
+      _checkForExpectedOneListTypeArgument(node, typeArguments);
+      _checkForListElementTypeNotAssignable(node, typeArguments);
+    }
+    return super.visitListLiteral(node);
+  }
+
+  @override
+  Object visitMapLiteral(MapLiteral node) {
+    TypeArgumentList typeArguments = node.typeArguments;
+    if (typeArguments != null) {
+      NodeList<TypeName> arguments = typeArguments.arguments;
+      if (arguments.length != 0) {
+        if (node.constKeyword != null) {
+          _checkForInvalidTypeArgumentInConstTypedLiteral(arguments,
+              CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_MAP);
+        }
+      }
+      _checkExpectedTwoMapTypeArguments(typeArguments);
+      _checkForMapTypeNotAssignable(node, typeArguments);
+    }
+    _checkForNonConstMapAsExpressionStatement(node);
+    return super.visitMapLiteral(node);
+  }
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    ExecutableElement previousFunction = _enclosingFunction;
+    try {
+      _isInStaticMethod = node.isStatic;
+      _enclosingFunction = node.element;
+      SimpleIdentifier identifier = node.name;
+      String methodName = "";
+      if (identifier != null) {
+        methodName = identifier.name;
+      }
+      TypeName returnTypeName = node.returnType;
+      if (node.isSetter || node.isGetter) {
+        _checkForMismatchedAccessorTypes(node, methodName);
+      }
+      if (node.isGetter) {
+        _checkForVoidReturnType(node);
+        _checkForConflictingStaticGetterAndInstanceSetter(node);
+      } else if (node.isSetter) {
+        _checkForInvalidModifierOnBody(
+            node.body, CompileTimeErrorCode.INVALID_MODIFIER_ON_SETTER);
+        _checkForWrongNumberOfParametersForSetter(node.name, node.parameters);
+        _checkForNonVoidReturnTypeForSetter(returnTypeName);
+        _checkForConflictingStaticSetterAndInstanceMember(node);
+      } else if (node.isOperator) {
+        _checkForOptionalParameterInOperator(node);
+        _checkForWrongNumberOfParametersForOperator(node);
+        _checkForNonVoidReturnTypeForOperator(node);
+      }
+      _checkForConcreteClassWithAbstractMember(node);
+      _checkForAllInvalidOverrideErrorCodesForMethod(node);
+      _checkForTypeAnnotationDeferredClass(returnTypeName);
+      _checkForIllegalReturnType(returnTypeName);
+      return super.visitMethodDeclaration(node);
+    } finally {
+      _enclosingFunction = previousFunction;
+      _isInStaticMethod = false;
+    }
+  }
+
+  @override
+  Object visitMethodInvocation(MethodInvocation node) {
+    Expression target = node.realTarget;
+    SimpleIdentifier methodName = node.methodName;
+    if (target != null) {
+      bool isConditional = node.operator.type == sc.TokenType.QUESTION_PERIOD;
+      ClassElement typeReference =
+          ElementResolver.getTypeReference(target, isConditional);
+      _checkForStaticAccessToInstanceMember(typeReference, methodName);
+      _checkForInstanceAccessToStaticMember(typeReference, methodName);
+    } else {
+      _checkForUnqualifiedReferenceToNonLocalStaticMember(methodName);
+    }
+    return super.visitMethodInvocation(node);
+  }
+
+  @override
+  Object visitNativeClause(NativeClause node) {
+    // TODO(brianwilkerson) Figure out the right rule for when 'native' is
+    // allowed.
+    if (!_isInSystemLibrary) {
+      _errorReporter.reportErrorForNode(
+          ParserErrorCode.NATIVE_CLAUSE_IN_NON_SDK_CODE, node);
+    }
+    return super.visitNativeClause(node);
+  }
+
+  @override
+  Object visitNativeFunctionBody(NativeFunctionBody node) {
+    _checkForNativeFunctionBodyInNonSDKCode(node);
+    return super.visitNativeFunctionBody(node);
+  }
+
+  @override
+  Object visitPostfixExpression(PostfixExpression node) {
+    _checkForAssignmentToFinal(node.operand);
+    _checkForIntNotAssignable(node.operand);
+    return super.visitPostfixExpression(node);
+  }
+
+  @override
+  Object visitPrefixedIdentifier(PrefixedIdentifier node) {
+    if (node.parent is! Annotation) {
+      ClassElement typeReference =
+          ElementResolver.getTypeReference(node.prefix, false);
+      SimpleIdentifier name = node.identifier;
+      _checkForStaticAccessToInstanceMember(typeReference, name);
+      _checkForInstanceAccessToStaticMember(typeReference, name);
+    }
+    return super.visitPrefixedIdentifier(node);
+  }
+
+  @override
+  Object visitPrefixExpression(PrefixExpression node) {
+    sc.TokenType operatorType = node.operator.type;
+    Expression operand = node.operand;
+    if (operatorType == sc.TokenType.BANG) {
+      _checkForNonBoolNegationExpression(operand);
+    } else if (operatorType.isIncrementOperator) {
+      _checkForAssignmentToFinal(operand);
+    }
+    _checkForIntNotAssignable(operand);
+    return super.visitPrefixExpression(node);
+  }
+
+  @override
+  Object visitPropertyAccess(PropertyAccess node) {
+    bool isConditional = node.operator.type == sc.TokenType.QUESTION_PERIOD;
+    ClassElement typeReference =
+        ElementResolver.getTypeReference(node.realTarget, isConditional);
+    SimpleIdentifier propertyName = node.propertyName;
+    _checkForStaticAccessToInstanceMember(typeReference, propertyName);
+    _checkForInstanceAccessToStaticMember(typeReference, propertyName);
+    return super.visitPropertyAccess(node);
+  }
+
+  @override
+  Object visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    _isInConstructorInitializer = true;
+    try {
+      return super.visitRedirectingConstructorInvocation(node);
+    } finally {
+      _isInConstructorInitializer = false;
+    }
+  }
+
+  @override
+  Object visitRethrowExpression(RethrowExpression node) {
+    _checkForRethrowOutsideCatch(node);
+    return super.visitRethrowExpression(node);
+  }
+
+  @override
+  Object visitReturnStatement(ReturnStatement node) {
+    if (node.expression == null) {
+      _returnsWithout.add(node);
+    } else {
+      _returnsWith.add(node);
+    }
+    _checkForAllReturnStatementErrorCodes(node);
+    return super.visitReturnStatement(node);
+  }
+
+  @override
+  Object visitSimpleFormalParameter(SimpleFormalParameter node) {
+    _checkForConstFormalParameter(node);
+    _checkForPrivateOptionalParameter(node);
+    _checkForTypeAnnotationDeferredClass(node.type);
+    return super.visitSimpleFormalParameter(node);
+  }
+
+  @override
+  Object visitSimpleIdentifier(SimpleIdentifier node) {
+    _checkForImplicitThisReferenceInInitializer(node);
+    if (!_isUnqualifiedReferenceToNonLocalStaticMemberAllowed(node)) {
+      _checkForUnqualifiedReferenceToNonLocalStaticMember(node);
+    }
+    return super.visitSimpleIdentifier(node);
+  }
+
+  @override
+  Object visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    _isInConstructorInitializer = true;
+    try {
+      return super.visitSuperConstructorInvocation(node);
+    } finally {
+      _isInConstructorInitializer = false;
+    }
+  }
+
+  @override
+  Object visitSwitchStatement(SwitchStatement node) {
+    _checkForSwitchExpressionNotAssignable(node);
+    _checkForCaseBlocksNotTerminated(node);
+    _checkForMissingEnumConstantInSwitch(node);
+    return super.visitSwitchStatement(node);
+  }
+
+  @override
+  Object visitThisExpression(ThisExpression node) {
+    _checkForInvalidReferenceToThis(node);
+    return super.visitThisExpression(node);
+  }
+
+  @override
+  Object visitThrowExpression(ThrowExpression node) {
+    _checkForConstEvalThrowsException(node);
+    return super.visitThrowExpression(node);
+  }
+
+  @override
+  Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+    _checkForFinalNotInitialized(node.variables);
+    return super.visitTopLevelVariableDeclaration(node);
+  }
+
+  @override
+  Object visitTypeArgumentList(TypeArgumentList node) {
+    NodeList<TypeName> list = node.arguments;
+    for (TypeName typeName in list) {
+      _checkForTypeAnnotationDeferredClass(typeName);
+    }
+    return super.visitTypeArgumentList(node);
+  }
+
+  @override
+  Object visitTypeName(TypeName node) {
+    _checkForTypeArgumentNotMatchingBounds(node);
+    _checkForTypeParameterReferencedByStatic(node);
+    return super.visitTypeName(node);
+  }
+
+  @override
+  Object visitTypeParameter(TypeParameter node) {
+    _checkForBuiltInIdentifierAsName(node.name,
+        CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME);
+    _checkForTypeParameterSupertypeOfItsBound(node);
+    _checkForTypeAnnotationDeferredClass(node.bound);
+    return super.visitTypeParameter(node);
+  }
+
+  @override
+  Object visitVariableDeclaration(VariableDeclaration node) {
+    SimpleIdentifier nameNode = node.name;
+    Expression initializerNode = node.initializer;
+    // do checks
+    _checkForInvalidAssignment(nameNode, initializerNode);
+    // visit name
+    nameNode.accept(this);
+    // visit initializer
+    String name = nameNode.name;
+    _namesForReferenceToDeclaredVariableInInitializer.add(name);
+    bool wasInInstanceVariableInitializer = _isInInstanceVariableInitializer;
+    _isInInstanceVariableInitializer = _isInInstanceVariableDeclaration;
+    try {
+      if (initializerNode != null) {
+        initializerNode.accept(this);
+      }
+    } finally {
+      _isInInstanceVariableInitializer = wasInInstanceVariableInitializer;
+      _namesForReferenceToDeclaredVariableInInitializer.remove(name);
+    }
+    // done
+    return null;
+  }
+
+  @override
+  Object visitVariableDeclarationList(VariableDeclarationList node) {
+    _checkForTypeAnnotationDeferredClass(node.type);
+    return super.visitVariableDeclarationList(node);
+  }
+
+  @override
+  Object visitVariableDeclarationStatement(VariableDeclarationStatement node) {
+    _checkForFinalNotInitialized(node.variables);
+    return super.visitVariableDeclarationStatement(node);
+  }
+
+  @override
+  Object visitWhileStatement(WhileStatement node) {
+    _checkForNonBoolCondition(node.condition);
+    return super.visitWhileStatement(node);
+  }
+
+  @override
+  Object visitYieldStatement(YieldStatement node) {
+    if (_inGenerator) {
+      _checkForYieldOfInvalidType(node.expression, node.star != null);
+    } else {
+      CompileTimeErrorCode errorCode;
+      if (node.star != null) {
+        errorCode = CompileTimeErrorCode.YIELD_EACH_IN_NON_GENERATOR;
+      } else {
+        errorCode = CompileTimeErrorCode.YIELD_IN_NON_GENERATOR;
+      }
+      _errorReporter.reportErrorForNode(errorCode, node);
+    }
+    return super.visitYieldStatement(node);
+  }
+
+  /**
+   * Verify that the given list of [typeArguments] contains exactly two
+   * elements.
+   *
+   * See [StaticTypeWarningCode.EXPECTED_TWO_MAP_TYPE_ARGUMENTS].
+   */
+  bool _checkExpectedTwoMapTypeArguments(TypeArgumentList typeArguments) {
+    // check number of type arguments
+    int num = typeArguments.arguments.length;
+    if (num == 2) {
+      return false;
+    }
+    // report problem
+    _errorReporter.reportErrorForNode(
+        StaticTypeWarningCode.EXPECTED_TWO_MAP_TYPE_ARGUMENTS, typeArguments,
+        [num]);
+    return true;
+  }
+
+  /**
+   * Verify that the given [constructor] declaration does not violate any of the
+   * error codes relating to the initialization of fields in the enclosing
+   * class.
+   *
+   * See [_initialFieldElementsMap],
+   * [StaticWarningCode.FINAL_INITIALIZED_IN_DECLARATION_AND_CONSTRUCTOR], and
+   * [CompileTimeErrorCode.FINAL_INITIALIZED_MULTIPLE_TIMES].
+   */
+  bool _checkForAllFinalInitializedErrorCodes(
+      ConstructorDeclaration constructor) {
+    if (constructor.factoryKeyword != null ||
+        constructor.redirectedConstructor != null ||
+        constructor.externalKeyword != null) {
+      return false;
+    }
+    // Ignore if native class.
+    if (_isInNativeClass) {
+      return false;
+    }
+    bool foundError = false;
+    HashMap<FieldElement, INIT_STATE> fieldElementsMap =
+        new HashMap<FieldElement, INIT_STATE>.from(_initialFieldElementsMap);
+    // Visit all of the field formal parameters
+    NodeList<FormalParameter> formalParameters =
+        constructor.parameters.parameters;
+    for (FormalParameter formalParameter in formalParameters) {
+      FormalParameter parameter = formalParameter;
+      if (parameter is DefaultFormalParameter) {
+        parameter = (parameter as DefaultFormalParameter).parameter;
+      }
+      if (parameter is FieldFormalParameter) {
+        FieldElement fieldElement =
+            (parameter.element as FieldFormalParameterElementImpl).field;
+        INIT_STATE state = fieldElementsMap[fieldElement];
+        if (state == INIT_STATE.NOT_INIT) {
+          fieldElementsMap[fieldElement] = INIT_STATE.INIT_IN_FIELD_FORMAL;
+        } else if (state == INIT_STATE.INIT_IN_DECLARATION) {
+          if (fieldElement.isFinal || fieldElement.isConst) {
+            _errorReporter.reportErrorForNode(
+                StaticWarningCode.FINAL_INITIALIZED_IN_DECLARATION_AND_CONSTRUCTOR,
+                formalParameter.identifier, [fieldElement.displayName]);
+            foundError = true;
+          }
+        } else if (state == INIT_STATE.INIT_IN_FIELD_FORMAL) {
+          if (fieldElement.isFinal || fieldElement.isConst) {
+            _errorReporter.reportErrorForNode(
+                CompileTimeErrorCode.FINAL_INITIALIZED_MULTIPLE_TIMES,
+                formalParameter.identifier, [fieldElement.displayName]);
+            foundError = true;
+          }
+        }
+      }
+    }
+    // Visit all of the initializers
+    NodeList<ConstructorInitializer> initializers = constructor.initializers;
+    for (ConstructorInitializer constructorInitializer in initializers) {
+      if (constructorInitializer is RedirectingConstructorInvocation) {
+        return false;
+      }
+      if (constructorInitializer is ConstructorFieldInitializer) {
+        ConstructorFieldInitializer constructorFieldInitializer =
+            constructorInitializer;
+        SimpleIdentifier fieldName = constructorFieldInitializer.fieldName;
+        Element element = fieldName.staticElement;
+        if (element is FieldElement) {
+          FieldElement fieldElement = element;
+          INIT_STATE state = fieldElementsMap[fieldElement];
+          if (state == INIT_STATE.NOT_INIT) {
+            fieldElementsMap[fieldElement] = INIT_STATE.INIT_IN_INITIALIZERS;
+          } else if (state == INIT_STATE.INIT_IN_DECLARATION) {
+            if (fieldElement.isFinal || fieldElement.isConst) {
+              _errorReporter.reportErrorForNode(
+                  StaticWarningCode.FIELD_INITIALIZED_IN_INITIALIZER_AND_DECLARATION,
+                  fieldName);
+              foundError = true;
+            }
+          } else if (state == INIT_STATE.INIT_IN_FIELD_FORMAL) {
+            _errorReporter.reportErrorForNode(
+                CompileTimeErrorCode.FIELD_INITIALIZED_IN_PARAMETER_AND_INITIALIZER,
+                fieldName);
+            foundError = true;
+          } else if (state == INIT_STATE.INIT_IN_INITIALIZERS) {
+            _errorReporter.reportErrorForNode(
+                CompileTimeErrorCode.FIELD_INITIALIZED_BY_MULTIPLE_INITIALIZERS,
+                fieldName, [fieldElement.displayName]);
+            foundError = true;
+          }
+        }
+      }
+    }
+    // Prepare a list of not initialized fields.
+    List<FieldElement> notInitFinalFields = <FieldElement>[];
+    fieldElementsMap.forEach((FieldElement fieldElement, INIT_STATE state) {
+      if (state == INIT_STATE.NOT_INIT) {
+        if (fieldElement.isFinal) {
+          notInitFinalFields.add(fieldElement);
+        }
+      }
+    });
+    // Visit all of the states in the map to ensure that none were never
+    // initialized.
+    fieldElementsMap.forEach((FieldElement fieldElement, INIT_STATE state) {
+      if (state == INIT_STATE.NOT_INIT) {
+        if (fieldElement.isConst) {
+          _errorReporter.reportErrorForNode(
+              CompileTimeErrorCode.CONST_NOT_INITIALIZED,
+              constructor.returnType, [fieldElement.name]);
+          foundError = true;
+        }
+      }
+    });
+    if (notInitFinalFields.isNotEmpty) {
+      foundError = true;
+      AnalysisErrorWithProperties analysisError;
+      if (notInitFinalFields.length == 1) {
+        analysisError = _errorReporter.newErrorWithProperties(
+            StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_1,
+            constructor.returnType, [notInitFinalFields[0].name]);
+      } else if (notInitFinalFields.length == 2) {
+        analysisError = _errorReporter.newErrorWithProperties(
+            StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_2,
+            constructor.returnType, [
+          notInitFinalFields[0].name,
+          notInitFinalFields[1].name
+        ]);
+      } else {
+        analysisError = _errorReporter.newErrorWithProperties(
+            StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_3_PLUS,
+            constructor.returnType, [
+          notInitFinalFields[0].name,
+          notInitFinalFields[1].name,
+          notInitFinalFields.length - 2
+        ]);
+      }
+      analysisError.setProperty(
+          ErrorProperty.NOT_INITIALIZED_FIELDS, notInitFinalFields);
+      _errorReporter.reportError(analysisError);
+    }
+    return foundError;
+  }
+
+  /**
+   * Check the given [executableElement] against override-error codes. The
+   * [overriddenExecutable] is the element that the executable element is
+   * overriding. The [parameters] is the parameters of the executable element.
+   * The [errorNameTarget] is the node to report problems on.
+   *
+   * See [StaticWarningCode.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC],
+   * [CompileTimeErrorCode.INVALID_OVERRIDE_REQUIRED],
+   * [CompileTimeErrorCode.INVALID_OVERRIDE_POSITIONAL],
+   * [CompileTimeErrorCode.INVALID_OVERRIDE_NAMED],
+   * [StaticWarningCode.INVALID_GETTER_OVERRIDE_RETURN_TYPE],
+   * [StaticWarningCode.INVALID_METHOD_OVERRIDE_RETURN_TYPE],
+   * [StaticWarningCode.INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE],
+   * [StaticWarningCode.INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE],
+   * [StaticWarningCode.INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE],
+   * [StaticWarningCode.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE], and
+   * [StaticWarningCode.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES].
+   */
+  bool _checkForAllInvalidOverrideErrorCodes(
+      ExecutableElement executableElement,
+      ExecutableElement overriddenExecutable, List<ParameterElement> parameters,
+      List<AstNode> parameterLocations, SimpleIdentifier errorNameTarget) {
+    bool isGetter = false;
+    bool isSetter = false;
+    if (executableElement is PropertyAccessorElement) {
+      PropertyAccessorElement accessorElement = executableElement;
+      isGetter = accessorElement.isGetter;
+      isSetter = accessorElement.isSetter;
+    }
+    String executableElementName = executableElement.name;
+    FunctionType overridingFT = executableElement.type;
+    FunctionType overriddenFT = overriddenExecutable.type;
+    InterfaceType enclosingType = _enclosingClass.type;
+    overriddenFT = _inheritanceManager
+        .substituteTypeArgumentsInMemberFromInheritance(
+            overriddenFT, executableElementName, enclosingType);
+    if (overridingFT == null || overriddenFT == null) {
+      return false;
+    }
+    DartType overridingFTReturnType = overridingFT.returnType;
+    DartType overriddenFTReturnType = overriddenFT.returnType;
+    List<DartType> overridingNormalPT = overridingFT.normalParameterTypes;
+    List<DartType> overriddenNormalPT = overriddenFT.normalParameterTypes;
+    List<DartType> overridingPositionalPT = overridingFT.optionalParameterTypes;
+    List<DartType> overriddenPositionalPT = overriddenFT.optionalParameterTypes;
+    Map<String, DartType> overridingNamedPT = overridingFT.namedParameterTypes;
+    Map<String, DartType> overriddenNamedPT = overriddenFT.namedParameterTypes;
+    // CTEC.INVALID_OVERRIDE_REQUIRED, CTEC.INVALID_OVERRIDE_POSITIONAL and
+    // CTEC.INVALID_OVERRIDE_NAMED
+    if (overridingNormalPT.length > overriddenNormalPT.length) {
+      _errorReporter.reportErrorForNode(
+          StaticWarningCode.INVALID_OVERRIDE_REQUIRED, errorNameTarget, [
+        overriddenNormalPT.length,
+        overriddenExecutable.enclosingElement.displayName
+      ]);
+      return true;
+    }
+    if (overridingNormalPT.length + overridingPositionalPT.length <
+        overriddenPositionalPT.length + overriddenNormalPT.length) {
+      _errorReporter.reportErrorForNode(
+          StaticWarningCode.INVALID_OVERRIDE_POSITIONAL, errorNameTarget, [
+        overriddenPositionalPT.length + overriddenNormalPT.length,
+        overriddenExecutable.enclosingElement.displayName
+      ]);
+      return true;
+    }
+    // For each named parameter in the overridden method, verify that there is
+    // the same name in the overriding method.
+    for (String overriddenParamName in overriddenNamedPT.keys) {
+      if (!overridingNamedPT.containsKey(overriddenParamName)) {
+        // The overridden method expected the overriding method to have
+        // overridingParamName, but it does not.
+        _errorReporter.reportErrorForNode(
+            StaticWarningCode.INVALID_OVERRIDE_NAMED, errorNameTarget, [
+          overriddenParamName,
+          overriddenExecutable.enclosingElement.displayName
+        ]);
+        return true;
+      }
+    }
+    // SWC.INVALID_METHOD_OVERRIDE_RETURN_TYPE
+    if (overriddenFTReturnType != VoidTypeImpl.instance &&
+        !overridingFTReturnType.isAssignableTo(overriddenFTReturnType)) {
+      _errorReporter.reportTypeErrorForNode(!isGetter
+              ? StaticWarningCode.INVALID_METHOD_OVERRIDE_RETURN_TYPE
+              : StaticWarningCode.INVALID_GETTER_OVERRIDE_RETURN_TYPE,
+          errorNameTarget, [
+        overridingFTReturnType,
+        overriddenFTReturnType,
+        overriddenExecutable.enclosingElement.displayName
+      ]);
+      return true;
+    }
+    // SWC.INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE
+    if (parameterLocations == null) {
+      return false;
+    }
+    int parameterIndex = 0;
+    for (int i = 0; i < overridingNormalPT.length; i++) {
+      if (!overridingNormalPT[i].isAssignableTo(overriddenNormalPT[i])) {
+        _errorReporter.reportTypeErrorForNode(!isSetter
+                ? StaticWarningCode.INVALID_METHOD_OVERRIDE_NORMAL_PARAM_TYPE
+                : StaticWarningCode.INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE,
+            parameterLocations[parameterIndex], [
+          overridingNormalPT[i],
+          overriddenNormalPT[i],
+          overriddenExecutable.enclosingElement.displayName
+        ]);
+        return true;
+      }
+      parameterIndex++;
+    }
+    // SWC.INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE
+    for (int i = 0; i < overriddenPositionalPT.length; i++) {
+      if (!overridingPositionalPT[i]
+          .isAssignableTo(overriddenPositionalPT[i])) {
+        _errorReporter.reportTypeErrorForNode(
+            StaticWarningCode.INVALID_METHOD_OVERRIDE_OPTIONAL_PARAM_TYPE,
+            parameterLocations[parameterIndex], [
+          overridingPositionalPT[i],
+          overriddenPositionalPT[i],
+          overriddenExecutable.enclosingElement.displayName
+        ]);
+        return true;
+      }
+      parameterIndex++;
+    }
+    // SWC.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE &
+    // SWC.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES
+    for (String overriddenName in overriddenNamedPT.keys) {
+      DartType overridingType = overridingNamedPT[overriddenName];
+      if (overridingType == null) {
+        // Error, this is never reached- INVALID_OVERRIDE_NAMED would have been
+        // created above if this could be reached.
+        continue;
+      }
+      DartType overriddenType = overriddenNamedPT[overriddenName];
+      if (!overriddenType.isAssignableTo(overridingType)) {
+        // lookup the parameter for the error to select
+        ParameterElement parameterToSelect = null;
+        AstNode parameterLocationToSelect = null;
+        for (int i = 0; i < parameters.length; i++) {
+          ParameterElement parameter = parameters[i];
+          if (parameter.parameterKind == ParameterKind.NAMED &&
+              overriddenName == parameter.name) {
+            parameterToSelect = parameter;
+            parameterLocationToSelect = parameterLocations[i];
+            break;
+          }
+        }
+        if (parameterToSelect != null) {
+          _errorReporter.reportTypeErrorForNode(
+              StaticWarningCode.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE,
+              parameterLocationToSelect, [
+            overridingType,
+            overriddenType,
+            overriddenExecutable.enclosingElement.displayName
+          ]);
+          return true;
+        }
+      }
+    }
+    // SWC.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES
+    //
+    // Create three lists: a list of the optional parameter ASTs
+    // (FormalParameters), a list of the optional parameters elements from our
+    // method, and finally a list of the optional parameter elements from the
+    // method we are overriding.
+    //
+    bool foundError = false;
+    List<AstNode> formalParameters = new List<AstNode>();
+    List<ParameterElementImpl> parameterElts = new List<ParameterElementImpl>();
+    List<ParameterElementImpl> overriddenParameterElts =
+        new List<ParameterElementImpl>();
+    List<ParameterElement> overriddenPEs = overriddenExecutable.parameters;
+    for (int i = 0; i < parameters.length; i++) {
+      ParameterElement parameter = parameters[i];
+      if (parameter.parameterKind.isOptional) {
+        formalParameters.add(parameterLocations[i]);
+        parameterElts.add(parameter as ParameterElementImpl);
+      }
+    }
+    for (ParameterElement parameterElt in overriddenPEs) {
+      if (parameterElt.parameterKind.isOptional) {
+        if (parameterElt is ParameterElementImpl) {
+          overriddenParameterElts.add(parameterElt);
+        }
+      }
+    }
+    //
+    // Next compare the list of optional parameter elements to the list of
+    // overridden optional parameter elements.
+    //
+    if (parameterElts.length > 0) {
+      if (parameterElts[0].parameterKind == ParameterKind.NAMED) {
+        // Named parameters, consider the names when matching the parameterElts
+        // to the overriddenParameterElts
+        for (int i = 0; i < parameterElts.length; i++) {
+          ParameterElementImpl parameterElt = parameterElts[i];
+          EvaluationResultImpl result = parameterElt.evaluationResult;
+          // TODO (jwren) Ignore Object types, see Dart bug 11287
+          if (_isUserDefinedObject(result)) {
+            continue;
+          }
+          String parameterName = parameterElt.name;
+          for (int j = 0; j < overriddenParameterElts.length; j++) {
+            ParameterElementImpl overriddenParameterElt =
+                overriddenParameterElts[j];
+            if (overriddenParameterElt.initializer == null) {
+              // There is no warning if the overridden parameter has an
+              // implicit default.
+              continue;
+            }
+            String overriddenParameterName = overriddenParameterElt.name;
+            if (parameterName != null &&
+                parameterName == overriddenParameterName) {
+              EvaluationResultImpl overriddenResult =
+                  overriddenParameterElt.evaluationResult;
+              if (_isUserDefinedObject(overriddenResult)) {
+                break;
+              }
+              if (!result.equalValues(_typeProvider, overriddenResult)) {
+                _errorReporter.reportErrorForNode(
+                    StaticWarningCode.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES_NAMED,
+                    formalParameters[i], [
+                  overriddenExecutable.enclosingElement.displayName,
+                  overriddenExecutable.displayName,
+                  parameterName
+                ]);
+                foundError = true;
+              }
+            }
+          }
+        }
+      } else {
+        // Positional parameters, consider the positions when matching the
+        // parameterElts to the overriddenParameterElts
+        for (int i = 0;
+            i < parameterElts.length && i < overriddenParameterElts.length;
+            i++) {
+          ParameterElementImpl parameterElt = parameterElts[i];
+          EvaluationResultImpl result = parameterElt.evaluationResult;
+          // TODO (jwren) Ignore Object types, see Dart bug 11287
+          if (_isUserDefinedObject(result)) {
+            continue;
+          }
+          ParameterElementImpl overriddenParameterElt =
+              overriddenParameterElts[i];
+          if (overriddenParameterElt.initializer == null) {
+            // There is no warning if the overridden parameter has an implicit
+            // default.
+            continue;
+          }
+          EvaluationResultImpl overriddenResult =
+              overriddenParameterElt.evaluationResult;
+          if (_isUserDefinedObject(overriddenResult)) {
+            continue;
+          }
+          if (!result.equalValues(_typeProvider, overriddenResult)) {
+            _errorReporter.reportErrorForNode(
+                StaticWarningCode.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES_POSITIONAL,
+                formalParameters[i], [
+              overriddenExecutable.enclosingElement.displayName,
+              overriddenExecutable.displayName
+            ]);
+            foundError = true;
+          }
+        }
+      }
+    }
+    return foundError;
+  }
+
+  /**
+   * Check the given [executableElement] against override-error codes. This
+   * method computes the given executableElement is overriding and calls
+   * [_checkForAllInvalidOverrideErrorCodes] when the [InheritanceManager]
+   * returns a [MultiplyInheritedExecutableElement], this method loops through
+   * the list in the [MultiplyInheritedExecutableElement]. The [parameters] are
+   * the parameters of the executable element. The [errorNameTarget] is the node
+   * to report problems on.
+   */
+  bool _checkForAllInvalidOverrideErrorCodesForExecutable(
+      ExecutableElement executableElement, List<ParameterElement> parameters,
+      List<AstNode> parameterLocations, SimpleIdentifier errorNameTarget) {
+    //
+    // Compute the overridden executable from the InheritanceManager
+    //
+    List<ExecutableElement> overriddenExecutables = _inheritanceManager
+        .lookupOverrides(_enclosingClass, executableElement.name);
+    if (_checkForInstanceMethodNameCollidesWithSuperclassStatic(
+        executableElement, errorNameTarget)) {
+      return true;
+    }
+    for (ExecutableElement overriddenElement in overriddenExecutables) {
+      if (_checkForAllInvalidOverrideErrorCodes(executableElement,
+          overriddenElement, parameters, parameterLocations, errorNameTarget)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Check the given field [declaration] against override-error codes.
+   *
+   * See [_checkForAllInvalidOverrideErrorCodes].
+   */
+  bool _checkForAllInvalidOverrideErrorCodesForField(
+      FieldDeclaration declaration) {
+    if (_enclosingClass == null || declaration.isStatic) {
+      return false;
+    }
+    bool hasProblems = false;
+    VariableDeclarationList fields = declaration.fields;
+    for (VariableDeclaration field in fields.variables) {
+      FieldElement element = field.element as FieldElement;
+      if (element == null) {
+        continue;
+      }
+      PropertyAccessorElement getter = element.getter;
+      PropertyAccessorElement setter = element.setter;
+      SimpleIdentifier fieldName = field.name;
+      if (getter != null) {
+        if (_checkForAllInvalidOverrideErrorCodesForExecutable(getter,
+            ParameterElement.EMPTY_LIST, AstNode.EMPTY_LIST, fieldName)) {
+          hasProblems = true;
+        }
+      }
+      if (setter != null) {
+        if (_checkForAllInvalidOverrideErrorCodesForExecutable(
+            setter, setter.parameters, <AstNode>[fieldName], fieldName)) {
+          hasProblems = true;
+        }
+      }
+    }
+    return hasProblems;
+  }
+
+  /**
+   * Check the given [method] declaration against override-error codes.
+   *
+   * See [_checkForAllInvalidOverrideErrorCodes].
+   */
+  bool _checkForAllInvalidOverrideErrorCodesForMethod(
+      MethodDeclaration method) {
+    if (_enclosingClass == null ||
+        method.isStatic ||
+        method.body is NativeFunctionBody) {
+      return false;
+    }
+    ExecutableElement executableElement = method.element;
+    if (executableElement == null) {
+      return false;
+    }
+    SimpleIdentifier methodName = method.name;
+    if (methodName.isSynthetic) {
+      return false;
+    }
+    FormalParameterList formalParameterList = method.parameters;
+    NodeList<FormalParameter> parameterList =
+        formalParameterList != null ? formalParameterList.parameters : null;
+    List<AstNode> parameters =
+        parameterList != null ? new List.from(parameterList) : null;
+    return _checkForAllInvalidOverrideErrorCodesForExecutable(executableElement,
+        executableElement.parameters, parameters, methodName);
+  }
+
+  /**
+   * Verify that all classes of the given [withClause] are valid.
+   *
+   * See [CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR],
+   * [CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT], and
+   * [CompileTimeErrorCode.MIXIN_REFERENCES_SUPER].
+   */
+  bool _checkForAllMixinErrorCodes(WithClause withClause) {
+    if (withClause == null) {
+      return false;
+    }
+    bool problemReported = false;
+    for (TypeName mixinName in withClause.mixinTypes) {
+      DartType mixinType = mixinName.type;
+      if (mixinType is! InterfaceType) {
+        continue;
+      }
+      if (_checkForExtendsOrImplementsDisallowedClass(
+          mixinName, CompileTimeErrorCode.MIXIN_OF_DISALLOWED_CLASS)) {
+        problemReported = true;
+      } else {
+        ClassElement mixinElement = (mixinType as InterfaceType).element;
+        if (_checkForExtendsOrImplementsDeferredClass(
+            mixinName, CompileTimeErrorCode.MIXIN_DEFERRED_CLASS)) {
+          problemReported = true;
+        }
+        if (_checkForMixinDeclaresConstructor(mixinName, mixinElement)) {
+          problemReported = true;
+        }
+        if (_checkForMixinInheritsNotFromObject(mixinName, mixinElement)) {
+          problemReported = true;
+        }
+        if (_checkForMixinReferencesSuper(mixinName, mixinElement)) {
+          problemReported = true;
+        }
+      }
+    }
+    return problemReported;
+  }
+
+  /**
+   * Check for errors related to the redirected constructors.
+   *
+   * See [StaticWarningCode.REDIRECT_TO_INVALID_RETURN_TYPE],
+   * [StaticWarningCode.REDIRECT_TO_INVALID_FUNCTION_TYPE], and
+   * [StaticWarningCode.REDIRECT_TO_MISSING_CONSTRUCTOR].
+   */
+  bool _checkForAllRedirectConstructorErrorCodes(
+      ConstructorDeclaration declaration) {
+    //
+    // Prepare redirected constructor node
+    //
+    ConstructorName redirectedConstructor = declaration.redirectedConstructor;
+    if (redirectedConstructor == null) {
+      return false;
+    }
+    //
+    // Prepare redirected constructor type
+    //
+    ConstructorElement redirectedElement = redirectedConstructor.staticElement;
+    if (redirectedElement == null) {
+      //
+      // If the element is null, we check for the
+      // REDIRECT_TO_MISSING_CONSTRUCTOR case
+      //
+      TypeName constructorTypeName = redirectedConstructor.type;
+      DartType redirectedType = constructorTypeName.type;
+      if (redirectedType != null &&
+          redirectedType.element != null &&
+          !redirectedType.isDynamic) {
+        //
+        // Prepare the constructor name
+        //
+        String constructorStrName = constructorTypeName.name.name;
+        if (redirectedConstructor.name != null) {
+          constructorStrName += ".${redirectedConstructor.name.name}";
+        }
+        ErrorCode errorCode = (declaration.constKeyword != null
+            ? CompileTimeErrorCode.REDIRECT_TO_MISSING_CONSTRUCTOR
+            : StaticWarningCode.REDIRECT_TO_MISSING_CONSTRUCTOR);
+        _errorReporter.reportErrorForNode(errorCode, redirectedConstructor, [
+          constructorStrName,
+          redirectedType.displayName
+        ]);
+        return true;
+      }
+      return false;
+    }
+    FunctionType redirectedType = redirectedElement.type;
+    DartType redirectedReturnType = redirectedType.returnType;
+    //
+    // Report specific problem when return type is incompatible
+    //
+    FunctionType constructorType = declaration.element.type;
+    DartType constructorReturnType = constructorType.returnType;
+    if (!redirectedReturnType.isAssignableTo(constructorReturnType)) {
+      _errorReporter.reportErrorForNode(
+          StaticWarningCode.REDIRECT_TO_INVALID_RETURN_TYPE,
+          redirectedConstructor, [redirectedReturnType, constructorReturnType]);
+      return true;
+    }
+    //
+    // Check parameters
+    //
+    if (!redirectedType.isSubtypeOf(constructorType)) {
+      _errorReporter.reportErrorForNode(
+          StaticWarningCode.REDIRECT_TO_INVALID_FUNCTION_TYPE,
+          redirectedConstructor, [redirectedType, constructorType]);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Check that the return [statement] of the form <i>return e;</i> is not in a
+   * generative constructor.
+   *
+   * Check that return statements without expressions are not in a generative
+   * constructor and the return type is not assignable to `null`; that is, we
+   * don't have `return;` if the enclosing method has a return type.
+   *
+   * Check that the return type matches the type of the declared return type in
+   * the enclosing method or function.
+   *
+   * See [CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR],
+   * [StaticWarningCode.RETURN_WITHOUT_VALUE], and
+   * [StaticTypeWarningCode.RETURN_OF_INVALID_TYPE].
+   */
+  bool _checkForAllReturnStatementErrorCodes(ReturnStatement statement) {
+    FunctionType functionType =
+        _enclosingFunction == null ? null : _enclosingFunction.type;
+    DartType expectedReturnType = functionType == null
+        ? DynamicTypeImpl.instance
+        : functionType.returnType;
+    Expression returnExpression = statement.expression;
+    // RETURN_IN_GENERATIVE_CONSTRUCTOR
+    bool isGenerativeConstructor = _enclosingFunction is ConstructorElement &&
+        !(_enclosingFunction as ConstructorElement).isFactory;
+    if (isGenerativeConstructor) {
+      if (returnExpression == null) {
+        return false;
+      }
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR,
+          returnExpression);
+      return true;
+    }
+    // RETURN_WITHOUT_VALUE
+    if (returnExpression == null) {
+      if (_inGenerator ||
+          _computeReturnTypeForMethod(null)
+              .isAssignableTo(expectedReturnType)) {
+        return false;
+      }
+      _hasReturnWithoutValue = true;
+      _errorReporter.reportErrorForNode(
+          StaticWarningCode.RETURN_WITHOUT_VALUE, statement);
+      return true;
+    } else if (_inGenerator) {
+      // RETURN_IN_GENERATOR
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.RETURN_IN_GENERATOR, statement);
+    }
+    // RETURN_OF_INVALID_TYPE
+    return _checkForReturnOfInvalidType(returnExpression, expectedReturnType);
+  }
+
+  /**
+   * Verify that the export namespace of the given export [directive] does not
+   * export any name already exported by another export directive. The
+   * [exportElement] is the [ExportElement] retrieved from the node. If the
+   * element in the node was `null`, then this method is not called. The
+   * [exportedLibrary] is the library element containing the exported element.
+   *
+   * See [CompileTimeErrorCode.AMBIGUOUS_EXPORT].
+   */
+  bool _checkForAmbiguousExport(ExportDirective directive,
+      ExportElement exportElement, LibraryElement exportedLibrary) {
+    if (exportedLibrary == null) {
+      return false;
+    }
+    // check exported names
+    Namespace namespace =
+        new NamespaceBuilder().createExportNamespaceForDirective(exportElement);
+    Map<String, Element> definedNames = namespace.definedNames;
+    for (String name in definedNames.keys) {
+      Element element = definedNames[name];
+      Element prevElement = _exportedElements[name];
+      if (element != null && prevElement != null && prevElement != element) {
+        _errorReporter.reportErrorForNode(CompileTimeErrorCode.AMBIGUOUS_EXPORT,
+            directive, [
+          name,
+          prevElement.library.definingCompilationUnit.displayName,
+          element.library.definingCompilationUnit.displayName
+        ]);
+        return true;
+      } else {
+        _exportedElements[name] = element;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given [expression] can be assigned to its corresponding
+   * parameters. The [expectedStaticType] is the expected static type of the
+   * parameter. The [actualStaticType] is the actual static type of the
+   * argument.
+   *
+   * This method corresponds to
+   * [BestPracticesVerifier.checkForArgumentTypeNotAssignable].
+   *
+   * See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE],
+   * [CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE],
+   * [StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE],
+   * [CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE],
+   * [CompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE],
+   * [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE], and
+   * [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE].
+   */
+  bool _checkForArgumentTypeNotAssignable(Expression expression,
+      DartType expectedStaticType, DartType actualStaticType,
+      ErrorCode errorCode) {
+    //
+    // Warning case: test static type information
+    //
+    if (actualStaticType != null && expectedStaticType != null) {
+      if (!actualStaticType.isAssignableTo(expectedStaticType)) {
+        _errorReporter.reportTypeErrorForNode(
+            errorCode, expression, [actualStaticType, expectedStaticType]);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given [argument] can be assigned to its corresponding
+   * parameter.
+   *
+   * This method corresponds to
+   * [BestPracticesVerifier.checkForArgumentTypeNotAssignableForArgument].
+   *
+   * See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
+   */
+  bool _checkForArgumentTypeNotAssignableForArgument(Expression argument) {
+    if (argument == null) {
+      return false;
+    }
+    ParameterElement staticParameterElement = argument.staticParameterElement;
+    DartType staticParameterType =
+        staticParameterElement == null ? null : staticParameterElement.type;
+    return _checkForArgumentTypeNotAssignableWithExpectedTypes(argument,
+        staticParameterType, StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE);
+  }
+
+  /**
+   * Verify that the given [expression] can be assigned to its corresponding
+   * parameters. The [expectedStaticType] is the expected static type.
+   *
+   * This method corresponds to
+   * [BestPracticesVerifier.checkForArgumentTypeNotAssignableWithExpectedTypes].
+   *
+   * See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE],
+   * [CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE],
+   * [StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE],
+   * [CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE],
+   * [CompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE],
+   * [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE], and
+   * [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE].
+   */
+  bool _checkForArgumentTypeNotAssignableWithExpectedTypes(
+      Expression expression, DartType expectedStaticType,
+      ErrorCode errorCode) => _checkForArgumentTypeNotAssignable(
+          expression, expectedStaticType, getStaticType(expression), errorCode);
+
+  /**
+   * Verify that the arguments in the given [argumentList] can be assigned to
+   * their corresponding parameters.
+   *
+   * This method corresponds to
+   * [BestPracticesVerifier.checkForArgumentTypesNotAssignableInList].
+   *
+   * See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
+   */
+  bool _checkForArgumentTypesNotAssignableInList(ArgumentList argumentList) {
+    if (argumentList == null) {
+      return false;
+    }
+    bool problemReported = false;
+    for (Expression argument in argumentList.arguments) {
+      if (_checkForArgumentTypeNotAssignableForArgument(argument)) {
+        problemReported = true;
+      }
+    }
+    return problemReported;
+  }
+
+  /**
+   * Check that the static type of the given expression is assignable to the
+   * given type. If it isn't, report an error with the given error code. The
+   * [type] is the type that the expression must be assignable to. The
+   * [errorCode] is the error code to be reported. The [arguments] are the
+   * arguments to pass in when creating the error.
+   */
+  bool _checkForAssignability(Expression expression, InterfaceType type,
+      ErrorCode errorCode, List<Object> arguments) {
+    if (expression == null) {
+      return false;
+    }
+    DartType expressionType = expression.staticType;
+    if (expressionType == null) {
+      return false;
+    }
+    if (expressionType.isAssignableTo(type)) {
+      return false;
+    }
+    _errorReporter.reportErrorForNode(errorCode, expression, arguments);
+    return true;
+  }
+
+  /**
+   * Verify that the given [expression] is not final.
+   *
+   * See [StaticWarningCode.ASSIGNMENT_TO_CONST],
+   * [StaticWarningCode.ASSIGNMENT_TO_FINAL], and
+   * [StaticWarningCode.ASSIGNMENT_TO_METHOD].
+   */
+  bool _checkForAssignmentToFinal(Expression expression) {
+    // prepare element
+    Element element = null;
+    AstNode highlightedNode = expression;
+    if (expression is Identifier) {
+      element = expression.staticElement;
+      if (expression is PrefixedIdentifier) {
+        highlightedNode = expression.identifier;
+      }
+    } else if (expression is PropertyAccess) {
+      PropertyAccess propertyAccess = expression;
+      element = propertyAccess.propertyName.staticElement;
+      highlightedNode = propertyAccess.propertyName;
+    }
+    // check if element is assignable
+    if (element is PropertyAccessorElement) {
+      PropertyAccessorElement accessor = element as PropertyAccessorElement;
+      element = accessor.variable;
+    }
+    if (element is VariableElement) {
+      if (element.isConst) {
+        _errorReporter.reportErrorForNode(
+            StaticWarningCode.ASSIGNMENT_TO_CONST, expression);
+        return true;
+      }
+      if (element.isFinal) {
+        if (element is FieldElementImpl &&
+            element.setter == null &&
+            element.isSynthetic) {
+          _errorReporter.reportErrorForNode(
+              StaticWarningCode.ASSIGNMENT_TO_FINAL_NO_SETTER, highlightedNode,
+              [element.name, element.enclosingElement.displayName]);
+          return true;
+        }
+        _errorReporter.reportErrorForNode(StaticWarningCode.ASSIGNMENT_TO_FINAL,
+            highlightedNode, [element.name]);
+        return true;
+      }
+      return false;
+    }
+    if (element is FunctionElement) {
+      _errorReporter.reportErrorForNode(
+          StaticWarningCode.ASSIGNMENT_TO_FUNCTION, expression);
+      return true;
+    }
+    if (element is MethodElement) {
+      _errorReporter.reportErrorForNode(
+          StaticWarningCode.ASSIGNMENT_TO_METHOD, expression);
+      return true;
+    }
+    if (element is ClassElement ||
+        element is FunctionTypeAliasElement ||
+        element is TypeParameterElement) {
+      _errorReporter.reportErrorForNode(
+          StaticWarningCode.ASSIGNMENT_TO_TYPE, expression);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given [identifier] is not a keyword, and generates the
+   * given [errorCode] on the identifier if it is a keyword.
+   *
+   * See [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME],
+   * [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_PARAMETER_NAME], and
+   * [CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME].
+   */
+  bool _checkForBuiltInIdentifierAsName(
+      SimpleIdentifier identifier, ErrorCode errorCode) {
+    sc.Token token = identifier.token;
+    if (token.type == sc.TokenType.KEYWORD) {
+      _errorReporter.reportErrorForNode(
+          errorCode, identifier, [identifier.name]);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given [switchCase] is terminated with 'break', 'continue',
+   * 'return' or 'throw'.
+   *
+   * see [StaticWarningCode.CASE_BLOCK_NOT_TERMINATED].
+   */
+  bool _checkForCaseBlockNotTerminated(SwitchCase switchCase) {
+    NodeList<Statement> statements = switchCase.statements;
+    if (statements.isEmpty) {
+      // fall-through without statements at all
+      AstNode parent = switchCase.parent;
+      if (parent is SwitchStatement) {
+        SwitchStatement switchStatement = parent;
+        NodeList<SwitchMember> members = switchStatement.members;
+        int index = members.indexOf(switchCase);
+        if (index != -1 && index < members.length - 1) {
+          return false;
+        }
+      }
+      // no other switch member after this one
+    } else {
+      Statement statement = statements[statements.length - 1];
+      // terminated with statement
+      if (statement is BreakStatement ||
+          statement is ContinueStatement ||
+          statement is ReturnStatement) {
+        return false;
+      }
+      // terminated with 'throw' expression
+      if (statement is ExpressionStatement) {
+        Expression expression = statement.expression;
+        if (expression is ThrowExpression) {
+          return false;
+        }
+      }
+    }
+    // report error
+    _errorReporter.reportErrorForToken(
+        StaticWarningCode.CASE_BLOCK_NOT_TERMINATED, switchCase.keyword);
+    return true;
+  }
+
+  /**
+   * Verify that the switch cases in the given switch [statement] are terminated
+   * with 'break', 'continue', 'return' or 'throw'.
+   *
+   * See [StaticWarningCode.CASE_BLOCK_NOT_TERMINATED].
+   */
+  bool _checkForCaseBlocksNotTerminated(SwitchStatement statement) {
+    bool foundError = false;
+    NodeList<SwitchMember> members = statement.members;
+    int lastMember = members.length - 1;
+    for (int i = 0; i < lastMember; i++) {
+      SwitchMember member = members[i];
+      if (member is SwitchCase && _checkForCaseBlockNotTerminated(member)) {
+        foundError = true;
+      }
+    }
+    return foundError;
+  }
+
+  /**
+   * Verify that the given [method] declaration is abstract only if the
+   * enclosing class is also abstract.
+   *
+   * See [StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER].
+   */
+  bool _checkForConcreteClassWithAbstractMember(MethodDeclaration method) {
+    if (method.isAbstract &&
+        _enclosingClass != null &&
+        !_enclosingClass.isAbstract) {
+      SimpleIdentifier nameNode = method.name;
+      String memberName = nameNode.name;
+      ExecutableElement overriddenMember;
+      if (method.isGetter) {
+        overriddenMember = _enclosingClass.lookUpInheritedConcreteGetter(
+            memberName, _currentLibrary);
+      } else if (method.isSetter) {
+        overriddenMember = _enclosingClass.lookUpInheritedConcreteSetter(
+            memberName, _currentLibrary);
+      } else {
+        overriddenMember = _enclosingClass.lookUpInheritedConcreteMethod(
+            memberName, _currentLibrary);
+      }
+      if (overriddenMember == null) {
+        _errorReporter.reportErrorForNode(
+            StaticWarningCode.CONCRETE_CLASS_WITH_ABSTRACT_MEMBER, nameNode, [
+          memberName,
+          _enclosingClass.displayName
+        ]);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Verify all possible conflicts of the given [constructor]'s name with other
+   * constructors and members of the same class. The [constructorElement] is the
+   * constructor's element.
+   *
+   * See [CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_DEFAULT],
+   * [CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_NAME],
+   * [CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_FIELD], and
+   * [CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_METHOD].
+   */
+  bool _checkForConflictingConstructorNameAndMember(
+      ConstructorDeclaration constructor,
+      ConstructorElement constructorElement) {
+    SimpleIdentifier constructorName = constructor.name;
+    String name = constructorElement.name;
+    ClassElement classElement = constructorElement.enclosingElement;
+    // constructors
+    List<ConstructorElement> constructors = classElement.constructors;
+    for (ConstructorElement otherConstructor in constructors) {
+      if (identical(otherConstructor, constructorElement)) {
+        continue;
+      }
+      if (name == otherConstructor.name) {
+        if (name == null || name.length == 0) {
+          _errorReporter.reportErrorForNode(
+              CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_DEFAULT, constructor);
+        } else {
+          _errorReporter.reportErrorForNode(
+              CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_NAME, constructor,
+              [name]);
+        }
+        return true;
+      }
+    }
+    // conflict with class member
+    if (constructorName != null &&
+        constructorElement != null &&
+        !constructorName.isSynthetic) {
+      // fields
+      FieldElement field = classElement.getField(name);
+      if (field != null) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_FIELD,
+            constructor, [name]);
+        return true;
+      }
+      // methods
+      MethodElement method = classElement.getMethod(name);
+      if (method != null) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.CONFLICTING_CONSTRUCTOR_NAME_AND_METHOD,
+            constructor, [name]);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the [_enclosingClass] does not have a method and getter pair
+   * with the same name on, via inheritance.
+   *
+   * See [CompileTimeErrorCode.CONFLICTING_GETTER_AND_METHOD], and
+   * [CompileTimeErrorCode.CONFLICTING_METHOD_AND_GETTER].
+   */
+  bool _checkForConflictingGetterAndMethod() {
+    if (_enclosingClass == null) {
+      return false;
+    }
+    bool hasProblem = false;
+    // method declared in the enclosing class vs. inherited getter
+    for (MethodElement method in _enclosingClass.methods) {
+      String name = method.name;
+      // find inherited property accessor (and can be only getter)
+      ExecutableElement inherited =
+          _inheritanceManager.lookupInheritance(_enclosingClass, name);
+      if (inherited is! PropertyAccessorElement) {
+        continue;
+      }
+      // report problem
+      hasProblem = true;
+      _errorReporter.reportErrorForOffset(
+          CompileTimeErrorCode.CONFLICTING_GETTER_AND_METHOD, method.nameOffset,
+          name.length, [
+        _enclosingClass.displayName,
+        inherited.enclosingElement.displayName,
+        name
+      ]);
+    }
+    // getter declared in the enclosing class vs. inherited method
+    for (PropertyAccessorElement accessor in _enclosingClass.accessors) {
+      if (!accessor.isGetter) {
+        continue;
+      }
+      String name = accessor.name;
+      // find inherited method
+      ExecutableElement inherited =
+          _inheritanceManager.lookupInheritance(_enclosingClass, name);
+      if (inherited is! MethodElement) {
+        continue;
+      }
+      // report problem
+      hasProblem = true;
+      _errorReporter.reportErrorForOffset(
+          CompileTimeErrorCode.CONFLICTING_METHOD_AND_GETTER,
+          accessor.nameOffset, name.length, [
+        _enclosingClass.displayName,
+        inherited.enclosingElement.displayName,
+        name
+      ]);
+    }
+    // done
+    return hasProblem;
+  }
+
+  /**
+   * Verify that the superclass of the [_enclosingClass] does not declare
+   * accessible static members with the same name as the instance
+   * getters/setters declared in [_enclosingClass].
+   *
+   * See [StaticWarningCode.CONFLICTING_INSTANCE_GETTER_AND_SUPERCLASS_MEMBER], and
+   * [StaticWarningCode.CONFLICTING_INSTANCE_SETTER_AND_SUPERCLASS_MEMBER].
+   */
+  bool _checkForConflictingInstanceGetterAndSuperclassMember() {
+    if (_enclosingClass == null) {
+      return false;
+    }
+    InterfaceType enclosingType = _enclosingClass.type;
+    // check every accessor
+    bool hasProblem = false;
+    for (PropertyAccessorElement accessor in _enclosingClass.accessors) {
+      // we analyze instance accessors here
+      if (accessor.isStatic) {
+        continue;
+      }
+      // prepare accessor properties
+      String name = accessor.displayName;
+      bool getter = accessor.isGetter;
+      // if non-final variable, ignore setter - we alreay reported problem for
+      // getter
+      if (accessor.isSetter && accessor.isSynthetic) {
+        continue;
+      }
+      // try to find super element
+      ExecutableElement superElement;
+      superElement =
+          enclosingType.lookUpGetterInSuperclass(name, _currentLibrary);
+      if (superElement == null) {
+        superElement =
+            enclosingType.lookUpSetterInSuperclass(name, _currentLibrary);
+      }
+      if (superElement == null) {
+        superElement =
+            enclosingType.lookUpMethodInSuperclass(name, _currentLibrary);
+      }
+      if (superElement == null) {
+        continue;
+      }
+      // OK, not static
+      if (!superElement.isStatic) {
+        continue;
+      }
+      // prepare "super" type to report its name
+      ClassElement superElementClass =
+          superElement.enclosingElement as ClassElement;
+      InterfaceType superElementType = superElementClass.type;
+      // report problem
+      hasProblem = true;
+      if (getter) {
+        _errorReporter.reportErrorForElement(
+            StaticWarningCode.CONFLICTING_INSTANCE_GETTER_AND_SUPERCLASS_MEMBER,
+            accessor, [superElementType.displayName]);
+      } else {
+        _errorReporter.reportErrorForElement(
+            StaticWarningCode.CONFLICTING_INSTANCE_SETTER_AND_SUPERCLASS_MEMBER,
+            accessor, [superElementType.displayName]);
+      }
+    }
+    // done
+    return hasProblem;
+  }
+
+  /**
+   * Verify that the enclosing class does not have a setter with the same name
+   * as the given instance method declaration.
+   *
+   * TODO(jwren) add other "conflicting" error codes into algorithm/ data
+   * structure.
+   *
+   * See [StaticWarningCode.CONFLICTING_INSTANCE_METHOD_SETTER].
+   */
+  bool _checkForConflictingInstanceMethodSetter(ClassDeclaration declaration) {
+    // Reference all of the class members in this class.
+    NodeList<ClassMember> classMembers = declaration.members;
+    if (classMembers.isEmpty) {
+      return false;
+    }
+    // Create a HashMap to track conflicting members, and then loop through
+    // members in the class to construct the HashMap, at the same time,
+    // look for violations.  Don't add members if they are part of a conflict,
+    // this prevents multiple warnings for one issue.
+    bool foundError = false;
+    HashMap<String, ClassMember> memberHashMap =
+        new HashMap<String, ClassMember>();
+    for (ClassMember classMember in classMembers) {
+      if (classMember is MethodDeclaration) {
+        MethodDeclaration method = classMember;
+        if (method.isStatic) {
+          continue;
+        }
+        // prepare name
+        SimpleIdentifier name = method.name;
+        if (name == null) {
+          continue;
+        }
+        bool addThisMemberToTheMap = true;
+        bool isGetter = method.isGetter;
+        bool isSetter = method.isSetter;
+        bool isOperator = method.isOperator;
+        bool isMethod = !isGetter && !isSetter && !isOperator;
+        // Do lookups in the enclosing class (and the inherited member) if the
+        // member is a method or a setter for
+        // StaticWarningCode.CONFLICTING_INSTANCE_METHOD_SETTER warning.
+        if (isMethod) {
+          String setterName = "${name.name}=";
+          Element enclosingElementOfSetter = null;
+          ClassMember conflictingSetter = memberHashMap[setterName];
+          if (conflictingSetter != null) {
+            enclosingElementOfSetter =
+                conflictingSetter.element.enclosingElement;
+          } else {
+            ExecutableElement elementFromInheritance = _inheritanceManager
+                .lookupInheritance(_enclosingClass, setterName);
+            if (elementFromInheritance != null) {
+              enclosingElementOfSetter =
+                  elementFromInheritance.enclosingElement;
+            }
+          }
+          if (enclosingElementOfSetter != null) {
+            // report problem
+            _errorReporter.reportErrorForNode(
+                StaticWarningCode.CONFLICTING_INSTANCE_METHOD_SETTER, name, [
+              _enclosingClass.displayName,
+              name.name,
+              enclosingElementOfSetter.displayName
+            ]);
+            foundError = true;
+            addThisMemberToTheMap = false;
+          }
+        } else if (isSetter) {
+          String methodName = name.name;
+          ClassMember conflictingMethod = memberHashMap[methodName];
+          if (conflictingMethod != null &&
+              conflictingMethod is MethodDeclaration &&
+              !conflictingMethod.isGetter) {
+            // report problem
+            _errorReporter.reportErrorForNode(
+                StaticWarningCode.CONFLICTING_INSTANCE_METHOD_SETTER2, name, [
+              _enclosingClass.displayName,
+              name.name
+            ]);
+            foundError = true;
+            addThisMemberToTheMap = false;
+          }
+        }
+        // Finally, add this member into the HashMap.
+        if (addThisMemberToTheMap) {
+          if (method.isSetter) {
+            memberHashMap["${name.name}="] = method;
+          } else {
+            memberHashMap[name.name] = method;
+          }
+        }
+      }
+    }
+    return foundError;
+  }
+
+  /**
+   * Verify that the enclosing class does not have an instance member with the
+   * same name as the given static [method] declaration.
+   *
+   * See [StaticWarningCode.CONFLICTING_STATIC_GETTER_AND_INSTANCE_SETTER].
+   */
+  bool _checkForConflictingStaticGetterAndInstanceSetter(
+      MethodDeclaration method) {
+    if (!method.isStatic) {
+      return false;
+    }
+    // prepare name
+    SimpleIdentifier nameNode = method.name;
+    if (nameNode == null) {
+      return false;
+    }
+    String name = nameNode.name;
+    // prepare enclosing type
+    if (_enclosingClass == null) {
+      return false;
+    }
+    InterfaceType enclosingType = _enclosingClass.type;
+    // try to find setter
+    ExecutableElement setter =
+        enclosingType.lookUpSetter(name, _currentLibrary);
+    if (setter == null) {
+      return false;
+    }
+    // OK, also static
+    if (setter.isStatic) {
+      return false;
+    }
+    // prepare "setter" type to report its name
+    ClassElement setterClass = setter.enclosingElement as ClassElement;
+    InterfaceType setterType = setterClass.type;
+    // report problem
+    _errorReporter.reportErrorForNode(
+        StaticWarningCode.CONFLICTING_STATIC_GETTER_AND_INSTANCE_SETTER,
+        nameNode, [setterType.displayName]);
+    return true;
+  }
+
+  /**
+   * Verify that the enclosing class does not have an instance member with the
+   * same name as the given static [method] declaration.
+   *
+   * See [StaticWarningCode.CONFLICTING_STATIC_SETTER_AND_INSTANCE_MEMBER].
+   */
+  bool _checkForConflictingStaticSetterAndInstanceMember(
+      MethodDeclaration method) {
+    if (!method.isStatic) {
+      return false;
+    }
+    // prepare name
+    SimpleIdentifier nameNode = method.name;
+    if (nameNode == null) {
+      return false;
+    }
+    String name = nameNode.name;
+    // prepare enclosing type
+    if (_enclosingClass == null) {
+      return false;
+    }
+    InterfaceType enclosingType = _enclosingClass.type;
+    // try to find member
+    ExecutableElement member;
+    member = enclosingType.lookUpMethod(name, _currentLibrary);
+    if (member == null) {
+      member = enclosingType.lookUpGetter(name, _currentLibrary);
+    }
+    if (member == null) {
+      member = enclosingType.lookUpSetter(name, _currentLibrary);
+    }
+    if (member == null) {
+      return false;
+    }
+    // OK, also static
+    if (member.isStatic) {
+      return false;
+    }
+    // prepare "member" type to report its name
+    ClassElement memberClass = member.enclosingElement as ClassElement;
+    InterfaceType memberType = memberClass.type;
+    // report problem
+    _errorReporter.reportErrorForNode(
+        StaticWarningCode.CONFLICTING_STATIC_SETTER_AND_INSTANCE_MEMBER,
+        nameNode, [memberType.displayName]);
+    return true;
+  }
+
+  /**
+   * Verify all conflicts between type variable and enclosing class.
+   * TODO(scheglov)
+   *
+   * See [CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_CLASS], and
+   * [CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER].
+   */
+  bool _checkForConflictingTypeVariableErrorCodes(
+      ClassDeclaration declaration) {
+    bool problemReported = false;
+    for (TypeParameterElement typeParameter in _enclosingClass.typeParameters) {
+      String name = typeParameter.name;
+      // name is same as the name of the enclosing class
+      if (_enclosingClass.name == name) {
+        _errorReporter.reportErrorForOffset(
+            CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_CLASS,
+            typeParameter.nameOffset, name.length, [name]);
+        problemReported = true;
+      }
+      // check members
+      if (_enclosingClass.getMethod(name) != null ||
+          _enclosingClass.getGetter(name) != null ||
+          _enclosingClass.getSetter(name) != null) {
+        _errorReporter.reportErrorForOffset(
+            CompileTimeErrorCode.CONFLICTING_TYPE_VARIABLE_AND_MEMBER,
+            typeParameter.nameOffset, name.length, [name]);
+        problemReported = true;
+      }
+    }
+    return problemReported;
+  }
+
+  /**
+   * Verify that if the given [constructor] declaration is 'const' then there
+   * are no invocations of non-'const' super constructors.
+   *
+   * See [CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER].
+   */
+  bool _checkForConstConstructorWithNonConstSuper(
+      ConstructorDeclaration constructor) {
+    if (!_isEnclosingConstructorConst) {
+      return false;
+    }
+    // OK, const factory, checked elsewhere
+    if (constructor.factoryKeyword != null) {
+      return false;
+    }
+    // check for mixins
+    if (_enclosingClass.mixins.length != 0) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_MIXIN,
+          constructor.returnType);
+      return true;
+    }
+    // try to find and check super constructor invocation
+    for (ConstructorInitializer initializer in constructor.initializers) {
+      if (initializer is SuperConstructorInvocation) {
+        SuperConstructorInvocation superInvocation = initializer;
+        ConstructorElement element = superInvocation.staticElement;
+        if (element == null || element.isConst) {
+          return false;
+        }
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER,
+            superInvocation, [element.enclosingElement.displayName]);
+        return true;
+      }
+    }
+    // no explicit super constructor invocation, check default constructor
+    InterfaceType supertype = _enclosingClass.supertype;
+    if (supertype == null) {
+      return false;
+    }
+    if (supertype.isObject) {
+      return false;
+    }
+    ConstructorElement unnamedConstructor =
+        supertype.element.unnamedConstructor;
+    if (unnamedConstructor == null) {
+      return false;
+    }
+    if (unnamedConstructor.isConst) {
+      return false;
+    }
+    // default constructor is not 'const', report problem
+    _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER,
+        constructor.returnType, [supertype.displayName]);
+    return true;
+  }
+
+  /**
+   * Verify that if the given [constructor] declaration is 'const' then there
+   * are no non-final instance variable. The [constructorElement] is the
+   * constructor element.
+   *
+   * See [CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD].
+   */
+  bool _checkForConstConstructorWithNonFinalField(
+      ConstructorDeclaration constructor,
+      ConstructorElement constructorElement) {
+    if (!_isEnclosingConstructorConst) {
+      return false;
+    }
+    // check if there is non-final field
+    ClassElement classElement = constructorElement.enclosingElement;
+    if (!classElement.hasNonFinalField) {
+      return false;
+    }
+    // report problem
+    _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD,
+        constructor);
+    return true;
+  }
+
+  /**
+   * Verify that the given 'const' instance creation [expression] is not
+   * creating a deferred type. The [constructorName] is the constructor name,
+   * always non-`null`. The [typeName] is the name of the type defining the
+   * constructor, always non-`null`.
+   *
+   * See [CompileTimeErrorCode.CONST_DEFERRED_CLASS].
+   */
+  bool _checkForConstDeferredClass(InstanceCreationExpression expression,
+      ConstructorName constructorName, TypeName typeName) {
+    if (typeName.isDeferred) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.CONST_DEFERRED_CLASS, constructorName,
+          [typeName.name.name]);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given throw [expression] is not enclosed in a 'const'
+   * constructor declaration.
+   *
+   * See [CompileTimeErrorCode.CONST_CONSTRUCTOR_THROWS_EXCEPTION].
+   */
+  bool _checkForConstEvalThrowsException(ThrowExpression expression) {
+    if (_isEnclosingConstructorConst) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.CONST_CONSTRUCTOR_THROWS_EXCEPTION, expression);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given normal formal [parameter] is not 'const'.
+   *
+   * See [CompileTimeErrorCode.CONST_FORMAL_PARAMETER].
+   */
+  bool _checkForConstFormalParameter(NormalFormalParameter parameter) {
+    if (parameter.isConst) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.CONST_FORMAL_PARAMETER, parameter);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given instance creation [expression] is not being invoked
+   * on an abstract class. The [typeName] is the [TypeName] of the
+   * [ConstructorName] from the [InstanceCreationExpression], this is the AST
+   * node that the error is attached to. The [type] is the type being
+   * constructed with this [InstanceCreationExpression].
+   *
+   * See [StaticWarningCode.CONST_WITH_ABSTRACT_CLASS], and
+   * [StaticWarningCode.NEW_WITH_ABSTRACT_CLASS].
+   */
+  bool _checkForConstOrNewWithAbstractClass(
+      InstanceCreationExpression expression, TypeName typeName,
+      InterfaceType type) {
+    if (type.element.isAbstract) {
+      ConstructorElement element = expression.staticElement;
+      if (element != null && !element.isFactory) {
+        if ((expression.keyword as sc.KeywordToken).keyword ==
+            sc.Keyword.CONST) {
+          _errorReporter.reportErrorForNode(
+              StaticWarningCode.CONST_WITH_ABSTRACT_CLASS, typeName);
+        } else {
+          _errorReporter.reportErrorForNode(
+              StaticWarningCode.NEW_WITH_ABSTRACT_CLASS, typeName);
+        }
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given instance creation [expression] is not being invoked
+   * on an enum. The [typeName] is the [TypeName] of the [ConstructorName] from
+   * the [InstanceCreationExpression], this is the AST node that the error is
+   * attached to. The [type] is the type being constructed with this
+   * [InstanceCreationExpression].
+   *
+   * See [CompileTimeErrorCode.INSTANTIATE_ENUM].
+   */
+  bool _checkForConstOrNewWithEnum(InstanceCreationExpression expression,
+      TypeName typeName, InterfaceType type) {
+    if (type.element.isEnum) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.INSTANTIATE_ENUM, typeName);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given 'const' instance creation [expression] is not being
+   * invoked on a constructor that is not 'const'.
+   *
+   * This method assumes that the instance creation was tested to be 'const'
+   * before being called.
+   *
+   * See [CompileTimeErrorCode.CONST_WITH_NON_CONST].
+   */
+  bool _checkForConstWithNonConst(InstanceCreationExpression expression) {
+    ConstructorElement constructorElement = expression.staticElement;
+    if (constructorElement != null && !constructorElement.isConst) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.CONST_WITH_NON_CONST, expression);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given [typeName] does not reference any type parameters.
+   *
+   * See [CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS].
+   */
+  bool _checkForConstWithTypeParameters(TypeName typeName) {
+    // something wrong with AST
+    if (typeName == null) {
+      return false;
+    }
+    Identifier name = typeName.name;
+    if (name == null) {
+      return false;
+    }
+    // should not be a type parameter
+    if (name.staticElement is TypeParameterElement) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS, name);
+    }
+    // check type arguments
+    TypeArgumentList typeArguments = typeName.typeArguments;
+    if (typeArguments != null) {
+      bool hasError = false;
+      for (TypeName argument in typeArguments.arguments) {
+        if (_checkForConstWithTypeParameters(argument)) {
+          hasError = true;
+        }
+      }
+      return hasError;
+    }
+    // OK
+    return false;
+  }
+
+  /**
+   * Verify that if the given 'const' instance creation [expression] is being
+   * invoked on the resolved constructor. The [constructorName] is the
+   * constructor name, always non-`null`. The [typeName] is the name of the type
+   * defining the constructor, always non-`null`.
+   *
+   * This method assumes that the instance creation was tested to be 'const'
+   * before being called.
+   *
+   * See [CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR], and
+   * [CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT].
+   */
+  bool _checkForConstWithUndefinedConstructor(
+      InstanceCreationExpression expression, ConstructorName constructorName,
+      TypeName typeName) {
+    // OK if resolved
+    if (expression.staticElement != null) {
+      return false;
+    }
+    DartType type = typeName.type;
+    if (type is InterfaceType) {
+      ClassElement element = type.element;
+      if (element != null && element.isEnum) {
+        // We have already reported the error.
+        return false;
+      }
+    }
+    Identifier className = typeName.name;
+    // report as named or default constructor absence
+    SimpleIdentifier name = constructorName.name;
+    if (name != null) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR, name, [
+        className,
+        name
+      ]);
+    } else {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.CONST_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT,
+          constructorName, [className]);
+    }
+    return true;
+  }
+
+  /**
+   * Verify that there are no default parameters in the given function type
+   * [alias].
+   *
+   * See [CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPE_ALIAS].
+   */
+  bool _checkForDefaultValueInFunctionTypeAlias(FunctionTypeAlias alias) {
+    bool result = false;
+    FormalParameterList formalParameterList = alias.parameters;
+    NodeList<FormalParameter> parameters = formalParameterList.parameters;
+    for (FormalParameter formalParameter in parameters) {
+      if (formalParameter is DefaultFormalParameter) {
+        DefaultFormalParameter defaultFormalParameter = formalParameter;
+        if (defaultFormalParameter.defaultValue != null) {
+          _errorReporter.reportErrorForNode(
+              CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPE_ALIAS, alias);
+          result = true;
+        }
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Verify that the given default formal [parameter] is not part of a function
+   * typed parameter.
+   *
+   * See [CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPED_PARAMETER].
+   */
+  bool _checkForDefaultValueInFunctionTypedParameter(
+      DefaultFormalParameter parameter) {
+    // OK, not in a function typed parameter.
+    if (!_isInFunctionTypedFormalParameter) {
+      return false;
+    }
+    // OK, no default value.
+    if (parameter.defaultValue == null) {
+      return false;
+    }
+    // Report problem.
+    _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPED_PARAMETER,
+        parameter);
+    return true;
+  }
+
+  /**
+   * Verify that any deferred imports in the given compilation [unit] have a
+   * unique prefix.
+   *
+   * See [CompileTimeErrorCode.SHARED_DEFERRED_PREFIX].
+   */
+  bool _checkForDeferredPrefixCollisions(CompilationUnit unit) {
+    bool foundError = false;
+    NodeList<Directive> directives = unit.directives;
+    int count = directives.length;
+    if (count > 0) {
+      HashMap<PrefixElement, List<ImportDirective>> prefixToDirectivesMap =
+          new HashMap<PrefixElement, List<ImportDirective>>();
+      for (int i = 0; i < count; i++) {
+        Directive directive = directives[i];
+        if (directive is ImportDirective) {
+          ImportDirective importDirective = directive;
+          SimpleIdentifier prefix = importDirective.prefix;
+          if (prefix != null) {
+            Element element = prefix.staticElement;
+            if (element is PrefixElement) {
+              PrefixElement prefixElement = element;
+              List<ImportDirective> elements =
+                  prefixToDirectivesMap[prefixElement];
+              if (elements == null) {
+                elements = new List<ImportDirective>();
+                prefixToDirectivesMap[prefixElement] = elements;
+              }
+              elements.add(importDirective);
+            }
+          }
+        }
+      }
+      for (List<ImportDirective> imports in prefixToDirectivesMap.values) {
+        if (_hasDeferredPrefixCollision(imports)) {
+          foundError = true;
+        }
+      }
+    }
+    return foundError;
+  }
+
+  /**
+   * Verify that the enclosing class does not have an instance member with the
+   * given name of the static member.
+   *
+   * See [CompileTimeErrorCode.DUPLICATE_DEFINITION_INHERITANCE].
+   */
+  bool _checkForDuplicateDefinitionInheritance() {
+    if (_enclosingClass == null) {
+      return false;
+    }
+    bool hasProblem = false;
+    for (ExecutableElement member in _enclosingClass.methods) {
+      if (member.isStatic && _checkForDuplicateDefinitionOfMember(member)) {
+        hasProblem = true;
+      }
+    }
+    for (ExecutableElement member in _enclosingClass.accessors) {
+      if (member.isStatic && _checkForDuplicateDefinitionOfMember(member)) {
+        hasProblem = true;
+      }
+    }
+    return hasProblem;
+  }
+
+  /**
+   * Verify that the enclosing class does not have an instance member with the
+   * given name of the [staticMember].
+   *
+   * See [CompileTimeErrorCode.DUPLICATE_DEFINITION_INHERITANCE].
+   */
+  bool _checkForDuplicateDefinitionOfMember(ExecutableElement staticMember) {
+    // prepare name
+    String name = staticMember.name;
+    if (name == null) {
+      return false;
+    }
+    // try to find member
+    ExecutableElement inheritedMember =
+        _inheritanceManager.lookupInheritance(_enclosingClass, name);
+    if (inheritedMember == null) {
+      return false;
+    }
+    // OK, also static
+    if (inheritedMember.isStatic) {
+      return false;
+    }
+    // determine the display name, use the extended display name if the
+    // enclosing class of the inherited member is in a different source
+    String displayName;
+    Element enclosingElement = inheritedMember.enclosingElement;
+    if (enclosingElement.source == _enclosingClass.source) {
+      displayName = enclosingElement.displayName;
+    } else {
+      displayName = enclosingElement.getExtendedDisplayName(null);
+    }
+    // report problem
+    _errorReporter.reportErrorForOffset(
+        CompileTimeErrorCode.DUPLICATE_DEFINITION_INHERITANCE,
+        staticMember.nameOffset, name.length, [name, displayName]);
+    return true;
+  }
+
+  /**
+   * Verify that if the given list [literal] has type arguments then there is
+   * exactly one. The [typeArguments] are the type arguments.
+   *
+   * See [StaticTypeWarningCode.EXPECTED_ONE_LIST_TYPE_ARGUMENTS].
+   */
+  bool _checkForExpectedOneListTypeArgument(
+      ListLiteral literal, TypeArgumentList typeArguments) {
+    // check number of type arguments
+    int num = typeArguments.arguments.length;
+    if (num == 1) {
+      return false;
+    }
+    // report problem
+    _errorReporter.reportErrorForNode(
+        StaticTypeWarningCode.EXPECTED_ONE_LIST_TYPE_ARGUMENTS, typeArguments,
+        [num]);
+    return true;
+  }
+
+  /**
+   * Verify that the given export [directive] has a unique name among other
+   * exported libraries. The [exportElement] is the [ExportElement] retrieved
+   * from the node, if the element in the node was `null`, then this method is
+   * not called. The [exportedLibrary] is the library element containing the
+   * exported element.
+   *
+   * See [CompileTimeErrorCode.EXPORT_DUPLICATED_LIBRARY_NAME].
+   */
+  bool _checkForExportDuplicateLibraryName(ExportDirective directive,
+      ExportElement exportElement, LibraryElement exportedLibrary) {
+    if (exportedLibrary == null) {
+      return false;
+    }
+    String name = exportedLibrary.name;
+    // check if there is other exported library with the same name
+    LibraryElement prevLibrary = _nameToExportElement[name];
+    if (prevLibrary != null) {
+      if (prevLibrary != exportedLibrary) {
+        if (name.isEmpty) {
+          _errorReporter.reportErrorForNode(
+              StaticWarningCode.EXPORT_DUPLICATED_LIBRARY_UNNAMED, directive, [
+            prevLibrary.definingCompilationUnit.displayName,
+            exportedLibrary.definingCompilationUnit.displayName
+          ]);
+        } else {
+          _errorReporter.reportErrorForNode(
+              StaticWarningCode.EXPORT_DUPLICATED_LIBRARY_NAMED, directive, [
+            prevLibrary.definingCompilationUnit.displayName,
+            exportedLibrary.definingCompilationUnit.displayName,
+            name
+          ]);
+        }
+        return true;
+      }
+    } else {
+      _nameToExportElement[name] = exportedLibrary;
+    }
+    // OK
+    return false;
+  }
+
+  /**
+   * Check that if the visiting library is not system, then any given library
+   * should not be SDK internal library. The [exportElement] is the
+   * [ExportElement] retrieved from the node, if the element in the node was
+   * `null`, then this method is not called.
+   *
+   * See [CompileTimeErrorCode.EXPORT_INTERNAL_LIBRARY].
+   */
+  bool _checkForExportInternalLibrary(
+      ExportDirective directive, ExportElement exportElement) {
+    if (_isInSystemLibrary) {
+      return false;
+    }
+    // should be private
+    DartSdk sdk = _currentLibrary.context.sourceFactory.dartSdk;
+    String uri = exportElement.uri;
+    SdkLibrary sdkLibrary = sdk.getSdkLibrary(uri);
+    if (sdkLibrary == null) {
+      return false;
+    }
+    if (!sdkLibrary.isInternal) {
+      return false;
+    }
+    // report problem
+    _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.EXPORT_INTERNAL_LIBRARY, directive,
+        [directive.uri]);
+    return true;
+  }
+
+  /**
+   * Verify that the given extends [clause] does not extend a deferred class.
+   *
+   * See [CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS].
+   */
+  bool _checkForExtendsDeferredClass(ExtendsClause clause) {
+    if (clause == null) {
+      return false;
+    }
+    return _checkForExtendsOrImplementsDeferredClass(
+        clause.superclass, CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS);
+  }
+
+  /**
+   * Verify that the given type [alias] does not extend a deferred class.
+   *
+   * See [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS].
+   */
+  bool _checkForExtendsDeferredClassInTypeAlias(ClassTypeAlias alias) {
+    if (alias == null) {
+      return false;
+    }
+    return _checkForExtendsOrImplementsDeferredClass(
+        alias.superclass, CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS);
+  }
+
+  /**
+   * Verify that the given extends [clause] does not extend classes such as
+   * 'num' or 'String'.
+   *
+   * See [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS].
+   */
+  bool _checkForExtendsDisallowedClass(ExtendsClause clause) {
+    if (clause == null) {
+      return false;
+    }
+    return _checkForExtendsOrImplementsDisallowedClass(
+        clause.superclass, CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS);
+  }
+
+  /**
+   * Verify that the given type [alias] does not extend classes such as 'num' or
+   * 'String'.
+   *
+   * See [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS].
+   */
+  bool _checkForExtendsDisallowedClassInTypeAlias(ClassTypeAlias alias) {
+    if (alias == null) {
+      return false;
+    }
+    return _checkForExtendsOrImplementsDisallowedClass(
+        alias.superclass, CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS);
+  }
+
+  /**
+   * Verify that the given [typeName] does not extend, implement or mixin
+   * classes that are deferred.
+   *
+   * See [_checkForExtendsDeferredClass],
+   * [_checkForExtendsDeferredClassInTypeAlias],
+   * [_checkForImplementsDeferredClass],
+   * [_checkForAllMixinErrorCodes],
+   * [CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS],
+   * [CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS], and
+   * [CompileTimeErrorCode.MIXIN_DEFERRED_CLASS].
+   */
+  bool _checkForExtendsOrImplementsDeferredClass(
+      TypeName typeName, ErrorCode errorCode) {
+    if (typeName.isSynthetic) {
+      return false;
+    }
+    if (typeName.isDeferred) {
+      _errorReporter.reportErrorForNode(
+          errorCode, typeName, [typeName.name.name]);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given [typeName] does not extend, implement or mixin
+   * classes such as 'num' or 'String'.
+   *
+   * See [_checkForExtendsDisallowedClass],
+   * [_checkForExtendsDisallowedClassInTypeAlias],
+   * [_checkForImplementsDisallowedClass],
+   * [_checkForAllMixinErrorCodes],
+   * [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS],
+   * [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS], and
+   * [CompileTimeErrorCode.MIXIN_OF_DISALLOWED_CLASS].
+   */
+  bool _checkForExtendsOrImplementsDisallowedClass(
+      TypeName typeName, ErrorCode errorCode) {
+    if (typeName.isSynthetic) {
+      return false;
+    }
+    DartType superType = typeName.type;
+    for (InterfaceType disallowedType
+        in _DISALLOWED_TYPES_TO_EXTEND_OR_IMPLEMENT) {
+      if (superType != null && superType == disallowedType) {
+        // if the violating type happens to be 'num', we need to rule out the
+        // case where the enclosing class is 'int' or 'double'
+        if (superType == _typeProvider.numType) {
+          AstNode grandParent = typeName.parent.parent;
+          // Note: this is a corner case that won't happen often, so adding a
+          // field currentClass (see currentFunction) to ErrorVerifier isn't
+          // worth if for this case, but if the field currentClass is added,
+          // then this message should become a todo to not lookup the
+          // grandparent node.
+          if (grandParent is ClassDeclaration) {
+            ClassElement classElement = grandParent.element;
+            DartType classType = classElement.type;
+            if (classType != null &&
+                (classType == _intType ||
+                    classType == _typeProvider.doubleType)) {
+              return false;
+            }
+          }
+        }
+        // otherwise, report the error
+        _errorReporter.reportErrorForNode(
+            errorCode, typeName, [disallowedType.displayName]);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given constructor field [initializer] has compatible field
+   * and initializer expression types. The [staticElement] is the static element
+   * from the name in the [ConstructorFieldInitializer].
+   *
+   * See [CompileTimeErrorCode.CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE], and
+   * [StaticWarningCode.FIELD_INITIALIZER_NOT_ASSIGNABLE].
+   */
+  bool _checkForFieldInitializerNotAssignable(
+      ConstructorFieldInitializer initializer, Element staticElement) {
+    // prepare field element
+    if (staticElement is! FieldElement) {
+      return false;
+    }
+    FieldElement fieldElement = staticElement as FieldElement;
+    // prepare field type
+    DartType fieldType = fieldElement.type;
+    // prepare expression type
+    Expression expression = initializer.expression;
+    if (expression == null) {
+      return false;
+    }
+    // test the static type of the expression
+    DartType staticType = getStaticType(expression);
+    if (staticType == null) {
+      return false;
+    }
+    if (staticType.isAssignableTo(fieldType)) {
+      return false;
+    }
+    // report problem
+    if (_isEnclosingConstructorConst) {
+      // TODO(paulberry): this error should be based on the actual type of the
+      // constant, not the static type.  See dartbug.com/21119.
+      _errorReporter.reportTypeErrorForNode(
+          CheckedModeCompileTimeErrorCode.CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE,
+          expression, [staticType, fieldType]);
+    }
+    _errorReporter.reportTypeErrorForNode(
+        StaticWarningCode.FIELD_INITIALIZER_NOT_ASSIGNABLE, expression, [
+      staticType,
+      fieldType
+    ]);
+    return true;
+    // TODO(brianwilkerson) Define a hint corresponding to these errors and
+    // report it if appropriate.
+//        // test the propagated type of the expression
+//        Type propagatedType = expression.getPropagatedType();
+//        if (propagatedType != null && propagatedType.isAssignableTo(fieldType)) {
+//          return false;
+//        }
+//        // report problem
+//        if (isEnclosingConstructorConst) {
+//          errorReporter.reportTypeErrorForNode(
+//              CompileTimeErrorCode.CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE,
+//              expression,
+//              propagatedType == null ? staticType : propagatedType,
+//              fieldType);
+//        } else {
+//          errorReporter.reportTypeErrorForNode(
+//              StaticWarningCode.FIELD_INITIALIZER_NOT_ASSIGNABLE,
+//              expression,
+//              propagatedType == null ? staticType : propagatedType,
+//              fieldType);
+//        }
+//        return true;
+  }
+
+  /**
+   * Verify that the given field formal [parameter] is in a constructor
+   * declaration.
+   *
+   * See [CompileTimeErrorCode.FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR].
+   */
+  bool _checkForFieldInitializingFormalRedirectingConstructor(
+      FieldFormalParameter parameter) {
+    ConstructorDeclaration constructor =
+        parameter.getAncestor((node) => node is ConstructorDeclaration);
+    if (constructor == null) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR,
+          parameter);
+      return true;
+    }
+    // constructor cannot be a factory
+    if (constructor.factoryKeyword != null) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.FIELD_INITIALIZER_FACTORY_CONSTRUCTOR,
+          parameter);
+      return true;
+    }
+    // constructor cannot have a redirection
+    for (ConstructorInitializer initializer in constructor.initializers) {
+      if (initializer is RedirectingConstructorInvocation) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR,
+            parameter);
+        return true;
+      }
+    }
+    // OK
+    return false;
+  }
+
+  /**
+   * Verify that the given variable declaration [list] has only initialized
+   * variables if the list is final or const.
+   *
+   * See [CompileTimeErrorCode.CONST_NOT_INITIALIZED], and
+   * [StaticWarningCode.FINAL_NOT_INITIALIZED].
+   */
+  bool _checkForFinalNotInitialized(VariableDeclarationList list) {
+    if (_isInNativeClass) {
+      return false;
+    }
+    bool foundError = false;
+    if (!list.isSynthetic) {
+      NodeList<VariableDeclaration> variables = list.variables;
+      for (VariableDeclaration variable in variables) {
+        if (variable.initializer == null) {
+          if (list.isConst) {
+            _errorReporter.reportErrorForNode(
+                CompileTimeErrorCode.CONST_NOT_INITIALIZED, variable.name,
+                [variable.name.name]);
+          } else if (list.isFinal) {
+            _errorReporter.reportErrorForNode(
+                StaticWarningCode.FINAL_NOT_INITIALIZED, variable.name,
+                [variable.name.name]);
+          }
+          foundError = true;
+        }
+      }
+    }
+    return foundError;
+  }
+
+  /**
+   * Verify that final fields in the given clas [declaration] that are declared,
+   * without any constructors in the enclosing class, are initialized. Cases in
+   * which there is at least one constructor are handled at the end of
+   * [_checkForAllFinalInitializedErrorCodes].
+   *
+   * See [CompileTimeErrorCode.CONST_NOT_INITIALIZED], and
+   * [StaticWarningCode.FINAL_NOT_INITIALIZED].
+   */
+  bool _checkForFinalNotInitializedInClass(ClassDeclaration declaration) {
+    NodeList<ClassMember> classMembers = declaration.members;
+    for (ClassMember classMember in classMembers) {
+      if (classMember is ConstructorDeclaration) {
+        return false;
+      }
+    }
+    bool foundError = false;
+    for (ClassMember classMember in classMembers) {
+      if (classMember is FieldDeclaration &&
+          _checkForFinalNotInitialized(classMember.fields)) {
+        foundError = true;
+      }
+    }
+    return foundError;
+  }
+
+  /**
+   * If the current function is async, async*, or sync*, verify that its
+   * declared return type is assignable to Future, Stream, or Iterable,
+   * respectively.  If not, report the error using [returnType].
+   */
+  void _checkForIllegalReturnType(TypeName returnType) {
+    if (returnType == null) {
+      // No declared return type, so the return type must be dynamic, which is
+      // assignable to everything.
+      return;
+    }
+    if (_enclosingFunction.isAsynchronous) {
+      if (_enclosingFunction.isGenerator) {
+        if (!_enclosingFunction.returnType
+            .isAssignableTo(_typeProvider.streamDynamicType)) {
+          _errorReporter.reportErrorForNode(
+              StaticTypeWarningCode.ILLEGAL_ASYNC_GENERATOR_RETURN_TYPE,
+              returnType);
+        }
+      } else {
+        if (!_enclosingFunction.returnType
+            .isAssignableTo(_typeProvider.futureDynamicType)) {
+          _errorReporter.reportErrorForNode(
+              StaticTypeWarningCode.ILLEGAL_ASYNC_RETURN_TYPE, returnType);
+        }
+      }
+    } else if (_enclosingFunction.isGenerator) {
+      if (!_enclosingFunction.returnType
+          .isAssignableTo(_typeProvider.iterableDynamicType)) {
+        _errorReporter.reportErrorForNode(
+            StaticTypeWarningCode.ILLEGAL_SYNC_GENERATOR_RETURN_TYPE,
+            returnType);
+      }
+    }
+  }
+
+  /**
+   * Verify that the given implements [clause] does not implement classes that
+   * are deferred.
+   *
+   * See [CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS].
+   */
+  bool _checkForImplementsDeferredClass(ImplementsClause clause) {
+    if (clause == null) {
+      return false;
+    }
+    bool foundError = false;
+    for (TypeName type in clause.interfaces) {
+      if (_checkForExtendsOrImplementsDeferredClass(
+          type, CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS)) {
+        foundError = true;
+      }
+    }
+    return foundError;
+  }
+
+  /**
+   * Verify that the given implements [clause] does not implement classes such
+   * as 'num' or 'String'.
+   *
+   * See [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS].
+   */
+  bool _checkForImplementsDisallowedClass(ImplementsClause clause) {
+    if (clause == null) {
+      return false;
+    }
+    bool foundError = false;
+    for (TypeName type in clause.interfaces) {
+      if (_checkForExtendsOrImplementsDisallowedClass(
+          type, CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS)) {
+        foundError = true;
+      }
+    }
+    return foundError;
+  }
+
+  /**
+   * Verify that if the given [identifier] is part of a constructor initializer,
+   * then it does not implicitly reference 'this' expression.
+   *
+   * See [CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER], and
+   * [CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC].
+   * TODO(scheglov) rename thid method
+   */
+  bool _checkForImplicitThisReferenceInInitializer(
+      SimpleIdentifier identifier) {
+    if (!_isInConstructorInitializer &&
+        !_isInStaticMethod &&
+        !_isInFactory &&
+        !_isInInstanceVariableInitializer &&
+        !_isInStaticVariableDeclaration) {
+      return false;
+    }
+    // prepare element
+    Element element = identifier.staticElement;
+    if (!(element is MethodElement || element is PropertyAccessorElement)) {
+      return false;
+    }
+    // static element
+    ExecutableElement executableElement = element as ExecutableElement;
+    if (executableElement.isStatic) {
+      return false;
+    }
+    // not a class member
+    Element enclosingElement = element.enclosingElement;
+    if (enclosingElement is! ClassElement) {
+      return false;
+    }
+    // comment
+    AstNode parent = identifier.parent;
+    if (parent is CommentReference) {
+      return false;
+    }
+    // qualified method invocation
+    if (parent is MethodInvocation) {
+      MethodInvocation invocation = parent;
+      if (identical(invocation.methodName, identifier) &&
+          invocation.realTarget != null) {
+        return false;
+      }
+    }
+    // qualified property access
+    if (parent is PropertyAccess) {
+      PropertyAccess access = parent;
+      if (identical(access.propertyName, identifier) &&
+          access.realTarget != null) {
+        return false;
+      }
+    }
+    if (parent is PrefixedIdentifier) {
+      PrefixedIdentifier prefixed = parent;
+      if (identical(prefixed.identifier, identifier)) {
+        return false;
+      }
+    }
+    // report problem
+    if (_isInStaticMethod) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC, identifier);
+    } else if (_isInFactory) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, identifier);
+    } else {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER,
+          identifier);
+    }
+    return true;
+  }
+
+  /**
+   * Verify that the given import [directive] has a unique name among other
+   * imported libraries. The [importElement] is the [ImportElement] retrieved
+   * from the node, if the element in the node was `null`, then this method is
+   * not called.
+   *
+   * See [CompileTimeErrorCode.IMPORT_DUPLICATED_LIBRARY_NAME].
+   */
+  bool _checkForImportDuplicateLibraryName(
+      ImportDirective directive, ImportElement importElement) {
+    // prepare imported library
+    LibraryElement nodeLibrary = importElement.importedLibrary;
+    if (nodeLibrary == null) {
+      return false;
+    }
+    String name = nodeLibrary.name;
+    // check if there is another imported library with the same name
+    LibraryElement prevLibrary = _nameToImportElement[name];
+    if (prevLibrary != null) {
+      if (prevLibrary != nodeLibrary) {
+        if (name.isEmpty) {
+          _errorReporter.reportErrorForNode(
+              StaticWarningCode.IMPORT_DUPLICATED_LIBRARY_UNNAMED, directive, [
+            prevLibrary.definingCompilationUnit.displayName,
+            nodeLibrary.definingCompilationUnit.displayName
+          ]);
+        } else {
+          _errorReporter.reportErrorForNode(
+              StaticWarningCode.IMPORT_DUPLICATED_LIBRARY_NAMED, directive, [
+            prevLibrary.definingCompilationUnit.displayName,
+            nodeLibrary.definingCompilationUnit.displayName,
+            name
+          ]);
+        }
+        return true;
+      }
+    } else {
+      _nameToImportElement[name] = nodeLibrary;
+    }
+    // OK
+    return false;
+  }
+
+  /**
+   * Check that if the visiting library is not system, then any given library
+   * should not be SDK internal library. The [importElement] is the
+   * [ImportElement] retrieved from the node, if the element in the node was
+   * `null`, then this method is not called
+   *
+   * See [CompileTimeErrorCode.IMPORT_INTERNAL_LIBRARY].
+   */
+  bool _checkForImportInternalLibrary(
+      ImportDirective directive, ImportElement importElement) {
+    if (_isInSystemLibrary) {
+      return false;
+    }
+    // should be private
+    DartSdk sdk = _currentLibrary.context.sourceFactory.dartSdk;
+    String uri = importElement.uri;
+    SdkLibrary sdkLibrary = sdk.getSdkLibrary(uri);
+    if (sdkLibrary == null) {
+      return false;
+    }
+    if (!sdkLibrary.isInternal) {
+      return false;
+    }
+    // report problem
+    _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.IMPORT_INTERNAL_LIBRARY, directive,
+        [directive.uri]);
+    return true;
+  }
+
+  /**
+   * For each class declaration, this method is called which verifies that all
+   * inherited members are inherited consistently.
+   *
+   * See [StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE].
+   */
+  bool _checkForInconsistentMethodInheritance() {
+    // Ensure that the inheritance manager has a chance to generate all errors
+    // we may care about, note that we ensure that the interfaces data since
+    // there are no errors.
+    _inheritanceManager.getMapOfMembersInheritedFromInterfaces(_enclosingClass);
+    HashSet<AnalysisError> errors =
+        _inheritanceManager.getErrors(_enclosingClass);
+    if (errors == null || errors.isEmpty) {
+      return false;
+    }
+    for (AnalysisError error in errors) {
+      _errorReporter.reportError(error);
+    }
+    return true;
+  }
+
+  /**
+   * Check that the given [typeReference] is not a type reference and that then
+   * the [name] is reference to an instance member.
+   *
+   * See [StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER].
+   */
+  bool _checkForInstanceAccessToStaticMember(
+      ClassElement typeReference, SimpleIdentifier name) {
+    // OK, in comment
+    if (_isInComment) {
+      return false;
+    }
+    // OK, target is a type
+    if (typeReference != null) {
+      return false;
+    }
+    // prepare member Element
+    Element element = name.staticElement;
+    if (element is! ExecutableElement) {
+      return false;
+    }
+    ExecutableElement executableElement = element as ExecutableElement;
+    // OK, top-level element
+    if (executableElement.enclosingElement is! ClassElement) {
+      return false;
+    }
+    // OK, instance member
+    if (!executableElement.isStatic) {
+      return false;
+    }
+    // report problem
+    _errorReporter.reportErrorForNode(
+        StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER, name,
+        [name.name]);
+    return true;
+  }
+
+  /**
+   * Check whether the given [executableElement] collides with the name of a
+   * static method in one of its superclasses, and reports the appropriate
+   * warning if it does. The [errorNameTarget] is the node to report problems
+   * on.
+   *
+   * See [StaticTypeWarningCode.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC].
+   */
+  bool _checkForInstanceMethodNameCollidesWithSuperclassStatic(
+      ExecutableElement executableElement, SimpleIdentifier errorNameTarget) {
+    String executableElementName = executableElement.name;
+    if (executableElement is! PropertyAccessorElement &&
+        !executableElement.isOperator) {
+      HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
+      InterfaceType superclassType = _enclosingClass.supertype;
+      ClassElement superclassElement =
+          superclassType == null ? null : superclassType.element;
+      bool executableElementPrivate =
+          Identifier.isPrivateName(executableElementName);
+      while (superclassElement != null &&
+          !visitedClasses.contains(superclassElement)) {
+        visitedClasses.add(superclassElement);
+        LibraryElement superclassLibrary = superclassElement.library;
+        // Check fields.
+        FieldElement fieldElt =
+            superclassElement.getField(executableElementName);
+        if (fieldElt != null) {
+          // Ignore if private in a different library - cannot collide.
+          if (executableElementPrivate &&
+              _currentLibrary != superclassLibrary) {
+            continue;
+          }
+          // instance vs. static
+          if (fieldElt.isStatic) {
+            _errorReporter.reportErrorForNode(
+                StaticWarningCode.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC,
+                errorNameTarget, [
+              executableElementName,
+              fieldElt.enclosingElement.displayName
+            ]);
+            return true;
+          }
+        }
+        // Check methods.
+        List<MethodElement> methodElements = superclassElement.methods;
+        for (MethodElement methodElement in methodElements) {
+          // We need the same name.
+          if (methodElement.name != executableElementName) {
+            continue;
+          }
+          // Ignore if private in a different library - cannot collide.
+          if (executableElementPrivate &&
+              _currentLibrary != superclassLibrary) {
+            continue;
+          }
+          // instance vs. static
+          if (methodElement.isStatic) {
+            _errorReporter.reportErrorForNode(
+                StaticWarningCode.INSTANCE_METHOD_NAME_COLLIDES_WITH_SUPERCLASS_STATIC,
+                errorNameTarget, [
+              executableElementName,
+              methodElement.enclosingElement.displayName
+            ]);
+            return true;
+          }
+        }
+        superclassType = superclassElement.supertype;
+        superclassElement =
+            superclassType == null ? null : superclassType.element;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Verify that an 'int' can be assigned to the parameter corresponding to the
+   * given [argument]. This is used for prefix and postfix expressions where
+   * the argument value is implicit.
+   *
+   * See [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
+   */
+  bool _checkForIntNotAssignable(Expression argument) {
+    if (argument == null) {
+      return false;
+    }
+    ParameterElement staticParameterElement = argument.staticParameterElement;
+    DartType staticParameterType =
+        staticParameterElement == null ? null : staticParameterElement.type;
+    return _checkForArgumentTypeNotAssignable(argument, staticParameterType,
+        _intType, StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE);
+  }
+
+  /**
+   * Verify that the given [annotation] isn't defined in a deferred library.
+   *
+   * See [CompileTimeErrorCode.INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY].
+   */
+  bool _checkForInvalidAnnotationFromDeferredLibrary(Annotation annotation) {
+    Identifier nameIdentifier = annotation.name;
+    if (nameIdentifier is PrefixedIdentifier) {
+      if (nameIdentifier.isDeferred) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.INVALID_ANNOTATION_FROM_DEFERRED_LIBRARY,
+            annotation.name);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given left hand side ([lhs]) and right hand side ([rhs])
+   * represent a valid assignment.
+   *
+   * See [StaticTypeWarningCode.INVALID_ASSIGNMENT].
+   */
+  bool _checkForInvalidAssignment(Expression lhs, Expression rhs) {
+    if (lhs == null || rhs == null) {
+      return false;
+    }
+    VariableElement leftVariableElement = getVariableElement(lhs);
+    DartType leftType = (leftVariableElement == null)
+        ? getStaticType(lhs)
+        : leftVariableElement.type;
+    DartType staticRightType = getStaticType(rhs);
+    if (!staticRightType.isAssignableTo(leftType)) {
+      _errorReporter.reportTypeErrorForNode(
+          StaticTypeWarningCode.INVALID_ASSIGNMENT, rhs, [
+        staticRightType,
+        leftType
+      ]);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Given an [assignment] using a compound assignment operator, this verifies
+   * that the given assignment is valid. The [lhs] is the left hand side
+   * expression. The [rhs] is the right hand side expression.
+   *
+   * See [StaticTypeWarningCode.INVALID_ASSIGNMENT].
+   */
+  bool _checkForInvalidCompoundAssignment(
+      AssignmentExpression assignment, Expression lhs, Expression rhs) {
+    if (lhs == null) {
+      return false;
+    }
+    VariableElement leftVariableElement = getVariableElement(lhs);
+    DartType leftType = (leftVariableElement == null)
+        ? getStaticType(lhs)
+        : leftVariableElement.type;
+    MethodElement invokedMethod = assignment.staticElement;
+    if (invokedMethod == null) {
+      return false;
+    }
+    DartType rightType = invokedMethod.type.returnType;
+    if (leftType == null || rightType == null) {
+      return false;
+    }
+    if (!rightType.isAssignableTo(leftType)) {
+      _errorReporter.reportTypeErrorForNode(
+          StaticTypeWarningCode.INVALID_ASSIGNMENT, rhs, [rightType, leftType]);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Check the given [initializer] to ensure that the field being initialized is
+   * a valid field. The [fieldName] is the field name from the
+   * [ConstructorFieldInitializer]. The [staticElement] is the static element
+   * from the name in the [ConstructorFieldInitializer].
+   */
+  void _checkForInvalidField(ConstructorFieldInitializer initializer,
+      SimpleIdentifier fieldName, Element staticElement) {
+    if (staticElement is FieldElement) {
+      FieldElement fieldElement = staticElement;
+      if (fieldElement.isSynthetic) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTENT_FIELD,
+            initializer, [fieldName]);
+      } else if (fieldElement.isStatic) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.INITIALIZER_FOR_STATIC_FIELD, initializer,
+            [fieldName]);
+      }
+    } else {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTENT_FIELD, initializer,
+          [fieldName]);
+      return;
+    }
+  }
+
+  /**
+   * Check to see whether the given function [body] has a modifier associated
+   * with it, and report it as an error if it does.
+   */
+  bool _checkForInvalidModifierOnBody(
+      FunctionBody body, CompileTimeErrorCode errorCode) {
+    sc.Token keyword = body.keyword;
+    if (keyword != null) {
+      _errorReporter.reportErrorForToken(errorCode, keyword, [keyword.lexeme]);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the usage of the given 'this' is valid.
+   *
+   * See [CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS].
+   */
+  bool _checkForInvalidReferenceToThis(ThisExpression expression) {
+    if (!_isThisInValidContext(expression)) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, expression);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Checks to ensure that the given list of type [arguments] does not have a
+   * type parameter as a type argument. The [errorCode] is either
+   * [CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_LIST] or
+   * [CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_MAP].
+   */
+  bool _checkForInvalidTypeArgumentInConstTypedLiteral(
+      NodeList<TypeName> arguments, ErrorCode errorCode) {
+    bool foundError = false;
+    for (TypeName typeName in arguments) {
+      if (typeName.type is TypeParameterType) {
+        _errorReporter.reportErrorForNode(errorCode, typeName, [typeName.name]);
+        foundError = true;
+      }
+    }
+    return foundError;
+  }
+
+  /**
+   * Verify that the elements given list [literal] are subtypes of the specified
+   * element type. The [typeArguments] are the type arguments.
+   *
+   * See [CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE], and
+   * [StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE].
+   */
+  bool _checkForListElementTypeNotAssignable(
+      ListLiteral literal, TypeArgumentList typeArguments) {
+    NodeList<TypeName> typeNames = typeArguments.arguments;
+    if (typeNames.length < 1) {
+      return false;
+    }
+    DartType listElementType = typeNames[0].type;
+    // Check every list element.
+    bool hasProblems = false;
+    for (Expression element in literal.elements) {
+      if (literal.constKeyword != null) {
+        // TODO(paulberry): this error should be based on the actual type of the
+        // list element, not the static type.  See dartbug.com/21119.
+        if (_checkForArgumentTypeNotAssignableWithExpectedTypes(element,
+            listElementType,
+            CheckedModeCompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE)) {
+          hasProblems = true;
+        }
+      }
+      if (_checkForArgumentTypeNotAssignableWithExpectedTypes(element,
+          listElementType,
+          StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE)) {
+        hasProblems = true;
+      }
+    }
+    return hasProblems;
+  }
+
+  /**
+   * Verify that the key/value of entries of the given map [literal] are
+   * subtypes of the key/value types specified in the type arguments. The
+   * [typeArguments] are the type arguments.
+   *
+   * See [CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE],
+   * [CompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE],
+   * [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE], and
+   * [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE].
+   */
+  bool _checkForMapTypeNotAssignable(
+      MapLiteral literal, TypeArgumentList typeArguments) {
+    // Prepare maps key/value types.
+    NodeList<TypeName> typeNames = typeArguments.arguments;
+    if (typeNames.length < 2) {
+      return false;
+    }
+    DartType keyType = typeNames[0].type;
+    DartType valueType = typeNames[1].type;
+    // Check every map entry.
+    bool hasProblems = false;
+    NodeList<MapLiteralEntry> entries = literal.entries;
+    for (MapLiteralEntry entry in entries) {
+      Expression key = entry.key;
+      Expression value = entry.value;
+      if (literal.constKeyword != null) {
+        // TODO(paulberry): this error should be based on the actual type of the
+        // list element, not the static type.  See dartbug.com/21119.
+        if (_checkForArgumentTypeNotAssignableWithExpectedTypes(key, keyType,
+            CheckedModeCompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE)) {
+          hasProblems = true;
+        }
+        if (_checkForArgumentTypeNotAssignableWithExpectedTypes(value,
+            valueType,
+            CheckedModeCompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE)) {
+          hasProblems = true;
+        }
+      }
+      if (_checkForArgumentTypeNotAssignableWithExpectedTypes(
+          key, keyType, StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE)) {
+        hasProblems = true;
+      }
+      if (_checkForArgumentTypeNotAssignableWithExpectedTypes(
+          value, valueType, StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE)) {
+        hasProblems = true;
+      }
+    }
+    return hasProblems;
+  }
+
+  /**
+   * Verify that the [_enclosingClass] does not define members with the same name
+   * as the enclosing class.
+   *
+   * See [CompileTimeErrorCode.MEMBER_WITH_CLASS_NAME].
+   */
+  bool _checkForMemberWithClassName() {
+    if (_enclosingClass == null) {
+      return false;
+    }
+    String className = _enclosingClass.name;
+    if (className == null) {
+      return false;
+    }
+    bool problemReported = false;
+    // check accessors
+    for (PropertyAccessorElement accessor in _enclosingClass.accessors) {
+      if (className == accessor.name) {
+        _errorReporter.reportErrorForOffset(
+            CompileTimeErrorCode.MEMBER_WITH_CLASS_NAME, accessor.nameOffset,
+            className.length);
+        problemReported = true;
+      }
+    }
+    // don't check methods, they would be constructors
+    // done
+    return problemReported;
+  }
+
+  /**
+   * Check to make sure that all similarly typed accessors are of the same type
+   * (including inherited accessors).
+   *
+   * See [StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES], and
+   * [StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE].
+   */
+  bool _checkForMismatchedAccessorTypes(
+      Declaration accessorDeclaration, String accessorTextName) {
+    ExecutableElement accessorElement =
+        accessorDeclaration.element as ExecutableElement;
+    if (accessorElement is! PropertyAccessorElement) {
+      return false;
+    }
+    PropertyAccessorElement propertyAccessorElement =
+        accessorElement as PropertyAccessorElement;
+    PropertyAccessorElement counterpartAccessor = null;
+    ClassElement enclosingClassForCounterpart = null;
+    if (propertyAccessorElement.isGetter) {
+      counterpartAccessor = propertyAccessorElement.correspondingSetter;
+    } else {
+      counterpartAccessor = propertyAccessorElement.correspondingGetter;
+      // If the setter and getter are in the same enclosing element, return,
+      // this prevents having MISMATCHED_GETTER_AND_SETTER_TYPES reported twice.
+      if (counterpartAccessor != null &&
+          identical(counterpartAccessor.enclosingElement,
+              propertyAccessorElement.enclosingElement)) {
+        return false;
+      }
+    }
+    if (counterpartAccessor == null) {
+      // If the accessor is declared in a class, check the superclasses.
+      if (_enclosingClass != null) {
+        // Figure out the correct identifier to lookup in the inheritance graph,
+        // if 'x', then 'x=', or if 'x=', then 'x'.
+        String lookupIdentifier = propertyAccessorElement.name;
+        if (StringUtilities.endsWithChar(lookupIdentifier, 0x3D)) {
+          lookupIdentifier =
+              lookupIdentifier.substring(0, lookupIdentifier.length - 1);
+        } else {
+          lookupIdentifier += "=";
+        }
+        // lookup with the identifier.
+        ExecutableElement elementFromInheritance = _inheritanceManager
+            .lookupInheritance(_enclosingClass, lookupIdentifier);
+        // Verify that we found something, and that it is an accessor
+        if (elementFromInheritance != null &&
+            elementFromInheritance is PropertyAccessorElement) {
+          enclosingClassForCounterpart =
+              elementFromInheritance.enclosingElement as ClassElement;
+          counterpartAccessor = elementFromInheritance;
+        }
+      }
+      if (counterpartAccessor == null) {
+        return false;
+      }
+    }
+    // Default of null == no accessor or no type (dynamic)
+    DartType getterType = null;
+    DartType setterType = null;
+    // Get an existing counterpart accessor if any.
+    if (propertyAccessorElement.isGetter) {
+      getterType = _getGetterType(propertyAccessorElement);
+      setterType = _getSetterType(counterpartAccessor);
+    } else if (propertyAccessorElement.isSetter) {
+      setterType = _getSetterType(propertyAccessorElement);
+      getterType = _getGetterType(counterpartAccessor);
+    }
+    // If either types are not assignable to each other, report an error
+    // (if the getter is null, it is dynamic which is assignable to everything).
+    if (setterType != null &&
+        getterType != null &&
+        !getterType.isAssignableTo(setterType)) {
+      if (enclosingClassForCounterpart == null) {
+        _errorReporter.reportTypeErrorForNode(
+            StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES,
+            accessorDeclaration, [accessorTextName, setterType, getterType]);
+        return true;
+      } else {
+        _errorReporter.reportTypeErrorForNode(
+            StaticWarningCode.MISMATCHED_GETTER_AND_SETTER_TYPES_FROM_SUPERTYPE,
+            accessorDeclaration, [
+          accessorTextName,
+          setterType,
+          getterType,
+          enclosingClassForCounterpart.displayName
+        ]);
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Check to make sure that the given switch [statement] whose static type is
+   * an enum type either have a default case or include all of the enum
+   * constants.
+   */
+  bool _checkForMissingEnumConstantInSwitch(SwitchStatement statement) {
+    // TODO(brianwilkerson) This needs to be checked after constant values have
+    // been computed.
+    Expression expression = statement.expression;
+    DartType expressionType = getStaticType(expression);
+    if (expressionType == null) {
+      return false;
+    }
+    Element expressionElement = expressionType.element;
+    if (expressionElement is! ClassElement) {
+      return false;
+    }
+    ClassElement classElement = expressionElement as ClassElement;
+    if (!classElement.isEnum) {
+      return false;
+    }
+    List<String> constantNames = new List<String>();
+    List<FieldElement> fields = classElement.fields;
+    int fieldCount = fields.length;
+    for (int i = 0; i < fieldCount; i++) {
+      FieldElement field = fields[i];
+      if (field.isStatic && !field.isSynthetic) {
+        constantNames.add(field.name);
+      }
+    }
+    NodeList<SwitchMember> members = statement.members;
+    int memberCount = members.length;
+    for (int i = 0; i < memberCount; i++) {
+      SwitchMember member = members[i];
+      if (member is SwitchDefault) {
+        return false;
+      }
+      String constantName = _getConstantName((member as SwitchCase).expression);
+      if (constantName != null) {
+        constantNames.remove(constantName);
+      }
+    }
+    int nameCount = constantNames.length;
+    if (nameCount == 0) {
+      return false;
+    }
+    for (int i = 0; i < nameCount; i++) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.MISSING_ENUM_CONSTANT_IN_SWITCH, statement,
+          [constantNames[i]]);
+    }
+    return true;
+  }
+
+  /**
+   * Verify that the given function [body] does not contain return statements
+   * that both have and do not have return values.
+   *
+   * See [StaticWarningCode.MIXED_RETURN_TYPES].
+   */
+  bool _checkForMixedReturns(BlockFunctionBody body) {
+    if (_hasReturnWithoutValue) {
+      return false;
+    }
+    int withCount = _returnsWith.length;
+    int withoutCount = _returnsWithout.length;
+    if (withCount > 0 && withoutCount > 0) {
+      for (int i = 0; i < withCount; i++) {
+        _errorReporter.reportErrorForToken(StaticWarningCode.MIXED_RETURN_TYPES,
+            _returnsWith[i].returnKeyword);
+      }
+      for (int i = 0; i < withoutCount; i++) {
+        _errorReporter.reportErrorForToken(StaticWarningCode.MIXED_RETURN_TYPES,
+            _returnsWithout[i].returnKeyword);
+      }
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given mixin does not have an explicitly declared
+   * constructor. The [mixinName] is the node to report problem on. The
+   * [mixinElement] is the mixing to evaluate.
+   *
+   * See [CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR].
+   */
+  bool _checkForMixinDeclaresConstructor(
+      TypeName mixinName, ClassElement mixinElement) {
+    for (ConstructorElement constructor in mixinElement.constructors) {
+      if (!constructor.isSynthetic && !constructor.isFactory) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.MIXIN_DECLARES_CONSTRUCTOR, mixinName,
+            [mixinElement.name]);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given mixin has the 'Object' superclass. The [mixinName] is
+   * the node to report problem on. The [mixinElement] is the mixing to
+   * evaluate.
+   *
+   * See [CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT].
+   */
+  bool _checkForMixinInheritsNotFromObject(
+      TypeName mixinName, ClassElement mixinElement) {
+    InterfaceType mixinSupertype = mixinElement.supertype;
+    if (mixinSupertype != null) {
+      if (!mixinSupertype.isObject ||
+          !mixinElement.isTypedef && mixinElement.mixins.length != 0) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.MIXIN_INHERITS_FROM_NOT_OBJECT, mixinName,
+            [mixinElement.name]);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given mixin does not reference 'super'. The [mixinName] is
+   * the node to report problem on. The [mixinElement] is the mixing to
+   * evaluate.
+   *
+   * See [CompileTimeErrorCode.MIXIN_REFERENCES_SUPER].
+   */
+  bool _checkForMixinReferencesSuper(
+      TypeName mixinName, ClassElement mixinElement) {
+    if (mixinElement.hasReferenceToSuper) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.MIXIN_REFERENCES_SUPER, mixinName,
+          [mixinElement.name]);
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given [constructor] has at most one 'super' initializer.
+   *
+   * See [CompileTimeErrorCode.MULTIPLE_SUPER_INITIALIZERS].
+   */
+  bool _checkForMultipleSuperInitializers(ConstructorDeclaration constructor) {
+    int numSuperInitializers = 0;
+    for (ConstructorInitializer initializer in constructor.initializers) {
+      if (initializer is SuperConstructorInvocation) {
+        numSuperInitializers++;
+        if (numSuperInitializers > 1) {
+          _errorReporter.reportErrorForNode(
+              CompileTimeErrorCode.MULTIPLE_SUPER_INITIALIZERS, initializer);
+        }
+      }
+    }
+    return numSuperInitializers > 0;
+  }
+
+  /**
+   * Checks to ensure that the given native function [body] is in SDK code.
+   *
+   * See [ParserErrorCode.NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE].
+   */
+  bool _checkForNativeFunctionBodyInNonSDKCode(NativeFunctionBody body) {
+    if (!_isInSystemLibrary && !_hasExtUri) {
+      _errorReporter.reportErrorForNode(
+          ParserErrorCode.NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE, body);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given instance creation [expression] invokes an existing
+   * constructor. The [constructorName] is the constructor name. The [typeName]
+   * is the name of the type defining the constructor.
+   *
+   * This method assumes that the instance creation was tested to be 'new'
+   * before being called.
+   *
+   * See [StaticWarningCode.NEW_WITH_UNDEFINED_CONSTRUCTOR].
+   */
+  bool _checkForNewWithUndefinedConstructor(
+      InstanceCreationExpression expression, ConstructorName constructorName,
+      TypeName typeName) {
+    // OK if resolved
+    if (expression.staticElement != null) {
+      return false;
+    }
+    DartType type = typeName.type;
+    if (type is InterfaceType) {
+      ClassElement element = type.element;
+      if (element != null && element.isEnum) {
+        // We have already reported the error.
+        return false;
+      }
+    }
+    // prepare class name
+    Identifier className = typeName.name;
+    // report as named or default constructor absence
+    SimpleIdentifier name = constructorName.name;
+    if (name != null) {
+      _errorReporter.reportErrorForNode(
+          StaticWarningCode.NEW_WITH_UNDEFINED_CONSTRUCTOR, name, [
+        className,
+        name
+      ]);
+    } else {
+      _errorReporter.reportErrorForNode(
+          StaticWarningCode.NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT,
+          constructorName, [className]);
+    }
+    return true;
+  }
+
+  /**
+   * Check that if the given class [declaration] implicitly calls default
+   * constructor of its superclass, there should be such default constructor -
+   * implicit or explicit.
+   *
+   * See [CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT].
+   */
+  bool _checkForNoDefaultSuperConstructorImplicit(
+      ClassDeclaration declaration) {
+    // do nothing if mixin errors have already been reported for this class.
+    ClassElementImpl enclosingClass = _enclosingClass;
+    if (enclosingClass.mixinErrorsReported) {
+      return false;
+    }
+    // do nothing if there is explicit constructor
+    List<ConstructorElement> constructors = _enclosingClass.constructors;
+    if (!constructors[0].isSynthetic) {
+      return false;
+    }
+    // prepare super
+    InterfaceType superType = _enclosingClass.supertype;
+    if (superType == null) {
+      return false;
+    }
+    ClassElement superElement = superType.element;
+    // try to find default generative super constructor
+    ConstructorElement superUnnamedConstructor =
+        superElement.unnamedConstructor;
+    if (superUnnamedConstructor != null) {
+      if (superUnnamedConstructor.isFactory) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR, declaration.name,
+            [superUnnamedConstructor]);
+        return true;
+      }
+      if (superUnnamedConstructor.isDefaultConstructor &&
+          _enclosingClass
+              .isSuperConstructorAccessible(superUnnamedConstructor)) {
+        return true;
+      }
+    }
+    // report problem
+    _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT,
+        declaration.name, [superType.displayName]);
+    return true;
+  }
+
+  /**
+   * Check that the given class declaration overrides all members required by
+   * its superclasses and interfaces. The [classNameNode] is the
+   * [SimpleIdentifier] to be used if there is a violation, this is either the
+   * named from the [ClassDeclaration] or from the [ClassTypeAlias].
+   *
+   * See [StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE],
+   * [StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_TWO],
+   * [StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_THREE],
+   * [StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR], and
+   * [StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_PLUS].
+   */
+  bool _checkForNonAbstractClassInheritsAbstractMember(
+      SimpleIdentifier classNameNode) {
+    if (_enclosingClass.isAbstract) {
+      return false;
+    }
+    //
+    // Store in local sets the set of all method and accessor names
+    //
+    MethodElement method =
+        _enclosingClass.getMethod(FunctionElement.NO_SUCH_METHOD_METHOD_NAME);
+    if (method != null) {
+      // If the enclosing class declares the method noSuchMethod(), then return.
+      // From Spec:  It is a static warning if a concrete class does not have an
+      // implementation for a method in any of its superinterfaces unless it
+      // declares its own noSuchMethod method (7.10).
+      return false;
+    }
+    HashSet<ExecutableElement> missingOverrides =
+        new HashSet<ExecutableElement>();
+    //
+    // Loop through the set of all executable elements declared in the implicit
+    // interface.
+    //
+    MemberMap membersInheritedFromInterfaces = _inheritanceManager
+        .getMapOfMembersInheritedFromInterfaces(_enclosingClass);
+    MemberMap membersInheritedFromSuperclasses = _inheritanceManager
+        .getMapOfMembersInheritedFromClasses(_enclosingClass);
+    for (int i = 0; i < membersInheritedFromInterfaces.size; i++) {
+      String memberName = membersInheritedFromInterfaces.getKey(i);
+      ExecutableElement executableElt =
+          membersInheritedFromInterfaces.getValue(i);
+      if (memberName == null) {
+        break;
+      }
+      // If the element is not synthetic and can be determined to be defined in
+      // Object, skip it.
+      if (executableElt.enclosingElement != null &&
+          (executableElt.enclosingElement as ClassElement).type.isObject) {
+        continue;
+      }
+      // Check to see if some element is in local enclosing class that matches
+      // the name of the required member.
+      if (_isMemberInClassOrMixin(executableElt, _enclosingClass)) {
+        // We do not have to verify that this implementation of the found method
+        // matches the required function type: the set of
+        // StaticWarningCode.INVALID_METHOD_OVERRIDE_* warnings break out the
+        // different specific situations.
+        continue;
+      }
+      // First check to see if this element was declared in the superclass
+      // chain, in which case there is already a concrete implementation.
+      ExecutableElement elt = membersInheritedFromSuperclasses.get(memberName);
+      // Check to see if an element was found in the superclass chain with the
+      // correct name.
+      if (elt != null) {
+        // Reference the types, if any are null then continue.
+        InterfaceType enclosingType = _enclosingClass.type;
+        FunctionType concreteType = elt.type;
+        FunctionType requiredMemberType = executableElt.type;
+        if (enclosingType == null ||
+            concreteType == null ||
+            requiredMemberType == null) {
+          continue;
+        }
+        // Some element was found in the superclass chain that matches the name
+        // of the required member.
+        // If it is not abstract and it is the correct one (types match- the
+        // version of this method that we have has the correct number of
+        // parameters, etc), then this class has a valid implementation of this
+        // method, so skip it.
+        if ((elt is MethodElement && !elt.isAbstract) ||
+            (elt is PropertyAccessorElement && !elt.isAbstract)) {
+          // Since we are comparing two function types, we need to do the
+          // appropriate type substitutions first ().
+          FunctionType foundConcreteFT = _inheritanceManager
+              .substituteTypeArgumentsInMemberFromInheritance(
+                  concreteType, memberName, enclosingType);
+          FunctionType requiredMemberFT = _inheritanceManager
+              .substituteTypeArgumentsInMemberFromInheritance(
+                  requiredMemberType, memberName, enclosingType);
+          if (foundConcreteFT.isSubtypeOf(requiredMemberFT)) {
+            continue;
+          }
+        }
+      }
+      // The not qualifying concrete executable element was found, add it to the
+      // list.
+      missingOverrides.add(executableElt);
+    }
+    // Now that we have the set of missing overrides, generate a warning on this
+    // class.
+    int missingOverridesSize = missingOverrides.length;
+    if (missingOverridesSize == 0) {
+      return false;
+    }
+    List<ExecutableElement> missingOverridesArray =
+        new List.from(missingOverrides);
+    List<String> stringMembersArrayListSet = new List<String>();
+    for (int i = 0; i < missingOverridesArray.length; i++) {
+      String newStrMember;
+      Element enclosingElement = missingOverridesArray[i].enclosingElement;
+      String prefix = StringUtilities.EMPTY;
+      if (missingOverridesArray[i] is PropertyAccessorElement) {
+        PropertyAccessorElement propertyAccessorElement =
+            missingOverridesArray[i] as PropertyAccessorElement;
+        if (propertyAccessorElement.isGetter) {
+          prefix = _GETTER_SPACE;
+          // "getter "
+        } else {
+          prefix = _SETTER_SPACE;
+          // "setter "
+        }
+      }
+      if (enclosingElement != null) {
+        newStrMember =
+            "$prefix'${enclosingElement.displayName}.${missingOverridesArray[i].displayName}'";
+      } else {
+        newStrMember = "$prefix'${missingOverridesArray[i].displayName}'";
+      }
+      stringMembersArrayListSet.add(newStrMember);
+    }
+    List<String> stringMembersArray = new List.from(stringMembersArrayListSet);
+    AnalysisErrorWithProperties analysisError;
+    if (stringMembersArray.length == 1) {
+      analysisError = _errorReporter.newErrorWithProperties(
+          StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_ONE,
+          classNameNode, [stringMembersArray[0]]);
+    } else if (stringMembersArray.length == 2) {
+      analysisError = _errorReporter.newErrorWithProperties(
+          StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_TWO,
+          classNameNode, [stringMembersArray[0], stringMembersArray[1]]);
+    } else if (stringMembersArray.length == 3) {
+      analysisError = _errorReporter.newErrorWithProperties(
+          StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_THREE,
+          classNameNode, [
+        stringMembersArray[0],
+        stringMembersArray[1],
+        stringMembersArray[2]
+      ]);
+    } else if (stringMembersArray.length == 4) {
+      analysisError = _errorReporter.newErrorWithProperties(
+          StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FOUR,
+          classNameNode, [
+        stringMembersArray[0],
+        stringMembersArray[1],
+        stringMembersArray[2],
+        stringMembersArray[3]
+      ]);
+    } else {
+      analysisError = _errorReporter.newErrorWithProperties(
+          StaticWarningCode.NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER_FIVE_PLUS,
+          classNameNode, [
+        stringMembersArray[0],
+        stringMembersArray[1],
+        stringMembersArray[2],
+        stringMembersArray[3],
+        stringMembersArray.length - 4
+      ]);
+    }
+    analysisError.setProperty(
+        ErrorProperty.UNIMPLEMENTED_METHODS, missingOverridesArray);
+    _errorReporter.reportError(analysisError);
+    return true;
+  }
+
+  /**
+   * Check to ensure that the [condition] is of type bool, are. Otherwise an
+   * error is reported on the expression.
+   *
+   * See [StaticTypeWarningCode.NON_BOOL_CONDITION].
+   */
+  bool _checkForNonBoolCondition(Expression condition) {
+    DartType conditionType = getStaticType(condition);
+    if (conditionType != null && !conditionType.isAssignableTo(_boolType)) {
+      _errorReporter.reportErrorForNode(
+          StaticTypeWarningCode.NON_BOOL_CONDITION, condition);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given assert [statement] has either a 'bool' or
+   * '() -> bool' input.
+   *
+   * See [StaticTypeWarningCode.NON_BOOL_EXPRESSION].
+   */
+  bool _checkForNonBoolExpression(AssertStatement statement) {
+    Expression expression = statement.condition;
+    DartType type = getStaticType(expression);
+    if (type is InterfaceType) {
+      if (!type.isAssignableTo(_boolType)) {
+        _errorReporter.reportErrorForNode(
+            StaticTypeWarningCode.NON_BOOL_EXPRESSION, expression);
+        return true;
+      }
+    } else if (type is FunctionType) {
+      FunctionType functionType = type;
+      if (functionType.typeArguments.length == 0 &&
+          !functionType.returnType.isAssignableTo(_boolType)) {
+        _errorReporter.reportErrorForNode(
+            StaticTypeWarningCode.NON_BOOL_EXPRESSION, expression);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Checks to ensure that the given [expression] is assignable to bool.
+   *
+   * See [StaticTypeWarningCode.NON_BOOL_NEGATION_EXPRESSION].
+   */
+  bool _checkForNonBoolNegationExpression(Expression expression) {
+    DartType conditionType = getStaticType(expression);
+    if (conditionType != null && !conditionType.isAssignableTo(_boolType)) {
+      _errorReporter.reportErrorForNode(
+          StaticTypeWarningCode.NON_BOOL_NEGATION_EXPRESSION, expression);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify the given map [literal] either:
+   * * has `const modifier`
+   * * has explicit type arguments
+   * * is not start of the statement
+   *
+   * See [CompileTimeErrorCode.NON_CONST_MAP_AS_EXPRESSION_STATEMENT].
+   */
+  bool _checkForNonConstMapAsExpressionStatement(MapLiteral literal) {
+    // "const"
+    if (literal.constKeyword != null) {
+      return false;
+    }
+    // has type arguments
+    if (literal.typeArguments != null) {
+      return false;
+    }
+    // prepare statement
+    Statement statement =
+        literal.getAncestor((node) => node is ExpressionStatement);
+    if (statement == null) {
+      return false;
+    }
+    // OK, statement does not start with map
+    if (!identical(statement.beginToken, literal.beginToken)) {
+      return false;
+    }
+    // report problem
+    _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.NON_CONST_MAP_AS_EXPRESSION_STATEMENT, literal);
+    return true;
+  }
+
+  /**
+   * Verify that the given method [declaration] of operator `[]=`, has `void`
+   * return type.
+   *
+   * See [StaticWarningCode.NON_VOID_RETURN_FOR_OPERATOR].
+   */
+  bool _checkForNonVoidReturnTypeForOperator(MethodDeclaration declaration) {
+    // check that []= operator
+    SimpleIdentifier name = declaration.name;
+    if (name.name != "[]=") {
+      return false;
+    }
+    // check return type
+    TypeName typeName = declaration.returnType;
+    if (typeName != null) {
+      DartType type = typeName.type;
+      if (type != null && !type.isVoid) {
+        _errorReporter.reportErrorForNode(
+            StaticWarningCode.NON_VOID_RETURN_FOR_OPERATOR, typeName);
+      }
+    }
+    // no warning
+    return false;
+  }
+
+  /**
+   * Verify the [typeName], used as the return type of a setter, is valid
+   * (either `null` or the type 'void').
+   *
+   * See [StaticWarningCode.NON_VOID_RETURN_FOR_SETTER].
+   */
+  bool _checkForNonVoidReturnTypeForSetter(TypeName typeName) {
+    if (typeName != null) {
+      DartType type = typeName.type;
+      if (type != null && !type.isVoid) {
+        _errorReporter.reportErrorForNode(
+            StaticWarningCode.NON_VOID_RETURN_FOR_SETTER, typeName);
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Verify the given operator-method [declaration], does not have an optional
+   * parameter. This method assumes that the method declaration was tested to be
+   * an operator declaration before being called.
+   *
+   * See [CompileTimeErrorCode.OPTIONAL_PARAMETER_IN_OPERATOR].
+   */
+  bool _checkForOptionalParameterInOperator(MethodDeclaration declaration) {
+    FormalParameterList parameterList = declaration.parameters;
+    if (parameterList == null) {
+      return false;
+    }
+    bool foundError = false;
+    NodeList<FormalParameter> formalParameters = parameterList.parameters;
+    for (FormalParameter formalParameter in formalParameters) {
+      if (formalParameter.kind.isOptional) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.OPTIONAL_PARAMETER_IN_OPERATOR,
+            formalParameter);
+        foundError = true;
+      }
+    }
+    return foundError;
+  }
+
+  /**
+   * Check that the given named optional [parameter] does not begin with '_'.
+   *
+   * See [CompileTimeErrorCode.PRIVATE_OPTIONAL_PARAMETER].
+   */
+  bool _checkForPrivateOptionalParameter(FormalParameter parameter) {
+    // should be named parameter
+    if (parameter.kind != ParameterKind.NAMED) {
+      return false;
+    }
+    // name should start with '_'
+    SimpleIdentifier name = parameter.identifier;
+    if (name.isSynthetic || !StringUtilities.startsWithChar(name.name, 0x5F)) {
+      return false;
+    }
+    // report problem
+    _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.PRIVATE_OPTIONAL_PARAMETER, parameter);
+    return true;
+  }
+
+  /**
+   * Check whether the given constructor [declaration] is the redirecting
+   * generative constructor and references itself directly or indirectly. The
+   * [constructorElement] is the constructor element.
+   *
+   * See [CompileTimeErrorCode.RECURSIVE_CONSTRUCTOR_REDIRECT].
+   */
+  bool _checkForRecursiveConstructorRedirect(ConstructorDeclaration declaration,
+      ConstructorElement constructorElement) {
+    // we check generative constructor here
+    if (declaration.factoryKeyword != null) {
+      return false;
+    }
+    // try to find redirecting constructor invocation and analyzer it for
+    // recursion
+    for (ConstructorInitializer initializer in declaration.initializers) {
+      if (initializer is RedirectingConstructorInvocation) {
+        // OK if no cycle
+        if (!_hasRedirectingFactoryConstructorCycle(constructorElement)) {
+          return false;
+        }
+        // report error
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.RECURSIVE_CONSTRUCTOR_REDIRECT, initializer);
+        return true;
+      }
+    }
+    // OK, no redirecting constructor invocation
+    return false;
+  }
+
+  /**
+   * Check whether the given constructor [declaration] has redirected
+   * constructor and references itself directly or indirectly. The
+   * constructor [element] is the element introduced by the declaration.
+   *
+   * See [CompileTimeErrorCode.RECURSIVE_FACTORY_REDIRECT].
+   */
+  bool _checkForRecursiveFactoryRedirect(
+      ConstructorDeclaration declaration, ConstructorElement element) {
+    // prepare redirected constructor
+    ConstructorName redirectedConstructorNode =
+        declaration.redirectedConstructor;
+    if (redirectedConstructorNode == null) {
+      return false;
+    }
+    // OK if no cycle
+    if (!_hasRedirectingFactoryConstructorCycle(element)) {
+      return false;
+    }
+    // report error
+    _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.RECURSIVE_FACTORY_REDIRECT,
+        redirectedConstructorNode);
+    return true;
+  }
+
+  /**
+   * Check that the class [element] is not a superinterface to itself.
+   *
+   * See [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE],
+   * [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS], and
+   * [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS].
+   */
+  bool _checkForRecursiveInterfaceInheritance(ClassElement element) {
+    if (element == null) {
+      return false;
+    }
+    return _safeCheckForRecursiveInterfaceInheritance(
+        element, new List<ClassElement>());
+  }
+
+  /**
+   * Check that the given constructor [declaration] has a valid combination of
+   * redirected constructor invocation(s), super constructor invocations and
+   * field initializers.
+   *
+   * See [CompileTimeErrorCode.DEFAULT_VALUE_IN_REDIRECTING_FACTORY_CONSTRUCTOR],
+   * [CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR],
+   * [CompileTimeErrorCode.MULTIPLE_REDIRECTING_CONSTRUCTOR_INVOCATIONS],
+   * [CompileTimeErrorCode.SUPER_IN_REDIRECTING_CONSTRUCTOR], and
+   * [CompileTimeErrorCode.REDIRECT_GENERATIVE_TO_NON_GENERATIVE_CONSTRUCTOR].
+   */
+  bool _checkForRedirectingConstructorErrorCodes(
+      ConstructorDeclaration declaration) {
+    bool errorReported = false;
+    //
+    // Check for default values in the parameters
+    //
+    ConstructorName redirectedConstructor = declaration.redirectedConstructor;
+    if (redirectedConstructor != null) {
+      for (FormalParameter parameter in declaration.parameters.parameters) {
+        if (parameter is DefaultFormalParameter &&
+            parameter.defaultValue != null) {
+          _errorReporter.reportErrorForNode(
+              CompileTimeErrorCode.DEFAULT_VALUE_IN_REDIRECTING_FACTORY_CONSTRUCTOR,
+              parameter.identifier);
+          errorReported = true;
+        }
+      }
+    }
+    // check if there are redirected invocations
+    int numRedirections = 0;
+    for (ConstructorInitializer initializer in declaration.initializers) {
+      if (initializer is RedirectingConstructorInvocation) {
+        if (numRedirections > 0) {
+          _errorReporter.reportErrorForNode(
+              CompileTimeErrorCode.MULTIPLE_REDIRECTING_CONSTRUCTOR_INVOCATIONS,
+              initializer);
+          errorReported = true;
+        }
+        if (declaration.factoryKeyword == null) {
+          RedirectingConstructorInvocation invocation = initializer;
+          ConstructorElement redirectingElement = invocation.staticElement;
+          if (redirectingElement == null) {
+            String enclosingTypeName = _enclosingClass.displayName;
+            String constructorStrName = enclosingTypeName;
+            if (invocation.constructorName != null) {
+              constructorStrName += ".${invocation.constructorName.name}";
+            }
+            _errorReporter.reportErrorForNode(
+                CompileTimeErrorCode.REDIRECT_GENERATIVE_TO_MISSING_CONSTRUCTOR,
+                invocation, [constructorStrName, enclosingTypeName]);
+          } else {
+            if (redirectingElement.isFactory) {
+              _errorReporter.reportErrorForNode(
+                  CompileTimeErrorCode.REDIRECT_GENERATIVE_TO_NON_GENERATIVE_CONSTRUCTOR,
+                  initializer);
+            }
+          }
+        }
+        numRedirections++;
+      }
+    }
+    // check for other initializers
+    if (numRedirections > 0) {
+      for (ConstructorInitializer initializer in declaration.initializers) {
+        if (initializer is SuperConstructorInvocation) {
+          _errorReporter.reportErrorForNode(
+              CompileTimeErrorCode.SUPER_IN_REDIRECTING_CONSTRUCTOR,
+              initializer);
+          errorReported = true;
+        }
+        if (initializer is ConstructorFieldInitializer) {
+          _errorReporter.reportErrorForNode(
+              CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR,
+              initializer);
+          errorReported = true;
+        }
+      }
+    }
+    // done
+    return errorReported;
+  }
+
+  /**
+   * Check whether the given constructor [declaration] has redirected
+   * constructor and references itself directly or indirectly. The
+   * constructor [element] is the element introduced by the declaration.
+   *
+   * See [CompileTimeErrorCode.REDIRECT_TO_NON_CONST_CONSTRUCTOR].
+   */
+  bool _checkForRedirectToNonConstConstructor(
+      ConstructorDeclaration declaration, ConstructorElement element) {
+    // prepare redirected constructor
+    ConstructorName redirectedConstructorNode =
+        declaration.redirectedConstructor;
+    if (redirectedConstructorNode == null) {
+      return false;
+    }
+    // prepare element
+    if (element == null) {
+      return false;
+    }
+    // OK, it is not 'const'
+    if (!element.isConst) {
+      return false;
+    }
+    // prepare redirected constructor
+    ConstructorElement redirectedConstructor = element.redirectedConstructor;
+    if (redirectedConstructor == null) {
+      return false;
+    }
+    // OK, it is also 'const'
+    if (redirectedConstructor.isConst) {
+      return false;
+    }
+    // report error
+    _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.REDIRECT_TO_NON_CONST_CONSTRUCTOR,
+        redirectedConstructorNode);
+    return true;
+  }
+
+  /**
+   * Check that the given rethrow [expression] is inside of a catch clause.
+   *
+   * See [CompileTimeErrorCode.RETHROW_OUTSIDE_CATCH].
+   */
+  bool _checkForRethrowOutsideCatch(RethrowExpression expression) {
+    if (!_isInCatchClause) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.RETHROW_OUTSIDE_CATCH, expression);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Check that if the the given constructor [declaration] is generative, then
+   * it does not have an expression function body.
+   *
+   * See [CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR].
+   */
+  bool _checkForReturnInGenerativeConstructor(
+      ConstructorDeclaration declaration) {
+    // ignore factory
+    if (declaration.factoryKeyword != null) {
+      return false;
+    }
+    // block body (with possible return statement) is checked elsewhere
+    FunctionBody body = declaration.body;
+    if (body is! ExpressionFunctionBody) {
+      return false;
+    }
+    // report error
+    _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR, body);
+    return true;
+  }
+
+  /**
+   * Check that a type mis-match between the type of the [returnExpression] and
+   * the [expectedReturnType] by the enclosing method or function.
+   *
+   * This method is called both by [_checkForAllReturnStatementErrorCodes]
+   * and [visitExpressionFunctionBody].
+   *
+   * See [StaticTypeWarningCode.RETURN_OF_INVALID_TYPE].
+   */
+  bool _checkForReturnOfInvalidType(
+      Expression returnExpression, DartType expectedReturnType) {
+    if (_enclosingFunction == null) {
+      return false;
+    }
+    if (_inGenerator) {
+      // "return expression;" is disallowed in generators, but this is checked
+      // elsewhere.  Bare "return" is always allowed in generators regardless
+      // of the return type.  So no need to do any further checking.
+      return false;
+    }
+    DartType staticReturnType = _computeReturnTypeForMethod(returnExpression);
+    if (expectedReturnType.isVoid) {
+      if (staticReturnType.isVoid ||
+          staticReturnType.isDynamic ||
+          staticReturnType.isBottom) {
+        return false;
+      }
+      _errorReporter.reportTypeErrorForNode(
+          StaticTypeWarningCode.RETURN_OF_INVALID_TYPE, returnExpression, [
+        staticReturnType,
+        expectedReturnType,
+        _enclosingFunction.displayName
+      ]);
+      return true;
+    }
+    if (staticReturnType.isAssignableTo(expectedReturnType)) {
+      return false;
+    }
+    _errorReporter.reportTypeErrorForNode(
+        StaticTypeWarningCode.RETURN_OF_INVALID_TYPE, returnExpression, [
+      staticReturnType,
+      expectedReturnType,
+      _enclosingFunction.displayName
+    ]);
+    return true;
+    // TODO(brianwilkerson) Define a hint corresponding to the warning and
+    // report it if appropriate.
+//        Type propagatedReturnType = returnExpression.getPropagatedType();
+//        boolean isPropagatedAssignable = propagatedReturnType.isAssignableTo(expectedReturnType);
+//        if (isStaticAssignable || isPropagatedAssignable) {
+//          return false;
+//        }
+//        errorReporter.reportTypeErrorForNode(
+//            StaticTypeWarningCode.RETURN_OF_INVALID_TYPE,
+//            returnExpression,
+//            staticReturnType,
+//            expectedReturnType,
+//            enclosingFunction.getDisplayName());
+//        return true;
+  }
+
+  /**
+   * Check the given [typeReference] and that the [name] is not the reference to
+   * an instance member.
+   *
+   * See [StaticWarningCode.STATIC_ACCESS_TO_INSTANCE_MEMBER].
+   */
+  bool _checkForStaticAccessToInstanceMember(
+      ClassElement typeReference, SimpleIdentifier name) {
+    // OK, target is not a type
+    if (typeReference == null) {
+      return false;
+    }
+    // prepare member Element
+    Element element = name.staticElement;
+    if (element is! ExecutableElement) {
+      return false;
+    }
+    ExecutableElement memberElement = element as ExecutableElement;
+    // OK, static
+    if (memberElement.isStatic) {
+      return false;
+    }
+    // report problem
+    _errorReporter.reportErrorForNode(
+        StaticWarningCode.STATIC_ACCESS_TO_INSTANCE_MEMBER, name, [name.name]);
+    return true;
+  }
+
+  /**
+   * Check that the type of the expression in the given 'switch' [statement] is
+   * assignable to the type of the 'case' members.
+   *
+   * See [StaticWarningCode.SWITCH_EXPRESSION_NOT_ASSIGNABLE].
+   */
+  bool _checkForSwitchExpressionNotAssignable(SwitchStatement statement) {
+    // prepare 'switch' expression type
+    Expression expression = statement.expression;
+    DartType expressionType = getStaticType(expression);
+    if (expressionType == null) {
+      return false;
+    }
+    // compare with type of the first 'case'
+    NodeList<SwitchMember> members = statement.members;
+    for (SwitchMember switchMember in members) {
+      if (switchMember is! SwitchCase) {
+        continue;
+      }
+      SwitchCase switchCase = switchMember as SwitchCase;
+      // prepare 'case' type
+      Expression caseExpression = switchCase.expression;
+      DartType caseType = getStaticType(caseExpression);
+      // check types
+      if (expressionType.isAssignableTo(caseType)) {
+        return false;
+      }
+      // report problem
+      _errorReporter.reportErrorForNode(
+          StaticWarningCode.SWITCH_EXPRESSION_NOT_ASSIGNABLE, expression, [
+        expressionType,
+        caseType
+      ]);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given function type [alias] does not reference itself
+   * directly.
+   *
+   * See [CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF].
+   */
+  bool _checkForTypeAliasCannotReferenceItself_function(
+      FunctionTypeAlias alias) {
+    FunctionTypeAliasElement element = alias.element;
+    if (!_hasTypedefSelfReference(element)) {
+      return false;
+    }
+    _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, alias);
+    return true;
+  }
+
+  /**
+   * Verify that the given type [name] is not a deferred type.
+   *
+   * See [StaticWarningCode.TYPE_ANNOTATION_DEFERRED_CLASS].
+   */
+  bool _checkForTypeAnnotationDeferredClass(TypeName name) {
+    if (name != null && name.isDeferred) {
+      _errorReporter.reportErrorForNode(
+          StaticWarningCode.TYPE_ANNOTATION_DEFERRED_CLASS, name, [name.name]);
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the type arguments in the given [typeName] are all within
+   * their bounds.
+   *
+   * See [StaticTypeWarningCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS].
+   */
+  bool _checkForTypeArgumentNotMatchingBounds(TypeName typeName) {
+    if (typeName.typeArguments == null) {
+      return false;
+    }
+    // prepare Type
+    DartType type = typeName.type;
+    if (type == null) {
+      return false;
+    }
+    // prepare ClassElement
+    Element element = type.element;
+    if (element is! ClassElement) {
+      return false;
+    }
+    ClassElement classElement = element as ClassElement;
+    // prepare type parameters
+    List<DartType> typeParameters = classElement.type.typeArguments;
+    List<TypeParameterElement> boundingElts = classElement.typeParameters;
+    // iterate over each bounded type parameter and corresponding argument
+    NodeList<TypeName> typeNameArgList = typeName.typeArguments.arguments;
+    List<DartType> typeArguments = (type as InterfaceType).typeArguments;
+    int loopThroughIndex =
+        math.min(typeNameArgList.length, boundingElts.length);
+    bool foundError = false;
+    for (int i = 0; i < loopThroughIndex; i++) {
+      TypeName argTypeName = typeNameArgList[i];
+      DartType argType = argTypeName.type;
+      DartType boundType = boundingElts[i].bound;
+      if (argType != null && boundType != null) {
+        if (typeArguments.length != 0 &&
+            typeArguments.length == typeParameters.length) {
+          boundType = boundType.substitute2(typeArguments, typeParameters);
+        }
+        if (!argType.isSubtypeOf(boundType)) {
+          ErrorCode errorCode;
+          if (_isInConstInstanceCreation) {
+            errorCode = CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS;
+          } else {
+            errorCode = StaticTypeWarningCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS;
+          }
+          _errorReporter.reportTypeErrorForNode(
+              errorCode, argTypeName, [argType, boundType]);
+          foundError = true;
+        }
+      }
+    }
+    return foundError;
+  }
+
+  /**
+   * Check whether the given type [name] is a type parameter being used to
+   * define a static member.
+   *
+   * See [StaticWarningCode.TYPE_PARAMETER_REFERENCED_BY_STATIC].
+   */
+  bool _checkForTypeParameterReferencedByStatic(TypeName name) {
+    if (_isInStaticMethod || _isInStaticVariableDeclaration) {
+      DartType type = name.type;
+      if (type is TypeParameterType) {
+        _errorReporter.reportErrorForNode(
+            StaticWarningCode.TYPE_PARAMETER_REFERENCED_BY_STATIC, name);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Check whether the given type [parameter] is a supertype of its bound.
+   *
+   * See [StaticTypeWarningCode.TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND].
+   */
+  bool _checkForTypeParameterSupertypeOfItsBound(TypeParameter parameter) {
+    TypeParameterElement element = parameter.element;
+    // prepare bound
+    DartType bound = element.bound;
+    if (bound == null) {
+      return false;
+    }
+    // OK, type parameter is not supertype of its bound
+    if (!bound.isMoreSpecificThan(element.type)) {
+      return false;
+    }
+    // report problem
+    _errorReporter.reportErrorForNode(
+        StaticTypeWarningCode.TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND, parameter,
+        [element.displayName]);
+    return true;
+  }
+
+  /**
+   * Check that if the given generative [constructor] has neither an explicit
+   * super constructor invocation nor a redirecting constructor invocation, that
+   * the superclass has a default generative constructor.
+   *
+   * See [CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT],
+   * [CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR], and
+   * [StaticWarningCode.NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT].
+   */
+  bool _checkForUndefinedConstructorInInitializerImplicit(
+      ConstructorDeclaration constructor) {
+    if (_enclosingClass == null) {
+      return false;
+    }
+    // do nothing if mixin errors have already been reported for this class.
+    ClassElementImpl enclosingClass = _enclosingClass;
+    if (enclosingClass.mixinErrorsReported) {
+      return false;
+    }
+    //
+    // Ignore if the constructor is not generative.
+    //
+    if (constructor.factoryKeyword != null) {
+      return false;
+    }
+    //
+    // Ignore if the constructor has either an implicit super constructor
+    // invocation or a redirecting constructor invocation.
+    //
+    for (ConstructorInitializer constructorInitializer
+        in constructor.initializers) {
+      if (constructorInitializer is SuperConstructorInvocation ||
+          constructorInitializer is RedirectingConstructorInvocation) {
+        return false;
+      }
+    }
+    //
+    // Check to see whether the superclass has a non-factory unnamed
+    // constructor.
+    //
+    InterfaceType superType = _enclosingClass.supertype;
+    if (superType == null) {
+      return false;
+    }
+    ClassElement superElement = superType.element;
+    ConstructorElement superUnnamedConstructor =
+        superElement.unnamedConstructor;
+    if (superUnnamedConstructor != null) {
+      if (superUnnamedConstructor.isFactory) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR,
+            constructor.returnType, [superUnnamedConstructor]);
+        return true;
+      }
+      if (!superUnnamedConstructor.isDefaultConstructor ||
+          !_enclosingClass
+              .isSuperConstructorAccessible(superUnnamedConstructor)) {
+        int offset;
+        int length;
+        {
+          Identifier returnType = constructor.returnType;
+          SimpleIdentifier name = constructor.name;
+          offset = returnType.offset;
+          length = (name != null ? name.end : returnType.end) - offset;
+        }
+        _errorReporter.reportErrorForOffset(
+            CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT, offset,
+            length, [superType.displayName]);
+      }
+      return false;
+    }
+    _errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT,
+        constructor.returnType, [superElement.name]);
+    return true;
+  }
+
+  /**
+   * Check that if the given [name] is a reference to a static member it is
+   * defined in the enclosing class rather than in a superclass.
+   *
+   * See [StaticTypeWarningCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER].
+   */
+  bool _checkForUnqualifiedReferenceToNonLocalStaticMember(
+      SimpleIdentifier name) {
+    Element element = name.staticElement;
+    if (element == null || element is TypeParameterElement) {
+      return false;
+    }
+    Element enclosingElement = element.enclosingElement;
+    if (enclosingElement is! ClassElement) {
+      return false;
+    }
+    if ((element is MethodElement && !element.isStatic) ||
+        (element is PropertyAccessorElement && !element.isStatic)) {
+      return false;
+    }
+    if (identical(enclosingElement, _enclosingClass)) {
+      return false;
+    }
+    _errorReporter.reportErrorForNode(
+        StaticTypeWarningCode.UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER,
+        name, [name.name]);
+    return true;
+  }
+
+  void _checkForValidField(FieldFormalParameter parameter) {
+    ParameterElement element = parameter.element;
+    if (element is FieldFormalParameterElement) {
+      FieldElement fieldElement = element.field;
+      if (fieldElement == null || fieldElement.isSynthetic) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD,
+            parameter, [parameter.identifier.name]);
+      } else {
+        ParameterElement parameterElement = parameter.element;
+        if (parameterElement is FieldFormalParameterElementImpl) {
+          FieldFormalParameterElementImpl fieldFormal = parameterElement;
+          DartType declaredType = fieldFormal.type;
+          DartType fieldType = fieldElement.type;
+          if (fieldElement.isSynthetic) {
+            _errorReporter.reportErrorForNode(
+                CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD,
+                parameter, [parameter.identifier.name]);
+          } else if (fieldElement.isStatic) {
+            _errorReporter.reportErrorForNode(
+                CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_STATIC_FIELD,
+                parameter, [parameter.identifier.name]);
+          } else if (declaredType != null &&
+              fieldType != null &&
+              !declaredType.isAssignableTo(fieldType)) {
+            _errorReporter.reportTypeErrorForNode(
+                StaticWarningCode.FIELD_INITIALIZING_FORMAL_NOT_ASSIGNABLE,
+                parameter, [declaredType, fieldType]);
+          }
+        } else {
+          if (fieldElement.isSynthetic) {
+            _errorReporter.reportErrorForNode(
+                CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_NON_EXISTENT_FIELD,
+                parameter, [parameter.identifier.name]);
+          } else if (fieldElement.isStatic) {
+            _errorReporter.reportErrorForNode(
+                CompileTimeErrorCode.INITIALIZING_FORMAL_FOR_STATIC_FIELD,
+                parameter, [parameter.identifier.name]);
+          }
+        }
+      }
+    }
+//        else {
+//        // TODO(jwren) Report error, constructor initializer variable is a top level element
+//        // (Either here or in ErrorVerifier.checkForAllFinalInitializedErrorCodes)
+//        }
+  }
+
+  /**
+   * Verify that the given [getter] does not have a return type of 'void'.
+   *
+   * See [StaticWarningCode.VOID_RETURN_FOR_GETTER].
+   */
+  bool _checkForVoidReturnType(MethodDeclaration getter) {
+    TypeName returnType = getter.returnType;
+    if (returnType == null || returnType.name.name != "void") {
+      return false;
+    }
+    _errorReporter.reportErrorForNode(
+        StaticWarningCode.VOID_RETURN_FOR_GETTER, returnType);
+    return true;
+  }
+
+  /**
+   * Verify the given operator-method [declaration], has correct number of
+   * parameters.
+   *
+   * This method assumes that the method declaration was tested to be an
+   * operator declaration before being called.
+   *
+   * See [CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR].
+   */
+  bool _checkForWrongNumberOfParametersForOperator(
+      MethodDeclaration declaration) {
+    // prepare number of parameters
+    FormalParameterList parameterList = declaration.parameters;
+    if (parameterList == null) {
+      return false;
+    }
+    int numParameters = parameterList.parameters.length;
+    // prepare operator name
+    SimpleIdentifier nameNode = declaration.name;
+    if (nameNode == null) {
+      return false;
+    }
+    String name = nameNode.name;
+    // check for exact number of parameters
+    int expected = -1;
+    if ("[]=" == name) {
+      expected = 2;
+    } else if ("<" == name ||
+        ">" == name ||
+        "<=" == name ||
+        ">=" == name ||
+        "==" == name ||
+        "+" == name ||
+        "/" == name ||
+        "~/" == name ||
+        "*" == name ||
+        "%" == name ||
+        "|" == name ||
+        "^" == name ||
+        "&" == name ||
+        "<<" == name ||
+        ">>" == name ||
+        "[]" == name) {
+      expected = 1;
+    } else if ("~" == name) {
+      expected = 0;
+    }
+    if (expected != -1 && numParameters != expected) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR,
+          nameNode, [name, expected, numParameters]);
+      return true;
+    }
+    // check for operator "-"
+    if ("-" == name && numParameters > 1) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS,
+          nameNode, [numParameters]);
+      return true;
+    }
+    // OK
+    return false;
+  }
+
+  /**
+   * Verify that the given setter [parameterList] has only one required
+   * parameter. The [setterName] is the name of the setter to report problems
+   * on.
+   *
+   * This method assumes that the method declaration was tested to be a setter
+   * before being called.
+   *
+   * See [CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER].
+   */
+  bool _checkForWrongNumberOfParametersForSetter(
+      SimpleIdentifier setterName, FormalParameterList parameterList) {
+    if (setterName == null) {
+      return false;
+    }
+    if (parameterList == null) {
+      return false;
+    }
+    NodeList<FormalParameter> parameters = parameterList.parameters;
+    if (parameters.length != 1 ||
+        parameters[0].kind != ParameterKind.REQUIRED) {
+      _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER,
+          setterName);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Check for a type mis-match between the yielded type and the declared
+   * return type of a generator function.
+   *
+   * This method should only be called in generator functions.
+   */
+  bool _checkForYieldOfInvalidType(
+      Expression yieldExpression, bool isYieldEach) {
+    assert(_inGenerator);
+    if (_enclosingFunction == null) {
+      return false;
+    }
+    DartType declaredReturnType = _enclosingFunction.returnType;
+    DartType staticYieldedType = getStaticType(yieldExpression);
+    DartType impliedReturnType;
+    if (isYieldEach) {
+      impliedReturnType = staticYieldedType;
+    } else if (_enclosingFunction.isAsynchronous) {
+      impliedReturnType =
+          _typeProvider.streamType.substitute4(<DartType>[staticYieldedType]);
+    } else {
+      impliedReturnType =
+          _typeProvider.iterableType.substitute4(<DartType>[staticYieldedType]);
+    }
+    if (!impliedReturnType.isAssignableTo(declaredReturnType)) {
+      _errorReporter.reportTypeErrorForNode(
+          StaticTypeWarningCode.YIELD_OF_INVALID_TYPE, yieldExpression, [
+        impliedReturnType,
+        declaredReturnType
+      ]);
+      return true;
+    }
+    if (isYieldEach) {
+      // Since the declared return type might have been "dynamic", we need to
+      // also check that the implied return type is assignable to generic
+      // Stream/Iterable.
+      DartType requiredReturnType;
+      if (_enclosingFunction.isAsynchronous) {
+        requiredReturnType = _typeProvider.streamDynamicType;
+      } else {
+        requiredReturnType = _typeProvider.iterableDynamicType;
+      }
+      if (!impliedReturnType.isAssignableTo(requiredReturnType)) {
+        _errorReporter.reportTypeErrorForNode(
+            StaticTypeWarningCode.YIELD_OF_INVALID_TYPE, yieldExpression, [
+          impliedReturnType,
+          requiredReturnType
+        ]);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Verify that if the given class [declaration] implements the class Function
+   * that it has a concrete implementation of the call method.
+   *
+   * See [StaticWarningCode.FUNCTION_WITHOUT_CALL].
+   */
+  bool _checkImplementsFunctionWithoutCall(ClassDeclaration declaration) {
+    if (declaration.isAbstract) {
+      return false;
+    }
+    ClassElement classElement = declaration.element;
+    if (classElement == null) {
+      return false;
+    }
+    if (!classElement.type.isSubtypeOf(_typeProvider.functionType)) {
+      return false;
+    }
+    // If there is a noSuchMethod method, then don't report the warning,
+    // see dartbug.com/16078
+    if (classElement.getMethod(FunctionElement.NO_SUCH_METHOD_METHOD_NAME) !=
+        null) {
+      return false;
+    }
+    ExecutableElement callMethod = _inheritanceManager.lookupMember(
+        classElement, FunctionElement.CALL_METHOD_NAME);
+    if (callMethod == null ||
+        callMethod is! MethodElement ||
+        (callMethod as MethodElement).isAbstract) {
+      _errorReporter.reportErrorForNode(
+          StaticWarningCode.FUNCTION_WITHOUT_CALL, declaration.name);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Verify that the given class [declaration] does not have the same class in
+   * the 'extends' and 'implements' clauses.
+   *
+   * See [CompileTimeErrorCode.IMPLEMENTS_SUPER_CLASS].
+   */
+  bool _checkImplementsSuperClass(ClassDeclaration declaration) {
+    // prepare super type
+    InterfaceType superType = _enclosingClass.supertype;
+    if (superType == null) {
+      return false;
+    }
+    // prepare interfaces
+    ImplementsClause implementsClause = declaration.implementsClause;
+    if (implementsClause == null) {
+      return false;
+    }
+    // check interfaces
+    bool hasProblem = false;
+    for (TypeName interfaceNode in implementsClause.interfaces) {
+      if (interfaceNode.type == superType) {
+        hasProblem = true;
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.IMPLEMENTS_SUPER_CLASS, interfaceNode,
+            [superType.displayName]);
+      }
+    }
+    // done
+    return hasProblem;
+  }
+
+  DartType _computeReturnTypeForMethod(Expression returnExpression) {
+    // This method should never be called for generators, since generators are
+    // never allowed to contain return statements with expressions.
+    assert(!_inGenerator);
+    if (returnExpression == null) {
+      if (_enclosingFunction.isAsynchronous) {
+        return _typeProvider.futureNullType;
+      } else {
+        return VoidTypeImpl.instance;
+      }
+    }
+    DartType staticReturnType = getStaticType(returnExpression);
+    if (staticReturnType != null && _enclosingFunction.isAsynchronous) {
+      return _typeProvider.futureType.substitute4(<DartType>[
+        StaticTypeAnalyzer.flattenFutures(_typeProvider, staticReturnType)
+      ]);
+    }
+    return staticReturnType;
+  }
+
+  /**
+   * Return the error code that should be used when the given class [element]
+   * references itself directly.
+   */
+  ErrorCode _getBaseCaseErrorCode(ClassElement element) {
+    InterfaceType supertype = element.supertype;
+    if (supertype != null && _enclosingClass == supertype.element) {
+      return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS;
+    }
+    List<InterfaceType> mixins = element.mixins;
+    for (int i = 0; i < mixins.length; i++) {
+      if (_enclosingClass == mixins[i].element) {
+        return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_WITH;
+      }
+    }
+    return CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS;
+  }
+
+  /**
+   * Given an [expression] in a switch case whose value is expected to be an
+   * enum constant, return the name of the constant.
+   */
+  String _getConstantName(Expression expression) {
+    // TODO(brianwilkerson) Convert this to return the element representing the
+    // constant.
+    if (expression is SimpleIdentifier) {
+      return expression.name;
+    } else if (expression is PrefixedIdentifier) {
+      return expression.identifier.name;
+    } else if (expression is PropertyAccess) {
+      return expression.propertyName.name;
+    }
+    return null;
+  }
+
+  /**
+   * Return the return type of the given [getter].
+   */
+  DartType _getGetterType(PropertyAccessorElement getter) {
+    FunctionType functionType = getter.type;
+    if (functionType != null) {
+      return functionType.returnType;
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * Return the type of the first and only parameter of the given [setter].
+   */
+  DartType _getSetterType(PropertyAccessorElement setter) {
+    // Get the parameters for MethodDeclaration or FunctionDeclaration
+    List<ParameterElement> setterParameters = setter.parameters;
+    // If there are no setter parameters, return no type.
+    if (setterParameters.length == 0) {
+      return null;
+    }
+    return setterParameters[0].type;
+  }
+
+  /**
+   * Given a list of [directives] that have the same prefix, generate an error
+   * if there is more than one import and any of those imports is deferred.
+   *
+   * See [CompileTimeErrorCode.SHARED_DEFERRED_PREFIX].
+   */
+  bool _hasDeferredPrefixCollision(List<ImportDirective> directives) {
+    bool foundError = false;
+    int count = directives.length;
+    if (count > 1) {
+      for (int i = 0; i < count; i++) {
+        sc.Token deferredToken = directives[i].deferredKeyword;
+        if (deferredToken != null) {
+          _errorReporter.reportErrorForToken(
+              CompileTimeErrorCode.SHARED_DEFERRED_PREFIX, deferredToken);
+          foundError = true;
+        }
+      }
+    }
+    return foundError;
+  }
+
+  /**
+   * Return `true` if the given [constructor] redirects to itself, directly or
+   * indirectly.
+   */
+  bool _hasRedirectingFactoryConstructorCycle(ConstructorElement constructor) {
+    Set<ConstructorElement> constructors = new HashSet<ConstructorElement>();
+    ConstructorElement current = constructor;
+    while (current != null) {
+      if (constructors.contains(current)) {
+        return identical(current, constructor);
+      }
+      constructors.add(current);
+      current = current.redirectedConstructor;
+      if (current is ConstructorMember) {
+        current = (current as ConstructorMember).baseElement;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the given [element] has direct or indirect reference to
+   * itself from anywhere except a class element or type parameter bounds.
+   */
+  bool _hasTypedefSelfReference(Element element) {
+    Set<Element> checked = new HashSet<Element>();
+    List<Element> toCheck = new List<Element>();
+    GeneralizingElementVisitor_ErrorVerifier_hasTypedefSelfReference elementVisitor =
+        new GeneralizingElementVisitor_ErrorVerifier_hasTypedefSelfReference(
+            toCheck);
+    toCheck.add(element);
+    bool firstIteration = true;
+    while (true) {
+      Element current;
+      // get next element
+      while (true) {
+        // may be no more elements to check
+        if (toCheck.isEmpty) {
+          return false;
+        }
+        // try to get next element
+        current = toCheck.removeAt(toCheck.length - 1);
+        if (element == current) {
+          if (firstIteration) {
+            firstIteration = false;
+            break;
+          } else {
+            return true;
+          }
+        }
+        if (current != null && !checked.contains(current)) {
+          break;
+        }
+      }
+      // check current element
+      current.accept(elementVisitor);
+      checked.add(current);
+    }
+  }
+
+  bool _isFunctionType(DartType type) {
+    if (type.isDynamic || type.isBottom) {
+      return true;
+    } else if (type is FunctionType || type.isDartCoreFunction) {
+      return true;
+    } else if (type is InterfaceType) {
+      MethodElement callMethod =
+          type.lookUpMethod(FunctionElement.CALL_METHOD_NAME, _currentLibrary);
+      return callMethod != null;
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` iff the given [classElement] has a concrete method, getter or
+   * setter that matches the name of the given [executableElement] in either the
+   * class itself, or one of its' mixins.
+   *
+   * By "match", only the name of the member is tested to match, it does not
+   * have to equal or be a subtype of the given executable element, this is due
+   * to the specific use where this method is used in
+   * [_checkForNonAbstractClassInheritsAbstractMember].
+   */
+  bool _isMemberInClassOrMixin(
+      ExecutableElement executableElement, ClassElement classElement) {
+    ExecutableElement foundElt = null;
+    String executableName = executableElement.name;
+    if (executableElement is MethodElement) {
+      foundElt = classElement.getMethod(executableName);
+      if (foundElt != null && !(foundElt as MethodElement).isAbstract) {
+        return true;
+      }
+      List<InterfaceType> mixins = classElement.mixins;
+      for (int i = 0; i < mixins.length && foundElt == null; i++) {
+        foundElt = mixins[i].getMethod(executableName);
+      }
+      if (foundElt != null && !(foundElt as MethodElement).isAbstract) {
+        return true;
+      }
+    } else if (executableElement is PropertyAccessorElement) {
+      PropertyAccessorElement propertyAccessorElement = executableElement;
+      if (propertyAccessorElement.isGetter) {
+        foundElt = classElement.getGetter(executableName);
+      }
+      if (foundElt == null && propertyAccessorElement.isSetter) {
+        foundElt = classElement.getSetter(executableName);
+      }
+      if (foundElt != null &&
+          !(foundElt as PropertyAccessorElement).isAbstract) {
+        return true;
+      }
+      List<InterfaceType> mixins = classElement.mixins;
+      for (int i = 0; i < mixins.length && foundElt == null; i++) {
+        foundElt = mixins[i].getGetter(executableName);
+        if (foundElt == null) {
+          foundElt = mixins[i].getSetter(executableName);
+        }
+      }
+      if (foundElt != null &&
+          !(foundElt as PropertyAccessorElement).isAbstract) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the given 'this' [expression] is in a valid context.
+   */
+  bool _isThisInValidContext(ThisExpression expression) {
+    for (AstNode node = expression.parent; node != null; node = node.parent) {
+      if (node is CompilationUnit) {
+        return false;
+      }
+      if (node is ConstructorDeclaration) {
+        return node.factoryKeyword == null;
+      }
+      if (node is ConstructorInitializer) {
+        return false;
+      }
+      if (node is MethodDeclaration) {
+        return !node.isStatic;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the given [identifier] is in a location where it is
+   * allowed to resolve to a static member of a supertype.
+   */
+  bool _isUnqualifiedReferenceToNonLocalStaticMemberAllowed(
+      SimpleIdentifier identifier) {
+    if (identifier.inDeclarationContext()) {
+      return true;
+    }
+    AstNode parent = identifier.parent;
+    if (parent is ConstructorName ||
+        parent is MethodInvocation ||
+        parent is PropertyAccess ||
+        parent is SuperConstructorInvocation) {
+      return true;
+    }
+    if (parent is PrefixedIdentifier &&
+        identical(parent.identifier, identifier)) {
+      return true;
+    }
+    if (parent is Annotation && identical(parent.constructorName, identifier)) {
+      return true;
+    }
+    if (parent is CommentReference) {
+      CommentReference commentReference = parent;
+      if (commentReference.newKeyword != null) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool _isUserDefinedObject(EvaluationResultImpl result) => result == null ||
+      (result.value != null && result.value.isUserDefinedObject);
+
+  /**
+   * Check that the given class [element] is not a superinterface to itself. The
+   * [path] is a list containing the potentially cyclic implements path.
+   *
+   * See [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE],
+   * [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS],
+   * [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS],
+   * and [CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_WITH].
+   */
+  bool _safeCheckForRecursiveInterfaceInheritance(
+      ClassElement element, List<ClassElement> path) {
+    // Detect error condition.
+    int size = path.length;
+    // If this is not the base case (size > 0), and the enclosing class is the
+    // given class element then an error an error.
+    if (size > 0 && _enclosingClass == element) {
+      String enclosingClassName = _enclosingClass.displayName;
+      if (size > 1) {
+        // Construct a string showing the cyclic implements path:
+        // "A, B, C, D, A"
+        String separator = ", ";
+        StringBuffer buffer = new StringBuffer();
+        for (int i = 0; i < size; i++) {
+          buffer.write(path[i].displayName);
+          buffer.write(separator);
+        }
+        buffer.write(element.displayName);
+        _errorReporter.reportErrorForOffset(
+            CompileTimeErrorCode.RECURSIVE_INTERFACE_INHERITANCE,
+            _enclosingClass.nameOffset, enclosingClassName.length, [
+          enclosingClassName,
+          buffer.toString()
+        ]);
+        return true;
+      } else {
+        // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_EXTENDS or
+        // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_IMPLEMENTS or
+        // RECURSIVE_INTERFACE_INHERITANCE_BASE_CASE_WITH
+        _errorReporter.reportErrorForOffset(_getBaseCaseErrorCode(element),
+            _enclosingClass.nameOffset, enclosingClassName.length,
+            [enclosingClassName]);
+        return true;
+      }
+    }
+    if (path.indexOf(element) > 0) {
+      return false;
+    }
+    path.add(element);
+    // n-case
+    InterfaceType supertype = element.supertype;
+    if (supertype != null &&
+        _safeCheckForRecursiveInterfaceInheritance(supertype.element, path)) {
+      return true;
+    }
+    List<InterfaceType> interfaceTypes = element.interfaces;
+    for (InterfaceType interfaceType in interfaceTypes) {
+      if (_safeCheckForRecursiveInterfaceInheritance(
+          interfaceType.element, path)) {
+        return true;
+      }
+    }
+    List<InterfaceType> mixinTypes = element.mixins;
+    for (InterfaceType mixinType in mixinTypes) {
+      if (_safeCheckForRecursiveInterfaceInheritance(mixinType.element, path)) {
+        return true;
+      }
+    }
+    path.removeAt(path.length - 1);
+    return false;
+  }
+
+  /**
+   * Return the static type of the given [expression] that is to be used for
+   * type analysis.
+   */
+  static DartType getStaticType(Expression expression) {
+    DartType type = expression.staticType;
+    if (type == null) {
+      // TODO(brianwilkerson) This should never happen.
+      return DynamicTypeImpl.instance;
+    }
+    return type;
+  }
+
+  /**
+   * Return the variable element represented by the given [expression], or
+   * `null` if there is no such element.
+   */
+  static VariableElement getVariableElement(Expression expression) {
+    if (expression is Identifier) {
+      Element element = expression.staticElement;
+      if (element is VariableElement) {
+        return element;
+      }
+    }
+    return null;
+  }
+}
+
+class GeneralizingElementVisitor_ErrorVerifier_hasTypedefSelfReference
+    extends GeneralizingElementVisitor<Object> {
+  List<Element> toCheck;
+
+  GeneralizingElementVisitor_ErrorVerifier_hasTypedefSelfReference(this.toCheck)
+      : super();
+
+  @override
+  Object visitClassElement(ClassElement element) {
+    // Typedefs are allowed to reference themselves via classes.
+    return null;
+  }
+
+  @override
+  Object visitFunctionTypeAliasElement(FunctionTypeAliasElement element) {
+    _addTypeToCheck(element.returnType);
+    return super.visitFunctionTypeAliasElement(element);
+  }
+
+  @override
+  Object visitParameterElement(ParameterElement element) {
+    _addTypeToCheck(element.type);
+    return super.visitParameterElement(element);
+  }
+
+  @override
+  Object visitTypeParameterElement(TypeParameterElement element) {
+    _addTypeToCheck(element.bound);
+    return super.visitTypeParameterElement(element);
+  }
+
+  void _addTypeToCheck(DartType type) {
+    if (type == null) {
+      return;
+    }
+    // schedule for checking
+    toCheck.add(type.element);
+    // type arguments
+    if (type is InterfaceType) {
+      InterfaceType interfaceType = type;
+      for (DartType typeArgument in interfaceType.typeArguments) {
+        _addTypeToCheck(typeArgument);
+      }
+    }
+  }
+}
diff --git a/analyzer/lib/src/generated/html.dart b/analyzer/lib/src/generated/html.dart
new file mode 100644
index 0000000..c6ad0e0
--- /dev/null
+++ b/analyzer/lib/src/generated/html.dart
@@ -0,0 +1,1889 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.html;
+
+import 'dart:collection';
+
+import 'ast.dart';
+import 'element.dart';
+import 'engine.dart' show AnalysisOptions, AnalysisEngine;
+import 'error.dart' show AnalysisErrorListener;
+import 'java_core.dart';
+import 'java_engine.dart';
+import 'parser.dart' show Parser;
+import 'scanner.dart' as sc show Scanner, SubSequenceReader, Token;
+import 'source.dart';
+
+/**
+ * The abstract class `AbstractScanner` implements a scanner for HTML code. Subclasses are
+ * required to implement the interface used to access the characters being scanned.
+ */
+abstract class AbstractScanner {
+  static List<String> _NO_PASS_THROUGH_ELEMENTS = <String>[];
+
+  /**
+   * The source being scanned.
+   */
+  final Source source;
+
+  /**
+   * The token pointing to the head of the linked list of tokens.
+   */
+  Token _tokens;
+
+  /**
+   * The last token that was scanned.
+   */
+  Token _tail;
+
+  /**
+   * A list containing the offsets of the first character of each line in the source code.
+   */
+  List<int> _lineStarts = new List<int>();
+
+  /**
+   * An array of element tags for which the content between tags should be consider a single token.
+   */
+  List<String> _passThroughElements = _NO_PASS_THROUGH_ELEMENTS;
+
+  /**
+   * Initialize a newly created scanner.
+   *
+   * @param source the source being scanned
+   */
+  AbstractScanner(this.source) {
+    _tokens = new Token.con1(TokenType.EOF, -1);
+    _tokens.setNext(_tokens);
+    _tail = _tokens;
+    recordStartOfLine();
+  }
+
+  /**
+   * Return an array containing the offsets of the first character of each line in the source code.
+   *
+   * @return an array containing the offsets of the first character of each line in the source code
+   */
+  List<int> get lineStarts => _lineStarts;
+
+  /**
+   * Return the current offset relative to the beginning of the file. Return the initial offset if
+   * the scanner has not yet scanned the source code, and one (1) past the end of the source code if
+   * the source code has been scanned.
+   *
+   * @return the current offset of the scanner in the source
+   */
+  int get offset;
+
+  /**
+   * Set array of element tags for which the content between tags should be consider a single token.
+   */
+  void set passThroughElements(List<String> passThroughElements) {
+    this._passThroughElements = passThroughElements != null
+        ? passThroughElements
+        : _NO_PASS_THROUGH_ELEMENTS;
+  }
+
+  /**
+   * Advance the current position and return the character at the new current position.
+   *
+   * @return the character at the new current position
+   */
+  int advance();
+
+  /**
+   * Return the substring of the source code between the start offset and the modified current
+   * position. The current position is modified by adding the end delta.
+   *
+   * @param start the offset to the beginning of the string, relative to the start of the file
+   * @param endDelta the number of character after the current location to be included in the
+   *          string, or the number of characters before the current location to be excluded if the
+   *          offset is negative
+   * @return the specified substring of the source code
+   */
+  String getString(int start, int endDelta);
+
+  /**
+   * Return the character at the current position without changing the current position.
+   *
+   * @return the character at the current position
+   */
+  int peek();
+
+  /**
+   * Record the fact that we are at the beginning of a new line in the source.
+   */
+  void recordStartOfLine() {
+    _lineStarts.add(offset);
+  }
+
+  /**
+   * Scan the source code to produce a list of tokens representing the source.
+   *
+   * @return the first token in the list of tokens that were produced
+   */
+  Token tokenize() {
+    _scan();
+    _appendEofToken();
+    return _firstToken();
+  }
+
+  void _appendEofToken() {
+    Token eofToken = new Token.con1(TokenType.EOF, offset);
+    // The EOF token points to itself so that there is always infinite
+    // look-ahead.
+    eofToken.setNext(eofToken);
+    _tail = _tail.setNext(eofToken);
+  }
+
+  Token _emit(Token token) {
+    _tail.setNext(token);
+    _tail = token;
+    return token;
+  }
+
+  Token _emitWithOffset(TokenType type, int start) =>
+      _emit(new Token.con1(type, start));
+
+  Token _emitWithOffsetAndLength(TokenType type, int start, int count) =>
+      _emit(new Token.con2(type, start, getString(start, count)));
+
+  Token _firstToken() => _tokens.next;
+
+  int _recordStartOfLineAndAdvance(int c) {
+    if (c == 0xD) {
+      c = advance();
+      if (c == 0xA) {
+        c = advance();
+      }
+      recordStartOfLine();
+    } else if (c == 0xA) {
+      c = advance();
+      recordStartOfLine();
+    } else {
+      c = advance();
+    }
+    return c;
+  }
+
+  void _scan() {
+    bool inBrackets = false;
+    String endPassThrough = null;
+    int c = advance();
+    while (c >= 0) {
+      int start = offset;
+      if (c == 0x3C) {
+        c = advance();
+        if (c == 0x21) {
+          c = advance();
+          if (c == 0x2D && peek() == 0x2D) {
+            // handle a comment
+            c = advance();
+            int dashCount = 1;
+            while (c >= 0) {
+              if (c == 0x2D) {
+                dashCount++;
+              } else if (c == 0x3E && dashCount >= 2) {
+                c = advance();
+                break;
+              } else {
+                dashCount = 0;
+              }
+              c = _recordStartOfLineAndAdvance(c);
+            }
+            _emitWithOffsetAndLength(TokenType.COMMENT, start, -1);
+            // Capture <!--> and <!---> as tokens but report an error
+            if (_tail.length < 7) {
+              // TODO (danrubel): Report invalid HTML comment
+            }
+          } else {
+            // handle a declaration
+            while (c >= 0) {
+              if (c == 0x3E) {
+                c = advance();
+                break;
+              }
+              c = _recordStartOfLineAndAdvance(c);
+            }
+            _emitWithOffsetAndLength(TokenType.DECLARATION, start, -1);
+            if (!StringUtilities.endsWithChar(_tail.lexeme, 0x3E)) {
+              // TODO (danrubel): Report missing '>' in directive
+            }
+          }
+        } else if (c == 0x3F) {
+          // handle a directive
+          while (c >= 0) {
+            if (c == 0x3F) {
+              c = advance();
+              if (c == 0x3E) {
+                c = advance();
+                break;
+              }
+            } else {
+              c = _recordStartOfLineAndAdvance(c);
+            }
+          }
+          _emitWithOffsetAndLength(TokenType.DIRECTIVE, start, -1);
+          if (_tail.length < 4) {
+            // TODO (danrubel): Report invalid directive
+          }
+        } else if (c == 0x2F) {
+          _emitWithOffset(TokenType.LT_SLASH, start);
+          inBrackets = true;
+          c = advance();
+        } else {
+          inBrackets = true;
+          _emitWithOffset(TokenType.LT, start);
+          // ignore whitespace in braces
+          while (Character.isWhitespace(c)) {
+            c = _recordStartOfLineAndAdvance(c);
+          }
+          // get tag
+          if (Character.isLetterOrDigit(c)) {
+            int tagStart = offset;
+            c = advance();
+            while (Character.isLetterOrDigit(c) || c == 0x2D || c == 0x5F) {
+              c = advance();
+            }
+            _emitWithOffsetAndLength(TokenType.TAG, tagStart, -1);
+            // check tag against passThrough elements
+            String tag = _tail.lexeme;
+            for (String str in _passThroughElements) {
+              if (str == tag) {
+                endPassThrough = "</$str>";
+                break;
+              }
+            }
+          }
+        }
+      } else if (c == 0x3E) {
+        _emitWithOffset(TokenType.GT, start);
+        inBrackets = false;
+        c = advance();
+        // if passThrough != null, read until we match it
+        if (endPassThrough != null) {
+          bool endFound = false;
+          int len = endPassThrough.length;
+          int firstC = endPassThrough.codeUnitAt(0);
+          int index = 0;
+          int nextC = firstC;
+          while (c >= 0) {
+            if (c == nextC) {
+              index++;
+              if (index == len) {
+                endFound = true;
+                break;
+              }
+              nextC = endPassThrough.codeUnitAt(index);
+            } else if (c == firstC) {
+              index = 1;
+              nextC = endPassThrough.codeUnitAt(1);
+            } else {
+              index = 0;
+              nextC = firstC;
+            }
+            c = _recordStartOfLineAndAdvance(c);
+          }
+          if (start + 1 < offset) {
+            if (endFound) {
+              _emitWithOffsetAndLength(TokenType.TEXT, start + 1, -len);
+              _emitWithOffset(TokenType.LT_SLASH, offset - len + 1);
+              _emitWithOffsetAndLength(TokenType.TAG, offset - len + 3, -1);
+            } else {
+              _emitWithOffsetAndLength(TokenType.TEXT, start + 1, -1);
+            }
+          }
+          endPassThrough = null;
+        }
+      } else if (c == 0x2F && peek() == 0x3E) {
+        advance();
+        _emitWithOffset(TokenType.SLASH_GT, start);
+        inBrackets = false;
+        c = advance();
+      } else if (!inBrackets) {
+        c = _recordStartOfLineAndAdvance(c);
+        while (c != 0x3C && c >= 0) {
+          c = _recordStartOfLineAndAdvance(c);
+        }
+        _emitWithOffsetAndLength(TokenType.TEXT, start, -1);
+      } else if (c == 0x22 || c == 0x27) {
+        // read a string
+        int endQuote = c;
+        c = advance();
+        while (c >= 0) {
+          if (c == endQuote) {
+            c = advance();
+            break;
+          }
+          c = _recordStartOfLineAndAdvance(c);
+        }
+        _emitWithOffsetAndLength(TokenType.STRING, start, -1);
+      } else if (c == 0x3D) {
+        // a non-char token
+        _emitWithOffset(TokenType.EQ, start);
+        c = advance();
+      } else if (Character.isWhitespace(c)) {
+        // ignore whitespace in braces
+        do {
+          c = _recordStartOfLineAndAdvance(c);
+        } while (Character.isWhitespace(c));
+      } else if (Character.isLetterOrDigit(c)) {
+        c = advance();
+        while (Character.isLetterOrDigit(c) || c == 0x2D || c == 0x5F) {
+          c = advance();
+        }
+        _emitWithOffsetAndLength(TokenType.TAG, start, -1);
+      } else {
+        // a non-char token
+        _emitWithOffsetAndLength(TokenType.TEXT, start, 0);
+        c = advance();
+      }
+    }
+  }
+}
+
+/**
+ * Instances of the class `HtmlParser` are used to parse tokens into a AST structure comprised
+ * of [XmlNode]s.
+ */
+class HtmlParser extends XmlParser {
+  static String _APPLICATION_DART_IN_DOUBLE_QUOTES = "\"application/dart\"";
+
+  static String _APPLICATION_DART_IN_SINGLE_QUOTES = "'application/dart'";
+
+  static String _SCRIPT = "script";
+
+  static String _TYPE = "type";
+
+  /**
+   * A set containing the names of tags that do not have a closing tag.
+   */
+  static Set<String> SELF_CLOSING = new HashSet<String>.from(<String>[
+    "area",
+    "base",
+    "basefont",
+    "br",
+    "col",
+    "frame",
+    "hr",
+    "img",
+    "input",
+    "link",
+    "meta",
+    "param",
+    "!"
+  ]);
+
+  /**
+   * The line information associated with the source being parsed.
+   */
+  LineInfo _lineInfo;
+
+  /**
+   * The error listener to which errors will be reported.
+   */
+  final AnalysisErrorListener _errorListener;
+
+  final AnalysisOptions _options;
+
+  /**
+   * Construct a parser for the specified source.
+   *
+   * [source] is the source being parsed.  [_errorListener] is the error
+   * listener to which errors will be reported.  [_options] is the analysis
+   * options which should be used for parsing.
+   */
+  HtmlParser(Source source, this._errorListener, this._options) : super(source);
+
+  @override
+  XmlAttributeNode createAttributeNode(Token name, Token equals, Token value) =>
+      new XmlAttributeNode(name, equals, value);
+
+  @override
+  XmlTagNode createTagNode(Token nodeStart, Token tag,
+      List<XmlAttributeNode> attributes, Token attributeEnd,
+      List<XmlTagNode> tagNodes, Token contentEnd, Token closingTag,
+      Token nodeEnd) {
+    if (_isScriptNode(tag, attributes, tagNodes)) {
+      HtmlScriptTagNode tagNode = new HtmlScriptTagNode(nodeStart, tag,
+          attributes, attributeEnd, tagNodes, contentEnd, closingTag, nodeEnd);
+      String contents = tagNode.content;
+      int contentOffset = attributeEnd.end;
+      LineInfo_Location location = _lineInfo.getLocation(contentOffset);
+      sc.Scanner scanner = new sc.Scanner(source,
+          new sc.SubSequenceReader(contents, contentOffset), _errorListener);
+      scanner.enableNullAwareOperators = _options.enableNullAwareOperators;
+      scanner.setSourceStart(location.lineNumber, location.columnNumber);
+      sc.Token firstToken = scanner.tokenize();
+      Parser parser = new Parser(source, _errorListener);
+      CompilationUnit unit = parser.parseCompilationUnit(firstToken);
+      unit.lineInfo = _lineInfo;
+      tagNode.script = unit;
+      return tagNode;
+    }
+    return new XmlTagNode(nodeStart, tag, attributes, attributeEnd, tagNodes,
+        contentEnd, closingTag, nodeEnd);
+  }
+
+  @override
+  bool isSelfClosing(Token tag) => SELF_CLOSING.contains(tag.lexeme);
+
+  /**
+   * Parse the given tokens.
+   *
+   * @param token the first token in the stream of tokens to be parsed
+   * @param lineInfo the line information created by the scanner
+   * @return the parse result (not `null`)
+   */
+  HtmlUnit parse(Token token, LineInfo lineInfo) {
+    this._lineInfo = lineInfo;
+    List<XmlTagNode> tagNodes = parseTopTagNodes(token);
+    return new HtmlUnit(token, tagNodes, currentToken);
+  }
+
+  /**
+   * Determine if the specified node is a Dart script.
+   *
+   * @param node the node to be tested (not `null`)
+   * @return `true` if the node is a Dart script
+   */
+  bool _isScriptNode(
+      Token tag, List<XmlAttributeNode> attributes, List<XmlTagNode> tagNodes) {
+    if (tagNodes.length != 0 || tag.lexeme != _SCRIPT) {
+      return false;
+    }
+    for (XmlAttributeNode attribute in attributes) {
+      if (attribute.name == _TYPE) {
+        Token valueToken = attribute.valueToken;
+        if (valueToken != null) {
+          String value = valueToken.lexeme;
+          if (value == _APPLICATION_DART_IN_DOUBLE_QUOTES ||
+              value == _APPLICATION_DART_IN_SINGLE_QUOTES) {
+            return true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Given the contents of an embedded expression that occurs at the given offset, parse it as a
+   * Dart expression. The contents should not include the expression's delimiters.
+   *
+   * @param source the source that contains that given token
+   * @param token the token to start parsing from
+   * @return the Dart expression that was parsed
+   */
+  static Expression parseEmbeddedExpression(
+      Source source, sc.Token token, AnalysisErrorListener errorListener) {
+    Parser parser = new Parser(source, errorListener);
+    return parser.parseExpression(token);
+  }
+
+  /**
+   * Given the contents of an embedded expression that occurs at the given offset, scans it as a
+   * Dart code.
+   *
+   * @param source the source of that contains the given contents
+   * @param contents the contents to scan
+   * @param contentOffset the offset of the contents in the larger file
+   * @return the first Dart token
+   */
+  static sc.Token scanDartSource(Source source, LineInfo lineInfo,
+      String contents, int contentOffset, AnalysisErrorListener errorListener) {
+    LineInfo_Location location = lineInfo.getLocation(contentOffset);
+    sc.Scanner scanner = new sc.Scanner(source,
+        new sc.SubSequenceReader(contents, contentOffset), errorListener);
+    scanner.setSourceStart(location.lineNumber, location.columnNumber);
+    return scanner.tokenize();
+  }
+}
+
+/**
+ * Instances of the class `HtmlScriptTagNode` represent a script tag within an HTML file that
+ * references a Dart script.
+ */
+class HtmlScriptTagNode extends XmlTagNode {
+  /**
+   * The AST structure representing the Dart code within this tag.
+   */
+  CompilationUnit _script;
+
+  /**
+   * The element representing this script.
+   */
+  HtmlScriptElement scriptElement;
+
+  /**
+   * Initialize a newly created node to represent a script tag within an HTML file that references a
+   * Dart script.
+   *
+   * @param nodeStart the token marking the beginning of the tag
+   * @param tag the name of the tag
+   * @param attributes the attributes in the tag
+   * @param attributeEnd the token terminating the region where attributes can be
+   * @param tagNodes the children of the tag
+   * @param contentEnd the token that starts the closing tag
+   * @param closingTag the name of the tag that occurs in the closing tag
+   * @param nodeEnd the last token in the tag
+   */
+  HtmlScriptTagNode(Token nodeStart, Token tag,
+      List<XmlAttributeNode> attributes, Token attributeEnd,
+      List<XmlTagNode> tagNodes, Token contentEnd, Token closingTag,
+      Token nodeEnd)
+      : super(nodeStart, tag, attributes, attributeEnd, tagNodes, contentEnd,
+          closingTag, nodeEnd);
+
+  /**
+   * Return the AST structure representing the Dart code within this tag, or `null` if this
+   * tag references an external script.
+   *
+   * @return the AST structure representing the Dart code within this tag
+   */
+  CompilationUnit get script => _script;
+
+  /**
+   * Set the AST structure representing the Dart code within this tag to the given compilation unit.
+   *
+   * @param unit the AST structure representing the Dart code within this tag
+   */
+  void set script(CompilationUnit unit) {
+    _script = unit;
+  }
+
+  @override
+  accept(XmlVisitor visitor) => visitor.visitHtmlScriptTagNode(this);
+}
+
+/**
+ * Instances of the class `HtmlUnit` represent the contents of an HTML file.
+ */
+class HtmlUnit extends XmlNode {
+  /**
+   * The first token in the token stream that was parsed to form this HTML unit.
+   */
+  final Token beginToken;
+
+  /**
+   * The last token in the token stream that was parsed to form this compilation unit. This token
+   * should always have a type of [TokenType.EOF].
+   */
+  final Token endToken;
+
+  /**
+   * The tag nodes contained in the receiver (not `null`, contains no `null`s).
+   */
+  List<XmlTagNode> _tagNodes;
+
+  /**
+   * Construct a new instance representing the content of an HTML file.
+   *
+   * @param beginToken the first token in the file (not `null`)
+   * @param tagNodes child tag nodes of the receiver (not `null`, contains no `null`s)
+   * @param endToken the last token in the token stream which should be of type
+   *          [TokenType.EOF]
+   */
+  HtmlUnit(this.beginToken, List<XmlTagNode> tagNodes, this.endToken) {
+    this._tagNodes = becomeParentOfAll(tagNodes);
+  }
+
+  /**
+   * Return the element associated with this HTML unit.
+   *
+   * @return the element or `null` if the receiver is not resolved
+   */
+  @override
+  HtmlElement get element => super.element as HtmlElement;
+
+  @override
+  void set element(Element element) {
+    if (element != null && element is! HtmlElement) {
+      throw new IllegalArgumentException(
+          "HtmlElement expected, but ${element.runtimeType} given");
+    }
+    super.element = element;
+  }
+
+  /**
+   * Answer the tag nodes contained in the receiver. Callers should not manipulate the returned list
+   * to edit the AST structure.
+   *
+   * @return the children (not `null`, contains no `null`s)
+   */
+  List<XmlTagNode> get tagNodes => _tagNodes;
+
+  @override
+  accept(XmlVisitor visitor) => visitor.visitHtmlUnit(this);
+
+  @override
+  void visitChildren(XmlVisitor visitor) {
+    for (XmlTagNode node in _tagNodes) {
+      node.accept(visitor);
+    }
+  }
+}
+
+/**
+ * Instances of the class `RecursiveXmlVisitor` implement an XML visitor that will recursively
+ * visit all of the nodes in an XML structure. For example, using an instance of this class to visit
+ * a [XmlTagNode] will also cause all of the contained [XmlAttributeNode]s and
+ * [XmlTagNode]s to be visited.
+ *
+ * Subclasses that override a visit method must either invoke the overridden visit method or must
+ * explicitly ask the visited node to visit its children. Failure to do so will cause the children
+ * of the visited node to not be visited.
+ */
+class RecursiveXmlVisitor<R> implements XmlVisitor<R> {
+  @override
+  R visitHtmlScriptTagNode(HtmlScriptTagNode node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitHtmlUnit(HtmlUnit node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitXmlAttributeNode(XmlAttributeNode node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  R visitXmlTagNode(XmlTagNode node) {
+    node.visitChildren(this);
+    return null;
+  }
+}
+
+/**
+ * Instances of the class `SimpleXmlVisitor` implement an AST visitor that will do nothing
+ * when visiting an AST node. It is intended to be a superclass for classes that use the visitor
+ * pattern primarily as a dispatch mechanism (and hence don't need to recursively visit a whole
+ * structure) and that only need to visit a small number of node types.
+ */
+class SimpleXmlVisitor<R> implements XmlVisitor<R> {
+  @override
+  R visitHtmlScriptTagNode(HtmlScriptTagNode node) => null;
+
+  @override
+  R visitHtmlUnit(HtmlUnit htmlUnit) => null;
+
+  @override
+  R visitXmlAttributeNode(XmlAttributeNode xmlAttributeNode) => null;
+
+  @override
+  R visitXmlTagNode(XmlTagNode xmlTagNode) => null;
+}
+
+/**
+ * Instances of the class `StringScanner` implement a scanner that reads from a string. The
+ * scanning logic is in the superclass.
+ */
+class StringScanner extends AbstractScanner {
+  /**
+   * The string from which characters will be read.
+   */
+  final String _string;
+
+  /**
+   * The number of characters in the string.
+   */
+  int _stringLength = 0;
+
+  /**
+   * The index, relative to the string, of the last character that was read.
+   */
+  int _charOffset = 0;
+
+  /**
+   * Initialize a newly created scanner to scan the characters in the given string.
+   *
+   * @param source the source being scanned
+   * @param string the string from which characters will be read
+   */
+  StringScanner(Source source, this._string) : super(source) {
+    this._stringLength = _string.length;
+    this._charOffset = -1;
+  }
+
+  @override
+  int get offset => _charOffset;
+
+  void set offset(int offset) {
+    _charOffset = offset;
+  }
+
+  @override
+  int advance() {
+    if (++_charOffset < _stringLength) {
+      return _string.codeUnitAt(_charOffset);
+    }
+    _charOffset = _stringLength;
+    return -1;
+  }
+
+  @override
+  String getString(int start, int endDelta) =>
+      _string.substring(start, _charOffset + 1 + endDelta).toString();
+
+  @override
+  int peek() {
+    if (_charOffset + 1 < _stringLength) {
+      return _string.codeUnitAt(_charOffset + 1);
+    }
+    return -1;
+  }
+}
+
+/**
+ * Instances of the class `Token` represent a token that was scanned from the input. Each
+ * token knows which token follows it, acting as the head of a linked list of tokens.
+ */
+class Token {
+  /**
+   * The offset from the beginning of the file to the first character in the token.
+   */
+  final int offset;
+
+  /**
+   * The previous token in the token stream.
+   */
+  Token previous;
+
+  /**
+   * The next token in the token stream.
+   */
+  Token _next;
+
+  /**
+   * The type of the token.
+   */
+  final TokenType type;
+
+  /**
+   * The lexeme represented by this token.
+   */
+  String _value;
+
+  /**
+   * Initialize a newly created token.
+   *
+   * @param type the token type (not `null`)
+   * @param offset the offset from the beginning of the file to the first character in the token
+   */
+  Token.con1(TokenType type, int offset) : this.con2(type, offset, type.lexeme);
+
+  /**
+   * Initialize a newly created token.
+   *
+   * @param type the token type (not `null`)
+   * @param offset the offset from the beginning of the file to the first character in the token
+   * @param value the lexeme represented by this token (not `null`)
+   */
+  Token.con2(this.type, this.offset, String value) {
+    this._value = StringUtilities.intern(value);
+  }
+
+  /**
+   * Return the offset from the beginning of the file to the character after last character of the
+   * token.
+   *
+   * @return the offset from the beginning of the file to the first character after last character
+   *         of the token
+   */
+  int get end => offset + length;
+
+  /**
+   * Return `true` if this token is a synthetic token. A synthetic token is a token that was
+   * introduced by the parser in order to recover from an error in the code. Synthetic tokens always
+   * have a length of zero (`0`).
+   *
+   * @return `true` if this token is a synthetic token
+   */
+  bool get isSynthetic => length == 0;
+
+  /**
+   * Return the number of characters in the node's source range.
+   *
+   * @return the number of characters in the node's source range
+   */
+  int get length => lexeme.length;
+
+  /**
+   * Return the lexeme that represents this token.
+   *
+   * @return the lexeme (not `null`)
+   */
+  String get lexeme => _value;
+
+  /**
+   * Return the next token in the token stream.
+   *
+   * @return the next token in the token stream
+   */
+  Token get next => _next;
+
+  /**
+   * Set the next token in the token stream to the given token. This has the side-effect of setting
+   * this token to be the previous token for the given token.
+   *
+   * @param token the next token in the token stream
+   * @return the token that was passed in
+   */
+  Token setNext(Token token) {
+    _next = token;
+    token.previous = this;
+    return token;
+  }
+
+  @override
+  String toString() => lexeme;
+}
+
+/**
+ * The enumeration `TokenType` defines the types of tokens that can be returned by the
+ * scanner.
+ */
+class TokenType extends Enum<TokenType> {
+  /**
+   * The type of the token that marks the end of the input.
+   */
+  static const TokenType EOF = const TokenType_EOF('EOF', 0, "");
+
+  static const TokenType EQ = const TokenType('EQ', 1, "=");
+
+  static const TokenType GT = const TokenType('GT', 2, ">");
+
+  static const TokenType LT_SLASH = const TokenType('LT_SLASH', 3, "</");
+
+  static const TokenType LT = const TokenType('LT', 4, "<");
+
+  static const TokenType SLASH_GT = const TokenType('SLASH_GT', 5, "/>");
+
+  static const TokenType COMMENT = const TokenType('COMMENT', 6, null);
+
+  static const TokenType DECLARATION = const TokenType('DECLARATION', 7, null);
+
+  static const TokenType DIRECTIVE = const TokenType('DIRECTIVE', 8, null);
+
+  static const TokenType STRING = const TokenType('STRING', 9, null);
+
+  static const TokenType TAG = const TokenType('TAG', 10, null);
+
+  static const TokenType TEXT = const TokenType('TEXT', 11, null);
+
+  static const List<TokenType> values = const [
+    EOF,
+    EQ,
+    GT,
+    LT_SLASH,
+    LT,
+    SLASH_GT,
+    COMMENT,
+    DECLARATION,
+    DIRECTIVE,
+    STRING,
+    TAG,
+    TEXT
+  ];
+
+  /**
+   * The lexeme that defines this type of token, or `null` if there is more than one possible
+   * lexeme for this type of token.
+   */
+  final String lexeme;
+
+  const TokenType(String name, int ordinal, this.lexeme) : super(name, ordinal);
+}
+
+class TokenType_EOF extends TokenType {
+  const TokenType_EOF(String name, int ordinal, String arg0)
+      : super(name, ordinal, arg0);
+
+  @override
+  String toString() => "-eof-";
+}
+
+/**
+ * Instances of the class `ToSourceVisitor` write a source representation of a visited XML
+ * node (and all of it's children) to a writer.
+ */
+class ToSourceVisitor implements XmlVisitor<Object> {
+  /**
+   * The writer to which the source is to be written.
+   */
+  final PrintWriter _writer;
+
+  /**
+   * Initialize a newly created visitor to write source code representing the visited nodes to the
+   * given writer.
+   *
+   * @param writer the writer to which the source is to be written
+   */
+  ToSourceVisitor(this._writer);
+
+  @override
+  Object visitHtmlScriptTagNode(HtmlScriptTagNode node) =>
+      visitXmlTagNode(node);
+
+  @override
+  Object visitHtmlUnit(HtmlUnit node) {
+    for (XmlTagNode child in node.tagNodes) {
+      _visit(child);
+    }
+    return null;
+  }
+
+  @override
+  Object visitXmlAttributeNode(XmlAttributeNode node) {
+    String name = node.name;
+    Token value = node.valueToken;
+    if (name.length == 0) {
+      _writer.print("__");
+    } else {
+      _writer.print(name);
+    }
+    _writer.print("=");
+    if (value == null) {
+      _writer.print("__");
+    } else {
+      _writer.print(value.lexeme);
+    }
+    return null;
+  }
+
+  @override
+  Object visitXmlTagNode(XmlTagNode node) {
+    _writer.print("<");
+    String tagName = node.tag;
+    _writer.print(tagName);
+    for (XmlAttributeNode attribute in node.attributes) {
+      _writer.print(" ");
+      _visit(attribute);
+    }
+    _writer.print(node.attributeEnd.lexeme);
+    if (node.closingTag != null) {
+      for (XmlTagNode child in node.tagNodes) {
+        _visit(child);
+      }
+      _writer.print("</");
+      _writer.print(tagName);
+      _writer.print(">");
+    }
+    return null;
+  }
+
+  /**
+   * Safely visit the given node.
+   *
+   * @param node the node to be visited
+   */
+  void _visit(XmlNode node) {
+    if (node != null) {
+      node.accept(this);
+    }
+  }
+}
+
+/**
+ * Instances of `XmlAttributeNode` represent name/value pairs owned by an [XmlTagNode].
+ */
+class XmlAttributeNode extends XmlNode {
+  /**
+   * An empty list of XML attribute nodes.
+   */
+  static const List<XmlAttributeNode> EMPTY_LIST = const <XmlAttributeNode>[];
+
+  final Token _name;
+
+  final Token equals;
+
+  final Token _value;
+
+  List<XmlExpression> expressions = XmlExpression.EMPTY_ARRAY;
+
+  /**
+   * Construct a new instance representing an XML attribute.
+   *
+   * @param name the name token (not `null`). This may be a zero length token if the attribute
+   *          is badly formed.
+   * @param equals the equals sign or `null` if none
+   * @param value the value token (not `null`)
+   */
+  XmlAttributeNode(this._name, this.equals, this._value);
+
+  @override
+  Token get beginToken => _name;
+
+  @override
+  Token get endToken => _value;
+
+  /**
+   * Answer the attribute name. This may be a zero length string if the attribute is badly formed.
+   *
+   * @return the name (not `null`)
+   */
+  String get name => _name.lexeme;
+
+  /**
+   * Answer the attribute name token. This may be a zero length token if the attribute is badly
+   * formed.
+   *
+   * @return the name token (not `null`)
+   */
+  Token get nameToken => _name;
+
+  /**
+   * Answer the lexeme for the value token without the leading and trailing quotes.
+   *
+   * @return the text or `null` if the value is not specified
+   */
+  String get text {
+    if (_value == null) {
+      return null;
+    }
+    //TODO (danrubel): replace HTML character encodings with the actual
+    // characters
+    String text = _value.lexeme;
+    int len = text.length;
+    if (len > 0) {
+      if (text.codeUnitAt(0) == 0x22) {
+        if (len > 1 && text.codeUnitAt(len - 1) == 0x22) {
+          return text.substring(1, len - 1);
+        } else {
+          return text.substring(1);
+        }
+      } else if (text.codeUnitAt(0) == 0x27) {
+        if (len > 1 && text.codeUnitAt(len - 1) == 0x27) {
+          return text.substring(1, len - 1);
+        } else {
+          return text.substring(1);
+        }
+      }
+    }
+    return text;
+  }
+
+  /**
+   * Answer the offset of the value after the leading quote.
+   *
+   * @return the offset of the value, or `-1` if the value is not specified
+   */
+  int get textOffset {
+    if (_value == null) {
+      return -1;
+    }
+    String text = _value.lexeme;
+    if (StringUtilities.startsWithChar(text, 0x22) ||
+        StringUtilities.startsWithChar(text, 0x27)) {
+      return _value.offset + 1;
+    }
+    return _value.offset;
+  }
+
+  /**
+   * Answer the attribute value token. A properly formed value will start and end with matching
+   * quote characters, but the value returned may not be properly formed.
+   *
+   * @return the value token or `null` if this represents a badly formed attribute
+   */
+  Token get valueToken => _value;
+
+  @override
+  accept(XmlVisitor visitor) => visitor.visitXmlAttributeNode(this);
+
+  @override
+  void visitChildren(XmlVisitor visitor) {
+    // no children to visit
+  }
+}
+
+/**
+ * Instances of the class `XmlExpression` represent an abstract expression embedded into
+ * [XmlNode].
+ */
+abstract class XmlExpression {
+  /**
+   * An empty list of expressions.
+   */
+  static const List<XmlExpression> EMPTY_ARRAY = const <XmlExpression>[];
+
+  /**
+   * Return the offset of the character immediately following the last character of this
+   * expression's source range. This is equivalent to `getOffset() + getLength()`.
+   *
+   * @return the offset of the character just past the expression's source range
+   */
+  int get end;
+
+  /**
+   * Return the number of characters in the expression's source range.
+   */
+  int get length;
+
+  /**
+   * Return the offset of the first character in the expression's source range.
+   */
+  int get offset;
+
+  /**
+   * Check if the given offset belongs to the expression's source range.
+   */
+  bool contains(int offset) => this.offset <= offset && offset < end;
+
+  /**
+   * Return the [Reference] at the given offset.
+   *
+   * @param offset the offset from the beginning of the file
+   * @return the [Reference] at the given offset, maybe `null`
+   */
+  XmlExpression_Reference getReference(int offset);
+}
+
+/**
+ * The reference to the [Element].
+ */
+class XmlExpression_Reference {
+  Element element;
+
+  int offset = 0;
+
+  int length = 0;
+
+  XmlExpression_Reference(Element element, int offset, int length) {
+    this.element = element;
+    this.offset = offset;
+    this.length = length;
+  }
+}
+
+/**
+ * The abstract class `XmlNode` defines behavior common to all XML/HTML nodes.
+ */
+abstract class XmlNode {
+  /**
+   * The parent of the node, or `null` if the node is the root of an AST structure.
+   */
+  XmlNode _parent;
+
+  /**
+   * The element associated with this node or `null` if the receiver is not resolved.
+   */
+  Element _element;
+
+  /**
+   * Return the first token included in this node's source range.
+   *
+   * @return the first token or `null` if none
+   */
+  Token get beginToken;
+
+  /**
+   * Return the element associated with this node.
+   *
+   * @return the element or `null` if the receiver is not resolved
+   */
+  Element get element => _element;
+
+  /**
+   * Set the element associated with this node.
+   *
+   * @param element the element
+   */
+  void set element(Element element) {
+    this._element = element;
+  }
+
+  /**
+   * Return the offset of the character immediately following the last character of this node's
+   * source range. This is equivalent to `node.getOffset() + node.getLength()`. For an html
+   * unit this will be equal to the length of the unit's source.
+   *
+   * @return the offset of the character just past the node's source range
+   */
+  int get end => offset + length;
+
+  /**
+   * Return the last token included in this node's source range.
+   *
+   * @return the last token or `null` if none
+   */
+  Token get endToken;
+
+  /**
+   * Return the number of characters in the node's source range.
+   *
+   * @return the number of characters in the node's source range
+   */
+  int get length {
+    Token beginToken = this.beginToken;
+    Token endToken = this.endToken;
+    if (beginToken == null || endToken == null) {
+      return -1;
+    }
+    return endToken.offset + endToken.length - beginToken.offset;
+  }
+
+  /**
+   * Return the offset from the beginning of the file to the first character in the node's source
+   * range.
+   *
+   * @return the offset from the beginning of the file to the first character in the node's source
+   *         range
+   */
+  int get offset {
+    Token beginToken = this.beginToken;
+    if (beginToken == null) {
+      return -1;
+    }
+    return this.beginToken.offset;
+  }
+
+  /**
+   * Return this node's parent node, or `null` if this node is the root of an AST structure.
+   *
+   * Note that the relationship between an AST node and its parent node may change over the lifetime
+   * of a node.
+   *
+   * @return the parent of this node, or `null` if none
+   */
+  XmlNode get parent => _parent;
+
+  /**
+   * Set the parent of this node to the given node.
+   *
+   * @param newParent the node that is to be made the parent of this node
+   */
+  void set parent(XmlNode newParent) {
+    XmlNode current = newParent;
+    while (current != null) {
+      if (identical(current, this)) {
+        AnalysisEngine.instance.logger.logError(
+            "Circular structure while setting an XML node's parent",
+            new CaughtException(
+                new ArgumentError(_buildRecursiveStructureMessage(newParent)),
+                null));
+        return;
+      }
+      current = current.parent;
+    }
+    _parent = newParent;
+  }
+
+  /**
+   * Use the given visitor to visit this node.
+   *
+   * @param visitor the visitor that will visit this node
+   * @return the value returned by the visitor as a result of visiting this node
+   */
+  accept(XmlVisitor visitor);
+
+  /**
+   * Make this node the parent of the given child node.
+   *
+   * @param child the node that will become a child of this node
+   * @return the node that was made a child of this node
+   */
+  XmlNode becomeParentOf(XmlNode child) {
+    if (child != null) {
+      XmlNode node = child;
+      node.parent = this;
+    }
+    return child;
+  }
+
+  /**
+   * Make this node the parent of the given child nodes.
+   *
+   * @param children the nodes that will become the children of this node
+   * @param ifEmpty the (empty) nodes to return if "children" is empty
+   * @return the nodes that were made children of this node
+   */
+  List becomeParentOfAll(List children, {List ifEmpty}) {
+    if (children == null || children.isEmpty) {
+      if (ifEmpty != null) {
+        return ifEmpty;
+      }
+    }
+    if (children != null) {
+      children.forEach((XmlNode node) {
+        node.parent = this;
+      });
+    }
+    return children;
+  }
+
+  @override
+  String toString() {
+    PrintStringWriter writer = new PrintStringWriter();
+    accept(new ToSourceVisitor(writer));
+    return writer.toString();
+  }
+
+  /**
+   * Use the given visitor to visit all of the children of this node. The children will be visited
+   * in source order.
+   *
+   * @param visitor the visitor that will be used to visit the children of this node
+   */
+  void visitChildren(XmlVisitor visitor);
+
+  /**
+   * This method exists for debugging purposes only.
+   */
+  void _appendIdentifier(StringBuffer buffer, XmlNode node) {
+    if (node is XmlTagNode) {
+      buffer.write(node.tag);
+    } else if (node is XmlAttributeNode) {
+      buffer.write(node.name);
+    } else {
+      buffer.write("htmlUnit");
+    }
+  }
+
+  /**
+   * This method exists for debugging purposes only.
+   */
+  String _buildRecursiveStructureMessage(XmlNode newParent) {
+    StringBuffer buffer = new StringBuffer();
+    buffer.write("Attempt to create recursive structure: ");
+    XmlNode current = newParent;
+    while (current != null) {
+      if (!identical(current, newParent)) {
+        buffer.write(" -> ");
+      }
+      if (identical(current, this)) {
+        buffer.writeCharCode(0x2A);
+        _appendIdentifier(buffer, current);
+        buffer.writeCharCode(0x2A);
+      } else {
+        _appendIdentifier(buffer, current);
+      }
+      current = current.parent;
+    }
+    return buffer.toString();
+  }
+}
+
+/**
+ * Instances of the class `XmlParser` are used to parse tokens into a AST structure comprised
+ * of [XmlNode]s.
+ */
+class XmlParser {
+  /**
+   * The source being parsed.
+   */
+  final Source source;
+
+  /**
+   * The next token to be parsed.
+   */
+  Token _currentToken;
+
+  /**
+   * Construct a parser for the specified source.
+   *
+   * @param source the source being parsed
+   */
+  XmlParser(this.source);
+
+  /**
+   * Answer the current token.
+   *
+   * @return the current token
+   */
+  Token get currentToken => _currentToken;
+
+  /**
+   * Create a node representing an attribute.
+   *
+   * @param name the name of the attribute
+   * @param equals the equals sign, or `null` if there is no value
+   * @param value the value of the attribute
+   * @return the node that was created
+   */
+  XmlAttributeNode createAttributeNode(Token name, Token equals, Token value) =>
+      new XmlAttributeNode(name, equals, value);
+
+  /**
+   * Create a node representing a tag.
+   *
+   * @param nodeStart the token marking the beginning of the tag
+   * @param tag the name of the tag
+   * @param attributes the attributes in the tag
+   * @param attributeEnd the token terminating the region where attributes can be
+   * @param tagNodes the children of the tag
+   * @param contentEnd the token that starts the closing tag
+   * @param closingTag the name of the tag that occurs in the closing tag
+   * @param nodeEnd the last token in the tag
+   * @return the node that was created
+   */
+  XmlTagNode createTagNode(Token nodeStart, Token tag,
+      List<XmlAttributeNode> attributes, Token attributeEnd,
+      List<XmlTagNode> tagNodes, Token contentEnd, Token closingTag,
+      Token nodeEnd) => new XmlTagNode(nodeStart, tag, attributes, attributeEnd,
+      tagNodes, contentEnd, closingTag, nodeEnd);
+
+  /**
+   * Answer `true` if the specified tag is self closing and thus should never have content or
+   * child tag nodes.
+   *
+   * @param tag the tag (not `null`)
+   * @return `true` if self closing
+   */
+  bool isSelfClosing(Token tag) => false;
+
+  /**
+   * Parse the entire token stream and in the process, advance the current token to the end of the
+   * token stream.
+   *
+   * @return the list of tag nodes found (not `null`, contains no `null`)
+   */
+  List<XmlTagNode> parseTopTagNodes(Token firstToken) {
+    _currentToken = firstToken;
+    List<XmlTagNode> tagNodes = new List<XmlTagNode>();
+    TokenType type = _currentToken.type;
+    while (type != TokenType.EOF) {
+      if (type == TokenType.LT) {
+        tagNodes.add(_parseTagNode());
+      } else if (type == TokenType.DECLARATION ||
+          type == TokenType.DIRECTIVE ||
+          type == TokenType.COMMENT) {
+        // ignored tokens
+        _currentToken = _currentToken.next;
+      } else {
+        _reportUnexpectedToken();
+        _currentToken = _currentToken.next;
+      }
+      type = _currentToken.type;
+    }
+    return tagNodes;
+  }
+
+  /**
+   * Insert a synthetic token of the specified type before the current token
+   *
+   * @param type the type of token to be inserted (not `null`)
+   * @return the synthetic token that was inserted (not `null`)
+   */
+  Token _insertSyntheticToken(TokenType type) {
+    Token token = new Token.con2(type, _currentToken.offset, "");
+    _currentToken.previous.setNext(token);
+    token.setNext(_currentToken);
+    return token;
+  }
+
+  /**
+   * Parse the token stream for an attribute. This method advances the current token over the
+   * attribute, but should not be called if the [currentToken] is not [TokenType.TAG].
+   *
+   * @return the attribute (not `null`)
+   */
+  XmlAttributeNode _parseAttribute() {
+    // Assume the current token is a tag
+    Token name = _currentToken;
+    _currentToken = _currentToken.next;
+    // Equals sign
+    Token equals;
+    if (_currentToken.type == TokenType.EQ) {
+      equals = _currentToken;
+      _currentToken = _currentToken.next;
+    } else {
+      _reportUnexpectedToken();
+      equals = _insertSyntheticToken(TokenType.EQ);
+    }
+    // String value
+    Token value;
+    if (_currentToken.type == TokenType.STRING) {
+      value = _currentToken;
+      _currentToken = _currentToken.next;
+    } else {
+      _reportUnexpectedToken();
+      value = _insertSyntheticToken(TokenType.STRING);
+    }
+    return createAttributeNode(name, equals, value);
+  }
+
+  /**
+   * Parse the stream for a sequence of attributes. This method advances the current token to the
+   * next [TokenType.GT], [TokenType.SLASH_GT], or [TokenType.EOF].
+   *
+   * @return a collection of zero or more attributes (not `null`, contains no `null`s)
+   */
+  List<XmlAttributeNode> _parseAttributes() {
+    TokenType type = _currentToken.type;
+    if (type == TokenType.GT ||
+        type == TokenType.SLASH_GT ||
+        type == TokenType.EOF) {
+      return XmlTagNode.NO_ATTRIBUTES;
+    }
+    List<XmlAttributeNode> attributes = new List<XmlAttributeNode>();
+    while (type != TokenType.GT &&
+        type != TokenType.SLASH_GT &&
+        type != TokenType.EOF) {
+      if (type == TokenType.TAG) {
+        attributes.add(_parseAttribute());
+      } else {
+        _reportUnexpectedToken();
+        _currentToken = _currentToken.next;
+      }
+      type = _currentToken.type;
+    }
+    return attributes;
+  }
+
+  /**
+   * Parse the stream for a sequence of tag nodes existing within a parent tag node. This method
+   * advances the current token to the next [TokenType.LT_SLASH] or [TokenType.EOF].
+   *
+   * @return a list of nodes (not `null`, contains no `null`s)
+   */
+  List<XmlTagNode> _parseChildTagNodes() {
+    TokenType type = _currentToken.type;
+    if (type == TokenType.LT_SLASH || type == TokenType.EOF) {
+      return XmlTagNode.NO_TAG_NODES;
+    }
+    List<XmlTagNode> nodes = new List<XmlTagNode>();
+    while (type != TokenType.LT_SLASH && type != TokenType.EOF) {
+      if (type == TokenType.LT) {
+        nodes.add(_parseTagNode());
+      } else if (type == TokenType.COMMENT) {
+        // ignored token
+        _currentToken = _currentToken.next;
+      } else {
+        _reportUnexpectedToken();
+        _currentToken = _currentToken.next;
+      }
+      type = _currentToken.type;
+    }
+    return nodes;
+  }
+
+  /**
+   * Parse the token stream for the next tag node. This method advances current token over the
+   * parsed tag node, but should only be called if the current token is [TokenType.LT]
+   *
+   * @return the tag node or `null` if none found
+   */
+  XmlTagNode _parseTagNode() {
+    // Assume that the current node is a tag node start TokenType.LT
+    Token nodeStart = _currentToken;
+    _currentToken = _currentToken.next;
+    // Get the tag or create a synthetic tag and report an error
+    Token tag;
+    if (_currentToken.type == TokenType.TAG) {
+      tag = _currentToken;
+      _currentToken = _currentToken.next;
+    } else {
+      _reportUnexpectedToken();
+      tag = _insertSyntheticToken(TokenType.TAG);
+    }
+    // Parse the attributes
+    List<XmlAttributeNode> attributes = _parseAttributes();
+    // Token ending attribute list
+    Token attributeEnd;
+    if (_currentToken.type == TokenType.GT ||
+        _currentToken.type == TokenType.SLASH_GT) {
+      attributeEnd = _currentToken;
+      _currentToken = _currentToken.next;
+    } else {
+      _reportUnexpectedToken();
+      attributeEnd = _insertSyntheticToken(TokenType.SLASH_GT);
+    }
+    // If the node has no children, then return the node
+    if (attributeEnd.type == TokenType.SLASH_GT || isSelfClosing(tag)) {
+      return createTagNode(nodeStart, tag, attributes, attributeEnd,
+          XmlTagNode.NO_TAG_NODES, _currentToken, null, attributeEnd);
+    }
+    // Parse the child tag nodes
+    List<XmlTagNode> tagNodes = _parseChildTagNodes();
+    // Token ending child tag nodes
+    Token contentEnd;
+    if (_currentToken.type == TokenType.LT_SLASH) {
+      contentEnd = _currentToken;
+      _currentToken = _currentToken.next;
+    } else {
+      // TODO (danrubel): handle self closing HTML elements by inserting
+      // synthetic tokens but not reporting an error
+      _reportUnexpectedToken();
+      contentEnd = _insertSyntheticToken(TokenType.LT_SLASH);
+    }
+    // Closing tag
+    Token closingTag;
+    if (_currentToken.type == TokenType.TAG) {
+      closingTag = _currentToken;
+      _currentToken = _currentToken.next;
+    } else {
+      _reportUnexpectedToken();
+      closingTag = _insertSyntheticToken(TokenType.TAG);
+    }
+    // Token ending node
+    Token nodeEnd;
+    if (_currentToken.type == TokenType.GT) {
+      nodeEnd = _currentToken;
+      _currentToken = _currentToken.next;
+    } else {
+      _reportUnexpectedToken();
+      nodeEnd = _insertSyntheticToken(TokenType.GT);
+    }
+    return createTagNode(nodeStart, tag, attributes, attributeEnd, tagNodes,
+        contentEnd, closingTag, nodeEnd);
+  }
+
+  /**
+   * Report the current token as unexpected
+   */
+  void _reportUnexpectedToken() {
+    // TODO (danrubel): report unexpected token
+  }
+}
+
+/**
+ * Instances of `XmlTagNode` represent XML or HTML elements such as `` and
+ * `<body foo="bar"> ... </body>`.
+ */
+class XmlTagNode extends XmlNode {
+  /**
+   * Constant representing empty list of attributes.
+   */
+  static List<XmlAttributeNode> NO_ATTRIBUTES =
+      new UnmodifiableListView(new List<XmlAttributeNode>());
+
+  /**
+   * Constant representing empty list of tag nodes.
+   */
+  static List<XmlTagNode> NO_TAG_NODES =
+      new UnmodifiableListView(new List<XmlTagNode>());
+
+  /**
+   * The starting [TokenType.LT] token (not `null`).
+   */
+  final Token nodeStart;
+
+  /**
+   * The [TokenType.TAG] token after the starting '&lt;' (not `null`).
+   */
+  final Token _tag;
+
+  /**
+   * The attributes contained by the receiver (not `null`, contains no `null`s).
+   */
+  List<XmlAttributeNode> _attributes;
+
+  /**
+   * The [TokenType.GT] or [TokenType.SLASH_GT] token after the attributes (not
+   * `null`). The token may be the same token as [nodeEnd] if there are no child
+   * [tagNodes].
+   */
+  final Token attributeEnd;
+
+  /**
+   * The tag nodes contained in the receiver (not `null`, contains no `null`s).
+   */
+  List<XmlTagNode> _tagNodes;
+
+  /**
+   * The token (not `null`) after the content, which may be
+   * * (1) [TokenType.LT_SLASH] for nodes with open and close tags, or
+   * * (2) the [TokenType.LT] nodeStart of the next sibling node if this node is self
+   * closing or the attributeEnd is [TokenType.SLASH_GT], or
+   * * (3) [TokenType.EOF] if the node does not have a closing tag and is the last node in
+   * the stream [TokenType.LT_SLASH] token after the content, or `null` if there is no
+   * content and the attributes ended with [TokenType.SLASH_GT].
+   */
+  final Token contentEnd;
+
+  /**
+   * The closing [TokenType.TAG] after the child elements or `null` if there is no
+   * content and the attributes ended with [TokenType.SLASH_GT]
+   */
+  final Token closingTag;
+
+  /**
+   * The ending [TokenType.GT] or [TokenType.SLASH_GT] token (not `null`).
+   */
+  final Token nodeEnd;
+
+  /**
+   * The expressions that are embedded in the tag's content.
+   */
+  List<XmlExpression> expressions = XmlExpression.EMPTY_ARRAY;
+
+  /**
+   * Construct a new instance representing an XML or HTML element
+   *
+   * @param nodeStart the starting [TokenType.LT] token (not `null`)
+   * @param tag the [TokenType.TAG] token after the starting '&lt;' (not `null`).
+   * @param attributes the attributes associated with this element or [NO_ATTRIBUTES] (not
+   *          `null`, contains no `null`s)
+   * @param attributeEnd The [TokenType.GT] or [TokenType.SLASH_GT] token after the
+   *          attributes (not `null`). The token may be the same token as [nodeEnd] if
+   *          there are no child [tagNodes].
+   * @param tagNodes child tag nodes of the receiver or [NO_TAG_NODES] (not `null`,
+   *          contains no `null`s)
+   * @param contentEnd the token (not `null`) after the content, which may be
+   *          * (1) [TokenType.LT_SLASH] for nodes with open and close tags, or
+   *          * (2) the [TokenType.LT] nodeStart of the next sibling node if this node is
+   *          self closing or the attributeEnd is [TokenType.SLASH_GT], or
+   *          * (3) [TokenType.EOF] if the node does not have a closing tag and is the last
+   *          node in the stream [TokenType.LT_SLASH] token after the content, or `null`
+   *          if there is no content and the attributes ended with [TokenType.SLASH_GT].
+   * @param closingTag the closing [TokenType.TAG] after the child elements or `null` if
+   *          there is no content and the attributes ended with [TokenType.SLASH_GT]
+   * @param nodeEnd the ending [TokenType.GT] or [TokenType.SLASH_GT] token (not
+   *          `null`)
+   */
+  XmlTagNode(this.nodeStart, this._tag, List<XmlAttributeNode> attributes,
+      this.attributeEnd, List<XmlTagNode> tagNodes, this.contentEnd,
+      this.closingTag, this.nodeEnd) {
+    this._attributes = becomeParentOfAll(attributes, ifEmpty: NO_ATTRIBUTES);
+    this._tagNodes = becomeParentOfAll(tagNodes, ifEmpty: NO_TAG_NODES);
+  }
+
+  /**
+   * Answer the receiver's attributes. Callers should not manipulate the returned list to edit the
+   * AST structure.
+   *
+   * @return the attributes (not `null`, contains no `null`s)
+   */
+  List<XmlAttributeNode> get attributes => _attributes;
+
+  @override
+  Token get beginToken => nodeStart;
+
+  /**
+   * Return a string representing the content contained in the receiver. This
+   * includes the textual representation of any child tag nodes ([getTagNodes]).
+   * Whitespace between '&lt;', '&lt;/', and '>', '/>' is discarded, but all
+   * other whitespace is preserved.
+   */
+  String get content {
+    Token token = attributeEnd.next;
+    if (identical(token, contentEnd)) {
+      return "";
+    }
+    // TODO(danrubel) Handle CDATA and replace HTML character encodings with
+    // the actual characters.
+    String content = token.lexeme;
+    token = token.next;
+    if (identical(token, contentEnd)) {
+      return content;
+    }
+    StringBuffer buffer = new StringBuffer();
+    buffer.write(content);
+    while (!identical(token, contentEnd)) {
+      buffer.write(token.lexeme);
+      token = token.next;
+    }
+    return buffer.toString();
+  }
+
+  @override
+  Token get endToken {
+    if (nodeEnd != null) {
+      return nodeEnd;
+    }
+    if (closingTag != null) {
+      return closingTag;
+    }
+    if (contentEnd != null) {
+      return contentEnd;
+    }
+    if (!_tagNodes.isEmpty) {
+      return _tagNodes[_tagNodes.length - 1].endToken;
+    }
+    if (attributeEnd != null) {
+      return attributeEnd;
+    }
+    if (!_attributes.isEmpty) {
+      return _attributes[_attributes.length - 1].endToken;
+    }
+    return _tag;
+  }
+
+  /**
+   * Answer the tag name after the starting '&lt;'.
+   *
+   * @return the tag name (not `null`)
+   */
+  String get tag => _tag.lexeme;
+
+  /**
+   * Answer the tag nodes contained in the receiver. Callers should not manipulate the returned list
+   * to edit the AST structure.
+   *
+   * @return the children (not `null`, contains no `null`s)
+   */
+  List<XmlTagNode> get tagNodes => _tagNodes;
+
+  /**
+   * Answer the [TokenType.TAG] token after the starting '&lt;'.
+   *
+   * @return the token (not `null`)
+   */
+  Token get tagToken => _tag;
+
+  @override
+  accept(XmlVisitor visitor) => visitor.visitXmlTagNode(this);
+
+  /**
+   * Answer the attribute with the specified name.
+   *
+   * @param name the attribute name
+   * @return the attribute or `null` if no matching attribute is found
+   */
+  XmlAttributeNode getAttribute(String name) {
+    for (XmlAttributeNode attribute in _attributes) {
+      if (attribute.name == name) {
+        return attribute;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Find the attribute with the given name (see [getAttribute] and answer the lexeme
+   * for the attribute's value token without the leading and trailing quotes (see
+   * [XmlAttributeNode.getText]).
+   *
+   * @param name the attribute name
+   * @return the attribute text or `null` if no matching attribute is found
+   */
+  String getAttributeText(String name) {
+    XmlAttributeNode attribute = getAttribute(name);
+    return attribute != null ? attribute.text : null;
+  }
+
+  @override
+  void visitChildren(XmlVisitor visitor) {
+    for (XmlAttributeNode node in _attributes) {
+      node.accept(visitor);
+    }
+    for (XmlTagNode node in _tagNodes) {
+      node.accept(visitor);
+    }
+  }
+}
+
+/**
+ * The interface `XmlVisitor` defines the behavior of objects that can be used to visit an
+ * [XmlNode] structure.
+ */
+abstract class XmlVisitor<R> {
+  R visitHtmlScriptTagNode(HtmlScriptTagNode node);
+
+  R visitHtmlUnit(HtmlUnit htmlUnit);
+
+  R visitXmlAttributeNode(XmlAttributeNode xmlAttributeNode);
+
+  R visitXmlTagNode(XmlTagNode xmlTagNode);
+}
diff --git a/analyzer/lib/src/generated/incremental_logger.dart b/analyzer/lib/src/generated/incremental_logger.dart
new file mode 100644
index 0000000..8abc6fe
--- /dev/null
+++ b/analyzer/lib/src/generated/incremental_logger.dart
@@ -0,0 +1,186 @@
+// 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 engine.incremental_logger;
+
+/**
+ * The shared instance of [Logger] used by several incremental resolution
+ * classes. It is initialized externally by the Analysis Engine client.
+ */
+Logger logger = NULL_LOGGER;
+
+/**
+ * An instance of [Logger] that does not print anything.
+ */
+final Logger NULL_LOGGER = new _NullLogger();
+
+/**
+ * An instance of [Logger] that uses `print` for output.
+ */
+final Logger PRINT_LOGGER = new StringSinkLogger(new _PrintStringSink());
+
+/**
+ * A simple hierarchical logger.
+ */
+abstract class Logger {
+  /**
+   * Mark an enter to a new section with the given [name].
+   */
+  void enter(String name);
+
+  /**
+   * Mark an exit from the current sections, logs the duration.
+   */
+  void exit();
+
+  /**
+   * Logs the given [obj].
+   */
+  void log(Object obj);
+
+  /**
+   * Starts a new timer.
+   */
+  LoggingTimer startTimer();
+}
+
+/**
+ * The handle of a timer.
+ */
+class LoggingTimer {
+  final Logger _logger;
+  final Stopwatch _stopwatch = new Stopwatch();
+
+  LoggingTimer(this._logger) {
+    _stopwatch.start();
+  }
+
+  /**
+   * This methods stop the timer and logs the elapsed time.
+   */
+  void stop(String message) {
+    _stopwatch.stop();
+    _logger.log('$message in ${_stopwatch.elapsedMilliseconds} ms');
+  }
+}
+
+/**
+ * A [Logger] that writes to a [StringSink].
+ */
+class StringSinkLogger implements Logger {
+  static const int _MAX_LINE_LENGTH = 512;
+  final StringSink _sink;
+  final List<_LoggerSection> _sectionStack = <_LoggerSection>[];
+  _LoggerSection _section = new _LoggerSection('', 'ROOT');
+
+  StringSinkLogger(this._sink);
+
+  @override
+  void enter(String name) {
+    log('+++ $name');
+    _sectionStack.add(_section);
+    _section = new _LoggerSection(_section.indent + '\t', name);
+  }
+
+  @override
+  void exit() {
+    DateTime now = new DateTime.now();
+    Duration duration = now.difference(_section.start);
+    String message = '--- ${_section.name} in ${duration.inMilliseconds} ms';
+    _section = _sectionStack.removeLast();
+    log(message);
+  }
+
+  @override
+  void log(Object obj) {
+    DateTime now = new DateTime.now();
+    String indent = _section.indent;
+    String objStr = _getObjectString(obj);
+    String line = '[$now] $indent$objStr';
+    _sink.writeln(line);
+  }
+
+  @override
+  LoggingTimer startTimer() {
+    return new LoggingTimer(this);
+  }
+
+  String _getObjectString(Object obj) {
+    if (obj == null) {
+      return 'null';
+    }
+    if (obj is Function) {
+      obj = obj();
+    }
+    String str = obj.toString();
+    if (str.length < _MAX_LINE_LENGTH) {
+      return str;
+    }
+    return str.split('\n').map((String line) {
+      if (line.length > _MAX_LINE_LENGTH) {
+        line = line.substring(0, _MAX_LINE_LENGTH) + '...';
+      }
+      return line;
+    }).join('\n');
+  }
+}
+
+class _LoggerSection {
+  final DateTime start = new DateTime.now();
+  final String indent;
+  final String name;
+  _LoggerSection(this.indent, this.name);
+}
+
+/**
+ * A [Logger] that does nothing.
+ */
+class _NullLogger implements Logger {
+  @override
+  void enter(String name) {}
+
+  @override
+  void exit() {}
+
+  @override
+  void log(Object obj) {}
+
+  @override
+  LoggingTimer startTimer() {
+    return new LoggingTimer(this);
+  }
+}
+
+/**
+ * A [StringSink] implementation that uses `print`.
+ */
+class _PrintStringSink implements StringSink {
+  String _line = '';
+
+  @override
+  void write(Object obj) {
+    if (obj == null) {
+      _line += 'null';
+    } else {
+      _line += obj.toString();
+    }
+  }
+
+  @override
+  void writeAll(Iterable objects, [String separator = '']) {
+    _line += objects.join(separator);
+  }
+
+  @override
+  void writeCharCode(int charCode) {
+    _line += new String.fromCharCode(charCode);
+  }
+
+  @override
+  void writeln([Object obj = '']) {
+    _line += obj;
+    print(_line);
+    _line = '';
+  }
+}
diff --git a/analyzer/lib/src/generated/incremental_resolution_validator.dart b/analyzer/lib/src/generated/incremental_resolution_validator.dart
new file mode 100644
index 0000000..bab107e
--- /dev/null
+++ b/analyzer/lib/src/generated/incremental_resolution_validator.dart
@@ -0,0 +1,938 @@
+// 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 engine.incremental_resolution_validator;
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+
+/**
+ * Validates that the [actual] and the [expected] units have the same structure
+ * and resolution. Throws [IncrementalResolutionMismatch] otherwise.
+ */
+void assertSameResolution(CompilationUnit actual, CompilationUnit expected,
+    {bool validateTypes: false}) {
+  _SameResolutionValidator validator =
+      new _SameResolutionValidator(validateTypes, expected);
+  actual.accept(validator);
+}
+
+/**
+ * This exception is thrown when a mismatch between actual and expected AST
+ * or resolution is found.
+ */
+class IncrementalResolutionMismatch {
+  final String message;
+  IncrementalResolutionMismatch(this.message);
+}
+
+class _SameResolutionValidator implements AstVisitor {
+  final bool validateTypes;
+  AstNode other;
+
+  _SameResolutionValidator(this.validateTypes, this.other);
+
+  @override
+  visitAdjacentStrings(AdjacentStrings node) {}
+
+  @override
+  visitAnnotation(Annotation node) {
+    Annotation other = this.other;
+    _visitNode(node.name, other.name);
+    _visitNode(node.constructorName, other.constructorName);
+    _visitNode(node.arguments, other.arguments);
+    _verifyElement(node.element, other.element);
+  }
+
+  @override
+  visitArgumentList(ArgumentList node) {
+    ArgumentList other = this.other;
+    _visitList(node.arguments, other.arguments);
+  }
+
+  @override
+  visitAsExpression(AsExpression node) {
+    AsExpression other = this.other;
+    _visitExpression(node, other);
+    _visitNode(node.expression, other.expression);
+    _visitNode(node.type, other.type);
+  }
+
+  @override
+  visitAssertStatement(AssertStatement node) {
+    AssertStatement other = this.other;
+    _visitNode(node.condition, other.condition);
+  }
+
+  @override
+  visitAssignmentExpression(AssignmentExpression node) {
+    AssignmentExpression other = this.other;
+    _visitExpression(node, other);
+    _verifyElement(node.staticElement, other.staticElement);
+    _verifyElement(node.propagatedElement, other.propagatedElement);
+    _visitNode(node.leftHandSide, other.leftHandSide);
+    _visitNode(node.rightHandSide, other.rightHandSide);
+  }
+
+  @override
+  visitAwaitExpression(AwaitExpression node) {
+    AwaitExpression other = this.other;
+    _visitExpression(node, other);
+    _visitNode(node.expression, other.expression);
+  }
+
+  @override
+  visitBinaryExpression(BinaryExpression node) {
+    BinaryExpression other = this.other;
+    _visitExpression(node, other);
+    _verifyElement(node.staticElement, other.staticElement);
+    _verifyElement(node.propagatedElement, other.propagatedElement);
+    _visitNode(node.leftOperand, other.leftOperand);
+    _visitNode(node.rightOperand, other.rightOperand);
+  }
+
+  @override
+  visitBlock(Block node) {
+    Block other = this.other;
+    _visitList(node.statements, other.statements);
+  }
+
+  @override
+  visitBlockFunctionBody(BlockFunctionBody node) {
+    BlockFunctionBody other = this.other;
+    _visitNode(node.block, other.block);
+  }
+
+  @override
+  visitBooleanLiteral(BooleanLiteral node) {
+    BooleanLiteral other = this.other;
+    _visitExpression(node, other);
+  }
+
+  @override
+  visitBreakStatement(BreakStatement node) {
+    BreakStatement other = this.other;
+    _visitNode(node.label, other.label);
+  }
+
+  @override
+  visitCascadeExpression(CascadeExpression node) {
+    CascadeExpression other = this.other;
+    _visitExpression(node, other);
+    _visitNode(node.target, other.target);
+    _visitList(node.cascadeSections, other.cascadeSections);
+  }
+
+  @override
+  visitCatchClause(CatchClause node) {
+    CatchClause other = this.other;
+    _visitNode(node.exceptionType, other.exceptionType);
+    _visitNode(node.exceptionParameter, other.exceptionParameter);
+    _visitNode(node.stackTraceParameter, other.stackTraceParameter);
+    _visitNode(node.body, other.body);
+  }
+
+  @override
+  visitClassDeclaration(ClassDeclaration node) {
+    ClassDeclaration other = this.other;
+    _visitDeclaration(node, other);
+    _visitNode(node.name, other.name);
+    _visitNode(node.typeParameters, other.typeParameters);
+    _visitNode(node.extendsClause, other.extendsClause);
+    _visitNode(node.implementsClause, other.implementsClause);
+    _visitNode(node.withClause, other.withClause);
+    _visitList(node.members, other.members);
+  }
+
+  @override
+  visitClassTypeAlias(ClassTypeAlias node) {
+    ClassTypeAlias other = this.other;
+    _visitDeclaration(node, other);
+    _visitNode(node.name, other.name);
+    _visitNode(node.typeParameters, other.typeParameters);
+    _visitNode(node.superclass, other.superclass);
+    _visitNode(node.withClause, other.withClause);
+  }
+
+  @override
+  visitComment(Comment node) {
+    Comment other = this.other;
+    _visitList(node.references, other.references);
+  }
+
+  @override
+  visitCommentReference(CommentReference node) {
+    CommentReference other = this.other;
+    _visitNode(node.identifier, other.identifier);
+  }
+
+  @override
+  visitCompilationUnit(CompilationUnit node) {
+    CompilationUnit other = this.other;
+    _verifyElement(node.element, other.element);
+    _visitList(node.directives, other.directives);
+    _visitList(node.declarations, other.declarations);
+  }
+
+  @override
+  visitConditionalExpression(ConditionalExpression node) {
+    ConditionalExpression other = this.other;
+    _visitExpression(node, other);
+    _visitNode(node.condition, other.condition);
+    _visitNode(node.thenExpression, other.thenExpression);
+    _visitNode(node.elseExpression, other.elseExpression);
+  }
+
+  @override
+  visitConstructorDeclaration(ConstructorDeclaration node) {
+    ConstructorDeclaration other = this.other;
+    _visitDeclaration(node, other);
+    _visitNode(node.returnType, other.returnType);
+    _visitNode(node.name, other.name);
+    _visitNode(node.parameters, other.parameters);
+    _visitNode(node.redirectedConstructor, other.redirectedConstructor);
+    _visitList(node.initializers, other.initializers);
+  }
+
+  @override
+  visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+    ConstructorFieldInitializer other = this.other;
+    _visitNode(node.fieldName, other.fieldName);
+    _visitNode(node.expression, other.expression);
+  }
+
+  @override
+  visitConstructorName(ConstructorName node) {
+    ConstructorName other = this.other;
+    _verifyElement(node.staticElement, other.staticElement);
+    _visitNode(node.type, other.type);
+    _visitNode(node.name, other.name);
+  }
+
+  @override
+  visitContinueStatement(ContinueStatement node) {
+    ContinueStatement other = this.other;
+    _visitNode(node.label, other.label);
+  }
+
+  @override
+  visitDeclaredIdentifier(DeclaredIdentifier node) {
+    DeclaredIdentifier other = this.other;
+    _visitNode(node.type, other.type);
+    _visitNode(node.identifier, other.identifier);
+  }
+
+  @override
+  visitDefaultFormalParameter(DefaultFormalParameter node) {
+    DefaultFormalParameter other = this.other;
+    _visitNode(node.parameter, other.parameter);
+    _visitNode(node.defaultValue, other.defaultValue);
+  }
+
+  @override
+  visitDoStatement(DoStatement node) {
+    DoStatement other = this.other;
+    _visitNode(node.condition, other.condition);
+    _visitNode(node.body, other.body);
+  }
+
+  @override
+  visitDoubleLiteral(DoubleLiteral node) {
+    DoubleLiteral other = this.other;
+    _visitExpression(node, other);
+  }
+
+  @override
+  visitEmptyFunctionBody(EmptyFunctionBody node) {}
+
+  @override
+  visitEmptyStatement(EmptyStatement node) {}
+
+  @override
+  visitEnumConstantDeclaration(EnumConstantDeclaration node) {
+    EnumConstantDeclaration other = this.other;
+    _visitDeclaration(node, other);
+    _visitNode(node.name, other.name);
+  }
+
+  @override
+  visitEnumDeclaration(EnumDeclaration node) {
+    EnumDeclaration other = this.other;
+    _visitDeclaration(node, other);
+    _visitNode(node.name, other.name);
+    _visitList(node.constants, other.constants);
+  }
+
+  @override
+  visitExportDirective(ExportDirective node) {
+    ExportDirective other = this.other;
+    _visitDirective(node, other);
+  }
+
+  @override
+  visitExpressionFunctionBody(ExpressionFunctionBody node) {
+    ExpressionFunctionBody other = this.other;
+    _visitNode(node.expression, other.expression);
+  }
+
+  @override
+  visitExpressionStatement(ExpressionStatement node) {
+    ExpressionStatement other = this.other;
+    _visitNode(node.expression, other.expression);
+  }
+
+  @override
+  visitExtendsClause(ExtendsClause node) {
+    ExtendsClause other = this.other;
+    _visitNode(node.superclass, other.superclass);
+  }
+
+  @override
+  visitFieldDeclaration(FieldDeclaration node) {
+    FieldDeclaration other = this.other;
+    _visitDeclaration(node, other);
+    _visitNode(node.fields, other.fields);
+  }
+
+  @override
+  visitFieldFormalParameter(FieldFormalParameter node) {
+    FieldFormalParameter other = this.other;
+    _visitNormalFormalParameter(node, other);
+    _visitNode(node.type, other.type);
+    _visitNode(node.parameters, other.parameters);
+  }
+
+  @override
+  visitForEachStatement(ForEachStatement node) {
+    ForEachStatement other = this.other;
+    _visitNode(node.identifier, other.identifier);
+    _visitNode(node.loopVariable, other.loopVariable);
+    _visitNode(node.iterable, other.iterable);
+  }
+
+  @override
+  visitFormalParameterList(FormalParameterList node) {
+    FormalParameterList other = this.other;
+    _visitList(node.parameters, other.parameters);
+  }
+
+  @override
+  visitForStatement(ForStatement node) {
+    ForStatement other = this.other;
+    _visitNode(node.variables, other.variables);
+    _visitNode(node.initialization, other.initialization);
+    _visitNode(node.condition, other.condition);
+    _visitList(node.updaters, other.updaters);
+    _visitNode(node.body, other.body);
+  }
+
+  @override
+  visitFunctionDeclaration(FunctionDeclaration node) {
+    FunctionDeclaration other = this.other;
+    _visitDeclaration(node, other);
+    _visitNode(node.returnType, other.returnType);
+    _visitNode(node.name, other.name);
+    _visitNode(node.functionExpression, other.functionExpression);
+  }
+
+  @override
+  visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
+    FunctionDeclarationStatement other = this.other;
+    _visitNode(node.functionDeclaration, other.functionDeclaration);
+  }
+
+  @override
+  visitFunctionExpression(FunctionExpression node) {
+    FunctionExpression other = this.other;
+    _visitExpression(node, other);
+    _verifyElement(node.element, other.element);
+    _visitNode(node.parameters, other.parameters);
+    _visitNode(node.body, other.body);
+  }
+
+  @override
+  visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    FunctionExpressionInvocation other = this.other;
+    _visitExpression(node, other);
+    _verifyElement(node.staticElement, other.staticElement);
+    _verifyElement(node.propagatedElement, other.propagatedElement);
+    _visitNode(node.function, other.function);
+    _visitNode(node.argumentList, other.argumentList);
+  }
+
+  @override
+  visitFunctionTypeAlias(FunctionTypeAlias node) {
+    FunctionTypeAlias other = this.other;
+    _visitDeclaration(node, other);
+    _visitNode(node.returnType, other.returnType);
+    _visitNode(node.name, other.name);
+    _visitNode(node.typeParameters, other.typeParameters);
+    _visitNode(node.parameters, other.parameters);
+  }
+
+  @override
+  visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+    FunctionTypedFormalParameter other = this.other;
+    _visitNormalFormalParameter(node, other);
+    _visitNode(node.returnType, other.returnType);
+    _visitNode(node.parameters, other.parameters);
+  }
+
+  @override
+  visitHideCombinator(HideCombinator node) {
+    HideCombinator other = this.other;
+    _visitList(node.hiddenNames, other.hiddenNames);
+  }
+
+  @override
+  visitIfStatement(IfStatement node) {
+    IfStatement other = this.other;
+    _visitNode(node.condition, other.condition);
+    _visitNode(node.thenStatement, other.thenStatement);
+    _visitNode(node.elseStatement, other.elseStatement);
+  }
+
+  @override
+  visitImplementsClause(ImplementsClause node) {
+    ImplementsClause other = this.other;
+    _visitList(node.interfaces, other.interfaces);
+  }
+
+  @override
+  visitImportDirective(ImportDirective node) {
+    ImportDirective other = this.other;
+    _visitDirective(node, other);
+    _visitNode(node.prefix, other.prefix);
+    _verifyElement(node.uriElement, other.uriElement);
+  }
+
+  @override
+  visitIndexExpression(IndexExpression node) {
+    IndexExpression other = this.other;
+    _visitExpression(node, other);
+    _verifyElement(node.staticElement, other.staticElement);
+    _verifyElement(node.propagatedElement, other.propagatedElement);
+    _visitNode(node.target, other.target);
+    _visitNode(node.index, other.index);
+  }
+
+  @override
+  visitInstanceCreationExpression(InstanceCreationExpression node) {
+    InstanceCreationExpression other = this.other;
+    _visitExpression(node, other);
+    _verifyElement(node.staticElement, other.staticElement);
+    _visitNode(node.constructorName, other.constructorName);
+    _visitNode(node.argumentList, other.argumentList);
+  }
+
+  @override
+  visitIntegerLiteral(IntegerLiteral node) {
+    IntegerLiteral other = this.other;
+    _visitExpression(node, other);
+  }
+
+  @override
+  visitInterpolationExpression(InterpolationExpression node) {
+    InterpolationExpression other = this.other;
+    _visitNode(node.expression, other.expression);
+  }
+
+  @override
+  visitInterpolationString(InterpolationString node) {}
+
+  @override
+  visitIsExpression(IsExpression node) {
+    IsExpression other = this.other;
+    _visitExpression(node, other);
+    _visitNode(node.expression, other.expression);
+    _visitNode(node.type, other.type);
+  }
+
+  @override
+  visitLabel(Label node) {
+    Label other = this.other;
+    _visitNode(node.label, other.label);
+  }
+
+  @override
+  visitLabeledStatement(LabeledStatement node) {
+    LabeledStatement other = this.other;
+    _visitList(node.labels, other.labels);
+    _visitNode(node.statement, other.statement);
+  }
+
+  @override
+  visitLibraryDirective(LibraryDirective node) {
+    LibraryDirective other = this.other;
+    _visitDirective(node, other);
+    _visitNode(node.name, other.name);
+  }
+
+  @override
+  visitLibraryIdentifier(LibraryIdentifier node) {
+    LibraryIdentifier other = this.other;
+    _visitList(node.components, other.components);
+  }
+
+  @override
+  visitListLiteral(ListLiteral node) {
+    ListLiteral other = this.other;
+    _visitExpression(node, other);
+    _visitList(node.elements, other.elements);
+  }
+
+  @override
+  visitMapLiteral(MapLiteral node) {
+    MapLiteral other = this.other;
+    _visitExpression(node, other);
+    _visitList(node.entries, other.entries);
+  }
+
+  @override
+  visitMapLiteralEntry(MapLiteralEntry node) {
+    MapLiteralEntry other = this.other;
+    _visitNode(node.key, other.key);
+    _visitNode(node.value, other.value);
+  }
+
+  @override
+  visitMethodDeclaration(MethodDeclaration node) {
+    MethodDeclaration other = this.other;
+    _visitDeclaration(node, other);
+    _visitNode(node.name, other.name);
+    _visitNode(node.parameters, other.parameters);
+    _visitNode(node.body, other.body);
+  }
+
+  @override
+  visitMethodInvocation(MethodInvocation node) {
+    MethodInvocation other = this.other;
+    _visitNode(node.target, other.target);
+    _visitNode(node.methodName, other.methodName);
+    _visitNode(node.argumentList, other.argumentList);
+  }
+
+  @override
+  visitNamedExpression(NamedExpression node) {
+    NamedExpression other = this.other;
+    _visitNode(node.name, other.name);
+    _visitNode(node.expression, other.expression);
+  }
+
+  @override
+  visitNativeClause(NativeClause node) {}
+
+  @override
+  visitNativeFunctionBody(NativeFunctionBody node) {}
+
+  @override
+  visitNullLiteral(NullLiteral node) {
+    NullLiteral other = this.other;
+    _visitExpression(node, other);
+  }
+
+  @override
+  visitParenthesizedExpression(ParenthesizedExpression node) {
+    ParenthesizedExpression other = this.other;
+    _visitNode(node.expression, other.expression);
+  }
+
+  @override
+  visitPartDirective(PartDirective node) {
+    PartDirective other = this.other;
+    _visitDirective(node, other);
+  }
+
+  @override
+  visitPartOfDirective(PartOfDirective node) {
+    PartOfDirective other = this.other;
+    _visitDirective(node, other);
+    _visitNode(node.libraryName, other.libraryName);
+  }
+
+  @override
+  visitPostfixExpression(PostfixExpression node) {
+    PostfixExpression other = this.other;
+    _visitExpression(node, other);
+    _verifyElement(node.staticElement, other.staticElement);
+    _verifyElement(node.propagatedElement, other.propagatedElement);
+    _visitNode(node.operand, other.operand);
+  }
+
+  @override
+  visitPrefixedIdentifier(PrefixedIdentifier node) {
+    PrefixedIdentifier other = this.other;
+    _visitExpression(node, other);
+    _visitNode(node.prefix, other.prefix);
+    _visitNode(node.identifier, other.identifier);
+  }
+
+  @override
+  visitPrefixExpression(PrefixExpression node) {
+    PrefixExpression other = this.other;
+    _visitExpression(node, other);
+    _verifyElement(node.staticElement, other.staticElement);
+    _verifyElement(node.propagatedElement, other.propagatedElement);
+    _visitNode(node.operand, other.operand);
+  }
+
+  @override
+  visitPropertyAccess(PropertyAccess node) {
+    PropertyAccess other = this.other;
+    _visitExpression(node, other);
+    _visitNode(node.target, other.target);
+    _visitNode(node.propertyName, other.propertyName);
+  }
+
+  @override
+  visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) {
+    RedirectingConstructorInvocation other = this.other;
+    _verifyElement(node.staticElement, other.staticElement);
+    _visitNode(node.constructorName, other.constructorName);
+    _visitNode(node.argumentList, other.argumentList);
+  }
+
+  @override
+  visitRethrowExpression(RethrowExpression node) {
+    RethrowExpression other = this.other;
+    _visitExpression(node, other);
+  }
+
+  @override
+  visitReturnStatement(ReturnStatement node) {
+    ReturnStatement other = this.other;
+    _visitNode(node.expression, other.expression);
+  }
+
+  @override
+  visitScriptTag(ScriptTag node) {}
+
+  @override
+  visitShowCombinator(ShowCombinator node) {
+    ShowCombinator other = this.other;
+    _visitList(node.shownNames, other.shownNames);
+  }
+
+  @override
+  visitSimpleFormalParameter(SimpleFormalParameter node) {
+    SimpleFormalParameter other = this.other;
+    _visitNormalFormalParameter(node, other);
+    _visitNode(node.type, other.type);
+  }
+
+  @override
+  visitSimpleIdentifier(SimpleIdentifier node) {
+    SimpleIdentifier other = this.other;
+    _verifyElement(node.staticElement, other.staticElement);
+    _verifyElement(node.propagatedElement, other.propagatedElement);
+    _visitExpression(node, other);
+  }
+
+  @override
+  visitSimpleStringLiteral(SimpleStringLiteral node) {}
+
+  @override
+  visitStringInterpolation(StringInterpolation node) {
+    StringInterpolation other = this.other;
+    _visitList(node.elements, other.elements);
+  }
+
+  @override
+  visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    SuperConstructorInvocation other = this.other;
+    _verifyElement(node.staticElement, other.staticElement);
+    _visitNode(node.constructorName, other.constructorName);
+    _visitNode(node.argumentList, other.argumentList);
+  }
+
+  @override
+  visitSuperExpression(SuperExpression node) {
+    SuperExpression other = this.other;
+    _visitExpression(node, other);
+  }
+
+  @override
+  visitSwitchCase(SwitchCase node) {
+    SwitchCase other = this.other;
+    _visitList(node.labels, other.labels);
+    _visitNode(node.expression, other.expression);
+    _visitList(node.statements, other.statements);
+  }
+
+  @override
+  visitSwitchDefault(SwitchDefault node) {
+    SwitchDefault other = this.other;
+    _visitList(node.statements, other.statements);
+  }
+
+  @override
+  visitSwitchStatement(SwitchStatement node) {
+    SwitchStatement other = this.other;
+    _visitNode(node.expression, other.expression);
+    _visitList(node.members, other.members);
+  }
+
+  @override
+  visitSymbolLiteral(SymbolLiteral node) {}
+
+  @override
+  visitThisExpression(ThisExpression node) {
+    ThisExpression other = this.other;
+    _visitExpression(node, other);
+  }
+
+  @override
+  visitThrowExpression(ThrowExpression node) {
+    ThrowExpression other = this.other;
+    _visitNode(node.expression, other.expression);
+  }
+
+  @override
+  visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+    TopLevelVariableDeclaration other = this.other;
+    _visitNode(node.variables, other.variables);
+  }
+
+  @override
+  visitTryStatement(TryStatement node) {
+    TryStatement other = this.other;
+    _visitNode(node.body, other.body);
+    _visitList(node.catchClauses, other.catchClauses);
+    _visitNode(node.finallyBlock, other.finallyBlock);
+  }
+
+  @override
+  visitTypeArgumentList(TypeArgumentList node) {
+    TypeArgumentList other = this.other;
+    _visitList(node.arguments, other.arguments);
+  }
+
+  @override
+  visitTypeName(TypeName node) {
+    TypeName other = this.other;
+    _verifyType(node.type, other.type);
+    _visitNode(node.name, node.name);
+    _visitNode(node.typeArguments, other.typeArguments);
+  }
+
+  @override
+  visitTypeParameter(TypeParameter node) {
+    TypeParameter other = this.other;
+    _visitNode(node.name, other.name);
+    _visitNode(node.bound, other.bound);
+  }
+
+  @override
+  visitTypeParameterList(TypeParameterList node) {
+    TypeParameterList other = this.other;
+    _visitList(node.typeParameters, other.typeParameters);
+  }
+
+  @override
+  visitVariableDeclaration(VariableDeclaration node) {
+    VariableDeclaration other = this.other;
+    _visitDeclaration(node, other);
+    _visitNode(node.name, other.name);
+    _visitNode(node.initializer, other.initializer);
+  }
+
+  @override
+  visitVariableDeclarationList(VariableDeclarationList node) {
+    VariableDeclarationList other = this.other;
+    _visitNode(node.type, other.type);
+    _visitList(node.variables, other.variables);
+  }
+
+  @override
+  visitVariableDeclarationStatement(VariableDeclarationStatement node) {
+    VariableDeclarationStatement other = this.other;
+    _visitNode(node.variables, other.variables);
+  }
+
+  @override
+  visitWhileStatement(WhileStatement node) {
+    WhileStatement other = this.other;
+    _visitNode(node.condition, other.condition);
+    _visitNode(node.body, other.body);
+  }
+
+  @override
+  visitWithClause(WithClause node) {
+    WithClause other = this.other;
+    _visitList(node.mixinTypes, other.mixinTypes);
+  }
+
+  @override
+  visitYieldStatement(YieldStatement node) {
+    YieldStatement other = this.other;
+    _visitNode(node.expression, other.expression);
+  }
+
+  void _assertNode(AstNode a, AstNode b) {
+    _expectEquals(a.offset, b.offset);
+    _expectEquals(a.length, b.length);
+  }
+
+  void _expectEquals(actual, expected) {
+    if (actual != expected) {
+      String message = '';
+      message += 'Expected: $expected\n';
+      message += '  Actual: $actual\n';
+      _fail(message);
+    }
+  }
+
+  void _expectIsNull(obj) {
+    if (obj != null) {
+      String message = '';
+      message += 'Expected: null\n';
+      message += '  Actual: $obj\n';
+      _fail(message);
+    }
+  }
+
+  void _expectLength(List actualList, int expected) {
+    String message = '';
+    message += 'Expected length: $expected\n';
+    if (actualList == null) {
+      message += 'but null found.';
+      _fail(message);
+    }
+    int actual = actualList.length;
+    if (actual != expected) {
+      message += 'but $actual found\n';
+      message += 'in $actualList';
+      _fail(message);
+    }
+  }
+
+  void _fail(String message) {
+    throw new IncrementalResolutionMismatch(message);
+  }
+
+  void _verifyElement(Element a, Element b) {
+    if (a is Member && b is Member) {
+      a = (a as Member).baseElement;
+      b = (b as Member).baseElement;
+    }
+    String locationA = _getElementLocationWithoutUri(a);
+    String locationB = _getElementLocationWithoutUri(b);
+    if (locationA != locationB) {
+      int offset = other.offset;
+      _fail('[$offset]\nExpected: $b ($locationB)\n  Actual: $a ($locationA)');
+    }
+    if (a == null && b == null) {
+      return;
+    }
+    if (a.nameOffset != b.nameOffset) {
+      _fail('Expected: ${b.nameOffset}\n  Actual: ${a.nameOffset}');
+    }
+  }
+
+  void _verifyType(DartType a, DartType b) {
+    if (!validateTypes) {
+      return;
+    }
+    if (a != b) {
+      int offset = other.offset;
+      _fail('[$offset]\nExpected: $b\n  Actual: $a');
+    }
+  }
+
+  void _visitAnnotatedNode(AnnotatedNode node, AnnotatedNode other) {
+    _visitNode(node.documentationComment, other.documentationComment);
+    _visitList(node.metadata, other.metadata);
+  }
+
+  _visitDeclaration(Declaration node, Declaration other) {
+    _verifyElement(node.element, other.element);
+    _visitAnnotatedNode(node, other);
+  }
+
+  _visitDirective(Directive node, Directive other) {
+    _verifyElement(node.element, other.element);
+    _visitAnnotatedNode(node, other);
+  }
+
+  void _visitExpression(Expression a, Expression b) {
+//    print('[${a.offset}] |$a| vs. [${b.offset}] |$b|');
+    _verifyType(a.staticType, b.staticType);
+    _verifyType(a.propagatedType, b.propagatedType);
+    _verifyElement(a.staticParameterElement, b.staticParameterElement);
+    _verifyElement(a.propagatedParameterElement, b.propagatedParameterElement);
+    _assertNode(a, b);
+  }
+
+  void _visitList(NodeList nodeList, NodeList otherList) {
+    int length = nodeList.length;
+    _expectLength(otherList, length);
+    for (int i = 0; i < length; i++) {
+      _visitNode(nodeList[i], otherList[i]);
+    }
+  }
+
+  void _visitNode(AstNode node, AstNode other) {
+    if (node == null) {
+      _expectIsNull(other);
+    } else {
+      this.other = other;
+      _assertNode(node, other);
+      node.accept(this);
+    }
+  }
+
+  void _visitNormalFormalParameter(
+      NormalFormalParameter node, NormalFormalParameter other) {
+    _verifyElement(node.element, other.element);
+    _visitNode(node.documentationComment, other.documentationComment);
+    _visitList(node.metadata, other.metadata);
+    _visitNode(node.identifier, other.identifier);
+  }
+
+  /**
+   * Returns an URI scheme independent version of the [element] location.
+   */
+  static String _getElementLocationWithoutUri(Element element) {
+    if (element == null) {
+      return '<null>';
+    }
+    if (element is UriReferencedElementImpl) {
+      return '<ignored>';
+    }
+    ElementLocation location = element.location;
+    List<String> components = location.components;
+    String uriPrefix = '';
+    Element unit = element is CompilationUnitElement
+        ? element
+        : element.getAncestor((e) => e is CompilationUnitElement);
+    if (unit != null) {
+      String libComponent = components[0];
+      String unitComponent = components[1];
+      components = components.sublist(2);
+      uriPrefix = _getShortElementLocationUri(libComponent) +
+          ':' +
+          _getShortElementLocationUri(unitComponent);
+    } else {
+      String libComponent = components[0];
+      components = components.sublist(1);
+      uriPrefix = _getShortElementLocationUri(libComponent);
+    }
+    return uriPrefix + ':' + components.join(':');
+  }
+
+  /**
+   * Returns a "short" version of the given [uri].
+   *
+   * For example:
+   *     /User/me/project/lib/my_lib.dart -> my_lib.dart
+   *     package:project/my_lib.dart      -> my_lib.dart
+   */
+  static String _getShortElementLocationUri(String uri) {
+    int index = uri.lastIndexOf('/');
+    if (index == -1) {
+      return uri;
+    }
+    return uri.substring(index + 1);
+  }
+}
diff --git a/analyzer/lib/src/generated/incremental_resolver.dart b/analyzer/lib/src/generated/incremental_resolver.dart
new file mode 100644
index 0000000..531ac3b
--- /dev/null
+++ b/analyzer/lib/src/generated/incremental_resolver.dart
@@ -0,0 +1,1852 @@
+// 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 engine.incremental_resolver;
+
+import 'dart:collection';
+import 'dart:math' as math;
+
+import 'package:analyzer/src/generated/constant.dart';
+import 'package:analyzer/src/services/lint.dart';
+
+import 'ast.dart';
+import 'element.dart';
+import 'engine.dart';
+import 'error.dart';
+import 'error_verifier.dart';
+import 'incremental_logger.dart' show logger, LoggingTimer;
+import 'java_engine.dart';
+import 'parser.dart';
+import 'resolver.dart';
+import 'scanner.dart';
+import 'source.dart';
+import 'utilities_dart.dart';
+
+/**
+ * If `true`, an attempt to resolve API-changing modifications is made.
+ */
+bool _resolveApiChanges = false;
+
+/**
+ * This method is used to enable/disable API-changing modifications resolution.
+ */
+void set test_resolveApiChanges(bool value) {
+  _resolveApiChanges = value;
+}
+
+/**
+ * Instances of the class [DeclarationMatcher] determine whether the element
+ * model defined by a given AST structure matches an existing element model.
+ */
+class DeclarationMatcher extends RecursiveAstVisitor {
+  /**
+   * The libary containing the AST nodes being visited.
+   */
+  LibraryElement _enclosingLibrary;
+
+  /**
+   * The compilation unit containing the AST nodes being visited.
+   */
+  CompilationUnitElement _enclosingUnit;
+
+  /**
+   * The function type alias containing the AST nodes being visited, or `null` if we are not
+   * in the scope of a function type alias.
+   */
+  FunctionTypeAliasElement _enclosingAlias;
+
+  /**
+   * The class containing the AST nodes being visited, or `null` if we are not
+   * in the scope of a class.
+   */
+  ClassElementImpl _enclosingClass;
+
+  /**
+   * The parameter containing the AST nodes being visited, or `null` if we are not in the
+   * scope of a parameter.
+   */
+  ParameterElement _enclosingParameter;
+
+  FieldDeclaration _enclosingFieldNode = null;
+  bool _inTopLevelVariableDeclaration = false;
+
+  /**
+   * Is `true` if the current class declaration has a constructor.
+   */
+  bool _hasConstructor = false;
+
+  /**
+   * A set containing all of the elements in the element model that were defined by the old AST node
+   * corresponding to the AST node being visited.
+   */
+  HashSet<Element> _allElements = new HashSet<Element>();
+
+  /**
+   * A set containing all of the elements were defined in the old element model,
+   * but are not defined in the new element model.
+   */
+  HashSet<Element> _removedElements = new HashSet<Element>();
+
+  /**
+   * A set containing all of the elements are defined in the new element model,
+   * but were not defined in the old element model.
+   */
+  HashSet<Element> _addedElements = new HashSet<Element>();
+
+  /**
+   * Determines how elements model corresponding to the given [node] differs
+   * from the [element].
+   */
+  DeclarationMatchKind matches(AstNode node, Element element) {
+    logger.enter('match $element @ ${element.nameOffset}');
+    try {
+      _captureEnclosingElements(element);
+      _gatherElements(element);
+      node.accept(this);
+    } on _DeclarationMismatchException {
+      return DeclarationMatchKind.MISMATCH;
+    } finally {
+      logger.exit();
+    }
+    // no API changes
+    if (_removedElements.isEmpty && _addedElements.isEmpty) {
+      return DeclarationMatchKind.MATCH;
+    }
+    // simple API change
+    logger.log('_removedElements: $_removedElements');
+    logger.log('_addedElements: $_addedElements');
+    _removedElements.forEach(_removeElement);
+    if (_removedElements.length <= 1 && _addedElements.length == 1) {
+      return DeclarationMatchKind.MISMATCH_OK;
+    }
+    // something more complex
+    return DeclarationMatchKind.MISMATCH;
+  }
+
+  @override
+  visitBlockFunctionBody(BlockFunctionBody node) {
+    // ignore bodies
+  }
+
+  @override
+  visitClassDeclaration(ClassDeclaration node) {
+    String name = node.name.name;
+    ClassElement element = _findElement(_enclosingUnit.types, name);
+    _enclosingClass = element;
+    _processElement(element);
+    _assertSameAnnotations(node, element);
+    _assertSameTypeParameters(node.typeParameters, element.typeParameters);
+    // check for missing clauses
+    if (node.extendsClause == null) {
+      _assertTrue(element.supertype.name == 'Object');
+    }
+    if (node.implementsClause == null) {
+      _assertTrue(element.interfaces.isEmpty);
+    }
+    if (node.withClause == null) {
+      _assertTrue(element.mixins.isEmpty);
+    }
+    // process clauses and members
+    _hasConstructor = false;
+    super.visitClassDeclaration(node);
+    // process default constructor
+    if (!_hasConstructor) {
+      ConstructorElement constructor = element.unnamedConstructor;
+      _processElement(constructor);
+      if (!constructor.isSynthetic) {
+        _assertEquals(constructor.parameters.length, 0);
+      }
+    }
+  }
+
+  @override
+  visitClassTypeAlias(ClassTypeAlias node) {
+    String name = node.name.name;
+    ClassElement element = _findElement(_enclosingUnit.types, name);
+    _enclosingClass = element;
+    _processElement(element);
+    _assertSameTypeParameters(node.typeParameters, element.typeParameters);
+    _processElement(element.unnamedConstructor);
+    super.visitClassTypeAlias(node);
+  }
+
+  @override
+  visitCompilationUnit(CompilationUnit node) {
+    _processElement(_enclosingUnit);
+    super.visitCompilationUnit(node);
+  }
+
+  @override
+  visitConstructorDeclaration(ConstructorDeclaration node) {
+    _hasConstructor = true;
+    SimpleIdentifier constructorName = node.name;
+    ConstructorElementImpl element = constructorName == null
+        ? _enclosingClass.unnamedConstructor
+        : _enclosingClass.getNamedConstructor(constructorName.name);
+    _processElement(element);
+    _assertEquals(node.constKeyword != null, element.isConst);
+    _assertEquals(node.factoryKeyword != null, element.isFactory);
+    _assertCompatibleParameters(node.parameters, element.parameters);
+    // matches, update the existing element
+    ExecutableElement newElement = node.element;
+    node.element = element;
+    _setLocalElements(element, newElement);
+  }
+
+  @override
+  visitEnumConstantDeclaration(EnumConstantDeclaration node) {
+    String name = node.name.name;
+    FieldElement element = _findElement(_enclosingClass.fields, name);
+    _processElement(element);
+  }
+
+  @override
+  visitEnumDeclaration(EnumDeclaration node) {
+    String name = node.name.name;
+    ClassElement element = _findElement(_enclosingUnit.enums, name);
+    _enclosingClass = element;
+    _processElement(element);
+    _assertTrue(element.isEnum);
+    super.visitEnumDeclaration(node);
+  }
+
+  @override
+  visitExportDirective(ExportDirective node) {
+    String uri = _getStringValue(node.uri);
+    if (uri != null) {
+      ExportElement element =
+          _findUriReferencedElement(_enclosingLibrary.exports, uri);
+      _processElement(element);
+      _assertCombinators(node.combinators, element.combinators);
+    }
+  }
+
+  @override
+  visitExpressionFunctionBody(ExpressionFunctionBody node) {
+    // ignore bodies
+  }
+
+  @override
+  visitExtendsClause(ExtendsClause node) {
+    _assertSameType(node.superclass, _enclosingClass.supertype);
+  }
+
+  @override
+  visitFieldDeclaration(FieldDeclaration node) {
+    _enclosingFieldNode = node;
+    try {
+      super.visitFieldDeclaration(node);
+    } finally {
+      _enclosingFieldNode = null;
+    }
+  }
+
+  @override
+  visitFunctionDeclaration(FunctionDeclaration node) {
+    // prepare element name
+    String name = node.name.name;
+    if (node.isSetter) {
+      name += '=';
+    }
+    // prepare element
+    Token property = node.propertyKeyword;
+    ExecutableElementImpl element;
+    if (property == null) {
+      element = _findElement(_enclosingUnit.functions, name);
+    } else {
+      element = _findElement(_enclosingUnit.accessors, name);
+    }
+    // process element
+    _processElement(element);
+    _assertSameAnnotations(node, element);
+    _assertFalse(element.isSynthetic);
+    _assertSameType(node.returnType, element.returnType);
+    _assertCompatibleParameters(
+        node.functionExpression.parameters, element.parameters);
+    _assertBody(node.functionExpression.body, element);
+    // matches, update the existing element
+    ExecutableElement newElement = node.element;
+    node.name.staticElement = element;
+    node.functionExpression.element = element;
+    _setLocalElements(element, newElement);
+  }
+
+  @override
+  visitFunctionTypeAlias(FunctionTypeAlias node) {
+    String name = node.name.name;
+    FunctionTypeAliasElement element =
+        _findElement(_enclosingUnit.functionTypeAliases, name);
+    _processElement(element);
+    _assertSameTypeParameters(node.typeParameters, element.typeParameters);
+    _assertSameType(node.returnType, element.returnType);
+    _assertCompatibleParameters(node.parameters, element.parameters);
+  }
+
+  @override
+  visitImplementsClause(ImplementsClause node) {
+    List<TypeName> nodes = node.interfaces;
+    List<InterfaceType> types = _enclosingClass.interfaces;
+    _assertSameTypes(nodes, types);
+  }
+
+  @override
+  visitImportDirective(ImportDirective node) {
+    String uri = _getStringValue(node.uri);
+    if (uri != null) {
+      ImportElement element =
+          _findUriReferencedElement(_enclosingLibrary.imports, uri);
+      _processElement(element);
+      // match the prefix
+      SimpleIdentifier prefixNode = node.prefix;
+      PrefixElement prefixElement = element.prefix;
+      if (prefixNode == null) {
+        _assertNull(prefixElement);
+      } else {
+        _assertNotNull(prefixElement);
+        _assertEquals(prefixNode.name, prefixElement.name);
+      }
+      // match combinators
+      _assertCombinators(node.combinators, element.combinators);
+    }
+  }
+
+  @override
+  visitMethodDeclaration(MethodDeclaration node) {
+    // prepare element name
+    String name = node.name.name;
+    if (name == TokenType.MINUS.lexeme &&
+        node.parameters.parameters.length == 0) {
+      name = "unary-";
+    }
+    if (node.isSetter) {
+      name += '=';
+    }
+    // prepare element
+    Token property = node.propertyKeyword;
+    ExecutableElementImpl element;
+    if (property == null) {
+      element = _findElement(_enclosingClass.methods, name);
+    } else {
+      element = _findElement(_enclosingClass.accessors, name);
+    }
+    // process element
+    ExecutableElement newElement = node.element;
+    try {
+      _assertNotNull(element);
+      _assertSameAnnotations(node, element);
+      _assertEquals(node.isStatic, element.isStatic);
+      _assertSameType(node.returnType, element.returnType);
+      _assertCompatibleParameters(node.parameters, element.parameters);
+      _assertBody(node.body, element);
+      _removedElements.remove(element);
+      // matches, update the existing element
+      node.name.staticElement = element;
+      _setLocalElements(element, newElement);
+    } on _DeclarationMismatchException {
+      _addedElements.add(newElement);
+      _removeElement(element);
+      // add new element
+      if (newElement is MethodElement) {
+        List<MethodElement> methods = _enclosingClass.methods;
+        methods.add(newElement);
+        _enclosingClass.methods = methods;
+      } else {
+        List<PropertyAccessorElement> accessors = _enclosingClass.accessors;
+        accessors.add(newElement);
+        _enclosingClass.accessors = accessors;
+      }
+    }
+  }
+
+  @override
+  visitPartDirective(PartDirective node) {
+    String uri = _getStringValue(node.uri);
+    if (uri != null) {
+      CompilationUnitElement element =
+          _findUriReferencedElement(_enclosingLibrary.parts, uri);
+      _processElement(element);
+    }
+    super.visitPartDirective(node);
+  }
+
+  @override
+  visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+    _inTopLevelVariableDeclaration = true;
+    try {
+      super.visitTopLevelVariableDeclaration(node);
+    } finally {
+      _inTopLevelVariableDeclaration = false;
+    }
+  }
+
+  @override
+  visitVariableDeclaration(VariableDeclaration node) {
+    // prepare variable
+    String name = node.name.name;
+    PropertyInducingElement element;
+    if (_inTopLevelVariableDeclaration) {
+      element = _findElement(_enclosingUnit.topLevelVariables, name);
+    } else {
+      element = _findElement(_enclosingClass.fields, name);
+    }
+    // verify
+    PropertyInducingElement newElement = node.name.staticElement;
+    _processElement(element);
+    _assertSameAnnotations(node, element);
+    _assertEquals(node.isConst, element.isConst);
+    _assertEquals(node.isFinal, element.isFinal);
+    if (_enclosingFieldNode != null) {
+      _assertEquals(_enclosingFieldNode.isStatic, element.isStatic);
+    }
+    _assertSameType(
+        (node.parent as VariableDeclarationList).type, element.type);
+    // matches, restore the existing element
+    node.name.staticElement = element;
+    if (element is VariableElementImpl) {
+      (element as VariableElementImpl).initializer = newElement.initializer;
+    }
+  }
+
+  @override
+  visitWithClause(WithClause node) {
+    List<TypeName> nodes = node.mixinTypes;
+    List<InterfaceType> types = _enclosingClass.mixins;
+    _assertSameTypes(nodes, types);
+  }
+
+  /**
+   * Assert that the given [body] is compatible with the given [element].
+   * It should not be empty if the [element] is not an abstract class member.
+   * If it is present, it should have the same async / generator modifiers.
+   */
+  void _assertBody(FunctionBody body, ExecutableElementImpl element) {
+    if (body is EmptyFunctionBody) {
+      _assertTrue(element.isAbstract);
+    } else {
+      _assertFalse(element.isAbstract);
+      _assertEquals(body.isSynchronous, element.isSynchronous);
+      _assertEquals(body.isGenerator, element.isGenerator);
+    }
+  }
+
+  void _assertCombinators(List<Combinator> nodeCombinators,
+      List<NamespaceCombinator> elementCombinators) {
+    // prepare shown/hidden names in the element
+    Set<String> showNames = new Set<String>();
+    Set<String> hideNames = new Set<String>();
+    for (NamespaceCombinator combinator in elementCombinators) {
+      if (combinator is ShowElementCombinator) {
+        showNames.addAll(combinator.shownNames);
+      } else if (combinator is HideElementCombinator) {
+        hideNames.addAll(combinator.hiddenNames);
+      }
+    }
+    // match combinators with the node
+    for (Combinator combinator in nodeCombinators) {
+      if (combinator is ShowCombinator) {
+        for (SimpleIdentifier nameNode in combinator.shownNames) {
+          String name = nameNode.name;
+          _assertTrue(showNames.remove(name));
+        }
+      } else if (combinator is HideCombinator) {
+        for (SimpleIdentifier nameNode in combinator.hiddenNames) {
+          String name = nameNode.name;
+          _assertTrue(hideNames.remove(name));
+        }
+      }
+    }
+    _assertTrue(showNames.isEmpty);
+    _assertTrue(hideNames.isEmpty);
+  }
+
+  void _assertCompatibleParameter(
+      FormalParameter node, ParameterElement element) {
+    _assertEquals(node.kind, element.parameterKind);
+    if (node.kind == ParameterKind.NAMED) {
+      _assertEquals(node.identifier.name, element.name);
+    }
+    // check parameter type specific properties
+    if (node is DefaultFormalParameter) {
+      Expression nodeDefault = node.defaultValue;
+      if (nodeDefault == null) {
+        _assertNull(element.defaultValueCode);
+      } else {
+        _assertEquals(nodeDefault.toSource(), element.defaultValueCode);
+      }
+      _assertCompatibleParameter(node.parameter, element);
+    } else if (node is FieldFormalParameter) {
+      _assertTrue(element.isInitializingFormal);
+      _assertCompatibleParameters(node.parameters, element.parameters);
+    } else if (node is FunctionTypedFormalParameter) {
+      _assertFalse(element.isInitializingFormal);
+      _assertTrue(element.type is FunctionType);
+      FunctionType elementType = element.type;
+      _assertCompatibleParameters(node.parameters, element.parameters);
+      _assertSameType(node.returnType, elementType.returnType);
+    } else if (node is SimpleFormalParameter) {
+      _assertFalse(element.isInitializingFormal);
+      _assertSameType(node.type, element.type);
+    }
+  }
+
+  void _assertCompatibleParameters(
+      FormalParameterList nodes, List<ParameterElement> elements) {
+    if (nodes == null) {
+      return _assertEquals(elements.length, 0);
+    }
+    List<FormalParameter> parameters = nodes.parameters;
+    int length = parameters.length;
+    _assertEquals(length, elements.length);
+    for (int i = 0; i < length; i++) {
+      _assertCompatibleParameter(parameters[i], elements[i]);
+    }
+  }
+
+  void _assertEquals(Object a, Object b) {
+    if (a != b) {
+      throw new _DeclarationMismatchException();
+    }
+  }
+
+  void _assertFalse(bool condition) {
+    if (condition) {
+      throw new _DeclarationMismatchException();
+    }
+  }
+
+  void _assertNotNull(Object object) {
+    if (object == null) {
+      throw new _DeclarationMismatchException();
+    }
+  }
+
+  void _assertNull(Object object) {
+    if (object != null) {
+      throw new _DeclarationMismatchException();
+    }
+  }
+
+  void _assertSameAnnotation(Annotation node, ElementAnnotation annotation) {
+    Element element = annotation.element;
+    if (element is ConstructorElement) {
+      _assertTrue(node.name is SimpleIdentifier);
+      _assertNull(node.constructorName);
+      TypeName nodeType = new TypeName(node.name, null);
+      _assertSameType(nodeType, element.returnType);
+      // TODO(scheglov) validate arguments
+    }
+    if (element is PropertyAccessorElement) {
+      _assertTrue(node.name is SimpleIdentifier);
+      String nodeName = node.name.name;
+      String elementName = element.displayName;
+      _assertEquals(nodeName, elementName);
+    }
+  }
+
+  void _assertSameAnnotations(AnnotatedNode node, Element element) {
+    List<Annotation> nodeAnnotaitons = node.metadata;
+    List<ElementAnnotation> elementAnnotations = element.metadata;
+    int length = nodeAnnotaitons.length;
+    _assertEquals(elementAnnotations.length, length);
+    for (int i = 0; i < length; i++) {
+      _assertSameAnnotation(nodeAnnotaitons[i], elementAnnotations[i]);
+    }
+  }
+
+  void _assertSameType(TypeName node, DartType type) {
+    // no return type == dynamic
+    if (node == null) {
+      return _assertTrue(type == null || type.isDynamic);
+    }
+    if (type == null) {
+      return _assertTrue(false);
+    }
+    // prepare name
+    Identifier nameIdentifier = node.name;
+    if (nameIdentifier is PrefixedIdentifier) {
+      nameIdentifier = (nameIdentifier as PrefixedIdentifier).identifier;
+    }
+    String nodeName = nameIdentifier.name;
+    // check specific type kinds
+    if (type is ParameterizedType) {
+      _assertEquals(nodeName, type.name);
+      // check arguments
+      TypeArgumentList nodeArgumentList = node.typeArguments;
+      List<DartType> typeArguments = type.typeArguments;
+      if (nodeArgumentList == null) {
+        // Node doesn't have type arguments, so all type arguments of the
+        // element must be "dynamic".
+        for (DartType typeArgument in typeArguments) {
+          _assertTrue(typeArgument.isDynamic);
+        }
+      } else {
+        List<TypeName> nodeArguments = nodeArgumentList.arguments;
+        _assertSameTypes(nodeArguments, typeArguments);
+      }
+    } else if (type is TypeParameterType) {
+      _assertEquals(nodeName, type.name);
+      // TODO(scheglov) it should be possible to rename type parameters
+    } else if (type.isVoid) {
+      _assertEquals(nodeName, 'void');
+    } else if (type.isDynamic) {
+      _assertEquals(nodeName, 'dynamic');
+    } else {
+      // TODO(scheglov) support other types
+      logger.log('node: $node type: $type  type.type: ${type.runtimeType}');
+      _assertTrue(false);
+    }
+  }
+
+  void _assertSameTypeParameter(
+      TypeParameter node, TypeParameterElement element) {
+    _assertSameType(node.bound, element.bound);
+  }
+
+  void _assertSameTypeParameters(
+      TypeParameterList nodesList, List<TypeParameterElement> elements) {
+    if (nodesList == null) {
+      return _assertEquals(elements.length, 0);
+    }
+    List<TypeParameter> nodes = nodesList.typeParameters;
+    int length = nodes.length;
+    _assertEquals(length, elements.length);
+    for (int i = 0; i < length; i++) {
+      _assertSameTypeParameter(nodes[i], elements[i]);
+    }
+  }
+
+  void _assertSameTypes(List<TypeName> nodes, List<DartType> types) {
+    int length = nodes.length;
+    _assertEquals(length, types.length);
+    for (int i = 0; i < length; i++) {
+      _assertSameType(nodes[i], types[i]);
+    }
+  }
+
+  void _assertTrue(bool condition) {
+    if (!condition) {
+      throw new _DeclarationMismatchException();
+    }
+  }
+
+  /**
+   * Given that the comparison is to begin with the given [element], capture
+   * the enclosing elements that might be used while performing the comparison.
+   */
+  void _captureEnclosingElements(Element element) {
+    Element parent =
+        element is CompilationUnitElement ? element : element.enclosingElement;
+    while (parent != null) {
+      if (parent is CompilationUnitElement) {
+        _enclosingUnit = parent;
+        _enclosingLibrary = element.library;
+      } else if (parent is ClassElement) {
+        if (_enclosingClass == null) {
+          _enclosingClass = parent;
+        }
+      } else if (parent is FunctionTypeAliasElement) {
+        if (_enclosingAlias == null) {
+          _enclosingAlias = parent;
+        }
+      } else if (parent is ParameterElement) {
+        if (_enclosingParameter == null) {
+          _enclosingParameter = parent;
+        }
+      }
+      parent = parent.enclosingElement;
+    }
+  }
+
+  void _gatherElements(Element element) {
+    _ElementsGatherer gatherer = new _ElementsGatherer(this);
+    element.accept(gatherer);
+    // TODO(scheglov) what if a change in a directive?
+    if (identical(element, _enclosingLibrary.definingCompilationUnit)) {
+      gatherer.addElements(_enclosingLibrary.imports);
+      gatherer.addElements(_enclosingLibrary.exports);
+      gatherer.addElements(_enclosingLibrary.parts);
+    }
+  }
+
+  void _processElement(Element element) {
+    _assertNotNull(element);
+    if (!_allElements.contains(element)) {
+      throw new _DeclarationMismatchException();
+    }
+    _removedElements.remove(element);
+  }
+
+  void _removeElement(Element element) {
+    if (element != null) {
+      Element enclosingElement = element.enclosingElement;
+      if (element is MethodElement) {
+        ClassElement classElement = enclosingElement;
+        _removeIdenticalElement(classElement.methods, element);
+      } else if (element is PropertyAccessorElement) {
+        if (enclosingElement is ClassElement) {
+          _removeIdenticalElement(enclosingElement.accessors, element);
+        }
+        if (enclosingElement is CompilationUnitElement) {
+          _removeIdenticalElement(enclosingElement.accessors, element);
+        }
+      }
+    }
+  }
+
+  /**
+   * Return the [Element] in [elements] with the given [name].
+   */
+  static Element _findElement(List<Element> elements, String name) {
+    for (Element element in elements) {
+      if (element.name == name) {
+        return element;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the [UriReferencedElement] from [elements] with the given [uri], or
+   * `null` if there is no such element.
+   */
+  static UriReferencedElement _findUriReferencedElement(
+      List<UriReferencedElement> elements, String uri) {
+    for (UriReferencedElement element in elements) {
+      if (element.uri == uri) {
+        return element;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the value of [literal], or `null` if the string is not a constant
+   * string without any string interpolation.
+   */
+  static String _getStringValue(StringLiteral literal) {
+    if (literal is StringInterpolation) {
+      return null;
+    }
+    return literal.stringValue;
+  }
+
+  /**
+   * Removes the first element identical to the given [element] from [elements].
+   */
+  static void _removeIdenticalElement(List elements, Object element) {
+    int length = elements.length;
+    for (int i = 0; i < length; i++) {
+      if (identical(elements[i], element)) {
+        elements.removeAt(i);
+        return;
+      }
+    }
+  }
+
+  static void _setLocalElements(
+      ExecutableElementImpl to, ExecutableElement from) {
+    to.functions = from.functions;
+    to.labels = from.labels;
+    to.localVariables = from.localVariables;
+    to.parameters = from.parameters;
+  }
+}
+
+/**
+ * Describes how declarations match an existing elements model.
+ */
+class DeclarationMatchKind {
+  /**
+   * Complete match, no API changes.
+   */
+  static const MATCH = const DeclarationMatchKind('MATCH');
+
+  /**
+   * Has API changes that we might be able to resolve incrementally.
+   */
+  static const MISMATCH_OK = const DeclarationMatchKind('MISMATCH_OK');
+
+  /**
+   * Has API changes that we cannot resolve incrementally.
+   */
+  static const MISMATCH = const DeclarationMatchKind('MISMATCH');
+
+  final String name;
+
+  const DeclarationMatchKind(this.name);
+
+  @override
+  String toString() => name;
+}
+
+/**
+ * Instances of the class [IncrementalResolver] resolve the smallest portion of
+ * an AST structure that we currently know how to resolve.
+ */
+class IncrementalResolver {
+  /**
+   * The element of the compilation unit being resolved.
+   */
+  final CompilationUnitElementImpl _definingUnit;
+
+  /**
+   * The context the compilation unit being resolved in.
+   */
+  AnalysisContextImpl _context;
+
+  /**
+   * The object used to access the types from the core library.
+   */
+  TypeProvider _typeProvider;
+
+  /**
+   * The element for the library containing the compilation unit being resolved.
+   */
+  LibraryElementImpl _definingLibrary;
+
+  /**
+   * The [DartEntry] corresponding to the source being resolved.
+   */
+  DartEntry entry;
+
+  /**
+   * The source representing the compilation unit being visited.
+   */
+  Source _source;
+
+  /**
+   * The source representing the library of the compilation unit being visited.
+   */
+  Source _librarySource;
+
+  /**
+   * The offset of the changed contents.
+   */
+  final int _updateOffset;
+
+  /**
+   * The end of the changed contents in the old unit.
+   */
+  final int _updateEndOld;
+
+  /**
+   * The end of the changed contents in the new unit.
+   */
+  final int _updateEndNew;
+
+  int _updateDelta;
+
+  RecordingErrorListener errorListener = new RecordingErrorListener();
+  ResolutionContext _resolutionContext;
+
+  List<AnalysisError> _resolveErrors = AnalysisError.NO_ERRORS;
+  List<AnalysisError> _verifyErrors = AnalysisError.NO_ERRORS;
+  List<AnalysisError> _lints = AnalysisError.NO_ERRORS;
+
+  /**
+   * Initialize a newly created incremental resolver to resolve a node in the
+   * given source in the given library.
+   */
+  IncrementalResolver(this._definingUnit, this._updateOffset,
+      this._updateEndOld, this._updateEndNew) {
+    _updateDelta = _updateEndNew - _updateEndOld;
+    _definingLibrary = _definingUnit.library;
+    _librarySource = _definingLibrary.source;
+    _source = _definingUnit.source;
+    _context = _definingUnit.context;
+    _typeProvider = _context.typeProvider;
+    entry = _context.getReadableSourceEntryOrNull(_source);
+  }
+
+  /**
+   * Resolve [node], reporting any errors or warnings to the given listener.
+   *
+   * [node] - the root of the AST structure to be resolved.
+   *
+   * Returns `true` if resolution was successful.
+   */
+  bool resolve(AstNode node) {
+    logger.enter('resolve: $_definingUnit');
+    try {
+      AstNode rootNode = _findResolutionRoot(node);
+      _prepareResolutionContext(rootNode);
+      // update elements
+      _updateElementNameOffsets();
+      _buildElements(rootNode);
+      if (!_canBeIncrementallyResolved(rootNode)) {
+        return false;
+      }
+      // resolve
+      _resolveReferences(rootNode);
+      _computeConstants(rootNode);
+      _resolveErrors = errorListener.getErrorsForSource(_source);
+      // verify
+      _verify(rootNode);
+      _context.invalidateLibraryHints(_librarySource);
+      _generateLints(rootNode);
+      // update entry errors
+      _updateEntry();
+      // notify unit
+      _definingUnit.afterIncrementalResolution();
+      // OK
+      return true;
+    } finally {
+      logger.exit();
+    }
+  }
+
+  void _buildElements(AstNode node) {
+    LoggingTimer timer = logger.startTimer();
+    try {
+      ElementHolder holder = new ElementHolder();
+      ElementBuilder builder = new ElementBuilder(holder);
+      if (_resolutionContext.enclosingClassDeclaration != null) {
+        builder.visitClassDeclarationIncrementally(
+            _resolutionContext.enclosingClassDeclaration);
+      }
+      node.accept(builder);
+    } finally {
+      timer.stop('build elements');
+    }
+  }
+
+  /**
+   * Return `true` if [node] does not have element model changes, or these
+   * changes can be incrementally propagated.
+   */
+  bool _canBeIncrementallyResolved(AstNode node) {
+    // If we are replacing the whole declaration, this means that its signature
+    // is changed. It might be an API change, or not.
+    //
+    // If, for example, a required parameter is changed, it is not an API
+    // change, but we want to find the existing corresponding Element in the
+    // enclosing one, set it for the node and update as needed.
+    //
+    // If, for example, the name of a method is changed, it is an API change,
+    // we need to know the old Element and the new Element. Again, we need to
+    // check the whole enclosing Element.
+    if (node is Declaration) {
+      node = node.parent;
+    }
+    Element element = _getElement(node);
+    DeclarationMatcher matcher = new DeclarationMatcher();
+    DeclarationMatchKind matchKind = matcher.matches(node, element);
+    if (matchKind == DeclarationMatchKind.MATCH) {
+      return true;
+    }
+    // mismatch that cannot be incrementally fixed
+    return false;
+  }
+
+  /**
+   * Return `true` if the given node can be resolved independently of any other
+   * nodes.
+   *
+   * *Note*: This method needs to be kept in sync with
+   * [ScopeBuilder.ContextBuilder].
+   *
+   * [node] - the node being tested.
+   */
+  bool _canBeResolved(AstNode node) => node is ClassDeclaration ||
+      node is ClassTypeAlias ||
+      node is CompilationUnit ||
+      node is ConstructorDeclaration ||
+      node is FunctionDeclaration ||
+      node is FunctionTypeAlias ||
+      node is MethodDeclaration ||
+      node is TopLevelVariableDeclaration;
+
+  /**
+   * Compute a value for all of the constants in the given [node].
+   */
+  void _computeConstants(AstNode node) {
+    // compute values
+    {
+      CompilationUnit unit = node.getAncestor((n) => n is CompilationUnit);
+      ConstantValueComputer computer =
+          new ConstantValueComputer(_typeProvider, _context.declaredVariables);
+      computer.add(unit);
+      computer.computeValues();
+    }
+    // validate
+    {
+      ErrorReporter errorReporter = new ErrorReporter(errorListener, _source);
+      ConstantVerifier constantVerifier =
+          new ConstantVerifier(errorReporter, _definingLibrary, _typeProvider);
+      node.accept(constantVerifier);
+    }
+  }
+
+  /**
+   * Starting at [node], find the smallest AST node that can be resolved
+   * independently of any other nodes. Return the node that was found.
+   *
+   * [node] - the node at which the search is to begin
+   *
+   * Throws [AnalysisException] if there is no such node.
+   */
+  AstNode _findResolutionRoot(AstNode node) {
+    while (node != null) {
+      if (_canBeResolved(node)) {
+        return node;
+      }
+      node = node.parent;
+    }
+    throw new AnalysisException("Cannot resolve node: no resolvable node");
+  }
+
+  void _generateLints(AstNode node) {
+    LoggingTimer timer = logger.startTimer();
+    try {
+      RecordingErrorListener errorListener = new RecordingErrorListener();
+      CompilationUnit unit = node.getAncestor((n) => n is CompilationUnit);
+      LintGenerator lintGenerator =
+          new LintGenerator(<CompilationUnit>[unit], errorListener);
+      lintGenerator.generate();
+      _lints = errorListener.getErrorsForSource(_source);
+    } finally {
+      timer.stop('generate lints');
+    }
+  }
+
+  /**
+   * Return the element defined by [node], or `null` if the node does not
+   * define an element.
+   */
+  Element _getElement(AstNode node) {
+    if (node is Declaration) {
+      return node.element;
+    } else if (node is CompilationUnit) {
+      return node.element;
+    }
+    return null;
+  }
+
+  void _prepareResolutionContext(AstNode node) {
+    if (_resolutionContext == null) {
+      _resolutionContext =
+          ResolutionContextBuilder.contextFor(node, errorListener);
+    }
+  }
+
+  _resolveReferences(AstNode node) {
+    LoggingTimer timer = logger.startTimer();
+    try {
+      _prepareResolutionContext(node);
+      Scope scope = _resolutionContext.scope;
+      // resolve types
+      {
+        TypeResolverVisitor visitor = new TypeResolverVisitor.con3(
+            _definingLibrary, _source, _typeProvider, scope, errorListener);
+        node.accept(visitor);
+      }
+      // resolve variables
+      {
+        VariableResolverVisitor visitor = new VariableResolverVisitor.con2(
+            _definingLibrary, _source, _typeProvider, scope, errorListener);
+        node.accept(visitor);
+      }
+      // resolve references
+      {
+        ResolverVisitor visitor = new ResolverVisitor.con3(
+            _definingLibrary, _source, _typeProvider, scope, errorListener);
+        if (_resolutionContext.enclosingClassDeclaration != null) {
+          visitor.visitClassDeclarationIncrementally(
+              _resolutionContext.enclosingClassDeclaration);
+        }
+        if (node is Comment) {
+          visitor.resolveOnlyCommentInFunctionBody = true;
+          node = node.parent;
+        }
+        visitor.initForIncrementalResolution();
+        node.accept(visitor);
+      }
+    } finally {
+      timer.stop('resolve references');
+    }
+  }
+
+  void _shiftEntryErrors() {
+    _shiftErrors(DartEntry.RESOLUTION_ERRORS);
+    _shiftErrors(DartEntry.VERIFICATION_ERRORS);
+    _shiftErrors(DartEntry.HINTS);
+    _shiftErrors(DartEntry.LINTS);
+  }
+
+  void _shiftErrors(DataDescriptor<List<AnalysisError>> descriptor) {
+    List<AnalysisError> errors =
+        entry.getValueInLibrary(descriptor, _librarySource);
+    for (AnalysisError error in errors) {
+      int errorOffset = error.offset;
+      if (errorOffset > _updateOffset) {
+        error.offset += _updateDelta;
+      }
+    }
+  }
+
+  void _updateElementNameOffsets() {
+    LoggingTimer timer = logger.startTimer();
+    try {
+      _definingUnit
+          .accept(new _ElementNameOffsetUpdater(_updateOffset, _updateDelta));
+    } finally {
+      timer.stop('update element offsets');
+    }
+  }
+
+  void _updateEntry() {
+    {
+      List<AnalysisError> oldErrors =
+          entry.getValueInLibrary(DartEntry.RESOLUTION_ERRORS, _librarySource);
+      List<AnalysisError> errors = _updateErrors(oldErrors, _resolveErrors);
+      entry.setValueInLibrary(
+          DartEntry.RESOLUTION_ERRORS, _librarySource, errors);
+    }
+    {
+      List<AnalysisError> oldErrors = entry.getValueInLibrary(
+          DartEntry.VERIFICATION_ERRORS, _librarySource);
+      List<AnalysisError> errors = _updateErrors(oldErrors, _verifyErrors);
+      entry.setValueInLibrary(
+          DartEntry.VERIFICATION_ERRORS, _librarySource, errors);
+    }
+    entry.setValueInLibrary(DartEntry.LINTS, _librarySource, _lints);
+  }
+
+  List<AnalysisError> _updateErrors(
+      List<AnalysisError> oldErrors, List<AnalysisError> newErrors) {
+    List<AnalysisError> errors = new List<AnalysisError>();
+    // add updated old errors
+    for (AnalysisError error in oldErrors) {
+      int errorOffset = error.offset;
+      if (errorOffset < _updateOffset) {
+        errors.add(error);
+      } else if (errorOffset > _updateEndOld) {
+        error.offset += _updateDelta;
+        errors.add(error);
+      }
+    }
+    // add new errors
+    for (AnalysisError error in newErrors) {
+      int errorOffset = error.offset;
+      if (errorOffset > _updateOffset && errorOffset < _updateEndNew) {
+        errors.add(error);
+      }
+    }
+    // done
+    return errors;
+  }
+
+  void _verify(AstNode node) {
+    LoggingTimer timer = logger.startTimer();
+    try {
+      RecordingErrorListener errorListener = new RecordingErrorListener();
+      ErrorReporter errorReporter = new ErrorReporter(errorListener, _source);
+      ErrorVerifier errorVerifier = new ErrorVerifier(errorReporter,
+          _definingLibrary, _typeProvider,
+          new InheritanceManager(_definingLibrary));
+      if (_resolutionContext.enclosingClassDeclaration != null) {
+        errorVerifier.visitClassDeclarationIncrementally(
+            _resolutionContext.enclosingClassDeclaration);
+      }
+      node.accept(errorVerifier);
+      _verifyErrors = errorListener.getErrorsForSource(_source);
+    } finally {
+      timer.stop('verify');
+    }
+  }
+}
+
+class PoorMansIncrementalResolver {
+  final TypeProvider _typeProvider;
+  final Source _unitSource;
+  final DartEntry _entry;
+  final CompilationUnit _oldUnit;
+  final AnalysisOptions _options;
+  CompilationUnitElement _unitElement;
+
+  int _updateOffset;
+  int _updateDelta;
+  int _updateEndOld;
+  int _updateEndNew;
+
+  List<AnalysisError> _newScanErrors = <AnalysisError>[];
+  List<AnalysisError> _newParseErrors = <AnalysisError>[];
+
+  PoorMansIncrementalResolver(this._typeProvider, this._unitSource, this._entry,
+      this._oldUnit, bool resolveApiChanges, this._options) {
+    _resolveApiChanges = resolveApiChanges;
+  }
+
+  /**
+   * Attempts to update [_oldUnit] to the state corresponding to [newCode].
+   * Returns `true` if success, or `false` otherwise.
+   * The [_oldUnit] might be damaged.
+   */
+  bool resolve(String newCode) {
+    logger.enter('diff/resolve $_unitSource');
+    try {
+      // prepare old unit
+      if (!_areCurlyBracketsBalanced(_oldUnit.beginToken)) {
+        logger.log('Unbalanced number of curly brackets in the old unit.');
+        return false;
+      }
+      _unitElement = _oldUnit.element;
+      // prepare new unit
+      CompilationUnit newUnit = _parseUnit(newCode);
+      if (!_areCurlyBracketsBalanced(newUnit.beginToken)) {
+        logger.log('Unbalanced number of curly brackets in the new unit.');
+        return false;
+      }
+      // find difference
+      _TokenPair firstPair =
+          _findFirstDifferentToken(_oldUnit.beginToken, newUnit.beginToken);
+      _TokenPair lastPair =
+          _findLastDifferentToken(_oldUnit.endToken, newUnit.endToken);
+      if (firstPair != null && lastPair != null) {
+        int firstOffsetOld = firstPair.oldToken.offset;
+        int firstOffsetNew = firstPair.newToken.offset;
+        int lastOffsetOld = lastPair.oldToken.end;
+        int lastOffsetNew = lastPair.newToken.end;
+        int beginOffsetOld = math.min(firstOffsetOld, lastOffsetOld);
+        int endOffsetOld = math.max(firstOffsetOld, lastOffsetOld);
+        int beginOffsetNew = math.min(firstOffsetNew, lastOffsetNew);
+        int endOffsetNew = math.max(firstOffsetNew, lastOffsetNew);
+        // check for a whitespace only change
+        if (identical(lastPair.oldToken, firstPair.oldToken) &&
+            identical(lastPair.newToken, firstPair.newToken)) {
+          _updateOffset = beginOffsetOld - 1;
+          _updateEndOld = endOffsetOld;
+          _updateEndNew = endOffsetNew;
+          _updateDelta = newUnit.length - _oldUnit.length;
+          // A Dart documentation comment change.
+          if (firstPair.kind == _TokenDifferenceKind.COMMENT_DOC) {
+            bool success = _resolveCommentDoc(newUnit, firstPair);
+            logger.log('Documentation comment resolved: $success');
+            return success;
+          }
+          // A pure whitespace change.
+          if (firstPair.kind == _TokenDifferenceKind.OFFSET) {
+            logger.log('Whitespace change.');
+            _shiftTokens(firstPair.oldToken);
+            {
+              IncrementalResolver incrementalResolver = new IncrementalResolver(
+                  _unitElement, _updateOffset, _updateEndOld, _updateEndNew);
+              incrementalResolver._updateElementNameOffsets();
+              incrementalResolver._shiftEntryErrors();
+            }
+            _updateEntry();
+            logger.log('Success.');
+            return true;
+          }
+          // fall-through, end-of-line comment
+        }
+        // Find nodes covering the "old" and "new" token ranges.
+        AstNode oldNode =
+            _findNodeCovering(_oldUnit, beginOffsetOld, endOffsetOld - 1);
+        AstNode newNode =
+            _findNodeCovering(newUnit, beginOffsetNew, endOffsetNew - 1);
+        logger.log(() => 'oldNode: $oldNode');
+        logger.log(() => 'newNode: $newNode');
+        // Try to find the smallest common node, a FunctionBody currently.
+        {
+          List<AstNode> oldParents = _getParents(oldNode);
+          List<AstNode> newParents = _getParents(newNode);
+          int length = math.min(oldParents.length, newParents.length);
+          bool found = false;
+          for (int i = 0; i < length; i++) {
+            AstNode oldParent = oldParents[i];
+            AstNode newParent = newParents[i];
+            if (oldParent is ConstructorInitializer ||
+                newParent is ConstructorInitializer) {
+              logger.log('Failure: changes in constant constructor initializers'
+                  ' may cause external changes in constant objects.');
+              return false;
+            }
+            if (oldParent is FunctionDeclaration &&
+                    newParent is FunctionDeclaration ||
+                oldParent is MethodDeclaration &&
+                    newParent is MethodDeclaration ||
+                oldParent is ConstructorDeclaration &&
+                    newParent is ConstructorDeclaration) {
+              oldNode = oldParent;
+              newNode = newParent;
+              found = true;
+            }
+            if (oldParent is FunctionBody && newParent is FunctionBody) {
+              oldNode = oldParent;
+              newNode = newParent;
+              found = true;
+              break;
+            }
+          }
+          if (!found) {
+            logger.log('Failure: no enclosing function body or executable.');
+            return false;
+          }
+          // fail if a comment change outside the bodies
+          if (firstPair.kind == _TokenDifferenceKind.COMMENT) {
+            if (beginOffsetOld <= oldNode.offset ||
+                beginOffsetNew <= newNode.offset) {
+              logger.log('Failure: comment outside a function body.');
+              return false;
+            }
+          }
+        }
+        logger.log(() => 'oldNode: $oldNode');
+        logger.log(() => 'newNode: $newNode');
+        // prepare update range
+        _updateOffset = oldNode.offset;
+        _updateEndOld = oldNode.end;
+        _updateEndNew = newNode.end;
+        _updateDelta = _updateEndNew - _updateEndOld;
+        // replace node
+        NodeReplacer.replace(oldNode, newNode);
+        // update token references
+        {
+          Token oldBeginToken = _getBeginTokenNotComment(oldNode);
+          Token newBeginToken = _getBeginTokenNotComment(newNode);
+          if (oldBeginToken.previous.type == TokenType.EOF) {
+            _oldUnit.beginToken = newBeginToken;
+          } else {
+            oldBeginToken.previous.setNext(newBeginToken);
+          }
+          newNode.endToken.setNext(oldNode.endToken.next);
+          _shiftTokens(oldNode.endToken.next);
+        }
+        // perform incremental resolution
+        IncrementalResolver incrementalResolver = new IncrementalResolver(
+            _unitElement, _updateOffset, _updateEndOld, _updateEndNew);
+        bool success = incrementalResolver.resolve(newNode);
+        // check if success
+        if (!success) {
+          logger.log('Failure: element model changed.');
+          return false;
+        }
+        // update DartEntry
+        _updateEntry();
+        logger.log('Success.');
+        return true;
+      }
+    } catch (e, st) {
+      logger.log(e);
+      logger.log(st);
+      logger.log('Failure: exception.');
+    } finally {
+      logger.exit();
+    }
+    return false;
+  }
+
+  CompilationUnit _parseUnit(String code) {
+    LoggingTimer timer = logger.startTimer();
+    try {
+      Token token = _scan(code);
+      RecordingErrorListener errorListener = new RecordingErrorListener();
+      Parser parser = new Parser(_unitSource, errorListener);
+      CompilationUnit unit = parser.parseCompilationUnit(token);
+      _newParseErrors = errorListener.errors;
+      return unit;
+    } finally {
+      timer.stop('parse');
+    }
+  }
+
+  /**
+   * Attempts to resolve a documentation comment change.
+   * Returns `true` if success.
+   */
+  bool _resolveCommentDoc(CompilationUnit newUnit, _TokenPair firstPair) {
+    Token oldToken = firstPair.oldToken;
+    Token newToken = firstPair.newToken;
+    CommentToken oldComments = oldToken.precedingComments;
+    CommentToken newComments = newToken.precedingComments;
+    if (oldComments == null || newComments == null) {
+      return false;
+    }
+    // find nodes
+    int offset = oldComments.offset;
+    logger.log('offset: $offset');
+    Comment oldComment = _findNodeCovering(_oldUnit, offset, offset);
+    Comment newComment = _findNodeCovering(newUnit, offset, offset);
+    logger.log('oldComment.beginToken: ${oldComment.beginToken}');
+    logger.log('newComment.beginToken: ${newComment.beginToken}');
+    _updateOffset = oldToken.offset - 1;
+    // update token references
+    _shiftTokens(firstPair.oldToken);
+    _setPrecedingComments(oldToken, newComment.tokens.first);
+    // replace node
+    NodeReplacer.replace(oldComment, newComment);
+    // update elements
+    IncrementalResolver incrementalResolver = new IncrementalResolver(
+        _unitElement, _updateOffset, _updateEndOld, _updateEndNew);
+    incrementalResolver._updateElementNameOffsets();
+    incrementalResolver._shiftEntryErrors();
+    _updateEntry();
+    // resolve references in the comment
+    incrementalResolver._resolveReferences(newComment);
+    // OK
+    return true;
+  }
+
+  Token _scan(String code) {
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    CharSequenceReader reader = new CharSequenceReader(code);
+    Scanner scanner = new Scanner(_unitSource, reader, errorListener);
+    scanner.enableNullAwareOperators = _options.enableNullAwareOperators;
+    Token token = scanner.tokenize();
+    _newScanErrors = errorListener.errors;
+    return token;
+  }
+
+  /**
+   * Set the given [comment] as a "precedingComments" for [token].
+   */
+  void _setPrecedingComments(Token token, CommentToken comment) {
+    if (token is BeginTokenWithComment) {
+      token.precedingComments = comment;
+    } else if (token is KeywordTokenWithComment) {
+      token.precedingComments = comment;
+    } else if (token is StringTokenWithComment) {
+      token.precedingComments = comment;
+    } else if (token is TokenWithComment) {
+      token.precedingComments = comment;
+    } else {
+      Type parentType = token != null ? token.runtimeType : null;
+      throw new AnalysisException('Uknown parent token type: $parentType');
+    }
+  }
+
+  void _shiftTokens(Token token) {
+    while (token != null) {
+      if (token.offset > _updateOffset) {
+        token.offset += _updateDelta;
+      }
+      // comments
+      _shiftTokens(token.precedingComments);
+      if (token is DocumentationCommentToken) {
+        for (Token reference in token.references) {
+          _shiftTokens(reference);
+        }
+      }
+      // next
+      if (token.type == TokenType.EOF) {
+        break;
+      }
+      token = token.next;
+    }
+  }
+
+  void _updateEntry() {
+    _entry.setValue(DartEntry.SCAN_ERRORS, _newScanErrors);
+    _entry.setValue(DartEntry.PARSE_ERRORS, _newParseErrors);
+  }
+
+  /**
+   * Checks if [token] has a balanced number of open and closed curly brackets.
+   */
+  static bool _areCurlyBracketsBalanced(Token token) {
+    int numOpen = _getTokenCount(token, TokenType.OPEN_CURLY_BRACKET);
+    int numOpen2 =
+        _getTokenCount(token, TokenType.STRING_INTERPOLATION_EXPRESSION);
+    int numClosed = _getTokenCount(token, TokenType.CLOSE_CURLY_BRACKET);
+    return numOpen + numOpen2 == numClosed;
+  }
+
+  static _TokenDifferenceKind _compareToken(
+      Token oldToken, Token newToken, int delta, bool forComment) {
+    while (true) {
+      if (oldToken == null && newToken == null) {
+        return null;
+      }
+      if (oldToken == null || newToken == null) {
+        return _TokenDifferenceKind.CONTENT;
+      }
+      if (oldToken.type != newToken.type) {
+        return _TokenDifferenceKind.CONTENT;
+      }
+      if (oldToken.lexeme != newToken.lexeme) {
+        return _TokenDifferenceKind.CONTENT;
+      }
+      if (newToken.offset - oldToken.offset != delta) {
+        return _TokenDifferenceKind.OFFSET;
+      }
+      // continue if comment tokens are being checked
+      if (!forComment) {
+        break;
+      }
+      oldToken = oldToken.next;
+      newToken = newToken.next;
+    }
+    return null;
+  }
+
+  static _TokenPair _findFirstDifferentToken(Token oldToken, Token newToken) {
+    while (true) {
+      if (oldToken.type == TokenType.EOF && newToken.type == TokenType.EOF) {
+        return null;
+      }
+      if (oldToken.type == TokenType.EOF || newToken.type == TokenType.EOF) {
+        return new _TokenPair(_TokenDifferenceKind.CONTENT, oldToken, newToken);
+      }
+      // compare comments
+      {
+        Token oldComment = oldToken.precedingComments;
+        Token newComment = newToken.precedingComments;
+        if (_compareToken(oldComment, newComment, 0, true) != null) {
+          _TokenDifferenceKind diffKind = _TokenDifferenceKind.COMMENT;
+          if (oldComment is DocumentationCommentToken ||
+              newComment is DocumentationCommentToken) {
+            diffKind = _TokenDifferenceKind.COMMENT_DOC;
+          }
+          return new _TokenPair(diffKind, oldToken, newToken);
+        }
+      }
+      // compare tokens
+      _TokenDifferenceKind diffKind =
+          _compareToken(oldToken, newToken, 0, false);
+      if (diffKind != null) {
+        return new _TokenPair(diffKind, oldToken, newToken);
+      }
+      // next tokens
+      oldToken = oldToken.next;
+      newToken = newToken.next;
+    }
+    // no difference
+    return null;
+  }
+
+  static _TokenPair _findLastDifferentToken(Token oldToken, Token newToken) {
+    int delta = newToken.offset - oldToken.offset;
+    while (oldToken.previous != oldToken && newToken.previous != newToken) {
+      // compare tokens
+      _TokenDifferenceKind diffKind =
+          _compareToken(oldToken, newToken, delta, false);
+      if (diffKind != null) {
+        return new _TokenPair(diffKind, oldToken.next, newToken.next);
+      }
+      // compare comments
+      {
+        Token oldComment = oldToken.precedingComments;
+        Token newComment = newToken.precedingComments;
+        if (_compareToken(oldComment, newComment, delta, true) != null) {
+          _TokenDifferenceKind diffKind = _TokenDifferenceKind.COMMENT;
+          if (oldComment is DocumentationCommentToken ||
+              newComment is DocumentationCommentToken) {
+            diffKind = _TokenDifferenceKind.COMMENT_DOC;
+          }
+          return new _TokenPair(diffKind, oldToken, newToken);
+        }
+      }
+      // next tokens
+      oldToken = oldToken.previous;
+      newToken = newToken.previous;
+    }
+    return null;
+  }
+
+  static AstNode _findNodeCovering(AstNode root, int offset, int end) {
+    NodeLocator nodeLocator = new NodeLocator.con2(offset, end);
+    return nodeLocator.searchWithin(root);
+  }
+
+  static Token _getBeginTokenNotComment(AstNode node) {
+    Token oldBeginToken = node.beginToken;
+    if (oldBeginToken is CommentToken) {
+      oldBeginToken = (oldBeginToken as CommentToken).parent;
+    }
+    return oldBeginToken;
+  }
+
+  static List<AstNode> _getParents(AstNode node) {
+    List<AstNode> parents = <AstNode>[];
+    while (node != null) {
+      parents.insert(0, node);
+      node = node.parent;
+    }
+    return parents;
+  }
+
+  /**
+   * Returns number of tokens with the given [type].
+   */
+  static int _getTokenCount(Token token, TokenType type) {
+    int count = 0;
+    while (token.type != TokenType.EOF) {
+      if (token.type == type) {
+        count++;
+      }
+      token = token.next;
+    }
+    return count;
+  }
+}
+
+/**
+ * The context to resolve an [AstNode] in.
+ */
+class ResolutionContext {
+  CompilationUnitElement enclosingUnit;
+  ClassDeclaration enclosingClassDeclaration;
+  ClassElement enclosingClass;
+  Scope scope;
+}
+
+/**
+ * Instances of the class [ResolutionContextBuilder] build the context for a
+ * given node in an AST structure. At the moment, this class only handles
+ * top-level and class-level declarations.
+ */
+class ResolutionContextBuilder {
+  /**
+   * The listener to which analysis errors will be reported.
+   */
+  final AnalysisErrorListener _errorListener;
+
+  /**
+   * The class containing the enclosing [CompilationUnitElement].
+   */
+  CompilationUnitElement _enclosingUnit;
+
+  /**
+   * The class containing the enclosing [ClassDeclaration], or `null` if we are
+   * not in the scope of a class.
+   */
+  ClassDeclaration _enclosingClassDeclaration;
+
+  /**
+   * The class containing the enclosing [ClassElement], or `null` if we are not
+   * in the scope of a class.
+   */
+  ClassElement _enclosingClass;
+
+  /**
+   * Initialize a newly created scope builder to generate a scope that will
+   * report errors to the given listener.
+   */
+  ResolutionContextBuilder(this._errorListener);
+
+  Scope _scopeFor(AstNode node) {
+    if (node is CompilationUnit) {
+      return _scopeForAstNode(node);
+    }
+    AstNode parent = node.parent;
+    if (parent == null) {
+      throw new AnalysisException(
+          "Cannot create scope: node is not part of a CompilationUnit");
+    }
+    return _scopeForAstNode(parent);
+  }
+
+  /**
+   * Return the scope in which the given AST structure should be resolved.
+   *
+   * *Note:* This method needs to be kept in sync with
+   * [IncrementalResolver.canBeResolved].
+   *
+   * [node] - the root of the AST structure to be resolved.
+   *
+   * Throws [AnalysisException] if the AST structure has not been resolved or
+   * is not part of a [CompilationUnit]
+   */
+  Scope _scopeForAstNode(AstNode node) {
+    if (node is CompilationUnit) {
+      return _scopeForCompilationUnit(node);
+    }
+    AstNode parent = node.parent;
+    if (parent == null) {
+      throw new AnalysisException(
+          "Cannot create scope: node is not part of a CompilationUnit");
+    }
+    Scope scope = _scopeForAstNode(parent);
+    if (node is ClassDeclaration) {
+      _enclosingClassDeclaration = node;
+      _enclosingClass = node.element;
+      if (_enclosingClass == null) {
+        throw new AnalysisException(
+            "Cannot build a scope for an unresolved class");
+      }
+      scope = new ClassScope(
+          new TypeParameterScope(scope, _enclosingClass), _enclosingClass);
+    } else if (node is ClassTypeAlias) {
+      ClassElement element = node.element;
+      if (element == null) {
+        throw new AnalysisException(
+            "Cannot build a scope for an unresolved class type alias");
+      }
+      scope = new ClassScope(new TypeParameterScope(scope, element), element);
+    } else if (node is ConstructorDeclaration) {
+      ConstructorElement element = node.element;
+      if (element == null) {
+        throw new AnalysisException(
+            "Cannot build a scope for an unresolved constructor");
+      }
+      FunctionScope functionScope = new FunctionScope(scope, element);
+      functionScope.defineParameters();
+      scope = functionScope;
+    } else if (node is FunctionDeclaration) {
+      ExecutableElement element = node.element;
+      if (element == null) {
+        throw new AnalysisException(
+            "Cannot build a scope for an unresolved function");
+      }
+      FunctionScope functionScope = new FunctionScope(scope, element);
+      functionScope.defineParameters();
+      scope = functionScope;
+    } else if (node is FunctionTypeAlias) {
+      scope = new FunctionTypeScope(scope, node.element);
+    } else if (node is MethodDeclaration) {
+      ExecutableElement element = node.element;
+      if (element == null) {
+        throw new AnalysisException(
+            "Cannot build a scope for an unresolved method");
+      }
+      FunctionScope functionScope = new FunctionScope(scope, element);
+      functionScope.defineParameters();
+      scope = functionScope;
+    }
+    return scope;
+  }
+
+  Scope _scopeForCompilationUnit(CompilationUnit node) {
+    _enclosingUnit = node.element;
+    if (_enclosingUnit == null) {
+      throw new AnalysisException(
+          "Cannot create scope: compilation unit is not resolved");
+    }
+    LibraryElement libraryElement = _enclosingUnit.library;
+    if (libraryElement == null) {
+      throw new AnalysisException(
+          "Cannot create scope: compilation unit is not part of a library");
+    }
+    return new LibraryScope(libraryElement, _errorListener);
+  }
+
+  /**
+   * Return the context in which the given AST structure should be resolved.
+   *
+   * [node] - the root of the AST structure to be resolved.
+   * [errorListener] - the listener to which analysis errors will be reported.
+   *
+   * Throws [AnalysisException] if the AST structure has not been resolved or
+   * is not part of a [CompilationUnit]
+   */
+  static ResolutionContext contextFor(
+      AstNode node, AnalysisErrorListener errorListener) {
+    if (node == null) {
+      throw new AnalysisException("Cannot create context: node is null");
+    }
+    // build scope
+    ResolutionContextBuilder builder =
+        new ResolutionContextBuilder(errorListener);
+    Scope scope = builder._scopeFor(node);
+    // prepare context
+    ResolutionContext context = new ResolutionContext();
+    context.scope = scope;
+    context.enclosingUnit = builder._enclosingUnit;
+    context.enclosingClassDeclaration = builder._enclosingClassDeclaration;
+    context.enclosingClass = builder._enclosingClass;
+    return context;
+  }
+}
+
+/**
+ * Instances of the class [_DeclarationMismatchException] represent an exception
+ * that is thrown when the element model defined by a given AST structure does
+ * not match an existing element model.
+ */
+class _DeclarationMismatchException {}
+
+class _ElementNameOffsetUpdater extends GeneralizingElementVisitor {
+  final int updateOffset;
+  final int updateDelta;
+
+  _ElementNameOffsetUpdater(this.updateOffset, this.updateDelta);
+
+  @override
+  visitElement(Element element) {
+    int nameOffset = element.nameOffset;
+    if (nameOffset > updateOffset) {
+      (element as ElementImpl).nameOffset = nameOffset + updateDelta;
+    }
+    super.visitElement(element);
+  }
+}
+
+class _ElementsGatherer extends GeneralizingElementVisitor {
+  final DeclarationMatcher matcher;
+
+  _ElementsGatherer(this.matcher);
+
+  void addElements(List<Element> elements) {
+    for (Element element in elements) {
+      if (!element.isSynthetic) {
+        _addElement(element);
+      }
+    }
+  }
+
+  @override
+  visitElement(Element element) {
+    _addElement(element);
+    super.visitElement(element);
+  }
+
+  @override
+  visitExecutableElement(ExecutableElement element) {
+    _addElement(element);
+  }
+
+  @override
+  visitParameterElement(ParameterElement element) {}
+
+  @override
+  visitPropertyAccessorElement(PropertyAccessorElement element) {
+    if (!element.isSynthetic) {
+      _addElement(element);
+    }
+    // Don't visit children (such as synthetic setter parameters).
+  }
+
+  @override
+  visitPropertyInducingElement(PropertyInducingElement element) {
+    if (!element.isSynthetic) {
+      _addElement(element);
+    }
+    // Don't visit children (such as property accessors).
+  }
+
+  @override
+  visitTypeParameterElement(TypeParameterElement element) {}
+
+  void _addElement(Element element) {
+    if (element != null) {
+      matcher._allElements.add(element);
+      matcher._removedElements.add(element);
+    }
+  }
+}
+
+/**
+ * Describes how two [Token]s are different.
+ */
+class _TokenDifferenceKind {
+  static const COMMENT = const _TokenDifferenceKind('COMMENT');
+  static const COMMENT_DOC = const _TokenDifferenceKind('COMMENT_DOC');
+  static const CONTENT = const _TokenDifferenceKind('CONTENT');
+  static const OFFSET = const _TokenDifferenceKind('OFFSET');
+
+  final String name;
+
+  const _TokenDifferenceKind(this.name);
+
+  @override
+  String toString() => name;
+}
+
+class _TokenPair {
+  final _TokenDifferenceKind kind;
+  final Token oldToken;
+  final Token newToken;
+  _TokenPair(this.kind, this.oldToken, this.newToken);
+}
diff --git a/analyzer/lib/src/generated/incremental_scanner.dart b/analyzer/lib/src/generated/incremental_scanner.dart
new file mode 100644
index 0000000..eed2545
--- /dev/null
+++ b/analyzer/lib/src/generated/incremental_scanner.dart
@@ -0,0 +1,249 @@
+// 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 engine.incremental_scanner;
+
+import "dart:math" as math;
+
+import 'package:analyzer/src/generated/engine.dart';
+
+import 'error.dart';
+import 'scanner.dart';
+import 'source.dart';
+import 'utilities_collection.dart' show TokenMap;
+
+/**
+ * An `IncrementalScanner` is a scanner that scans a subset of a string and
+ * inserts the resulting tokens into the middle of an existing token stream.
+ */
+class IncrementalScanner {
+  /**
+   * The source being scanned.
+   */
+  final Source source;
+
+  /**
+   * The reader used to access the characters in the source.
+   */
+  final CharacterReader reader;
+
+  /**
+   * The error listener that will be informed of any errors that are found
+   * during the scan.
+   *
+   * TODO(brianwilkerson) Replace this with a list of errors so that we can
+   * update the errors.
+   */
+  final AnalysisErrorListener errorListener;
+
+  final AnalysisOptions _options;
+
+  /**
+   * A map from tokens that were copied to the copies of the tokens.
+   */
+  TokenMap _tokenMap = new TokenMap();
+
+  /**
+   * The token immediately to the left of the range of tokens that were
+   * modified.
+   */
+  Token leftToken;
+
+  /**
+   * The token immediately to the right of the range of tokens that were
+   * modified.
+   */
+  Token rightToken;
+
+  /**
+   * A flag indicating whether there were any non-comment tokens changed (other
+   * than having their position updated) as a result of the modification.
+   */
+  bool hasNonWhitespaceChange = false;
+
+  /**
+   * Initialize a newly created scanner to scan characters within the given
+   * [source]. The content of the source can be read using the given [reader].
+   * Any errors that are found will be reported to the given [errorListener].
+   * [_options] will determine how scanning is to be performed.
+   */
+  IncrementalScanner(
+      this.source, this.reader, this.errorListener, this._options);
+
+  /**
+   * Return a map from tokens that were copied to the copies of the tokens.
+   *
+   * @return a map from tokens that were copied to the copies of the tokens
+   */
+  TokenMap get tokenMap => _tokenMap;
+
+  /**
+   * Given the [stream] of tokens scanned from the original source, the modified
+   * source (the result of replacing one contiguous range of characters with
+   * another string of characters), and a specification of the modification that
+   * was made, update the token stream to reflect the modified source. Return
+   * the first token in the updated token stream.
+   *
+   * The [stream] is expected to be the first non-EOF token in the token stream.
+   *
+   * The modification is specified by the [index] of the first character in both
+   * the original and modified source that was affected by the modification, the
+   * number of characters removed from the original source (the [removedLength])
+   * and the number of characters added to the modified source (the
+   * [insertedLength]).
+   */
+  Token rescan(Token stream, int index, int removedLength, int insertedLength) {
+    Token leftEof = stream.previous;
+    //
+    // Compute the delta between the character index of characters after the
+    // modified region in the original source and the index of the corresponding
+    // character in the modified source.
+    //
+    int delta = insertedLength - removedLength;
+    //
+    // Skip past the tokens whose end is less than the replacement start. (If
+    // the replacement start is equal to the end of an existing token, then it
+    // means that the existing token might have been modified, so we need to
+    // rescan it.)
+    //
+    while (stream.type != TokenType.EOF && stream.end < index) {
+      _tokenMap.put(stream, stream);
+      stream = stream.next;
+    }
+    Token oldFirst = stream;
+    Token oldLeftToken = stream.previous;
+    leftToken = oldLeftToken;
+    //
+    // Skip past tokens until we find a token whose offset is greater than the
+    // end of the removed region. (If the end of the removed region is equal to
+    // the beginning of an existing token, then it means that the existing token
+    // might have been modified, so we need to rescan it.)
+    //
+    int removedEnd = index + (removedLength == 0 ? 0 : removedLength - 1);
+    while (stream.type != TokenType.EOF && stream.offset <= removedEnd) {
+      stream = stream.next;
+    }
+    //
+    // Figure out which region of characters actually needs to be re-scanned.
+    //
+    Token oldLast;
+    Token oldRightToken;
+    if (stream.type != TokenType.EOF && removedEnd + 1 == stream.offset) {
+      oldLast = stream;
+      stream = stream.next;
+      oldRightToken = stream;
+    } else {
+      oldLast = stream.previous;
+      oldRightToken = stream;
+    }
+    //
+    // Compute the range of characters that are known to need to be rescanned.
+    // If the index is within an existing token, then we need to start at the
+    // beginning of the token.
+    //
+    int scanStart = math.max(oldLeftToken.end, 0);
+    int scanEnd = oldRightToken.offset + delta;
+    //
+    // Rescan the characters that need to be rescanned.
+    //
+    Token replacementStart = _scanRange(scanStart, scanEnd);
+    oldLeftToken.setNext(replacementStart);
+    Token replacementEnd = _findEof(replacementStart).previous;
+    replacementEnd.setNext(stream);
+    //
+    // Apply the delta to the tokens after the last new token.
+    //
+    _updateOffsets(stream, delta);
+    rightToken = stream;
+    //
+    // If the index is immediately after an existing token and the inserted
+    // characters did not change that original token, then adjust the leftToken
+    // to be the next token. For example, in "a; c;" --> "a;b c;", the leftToken
+    // was ";", but this code advances it to "b" since "b" is the first new
+    // token.
+    //
+    Token newFirst = leftToken.next;
+    while (!identical(newFirst, rightToken) &&
+        !identical(oldFirst, oldRightToken) &&
+        newFirst.type != TokenType.EOF &&
+        _equalTokens(oldFirst, newFirst)) {
+      _tokenMap.put(oldFirst, newFirst);
+      oldLeftToken = oldFirst;
+      oldFirst = oldFirst.next;
+      leftToken = newFirst;
+      newFirst = newFirst.next;
+    }
+    Token newLast = rightToken.previous;
+    while (!identical(newLast, leftToken) &&
+        !identical(oldLast, oldLeftToken) &&
+        newLast.type != TokenType.EOF &&
+        _equalTokens(oldLast, newLast)) {
+      _tokenMap.put(oldLast, newLast);
+      oldRightToken = oldLast;
+      oldLast = oldLast.previous;
+      rightToken = newLast;
+      newLast = newLast.previous;
+    }
+    hasNonWhitespaceChange = !identical(leftToken.next, rightToken) ||
+        !identical(oldLeftToken.next, oldRightToken);
+    //
+    // TODO(brianwilkerson) Begin tokens are not getting associated with the
+    // corresponding end tokens (because the end tokens have not been copied
+    // when we're copying the begin tokens). This could have implications for
+    // parsing.
+    // TODO(brianwilkerson) Update the lineInfo.
+    //
+    return leftEof.next;
+  }
+
+  /**
+   * Return `true` if the [oldToken] and the [newToken] are equal to each other.
+   * For the purposes of the incremental scanner, two tokens are equal if they
+   * have the same type and lexeme.
+   */
+  bool _equalTokens(Token oldToken, Token newToken) =>
+      oldToken.type == newToken.type &&
+          oldToken.length == newToken.length &&
+          oldToken.lexeme == newToken.lexeme;
+
+  /**
+   * Given a [token], return the EOF token that follows the token.
+   */
+  Token _findEof(Token token) {
+    while (token.type != TokenType.EOF) {
+      token = token.next;
+    }
+    return token;
+  }
+
+  /**
+   * Scan the token between the [start] (inclusive) and [end] (exclusive)
+   * offsets.
+   */
+  Token _scanRange(int start, int end) {
+    Scanner scanner = new Scanner(
+        source, new CharacterRangeReader(reader, start, end), errorListener);
+    scanner.enableNullAwareOperators = _options.enableNullAwareOperators;
+    return scanner.tokenize();
+  }
+
+  /**
+   * Update the offsets of every token from the given [token] to the end of the
+   * stream by adding the given [delta].
+   */
+  void _updateOffsets(Token token, int delta) {
+    while (token.type != TokenType.EOF) {
+      _tokenMap.put(token, token);
+      token.offset += delta;
+      Token comment = token.precedingComments;
+      while (comment != null) {
+        comment.offset += delta;
+        comment = comment.next;
+      }
+      token = token.next;
+    }
+    _tokenMap.put(token, token);
+    token.offset += delta;
+  }
+}
diff --git a/analyzer/lib/src/generated/interner.dart b/analyzer/lib/src/generated/interner.dart
new file mode 100644
index 0000000..9ff047b
--- /dev/null
+++ b/analyzer/lib/src/generated/interner.dart
@@ -0,0 +1,49 @@
+// 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 interner;
+
+import 'dart:collection';
+
+/**
+ * The interface `Interner` defines the behavior of objects that can intern
+ * strings.
+ */
+abstract class Interner {
+  /**
+   * Return a string that is identical to all of the other strings that have
+   * been interned that are equal to the given [string].
+   */
+  String intern(String string);
+}
+
+/**
+ * The class `MappedInterner` implements an interner that uses a map to manage
+ * the strings that have been interned.
+ */
+class MappedInterner implements Interner {
+  /**
+   * A table mapping strings to themselves.
+   */
+  Map<String, String> _table = new HashMap<String, String>();
+
+  @override
+  String intern(String string) {
+    String original = _table[string];
+    if (original == null) {
+      _table[string] = string;
+      return string;
+    }
+    return original;
+  }
+}
+
+/**
+ * The class `NullInterner` implements an interner that does nothing (does not
+ * actually intern any strings).
+ */
+class NullInterner implements Interner {
+  @override
+  String intern(String string) => string;
+}
diff --git a/analyzer/lib/src/generated/java_core.dart b/analyzer/lib/src/generated/java_core.dart
new file mode 100644
index 0000000..9a0185a
--- /dev/null
+++ b/analyzer/lib/src/generated/java_core.dart
@@ -0,0 +1,440 @@
+library java.core;
+
+const int LONG_MAX_VALUE = 0x7fffffffffffffff;
+
+final Stopwatch nanoTimeStopwatch = new Stopwatch();
+
+/**
+ * Inserts the given arguments into [pattern].
+ *
+ *     format('Hello, {0}!', 'John') = 'Hello, John!'
+ *     format('{0} are you {1}ing?', 'How', 'do') = 'How are you doing?'
+ *     format('{0} are you {1}ing?', 'What', 'read') = 'What are you reading?'
+ */
+String format(String pattern,
+    [arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7]) {
+  return formatList(pattern, [arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7]);
+}
+
+/**
+ * Inserts the given [args] into [pattern].
+ *
+ *     format('Hello, {0}!', ['John']) = 'Hello, John!'
+ *     format('{0} are you {1}ing?', ['How', 'do']) = 'How are you doing?'
+ *     format('{0} are you {1}ing?', ['What', 'read']) = 'What are you reading?'
+ */
+String formatList(String pattern, List<Object> arguments) {
+  if (arguments == null || arguments.isEmpty) {
+    assert(!pattern.contains(new RegExp(r'\{(\d+)\}')));
+    return pattern;
+  }
+  return pattern.replaceAllMapped(new RegExp(r'\{(\d+)\}'), (match) {
+    String indexStr = match.group(1);
+    int index = int.parse(indexStr);
+    Object arg = arguments[index];
+    assert(arg != null);
+    return arg != null ? arg.toString() : null;
+  });
+}
+
+bool javaCollectionContainsAll(Iterable list, Iterable c) {
+  return c.fold(true, (bool prev, e) => prev && list.contains(e));
+}
+
+javaListSet(List list, int index, newValue) {
+  var oldValue = list[index];
+  list[index] = newValue;
+  return oldValue;
+}
+
+bool javaSetEquals(Set a, Set b) {
+  return a.containsAll(b) && b.containsAll(a);
+}
+
+bool javaStringEqualsIgnoreCase(String a, String b) {
+  return a.toLowerCase() == b.toLowerCase();
+}
+
+bool javaStringRegionMatches(
+    String t, int toffset, String o, int ooffset, int len) {
+  if (toffset < 0) return false;
+  if (ooffset < 0) return false;
+  var tend = toffset + len;
+  var oend = ooffset + len;
+  if (tend > t.length) return false;
+  if (oend > o.length) return false;
+  return t.substring(toffset, tend) == o.substring(ooffset, oend);
+}
+
+/// Parses given string to [Uri], throws [URISyntaxException] if invalid.
+Uri parseUriWithException(String str) {
+  Uri uri;
+  try {
+    uri = Uri.parse(str);
+  } on FormatException catch (e) {
+    throw new URISyntaxException(e.toString());
+  }
+  if (uri.path.isEmpty) {
+    throw new URISyntaxException('empty path');
+  }
+  return uri;
+}
+
+/**
+ * Very limited printf implementation, supports only %s and %d.
+ */
+String _printf(String fmt, List args) {
+  StringBuffer sb = new StringBuffer();
+  bool markFound = false;
+  int argIndex = 0;
+  for (int i = 0; i < fmt.length; i++) {
+    int c = fmt.codeUnitAt(i);
+    if (c == 0x25) {
+      if (markFound) {
+        sb.writeCharCode(c);
+        markFound = false;
+      } else {
+        markFound = true;
+      }
+      continue;
+    }
+    if (markFound) {
+      markFound = false;
+      // %d
+      if (c == 0x64) {
+        sb.write(args[argIndex++]);
+        continue;
+      }
+      // %s
+      if (c == 0x73) {
+        sb.write(args[argIndex++]);
+        continue;
+      }
+      // unknown
+      throw new IllegalArgumentException(
+          '[$fmt][$i] = 0x${c.toRadixString(16)}');
+    } else {
+      sb.writeCharCode(c);
+    }
+  }
+  return sb.toString();
+}
+
+class Character {
+  static const int MAX_VALUE = 0xffff;
+  static const int MAX_CODE_POINT = 0x10ffff;
+  static const int MIN_SUPPLEMENTARY_CODE_POINT = 0x010000;
+  static const int MIN_LOW_SURROGATE = 0xDC00;
+  static const int MIN_HIGH_SURROGATE = 0xD800;
+  static int digit(int codePoint, int radix) {
+    if (radix != 16) {
+      throw new ArgumentError("only radix == 16 is supported");
+    }
+    if (0x30 <= codePoint && codePoint <= 0x39) {
+      return codePoint - 0x30;
+    }
+    if (0x41 <= codePoint && codePoint <= 0x46) {
+      return 0xA + (codePoint - 0x41);
+    }
+    if (0x61 <= codePoint && codePoint <= 0x66) {
+      return 0xA + (codePoint - 0x61);
+    }
+    return -1;
+  }
+  static bool isDigit(int c) {
+    return c >= 0x30 && c <= 0x39;
+  }
+  static bool isLetter(int c) {
+    return c >= 0x41 && c <= 0x5A || c >= 0x61 && c <= 0x7A;
+  }
+  static bool isLetterOrDigit(int c) {
+    return isLetter(c) || isDigit(c);
+  }
+  static bool isLowerCase(int c) {
+    return c >= 0x61 && c <= 0x7A;
+  }
+  static bool isUpperCase(int c) {
+    return c >= 0x41 && c <= 0x5A;
+  }
+  static bool isWhitespace(int c) {
+    return c == 0x09 || c == 0x20 || c == 0x0A || c == 0x0D;
+  }
+  static String toChars(int codePoint) {
+    if (codePoint < 0 || codePoint > MAX_CODE_POINT) {
+      throw new IllegalArgumentException();
+    }
+    if (codePoint < MIN_SUPPLEMENTARY_CODE_POINT) {
+      return new String.fromCharCode(codePoint);
+    }
+    int offset = codePoint - MIN_SUPPLEMENTARY_CODE_POINT;
+    int c0 = ((offset & 0x7FFFFFFF) >> 10) + MIN_HIGH_SURROGATE;
+    int c1 = (offset & 0x3ff) + MIN_LOW_SURROGATE;
+    return new String.fromCharCodes([c0, c1]);
+  }
+  static int toLowerCase(int c) {
+    if (c >= 0x41 && c <= 0x5A) {
+      return 0x61 + (c - 0x41);
+    }
+    return c;
+  }
+  static int toUpperCase(int c) {
+    if (c >= 0x61 && c <= 0x7A) {
+      return 0x41 + (c - 0x61);
+    }
+    return c;
+  }
+}
+
+abstract class Enum<E extends Enum> implements Comparable<E> {
+  /// The name of this enum constant, as declared in the enum declaration.
+  final String name;
+  /// The position in the enum declaration.
+  final int ordinal;
+  const Enum(this.name, this.ordinal);
+  int get hashCode => ordinal;
+  int compareTo(E other) => ordinal - other.ordinal;
+  String toString() => name;
+}
+
+class IllegalArgumentException extends JavaException {
+  IllegalArgumentException([message = "", cause = null])
+      : super(message, cause);
+}
+
+class IllegalStateException extends JavaException {
+  IllegalStateException([message = ""]) : super(message);
+}
+
+class JavaArrays {
+  static bool equals(List a, List b) {
+    if (identical(a, b)) {
+      return true;
+    }
+    if (a.length != b.length) {
+      return false;
+    }
+    var len = a.length;
+    for (int i = 0; i < len; i++) {
+      if (a[i] != b[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+  static int makeHashCode(List a) {
+    if (a == null) {
+      return 0;
+    }
+    int result = 1;
+    for (var element in a) {
+      result = 31 * result + (element == null ? 0 : element.hashCode);
+    }
+    return result;
+  }
+}
+
+class JavaException implements Exception {
+  final String message;
+  final Exception cause;
+  JavaException([this.message = "", this.cause = null]);
+  JavaException.withCause(this.cause) : message = null;
+  String toString() => "$runtimeType: $message $cause";
+}
+
+class JavaIOException extends JavaException {
+  JavaIOException([message = "", cause = null]) : super(message, cause);
+}
+
+class JavaPatternMatcher {
+  Iterator<Match> _matches;
+  Match _match;
+  JavaPatternMatcher(RegExp re, String input) {
+    _matches = re.allMatches(input).iterator;
+  }
+  int end() => _match.end;
+  bool find() {
+    if (!_matches.moveNext()) {
+      return false;
+    }
+    _match = _matches.current;
+    return true;
+  }
+  String group(int i) => _match[i];
+  bool matches() => find();
+  int start() => _match.start;
+}
+
+class JavaString {
+  static int indexOf(String target, String str, int fromIndex) {
+    if (fromIndex > target.length) return -1;
+    if (fromIndex < 0) fromIndex = 0;
+    return target.indexOf(str, fromIndex);
+  }
+  static int lastIndexOf(String target, String str, int fromIndex) {
+    if (fromIndex > target.length) return -1;
+    if (fromIndex < 0) fromIndex = 0;
+    return target.lastIndexOf(str, fromIndex);
+  }
+  static bool startsWithBefore(String s, String other, int start) {
+    return s.indexOf(other, start) != -1;
+  }
+}
+
+class JavaSystem {
+  @deprecated
+  static void arraycopy(
+      List src, int srcPos, List dest, int destPos, int length) {
+    for (int i = 0; i < length; i++) {
+      dest[destPos + i] = src[srcPos + i];
+    }
+  }
+
+  static int currentTimeMillis() {
+    return (new DateTime.now()).millisecondsSinceEpoch;
+  }
+
+  static int nanoTime() {
+    if (!nanoTimeStopwatch.isRunning) {
+      nanoTimeStopwatch.start();
+    }
+    return nanoTimeStopwatch.elapsedMicroseconds * 1000;
+  }
+}
+
+class MissingFormatArgumentException implements Exception {
+  final String s;
+
+  MissingFormatArgumentException(this.s);
+
+  String toString() => "MissingFormatArgumentException: $s";
+}
+
+class NoSuchElementException extends JavaException {
+  String toString() => "NoSuchElementException";
+}
+
+class NotImplementedException extends JavaException {
+  NotImplementedException(message) : super(message);
+}
+
+class NumberFormatException extends JavaException {
+  String toString() => "NumberFormatException";
+}
+
+class PrintStringWriter extends PrintWriter {
+  final StringBuffer _sb = new StringBuffer();
+
+  void print(x) {
+    _sb.write(x);
+  }
+
+  String toString() => _sb.toString();
+}
+
+abstract class PrintWriter {
+  void newLine() {
+    this.print('\n');
+  }
+
+  void print(x);
+
+  void printf(String fmt, List args) {
+    this.print(_printf(fmt, args));
+  }
+
+  void println(String s) {
+    this.print(s);
+    this.newLine();
+  }
+}
+
+class RuntimeException extends JavaException {
+  RuntimeException({String message: "", Exception cause: null})
+      : super(message, cause);
+}
+
+class StringIndexOutOfBoundsException extends JavaException {
+  StringIndexOutOfBoundsException(int index) : super('$index');
+}
+
+class StringUtils {
+  static String capitalize(String str) {
+    if (isEmpty(str)) {
+      return str;
+    }
+    return str.substring(0, 1).toUpperCase() + str.substring(1);
+  }
+
+  static bool equals(String cs1, String cs2) {
+    if (cs1 == cs2) {
+      return true;
+    }
+    if (cs1 == null || cs2 == null) {
+      return false;
+    }
+    return cs1 == cs2;
+  }
+
+  static bool isEmpty(String str) {
+    return str == null || str.isEmpty;
+  }
+
+  static String join(Iterable iter,
+      [String separator = ' ', int start = 0, int end = -1]) {
+    if (start != 0) {
+      iter = iter.skip(start);
+    }
+    if (end != -1) {
+      iter = iter.take(end - start);
+    }
+    return iter.join(separator);
+  }
+
+  static void printf(StringBuffer buffer, String fmt, List args) {
+    buffer.write(_printf(fmt, args));
+  }
+
+  static String remove(String str, String remove) {
+    if (isEmpty(str) || isEmpty(remove)) {
+      return str;
+    }
+    return str.replaceAll(remove, '');
+  }
+
+  static String removeStart(String str, String remove) {
+    if (isEmpty(str) || isEmpty(remove)) {
+      return str;
+    }
+    if (str.startsWith(remove)) {
+      return str.substring(remove.length);
+    }
+    return str;
+  }
+
+  static String repeat(String s, int n) {
+    StringBuffer sb = new StringBuffer();
+    for (int i = 0; i < n; i++) {
+      sb.write(s);
+    }
+    return sb.toString();
+  }
+
+  static List<String> split(String s, [String pattern = ' ']) {
+    return s.split(pattern);
+  }
+
+  static List<String> splitByWholeSeparatorPreserveAllTokens(
+      String s, String pattern) {
+    return s.split(pattern);
+  }
+}
+
+class UnsupportedOperationException extends JavaException {
+  UnsupportedOperationException([message = ""]) : super(message);
+}
+
+class URISyntaxException implements Exception {
+  final String message;
+  URISyntaxException(this.message);
+  String toString() => "URISyntaxException: $message";
+}
diff --git a/analyzer/lib/src/generated/java_engine.dart b/analyzer/lib/src/generated/java_engine.dart
new file mode 100644
index 0000000..98ff8e4
--- /dev/null
+++ b/analyzer/lib/src/generated/java_engine.dart
@@ -0,0 +1,331 @@
+library java.engine;
+
+import 'interner.dart';
+import 'java_core.dart';
+
+/**
+ * A predicate is a one-argument function that returns a boolean value.
+ */
+typedef bool Predicate<E>(E argument);
+
+/**
+ * Instances of the class `AnalysisException` represent an exception that
+ * occurred during the analysis of one or more sources.
+ */
+class AnalysisException implements Exception {
+  /**
+   * The message that explains why the exception occurred.
+   */
+  final String message;
+
+  /**
+   * The exception that caused this exception, or `null` if this exception was
+   * not caused by another exception.
+   */
+  final CaughtException cause;
+
+  /**
+   * Initialize a newly created exception to have the given [message] and
+   * [cause].
+   */
+  AnalysisException([this.message = 'Exception', this.cause = null]);
+
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    buffer.write("AnalysisException: ");
+    buffer.writeln(message);
+    if (cause != null) {
+      buffer.write('Caused by ');
+      cause._writeOn(buffer);
+    }
+    return buffer.toString();
+  }
+}
+
+/**
+ * Instances of the class `CaughtException` represent an exception that was
+ * caught and has an associated stack trace.
+ */
+class CaughtException implements Exception {
+  /**
+   * The exception that was caught.
+   */
+  final Object exception;
+
+  /**
+   * The stack trace associated with the exception.
+   */
+  StackTrace stackTrace;
+
+  /**
+   * Initialize a newly created caught exception to have the given [exception]
+   * and [stackTrace].
+   */
+  CaughtException(this.exception, stackTrace) {
+    if (stackTrace == null) {
+      try {
+        throw this;
+      } catch (_, st) {
+        stackTrace = st;
+      }
+    }
+    this.stackTrace = stackTrace;
+  }
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    _writeOn(buffer);
+    return buffer.toString();
+  }
+
+  /**
+   * Write a textual representation of the caught exception and its associated
+   * stack trace.
+   */
+  void _writeOn(StringBuffer buffer) {
+    if (exception is AnalysisException) {
+      AnalysisException analysisException = exception;
+      buffer.writeln(analysisException.message);
+      if (stackTrace != null) {
+        buffer.writeln(stackTrace.toString());
+      }
+      CaughtException cause = analysisException.cause;
+      if (cause != null) {
+        buffer.write('Caused by ');
+        cause._writeOn(buffer);
+      }
+    } else {
+      buffer.writeln(exception.toString());
+      buffer.writeln(stackTrace.toString());
+    }
+  }
+}
+
+class FileNameUtilities {
+  static String getExtension(String fileName) {
+    if (fileName == null) {
+      return "";
+    }
+    int index = fileName.lastIndexOf('.');
+    if (index >= 0) {
+      return fileName.substring(index + 1);
+    }
+    return "";
+  }
+}
+
+class StringUtilities {
+  static const String EMPTY = '';
+  static const List<String> EMPTY_ARRAY = const <String>[];
+
+  static Interner INTERNER = new NullInterner();
+
+  static endsWith3(String str, int c1, int c2, int c3) {
+    var length = str.length;
+    return length >= 3 &&
+        str.codeUnitAt(length - 3) == c1 &&
+        str.codeUnitAt(length - 2) == c2 &&
+        str.codeUnitAt(length - 1) == c3;
+  }
+  static endsWithChar(String str, int c) {
+    int length = str.length;
+    return length > 0 && str.codeUnitAt(length - 1) == c;
+  }
+  static int indexOf1(String str, int start, int c) {
+    int index = start;
+    int last = str.length;
+    while (index < last) {
+      if (str.codeUnitAt(index) == c) {
+        return index;
+      }
+      index++;
+    }
+    return -1;
+  }
+  static int indexOf2(String str, int start, int c1, int c2) {
+    int index = start;
+    int last = str.length - 1;
+    while (index < last) {
+      if (str.codeUnitAt(index) == c1 && str.codeUnitAt(index + 1) == c2) {
+        return index;
+      }
+      index++;
+    }
+    return -1;
+  }
+  static int indexOf4(
+      String string, int start, int c1, int c2, int c3, int c4) {
+    int index = start;
+    int last = string.length - 3;
+    while (index < last) {
+      if (string.codeUnitAt(index) == c1 &&
+          string.codeUnitAt(index + 1) == c2 &&
+          string.codeUnitAt(index + 2) == c3 &&
+          string.codeUnitAt(index + 3) == c4) {
+        return index;
+      }
+      index++;
+    }
+    return -1;
+  }
+  static int indexOf5(
+      String str, int start, int c1, int c2, int c3, int c4, int c5) {
+    int index = start;
+    int last = str.length - 4;
+    while (index < last) {
+      if (str.codeUnitAt(index) == c1 &&
+          str.codeUnitAt(index + 1) == c2 &&
+          str.codeUnitAt(index + 2) == c3 &&
+          str.codeUnitAt(index + 3) == c4 &&
+          str.codeUnitAt(index + 4) == c5) {
+        return index;
+      }
+      index++;
+    }
+    return -1;
+  }
+
+  /**
+   * Return the index of the first not letter/digit character in the [string]
+   * that is at or after the [startIndex]. Return the length of the [string] if
+   * all characters to the end are letters/digits.
+   */
+  static int indexOfFirstNotLetterDigit(String string, int startIndex) {
+    int index = startIndex;
+    int last = string.length;
+    while (index < last) {
+      int c = string.codeUnitAt(index);
+      if (!Character.isLetterOrDigit(c)) {
+        return index;
+      }
+      index++;
+    }
+    return last;
+  }
+  static String intern(String string) => INTERNER.intern(string);
+  static bool isEmpty(String s) {
+    return s == null || s.isEmpty;
+  }
+  static bool isTagName(String s) {
+    if (s == null || s.length == 0) {
+      return false;
+    }
+    int sz = s.length;
+    for (int i = 0; i < sz; i++) {
+      int c = s.codeUnitAt(i);
+      if (!Character.isLetter(c)) {
+        if (i == 0) {
+          return false;
+        }
+        if (!Character.isDigit(c) && c != 0x2D) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+  /**
+   * Produce a string containing all of the names in the given array, surrounded by single quotes,
+   * and separated by commas. The list must contain at least two elements.
+   *
+   * @param names the names to be printed
+   * @return the result of printing the names
+   */
+  static String printListOfQuotedNames(List<String> names) {
+    if (names == null) {
+      throw new IllegalArgumentException("The list must not be null");
+    }
+    int count = names.length;
+    if (count < 2) {
+      throw new IllegalArgumentException(
+          "The list must contain at least two names");
+    }
+    StringBuffer buffer = new StringBuffer();
+    buffer.write("'");
+    buffer.write(names[0]);
+    buffer.write("'");
+    for (int i = 1; i < count - 1; i++) {
+      buffer.write(", '");
+      buffer.write(names[i]);
+      buffer.write("'");
+    }
+    buffer.write(" and '");
+    buffer.write(names[count - 1]);
+    buffer.write("'");
+    return buffer.toString();
+  }
+  static startsWith2(String str, int start, int c1, int c2) {
+    return str.length - start >= 2 &&
+        str.codeUnitAt(start) == c1 &&
+        str.codeUnitAt(start + 1) == c2;
+  }
+  static startsWith3(String str, int start, int c1, int c2, int c3) {
+    return str.length - start >= 3 &&
+        str.codeUnitAt(start) == c1 &&
+        str.codeUnitAt(start + 1) == c2 &&
+        str.codeUnitAt(start + 2) == c3;
+  }
+  static startsWith4(String str, int start, int c1, int c2, int c3, int c4) {
+    return str.length - start >= 4 &&
+        str.codeUnitAt(start) == c1 &&
+        str.codeUnitAt(start + 1) == c2 &&
+        str.codeUnitAt(start + 2) == c3 &&
+        str.codeUnitAt(start + 3) == c4;
+  }
+  static startsWith5(
+      String str, int start, int c1, int c2, int c3, int c4, int c5) {
+    return str.length - start >= 5 &&
+        str.codeUnitAt(start) == c1 &&
+        str.codeUnitAt(start + 1) == c2 &&
+        str.codeUnitAt(start + 2) == c3 &&
+        str.codeUnitAt(start + 3) == c4 &&
+        str.codeUnitAt(start + 4) == c5;
+  }
+  static startsWith6(
+      String str, int start, int c1, int c2, int c3, int c4, int c5, int c6) {
+    return str.length - start >= 6 &&
+        str.codeUnitAt(start) == c1 &&
+        str.codeUnitAt(start + 1) == c2 &&
+        str.codeUnitAt(start + 2) == c3 &&
+        str.codeUnitAt(start + 3) == c4 &&
+        str.codeUnitAt(start + 4) == c5 &&
+        str.codeUnitAt(start + 5) == c6;
+  }
+  static startsWithChar(String str, int c) {
+    return str.length != 0 && str.codeUnitAt(0) == c;
+  }
+
+  static String substringBefore(String str, String separator) {
+    if (str == null || str.isEmpty) {
+      return str;
+    }
+    if (separator == null) {
+      return str;
+    }
+    int pos = str.indexOf(separator);
+    if (pos < 0) {
+      return str;
+    }
+    return str.substring(0, pos);
+  }
+
+  static String substringBeforeChar(String str, int c) {
+    if (isEmpty(str)) {
+      return str;
+    }
+    int pos = indexOf1(str, 0, c);
+    if (pos < 0) {
+      return str;
+    }
+    return str.substring(0, pos);
+  }
+}
+
+class UUID {
+  static int __nextId = 0;
+  final String id;
+  UUID(this.id);
+  String toString() => id;
+  static UUID randomUUID() => new UUID((__nextId).toString());
+}
diff --git a/analyzer/lib/src/generated/java_engine_io.dart b/analyzer/lib/src/generated/java_engine_io.dart
new file mode 100644
index 0000000..749695a
--- /dev/null
+++ b/analyzer/lib/src/generated/java_engine_io.dart
@@ -0,0 +1,16 @@
+library java.engine.io;
+
+import "dart:io";
+import "java_io.dart";
+
+class OSUtilities {
+  static String LINE_SEPARATOR = isWindows() ? '\r\n' : '\n';
+  static bool isWindows() => Platform.operatingSystem == 'windows';
+  static bool isMac() => Platform.operatingSystem == 'macos';
+}
+
+class FileUtilities2 {
+  static JavaFile createFile(String path) {
+    return new JavaFile(path).getAbsoluteFile();
+  }
+}
diff --git a/analyzer/lib/src/generated/java_io.dart b/analyzer/lib/src/generated/java_io.dart
new file mode 100644
index 0000000..e947d80
--- /dev/null
+++ b/analyzer/lib/src/generated/java_io.dart
@@ -0,0 +1,143 @@
+library java.io;
+
+import "dart:io";
+
+import 'package:path/path.dart' as pathos;
+
+import 'java_core.dart' show JavaIOException;
+
+class JavaFile {
+  static final String separator = Platform.pathSeparator;
+  static final int separatorChar = Platform.pathSeparator.codeUnitAt(0);
+  String _path;
+  JavaFile(String path) {
+    _path = path;
+  }
+  JavaFile.fromUri(Uri uri) : this(pathos.fromUri(uri));
+  JavaFile.relative(JavaFile base, String child) {
+    if (child.isEmpty) {
+      this._path = base._path;
+    } else {
+      this._path = pathos.join(base._path, child);
+    }
+  }
+  int get hashCode => _path.hashCode;
+  bool operator ==(other) {
+    return other is JavaFile && other._path == _path;
+  }
+  bool exists() {
+    if (_newFile().existsSync()) {
+      return true;
+    }
+    if (_newDirectory().existsSync()) {
+      return true;
+    }
+    return false;
+  }
+  JavaFile getAbsoluteFile() => new JavaFile(getAbsolutePath());
+  String getAbsolutePath() {
+    String path = pathos.absolute(_path);
+    path = pathos.normalize(path);
+    return path;
+  }
+  JavaFile getCanonicalFile() => new JavaFile(getCanonicalPath());
+  String getCanonicalPath() {
+    try {
+      return _newFile().resolveSymbolicLinksSync();
+    } catch (e) {
+      throw new JavaIOException('IOException', e);
+    }
+  }
+  String getName() => pathos.basename(_path);
+  String getParent() {
+    var result = pathos.dirname(_path);
+    // "." or  "/" or  "C:\"
+    if (result.length < 4) return null;
+    return result;
+  }
+  JavaFile getParentFile() {
+    var parent = getParent();
+    if (parent == null) return null;
+    return new JavaFile(parent);
+  }
+  String getPath() => _path;
+  bool isDirectory() {
+    return _newDirectory().existsSync();
+  }
+  bool isExecutable() {
+    return _newFile().statSync().mode & 0x111 != 0;
+  }
+  bool isFile() {
+    return _newFile().existsSync();
+  }
+  int lastModified() {
+    try {
+      return _newFile().lastModifiedSync().millisecondsSinceEpoch;
+    } catch (exception) {
+      return -1;
+    }
+  }
+  List<JavaFile> listFiles() {
+    var files = <JavaFile>[];
+    var entities = _newDirectory().listSync();
+    for (FileSystemEntity entity in entities) {
+      files.add(new JavaFile(entity.path));
+    }
+    return files;
+  }
+  String readAsStringSync() => _newFile().readAsStringSync();
+  String toString() => _path.toString();
+  Uri toURI() {
+    String path = getAbsolutePath();
+    return pathos.toUri(path);
+  }
+  Directory _newDirectory() => new Directory(_path);
+  File _newFile() => new File(_path);
+}
+
+class JavaSystemIO {
+  static Map<String, String> _properties = new Map();
+  static String getenv(String name) => Platform.environment[name];
+  static String getProperty(String name) {
+    {
+      String value = _properties[name];
+      if (value != null) {
+        return value;
+      }
+    }
+    if (name == 'os.name') {
+      return Platform.operatingSystem;
+    }
+    if (name == 'line.separator') {
+      if (Platform.isWindows) {
+        return '\r\n';
+      }
+      return '\n';
+    }
+    if (name == 'com.google.dart.sdk') {
+      String exec = Platform.executable;
+      if (exec.length != 0) {
+        String sdkPath;
+        // may be "xcodebuild/ReleaseIA32/dart" with "sdk" sibling
+        {
+          var outDir = pathos.dirname(pathos.dirname(exec));
+          sdkPath = pathos.join(pathos.dirname(outDir), "sdk");
+          if (new Directory(sdkPath).existsSync()) {
+            _properties[name] = sdkPath;
+            return sdkPath;
+          }
+        }
+        // probably be "dart-sdk/bin/dart"
+        sdkPath = pathos.dirname(pathos.dirname(exec));
+        _properties[name] = sdkPath;
+        return sdkPath;
+      }
+    }
+    return null;
+  }
+  static String setProperty(String name, String value) {
+    String oldValue = _properties[name];
+    _properties[name] = value;
+    return oldValue;
+  }
+}
diff --git a/analyzer/lib/src/generated/parser.dart b/analyzer/lib/src/generated/parser.dart
new file mode 100644
index 0000000..89ae664
--- /dev/null
+++ b/analyzer/lib/src/generated/parser.dart
@@ -0,0 +1,10596 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.parser;
+
+import "dart:math" as math;
+import 'dart:collection';
+
+import 'ast.dart';
+import 'engine.dart' show AnalysisEngine, AnalysisOptionsImpl;
+import 'error.dart';
+import 'java_core.dart';
+import 'java_engine.dart';
+import 'scanner.dart';
+import 'source.dart';
+import 'utilities_collection.dart' show TokenMap;
+import 'utilities_dart.dart';
+
+Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
+  'parseCompilationUnit_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseCompilationUnit(arg0)),
+  'parseDirectives_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseDirectives(arg0)),
+  'parseExpression_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseExpression(arg0)),
+  'parseStatement_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseStatement(arg0)),
+  'parseStatements_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseStatements(arg0)),
+  'parseAnnotation_0':
+      new MethodTrampoline(0, (Parser target) => target.parseAnnotation()),
+  'parseArgument_0':
+      new MethodTrampoline(0, (Parser target) => target.parseArgument()),
+  'parseArgumentList_0':
+      new MethodTrampoline(0, (Parser target) => target.parseArgumentList()),
+  'parseBitwiseOrExpression_0': new MethodTrampoline(
+      0, (Parser target) => target.parseBitwiseOrExpression()),
+  'parseBlock_0':
+      new MethodTrampoline(0, (Parser target) => target.parseBlock()),
+  'parseClassMember_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target.parseClassMember(arg0)),
+  'parseCompilationUnit_0': new MethodTrampoline(
+      0, (Parser target) => target.parseCompilationUnit2()),
+  'parseConditionalExpression_0': new MethodTrampoline(
+      0, (Parser target) => target.parseConditionalExpression()),
+  'parseConstructorName_0':
+      new MethodTrampoline(0, (Parser target) => target.parseConstructorName()),
+  'parseExpression_0':
+      new MethodTrampoline(0, (Parser target) => target.parseExpression2()),
+  'parseExpressionWithoutCascade_0': new MethodTrampoline(
+      0, (Parser target) => target.parseExpressionWithoutCascade()),
+  'parseExtendsClause_0':
+      new MethodTrampoline(0, (Parser target) => target.parseExtendsClause()),
+  'parseFormalParameterList_0': new MethodTrampoline(
+      0, (Parser target) => target.parseFormalParameterList()),
+  'parseFunctionExpression_0': new MethodTrampoline(
+      0, (Parser target) => target.parseFunctionExpression()),
+  'parseImplementsClause_0': new MethodTrampoline(
+      0, (Parser target) => target.parseImplementsClause()),
+  'parseLabel_0':
+      new MethodTrampoline(0, (Parser target) => target.parseLabel()),
+  'parseLibraryIdentifier_0': new MethodTrampoline(
+      0, (Parser target) => target.parseLibraryIdentifier()),
+  'parseLogicalOrExpression_0': new MethodTrampoline(
+      0, (Parser target) => target.parseLogicalOrExpression()),
+  'parseMapLiteralEntry_0':
+      new MethodTrampoline(0, (Parser target) => target.parseMapLiteralEntry()),
+  'parseNormalFormalParameter_0': new MethodTrampoline(
+      0, (Parser target) => target.parseNormalFormalParameter()),
+  'parsePrefixedIdentifier_0': new MethodTrampoline(
+      0, (Parser target) => target.parsePrefixedIdentifier()),
+  'parseReturnType_0':
+      new MethodTrampoline(0, (Parser target) => target.parseReturnType()),
+  'parseSimpleIdentifier_0': new MethodTrampoline(
+      0, (Parser target) => target.parseSimpleIdentifier()),
+  'parseStatement_0':
+      new MethodTrampoline(0, (Parser target) => target.parseStatement2()),
+  'parseStringLiteral_0':
+      new MethodTrampoline(0, (Parser target) => target.parseStringLiteral()),
+  'parseTypeArgumentList_0': new MethodTrampoline(
+      0, (Parser target) => target.parseTypeArgumentList()),
+  'parseTypeName_0':
+      new MethodTrampoline(0, (Parser target) => target.parseTypeName()),
+  'parseTypeParameter_0':
+      new MethodTrampoline(0, (Parser target) => target.parseTypeParameter()),
+  'parseTypeParameterList_0': new MethodTrampoline(
+      0, (Parser target) => target.parseTypeParameterList()),
+  'parseWithClause_0':
+      new MethodTrampoline(0, (Parser target) => target.parseWithClause()),
+  'advance_0': new MethodTrampoline(0, (Parser target) => target._advance()),
+  'appendScalarValue_5': new MethodTrampoline(5, (Parser target, arg0, arg1,
+      arg2, arg3,
+      arg4) => target._appendScalarValue(arg0, arg1, arg2, arg3, arg4)),
+  'computeStringValue_3': new MethodTrampoline(3, (Parser target, arg0, arg1,
+      arg2) => target._computeStringValue(arg0, arg1, arg2)),
+  'convertToFunctionDeclaration_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._convertToFunctionDeclaration(arg0)),
+  'couldBeStartOfCompilationUnitMember_0': new MethodTrampoline(
+      0, (Parser target) => target._couldBeStartOfCompilationUnitMember()),
+  'createSyntheticIdentifier_0': new MethodTrampoline(
+      0, (Parser target) => target._createSyntheticIdentifier()),
+  'createSyntheticKeyword_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._createSyntheticKeyword(arg0)),
+  'createSyntheticStringLiteral_0': new MethodTrampoline(
+      0, (Parser target) => target._createSyntheticStringLiteral()),
+  'createSyntheticToken_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._createSyntheticToken(arg0)),
+  'ensureAssignable_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._ensureAssignable(arg0)),
+  'expect_1':
+      new MethodTrampoline(1, (Parser target, arg0) => target._expect(arg0)),
+  'expectGt_0': new MethodTrampoline(0, (Parser target) => target._expectGt()),
+  'expectKeyword_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._expectKeyword(arg0)),
+  'expectSemicolon_0':
+      new MethodTrampoline(0, (Parser target) => target._expectSemicolon()),
+  'findRange_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._findRange(arg0, arg1)),
+  'getCodeBlockRanges_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._getCodeBlockRanges(arg0)),
+  'getEndToken_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._getEndToken(arg0)),
+  'injectToken_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._injectToken(arg0)),
+  'isFunctionDeclaration_0': new MethodTrampoline(
+      0, (Parser target) => target._isFunctionDeclaration()),
+  'isFunctionExpression_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._isFunctionExpression(arg0)),
+  'isHexDigit_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._isHexDigit(arg0)),
+  'isInitializedVariableDeclaration_0': new MethodTrampoline(
+      0, (Parser target) => target._isInitializedVariableDeclaration()),
+  'isLinkText_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._isLinkText(arg0, arg1)),
+  'isOperator_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._isOperator(arg0)),
+  'isSwitchMember_0':
+      new MethodTrampoline(0, (Parser target) => target._isSwitchMember()),
+  'isTypedIdentifier_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._isTypedIdentifier(arg0)),
+  'lockErrorListener_0':
+      new MethodTrampoline(0, (Parser target) => target._lockErrorListener()),
+  'matches_1':
+      new MethodTrampoline(1, (Parser target, arg0) => target._matches(arg0)),
+  'matchesGt_0':
+      new MethodTrampoline(0, (Parser target) => target._matchesGt()),
+  'matchesIdentifier_0':
+      new MethodTrampoline(0, (Parser target) => target._matchesIdentifier()),
+  'matchesKeyword_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._matchesKeyword(arg0)),
+  'matchesString_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._matchesString(arg0)),
+  'optional_1':
+      new MethodTrampoline(1, (Parser target, arg0) => target._optional(arg0)),
+  'parseAdditiveExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseAdditiveExpression()),
+  'parseAssertStatement_0': new MethodTrampoline(
+      0, (Parser target) => target._parseAssertStatement()),
+  'parseAssignableExpression_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseAssignableExpression(arg0)),
+  'parseAssignableSelector_2': new MethodTrampoline(2, (Parser target, arg0,
+      arg1) => target._parseAssignableSelector(arg0, arg1)),
+  'parseAwaitExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseAwaitExpression()),
+  'parseBitwiseAndExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseBitwiseAndExpression()),
+  'parseBitwiseXorExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseBitwiseXorExpression()),
+  'parseBreakStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseBreakStatement()),
+  'parseCascadeSection_0':
+      new MethodTrampoline(0, (Parser target) => target._parseCascadeSection()),
+  'parseClassDeclaration_2': new MethodTrampoline(2,
+      (Parser target, arg0, arg1) => target._parseClassDeclaration(arg0, arg1)),
+  'parseClassMembers_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._parseClassMembers(arg0, arg1)),
+  'parseClassTypeAlias_3': new MethodTrampoline(3, (Parser target, arg0, arg1,
+      arg2) => target._parseClassTypeAlias(arg0, arg1, arg2)),
+  'parseCombinator_0':
+      new MethodTrampoline(0, (Parser target) => target.parseCombinator()),
+  'parseCombinators_0':
+      new MethodTrampoline(0, (Parser target) => target._parseCombinators()),
+  'parseCommentAndMetadata_0': new MethodTrampoline(
+      0, (Parser target) => target._parseCommentAndMetadata()),
+  'parseCommentReference_2': new MethodTrampoline(2,
+      (Parser target, arg0, arg1) => target._parseCommentReference(arg0, arg1)),
+  'parseCommentReferences_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseCommentReferences(arg0)),
+  'parseCompilationUnitMember_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseCompilationUnitMember(arg0)),
+  'parseConstExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseConstExpression()),
+  'parseConstructor_8': new MethodTrampoline(8, (Parser target, arg0, arg1,
+          arg2, arg3, arg4, arg5, arg6, arg7) =>
+      target._parseConstructor(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)),
+  'parseConstructorFieldInitializer_0': new MethodTrampoline(
+      0, (Parser target) => target._parseConstructorFieldInitializer()),
+  'parseContinueStatement_0': new MethodTrampoline(
+      0, (Parser target) => target._parseContinueStatement()),
+  'parseDirective_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseDirective(arg0)),
+  'parseDirectives_0':
+      new MethodTrampoline(0, (Parser target) => target._parseDirectives()),
+  'parseDocumentationComment_0': new MethodTrampoline(
+      0, (Parser target) => target._parseDocumentationComment()),
+  'parseDoStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseDoStatement()),
+  'parseEmptyStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseEmptyStatement()),
+  'parseEnumConstantDeclaration_0': new MethodTrampoline(
+      0, (Parser target) => target._parseEnumConstantDeclaration()),
+  'parseEnumDeclaration_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseEnumDeclaration(arg0)),
+  'parseEqualityExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseEqualityExpression()),
+  'parseExportDirective_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseExportDirective(arg0)),
+  'parseExpressionList_0':
+      new MethodTrampoline(0, (Parser target) => target._parseExpressionList()),
+  'parseFinalConstVarOrType_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseFinalConstVarOrType(arg0)),
+  'parseFormalParameter_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseFormalParameter(arg0)),
+  'parseForStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseForStatement()),
+  'parseFunctionBody_3': new MethodTrampoline(3, (Parser target, arg0, arg1,
+      arg2) => target._parseFunctionBody(arg0, arg1, arg2)),
+  'parseFunctionDeclaration_3': new MethodTrampoline(3, (Parser target, arg0,
+      arg1, arg2) => target._parseFunctionDeclaration(arg0, arg1, arg2)),
+  'parseFunctionDeclarationStatement_0': new MethodTrampoline(
+      0, (Parser target) => target._parseFunctionDeclarationStatement()),
+  'parseFunctionDeclarationStatementAfterReturnType_2': new MethodTrampoline(2,
+      (Parser target, arg0, arg1) =>
+          target._parseFunctionDeclarationStatementAfterReturnType(arg0, arg1)),
+  'parseFunctionTypeAlias_2': new MethodTrampoline(2, (Parser target, arg0,
+      arg1) => target._parseFunctionTypeAlias(arg0, arg1)),
+  'parseGetter_4': new MethodTrampoline(4, (Parser target, arg0, arg1, arg2,
+      arg3) => target._parseGetter(arg0, arg1, arg2, arg3)),
+  'parseIdentifierList_0':
+      new MethodTrampoline(0, (Parser target) => target._parseIdentifierList()),
+  'parseIfStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseIfStatement()),
+  'parseImportDirective_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseImportDirective(arg0)),
+  'parseInitializedIdentifierList_4': new MethodTrampoline(4,
+      (Parser target, arg0, arg1, arg2, arg3) =>
+          target._parseInitializedIdentifierList(arg0, arg1, arg2, arg3)),
+  'parseInstanceCreationExpression_1': new MethodTrampoline(1,
+      (Parser target, arg0) => target._parseInstanceCreationExpression(arg0)),
+  'parseLibraryDirective_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseLibraryDirective(arg0)),
+  'parseLibraryName_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._parseLibraryName(arg0, arg1)),
+  'parseListLiteral_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._parseListLiteral(arg0, arg1)),
+  'parseListOrMapLiteral_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseListOrMapLiteral(arg0)),
+  'parseLogicalAndExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseLogicalAndExpression()),
+  'parseMapLiteral_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._parseMapLiteral(arg0, arg1)),
+  'parseMethodDeclarationAfterParameters_6': new MethodTrampoline(6,
+      (Parser target, arg0, arg1, arg2, arg3, arg4, arg5) => target
+          ._parseMethodDeclarationAfterParameters(
+              arg0, arg1, arg2, arg3, arg4, arg5)),
+  'parseMethodDeclarationAfterReturnType_4': new MethodTrampoline(4,
+      (Parser target, arg0, arg1, arg2, arg3) => target
+          ._parseMethodDeclarationAfterReturnType(arg0, arg1, arg2, arg3)),
+  'parseModifiers_0':
+      new MethodTrampoline(0, (Parser target) => target._parseModifiers()),
+  'parseMultiplicativeExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseMultiplicativeExpression()),
+  'parseNativeClause_0':
+      new MethodTrampoline(0, (Parser target) => target._parseNativeClause()),
+  'parseNewExpression_0':
+      new MethodTrampoline(0, (Parser target) => target._parseNewExpression()),
+  'parseNonLabeledStatement_0': new MethodTrampoline(
+      0, (Parser target) => target._parseNonLabeledStatement()),
+  'parseOperator_3': new MethodTrampoline(3, (Parser target, arg0, arg1,
+      arg2) => target._parseOperator(arg0, arg1, arg2)),
+  'parseOptionalReturnType_0': new MethodTrampoline(
+      0, (Parser target) => target._parseOptionalReturnType()),
+  'parsePartDirective_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parsePartDirective(arg0)),
+  'parsePostfixExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parsePostfixExpression()),
+  'parsePrimaryExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parsePrimaryExpression()),
+  'parseRedirectingConstructorInvocation_0': new MethodTrampoline(
+      0, (Parser target) => target._parseRedirectingConstructorInvocation()),
+  'parseRelationalExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseRelationalExpression()),
+  'parseRethrowExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseRethrowExpression()),
+  'parseReturnStatement_0': new MethodTrampoline(
+      0, (Parser target) => target._parseReturnStatement()),
+  'parseSetter_4': new MethodTrampoline(4, (Parser target, arg0, arg1, arg2,
+      arg3) => target._parseSetter(arg0, arg1, arg2, arg3)),
+  'parseShiftExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseShiftExpression()),
+  'parseStatementList_0':
+      new MethodTrampoline(0, (Parser target) => target._parseStatementList()),
+  'parseStringInterpolation_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseStringInterpolation(arg0)),
+  'parseSuperConstructorInvocation_0': new MethodTrampoline(
+      0, (Parser target) => target._parseSuperConstructorInvocation()),
+  'parseSwitchStatement_0': new MethodTrampoline(
+      0, (Parser target) => target._parseSwitchStatement()),
+  'parseSymbolLiteral_0':
+      new MethodTrampoline(0, (Parser target) => target._parseSymbolLiteral()),
+  'parseThrowExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseThrowExpression()),
+  'parseThrowExpressionWithoutCascade_0': new MethodTrampoline(
+      0, (Parser target) => target._parseThrowExpressionWithoutCascade()),
+  'parseTryStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseTryStatement()),
+  'parseTypeAlias_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._parseTypeAlias(arg0)),
+  'parseUnaryExpression_0': new MethodTrampoline(
+      0, (Parser target) => target._parseUnaryExpression()),
+  'parseVariableDeclaration_0': new MethodTrampoline(
+      0, (Parser target) => target._parseVariableDeclaration()),
+  'parseVariableDeclarationListAfterMetadata_1': new MethodTrampoline(1,
+      (Parser target, arg0) =>
+          target._parseVariableDeclarationListAfterMetadata(arg0)),
+  'parseVariableDeclarationListAfterType_3': new MethodTrampoline(3,
+      (Parser target, arg0, arg1, arg2) =>
+          target._parseVariableDeclarationListAfterType(arg0, arg1, arg2)),
+  'parseVariableDeclarationStatementAfterMetadata_1': new MethodTrampoline(1,
+      (Parser target, arg0) =>
+          target._parseVariableDeclarationStatementAfterMetadata(arg0)),
+  'parseVariableDeclarationStatementAfterType_3': new MethodTrampoline(3,
+      (Parser target, arg0, arg1, arg2) =>
+          target._parseVariableDeclarationStatementAfterType(arg0, arg1, arg2)),
+  'parseWhileStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseWhileStatement()),
+  'parseYieldStatement_0':
+      new MethodTrampoline(0, (Parser target) => target._parseYieldStatement()),
+  'peek_0': new MethodTrampoline(0, (Parser target) => target._peek()),
+  'peekAt_1':
+      new MethodTrampoline(1, (Parser target, arg0) => target._peekAt(arg0)),
+  'reportError_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._reportError(arg0)),
+  'reportErrorForCurrentToken_2': new MethodTrampoline(2, (Parser target, arg0,
+      arg1) => target._reportErrorForCurrentToken(arg0, arg1)),
+  'reportErrorForNode_3': new MethodTrampoline(3, (Parser target, arg0, arg1,
+      arg2) => target._reportErrorForNode(arg0, arg1, arg2)),
+  'reportErrorForToken_3': new MethodTrampoline(3, (Parser target, arg0, arg1,
+      arg2) => target._reportErrorForToken(arg0, arg1, arg2)),
+  'skipBlock_0':
+      new MethodTrampoline(0, (Parser target) => target._skipBlock()),
+  'skipFinalConstVarOrType_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipFinalConstVarOrType(arg0)),
+  'skipFormalParameterList_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipFormalParameterList(arg0)),
+  'skipPastMatchingToken_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipPastMatchingToken(arg0)),
+  'skipPrefixedIdentifier_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipPrefixedIdentifier(arg0)),
+  'skipReturnType_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipReturnType(arg0)),
+  'skipSimpleIdentifier_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipSimpleIdentifier(arg0)),
+  'skipStringInterpolation_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipStringInterpolation(arg0)),
+  'skipStringLiteral_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipStringLiteral(arg0)),
+  'skipTypeArgumentList_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipTypeArgumentList(arg0)),
+  'skipTypeName_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipTypeName(arg0)),
+  'skipTypeParameterList_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._skipTypeParameterList(arg0)),
+  'tokenMatches_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._tokenMatches(arg0, arg1)),
+  'tokenMatchesIdentifier_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._tokenMatchesIdentifier(arg0)),
+  'tokenMatchesKeyword_2': new MethodTrampoline(2,
+      (Parser target, arg0, arg1) => target._tokenMatchesKeyword(arg0, arg1)),
+  'tokenMatchesString_2': new MethodTrampoline(
+      2, (Parser target, arg0, arg1) => target._tokenMatchesString(arg0, arg1)),
+  'translateCharacter_3': new MethodTrampoline(3, (Parser target, arg0, arg1,
+      arg2) => target._translateCharacter(arg0, arg1, arg2)),
+  'unlockErrorListener_0':
+      new MethodTrampoline(0, (Parser target) => target._unlockErrorListener()),
+  'validateFormalParameterList_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._validateFormalParameterList(arg0)),
+  'validateModifiersForClass_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._validateModifiersForClass(arg0)),
+  'validateModifiersForConstructor_1': new MethodTrampoline(1,
+      (Parser target, arg0) => target._validateModifiersForConstructor(arg0)),
+  'validateModifiersForEnum_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._validateModifiersForEnum(arg0)),
+  'validateModifiersForField_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._validateModifiersForField(arg0)),
+  'validateModifiersForFunctionDeclarationStatement_1': new MethodTrampoline(1,
+      (Parser target, arg0) =>
+          target._validateModifiersForFunctionDeclarationStatement(arg0)),
+  'validateModifiersForGetterOrSetterOrMethod_1': new MethodTrampoline(1,
+      (Parser target, arg0) =>
+          target._validateModifiersForGetterOrSetterOrMethod(arg0)),
+  'validateModifiersForOperator_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._validateModifiersForOperator(arg0)),
+  'validateModifiersForTopLevelDeclaration_1': new MethodTrampoline(1,
+      (Parser target, arg0) =>
+          target._validateModifiersForTopLevelDeclaration(arg0)),
+  'validateModifiersForTopLevelFunction_1': new MethodTrampoline(1,
+      (Parser target, arg0) =>
+          target._validateModifiersForTopLevelFunction(arg0)),
+  'validateModifiersForTopLevelVariable_1': new MethodTrampoline(1,
+      (Parser target, arg0) =>
+          target._validateModifiersForTopLevelVariable(arg0)),
+  'validateModifiersForTypedef_1': new MethodTrampoline(
+      1, (Parser target, arg0) => target._validateModifiersForTypedef(arg0)),
+};
+
+Object invokeParserMethodImpl(
+    Parser parser, String methodName, List<Object> objects, Token tokenStream) {
+  parser.currentToken = tokenStream;
+  MethodTrampoline method =
+      methodTable_Parser['${methodName}_${objects.length}'];
+  if (method == null) {
+    throw new IllegalArgumentException('There is no method named $methodName');
+  }
+  return method.invoke(parser, objects);
+}
+
+/**
+ * A simple data-holder for a method that needs to return multiple values.
+ */
+class CommentAndMetadata {
+  /**
+   * The documentation comment that was parsed, or `null` if none was given.
+   */
+  final Comment comment;
+
+  /**
+   * The metadata that was parsed.
+   */
+  final List<Annotation> metadata;
+
+  /**
+   * Initialize a newly created holder with the given [comment] and [metadata].
+   */
+  CommentAndMetadata(this.comment, this.metadata);
+}
+
+/**
+ * A simple data-holder for a method that needs to return multiple values.
+ */
+class FinalConstVarOrType {
+  /**
+   * The 'final', 'const' or 'var' keyword, or `null` if none was given.
+   */
+  final Token keyword;
+
+  /**
+   * The type, of `null` if no type was specified.
+   */
+  final TypeName type;
+
+  /**
+   * Initialize a newly created holder with the given [keyword] and [type].
+   */
+  FinalConstVarOrType(this.keyword, this.type);
+}
+
+/**
+ * A dispatcher that will invoke the right parse method when re-parsing a
+ * specified child of the visited node. All of the methods in this class assume
+ * that the parser is positioned to parse the replacement for the node. All of
+ * the methods will throw an [IncrementalParseException] if the node could not
+ * be parsed for some reason.
+ */
+class IncrementalParseDispatcher implements AstVisitor<AstNode> {
+  /**
+   * The parser used to parse the replacement for the node.
+   */
+  final Parser _parser;
+
+  /**
+   * The node that is to be replaced.
+   */
+  final AstNode _oldNode;
+
+  /**
+   * Initialize a newly created dispatcher to parse a single node that will
+   * use the [_parser] to replace the [_oldNode].
+   */
+  IncrementalParseDispatcher(this._parser, this._oldNode);
+
+  @override
+  AstNode visitAdjacentStrings(AdjacentStrings node) {
+    if (node.strings.contains(_oldNode)) {
+      return _parser.parseStringLiteral();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitAnnotation(Annotation node) {
+    if (identical(_oldNode, node.name)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.constructorName)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.arguments)) {
+      return _parser.parseArgumentList();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitArgumentList(ArgumentList node) {
+    if (node.arguments.contains(_oldNode)) {
+      return _parser.parseArgument();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitAsExpression(AsExpression node) {
+    if (identical(_oldNode, node.expression)) {
+      return _parser.parseBitwiseOrExpression();
+    } else if (identical(_oldNode, node.type)) {
+      return _parser.parseTypeName();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitAssertStatement(AssertStatement node) {
+    if (identical(_oldNode, node.condition)) {
+      return _parser.parseExpression2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitAssignmentExpression(AssignmentExpression node) {
+    if (identical(_oldNode, node.leftHandSide)) {
+      // TODO(brianwilkerson) If the assignment is part of a cascade section,
+      // then we don't have a single parse method that will work.
+      // Otherwise, we can parse a conditional expression, but need to ensure
+      // that the resulting expression is assignable.
+//      return parser.parseConditionalExpression();
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.rightHandSide)) {
+      if (_isCascadeAllowedInAssignment(node)) {
+        return _parser.parseExpression2();
+      }
+      return _parser.parseExpressionWithoutCascade();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitAwaitExpression(AwaitExpression node) {
+    if (identical(_oldNode, node.expression)) {
+      // TODO(brianwilkerson) Depending on precedence,
+      // this might not be sufficient.
+      return _parser.parseExpression2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitBinaryExpression(BinaryExpression node) {
+    if (identical(_oldNode, node.leftOperand)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.rightOperand)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitBlock(Block node) {
+    if (node.statements.contains(_oldNode)) {
+      return _parser.parseStatement2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitBlockFunctionBody(BlockFunctionBody node) {
+    if (identical(_oldNode, node.block)) {
+      return _parser.parseBlock();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitBooleanLiteral(BooleanLiteral node) => _notAChild(node);
+
+  @override
+  AstNode visitBreakStatement(BreakStatement node) {
+    if (identical(_oldNode, node.label)) {
+      return _parser.parseSimpleIdentifier();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitCascadeExpression(CascadeExpression node) {
+    if (identical(_oldNode, node.target)) {
+      return _parser.parseConditionalExpression();
+    } else if (node.cascadeSections.contains(_oldNode)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitCatchClause(CatchClause node) {
+    if (identical(_oldNode, node.exceptionType)) {
+      return _parser.parseTypeName();
+    } else if (identical(_oldNode, node.exceptionParameter)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.stackTraceParameter)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.body)) {
+      return _parser.parseBlock();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitClassDeclaration(ClassDeclaration node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.name)) {
+      // Changing the class name changes whether a member is interpreted as a
+      // constructor or not, so we'll just have to re-parse the entire class.
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.typeParameters)) {
+      return _parser.parseTypeParameterList();
+    } else if (identical(_oldNode, node.extendsClause)) {
+      return _parser.parseExtendsClause();
+    } else if (identical(_oldNode, node.withClause)) {
+      return _parser.parseWithClause();
+    } else if (identical(_oldNode, node.implementsClause)) {
+      return _parser.parseImplementsClause();
+    } else if (node.members.contains(_oldNode)) {
+      ClassMember member = _parser.parseClassMember(node.name.name);
+      if (member == null) {
+        throw new InsufficientContextException();
+      }
+      return member;
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitClassTypeAlias(ClassTypeAlias node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.name)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.typeParameters)) {
+      return _parser.parseTypeParameterList();
+    } else if (identical(_oldNode, node.superclass)) {
+      return _parser.parseTypeName();
+    } else if (identical(_oldNode, node.withClause)) {
+      return _parser.parseWithClause();
+    } else if (identical(_oldNode, node.implementsClause)) {
+      return _parser.parseImplementsClause();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitComment(Comment node) {
+    throw new InsufficientContextException();
+  }
+
+  @override
+  AstNode visitCommentReference(CommentReference node) {
+    if (identical(_oldNode, node.identifier)) {
+      return _parser.parsePrefixedIdentifier();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitCompilationUnit(CompilationUnit node) {
+    throw new InsufficientContextException();
+  }
+
+  @override
+  AstNode visitConditionalExpression(ConditionalExpression node) {
+    if (identical(_oldNode, node.condition)) {
+      return _parser.parseIfNullExpression();
+    } else if (identical(_oldNode, node.thenExpression)) {
+      return _parser.parseExpressionWithoutCascade();
+    } else if (identical(_oldNode, node.elseExpression)) {
+      return _parser.parseExpressionWithoutCascade();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitConstructorDeclaration(ConstructorDeclaration node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.returnType)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.name)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.parameters)) {
+      return _parser.parseFormalParameterList();
+    } else if (identical(_oldNode, node.redirectedConstructor)) {
+      throw new InsufficientContextException();
+    } else if (node.initializers.contains(_oldNode)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.body)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+    if (identical(_oldNode, node.fieldName)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.expression)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitConstructorName(ConstructorName node) {
+    if (identical(_oldNode, node.type)) {
+      return _parser.parseTypeName();
+    } else if (identical(_oldNode, node.name)) {
+      return _parser.parseSimpleIdentifier();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitContinueStatement(ContinueStatement node) {
+    if (identical(_oldNode, node.label)) {
+      return _parser.parseSimpleIdentifier();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitDeclaredIdentifier(DeclaredIdentifier node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.type)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.identifier)) {
+      return _parser.parseSimpleIdentifier();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitDefaultFormalParameter(DefaultFormalParameter node) {
+    if (identical(_oldNode, node.parameter)) {
+      return _parser.parseNormalFormalParameter();
+    } else if (identical(_oldNode, node.defaultValue)) {
+      return _parser.parseExpression2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitDoStatement(DoStatement node) {
+    if (identical(_oldNode, node.body)) {
+      return _parser.parseStatement2();
+    } else if (identical(_oldNode, node.condition)) {
+      return _parser.parseExpression2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitDoubleLiteral(DoubleLiteral node) => _notAChild(node);
+
+  @override
+  AstNode visitEmptyFunctionBody(EmptyFunctionBody node) => _notAChild(node);
+
+  @override
+  AstNode visitEmptyStatement(EmptyStatement node) => _notAChild(node);
+
+  @override
+  AstNode visitEnumConstantDeclaration(EnumConstantDeclaration node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.name)) {
+      return _parser.parseSimpleIdentifier();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitEnumDeclaration(EnumDeclaration node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.name)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (node.constants.contains(_oldNode)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitExportDirective(ExportDirective node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.uri)) {
+      return _parser.parseStringLiteral();
+    } else if (node.combinators.contains(_oldNode)) {
+      throw new IncrementalParseException();
+      //return parser.parseCombinator();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitExpressionFunctionBody(ExpressionFunctionBody node) {
+    if (identical(_oldNode, node.expression)) {
+      return _parser.parseExpression2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitExpressionStatement(ExpressionStatement node) {
+    if (identical(_oldNode, node.expression)) {
+      return _parser.parseExpression2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitExtendsClause(ExtendsClause node) {
+    if (identical(_oldNode, node.superclass)) {
+      return _parser.parseTypeName();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitFieldDeclaration(FieldDeclaration node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.fields)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitFieldFormalParameter(FieldFormalParameter node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.type)) {
+      return _parser.parseTypeName();
+    } else if (identical(_oldNode, node.identifier)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.parameters)) {
+      return _parser.parseFormalParameterList();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitForEachStatement(ForEachStatement node) {
+    if (identical(_oldNode, node.loopVariable)) {
+      throw new InsufficientContextException();
+      //return parser.parseDeclaredIdentifier();
+    } else if (identical(_oldNode, node.identifier)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.body)) {
+      return _parser.parseStatement2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitFormalParameterList(FormalParameterList node) {
+    // We don't know which kind of parameter to parse.
+    throw new InsufficientContextException();
+  }
+
+  @override
+  AstNode visitForStatement(ForStatement node) {
+    if (identical(_oldNode, node.variables)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.initialization)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.condition)) {
+      return _parser.parseExpression2();
+    } else if (node.updaters.contains(_oldNode)) {
+      return _parser.parseExpression2();
+    } else if (identical(_oldNode, node.body)) {
+      return _parser.parseStatement2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitFunctionDeclaration(FunctionDeclaration node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.returnType)) {
+      return _parser.parseReturnType();
+    } else if (identical(_oldNode, node.name)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.functionExpression)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
+    if (identical(_oldNode, node.functionDeclaration)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitFunctionExpression(FunctionExpression node) {
+    if (identical(_oldNode, node.parameters)) {
+      return _parser.parseFormalParameterList();
+    } else if (identical(_oldNode, node.body)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    if (identical(_oldNode, node.function)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.argumentList)) {
+      return _parser.parseArgumentList();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitFunctionTypeAlias(FunctionTypeAlias node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.returnType)) {
+      return _parser.parseReturnType();
+    } else if (identical(_oldNode, node.name)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.typeParameters)) {
+      return _parser.parseTypeParameterList();
+    } else if (identical(_oldNode, node.parameters)) {
+      return _parser.parseFormalParameterList();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.returnType)) {
+      return _parser.parseReturnType();
+    } else if (identical(_oldNode, node.identifier)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.parameters)) {
+      return _parser.parseFormalParameterList();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitHideCombinator(HideCombinator node) {
+    if (node.hiddenNames.contains(_oldNode)) {
+      return _parser.parseSimpleIdentifier();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitIfStatement(IfStatement node) {
+    if (identical(_oldNode, node.condition)) {
+      return _parser.parseExpression2();
+    } else if (identical(_oldNode, node.thenStatement)) {
+      return _parser.parseStatement2();
+    } else if (identical(_oldNode, node.elseStatement)) {
+      return _parser.parseStatement2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitImplementsClause(ImplementsClause node) {
+    if (node.interfaces.contains(node)) {
+      return _parser.parseTypeName();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitImportDirective(ImportDirective node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.uri)) {
+      return _parser.parseStringLiteral();
+    } else if (identical(_oldNode, node.prefix)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (node.combinators.contains(_oldNode)) {
+      return _parser.parseCombinator();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitIndexExpression(IndexExpression node) {
+    if (identical(_oldNode, node.target)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.index)) {
+      return _parser.parseExpression2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitInstanceCreationExpression(InstanceCreationExpression node) {
+    if (identical(_oldNode, node.constructorName)) {
+      return _parser.parseConstructorName();
+    } else if (identical(_oldNode, node.argumentList)) {
+      return _parser.parseArgumentList();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitIntegerLiteral(IntegerLiteral node) => _notAChild(node);
+
+  @override
+  AstNode visitInterpolationExpression(InterpolationExpression node) {
+    if (identical(_oldNode, node.expression)) {
+      if (node.leftBracket == null) {
+        throw new InsufficientContextException();
+        //return parser.parseThisOrSimpleIdentifier();
+      }
+      return _parser.parseExpression2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitInterpolationString(InterpolationString node) {
+    throw new InsufficientContextException();
+  }
+
+  @override
+  AstNode visitIsExpression(IsExpression node) {
+    if (identical(_oldNode, node.expression)) {
+      return _parser.parseBitwiseOrExpression();
+    } else if (identical(_oldNode, node.type)) {
+      return _parser.parseTypeName();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitLabel(Label node) {
+    if (identical(_oldNode, node.label)) {
+      return _parser.parseSimpleIdentifier();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitLabeledStatement(LabeledStatement node) {
+    if (node.labels.contains(_oldNode)) {
+      return _parser.parseLabel();
+    } else if (identical(_oldNode, node.statement)) {
+      return _parser.parseStatement2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitLibraryDirective(LibraryDirective node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.name)) {
+      return _parser.parseLibraryIdentifier();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitLibraryIdentifier(LibraryIdentifier node) {
+    if (node.components.contains(_oldNode)) {
+      return _parser.parseSimpleIdentifier();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitListLiteral(ListLiteral node) {
+    if (identical(_oldNode, node.typeArguments)) {
+      return _parser.parseTypeArgumentList();
+    } else if (node.elements.contains(_oldNode)) {
+      return _parser.parseExpression2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitMapLiteral(MapLiteral node) {
+    if (identical(_oldNode, node.typeArguments)) {
+      return _parser.parseTypeArgumentList();
+    } else if (node.entries.contains(_oldNode)) {
+      return _parser.parseMapLiteralEntry();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitMapLiteralEntry(MapLiteralEntry node) {
+    if (identical(_oldNode, node.key)) {
+      return _parser.parseExpression2();
+    } else if (identical(_oldNode, node.value)) {
+      return _parser.parseExpression2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitMethodDeclaration(MethodDeclaration node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.returnType)) {
+      throw new InsufficientContextException();
+      //return parser.parseTypeName();
+      //return parser.parseReturnType();
+    } else if (identical(_oldNode, node.name)) {
+      if (node.operatorKeyword != null) {
+        throw new InsufficientContextException();
+      }
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.body)) {
+      //return parser.parseFunctionBody();
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.parameters)) {
+      // TODO(paulberry): if we want errors to be correct, we'll need to also
+      // call _validateFormalParameterList, and sometimes
+      // _validateModifiersForGetterOrSetterOrMethod.
+      return _parser.parseFormalParameterList();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitMethodInvocation(MethodInvocation node) {
+    if (identical(_oldNode, node.target)) {
+      throw new IncrementalParseException();
+    } else if (identical(_oldNode, node.methodName)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.argumentList)) {
+      return _parser.parseArgumentList();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitNamedExpression(NamedExpression node) {
+    if (identical(_oldNode, node.name)) {
+      return _parser.parseLabel();
+    } else if (identical(_oldNode, node.expression)) {
+      return _parser.parseExpression2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitNativeClause(NativeClause node) {
+    if (identical(_oldNode, node.name)) {
+      return _parser.parseStringLiteral();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitNativeFunctionBody(NativeFunctionBody node) {
+    if (identical(_oldNode, node.stringLiteral)) {
+      return _parser.parseStringLiteral();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitNullLiteral(NullLiteral node) => _notAChild(node);
+
+  @override
+  AstNode visitParenthesizedExpression(ParenthesizedExpression node) {
+    if (identical(_oldNode, node.expression)) {
+      return _parser.parseExpression2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitPartDirective(PartDirective node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.uri)) {
+      return _parser.parseStringLiteral();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitPartOfDirective(PartOfDirective node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.libraryName)) {
+      return _parser.parseLibraryIdentifier();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitPostfixExpression(PostfixExpression node) {
+    if (identical(_oldNode, node.operand)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitPrefixedIdentifier(PrefixedIdentifier node) {
+    if (identical(_oldNode, node.prefix)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.identifier)) {
+      return _parser.parseSimpleIdentifier();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitPrefixExpression(PrefixExpression node) {
+    if (identical(_oldNode, node.operand)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitPropertyAccess(PropertyAccess node) {
+    if (identical(_oldNode, node.target)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.propertyName)) {
+      return _parser.parseSimpleIdentifier();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    if (identical(_oldNode, node.constructorName)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.argumentList)) {
+      return _parser.parseArgumentList();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitRethrowExpression(RethrowExpression node) => _notAChild(node);
+
+  @override
+  AstNode visitReturnStatement(ReturnStatement node) {
+    if (identical(_oldNode, node.expression)) {
+      return _parser.parseExpression2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitScriptTag(ScriptTag node) => _notAChild(node);
+
+  @override
+  AstNode visitShowCombinator(ShowCombinator node) {
+    if (node.shownNames.contains(_oldNode)) {
+      return _parser.parseSimpleIdentifier();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitSimpleFormalParameter(SimpleFormalParameter node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.type)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.identifier)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitSimpleIdentifier(SimpleIdentifier node) => _notAChild(node);
+
+  @override
+  AstNode visitSimpleStringLiteral(SimpleStringLiteral node) =>
+      _notAChild(node);
+
+  @override
+  AstNode visitStringInterpolation(StringInterpolation node) {
+    if (node.elements.contains(_oldNode)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    if (identical(_oldNode, node.constructorName)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.argumentList)) {
+      return _parser.parseArgumentList();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitSuperExpression(SuperExpression node) => _notAChild(node);
+
+  @override
+  AstNode visitSwitchCase(SwitchCase node) {
+    if (node.labels.contains(_oldNode)) {
+      return _parser.parseLabel();
+    } else if (identical(_oldNode, node.expression)) {
+      return _parser.parseExpression2();
+    } else if (node.statements.contains(_oldNode)) {
+      return _parser.parseStatement2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitSwitchDefault(SwitchDefault node) {
+    if (node.labels.contains(_oldNode)) {
+      return _parser.parseLabel();
+    } else if (node.statements.contains(_oldNode)) {
+      return _parser.parseStatement2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitSwitchStatement(SwitchStatement node) {
+    if (identical(_oldNode, node.expression)) {
+      return _parser.parseExpression2();
+    } else if (node.members.contains(_oldNode)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitSymbolLiteral(SymbolLiteral node) => _notAChild(node);
+
+  @override
+  AstNode visitThisExpression(ThisExpression node) => _notAChild(node);
+
+  @override
+  AstNode visitThrowExpression(ThrowExpression node) {
+    if (identical(_oldNode, node.expression)) {
+      if (_isCascadeAllowedInThrow(node)) {
+        return _parser.parseExpression2();
+      }
+      return _parser.parseExpressionWithoutCascade();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.variables)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitTryStatement(TryStatement node) {
+    if (identical(_oldNode, node.body)) {
+      return _parser.parseBlock();
+    } else if (node.catchClauses.contains(_oldNode)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.finallyBlock)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitTypeArgumentList(TypeArgumentList node) {
+    if (node.arguments.contains(_oldNode)) {
+      return _parser.parseTypeName();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitTypeName(TypeName node) {
+    if (identical(_oldNode, node.name)) {
+      return _parser.parsePrefixedIdentifier();
+    } else if (identical(_oldNode, node.typeArguments)) {
+      return _parser.parseTypeArgumentList();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitTypeParameter(TypeParameter node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.name)) {
+      return _parser.parseSimpleIdentifier();
+    } else if (identical(_oldNode, node.bound)) {
+      return _parser.parseTypeName();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitTypeParameterList(TypeParameterList node) {
+    if (node.typeParameters.contains(node)) {
+      return _parser.parseTypeParameter();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitVariableDeclaration(VariableDeclaration node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.name)) {
+      throw new InsufficientContextException();
+    } else if (identical(_oldNode, node.initializer)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitVariableDeclarationList(VariableDeclarationList node) {
+    if (identical(_oldNode, node.documentationComment)) {
+      throw new InsufficientContextException();
+    } else if (node.metadata.contains(_oldNode)) {
+      return _parser.parseAnnotation();
+    } else if (identical(_oldNode, node.type)) {
+      // There is not enough context to know whether we should reparse the type
+      // using parseReturnType() (which allows 'void') or parseTypeName()
+      // (which doesn't).  Note that even though the language disallows
+      // variables of type 'void', the parser sometimes accepts them in the
+      // course of error recovery (e.g. "class C { void v; }"
+      throw new InsufficientContextException();
+    } else if (node.variables.contains(_oldNode)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitVariableDeclarationStatement(VariableDeclarationStatement node) {
+    if (identical(_oldNode, node.variables)) {
+      throw new InsufficientContextException();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitWhileStatement(WhileStatement node) {
+    if (identical(_oldNode, node.condition)) {
+      return _parser.parseExpression2();
+    } else if (identical(_oldNode, node.body)) {
+      return _parser.parseStatement2();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitWithClause(WithClause node) {
+    if (node.mixinTypes.contains(node)) {
+      return _parser.parseTypeName();
+    }
+    return _notAChild(node);
+  }
+
+  @override
+  AstNode visitYieldStatement(YieldStatement node) {
+    if (identical(_oldNode, node.expression)) {
+      return _parser.parseExpression2();
+    }
+    return _notAChild(node);
+  }
+
+  /**
+   * Return `true` if the given assignment [expression] can have a cascade
+   * expression on the right-hand side.
+   */
+  bool _isCascadeAllowedInAssignment(AssignmentExpression expression) {
+    // TODO(brianwilkerson) Implement this method.
+    throw new InsufficientContextException();
+  }
+
+  /**
+   * Return `true` if the given throw [expression] can have a cascade
+   * expression.
+   */
+  bool _isCascadeAllowedInThrow(ThrowExpression expression) {
+    // TODO(brianwilkerson) Implement this method.
+    throw new InsufficientContextException();
+  }
+
+  /**
+   * Throw an exception indicating that the visited [node] was not the parent of
+   * the node to be replaced.
+   */
+  AstNode _notAChild(AstNode node) {
+    throw new IncrementalParseException.con1(
+        "Internal error: the visited node (a ${node.runtimeType}) was not the parent of the node to be replaced (a ${_oldNode.runtimeType})");
+  }
+}
+
+/**
+ * An exception that occurred while attempting to parse a replacement for a
+ * specified node in an existing AST structure.
+ */
+class IncrementalParseException extends RuntimeException {
+  /**
+   * Initialize a newly created exception to have no message and to be its own
+   * cause.
+   */
+  IncrementalParseException() : super();
+
+  /**
+   * Initialize a newly created exception to have the given [message] and to be
+   * its own cause.
+   */
+  IncrementalParseException.con1(String message) : super(message: message);
+
+  /**
+   * Initialize a newly created exception to have no message and to have the
+   * given [cause].
+   */
+  IncrementalParseException.con2(Exception cause) : super(cause: cause);
+}
+
+/**
+ * An object used to re-parse a single AST structure within a larger AST
+ * structure.
+ */
+class IncrementalParser {
+  /**
+   * The source being parsed.
+   */
+  final Source _source;
+
+  /**
+   * A map from old tokens to new tokens used during the cloning process.
+   */
+  final TokenMap _tokenMap;
+
+  /**
+   * The error listener that will be informed of any errors that are found
+   * during the parse.
+   */
+  final AnalysisErrorListener _errorListener;
+
+  /**
+   * The node in the AST structure that contains the revised content.
+   */
+  AstNode _updatedNode;
+
+  /**
+   * Initialize a newly created incremental parser to parse a portion of the
+   * content of the given [_source]. The [_tokenMap] is a map from old tokens to
+   * new tokens that is used during the cloning process. The [_errorListener]
+   * will be informed of any errors that are found during the parse.
+   */
+  IncrementalParser(this._source, this._tokenMap, this._errorListener);
+
+  /**
+   * Return the node in the AST structure that contains the revised content.
+   */
+  AstNode get updatedNode => _updatedNode;
+
+  /**
+   * Given a range of tokens that were re-scanned, re-parse the minimum number
+   * of tokens to produce a consistent AST structure. The range is represented
+   * by the first and last tokens in the range.
+   *
+   * More specifically, the [leftToken] is the token in the new token stream
+   * immediately to the left of the range of tokens that were inserted and the
+   * [rightToken] is the token in the new token stream immediately to the right
+   * of the range of tokens that were inserted. The [originalStart] and
+   * [originalEnd] are the offsets in the original source of the first and last
+   * characters that were modified.
+   *
+   * The tokens are assumed to be contained in the same token stream.
+   */
+  AstNode reparse(AstNode originalStructure, Token leftToken, Token rightToken,
+      int originalStart, int originalEnd) {
+    AstNode oldNode = null;
+    AstNode newNode = null;
+    //
+    // Find the first token that needs to be re-parsed.
+    //
+    Token firstToken = leftToken.next;
+    if (identical(firstToken, rightToken)) {
+      // If there are no new tokens, then we need to include at least one copied
+      // node in the range.
+      firstToken = leftToken;
+    }
+    //
+    // Find the smallest AST node that encompasses the range of re-scanned
+    // tokens.
+    //
+    if (originalEnd < originalStart) {
+      oldNode =
+          new NodeLocator.con1(originalStart).searchWithin(originalStructure);
+    } else {
+      oldNode = new NodeLocator.con2(originalStart, originalEnd)
+          .searchWithin(originalStructure);
+    }
+    //
+    // Find the token at which parsing is to begin.
+    //
+    int originalOffset = oldNode.offset;
+    Token parseToken = _findTokenAt(firstToken, originalOffset);
+    if (parseToken == null) {
+      return null;
+    }
+    //
+    // Parse the appropriate AST structure starting at the appropriate place.
+    //
+    Parser parser = new Parser(_source, _errorListener);
+    parser.currentToken = parseToken;
+    while (newNode == null) {
+      AstNode parent = oldNode.parent;
+      if (parent == null) {
+        parseToken = _findFirstToken(parseToken);
+        parser.currentToken = parseToken;
+        return parser.parseCompilationUnit2();
+      }
+      bool advanceToParent = false;
+      try {
+        IncrementalParseDispatcher dispatcher =
+            new IncrementalParseDispatcher(parser, oldNode);
+        IncrementalParseStateBuilder contextBuilder =
+            new IncrementalParseStateBuilder(parser);
+        contextBuilder.buildState(oldNode);
+        newNode = parent.accept(dispatcher);
+        //
+        // Validate that the new node can replace the old node.
+        //
+        Token mappedToken = _tokenMap.get(oldNode.endToken.next);
+        if (mappedToken == null ||
+            newNode == null ||
+            mappedToken.offset != newNode.endToken.next.offset ||
+            newNode.offset != oldNode.offset) {
+          advanceToParent = true;
+        }
+      } on InsufficientContextException {
+        advanceToParent = true;
+      } catch (exception) {
+        return null;
+      }
+      if (advanceToParent) {
+        newNode = null;
+        oldNode = parent;
+        originalOffset = oldNode.offset;
+        parseToken = _findTokenAt(parseToken, originalOffset);
+        parser.currentToken = parseToken;
+      }
+    }
+    _updatedNode = newNode;
+    //
+    // Replace the old node with the new node in a copy of the original AST
+    // structure.
+    //
+    if (identical(oldNode, originalStructure)) {
+      // We ended up re-parsing the whole structure, so there's no need for a
+      // copy.
+      ResolutionCopier.copyResolutionData(oldNode, newNode);
+      return newNode;
+    }
+    ResolutionCopier.copyResolutionData(oldNode, newNode);
+    IncrementalAstCloner cloner =
+        new IncrementalAstCloner(oldNode, newNode, _tokenMap);
+    return originalStructure.accept(cloner) as AstNode;
+  }
+
+  /**
+   * Return the first (non-EOF) token in the token stream containing the
+   * [firstToken].
+   */
+  Token _findFirstToken(Token firstToken) {
+    while (firstToken.type != TokenType.EOF) {
+      firstToken = firstToken.previous;
+    }
+    return firstToken.next;
+  }
+
+  /**
+   * Find the token at or before the [firstToken] with the given [offset], or
+   * `null` if there is no such token.
+   */
+  Token _findTokenAt(Token firstToken, int offset) {
+    while (firstToken.offset > offset && firstToken.type != TokenType.EOF) {
+      firstToken = firstToken.previous;
+    }
+    return firstToken;
+  }
+}
+
+/**
+ * A visitor capable of inferring the correct parser state for incremental
+ * parsing.  This visitor visits each parent/child relationship in the chain of
+ * ancestors of the node to be replaced (starting with the root of the parse
+ * tree), updating the parser to the correct state for parsing the child of the
+ * given parent.  Once it has visited all of these relationships, the parser
+ * will be in the correct state for reparsing the node to be replaced.
+ */
+class IncrementalParseStateBuilder extends SimpleAstVisitor {
+  // TODO(paulberry): add support for other pieces of parser state (_inAsync,
+  // _inGenerator, _inLoop, and _inSwitch).  Note that _inLoop and _inSwitch
+  // only affect error message generation.
+
+  /**
+   * The parser whose state should be built.
+   */
+  final Parser _parser;
+
+  /**
+   * The child node in the parent/child relationship currently being visited.
+   * (The corresponding parent is the node passed to the visit...() function.)
+   */
+  AstNode _childNode;
+
+  /**
+   * Create an IncrementalParseStateBuilder which will build the correct state
+   * for [_parser].
+   */
+  IncrementalParseStateBuilder(this._parser);
+
+  /**
+   * Build the correct parser state for parsing a replacement for [node].
+   */
+  void buildState(AstNode node) {
+    List<AstNode> ancestors = <AstNode>[];
+    while (node != null) {
+      ancestors.add(node);
+      node = node.parent;
+    }
+    _parser._inInitializer = false;
+    for (int i = ancestors.length - 2; i >= 0; i--) {
+      _childNode = ancestors[i];
+      ancestors[i + 1].accept(this);
+    }
+  }
+
+  @override
+  void visitArgumentList(ArgumentList node) {
+    _parser._inInitializer = false;
+  }
+
+  @override
+  void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+    if (identical(_childNode, node.expression)) {
+      _parser._inInitializer = true;
+    }
+  }
+
+  @override
+  void visitIndexExpression(IndexExpression node) {
+    if (identical(_childNode, node.index)) {
+      _parser._inInitializer = false;
+    }
+  }
+
+  @override
+  void visitInterpolationExpression(InterpolationExpression node) {
+    if (identical(_childNode, node.expression)) {
+      _parser._inInitializer = false;
+    }
+  }
+
+  @override
+  void visitListLiteral(ListLiteral node) {
+    if (node.elements.contains(_childNode)) {
+      _parser._inInitializer = false;
+    }
+  }
+
+  @override
+  void visitMapLiteral(MapLiteral node) {
+    if (node.entries.contains(_childNode)) {
+      _parser._inInitializer = false;
+    }
+  }
+
+  @override
+  void visitParenthesizedExpression(ParenthesizedExpression node) {
+    if (identical(_childNode, node.expression)) {
+      _parser._inInitializer = false;
+    }
+  }
+}
+
+/**
+ * An exception indicating that an AST node cannot be re-parsed because there is
+ * not enough context to know how to re-parse the node. Clients can attempt to
+ * re-parse the parent of the node.
+ */
+class InsufficientContextException extends IncrementalParseException {
+  /**
+   * Initialize a newly created exception to have no message and to be its own
+   * cause.
+   */
+  InsufficientContextException() : super();
+
+  /**
+   * Initialize a newly created exception to have the given [message] and to be
+   * its own cause.
+   */
+  InsufficientContextException.con1(String message) : super.con1(message);
+
+  /**
+   * Initialize a newly created exception to have no message and to have the
+   * given [cause].
+   */
+  InsufficientContextException.con2(Exception cause) : super.con2(cause);
+}
+
+/**
+ * Wrapper around [Function] which should be called with "target" and
+ * "arguments".
+ */
+class MethodTrampoline {
+  int parameterCount;
+  Function trampoline;
+  MethodTrampoline(this.parameterCount, this.trampoline);
+  Object invoke(target, List arguments) {
+    if (arguments.length != parameterCount) {
+      throw new IllegalArgumentException(
+          "${arguments.length} != $parameterCount");
+    }
+    switch (parameterCount) {
+      case 0:
+        return trampoline(target);
+      case 1:
+        return trampoline(target, arguments[0]);
+      case 2:
+        return trampoline(target, arguments[0], arguments[1]);
+      case 3:
+        return trampoline(target, arguments[0], arguments[1], arguments[2]);
+      case 4:
+        return trampoline(
+            target, arguments[0], arguments[1], arguments[2], arguments[3]);
+      default:
+        throw new IllegalArgumentException("Not implemented for > 4 arguments");
+    }
+  }
+}
+
+/**
+ * A simple data-holder for a method that needs to return multiple values.
+ */
+class Modifiers {
+  /**
+   * The token representing the keyword 'abstract', or `null` if the keyword was
+   * not found.
+   */
+  Token abstractKeyword;
+
+  /**
+   * The token representing the keyword 'const', or `null` if the keyword was
+   * not found.
+   */
+  Token constKeyword;
+
+  /**
+   * The token representing the keyword 'external', or `null` if the keyword was
+   * not found.
+   */
+  Token externalKeyword;
+
+  /**
+   * The token representing the keyword 'factory', or `null` if the keyword was
+   * not found.
+   */
+  Token factoryKeyword;
+
+  /**
+   * The token representing the keyword 'final', or `null` if the keyword was
+   * not found.
+   */
+  Token finalKeyword;
+
+  /**
+   * The token representing the keyword 'static', or `null` if the keyword was
+   * not found.
+   */
+  Token staticKeyword;
+
+  /**
+   * The token representing the keyword 'var', or `null` if the keyword was not
+   * found.
+   */
+  Token varKeyword;
+
+  @override
+  String toString() {
+    StringBuffer buffer = new StringBuffer();
+    bool needsSpace = _appendKeyword(buffer, false, abstractKeyword);
+    needsSpace = _appendKeyword(buffer, needsSpace, constKeyword);
+    needsSpace = _appendKeyword(buffer, needsSpace, externalKeyword);
+    needsSpace = _appendKeyword(buffer, needsSpace, factoryKeyword);
+    needsSpace = _appendKeyword(buffer, needsSpace, finalKeyword);
+    needsSpace = _appendKeyword(buffer, needsSpace, staticKeyword);
+    _appendKeyword(buffer, needsSpace, varKeyword);
+    return buffer.toString();
+  }
+
+  /**
+   * If the given [keyword] is not `null`, append it to the given [builder],
+   * prefixing it with a space if [needsSpace] is `true`. Return `true` if
+   * subsequent keywords need to be prefixed with a space.
+   */
+  bool _appendKeyword(StringBuffer buffer, bool needsSpace, Token keyword) {
+    if (keyword != null) {
+      if (needsSpace) {
+        buffer.writeCharCode(0x20);
+      }
+      buffer.write(keyword.lexeme);
+      return true;
+    }
+    return needsSpace;
+  }
+}
+
+/**
+ * A parser used to parse tokens into an AST structure.
+ */
+class Parser {
+  static String ASYNC = "async";
+
+  static String _AWAIT = "await";
+
+  static String _HIDE = "hide";
+
+  static String _OF = "of";
+
+  static String _ON = "on";
+
+  static String _NATIVE = "native";
+
+  static String _SHOW = "show";
+
+  static String SYNC = "sync";
+
+  static String _YIELD = "yield";
+
+  /**
+   * The source being parsed.
+   */
+  final Source _source;
+
+  /**
+   * The error listener that will be informed of any errors that are found
+   * during the parse.
+   */
+  final AnalysisErrorListener _errorListener;
+
+  /**
+   * An [errorListener] lock, if more than `0`, then errors are not reported.
+   */
+  int _errorListenerLock = 0;
+
+  /**
+   * A flag indicating whether parser is to parse function bodies.
+   */
+  bool _parseFunctionBodies = true;
+
+  /**
+   * The next token to be parsed.
+   */
+  Token _currentToken;
+
+  /**
+   * A flag indicating whether the parser is currently in a function body marked
+   * as being 'async'.
+   */
+  bool _inAsync = false;
+
+  /**
+   * A flag indicating whether the parser is currently in a function body marked
+   * as being 'async'.
+   */
+  bool _inGenerator = false;
+
+  /**
+   * A flag indicating whether the parser is currently in the body of a loop.
+   */
+  bool _inLoop = false;
+
+  /**
+   * A flag indicating whether the parser is currently in a switch statement.
+   */
+  bool _inSwitch = false;
+
+  /**
+   * A flag indicating whether the parser is currently in a constructor field
+   * initializer, with no intervening parens, braces, or brackets.
+   */
+  bool _inInitializer = false;
+
+  /**
+   * Initialize a newly created parser to parse the content of the given
+   * [_source] and to report any errors that are found to the given
+   * [_errorListener].
+   */
+  Parser(this._source, this._errorListener);
+
+  void set currentToken(Token currentToken) {
+    this._currentToken = currentToken;
+  }
+
+  /**
+   * Return `true` if the current token is the first token of a return type that
+   * is followed by an identifier, possibly followed by a list of type
+   * parameters, followed by a left-parenthesis. This is used by
+   * [_parseTypeAlias] to determine whether or not to parse a return type.
+   */
+  bool get hasReturnTypeInTypeAlias {
+    Token next = _skipReturnType(_currentToken);
+    if (next == null) {
+      return false;
+    }
+    return _tokenMatchesIdentifier(next);
+  }
+
+  /**
+   * Set whether the parser is to parse the async support.
+   */
+  @deprecated
+  void set parseAsync(bool parseAsync) {
+    // Async support cannot be disabled
+  }
+
+  /**
+   * Set whether the parser is to parse deferred libraries.
+   */
+  @deprecated
+  void set parseDeferredLibraries(bool parseDeferredLibraries) {
+    // Deferred libraries support cannot be disabled
+  }
+
+  /**
+   * Set whether the parser is to parse enum declarations.
+   */
+  @deprecated
+  void set parseEnum(bool parseEnum) {
+    // Enum support cannot be disabled
+  }
+
+  /**
+   * Set whether parser is to parse function bodies.
+   */
+  void set parseFunctionBodies(bool parseFunctionBodies) {
+    this._parseFunctionBodies = parseFunctionBodies;
+  }
+
+  /**
+   * Advance to the next token in the token stream, making it the new current
+   * token and return the token that was current before this method was invoked.
+   */
+  Token getAndAdvance() {
+    Token token = _currentToken;
+    _advance();
+    return token;
+  }
+
+  /**
+   * Parse an annotation. Return the annotation that was parsed.
+   *
+   *     annotation ::=
+   *         '@' qualified ('.' identifier)? arguments?
+   *
+   */
+  Annotation parseAnnotation() {
+    Token atSign = _expect(TokenType.AT);
+    Identifier name = parsePrefixedIdentifier();
+    Token period = null;
+    SimpleIdentifier constructorName = null;
+    if (_matches(TokenType.PERIOD)) {
+      period = getAndAdvance();
+      constructorName = parseSimpleIdentifier();
+    }
+    ArgumentList arguments = null;
+    if (_matches(TokenType.OPEN_PAREN)) {
+      arguments = parseArgumentList();
+    }
+    return new Annotation(atSign, name, period, constructorName, arguments);
+  }
+
+  /**
+   * Parse an argument. Return the argument that was parsed.
+   *
+   *     argument ::=
+   *         namedArgument
+   *       | expression
+   *
+   *     namedArgument ::=
+   *         label expression
+   */
+  Expression parseArgument() {
+    //
+    // Both namedArgument and expression can start with an identifier, but only
+    // namedArgument can have an identifier followed by a colon.
+    //
+    if (_matchesIdentifier() && _tokenMatches(_peek(), TokenType.COLON)) {
+      return new NamedExpression(parseLabel(), parseExpression2());
+    } else {
+      return parseExpression2();
+    }
+  }
+
+  /**
+   * Parse a list of arguments. Return the argument list that was parsed.
+   *
+   *     arguments ::=
+   *         '(' argumentList? ')'
+   *
+   *     argumentList ::=
+   *         namedArgument (',' namedArgument)*
+   *       | expressionList (',' namedArgument)*
+   */
+  ArgumentList parseArgumentList() {
+    Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
+    List<Expression> arguments = new List<Expression>();
+    if (_matches(TokenType.CLOSE_PAREN)) {
+      return new ArgumentList(leftParenthesis, arguments, getAndAdvance());
+    }
+    //
+    // Even though unnamed arguments must all appear before any named arguments,
+    // we allow them to appear in any order so that we can recover faster.
+    //
+    bool wasInInitializer = _inInitializer;
+    _inInitializer = false;
+    try {
+      Expression argument = parseArgument();
+      arguments.add(argument);
+      bool foundNamedArgument = argument is NamedExpression;
+      bool generatedError = false;
+      while (_optional(TokenType.COMMA)) {
+        argument = parseArgument();
+        arguments.add(argument);
+        if (foundNamedArgument) {
+          bool blankArgument =
+              argument is SimpleIdentifier && argument.name.isEmpty;
+          if (!generatedError &&
+              !(argument is NamedExpression && !blankArgument)) {
+            // Report the error, once, but allow the arguments to be in any
+            // order in the AST.
+            _reportErrorForCurrentToken(
+                ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT);
+            generatedError = true;
+          }
+        } else if (argument is NamedExpression) {
+          foundNamedArgument = true;
+        }
+      }
+      // TODO(brianwilkerson) Recovery: Look at the left parenthesis to see
+      // whether there is a matching right parenthesis. If there is, then we're
+      // more likely missing a comma and should go back to parsing arguments.
+      Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+      return new ArgumentList(leftParenthesis, arguments, rightParenthesis);
+    } finally {
+      _inInitializer = wasInInitializer;
+    }
+  }
+
+  /**
+   * Parse a bitwise or expression. Return the bitwise or expression that was
+   * parsed.
+   *
+   *     bitwiseOrExpression ::=
+   *         bitwiseXorExpression ('|' bitwiseXorExpression)*
+   *       | 'super' ('|' bitwiseXorExpression)+
+   */
+  Expression parseBitwiseOrExpression() {
+    Expression expression;
+    if (_matchesKeyword(Keyword.SUPER) &&
+        _tokenMatches(_peek(), TokenType.BAR)) {
+      expression = new SuperExpression(getAndAdvance());
+    } else {
+      expression = _parseBitwiseXorExpression();
+    }
+    while (_matches(TokenType.BAR)) {
+      Token operator = getAndAdvance();
+      expression = new BinaryExpression(
+          expression, operator, _parseBitwiseXorExpression());
+    }
+    return expression;
+  }
+
+  /**
+   * Parse a block. Return the block that was parsed.
+   *
+   *     block ::=
+   *         '{' statements '}'
+   */
+  Block parseBlock() {
+    Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
+    List<Statement> statements = new List<Statement>();
+    Token statementStart = _currentToken;
+    while (
+        !_matches(TokenType.EOF) && !_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+      Statement statement = parseStatement2();
+      if (statement != null) {
+        statements.add(statement);
+      }
+      if (identical(_currentToken, statementStart)) {
+        // Ensure that we are making progress and report an error if we're not.
+        _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
+            [_currentToken.lexeme]);
+        _advance();
+      }
+      statementStart = _currentToken;
+    }
+    Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
+    return new Block(leftBracket, statements, rightBracket);
+  }
+
+  /**
+   * Parse a class member. The [className] is the name of the class containing
+   * the member being parsed. Return the class member that was parsed, or `null`
+   * if what was found was not a valid class member.
+   *
+   *     classMemberDefinition ::=
+   *         declaration ';'
+   *       | methodSignature functionBody
+   */
+  ClassMember parseClassMember(String className) {
+    CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
+    Modifiers modifiers = _parseModifiers();
+    if (_matchesKeyword(Keyword.VOID)) {
+      TypeName returnType = parseReturnType();
+      if (_matchesKeyword(Keyword.GET) && _tokenMatchesIdentifier(_peek())) {
+        _validateModifiersForGetterOrSetterOrMethod(modifiers);
+        return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
+            modifiers.staticKeyword, returnType);
+      } else if (_matchesKeyword(Keyword.SET) &&
+          _tokenMatchesIdentifier(_peek())) {
+        _validateModifiersForGetterOrSetterOrMethod(modifiers);
+        return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
+            modifiers.staticKeyword, returnType);
+      } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
+        _validateModifiersForOperator(modifiers);
+        return _parseOperator(
+            commentAndMetadata, modifiers.externalKeyword, returnType);
+      } else if (_matchesIdentifier() &&
+          _peek().matchesAny([
+        TokenType.OPEN_PAREN,
+        TokenType.OPEN_CURLY_BRACKET,
+        TokenType.FUNCTION
+      ])) {
+        _validateModifiersForGetterOrSetterOrMethod(modifiers);
+        return _parseMethodDeclarationAfterReturnType(commentAndMetadata,
+            modifiers.externalKeyword, modifiers.staticKeyword, returnType);
+      } else {
+        //
+        // We have found an error of some kind. Try to recover.
+        //
+        if (_matchesIdentifier()) {
+          if (_peek().matchesAny(
+              [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
+            //
+            // We appear to have a variable declaration with a type of "void".
+            //
+            _reportErrorForNode(ParserErrorCode.VOID_VARIABLE, returnType);
+            return _parseInitializedIdentifierList(commentAndMetadata,
+                modifiers.staticKeyword, _validateModifiersForField(modifiers),
+                returnType);
+          }
+        }
+        if (_isOperator(_currentToken)) {
+          //
+          // We appear to have found an operator declaration without the
+          // 'operator' keyword.
+          //
+          _validateModifiersForOperator(modifiers);
+          return _parseOperator(
+              commentAndMetadata, modifiers.externalKeyword, returnType);
+        }
+        _reportErrorForToken(
+            ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
+        return null;
+      }
+    } else if (_matchesKeyword(Keyword.GET) &&
+        _tokenMatchesIdentifier(_peek())) {
+      _validateModifiersForGetterOrSetterOrMethod(modifiers);
+      return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
+          modifiers.staticKeyword, null);
+    } else if (_matchesKeyword(Keyword.SET) &&
+        _tokenMatchesIdentifier(_peek())) {
+      _validateModifiersForGetterOrSetterOrMethod(modifiers);
+      return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
+          modifiers.staticKeyword, null);
+    } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
+      _validateModifiersForOperator(modifiers);
+      return _parseOperator(
+          commentAndMetadata, modifiers.externalKeyword, null);
+    } else if (!_matchesIdentifier()) {
+      //
+      // Recover from an error.
+      //
+      if (_matchesKeyword(Keyword.CLASS)) {
+        _reportErrorForCurrentToken(ParserErrorCode.CLASS_IN_CLASS);
+        // TODO(brianwilkerson) We don't currently have any way to capture the
+        // class that was parsed.
+        _parseClassDeclaration(commentAndMetadata, null);
+        return null;
+      } else if (_matchesKeyword(Keyword.ABSTRACT) &&
+          _tokenMatchesKeyword(_peek(), Keyword.CLASS)) {
+        _reportErrorForToken(ParserErrorCode.CLASS_IN_CLASS, _peek());
+        // TODO(brianwilkerson) We don't currently have any way to capture the
+        // class that was parsed.
+        _parseClassDeclaration(commentAndMetadata, getAndAdvance());
+        return null;
+      } else if (_matchesKeyword(Keyword.ENUM)) {
+        _reportErrorForToken(ParserErrorCode.ENUM_IN_CLASS, _peek());
+        // TODO(brianwilkerson) We don't currently have any way to capture the
+        // enum that was parsed.
+        _parseEnumDeclaration(commentAndMetadata);
+        return null;
+      } else if (_isOperator(_currentToken)) {
+        //
+        // We appear to have found an operator declaration without the
+        // 'operator' keyword.
+        //
+        _validateModifiersForOperator(modifiers);
+        return _parseOperator(
+            commentAndMetadata, modifiers.externalKeyword, null);
+      }
+      Token keyword = modifiers.varKeyword;
+      if (keyword == null) {
+        keyword = modifiers.finalKeyword;
+      }
+      if (keyword == null) {
+        keyword = modifiers.constKeyword;
+      }
+      if (keyword != null) {
+        //
+        // We appear to have found an incomplete field declaration.
+        //
+        _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+        List<VariableDeclaration> variables = new List<VariableDeclaration>();
+        variables.add(
+            new VariableDeclaration(_createSyntheticIdentifier(), null, null));
+        return new FieldDeclaration(commentAndMetadata.comment,
+            commentAndMetadata.metadata, null,
+            new VariableDeclarationList(null, null, keyword, null, variables),
+            _expectSemicolon());
+      }
+      _reportErrorForToken(
+          ParserErrorCode.EXPECTED_CLASS_MEMBER, _currentToken);
+      if (commentAndMetadata.comment != null ||
+          !commentAndMetadata.metadata.isEmpty) {
+        //
+        // We appear to have found an incomplete declaration at the end of the
+        // class. At this point it consists of a metadata, which we don't want
+        // to loose, so we'll treat it as a method declaration with a missing
+        // name, parameters and empty body.
+        //
+        return new MethodDeclaration(commentAndMetadata.comment,
+            commentAndMetadata.metadata, null, null, null, null, null,
+            _createSyntheticIdentifier(), new FormalParameterList(
+                null, new List<FormalParameter>(), null, null, null),
+            new EmptyFunctionBody(_createSyntheticToken(TokenType.SEMICOLON)));
+      }
+      return null;
+    } else if (_tokenMatches(_peek(), TokenType.PERIOD) &&
+        _tokenMatchesIdentifier(_peekAt(2)) &&
+        _tokenMatches(_peekAt(3), TokenType.OPEN_PAREN)) {
+      return _parseConstructor(commentAndMetadata, modifiers.externalKeyword,
+          _validateModifiersForConstructor(modifiers), modifiers.factoryKeyword,
+          parseSimpleIdentifier(), getAndAdvance(), parseSimpleIdentifier(),
+          parseFormalParameterList());
+    } else if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+      SimpleIdentifier methodName = parseSimpleIdentifier();
+      FormalParameterList parameters = parseFormalParameterList();
+      if (_matches(TokenType.COLON) ||
+          modifiers.factoryKeyword != null ||
+          methodName.name == className) {
+        return _parseConstructor(commentAndMetadata, modifiers.externalKeyword,
+            _validateModifiersForConstructor(modifiers),
+            modifiers.factoryKeyword, methodName, null, null, parameters);
+      }
+      _validateModifiersForGetterOrSetterOrMethod(modifiers);
+      _validateFormalParameterList(parameters);
+      return _parseMethodDeclarationAfterParameters(commentAndMetadata,
+          modifiers.externalKeyword, modifiers.staticKeyword, null, methodName,
+          parameters);
+    } else if (_peek()
+        .matchesAny([TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
+      if (modifiers.constKeyword == null &&
+          modifiers.finalKeyword == null &&
+          modifiers.varKeyword == null) {
+        _reportErrorForCurrentToken(
+            ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE);
+      }
+      return _parseInitializedIdentifierList(commentAndMetadata,
+          modifiers.staticKeyword, _validateModifiersForField(modifiers), null);
+    } else if (_matchesKeyword(Keyword.TYPEDEF)) {
+      _reportErrorForCurrentToken(ParserErrorCode.TYPEDEF_IN_CLASS);
+      // TODO(brianwilkerson) We don't currently have any way to capture the
+      // function type alias that was parsed.
+      _parseFunctionTypeAlias(commentAndMetadata, getAndAdvance());
+      return null;
+    }
+    TypeName type = parseTypeName();
+    if (_matchesKeyword(Keyword.GET) && _tokenMatchesIdentifier(_peek())) {
+      _validateModifiersForGetterOrSetterOrMethod(modifiers);
+      return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
+          modifiers.staticKeyword, type);
+    } else if (_matchesKeyword(Keyword.SET) &&
+        _tokenMatchesIdentifier(_peek())) {
+      _validateModifiersForGetterOrSetterOrMethod(modifiers);
+      return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
+          modifiers.staticKeyword, type);
+    } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
+      _validateModifiersForOperator(modifiers);
+      return _parseOperator(
+          commentAndMetadata, modifiers.externalKeyword, type);
+    } else if (!_matchesIdentifier()) {
+      if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+        //
+        // We appear to have found an incomplete declaration at the end of the
+        // class. At this point it consists of a type name, so we'll treat it as
+        // a field declaration with a missing field name and semicolon.
+        //
+        return _parseInitializedIdentifierList(commentAndMetadata,
+            modifiers.staticKeyword, _validateModifiersForField(modifiers),
+            type);
+      }
+      if (_isOperator(_currentToken)) {
+        //
+        // We appear to have found an operator declaration without the
+        // 'operator' keyword.
+        //
+        _validateModifiersForOperator(modifiers);
+        return _parseOperator(
+            commentAndMetadata, modifiers.externalKeyword, type);
+      }
+      //
+      // We appear to have found an incomplete declaration before another
+      // declaration. At this point it consists of a type name, so we'll treat
+      // it as a field declaration with a missing field name and semicolon.
+      //
+      _reportErrorForToken(
+          ParserErrorCode.EXPECTED_CLASS_MEMBER, _currentToken);
+      try {
+        _lockErrorListener();
+        return _parseInitializedIdentifierList(commentAndMetadata,
+            modifiers.staticKeyword, _validateModifiersForField(modifiers),
+            type);
+      } finally {
+        _unlockErrorListener();
+      }
+    } else if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+      SimpleIdentifier methodName = parseSimpleIdentifier();
+      FormalParameterList parameters = parseFormalParameterList();
+      if (methodName.name == className) {
+        _reportErrorForNode(ParserErrorCode.CONSTRUCTOR_WITH_RETURN_TYPE, type);
+        return _parseConstructor(commentAndMetadata, modifiers.externalKeyword,
+            _validateModifiersForConstructor(modifiers),
+            modifiers.factoryKeyword, methodName, null, null, parameters);
+      }
+      _validateModifiersForGetterOrSetterOrMethod(modifiers);
+      _validateFormalParameterList(parameters);
+      return _parseMethodDeclarationAfterParameters(commentAndMetadata,
+          modifiers.externalKeyword, modifiers.staticKeyword, type, methodName,
+          parameters);
+    } else if (_tokenMatches(_peek(), TokenType.OPEN_CURLY_BRACKET)) {
+      // We have found "TypeName identifier {", and are guessing that this is a
+      // getter without the keyword 'get'.
+      _validateModifiersForGetterOrSetterOrMethod(modifiers);
+      _reportErrorForCurrentToken(ParserErrorCode.MISSING_GET);
+      _currentToken = _injectToken(
+          new Parser_SyntheticKeywordToken(Keyword.GET, _currentToken.offset));
+      return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
+          modifiers.staticKeyword, type);
+    }
+    return _parseInitializedIdentifierList(commentAndMetadata,
+        modifiers.staticKeyword, _validateModifiersForField(modifiers), type);
+  }
+
+  /**
+   * Parse a single combinator. Return the combinator that was parsed, or `null`
+   * if no combinator is found.
+   *
+   *     combinator ::=
+   *         'show' identifier (',' identifier)*
+   *       | 'hide' identifier (',' identifier)*
+   */
+  Combinator parseCombinator() {
+    if (_matchesString(_SHOW) || _matchesString(_HIDE)) {
+      Token keyword = getAndAdvance();
+      List<SimpleIdentifier> names = _parseIdentifierList();
+      if (keyword.lexeme == _SHOW) {
+        return new ShowCombinator(keyword, names);
+      } else {
+        return new HideCombinator(keyword, names);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Parse a compilation unit, starting with the given [token]. Return the
+   * compilation unit that was parsed.
+   */
+  CompilationUnit parseCompilationUnit(Token token) {
+    _currentToken = token;
+    return parseCompilationUnit2();
+  }
+
+  /**
+   * Parse a compilation unit. Return the compilation unit that was parsed.
+   *
+   * Specified:
+   *
+   *     compilationUnit ::=
+   *         scriptTag? directive* topLevelDeclaration*
+   *
+   * Actual:
+   *
+   *     compilationUnit ::=
+   *         scriptTag? topLevelElement*
+   *
+   *     topLevelElement ::=
+   *         directive
+   *       | topLevelDeclaration
+   */
+  CompilationUnit parseCompilationUnit2() {
+    Token firstToken = _currentToken;
+    ScriptTag scriptTag = null;
+    if (_matches(TokenType.SCRIPT_TAG)) {
+      scriptTag = new ScriptTag(getAndAdvance());
+    }
+    //
+    // Even though all directives must appear before declarations and must occur
+    // in a given order, we allow directives and declarations to occur in any
+    // order so that we can recover better.
+    //
+    bool libraryDirectiveFound = false;
+    bool partOfDirectiveFound = false;
+    bool partDirectiveFound = false;
+    bool directiveFoundAfterDeclaration = false;
+    List<Directive> directives = new List<Directive>();
+    List<CompilationUnitMember> declarations =
+        new List<CompilationUnitMember>();
+    Token memberStart = _currentToken;
+    while (!_matches(TokenType.EOF)) {
+      CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
+      if ((_matchesKeyword(Keyword.IMPORT) ||
+              _matchesKeyword(Keyword.EXPORT) ||
+              _matchesKeyword(Keyword.LIBRARY) ||
+              _matchesKeyword(Keyword.PART)) &&
+          !_tokenMatches(_peek(), TokenType.PERIOD) &&
+          !_tokenMatches(_peek(), TokenType.LT) &&
+          !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+        Directive directive = _parseDirective(commentAndMetadata);
+        if (declarations.length > 0 && !directiveFoundAfterDeclaration) {
+          _reportErrorForToken(ParserErrorCode.DIRECTIVE_AFTER_DECLARATION,
+              directive.beginToken);
+          directiveFoundAfterDeclaration = true;
+        }
+        if (directive is LibraryDirective) {
+          if (libraryDirectiveFound) {
+            _reportErrorForCurrentToken(
+                ParserErrorCode.MULTIPLE_LIBRARY_DIRECTIVES);
+          } else {
+            if (directives.length > 0) {
+              _reportErrorForToken(ParserErrorCode.LIBRARY_DIRECTIVE_NOT_FIRST,
+                  directive.libraryKeyword);
+            }
+            libraryDirectiveFound = true;
+          }
+        } else if (directive is PartDirective) {
+          partDirectiveFound = true;
+        } else if (partDirectiveFound) {
+          if (directive is ExportDirective) {
+            _reportErrorForToken(
+                ParserErrorCode.EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE,
+                directive.keyword);
+          } else if (directive is ImportDirective) {
+            _reportErrorForToken(
+                ParserErrorCode.IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE,
+                directive.keyword);
+          }
+        }
+        if (directive is PartOfDirective) {
+          if (partOfDirectiveFound) {
+            _reportErrorForCurrentToken(
+                ParserErrorCode.MULTIPLE_PART_OF_DIRECTIVES);
+          } else {
+            int directiveCount = directives.length;
+            for (int i = 0; i < directiveCount; i++) {
+              _reportErrorForToken(
+                  ParserErrorCode.NON_PART_OF_DIRECTIVE_IN_PART,
+                  directives[i].keyword);
+            }
+            partOfDirectiveFound = true;
+          }
+        } else {
+          if (partOfDirectiveFound) {
+            _reportErrorForToken(ParserErrorCode.NON_PART_OF_DIRECTIVE_IN_PART,
+                directive.keyword);
+          }
+        }
+        directives.add(directive);
+      } else if (_matches(TokenType.SEMICOLON)) {
+        _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
+            [_currentToken.lexeme]);
+        _advance();
+      } else {
+        CompilationUnitMember member =
+            _parseCompilationUnitMember(commentAndMetadata);
+        if (member != null) {
+          declarations.add(member);
+        }
+      }
+      if (identical(_currentToken, memberStart)) {
+        _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
+            [_currentToken.lexeme]);
+        _advance();
+        while (!_matches(TokenType.EOF) &&
+            !_couldBeStartOfCompilationUnitMember()) {
+          _advance();
+        }
+      }
+      memberStart = _currentToken;
+    }
+    return new CompilationUnit(
+        firstToken, scriptTag, directives, declarations, _currentToken);
+  }
+
+  /**
+   * Parse a conditional expression. Return the conditional expression that was
+   * parsed.
+   *
+   *     conditionalExpression ::=
+   *         ifNullExpression ('?' expressionWithoutCascade ':' expressionWithoutCascade)?
+   */
+  Expression parseConditionalExpression() {
+    Expression condition = parseIfNullExpression();
+    if (!_matches(TokenType.QUESTION)) {
+      return condition;
+    }
+    Token question = getAndAdvance();
+    Expression thenExpression = parseExpressionWithoutCascade();
+    Token colon = _expect(TokenType.COLON);
+    Expression elseExpression = parseExpressionWithoutCascade();
+    return new ConditionalExpression(
+        condition, question, thenExpression, colon, elseExpression);
+  }
+
+  /**
+   * Parse the name of a constructor. Return the constructor name that was
+   * parsed.
+   *
+   *     constructorName:
+   *         type ('.' identifier)?
+   */
+  ConstructorName parseConstructorName() {
+    TypeName type = parseTypeName();
+    Token period = null;
+    SimpleIdentifier name = null;
+    if (_matches(TokenType.PERIOD)) {
+      period = getAndAdvance();
+      name = parseSimpleIdentifier();
+    }
+    return new ConstructorName(type, period, name);
+  }
+
+  /**
+   * Parse the script tag and directives in a compilation unit, starting with
+   * the given [token], until the first non-directive is encountered. The
+   * remainder of the compilation unit will not be parsed. Specifically, if
+   * there are directives later in the file, they will not be parsed. Return the
+   * compilation unit that was parsed.
+   */
+  CompilationUnit parseDirectives(Token token) {
+    _currentToken = token;
+    return _parseDirectives();
+  }
+
+  /**
+   * Parse an expression, starting with the given [token]. Return the expression
+   * that was parsed, or `null` if the tokens do not represent a recognizable
+   * expression.
+   */
+  Expression parseExpression(Token token) {
+    _currentToken = token;
+    return parseExpression2();
+  }
+
+  /**
+   * Parse an expression that might contain a cascade. Return the expression
+   * that was parsed.
+   *
+   *     expression ::=
+   *         assignableExpression assignmentOperator expression
+   *       | conditionalExpression cascadeSection*
+   *       | throwExpression
+   */
+  Expression parseExpression2() {
+    if (_matchesKeyword(Keyword.THROW)) {
+      return _parseThrowExpression();
+    } else if (_matchesKeyword(Keyword.RETHROW)) {
+      // TODO(brianwilkerson) Rethrow is a statement again.
+      return _parseRethrowExpression();
+    }
+    //
+    // assignableExpression is a subset of conditionalExpression, so we can
+    // parse a conditional expression and then determine whether it is followed
+    // by an assignmentOperator, checking for conformance to the restricted
+    // grammar after making that determination.
+    //
+    Expression expression = parseConditionalExpression();
+    TokenType tokenType = _currentToken.type;
+    if (tokenType == TokenType.PERIOD_PERIOD) {
+      List<Expression> cascadeSections = new List<Expression>();
+      while (tokenType == TokenType.PERIOD_PERIOD) {
+        Expression section = _parseCascadeSection();
+        if (section != null) {
+          cascadeSections.add(section);
+        }
+        tokenType = _currentToken.type;
+      }
+      return new CascadeExpression(expression, cascadeSections);
+    } else if (tokenType.isAssignmentOperator) {
+      Token operator = getAndAdvance();
+      _ensureAssignable(expression);
+      return new AssignmentExpression(expression, operator, parseExpression2());
+    }
+    return expression;
+  }
+
+  /**
+   * Parse an expression that does not contain any cascades. Return the
+   * expression that was parsed.
+   *
+   *     expressionWithoutCascade ::=
+   *         assignableExpression assignmentOperator expressionWithoutCascade
+   *       | conditionalExpression
+   *       | throwExpressionWithoutCascade
+   */
+  Expression parseExpressionWithoutCascade() {
+    if (_matchesKeyword(Keyword.THROW)) {
+      return _parseThrowExpressionWithoutCascade();
+    } else if (_matchesKeyword(Keyword.RETHROW)) {
+      return _parseRethrowExpression();
+    }
+    //
+    // assignableExpression is a subset of conditionalExpression, so we can
+    // parse a conditional expression and then determine whether it is followed
+    // by an assignmentOperator, checking for conformance to the restricted
+    // grammar after making that determination.
+    //
+    Expression expression = parseConditionalExpression();
+    if (_currentToken.type.isAssignmentOperator) {
+      Token operator = getAndAdvance();
+      _ensureAssignable(expression);
+      expression = new AssignmentExpression(
+          expression, operator, parseExpressionWithoutCascade());
+    }
+    return expression;
+  }
+
+  /**
+   * Parse a class extends clause. Return the class extends clause that was
+   * parsed.
+   *
+   *     classExtendsClause ::=
+   *         'extends' type
+   */
+  ExtendsClause parseExtendsClause() {
+    Token keyword = _expectKeyword(Keyword.EXTENDS);
+    TypeName superclass = parseTypeName();
+    return new ExtendsClause(keyword, superclass);
+  }
+
+  /**
+   * Parse a list of formal parameters. Return the formal parameters that were
+   * parsed.
+   *
+   *     formalParameterList ::=
+   *         '(' ')'
+   *       | '(' normalFormalParameters (',' optionalFormalParameters)? ')'
+   *       | '(' optionalFormalParameters ')'
+   *
+   *     normalFormalParameters ::=
+   *         normalFormalParameter (',' normalFormalParameter)*
+   *
+   *     optionalFormalParameters ::=
+   *         optionalPositionalFormalParameters
+   *       | namedFormalParameters
+   *
+   *     optionalPositionalFormalParameters ::=
+   *         '[' defaultFormalParameter (',' defaultFormalParameter)* ']'
+   *
+   *     namedFormalParameters ::=
+   *         '{' defaultNamedParameter (',' defaultNamedParameter)* '}'
+   */
+  FormalParameterList parseFormalParameterList() {
+    Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
+    if (_matches(TokenType.CLOSE_PAREN)) {
+      return new FormalParameterList(
+          leftParenthesis, null, null, null, getAndAdvance());
+    }
+    //
+    // Even though it is invalid to have default parameters outside of brackets,
+    // required parameters inside of brackets, or multiple groups of default and
+    // named parameters, we allow all of these cases so that we can recover
+    // better.
+    //
+    List<FormalParameter> parameters = new List<FormalParameter>();
+    List<FormalParameter> normalParameters = new List<FormalParameter>();
+    List<FormalParameter> positionalParameters = new List<FormalParameter>();
+    List<FormalParameter> namedParameters = new List<FormalParameter>();
+    List<FormalParameter> currentParameters = normalParameters;
+    Token leftSquareBracket = null;
+    Token rightSquareBracket = null;
+    Token leftCurlyBracket = null;
+    Token rightCurlyBracket = null;
+    ParameterKind kind = ParameterKind.REQUIRED;
+    bool firstParameter = true;
+    bool reportedMuliplePositionalGroups = false;
+    bool reportedMulipleNamedGroups = false;
+    bool reportedMixedGroups = false;
+    bool wasOptionalParameter = false;
+    Token initialToken = null;
+    do {
+      if (firstParameter) {
+        firstParameter = false;
+      } else if (!_optional(TokenType.COMMA)) {
+        // TODO(brianwilkerson) The token is wrong, we need to recover from this
+        // case.
+        if (_getEndToken(leftParenthesis) != null) {
+          _reportErrorForCurrentToken(
+              ParserErrorCode.EXPECTED_TOKEN, [TokenType.COMMA.lexeme]);
+        } else {
+          _reportErrorForToken(ParserErrorCode.MISSING_CLOSING_PARENTHESIS,
+              _currentToken.previous);
+          break;
+        }
+      }
+      initialToken = _currentToken;
+      //
+      // Handle the beginning of parameter groups.
+      //
+      if (_matches(TokenType.OPEN_SQUARE_BRACKET)) {
+        wasOptionalParameter = true;
+        if (leftSquareBracket != null && !reportedMuliplePositionalGroups) {
+          _reportErrorForCurrentToken(
+              ParserErrorCode.MULTIPLE_POSITIONAL_PARAMETER_GROUPS);
+          reportedMuliplePositionalGroups = true;
+        }
+        if (leftCurlyBracket != null && !reportedMixedGroups) {
+          _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS);
+          reportedMixedGroups = true;
+        }
+        leftSquareBracket = getAndAdvance();
+        currentParameters = positionalParameters;
+        kind = ParameterKind.POSITIONAL;
+      } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+        wasOptionalParameter = true;
+        if (leftCurlyBracket != null && !reportedMulipleNamedGroups) {
+          _reportErrorForCurrentToken(
+              ParserErrorCode.MULTIPLE_NAMED_PARAMETER_GROUPS);
+          reportedMulipleNamedGroups = true;
+        }
+        if (leftSquareBracket != null && !reportedMixedGroups) {
+          _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS);
+          reportedMixedGroups = true;
+        }
+        leftCurlyBracket = getAndAdvance();
+        currentParameters = namedParameters;
+        kind = ParameterKind.NAMED;
+      }
+      //
+      // Parse and record the parameter.
+      //
+      FormalParameter parameter = _parseFormalParameter(kind);
+      parameters.add(parameter);
+      currentParameters.add(parameter);
+      if (kind == ParameterKind.REQUIRED && wasOptionalParameter) {
+        _reportErrorForNode(
+            ParserErrorCode.NORMAL_BEFORE_OPTIONAL_PARAMETERS, parameter);
+      }
+      //
+      // Handle the end of parameter groups.
+      //
+      // TODO(brianwilkerson) Improve the detection and reporting of missing and
+      // mismatched delimiters.
+      if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
+        rightSquareBracket = getAndAdvance();
+        currentParameters = normalParameters;
+        if (leftSquareBracket == null) {
+          if (leftCurlyBracket != null) {
+            _reportErrorForCurrentToken(
+                ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
+            rightCurlyBracket = rightSquareBracket;
+            rightSquareBracket = null;
+          } else {
+            _reportErrorForCurrentToken(
+                ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP,
+                ["["]);
+          }
+        }
+        kind = ParameterKind.REQUIRED;
+      } else if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+        rightCurlyBracket = getAndAdvance();
+        currentParameters = normalParameters;
+        if (leftCurlyBracket == null) {
+          if (leftSquareBracket != null) {
+            _reportErrorForCurrentToken(
+                ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
+            rightSquareBracket = rightCurlyBracket;
+            rightCurlyBracket = null;
+          } else {
+            _reportErrorForCurrentToken(
+                ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP,
+                ["{"]);
+          }
+        }
+        kind = ParameterKind.REQUIRED;
+      }
+    } while (!_matches(TokenType.CLOSE_PAREN) &&
+        !identical(initialToken, _currentToken));
+    Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+    //
+    // Check that the groups were closed correctly.
+    //
+    if (leftSquareBracket != null && rightSquareBracket == null) {
+      _reportErrorForCurrentToken(
+          ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
+    }
+    if (leftCurlyBracket != null && rightCurlyBracket == null) {
+      _reportErrorForCurrentToken(
+          ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
+    }
+    //
+    // Build the parameter list.
+    //
+    if (leftSquareBracket == null) {
+      leftSquareBracket = leftCurlyBracket;
+    }
+    if (rightSquareBracket == null) {
+      rightSquareBracket = rightCurlyBracket;
+    }
+    return new FormalParameterList(leftParenthesis, parameters,
+        leftSquareBracket, rightSquareBracket, rightParenthesis);
+  }
+
+  /**
+   * Parse a function expression. Return the function expression that was
+   * parsed.
+   *
+   *     functionExpression ::=
+   *         formalParameterList functionExpressionBody
+   */
+  FunctionExpression parseFunctionExpression() {
+    FormalParameterList parameters = parseFormalParameterList();
+    _validateFormalParameterList(parameters);
+    FunctionBody body =
+        _parseFunctionBody(false, ParserErrorCode.MISSING_FUNCTION_BODY, true);
+    return new FunctionExpression(parameters, body);
+  }
+
+  /**
+   * Parse an if-null expression.  Return the if-null expression that was
+   * parsed.
+   *
+   *     ifNullExpression ::= logicalOrExpression ('??' logicalOrExpression)*
+   */
+  Expression parseIfNullExpression() {
+    Expression expression = parseLogicalOrExpression();
+    while (_matches(TokenType.QUESTION_QUESTION)) {
+      Token operator = getAndAdvance();
+      expression = new BinaryExpression(
+          expression, operator, parseLogicalOrExpression());
+    }
+    return expression;
+  }
+
+  /**
+   * Parse an implements clause. Return the implements clause that was parsed.
+   *
+   *     implementsClause ::=
+   *         'implements' type (',' type)*
+   */
+  ImplementsClause parseImplementsClause() {
+    Token keyword = _expectKeyword(Keyword.IMPLEMENTS);
+    List<TypeName> interfaces = new List<TypeName>();
+    interfaces.add(parseTypeName());
+    while (_optional(TokenType.COMMA)) {
+      interfaces.add(parseTypeName());
+    }
+    return new ImplementsClause(keyword, interfaces);
+  }
+
+  /**
+   * Parse a label. Return the label that was parsed.
+   *
+   *     label ::=
+   *         identifier ':'
+   */
+  Label parseLabel() {
+    SimpleIdentifier label = parseSimpleIdentifier();
+    Token colon = _expect(TokenType.COLON);
+    return new Label(label, colon);
+  }
+
+  /**
+   * Parse a library identifier. Return the library identifier that was parsed.
+   *
+   *     libraryIdentifier ::=
+   *         identifier ('.' identifier)*
+   */
+  LibraryIdentifier parseLibraryIdentifier() {
+    List<SimpleIdentifier> components = new List<SimpleIdentifier>();
+    components.add(parseSimpleIdentifier());
+    while (_matches(TokenType.PERIOD)) {
+      _advance();
+      components.add(parseSimpleIdentifier());
+    }
+    return new LibraryIdentifier(components);
+  }
+
+  /**
+   * Parse a logical or expression. Return the logical or expression that was
+   * parsed.
+   *
+   *     logicalOrExpression ::=
+   *         logicalAndExpression ('||' logicalAndExpression)*
+   */
+  Expression parseLogicalOrExpression() {
+    Expression expression = _parseLogicalAndExpression();
+    while (_matches(TokenType.BAR_BAR)) {
+      Token operator = getAndAdvance();
+      expression = new BinaryExpression(
+          expression, operator, _parseLogicalAndExpression());
+    }
+    return expression;
+  }
+
+  /**
+   * Parse a map literal entry. Return the map literal entry that was parsed.
+   *
+   *     mapLiteralEntry ::=
+   *         expression ':' expression
+   */
+  MapLiteralEntry parseMapLiteralEntry() {
+    Expression key = parseExpression2();
+    Token separator = _expect(TokenType.COLON);
+    Expression value = parseExpression2();
+    return new MapLiteralEntry(key, separator, value);
+  }
+
+  /**
+   * Parse a normal formal parameter. Return the normal formal parameter that
+   * was parsed.
+   *
+   *     normalFormalParameter ::=
+   *         functionSignature
+   *       | fieldFormalParameter
+   *       | simpleFormalParameter
+   *
+   *     functionSignature:
+   *         metadata returnType? identifier formalParameterList
+   *
+   *     fieldFormalParameter ::=
+   *         metadata finalConstVarOrType? 'this' '.' identifier
+   *
+   *     simpleFormalParameter ::=
+   *         declaredIdentifier
+   *       | metadata identifier
+   */
+  NormalFormalParameter parseNormalFormalParameter() {
+    CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
+    FinalConstVarOrType holder = _parseFinalConstVarOrType(true);
+    Token thisKeyword = null;
+    Token period = null;
+    if (_matchesKeyword(Keyword.THIS)) {
+      thisKeyword = getAndAdvance();
+      period = _expect(TokenType.PERIOD);
+    }
+    SimpleIdentifier identifier = parseSimpleIdentifier();
+    if (_matches(TokenType.OPEN_PAREN)) {
+      FormalParameterList parameters = parseFormalParameterList();
+      if (thisKeyword == null) {
+        if (holder.keyword != null) {
+          _reportErrorForToken(
+              ParserErrorCode.FUNCTION_TYPED_PARAMETER_VAR, holder.keyword);
+        }
+        return new FunctionTypedFormalParameter(commentAndMetadata.comment,
+            commentAndMetadata.metadata, holder.type, identifier, parameters);
+      } else {
+        return new FieldFormalParameter(commentAndMetadata.comment,
+            commentAndMetadata.metadata, holder.keyword, holder.type,
+            thisKeyword, period, identifier, parameters);
+      }
+    }
+    TypeName type = holder.type;
+    if (type != null) {
+      if (_tokenMatchesKeyword(type.name.beginToken, Keyword.VOID)) {
+        _reportErrorForToken(
+            ParserErrorCode.VOID_PARAMETER, type.name.beginToken);
+      } else if (holder.keyword != null &&
+          _tokenMatchesKeyword(holder.keyword, Keyword.VAR)) {
+        _reportErrorForToken(ParserErrorCode.VAR_AND_TYPE, holder.keyword);
+      }
+    }
+    if (thisKeyword != null) {
+      return new FieldFormalParameter(commentAndMetadata.comment,
+          commentAndMetadata.metadata, holder.keyword, holder.type, thisKeyword,
+          period, identifier, null);
+    }
+    return new SimpleFormalParameter(commentAndMetadata.comment,
+        commentAndMetadata.metadata, holder.keyword, holder.type, identifier);
+  }
+
+  /**
+   * Parse a prefixed identifier. Return the prefixed identifier that was
+   * parsed.
+   *
+   *     prefixedIdentifier ::=
+   *         identifier ('.' identifier)?
+   */
+  Identifier parsePrefixedIdentifier() {
+    SimpleIdentifier qualifier = parseSimpleIdentifier();
+    if (!_matches(TokenType.PERIOD)) {
+      return qualifier;
+    }
+    Token period = getAndAdvance();
+    SimpleIdentifier qualified = parseSimpleIdentifier();
+    return new PrefixedIdentifier(qualifier, period, qualified);
+  }
+
+  /**
+   * Parse a return type. Return the return type that was parsed.
+   *
+   *     returnType ::=
+   *         'void'
+   *       | type
+   */
+  TypeName parseReturnType() {
+    if (_matchesKeyword(Keyword.VOID)) {
+      return new TypeName(new SimpleIdentifier(getAndAdvance()), null);
+    } else {
+      return parseTypeName();
+    }
+  }
+
+  /**
+   * Parse a simple identifier. Return the simple identifier that was parsed.
+   *
+   *     identifier ::=
+   *         IDENTIFIER
+   */
+  SimpleIdentifier parseSimpleIdentifier() {
+    if (_matchesIdentifier()) {
+      String lexeme = _currentToken.lexeme;
+      if ((_inAsync || _inGenerator) &&
+          (lexeme == 'async' || lexeme == 'await' || lexeme == 'yield')) {
+        _reportErrorForCurrentToken(
+            ParserErrorCode.ASYNC_KEYWORD_USED_AS_IDENTIFIER);
+      }
+      return new SimpleIdentifier(getAndAdvance());
+    }
+    _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+    return _createSyntheticIdentifier();
+  }
+
+  /**
+   * Parse a statement, starting with the given [token]. Return the statement
+   * that was parsed, or `null` if the tokens do not represent a recognizable
+   * statement.
+   */
+  Statement parseStatement(Token token) {
+    _currentToken = token;
+    return parseStatement2();
+  }
+
+  /**
+   * Parse a statement. Return the statement that was parsed.
+   *
+   *     statement ::=
+   *         label* nonLabeledStatement
+   */
+  Statement parseStatement2() {
+    List<Label> labels = new List<Label>();
+    while (_matchesIdentifier() && _tokenMatches(_peek(), TokenType.COLON)) {
+      labels.add(parseLabel());
+    }
+    Statement statement = _parseNonLabeledStatement();
+    if (labels.isEmpty) {
+      return statement;
+    }
+    return new LabeledStatement(labels, statement);
+  }
+
+  /**
+   * Parse a sequence of statements, starting with the given [token]. Return the
+   * statements that were parsed, or `null` if the tokens do not represent a
+   * recognizable sequence of statements.
+   */
+  List<Statement> parseStatements(Token token) {
+    _currentToken = token;
+    return _parseStatementList();
+  }
+
+  /**
+   * Parse a string literal. Return the string literal that was parsed.
+   *
+   *     stringLiteral ::=
+   *         MULTI_LINE_STRING+
+   *       | SINGLE_LINE_STRING+
+   */
+  StringLiteral parseStringLiteral() {
+    List<StringLiteral> strings = new List<StringLiteral>();
+    while (_matches(TokenType.STRING)) {
+      Token string = getAndAdvance();
+      if (_matches(TokenType.STRING_INTERPOLATION_EXPRESSION) ||
+          _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER)) {
+        strings.add(_parseStringInterpolation(string));
+      } else {
+        strings.add(new SimpleStringLiteral(
+            string, _computeStringValue(string.lexeme, true, true)));
+      }
+    }
+    if (strings.length < 1) {
+      _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_STRING_LITERAL);
+      return _createSyntheticStringLiteral();
+    } else if (strings.length == 1) {
+      return strings[0];
+    } else {
+      return new AdjacentStrings(strings);
+    }
+  }
+
+  /**
+   * Parse a list of type arguments. Return the type argument list that was
+   * parsed.
+   *
+   *     typeArguments ::=
+   *         '<' typeList '>'
+   *
+   *     typeList ::=
+   *         type (',' type)*
+   */
+  TypeArgumentList parseTypeArgumentList() {
+    Token leftBracket = _expect(TokenType.LT);
+    List<TypeName> arguments = new List<TypeName>();
+    arguments.add(parseTypeName());
+    while (_optional(TokenType.COMMA)) {
+      arguments.add(parseTypeName());
+    }
+    Token rightBracket = _expectGt();
+    return new TypeArgumentList(leftBracket, arguments, rightBracket);
+  }
+
+  /**
+   * Parse a type name. Return the type name that was parsed.
+   *
+   *     type ::=
+   *         qualified typeArguments?
+   */
+  TypeName parseTypeName() {
+    Identifier typeName;
+    if (_matchesKeyword(Keyword.VAR)) {
+      _reportErrorForCurrentToken(ParserErrorCode.VAR_AS_TYPE_NAME);
+      typeName = new SimpleIdentifier(getAndAdvance());
+    } else if (_matchesIdentifier()) {
+      typeName = parsePrefixedIdentifier();
+    } else {
+      typeName = _createSyntheticIdentifier();
+      _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TYPE_NAME);
+    }
+    TypeArgumentList typeArguments = null;
+    if (_matches(TokenType.LT)) {
+      typeArguments = parseTypeArgumentList();
+    }
+    return new TypeName(typeName, typeArguments);
+  }
+
+  /**
+   * Parse a type parameter. Return the type parameter that was parsed.
+   *
+   *     typeParameter ::=
+   *         metadata name ('extends' bound)?
+   */
+  TypeParameter parseTypeParameter() {
+    CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
+    SimpleIdentifier name = parseSimpleIdentifier();
+    if (_matchesKeyword(Keyword.EXTENDS)) {
+      Token keyword = getAndAdvance();
+      TypeName bound = parseTypeName();
+      return new TypeParameter(commentAndMetadata.comment,
+          commentAndMetadata.metadata, name, keyword, bound);
+    }
+    return new TypeParameter(commentAndMetadata.comment,
+        commentAndMetadata.metadata, name, null, null);
+  }
+
+  /**
+   * Parse a list of type parameters. Return the list of type parameters that
+   * were parsed.
+   *
+   *     typeParameterList ::=
+   *         '<' typeParameter (',' typeParameter)* '>'
+   */
+  TypeParameterList parseTypeParameterList() {
+    Token leftBracket = _expect(TokenType.LT);
+    List<TypeParameter> typeParameters = new List<TypeParameter>();
+    typeParameters.add(parseTypeParameter());
+    while (_optional(TokenType.COMMA)) {
+      typeParameters.add(parseTypeParameter());
+    }
+    Token rightBracket = _expectGt();
+    return new TypeParameterList(leftBracket, typeParameters, rightBracket);
+  }
+
+  /**
+   * Parse a with clause. Return the with clause that was parsed.
+   *
+   *     withClause ::=
+   *         'with' typeName (',' typeName)*
+   */
+  WithClause parseWithClause() {
+    Token with2 = _expectKeyword(Keyword.WITH);
+    List<TypeName> types = new List<TypeName>();
+    types.add(parseTypeName());
+    while (_optional(TokenType.COMMA)) {
+      types.add(parseTypeName());
+    }
+    return new WithClause(with2, types);
+  }
+
+  /**
+   * Advance to the next token in the token stream.
+   */
+  void _advance() {
+    _currentToken = _currentToken.next;
+  }
+
+  /**
+   * Append the character equivalent of the given [scalarValue] to the given
+   * [builder]. Use the [startIndex] and [endIndex] to report an error, and
+   * don't append anything to the builder, if the scalar value is invalid. The
+   * [escapeSequence] is the escape sequence that was parsed to produce the
+   * scalar value (used for error reporting).
+   */
+  void _appendScalarValue(StringBuffer buffer, String escapeSequence,
+      int scalarValue, int startIndex, int endIndex) {
+    if (scalarValue < 0 ||
+        scalarValue > Character.MAX_CODE_POINT ||
+        (scalarValue >= 0xD800 && scalarValue <= 0xDFFF)) {
+      _reportErrorForCurrentToken(
+          ParserErrorCode.INVALID_CODE_POINT, [escapeSequence]);
+      return;
+    }
+    if (scalarValue < Character.MAX_VALUE) {
+      buffer.writeCharCode(scalarValue);
+    } else {
+      buffer.write(Character.toChars(scalarValue));
+    }
+  }
+
+  /**
+   * Return the content of a string with the given literal representation. The
+   * [lexeme] is the literal representation of the string. The flag [isFirst] is
+   * `true` if this is the first token in a string literal. The flag [isLast] is
+   * `true` if this is the last token in a string literal.
+   */
+  String _computeStringValue(String lexeme, bool isFirst, bool isLast) {
+    StringLexemeHelper helper = new StringLexemeHelper(lexeme, isFirst, isLast);
+    int start = helper.start;
+    int end = helper.end;
+    bool stringEndsAfterStart = end >= start;
+    assert(stringEndsAfterStart);
+    if (!stringEndsAfterStart) {
+      AnalysisEngine.instance.logger.logError(
+          "Internal error: computeStringValue($lexeme, $isFirst, $isLast)");
+      return "";
+    }
+    if (helper.isRaw) {
+      return lexeme.substring(start, end);
+    }
+    StringBuffer buffer = new StringBuffer();
+    int index = start;
+    while (index < end) {
+      index = _translateCharacter(buffer, lexeme, index);
+    }
+    return buffer.toString();
+  }
+
+  /**
+   * Convert the given [method] declaration into the nearest valid top-level
+   * function declaration (that is, the function declaration that most closely
+   * captures the components of the given method declaration).
+   */
+  FunctionDeclaration _convertToFunctionDeclaration(MethodDeclaration method) =>
+      new FunctionDeclaration(method.documentationComment, method.metadata,
+          method.externalKeyword, method.returnType, method.propertyKeyword,
+          method.name, new FunctionExpression(method.parameters, method.body));
+
+  /**
+   * Return `true` if the current token could be the start of a compilation unit
+   * member. This method is used for recovery purposes to decide when to stop
+   * skipping tokens after finding an error while parsing a compilation unit
+   * member.
+   */
+  bool _couldBeStartOfCompilationUnitMember() {
+    if ((_matchesKeyword(Keyword.IMPORT) ||
+            _matchesKeyword(Keyword.EXPORT) ||
+            _matchesKeyword(Keyword.LIBRARY) ||
+            _matchesKeyword(Keyword.PART)) &&
+        !_tokenMatches(_peek(), TokenType.PERIOD) &&
+        !_tokenMatches(_peek(), TokenType.LT)) {
+      // This looks like the start of a directive
+      return true;
+    } else if (_matchesKeyword(Keyword.CLASS)) {
+      // This looks like the start of a class definition
+      return true;
+    } else if (_matchesKeyword(Keyword.TYPEDEF) &&
+        !_tokenMatches(_peek(), TokenType.PERIOD) &&
+        !_tokenMatches(_peek(), TokenType.LT)) {
+      // This looks like the start of a typedef
+      return true;
+    } else if (_matchesKeyword(Keyword.VOID) ||
+        ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) &&
+            _tokenMatchesIdentifier(_peek())) ||
+        (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek()))) {
+      // This looks like the start of a function
+      return true;
+    } else if (_matchesIdentifier()) {
+      if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+        // This looks like the start of a function
+        return true;
+      }
+      Token token = _skipReturnType(_currentToken);
+      if (token == null) {
+        return false;
+      }
+      if (_matchesKeyword(Keyword.GET) ||
+          _matchesKeyword(Keyword.SET) ||
+          (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) ||
+          _matchesIdentifier()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Return a synthetic identifier.
+   */
+  SimpleIdentifier _createSyntheticIdentifier() {
+    Token syntheticToken;
+    if (_currentToken.type == TokenType.KEYWORD) {
+      // Consider current keyword token as an identifier.
+      // It is not always true, e.g. "^is T" where "^" is place the place for
+      // synthetic identifier. By creating SyntheticStringToken we can
+      // distinguish a real identifier from synthetic. In the code completion
+      // behavior will depend on a cursor position - before or on "is".
+      syntheticToken = _injectToken(new SyntheticStringToken(
+          TokenType.IDENTIFIER, _currentToken.lexeme, _currentToken.offset));
+    } else {
+      syntheticToken = _createSyntheticToken(TokenType.IDENTIFIER);
+    }
+    return new SimpleIdentifier(syntheticToken);
+  }
+
+  /**
+   * Return a synthetic token representing the given [keyword].
+   */
+  Token _createSyntheticKeyword(Keyword keyword) => _injectToken(
+      new Parser_SyntheticKeywordToken(keyword, _currentToken.offset));
+
+  /**
+   * Return a synthetic string literal.
+   */
+  SimpleStringLiteral _createSyntheticStringLiteral() =>
+      new SimpleStringLiteral(_createSyntheticToken(TokenType.STRING), "");
+
+  /**
+   * Return a synthetic token with the given [type].
+   */
+  Token _createSyntheticToken(TokenType type) =>
+      _injectToken(new StringToken(type, "", _currentToken.offset));
+
+  /**
+   * Create and return a new token with the given [type]. The token will replace
+   * the first portion of the given [token], so it will have the same offset and
+   * will have any comments that might have preceeded the token.
+   */
+  Token _createToken(Token token, TokenType type, {bool isBegin: false}) {
+    CommentToken comments = token.precedingComments;
+    if (comments == null) {
+      if (isBegin) {
+        return new BeginToken(type, token.offset);
+      }
+      return new Token(type, token.offset);
+    } else if (isBegin) {
+      return new BeginTokenWithComment(type, token.offset, comments);
+    }
+    return new TokenWithComment(type, token.offset, comments);
+  }
+
+  /**
+   * Check that the given [expression] is assignable and report an error if it
+   * isn't.
+   *
+   *     assignableExpression ::=
+   *         primary (arguments* assignableSelector)+
+   *       | 'super' unconditionalAssignableSelector
+   *       | identifier
+   *
+   *     unconditionalAssignableSelector ::=
+   *         '[' expression ']'
+   *       | '.' identifier
+   *
+   *     assignableSelector ::=
+   *         unconditionalAssignableSelector
+   *       | '?.' identifier
+   */
+  void _ensureAssignable(Expression expression) {
+    if (expression != null && !expression.isAssignable) {
+      _reportErrorForCurrentToken(
+          ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE);
+    }
+  }
+
+  /**
+   * If the current token has the expected type, return it after advancing to
+   * the next token. Otherwise report an error and return the current token
+   * without advancing.
+   *
+   * Note that the method [_expectGt] should be used if the argument to this
+   * method would be [TokenType.GT].
+   *
+   * The [type] is the type of token that is expected.
+   */
+  Token _expect(TokenType type) {
+    if (_matches(type)) {
+      return getAndAdvance();
+    }
+    // Remove uses of this method in favor of matches?
+    // Pass in the error code to use to report the error?
+    if (type == TokenType.SEMICOLON) {
+      if (_tokenMatches(_currentToken.next, TokenType.SEMICOLON)) {
+        _reportErrorForCurrentToken(
+            ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
+        _advance();
+        return getAndAdvance();
+      }
+      _reportErrorForToken(ParserErrorCode.EXPECTED_TOKEN,
+          _currentToken.previous, [type.lexeme]);
+    } else {
+      _reportErrorForCurrentToken(
+          ParserErrorCode.EXPECTED_TOKEN, [type.lexeme]);
+    }
+    return _currentToken;
+  }
+
+  /**
+   * If the current token has the type [TokenType.GT], return it after advancing
+   * to the next token. Otherwise report an error and return the current token
+   * without advancing.
+   */
+  Token _expectGt() {
+    if (_matchesGt()) {
+      return getAndAdvance();
+    }
+    _reportErrorForCurrentToken(
+        ParserErrorCode.EXPECTED_TOKEN, [TokenType.GT.lexeme]);
+    return _currentToken;
+  }
+
+  /**
+   * If the current token is a keyword matching the given [keyword], return it
+   * after advancing to the next token. Otherwise report an error and return the
+   * current token without advancing.
+   */
+  Token _expectKeyword(Keyword keyword) {
+    if (_matchesKeyword(keyword)) {
+      return getAndAdvance();
+    }
+    // Remove uses of this method in favor of matches?
+    // Pass in the error code to use to report the error?
+    _reportErrorForCurrentToken(
+        ParserErrorCode.EXPECTED_TOKEN, [keyword.syntax]);
+    return _currentToken;
+  }
+
+  /**
+   * If the current token is a semicolon, return it after advancing to the next
+   * token. Otherwise report an error and create a synthetic semicolon.
+   */
+  Token _expectSemicolon() {
+    // TODO(scheglov) consider pushing this behavior into [_expect]
+    if (_matches(TokenType.SEMICOLON)) {
+      return getAndAdvance();
+    } else {
+      _reportErrorForToken(
+          ParserErrorCode.EXPECTED_TOKEN, _currentToken.previous, [";"]);
+      return _createSyntheticToken(TokenType.SEMICOLON);
+    }
+  }
+
+  /**
+   * Search the given list of [ranges] for a range that contains the given
+   * [index]. Return the range that was found, or `null` if none of the ranges
+   * contain the index.
+   */
+  List<int> _findRange(List<List<int>> ranges, int index) {
+    int rangeCount = ranges.length;
+    for (int i = 0; i < rangeCount; i++) {
+      List<int> range = ranges[i];
+      if (range[0] <= index && index <= range[1]) {
+        return range;
+      } else if (index < range[0]) {
+        return null;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return a list of the ranges of characters in the given [comment] that
+   * should be treated as code blocks.
+   */
+  List<List<int>> _getCodeBlockRanges(String comment) {
+    List<List<int>> ranges = new List<List<int>>();
+    int length = comment.length;
+    if (length < 3) {
+      return ranges;
+    }
+    int index = 0;
+    int firstChar = comment.codeUnitAt(0);
+    if (firstChar == 0x2F) {
+      int secondChar = comment.codeUnitAt(1);
+      int thirdChar = comment.codeUnitAt(2);
+      if ((secondChar == 0x2A && thirdChar == 0x2A) ||
+          (secondChar == 0x2F && thirdChar == 0x2F)) {
+        index = 3;
+      }
+    }
+    while (index < length) {
+      int currentChar = comment.codeUnitAt(index);
+      if (currentChar == 0xD || currentChar == 0xA) {
+        index = index + 1;
+        while (index < length &&
+            Character.isWhitespace(comment.codeUnitAt(index))) {
+          index = index + 1;
+        }
+        if (StringUtilities.startsWith6(
+            comment, index, 0x2A, 0x20, 0x20, 0x20, 0x20, 0x20)) {
+          int end = index + 6;
+          while (end < length &&
+              comment.codeUnitAt(end) != 0xD &&
+              comment.codeUnitAt(end) != 0xA) {
+            end = end + 1;
+          }
+          ranges.add(<int>[index, end]);
+          index = end;
+        }
+      } else if (index + 1 < length &&
+          currentChar == 0x5B &&
+          comment.codeUnitAt(index + 1) == 0x3A) {
+        int end = StringUtilities.indexOf2(comment, index + 2, 0x3A, 0x5D);
+        if (end < 0) {
+          end = length;
+        }
+        ranges.add(<int>[index, end]);
+        index = end + 1;
+      } else {
+        index = index + 1;
+      }
+    }
+    return ranges;
+  }
+
+  /**
+   * Return the end token associated with the given [beginToken], or `null` if
+   * either the given token is not a begin token or it does not have an end
+   * token associated with it.
+   */
+  Token _getEndToken(Token beginToken) {
+    if (beginToken is BeginToken) {
+      return beginToken.endToken;
+    }
+    return null;
+  }
+
+  /**
+   * Inject the given [token] into the token stream immediately before the
+   * current token.
+   */
+  Token _injectToken(Token token) {
+    Token previous = _currentToken.previous;
+    token.setNext(_currentToken);
+    previous.setNext(token);
+    return token;
+  }
+
+  /**
+   * Return `true` if the current token appears to be the beginning of a
+   * function declaration.
+   */
+  bool _isFunctionDeclaration() {
+    if (_matchesKeyword(Keyword.VOID)) {
+      return true;
+    }
+    Token afterReturnType = _skipTypeName(_currentToken);
+    if (afterReturnType == null) {
+      // There was no return type, but it is optional, so go back to where we
+      // started.
+      afterReturnType = _currentToken;
+    }
+    Token afterIdentifier = _skipSimpleIdentifier(afterReturnType);
+    if (afterIdentifier == null) {
+      // It's possible that we parsed the function name as if it were a type
+      // name, so see whether it makes sense if we assume that there is no type.
+      afterIdentifier = _skipSimpleIdentifier(_currentToken);
+    }
+    if (afterIdentifier == null) {
+      return false;
+    }
+    if (_isFunctionExpression(afterIdentifier)) {
+      return true;
+    }
+    // It's possible that we have found a getter. While this isn't valid at this
+    // point we test for it in order to recover better.
+    if (_matchesKeyword(Keyword.GET)) {
+      Token afterName = _skipSimpleIdentifier(_currentToken.next);
+      if (afterName == null) {
+        return false;
+      }
+      return _tokenMatches(afterName, TokenType.FUNCTION) ||
+          _tokenMatches(afterName, TokenType.OPEN_CURLY_BRACKET);
+    } else if (_tokenMatchesKeyword(afterReturnType, Keyword.GET)) {
+      Token afterName = _skipSimpleIdentifier(afterReturnType.next);
+      if (afterName == null) {
+        return false;
+      }
+      return _tokenMatches(afterName, TokenType.FUNCTION) ||
+          _tokenMatches(afterName, TokenType.OPEN_CURLY_BRACKET);
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the given [token] appears to be the beginning of a
+   * function expression.
+   */
+  bool _isFunctionExpression(Token token) {
+    // Function expressions aren't allowed in initializer lists.
+    if (_inInitializer) {
+      return false;
+    }
+    Token afterParameters = _skipFormalParameterList(token);
+    if (afterParameters == null) {
+      return false;
+    }
+    if (afterParameters
+        .matchesAny([TokenType.OPEN_CURLY_BRACKET, TokenType.FUNCTION])) {
+      return true;
+    }
+    String lexeme = afterParameters.lexeme;
+    return lexeme == ASYNC || lexeme == SYNC;
+  }
+
+  /**
+   * Return `true` if the given [character] is a valid hexadecimal digit.
+   */
+  bool _isHexDigit(int character) => (0x30 <= character && character <= 0x39) ||
+      (0x41 <= character && character <= 0x46) ||
+      (0x61 <= character && character <= 0x66);
+
+  /**
+   * Return `true` if the current token is the first token in an initialized
+   * variable declaration rather than an expression. This method assumes that we
+   * have already skipped past any metadata that might be associated with the
+   * declaration.
+   *
+   *     initializedVariableDeclaration ::=
+   *         declaredIdentifier ('=' expression)? (',' initializedIdentifier)*
+   *
+   *     declaredIdentifier ::=
+   *         metadata finalConstVarOrType identifier
+   *
+   *     finalConstVarOrType ::=
+   *         'final' type?
+   *       | 'const' type?
+   *       | 'var'
+   *       | type
+   *
+   *     type ::=
+   *         qualified typeArguments?
+   *
+   *     initializedIdentifier ::=
+   *         identifier ('=' expression)?
+   */
+  bool _isInitializedVariableDeclaration() {
+    if (_matchesKeyword(Keyword.FINAL) || _matchesKeyword(Keyword.VAR)) {
+      // An expression cannot start with a keyword other than 'const',
+      // 'rethrow', or 'throw'.
+      return true;
+    }
+    if (_matchesKeyword(Keyword.CONST)) {
+      // Look to see whether we might be at the start of a list or map literal,
+      // otherwise this should be the start of a variable declaration.
+      return !_peek().matchesAny([
+        TokenType.LT,
+        TokenType.OPEN_CURLY_BRACKET,
+        TokenType.OPEN_SQUARE_BRACKET,
+        TokenType.INDEX
+      ]);
+    }
+    // We know that we have an identifier, and need to see whether it might be
+    // a type name.
+    Token token = _skipTypeName(_currentToken);
+    if (token == null) {
+      // There was no type name, so this can't be a declaration.
+      return false;
+    }
+    token = _skipSimpleIdentifier(token);
+    if (token == null) {
+      return false;
+    }
+    TokenType type = token.type;
+    return type == TokenType.EQ ||
+        type == TokenType.COMMA ||
+        type == TokenType.SEMICOLON ||
+        _tokenMatchesKeyword(token, Keyword.IN);
+  }
+
+  /**
+   * Given that we have just found bracketed text within the given [comment],
+   * look to see whether that text is (a) followed by a parenthesized link
+   * address, (b) followed by a colon, or (c) followed by optional whitespace
+   * and another square bracket. The [rightIndex] is the index of the right
+   * bracket. Return `true` if the bracketed text is followed by a link address.
+   *
+   * This method uses the syntax described by the
+   * <a href="http://daringfireball.net/projects/markdown/syntax">markdown</a>
+   * project.
+   */
+  bool _isLinkText(String comment, int rightIndex) {
+    int length = comment.length;
+    int index = rightIndex + 1;
+    if (index >= length) {
+      return false;
+    }
+    int nextChar = comment.codeUnitAt(index);
+    if (nextChar == 0x28 || nextChar == 0x3A) {
+      return true;
+    }
+    while (Character.isWhitespace(nextChar)) {
+      index = index + 1;
+      if (index >= length) {
+        return false;
+      }
+      nextChar = comment.codeUnitAt(index);
+    }
+    return nextChar == 0x5B;
+  }
+
+  /**
+   * Return `true` if the given [startToken] appears to be the beginning of an
+   * operator declaration.
+   */
+  bool _isOperator(Token startToken) {
+    // Accept any operator here, even if it is not user definable.
+    if (!startToken.isOperator) {
+      return false;
+    }
+    // Token "=" means that it is actually field initializer.
+    if (startToken.type == TokenType.EQ) {
+      return false;
+    }
+    // Consume all operator tokens.
+    Token token = startToken.next;
+    while (token.isOperator) {
+      token = token.next;
+    }
+    // Formal parameter list is expect now.
+    return _tokenMatches(token, TokenType.OPEN_PAREN);
+  }
+
+  /**
+   * Return `true` if the current token appears to be the beginning of a switch
+   * member.
+   */
+  bool _isSwitchMember() {
+    Token token = _currentToken;
+    while (_tokenMatches(token, TokenType.IDENTIFIER) &&
+        _tokenMatches(token.next, TokenType.COLON)) {
+      token = token.next.next;
+    }
+    if (token.type == TokenType.KEYWORD) {
+      Keyword keyword = (token as KeywordToken).keyword;
+      return keyword == Keyword.CASE || keyword == Keyword.DEFAULT;
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the [startToken] appears to be the first token of a type
+   * name that is followed by a variable or field formal parameter.
+   */
+  bool _isTypedIdentifier(Token startToken) {
+    Token token = _skipReturnType(startToken);
+    if (token == null) {
+      return false;
+    } else if (_tokenMatchesIdentifier(token)) {
+      return true;
+    } else if (_tokenMatchesKeyword(token, Keyword.THIS) &&
+        _tokenMatches(token.next, TokenType.PERIOD) &&
+        _tokenMatchesIdentifier(token.next.next)) {
+      return true;
+    } else if (_tokenMatchesKeyword(startToken, Keyword.VOID)) {
+      // The keyword 'void' isn't a valid identifier, so it should be assumed to
+      // be a type name.
+      return true;
+    } else if (startToken.next != token) {
+      // The type is more than a simple identifier, so it should be assumed to
+      // be a type name.
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Increments the error reporting lock level. If level is more than `0`, then
+   * [reportError] wont report any error.
+   */
+  void _lockErrorListener() {
+    _errorListenerLock++;
+  }
+
+  /**
+   * Return `true` if the current token has the given [type]. Note that the
+   * method [_matchesGt] should be used if the argument to this method would be
+   * [TokenType.GT].
+   */
+  bool _matches(TokenType type) => _currentToken.type == type;
+
+  /**
+   * Return `true` if the current token has a type of [TokenType.GT]. Note that
+   * this method, unlike other variants, will modify the token stream if
+   * possible to match desired type. In particular, if the next token is either
+   * a '>>' or '>>>', the token stream will be re-written and `true` will be
+   * returned.
+   */
+  bool _matchesGt() {
+    TokenType currentType = _currentToken.type;
+    if (currentType == TokenType.GT) {
+      return true;
+    } else if (currentType == TokenType.GT_GT) {
+      Token first = _createToken(_currentToken, TokenType.GT);
+      Token second = new Token(TokenType.GT, _currentToken.offset + 1);
+      second.setNext(_currentToken.next);
+      first.setNext(second);
+      _currentToken.previous.setNext(first);
+      _currentToken = first;
+      return true;
+    } else if (currentType == TokenType.GT_EQ) {
+      Token first = _createToken(_currentToken, TokenType.GT);
+      Token second = new Token(TokenType.EQ, _currentToken.offset + 1);
+      second.setNext(_currentToken.next);
+      first.setNext(second);
+      _currentToken.previous.setNext(first);
+      _currentToken = first;
+      return true;
+    } else if (currentType == TokenType.GT_GT_EQ) {
+      int offset = _currentToken.offset;
+      Token first = _createToken(_currentToken, TokenType.GT);
+      Token second = new Token(TokenType.GT, offset + 1);
+      Token third = new Token(TokenType.EQ, offset + 2);
+      third.setNext(_currentToken.next);
+      second.setNext(third);
+      first.setNext(second);
+      _currentToken.previous.setNext(first);
+      _currentToken = first;
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the current token is a valid identifier. Valid identifiers
+   * include built-in identifiers (pseudo-keywords).
+   */
+  bool _matchesIdentifier() => _tokenMatchesIdentifier(_currentToken);
+
+  /**
+   * Return `true` if the current token matches the given [keyword].
+   */
+  bool _matchesKeyword(Keyword keyword) =>
+      _tokenMatchesKeyword(_currentToken, keyword);
+
+  /**
+   * Return `true` if the current token matches the given [identifier].
+   */
+  bool _matchesString(String identifier) =>
+      _currentToken.type == TokenType.IDENTIFIER &&
+          _currentToken.lexeme == identifier;
+
+  /**
+   * If the current token has the given [type], then advance to the next token
+   * and return `true`. Otherwise, return `false` without advancing. This method
+   * should not be invoked with an argument value of [TokenType.GT].
+   */
+  bool _optional(TokenType type) {
+    if (_matches(type)) {
+      _advance();
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Parse an additive expression. Return the additive expression that was
+   * parsed.
+   *
+   *     additiveExpression ::=
+   *         multiplicativeExpression (additiveOperator multiplicativeExpression)*
+   *       | 'super' (additiveOperator multiplicativeExpression)+
+   */
+  Expression _parseAdditiveExpression() {
+    Expression expression;
+    if (_matchesKeyword(Keyword.SUPER) &&
+        _currentToken.next.type.isAdditiveOperator) {
+      expression = new SuperExpression(getAndAdvance());
+    } else {
+      expression = _parseMultiplicativeExpression();
+    }
+    while (_currentToken.type.isAdditiveOperator) {
+      Token operator = getAndAdvance();
+      expression = new BinaryExpression(
+          expression, operator, _parseMultiplicativeExpression());
+    }
+    return expression;
+  }
+
+  /**
+   * Parse an assert statement. Return the assert statement.
+   *
+   *     assertStatement ::=
+   *         'assert' '(' conditionalExpression ')' ';'
+   */
+  AssertStatement _parseAssertStatement() {
+    Token keyword = _expectKeyword(Keyword.ASSERT);
+    Token leftParen = _expect(TokenType.OPEN_PAREN);
+    Expression expression = parseExpression2();
+    if (expression is AssignmentExpression) {
+      _reportErrorForNode(
+          ParserErrorCode.ASSERT_DOES_NOT_TAKE_ASSIGNMENT, expression);
+    } else if (expression is CascadeExpression) {
+      _reportErrorForNode(
+          ParserErrorCode.ASSERT_DOES_NOT_TAKE_CASCADE, expression);
+    } else if (expression is ThrowExpression) {
+      _reportErrorForNode(
+          ParserErrorCode.ASSERT_DOES_NOT_TAKE_THROW, expression);
+    } else if (expression is RethrowExpression) {
+      _reportErrorForNode(
+          ParserErrorCode.ASSERT_DOES_NOT_TAKE_RETHROW, expression);
+    }
+    Token rightParen = _expect(TokenType.CLOSE_PAREN);
+    Token semicolon = _expect(TokenType.SEMICOLON);
+    return new AssertStatement(
+        keyword, leftParen, expression, rightParen, semicolon);
+  }
+
+  /**
+   * Parse an assignable expression. The [primaryAllowed] is `true` if the
+   * expression is allowed to be a primary without any assignable selector.
+   * Return the assignable expression that was parsed.
+   *
+   *     assignableExpression ::=
+   *         primary (arguments* assignableSelector)+
+   *       | 'super' unconditionalAssignableSelector
+   *       | identifier
+   */
+  Expression _parseAssignableExpression(bool primaryAllowed) {
+    if (_matchesKeyword(Keyword.SUPER)) {
+      return _parseAssignableSelector(
+          new SuperExpression(getAndAdvance()), false, allowConditional: false);
+    }
+    //
+    // A primary expression can start with an identifier. We resolve the
+    // ambiguity by determining whether the primary consists of anything other
+    // than an identifier and/or is followed by an assignableSelector.
+    //
+    Expression expression = _parsePrimaryExpression();
+    bool isOptional = primaryAllowed || expression is SimpleIdentifier;
+    while (true) {
+      while (_matches(TokenType.OPEN_PAREN)) {
+        ArgumentList argumentList = parseArgumentList();
+        if (expression is SimpleIdentifier) {
+          expression = new MethodInvocation(
+              null, null, expression as SimpleIdentifier, argumentList);
+        } else if (expression is PrefixedIdentifier) {
+          PrefixedIdentifier identifier = expression as PrefixedIdentifier;
+          expression = new MethodInvocation(identifier.prefix,
+              identifier.period, identifier.identifier, argumentList);
+        } else if (expression is PropertyAccess) {
+          PropertyAccess access = expression as PropertyAccess;
+          expression = new MethodInvocation(access.target, access.operator,
+              access.propertyName, argumentList);
+        } else {
+          expression =
+              new FunctionExpressionInvocation(expression, argumentList);
+        }
+        if (!primaryAllowed) {
+          isOptional = false;
+        }
+      }
+      Expression selectorExpression = _parseAssignableSelector(
+          expression, isOptional || (expression is PrefixedIdentifier));
+      if (identical(selectorExpression, expression)) {
+        if (!isOptional && (expression is PrefixedIdentifier)) {
+          PrefixedIdentifier identifier = expression as PrefixedIdentifier;
+          expression = new PropertyAccess(
+              identifier.prefix, identifier.period, identifier.identifier);
+        }
+        return expression;
+      }
+      expression = selectorExpression;
+      isOptional = true;
+    }
+  }
+
+  /**
+   * Parse an assignable selector. The [prefix] is the expression preceding the
+   * selector. The [optional] is `true` if the selector is optional. Return the
+   * assignable selector that was parsed, or the original prefix if there was no
+   * assignable selector.  If [allowConditional] is false, then the '?.'
+   * operator will still be parsed, but a parse error will be generated.
+   *
+   *     unconditionalAssignableSelector ::=
+   *         '[' expression ']'
+   *       | '.' identifier
+   *
+   *     assignableSelector ::=
+   *         unconditionalAssignableSelector
+   *       | '?.' identifier
+   */
+  Expression _parseAssignableSelector(Expression prefix, bool optional,
+      {bool allowConditional: true}) {
+    if (_matches(TokenType.OPEN_SQUARE_BRACKET)) {
+      Token leftBracket = getAndAdvance();
+      bool wasInInitializer = _inInitializer;
+      _inInitializer = false;
+      try {
+        Expression index = parseExpression2();
+        Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
+        return new IndexExpression.forTarget(
+            prefix, leftBracket, index, rightBracket);
+      } finally {
+        _inInitializer = wasInInitializer;
+      }
+    } else if (_matches(TokenType.PERIOD) ||
+        _matches(TokenType.QUESTION_PERIOD)) {
+      if (_matches(TokenType.QUESTION_PERIOD) && !allowConditional) {
+        _reportErrorForCurrentToken(
+            ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [_currentToken.lexeme]);
+      }
+      Token operator = getAndAdvance();
+      return new PropertyAccess(prefix, operator, parseSimpleIdentifier());
+    } else {
+      if (!optional) {
+        // Report the missing selector.
+        _reportErrorForCurrentToken(
+            ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR);
+      }
+      return prefix;
+    }
+  }
+
+  /**
+   * Parse a await expression. Return the await expression that was parsed.
+   *
+   *     awaitExpression ::=
+   *         'await' unaryExpression
+   */
+  AwaitExpression _parseAwaitExpression() {
+    Token awaitToken = getAndAdvance();
+    Expression expression = _parseUnaryExpression();
+    return new AwaitExpression(awaitToken, expression);
+  }
+
+  /**
+   * Parse a bitwise and expression. Return the bitwise and expression that was
+   * parsed.
+   *
+   *     bitwiseAndExpression ::=
+   *         shiftExpression ('&' shiftExpression)*
+   *       | 'super' ('&' shiftExpression)+
+   */
+  Expression _parseBitwiseAndExpression() {
+    Expression expression;
+    if (_matchesKeyword(Keyword.SUPER) &&
+        _tokenMatches(_peek(), TokenType.AMPERSAND)) {
+      expression = new SuperExpression(getAndAdvance());
+    } else {
+      expression = _parseShiftExpression();
+    }
+    while (_matches(TokenType.AMPERSAND)) {
+      Token operator = getAndAdvance();
+      expression =
+          new BinaryExpression(expression, operator, _parseShiftExpression());
+    }
+    return expression;
+  }
+
+  /**
+   * Parse a bitwise exclusive-or expression. Return the bitwise exclusive-or
+   * expression that was parsed.
+   *
+   *     bitwiseXorExpression ::=
+   *         bitwiseAndExpression ('^' bitwiseAndExpression)*
+   *       | 'super' ('^' bitwiseAndExpression)+
+   */
+  Expression _parseBitwiseXorExpression() {
+    Expression expression;
+    if (_matchesKeyword(Keyword.SUPER) &&
+        _tokenMatches(_peek(), TokenType.CARET)) {
+      expression = new SuperExpression(getAndAdvance());
+    } else {
+      expression = _parseBitwiseAndExpression();
+    }
+    while (_matches(TokenType.CARET)) {
+      Token operator = getAndAdvance();
+      expression = new BinaryExpression(
+          expression, operator, _parseBitwiseAndExpression());
+    }
+    return expression;
+  }
+
+  /**
+   * Parse a break statement. Return the break statement that was parsed.
+   *
+   *     breakStatement ::=
+   *         'break' identifier? ';'
+   */
+  Statement _parseBreakStatement() {
+    Token breakKeyword = _expectKeyword(Keyword.BREAK);
+    SimpleIdentifier label = null;
+    if (_matchesIdentifier()) {
+      label = parseSimpleIdentifier();
+    }
+    if (!_inLoop && !_inSwitch && label == null) {
+      _reportErrorForToken(ParserErrorCode.BREAK_OUTSIDE_OF_LOOP, breakKeyword);
+    }
+    Token semicolon = _expect(TokenType.SEMICOLON);
+    return new BreakStatement(breakKeyword, label, semicolon);
+  }
+
+  /**
+   * Parse a cascade section. Return the expression representing the cascaded
+   * method invocation.
+   *
+   *     cascadeSection ::=
+   *         '..' (cascadeSelector arguments*) (assignableSelector arguments*)* cascadeAssignment?
+   *
+   *     cascadeSelector ::=
+   *         '[' expression ']'
+   *       | identifier
+   *
+   *     cascadeAssignment ::=
+   *         assignmentOperator expressionWithoutCascade
+   */
+  Expression _parseCascadeSection() {
+    Token period = _expect(TokenType.PERIOD_PERIOD);
+    Expression expression = null;
+    SimpleIdentifier functionName = null;
+    if (_matchesIdentifier()) {
+      functionName = parseSimpleIdentifier();
+    } else if (_currentToken.type == TokenType.OPEN_SQUARE_BRACKET) {
+      Token leftBracket = getAndAdvance();
+      bool wasInInitializer = _inInitializer;
+      _inInitializer = false;
+      try {
+        Expression index = parseExpression2();
+        Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
+        expression = new IndexExpression.forCascade(
+            period, leftBracket, index, rightBracket);
+        period = null;
+      } finally {
+        _inInitializer = wasInInitializer;
+      }
+    } else {
+      _reportErrorForToken(ParserErrorCode.MISSING_IDENTIFIER, _currentToken,
+          [_currentToken.lexeme]);
+      functionName = _createSyntheticIdentifier();
+    }
+    assert((expression == null && functionName != null) ||
+        (expression != null && functionName == null));
+    if (_currentToken.type == TokenType.OPEN_PAREN) {
+      while (_currentToken.type == TokenType.OPEN_PAREN) {
+        if (functionName != null) {
+          expression = new MethodInvocation(
+              expression, period, functionName, parseArgumentList());
+          period = null;
+          functionName = null;
+        } else if (expression == null) {
+          // It should not be possible to get here.
+          expression = new MethodInvocation(expression, period,
+              _createSyntheticIdentifier(), parseArgumentList());
+        } else {
+          expression =
+              new FunctionExpressionInvocation(expression, parseArgumentList());
+        }
+      }
+    } else if (functionName != null) {
+      expression = new PropertyAccess(expression, period, functionName);
+      period = null;
+    }
+    assert(expression != null);
+    bool progress = true;
+    while (progress) {
+      progress = false;
+      Expression selector = _parseAssignableSelector(expression, true);
+      if (!identical(selector, expression)) {
+        expression = selector;
+        progress = true;
+        while (_currentToken.type == TokenType.OPEN_PAREN) {
+          if (expression is PropertyAccess) {
+            PropertyAccess propertyAccess = expression as PropertyAccess;
+            expression = new MethodInvocation(propertyAccess.target,
+                propertyAccess.operator, propertyAccess.propertyName,
+                parseArgumentList());
+          } else {
+            expression = new FunctionExpressionInvocation(
+                expression, parseArgumentList());
+          }
+        }
+      }
+    }
+    if (_currentToken.type.isAssignmentOperator) {
+      Token operator = getAndAdvance();
+      _ensureAssignable(expression);
+      expression = new AssignmentExpression(
+          expression, operator, parseExpressionWithoutCascade());
+    }
+    return expression;
+  }
+
+  /**
+   * Parse a class declaration. The [commentAndMetadata] is the metadata to be
+   * associated with the member. The [abstractKeyword] is the token for the
+   * keyword 'abstract', or `null` if the keyword was not given. Return the
+   * class declaration that was parsed.
+   *
+   *     classDeclaration ::=
+   *         metadata 'abstract'? 'class' name typeParameterList? (extendsClause withClause?)? implementsClause? '{' classMembers '}' |
+   *         metadata 'abstract'? 'class' mixinApplicationClass
+   */
+  CompilationUnitMember _parseClassDeclaration(
+      CommentAndMetadata commentAndMetadata, Token abstractKeyword) {
+    Token keyword = _expectKeyword(Keyword.CLASS);
+    if (_matchesIdentifier()) {
+      Token next = _peek();
+      if (_tokenMatches(next, TokenType.LT)) {
+        next = _skipTypeParameterList(next);
+        if (next != null && _tokenMatches(next, TokenType.EQ)) {
+          return _parseClassTypeAlias(
+              commentAndMetadata, abstractKeyword, keyword);
+        }
+      } else if (_tokenMatches(next, TokenType.EQ)) {
+        return _parseClassTypeAlias(
+            commentAndMetadata, abstractKeyword, keyword);
+      }
+    }
+    SimpleIdentifier name = parseSimpleIdentifier();
+    String className = name.name;
+    TypeParameterList typeParameters = null;
+    if (_matches(TokenType.LT)) {
+      typeParameters = parseTypeParameterList();
+    }
+    //
+    // Parse the clauses. The parser accepts clauses in any order, but will
+    // generate errors if they are not in the order required by the
+    // specification.
+    //
+    ExtendsClause extendsClause = null;
+    WithClause withClause = null;
+    ImplementsClause implementsClause = null;
+    bool foundClause = true;
+    while (foundClause) {
+      if (_matchesKeyword(Keyword.EXTENDS)) {
+        if (extendsClause == null) {
+          extendsClause = parseExtendsClause();
+          if (withClause != null) {
+            _reportErrorForToken(
+                ParserErrorCode.WITH_BEFORE_EXTENDS, withClause.withKeyword);
+          } else if (implementsClause != null) {
+            _reportErrorForToken(ParserErrorCode.IMPLEMENTS_BEFORE_EXTENDS,
+                implementsClause.implementsKeyword);
+          }
+        } else {
+          _reportErrorForToken(ParserErrorCode.MULTIPLE_EXTENDS_CLAUSES,
+              extendsClause.extendsKeyword);
+          parseExtendsClause();
+        }
+      } else if (_matchesKeyword(Keyword.WITH)) {
+        if (withClause == null) {
+          withClause = parseWithClause();
+          if (implementsClause != null) {
+            _reportErrorForToken(ParserErrorCode.IMPLEMENTS_BEFORE_WITH,
+                implementsClause.implementsKeyword);
+          }
+        } else {
+          _reportErrorForToken(
+              ParserErrorCode.MULTIPLE_WITH_CLAUSES, withClause.withKeyword);
+          parseWithClause();
+          // TODO(brianwilkerson) Should we merge the list of applied mixins
+          // into a single list?
+        }
+      } else if (_matchesKeyword(Keyword.IMPLEMENTS)) {
+        if (implementsClause == null) {
+          implementsClause = parseImplementsClause();
+        } else {
+          _reportErrorForToken(ParserErrorCode.MULTIPLE_IMPLEMENTS_CLAUSES,
+              implementsClause.implementsKeyword);
+          parseImplementsClause();
+          // TODO(brianwilkerson) Should we merge the list of implemented
+          // classes into a single list?
+        }
+      } else {
+        foundClause = false;
+      }
+    }
+    if (withClause != null && extendsClause == null) {
+      _reportErrorForToken(
+          ParserErrorCode.WITH_WITHOUT_EXTENDS, withClause.withKeyword);
+    }
+    //
+    // Look for and skip over the extra-lingual 'native' specification.
+    //
+    NativeClause nativeClause = null;
+    if (_matchesString(_NATIVE) && _tokenMatches(_peek(), TokenType.STRING)) {
+      nativeClause = _parseNativeClause();
+    }
+    //
+    // Parse the body of the class.
+    //
+    Token leftBracket = null;
+    List<ClassMember> members = null;
+    Token rightBracket = null;
+    if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+      leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
+      members = _parseClassMembers(className, _getEndToken(leftBracket));
+      rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
+    } else {
+      leftBracket = _createSyntheticToken(TokenType.OPEN_CURLY_BRACKET);
+      rightBracket = _createSyntheticToken(TokenType.CLOSE_CURLY_BRACKET);
+      _reportErrorForCurrentToken(ParserErrorCode.MISSING_CLASS_BODY);
+    }
+    ClassDeclaration classDeclaration = new ClassDeclaration(
+        commentAndMetadata.comment, commentAndMetadata.metadata,
+        abstractKeyword, keyword, name, typeParameters, extendsClause,
+        withClause, implementsClause, leftBracket, members, rightBracket);
+    classDeclaration.nativeClause = nativeClause;
+    return classDeclaration;
+  }
+
+  /**
+   * Parse a list of class members. The [className] is the name of the class
+   * whose members are being parsed. The [closingBracket] is the closing bracket
+   * for the class, or `null` if the closing bracket is missing. Return the list
+   * of class members that were parsed.
+   *
+   *     classMembers ::=
+   *         (metadata memberDefinition)*
+   */
+  List<ClassMember> _parseClassMembers(String className, Token closingBracket) {
+    List<ClassMember> members = new List<ClassMember>();
+    Token memberStart = _currentToken;
+    while (!_matches(TokenType.EOF) &&
+        !_matches(TokenType.CLOSE_CURLY_BRACKET) &&
+        (closingBracket != null ||
+            (!_matchesKeyword(Keyword.CLASS) &&
+                !_matchesKeyword(Keyword.TYPEDEF)))) {
+      if (_matches(TokenType.SEMICOLON)) {
+        _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
+            [_currentToken.lexeme]);
+        _advance();
+      } else {
+        ClassMember member = parseClassMember(className);
+        if (member != null) {
+          members.add(member);
+        }
+      }
+      if (identical(_currentToken, memberStart)) {
+        _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
+            [_currentToken.lexeme]);
+        _advance();
+      }
+      memberStart = _currentToken;
+    }
+    return members;
+  }
+
+  /**
+   * Parse a class type alias. The [commentAndMetadata] is the metadata to be
+   * associated with the member. The [abstractKeyword] is the token representing
+   * the 'abstract' keyword. The [classKeyword] is the token representing the
+   * 'class' keyword. Return the class type alias that was parsed.
+   *
+   *     classTypeAlias ::=
+   *         identifier typeParameters? '=' 'abstract'? mixinApplication
+   *
+   *     mixinApplication ::=
+   *         type withClause implementsClause? ';'
+   */
+  ClassTypeAlias _parseClassTypeAlias(CommentAndMetadata commentAndMetadata,
+      Token abstractKeyword, Token classKeyword) {
+    SimpleIdentifier className = parseSimpleIdentifier();
+    TypeParameterList typeParameters = null;
+    if (_matches(TokenType.LT)) {
+      typeParameters = parseTypeParameterList();
+    }
+    Token equals = _expect(TokenType.EQ);
+    TypeName superclass = parseTypeName();
+    WithClause withClause = null;
+    if (_matchesKeyword(Keyword.WITH)) {
+      withClause = parseWithClause();
+    } else {
+      _reportErrorForCurrentToken(
+          ParserErrorCode.EXPECTED_TOKEN, [Keyword.WITH.syntax]);
+    }
+    ImplementsClause implementsClause = null;
+    if (_matchesKeyword(Keyword.IMPLEMENTS)) {
+      implementsClause = parseImplementsClause();
+    }
+    Token semicolon;
+    if (_matches(TokenType.SEMICOLON)) {
+      semicolon = getAndAdvance();
+    } else {
+      if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+        _reportErrorForCurrentToken(
+            ParserErrorCode.EXPECTED_TOKEN, [TokenType.SEMICOLON.lexeme]);
+        Token leftBracket = getAndAdvance();
+        _parseClassMembers(className.name, _getEndToken(leftBracket));
+        _expect(TokenType.CLOSE_CURLY_BRACKET);
+      } else {
+        _reportErrorForToken(ParserErrorCode.EXPECTED_TOKEN,
+            _currentToken.previous, [TokenType.SEMICOLON.lexeme]);
+      }
+      semicolon = _createSyntheticToken(TokenType.SEMICOLON);
+    }
+    return new ClassTypeAlias(commentAndMetadata.comment,
+        commentAndMetadata.metadata, classKeyword, className, typeParameters,
+        equals, abstractKeyword, superclass, withClause, implementsClause,
+        semicolon);
+  }
+
+  /**
+   * Parse a list of combinators in a directive. Return the combinators that
+   * were parsed.
+   *
+   *     combinator ::=
+   *         'show' identifier (',' identifier)*
+   *       | 'hide' identifier (',' identifier)*
+   */
+  List<Combinator> _parseCombinators() {
+    List<Combinator> combinators = new List<Combinator>();
+    while (true) {
+      Combinator combinator = parseCombinator();
+      if (combinator == null) {
+        break;
+      }
+      combinators.add(combinator);
+    }
+    return combinators;
+  }
+
+  /**
+   * Parse the documentation comment and metadata preceding a declaration. This
+   * method allows any number of documentation comments to occur before, after
+   * or between the metadata, but only returns the last (right-most)
+   * documentation comment that is found. Return the documentation comment and
+   * metadata that were parsed.
+   *
+   *     metadata ::=
+   *         annotation*
+   */
+  CommentAndMetadata _parseCommentAndMetadata() {
+    Comment comment = _parseDocumentationComment();
+    List<Annotation> metadata = new List<Annotation>();
+    while (_matches(TokenType.AT)) {
+      metadata.add(parseAnnotation());
+      Comment optionalComment = _parseDocumentationComment();
+      if (optionalComment != null) {
+        comment = optionalComment;
+      }
+    }
+    return new CommentAndMetadata(comment, metadata);
+  }
+
+  /**
+   * Parse a comment reference from the source between square brackets. The
+   * [referenceSource] is the source occurring between the square brackets
+   * within a documentation comment. The [sourceOffset] is the offset of the
+   * first character of the reference source. Return the comment reference that
+   * was parsed, or `null` if no reference could be found.
+   *
+   *     commentReference ::=
+   *         'new'? prefixedIdentifier
+   */
+  CommentReference _parseCommentReference(
+      String referenceSource, int sourceOffset) {
+    // TODO(brianwilkerson) The errors are not getting the right offset/length
+    // and are being duplicated.
+    if (referenceSource.length == 0) {
+      Token syntheticToken =
+          new SyntheticStringToken(TokenType.IDENTIFIER, "", sourceOffset);
+      return new CommentReference(null, new SimpleIdentifier(syntheticToken));
+    }
+    try {
+      BooleanErrorListener listener = new BooleanErrorListener();
+      Scanner scanner = new Scanner(
+          null, new SubSequenceReader(referenceSource, sourceOffset), listener);
+      scanner.setSourceStart(1, 1);
+      Token firstToken = scanner.tokenize();
+      if (listener.errorReported) {
+        return null;
+      }
+      Token newKeyword = null;
+      if (_tokenMatchesKeyword(firstToken, Keyword.NEW)) {
+        newKeyword = firstToken;
+        firstToken = firstToken.next;
+      }
+      if (_tokenMatchesIdentifier(firstToken)) {
+        Token secondToken = firstToken.next;
+        Token thirdToken = secondToken.next;
+        Token nextToken;
+        Identifier identifier;
+        if (_tokenMatches(secondToken, TokenType.PERIOD) &&
+            _tokenMatchesIdentifier(thirdToken)) {
+          identifier = new PrefixedIdentifier(new SimpleIdentifier(firstToken),
+              secondToken, new SimpleIdentifier(thirdToken));
+          nextToken = thirdToken.next;
+        } else {
+          identifier = new SimpleIdentifier(firstToken);
+          nextToken = firstToken.next;
+        }
+        if (nextToken.type != TokenType.EOF) {
+          return null;
+        }
+        return new CommentReference(newKeyword, identifier);
+      } else if (_tokenMatchesKeyword(firstToken, Keyword.THIS) ||
+          _tokenMatchesKeyword(firstToken, Keyword.NULL) ||
+          _tokenMatchesKeyword(firstToken, Keyword.TRUE) ||
+          _tokenMatchesKeyword(firstToken, Keyword.FALSE)) {
+        // TODO(brianwilkerson) If we want to support this we will need to
+        // extend the definition of CommentReference to take an expression
+        // rather than an identifier. For now we just ignore it to reduce the
+        // number of errors produced, but that's probably not a valid long term
+        // approach.
+        return null;
+      }
+    } catch (exception) {
+      // Ignored because we assume that it wasn't a real comment reference.
+    }
+    return null;
+  }
+
+  /**
+   * Parse all of the comment references occurring in the given array of
+   * documentation comments. The [tokens] are the comment tokens representing
+   * the documentation comments to be parsed. Return the comment references that
+   * were parsed.
+   *
+   *     commentReference ::=
+   *         '[' 'new'? qualified ']' libraryReference?
+   *
+   *     libraryReference ::=
+   *          '(' stringLiteral ')'
+   */
+  List<CommentReference> _parseCommentReferences(
+      List<DocumentationCommentToken> tokens) {
+    List<CommentReference> references = new List<CommentReference>();
+    for (DocumentationCommentToken token in tokens) {
+      String comment = token.lexeme;
+      int length = comment.length;
+      List<List<int>> codeBlockRanges = _getCodeBlockRanges(comment);
+      int leftIndex = comment.indexOf('[');
+      while (leftIndex >= 0 && leftIndex + 1 < length) {
+        List<int> range = _findRange(codeBlockRanges, leftIndex);
+        if (range == null) {
+          int nameOffset = token.offset + leftIndex + 1;
+          int rightIndex = JavaString.indexOf(comment, ']', leftIndex);
+          if (rightIndex >= 0) {
+            int firstChar = comment.codeUnitAt(leftIndex + 1);
+            if (firstChar != 0x27 && firstChar != 0x22) {
+              if (_isLinkText(comment, rightIndex)) {
+                // TODO(brianwilkerson) Handle the case where there's a library
+                // URI in the link text.
+              } else {
+                CommentReference reference = _parseCommentReference(
+                    comment.substring(leftIndex + 1, rightIndex), nameOffset);
+                if (reference != null) {
+                  references.add(reference);
+                  token.references.add(reference.beginToken);
+                }
+              }
+            }
+          } else {
+            // terminating ']' is not typed yet
+            int charAfterLeft = comment.codeUnitAt(leftIndex + 1);
+            if (Character.isLetterOrDigit(charAfterLeft)) {
+              int nameEnd = StringUtilities.indexOfFirstNotLetterDigit(
+                  comment, leftIndex + 1);
+              String name = comment.substring(leftIndex + 1, nameEnd);
+              Token nameToken =
+                  new StringToken(TokenType.IDENTIFIER, name, nameOffset);
+              references.add(
+                  new CommentReference(null, new SimpleIdentifier(nameToken)));
+            } else {
+              Token nameToken = new SyntheticStringToken(
+                  TokenType.IDENTIFIER, "", nameOffset);
+              references.add(
+                  new CommentReference(null, new SimpleIdentifier(nameToken)));
+            }
+            // next character
+            rightIndex = leftIndex + 1;
+          }
+          leftIndex = JavaString.indexOf(comment, '[', rightIndex);
+        } else {
+          leftIndex = JavaString.indexOf(comment, '[', range[1] + 1);
+        }
+      }
+    }
+    return references;
+  }
+
+  /**
+   * Parse a compilation unit member. The [commentAndMetadata] is the metadata
+   * to be associated with the member. Return the compilation unit member that
+   * was parsed, or `null` if what was parsed could not be represented as a
+   * compilation unit member.
+   *
+   *     compilationUnitMember ::=
+   *         classDefinition
+   *       | functionTypeAlias
+   *       | external functionSignature
+   *       | external getterSignature
+   *       | external setterSignature
+   *       | functionSignature functionBody
+   *       | returnType? getOrSet identifier formalParameterList functionBody
+   *       | (final | const) type? staticFinalDeclarationList ';'
+   *       | variableDeclaration ';'
+   */
+  CompilationUnitMember _parseCompilationUnitMember(
+      CommentAndMetadata commentAndMetadata) {
+    Modifiers modifiers = _parseModifiers();
+    if (_matchesKeyword(Keyword.CLASS)) {
+      return _parseClassDeclaration(
+          commentAndMetadata, _validateModifiersForClass(modifiers));
+    } else if (_matchesKeyword(Keyword.TYPEDEF) &&
+        !_tokenMatches(_peek(), TokenType.PERIOD) &&
+        !_tokenMatches(_peek(), TokenType.LT) &&
+        !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+      _validateModifiersForTypedef(modifiers);
+      return _parseTypeAlias(commentAndMetadata);
+    } else if (_matchesKeyword(Keyword.ENUM)) {
+      _validateModifiersForEnum(modifiers);
+      return _parseEnumDeclaration(commentAndMetadata);
+    }
+    if (_matchesKeyword(Keyword.VOID)) {
+      TypeName returnType = parseReturnType();
+      if ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) &&
+          _tokenMatchesIdentifier(_peek())) {
+        _validateModifiersForTopLevelFunction(modifiers);
+        return _parseFunctionDeclaration(
+            commentAndMetadata, modifiers.externalKeyword, returnType);
+      } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
+        _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
+        return _convertToFunctionDeclaration(_parseOperator(
+            commentAndMetadata, modifiers.externalKeyword, returnType));
+      } else if (_matchesIdentifier() &&
+          _peek().matchesAny([
+        TokenType.OPEN_PAREN,
+        TokenType.OPEN_CURLY_BRACKET,
+        TokenType.FUNCTION
+      ])) {
+        _validateModifiersForTopLevelFunction(modifiers);
+        return _parseFunctionDeclaration(
+            commentAndMetadata, modifiers.externalKeyword, returnType);
+      } else {
+        //
+        // We have found an error of some kind. Try to recover.
+        //
+        if (_matchesIdentifier()) {
+          if (_peek().matchesAny(
+              [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
+            //
+            // We appear to have a variable declaration with a type of "void".
+            //
+            _reportErrorForNode(ParserErrorCode.VOID_VARIABLE, returnType);
+            return new TopLevelVariableDeclaration(commentAndMetadata.comment,
+                commentAndMetadata.metadata,
+                _parseVariableDeclarationListAfterType(null,
+                    _validateModifiersForTopLevelVariable(modifiers), null),
+                _expect(TokenType.SEMICOLON));
+          }
+        }
+        _reportErrorForToken(
+            ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
+        return null;
+      }
+    } else if ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) &&
+        _tokenMatchesIdentifier(_peek())) {
+      _validateModifiersForTopLevelFunction(modifiers);
+      return _parseFunctionDeclaration(
+          commentAndMetadata, modifiers.externalKeyword, null);
+    } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
+      _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
+      return _convertToFunctionDeclaration(
+          _parseOperator(commentAndMetadata, modifiers.externalKeyword, null));
+    } else if (!_matchesIdentifier()) {
+      Token keyword = modifiers.varKeyword;
+      if (keyword == null) {
+        keyword = modifiers.finalKeyword;
+      }
+      if (keyword == null) {
+        keyword = modifiers.constKeyword;
+      }
+      if (keyword != null) {
+        //
+        // We appear to have found an incomplete top-level variable declaration.
+        //
+        _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+        List<VariableDeclaration> variables = new List<VariableDeclaration>();
+        variables.add(
+            new VariableDeclaration(_createSyntheticIdentifier(), null, null));
+        return new TopLevelVariableDeclaration(commentAndMetadata.comment,
+            commentAndMetadata.metadata,
+            new VariableDeclarationList(null, null, keyword, null, variables),
+            _expectSemicolon());
+      }
+      _reportErrorForToken(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
+      return null;
+    } else if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+      _validateModifiersForTopLevelFunction(modifiers);
+      return _parseFunctionDeclaration(
+          commentAndMetadata, modifiers.externalKeyword, null);
+    } else if (_peek()
+        .matchesAny([TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
+      if (modifiers.constKeyword == null &&
+          modifiers.finalKeyword == null &&
+          modifiers.varKeyword == null) {
+        _reportErrorForCurrentToken(
+            ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE);
+      }
+      return new TopLevelVariableDeclaration(commentAndMetadata.comment,
+          commentAndMetadata.metadata, _parseVariableDeclarationListAfterType(
+              null, _validateModifiersForTopLevelVariable(modifiers), null),
+          _expect(TokenType.SEMICOLON));
+    }
+    TypeName returnType = parseReturnType();
+    if ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) &&
+        _tokenMatchesIdentifier(_peek())) {
+      _validateModifiersForTopLevelFunction(modifiers);
+      return _parseFunctionDeclaration(
+          commentAndMetadata, modifiers.externalKeyword, returnType);
+    } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
+      _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
+      return _convertToFunctionDeclaration(_parseOperator(
+          commentAndMetadata, modifiers.externalKeyword, returnType));
+    } else if (_matches(TokenType.AT)) {
+      return new TopLevelVariableDeclaration(commentAndMetadata.comment,
+          commentAndMetadata.metadata, _parseVariableDeclarationListAfterType(
+              null, _validateModifiersForTopLevelVariable(modifiers),
+              returnType), _expect(TokenType.SEMICOLON));
+    } else if (!_matchesIdentifier()) {
+      // TODO(brianwilkerson) Generalize this error. We could also be parsing a
+      // top-level variable at this point.
+      _reportErrorForToken(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
+      Token semicolon;
+      if (_matches(TokenType.SEMICOLON)) {
+        semicolon = getAndAdvance();
+      } else {
+        semicolon = _createSyntheticToken(TokenType.SEMICOLON);
+      }
+      List<VariableDeclaration> variables = new List<VariableDeclaration>();
+      variables.add(
+          new VariableDeclaration(_createSyntheticIdentifier(), null, null));
+      return new TopLevelVariableDeclaration(commentAndMetadata.comment,
+          commentAndMetadata.metadata,
+          new VariableDeclarationList(null, null, null, returnType, variables),
+          semicolon);
+    }
+    if (_peek().matchesAny([
+      TokenType.OPEN_PAREN,
+      TokenType.FUNCTION,
+      TokenType.OPEN_CURLY_BRACKET
+    ])) {
+      _validateModifiersForTopLevelFunction(modifiers);
+      return _parseFunctionDeclaration(
+          commentAndMetadata, modifiers.externalKeyword, returnType);
+    }
+    return new TopLevelVariableDeclaration(commentAndMetadata.comment,
+        commentAndMetadata.metadata, _parseVariableDeclarationListAfterType(
+            null, _validateModifiersForTopLevelVariable(modifiers), returnType),
+        _expect(TokenType.SEMICOLON));
+  }
+
+  /**
+   * Parse a const expression. Return the const expression that was parsed.
+   *
+   *     constExpression ::=
+   *         instanceCreationExpression
+   *       | listLiteral
+   *       | mapLiteral
+   */
+  Expression _parseConstExpression() {
+    Token keyword = _expectKeyword(Keyword.CONST);
+    if (_matches(TokenType.OPEN_SQUARE_BRACKET) || _matches(TokenType.INDEX)) {
+      return _parseListLiteral(keyword, null);
+    } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+      return _parseMapLiteral(keyword, null);
+    } else if (_matches(TokenType.LT)) {
+      return _parseListOrMapLiteral(keyword);
+    }
+    return _parseInstanceCreationExpression(keyword);
+  }
+
+  ConstructorDeclaration _parseConstructor(
+      CommentAndMetadata commentAndMetadata, Token externalKeyword,
+      Token constKeyword, Token factoryKeyword, SimpleIdentifier returnType,
+      Token period, SimpleIdentifier name, FormalParameterList parameters) {
+    bool bodyAllowed = externalKeyword == null;
+    Token separator = null;
+    List<ConstructorInitializer> initializers = null;
+    if (_matches(TokenType.COLON)) {
+      separator = getAndAdvance();
+      initializers = new List<ConstructorInitializer>();
+      do {
+        if (_matchesKeyword(Keyword.THIS)) {
+          if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+            bodyAllowed = false;
+            initializers.add(_parseRedirectingConstructorInvocation());
+          } else if (_tokenMatches(_peek(), TokenType.PERIOD) &&
+              _tokenMatches(_peekAt(3), TokenType.OPEN_PAREN)) {
+            bodyAllowed = false;
+            initializers.add(_parseRedirectingConstructorInvocation());
+          } else {
+            initializers.add(_parseConstructorFieldInitializer());
+          }
+        } else if (_matchesKeyword(Keyword.SUPER)) {
+          initializers.add(_parseSuperConstructorInvocation());
+        } else if (_matches(TokenType.OPEN_CURLY_BRACKET) ||
+            _matches(TokenType.FUNCTION)) {
+          _reportErrorForCurrentToken(ParserErrorCode.MISSING_INITIALIZER);
+        } else {
+          initializers.add(_parseConstructorFieldInitializer());
+        }
+      } while (_optional(TokenType.COMMA));
+      if (factoryKeyword != null) {
+        _reportErrorForToken(
+            ParserErrorCode.FACTORY_WITH_INITIALIZERS, factoryKeyword);
+      }
+    }
+    ConstructorName redirectedConstructor = null;
+    FunctionBody body;
+    if (_matches(TokenType.EQ)) {
+      separator = getAndAdvance();
+      redirectedConstructor = parseConstructorName();
+      body = new EmptyFunctionBody(_expect(TokenType.SEMICOLON));
+      if (factoryKeyword == null) {
+        _reportErrorForNode(
+            ParserErrorCode.REDIRECTION_IN_NON_FACTORY_CONSTRUCTOR,
+            redirectedConstructor);
+      }
+    } else {
+      body = _parseFunctionBody(
+          true, ParserErrorCode.MISSING_FUNCTION_BODY, false);
+      if (constKeyword != null &&
+          factoryKeyword != null &&
+          externalKeyword == null) {
+        _reportErrorForToken(ParserErrorCode.CONST_FACTORY, factoryKeyword);
+      } else if (body is EmptyFunctionBody) {
+        if (factoryKeyword != null && externalKeyword == null) {
+          _reportErrorForToken(
+              ParserErrorCode.FACTORY_WITHOUT_BODY, factoryKeyword);
+        }
+      } else {
+        if (constKeyword != null) {
+          _reportErrorForNode(
+              ParserErrorCode.CONST_CONSTRUCTOR_WITH_BODY, body);
+        } else if (!bodyAllowed) {
+          _reportErrorForNode(
+              ParserErrorCode.EXTERNAL_CONSTRUCTOR_WITH_BODY, body);
+        }
+      }
+    }
+    return new ConstructorDeclaration(commentAndMetadata.comment,
+        commentAndMetadata.metadata, externalKeyword, constKeyword,
+        factoryKeyword, returnType, period, name, parameters, separator,
+        initializers, redirectedConstructor, body);
+  }
+
+  /**
+   * Parse a field initializer within a constructor. Return the field
+   * initializer that was parsed.
+   *
+   *     fieldInitializer:
+   *         ('this' '.')? identifier '=' conditionalExpression cascadeSection*
+   */
+  ConstructorFieldInitializer _parseConstructorFieldInitializer() {
+    Token keyword = null;
+    Token period = null;
+    if (_matchesKeyword(Keyword.THIS)) {
+      keyword = getAndAdvance();
+      period = _expect(TokenType.PERIOD);
+    }
+    SimpleIdentifier fieldName = parseSimpleIdentifier();
+    Token equals = null;
+    if (_matches(TokenType.EQ)) {
+      equals = getAndAdvance();
+    } else if (!_matchesKeyword(Keyword.THIS) &&
+        !_matchesKeyword(Keyword.SUPER) &&
+        !_matches(TokenType.OPEN_CURLY_BRACKET) &&
+        !_matches(TokenType.FUNCTION)) {
+      _reportErrorForCurrentToken(
+          ParserErrorCode.MISSING_ASSIGNMENT_IN_INITIALIZER);
+      equals = _createSyntheticToken(TokenType.EQ);
+    } else {
+      _reportErrorForCurrentToken(
+          ParserErrorCode.MISSING_ASSIGNMENT_IN_INITIALIZER);
+      return new ConstructorFieldInitializer(keyword, period, fieldName,
+          _createSyntheticToken(TokenType.EQ), _createSyntheticIdentifier());
+    }
+    bool wasInInitializer = _inInitializer;
+    _inInitializer = true;
+    try {
+      Expression expression = parseConditionalExpression();
+      TokenType tokenType = _currentToken.type;
+      if (tokenType == TokenType.PERIOD_PERIOD) {
+        List<Expression> cascadeSections = new List<Expression>();
+        while (tokenType == TokenType.PERIOD_PERIOD) {
+          Expression section = _parseCascadeSection();
+          if (section != null) {
+            cascadeSections.add(section);
+          }
+          tokenType = _currentToken.type;
+        }
+        expression = new CascadeExpression(expression, cascadeSections);
+      }
+      return new ConstructorFieldInitializer(
+          keyword, period, fieldName, equals, expression);
+    } finally {
+      _inInitializer = wasInInitializer;
+    }
+  }
+
+  /**
+   * Parse a continue statement. Return the continue statement that was parsed.
+   *
+   *     continueStatement ::=
+   *         'continue' identifier? ';'
+   */
+  Statement _parseContinueStatement() {
+    Token continueKeyword = _expectKeyword(Keyword.CONTINUE);
+    if (!_inLoop && !_inSwitch) {
+      _reportErrorForToken(
+          ParserErrorCode.CONTINUE_OUTSIDE_OF_LOOP, continueKeyword);
+    }
+    SimpleIdentifier label = null;
+    if (_matchesIdentifier()) {
+      label = parseSimpleIdentifier();
+    }
+    if (_inSwitch && !_inLoop && label == null) {
+      _reportErrorForToken(
+          ParserErrorCode.CONTINUE_WITHOUT_LABEL_IN_CASE, continueKeyword);
+    }
+    Token semicolon = _expect(TokenType.SEMICOLON);
+    return new ContinueStatement(continueKeyword, label, semicolon);
+  }
+
+  /**
+   * Parse a directive. The [commentAndMetadata] is the metadata to be
+   * associated with the directive. Return the directive that was parsed.
+   *
+   *     directive ::=
+   *         exportDirective
+   *       | libraryDirective
+   *       | importDirective
+   *       | partDirective
+   */
+  Directive _parseDirective(CommentAndMetadata commentAndMetadata) {
+    if (_matchesKeyword(Keyword.IMPORT)) {
+      return _parseImportDirective(commentAndMetadata);
+    } else if (_matchesKeyword(Keyword.EXPORT)) {
+      return _parseExportDirective(commentAndMetadata);
+    } else if (_matchesKeyword(Keyword.LIBRARY)) {
+      return _parseLibraryDirective(commentAndMetadata);
+    } else if (_matchesKeyword(Keyword.PART)) {
+      return _parsePartDirective(commentAndMetadata);
+    } else {
+      // Internal error: this method should not have been invoked if the current
+      // token was something other than one of the above.
+      throw new IllegalStateException(
+          "parseDirective invoked in an invalid state; currentToken = $_currentToken");
+    }
+  }
+
+  /**
+   * Parse the script tag and directives in a compilation unit until the first
+   * non-directive is encountered. Return the compilation unit that was parsed.
+   *
+   *     compilationUnit ::=
+   *         scriptTag? directive*
+   */
+  CompilationUnit _parseDirectives() {
+    Token firstToken = _currentToken;
+    ScriptTag scriptTag = null;
+    if (_matches(TokenType.SCRIPT_TAG)) {
+      scriptTag = new ScriptTag(getAndAdvance());
+    }
+    List<Directive> directives = new List<Directive>();
+    while (!_matches(TokenType.EOF)) {
+      CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
+      if ((_matchesKeyword(Keyword.IMPORT) ||
+              _matchesKeyword(Keyword.EXPORT) ||
+              _matchesKeyword(Keyword.LIBRARY) ||
+              _matchesKeyword(Keyword.PART)) &&
+          !_tokenMatches(_peek(), TokenType.PERIOD) &&
+          !_tokenMatches(_peek(), TokenType.LT) &&
+          !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+        directives.add(_parseDirective(commentAndMetadata));
+      } else if (_matches(TokenType.SEMICOLON)) {
+        _advance();
+      } else {
+        while (!_matches(TokenType.EOF)) {
+          _advance();
+        }
+        return new CompilationUnit(firstToken, scriptTag, directives,
+            new List<CompilationUnitMember>(), _currentToken);
+      }
+    }
+    return new CompilationUnit(firstToken, scriptTag, directives,
+        new List<CompilationUnitMember>(), _currentToken);
+  }
+
+  /**
+   * Parse a documentation comment. Return the documentation comment that was
+   * parsed, or `null` if there was no comment.
+   *
+   *     documentationComment ::=
+   *         multiLineComment?
+   *       | singleLineComment*
+   */
+  Comment _parseDocumentationComment() {
+    List<DocumentationCommentToken> documentationTokens =
+        <DocumentationCommentToken>[];
+    CommentToken commentToken = _currentToken.precedingComments;
+    while (commentToken != null) {
+      if (commentToken is DocumentationCommentToken) {
+        if (documentationTokens.isNotEmpty) {
+          if (commentToken.type == TokenType.SINGLE_LINE_COMMENT) {
+            if (documentationTokens[0].type != TokenType.SINGLE_LINE_COMMENT) {
+              documentationTokens.clear();
+            }
+          } else {
+            documentationTokens.clear();
+          }
+        }
+        documentationTokens.add(commentToken);
+      }
+      commentToken = commentToken.next;
+    }
+    if (documentationTokens.isEmpty) {
+      return null;
+    }
+    List<CommentReference> references =
+        _parseCommentReferences(documentationTokens);
+    return Comment.createDocumentationCommentWithReferences(
+        documentationTokens, references);
+  }
+
+  /**
+   * Parse a do statement. Return the do statement that was parsed.
+   *
+   *     doStatement ::=
+   *         'do' statement 'while' '(' expression ')' ';'
+   */
+  Statement _parseDoStatement() {
+    bool wasInLoop = _inLoop;
+    _inLoop = true;
+    try {
+      Token doKeyword = _expectKeyword(Keyword.DO);
+      Statement body = parseStatement2();
+      Token whileKeyword = _expectKeyword(Keyword.WHILE);
+      Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
+      Expression condition = parseExpression2();
+      Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+      Token semicolon = _expect(TokenType.SEMICOLON);
+      return new DoStatement(doKeyword, body, whileKeyword, leftParenthesis,
+          condition, rightParenthesis, semicolon);
+    } finally {
+      _inLoop = wasInLoop;
+    }
+  }
+
+  /**
+   * Parse an empty statement. Return the empty statement that was parsed.
+   *
+   *     emptyStatement ::=
+   *         ';'
+   */
+  Statement _parseEmptyStatement() => new EmptyStatement(getAndAdvance());
+
+  EnumConstantDeclaration _parseEnumConstantDeclaration() {
+    CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
+    SimpleIdentifier name;
+    if (_matchesIdentifier()) {
+      name = parseSimpleIdentifier();
+    } else {
+      name = _createSyntheticIdentifier();
+    }
+    if (commentAndMetadata.metadata.isNotEmpty) {
+      _reportErrorForNode(ParserErrorCode.ANNOTATION_ON_ENUM_CONSTANT,
+          commentAndMetadata.metadata[0]);
+    }
+    return new EnumConstantDeclaration(
+        commentAndMetadata.comment, commentAndMetadata.metadata, name);
+  }
+
+  /**
+   * Parse an enum declaration. The [commentAndMetadata] is the metadata to be
+   * associated with the member. Return the enum declaration that was parsed.
+   *
+   *     enumType ::=
+   *         metadata 'enum' id '{' id (',' id)* (',')? '}'
+   */
+  EnumDeclaration _parseEnumDeclaration(CommentAndMetadata commentAndMetadata) {
+    Token keyword = _expectKeyword(Keyword.ENUM);
+    SimpleIdentifier name = parseSimpleIdentifier();
+    Token leftBracket = null;
+    List<EnumConstantDeclaration> constants =
+        new List<EnumConstantDeclaration>();
+    Token rightBracket = null;
+    if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+      leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
+      if (_matchesIdentifier() || _matches(TokenType.AT)) {
+        constants.add(_parseEnumConstantDeclaration());
+      } else if (_matches(TokenType.COMMA) &&
+          _tokenMatchesIdentifier(_peek())) {
+        constants.add(_parseEnumConstantDeclaration());
+        _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+      } else {
+        constants.add(_parseEnumConstantDeclaration());
+        _reportErrorForCurrentToken(ParserErrorCode.EMPTY_ENUM_BODY);
+      }
+      while (_optional(TokenType.COMMA)) {
+        if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+          break;
+        }
+        constants.add(_parseEnumConstantDeclaration());
+      }
+      rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
+    } else {
+      leftBracket = _createSyntheticToken(TokenType.OPEN_CURLY_BRACKET);
+      rightBracket = _createSyntheticToken(TokenType.CLOSE_CURLY_BRACKET);
+      _reportErrorForCurrentToken(ParserErrorCode.MISSING_ENUM_BODY);
+    }
+    return new EnumDeclaration(commentAndMetadata.comment,
+        commentAndMetadata.metadata, keyword, name, leftBracket, constants,
+        rightBracket);
+  }
+
+  /**
+   * Parse an equality expression. Return the equality expression that was
+   * parsed.
+   *
+   *     equalityExpression ::=
+   *         relationalExpression (equalityOperator relationalExpression)?
+   *       | 'super' equalityOperator relationalExpression
+   */
+  Expression _parseEqualityExpression() {
+    Expression expression;
+    if (_matchesKeyword(Keyword.SUPER) &&
+        _currentToken.next.type.isEqualityOperator) {
+      expression = new SuperExpression(getAndAdvance());
+    } else {
+      expression = _parseRelationalExpression();
+    }
+    bool leftEqualityExpression = false;
+    while (_currentToken.type.isEqualityOperator) {
+      Token operator = getAndAdvance();
+      if (leftEqualityExpression) {
+        _reportErrorForNode(
+            ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND, expression);
+      }
+      expression = new BinaryExpression(
+          expression, operator, _parseRelationalExpression());
+      leftEqualityExpression = true;
+    }
+    return expression;
+  }
+
+  /**
+   * Parse an export directive. The [commentAndMetadata] is the metadata to be
+   * associated with the directive. Return the export directive that was parsed.
+   *
+   *     exportDirective ::=
+   *         metadata 'export' stringLiteral combinator*';'
+   */
+  ExportDirective _parseExportDirective(CommentAndMetadata commentAndMetadata) {
+    Token exportKeyword = _expectKeyword(Keyword.EXPORT);
+    StringLiteral libraryUri = _parseUri();
+    List<Combinator> combinators = _parseCombinators();
+    Token semicolon = _expectSemicolon();
+    return new ExportDirective(commentAndMetadata.comment,
+        commentAndMetadata.metadata, exportKeyword, libraryUri, combinators,
+        semicolon);
+  }
+
+  /**
+   * Parse a list of expressions. Return the expression that was parsed.
+   *
+   *     expressionList ::=
+   *         expression (',' expression)*
+   */
+  List<Expression> _parseExpressionList() {
+    List<Expression> expressions = new List<Expression>();
+    expressions.add(parseExpression2());
+    while (_optional(TokenType.COMMA)) {
+      expressions.add(parseExpression2());
+    }
+    return expressions;
+  }
+
+  /**
+   * Parse the 'final', 'const', 'var' or type preceding a variable declaration.
+   * The [optional] is `true` if the keyword and type are optional. Return the
+   * 'final', 'const', 'var' or type that was parsed.
+   *
+   *     finalConstVarOrType ::=
+   *         'final' type?
+   *       | 'const' type?
+   *       | 'var'
+   *       | type
+   */
+  FinalConstVarOrType _parseFinalConstVarOrType(bool optional) {
+    Token keyword = null;
+    TypeName type = null;
+    if (_matchesKeyword(Keyword.FINAL) || _matchesKeyword(Keyword.CONST)) {
+      keyword = getAndAdvance();
+      if (_isTypedIdentifier(_currentToken)) {
+        type = parseTypeName();
+      }
+    } else if (_matchesKeyword(Keyword.VAR)) {
+      keyword = getAndAdvance();
+    } else {
+      if (_isTypedIdentifier(_currentToken)) {
+        type = parseReturnType();
+      } else if (!optional) {
+        _reportErrorForCurrentToken(
+            ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE);
+      }
+    }
+    return new FinalConstVarOrType(keyword, type);
+  }
+
+  /**
+   * Parse a formal parameter. At most one of `isOptional` and `isNamed` can be
+   * `true`. The [kind] is the kind of parameter being expected based on the
+   * presence or absence of group delimiters. Return the formal parameter that
+   * was parsed.
+   *
+   *     defaultFormalParameter ::=
+   *         normalFormalParameter ('=' expression)?
+   *
+   *     defaultNamedParameter ::=
+   *         normalFormalParameter (':' expression)?
+   */
+  FormalParameter _parseFormalParameter(ParameterKind kind) {
+    NormalFormalParameter parameter = parseNormalFormalParameter();
+    if (_matches(TokenType.EQ)) {
+      Token seperator = getAndAdvance();
+      Expression defaultValue = parseExpression2();
+      if (kind == ParameterKind.NAMED) {
+        _reportErrorForToken(
+            ParserErrorCode.WRONG_SEPARATOR_FOR_NAMED_PARAMETER, seperator);
+      } else if (kind == ParameterKind.REQUIRED) {
+        _reportErrorForNode(
+            ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP, parameter);
+      }
+      return new DefaultFormalParameter(
+          parameter, kind, seperator, defaultValue);
+    } else if (_matches(TokenType.COLON)) {
+      Token seperator = getAndAdvance();
+      Expression defaultValue = parseExpression2();
+      if (kind == ParameterKind.POSITIONAL) {
+        _reportErrorForToken(
+            ParserErrorCode.WRONG_SEPARATOR_FOR_POSITIONAL_PARAMETER,
+            seperator);
+      } else if (kind == ParameterKind.REQUIRED) {
+        _reportErrorForNode(
+            ParserErrorCode.NAMED_PARAMETER_OUTSIDE_GROUP, parameter);
+      }
+      return new DefaultFormalParameter(
+          parameter, kind, seperator, defaultValue);
+    } else if (kind != ParameterKind.REQUIRED) {
+      return new DefaultFormalParameter(parameter, kind, null, null);
+    }
+    return parameter;
+  }
+
+  /**
+   * Parse a for statement. Return the for statement that was parsed.
+   *
+   *     forStatement ::=
+   *         'for' '(' forLoopParts ')' statement
+   *
+   *     forLoopParts ::=
+   *         forInitializerStatement expression? ';' expressionList?
+   *       | declaredIdentifier 'in' expression
+   *       | identifier 'in' expression
+   *
+   *     forInitializerStatement ::=
+   *         localVariableDeclaration ';'
+   *       | expression? ';'
+   */
+  Statement _parseForStatement() {
+    bool wasInLoop = _inLoop;
+    _inLoop = true;
+    try {
+      Token awaitKeyword = null;
+      if (_matchesString(_AWAIT)) {
+        awaitKeyword = getAndAdvance();
+      }
+      Token forKeyword = _expectKeyword(Keyword.FOR);
+      Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
+      VariableDeclarationList variableList = null;
+      Expression initialization = null;
+      if (!_matches(TokenType.SEMICOLON)) {
+        CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
+        if (_matchesIdentifier() &&
+            (_tokenMatchesKeyword(_peek(), Keyword.IN) ||
+                _tokenMatches(_peek(), TokenType.COLON))) {
+          List<VariableDeclaration> variables = new List<VariableDeclaration>();
+          SimpleIdentifier variableName = parseSimpleIdentifier();
+          variables.add(new VariableDeclaration(variableName, null, null));
+          variableList = new VariableDeclarationList(commentAndMetadata.comment,
+              commentAndMetadata.metadata, null, null, variables);
+        } else if (_isInitializedVariableDeclaration()) {
+          variableList =
+              _parseVariableDeclarationListAfterMetadata(commentAndMetadata);
+        } else {
+          initialization = parseExpression2();
+        }
+        if (_matchesKeyword(Keyword.IN) || _matches(TokenType.COLON)) {
+          if (_matches(TokenType.COLON)) {
+            _reportErrorForCurrentToken(ParserErrorCode.COLON_IN_PLACE_OF_IN);
+          }
+          DeclaredIdentifier loopVariable = null;
+          SimpleIdentifier identifier = null;
+          if (variableList == null) {
+            // We found: <expression> 'in'
+            _reportErrorForCurrentToken(
+                ParserErrorCode.MISSING_VARIABLE_IN_FOR_EACH);
+          } else {
+            NodeList<VariableDeclaration> variables = variableList.variables;
+            if (variables.length > 1) {
+              _reportErrorForCurrentToken(
+                  ParserErrorCode.MULTIPLE_VARIABLES_IN_FOR_EACH,
+                  [variables.length.toString()]);
+            }
+            VariableDeclaration variable = variables[0];
+            if (variable.initializer != null) {
+              _reportErrorForCurrentToken(
+                  ParserErrorCode.INITIALIZED_VARIABLE_IN_FOR_EACH);
+            }
+            Token keyword = variableList.keyword;
+            TypeName type = variableList.type;
+            if (keyword != null || type != null) {
+              loopVariable = new DeclaredIdentifier(commentAndMetadata.comment,
+                  commentAndMetadata.metadata, keyword, type, variable.name);
+            } else {
+              if (!commentAndMetadata.metadata.isEmpty) {
+                // TODO(jwren) metadata isn't allowed before the identifier in
+                // "identifier in expression", add warning if commentAndMetadata
+                // has content
+              }
+              identifier = variable.name;
+            }
+          }
+          Token inKeyword = getAndAdvance();
+          Expression iterator = parseExpression2();
+          Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+          Statement body = parseStatement2();
+          if (loopVariable == null) {
+            return new ForEachStatement.con2(awaitKeyword, forKeyword,
+                leftParenthesis, identifier, inKeyword, iterator,
+                rightParenthesis, body);
+          }
+          return new ForEachStatement.con1(awaitKeyword, forKeyword,
+              leftParenthesis, loopVariable, inKeyword, iterator,
+              rightParenthesis, body);
+        }
+      }
+      if (awaitKeyword != null) {
+        _reportErrorForToken(
+            ParserErrorCode.INVALID_AWAIT_IN_FOR, awaitKeyword);
+      }
+      Token leftSeparator = _expect(TokenType.SEMICOLON);
+      Expression condition = null;
+      if (!_matches(TokenType.SEMICOLON)) {
+        condition = parseExpression2();
+      }
+      Token rightSeparator = _expect(TokenType.SEMICOLON);
+      List<Expression> updaters = null;
+      if (!_matches(TokenType.CLOSE_PAREN)) {
+        updaters = _parseExpressionList();
+      }
+      Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+      Statement body = parseStatement2();
+      return new ForStatement(forKeyword, leftParenthesis, variableList,
+          initialization, leftSeparator, condition, rightSeparator, updaters,
+          rightParenthesis, body);
+    } finally {
+      _inLoop = wasInLoop;
+    }
+  }
+
+  /**
+   * Parse a function body. The [mayBeEmpty] is `true` if the function body is
+   * allowed to be empty. The [emptyErrorCode] is the error code to report if
+   * function body expected, but not found. The [inExpression] is `true` if the
+   * function body is being parsed as part of an expression and therefore does
+   * not have a terminating semicolon. Return the function body that was parsed.
+   *
+   *     functionBody ::=
+   *         '=>' expression ';'
+   *       | block
+   *
+   *     functionExpressionBody ::=
+   *         '=>' expression
+   *       | block
+   */
+  FunctionBody _parseFunctionBody(
+      bool mayBeEmpty, ParserErrorCode emptyErrorCode, bool inExpression) {
+    bool wasInAsync = _inAsync;
+    bool wasInGenerator = _inGenerator;
+    bool wasInLoop = _inLoop;
+    bool wasInSwitch = _inSwitch;
+    _inAsync = false;
+    _inGenerator = false;
+    _inLoop = false;
+    _inSwitch = false;
+    try {
+      if (_matches(TokenType.SEMICOLON)) {
+        if (!mayBeEmpty) {
+          _reportErrorForCurrentToken(emptyErrorCode);
+        }
+        return new EmptyFunctionBody(getAndAdvance());
+      } else if (_matchesString(_NATIVE)) {
+        Token nativeToken = getAndAdvance();
+        StringLiteral stringLiteral = null;
+        if (_matches(TokenType.STRING)) {
+          stringLiteral = parseStringLiteral();
+        }
+        return new NativeFunctionBody(
+            nativeToken, stringLiteral, _expect(TokenType.SEMICOLON));
+      }
+      Token keyword = null;
+      Token star = null;
+      if (_matchesString(ASYNC)) {
+        keyword = getAndAdvance();
+        if (_matches(TokenType.STAR)) {
+          star = getAndAdvance();
+          _inGenerator = true;
+        }
+        _inAsync = true;
+      } else if (_matchesString(SYNC)) {
+        keyword = getAndAdvance();
+        if (_matches(TokenType.STAR)) {
+          star = getAndAdvance();
+          _inGenerator = true;
+        }
+      }
+      if (_matches(TokenType.FUNCTION)) {
+        if (keyword != null) {
+          if (!_tokenMatchesString(keyword, ASYNC)) {
+            _reportErrorForToken(ParserErrorCode.INVALID_SYNC, keyword);
+            keyword = null;
+          } else if (star != null) {
+            _reportErrorForToken(
+                ParserErrorCode.INVALID_STAR_AFTER_ASYNC, star);
+          }
+        }
+        Token functionDefinition = getAndAdvance();
+        if (_matchesKeyword(Keyword.RETURN)) {
+          _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
+              [_currentToken.lexeme]);
+          _advance();
+        }
+        Expression expression = parseExpression2();
+        Token semicolon = null;
+        if (!inExpression) {
+          semicolon = _expect(TokenType.SEMICOLON);
+        }
+        if (!_parseFunctionBodies) {
+          return new EmptyFunctionBody(
+              _createSyntheticToken(TokenType.SEMICOLON));
+        }
+        return new ExpressionFunctionBody(
+            keyword, functionDefinition, expression, semicolon);
+      } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+        if (keyword != null) {
+          if (_tokenMatchesString(keyword, SYNC) && star == null) {
+            _reportErrorForToken(
+                ParserErrorCode.MISSING_STAR_AFTER_SYNC, keyword);
+          }
+        }
+        if (!_parseFunctionBodies) {
+          _skipBlock();
+          return new EmptyFunctionBody(
+              _createSyntheticToken(TokenType.SEMICOLON));
+        }
+        return new BlockFunctionBody(keyword, star, parseBlock());
+      } else {
+        // Invalid function body
+        _reportErrorForCurrentToken(emptyErrorCode);
+        return new EmptyFunctionBody(
+            _createSyntheticToken(TokenType.SEMICOLON));
+      }
+    } finally {
+      _inAsync = wasInAsync;
+      _inGenerator = wasInGenerator;
+      _inLoop = wasInLoop;
+      _inSwitch = wasInSwitch;
+    }
+  }
+
+  /**
+   * Parse a function declaration. The [commentAndMetadata] is the documentation
+   * comment and metadata to be associated with the declaration. The
+   * [externalKeyword] is the 'external' keyword, or `null` if the function is
+   * not external. The [returnType] is the return type, or `null` if there is no
+   * return type. The [isStatement] is `true` if the function declaration is
+   * being parsed as a statement. Return the function declaration that was
+   * parsed.
+   *
+   *     functionDeclaration ::=
+   *         functionSignature functionBody
+   *       | returnType? getOrSet identifier formalParameterList functionBody
+   */
+  FunctionDeclaration _parseFunctionDeclaration(
+      CommentAndMetadata commentAndMetadata, Token externalKeyword,
+      TypeName returnType) {
+    Token keyword = null;
+    bool isGetter = false;
+    if (_matchesKeyword(Keyword.GET) &&
+        !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+      keyword = getAndAdvance();
+      isGetter = true;
+    } else if (_matchesKeyword(Keyword.SET) &&
+        !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+      keyword = getAndAdvance();
+    }
+    SimpleIdentifier name = parseSimpleIdentifier();
+    FormalParameterList parameters = null;
+    if (!isGetter) {
+      if (_matches(TokenType.OPEN_PAREN)) {
+        parameters = parseFormalParameterList();
+        _validateFormalParameterList(parameters);
+      } else {
+        _reportErrorForCurrentToken(
+            ParserErrorCode.MISSING_FUNCTION_PARAMETERS);
+        parameters = new FormalParameterList(
+            _createSyntheticToken(TokenType.OPEN_PAREN), null, null, null,
+            _createSyntheticToken(TokenType.CLOSE_PAREN));
+      }
+    } else if (_matches(TokenType.OPEN_PAREN)) {
+      _reportErrorForCurrentToken(ParserErrorCode.GETTER_WITH_PARAMETERS);
+      parseFormalParameterList();
+    }
+    FunctionBody body;
+    if (externalKeyword == null) {
+      body = _parseFunctionBody(
+          false, ParserErrorCode.MISSING_FUNCTION_BODY, false);
+    } else {
+      body = new EmptyFunctionBody(_expect(TokenType.SEMICOLON));
+    }
+//        if (!isStatement && matches(TokenType.SEMICOLON)) {
+//          // TODO(brianwilkerson) Improve this error message.
+//          reportError(ParserErrorCode.UNEXPECTED_TOKEN, currentToken.getLexeme());
+//          advance();
+//        }
+    return new FunctionDeclaration(commentAndMetadata.comment,
+        commentAndMetadata.metadata, externalKeyword, returnType, keyword, name,
+        new FunctionExpression(parameters, body));
+  }
+
+  /**
+   * Parse a function declaration statement. Return the function declaration
+   * statement that was parsed.
+   *
+   *     functionDeclarationStatement ::=
+   *         functionSignature functionBody
+   */
+  Statement _parseFunctionDeclarationStatement() {
+    Modifiers modifiers = _parseModifiers();
+    _validateModifiersForFunctionDeclarationStatement(modifiers);
+    return _parseFunctionDeclarationStatementAfterReturnType(
+        _parseCommentAndMetadata(), _parseOptionalReturnType());
+  }
+
+  /**
+   * Parse a function declaration statement. The [commentAndMetadata] is the
+   * documentation comment and metadata to be associated with the declaration.
+   * The [returnType] is the return type, or `null` if there is no return type.
+   * Return the function declaration statement that was parsed.
+   *
+   *     functionDeclarationStatement ::=
+   *         functionSignature functionBody
+   */
+  Statement _parseFunctionDeclarationStatementAfterReturnType(
+      CommentAndMetadata commentAndMetadata, TypeName returnType) {
+    FunctionDeclaration declaration =
+        _parseFunctionDeclaration(commentAndMetadata, null, returnType);
+    Token propertyKeyword = declaration.propertyKeyword;
+    if (propertyKeyword != null) {
+      if ((propertyKeyword as KeywordToken).keyword == Keyword.GET) {
+        _reportErrorForToken(
+            ParserErrorCode.GETTER_IN_FUNCTION, propertyKeyword);
+      } else {
+        _reportErrorForToken(
+            ParserErrorCode.SETTER_IN_FUNCTION, propertyKeyword);
+      }
+    }
+    return new FunctionDeclarationStatement(declaration);
+  }
+
+  /**
+   * Parse a function type alias. The [commentAndMetadata] is the metadata to be
+   * associated with the member. The [keyword] is the token representing the
+   * 'typedef' keyword. Return the function type alias that was parsed.
+   *
+   *     functionTypeAlias ::=
+   *         functionPrefix typeParameterList? formalParameterList ';'
+   *
+   *     functionPrefix ::=
+   *         returnType? name
+   */
+  FunctionTypeAlias _parseFunctionTypeAlias(
+      CommentAndMetadata commentAndMetadata, Token keyword) {
+    TypeName returnType = null;
+    if (hasReturnTypeInTypeAlias) {
+      returnType = parseReturnType();
+    }
+    SimpleIdentifier name = parseSimpleIdentifier();
+    TypeParameterList typeParameters = null;
+    if (_matches(TokenType.LT)) {
+      typeParameters = parseTypeParameterList();
+    }
+    if (_matches(TokenType.SEMICOLON) || _matches(TokenType.EOF)) {
+      _reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS);
+      FormalParameterList parameters = new FormalParameterList(
+          _createSyntheticToken(TokenType.OPEN_PAREN), null, null, null,
+          _createSyntheticToken(TokenType.CLOSE_PAREN));
+      Token semicolon = _expect(TokenType.SEMICOLON);
+      return new FunctionTypeAlias(commentAndMetadata.comment,
+          commentAndMetadata.metadata, keyword, returnType, name,
+          typeParameters, parameters, semicolon);
+    } else if (!_matches(TokenType.OPEN_PAREN)) {
+      _reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS);
+      // TODO(brianwilkerson) Recover from this error. At the very least we
+      // should skip to the start of the next valid compilation unit member,
+      // allowing for the possibility of finding the typedef parameters before
+      // that point.
+      return new FunctionTypeAlias(commentAndMetadata.comment,
+          commentAndMetadata.metadata, keyword, returnType, name,
+          typeParameters, new FormalParameterList(
+              _createSyntheticToken(TokenType.OPEN_PAREN), null, null, null,
+              _createSyntheticToken(TokenType.CLOSE_PAREN)),
+          _createSyntheticToken(TokenType.SEMICOLON));
+    }
+    FormalParameterList parameters = parseFormalParameterList();
+    _validateFormalParameterList(parameters);
+    Token semicolon = _expect(TokenType.SEMICOLON);
+    return new FunctionTypeAlias(commentAndMetadata.comment,
+        commentAndMetadata.metadata, keyword, returnType, name, typeParameters,
+        parameters, semicolon);
+  }
+
+  /**
+   * Parse a getter. The [commentAndMetadata] is the documentation comment and
+   * metadata to be associated with the declaration. The externalKeyword] is the
+   * 'external' token. The staticKeyword] is the static keyword, or `null` if
+   * the getter is not static. The [returnType] the return type that has already
+   * been parsed, or `null` if there was no return type. Return the getter that
+   * was parsed.
+   *
+   *     getter ::=
+   *         getterSignature functionBody?
+   *
+   *     getterSignature ::=
+   *         'external'? 'static'? returnType? 'get' identifier
+   */
+  MethodDeclaration _parseGetter(CommentAndMetadata commentAndMetadata,
+      Token externalKeyword, Token staticKeyword, TypeName returnType) {
+    Token propertyKeyword = _expectKeyword(Keyword.GET);
+    SimpleIdentifier name = parseSimpleIdentifier();
+    if (_matches(TokenType.OPEN_PAREN) &&
+        _tokenMatches(_peek(), TokenType.CLOSE_PAREN)) {
+      _reportErrorForCurrentToken(ParserErrorCode.GETTER_WITH_PARAMETERS);
+      _advance();
+      _advance();
+    }
+    FunctionBody body = _parseFunctionBody(
+        externalKeyword != null || staticKeyword == null,
+        ParserErrorCode.STATIC_GETTER_WITHOUT_BODY, false);
+    if (externalKeyword != null && body is! EmptyFunctionBody) {
+      _reportErrorForCurrentToken(ParserErrorCode.EXTERNAL_GETTER_WITH_BODY);
+    }
+    return new MethodDeclaration(commentAndMetadata.comment,
+        commentAndMetadata.metadata, externalKeyword, staticKeyword, returnType,
+        propertyKeyword, null, name, null, body);
+  }
+
+  /**
+   * Parse a list of identifiers. Return the list of identifiers that were
+   * parsed.
+   *
+   *     identifierList ::=
+   *         identifier (',' identifier)*
+   */
+  List<SimpleIdentifier> _parseIdentifierList() {
+    List<SimpleIdentifier> identifiers = new List<SimpleIdentifier>();
+    identifiers.add(parseSimpleIdentifier());
+    while (_matches(TokenType.COMMA)) {
+      _advance();
+      identifiers.add(parseSimpleIdentifier());
+    }
+    return identifiers;
+  }
+
+  /**
+   * Parse an if statement. Return the if statement that was parsed.
+   *
+   *     ifStatement ::=
+   *         'if' '(' expression ')' statement ('else' statement)?
+   */
+  Statement _parseIfStatement() {
+    Token ifKeyword = _expectKeyword(Keyword.IF);
+    Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
+    Expression condition = parseExpression2();
+    Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+    Statement thenStatement = parseStatement2();
+    Token elseKeyword = null;
+    Statement elseStatement = null;
+    if (_matchesKeyword(Keyword.ELSE)) {
+      elseKeyword = getAndAdvance();
+      elseStatement = parseStatement2();
+    }
+    return new IfStatement(ifKeyword, leftParenthesis, condition,
+        rightParenthesis, thenStatement, elseKeyword, elseStatement);
+  }
+
+  /**
+   * Parse an import directive. The [commentAndMetadata] is the metadata to be
+   * associated with the directive. Return the import directive that was parsed.
+   *
+   *     importDirective ::=
+   *         metadata 'import' stringLiteral (deferred)? ('as' identifier)? combinator*';'
+   */
+  ImportDirective _parseImportDirective(CommentAndMetadata commentAndMetadata) {
+    Token importKeyword = _expectKeyword(Keyword.IMPORT);
+    StringLiteral libraryUri = _parseUri();
+    Token deferredToken = null;
+    Token asToken = null;
+    SimpleIdentifier prefix = null;
+    if (_matchesKeyword(Keyword.DEFERRED)) {
+      deferredToken = getAndAdvance();
+    }
+    if (_matchesKeyword(Keyword.AS)) {
+      asToken = getAndAdvance();
+      prefix = parseSimpleIdentifier();
+    } else if (deferredToken != null) {
+      _reportErrorForCurrentToken(
+          ParserErrorCode.MISSING_PREFIX_IN_DEFERRED_IMPORT);
+    } else if (!_matches(TokenType.SEMICOLON) &&
+        !_matchesString(_SHOW) &&
+        !_matchesString(_HIDE)) {
+      Token nextToken = _peek();
+      if (_tokenMatchesKeyword(nextToken, Keyword.AS) ||
+          _tokenMatchesString(nextToken, _SHOW) ||
+          _tokenMatchesString(nextToken, _HIDE)) {
+        _reportErrorForCurrentToken(
+            ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken]);
+        _advance();
+        if (_matchesKeyword(Keyword.AS)) {
+          asToken = getAndAdvance();
+          prefix = parseSimpleIdentifier();
+        }
+      }
+    }
+    List<Combinator> combinators = _parseCombinators();
+    Token semicolon = _expectSemicolon();
+    return new ImportDirective(commentAndMetadata.comment,
+        commentAndMetadata.metadata, importKeyword, libraryUri, deferredToken,
+        asToken, prefix, combinators, semicolon);
+  }
+
+  /**
+   * Parse a list of initialized identifiers. The [commentAndMetadata] is the
+   * documentation comment and metadata to be associated with the declaration.
+   * The [staticKeyword] is the static keyword, or `null` if the getter is not
+   * static. The [keyword] is the token representing the 'final', 'const' or
+   * 'var' keyword, or `null` if there is no keyword. The [type] is the type
+   * that has already been parsed, or `null` if 'var' was provided. Return the
+   * getter that was parsed.
+   *
+   *     ?? ::=
+   *         'static'? ('var' | type) initializedIdentifierList ';'
+   *       | 'final' type? initializedIdentifierList ';'
+   *
+   *     initializedIdentifierList ::=
+   *         initializedIdentifier (',' initializedIdentifier)*
+   *
+   *     initializedIdentifier ::=
+   *         identifier ('=' expression)?
+   */
+  FieldDeclaration _parseInitializedIdentifierList(
+      CommentAndMetadata commentAndMetadata, Token staticKeyword, Token keyword,
+      TypeName type) {
+    VariableDeclarationList fieldList =
+        _parseVariableDeclarationListAfterType(null, keyword, type);
+    return new FieldDeclaration(commentAndMetadata.comment,
+        commentAndMetadata.metadata, staticKeyword, fieldList,
+        _expect(TokenType.SEMICOLON));
+  }
+
+  /**
+   * Parse an instance creation expression. The [keyword] is the 'new' or
+   * 'const' keyword that introduces the expression. Return the instance
+   * creation expression that was parsed.
+   *
+   *     instanceCreationExpression ::=
+   *         ('new' | 'const') type ('.' identifier)? argumentList
+   */
+  InstanceCreationExpression _parseInstanceCreationExpression(Token keyword) {
+    ConstructorName constructorName = parseConstructorName();
+    ArgumentList argumentList = parseArgumentList();
+    return new InstanceCreationExpression(
+        keyword, constructorName, argumentList);
+  }
+
+  /**
+   * Parse a library directive. The [commentAndMetadata] is the metadata to be
+   * associated with the directive. Return the library directive that was
+   * parsed.
+   *
+   *     libraryDirective ::=
+   *         metadata 'library' identifier ';'
+   */
+  LibraryDirective _parseLibraryDirective(
+      CommentAndMetadata commentAndMetadata) {
+    Token keyword = _expectKeyword(Keyword.LIBRARY);
+    LibraryIdentifier libraryName = _parseLibraryName(
+        ParserErrorCode.MISSING_NAME_IN_LIBRARY_DIRECTIVE, keyword);
+    Token semicolon = _expect(TokenType.SEMICOLON);
+    return new LibraryDirective(commentAndMetadata.comment,
+        commentAndMetadata.metadata, keyword, libraryName, semicolon);
+  }
+
+  /**
+   * Parse a library name. The [missingNameError] is the error code to be used
+   * if the library name is missing. The [missingNameToken] is the token
+   * associated with the error produced if the library name is missing. Return
+   * the library name that was parsed.
+   *
+   *     libraryName ::=
+   *         libraryIdentifier
+   */
+  LibraryIdentifier _parseLibraryName(
+      ParserErrorCode missingNameError, Token missingNameToken) {
+    if (_matchesIdentifier()) {
+      return parseLibraryIdentifier();
+    } else if (_matches(TokenType.STRING)) {
+      // TODO(brianwilkerson) Recovery: This should be extended to handle
+      // arbitrary tokens until we can find a token that can start a compilation
+      // unit member.
+      StringLiteral string = parseStringLiteral();
+      _reportErrorForNode(ParserErrorCode.NON_IDENTIFIER_LIBRARY_NAME, string);
+    } else {
+      _reportErrorForToken(missingNameError, missingNameToken);
+    }
+    List<SimpleIdentifier> components = new List<SimpleIdentifier>();
+    components.add(_createSyntheticIdentifier());
+    return new LibraryIdentifier(components);
+  }
+
+  /**
+   * Parse a list literal. The [modifier] is the 'const' modifier appearing
+   * before the literal, or `null` if there is no modifier. The [typeArguments]
+   * is the type arguments appearing before the literal, or `null` if there are
+   * no type arguments. Return the list literal that was parsed.
+   *
+   *     listLiteral ::=
+   *         'const'? typeArguments? '[' (expressionList ','?)? ']'
+   */
+  ListLiteral _parseListLiteral(
+      Token modifier, TypeArgumentList typeArguments) {
+    // may be empty list literal
+    if (_matches(TokenType.INDEX)) {
+      BeginToken leftBracket = _createToken(
+          _currentToken, TokenType.OPEN_SQUARE_BRACKET, isBegin: true);
+      Token rightBracket =
+          new Token(TokenType.CLOSE_SQUARE_BRACKET, _currentToken.offset + 1);
+      leftBracket.endToken = rightBracket;
+      rightBracket.setNext(_currentToken.next);
+      leftBracket.setNext(rightBracket);
+      _currentToken.previous.setNext(leftBracket);
+      _currentToken = _currentToken.next;
+      return new ListLiteral(
+          modifier, typeArguments, leftBracket, null, rightBracket);
+    }
+    // open
+    Token leftBracket = _expect(TokenType.OPEN_SQUARE_BRACKET);
+    if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
+      return new ListLiteral(
+          modifier, typeArguments, leftBracket, null, getAndAdvance());
+    }
+    bool wasInInitializer = _inInitializer;
+    _inInitializer = false;
+    try {
+      List<Expression> elements = new List<Expression>();
+      elements.add(parseExpression2());
+      while (_optional(TokenType.COMMA)) {
+        if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
+          return new ListLiteral(
+              modifier, typeArguments, leftBracket, elements, getAndAdvance());
+        }
+        elements.add(parseExpression2());
+      }
+      Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
+      return new ListLiteral(
+          modifier, typeArguments, leftBracket, elements, rightBracket);
+    } finally {
+      _inInitializer = wasInInitializer;
+    }
+  }
+
+  /**
+   * Parse a list or map literal. The [modifier] is the 'const' modifier
+   * appearing before the literal, or `null` if there is no modifier. Return the
+   * list or map literal that was parsed.
+   *
+   *     listOrMapLiteral ::=
+   *         listLiteral
+   *       | mapLiteral
+   */
+  TypedLiteral _parseListOrMapLiteral(Token modifier) {
+    TypeArgumentList typeArguments = null;
+    if (_matches(TokenType.LT)) {
+      typeArguments = parseTypeArgumentList();
+    }
+    if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+      return _parseMapLiteral(modifier, typeArguments);
+    } else if (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
+        _matches(TokenType.INDEX)) {
+      return _parseListLiteral(modifier, typeArguments);
+    }
+    _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL);
+    return new ListLiteral(modifier, typeArguments,
+        _createSyntheticToken(TokenType.OPEN_SQUARE_BRACKET), null,
+        _createSyntheticToken(TokenType.CLOSE_SQUARE_BRACKET));
+  }
+
+  /**
+   * Parse a logical and expression. Return the logical and expression that was
+   * parsed.
+   *
+   *     logicalAndExpression ::=
+   *         equalityExpression ('&&' equalityExpression)*
+   */
+  Expression _parseLogicalAndExpression() {
+    Expression expression = _parseEqualityExpression();
+    while (_matches(TokenType.AMPERSAND_AMPERSAND)) {
+      Token operator = getAndAdvance();
+      expression = new BinaryExpression(
+          expression, operator, _parseEqualityExpression());
+    }
+    return expression;
+  }
+
+  /**
+   * Parse a map literal. The [modifier] is the 'const' modifier appearing
+   * before the literal, or `null` if there is no modifier. The [typeArguments]
+   * is the type arguments that were declared, or `null` if there are no type
+   * arguments. Return the map literal that was parsed.
+   *
+   *     mapLiteral ::=
+   *         'const'? typeArguments? '{' (mapLiteralEntry (',' mapLiteralEntry)* ','?)? '}'
+   */
+  MapLiteral _parseMapLiteral(Token modifier, TypeArgumentList typeArguments) {
+    Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
+    List<MapLiteralEntry> entries = new List<MapLiteralEntry>();
+    if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+      return new MapLiteral(
+          modifier, typeArguments, leftBracket, entries, getAndAdvance());
+    }
+    bool wasInInitializer = _inInitializer;
+    _inInitializer = false;
+    try {
+      entries.add(parseMapLiteralEntry());
+      while (_optional(TokenType.COMMA)) {
+        if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+          return new MapLiteral(
+              modifier, typeArguments, leftBracket, entries, getAndAdvance());
+        }
+        entries.add(parseMapLiteralEntry());
+      }
+      Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
+      return new MapLiteral(
+          modifier, typeArguments, leftBracket, entries, rightBracket);
+    } finally {
+      _inInitializer = wasInInitializer;
+    }
+  }
+
+  /**
+   * Parse a method declaration. The [commentAndMetadata] is the documentation
+   * comment and metadata to be associated with the declaration. The
+   * [externalKeyword] is the 'external' token. The [staticKeyword] is the
+   * static keyword, or `null` if the getter is not static. The [returnType] is
+   * the return type of the method. The [name] is the name of the method. The
+   * [parameters] is the parameters to the method. Return the method declaration
+   * that was parsed.
+   *
+   *     functionDeclaration ::=
+   *         ('external' 'static'?)? functionSignature functionBody
+   *       | 'external'? functionSignature ';'
+   */
+  MethodDeclaration _parseMethodDeclarationAfterParameters(
+      CommentAndMetadata commentAndMetadata, Token externalKeyword,
+      Token staticKeyword, TypeName returnType, SimpleIdentifier name,
+      FormalParameterList parameters) {
+    FunctionBody body = _parseFunctionBody(
+        externalKeyword != null || staticKeyword == null,
+        ParserErrorCode.MISSING_FUNCTION_BODY, false);
+    if (externalKeyword != null) {
+      if (body is! EmptyFunctionBody) {
+        _reportErrorForNode(ParserErrorCode.EXTERNAL_METHOD_WITH_BODY, body);
+      }
+    } else if (staticKeyword != null) {
+      if (body is EmptyFunctionBody && _parseFunctionBodies) {
+        _reportErrorForNode(ParserErrorCode.ABSTRACT_STATIC_METHOD, body);
+      }
+    }
+    return new MethodDeclaration(commentAndMetadata.comment,
+        commentAndMetadata.metadata, externalKeyword, staticKeyword, returnType,
+        null, null, name, parameters, body);
+  }
+
+  /**
+   * Parse a method declaration. The [commentAndMetadata] is the documentation
+   * comment and metadata to be associated with the declaration. The
+   * [externalKeyword] is the 'external' token. The [staticKeyword] is the
+   * static keyword, or `null` if the getter is not static. The [returnType] is
+   * the return type of the method. Return the method declaration that was
+   * parsed.
+   *
+   *     functionDeclaration ::=
+   *         'external'? 'static'? functionSignature functionBody
+   *       | 'external'? functionSignature ';'
+   */
+  MethodDeclaration _parseMethodDeclarationAfterReturnType(
+      CommentAndMetadata commentAndMetadata, Token externalKeyword,
+      Token staticKeyword, TypeName returnType) {
+    SimpleIdentifier methodName = parseSimpleIdentifier();
+    FormalParameterList parameters;
+    if (!_matches(TokenType.OPEN_PAREN) &&
+        (_matches(TokenType.OPEN_CURLY_BRACKET) ||
+            _matches(TokenType.FUNCTION))) {
+      _reportErrorForToken(
+          ParserErrorCode.MISSING_METHOD_PARAMETERS, _currentToken.previous);
+      parameters = new FormalParameterList(
+          _createSyntheticToken(TokenType.OPEN_PAREN), null, null, null,
+          _createSyntheticToken(TokenType.CLOSE_PAREN));
+    } else {
+      parameters = parseFormalParameterList();
+    }
+    _validateFormalParameterList(parameters);
+    return _parseMethodDeclarationAfterParameters(commentAndMetadata,
+        externalKeyword, staticKeyword, returnType, methodName, parameters);
+  }
+
+  /**
+   * Parse the modifiers preceding a declaration. This method allows the
+   * modifiers to appear in any order but does generate errors for duplicated
+   * modifiers. Checks for other problems, such as having the modifiers appear
+   * in the wrong order or specifying both 'const' and 'final', are reported in
+   * one of the methods whose name is prefixed with `validateModifiersFor`.
+   * Return the modifiers that were parsed.
+   *
+   *     modifiers ::=
+   *         ('abstract' | 'const' | 'external' | 'factory' | 'final' | 'static' | 'var')*
+   */
+  Modifiers _parseModifiers() {
+    Modifiers modifiers = new Modifiers();
+    bool progress = true;
+    while (progress) {
+      if (_tokenMatches(_peek(), TokenType.PERIOD) ||
+          _tokenMatches(_peek(), TokenType.LT) ||
+          _tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+        return modifiers;
+      }
+      if (_matchesKeyword(Keyword.ABSTRACT)) {
+        if (modifiers.abstractKeyword != null) {
+          _reportErrorForCurrentToken(
+              ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
+          _advance();
+        } else {
+          modifiers.abstractKeyword = getAndAdvance();
+        }
+      } else if (_matchesKeyword(Keyword.CONST)) {
+        if (modifiers.constKeyword != null) {
+          _reportErrorForCurrentToken(
+              ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
+          _advance();
+        } else {
+          modifiers.constKeyword = getAndAdvance();
+        }
+      } else if (_matchesKeyword(Keyword.EXTERNAL) &&
+          !_tokenMatches(_peek(), TokenType.PERIOD) &&
+          !_tokenMatches(_peek(), TokenType.LT)) {
+        if (modifiers.externalKeyword != null) {
+          _reportErrorForCurrentToken(
+              ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
+          _advance();
+        } else {
+          modifiers.externalKeyword = getAndAdvance();
+        }
+      } else if (_matchesKeyword(Keyword.FACTORY) &&
+          !_tokenMatches(_peek(), TokenType.PERIOD) &&
+          !_tokenMatches(_peek(), TokenType.LT)) {
+        if (modifiers.factoryKeyword != null) {
+          _reportErrorForCurrentToken(
+              ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
+          _advance();
+        } else {
+          modifiers.factoryKeyword = getAndAdvance();
+        }
+      } else if (_matchesKeyword(Keyword.FINAL)) {
+        if (modifiers.finalKeyword != null) {
+          _reportErrorForCurrentToken(
+              ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
+          _advance();
+        } else {
+          modifiers.finalKeyword = getAndAdvance();
+        }
+      } else if (_matchesKeyword(Keyword.STATIC) &&
+          !_tokenMatches(_peek(), TokenType.PERIOD) &&
+          !_tokenMatches(_peek(), TokenType.LT)) {
+        if (modifiers.staticKeyword != null) {
+          _reportErrorForCurrentToken(
+              ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
+          _advance();
+        } else {
+          modifiers.staticKeyword = getAndAdvance();
+        }
+      } else if (_matchesKeyword(Keyword.VAR)) {
+        if (modifiers.varKeyword != null) {
+          _reportErrorForCurrentToken(
+              ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
+          _advance();
+        } else {
+          modifiers.varKeyword = getAndAdvance();
+        }
+      } else {
+        progress = false;
+      }
+    }
+    return modifiers;
+  }
+
+  /**
+   * Parse a multiplicative expression. Return the multiplicative expression
+   * that was parsed.
+   *
+   *     multiplicativeExpression ::=
+   *         unaryExpression (multiplicativeOperator unaryExpression)*
+   *       | 'super' (multiplicativeOperator unaryExpression)+
+   */
+  Expression _parseMultiplicativeExpression() {
+    Expression expression;
+    if (_matchesKeyword(Keyword.SUPER) &&
+        _currentToken.next.type.isMultiplicativeOperator) {
+      expression = new SuperExpression(getAndAdvance());
+    } else {
+      expression = _parseUnaryExpression();
+    }
+    while (_currentToken.type.isMultiplicativeOperator) {
+      Token operator = getAndAdvance();
+      expression =
+          new BinaryExpression(expression, operator, _parseUnaryExpression());
+    }
+    return expression;
+  }
+
+  /**
+   * Parse a class native clause. Return the native clause that was parsed.
+   *
+   *     classNativeClause ::=
+   *         'native' name
+   */
+  NativeClause _parseNativeClause() {
+    Token keyword = getAndAdvance();
+    StringLiteral name = parseStringLiteral();
+    return new NativeClause(keyword, name);
+  }
+
+  /**
+   * Parse a new expression. Return the new expression that was parsed.
+   *
+   *     newExpression ::=
+   *         instanceCreationExpression
+   */
+  InstanceCreationExpression _parseNewExpression() =>
+      _parseInstanceCreationExpression(_expectKeyword(Keyword.NEW));
+
+  /**
+   * Parse a non-labeled statement. Return the non-labeled statement that was
+   * parsed.
+   *
+   *     nonLabeledStatement ::=
+   *         block
+   *       | assertStatement
+   *       | breakStatement
+   *       | continueStatement
+   *       | doStatement
+   *       | forStatement
+   *       | ifStatement
+   *       | returnStatement
+   *       | switchStatement
+   *       | tryStatement
+   *       | whileStatement
+   *       | variableDeclarationList ';'
+   *       | expressionStatement
+   *       | functionSignature functionBody
+   */
+  Statement _parseNonLabeledStatement() {
+    // TODO(brianwilkerson) Pass the comment and metadata on where appropriate.
+    CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
+    if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+      if (_tokenMatches(_peek(), TokenType.STRING)) {
+        Token afterString = _skipStringLiteral(_currentToken.next);
+        if (afterString != null && afterString.type == TokenType.COLON) {
+          return new ExpressionStatement(
+              parseExpression2(), _expect(TokenType.SEMICOLON));
+        }
+      }
+      return parseBlock();
+    } else if (_matches(TokenType.KEYWORD) &&
+        !(_currentToken as KeywordToken).keyword.isPseudoKeyword) {
+      Keyword keyword = (_currentToken as KeywordToken).keyword;
+      // TODO(jwren) compute some metrics to figure out a better order for this
+      // if-then sequence to optimize performance
+      if (keyword == Keyword.ASSERT) {
+        return _parseAssertStatement();
+      } else if (keyword == Keyword.BREAK) {
+        return _parseBreakStatement();
+      } else if (keyword == Keyword.CONTINUE) {
+        return _parseContinueStatement();
+      } else if (keyword == Keyword.DO) {
+        return _parseDoStatement();
+      } else if (keyword == Keyword.FOR) {
+        return _parseForStatement();
+      } else if (keyword == Keyword.IF) {
+        return _parseIfStatement();
+      } else if (keyword == Keyword.RETHROW) {
+        return new ExpressionStatement(
+            _parseRethrowExpression(), _expect(TokenType.SEMICOLON));
+      } else if (keyword == Keyword.RETURN) {
+        return _parseReturnStatement();
+      } else if (keyword == Keyword.SWITCH) {
+        return _parseSwitchStatement();
+      } else if (keyword == Keyword.THROW) {
+        return new ExpressionStatement(
+            _parseThrowExpression(), _expect(TokenType.SEMICOLON));
+      } else if (keyword == Keyword.TRY) {
+        return _parseTryStatement();
+      } else if (keyword == Keyword.WHILE) {
+        return _parseWhileStatement();
+      } else if (keyword == Keyword.VAR || keyword == Keyword.FINAL) {
+        return _parseVariableDeclarationStatementAfterMetadata(
+            commentAndMetadata);
+      } else if (keyword == Keyword.VOID) {
+        TypeName returnType = parseReturnType();
+        if (_matchesIdentifier() &&
+            _peek().matchesAny([
+          TokenType.OPEN_PAREN,
+          TokenType.OPEN_CURLY_BRACKET,
+          TokenType.FUNCTION
+        ])) {
+          return _parseFunctionDeclarationStatementAfterReturnType(
+              commentAndMetadata, returnType);
+        } else {
+          //
+          // We have found an error of some kind. Try to recover.
+          //
+          if (_matchesIdentifier()) {
+            if (_peek().matchesAny(
+                [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
+              //
+              // We appear to have a variable declaration with a type of "void".
+              //
+              _reportErrorForNode(ParserErrorCode.VOID_VARIABLE, returnType);
+              return _parseVariableDeclarationStatementAfterMetadata(
+                  commentAndMetadata);
+            }
+          } else if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+            //
+            // We appear to have found an incomplete statement at the end of a
+            // block. Parse it as a variable declaration.
+            //
+            return _parseVariableDeclarationStatementAfterType(
+                commentAndMetadata, null, returnType);
+          }
+          _reportErrorForCurrentToken(ParserErrorCode.MISSING_STATEMENT);
+          // TODO(brianwilkerson) Recover from this error.
+          return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
+        }
+      } else if (keyword == Keyword.CONST) {
+        if (_peek().matchesAny([
+          TokenType.LT,
+          TokenType.OPEN_CURLY_BRACKET,
+          TokenType.OPEN_SQUARE_BRACKET,
+          TokenType.INDEX
+        ])) {
+          return new ExpressionStatement(
+              parseExpression2(), _expect(TokenType.SEMICOLON));
+        } else if (_tokenMatches(_peek(), TokenType.IDENTIFIER)) {
+          Token afterType = _skipTypeName(_peek());
+          if (afterType != null) {
+            if (_tokenMatches(afterType, TokenType.OPEN_PAREN) ||
+                (_tokenMatches(afterType, TokenType.PERIOD) &&
+                    _tokenMatches(afterType.next, TokenType.IDENTIFIER) &&
+                    _tokenMatches(afterType.next.next, TokenType.OPEN_PAREN))) {
+              return new ExpressionStatement(
+                  parseExpression2(), _expect(TokenType.SEMICOLON));
+            }
+          }
+        }
+        return _parseVariableDeclarationStatementAfterMetadata(
+            commentAndMetadata);
+      } else if (keyword == Keyword.NEW ||
+          keyword == Keyword.TRUE ||
+          keyword == Keyword.FALSE ||
+          keyword == Keyword.NULL ||
+          keyword == Keyword.SUPER ||
+          keyword == Keyword.THIS) {
+        return new ExpressionStatement(
+            parseExpression2(), _expect(TokenType.SEMICOLON));
+      } else {
+        //
+        // We have found an error of some kind. Try to recover.
+        //
+        _reportErrorForCurrentToken(ParserErrorCode.MISSING_STATEMENT);
+        return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
+      }
+    } else if (_inGenerator && _matchesString(_YIELD)) {
+      return _parseYieldStatement();
+    } else if (_inAsync && _matchesString(_AWAIT)) {
+      if (_tokenMatchesKeyword(_peek(), Keyword.FOR)) {
+        return _parseForStatement();
+      }
+      return new ExpressionStatement(
+          parseExpression2(), _expect(TokenType.SEMICOLON));
+    } else if (_matchesString(_AWAIT) &&
+        _tokenMatchesKeyword(_peek(), Keyword.FOR)) {
+      Token awaitToken = _currentToken;
+      Statement statement = _parseForStatement();
+      if (statement is! ForStatement) {
+        _reportErrorForToken(
+            CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT, awaitToken);
+      }
+      return statement;
+    } else if (_matches(TokenType.SEMICOLON)) {
+      return _parseEmptyStatement();
+    } else if (_isInitializedVariableDeclaration()) {
+      return _parseVariableDeclarationStatementAfterMetadata(
+          commentAndMetadata);
+    } else if (_isFunctionDeclaration()) {
+      return _parseFunctionDeclarationStatement();
+    } else if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+      _reportErrorForCurrentToken(ParserErrorCode.MISSING_STATEMENT);
+      return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
+    } else {
+      return new ExpressionStatement(
+          parseExpression2(), _expect(TokenType.SEMICOLON));
+    }
+  }
+
+  /**
+   * Parse an operator declaration. The [commentAndMetadata] is the
+   * documentation comment and metadata to be associated with the declaration.
+   * The [externalKeyword] is the 'external' token. The [returnType] is the
+   * return type that has already been parsed, or `null` if there was no return
+   * type. Return the operator declaration that was parsed.
+   *
+   *     operatorDeclaration ::=
+   *         operatorSignature (';' | functionBody)
+   *
+   *     operatorSignature ::=
+   *         'external'? returnType? 'operator' operator formalParameterList
+   */
+  MethodDeclaration _parseOperator(CommentAndMetadata commentAndMetadata,
+      Token externalKeyword, TypeName returnType) {
+    Token operatorKeyword;
+    if (_matchesKeyword(Keyword.OPERATOR)) {
+      operatorKeyword = getAndAdvance();
+    } else {
+      _reportErrorForToken(
+          ParserErrorCode.MISSING_KEYWORD_OPERATOR, _currentToken);
+      operatorKeyword = _createSyntheticKeyword(Keyword.OPERATOR);
+    }
+    if (!_currentToken.isUserDefinableOperator) {
+      _reportErrorForCurrentToken(
+          ParserErrorCode.NON_USER_DEFINABLE_OPERATOR, [_currentToken.lexeme]);
+    }
+    SimpleIdentifier name = new SimpleIdentifier(getAndAdvance());
+    if (_matches(TokenType.EQ)) {
+      Token previous = _currentToken.previous;
+      if ((_tokenMatches(previous, TokenType.EQ_EQ) ||
+              _tokenMatches(previous, TokenType.BANG_EQ)) &&
+          _currentToken.offset == previous.offset + 2) {
+        _reportErrorForCurrentToken(ParserErrorCode.INVALID_OPERATOR,
+            ["${previous.lexeme}${_currentToken.lexeme}"]);
+        _advance();
+      }
+    }
+    FormalParameterList parameters = parseFormalParameterList();
+    _validateFormalParameterList(parameters);
+    FunctionBody body =
+        _parseFunctionBody(true, ParserErrorCode.MISSING_FUNCTION_BODY, false);
+    if (externalKeyword != null && body is! EmptyFunctionBody) {
+      _reportErrorForCurrentToken(ParserErrorCode.EXTERNAL_OPERATOR_WITH_BODY);
+    }
+    return new MethodDeclaration(commentAndMetadata.comment,
+        commentAndMetadata.metadata, externalKeyword, null, returnType, null,
+        operatorKeyword, name, parameters, body);
+  }
+
+  /**
+   * Parse a return type if one is given, otherwise return `null` without
+   * advancing. Return the return type that was parsed.
+   */
+  TypeName _parseOptionalReturnType() {
+    if (_matchesKeyword(Keyword.VOID)) {
+      return parseReturnType();
+    } else if (_matchesIdentifier() &&
+        !_matchesKeyword(Keyword.GET) &&
+        !_matchesKeyword(Keyword.SET) &&
+        !_matchesKeyword(Keyword.OPERATOR) &&
+        (_tokenMatchesIdentifier(_peek()) ||
+            _tokenMatches(_peek(), TokenType.LT))) {
+      return parseReturnType();
+    } else if (_matchesIdentifier() &&
+        _tokenMatches(_peek(), TokenType.PERIOD) &&
+        _tokenMatchesIdentifier(_peekAt(2)) &&
+        (_tokenMatchesIdentifier(_peekAt(3)) ||
+            _tokenMatches(_peekAt(3), TokenType.LT))) {
+      return parseReturnType();
+    }
+    return null;
+  }
+
+  /**
+   * Parse a part or part-of directive. The [commentAndMetadata] is the metadata
+   * to be associated with the directive. Return the part or part-of directive
+   * that was parsed.
+   *
+   *     partDirective ::=
+   *         metadata 'part' stringLiteral ';'
+   *
+   *     partOfDirective ::=
+   *         metadata 'part' 'of' identifier ';'
+   */
+  Directive _parsePartDirective(CommentAndMetadata commentAndMetadata) {
+    Token partKeyword = _expectKeyword(Keyword.PART);
+    if (_matchesString(_OF)) {
+      Token ofKeyword = getAndAdvance();
+      LibraryIdentifier libraryName = _parseLibraryName(
+          ParserErrorCode.MISSING_NAME_IN_PART_OF_DIRECTIVE, ofKeyword);
+      Token semicolon = _expect(TokenType.SEMICOLON);
+      return new PartOfDirective(commentAndMetadata.comment,
+          commentAndMetadata.metadata, partKeyword, ofKeyword, libraryName,
+          semicolon);
+    }
+    StringLiteral partUri = _parseUri();
+    Token semicolon = _expect(TokenType.SEMICOLON);
+    return new PartDirective(commentAndMetadata.comment,
+        commentAndMetadata.metadata, partKeyword, partUri, semicolon);
+  }
+
+  /**
+   * Parse a postfix expression. Return the postfix expression that was parsed.
+   *
+   *     postfixExpression ::=
+   *         assignableExpression postfixOperator
+   *       | primary selector*
+   *
+   *     selector ::=
+   *         assignableSelector
+   *       | argumentList
+   */
+  Expression _parsePostfixExpression() {
+    Expression operand = _parseAssignableExpression(true);
+    if (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
+        _matches(TokenType.PERIOD) ||
+        _matches(TokenType.QUESTION_PERIOD) ||
+        _matches(TokenType.OPEN_PAREN)) {
+      do {
+        if (_matches(TokenType.OPEN_PAREN)) {
+          ArgumentList argumentList = parseArgumentList();
+          if (operand is PropertyAccess) {
+            PropertyAccess access = operand as PropertyAccess;
+            operand = new MethodInvocation(access.target, access.operator,
+                access.propertyName, argumentList);
+          } else {
+            operand = new FunctionExpressionInvocation(operand, argumentList);
+          }
+        } else {
+          operand = _parseAssignableSelector(operand, true);
+        }
+      } while (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
+          _matches(TokenType.PERIOD) ||
+          _matches(TokenType.QUESTION_PERIOD) ||
+          _matches(TokenType.OPEN_PAREN));
+      return operand;
+    }
+    if (!_currentToken.type.isIncrementOperator) {
+      return operand;
+    }
+    _ensureAssignable(operand);
+    Token operator = getAndAdvance();
+    return new PostfixExpression(operand, operator);
+  }
+
+  /**
+   * Parse a primary expression. Return the primary expression that was parsed.
+   *
+   *     primary ::=
+   *         thisExpression
+   *       | 'super' unconditionalAssignableSelector
+   *       | functionExpression
+   *       | literal
+   *       | identifier
+   *       | newExpression
+   *       | constObjectExpression
+   *       | '(' expression ')'
+   *       | argumentDefinitionTest
+   *
+   *     literal ::=
+   *         nullLiteral
+   *       | booleanLiteral
+   *       | numericLiteral
+   *       | stringLiteral
+   *       | symbolLiteral
+   *       | mapLiteral
+   *       | listLiteral
+   */
+  Expression _parsePrimaryExpression() {
+    if (_matchesKeyword(Keyword.THIS)) {
+      return new ThisExpression(getAndAdvance());
+    } else if (_matchesKeyword(Keyword.SUPER)) {
+      // TODO(paulberry): verify with Gilad that "super" must be followed by
+      // unconditionalAssignableSelector in this case.
+      return _parseAssignableSelector(
+          new SuperExpression(getAndAdvance()), false, allowConditional: false);
+    } else if (_matchesKeyword(Keyword.NULL)) {
+      return new NullLiteral(getAndAdvance());
+    } else if (_matchesKeyword(Keyword.FALSE)) {
+      return new BooleanLiteral(getAndAdvance(), false);
+    } else if (_matchesKeyword(Keyword.TRUE)) {
+      return new BooleanLiteral(getAndAdvance(), true);
+    } else if (_matches(TokenType.DOUBLE)) {
+      Token token = getAndAdvance();
+      double value = 0.0;
+      try {
+        value = double.parse(token.lexeme);
+      } on FormatException {
+        // The invalid format should have been reported by the scanner.
+      }
+      return new DoubleLiteral(token, value);
+    } else if (_matches(TokenType.HEXADECIMAL)) {
+      Token token = getAndAdvance();
+      int value = null;
+      try {
+        value = int.parse(token.lexeme.substring(2), radix: 16);
+      } on FormatException {
+        // The invalid format should have been reported by the scanner.
+      }
+      return new IntegerLiteral(token, value);
+    } else if (_matches(TokenType.INT)) {
+      Token token = getAndAdvance();
+      int value = null;
+      try {
+        value = int.parse(token.lexeme);
+      } on FormatException {
+        // The invalid format should have been reported by the scanner.
+      }
+      return new IntegerLiteral(token, value);
+    } else if (_matches(TokenType.STRING)) {
+      return parseStringLiteral();
+    } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+      return _parseMapLiteral(null, null);
+    } else if (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
+        _matches(TokenType.INDEX)) {
+      return _parseListLiteral(null, null);
+    } else if (_matchesIdentifier()) {
+      // TODO(brianwilkerson) The code below was an attempt to recover from an
+      // error case, but it needs to be applied as a recovery only after we
+      // know that parsing it as an identifier doesn't work. Leaving the code as
+      // a reminder of how to recover.
+//            if (isFunctionExpression(peek())) {
+//              //
+//              // Function expressions were allowed to have names at one point, but this is now illegal.
+//              //
+//              reportError(ParserErrorCode.NAMED_FUNCTION_EXPRESSION, getAndAdvance());
+//              return parseFunctionExpression();
+//            }
+      return parsePrefixedIdentifier();
+    } else if (_matchesKeyword(Keyword.NEW)) {
+      return _parseNewExpression();
+    } else if (_matchesKeyword(Keyword.CONST)) {
+      return _parseConstExpression();
+    } else if (_matches(TokenType.OPEN_PAREN)) {
+      if (_isFunctionExpression(_currentToken)) {
+        return parseFunctionExpression();
+      }
+      Token leftParenthesis = getAndAdvance();
+      bool wasInInitializer = _inInitializer;
+      _inInitializer = false;
+      try {
+        Expression expression = parseExpression2();
+        Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+        return new ParenthesizedExpression(
+            leftParenthesis, expression, rightParenthesis);
+      } finally {
+        _inInitializer = wasInInitializer;
+      }
+    } else if (_matches(TokenType.LT)) {
+      return _parseListOrMapLiteral(null);
+    } else if (_matches(TokenType.QUESTION) &&
+        _tokenMatches(_peek(), TokenType.IDENTIFIER)) {
+      _reportErrorForCurrentToken(
+          ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
+      _advance();
+      return _parsePrimaryExpression();
+    } else if (_matchesKeyword(Keyword.VOID)) {
+      //
+      // Recover from having a return type of "void" where a return type is not
+      // expected.
+      //
+      // TODO(brianwilkerson) Improve this error message.
+      _reportErrorForCurrentToken(
+          ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
+      _advance();
+      return _parsePrimaryExpression();
+    } else if (_matches(TokenType.HASH)) {
+      return _parseSymbolLiteral();
+    } else {
+      _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+      return _createSyntheticIdentifier();
+    }
+  }
+
+  /**
+   * Parse a redirecting constructor invocation. Return the redirecting
+   * constructor invocation that was parsed.
+   *
+   *     redirectingConstructorInvocation ::=
+   *         'this' ('.' identifier)? arguments
+   */
+  RedirectingConstructorInvocation _parseRedirectingConstructorInvocation() {
+    Token keyword = _expectKeyword(Keyword.THIS);
+    Token period = null;
+    SimpleIdentifier constructorName = null;
+    if (_matches(TokenType.PERIOD)) {
+      period = getAndAdvance();
+      constructorName = parseSimpleIdentifier();
+    }
+    ArgumentList argumentList = parseArgumentList();
+    return new RedirectingConstructorInvocation(
+        keyword, period, constructorName, argumentList);
+  }
+
+  /**
+   * Parse a relational expression. Return the relational expression that was
+   * parsed.
+   *
+   *     relationalExpression ::=
+   *         bitwiseOrExpression ('is' '!'? type | 'as' type | relationalOperator bitwiseOrExpression)?
+   *       | 'super' relationalOperator bitwiseOrExpression
+   */
+  Expression _parseRelationalExpression() {
+    if (_matchesKeyword(Keyword.SUPER) &&
+        _currentToken.next.type.isRelationalOperator) {
+      Expression expression = new SuperExpression(getAndAdvance());
+      Token operator = getAndAdvance();
+      expression = new BinaryExpression(
+          expression, operator, parseBitwiseOrExpression());
+      return expression;
+    }
+    Expression expression = parseBitwiseOrExpression();
+    if (_matchesKeyword(Keyword.AS)) {
+      Token asOperator = getAndAdvance();
+      expression = new AsExpression(expression, asOperator, parseTypeName());
+    } else if (_matchesKeyword(Keyword.IS)) {
+      Token isOperator = getAndAdvance();
+      Token notOperator = null;
+      if (_matches(TokenType.BANG)) {
+        notOperator = getAndAdvance();
+      }
+      expression = new IsExpression(
+          expression, isOperator, notOperator, parseTypeName());
+    } else if (_currentToken.type.isRelationalOperator) {
+      Token operator = getAndAdvance();
+      expression = new BinaryExpression(
+          expression, operator, parseBitwiseOrExpression());
+    }
+    return expression;
+  }
+
+  /**
+   * Parse a rethrow expression. Return the rethrow expression that was parsed.
+   *
+   *     rethrowExpression ::=
+   *         'rethrow'
+   */
+  Expression _parseRethrowExpression() =>
+      new RethrowExpression(_expectKeyword(Keyword.RETHROW));
+
+  /**
+   * Parse a return statement. Return the return statement that was parsed.
+   *
+   *     returnStatement ::=
+   *         'return' expression? ';'
+   */
+  Statement _parseReturnStatement() {
+    Token returnKeyword = _expectKeyword(Keyword.RETURN);
+    if (_matches(TokenType.SEMICOLON)) {
+      return new ReturnStatement(returnKeyword, null, getAndAdvance());
+    }
+    Expression expression = parseExpression2();
+    Token semicolon = _expect(TokenType.SEMICOLON);
+    return new ReturnStatement(returnKeyword, expression, semicolon);
+  }
+
+  /**
+   * Parse a setter. The [commentAndMetadata] is the documentation comment and
+   * metadata to be associated with the declaration. The [externalKeyword] is
+   * the 'external' token. The [staticKeyword] is the static keyword, or `null`
+   * if the setter is not static. The [returnType] is the return type that has
+   * already been parsed, or `null` if there was no return type. Return the
+   * setter that was parsed.
+   *
+   *     setter ::=
+   *         setterSignature functionBody?
+   *
+   *     setterSignature ::=
+   *         'external'? 'static'? returnType? 'set' identifier formalParameterList
+   */
+  MethodDeclaration _parseSetter(CommentAndMetadata commentAndMetadata,
+      Token externalKeyword, Token staticKeyword, TypeName returnType) {
+    Token propertyKeyword = _expectKeyword(Keyword.SET);
+    SimpleIdentifier name = parseSimpleIdentifier();
+    FormalParameterList parameters = parseFormalParameterList();
+    _validateFormalParameterList(parameters);
+    FunctionBody body = _parseFunctionBody(
+        externalKeyword != null || staticKeyword == null,
+        ParserErrorCode.STATIC_SETTER_WITHOUT_BODY, false);
+    if (externalKeyword != null && body is! EmptyFunctionBody) {
+      _reportErrorForCurrentToken(ParserErrorCode.EXTERNAL_SETTER_WITH_BODY);
+    }
+    return new MethodDeclaration(commentAndMetadata.comment,
+        commentAndMetadata.metadata, externalKeyword, staticKeyword, returnType,
+        propertyKeyword, null, name, parameters, body);
+  }
+
+  /**
+   * Parse a shift expression. Return the shift expression that was parsed.
+   *
+   *     shiftExpression ::=
+   *         additiveExpression (shiftOperator additiveExpression)*
+   *       | 'super' (shiftOperator additiveExpression)+
+   */
+  Expression _parseShiftExpression() {
+    Expression expression;
+    if (_matchesKeyword(Keyword.SUPER) &&
+        _currentToken.next.type.isShiftOperator) {
+      expression = new SuperExpression(getAndAdvance());
+    } else {
+      expression = _parseAdditiveExpression();
+    }
+    while (_currentToken.type.isShiftOperator) {
+      Token operator = getAndAdvance();
+      expression = new BinaryExpression(
+          expression, operator, _parseAdditiveExpression());
+    }
+    return expression;
+  }
+
+  /**
+   * Parse a list of statements within a switch statement. Return the statements
+   * that were parsed.
+   *
+   *     statements ::=
+   *         statement*
+   */
+  List<Statement> _parseStatementList() {
+    List<Statement> statements = new List<Statement>();
+    Token statementStart = _currentToken;
+    while (!_matches(TokenType.EOF) &&
+        !_matches(TokenType.CLOSE_CURLY_BRACKET) &&
+        !_isSwitchMember()) {
+      statements.add(parseStatement2());
+      if (identical(_currentToken, statementStart)) {
+        _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
+            [_currentToken.lexeme]);
+        _advance();
+      }
+      statementStart = _currentToken;
+    }
+    return statements;
+  }
+
+  /**
+   * Parse a string literal that contains interpolations. Return the string
+   * literal that was parsed.
+   */
+  StringInterpolation _parseStringInterpolation(Token string) {
+    List<InterpolationElement> elements = new List<InterpolationElement>();
+    bool hasMore = _matches(TokenType.STRING_INTERPOLATION_EXPRESSION) ||
+        _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER);
+    elements.add(new InterpolationString(
+        string, _computeStringValue(string.lexeme, true, !hasMore)));
+    while (hasMore) {
+      if (_matches(TokenType.STRING_INTERPOLATION_EXPRESSION)) {
+        Token openToken = getAndAdvance();
+        bool wasInInitializer = _inInitializer;
+        _inInitializer = false;
+        try {
+          Expression expression = parseExpression2();
+          Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
+          elements.add(
+              new InterpolationExpression(openToken, expression, rightBracket));
+        } finally {
+          _inInitializer = wasInInitializer;
+        }
+      } else {
+        Token openToken = getAndAdvance();
+        Expression expression = null;
+        if (_matchesKeyword(Keyword.THIS)) {
+          expression = new ThisExpression(getAndAdvance());
+        } else {
+          expression = parseSimpleIdentifier();
+        }
+        elements.add(new InterpolationExpression(openToken, expression, null));
+      }
+      if (_matches(TokenType.STRING)) {
+        string = getAndAdvance();
+        hasMore = _matches(TokenType.STRING_INTERPOLATION_EXPRESSION) ||
+            _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER);
+        elements.add(new InterpolationString(
+            string, _computeStringValue(string.lexeme, false, !hasMore)));
+      } else {
+        hasMore = false;
+      }
+    }
+    return new StringInterpolation(elements);
+  }
+
+  /**
+   * Parse a super constructor invocation. Return the super constructor
+   * invocation that was parsed.
+   *
+   *     superConstructorInvocation ::=
+   *         'super' ('.' identifier)? arguments
+   */
+  SuperConstructorInvocation _parseSuperConstructorInvocation() {
+    Token keyword = _expectKeyword(Keyword.SUPER);
+    Token period = null;
+    SimpleIdentifier constructorName = null;
+    if (_matches(TokenType.PERIOD)) {
+      period = getAndAdvance();
+      constructorName = parseSimpleIdentifier();
+    }
+    ArgumentList argumentList = parseArgumentList();
+    return new SuperConstructorInvocation(
+        keyword, period, constructorName, argumentList);
+  }
+
+  /**
+   * Parse a switch statement. Return the switch statement that was parsed.
+   *
+   *     switchStatement ::=
+   *         'switch' '(' expression ')' '{' switchCase* defaultCase? '}'
+   *
+   *     switchCase ::=
+   *         label* ('case' expression ':') statements
+   *
+   *     defaultCase ::=
+   *         label* 'default' ':' statements
+   */
+  SwitchStatement _parseSwitchStatement() {
+    bool wasInSwitch = _inSwitch;
+    _inSwitch = true;
+    try {
+      HashSet<String> definedLabels = new HashSet<String>();
+      Token keyword = _expectKeyword(Keyword.SWITCH);
+      Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
+      Expression expression = parseExpression2();
+      Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+      Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
+      Token defaultKeyword = null;
+      List<SwitchMember> members = new List<SwitchMember>();
+      while (!_matches(TokenType.EOF) &&
+          !_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+        List<Label> labels = new List<Label>();
+        while (
+            _matchesIdentifier() && _tokenMatches(_peek(), TokenType.COLON)) {
+          SimpleIdentifier identifier = parseSimpleIdentifier();
+          String label = identifier.token.lexeme;
+          if (definedLabels.contains(label)) {
+            _reportErrorForToken(
+                ParserErrorCode.DUPLICATE_LABEL_IN_SWITCH_STATEMENT,
+                identifier.token, [label]);
+          } else {
+            definedLabels.add(label);
+          }
+          Token colon = _expect(TokenType.COLON);
+          labels.add(new Label(identifier, colon));
+        }
+        if (_matchesKeyword(Keyword.CASE)) {
+          Token caseKeyword = getAndAdvance();
+          Expression caseExpression = parseExpression2();
+          Token colon = _expect(TokenType.COLON);
+          members.add(new SwitchCase(labels, caseKeyword, caseExpression, colon,
+              _parseStatementList()));
+          if (defaultKeyword != null) {
+            _reportErrorForToken(
+                ParserErrorCode.SWITCH_HAS_CASE_AFTER_DEFAULT_CASE,
+                caseKeyword);
+          }
+        } else if (_matchesKeyword(Keyword.DEFAULT)) {
+          if (defaultKeyword != null) {
+            _reportErrorForToken(
+                ParserErrorCode.SWITCH_HAS_MULTIPLE_DEFAULT_CASES, _peek());
+          }
+          defaultKeyword = getAndAdvance();
+          Token colon = _expect(TokenType.COLON);
+          members.add(new SwitchDefault(
+              labels, defaultKeyword, colon, _parseStatementList()));
+        } else {
+          // We need to advance, otherwise we could end up in an infinite loop,
+          // but this could be a lot smarter about recovering from the error.
+          _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_CASE_OR_DEFAULT);
+          while (!_matches(TokenType.EOF) &&
+              !_matches(TokenType.CLOSE_CURLY_BRACKET) &&
+              !_matchesKeyword(Keyword.CASE) &&
+              !_matchesKeyword(Keyword.DEFAULT)) {
+            _advance();
+          }
+        }
+      }
+      Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
+      return new SwitchStatement(keyword, leftParenthesis, expression,
+          rightParenthesis, leftBracket, members, rightBracket);
+    } finally {
+      _inSwitch = wasInSwitch;
+    }
+  }
+
+  /**
+   * Parse a symbol literal. Return the symbol literal that was parsed.
+   *
+   *     symbolLiteral ::=
+   *         '#' identifier ('.' identifier)*
+   */
+  SymbolLiteral _parseSymbolLiteral() {
+    Token poundSign = getAndAdvance();
+    List<Token> components = new List<Token>();
+    if (_matchesIdentifier()) {
+      components.add(getAndAdvance());
+      while (_matches(TokenType.PERIOD)) {
+        _advance();
+        if (_matchesIdentifier()) {
+          components.add(getAndAdvance());
+        } else {
+          _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+          components.add(_createSyntheticToken(TokenType.IDENTIFIER));
+          break;
+        }
+      }
+    } else if (_currentToken.isOperator) {
+      components.add(getAndAdvance());
+    } else if (_tokenMatchesKeyword(_currentToken, Keyword.VOID)) {
+      components.add(getAndAdvance());
+    } else {
+      _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+      components.add(_createSyntheticToken(TokenType.IDENTIFIER));
+    }
+    return new SymbolLiteral(poundSign, components);
+  }
+
+  /**
+   * Parse a throw expression. Return the throw expression that was parsed.
+   *
+   *     throwExpression ::=
+   *         'throw' expression
+   */
+  Expression _parseThrowExpression() {
+    Token keyword = _expectKeyword(Keyword.THROW);
+    if (_matches(TokenType.SEMICOLON) || _matches(TokenType.CLOSE_PAREN)) {
+      _reportErrorForToken(
+          ParserErrorCode.MISSING_EXPRESSION_IN_THROW, _currentToken);
+      return new ThrowExpression(keyword, _createSyntheticIdentifier());
+    }
+    Expression expression = parseExpression2();
+    return new ThrowExpression(keyword, expression);
+  }
+
+  /**
+   * Parse a throw expression. Return the throw expression that was parsed.
+   *
+   *     throwExpressionWithoutCascade ::=
+   *         'throw' expressionWithoutCascade
+   */
+  Expression _parseThrowExpressionWithoutCascade() {
+    Token keyword = _expectKeyword(Keyword.THROW);
+    if (_matches(TokenType.SEMICOLON) || _matches(TokenType.CLOSE_PAREN)) {
+      _reportErrorForToken(
+          ParserErrorCode.MISSING_EXPRESSION_IN_THROW, _currentToken);
+      return new ThrowExpression(keyword, _createSyntheticIdentifier());
+    }
+    Expression expression = parseExpressionWithoutCascade();
+    return new ThrowExpression(keyword, expression);
+  }
+
+  /**
+   * Parse a try statement. Return the try statement that was parsed.
+   *
+   *     tryStatement ::=
+   *         'try' block (onPart+ finallyPart? | finallyPart)
+   *
+   *     onPart ::=
+   *         catchPart block
+   *       | 'on' type catchPart? block
+   *
+   *     catchPart ::=
+   *         'catch' '(' identifier (',' identifier)? ')'
+   *
+   *     finallyPart ::=
+   *         'finally' block
+   */
+  Statement _parseTryStatement() {
+    Token tryKeyword = _expectKeyword(Keyword.TRY);
+    Block body = parseBlock();
+    List<CatchClause> catchClauses = new List<CatchClause>();
+    Block finallyClause = null;
+    while (_matchesString(_ON) || _matchesKeyword(Keyword.CATCH)) {
+      Token onKeyword = null;
+      TypeName exceptionType = null;
+      if (_matchesString(_ON)) {
+        onKeyword = getAndAdvance();
+        exceptionType = parseTypeName();
+      }
+      Token catchKeyword = null;
+      Token leftParenthesis = null;
+      SimpleIdentifier exceptionParameter = null;
+      Token comma = null;
+      SimpleIdentifier stackTraceParameter = null;
+      Token rightParenthesis = null;
+      if (_matchesKeyword(Keyword.CATCH)) {
+        catchKeyword = getAndAdvance();
+        leftParenthesis = _expect(TokenType.OPEN_PAREN);
+        exceptionParameter = parseSimpleIdentifier();
+        if (_matches(TokenType.COMMA)) {
+          comma = getAndAdvance();
+          stackTraceParameter = parseSimpleIdentifier();
+        }
+        rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+      }
+      Block catchBody = parseBlock();
+      catchClauses.add(new CatchClause(onKeyword, exceptionType, catchKeyword,
+          leftParenthesis, exceptionParameter, comma, stackTraceParameter,
+          rightParenthesis, catchBody));
+    }
+    Token finallyKeyword = null;
+    if (_matchesKeyword(Keyword.FINALLY)) {
+      finallyKeyword = getAndAdvance();
+      finallyClause = parseBlock();
+    } else {
+      if (catchClauses.isEmpty) {
+        _reportErrorForCurrentToken(ParserErrorCode.MISSING_CATCH_OR_FINALLY);
+      }
+    }
+    return new TryStatement(
+        tryKeyword, body, catchClauses, finallyKeyword, finallyClause);
+  }
+
+  /**
+   * Parse a type alias. The [commentAndMetadata] is the metadata to be
+   * associated with the member. Return the type alias that was parsed.
+   *
+   *     typeAlias ::=
+   *         'typedef' typeAliasBody
+   *
+   *     typeAliasBody ::=
+   *         classTypeAlias
+   *       | functionTypeAlias
+   *
+   *     classTypeAlias ::=
+   *         identifier typeParameters? '=' 'abstract'? mixinApplication
+   *
+   *     mixinApplication ::=
+   *         qualified withClause implementsClause? ';'
+   *
+   *     functionTypeAlias ::=
+   *         functionPrefix typeParameterList? formalParameterList ';'
+   *
+   *     functionPrefix ::=
+   *         returnType? name
+   */
+  TypeAlias _parseTypeAlias(CommentAndMetadata commentAndMetadata) {
+    Token keyword = _expectKeyword(Keyword.TYPEDEF);
+    if (_matchesIdentifier()) {
+      Token next = _peek();
+      if (_tokenMatches(next, TokenType.LT)) {
+        next = _skipTypeParameterList(next);
+        if (next != null && _tokenMatches(next, TokenType.EQ)) {
+          TypeAlias typeAlias =
+              _parseClassTypeAlias(commentAndMetadata, null, keyword);
+          _reportErrorForToken(
+              ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS, keyword);
+          return typeAlias;
+        }
+      } else if (_tokenMatches(next, TokenType.EQ)) {
+        TypeAlias typeAlias =
+            _parseClassTypeAlias(commentAndMetadata, null, keyword);
+        _reportErrorForToken(
+            ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS, keyword);
+        return typeAlias;
+      }
+    }
+    return _parseFunctionTypeAlias(commentAndMetadata, keyword);
+  }
+
+  /**
+   * Parse a unary expression. Return the unary expression that was parsed.
+   *
+   *     unaryExpression ::=
+   *         prefixOperator unaryExpression
+   *       | awaitExpression
+   *       | postfixExpression
+   *       | unaryOperator 'super'
+   *       | '-' 'super'
+   *       | incrementOperator assignableExpression
+   */
+  Expression _parseUnaryExpression() {
+    if (_matches(TokenType.MINUS) ||
+        _matches(TokenType.BANG) ||
+        _matches(TokenType.TILDE)) {
+      Token operator = getAndAdvance();
+      if (_matchesKeyword(Keyword.SUPER)) {
+        if (_tokenMatches(_peek(), TokenType.OPEN_SQUARE_BRACKET) ||
+            _tokenMatches(_peek(), TokenType.PERIOD)) {
+          //     "prefixOperator unaryExpression"
+          // --> "prefixOperator postfixExpression"
+          // --> "prefixOperator primary                    selector*"
+          // --> "prefixOperator 'super' assignableSelector selector*"
+          return new PrefixExpression(operator, _parseUnaryExpression());
+        }
+        return new PrefixExpression(
+            operator, new SuperExpression(getAndAdvance()));
+      }
+      return new PrefixExpression(operator, _parseUnaryExpression());
+    } else if (_currentToken.type.isIncrementOperator) {
+      Token operator = getAndAdvance();
+      if (_matchesKeyword(Keyword.SUPER)) {
+        if (_tokenMatches(_peek(), TokenType.OPEN_SQUARE_BRACKET) ||
+            _tokenMatches(_peek(), TokenType.PERIOD)) {
+          // --> "prefixOperator 'super' assignableSelector selector*"
+          return new PrefixExpression(operator, _parseUnaryExpression());
+        }
+        //
+        // Even though it is not valid to use an incrementing operator
+        // ('++' or '--') before 'super', we can (and therefore must) interpret
+        // "--super" as semantically equivalent to "-(-super)". Unfortunately,
+        // we cannot do the same for "++super" because "+super" is also not
+        // valid.
+        //
+        if (operator.type == TokenType.MINUS_MINUS) {
+          Token firstOperator = _createToken(operator, TokenType.MINUS);
+          Token secondOperator =
+              new Token(TokenType.MINUS, operator.offset + 1);
+          secondOperator.setNext(_currentToken);
+          firstOperator.setNext(secondOperator);
+          operator.previous.setNext(firstOperator);
+          return new PrefixExpression(firstOperator, new PrefixExpression(
+              secondOperator, new SuperExpression(getAndAdvance())));
+        } else {
+          // Invalid operator before 'super'
+          _reportErrorForCurrentToken(
+              ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [operator.lexeme]);
+          return new PrefixExpression(
+              operator, new SuperExpression(getAndAdvance()));
+        }
+      }
+      return new PrefixExpression(operator, _parseAssignableExpression(false));
+    } else if (_matches(TokenType.PLUS)) {
+      _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+      return _createSyntheticIdentifier();
+    } else if (_inAsync && _matchesString(_AWAIT)) {
+      return _parseAwaitExpression();
+    }
+    return _parsePostfixExpression();
+  }
+
+  /**
+   * Parse a string literal representing a URI. Return the string literal that
+   * was parsed.
+   */
+  StringLiteral _parseUri() {
+    bool iskeywordAfterUri(Token token) => token.lexeme == Keyword.AS.syntax ||
+        token.lexeme == _HIDE ||
+        token.lexeme == _SHOW;
+    if (!_matches(TokenType.STRING) &&
+        !_matches(TokenType.SEMICOLON) &&
+        !iskeywordAfterUri(_currentToken)) {
+      // Attempt to recover in the case where the URI was not enclosed in
+      // quotes.
+      Token token = _currentToken;
+      while ((_tokenMatchesIdentifier(token) && !iskeywordAfterUri(token)) ||
+          _tokenMatches(token, TokenType.COLON) ||
+          _tokenMatches(token, TokenType.SLASH) ||
+          _tokenMatches(token, TokenType.PERIOD) ||
+          _tokenMatches(token, TokenType.PERIOD_PERIOD) ||
+          _tokenMatches(token, TokenType.PERIOD_PERIOD_PERIOD) ||
+          _tokenMatches(token, TokenType.INT) ||
+          _tokenMatches(token, TokenType.DOUBLE)) {
+        token = token.next;
+      }
+      if (_tokenMatches(token, TokenType.SEMICOLON) ||
+          iskeywordAfterUri(token)) {
+        Token endToken = token.previous;
+        token = _currentToken;
+        int endOffset = token.end;
+        StringBuffer buffer = new StringBuffer();
+        buffer.write(token.lexeme);
+        while (token != endToken) {
+          token = token.next;
+          if (token.offset != endOffset || token.precedingComments != null) {
+            return parseStringLiteral();
+          }
+          buffer.write(token.lexeme);
+          endOffset = token.end;
+        }
+        String value = buffer.toString();
+        Token newToken =
+            new StringToken(TokenType.STRING, "'$value'", _currentToken.offset);
+        _reportErrorForToken(
+            ParserErrorCode.NON_STRING_LITERAL_AS_URI, newToken);
+        _currentToken = endToken.next;
+        return new SimpleStringLiteral(newToken, value);
+      }
+    }
+    return parseStringLiteral();
+  }
+
+  /**
+   * Parse a variable declaration. Return the variable declaration that was
+   * parsed.
+   *
+   *     variableDeclaration ::=
+   *         identifier ('=' expression)?
+   */
+  VariableDeclaration _parseVariableDeclaration() {
+    // TODO(paulberry): prior to the fix for bug 23204, we permitted
+    // annotations before variable declarations (e.g. "String @deprecated s;").
+    // Although such constructions are prohibited by the spec, we may want to
+    // consider handling them anyway to allow for better parser recovery in the
+    // event that the user erroneously tries to use them.  However, as a
+    // counterargument, this would likely degrade parser recovery in the event
+    // of a construct like "class C { int @deprecated foo() {} }" (i.e. the
+    // user is in the middle of inserting "int bar;" prior to
+    // "@deprecated foo() {}").
+    SimpleIdentifier name = parseSimpleIdentifier();
+    Token equals = null;
+    Expression initializer = null;
+    if (_matches(TokenType.EQ)) {
+      equals = getAndAdvance();
+      initializer = parseExpression2();
+    }
+    return new VariableDeclaration(name, equals, initializer);
+  }
+
+  /**
+   * Parse a variable declaration list. The [commentAndMetadata] is the metadata
+   * to be associated with the variable declaration list. Return the variable
+   * declaration list that was parsed.
+   *
+   *     variableDeclarationList ::=
+   *         finalConstVarOrType variableDeclaration (',' variableDeclaration)*
+   */
+  VariableDeclarationList _parseVariableDeclarationListAfterMetadata(
+      CommentAndMetadata commentAndMetadata) {
+    FinalConstVarOrType holder = _parseFinalConstVarOrType(false);
+    return _parseVariableDeclarationListAfterType(
+        commentAndMetadata, holder.keyword, holder.type);
+  }
+
+  /**
+   * Parse a variable declaration list. The [commentAndMetadata] is the metadata
+   * to be associated with the variable declaration list, or `null` if there is
+   * no attempt at parsing the comment and metadata. The [keyword] is the token
+   * representing the 'final', 'const' or 'var' keyword, or `null` if there is
+   * no keyword. The [type] is the type of the variables in the list. Return the
+   * variable declaration list that was parsed.
+   *
+   *     variableDeclarationList ::=
+   *         finalConstVarOrType variableDeclaration (',' variableDeclaration)*
+   */
+  VariableDeclarationList _parseVariableDeclarationListAfterType(
+      CommentAndMetadata commentAndMetadata, Token keyword, TypeName type) {
+    if (type != null &&
+        keyword != null &&
+        _tokenMatchesKeyword(keyword, Keyword.VAR)) {
+      _reportErrorForToken(ParserErrorCode.VAR_AND_TYPE, keyword);
+    }
+    List<VariableDeclaration> variables = new List<VariableDeclaration>();
+    variables.add(_parseVariableDeclaration());
+    while (_matches(TokenType.COMMA)) {
+      _advance();
+      variables.add(_parseVariableDeclaration());
+    }
+    return new VariableDeclarationList(
+        commentAndMetadata != null ? commentAndMetadata.comment : null,
+        commentAndMetadata != null ? commentAndMetadata.metadata : null,
+        keyword, type, variables);
+  }
+
+  /**
+   * Parse a variable declaration statement. The [commentAndMetadata] is the
+   * metadata to be associated with the variable declaration statement, or
+   * `null` if there is no attempt at parsing the comment and metadata. Return
+   * the variable declaration statement that was parsed.
+   *
+   *     variableDeclarationStatement ::=
+   *         variableDeclarationList ';'
+   */
+  VariableDeclarationStatement _parseVariableDeclarationStatementAfterMetadata(
+      CommentAndMetadata commentAndMetadata) {
+    //    Token startToken = currentToken;
+    VariableDeclarationList variableList =
+        _parseVariableDeclarationListAfterMetadata(commentAndMetadata);
+//        if (!matches(TokenType.SEMICOLON)) {
+//          if (matches(startToken, Keyword.VAR) && isTypedIdentifier(startToken.getNext())) {
+//            // TODO(brianwilkerson) This appears to be of the form "var type variable". We should do
+//            // a better job of recovering in this case.
+//          }
+//        }
+    Token semicolon = _expect(TokenType.SEMICOLON);
+    return new VariableDeclarationStatement(variableList, semicolon);
+  }
+
+  /**
+   * Parse a variable declaration statement. The [commentAndMetadata] is the
+   * metadata to be associated with the variable declaration statement, or
+   * `null` if there is no attempt at parsing the comment and metadata. The
+   * [keyword] is the token representing the 'final', 'const' or 'var' keyword,
+   * or `null` if there is no keyword. The [type] is the type of the variables
+   * in the list. Return the variable declaration statement that was parsed.
+   *
+   *     variableDeclarationStatement ::=
+   *         variableDeclarationList ';'
+   */
+  VariableDeclarationStatement _parseVariableDeclarationStatementAfterType(
+      CommentAndMetadata commentAndMetadata, Token keyword, TypeName type) {
+    VariableDeclarationList variableList =
+        _parseVariableDeclarationListAfterType(
+            commentAndMetadata, keyword, type);
+    Token semicolon = _expect(TokenType.SEMICOLON);
+    return new VariableDeclarationStatement(variableList, semicolon);
+  }
+
+  /**
+   * Parse a while statement. Return the while statement that was parsed.
+   *
+   *     whileStatement ::=
+   *         'while' '(' expression ')' statement
+   */
+  Statement _parseWhileStatement() {
+    bool wasInLoop = _inLoop;
+    _inLoop = true;
+    try {
+      Token keyword = _expectKeyword(Keyword.WHILE);
+      Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
+      Expression condition = parseExpression2();
+      Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+      Statement body = parseStatement2();
+      return new WhileStatement(
+          keyword, leftParenthesis, condition, rightParenthesis, body);
+    } finally {
+      _inLoop = wasInLoop;
+    }
+  }
+
+  /**
+   * Parse a yield statement. Return the yield statement that was parsed.
+   *
+   *     yieldStatement ::=
+   *         'yield' '*'? expression ';'
+   */
+  YieldStatement _parseYieldStatement() {
+    Token yieldToken = getAndAdvance();
+    Token star = null;
+    if (_matches(TokenType.STAR)) {
+      star = getAndAdvance();
+    }
+    Expression expression = parseExpression2();
+    Token semicolon = _expect(TokenType.SEMICOLON);
+    return new YieldStatement(yieldToken, star, expression, semicolon);
+  }
+
+  /**
+   * Return the token that is immediately after the current token. This is
+   * equivalent to [_peekAt](1).
+   */
+  Token _peek() => _currentToken.next;
+
+  /**
+   * Return the token that is the given [distance] after the current token,
+   * where the distance is the number of tokens to look ahead. A distance of `0`
+   * is the current token, `1` is the next token, etc.
+   */
+  Token _peekAt(int distance) {
+    Token token = _currentToken;
+    for (int i = 0; i < distance; i++) {
+      token = token.next;
+    }
+    return token;
+  }
+
+  /**
+   * Report the given [error].
+   */
+  void _reportError(AnalysisError error) {
+    if (_errorListenerLock != 0) {
+      return;
+    }
+    _errorListener.onError(error);
+  }
+
+  /**
+   * Report an error with the given [errorCode] and [arguments] associated with
+   * the current token.
+   */
+  void _reportErrorForCurrentToken(ParserErrorCode errorCode,
+      [List<Object> arguments]) {
+    _reportErrorForToken(errorCode, _currentToken, arguments);
+  }
+
+  /**
+   * Report an error with the given [errorCode] and [arguments] associated with
+   * the given [node].
+   */
+  void _reportErrorForNode(ParserErrorCode errorCode, AstNode node,
+      [List<Object> arguments]) {
+    _reportError(new AnalysisError.con2(
+        _source, node.offset, node.length, errorCode, arguments));
+  }
+
+  /**
+   * Report an error with the given [errorCode] and [arguments] associated with
+   * the given [token].
+   */
+  void _reportErrorForToken(ErrorCode errorCode, Token token,
+      [List<Object> arguments]) {
+    if (token.type == TokenType.EOF) {
+      token = token.previous;
+    }
+    _reportError(new AnalysisError.con2(_source, token.offset,
+        math.max(token.length, 1), errorCode, arguments));
+  }
+
+  /**
+   * Skips a block with all containing blocks.
+   */
+  void _skipBlock() {
+    Token endToken = (_currentToken as BeginToken).endToken;
+    if (endToken == null) {
+      endToken = _currentToken.next;
+      while (!identical(endToken, _currentToken)) {
+        _currentToken = endToken;
+        endToken = _currentToken.next;
+      }
+      _reportErrorForToken(
+          ParserErrorCode.EXPECTED_TOKEN, _currentToken.previous, ["}"]);
+    } else {
+      _currentToken = endToken.next;
+    }
+  }
+
+  /**
+   * Parse the 'final', 'const', 'var' or type preceding a variable declaration,
+   * starting at the given token, without actually creating a type or changing
+   * the current token. Return the token following the type that was parsed, or
+   * `null` if the given token is not the first token in a valid type. The
+   * [startToken] is the token at which parsing is to begin. Return the token
+   * following the type that was parsed.
+   *
+   * finalConstVarOrType ::=
+   *   | 'final' type?
+   *   | 'const' type?
+   *   | 'var'
+   *   | type
+   */
+  Token _skipFinalConstVarOrType(Token startToken) {
+    if (_tokenMatchesKeyword(startToken, Keyword.FINAL) ||
+        _tokenMatchesKeyword(startToken, Keyword.CONST)) {
+      Token next = startToken.next;
+      if (_tokenMatchesIdentifier(next)) {
+        Token next2 = next.next;
+        // "Type parameter" or "Type<" or "prefix.Type"
+        if (_tokenMatchesIdentifier(next2) ||
+            _tokenMatches(next2, TokenType.LT) ||
+            _tokenMatches(next2, TokenType.PERIOD)) {
+          return _skipTypeName(next);
+        }
+        // "parameter"
+        return next;
+      }
+    } else if (_tokenMatchesKeyword(startToken, Keyword.VAR)) {
+      return startToken.next;
+    } else if (_tokenMatchesIdentifier(startToken)) {
+      Token next = startToken.next;
+      if (_tokenMatchesIdentifier(next) ||
+          _tokenMatches(next, TokenType.LT) ||
+          _tokenMatchesKeyword(next, Keyword.THIS) ||
+          (_tokenMatches(next, TokenType.PERIOD) &&
+              _tokenMatchesIdentifier(next.next) &&
+              (_tokenMatchesIdentifier(next.next.next) ||
+                  _tokenMatches(next.next.next, TokenType.LT) ||
+                  _tokenMatchesKeyword(next.next.next, Keyword.THIS)))) {
+        return _skipReturnType(startToken);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Parse a list of formal parameters, starting at the [startToken], without
+   * actually creating a formal parameter list or changing the current token.
+   * Return the token following the formal parameter list that was parsed, or
+   * `null` if the given token is not the first token in a valid list of formal
+   * parameter.
+   *
+   * Note that unlike other skip methods, this method uses a heuristic. In the
+   * worst case, the parameters could be prefixed by metadata, which would
+   * require us to be able to skip arbitrary expressions. Rather than duplicate
+   * the logic of most of the parse methods we simply look for something that is
+   * likely to be a list of parameters and then skip to returning the token
+   * after the closing parenthesis.
+   *
+   * This method must be kept in sync with [parseFormalParameterList].
+   *
+   *     formalParameterList ::=
+   *         '(' ')'
+   *       | '(' normalFormalParameters (',' optionalFormalParameters)? ')'
+   *       | '(' optionalFormalParameters ')'
+   *
+   *     normalFormalParameters ::=
+   *         normalFormalParameter (',' normalFormalParameter)*
+   *
+   *     optionalFormalParameters ::=
+   *         optionalPositionalFormalParameters
+   *       | namedFormalParameters
+   *
+   *     optionalPositionalFormalParameters ::=
+   *         '[' defaultFormalParameter (',' defaultFormalParameter)* ']'
+   *
+   *     namedFormalParameters ::=
+   *         '{' defaultNamedParameter (',' defaultNamedParameter)* '}'
+   */
+  Token _skipFormalParameterList(Token startToken) {
+    if (!_tokenMatches(startToken, TokenType.OPEN_PAREN)) {
+      return null;
+    }
+    Token next = startToken.next;
+    if (_tokenMatches(next, TokenType.CLOSE_PAREN)) {
+      return next.next;
+    }
+    //
+    // Look to see whether the token after the open parenthesis is something
+    // that should only occur at the beginning of a parameter list.
+    //
+    if (next.matchesAny([
+      TokenType.AT,
+      TokenType.OPEN_SQUARE_BRACKET,
+      TokenType.OPEN_CURLY_BRACKET
+    ]) ||
+        _tokenMatchesKeyword(next, Keyword.VOID) ||
+        (_tokenMatchesIdentifier(next) &&
+            (next.next.matchesAny([TokenType.COMMA, TokenType.CLOSE_PAREN])))) {
+      return _skipPastMatchingToken(startToken);
+    }
+    //
+    // Look to see whether the first parameter is a function typed parameter
+    // without a return type.
+    //
+    if (_tokenMatchesIdentifier(next) &&
+        _tokenMatches(next.next, TokenType.OPEN_PAREN)) {
+      Token afterParameters = _skipFormalParameterList(next.next);
+      if (afterParameters != null &&
+          (afterParameters
+              .matchesAny([TokenType.COMMA, TokenType.CLOSE_PAREN]))) {
+        return _skipPastMatchingToken(startToken);
+      }
+    }
+    //
+    // Look to see whether the first parameter has a type or is a function typed
+    // parameter with a return type.
+    //
+    Token afterType = _skipFinalConstVarOrType(next);
+    if (afterType == null) {
+      return null;
+    }
+    if (_skipSimpleIdentifier(afterType) == null) {
+      return null;
+    }
+    return _skipPastMatchingToken(startToken);
+  }
+
+  /**
+   * If the [startToken] is a begin token with an associated end token, then
+   * return the token following the end token. Otherwise, return `null`.
+   */
+  Token _skipPastMatchingToken(Token startToken) {
+    if (startToken is! BeginToken) {
+      return null;
+    }
+    Token closeParen = (startToken as BeginToken).endToken;
+    if (closeParen == null) {
+      return null;
+    }
+    return closeParen.next;
+  }
+
+  /**
+   * Parse a prefixed identifier, starting at the [startToken], without actually
+   * creating a prefixed identifier or changing the current token. Return the
+   * token following the prefixed identifier that was parsed, or `null` if the
+   * given token is not the first token in a valid prefixed identifier.
+   *
+   * This method must be kept in sync with [parsePrefixedIdentifier].
+   *
+   *     prefixedIdentifier ::=
+   *         identifier ('.' identifier)?
+   */
+  Token _skipPrefixedIdentifier(Token startToken) {
+    Token token = _skipSimpleIdentifier(startToken);
+    if (token == null) {
+      return null;
+    } else if (!_tokenMatches(token, TokenType.PERIOD)) {
+      return token;
+    }
+    token = token.next;
+    Token nextToken = _skipSimpleIdentifier(token);
+    if (nextToken != null) {
+      return nextToken;
+    } else if (_tokenMatches(token, TokenType.CLOSE_PAREN) ||
+        _tokenMatches(token, TokenType.COMMA)) {
+      // If the `id.` is followed by something that cannot produce a valid
+      // structure then assume this is a prefixed identifier but missing the
+      // trailing identifier
+      return token;
+    }
+    return null;
+  }
+
+  /**
+   * Parse a return type, starting at the [startToken], without actually
+   * creating a return type or changing the current token. Return the token
+   * following the return type that was parsed, or `null` if the given token is
+   * not the first token in a valid return type.
+   *
+   * This method must be kept in sync with [parseReturnType].
+   *
+   *     returnType ::=
+   *         'void'
+   *       | type
+   */
+  Token _skipReturnType(Token startToken) {
+    if (_tokenMatchesKeyword(startToken, Keyword.VOID)) {
+      return startToken.next;
+    } else {
+      return _skipTypeName(startToken);
+    }
+  }
+
+  /**
+   * Parse a simple identifier, starting at the [startToken], without actually
+   * creating a simple identifier or changing the current token. Return the
+   * token following the simple identifier that was parsed, or `null` if the
+   * given token is not the first token in a valid simple identifier.
+   *
+   * This method must be kept in sync with [parseSimpleIdentifier].
+   *
+   *     identifier ::=
+   *         IDENTIFIER
+   */
+  Token _skipSimpleIdentifier(Token startToken) {
+    if (_tokenMatches(startToken, TokenType.IDENTIFIER) ||
+        (_tokenMatches(startToken, TokenType.KEYWORD) &&
+            (startToken as KeywordToken).keyword.isPseudoKeyword)) {
+      return startToken.next;
+    }
+    return null;
+  }
+
+  /**
+   * Parse a string literal that contains interpolations, starting at the
+   * [startToken], without actually creating a string literal or changing the
+   * current token. Return the token following the string literal that was
+   * parsed, or `null` if the given token is not the first token in a valid
+   * string literal.
+   *
+   * This method must be kept in sync with [parseStringInterpolation].
+   */
+  Token _skipStringInterpolation(Token startToken) {
+    Token token = startToken;
+    TokenType type = token.type;
+    while (type == TokenType.STRING_INTERPOLATION_EXPRESSION ||
+        type == TokenType.STRING_INTERPOLATION_IDENTIFIER) {
+      if (type == TokenType.STRING_INTERPOLATION_EXPRESSION) {
+        token = token.next;
+        type = token.type;
+        //
+        // Rather than verify that the following tokens represent a valid
+        // expression, we simply skip tokens until we reach the end of the
+        // interpolation, being careful to handle nested string literals.
+        //
+        int bracketNestingLevel = 1;
+        while (bracketNestingLevel > 0) {
+          if (type == TokenType.EOF) {
+            return null;
+          } else if (type == TokenType.OPEN_CURLY_BRACKET) {
+            bracketNestingLevel++;
+          } else if (type == TokenType.CLOSE_CURLY_BRACKET) {
+            bracketNestingLevel--;
+          } else if (type == TokenType.STRING) {
+            token = _skipStringLiteral(token);
+            if (token == null) {
+              return null;
+            }
+          } else {
+            token = token.next;
+          }
+          type = token.type;
+        }
+        token = token.next;
+        type = token.type;
+      } else {
+        token = token.next;
+        if (token.type != TokenType.IDENTIFIER) {
+          return null;
+        }
+        token = token.next;
+      }
+      type = token.type;
+      if (type == TokenType.STRING) {
+        token = token.next;
+        type = token.type;
+      }
+    }
+    return token;
+  }
+
+  /**
+   * Parse a string literal, starting at the [startToken], without actually
+   * creating a string literal or changing the current token. Return the token
+   * following the string literal that was parsed, or `null` if the given token
+   * is not the first token in a valid string literal.
+   *
+   * This method must be kept in sync with [parseStringLiteral].
+   *
+   *     stringLiteral ::=
+   *         MULTI_LINE_STRING+
+   *       | SINGLE_LINE_STRING+
+   */
+  Token _skipStringLiteral(Token startToken) {
+    Token token = startToken;
+    while (token != null && _tokenMatches(token, TokenType.STRING)) {
+      token = token.next;
+      TokenType type = token.type;
+      if (type == TokenType.STRING_INTERPOLATION_EXPRESSION ||
+          type == TokenType.STRING_INTERPOLATION_IDENTIFIER) {
+        token = _skipStringInterpolation(token);
+      }
+    }
+    if (identical(token, startToken)) {
+      return null;
+    }
+    return token;
+  }
+
+  /**
+   * Parse a list of type arguments, starting at the [startToken], without
+   * actually creating a type argument list or changing the current token.
+   * Return the token following the type argument list that was parsed, or
+   * `null` if the given token is not the first token in a valid type argument
+   * list.
+   *
+   * This method must be kept in sync with [parseTypeArgumentList].
+   *
+   *     typeArguments ::=
+   *         '<' typeList '>'
+   *
+   *     typeList ::=
+   *         type (',' type)*
+   */
+  Token _skipTypeArgumentList(Token startToken) {
+    Token token = startToken;
+    if (!_tokenMatches(token, TokenType.LT)) {
+      return null;
+    }
+    token = _skipTypeName(token.next);
+    if (token == null) {
+      // If the start token '<' is followed by '>'
+      // then assume this should be type argument list but is missing a type
+      token = startToken.next;
+      if (_tokenMatches(token, TokenType.GT)) {
+        return token.next;
+      }
+      return null;
+    }
+    while (_tokenMatches(token, TokenType.COMMA)) {
+      token = _skipTypeName(token.next);
+      if (token == null) {
+        return null;
+      }
+    }
+    if (token.type == TokenType.GT) {
+      return token.next;
+    } else if (token.type == TokenType.GT_GT) {
+      Token second = new Token(TokenType.GT, token.offset + 1);
+      second.setNextWithoutSettingPrevious(token.next);
+      return second;
+    }
+    return null;
+  }
+
+  /**
+   * Parse a type name, starting at the [startToken], without actually creating
+   * a type name or changing the current token. Return the token following the
+   * type name that was parsed, or `null` if the given token is not the first
+   * token in a valid type name.
+   *
+   * This method must be kept in sync with [parseTypeName].
+   *
+   *     type ::=
+   *         qualified typeArguments?
+   */
+  Token _skipTypeName(Token startToken) {
+    Token token = _skipPrefixedIdentifier(startToken);
+    if (token == null) {
+      return null;
+    }
+    if (_tokenMatches(token, TokenType.LT)) {
+      token = _skipTypeArgumentList(token);
+    }
+    return token;
+  }
+
+  /**
+   * Parse a list of type parameters, starting at the [startToken], without
+   * actually creating a type parameter list or changing the current token.
+   * Return the token following the type parameter list that was parsed, or
+   * `null` if the given token is not the first token in a valid type parameter
+   * list.
+   *
+   * This method must be kept in sync with [parseTypeParameterList].
+   *
+   *     typeParameterList ::=
+   *         '<' typeParameter (',' typeParameter)* '>'
+   */
+  Token _skipTypeParameterList(Token startToken) {
+    if (!_tokenMatches(startToken, TokenType.LT)) {
+      return null;
+    }
+    //
+    // We can't skip a type parameter because it can be preceeded by metadata,
+    // so we just assume that everything before the matching end token is valid.
+    //
+    int depth = 1;
+    Token next = startToken.next;
+    while (depth > 0) {
+      if (_tokenMatches(next, TokenType.EOF)) {
+        return null;
+      } else if (_tokenMatches(next, TokenType.LT)) {
+        depth++;
+      } else if (_tokenMatches(next, TokenType.GT)) {
+        depth--;
+      } else if (_tokenMatches(next, TokenType.GT_EQ)) {
+        if (depth == 1) {
+          Token fakeEquals = new Token(TokenType.EQ, next.offset + 2);
+          fakeEquals.setNextWithoutSettingPrevious(next.next);
+          return fakeEquals;
+        }
+        depth--;
+      } else if (_tokenMatches(next, TokenType.GT_GT)) {
+        depth -= 2;
+      } else if (_tokenMatches(next, TokenType.GT_GT_EQ)) {
+        if (depth < 2) {
+          return null;
+        } else if (depth == 2) {
+          Token fakeEquals = new Token(TokenType.EQ, next.offset + 2);
+          fakeEquals.setNextWithoutSettingPrevious(next.next);
+          return fakeEquals;
+        }
+        depth -= 2;
+      }
+      next = next.next;
+    }
+    return next;
+  }
+
+  /**
+   * Return `true` if the given [token] has the given [type].
+   */
+  bool _tokenMatches(Token token, TokenType type) => token.type == type;
+
+  /**
+   * Return `true` if the given [token] is a valid identifier. Valid identifiers
+   * include built-in identifiers (pseudo-keywords).
+   */
+  bool _tokenMatchesIdentifier(Token token) =>
+      _tokenMatches(token, TokenType.IDENTIFIER) ||
+          (_tokenMatches(token, TokenType.KEYWORD) &&
+              (token as KeywordToken).keyword.isPseudoKeyword);
+
+  /**
+   * Return `true` if the given [token] matches the given [keyword].
+   */
+  bool _tokenMatchesKeyword(Token token, Keyword keyword) =>
+      token.type == TokenType.KEYWORD &&
+          (token as KeywordToken).keyword == keyword;
+
+  /**
+   * Return `true` if the given [token] matches the given [identifier].
+   */
+  bool _tokenMatchesString(Token token, String identifier) =>
+      token.type == TokenType.IDENTIFIER && token.lexeme == identifier;
+
+  /**
+   * Translate the characters at the given [index] in the given [lexeme],
+   * appending the translated character to the given [buffer]. The index is
+   * assumed to be valid.
+   */
+  int _translateCharacter(StringBuffer buffer, String lexeme, int index) {
+    int currentChar = lexeme.codeUnitAt(index);
+    if (currentChar != 0x5C) {
+      buffer.writeCharCode(currentChar);
+      return index + 1;
+    }
+    //
+    // We have found an escape sequence, so we parse the string to determine
+    // what kind of escape sequence and what character to add to the builder.
+    //
+    int length = lexeme.length;
+    int currentIndex = index + 1;
+    if (currentIndex >= length) {
+      // Illegal escape sequence: no char after escape.
+      // This cannot actually happen because it would require the escape
+      // character to be the last character in the string, but if it were it
+      // would escape the closing quote, leaving the string unclosed.
+      // reportError(ParserErrorCode.MISSING_CHAR_IN_ESCAPE_SEQUENCE);
+      return length;
+    }
+    currentChar = lexeme.codeUnitAt(currentIndex);
+    if (currentChar == 0x6E) {
+      buffer.writeCharCode(0xA);
+      // newline
+    } else if (currentChar == 0x72) {
+      buffer.writeCharCode(0xD);
+      // carriage return
+    } else if (currentChar == 0x66) {
+      buffer.writeCharCode(0xC);
+      // form feed
+    } else if (currentChar == 0x62) {
+      buffer.writeCharCode(0x8);
+      // backspace
+    } else if (currentChar == 0x74) {
+      buffer.writeCharCode(0x9);
+      // tab
+    } else if (currentChar == 0x76) {
+      buffer.writeCharCode(0xB);
+      // vertical tab
+    } else if (currentChar == 0x78) {
+      if (currentIndex + 2 >= length) {
+        // Illegal escape sequence: not enough hex digits
+        _reportErrorForCurrentToken(ParserErrorCode.INVALID_HEX_ESCAPE);
+        return length;
+      }
+      int firstDigit = lexeme.codeUnitAt(currentIndex + 1);
+      int secondDigit = lexeme.codeUnitAt(currentIndex + 2);
+      if (!_isHexDigit(firstDigit) || !_isHexDigit(secondDigit)) {
+        // Illegal escape sequence: invalid hex digit
+        _reportErrorForCurrentToken(ParserErrorCode.INVALID_HEX_ESCAPE);
+      } else {
+        int charCode = (Character.digit(firstDigit, 16) << 4) +
+            Character.digit(secondDigit, 16);
+        buffer.writeCharCode(charCode);
+      }
+      return currentIndex + 3;
+    } else if (currentChar == 0x75) {
+      currentIndex++;
+      if (currentIndex >= length) {
+        // Illegal escape sequence: not enough hex digits
+        _reportErrorForCurrentToken(ParserErrorCode.INVALID_UNICODE_ESCAPE);
+        return length;
+      }
+      currentChar = lexeme.codeUnitAt(currentIndex);
+      if (currentChar == 0x7B) {
+        currentIndex++;
+        if (currentIndex >= length) {
+          // Illegal escape sequence: incomplete escape
+          _reportErrorForCurrentToken(ParserErrorCode.INVALID_UNICODE_ESCAPE);
+          return length;
+        }
+        currentChar = lexeme.codeUnitAt(currentIndex);
+        int digitCount = 0;
+        int value = 0;
+        while (currentChar != 0x7D) {
+          if (!_isHexDigit(currentChar)) {
+            // Illegal escape sequence: invalid hex digit
+            _reportErrorForCurrentToken(ParserErrorCode.INVALID_UNICODE_ESCAPE);
+            currentIndex++;
+            while (currentIndex < length &&
+                lexeme.codeUnitAt(currentIndex) != 0x7D) {
+              currentIndex++;
+            }
+            return currentIndex + 1;
+          }
+          digitCount++;
+          value = (value << 4) + Character.digit(currentChar, 16);
+          currentIndex++;
+          if (currentIndex >= length) {
+            // Illegal escape sequence: incomplete escape
+            _reportErrorForCurrentToken(ParserErrorCode.INVALID_UNICODE_ESCAPE);
+            return length;
+          }
+          currentChar = lexeme.codeUnitAt(currentIndex);
+        }
+        if (digitCount < 1 || digitCount > 6) {
+          // Illegal escape sequence: not enough or too many hex digits
+          _reportErrorForCurrentToken(ParserErrorCode.INVALID_UNICODE_ESCAPE);
+        }
+        _appendScalarValue(buffer, lexeme.substring(index, currentIndex + 1),
+            value, index, currentIndex);
+        return currentIndex + 1;
+      } else {
+        if (currentIndex + 3 >= length) {
+          // Illegal escape sequence: not enough hex digits
+          _reportErrorForCurrentToken(ParserErrorCode.INVALID_UNICODE_ESCAPE);
+          return length;
+        }
+        int firstDigit = currentChar;
+        int secondDigit = lexeme.codeUnitAt(currentIndex + 1);
+        int thirdDigit = lexeme.codeUnitAt(currentIndex + 2);
+        int fourthDigit = lexeme.codeUnitAt(currentIndex + 3);
+        if (!_isHexDigit(firstDigit) ||
+            !_isHexDigit(secondDigit) ||
+            !_isHexDigit(thirdDigit) ||
+            !_isHexDigit(fourthDigit)) {
+          // Illegal escape sequence: invalid hex digits
+          _reportErrorForCurrentToken(ParserErrorCode.INVALID_UNICODE_ESCAPE);
+        } else {
+          _appendScalarValue(
+              buffer,
+              lexeme
+                  .substring(
+                      index,
+                      currentIndex + 1),
+              (((((Character.digit(firstDigit, 16) << 4) +
+                                  Character.digit(secondDigit, 16)) <<
+                              4) +
+                          Character.digit(thirdDigit, 16)) <<
+                      4) +
+                  Character
+                      .digit(fourthDigit, 16),
+              index,
+              currentIndex +
+                  3);
+        }
+        return currentIndex + 4;
+      }
+    } else {
+      buffer.writeCharCode(currentChar);
+    }
+    return currentIndex + 1;
+  }
+
+  /**
+   * Decrements the error reporting lock level. If level is more than `0`, then
+   * [reportError] wont report any error.
+   */
+  void _unlockErrorListener() {
+    if (_errorListenerLock == 0) {
+      throw new IllegalStateException(
+          "Attempt to unlock not locked error listener.");
+    }
+    _errorListenerLock--;
+  }
+
+  /**
+   * Validate that the given [parameterList] does not contain any field
+   * initializers.
+   */
+  void _validateFormalParameterList(FormalParameterList parameterList) {
+    for (FormalParameter parameter in parameterList.parameters) {
+      if (parameter is FieldFormalParameter) {
+        _reportErrorForNode(
+            ParserErrorCode.FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR,
+            parameter.identifier);
+      }
+    }
+  }
+
+  /**
+   * Validate that the given set of [modifiers] is appropriate for a class and
+   * return the 'abstract' keyword if there is one.
+   */
+  Token _validateModifiersForClass(Modifiers modifiers) {
+    _validateModifiersForTopLevelDeclaration(modifiers);
+    if (modifiers.constKeyword != null) {
+      _reportErrorForToken(ParserErrorCode.CONST_CLASS, modifiers.constKeyword);
+    }
+    if (modifiers.externalKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.EXTERNAL_CLASS, modifiers.externalKeyword);
+    }
+    if (modifiers.finalKeyword != null) {
+      _reportErrorForToken(ParserErrorCode.FINAL_CLASS, modifiers.finalKeyword);
+    }
+    if (modifiers.varKeyword != null) {
+      _reportErrorForToken(ParserErrorCode.VAR_CLASS, modifiers.varKeyword);
+    }
+    return modifiers.abstractKeyword;
+  }
+
+  /**
+   * Validate that the given set of [modifiers] is appropriate for a constructor
+   * and return the 'const' keyword if there is one.
+   */
+  Token _validateModifiersForConstructor(Modifiers modifiers) {
+    if (modifiers.abstractKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.ABSTRACT_CLASS_MEMBER, modifiers.abstractKeyword);
+    }
+    if (modifiers.finalKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.FINAL_CONSTRUCTOR, modifiers.finalKeyword);
+    }
+    if (modifiers.staticKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.STATIC_CONSTRUCTOR, modifiers.staticKeyword);
+    }
+    if (modifiers.varKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.CONSTRUCTOR_WITH_RETURN_TYPE, modifiers.varKeyword);
+    }
+    Token externalKeyword = modifiers.externalKeyword;
+    Token constKeyword = modifiers.constKeyword;
+    Token factoryKeyword = modifiers.factoryKeyword;
+    if (externalKeyword != null &&
+        constKeyword != null &&
+        constKeyword.offset < externalKeyword.offset) {
+      _reportErrorForToken(
+          ParserErrorCode.EXTERNAL_AFTER_CONST, externalKeyword);
+    }
+    if (externalKeyword != null &&
+        factoryKeyword != null &&
+        factoryKeyword.offset < externalKeyword.offset) {
+      _reportErrorForToken(
+          ParserErrorCode.EXTERNAL_AFTER_FACTORY, externalKeyword);
+    }
+    return constKeyword;
+  }
+
+  /**
+   * Validate that the given set of [modifiers] is appropriate for a class and
+   * return the 'abstract' keyword if there is one.
+   */
+  void _validateModifiersForEnum(Modifiers modifiers) {
+    _validateModifiersForTopLevelDeclaration(modifiers);
+    if (modifiers.abstractKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.ABSTRACT_ENUM, modifiers.abstractKeyword);
+    }
+    if (modifiers.constKeyword != null) {
+      _reportErrorForToken(ParserErrorCode.CONST_ENUM, modifiers.constKeyword);
+    }
+    if (modifiers.externalKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.EXTERNAL_ENUM, modifiers.externalKeyword);
+    }
+    if (modifiers.finalKeyword != null) {
+      _reportErrorForToken(ParserErrorCode.FINAL_ENUM, modifiers.finalKeyword);
+    }
+    if (modifiers.varKeyword != null) {
+      _reportErrorForToken(ParserErrorCode.VAR_ENUM, modifiers.varKeyword);
+    }
+  }
+
+  /**
+   * Validate that the given set of [modifiers] is appropriate for a field and
+   * return the 'final', 'const' or 'var' keyword if there is one.
+   */
+  Token _validateModifiersForField(Modifiers modifiers) {
+    if (modifiers.abstractKeyword != null) {
+      _reportErrorForCurrentToken(ParserErrorCode.ABSTRACT_CLASS_MEMBER);
+    }
+    if (modifiers.externalKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.EXTERNAL_FIELD, modifiers.externalKeyword);
+    }
+    if (modifiers.factoryKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.NON_CONSTRUCTOR_FACTORY, modifiers.factoryKeyword);
+    }
+    Token staticKeyword = modifiers.staticKeyword;
+    Token constKeyword = modifiers.constKeyword;
+    Token finalKeyword = modifiers.finalKeyword;
+    Token varKeyword = modifiers.varKeyword;
+    if (constKeyword != null) {
+      if (finalKeyword != null) {
+        _reportErrorForToken(ParserErrorCode.CONST_AND_FINAL, finalKeyword);
+      }
+      if (varKeyword != null) {
+        _reportErrorForToken(ParserErrorCode.CONST_AND_VAR, varKeyword);
+      }
+      if (staticKeyword != null && constKeyword.offset < staticKeyword.offset) {
+        _reportErrorForToken(ParserErrorCode.STATIC_AFTER_CONST, staticKeyword);
+      }
+    } else if (finalKeyword != null) {
+      if (varKeyword != null) {
+        _reportErrorForToken(ParserErrorCode.FINAL_AND_VAR, varKeyword);
+      }
+      if (staticKeyword != null && finalKeyword.offset < staticKeyword.offset) {
+        _reportErrorForToken(ParserErrorCode.STATIC_AFTER_FINAL, staticKeyword);
+      }
+    } else if (varKeyword != null &&
+        staticKeyword != null &&
+        varKeyword.offset < staticKeyword.offset) {
+      _reportErrorForToken(ParserErrorCode.STATIC_AFTER_VAR, staticKeyword);
+    }
+    return Token.lexicallyFirst([constKeyword, finalKeyword, varKeyword]);
+  }
+
+  /**
+   * Validate that the given set of [modifiers] is appropriate for a local
+   * function.
+   */
+  void _validateModifiersForFunctionDeclarationStatement(Modifiers modifiers) {
+    if (modifiers.abstractKeyword != null ||
+        modifiers.constKeyword != null ||
+        modifiers.externalKeyword != null ||
+        modifiers.factoryKeyword != null ||
+        modifiers.finalKeyword != null ||
+        modifiers.staticKeyword != null ||
+        modifiers.varKeyword != null) {
+      _reportErrorForCurrentToken(
+          ParserErrorCode.LOCAL_FUNCTION_DECLARATION_MODIFIER);
+    }
+  }
+
+  /**
+   * Validate that the given set of [modifiers] is appropriate for a getter,
+   * setter, or method.
+   */
+  void _validateModifiersForGetterOrSetterOrMethod(Modifiers modifiers) {
+    if (modifiers.abstractKeyword != null) {
+      _reportErrorForCurrentToken(ParserErrorCode.ABSTRACT_CLASS_MEMBER);
+    }
+    if (modifiers.constKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.CONST_METHOD, modifiers.constKeyword);
+    }
+    if (modifiers.factoryKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.NON_CONSTRUCTOR_FACTORY, modifiers.factoryKeyword);
+    }
+    if (modifiers.finalKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.FINAL_METHOD, modifiers.finalKeyword);
+    }
+    if (modifiers.varKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.VAR_RETURN_TYPE, modifiers.varKeyword);
+    }
+    Token externalKeyword = modifiers.externalKeyword;
+    Token staticKeyword = modifiers.staticKeyword;
+    if (externalKeyword != null &&
+        staticKeyword != null &&
+        staticKeyword.offset < externalKeyword.offset) {
+      _reportErrorForToken(
+          ParserErrorCode.EXTERNAL_AFTER_STATIC, externalKeyword);
+    }
+  }
+
+  /**
+   * Validate that the given set of [modifiers] is appropriate for a getter,
+   * setter, or method.
+   */
+  void _validateModifiersForOperator(Modifiers modifiers) {
+    if (modifiers.abstractKeyword != null) {
+      _reportErrorForCurrentToken(ParserErrorCode.ABSTRACT_CLASS_MEMBER);
+    }
+    if (modifiers.constKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.CONST_METHOD, modifiers.constKeyword);
+    }
+    if (modifiers.factoryKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.NON_CONSTRUCTOR_FACTORY, modifiers.factoryKeyword);
+    }
+    if (modifiers.finalKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.FINAL_METHOD, modifiers.finalKeyword);
+    }
+    if (modifiers.staticKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.STATIC_OPERATOR, modifiers.staticKeyword);
+    }
+    if (modifiers.varKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.VAR_RETURN_TYPE, modifiers.varKeyword);
+    }
+  }
+
+  /**
+   * Validate that the given set of [modifiers] is appropriate for a top-level
+   * declaration.
+   */
+  void _validateModifiersForTopLevelDeclaration(Modifiers modifiers) {
+    if (modifiers.factoryKeyword != null) {
+      _reportErrorForToken(ParserErrorCode.FACTORY_TOP_LEVEL_DECLARATION,
+          modifiers.factoryKeyword);
+    }
+    if (modifiers.staticKeyword != null) {
+      _reportErrorForToken(ParserErrorCode.STATIC_TOP_LEVEL_DECLARATION,
+          modifiers.staticKeyword);
+    }
+  }
+
+  /**
+   * Validate that the given set of [modifiers] is appropriate for a top-level
+   * function.
+   */
+  void _validateModifiersForTopLevelFunction(Modifiers modifiers) {
+    _validateModifiersForTopLevelDeclaration(modifiers);
+    if (modifiers.abstractKeyword != null) {
+      _reportErrorForCurrentToken(ParserErrorCode.ABSTRACT_TOP_LEVEL_FUNCTION);
+    }
+    if (modifiers.constKeyword != null) {
+      _reportErrorForToken(ParserErrorCode.CONST_CLASS, modifiers.constKeyword);
+    }
+    if (modifiers.finalKeyword != null) {
+      _reportErrorForToken(ParserErrorCode.FINAL_CLASS, modifiers.finalKeyword);
+    }
+    if (modifiers.varKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.VAR_RETURN_TYPE, modifiers.varKeyword);
+    }
+  }
+
+  /**
+   * Validate that the given set of [modifiers] is appropriate for a field and
+   * return the 'final', 'const' or 'var' keyword if there is one.
+   */
+  Token _validateModifiersForTopLevelVariable(Modifiers modifiers) {
+    _validateModifiersForTopLevelDeclaration(modifiers);
+    if (modifiers.abstractKeyword != null) {
+      _reportErrorForCurrentToken(ParserErrorCode.ABSTRACT_TOP_LEVEL_VARIABLE);
+    }
+    if (modifiers.externalKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.EXTERNAL_FIELD, modifiers.externalKeyword);
+    }
+    Token constKeyword = modifiers.constKeyword;
+    Token finalKeyword = modifiers.finalKeyword;
+    Token varKeyword = modifiers.varKeyword;
+    if (constKeyword != null) {
+      if (finalKeyword != null) {
+        _reportErrorForToken(ParserErrorCode.CONST_AND_FINAL, finalKeyword);
+      }
+      if (varKeyword != null) {
+        _reportErrorForToken(ParserErrorCode.CONST_AND_VAR, varKeyword);
+      }
+    } else if (finalKeyword != null) {
+      if (varKeyword != null) {
+        _reportErrorForToken(ParserErrorCode.FINAL_AND_VAR, varKeyword);
+      }
+    }
+    return Token.lexicallyFirst([constKeyword, finalKeyword, varKeyword]);
+  }
+
+  /**
+   * Validate that the given set of [modifiers] is appropriate for a class and
+   * return the 'abstract' keyword if there is one.
+   */
+  void _validateModifiersForTypedef(Modifiers modifiers) {
+    _validateModifiersForTopLevelDeclaration(modifiers);
+    if (modifiers.abstractKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.ABSTRACT_TYPEDEF, modifiers.abstractKeyword);
+    }
+    if (modifiers.constKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.CONST_TYPEDEF, modifiers.constKeyword);
+    }
+    if (modifiers.externalKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.EXTERNAL_TYPEDEF, modifiers.externalKeyword);
+    }
+    if (modifiers.finalKeyword != null) {
+      _reportErrorForToken(
+          ParserErrorCode.FINAL_TYPEDEF, modifiers.finalKeyword);
+    }
+    if (modifiers.varKeyword != null) {
+      _reportErrorForToken(ParserErrorCode.VAR_TYPEDEF, modifiers.varKeyword);
+    }
+  }
+}
+/**
+ * A synthetic keyword token.
+ */
+class Parser_SyntheticKeywordToken extends KeywordToken {
+  /**
+   * Initialize a newly created token to represent the given [keyword] at the
+   * given [offset].
+   */
+  Parser_SyntheticKeywordToken(Keyword keyword, int offset)
+      : super(keyword, offset);
+
+  @override
+  int get length => 0;
+
+  @override
+  Token copy() => new Parser_SyntheticKeywordToken(keyword, offset);
+}
+
+/**
+ * The error codes used for errors detected by the parser. The convention for
+ * this class is for the name of the error code to indicate the problem that
+ * caused the error to be generated and for the error message to explain what
+ * is wrong and, when appropriate, how the problem can be corrected.
+ */
+class ParserErrorCode extends ErrorCode {
+  static const ParserErrorCode ABSTRACT_CLASS_MEMBER = const ParserErrorCode(
+      'ABSTRACT_CLASS_MEMBER',
+      "Members of classes cannot be declared to be 'abstract'");
+
+  static const ParserErrorCode ABSTRACT_ENUM = const ParserErrorCode(
+      'ABSTRACT_ENUM', "Enums cannot be declared to be 'abstract'");
+
+  static const ParserErrorCode ABSTRACT_STATIC_METHOD = const ParserErrorCode(
+      'ABSTRACT_STATIC_METHOD',
+      "Static methods cannot be declared to be 'abstract'");
+
+  static const ParserErrorCode ABSTRACT_TOP_LEVEL_FUNCTION =
+      const ParserErrorCode('ABSTRACT_TOP_LEVEL_FUNCTION',
+          "Top-level functions cannot be declared to be 'abstract'");
+
+  static const ParserErrorCode ABSTRACT_TOP_LEVEL_VARIABLE =
+      const ParserErrorCode('ABSTRACT_TOP_LEVEL_VARIABLE',
+          "Top-level variables cannot be declared to be 'abstract'");
+
+  static const ParserErrorCode ABSTRACT_TYPEDEF = const ParserErrorCode(
+      'ABSTRACT_TYPEDEF', "Type aliases cannot be declared to be 'abstract'");
+
+  static const ParserErrorCode ANNOTATION_ON_ENUM_CONSTANT =
+      const ParserErrorCode('ANNOTATION_ON_ENUM_CONSTANT',
+          "Enum constants cannot have annotations");
+
+  static const ParserErrorCode ASSERT_DOES_NOT_TAKE_ASSIGNMENT =
+      const ParserErrorCode('ASSERT_DOES_NOT_TAKE_ASSIGNMENT',
+          "Assert cannot be called on an assignment");
+
+  static const ParserErrorCode ASSERT_DOES_NOT_TAKE_CASCADE =
+      const ParserErrorCode(
+          'ASSERT_DOES_NOT_TAKE_CASCADE', "Assert cannot be called on cascade");
+
+  static const ParserErrorCode ASSERT_DOES_NOT_TAKE_THROW =
+      const ParserErrorCode(
+          'ASSERT_DOES_NOT_TAKE_THROW', "Assert cannot be called on throws");
+
+  static const ParserErrorCode ASSERT_DOES_NOT_TAKE_RETHROW =
+      const ParserErrorCode('ASSERT_DOES_NOT_TAKE_RETHROW',
+          "Assert cannot be called on rethrows");
+
+  /**
+   * 16.32 Identifier Reference: It is a compile-time error if any of the
+   * identifiers async, await, or yield is used as an identifier in a function
+   * body marked with either async, async*, or sync*.
+   */
+  static const ParserErrorCode ASYNC_KEYWORD_USED_AS_IDENTIFIER =
+      const ParserErrorCode('ASYNC_KEYWORD_USED_AS_IDENTIFIER',
+          "The keywords 'async', 'await', and 'yield' may not be used as identifiers in an asynchronous or generator function.");
+
+  static const ParserErrorCode BREAK_OUTSIDE_OF_LOOP = const ParserErrorCode(
+      'BREAK_OUTSIDE_OF_LOOP',
+      "A break statement cannot be used outside of a loop or switch statement");
+
+  static const ParserErrorCode CLASS_IN_CLASS = const ParserErrorCode(
+      'CLASS_IN_CLASS', "Classes cannot be declared inside other classes");
+
+  static const ParserErrorCode COLON_IN_PLACE_OF_IN = const ParserErrorCode(
+      'COLON_IN_PLACE_OF_IN', "For-in loops use 'in' rather than a colon");
+
+  static const ParserErrorCode CONST_AND_FINAL = const ParserErrorCode(
+      'CONST_AND_FINAL',
+      "Members cannot be declared to be both 'const' and 'final'");
+
+  static const ParserErrorCode CONST_AND_VAR = const ParserErrorCode(
+      'CONST_AND_VAR',
+      "Members cannot be declared to be both 'const' and 'var'");
+
+  static const ParserErrorCode CONST_CLASS = const ParserErrorCode(
+      'CONST_CLASS', "Classes cannot be declared to be 'const'");
+
+  static const ParserErrorCode CONST_CONSTRUCTOR_WITH_BODY =
+      const ParserErrorCode('CONST_CONSTRUCTOR_WITH_BODY',
+          "'const' constructors cannot have a body");
+
+  static const ParserErrorCode CONST_ENUM = const ParserErrorCode(
+      'CONST_ENUM', "Enums cannot be declared to be 'const'");
+
+  static const ParserErrorCode CONST_FACTORY = const ParserErrorCode(
+      'CONST_FACTORY',
+      "Only redirecting factory constructors can be declared to be 'const'");
+
+  static const ParserErrorCode CONST_METHOD = const ParserErrorCode(
+      'CONST_METHOD',
+      "Getters, setters and methods cannot be declared to be 'const'");
+
+  static const ParserErrorCode CONST_TYPEDEF = const ParserErrorCode(
+      'CONST_TYPEDEF', "Type aliases cannot be declared to be 'const'");
+
+  static const ParserErrorCode CONSTRUCTOR_WITH_RETURN_TYPE =
+      const ParserErrorCode('CONSTRUCTOR_WITH_RETURN_TYPE',
+          "Constructors cannot have a return type");
+
+  static const ParserErrorCode CONTINUE_OUTSIDE_OF_LOOP = const ParserErrorCode(
+      'CONTINUE_OUTSIDE_OF_LOOP',
+      "A continue statement cannot be used outside of a loop or switch statement");
+
+  static const ParserErrorCode CONTINUE_WITHOUT_LABEL_IN_CASE =
+      const ParserErrorCode('CONTINUE_WITHOUT_LABEL_IN_CASE',
+          "A continue statement in a switch statement must have a label as a target");
+
+  static const ParserErrorCode DEPRECATED_CLASS_TYPE_ALIAS =
+      const ParserErrorCode('DEPRECATED_CLASS_TYPE_ALIAS',
+          "The 'typedef' mixin application was replaced with 'class'");
+
+  static const ParserErrorCode DIRECTIVE_AFTER_DECLARATION =
+      const ParserErrorCode('DIRECTIVE_AFTER_DECLARATION',
+          "Directives must appear before any declarations");
+
+  static const ParserErrorCode DUPLICATE_LABEL_IN_SWITCH_STATEMENT =
+      const ParserErrorCode('DUPLICATE_LABEL_IN_SWITCH_STATEMENT',
+          "The label {0} was already used in this switch statement");
+
+  static const ParserErrorCode DUPLICATED_MODIFIER = const ParserErrorCode(
+      'DUPLICATED_MODIFIER', "The modifier '{0}' was already specified.");
+
+  static const ParserErrorCode EMPTY_ENUM_BODY = const ParserErrorCode(
+      'EMPTY_ENUM_BODY', "An enum must declare at least one constant name");
+
+  static const ParserErrorCode ENUM_IN_CLASS = const ParserErrorCode(
+      'ENUM_IN_CLASS', "Enums cannot be declared inside classes");
+
+  static const ParserErrorCode EQUALITY_CANNOT_BE_EQUALITY_OPERAND =
+      const ParserErrorCode('EQUALITY_CANNOT_BE_EQUALITY_OPERAND',
+          "Equality expression cannot be operand of another equality expression.");
+
+  static const ParserErrorCode EXPECTED_CASE_OR_DEFAULT = const ParserErrorCode(
+      'EXPECTED_CASE_OR_DEFAULT', "Expected 'case' or 'default'");
+
+  static const ParserErrorCode EXPECTED_CLASS_MEMBER =
+      const ParserErrorCode('EXPECTED_CLASS_MEMBER', "Expected a class member");
+
+  static const ParserErrorCode EXPECTED_EXECUTABLE = const ParserErrorCode(
+      'EXPECTED_EXECUTABLE',
+      "Expected a method, getter, setter or operator declaration");
+
+  static const ParserErrorCode EXPECTED_LIST_OR_MAP_LITERAL =
+      const ParserErrorCode(
+          'EXPECTED_LIST_OR_MAP_LITERAL', "Expected a list or map literal");
+
+  static const ParserErrorCode EXPECTED_STRING_LITERAL = const ParserErrorCode(
+      'EXPECTED_STRING_LITERAL', "Expected a string literal");
+
+  static const ParserErrorCode EXPECTED_TOKEN =
+      const ParserErrorCode('EXPECTED_TOKEN', "Expected to find '{0}'");
+
+  static const ParserErrorCode EXPECTED_TYPE_NAME =
+      const ParserErrorCode('EXPECTED_TYPE_NAME', "Expected a type name");
+
+  static const ParserErrorCode EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE =
+      const ParserErrorCode('EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE',
+          "Export directives must preceed part directives");
+
+  static const ParserErrorCode EXTERNAL_AFTER_CONST = const ParserErrorCode(
+      'EXTERNAL_AFTER_CONST',
+      "The modifier 'external' should be before the modifier 'const'");
+
+  static const ParserErrorCode EXTERNAL_AFTER_FACTORY = const ParserErrorCode(
+      'EXTERNAL_AFTER_FACTORY',
+      "The modifier 'external' should be before the modifier 'factory'");
+
+  static const ParserErrorCode EXTERNAL_AFTER_STATIC = const ParserErrorCode(
+      'EXTERNAL_AFTER_STATIC',
+      "The modifier 'external' should be before the modifier 'static'");
+
+  static const ParserErrorCode EXTERNAL_CLASS = const ParserErrorCode(
+      'EXTERNAL_CLASS', "Classes cannot be declared to be 'external'");
+
+  static const ParserErrorCode EXTERNAL_CONSTRUCTOR_WITH_BODY =
+      const ParserErrorCode('EXTERNAL_CONSTRUCTOR_WITH_BODY',
+          "External constructors cannot have a body");
+
+  static const ParserErrorCode EXTERNAL_ENUM = const ParserErrorCode(
+      'EXTERNAL_ENUM', "Enums cannot be declared to be 'external'");
+
+  static const ParserErrorCode EXTERNAL_FIELD = const ParserErrorCode(
+      'EXTERNAL_FIELD', "Fields cannot be declared to be 'external'");
+
+  static const ParserErrorCode EXTERNAL_GETTER_WITH_BODY =
+      const ParserErrorCode(
+          'EXTERNAL_GETTER_WITH_BODY', "External getters cannot have a body");
+
+  static const ParserErrorCode EXTERNAL_METHOD_WITH_BODY =
+      const ParserErrorCode(
+          'EXTERNAL_METHOD_WITH_BODY', "External methods cannot have a body");
+
+  static const ParserErrorCode EXTERNAL_OPERATOR_WITH_BODY =
+      const ParserErrorCode('EXTERNAL_OPERATOR_WITH_BODY',
+          "External operators cannot have a body");
+
+  static const ParserErrorCode EXTERNAL_SETTER_WITH_BODY =
+      const ParserErrorCode(
+          'EXTERNAL_SETTER_WITH_BODY', "External setters cannot have a body");
+
+  static const ParserErrorCode EXTERNAL_TYPEDEF = const ParserErrorCode(
+      'EXTERNAL_TYPEDEF', "Type aliases cannot be declared to be 'external'");
+
+  static const ParserErrorCode FACTORY_TOP_LEVEL_DECLARATION =
+      const ParserErrorCode('FACTORY_TOP_LEVEL_DECLARATION',
+          "Top-level declarations cannot be declared to be 'factory'");
+
+  static const ParserErrorCode FACTORY_WITH_INITIALIZERS =
+      const ParserErrorCode('FACTORY_WITH_INITIALIZERS',
+          "A 'factory' constructor cannot have initializers",
+          "Either remove the 'factory' keyword to make this a generative "
+          "constructor or remove the initializers.");
+
+  static const ParserErrorCode FACTORY_WITHOUT_BODY = const ParserErrorCode(
+      'FACTORY_WITHOUT_BODY',
+      "A non-redirecting 'factory' constructor must have a body");
+
+  static const ParserErrorCode FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR =
+      const ParserErrorCode('FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR',
+          "Field initializers can only be used in a constructor");
+
+  static const ParserErrorCode FINAL_AND_VAR = const ParserErrorCode(
+      'FINAL_AND_VAR',
+      "Members cannot be declared to be both 'final' and 'var'");
+
+  static const ParserErrorCode FINAL_CLASS = const ParserErrorCode(
+      'FINAL_CLASS', "Classes cannot be declared to be 'final'");
+
+  static const ParserErrorCode FINAL_CONSTRUCTOR = const ParserErrorCode(
+      'FINAL_CONSTRUCTOR', "A constructor cannot be declared to be 'final'");
+
+  static const ParserErrorCode FINAL_ENUM = const ParserErrorCode(
+      'FINAL_ENUM', "Enums cannot be declared to be 'final'");
+
+  static const ParserErrorCode FINAL_METHOD = const ParserErrorCode(
+      'FINAL_METHOD',
+      "Getters, setters and methods cannot be declared to be 'final'");
+
+  static const ParserErrorCode FINAL_TYPEDEF = const ParserErrorCode(
+      'FINAL_TYPEDEF', "Type aliases cannot be declared to be 'final'");
+
+  static const ParserErrorCode FUNCTION_TYPED_PARAMETER_VAR = const ParserErrorCode(
+      'FUNCTION_TYPED_PARAMETER_VAR',
+      "Function typed parameters cannot specify 'const', 'final' or 'var' instead of return type");
+
+  static const ParserErrorCode GETTER_IN_FUNCTION = const ParserErrorCode(
+      'GETTER_IN_FUNCTION',
+      "Getters cannot be defined within methods or functions");
+
+  static const ParserErrorCode GETTER_WITH_PARAMETERS = const ParserErrorCode(
+      'GETTER_WITH_PARAMETERS',
+      "Getter should be declared without a parameter list");
+
+  static const ParserErrorCode ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE =
+      const ParserErrorCode('ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE',
+          "Illegal assignment to non-assignable expression");
+
+  static const ParserErrorCode IMPLEMENTS_BEFORE_EXTENDS =
+      const ParserErrorCode('IMPLEMENTS_BEFORE_EXTENDS',
+          "The extends clause must be before the implements clause");
+
+  static const ParserErrorCode IMPLEMENTS_BEFORE_WITH = const ParserErrorCode(
+      'IMPLEMENTS_BEFORE_WITH',
+      "The with clause must be before the implements clause");
+
+  static const ParserErrorCode IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE =
+      const ParserErrorCode('IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE',
+          "Import directives must preceed part directives");
+
+  static const ParserErrorCode INITIALIZED_VARIABLE_IN_FOR_EACH =
+      const ParserErrorCode('INITIALIZED_VARIABLE_IN_FOR_EACH',
+          "The loop variable in a for-each loop cannot be initialized");
+
+  static const ParserErrorCode INVALID_AWAIT_IN_FOR = const ParserErrorCode(
+      'INVALID_AWAIT_IN_FOR',
+      "The modifier 'await' is not allowed for a normal 'for' statement",
+      "Remove the keyword or use a for-each statement.");
+
+  static const ParserErrorCode INVALID_CODE_POINT = const ParserErrorCode(
+      'INVALID_CODE_POINT',
+      "The escape sequence '{0}' is not a valid code point");
+
+  static const ParserErrorCode INVALID_COMMENT_REFERENCE = const ParserErrorCode(
+      'INVALID_COMMENT_REFERENCE',
+      "Comment references should contain a possibly prefixed identifier and can start with 'new', but should not contain anything else");
+
+  static const ParserErrorCode INVALID_HEX_ESCAPE = const ParserErrorCode(
+      'INVALID_HEX_ESCAPE',
+      "An escape sequence starting with '\\x' must be followed by 2 hexidecimal digits");
+
+  static const ParserErrorCode INVALID_OPERATOR = const ParserErrorCode(
+      'INVALID_OPERATOR', "The string '{0}' is not a valid operator");
+
+  static const ParserErrorCode INVALID_OPERATOR_FOR_SUPER =
+      const ParserErrorCode('INVALID_OPERATOR_FOR_SUPER',
+          "The operator '{0}' cannot be used with 'super'");
+
+  static const ParserErrorCode INVALID_STAR_AFTER_ASYNC = const ParserErrorCode(
+      'INVALID_STAR_AFTER_ASYNC',
+      "The modifier 'async*' is not allowed for an expression function body",
+      "Convert the body to a block.");
+
+  static const ParserErrorCode INVALID_SYNC = const ParserErrorCode(
+      'INVALID_SYNC',
+      "The modifier 'sync' is not allowed for an exrpression function body",
+      "Convert the body to a block.");
+
+  static const ParserErrorCode INVALID_UNICODE_ESCAPE = const ParserErrorCode(
+      'INVALID_UNICODE_ESCAPE',
+      "An escape sequence starting with '\\u' must be followed by 4 hexidecimal digits or from 1 to 6 digits between '{' and '}'");
+
+  static const ParserErrorCode LIBRARY_DIRECTIVE_NOT_FIRST =
+      const ParserErrorCode('LIBRARY_DIRECTIVE_NOT_FIRST',
+          "The library directive must appear before all other directives");
+
+  static const ParserErrorCode LOCAL_FUNCTION_DECLARATION_MODIFIER =
+      const ParserErrorCode('LOCAL_FUNCTION_DECLARATION_MODIFIER',
+          "Local function declarations cannot specify any modifier");
+
+  static const ParserErrorCode MISSING_ASSIGNABLE_SELECTOR =
+      const ParserErrorCode('MISSING_ASSIGNABLE_SELECTOR',
+          "Missing selector such as \".<identifier>\" or \"[0]\"");
+
+  static const ParserErrorCode MISSING_ASSIGNMENT_IN_INITIALIZER =
+      const ParserErrorCode('MISSING_ASSIGNMENT_IN_INITIALIZER',
+          "Expected an assignment after the field name");
+
+  static const ParserErrorCode MISSING_CATCH_OR_FINALLY = const ParserErrorCode(
+      'MISSING_CATCH_OR_FINALLY',
+      "A try statement must have either a catch or finally clause");
+
+  static const ParserErrorCode MISSING_CLASS_BODY = const ParserErrorCode(
+      'MISSING_CLASS_BODY',
+      "A class definition must have a body, even if it is empty");
+
+  static const ParserErrorCode MISSING_CLOSING_PARENTHESIS =
+      const ParserErrorCode(
+          'MISSING_CLOSING_PARENTHESIS', "The closing parenthesis is missing");
+
+  static const ParserErrorCode MISSING_CONST_FINAL_VAR_OR_TYPE =
+      const ParserErrorCode('MISSING_CONST_FINAL_VAR_OR_TYPE',
+          "Variables must be declared using the keywords 'const', 'final', 'var' or a type name");
+
+  static const ParserErrorCode MISSING_ENUM_BODY = const ParserErrorCode(
+      'MISSING_ENUM_BODY',
+      "An enum definition must have a body with at least one constant name");
+
+  static const ParserErrorCode MISSING_EXPRESSION_IN_INITIALIZER =
+      const ParserErrorCode('MISSING_EXPRESSION_IN_INITIALIZER',
+          "Expected an expression after the assignment operator");
+
+  static const ParserErrorCode MISSING_EXPRESSION_IN_THROW =
+      const ParserErrorCode('MISSING_EXPRESSION_IN_THROW',
+          "Throw expressions must compute the object to be thrown");
+
+  static const ParserErrorCode MISSING_FUNCTION_BODY = const ParserErrorCode(
+      'MISSING_FUNCTION_BODY', "A function body must be provided");
+
+  static const ParserErrorCode MISSING_FUNCTION_PARAMETERS =
+      const ParserErrorCode('MISSING_FUNCTION_PARAMETERS',
+          "Functions must have an explicit list of parameters");
+
+  static const ParserErrorCode MISSING_METHOD_PARAMETERS =
+      const ParserErrorCode('MISSING_METHOD_PARAMETERS',
+          "Methods must have an explicit list of parameters");
+
+  static const ParserErrorCode MISSING_GET = const ParserErrorCode(
+      'MISSING_GET',
+      "Getters must have the keyword 'get' before the getter name");
+
+  static const ParserErrorCode MISSING_IDENTIFIER =
+      const ParserErrorCode('MISSING_IDENTIFIER', "Expected an identifier");
+
+  static const ParserErrorCode MISSING_INITIALIZER =
+      const ParserErrorCode('MISSING_INITIALIZER', "Expected an initializer");
+
+  static const ParserErrorCode MISSING_KEYWORD_OPERATOR = const ParserErrorCode(
+      'MISSING_KEYWORD_OPERATOR',
+      "Operator declarations must be preceeded by the keyword 'operator'");
+
+  static const ParserErrorCode MISSING_NAME_IN_LIBRARY_DIRECTIVE =
+      const ParserErrorCode('MISSING_NAME_IN_LIBRARY_DIRECTIVE',
+          "Library directives must include a library name");
+
+  static const ParserErrorCode MISSING_NAME_IN_PART_OF_DIRECTIVE =
+      const ParserErrorCode('MISSING_NAME_IN_PART_OF_DIRECTIVE',
+          "Library directives must include a library name");
+
+  static const ParserErrorCode MISSING_PREFIX_IN_DEFERRED_IMPORT =
+      const ParserErrorCode('MISSING_PREFIX_IN_DEFERRED_IMPORT',
+          "Deferred imports must have a prefix");
+
+  static const ParserErrorCode MISSING_STAR_AFTER_SYNC = const ParserErrorCode(
+      'MISSING_STAR_AFTER_SYNC',
+      "The modifier 'sync' must be followed by a star ('*')",
+      "Remove the modifier or add a star.");
+
+  static const ParserErrorCode MISSING_STATEMENT =
+      const ParserErrorCode('MISSING_STATEMENT', "Expected a statement");
+
+  static const ParserErrorCode MISSING_TERMINATOR_FOR_PARAMETER_GROUP =
+      const ParserErrorCode('MISSING_TERMINATOR_FOR_PARAMETER_GROUP',
+          "There is no '{0}' to close the parameter group");
+
+  static const ParserErrorCode MISSING_TYPEDEF_PARAMETERS =
+      const ParserErrorCode('MISSING_TYPEDEF_PARAMETERS',
+          "Type aliases for functions must have an explicit list of parameters");
+
+  static const ParserErrorCode MISSING_VARIABLE_IN_FOR_EACH = const ParserErrorCode(
+      'MISSING_VARIABLE_IN_FOR_EACH',
+      "A loop variable must be declared in a for-each loop before the 'in', but none were found");
+
+  static const ParserErrorCode MIXED_PARAMETER_GROUPS = const ParserErrorCode(
+      'MIXED_PARAMETER_GROUPS',
+      "Cannot have both positional and named parameters in a single parameter list");
+
+  static const ParserErrorCode MULTIPLE_EXTENDS_CLAUSES = const ParserErrorCode(
+      'MULTIPLE_EXTENDS_CLAUSES',
+      "Each class definition can have at most one extends clause");
+
+  static const ParserErrorCode MULTIPLE_IMPLEMENTS_CLAUSES =
+      const ParserErrorCode('MULTIPLE_IMPLEMENTS_CLAUSES',
+          "Each class definition can have at most one implements clause");
+
+  static const ParserErrorCode MULTIPLE_LIBRARY_DIRECTIVES =
+      const ParserErrorCode('MULTIPLE_LIBRARY_DIRECTIVES',
+          "Only one library directive may be declared in a file");
+
+  static const ParserErrorCode MULTIPLE_NAMED_PARAMETER_GROUPS =
+      const ParserErrorCode('MULTIPLE_NAMED_PARAMETER_GROUPS',
+          "Cannot have multiple groups of named parameters in a single parameter list");
+
+  static const ParserErrorCode MULTIPLE_PART_OF_DIRECTIVES =
+      const ParserErrorCode('MULTIPLE_PART_OF_DIRECTIVES',
+          "Only one part-of directive may be declared in a file");
+
+  static const ParserErrorCode MULTIPLE_POSITIONAL_PARAMETER_GROUPS =
+      const ParserErrorCode('MULTIPLE_POSITIONAL_PARAMETER_GROUPS',
+          "Cannot have multiple groups of positional parameters in a single parameter list");
+
+  static const ParserErrorCode MULTIPLE_VARIABLES_IN_FOR_EACH =
+      const ParserErrorCode('MULTIPLE_VARIABLES_IN_FOR_EACH',
+          "A single loop variable must be declared in a for-each loop before the 'in', but {0} were found");
+
+  static const ParserErrorCode MULTIPLE_WITH_CLAUSES = const ParserErrorCode(
+      'MULTIPLE_WITH_CLAUSES',
+      "Each class definition can have at most one with clause");
+
+  static const ParserErrorCode NAMED_FUNCTION_EXPRESSION =
+      const ParserErrorCode(
+          'NAMED_FUNCTION_EXPRESSION', "Function expressions cannot be named");
+
+  static const ParserErrorCode NAMED_PARAMETER_OUTSIDE_GROUP =
+      const ParserErrorCode('NAMED_PARAMETER_OUTSIDE_GROUP',
+          "Named parameters must be enclosed in curly braces ('{' and '}')");
+
+  static const ParserErrorCode NATIVE_CLAUSE_IN_NON_SDK_CODE =
+      const ParserErrorCode('NATIVE_CLAUSE_IN_NON_SDK_CODE',
+          "Native clause can only be used in the SDK and code that is loaded through native extensions");
+
+  static const ParserErrorCode NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE =
+      const ParserErrorCode('NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE',
+          "Native functions can only be declared in the SDK and code that is loaded through native extensions");
+
+  static const ParserErrorCode NON_CONSTRUCTOR_FACTORY = const ParserErrorCode(
+      'NON_CONSTRUCTOR_FACTORY',
+      "Only constructors can be declared to be a 'factory'");
+
+  static const ParserErrorCode NON_IDENTIFIER_LIBRARY_NAME =
+      const ParserErrorCode('NON_IDENTIFIER_LIBRARY_NAME',
+          "The name of a library must be an identifier");
+
+  static const ParserErrorCode NON_PART_OF_DIRECTIVE_IN_PART =
+      const ParserErrorCode('NON_PART_OF_DIRECTIVE_IN_PART',
+          "The part-of directive must be the only directive in a part");
+
+  static const ParserErrorCode NON_STRING_LITERAL_AS_URI =
+      const ParserErrorCode('NON_STRING_LITERAL_AS_URI',
+          "The URI must be a string literal",
+          "Enclose the URI in either single or double quotes.");
+
+  static const ParserErrorCode NON_USER_DEFINABLE_OPERATOR =
+      const ParserErrorCode('NON_USER_DEFINABLE_OPERATOR',
+          "The operator '{0}' is not user definable");
+
+  static const ParserErrorCode NORMAL_BEFORE_OPTIONAL_PARAMETERS =
+      const ParserErrorCode('NORMAL_BEFORE_OPTIONAL_PARAMETERS',
+          "Normal parameters must occur before optional parameters");
+
+  static const ParserErrorCode POSITIONAL_AFTER_NAMED_ARGUMENT =
+      const ParserErrorCode('POSITIONAL_AFTER_NAMED_ARGUMENT',
+          "Positional arguments must occur before named arguments");
+
+  static const ParserErrorCode POSITIONAL_PARAMETER_OUTSIDE_GROUP =
+      const ParserErrorCode('POSITIONAL_PARAMETER_OUTSIDE_GROUP',
+          "Positional parameters must be enclosed in square brackets ('[' and ']')");
+
+  static const ParserErrorCode REDIRECTION_IN_NON_FACTORY_CONSTRUCTOR =
+      const ParserErrorCode('REDIRECTION_IN_NON_FACTORY_CONSTRUCTOR',
+          "Only factory constructor can specify '=' redirection.");
+
+  static const ParserErrorCode SETTER_IN_FUNCTION = const ParserErrorCode(
+      'SETTER_IN_FUNCTION',
+      "Setters cannot be defined within methods or functions");
+
+  static const ParserErrorCode STATIC_AFTER_CONST = const ParserErrorCode(
+      'STATIC_AFTER_CONST',
+      "The modifier 'static' should be before the modifier 'const'");
+
+  static const ParserErrorCode STATIC_AFTER_FINAL = const ParserErrorCode(
+      'STATIC_AFTER_FINAL',
+      "The modifier 'static' should be before the modifier 'final'");
+
+  static const ParserErrorCode STATIC_AFTER_VAR = const ParserErrorCode(
+      'STATIC_AFTER_VAR',
+      "The modifier 'static' should be before the modifier 'var'");
+
+  static const ParserErrorCode STATIC_CONSTRUCTOR = const ParserErrorCode(
+      'STATIC_CONSTRUCTOR', "Constructors cannot be static");
+
+  static const ParserErrorCode STATIC_GETTER_WITHOUT_BODY =
+      const ParserErrorCode(
+          'STATIC_GETTER_WITHOUT_BODY', "A 'static' getter must have a body");
+
+  static const ParserErrorCode STATIC_OPERATOR =
+      const ParserErrorCode('STATIC_OPERATOR', "Operators cannot be static");
+
+  static const ParserErrorCode STATIC_SETTER_WITHOUT_BODY =
+      const ParserErrorCode(
+          'STATIC_SETTER_WITHOUT_BODY', "A 'static' setter must have a body");
+
+  static const ParserErrorCode STATIC_TOP_LEVEL_DECLARATION =
+      const ParserErrorCode('STATIC_TOP_LEVEL_DECLARATION',
+          "Top-level declarations cannot be declared to be 'static'");
+
+  static const ParserErrorCode SWITCH_HAS_CASE_AFTER_DEFAULT_CASE =
+      const ParserErrorCode('SWITCH_HAS_CASE_AFTER_DEFAULT_CASE',
+          "The 'default' case should be the last case in a switch statement");
+
+  static const ParserErrorCode SWITCH_HAS_MULTIPLE_DEFAULT_CASES =
+      const ParserErrorCode('SWITCH_HAS_MULTIPLE_DEFAULT_CASES',
+          "The 'default' case can only be declared once");
+
+  static const ParserErrorCode TOP_LEVEL_OPERATOR = const ParserErrorCode(
+      'TOP_LEVEL_OPERATOR', "Operators must be declared within a class");
+
+  static const ParserErrorCode TYPEDEF_IN_CLASS = const ParserErrorCode(
+      'TYPEDEF_IN_CLASS',
+      "Function type aliases cannot be declared inside classes");
+
+  static const ParserErrorCode UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP =
+      const ParserErrorCode('UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP',
+          "There is no '{0}' to open a parameter group");
+
+  static const ParserErrorCode UNEXPECTED_TOKEN =
+      const ParserErrorCode('UNEXPECTED_TOKEN', "Unexpected token '{0}'");
+
+  static const ParserErrorCode WITH_BEFORE_EXTENDS = const ParserErrorCode(
+      'WITH_BEFORE_EXTENDS',
+      "The extends clause must be before the with clause");
+
+  static const ParserErrorCode WITH_WITHOUT_EXTENDS = const ParserErrorCode(
+      'WITH_WITHOUT_EXTENDS',
+      "The with clause cannot be used without an extends clause");
+
+  static const ParserErrorCode WRONG_SEPARATOR_FOR_NAMED_PARAMETER =
+      const ParserErrorCode('WRONG_SEPARATOR_FOR_NAMED_PARAMETER',
+          "The default value of a named parameter should be preceeded by ':'");
+
+  static const ParserErrorCode WRONG_SEPARATOR_FOR_POSITIONAL_PARAMETER =
+      const ParserErrorCode('WRONG_SEPARATOR_FOR_POSITIONAL_PARAMETER',
+          "The default value of a positional parameter should be preceeded by '='");
+
+  static const ParserErrorCode WRONG_TERMINATOR_FOR_PARAMETER_GROUP =
+      const ParserErrorCode('WRONG_TERMINATOR_FOR_PARAMETER_GROUP',
+          "Expected '{0}' to close parameter group");
+
+  static const ParserErrorCode VAR_AND_TYPE = const ParserErrorCode(
+      'VAR_AND_TYPE',
+      "Variables cannot be declared using both 'var' and a type name; remove the 'var'");
+
+  static const ParserErrorCode VAR_AS_TYPE_NAME = const ParserErrorCode(
+      'VAR_AS_TYPE_NAME', "The keyword 'var' cannot be used as a type name");
+
+  static const ParserErrorCode VAR_CLASS = const ParserErrorCode(
+      'VAR_CLASS', "Classes cannot be declared to be 'var'");
+
+  static const ParserErrorCode VAR_ENUM =
+      const ParserErrorCode('VAR_ENUM', "Enums cannot be declared to be 'var'");
+
+  static const ParserErrorCode VAR_RETURN_TYPE = const ParserErrorCode(
+      'VAR_RETURN_TYPE', "The return type cannot be 'var'");
+
+  static const ParserErrorCode VAR_TYPEDEF = const ParserErrorCode(
+      'VAR_TYPEDEF', "Type aliases cannot be declared to be 'var'");
+
+  static const ParserErrorCode VOID_PARAMETER = const ParserErrorCode(
+      'VOID_PARAMETER', "Parameters cannot have a type of 'void'");
+
+  static const ParserErrorCode VOID_VARIABLE = const ParserErrorCode(
+      'VOID_VARIABLE', "Variables cannot have a type of 'void'");
+
+  /**
+   * Initialize a newly created error code to have the given [name]. The message
+   * associated with the error will be created from the given [message]
+   * template. The correction associated with the error will be created from the
+   * given [correction] template.
+   */
+  const ParserErrorCode(String name, String message, [String correction])
+      : super(name, message, correction);
+
+  @override
+  ErrorSeverity get errorSeverity => ErrorSeverity.ERROR;
+
+  @override
+  ErrorType get type => ErrorType.SYNTACTIC_ERROR;
+}
+
+/**
+ * An object that copies resolution information from one AST structure to
+ * another as long as the structures of the corresponding children of a pair of
+ * nodes are the same.
+ */
+class ResolutionCopier implements AstVisitor<bool> {
+  /**
+   * The AST node with which the node being visited is to be compared. This is
+   * only valid at the beginning of each visit method (until [isEqualNodes] is
+   * invoked).
+   */
+  AstNode _toNode;
+
+  @override
+  bool visitAdjacentStrings(AdjacentStrings node) {
+    AdjacentStrings toNode = this._toNode as AdjacentStrings;
+    return _isEqualNodeLists(node.strings, toNode.strings);
+  }
+
+  @override
+  bool visitAnnotation(Annotation node) {
+    Annotation toNode = this._toNode as Annotation;
+    if (_and(_isEqualTokens(node.atSign, toNode.atSign),
+        _isEqualNodes(node.name, toNode.name),
+        _isEqualTokens(node.period, toNode.period),
+        _isEqualNodes(node.constructorName, toNode.constructorName),
+        _isEqualNodes(node.arguments, toNode.arguments))) {
+      toNode.element = node.element;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitArgumentList(ArgumentList node) {
+    ArgumentList toNode = this._toNode as ArgumentList;
+    return _and(_isEqualTokens(node.leftParenthesis, toNode.leftParenthesis),
+        _isEqualNodeLists(node.arguments, toNode.arguments),
+        _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis));
+  }
+
+  @override
+  bool visitAsExpression(AsExpression node) {
+    AsExpression toNode = this._toNode as AsExpression;
+    if (_and(_isEqualNodes(node.expression, toNode.expression),
+        _isEqualTokens(node.asOperator, toNode.asOperator),
+        _isEqualNodes(node.type, toNode.type))) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitAssertStatement(AssertStatement node) {
+    AssertStatement toNode = this._toNode as AssertStatement;
+    return _and(_isEqualTokens(node.assertKeyword, toNode.assertKeyword),
+        _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis),
+        _isEqualNodes(node.condition, toNode.condition),
+        _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis),
+        _isEqualTokens(node.semicolon, toNode.semicolon));
+  }
+
+  @override
+  bool visitAssignmentExpression(AssignmentExpression node) {
+    AssignmentExpression toNode = this._toNode as AssignmentExpression;
+    if (_and(_isEqualNodes(node.leftHandSide, toNode.leftHandSide),
+        _isEqualTokens(node.operator, toNode.operator),
+        _isEqualNodes(node.rightHandSide, toNode.rightHandSide))) {
+      toNode.propagatedElement = node.propagatedElement;
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticElement = node.staticElement;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitAwaitExpression(AwaitExpression node) {
+    AwaitExpression toNode = this._toNode as AwaitExpression;
+    return _and(_isEqualTokens(node.awaitKeyword, toNode.awaitKeyword),
+        _isEqualNodes(node.expression, toNode.expression));
+  }
+
+  @override
+  bool visitBinaryExpression(BinaryExpression node) {
+    BinaryExpression toNode = this._toNode as BinaryExpression;
+    if (_and(_isEqualNodes(node.leftOperand, toNode.leftOperand),
+        _isEqualTokens(node.operator, toNode.operator),
+        _isEqualNodes(node.rightOperand, toNode.rightOperand))) {
+      toNode.propagatedElement = node.propagatedElement;
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticElement = node.staticElement;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitBlock(Block node) {
+    Block toNode = this._toNode as Block;
+    return _and(_isEqualTokens(node.leftBracket, toNode.leftBracket),
+        _isEqualNodeLists(node.statements, toNode.statements),
+        _isEqualTokens(node.rightBracket, toNode.rightBracket));
+  }
+
+  @override
+  bool visitBlockFunctionBody(BlockFunctionBody node) {
+    BlockFunctionBody toNode = this._toNode as BlockFunctionBody;
+    return _isEqualNodes(node.block, toNode.block);
+  }
+
+  @override
+  bool visitBooleanLiteral(BooleanLiteral node) {
+    BooleanLiteral toNode = this._toNode as BooleanLiteral;
+    if (_and(_isEqualTokens(node.literal, toNode.literal),
+        node.value == toNode.value)) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitBreakStatement(BreakStatement node) {
+    BreakStatement toNode = this._toNode as BreakStatement;
+    if (_and(_isEqualTokens(node.breakKeyword, toNode.breakKeyword),
+        _isEqualNodes(node.label, toNode.label),
+        _isEqualTokens(node.semicolon, toNode.semicolon))) {
+      // TODO(paulberry): map node.target to toNode.target.
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitCascadeExpression(CascadeExpression node) {
+    CascadeExpression toNode = this._toNode as CascadeExpression;
+    if (_and(_isEqualNodes(node.target, toNode.target),
+        _isEqualNodeLists(node.cascadeSections, toNode.cascadeSections))) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitCatchClause(CatchClause node) {
+    CatchClause toNode = this._toNode as CatchClause;
+    return _and(_isEqualTokens(node.onKeyword, toNode.onKeyword),
+        _isEqualNodes(node.exceptionType, toNode.exceptionType),
+        _isEqualTokens(node.catchKeyword, toNode.catchKeyword),
+        _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis),
+        _isEqualNodes(node.exceptionParameter, toNode.exceptionParameter),
+        _isEqualTokens(node.comma, toNode.comma),
+        _isEqualNodes(node.stackTraceParameter, toNode.stackTraceParameter),
+        _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis),
+        _isEqualNodes(node.body, toNode.body));
+  }
+
+  @override
+  bool visitClassDeclaration(ClassDeclaration node) {
+    ClassDeclaration toNode = this._toNode as ClassDeclaration;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.abstractKeyword, toNode.abstractKeyword),
+        _isEqualTokens(node.classKeyword, toNode.classKeyword),
+        _isEqualNodes(node.name, toNode.name),
+        _isEqualNodes(node.typeParameters, toNode.typeParameters),
+        _isEqualNodes(node.extendsClause, toNode.extendsClause),
+        _isEqualNodes(node.withClause, toNode.withClause),
+        _isEqualNodes(node.implementsClause, toNode.implementsClause),
+        _isEqualTokens(node.leftBracket, toNode.leftBracket),
+        _isEqualNodeLists(node.members, toNode.members),
+        _isEqualTokens(node.rightBracket, toNode.rightBracket));
+  }
+
+  @override
+  bool visitClassTypeAlias(ClassTypeAlias node) {
+    ClassTypeAlias toNode = this._toNode as ClassTypeAlias;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.typedefKeyword, toNode.typedefKeyword),
+        _isEqualNodes(node.name, toNode.name),
+        _isEqualNodes(node.typeParameters, toNode.typeParameters),
+        _isEqualTokens(node.equals, toNode.equals),
+        _isEqualTokens(node.abstractKeyword, toNode.abstractKeyword),
+        _isEqualNodes(node.superclass, toNode.superclass),
+        _isEqualNodes(node.withClause, toNode.withClause),
+        _isEqualNodes(node.implementsClause, toNode.implementsClause),
+        _isEqualTokens(node.semicolon, toNode.semicolon));
+  }
+
+  @override
+  bool visitComment(Comment node) {
+    Comment toNode = this._toNode as Comment;
+    return _isEqualNodeLists(node.references, toNode.references);
+  }
+
+  @override
+  bool visitCommentReference(CommentReference node) {
+    CommentReference toNode = this._toNode as CommentReference;
+    return _and(_isEqualTokens(node.newKeyword, toNode.newKeyword),
+        _isEqualNodes(node.identifier, toNode.identifier));
+  }
+
+  @override
+  bool visitCompilationUnit(CompilationUnit node) {
+    CompilationUnit toNode = this._toNode as CompilationUnit;
+    if (_and(_isEqualTokens(node.beginToken, toNode.beginToken),
+        _isEqualNodes(node.scriptTag, toNode.scriptTag),
+        _isEqualNodeLists(node.directives, toNode.directives),
+        _isEqualNodeLists(node.declarations, toNode.declarations),
+        _isEqualTokens(node.endToken, toNode.endToken))) {
+      toNode.element = node.element;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitConditionalExpression(ConditionalExpression node) {
+    ConditionalExpression toNode = this._toNode as ConditionalExpression;
+    if (_and(_isEqualNodes(node.condition, toNode.condition),
+        _isEqualTokens(node.question, toNode.question),
+        _isEqualNodes(node.thenExpression, toNode.thenExpression),
+        _isEqualTokens(node.colon, toNode.colon),
+        _isEqualNodes(node.elseExpression, toNode.elseExpression))) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitConstructorDeclaration(ConstructorDeclaration node) {
+    ConstructorDeclaration toNode = this._toNode as ConstructorDeclaration;
+    if (_and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.externalKeyword, toNode.externalKeyword),
+        _isEqualTokens(node.constKeyword, toNode.constKeyword),
+        _isEqualTokens(node.factoryKeyword, toNode.factoryKeyword),
+        _isEqualNodes(node.returnType, toNode.returnType),
+        _isEqualTokens(node.period, toNode.period),
+        _isEqualNodes(node.name, toNode.name),
+        _isEqualNodes(node.parameters, toNode.parameters),
+        _isEqualTokens(node.separator, toNode.separator),
+        _isEqualNodeLists(node.initializers, toNode.initializers),
+        _isEqualNodes(node.redirectedConstructor, toNode.redirectedConstructor),
+        _isEqualNodes(node.body, toNode.body))) {
+      toNode.element = node.element;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+    ConstructorFieldInitializer toNode =
+        this._toNode as ConstructorFieldInitializer;
+    return _and(_isEqualTokens(node.thisKeyword, toNode.thisKeyword),
+        _isEqualTokens(node.period, toNode.period),
+        _isEqualNodes(node.fieldName, toNode.fieldName),
+        _isEqualTokens(node.equals, toNode.equals),
+        _isEqualNodes(node.expression, toNode.expression));
+  }
+
+  @override
+  bool visitConstructorName(ConstructorName node) {
+    ConstructorName toNode = this._toNode as ConstructorName;
+    if (_and(_isEqualNodes(node.type, toNode.type),
+        _isEqualTokens(node.period, toNode.period),
+        _isEqualNodes(node.name, toNode.name))) {
+      toNode.staticElement = node.staticElement;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitContinueStatement(ContinueStatement node) {
+    ContinueStatement toNode = this._toNode as ContinueStatement;
+    if (_and(_isEqualTokens(node.continueKeyword, toNode.continueKeyword),
+        _isEqualNodes(node.label, toNode.label),
+        _isEqualTokens(node.semicolon, toNode.semicolon))) {
+      // TODO(paulberry): map node.target to toNode.target.
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitDeclaredIdentifier(DeclaredIdentifier node) {
+    DeclaredIdentifier toNode = this._toNode as DeclaredIdentifier;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.keyword, toNode.keyword),
+        _isEqualNodes(node.type, toNode.type),
+        _isEqualNodes(node.identifier, toNode.identifier));
+  }
+
+  @override
+  bool visitDefaultFormalParameter(DefaultFormalParameter node) {
+    DefaultFormalParameter toNode = this._toNode as DefaultFormalParameter;
+    return _and(_isEqualNodes(node.parameter, toNode.parameter),
+        node.kind == toNode.kind,
+        _isEqualTokens(node.separator, toNode.separator),
+        _isEqualNodes(node.defaultValue, toNode.defaultValue));
+  }
+
+  @override
+  bool visitDoStatement(DoStatement node) {
+    DoStatement toNode = this._toNode as DoStatement;
+    return _and(_isEqualTokens(node.doKeyword, toNode.doKeyword),
+        _isEqualNodes(node.body, toNode.body),
+        _isEqualTokens(node.whileKeyword, toNode.whileKeyword),
+        _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis),
+        _isEqualNodes(node.condition, toNode.condition),
+        _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis),
+        _isEqualTokens(node.semicolon, toNode.semicolon));
+  }
+
+  @override
+  bool visitDoubleLiteral(DoubleLiteral node) {
+    DoubleLiteral toNode = this._toNode as DoubleLiteral;
+    if (_and(_isEqualTokens(node.literal, toNode.literal),
+        node.value == toNode.value)) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitEmptyFunctionBody(EmptyFunctionBody node) {
+    EmptyFunctionBody toNode = this._toNode as EmptyFunctionBody;
+    return _isEqualTokens(node.semicolon, toNode.semicolon);
+  }
+
+  @override
+  bool visitEmptyStatement(EmptyStatement node) {
+    EmptyStatement toNode = this._toNode as EmptyStatement;
+    return _isEqualTokens(node.semicolon, toNode.semicolon);
+  }
+
+  @override
+  bool visitEnumConstantDeclaration(EnumConstantDeclaration node) {
+    EnumConstantDeclaration toNode = this._toNode as EnumConstantDeclaration;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualNodes(node.name, toNode.name));
+  }
+
+  @override
+  bool visitEnumDeclaration(EnumDeclaration node) {
+    EnumDeclaration toNode = this._toNode as EnumDeclaration;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.enumKeyword, toNode.enumKeyword),
+        _isEqualNodes(node.name, toNode.name),
+        _isEqualTokens(node.leftBracket, toNode.leftBracket),
+        _isEqualNodeLists(node.constants, toNode.constants),
+        _isEqualTokens(node.rightBracket, toNode.rightBracket));
+  }
+
+  @override
+  bool visitExportDirective(ExportDirective node) {
+    ExportDirective toNode = this._toNode as ExportDirective;
+    if (_and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.keyword, toNode.keyword),
+        _isEqualNodes(node.uri, toNode.uri),
+        _isEqualNodeLists(node.combinators, toNode.combinators),
+        _isEqualTokens(node.semicolon, toNode.semicolon))) {
+      toNode.element = node.element;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitExpressionFunctionBody(ExpressionFunctionBody node) {
+    ExpressionFunctionBody toNode = this._toNode as ExpressionFunctionBody;
+    return _and(
+        _isEqualTokens(node.functionDefinition, toNode.functionDefinition),
+        _isEqualNodes(node.expression, toNode.expression),
+        _isEqualTokens(node.semicolon, toNode.semicolon));
+  }
+
+  @override
+  bool visitExpressionStatement(ExpressionStatement node) {
+    ExpressionStatement toNode = this._toNode as ExpressionStatement;
+    return _and(_isEqualNodes(node.expression, toNode.expression),
+        _isEqualTokens(node.semicolon, toNode.semicolon));
+  }
+
+  @override
+  bool visitExtendsClause(ExtendsClause node) {
+    ExtendsClause toNode = this._toNode as ExtendsClause;
+    return _and(_isEqualTokens(node.extendsKeyword, toNode.extendsKeyword),
+        _isEqualNodes(node.superclass, toNode.superclass));
+  }
+
+  @override
+  bool visitFieldDeclaration(FieldDeclaration node) {
+    FieldDeclaration toNode = this._toNode as FieldDeclaration;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.staticKeyword, toNode.staticKeyword),
+        _isEqualNodes(node.fields, toNode.fields),
+        _isEqualTokens(node.semicolon, toNode.semicolon));
+  }
+
+  @override
+  bool visitFieldFormalParameter(FieldFormalParameter node) {
+    FieldFormalParameter toNode = this._toNode as FieldFormalParameter;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.keyword, toNode.keyword),
+        _isEqualNodes(node.type, toNode.type),
+        _isEqualTokens(node.thisKeyword, toNode.thisKeyword),
+        _isEqualTokens(node.period, toNode.period),
+        _isEqualNodes(node.identifier, toNode.identifier));
+  }
+
+  @override
+  bool visitForEachStatement(ForEachStatement node) {
+    ForEachStatement toNode = this._toNode as ForEachStatement;
+    return _and(_isEqualTokens(node.forKeyword, toNode.forKeyword),
+        _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis),
+        _isEqualNodes(node.loopVariable, toNode.loopVariable),
+        _isEqualTokens(node.inKeyword, toNode.inKeyword),
+        _isEqualNodes(node.iterable, toNode.iterable),
+        _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis),
+        _isEqualNodes(node.body, toNode.body));
+  }
+
+  @override
+  bool visitFormalParameterList(FormalParameterList node) {
+    FormalParameterList toNode = this._toNode as FormalParameterList;
+    return _and(_isEqualTokens(node.leftParenthesis, toNode.leftParenthesis),
+        _isEqualNodeLists(node.parameters, toNode.parameters),
+        _isEqualTokens(node.leftDelimiter, toNode.leftDelimiter),
+        _isEqualTokens(node.rightDelimiter, toNode.rightDelimiter),
+        _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis));
+  }
+
+  @override
+  bool visitForStatement(ForStatement node) {
+    ForStatement toNode = this._toNode as ForStatement;
+    return _and(_isEqualTokens(node.forKeyword, toNode.forKeyword),
+        _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis),
+        _isEqualNodes(node.variables, toNode.variables),
+        _isEqualNodes(node.initialization, toNode.initialization),
+        _isEqualTokens(node.leftSeparator, toNode.leftSeparator),
+        _isEqualNodes(node.condition, toNode.condition),
+        _isEqualTokens(node.rightSeparator, toNode.rightSeparator),
+        _isEqualNodeLists(node.updaters, toNode.updaters),
+        _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis),
+        _isEqualNodes(node.body, toNode.body));
+  }
+
+  @override
+  bool visitFunctionDeclaration(FunctionDeclaration node) {
+    FunctionDeclaration toNode = this._toNode as FunctionDeclaration;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.externalKeyword, toNode.externalKeyword),
+        _isEqualNodes(node.returnType, toNode.returnType),
+        _isEqualTokens(node.propertyKeyword, toNode.propertyKeyword),
+        _isEqualNodes(node.name, toNode.name),
+        _isEqualNodes(node.functionExpression, toNode.functionExpression));
+  }
+
+  @override
+  bool visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
+    FunctionDeclarationStatement toNode =
+        this._toNode as FunctionDeclarationStatement;
+    return _isEqualNodes(node.functionDeclaration, toNode.functionDeclaration);
+  }
+
+  @override
+  bool visitFunctionExpression(FunctionExpression node) {
+    FunctionExpression toNode = this._toNode as FunctionExpression;
+    if (_and(_isEqualNodes(node.parameters, toNode.parameters),
+        _isEqualNodes(node.body, toNode.body))) {
+      toNode.element = node.element;
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    FunctionExpressionInvocation toNode =
+        this._toNode as FunctionExpressionInvocation;
+    if (_and(_isEqualNodes(node.function, toNode.function),
+        _isEqualNodes(node.argumentList, toNode.argumentList))) {
+      toNode.propagatedElement = node.propagatedElement;
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticElement = node.staticElement;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitFunctionTypeAlias(FunctionTypeAlias node) {
+    FunctionTypeAlias toNode = this._toNode as FunctionTypeAlias;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.typedefKeyword, toNode.typedefKeyword),
+        _isEqualNodes(node.returnType, toNode.returnType),
+        _isEqualNodes(node.name, toNode.name),
+        _isEqualNodes(node.typeParameters, toNode.typeParameters),
+        _isEqualNodes(node.parameters, toNode.parameters),
+        _isEqualTokens(node.semicolon, toNode.semicolon));
+  }
+
+  @override
+  bool visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+    FunctionTypedFormalParameter toNode =
+        this._toNode as FunctionTypedFormalParameter;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualNodes(node.returnType, toNode.returnType),
+        _isEqualNodes(node.identifier, toNode.identifier),
+        _isEqualNodes(node.parameters, toNode.parameters));
+  }
+
+  @override
+  bool visitHideCombinator(HideCombinator node) {
+    HideCombinator toNode = this._toNode as HideCombinator;
+    return _and(_isEqualTokens(node.keyword, toNode.keyword),
+        _isEqualNodeLists(node.hiddenNames, toNode.hiddenNames));
+  }
+
+  @override
+  bool visitIfStatement(IfStatement node) {
+    IfStatement toNode = this._toNode as IfStatement;
+    return _and(_isEqualTokens(node.ifKeyword, toNode.ifKeyword),
+        _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis),
+        _isEqualNodes(node.condition, toNode.condition),
+        _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis),
+        _isEqualNodes(node.thenStatement, toNode.thenStatement),
+        _isEqualTokens(node.elseKeyword, toNode.elseKeyword),
+        _isEqualNodes(node.elseStatement, toNode.elseStatement));
+  }
+
+  @override
+  bool visitImplementsClause(ImplementsClause node) {
+    ImplementsClause toNode = this._toNode as ImplementsClause;
+    return _and(
+        _isEqualTokens(node.implementsKeyword, toNode.implementsKeyword),
+        _isEqualNodeLists(node.interfaces, toNode.interfaces));
+  }
+
+  @override
+  bool visitImportDirective(ImportDirective node) {
+    ImportDirective toNode = this._toNode as ImportDirective;
+    if (_and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.keyword, toNode.keyword),
+        _isEqualNodes(node.uri, toNode.uri),
+        _isEqualTokens(node.asKeyword, toNode.asKeyword),
+        _isEqualNodes(node.prefix, toNode.prefix),
+        _isEqualNodeLists(node.combinators, toNode.combinators),
+        _isEqualTokens(node.semicolon, toNode.semicolon))) {
+      toNode.element = node.element;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitIndexExpression(IndexExpression node) {
+    IndexExpression toNode = this._toNode as IndexExpression;
+    if (_and(_isEqualNodes(node.target, toNode.target),
+        _isEqualTokens(node.leftBracket, toNode.leftBracket),
+        _isEqualNodes(node.index, toNode.index),
+        _isEqualTokens(node.rightBracket, toNode.rightBracket))) {
+      toNode.auxiliaryElements = node.auxiliaryElements;
+      toNode.propagatedElement = node.propagatedElement;
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticElement = node.staticElement;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitInstanceCreationExpression(InstanceCreationExpression node) {
+    InstanceCreationExpression toNode =
+        this._toNode as InstanceCreationExpression;
+    if (_and(_isEqualTokens(node.keyword, toNode.keyword),
+        _isEqualNodes(node.constructorName, toNode.constructorName),
+        _isEqualNodes(node.argumentList, toNode.argumentList))) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticElement = node.staticElement;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitIntegerLiteral(IntegerLiteral node) {
+    IntegerLiteral toNode = this._toNode as IntegerLiteral;
+    if (_and(_isEqualTokens(node.literal, toNode.literal),
+        node.value == toNode.value)) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitInterpolationExpression(InterpolationExpression node) {
+    InterpolationExpression toNode = this._toNode as InterpolationExpression;
+    return _and(_isEqualTokens(node.leftBracket, toNode.leftBracket),
+        _isEqualNodes(node.expression, toNode.expression),
+        _isEqualTokens(node.rightBracket, toNode.rightBracket));
+  }
+
+  @override
+  bool visitInterpolationString(InterpolationString node) {
+    InterpolationString toNode = this._toNode as InterpolationString;
+    return _and(_isEqualTokens(node.contents, toNode.contents),
+        node.value == toNode.value);
+  }
+
+  @override
+  bool visitIsExpression(IsExpression node) {
+    IsExpression toNode = this._toNode as IsExpression;
+    if (_and(_isEqualNodes(node.expression, toNode.expression),
+        _isEqualTokens(node.isOperator, toNode.isOperator),
+        _isEqualTokens(node.notOperator, toNode.notOperator),
+        _isEqualNodes(node.type, toNode.type))) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitLabel(Label node) {
+    Label toNode = this._toNode as Label;
+    return _and(_isEqualNodes(node.label, toNode.label),
+        _isEqualTokens(node.colon, toNode.colon));
+  }
+
+  @override
+  bool visitLabeledStatement(LabeledStatement node) {
+    LabeledStatement toNode = this._toNode as LabeledStatement;
+    return _and(_isEqualNodeLists(node.labels, toNode.labels),
+        _isEqualNodes(node.statement, toNode.statement));
+  }
+
+  @override
+  bool visitLibraryDirective(LibraryDirective node) {
+    LibraryDirective toNode = this._toNode as LibraryDirective;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.libraryKeyword, toNode.libraryKeyword),
+        _isEqualNodes(node.name, toNode.name),
+        _isEqualTokens(node.semicolon, toNode.semicolon));
+  }
+
+  @override
+  bool visitLibraryIdentifier(LibraryIdentifier node) {
+    LibraryIdentifier toNode = this._toNode as LibraryIdentifier;
+    if (_isEqualNodeLists(node.components, toNode.components)) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitListLiteral(ListLiteral node) {
+    ListLiteral toNode = this._toNode as ListLiteral;
+    if (_and(_isEqualTokens(node.constKeyword, toNode.constKeyword),
+        _isEqualNodes(node.typeArguments, toNode.typeArguments),
+        _isEqualTokens(node.leftBracket, toNode.leftBracket),
+        _isEqualNodeLists(node.elements, toNode.elements),
+        _isEqualTokens(node.rightBracket, toNode.rightBracket))) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitMapLiteral(MapLiteral node) {
+    MapLiteral toNode = this._toNode as MapLiteral;
+    if (_and(_isEqualTokens(node.constKeyword, toNode.constKeyword),
+        _isEqualNodes(node.typeArguments, toNode.typeArguments),
+        _isEqualTokens(node.leftBracket, toNode.leftBracket),
+        _isEqualNodeLists(node.entries, toNode.entries),
+        _isEqualTokens(node.rightBracket, toNode.rightBracket))) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitMapLiteralEntry(MapLiteralEntry node) {
+    MapLiteralEntry toNode = this._toNode as MapLiteralEntry;
+    return _and(_isEqualNodes(node.key, toNode.key),
+        _isEqualTokens(node.separator, toNode.separator),
+        _isEqualNodes(node.value, toNode.value));
+  }
+
+  @override
+  bool visitMethodDeclaration(MethodDeclaration node) {
+    MethodDeclaration toNode = this._toNode as MethodDeclaration;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.externalKeyword, toNode.externalKeyword),
+        _isEqualTokens(node.modifierKeyword, toNode.modifierKeyword),
+        _isEqualNodes(node.returnType, toNode.returnType),
+        _isEqualTokens(node.propertyKeyword, toNode.propertyKeyword),
+        _isEqualTokens(node.propertyKeyword, toNode.propertyKeyword),
+        _isEqualNodes(node.name, toNode.name),
+        _isEqualNodes(node.parameters, toNode.parameters),
+        _isEqualNodes(node.body, toNode.body));
+  }
+
+  @override
+  bool visitMethodInvocation(MethodInvocation node) {
+    MethodInvocation toNode = this._toNode as MethodInvocation;
+    if (_and(_isEqualNodes(node.target, toNode.target),
+        _isEqualTokens(node.operator, toNode.operator),
+        _isEqualNodes(node.methodName, toNode.methodName),
+        _isEqualNodes(node.argumentList, toNode.argumentList))) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitNamedExpression(NamedExpression node) {
+    NamedExpression toNode = this._toNode as NamedExpression;
+    if (_and(_isEqualNodes(node.name, toNode.name),
+        _isEqualNodes(node.expression, toNode.expression))) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitNativeClause(NativeClause node) {
+    NativeClause toNode = this._toNode as NativeClause;
+    return _and(_isEqualTokens(node.nativeKeyword, toNode.nativeKeyword),
+        _isEqualNodes(node.name, toNode.name));
+  }
+
+  @override
+  bool visitNativeFunctionBody(NativeFunctionBody node) {
+    NativeFunctionBody toNode = this._toNode as NativeFunctionBody;
+    return _and(_isEqualTokens(node.nativeKeyword, toNode.nativeKeyword),
+        _isEqualNodes(node.stringLiteral, toNode.stringLiteral),
+        _isEqualTokens(node.semicolon, toNode.semicolon));
+  }
+
+  @override
+  bool visitNullLiteral(NullLiteral node) {
+    NullLiteral toNode = this._toNode as NullLiteral;
+    if (_isEqualTokens(node.literal, toNode.literal)) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitParenthesizedExpression(ParenthesizedExpression node) {
+    ParenthesizedExpression toNode = this._toNode as ParenthesizedExpression;
+    if (_and(_isEqualTokens(node.leftParenthesis, toNode.leftParenthesis),
+        _isEqualNodes(node.expression, toNode.expression),
+        _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis))) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitPartDirective(PartDirective node) {
+    PartDirective toNode = this._toNode as PartDirective;
+    if (_and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.partKeyword, toNode.partKeyword),
+        _isEqualNodes(node.uri, toNode.uri),
+        _isEqualTokens(node.semicolon, toNode.semicolon))) {
+      toNode.element = node.element;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitPartOfDirective(PartOfDirective node) {
+    PartOfDirective toNode = this._toNode as PartOfDirective;
+    if (_and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.partKeyword, toNode.partKeyword),
+        _isEqualTokens(node.ofKeyword, toNode.ofKeyword),
+        _isEqualNodes(node.libraryName, toNode.libraryName),
+        _isEqualTokens(node.semicolon, toNode.semicolon))) {
+      toNode.element = node.element;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitPostfixExpression(PostfixExpression node) {
+    PostfixExpression toNode = this._toNode as PostfixExpression;
+    if (_and(_isEqualNodes(node.operand, toNode.operand),
+        _isEqualTokens(node.operator, toNode.operator))) {
+      toNode.propagatedElement = node.propagatedElement;
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticElement = node.staticElement;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitPrefixedIdentifier(PrefixedIdentifier node) {
+    PrefixedIdentifier toNode = this._toNode as PrefixedIdentifier;
+    if (_and(_isEqualNodes(node.prefix, toNode.prefix),
+        _isEqualTokens(node.period, toNode.period),
+        _isEqualNodes(node.identifier, toNode.identifier))) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitPrefixExpression(PrefixExpression node) {
+    PrefixExpression toNode = this._toNode as PrefixExpression;
+    if (_and(_isEqualTokens(node.operator, toNode.operator),
+        _isEqualNodes(node.operand, toNode.operand))) {
+      toNode.propagatedElement = node.propagatedElement;
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticElement = node.staticElement;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitPropertyAccess(PropertyAccess node) {
+    PropertyAccess toNode = this._toNode as PropertyAccess;
+    if (_and(_isEqualNodes(node.target, toNode.target),
+        _isEqualTokens(node.operator, toNode.operator),
+        _isEqualNodes(node.propertyName, toNode.propertyName))) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    RedirectingConstructorInvocation toNode =
+        this._toNode as RedirectingConstructorInvocation;
+    if (_and(_isEqualTokens(node.thisKeyword, toNode.thisKeyword),
+        _isEqualTokens(node.period, toNode.period),
+        _isEqualNodes(node.constructorName, toNode.constructorName),
+        _isEqualNodes(node.argumentList, toNode.argumentList))) {
+      toNode.staticElement = node.staticElement;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitRethrowExpression(RethrowExpression node) {
+    RethrowExpression toNode = this._toNode as RethrowExpression;
+    if (_isEqualTokens(node.rethrowKeyword, toNode.rethrowKeyword)) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitReturnStatement(ReturnStatement node) {
+    ReturnStatement toNode = this._toNode as ReturnStatement;
+    return _and(_isEqualTokens(node.returnKeyword, toNode.returnKeyword),
+        _isEqualNodes(node.expression, toNode.expression),
+        _isEqualTokens(node.semicolon, toNode.semicolon));
+  }
+
+  @override
+  bool visitScriptTag(ScriptTag node) {
+    ScriptTag toNode = this._toNode as ScriptTag;
+    return _isEqualTokens(node.scriptTag, toNode.scriptTag);
+  }
+
+  @override
+  bool visitShowCombinator(ShowCombinator node) {
+    ShowCombinator toNode = this._toNode as ShowCombinator;
+    return _and(_isEqualTokens(node.keyword, toNode.keyword),
+        _isEqualNodeLists(node.shownNames, toNode.shownNames));
+  }
+
+  @override
+  bool visitSimpleFormalParameter(SimpleFormalParameter node) {
+    SimpleFormalParameter toNode = this._toNode as SimpleFormalParameter;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.keyword, toNode.keyword),
+        _isEqualNodes(node.type, toNode.type),
+        _isEqualNodes(node.identifier, toNode.identifier));
+  }
+
+  @override
+  bool visitSimpleIdentifier(SimpleIdentifier node) {
+    SimpleIdentifier toNode = this._toNode as SimpleIdentifier;
+    if (_isEqualTokens(node.token, toNode.token)) {
+      toNode.staticElement = node.staticElement;
+      toNode.staticType = node.staticType;
+      toNode.propagatedElement = node.propagatedElement;
+      toNode.propagatedType = node.propagatedType;
+      toNode.auxiliaryElements = node.auxiliaryElements;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitSimpleStringLiteral(SimpleStringLiteral node) {
+    SimpleStringLiteral toNode = this._toNode as SimpleStringLiteral;
+    if (_and(_isEqualTokens(node.literal, toNode.literal),
+        node.value == toNode.value)) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitStringInterpolation(StringInterpolation node) {
+    StringInterpolation toNode = this._toNode as StringInterpolation;
+    if (_isEqualNodeLists(node.elements, toNode.elements)) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    SuperConstructorInvocation toNode =
+        this._toNode as SuperConstructorInvocation;
+    if (_and(_isEqualTokens(node.superKeyword, toNode.superKeyword),
+        _isEqualTokens(node.period, toNode.period),
+        _isEqualNodes(node.constructorName, toNode.constructorName),
+        _isEqualNodes(node.argumentList, toNode.argumentList))) {
+      toNode.staticElement = node.staticElement;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitSuperExpression(SuperExpression node) {
+    SuperExpression toNode = this._toNode as SuperExpression;
+    if (_isEqualTokens(node.superKeyword, toNode.superKeyword)) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitSwitchCase(SwitchCase node) {
+    SwitchCase toNode = this._toNode as SwitchCase;
+    return _and(_isEqualNodeLists(node.labels, toNode.labels),
+        _isEqualTokens(node.keyword, toNode.keyword),
+        _isEqualNodes(node.expression, toNode.expression),
+        _isEqualTokens(node.colon, toNode.colon),
+        _isEqualNodeLists(node.statements, toNode.statements));
+  }
+
+  @override
+  bool visitSwitchDefault(SwitchDefault node) {
+    SwitchDefault toNode = this._toNode as SwitchDefault;
+    return _and(_isEqualNodeLists(node.labels, toNode.labels),
+        _isEqualTokens(node.keyword, toNode.keyword),
+        _isEqualTokens(node.colon, toNode.colon),
+        _isEqualNodeLists(node.statements, toNode.statements));
+  }
+
+  @override
+  bool visitSwitchStatement(SwitchStatement node) {
+    SwitchStatement toNode = this._toNode as SwitchStatement;
+    return _and(_isEqualTokens(node.switchKeyword, toNode.switchKeyword),
+        _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis),
+        _isEqualNodes(node.expression, toNode.expression),
+        _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis),
+        _isEqualTokens(node.leftBracket, toNode.leftBracket),
+        _isEqualNodeLists(node.members, toNode.members),
+        _isEqualTokens(node.rightBracket, toNode.rightBracket));
+  }
+
+  @override
+  bool visitSymbolLiteral(SymbolLiteral node) {
+    SymbolLiteral toNode = this._toNode as SymbolLiteral;
+    if (_and(_isEqualTokens(node.poundSign, toNode.poundSign),
+        _isEqualTokenLists(node.components, toNode.components))) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitThisExpression(ThisExpression node) {
+    ThisExpression toNode = this._toNode as ThisExpression;
+    if (_isEqualTokens(node.thisKeyword, toNode.thisKeyword)) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitThrowExpression(ThrowExpression node) {
+    ThrowExpression toNode = this._toNode as ThrowExpression;
+    if (_and(_isEqualTokens(node.throwKeyword, toNode.throwKeyword),
+        _isEqualNodes(node.expression, toNode.expression))) {
+      toNode.propagatedType = node.propagatedType;
+      toNode.staticType = node.staticType;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+    TopLevelVariableDeclaration toNode =
+        this._toNode as TopLevelVariableDeclaration;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualNodes(node.variables, toNode.variables),
+        _isEqualTokens(node.semicolon, toNode.semicolon));
+  }
+
+  @override
+  bool visitTryStatement(TryStatement node) {
+    TryStatement toNode = this._toNode as TryStatement;
+    return _and(_isEqualTokens(node.tryKeyword, toNode.tryKeyword),
+        _isEqualNodes(node.body, toNode.body),
+        _isEqualNodeLists(node.catchClauses, toNode.catchClauses),
+        _isEqualTokens(node.finallyKeyword, toNode.finallyKeyword),
+        _isEqualNodes(node.finallyBlock, toNode.finallyBlock));
+  }
+
+  @override
+  bool visitTypeArgumentList(TypeArgumentList node) {
+    TypeArgumentList toNode = this._toNode as TypeArgumentList;
+    return _and(_isEqualTokens(node.leftBracket, toNode.leftBracket),
+        _isEqualNodeLists(node.arguments, toNode.arguments),
+        _isEqualTokens(node.rightBracket, toNode.rightBracket));
+  }
+
+  @override
+  bool visitTypeName(TypeName node) {
+    TypeName toNode = this._toNode as TypeName;
+    if (_and(_isEqualNodes(node.name, toNode.name),
+        _isEqualNodes(node.typeArguments, toNode.typeArguments))) {
+      toNode.type = node.type;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitTypeParameter(TypeParameter node) {
+    TypeParameter toNode = this._toNode as TypeParameter;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualNodes(node.name, toNode.name),
+        _isEqualTokens(node.extendsKeyword, toNode.extendsKeyword),
+        _isEqualNodes(node.bound, toNode.bound));
+  }
+
+  @override
+  bool visitTypeParameterList(TypeParameterList node) {
+    TypeParameterList toNode = this._toNode as TypeParameterList;
+    return _and(_isEqualTokens(node.leftBracket, toNode.leftBracket),
+        _isEqualNodeLists(node.typeParameters, toNode.typeParameters),
+        _isEqualTokens(node.rightBracket, toNode.rightBracket));
+  }
+
+  @override
+  bool visitVariableDeclaration(VariableDeclaration node) {
+    VariableDeclaration toNode = this._toNode as VariableDeclaration;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualNodes(node.name, toNode.name),
+        _isEqualTokens(node.equals, toNode.equals),
+        _isEqualNodes(node.initializer, toNode.initializer));
+  }
+
+  @override
+  bool visitVariableDeclarationList(VariableDeclarationList node) {
+    VariableDeclarationList toNode = this._toNode as VariableDeclarationList;
+    return _and(
+        _isEqualNodes(node.documentationComment, toNode.documentationComment),
+        _isEqualNodeLists(node.metadata, toNode.metadata),
+        _isEqualTokens(node.keyword, toNode.keyword),
+        _isEqualNodes(node.type, toNode.type),
+        _isEqualNodeLists(node.variables, toNode.variables));
+  }
+
+  @override
+  bool visitVariableDeclarationStatement(VariableDeclarationStatement node) {
+    VariableDeclarationStatement toNode =
+        this._toNode as VariableDeclarationStatement;
+    return _and(_isEqualNodes(node.variables, toNode.variables),
+        _isEqualTokens(node.semicolon, toNode.semicolon));
+  }
+
+  @override
+  bool visitWhileStatement(WhileStatement node) {
+    WhileStatement toNode = this._toNode as WhileStatement;
+    return _and(_isEqualTokens(node.whileKeyword, toNode.whileKeyword),
+        _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis),
+        _isEqualNodes(node.condition, toNode.condition),
+        _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis),
+        _isEqualNodes(node.body, toNode.body));
+  }
+
+  @override
+  bool visitWithClause(WithClause node) {
+    WithClause toNode = this._toNode as WithClause;
+    return _and(_isEqualTokens(node.withKeyword, toNode.withKeyword),
+        _isEqualNodeLists(node.mixinTypes, toNode.mixinTypes));
+  }
+
+  @override
+  bool visitYieldStatement(YieldStatement node) {
+    YieldStatement toNode = this._toNode as YieldStatement;
+    return _and(_isEqualTokens(node.yieldKeyword, toNode.yieldKeyword),
+        _isEqualNodes(node.expression, toNode.expression),
+        _isEqualTokens(node.semicolon, toNode.semicolon));
+  }
+
+  /**
+   * Return `true` if all of the parameters are `true`.
+   */
+  bool _and(bool b1, bool b2, [bool b3 = true, bool b4 = true, bool b5 = true,
+      bool b6 = true, bool b7 = true, bool b8 = true, bool b9 = true,
+      bool b10 = true, bool b11 = true, bool b12 = true, bool b13 = true]) {
+    // TODO(brianwilkerson) Inline this method.
+    return b1 &&
+        b2 &&
+        b3 &&
+        b4 &&
+        b5 &&
+        b6 &&
+        b7 &&
+        b8 &&
+        b9 &&
+        b10 &&
+        b11 &&
+        b12 &&
+        b13;
+  }
+
+  /**
+   * Return `true` if the [first] and [second] lists of AST nodes have the same
+   * size and corresponding elements are equal.
+   */
+  bool _isEqualNodeLists(NodeList first, NodeList second) {
+    if (first == null) {
+      return second == null;
+    } else if (second == null) {
+      return false;
+    }
+    int size = first.length;
+    if (second.length != size) {
+      return false;
+    }
+    bool equal = true;
+    for (int i = 0; i < size; i++) {
+      if (!_isEqualNodes(first[i], second[i])) {
+        equal = false;
+      }
+    }
+    return equal;
+  }
+
+  /**
+   * Return `true` if the [fromNode] and [toNode] have the same structure. As a
+   * side-effect, if the nodes do have the same structure, any resolution data
+   * from the first node will be copied to the second node.
+   */
+  bool _isEqualNodes(AstNode fromNode, AstNode toNode) {
+    if (fromNode == null) {
+      return toNode == null;
+    } else if (toNode == null) {
+      return false;
+    } else if (fromNode.runtimeType == toNode.runtimeType) {
+      this._toNode = toNode;
+      return fromNode.accept(this);
+    }
+    //
+    // Check for a simple transformation caused by entering a period.
+    //
+    if (toNode is PrefixedIdentifier) {
+      SimpleIdentifier prefix = toNode.prefix;
+      if (fromNode.runtimeType == prefix.runtimeType) {
+        this._toNode = prefix;
+        return fromNode.accept(this);
+      }
+    } else if (toNode is PropertyAccess) {
+      Expression target = toNode.target;
+      if (fromNode.runtimeType == target.runtimeType) {
+        this._toNode = target;
+        return fromNode.accept(this);
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the [first] and [second] arrays of tokens have the same
+   * length and corresponding elements are equal.
+   */
+  bool _isEqualTokenLists(List<Token> first, List<Token> second) {
+    int length = first.length;
+    if (second.length != length) {
+      return false;
+    }
+    for (int i = 0; i < length; i++) {
+      if (!_isEqualTokens(first[i], second[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Return `true` if the [first] and [second] tokens have the same structure.
+   */
+  bool _isEqualTokens(Token first, Token second) {
+    if (first == null) {
+      return second == null;
+    } else if (second == null) {
+      return false;
+    }
+    return first.lexeme == second.lexeme;
+  }
+
+  /**
+   * Copy resolution data from the [fromNode] to the [toNode].
+   */
+  static void copyResolutionData(AstNode fromNode, AstNode toNode) {
+    ResolutionCopier copier = new ResolutionCopier();
+    copier._isEqualNodes(fromNode, toNode);
+  }
+}
diff --git a/analyzer/lib/src/generated/resolver.dart b/analyzer/lib/src/generated/resolver.dart
new file mode 100644
index 0000000..926244b
--- /dev/null
+++ b/analyzer/lib/src/generated/resolver.dart
@@ -0,0 +1,15420 @@
+// 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 engine.resolver;
+
+import "dart:math" as math;
+import 'dart:collection';
+
+import 'package:analyzer/src/generated/utilities_collection.dart';
+
+import 'ast.dart';
+import 'constant.dart';
+import 'element.dart';
+import 'element_resolver.dart';
+import 'engine.dart';
+import 'error.dart';
+import 'error_verifier.dart';
+import 'html.dart' as ht;
+import 'java_core.dart';
+import 'java_engine.dart';
+import 'scanner.dart' as sc;
+import 'sdk.dart' show DartSdk, SdkLibrary;
+import 'source.dart';
+import 'static_type_analyzer.dart';
+import 'utilities_dart.dart';
+
+/**
+ * Callback signature used by ImplicitConstructorBuilder to register
+ * computations to be performed, and their dependencies.  A call to this
+ * callback indicates that [computation] may be used to compute implicit
+ * constructors for [classElement], but that the computation may not be invoked
+ * until after implicit constructors have been built for [superclassElement].
+ */
+typedef void ImplicitConstructorBuilderCallback(ClassElement classElement,
+    ClassElement superclassElement, void computation());
+
+typedef LibraryResolver LibraryResolverFactory(AnalysisContext context);
+
+typedef ResolverVisitor ResolverVisitorFactory(
+    Library library, Source source, TypeProvider typeProvider);
+
+typedef StaticTypeAnalyzer StaticTypeAnalyzerFactory(ResolverVisitor visitor);
+
+typedef TypeResolverVisitor TypeResolverVisitorFactory(
+    Library library, Source source, TypeProvider typeProvider);
+
+typedef void VoidFunction();
+
+/**
+ * Instances of the class `BestPracticesVerifier` traverse an AST structure looking for
+ * violations of Dart best practices.
+ */
+class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
+//  static String _HASHCODE_GETTER_NAME = "hashCode";
+
+  static String _NULL_TYPE_NAME = "Null";
+
+  static String _TO_INT_METHOD_NAME = "toInt";
+
+  /**
+   * The class containing the AST nodes being visited, or `null` if we are not in the scope of
+   * a class.
+   */
+  ClassElement _enclosingClass;
+
+  /**
+   * The error reporter by which errors will be reported.
+   */
+  final ErrorReporter _errorReporter;
+
+  /**
+   * The type Future<Null>, which is needed for determining whether it is safe
+   * to have a bare "return;" in an async method.
+   */
+  final InterfaceType _futureNullType;
+
+  /**
+   * Create a new instance of the [BestPracticesVerifier].
+   *
+   * @param errorReporter the error reporter
+   */
+  BestPracticesVerifier(this._errorReporter, TypeProvider typeProvider)
+      : _futureNullType = typeProvider.futureNullType;
+
+  @override
+  Object visitArgumentList(ArgumentList node) {
+    _checkForArgumentTypesNotAssignableInList(node);
+    return super.visitArgumentList(node);
+  }
+
+  @override
+  Object visitAsExpression(AsExpression node) {
+    _checkForUnnecessaryCast(node);
+    return super.visitAsExpression(node);
+  }
+
+  @override
+  Object visitAssignmentExpression(AssignmentExpression node) {
+    sc.TokenType operatorType = node.operator.type;
+    if (operatorType == sc.TokenType.EQ) {
+      _checkForUseOfVoidResult(node.rightHandSide);
+      _checkForInvalidAssignment(node.leftHandSide, node.rightHandSide);
+    } else {
+      _checkForDeprecatedMemberUse(node.bestElement, node);
+    }
+    return super.visitAssignmentExpression(node);
+  }
+
+  @override
+  Object visitBinaryExpression(BinaryExpression node) {
+    _checkForDivisionOptimizationHint(node);
+    _checkForDeprecatedMemberUse(node.bestElement, node);
+    return super.visitBinaryExpression(node);
+  }
+
+  @override
+  Object visitClassDeclaration(ClassDeclaration node) {
+    ClassElement outerClass = _enclosingClass;
+    try {
+      _enclosingClass = node.element;
+      // Commented out until we decide that we want this hint in the analyzer
+      //    checkForOverrideEqualsButNotHashCode(node);
+      return super.visitClassDeclaration(node);
+    } finally {
+      _enclosingClass = outerClass;
+    }
+  }
+
+  @override
+  Object visitExportDirective(ExportDirective node) {
+    _checkForDeprecatedMemberUse(node.uriElement, node);
+    return super.visitExportDirective(node);
+  }
+
+  @override
+  Object visitFunctionDeclaration(FunctionDeclaration node) {
+    _checkForMissingReturn(node.returnType, node.functionExpression.body);
+    return super.visitFunctionDeclaration(node);
+  }
+
+  @override
+  Object visitImportDirective(ImportDirective node) {
+    _checkForDeprecatedMemberUse(node.uriElement, node);
+    ImportElement importElement = node.element;
+    if (importElement != null) {
+      if (importElement.isDeferred) {
+        _checkForLoadLibraryFunction(node, importElement);
+      }
+    }
+    return super.visitImportDirective(node);
+  }
+
+  @override
+  Object visitIndexExpression(IndexExpression node) {
+    _checkForDeprecatedMemberUse(node.bestElement, node);
+    return super.visitIndexExpression(node);
+  }
+
+  @override
+  Object visitInstanceCreationExpression(InstanceCreationExpression node) {
+    _checkForDeprecatedMemberUse(node.staticElement, node);
+    return super.visitInstanceCreationExpression(node);
+  }
+
+  @override
+  Object visitIsExpression(IsExpression node) {
+    _checkAllTypeChecks(node);
+    return super.visitIsExpression(node);
+  }
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    // This was determined to not be a good hint, see: dartbug.com/16029
+    //checkForOverridingPrivateMember(node);
+    _checkForMissingReturn(node.returnType, node.body);
+    return super.visitMethodDeclaration(node);
+  }
+
+  @override
+  Object visitPostfixExpression(PostfixExpression node) {
+    _checkForDeprecatedMemberUse(node.bestElement, node);
+    return super.visitPostfixExpression(node);
+  }
+
+  @override
+  Object visitPrefixExpression(PrefixExpression node) {
+    _checkForDeprecatedMemberUse(node.bestElement, node);
+    return super.visitPrefixExpression(node);
+  }
+
+  @override
+  Object visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    _checkForDeprecatedMemberUse(node.staticElement, node);
+    return super.visitRedirectingConstructorInvocation(node);
+  }
+
+  @override
+  Object visitSimpleIdentifier(SimpleIdentifier node) {
+    _checkForDeprecatedMemberUseAtIdentifier(node);
+    return super.visitSimpleIdentifier(node);
+  }
+
+  @override
+  Object visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    _checkForDeprecatedMemberUse(node.staticElement, node);
+    return super.visitSuperConstructorInvocation(node);
+  }
+
+  @override
+  Object visitVariableDeclaration(VariableDeclaration node) {
+    _checkForUseOfVoidResult(node.initializer);
+    _checkForInvalidAssignment(node.name, node.initializer);
+    return super.visitVariableDeclaration(node);
+  }
+
+  /**
+   * Check for the passed is expression for the unnecessary type check hint codes as well as null
+   * checks expressed using an is expression.
+   *
+   * @param node the is expression to check
+   * @return `true` if and only if a hint code is generated on the passed node
+   * See [HintCode.TYPE_CHECK_IS_NOT_NULL], [HintCode.TYPE_CHECK_IS_NULL],
+   * [HintCode.UNNECESSARY_TYPE_CHECK_TRUE], and
+   * [HintCode.UNNECESSARY_TYPE_CHECK_FALSE].
+   */
+  bool _checkAllTypeChecks(IsExpression node) {
+    Expression expression = node.expression;
+    TypeName typeName = node.type;
+    DartType lhsType = expression.staticType;
+    DartType rhsType = typeName.type;
+    if (lhsType == null || rhsType == null) {
+      return false;
+    }
+    String rhsNameStr = typeName.name.name;
+    // if x is dynamic
+    if (rhsType.isDynamic && rhsNameStr == sc.Keyword.DYNAMIC.syntax) {
+      if (node.notOperator == null) {
+        // the is case
+        _errorReporter.reportErrorForNode(
+            HintCode.UNNECESSARY_TYPE_CHECK_TRUE, node);
+      } else {
+        // the is not case
+        _errorReporter.reportErrorForNode(
+            HintCode.UNNECESSARY_TYPE_CHECK_FALSE, node);
+      }
+      return true;
+    }
+    Element rhsElement = rhsType.element;
+    LibraryElement libraryElement =
+        rhsElement != null ? rhsElement.library : null;
+    if (libraryElement != null && libraryElement.isDartCore) {
+      // if x is Object or null is Null
+      if (rhsType.isObject ||
+          (expression is NullLiteral && rhsNameStr == _NULL_TYPE_NAME)) {
+        if (node.notOperator == null) {
+          // the is case
+          _errorReporter.reportErrorForNode(
+              HintCode.UNNECESSARY_TYPE_CHECK_TRUE, node);
+        } else {
+          // the is not case
+          _errorReporter.reportErrorForNode(
+              HintCode.UNNECESSARY_TYPE_CHECK_FALSE, node);
+        }
+        return true;
+      } else if (rhsNameStr == _NULL_TYPE_NAME) {
+        if (node.notOperator == null) {
+          // the is case
+          _errorReporter.reportErrorForNode(HintCode.TYPE_CHECK_IS_NULL, node);
+        } else {
+          // the is not case
+          _errorReporter.reportErrorForNode(
+              HintCode.TYPE_CHECK_IS_NOT_NULL, node);
+        }
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * This verifies that the passed expression can be assigned to its corresponding parameters.
+   *
+   * This method corresponds to ErrorVerifier.checkForArgumentTypeNotAssignable.
+   *
+   * TODO (jwren) In the ErrorVerifier there are other warnings that we could have a corresponding
+   * hint for: see other callers of ErrorVerifier.checkForArgumentTypeNotAssignable(..).
+   *
+   * @param expression the expression to evaluate
+   * @param expectedStaticType the expected static type of the parameter
+   * @param actualStaticType the actual static type of the argument
+   * @param expectedPropagatedType the expected propagated type of the parameter, may be
+   *          `null`
+   * @param actualPropagatedType the expected propagated type of the parameter, may be `null`
+   * @return `true` if and only if an hint code is generated on the passed node
+   * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
+   */
+  bool _checkForArgumentTypeNotAssignable(Expression expression,
+      DartType expectedStaticType, DartType actualStaticType,
+      DartType expectedPropagatedType, DartType actualPropagatedType,
+      ErrorCode hintCode) {
+    //
+    // Warning case: test static type information
+    //
+    if (actualStaticType != null && expectedStaticType != null) {
+      if (!actualStaticType.isAssignableTo(expectedStaticType)) {
+        // A warning was created in the ErrorVerifier, return false, don't
+        // create a hint when a warning has already been created.
+        return false;
+      }
+    }
+    //
+    // Hint case: test propagated type information
+    //
+    // Compute the best types to use.
+    DartType expectedBestType = expectedPropagatedType != null
+        ? expectedPropagatedType
+        : expectedStaticType;
+    DartType actualBestType =
+        actualPropagatedType != null ? actualPropagatedType : actualStaticType;
+    if (actualBestType != null && expectedBestType != null) {
+      if (!actualBestType.isAssignableTo(expectedBestType)) {
+        _errorReporter.reportTypeErrorForNode(
+            hintCode, expression, [actualBestType, expectedBestType]);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * This verifies that the passed argument can be assigned to its corresponding parameter.
+   *
+   * This method corresponds to ErrorCode.checkForArgumentTypeNotAssignableForArgument.
+   *
+   * @param argument the argument to evaluate
+   * @return `true` if and only if an hint code is generated on the passed node
+   * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
+   */
+  bool _checkForArgumentTypeNotAssignableForArgument(Expression argument) {
+    if (argument == null) {
+      return false;
+    }
+    ParameterElement staticParameterElement = argument.staticParameterElement;
+    DartType staticParameterType =
+        staticParameterElement == null ? null : staticParameterElement.type;
+    ParameterElement propagatedParameterElement =
+        argument.propagatedParameterElement;
+    DartType propagatedParameterType = propagatedParameterElement == null
+        ? null
+        : propagatedParameterElement.type;
+    return _checkForArgumentTypeNotAssignableWithExpectedTypes(argument,
+        staticParameterType, propagatedParameterType,
+        HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE);
+  }
+
+  /**
+   * This verifies that the passed expression can be assigned to its corresponding parameters.
+   *
+   * This method corresponds to ErrorCode.checkForArgumentTypeNotAssignableWithExpectedTypes.
+   *
+   * @param expression the expression to evaluate
+   * @param expectedStaticType the expected static type
+   * @param expectedPropagatedType the expected propagated type, may be `null`
+   * @return `true` if and only if an hint code is generated on the passed node
+   * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
+   */
+  bool _checkForArgumentTypeNotAssignableWithExpectedTypes(
+          Expression expression, DartType expectedStaticType,
+          DartType expectedPropagatedType, ErrorCode errorCode) =>
+      _checkForArgumentTypeNotAssignable(expression, expectedStaticType,
+          expression.staticType, expectedPropagatedType,
+          expression.propagatedType, errorCode);
+
+  /**
+   * This verifies that the passed arguments can be assigned to their corresponding parameters.
+   *
+   * This method corresponds to ErrorCode.checkForArgumentTypesNotAssignableInList.
+   *
+   * @param node the arguments to evaluate
+   * @return `true` if and only if an hint code is generated on the passed node
+   * See [HintCode.ARGUMENT_TYPE_NOT_ASSIGNABLE].
+   */
+  bool _checkForArgumentTypesNotAssignableInList(ArgumentList argumentList) {
+    if (argumentList == null) {
+      return false;
+    }
+    bool problemReported = false;
+    for (Expression argument in argumentList.arguments) {
+      if (_checkForArgumentTypeNotAssignableForArgument(argument)) {
+        problemReported = true;
+      }
+    }
+    return problemReported;
+  }
+
+  /**
+   * Given some [Element], look at the associated metadata and report the use of the member if
+   * it is declared as deprecated.
+   *
+   * @param element some element to check for deprecated use of
+   * @param node the node use for the location of the error
+   * @return `true` if and only if a hint code is generated on the passed node
+   * See [HintCode.DEPRECATED_MEMBER_USE].
+   */
+  bool _checkForDeprecatedMemberUse(Element element, AstNode node) {
+    if (element != null && element.isDeprecated) {
+      String displayName = element.displayName;
+      if (element is ConstructorElement) {
+        // TODO(jwren) We should modify ConstructorElement.getDisplayName(),
+        // or have the logic centralized elsewhere, instead of doing this logic
+        // here.
+        ConstructorElement constructorElement = element;
+        displayName = constructorElement.enclosingElement.displayName;
+        if (!constructorElement.displayName.isEmpty) {
+          displayName = "$displayName.${constructorElement.displayName}";
+        }
+      }
+      _errorReporter.reportErrorForNode(
+          HintCode.DEPRECATED_MEMBER_USE, node, [displayName]);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * For [SimpleIdentifier]s, only call [checkForDeprecatedMemberUse]
+   * if the node is not in a declaration context.
+   *
+   * Also, if the identifier is a constructor name in a constructor invocation, then calls to the
+   * deprecated constructor will be caught by
+   * [visitInstanceCreationExpression] and
+   * [visitSuperConstructorInvocation], and can be ignored by
+   * this visit method.
+   *
+   * @param identifier some simple identifier to check for deprecated use of
+   * @return `true` if and only if a hint code is generated on the passed node
+   * See [HintCode.DEPRECATED_MEMBER_USE].
+   */
+  bool _checkForDeprecatedMemberUseAtIdentifier(SimpleIdentifier identifier) {
+    if (identifier.inDeclarationContext()) {
+      return false;
+    }
+    AstNode parent = identifier.parent;
+    if ((parent is ConstructorName && identical(identifier, parent.name)) ||
+        (parent is SuperConstructorInvocation &&
+            identical(identifier, parent.constructorName)) ||
+        parent is HideCombinator) {
+      return false;
+    }
+    return _checkForDeprecatedMemberUse(identifier.bestElement, identifier);
+  }
+
+  /**
+   * Check for the passed binary expression for the [HintCode.DIVISION_OPTIMIZATION].
+   *
+   * @param node the binary expression to check
+   * @return `true` if and only if a hint code is generated on the passed node
+   * See [HintCode.DIVISION_OPTIMIZATION].
+   */
+  bool _checkForDivisionOptimizationHint(BinaryExpression node) {
+    // Return if the operator is not '/'
+    if (node.operator.type != sc.TokenType.SLASH) {
+      return false;
+    }
+    // Return if the '/' operator is not defined in core, or if we don't know
+    // its static or propagated type
+    MethodElement methodElement = node.bestElement;
+    if (methodElement == null) {
+      return false;
+    }
+    LibraryElement libraryElement = methodElement.library;
+    if (libraryElement != null && !libraryElement.isDartCore) {
+      return false;
+    }
+    // Report error if the (x/y) has toInt() invoked on it
+    if (node.parent is ParenthesizedExpression) {
+      ParenthesizedExpression parenthesizedExpression =
+          _wrapParenthesizedExpression(node.parent as ParenthesizedExpression);
+      if (parenthesizedExpression.parent is MethodInvocation) {
+        MethodInvocation methodInvocation =
+            parenthesizedExpression.parent as MethodInvocation;
+        if (_TO_INT_METHOD_NAME == methodInvocation.methodName.name &&
+            methodInvocation.argumentList.arguments.isEmpty) {
+          _errorReporter.reportErrorForNode(
+              HintCode.DIVISION_OPTIMIZATION, methodInvocation);
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * This verifies that the passed left hand side and right hand side represent a valid assignment.
+   *
+   * This method corresponds to ErrorVerifier.checkForInvalidAssignment.
+   *
+   * @param lhs the left hand side expression
+   * @param rhs the right hand side expression
+   * @return `true` if and only if an error code is generated on the passed node
+   * See [HintCode.INVALID_ASSIGNMENT].
+   */
+  bool _checkForInvalidAssignment(Expression lhs, Expression rhs) {
+    if (lhs == null || rhs == null) {
+      return false;
+    }
+    VariableElement leftVariableElement = ErrorVerifier.getVariableElement(lhs);
+    DartType leftType = (leftVariableElement == null)
+        ? ErrorVerifier.getStaticType(lhs)
+        : leftVariableElement.type;
+    DartType staticRightType = ErrorVerifier.getStaticType(rhs);
+    if (!staticRightType.isAssignableTo(leftType)) {
+      // The warning was generated on this rhs
+      return false;
+    }
+    // Test for, and then generate the hint
+    DartType bestRightType = rhs.bestType;
+    if (leftType != null && bestRightType != null) {
+      if (!bestRightType.isAssignableTo(leftType)) {
+        _errorReporter.reportTypeErrorForNode(
+            HintCode.INVALID_ASSIGNMENT, rhs, [bestRightType, leftType]);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Check that the imported library does not define a loadLibrary function. The import has already
+   * been determined to be deferred when this is called.
+   *
+   * @param node the import directive to evaluate
+   * @param importElement the [ImportElement] retrieved from the node
+   * @return `true` if and only if an error code is generated on the passed node
+   * See [CompileTimeErrorCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION].
+   */
+  bool _checkForLoadLibraryFunction(
+      ImportDirective node, ImportElement importElement) {
+    LibraryElement importedLibrary = importElement.importedLibrary;
+    if (importedLibrary == null) {
+      return false;
+    }
+    if (importedLibrary.hasLoadLibraryFunction) {
+      _errorReporter.reportErrorForNode(
+          HintCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION, node,
+          [importedLibrary.name]);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Generate a hint for functions or methods that have a return type, but do not have a return
+   * statement on all branches. At the end of blocks with no return, Dart implicitly returns
+   * `null`, avoiding these implicit returns is considered a best practice.
+   *
+   * Note: for async functions/methods, this hint only applies when the
+   * function has a return type that Future<Null> is not assignable to.
+   *
+   * @param node the binary expression to check
+   * @param body the function body
+   * @return `true` if and only if a hint code is generated on the passed node
+   * See [HintCode.MISSING_RETURN].
+   */
+  bool _checkForMissingReturn(TypeName returnType, FunctionBody body) {
+    // Check that the method or function has a return type, and a function body
+    if (returnType == null || body == null) {
+      return false;
+    }
+    // Check that the body is a BlockFunctionBody
+    if (body is! BlockFunctionBody) {
+      return false;
+    }
+    // Generators are never required to have a return statement.
+    if (body.isGenerator) {
+      return false;
+    }
+    // Check that the type is resolvable, and is not "void"
+    DartType returnTypeType = returnType.type;
+    if (returnTypeType == null || returnTypeType.isVoid) {
+      return false;
+    }
+    // For async, give no hint if Future<Null> is assignable to the return
+    // type.
+    if (body.isAsynchronous && _futureNullType.isAssignableTo(returnTypeType)) {
+      return false;
+    }
+    // Check the block for a return statement, if not, create the hint
+    BlockFunctionBody blockFunctionBody = body as BlockFunctionBody;
+    if (!ExitDetector.exits(blockFunctionBody)) {
+      _errorReporter.reportErrorForNode(
+          HintCode.MISSING_RETURN, returnType, [returnTypeType.displayName]);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Check for the passed class declaration for the
+   * [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE] hint code.
+   *
+   * @param node the class declaration to check
+   * @return `true` if and only if a hint code is generated on the passed node
+   * See [HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE].
+   */
+//  bool _checkForOverrideEqualsButNotHashCode(ClassDeclaration node) {
+//    ClassElement classElement = node.element;
+//    if (classElement == null) {
+//      return false;
+//    }
+//    MethodElement equalsOperatorMethodElement =
+//        classElement.getMethod(sc.TokenType.EQ_EQ.lexeme);
+//    if (equalsOperatorMethodElement != null) {
+//      PropertyAccessorElement hashCodeElement =
+//          classElement.getGetter(_HASHCODE_GETTER_NAME);
+//      if (hashCodeElement == null) {
+//        _errorReporter.reportErrorForNode(
+//            HintCode.OVERRIDE_EQUALS_BUT_NOT_HASH_CODE,
+//            node.name,
+//            [classElement.displayName]);
+//        return true;
+//      }
+//    }
+//    return false;
+//  }
+
+  /**
+   * Check for the passed as expression for the [HintCode.UNNECESSARY_CAST] hint code.
+   *
+   * @param node the as expression to check
+   * @return `true` if and only if a hint code is generated on the passed node
+   * See [HintCode.UNNECESSARY_CAST].
+   */
+  bool _checkForUnnecessaryCast(AsExpression node) {
+    // TODO(jwren) After dartbug.com/13732, revisit this, we should be able to
+    // remove the (x is! TypeParameterType) checks.
+    AstNode parent = node.parent;
+    if (parent is ConditionalExpression &&
+        (node == parent.thenExpression || node == parent.elseExpression)) {
+      Expression thenExpression = parent.thenExpression;
+      DartType thenType;
+      if (thenExpression is AsExpression) {
+        thenType = thenExpression.expression.staticType;
+      } else {
+        thenType = thenExpression.staticType;
+      }
+      Expression elseExpression = parent.elseExpression;
+      DartType elseType;
+      if (elseExpression is AsExpression) {
+        elseType = elseExpression.expression.staticType;
+      } else {
+        elseType = elseExpression.staticType;
+      }
+      if (thenType != null &&
+          elseType != null &&
+          !thenType.isDynamic &&
+          !elseType.isDynamic &&
+          !thenType.isMoreSpecificThan(elseType) &&
+          !elseType.isMoreSpecificThan(thenType)) {
+        return false;
+      }
+    }
+    DartType lhsType = node.expression.staticType;
+    DartType rhsType = node.type.type;
+    if (lhsType != null &&
+        rhsType != null &&
+        !lhsType.isDynamic &&
+        !rhsType.isDynamic &&
+        lhsType.isMoreSpecificThan(rhsType)) {
+      _errorReporter.reportErrorForNode(HintCode.UNNECESSARY_CAST, node);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Check for situations where the result of a method or function is used, when it returns 'void'.
+   *
+   * TODO(jwren) Many other situations of use could be covered. We currently cover the cases var x =
+   * m() and x = m(), but we could also cover cases such as m().x, m()[k], a + m(), f(m()), return
+   * m().
+   *
+   * @param node expression on the RHS of some assignment
+   * @return `true` if and only if a hint code is generated on the passed node
+   * See [HintCode.USE_OF_VOID_RESULT].
+   */
+  bool _checkForUseOfVoidResult(Expression expression) {
+    if (expression == null || expression is! MethodInvocation) {
+      return false;
+    }
+    MethodInvocation methodInvocation = expression as MethodInvocation;
+    if (identical(methodInvocation.staticType, VoidTypeImpl.instance)) {
+      SimpleIdentifier methodName = methodInvocation.methodName;
+      _errorReporter.reportErrorForNode(
+          HintCode.USE_OF_VOID_RESULT, methodName, [methodName.name]);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Given a parenthesized expression, this returns the parent (or recursively grand-parent) of the
+   * expression that is a parenthesized expression, but whose parent is not a parenthesized
+   * expression.
+   *
+   * For example given the code `(((e)))`: `(e) -> (((e)))`.
+   *
+   * @param parenthesizedExpression some expression whose parent is a parenthesized expression
+   * @return the first parent or grand-parent that is a parenthesized expression, that does not have
+   *         a parenthesized expression parent
+   */
+  static ParenthesizedExpression _wrapParenthesizedExpression(
+      ParenthesizedExpression parenthesizedExpression) {
+    if (parenthesizedExpression.parent is ParenthesizedExpression) {
+      return _wrapParenthesizedExpression(
+          parenthesizedExpression.parent as ParenthesizedExpression);
+    }
+    return parenthesizedExpression;
+  }
+}
+
+/**
+ * Instances of the class `ClassScope` implement the scope defined by a class.
+ */
+class ClassScope extends EnclosedScope {
+  /**
+   * Initialize a newly created scope enclosed within another scope.
+   *
+   * @param enclosingScope the scope in which this scope is lexically enclosed
+   * @param typeElement the element representing the type represented by this scope
+   */
+  ClassScope(Scope enclosingScope, ClassElement typeElement)
+      : super(enclosingScope) {
+    if (typeElement == null) {
+      throw new IllegalArgumentException("class element cannot be null");
+    }
+    _defineMembers(typeElement);
+  }
+
+  @override
+  AnalysisError getErrorForDuplicate(Element existing, Element duplicate) {
+    if (existing is PropertyAccessorElement && duplicate is MethodElement) {
+      if (existing.nameOffset < duplicate.nameOffset) {
+        return new AnalysisError.con2(duplicate.source, duplicate.nameOffset,
+            duplicate.displayName.length,
+            CompileTimeErrorCode.METHOD_AND_GETTER_WITH_SAME_NAME,
+            [existing.displayName]);
+      } else {
+        return new AnalysisError.con2(existing.source, existing.nameOffset,
+            existing.displayName.length,
+            CompileTimeErrorCode.GETTER_AND_METHOD_WITH_SAME_NAME,
+            [existing.displayName]);
+      }
+    }
+    return super.getErrorForDuplicate(existing, duplicate);
+  }
+
+  /**
+   * Define the instance members defined by the class.
+   *
+   * @param typeElement the element representing the type represented by this scope
+   */
+  void _defineMembers(ClassElement typeElement) {
+    for (PropertyAccessorElement accessor in typeElement.accessors) {
+      define(accessor);
+    }
+    for (MethodElement method in typeElement.methods) {
+      define(method);
+    }
+  }
+}
+
+/**
+ * A `CompilationUnitBuilder` builds an element model for a single compilation
+ * unit.
+ */
+class CompilationUnitBuilder {
+  /**
+   * Build the compilation unit element for the given [source] based on the
+   * compilation [unit] associated with the source. Throw an AnalysisException
+   * if the element could not be built.
+   */
+  CompilationUnitElementImpl buildCompilationUnit(
+      Source source, CompilationUnit unit) {
+    return PerformanceStatistics.resolve.makeCurrentWhile(() {
+      if (unit == null) {
+        return null;
+      }
+      ElementHolder holder = new ElementHolder();
+      ElementBuilder builder = new ElementBuilder(holder);
+      unit.accept(builder);
+      CompilationUnitElementImpl element =
+          new CompilationUnitElementImpl(source.shortName);
+      element.accessors = holder.accessors;
+      element.enums = holder.enums;
+      element.functions = holder.functions;
+      element.source = source;
+      element.typeAliases = holder.typeAliases;
+      element.types = holder.types;
+      element.topLevelVariables = holder.topLevelVariables;
+      unit.element = element;
+      holder.validate();
+      return element;
+    });
+  }
+}
+
+/**
+ * Instances of the class `ConstantVerifier` traverse an AST structure looking for additional
+ * errors and warnings not covered by the parser and resolver. In particular, it looks for errors
+ * and warnings related to constant expressions.
+ */
+class ConstantVerifier extends RecursiveAstVisitor<Object> {
+  /**
+   * The error reporter by which errors will be reported.
+   */
+  final ErrorReporter _errorReporter;
+
+  /**
+   * The type provider used to access the known types.
+   */
+  final TypeProvider _typeProvider;
+
+  /**
+   * The type representing the type 'bool'.
+   */
+  InterfaceType _boolType;
+
+  /**
+   * The type representing the type 'int'.
+   */
+  InterfaceType _intType;
+
+  /**
+   * The type representing the type 'num'.
+   */
+  InterfaceType _numType;
+
+  /**
+   * The type representing the type 'string'.
+   */
+  InterfaceType _stringType;
+
+  /**
+   * The current library that is being analyzed.
+   */
+  final LibraryElement _currentLibrary;
+
+  /**
+   * Initialize a newly created constant verifier.
+   *
+   * @param errorReporter the error reporter by which errors will be reported
+   */
+  ConstantVerifier(
+      this._errorReporter, this._currentLibrary, this._typeProvider) {
+    this._boolType = _typeProvider.boolType;
+    this._intType = _typeProvider.intType;
+    this._numType = _typeProvider.numType;
+    this._stringType = _typeProvider.stringType;
+  }
+
+  @override
+  Object visitAnnotation(Annotation node) {
+    super.visitAnnotation(node);
+    // check annotation creation
+    Element element = node.element;
+    if (element is ConstructorElement) {
+      ConstructorElement constructorElement = element;
+      // should 'const' constructor
+      if (!constructorElement.isConst) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.NON_CONSTANT_ANNOTATION_CONSTRUCTOR, node);
+        return null;
+      }
+      // should have arguments
+      ArgumentList argumentList = node.arguments;
+      if (argumentList == null) {
+        _errorReporter.reportErrorForNode(
+            CompileTimeErrorCode.NO_ANNOTATION_CONSTRUCTOR_ARGUMENTS, node);
+        return null;
+      }
+      // arguments should be constants
+      _validateConstantArguments(argumentList);
+    }
+    return null;
+  }
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    if (node.constKeyword != null) {
+      _validateConstructorInitializers(node);
+      _validateFieldInitializers(node.parent as ClassDeclaration, node);
+    }
+    _validateDefaultValues(node.parameters);
+    return super.visitConstructorDeclaration(node);
+  }
+
+  @override
+  Object visitFunctionExpression(FunctionExpression node) {
+    super.visitFunctionExpression(node);
+    _validateDefaultValues(node.parameters);
+    return null;
+  }
+
+  @override
+  Object visitInstanceCreationExpression(InstanceCreationExpression node) {
+    if (node.isConst) {
+      EvaluationResultImpl evaluationResult = node.evaluationResult;
+      // Note: evaluationResult might be null if there are circular references
+      // among constants.
+      if (evaluationResult != null) {
+        _reportErrors(evaluationResult.errors, null);
+      }
+    }
+    _validateInstanceCreationArguments(node);
+    return super.visitInstanceCreationExpression(node);
+  }
+
+  @override
+  Object visitListLiteral(ListLiteral node) {
+    super.visitListLiteral(node);
+    if (node.constKeyword != null) {
+      DartObjectImpl result;
+      for (Expression element in node.elements) {
+        result =
+            _validate(element, CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT);
+        if (result != null) {
+          _reportErrorIfFromDeferredLibrary(element,
+              CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT_FROM_DEFERRED_LIBRARY);
+        }
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitMapLiteral(MapLiteral node) {
+    super.visitMapLiteral(node);
+    bool isConst = node.constKeyword != null;
+    bool reportEqualKeys = true;
+    HashSet<DartObject> keys = new HashSet<DartObject>();
+    List<Expression> invalidKeys = new List<Expression>();
+    for (MapLiteralEntry entry in node.entries) {
+      Expression key = entry.key;
+      if (isConst) {
+        DartObjectImpl keyResult =
+            _validate(key, CompileTimeErrorCode.NON_CONSTANT_MAP_KEY);
+        Expression valueExpression = entry.value;
+        DartObjectImpl valueResult = _validate(
+            valueExpression, CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE);
+        if (valueResult != null) {
+          _reportErrorIfFromDeferredLibrary(valueExpression,
+              CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE_FROM_DEFERRED_LIBRARY);
+        }
+        if (keyResult != null) {
+          _reportErrorIfFromDeferredLibrary(key,
+              CompileTimeErrorCode.NON_CONSTANT_MAP_KEY_FROM_DEFERRED_LIBRARY);
+          if (keys.contains(keyResult)) {
+            invalidKeys.add(key);
+          } else {
+            keys.add(keyResult);
+          }
+          DartType type = keyResult.type;
+          if (_implementsEqualsWhenNotAllowed(type)) {
+            _errorReporter.reportErrorForNode(
+                CompileTimeErrorCode.CONST_MAP_KEY_EXPRESSION_TYPE_IMPLEMENTS_EQUALS,
+                key, [type.displayName]);
+          }
+        }
+      } else {
+        // Note: we throw the errors away because this isn't actually a const.
+        AnalysisErrorListener errorListener =
+            AnalysisErrorListener.NULL_LISTENER;
+        ErrorReporter subErrorReporter =
+            new ErrorReporter(errorListener, _errorReporter.source);
+        DartObjectImpl result = key
+            .accept(new ConstantVisitor.con1(_typeProvider, subErrorReporter));
+        if (result != null) {
+          if (keys.contains(result)) {
+            invalidKeys.add(key);
+          } else {
+            keys.add(result);
+          }
+        } else {
+          reportEqualKeys = false;
+        }
+      }
+    }
+    if (reportEqualKeys) {
+      for (Expression key in invalidKeys) {
+        _errorReporter.reportErrorForNode(
+            StaticWarningCode.EQUAL_KEYS_IN_MAP, key);
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    super.visitMethodDeclaration(node);
+    _validateDefaultValues(node.parameters);
+    return null;
+  }
+
+  @override
+  Object visitSwitchStatement(SwitchStatement node) {
+    // TODO(paulberry): to minimize error messages, it would be nice to
+    // compare all types with the most popular type rather than the first
+    // type.
+    NodeList<SwitchMember> switchMembers = node.members;
+    bool foundError = false;
+    DartType firstType = null;
+    for (SwitchMember switchMember in switchMembers) {
+      if (switchMember is SwitchCase) {
+        SwitchCase switchCase = switchMember;
+        Expression expression = switchCase.expression;
+        DartObjectImpl caseResult = _validate(
+            expression, CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION);
+        if (caseResult != null) {
+          _reportErrorIfFromDeferredLibrary(expression,
+              CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION_FROM_DEFERRED_LIBRARY);
+          DartObject value = caseResult;
+          if (firstType == null) {
+            firstType = value.type;
+          } else {
+            DartType nType = value.type;
+            if (firstType != nType) {
+              _errorReporter.reportErrorForNode(
+                  CompileTimeErrorCode.INCONSISTENT_CASE_EXPRESSION_TYPES,
+                  expression, [expression.toSource(), firstType.displayName]);
+              foundError = true;
+            }
+          }
+        }
+      }
+    }
+    if (!foundError) {
+      _checkForCaseExpressionTypeImplementsEquals(node, firstType);
+    }
+    return super.visitSwitchStatement(node);
+  }
+
+  @override
+  Object visitVariableDeclaration(VariableDeclaration node) {
+    super.visitVariableDeclaration(node);
+    Expression initializer = node.initializer;
+    if (initializer != null && node.isConst) {
+      VariableElementImpl element = node.element as VariableElementImpl;
+      EvaluationResultImpl result = element.evaluationResult;
+      if (result == null) {
+        //
+        // Normally we don't need to visit const variable declarations because
+        // we have already computed their values. But if we missed it for some
+        // reason, this gives us a second chance.
+        //
+        result = new EvaluationResultImpl.con1(_validate(initializer,
+            CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE));
+        element.evaluationResult = result;
+        return null;
+      }
+      _reportErrors(result.errors,
+          CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE);
+      _reportErrorIfFromDeferredLibrary(initializer,
+          CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE_FROM_DEFERRED_LIBRARY);
+    }
+    return null;
+  }
+
+  /**
+   * This verifies that the passed switch statement does not have a case expression with the
+   * operator '==' overridden.
+   *
+   * @param node the switch statement to evaluate
+   * @param type the common type of all 'case' expressions
+   * @return `true` if and only if an error code is generated on the passed node
+   * See [CompileTimeErrorCode.CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS].
+   */
+  bool _checkForCaseExpressionTypeImplementsEquals(
+      SwitchStatement node, DartType type) {
+    if (!_implementsEqualsWhenNotAllowed(type)) {
+      return false;
+    }
+    // report error
+    _errorReporter.reportErrorForToken(
+        CompileTimeErrorCode.CASE_EXPRESSION_TYPE_IMPLEMENTS_EQUALS,
+        node.switchKeyword, [type.displayName]);
+    return true;
+  }
+
+  /**
+   * @return `true` if given [Type] implements operator <i>==</i>, and it is not
+   *         <i>int</i> or <i>String</i>.
+   */
+  bool _implementsEqualsWhenNotAllowed(DartType type) {
+    // ignore int or String
+    if (type == null || type == _intType || type == _typeProvider.stringType) {
+      return false;
+    } else if (type == _typeProvider.doubleType) {
+      return true;
+    }
+    // prepare ClassElement
+    Element element = type.element;
+    if (element is! ClassElement) {
+      return false;
+    }
+    ClassElement classElement = element as ClassElement;
+    // lookup for ==
+    MethodElement method =
+        classElement.lookUpConcreteMethod("==", _currentLibrary);
+    if (method == null || method.enclosingElement.type.isObject) {
+      return false;
+    }
+    // there is == that we don't like
+    return true;
+  }
+
+  /**
+   * Given some computed [Expression], this method generates the passed [ErrorCode] on
+   * the node if its' value consists of information from a deferred library.
+   *
+   * @param expression the expression to be tested for a deferred library reference
+   * @param errorCode the error code to be used if the expression is or consists of a reference to a
+   *          deferred library
+   */
+  void _reportErrorIfFromDeferredLibrary(
+      Expression expression, ErrorCode errorCode) {
+    DeferredLibraryReferenceDetector referenceDetector =
+        new DeferredLibraryReferenceDetector();
+    expression.accept(referenceDetector);
+    if (referenceDetector.result) {
+      _errorReporter.reportErrorForNode(errorCode, expression);
+    }
+  }
+
+  /**
+   * Report any errors in the given list. Except for special cases, use the given error code rather
+   * than the one reported in the error.
+   *
+   * @param errors the errors that need to be reported
+   * @param errorCode the error code to be used
+   */
+  void _reportErrors(List<AnalysisError> errors, ErrorCode errorCode) {
+    for (AnalysisError data in errors) {
+      ErrorCode dataErrorCode = data.errorCode;
+      if (identical(dataErrorCode,
+              CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION) ||
+          identical(
+              dataErrorCode, CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE) ||
+          identical(dataErrorCode,
+              CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING) ||
+          identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL) ||
+          identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_INT) ||
+          identical(dataErrorCode, CompileTimeErrorCode.CONST_EVAL_TYPE_NUM) ||
+          identical(dataErrorCode,
+              CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH) ||
+          identical(dataErrorCode,
+              CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH) ||
+          identical(dataErrorCode,
+              CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH)) {
+        _errorReporter.reportError(data);
+      } else if (errorCode != null) {
+        _errorReporter.reportError(new AnalysisError.con2(
+            data.source, data.offset, data.length, errorCode));
+      }
+    }
+  }
+
+  /**
+   * Validate that the given expression is a compile time constant. Return the value of the compile
+   * time constant, or `null` if the expression is not a compile time constant.
+   *
+   * @param expression the expression to be validated
+   * @param errorCode the error code to be used if the expression is not a compile time constant
+   * @return the value of the compile time constant
+   */
+  DartObjectImpl _validate(Expression expression, ErrorCode errorCode) {
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    ErrorReporter subErrorReporter =
+        new ErrorReporter(errorListener, _errorReporter.source);
+    DartObjectImpl result = expression
+        .accept(new ConstantVisitor.con1(_typeProvider, subErrorReporter));
+    _reportErrors(errorListener.errors, errorCode);
+    return result;
+  }
+
+  /**
+   * Validate that if the passed arguments are constant expressions.
+   *
+   * @param argumentList the argument list to evaluate
+   */
+  void _validateConstantArguments(ArgumentList argumentList) {
+    for (Expression argument in argumentList.arguments) {
+      if (argument is NamedExpression) {
+        argument = (argument as NamedExpression).expression;
+      }
+      _validate(
+          argument, CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT);
+    }
+  }
+
+  /**
+   * Validates that the expressions of the given initializers (of a constant constructor) are all
+   * compile time constants.
+   *
+   * @param constructor the constant constructor declaration to validate
+   */
+  void _validateConstructorInitializers(ConstructorDeclaration constructor) {
+    List<ParameterElement> parameterElements =
+        constructor.parameters.parameterElements;
+    NodeList<ConstructorInitializer> initializers = constructor.initializers;
+    for (ConstructorInitializer initializer in initializers) {
+      if (initializer is ConstructorFieldInitializer) {
+        ConstructorFieldInitializer fieldInitializer = initializer;
+        _validateInitializerExpression(
+            parameterElements, fieldInitializer.expression);
+      }
+      if (initializer is RedirectingConstructorInvocation) {
+        RedirectingConstructorInvocation invocation = initializer;
+        _validateInitializerInvocationArguments(
+            parameterElements, invocation.argumentList);
+      }
+      if (initializer is SuperConstructorInvocation) {
+        SuperConstructorInvocation invocation = initializer;
+        _validateInitializerInvocationArguments(
+            parameterElements, invocation.argumentList);
+      }
+    }
+  }
+
+  /**
+   * Validate that the default value associated with each of the parameters in the given list is a
+   * compile time constant.
+   *
+   * @param parameters the list of parameters to be validated
+   */
+  void _validateDefaultValues(FormalParameterList parameters) {
+    if (parameters == null) {
+      return;
+    }
+    for (FormalParameter parameter in parameters.parameters) {
+      if (parameter is DefaultFormalParameter) {
+        DefaultFormalParameter defaultParameter = parameter;
+        Expression defaultValue = defaultParameter.defaultValue;
+        DartObjectImpl result;
+        if (defaultValue == null) {
+          result =
+              new DartObjectImpl(_typeProvider.nullType, NullState.NULL_STATE);
+        } else {
+          result = _validate(
+              defaultValue, CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE);
+          if (result != null) {
+            _reportErrorIfFromDeferredLibrary(defaultValue,
+                CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE_FROM_DEFERRED_LIBRARY);
+          }
+        }
+        VariableElementImpl element = parameter.element as VariableElementImpl;
+        element.evaluationResult = new EvaluationResultImpl.con1(result);
+      }
+    }
+  }
+
+  /**
+   * Validates that the expressions of any field initializers in the class declaration are all
+   * compile time constants. Since this is only required if the class has a constant constructor,
+   * the error is reported at the constructor site.
+   *
+   * @param classDeclaration the class which should be validated
+   * @param errorSite the site at which errors should be reported.
+   */
+  void _validateFieldInitializers(
+      ClassDeclaration classDeclaration, ConstructorDeclaration errorSite) {
+    NodeList<ClassMember> members = classDeclaration.members;
+    for (ClassMember member in members) {
+      if (member is FieldDeclaration) {
+        FieldDeclaration fieldDeclaration = member;
+        if (!fieldDeclaration.isStatic) {
+          for (VariableDeclaration variableDeclaration
+              in fieldDeclaration.fields.variables) {
+            Expression initializer = variableDeclaration.initializer;
+            if (initializer != null) {
+              // Ignore any errors produced during validation--if the constant
+              // can't be eavluated we'll just report a single error.
+              AnalysisErrorListener errorListener =
+                  AnalysisErrorListener.NULL_LISTENER;
+              ErrorReporter subErrorReporter =
+                  new ErrorReporter(errorListener, _errorReporter.source);
+              DartObjectImpl result = initializer.accept(
+                  new ConstantVisitor.con1(_typeProvider, subErrorReporter));
+              if (result == null) {
+                _errorReporter.reportErrorForNode(
+                    CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_FIELD_INITIALIZED_BY_NON_CONST,
+                    errorSite, [variableDeclaration.name.name]);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Validates that the given expression is a compile time constant.
+   *
+   * @param parameterElements the elements of parameters of constant constructor, they are
+   *          considered as a valid potentially constant expressions
+   * @param expression the expression to validate
+   */
+  void _validateInitializerExpression(
+      List<ParameterElement> parameterElements, Expression expression) {
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    ErrorReporter subErrorReporter =
+        new ErrorReporter(errorListener, _errorReporter.source);
+    DartObjectImpl result = expression.accept(
+        new _ConstantVerifier_validateInitializerExpression(
+            _typeProvider, subErrorReporter, this, parameterElements));
+    _reportErrors(errorListener.errors,
+        CompileTimeErrorCode.NON_CONSTANT_VALUE_IN_INITIALIZER);
+    if (result != null) {
+      _reportErrorIfFromDeferredLibrary(expression,
+          CompileTimeErrorCode.NON_CONSTANT_VALUE_IN_INITIALIZER_FROM_DEFERRED_LIBRARY);
+    }
+  }
+
+  /**
+   * Validates that all of the arguments of a constructor initializer are compile time constants.
+   *
+   * @param parameterElements the elements of parameters of constant constructor, they are
+   *          considered as a valid potentially constant expressions
+   * @param argumentList the argument list to validate
+   */
+  void _validateInitializerInvocationArguments(
+      List<ParameterElement> parameterElements, ArgumentList argumentList) {
+    if (argumentList == null) {
+      return;
+    }
+    for (Expression argument in argumentList.arguments) {
+      _validateInitializerExpression(parameterElements, argument);
+    }
+  }
+
+  /**
+   * Validate that if the passed instance creation is 'const' then all its arguments are constant
+   * expressions.
+   *
+   * @param node the instance creation evaluate
+   */
+  void _validateInstanceCreationArguments(InstanceCreationExpression node) {
+    if (!node.isConst) {
+      return;
+    }
+    ArgumentList argumentList = node.argumentList;
+    if (argumentList == null) {
+      return;
+    }
+    _validateConstantArguments(argumentList);
+  }
+}
+
+/**
+ * Instances of the class `Dart2JSVerifier` traverse an AST structure looking for hints for
+ * code that will be compiled to JS, such as [HintCode.IS_DOUBLE].
+ */
+class Dart2JSVerifier extends RecursiveAstVisitor<Object> {
+  /**
+   * The name of the `double` type.
+   */
+  static String _DOUBLE_TYPE_NAME = "double";
+
+  /**
+   * The error reporter by which errors will be reported.
+   */
+  final ErrorReporter _errorReporter;
+
+  /**
+   * Create a new instance of the [Dart2JSVerifier].
+   *
+   * @param errorReporter the error reporter
+   */
+  Dart2JSVerifier(this._errorReporter);
+
+  @override
+  Object visitIsExpression(IsExpression node) {
+    _checkForIsDoubleHints(node);
+    return super.visitIsExpression(node);
+  }
+
+  /**
+   * Check for instances of `x is double`, `x is int`, `x is! double` and
+   * `x is! int`.
+   *
+   * @param node the is expression to check
+   * @return `true` if and only if a hint code is generated on the passed node
+   * See [HintCode.IS_DOUBLE],
+   * [HintCode.IS_INT],
+   * [HintCode.IS_NOT_DOUBLE], and
+   * [HintCode.IS_NOT_INT].
+   */
+  bool _checkForIsDoubleHints(IsExpression node) {
+    TypeName typeName = node.type;
+    DartType type = typeName.type;
+    if (type != null && type.element != null) {
+      Element element = type.element;
+      String typeNameStr = element.name;
+      LibraryElement libraryElement = element.library;
+      //      if (typeNameStr.equals(INT_TYPE_NAME) && libraryElement != null
+      //          && libraryElement.isDartCore()) {
+      //        if (node.getNotOperator() == null) {
+      //          errorReporter.reportError(HintCode.IS_INT, node);
+      //        } else {
+      //          errorReporter.reportError(HintCode.IS_NOT_INT, node);
+      //        }
+      //        return true;
+      //      } else
+      if (typeNameStr == _DOUBLE_TYPE_NAME &&
+          libraryElement != null &&
+          libraryElement.isDartCore) {
+        if (node.notOperator == null) {
+          _errorReporter.reportErrorForNode(HintCode.IS_DOUBLE, node);
+        } else {
+          _errorReporter.reportErrorForNode(HintCode.IS_NOT_DOUBLE, node);
+        }
+        return true;
+      }
+    }
+    return false;
+  }
+}
+
+/**
+ * Instances of the class `DeadCodeVerifier` traverse an AST structure looking for cases of
+ * [HintCode.DEAD_CODE].
+ */
+class DeadCodeVerifier extends RecursiveAstVisitor<Object> {
+  /**
+   * The error reporter by which errors will be reported.
+   */
+  final ErrorReporter _errorReporter;
+
+  /**
+   * Create a new instance of the [DeadCodeVerifier].
+   *
+   * @param errorReporter the error reporter
+   */
+  DeadCodeVerifier(this._errorReporter);
+
+  @override
+  Object visitBinaryExpression(BinaryExpression node) {
+    sc.Token operator = node.operator;
+    bool isAmpAmp = operator.type == sc.TokenType.AMPERSAND_AMPERSAND;
+    bool isBarBar = operator.type == sc.TokenType.BAR_BAR;
+    if (isAmpAmp || isBarBar) {
+      Expression lhsCondition = node.leftOperand;
+      if (!_isDebugConstant(lhsCondition)) {
+        EvaluationResultImpl lhsResult = _getConstantBooleanValue(lhsCondition);
+        if (lhsResult != null) {
+          if (lhsResult.value.isTrue && isBarBar) {
+            // report error on else block: true || !e!
+            _errorReporter.reportErrorForNode(
+                HintCode.DEAD_CODE, node.rightOperand);
+            // only visit the LHS:
+            _safelyVisit(lhsCondition);
+            return null;
+          } else if (lhsResult.value.isFalse && isAmpAmp) {
+            // report error on if block: false && !e!
+            _errorReporter.reportErrorForNode(
+                HintCode.DEAD_CODE, node.rightOperand);
+            // only visit the LHS:
+            _safelyVisit(lhsCondition);
+            return null;
+          }
+        }
+      }
+      // How do we want to handle the RHS? It isn't dead code, but "pointless"
+      // or "obscure"...
+//            Expression rhsCondition = node.getRightOperand();
+//            ValidResult rhsResult = getConstantBooleanValue(rhsCondition);
+//            if (rhsResult != null) {
+//              if (rhsResult == ValidResult.RESULT_TRUE && isBarBar) {
+//                // report error on else block: !e! || true
+//                errorReporter.reportError(HintCode.DEAD_CODE, node.getRightOperand());
+//                // only visit the RHS:
+//                safelyVisit(rhsCondition);
+//                return null;
+//              } else if (rhsResult == ValidResult.RESULT_FALSE && isAmpAmp) {
+//                // report error on if block: !e! && false
+//                errorReporter.reportError(HintCode.DEAD_CODE, node.getRightOperand());
+//                // only visit the RHS:
+//                safelyVisit(rhsCondition);
+//                return null;
+//              }
+//            }
+    }
+    return super.visitBinaryExpression(node);
+  }
+
+  /**
+   * For each [Block], this method reports and error on all statements between the end of the
+   * block and the first return statement (assuming there it is not at the end of the block.)
+   *
+   * @param node the block to evaluate
+   */
+  @override
+  Object visitBlock(Block node) {
+    NodeList<Statement> statements = node.statements;
+    _checkForDeadStatementsInNodeList(statements);
+    return null;
+  }
+
+  @override
+  Object visitConditionalExpression(ConditionalExpression node) {
+    Expression conditionExpression = node.condition;
+    _safelyVisit(conditionExpression);
+    if (!_isDebugConstant(conditionExpression)) {
+      EvaluationResultImpl result =
+          _getConstantBooleanValue(conditionExpression);
+      if (result != null) {
+        if (result.value.isTrue) {
+          // report error on else block: true ? 1 : !2!
+          _errorReporter.reportErrorForNode(
+              HintCode.DEAD_CODE, node.elseExpression);
+          _safelyVisit(node.thenExpression);
+          return null;
+        } else {
+          // report error on if block: false ? !1! : 2
+          _errorReporter.reportErrorForNode(
+              HintCode.DEAD_CODE, node.thenExpression);
+          _safelyVisit(node.elseExpression);
+          return null;
+        }
+      }
+    }
+    return super.visitConditionalExpression(node);
+  }
+
+  @override
+  Object visitIfStatement(IfStatement node) {
+    Expression conditionExpression = node.condition;
+    _safelyVisit(conditionExpression);
+    if (!_isDebugConstant(conditionExpression)) {
+      EvaluationResultImpl result =
+          _getConstantBooleanValue(conditionExpression);
+      if (result != null) {
+        if (result.value.isTrue) {
+          // report error on else block: if(true) {} else {!}
+          Statement elseStatement = node.elseStatement;
+          if (elseStatement != null) {
+            _errorReporter.reportErrorForNode(
+                HintCode.DEAD_CODE, elseStatement);
+            _safelyVisit(node.thenStatement);
+            return null;
+          }
+        } else {
+          // report error on if block: if (false) {!} else {}
+          _errorReporter.reportErrorForNode(
+              HintCode.DEAD_CODE, node.thenStatement);
+          _safelyVisit(node.elseStatement);
+          return null;
+        }
+      }
+    }
+    return super.visitIfStatement(node);
+  }
+
+  @override
+  Object visitSwitchCase(SwitchCase node) {
+    _checkForDeadStatementsInNodeList(node.statements);
+    return super.visitSwitchCase(node);
+  }
+
+  @override
+  Object visitSwitchDefault(SwitchDefault node) {
+    _checkForDeadStatementsInNodeList(node.statements);
+    return super.visitSwitchDefault(node);
+  }
+
+  @override
+  Object visitTryStatement(TryStatement node) {
+    _safelyVisit(node.body);
+    _safelyVisit(node.finallyBlock);
+    NodeList<CatchClause> catchClauses = node.catchClauses;
+    int numOfCatchClauses = catchClauses.length;
+    List<DartType> visitedTypes = new List<DartType>();
+    for (int i = 0; i < numOfCatchClauses; i++) {
+      CatchClause catchClause = catchClauses[i];
+      if (catchClause.onKeyword != null) {
+        // on-catch clause found, verify that the exception type is not a
+        // subtype of a previous on-catch exception type
+        TypeName typeName = catchClause.exceptionType;
+        if (typeName != null && typeName.type != null) {
+          DartType currentType = typeName.type;
+          if (currentType.isObject) {
+            // Found catch clause clause that has Object as an exception type,
+            // this is equivalent to having a catch clause that doesn't have an
+            // exception type, visit the block, but generate an error on any
+            // following catch clauses (and don't visit them).
+            _safelyVisit(catchClause);
+            if (i + 1 != numOfCatchClauses) {
+              // this catch clause is not the last in the try statement
+              CatchClause nextCatchClause = catchClauses[i + 1];
+              CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1];
+              int offset = nextCatchClause.offset;
+              int length = lastCatchClause.end - offset;
+              _errorReporter.reportErrorForOffset(
+                  HintCode.DEAD_CODE_CATCH_FOLLOWING_CATCH, offset, length);
+              return null;
+            }
+          }
+          for (DartType type in visitedTypes) {
+            if (currentType.isSubtypeOf(type)) {
+              CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1];
+              int offset = catchClause.offset;
+              int length = lastCatchClause.end - offset;
+              _errorReporter.reportErrorForOffset(
+                  HintCode.DEAD_CODE_ON_CATCH_SUBTYPE, offset, length, [
+                currentType.displayName,
+                type.displayName
+              ]);
+              return null;
+            }
+          }
+          visitedTypes.add(currentType);
+        }
+        _safelyVisit(catchClause);
+      } else {
+        // Found catch clause clause that doesn't have an exception type,
+        // visit the block, but generate an error on any following catch clauses
+        // (and don't visit them).
+        _safelyVisit(catchClause);
+        if (i + 1 != numOfCatchClauses) {
+          // this catch clause is not the last in the try statement
+          CatchClause nextCatchClause = catchClauses[i + 1];
+          CatchClause lastCatchClause = catchClauses[numOfCatchClauses - 1];
+          int offset = nextCatchClause.offset;
+          int length = lastCatchClause.end - offset;
+          _errorReporter.reportErrorForOffset(
+              HintCode.DEAD_CODE_CATCH_FOLLOWING_CATCH, offset, length);
+          return null;
+        }
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitWhileStatement(WhileStatement node) {
+    Expression conditionExpression = node.condition;
+    _safelyVisit(conditionExpression);
+    if (!_isDebugConstant(conditionExpression)) {
+      EvaluationResultImpl result =
+          _getConstantBooleanValue(conditionExpression);
+      if (result != null) {
+        if (result.value.isFalse) {
+          // report error on if block: while (false) {!}
+          _errorReporter.reportErrorForNode(HintCode.DEAD_CODE, node.body);
+          return null;
+        }
+      }
+    }
+    _safelyVisit(node.body);
+    return null;
+  }
+
+  /**
+   * Given some [NodeList] of [Statement]s, from either a [Block] or
+   * [SwitchMember], this loops through the list in reverse order searching for statements
+   * after a return, unlabeled break or unlabeled continue statement to mark them as dead code.
+   *
+   * @param statements some ordered list of statements in a [Block] or [SwitchMember]
+   */
+  void _checkForDeadStatementsInNodeList(NodeList<Statement> statements) {
+    int size = statements.length;
+    for (int i = 0; i < size; i++) {
+      Statement currentStatement = statements[i];
+      _safelyVisit(currentStatement);
+      bool returnOrBreakingStatement = currentStatement is ReturnStatement ||
+          (currentStatement is BreakStatement &&
+              currentStatement.label == null) ||
+          (currentStatement is ContinueStatement &&
+              currentStatement.label == null);
+      if (returnOrBreakingStatement && i != size - 1) {
+        Statement nextStatement = statements[i + 1];
+        Statement lastStatement = statements[size - 1];
+        int offset = nextStatement.offset;
+        int length = lastStatement.end - offset;
+        _errorReporter.reportErrorForOffset(HintCode.DEAD_CODE, offset, length);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Given some [Expression], this method returns [ValidResult.RESULT_TRUE] if it is
+   * `true`, [ValidResult.RESULT_FALSE] if it is `false`, or `null` if the
+   * expression is not a constant boolean value.
+   *
+   * @param expression the expression to evaluate
+   * @return [ValidResult.RESULT_TRUE] if it is `true`, [ValidResult.RESULT_FALSE]
+   *         if it is `false`, or `null` if the expression is not a constant boolean
+   *         value
+   */
+  EvaluationResultImpl _getConstantBooleanValue(Expression expression) {
+    if (expression is BooleanLiteral) {
+      if (expression.value) {
+        return new EvaluationResultImpl.con1(
+            new DartObjectImpl(null, BoolState.from(true)));
+      } else {
+        return new EvaluationResultImpl.con1(
+            new DartObjectImpl(null, BoolState.from(false)));
+      }
+    }
+    // Don't consider situations where we could evaluate to a constant boolean
+    // expression with the ConstantVisitor
+    // else {
+    // EvaluationResultImpl result = expression.accept(new ConstantVisitor());
+    // if (result == ValidResult.RESULT_TRUE) {
+    // return ValidResult.RESULT_TRUE;
+    // } else if (result == ValidResult.RESULT_FALSE) {
+    // return ValidResult.RESULT_FALSE;
+    // }
+    // return null;
+    // }
+    return null;
+  }
+
+  /**
+   * Return `true` if and only if the passed expression is resolved to a constant variable.
+   *
+   * @param expression some conditional expression
+   * @return `true` if and only if the passed expression is resolved to a constant variable
+   */
+  bool _isDebugConstant(Expression expression) {
+    Element element = null;
+    if (expression is Identifier) {
+      Identifier identifier = expression;
+      element = identifier.staticElement;
+    } else if (expression is PropertyAccess) {
+      PropertyAccess propertyAccess = expression;
+      element = propertyAccess.propertyName.staticElement;
+    }
+    if (element is PropertyAccessorElement) {
+      PropertyInducingElement variable = element.variable;
+      return variable != null && variable.isConst;
+    }
+    return false;
+  }
+
+  /**
+   * If the given node is not `null`, visit this instance of the dead code verifier.
+   *
+   * @param node the node to be visited
+   */
+  void _safelyVisit(AstNode node) {
+    if (node != null) {
+      node.accept(this);
+    }
+  }
+}
+
+/**
+ * Instances of the class `DeclarationResolver` are used to resolve declarations in an AST
+ * structure to already built elements.
+ */
+class DeclarationResolver extends RecursiveAstVisitor<Object> {
+  /**
+   * The compilation unit containing the AST nodes being visited.
+   */
+  CompilationUnitElement _enclosingUnit;
+
+  /**
+   * The function type alias containing the AST nodes being visited, or `null` if we are not
+   * in the scope of a function type alias.
+   */
+  FunctionTypeAliasElement _enclosingAlias;
+
+  /**
+   * The class containing the AST nodes being visited, or `null` if we are not in the scope of
+   * a class.
+   */
+  ClassElement _enclosingClass;
+
+  /**
+   * The method or function containing the AST nodes being visited, or `null` if we are not in
+   * the scope of a method or function.
+   */
+  ExecutableElement _enclosingExecutable;
+
+  /**
+   * The parameter containing the AST nodes being visited, or `null` if we are not in the
+   * scope of a parameter.
+   */
+  ParameterElement _enclosingParameter;
+
+  /**
+   * Resolve the declarations within the given compilation unit to the elements rooted at the given
+   * element.
+   *
+   * @param unit the compilation unit to be resolved
+   * @param element the root of the element model used to resolve the AST nodes
+   */
+  void resolve(CompilationUnit unit, CompilationUnitElement element) {
+    _enclosingUnit = element;
+    unit.element = element;
+    unit.accept(this);
+  }
+
+  @override
+  Object visitCatchClause(CatchClause node) {
+    SimpleIdentifier exceptionParameter = node.exceptionParameter;
+    if (exceptionParameter != null) {
+      List<LocalVariableElement> localVariables =
+          _enclosingExecutable.localVariables;
+      _findIdentifier(localVariables, exceptionParameter);
+      SimpleIdentifier stackTraceParameter = node.stackTraceParameter;
+      if (stackTraceParameter != null) {
+        _findIdentifier(localVariables, stackTraceParameter);
+      }
+    }
+    return super.visitCatchClause(node);
+  }
+
+  @override
+  Object visitClassDeclaration(ClassDeclaration node) {
+    ClassElement outerClass = _enclosingClass;
+    try {
+      SimpleIdentifier className = node.name;
+      _enclosingClass = _findIdentifier(_enclosingUnit.types, className);
+      return super.visitClassDeclaration(node);
+    } finally {
+      _enclosingClass = outerClass;
+    }
+  }
+
+  @override
+  Object visitClassTypeAlias(ClassTypeAlias node) {
+    ClassElement outerClass = _enclosingClass;
+    try {
+      SimpleIdentifier className = node.name;
+      _enclosingClass = _findIdentifier(_enclosingUnit.types, className);
+      return super.visitClassTypeAlias(node);
+    } finally {
+      _enclosingClass = outerClass;
+    }
+  }
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    ExecutableElement outerExecutable = _enclosingExecutable;
+    try {
+      SimpleIdentifier constructorName = node.name;
+      if (constructorName == null) {
+        _enclosingExecutable = _enclosingClass.unnamedConstructor;
+      } else {
+        _enclosingExecutable =
+            _enclosingClass.getNamedConstructor(constructorName.name);
+        constructorName.staticElement = _enclosingExecutable;
+      }
+      node.element = _enclosingExecutable as ConstructorElement;
+      return super.visitConstructorDeclaration(node);
+    } finally {
+      _enclosingExecutable = outerExecutable;
+    }
+  }
+
+  @override
+  Object visitDeclaredIdentifier(DeclaredIdentifier node) {
+    SimpleIdentifier variableName = node.identifier;
+    _findIdentifier(_enclosingExecutable.localVariables, variableName);
+    return super.visitDeclaredIdentifier(node);
+  }
+
+  @override
+  Object visitDefaultFormalParameter(DefaultFormalParameter node) {
+    SimpleIdentifier parameterName = node.parameter.identifier;
+    ParameterElement element = _getElementForParameter(node, parameterName);
+    Expression defaultValue = node.defaultValue;
+    if (defaultValue != null) {
+      ExecutableElement outerExecutable = _enclosingExecutable;
+      try {
+        if (element == null) {
+          // TODO(brianwilkerson) Report this internal error.
+        } else {
+          _enclosingExecutable = element.initializer;
+        }
+        defaultValue.accept(this);
+      } finally {
+        _enclosingExecutable = outerExecutable;
+      }
+    }
+    ParameterElement outerParameter = _enclosingParameter;
+    try {
+      _enclosingParameter = element;
+      return super.visitDefaultFormalParameter(node);
+    } finally {
+      _enclosingParameter = outerParameter;
+    }
+  }
+
+  @override
+  Object visitEnumDeclaration(EnumDeclaration node) {
+    ClassElement enclosingEnum =
+        _findIdentifier(_enclosingUnit.enums, node.name);
+    List<FieldElement> constants = enclosingEnum.fields;
+    for (EnumConstantDeclaration constant in node.constants) {
+      _findIdentifier(constants, constant.name);
+    }
+    return super.visitEnumDeclaration(node);
+  }
+
+  @override
+  Object visitExportDirective(ExportDirective node) {
+    String uri = _getStringValue(node.uri);
+    if (uri != null) {
+      LibraryElement library = _enclosingUnit.library;
+      ExportElement exportElement = _findExport(library.exports,
+          _enclosingUnit.context.sourceFactory.resolveUri(
+              _enclosingUnit.source, uri));
+      node.element = exportElement;
+    }
+    return super.visitExportDirective(node);
+  }
+
+  @override
+  Object visitFieldFormalParameter(FieldFormalParameter node) {
+    if (node.parent is! DefaultFormalParameter) {
+      SimpleIdentifier parameterName = node.identifier;
+      ParameterElement element = _getElementForParameter(node, parameterName);
+      ParameterElement outerParameter = _enclosingParameter;
+      try {
+        _enclosingParameter = element;
+        return super.visitFieldFormalParameter(node);
+      } finally {
+        _enclosingParameter = outerParameter;
+      }
+    } else {
+      return super.visitFieldFormalParameter(node);
+    }
+  }
+
+  @override
+  Object visitFunctionDeclaration(FunctionDeclaration node) {
+    ExecutableElement outerExecutable = _enclosingExecutable;
+    try {
+      SimpleIdentifier functionName = node.name;
+      sc.Token property = node.propertyKeyword;
+      if (property == null) {
+        if (_enclosingExecutable != null) {
+          _enclosingExecutable =
+              _findIdentifier(_enclosingExecutable.functions, functionName);
+        } else {
+          _enclosingExecutable =
+              _findIdentifier(_enclosingUnit.functions, functionName);
+        }
+      } else {
+        PropertyAccessorElement accessor =
+            _findIdentifier(_enclosingUnit.accessors, functionName);
+        if ((property as sc.KeywordToken).keyword == sc.Keyword.SET) {
+          accessor = accessor.variable.setter;
+          functionName.staticElement = accessor;
+        }
+        _enclosingExecutable = accessor;
+      }
+      node.functionExpression.element = _enclosingExecutable;
+      return super.visitFunctionDeclaration(node);
+    } finally {
+      _enclosingExecutable = outerExecutable;
+    }
+  }
+
+  @override
+  Object visitFunctionExpression(FunctionExpression node) {
+    if (node.parent is! FunctionDeclaration) {
+      FunctionElement element =
+          _findAtOffset(_enclosingExecutable.functions, node.beginToken.offset);
+      node.element = element;
+    }
+    ExecutableElement outerExecutable = _enclosingExecutable;
+    try {
+      _enclosingExecutable = node.element;
+      return super.visitFunctionExpression(node);
+    } finally {
+      _enclosingExecutable = outerExecutable;
+    }
+  }
+
+  @override
+  Object visitFunctionTypeAlias(FunctionTypeAlias node) {
+    FunctionTypeAliasElement outerAlias = _enclosingAlias;
+    try {
+      SimpleIdentifier aliasName = node.name;
+      _enclosingAlias =
+          _findIdentifier(_enclosingUnit.functionTypeAliases, aliasName);
+      return super.visitFunctionTypeAlias(node);
+    } finally {
+      _enclosingAlias = outerAlias;
+    }
+  }
+
+  @override
+  Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+    if (node.parent is! DefaultFormalParameter) {
+      SimpleIdentifier parameterName = node.identifier;
+      ParameterElement element = _getElementForParameter(node, parameterName);
+      ParameterElement outerParameter = _enclosingParameter;
+      try {
+        _enclosingParameter = element;
+        return super.visitFunctionTypedFormalParameter(node);
+      } finally {
+        _enclosingParameter = outerParameter;
+      }
+    } else {
+      return super.visitFunctionTypedFormalParameter(node);
+    }
+  }
+
+  @override
+  Object visitImportDirective(ImportDirective node) {
+    String uri = _getStringValue(node.uri);
+    if (uri != null) {
+      LibraryElement library = _enclosingUnit.library;
+      ImportElement importElement = _findImport(library.imports,
+          _enclosingUnit.context.sourceFactory.resolveUri(
+              _enclosingUnit.source, uri), node.prefix);
+      node.element = importElement;
+    }
+    return super.visitImportDirective(node);
+  }
+
+  @override
+  Object visitLabeledStatement(LabeledStatement node) {
+    for (Label label in node.labels) {
+      SimpleIdentifier labelName = label.label;
+      _findIdentifier(_enclosingExecutable.labels, labelName);
+    }
+    return super.visitLabeledStatement(node);
+  }
+
+  @override
+  Object visitLibraryDirective(LibraryDirective node) {
+    node.element = _enclosingUnit.library;
+    return super.visitLibraryDirective(node);
+  }
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    ExecutableElement outerExecutable = _enclosingExecutable;
+    try {
+      sc.Token property = node.propertyKeyword;
+      SimpleIdentifier methodName = node.name;
+      String nameOfMethod = methodName.name;
+      if (nameOfMethod == sc.TokenType.MINUS.lexeme &&
+          node.parameters.parameters.length == 0) {
+        nameOfMethod = "unary-";
+      }
+      if (property == null) {
+        _enclosingExecutable = _findWithNameAndOffset(
+            _enclosingClass.methods, nameOfMethod, methodName.offset);
+        methodName.staticElement = _enclosingExecutable;
+      } else {
+        PropertyAccessorElement accessor =
+            _findIdentifier(_enclosingClass.accessors, methodName);
+        if ((property as sc.KeywordToken).keyword == sc.Keyword.SET) {
+          accessor = accessor.variable.setter;
+          methodName.staticElement = accessor;
+        }
+        _enclosingExecutable = accessor;
+      }
+      return super.visitMethodDeclaration(node);
+    } finally {
+      _enclosingExecutable = outerExecutable;
+    }
+  }
+
+  @override
+  Object visitPartDirective(PartDirective node) {
+    String uri = _getStringValue(node.uri);
+    if (uri != null) {
+      Source partSource = _enclosingUnit.context.sourceFactory.resolveUri(
+          _enclosingUnit.source, uri);
+      node.element = _findPart(_enclosingUnit.library.parts, partSource);
+    }
+    return super.visitPartDirective(node);
+  }
+
+  @override
+  Object visitPartOfDirective(PartOfDirective node) {
+    node.element = _enclosingUnit.library;
+    return super.visitPartOfDirective(node);
+  }
+
+  @override
+  Object visitSimpleFormalParameter(SimpleFormalParameter node) {
+    if (node.parent is! DefaultFormalParameter) {
+      SimpleIdentifier parameterName = node.identifier;
+      ParameterElement element = _getElementForParameter(node, parameterName);
+      ParameterElement outerParameter = _enclosingParameter;
+      try {
+        _enclosingParameter = element;
+        return super.visitSimpleFormalParameter(node);
+      } finally {
+        _enclosingParameter = outerParameter;
+      }
+    } else {}
+    return super.visitSimpleFormalParameter(node);
+  }
+
+  @override
+  Object visitSwitchCase(SwitchCase node) {
+    for (Label label in node.labels) {
+      SimpleIdentifier labelName = label.label;
+      _findIdentifier(_enclosingExecutable.labels, labelName);
+    }
+    return super.visitSwitchCase(node);
+  }
+
+  @override
+  Object visitSwitchDefault(SwitchDefault node) {
+    for (Label label in node.labels) {
+      SimpleIdentifier labelName = label.label;
+      _findIdentifier(_enclosingExecutable.labels, labelName);
+    }
+    return super.visitSwitchDefault(node);
+  }
+
+  @override
+  Object visitTypeParameter(TypeParameter node) {
+    SimpleIdentifier parameterName = node.name;
+    if (_enclosingClass != null) {
+      _findIdentifier(_enclosingClass.typeParameters, parameterName);
+    } else if (_enclosingAlias != null) {
+      _findIdentifier(_enclosingAlias.typeParameters, parameterName);
+    }
+    return super.visitTypeParameter(node);
+  }
+
+  @override
+  Object visitVariableDeclaration(VariableDeclaration node) {
+    VariableElement element = null;
+    SimpleIdentifier variableName = node.name;
+    if (_enclosingExecutable != null) {
+      element =
+          _findIdentifier(_enclosingExecutable.localVariables, variableName);
+    }
+    if (element == null && _enclosingClass != null) {
+      element = _findIdentifier(_enclosingClass.fields, variableName);
+    }
+    if (element == null && _enclosingUnit != null) {
+      element = _findIdentifier(_enclosingUnit.topLevelVariables, variableName);
+    }
+    Expression initializer = node.initializer;
+    if (initializer != null) {
+      ExecutableElement outerExecutable = _enclosingExecutable;
+      try {
+        if (element == null) {
+          // TODO(brianwilkerson) Report this internal error.
+        } else {
+          _enclosingExecutable = element.initializer;
+        }
+        return super.visitVariableDeclaration(node);
+      } finally {
+        _enclosingExecutable = outerExecutable;
+      }
+    }
+    return super.visitVariableDeclaration(node);
+  }
+
+  /**
+   * Return the element in the given array of elements that was created for the declaration at the
+   * given offset. This method should only be used when there is no name
+   *
+   * @param elements the elements of the appropriate kind that exist in the current context
+   * @param offset the offset of the name of the element to be returned
+   * @return the element at the given offset
+   */
+  Element _findAtOffset(List<Element> elements, int offset) =>
+      _findWithNameAndOffset(elements, "", offset);
+
+  /**
+   * Return the export element from the given array whose library has the given source, or
+   * `null` if there is no such export.
+   *
+   * @param exports the export elements being searched
+   * @param source the source of the library associated with the export element to being searched
+   *          for
+   * @return the export element whose library has the given source
+   */
+  ExportElement _findExport(List<ExportElement> exports, Source source) {
+    for (ExportElement export in exports) {
+      if (export.exportedLibrary.source == source) {
+        return export;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the element in the given array of elements that was created for the declaration with the
+   * given name.
+   *
+   * @param elements the elements of the appropriate kind that exist in the current context
+   * @param identifier the name node in the declaration of the element to be returned
+   * @return the element created for the declaration with the given name
+   */
+  Element _findIdentifier(List<Element> elements, SimpleIdentifier identifier) {
+    Element element =
+        _findWithNameAndOffset(elements, identifier.name, identifier.offset);
+    identifier.staticElement = element;
+    return element;
+  }
+
+  /**
+   * Return the import element from the given array whose library has the given source and that has
+   * the given prefix, or `null` if there is no such import.
+   *
+   * @param imports the import elements being searched
+   * @param source the source of the library associated with the import element to being searched
+   *          for
+   * @param prefix the prefix with which the library was imported
+   * @return the import element whose library has the given source and prefix
+   */
+  ImportElement _findImport(
+      List<ImportElement> imports, Source source, SimpleIdentifier prefix) {
+    for (ImportElement element in imports) {
+      if (element.importedLibrary.source == source) {
+        PrefixElement prefixElement = element.prefix;
+        if (prefix == null) {
+          if (prefixElement == null) {
+            return element;
+          }
+        } else {
+          if (prefixElement != null &&
+              prefix.name == prefixElement.displayName) {
+            return element;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the element for the part with the given source, or `null` if there is no element
+   * for the given source.
+   *
+   * @param parts the elements for the parts
+   * @param partSource the source for the part whose element is to be returned
+   * @return the element for the part with the given source
+   */
+  CompilationUnitElement _findPart(
+      List<CompilationUnitElement> parts, Source partSource) {
+    for (CompilationUnitElement part in parts) {
+      if (part.source == partSource) {
+        return part;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the element in the given array of elements that was created for the declaration with the
+   * given name at the given offset.
+   *
+   * @param elements the elements of the appropriate kind that exist in the current context
+   * @param name the name of the element to be returned
+   * @param offset the offset of the name of the element to be returned
+   * @return the element with the given name and offset
+   */
+  Element _findWithNameAndOffset(
+      List<Element> elements, String name, int offset) {
+    for (Element element in elements) {
+      if (element.displayName == name && element.nameOffset == offset) {
+        return element;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Search the most closely enclosing list of parameters for a parameter with the given name.
+   *
+   * @param node the node defining the parameter with the given name
+   * @param parameterName the name of the parameter being searched for
+   * @return the element representing the parameter with that name
+   */
+  ParameterElement _getElementForParameter(
+      FormalParameter node, SimpleIdentifier parameterName) {
+    List<ParameterElement> parameters = null;
+    if (_enclosingParameter != null) {
+      parameters = _enclosingParameter.parameters;
+    }
+    if (parameters == null && _enclosingExecutable != null) {
+      parameters = _enclosingExecutable.parameters;
+    }
+    if (parameters == null && _enclosingAlias != null) {
+      parameters = _enclosingAlias.parameters;
+    }
+    ParameterElement element =
+        parameters == null ? null : _findIdentifier(parameters, parameterName);
+    if (element == null) {
+      StringBuffer buffer = new StringBuffer();
+      buffer.writeln("Invalid state found in the Analysis Engine:");
+      buffer.writeln(
+          "DeclarationResolver.getElementForParameter() is visiting a parameter that does not appear to be in a method or function.");
+      buffer.writeln("Ancestors:");
+      AstNode parent = node.parent;
+      while (parent != null) {
+        buffer.writeln(parent.runtimeType.toString());
+        buffer.writeln("---------");
+        parent = parent.parent;
+      }
+      AnalysisEngine.instance.logger.logError(buffer.toString(),
+          new CaughtException(new AnalysisException(), null));
+    }
+    return element;
+  }
+
+  /**
+   * Return the value of the given string literal, or `null` if the string is not a constant
+   * string without any string interpolation.
+   *
+   * @param literal the string literal whose value is to be returned
+   * @return the value of the given string literal
+   */
+  String _getStringValue(StringLiteral literal) {
+    if (literal is StringInterpolation) {
+      return null;
+    }
+    return literal.stringValue;
+  }
+}
+
+/**
+ * Instances of the class `ElementBuilder` traverse an AST structure and build the element
+ * model representing the AST structure.
+ */
+class ElementBuilder extends RecursiveAstVisitor<Object> {
+  /**
+   * The element holder associated with the element that is currently being built.
+   */
+  ElementHolder _currentHolder;
+
+  /**
+   * A flag indicating whether a variable declaration is in the context of a field declaration.
+   */
+  bool _inFieldContext = false;
+
+  /**
+   * A flag indicating whether a variable declaration is within the body of a method or function.
+   */
+  bool _inFunction = false;
+
+  /**
+   * A flag indicating whether the class currently being visited can be used as a mixin.
+   */
+  bool _isValidMixin = false;
+
+  /**
+   * A collection holding the function types defined in a class that need to have their type
+   * arguments set to the types of the type parameters for the class, or `null` if we are not
+   * currently processing nodes within a class.
+   */
+  List<FunctionTypeImpl> _functionTypesToFix = null;
+
+  /**
+   * A table mapping field names to field elements for the fields defined in the current class, or
+   * `null` if we are not in the scope of a class.
+   */
+  HashMap<String, FieldElement> _fieldMap;
+
+  /**
+   * Initialize a newly created element builder to build the elements for a compilation unit.
+   *
+   * @param initialHolder the element holder associated with the compilation unit being built
+   */
+  ElementBuilder(ElementHolder initialHolder) {
+    _currentHolder = initialHolder;
+  }
+
+  @override
+  Object visitBlock(Block node) {
+    bool wasInField = _inFieldContext;
+    _inFieldContext = false;
+    try {
+      node.visitChildren(this);
+    } finally {
+      _inFieldContext = wasInField;
+    }
+    return null;
+  }
+
+  @override
+  Object visitCatchClause(CatchClause node) {
+    SimpleIdentifier exceptionParameter = node.exceptionParameter;
+    if (exceptionParameter != null) {
+      // exception
+      LocalVariableElementImpl exception =
+          new LocalVariableElementImpl.forNode(exceptionParameter);
+      _currentHolder.addLocalVariable(exception);
+      exceptionParameter.staticElement = exception;
+      // stack trace
+      SimpleIdentifier stackTraceParameter = node.stackTraceParameter;
+      if (stackTraceParameter != null) {
+        LocalVariableElementImpl stackTrace =
+            new LocalVariableElementImpl.forNode(stackTraceParameter);
+        _currentHolder.addLocalVariable(stackTrace);
+        stackTraceParameter.staticElement = stackTrace;
+      }
+    }
+    return super.visitCatchClause(node);
+  }
+
+  @override
+  Object visitClassDeclaration(ClassDeclaration node) {
+    ElementHolder holder = new ElementHolder();
+    _isValidMixin = true;
+    _functionTypesToFix = new List<FunctionTypeImpl>();
+    //
+    // Process field declarations before constructors and methods so that field
+    // formal parameters can be correctly resolved to their fields.
+    //
+    ElementHolder previousHolder = _currentHolder;
+    _currentHolder = holder;
+    try {
+      List<ClassMember> nonFields = new List<ClassMember>();
+      node.visitChildren(
+          new _ElementBuilder_visitClassDeclaration(this, nonFields));
+      _buildFieldMap(holder.fieldsWithoutFlushing);
+      int count = nonFields.length;
+      for (int i = 0; i < count; i++) {
+        nonFields[i].accept(this);
+      }
+    } finally {
+      _currentHolder = previousHolder;
+    }
+    SimpleIdentifier className = node.name;
+    ClassElementImpl element = new ClassElementImpl.forNode(className);
+    List<TypeParameterElement> typeParameters = holder.typeParameters;
+    List<DartType> typeArguments = _createTypeParameterTypes(typeParameters);
+    InterfaceTypeImpl interfaceType = new InterfaceTypeImpl.con1(element);
+    interfaceType.typeArguments = typeArguments;
+    element.type = interfaceType;
+    List<ConstructorElement> constructors = holder.constructors;
+    if (constructors.length == 0) {
+      //
+      // Create the default constructor.
+      //
+      constructors = _createDefaultConstructors(interfaceType);
+    }
+    element.abstract = node.isAbstract;
+    element.accessors = holder.accessors;
+    element.constructors = constructors;
+    element.fields = holder.fields;
+    element.methods = holder.methods;
+    element.typeParameters = typeParameters;
+    element.validMixin = _isValidMixin;
+    int functionTypeCount = _functionTypesToFix.length;
+    for (int i = 0; i < functionTypeCount; i++) {
+      _functionTypesToFix[i].typeArguments = typeArguments;
+    }
+    _functionTypesToFix = null;
+    _currentHolder.addType(element);
+    className.staticElement = element;
+    _fieldMap = null;
+    holder.validate();
+    return null;
+  }
+
+  /**
+   * Implementation of this method should be synchronized with
+   * [visitClassDeclaration].
+   */
+  void visitClassDeclarationIncrementally(ClassDeclaration node) {
+    //
+    // Process field declarations before constructors and methods so that field
+    // formal parameters can be correctly resolved to their fields.
+    //
+    ClassElement classElement = node.element;
+    _buildFieldMap(classElement.fields);
+  }
+
+  @override
+  Object visitClassTypeAlias(ClassTypeAlias node) {
+    ElementHolder holder = new ElementHolder();
+    _functionTypesToFix = new List<FunctionTypeImpl>();
+    _visitChildren(holder, node);
+    SimpleIdentifier className = node.name;
+    ClassElementImpl element = new ClassElementImpl.forNode(className);
+    element.abstract = node.abstractKeyword != null;
+    element.typedef = true;
+    List<TypeParameterElement> typeParameters = holder.typeParameters;
+    element.typeParameters = typeParameters;
+    List<DartType> typeArguments = _createTypeParameterTypes(typeParameters);
+    InterfaceTypeImpl interfaceType = new InterfaceTypeImpl.con1(element);
+    interfaceType.typeArguments = typeArguments;
+    element.type = interfaceType;
+    // set default constructor
+    element.constructors = _createDefaultConstructors(interfaceType);
+    for (FunctionTypeImpl functionType in _functionTypesToFix) {
+      functionType.typeArguments = typeArguments;
+    }
+    _functionTypesToFix = null;
+    _currentHolder.addType(element);
+    className.staticElement = element;
+    holder.validate();
+    return null;
+  }
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    _isValidMixin = false;
+    ElementHolder holder = new ElementHolder();
+    bool wasInFunction = _inFunction;
+    _inFunction = true;
+    try {
+      _visitChildren(holder, node);
+    } finally {
+      _inFunction = wasInFunction;
+    }
+    FunctionBody body = node.body;
+    SimpleIdentifier constructorName = node.name;
+    ConstructorElementImpl element =
+        new ConstructorElementImpl.forNode(constructorName);
+    if (node.factoryKeyword != null) {
+      element.factory = true;
+    }
+    element.functions = holder.functions;
+    element.labels = holder.labels;
+    element.localVariables = holder.localVariables;
+    element.parameters = holder.parameters;
+    element.const2 = node.constKeyword != null;
+    if (body.isAsynchronous) {
+      element.asynchronous = true;
+    }
+    if (body.isGenerator) {
+      element.generator = true;
+    }
+    _currentHolder.addConstructor(element);
+    node.element = element;
+    if (constructorName == null) {
+      Identifier returnType = node.returnType;
+      if (returnType != null) {
+        element.nameOffset = returnType.offset;
+        element.nameEnd = returnType.end;
+      }
+    } else {
+      constructorName.staticElement = element;
+      element.periodOffset = node.period.offset;
+      element.nameEnd = constructorName.end;
+    }
+    holder.validate();
+    return null;
+  }
+
+  @override
+  Object visitDeclaredIdentifier(DeclaredIdentifier node) {
+    SimpleIdentifier variableName = node.identifier;
+    LocalVariableElementImpl element =
+        new LocalVariableElementImpl.forNode(variableName);
+    ForEachStatement statement = node.parent as ForEachStatement;
+    int declarationEnd = node.offset + node.length;
+    int statementEnd = statement.offset + statement.length;
+    element.setVisibleRange(declarationEnd, statementEnd - declarationEnd - 1);
+    element.const3 = node.isConst;
+    element.final2 = node.isFinal;
+    _currentHolder.addLocalVariable(element);
+    variableName.staticElement = element;
+    return super.visitDeclaredIdentifier(node);
+  }
+
+  @override
+  Object visitDefaultFormalParameter(DefaultFormalParameter node) {
+    ElementHolder holder = new ElementHolder();
+    NormalFormalParameter normalParameter = node.parameter;
+    SimpleIdentifier parameterName = normalParameter.identifier;
+    ParameterElementImpl parameter;
+    if (normalParameter is FieldFormalParameter) {
+      parameter = new DefaultFieldFormalParameterElementImpl(parameterName);
+      FieldElement field =
+          _fieldMap == null ? null : _fieldMap[parameterName.name];
+      if (field != null) {
+        (parameter as DefaultFieldFormalParameterElementImpl).field = field;
+      }
+    } else {
+      parameter = new DefaultParameterElementImpl(parameterName);
+    }
+    parameter.const3 = node.isConst;
+    parameter.final2 = node.isFinal;
+    parameter.parameterKind = node.kind;
+    // set initializer, default value range
+    Expression defaultValue = node.defaultValue;
+    if (defaultValue != null) {
+      _visit(holder, defaultValue);
+      FunctionElementImpl initializer =
+          new FunctionElementImpl.forOffset(defaultValue.beginToken.offset);
+      initializer.functions = holder.functions;
+      initializer.labels = holder.labels;
+      initializer.localVariables = holder.localVariables;
+      initializer.parameters = holder.parameters;
+      initializer.synthetic = true;
+      parameter.initializer = initializer;
+      parameter.defaultValueCode = defaultValue.toSource();
+    }
+    // visible range
+    _setParameterVisibleRange(node, parameter);
+    _currentHolder.addParameter(parameter);
+    parameterName.staticElement = parameter;
+    normalParameter.accept(this);
+    holder.validate();
+    return null;
+  }
+
+  @override
+  Object visitEnumDeclaration(EnumDeclaration node) {
+    SimpleIdentifier enumName = node.name;
+    ClassElementImpl enumElement = new ClassElementImpl.forNode(enumName);
+    enumElement.enum2 = true;
+    InterfaceTypeImpl enumType = new InterfaceTypeImpl.con1(enumElement);
+    enumElement.type = enumType;
+    _currentHolder.addEnum(enumElement);
+    enumName.staticElement = enumElement;
+    return super.visitEnumDeclaration(node);
+  }
+
+  @override
+  Object visitFieldDeclaration(FieldDeclaration node) {
+    bool wasInField = _inFieldContext;
+    _inFieldContext = true;
+    try {
+      node.visitChildren(this);
+    } finally {
+      _inFieldContext = wasInField;
+    }
+    return null;
+  }
+
+  @override
+  Object visitFieldFormalParameter(FieldFormalParameter node) {
+    if (node.parent is! DefaultFormalParameter) {
+      SimpleIdentifier parameterName = node.identifier;
+      FieldElement field =
+          _fieldMap == null ? null : _fieldMap[parameterName.name];
+      FieldFormalParameterElementImpl parameter =
+          new FieldFormalParameterElementImpl(parameterName);
+      parameter.const3 = node.isConst;
+      parameter.final2 = node.isFinal;
+      parameter.parameterKind = node.kind;
+      if (field != null) {
+        parameter.field = field;
+      }
+      _currentHolder.addParameter(parameter);
+      parameterName.staticElement = parameter;
+    }
+    //
+    // The children of this parameter include any parameters defined on the type
+    // of this parameter.
+    //
+    ElementHolder holder = new ElementHolder();
+    _visitChildren(holder, node);
+    (node.element as ParameterElementImpl).parameters = holder.parameters;
+    holder.validate();
+    return null;
+  }
+
+  @override
+  Object visitFunctionDeclaration(FunctionDeclaration node) {
+    FunctionExpression expression = node.functionExpression;
+    if (expression != null) {
+      ElementHolder holder = new ElementHolder();
+      bool wasInFunction = _inFunction;
+      _inFunction = true;
+      try {
+        _visitChildren(holder, expression);
+      } finally {
+        _inFunction = wasInFunction;
+      }
+      FunctionBody body = expression.body;
+      sc.Token property = node.propertyKeyword;
+      if (property == null || _inFunction) {
+        SimpleIdentifier functionName = node.name;
+        FunctionElementImpl element =
+            new FunctionElementImpl.forNode(functionName);
+        element.functions = holder.functions;
+        element.labels = holder.labels;
+        element.localVariables = holder.localVariables;
+        element.parameters = holder.parameters;
+        if (body.isAsynchronous) {
+          element.asynchronous = true;
+        }
+        if (body.isGenerator) {
+          element.generator = true;
+        }
+        if (_inFunction) {
+          Block enclosingBlock = node.getAncestor((node) => node is Block);
+          if (enclosingBlock != null) {
+            int functionEnd = node.offset + node.length;
+            int blockEnd = enclosingBlock.offset + enclosingBlock.length;
+            element.setVisibleRange(functionEnd, blockEnd - functionEnd - 1);
+          }
+        }
+        _currentHolder.addFunction(element);
+        expression.element = element;
+        functionName.staticElement = element;
+      } else {
+        SimpleIdentifier propertyNameNode = node.name;
+        if (propertyNameNode == null) {
+          // TODO(brianwilkerson) Report this internal error.
+          return null;
+        }
+        String propertyName = propertyNameNode.name;
+        TopLevelVariableElementImpl variable = _currentHolder
+            .getTopLevelVariable(propertyName) as TopLevelVariableElementImpl;
+        if (variable == null) {
+          variable = new TopLevelVariableElementImpl(node.name.name, -1);
+          variable.final2 = true;
+          variable.synthetic = true;
+          _currentHolder.addTopLevelVariable(variable);
+        }
+        if (node.isGetter) {
+          PropertyAccessorElementImpl getter =
+              new PropertyAccessorElementImpl.forNode(propertyNameNode);
+          getter.functions = holder.functions;
+          getter.labels = holder.labels;
+          getter.localVariables = holder.localVariables;
+          if (body.isAsynchronous) {
+            getter.asynchronous = true;
+          }
+          if (body.isGenerator) {
+            getter.generator = true;
+          }
+          getter.variable = variable;
+          getter.getter = true;
+          getter.static = true;
+          variable.getter = getter;
+          _currentHolder.addAccessor(getter);
+          expression.element = getter;
+          propertyNameNode.staticElement = getter;
+        } else {
+          PropertyAccessorElementImpl setter =
+              new PropertyAccessorElementImpl.forNode(propertyNameNode);
+          setter.functions = holder.functions;
+          setter.labels = holder.labels;
+          setter.localVariables = holder.localVariables;
+          setter.parameters = holder.parameters;
+          if (body.isAsynchronous) {
+            setter.asynchronous = true;
+          }
+          if (body.isGenerator) {
+            setter.generator = true;
+          }
+          setter.variable = variable;
+          setter.setter = true;
+          setter.static = true;
+          variable.setter = setter;
+          variable.final2 = false;
+          _currentHolder.addAccessor(setter);
+          expression.element = setter;
+          propertyNameNode.staticElement = setter;
+        }
+      }
+      holder.validate();
+    }
+    return null;
+  }
+
+  @override
+  Object visitFunctionExpression(FunctionExpression node) {
+    ElementHolder holder = new ElementHolder();
+    bool wasInFunction = _inFunction;
+    _inFunction = true;
+    try {
+      _visitChildren(holder, node);
+    } finally {
+      _inFunction = wasInFunction;
+    }
+    FunctionBody body = node.body;
+    FunctionElementImpl element =
+        new FunctionElementImpl.forOffset(node.beginToken.offset);
+    element.functions = holder.functions;
+    element.labels = holder.labels;
+    element.localVariables = holder.localVariables;
+    element.parameters = holder.parameters;
+    if (body.isAsynchronous) {
+      element.asynchronous = true;
+    }
+    if (body.isGenerator) {
+      element.generator = true;
+    }
+    if (_inFunction) {
+      Block enclosingBlock = node.getAncestor((node) => node is Block);
+      if (enclosingBlock != null) {
+        int functionEnd = node.offset + node.length;
+        int blockEnd = enclosingBlock.offset + enclosingBlock.length;
+        element.setVisibleRange(functionEnd, blockEnd - functionEnd - 1);
+      }
+    }
+    FunctionTypeImpl type = new FunctionTypeImpl.con1(element);
+    if (_functionTypesToFix != null) {
+      _functionTypesToFix.add(type);
+    }
+    element.type = type;
+    _currentHolder.addFunction(element);
+    node.element = element;
+    holder.validate();
+    return null;
+  }
+
+  @override
+  Object visitFunctionTypeAlias(FunctionTypeAlias node) {
+    ElementHolder holder = new ElementHolder();
+    _visitChildren(holder, node);
+    SimpleIdentifier aliasName = node.name;
+    List<ParameterElement> parameters = holder.parameters;
+    List<TypeParameterElement> typeParameters = holder.typeParameters;
+    FunctionTypeAliasElementImpl element =
+        new FunctionTypeAliasElementImpl.forNode(aliasName);
+    element.parameters = parameters;
+    element.typeParameters = typeParameters;
+    FunctionTypeImpl type = new FunctionTypeImpl.con2(element);
+    type.typeArguments = _createTypeParameterTypes(typeParameters);
+    element.type = type;
+    _currentHolder.addTypeAlias(element);
+    aliasName.staticElement = element;
+    holder.validate();
+    return null;
+  }
+
+  @override
+  Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+    if (node.parent is! DefaultFormalParameter) {
+      SimpleIdentifier parameterName = node.identifier;
+      ParameterElementImpl parameter =
+          new ParameterElementImpl.forNode(parameterName);
+      parameter.parameterKind = node.kind;
+      _setParameterVisibleRange(node, parameter);
+      _currentHolder.addParameter(parameter);
+      parameterName.staticElement = parameter;
+    }
+    //
+    // The children of this parameter include any parameters defined on the type
+    //of this parameter.
+    //
+    ElementHolder holder = new ElementHolder();
+    _visitChildren(holder, node);
+    (node.element as ParameterElementImpl).parameters = holder.parameters;
+    holder.validate();
+    return null;
+  }
+
+  @override
+  Object visitInstanceCreationExpression(InstanceCreationExpression node) {
+    if (node.isConst) {
+      node.constantHandle = new ConstantInstanceCreationHandle();
+    }
+    return super.visitInstanceCreationExpression(node);
+  }
+
+  @override
+  Object visitLabeledStatement(LabeledStatement node) {
+    bool onSwitchStatement = node.statement is SwitchStatement;
+    for (Label label in node.labels) {
+      SimpleIdentifier labelName = label.label;
+      LabelElementImpl element =
+          new LabelElementImpl(labelName, onSwitchStatement, false);
+      _currentHolder.addLabel(element);
+      labelName.staticElement = element;
+    }
+    return super.visitLabeledStatement(node);
+  }
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    try {
+      ElementHolder holder = new ElementHolder();
+      bool wasInFunction = _inFunction;
+      _inFunction = true;
+      try {
+        _visitChildren(holder, node);
+      } finally {
+        _inFunction = wasInFunction;
+      }
+      bool isStatic = node.isStatic;
+      sc.Token property = node.propertyKeyword;
+      FunctionBody body = node.body;
+      if (property == null) {
+        SimpleIdentifier methodName = node.name;
+        String nameOfMethod = methodName.name;
+        if (nameOfMethod == sc.TokenType.MINUS.lexeme &&
+            node.parameters.parameters.length == 0) {
+          nameOfMethod = "unary-";
+        }
+        MethodElementImpl element =
+            new MethodElementImpl(nameOfMethod, methodName.offset);
+        element.abstract = node.isAbstract;
+        element.functions = holder.functions;
+        element.labels = holder.labels;
+        element.localVariables = holder.localVariables;
+        element.parameters = holder.parameters;
+        element.static = isStatic;
+        if (body.isAsynchronous) {
+          element.asynchronous = true;
+        }
+        if (body.isGenerator) {
+          element.generator = true;
+        }
+        _currentHolder.addMethod(element);
+        methodName.staticElement = element;
+      } else {
+        SimpleIdentifier propertyNameNode = node.name;
+        String propertyName = propertyNameNode.name;
+        FieldElementImpl field =
+            _currentHolder.getField(propertyName) as FieldElementImpl;
+        if (field == null) {
+          field = new FieldElementImpl(node.name.name, -1);
+          field.final2 = true;
+          field.static = isStatic;
+          field.synthetic = true;
+          _currentHolder.addField(field);
+        }
+        if (node.isGetter) {
+          PropertyAccessorElementImpl getter =
+              new PropertyAccessorElementImpl.forNode(propertyNameNode);
+          getter.functions = holder.functions;
+          getter.labels = holder.labels;
+          getter.localVariables = holder.localVariables;
+          if (body.isAsynchronous) {
+            getter.asynchronous = true;
+          }
+          if (body.isGenerator) {
+            getter.generator = true;
+          }
+          getter.variable = field;
+          getter.abstract = node.isAbstract;
+          getter.getter = true;
+          getter.static = isStatic;
+          field.getter = getter;
+          _currentHolder.addAccessor(getter);
+          propertyNameNode.staticElement = getter;
+        } else {
+          PropertyAccessorElementImpl setter =
+              new PropertyAccessorElementImpl.forNode(propertyNameNode);
+          setter.functions = holder.functions;
+          setter.labels = holder.labels;
+          setter.localVariables = holder.localVariables;
+          setter.parameters = holder.parameters;
+          if (body.isAsynchronous) {
+            setter.asynchronous = true;
+          }
+          if (body.isGenerator) {
+            setter.generator = true;
+          }
+          setter.variable = field;
+          setter.abstract = node.isAbstract;
+          setter.setter = true;
+          setter.static = isStatic;
+          field.setter = setter;
+          field.final2 = false;
+          _currentHolder.addAccessor(setter);
+          propertyNameNode.staticElement = setter;
+        }
+      }
+      holder.validate();
+    } catch (exception, stackTrace) {
+      if (node.name.staticElement == null) {
+        ClassDeclaration classNode =
+            node.getAncestor((node) => node is ClassDeclaration);
+        StringBuffer buffer = new StringBuffer();
+        buffer.write("The element for the method ");
+        buffer.write(node.name);
+        buffer.write(" in ");
+        buffer.write(classNode.name);
+        buffer.write(" was not set while trying to build the element model.");
+        AnalysisEngine.instance.logger.logError(
+            buffer.toString(), new CaughtException(exception, stackTrace));
+      } else {
+        String message =
+            "Exception caught in ElementBuilder.visitMethodDeclaration()";
+        AnalysisEngine.instance.logger.logError(
+            message, new CaughtException(exception, stackTrace));
+      }
+    } finally {
+      if (node.name.staticElement == null) {
+        ClassDeclaration classNode =
+            node.getAncestor((node) => node is ClassDeclaration);
+        StringBuffer buffer = new StringBuffer();
+        buffer.write("The element for the method ");
+        buffer.write(node.name);
+        buffer.write(" in ");
+        buffer.write(classNode.name);
+        buffer.write(" was not set while trying to resolve types.");
+        AnalysisEngine.instance.logger.logError(buffer.toString(),
+            new CaughtException(
+                new AnalysisException(buffer.toString()), null));
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitSimpleFormalParameter(SimpleFormalParameter node) {
+    if (node.parent is! DefaultFormalParameter) {
+      SimpleIdentifier parameterName = node.identifier;
+      ParameterElementImpl parameter =
+          new ParameterElementImpl.forNode(parameterName);
+      parameter.const3 = node.isConst;
+      parameter.final2 = node.isFinal;
+      parameter.parameterKind = node.kind;
+      _setParameterVisibleRange(node, parameter);
+      _currentHolder.addParameter(parameter);
+      parameterName.staticElement = parameter;
+    }
+    return super.visitSimpleFormalParameter(node);
+  }
+
+  @override
+  Object visitSuperExpression(SuperExpression node) {
+    _isValidMixin = false;
+    return super.visitSuperExpression(node);
+  }
+
+  @override
+  Object visitSwitchCase(SwitchCase node) {
+    for (Label label in node.labels) {
+      SimpleIdentifier labelName = label.label;
+      LabelElementImpl element = new LabelElementImpl(labelName, false, true);
+      _currentHolder.addLabel(element);
+      labelName.staticElement = element;
+    }
+    return super.visitSwitchCase(node);
+  }
+
+  @override
+  Object visitSwitchDefault(SwitchDefault node) {
+    for (Label label in node.labels) {
+      SimpleIdentifier labelName = label.label;
+      LabelElementImpl element = new LabelElementImpl(labelName, false, true);
+      _currentHolder.addLabel(element);
+      labelName.staticElement = element;
+    }
+    return super.visitSwitchDefault(node);
+  }
+
+  @override
+  Object visitTypeParameter(TypeParameter node) {
+    SimpleIdentifier parameterName = node.name;
+    TypeParameterElementImpl typeParameter =
+        new TypeParameterElementImpl.forNode(parameterName);
+    TypeParameterTypeImpl typeParameterType =
+        new TypeParameterTypeImpl(typeParameter);
+    typeParameter.type = typeParameterType;
+    _currentHolder.addTypeParameter(typeParameter);
+    parameterName.staticElement = typeParameter;
+    return super.visitTypeParameter(node);
+  }
+
+  @override
+  Object visitVariableDeclaration(VariableDeclaration node) {
+    bool isConst = node.isConst;
+    bool isFinal = node.isFinal;
+    bool hasInitializer = node.initializer != null;
+    VariableElementImpl element;
+    if (_inFieldContext) {
+      SimpleIdentifier fieldName = node.name;
+      FieldElementImpl field;
+      if (isConst && hasInitializer) {
+        field = new ConstFieldElementImpl.con1(fieldName);
+      } else {
+        field = new FieldElementImpl.forNode(fieldName);
+      }
+      element = field;
+      _currentHolder.addField(field);
+      fieldName.staticElement = field;
+    } else if (_inFunction) {
+      SimpleIdentifier variableName = node.name;
+      LocalVariableElementImpl variable;
+      if (isConst && hasInitializer) {
+        variable = new ConstLocalVariableElementImpl.forNode(variableName);
+      } else {
+        variable = new LocalVariableElementImpl.forNode(variableName);
+      }
+      element = variable;
+      Block enclosingBlock = node.getAncestor((node) => node is Block);
+      // TODO(brianwilkerson) This isn't right for variables declared in a for
+      // loop.
+      variable.setVisibleRange(enclosingBlock.offset, enclosingBlock.length);
+      _currentHolder.addLocalVariable(variable);
+      variableName.staticElement = element;
+    } else {
+      SimpleIdentifier variableName = node.name;
+      TopLevelVariableElementImpl variable;
+      if (isConst && hasInitializer) {
+        variable = new ConstTopLevelVariableElementImpl(variableName);
+      } else {
+        variable = new TopLevelVariableElementImpl.forNode(variableName);
+      }
+      element = variable;
+      _currentHolder.addTopLevelVariable(variable);
+      variableName.staticElement = element;
+    }
+    element.const3 = isConst;
+    element.final2 = isFinal;
+    if (hasInitializer) {
+      ElementHolder holder = new ElementHolder();
+      bool wasInFieldContext = _inFieldContext;
+      _inFieldContext = false;
+      try {
+        _visit(holder, node.initializer);
+      } finally {
+        _inFieldContext = wasInFieldContext;
+      }
+      FunctionElementImpl initializer =
+          new FunctionElementImpl.forOffset(node.initializer.beginToken.offset);
+      initializer.functions = holder.functions;
+      initializer.labels = holder.labels;
+      initializer.localVariables = holder.localVariables;
+      initializer.synthetic = true;
+      element.initializer = initializer;
+      holder.validate();
+    }
+    if (element is PropertyInducingElementImpl) {
+      if (_inFieldContext) {
+        (element as FieldElementImpl).static =
+            (node.parent.parent as FieldDeclaration).isStatic;
+      }
+      PropertyAccessorElementImpl getter =
+          new PropertyAccessorElementImpl.forVariable(element);
+      getter.getter = true;
+      _currentHolder.addAccessor(getter);
+      element.getter = getter;
+      if (!isConst && !isFinal) {
+        PropertyAccessorElementImpl setter =
+            new PropertyAccessorElementImpl.forVariable(element);
+        setter.setter = true;
+        ParameterElementImpl parameter =
+            new ParameterElementImpl("_${element.name}", element.nameOffset);
+        parameter.synthetic = true;
+        parameter.parameterKind = ParameterKind.REQUIRED;
+        setter.parameters = <ParameterElement>[parameter];
+        _currentHolder.addAccessor(setter);
+        element.setter = setter;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Build the table mapping field names to field elements for the fields defined in the current
+   * class.
+   *
+   * @param fields the field elements defined in the current class
+   */
+  void _buildFieldMap(List<FieldElement> fields) {
+    _fieldMap = new HashMap<String, FieldElement>();
+    int count = fields.length;
+    for (int i = 0; i < count; i++) {
+      FieldElement field = fields[i];
+      _fieldMap[field.name] = field;
+    }
+  }
+
+  /**
+   * Creates the [ConstructorElement]s array with the single default constructor element.
+   *
+   * @param interfaceType the interface type for which to create a default constructor
+   * @return the [ConstructorElement]s array with the single default constructor element
+   */
+  List<ConstructorElement> _createDefaultConstructors(
+      InterfaceTypeImpl interfaceType) {
+    ConstructorElementImpl constructor =
+        new ConstructorElementImpl.forNode(null);
+    constructor.synthetic = true;
+    constructor.returnType = interfaceType;
+    FunctionTypeImpl type = new FunctionTypeImpl.con1(constructor);
+    _functionTypesToFix.add(type);
+    constructor.type = type;
+    return <ConstructorElement>[constructor];
+  }
+
+  /**
+   * Create the types associated with the given type parameters, setting the type of each type
+   * parameter, and return an array of types corresponding to the given parameters.
+   *
+   * @param typeParameters the type parameters for which types are to be created
+   * @return an array of types corresponding to the given parameters
+   */
+  List<DartType> _createTypeParameterTypes(
+      List<TypeParameterElement> typeParameters) {
+    int typeParameterCount = typeParameters.length;
+    List<DartType> typeArguments = new List<DartType>(typeParameterCount);
+    for (int i = 0; i < typeParameterCount; i++) {
+      TypeParameterElementImpl typeParameter =
+          typeParameters[i] as TypeParameterElementImpl;
+      TypeParameterTypeImpl typeParameterType =
+          new TypeParameterTypeImpl(typeParameter);
+      typeParameter.type = typeParameterType;
+      typeArguments[i] = typeParameterType;
+    }
+    return typeArguments;
+  }
+
+  /**
+   * Return the body of the function that contains the given parameter, or `null` if no
+   * function body could be found.
+   *
+   * @param node the parameter contained in the function whose body is to be returned
+   * @return the body of the function that contains the given parameter
+   */
+  FunctionBody _getFunctionBody(FormalParameter node) {
+    AstNode parent = node.parent;
+    while (parent != null) {
+      if (parent is ConstructorDeclaration) {
+        return parent.body;
+      } else if (parent is FunctionExpression) {
+        return parent.body;
+      } else if (parent is MethodDeclaration) {
+        return parent.body;
+      }
+      parent = parent.parent;
+    }
+    return null;
+  }
+
+  /**
+   * Sets the visible source range for formal parameter.
+   */
+  void _setParameterVisibleRange(
+      FormalParameter node, ParameterElementImpl element) {
+    FunctionBody body = _getFunctionBody(node);
+    if (body != null) {
+      element.setVisibleRange(body.offset, body.length);
+    }
+  }
+
+  /**
+   * Make the given holder be the current holder while visiting the given node.
+   *
+   * @param holder the holder that will gather elements that are built while visiting the children
+   * @param node the node to be visited
+   */
+  void _visit(ElementHolder holder, AstNode node) {
+    if (node != null) {
+      ElementHolder previousHolder = _currentHolder;
+      _currentHolder = holder;
+      try {
+        node.accept(this);
+      } finally {
+        _currentHolder = previousHolder;
+      }
+    }
+  }
+
+  /**
+   * Make the given holder be the current holder while visiting the children of the given node.
+   *
+   * @param holder the holder that will gather elements that are built while visiting the children
+   * @param node the node whose children are to be visited
+   */
+  void _visitChildren(ElementHolder holder, AstNode node) {
+    if (node != null) {
+      ElementHolder previousHolder = _currentHolder;
+      _currentHolder = holder;
+      try {
+        node.visitChildren(this);
+      } finally {
+        _currentHolder = previousHolder;
+      }
+    }
+  }
+}
+
+/**
+ * Instances of the class `ElementHolder` hold on to elements created while traversing an AST
+ * structure so that they can be accessed when creating their enclosing element.
+ */
+class ElementHolder {
+  List<PropertyAccessorElement> _accessors;
+
+  List<ConstructorElement> _constructors;
+
+  List<ClassElement> _enums;
+
+  List<FieldElement> _fields;
+
+  List<FunctionElement> _functions;
+
+  List<LabelElement> _labels;
+
+  List<LocalVariableElement> _localVariables;
+
+  List<MethodElement> _methods;
+
+  List<ParameterElement> _parameters;
+
+  List<TopLevelVariableElement> _topLevelVariables;
+
+  List<ClassElement> _types;
+
+  List<FunctionTypeAliasElement> _typeAliases;
+
+  List<TypeParameterElement> _typeParameters;
+
+  List<PropertyAccessorElement> get accessors {
+    if (_accessors == null) {
+      return PropertyAccessorElement.EMPTY_LIST;
+    }
+    List<PropertyAccessorElement> result = _accessors;
+    _accessors = null;
+    return result;
+  }
+
+  List<ConstructorElement> get constructors {
+    if (_constructors == null) {
+      return ConstructorElement.EMPTY_LIST;
+    }
+    List<ConstructorElement> result = _constructors;
+    _constructors = null;
+    return result;
+  }
+
+  List<ClassElement> get enums {
+    if (_enums == null) {
+      return ClassElement.EMPTY_LIST;
+    }
+    List<ClassElement> result = _enums;
+    _enums = null;
+    return result;
+  }
+
+  List<FieldElement> get fields {
+    if (_fields == null) {
+      return FieldElement.EMPTY_LIST;
+    }
+    List<FieldElement> result = _fields;
+    _fields = null;
+    return result;
+  }
+
+  List<FieldElement> get fieldsWithoutFlushing {
+    if (_fields == null) {
+      return FieldElement.EMPTY_LIST;
+    }
+    List<FieldElement> result = _fields;
+    return result;
+  }
+
+  List<FunctionElement> get functions {
+    if (_functions == null) {
+      return FunctionElement.EMPTY_LIST;
+    }
+    List<FunctionElement> result = _functions;
+    _functions = null;
+    return result;
+  }
+
+  List<LabelElement> get labels {
+    if (_labels == null) {
+      return LabelElement.EMPTY_LIST;
+    }
+    List<LabelElement> result = _labels;
+    _labels = null;
+    return result;
+  }
+
+  List<LocalVariableElement> get localVariables {
+    if (_localVariables == null) {
+      return LocalVariableElement.EMPTY_LIST;
+    }
+    List<LocalVariableElement> result = _localVariables;
+    _localVariables = null;
+    return result;
+  }
+
+  List<MethodElement> get methods {
+    if (_methods == null) {
+      return MethodElement.EMPTY_LIST;
+    }
+    List<MethodElement> result = _methods;
+    _methods = null;
+    return result;
+  }
+
+  List<ParameterElement> get parameters {
+    if (_parameters == null) {
+      return ParameterElement.EMPTY_LIST;
+    }
+    List<ParameterElement> result = _parameters;
+    _parameters = null;
+    return result;
+  }
+
+  List<TopLevelVariableElement> get topLevelVariables {
+    if (_topLevelVariables == null) {
+      return TopLevelVariableElement.EMPTY_LIST;
+    }
+    List<TopLevelVariableElement> result = _topLevelVariables;
+    _topLevelVariables = null;
+    return result;
+  }
+
+  List<FunctionTypeAliasElement> get typeAliases {
+    if (_typeAliases == null) {
+      return FunctionTypeAliasElement.EMPTY_LIST;
+    }
+    List<FunctionTypeAliasElement> result = _typeAliases;
+    _typeAliases = null;
+    return result;
+  }
+
+  List<TypeParameterElement> get typeParameters {
+    if (_typeParameters == null) {
+      return TypeParameterElement.EMPTY_LIST;
+    }
+    List<TypeParameterElement> result = _typeParameters;
+    _typeParameters = null;
+    return result;
+  }
+
+  List<ClassElement> get types {
+    if (_types == null) {
+      return ClassElement.EMPTY_LIST;
+    }
+    List<ClassElement> result = _types;
+    _types = null;
+    return result;
+  }
+
+  void addAccessor(PropertyAccessorElement element) {
+    if (_accessors == null) {
+      _accessors = new List<PropertyAccessorElement>();
+    }
+    _accessors.add(element);
+  }
+
+  void addConstructor(ConstructorElement element) {
+    if (_constructors == null) {
+      _constructors = new List<ConstructorElement>();
+    }
+    _constructors.add(element);
+  }
+
+  void addEnum(ClassElement element) {
+    if (_enums == null) {
+      _enums = new List<ClassElement>();
+    }
+    _enums.add(element);
+  }
+
+  void addField(FieldElement element) {
+    if (_fields == null) {
+      _fields = new List<FieldElement>();
+    }
+    _fields.add(element);
+  }
+
+  void addFunction(FunctionElement element) {
+    if (_functions == null) {
+      _functions = new List<FunctionElement>();
+    }
+    _functions.add(element);
+  }
+
+  void addLabel(LabelElement element) {
+    if (_labels == null) {
+      _labels = new List<LabelElement>();
+    }
+    _labels.add(element);
+  }
+
+  void addLocalVariable(LocalVariableElement element) {
+    if (_localVariables == null) {
+      _localVariables = new List<LocalVariableElement>();
+    }
+    _localVariables.add(element);
+  }
+
+  void addMethod(MethodElement element) {
+    if (_methods == null) {
+      _methods = new List<MethodElement>();
+    }
+    _methods.add(element);
+  }
+
+  void addParameter(ParameterElement element) {
+    if (_parameters == null) {
+      _parameters = new List<ParameterElement>();
+    }
+    _parameters.add(element);
+  }
+
+  void addTopLevelVariable(TopLevelVariableElement element) {
+    if (_topLevelVariables == null) {
+      _topLevelVariables = new List<TopLevelVariableElement>();
+    }
+    _topLevelVariables.add(element);
+  }
+
+  void addType(ClassElement element) {
+    if (_types == null) {
+      _types = new List<ClassElement>();
+    }
+    _types.add(element);
+  }
+
+  void addTypeAlias(FunctionTypeAliasElement element) {
+    if (_typeAliases == null) {
+      _typeAliases = new List<FunctionTypeAliasElement>();
+    }
+    _typeAliases.add(element);
+  }
+
+  void addTypeParameter(TypeParameterElement element) {
+    if (_typeParameters == null) {
+      _typeParameters = new List<TypeParameterElement>();
+    }
+    _typeParameters.add(element);
+  }
+
+  FieldElement getField(String fieldName) {
+    if (_fields == null) {
+      return null;
+    }
+    for (FieldElement field in _fields) {
+      if (field.name == fieldName) {
+        return field;
+      }
+    }
+    return null;
+  }
+
+  TopLevelVariableElement getTopLevelVariable(String variableName) {
+    if (_topLevelVariables == null) {
+      return null;
+    }
+    for (TopLevelVariableElement variable in _topLevelVariables) {
+      if (variable.name == variableName) {
+        return variable;
+      }
+    }
+    return null;
+  }
+
+  void validate() {
+    StringBuffer buffer = new StringBuffer();
+    if (_accessors != null) {
+      buffer.write(_accessors.length);
+      buffer.write(" accessors");
+    }
+    if (_constructors != null) {
+      if (buffer.length > 0) {
+        buffer.write("; ");
+      }
+      buffer.write(_constructors.length);
+      buffer.write(" constructors");
+    }
+    if (_fields != null) {
+      if (buffer.length > 0) {
+        buffer.write("; ");
+      }
+      buffer.write(_fields.length);
+      buffer.write(" fields");
+    }
+    if (_functions != null) {
+      if (buffer.length > 0) {
+        buffer.write("; ");
+      }
+      buffer.write(_functions.length);
+      buffer.write(" functions");
+    }
+    if (_labels != null) {
+      if (buffer.length > 0) {
+        buffer.write("; ");
+      }
+      buffer.write(_labels.length);
+      buffer.write(" labels");
+    }
+    if (_localVariables != null) {
+      if (buffer.length > 0) {
+        buffer.write("; ");
+      }
+      buffer.write(_localVariables.length);
+      buffer.write(" local variables");
+    }
+    if (_methods != null) {
+      if (buffer.length > 0) {
+        buffer.write("; ");
+      }
+      buffer.write(_methods.length);
+      buffer.write(" methods");
+    }
+    if (_parameters != null) {
+      if (buffer.length > 0) {
+        buffer.write("; ");
+      }
+      buffer.write(_parameters.length);
+      buffer.write(" parameters");
+    }
+    if (_topLevelVariables != null) {
+      if (buffer.length > 0) {
+        buffer.write("; ");
+      }
+      buffer.write(_topLevelVariables.length);
+      buffer.write(" top-level variables");
+    }
+    if (_types != null) {
+      if (buffer.length > 0) {
+        buffer.write("; ");
+      }
+      buffer.write(_types.length);
+      buffer.write(" types");
+    }
+    if (_typeAliases != null) {
+      if (buffer.length > 0) {
+        buffer.write("; ");
+      }
+      buffer.write(_typeAliases.length);
+      buffer.write(" type aliases");
+    }
+    if (_typeParameters != null) {
+      if (buffer.length > 0) {
+        buffer.write("; ");
+      }
+      buffer.write(_typeParameters.length);
+      buffer.write(" type parameters");
+    }
+    if (buffer.length > 0) {
+      AnalysisEngine.instance.logger
+          .logError("Failed to capture elements: $buffer");
+    }
+  }
+}
+
+/**
+ * Instances of the class `EnclosedScope` implement a scope that is lexically enclosed in
+ * another scope.
+ */
+class EnclosedScope extends Scope {
+  /**
+   * The scope in which this scope is lexically enclosed.
+   */
+  final Scope enclosingScope;
+
+  /**
+   * A table mapping names that will be defined in this scope, but right now are not initialized.
+   * According to the scoping rules these names are hidden, even if they were defined in an outer
+   * scope.
+   */
+  HashMap<String, Element> _hiddenElements = new HashMap<String, Element>();
+
+  /**
+   * A flag indicating whether there are any names defined in this scope.
+   */
+  bool _hasHiddenName = false;
+
+  /**
+   * Initialize a newly created scope enclosed within another scope.
+   *
+   * @param enclosingScope the scope in which this scope is lexically enclosed
+   */
+  EnclosedScope(this.enclosingScope);
+
+  @override
+  AnalysisErrorListener get errorListener => enclosingScope.errorListener;
+
+  /**
+   * Record that given element is declared in this scope, but hasn't been initialized yet, so it is
+   * error to use. If there is already an element with the given name defined in an outer scope,
+   * then it will become unavailable.
+   *
+   * @param element the element declared, but not initialized in this scope
+   */
+  void hide(Element element) {
+    if (element != null) {
+      String name = element.name;
+      if (name != null && !name.isEmpty) {
+        _hiddenElements[name] = element;
+        _hasHiddenName = true;
+      }
+    }
+  }
+
+  @override
+  Element internalLookup(
+      Identifier identifier, String name, LibraryElement referencingLibrary) {
+    Element element = localLookup(name, referencingLibrary);
+    if (element != null) {
+      return element;
+    }
+    // May be there is a hidden Element.
+    if (_hasHiddenName) {
+      Element hiddenElement = _hiddenElements[name];
+      if (hiddenElement != null) {
+        errorListener.onError(new AnalysisError.con2(getSource(identifier),
+            identifier.offset, identifier.length,
+            CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION, []));
+        return hiddenElement;
+      }
+    }
+    // Check enclosing scope.
+    return enclosingScope.internalLookup(identifier, name, referencingLibrary);
+  }
+}
+
+/**
+ * Instances of the class `EnumMemberBuilder` build the members in enum declarations.
+ */
+class EnumMemberBuilder extends RecursiveAstVisitor<Object> {
+  /**
+   * The type provider used to access the types needed to build an element model for enum
+   * declarations.
+   */
+  final TypeProvider _typeProvider;
+
+  /**
+   * Initialize a newly created enum member builder.
+   *
+   * @param typeProvider the type provider used to access the types needed to build an element model
+   *          for enum declarations
+   */
+  EnumMemberBuilder(this._typeProvider);
+
+  @override
+  Object visitEnumDeclaration(EnumDeclaration node) {
+    //
+    // Finish building the enum.
+    //
+    ClassElementImpl enumElement = node.name.staticElement as ClassElementImpl;
+    InterfaceType enumType = enumElement.type;
+    enumElement.supertype = _typeProvider.objectType;
+    //
+    // Populate the fields.
+    //
+    List<FieldElement> fields = new List<FieldElement>();
+    List<PropertyAccessorElement> getters = new List<PropertyAccessorElement>();
+    InterfaceType intType = _typeProvider.intType;
+    String indexFieldName = "index";
+    FieldElementImpl indexField = new FieldElementImpl(indexFieldName, -1);
+    indexField.final2 = true;
+    indexField.synthetic = true;
+    indexField.type = intType;
+    fields.add(indexField);
+    getters.add(_createGetter(indexField));
+    ConstFieldElementImpl valuesField =
+        new ConstFieldElementImpl.con2("values", -1);
+    valuesField.static = true;
+    valuesField.const3 = true;
+    valuesField.synthetic = true;
+    valuesField.type = _typeProvider.listType.substitute4(<DartType>[enumType]);
+    fields.add(valuesField);
+    getters.add(_createGetter(valuesField));
+    //
+    // Build the enum constants.
+    //
+    NodeList<EnumConstantDeclaration> constants = node.constants;
+    List<DartObjectImpl> constantValues = new List<DartObjectImpl>();
+    int constantCount = constants.length;
+    for (int i = 0; i < constantCount; i++) {
+      SimpleIdentifier constantName = constants[i].name;
+      FieldElementImpl constantField =
+          new ConstFieldElementImpl.con1(constantName);
+      constantField.static = true;
+      constantField.const3 = true;
+      constantField.type = enumType;
+      //
+      // Create a value for the constant.
+      //
+      HashMap<String, DartObjectImpl> fieldMap =
+          new HashMap<String, DartObjectImpl>();
+      fieldMap[indexFieldName] = new DartObjectImpl(intType, new IntState(i));
+      DartObjectImpl value =
+          new DartObjectImpl(enumType, new GenericState(fieldMap));
+      constantValues.add(value);
+      constantField.evaluationResult = new EvaluationResultImpl.con1(value);
+      fields.add(constantField);
+      getters.add(_createGetter(constantField));
+      constantName.staticElement = constantField;
+    }
+    //
+    // Build the value of the 'values' field.
+    //
+    valuesField.evaluationResult = new EvaluationResultImpl.con1(
+        new DartObjectImpl(valuesField.type, new ListState(constantValues)));
+    //
+    // Finish building the enum.
+    //
+    enumElement.fields = fields;
+    enumElement.accessors = getters;
+    // Client code isn't allowed to invoke the constructor, so we do not model
+    // it.
+    return super.visitEnumDeclaration(node);
+  }
+
+  /**
+   * Create a getter that corresponds to the given field.
+   *
+   * @param field the field for which a getter is to be created
+   * @return the getter that was created
+   */
+  PropertyAccessorElement _createGetter(FieldElementImpl field) {
+    PropertyAccessorElementImpl getter =
+        new PropertyAccessorElementImpl.forVariable(field);
+    getter.getter = true;
+    getter.returnType = field.type;
+    getter.type = new FunctionTypeImpl.con1(getter);
+    field.getter = getter;
+    return getter;
+  }
+}
+
+/**
+ * Instances of the class `ExitDetector` determine whether the visited AST node is guaranteed
+ * to terminate by executing a `return` statement, `throw` expression, `rethrow`
+ * expression, or simple infinite loop such as `while(true)`.
+ */
+class ExitDetector extends GeneralizingAstVisitor<bool> {
+  /**
+   * Set to `true` when a `break` is encountered, and reset to `false` when a
+   * `do`, `while`, `for` or `switch` block is entered.
+   */
+  bool _enclosingBlockContainsBreak = false;
+
+  @override
+  bool visitArgumentList(ArgumentList node) =>
+      _visitExpressions(node.arguments);
+
+  @override
+  bool visitAsExpression(AsExpression node) => _nodeExits(node.expression);
+
+  @override
+  bool visitAssertStatement(AssertStatement node) => _nodeExits(node.condition);
+
+  @override
+  bool visitAssignmentExpression(AssignmentExpression node) =>
+      _nodeExits(node.leftHandSide) || _nodeExits(node.rightHandSide);
+
+  @override
+  bool visitAwaitExpression(AwaitExpression node) =>
+      _nodeExits(node.expression);
+
+  @override
+  bool visitBinaryExpression(BinaryExpression node) {
+    Expression lhsExpression = node.leftOperand;
+    sc.TokenType operatorType = node.operator.type;
+    // If the operator is || and the left hand side is false literal, don't
+    // consider the RHS of the binary expression.
+    // TODO(jwren) Do we want to take constant expressions into account,
+    // evaluate if(false) {} differently than if(<condition>), when <condition>
+    // evaluates to a constant false value?
+    if (operatorType == sc.TokenType.BAR_BAR) {
+      if (lhsExpression is BooleanLiteral) {
+        BooleanLiteral booleanLiteral = lhsExpression;
+        if (!booleanLiteral.value) {
+          return false;
+        }
+      }
+    }
+    // If the operator is && and the left hand side is true literal, don't
+    // consider the RHS of the binary expression.
+    if (operatorType == sc.TokenType.AMPERSAND_AMPERSAND) {
+      if (lhsExpression is BooleanLiteral) {
+        BooleanLiteral booleanLiteral = lhsExpression;
+        if (booleanLiteral.value) {
+          return false;
+        }
+      }
+    }
+    Expression rhsExpression = node.rightOperand;
+    return _nodeExits(lhsExpression) || _nodeExits(rhsExpression);
+  }
+
+  @override
+  bool visitBlock(Block node) => _visitStatements(node.statements);
+
+  @override
+  bool visitBlockFunctionBody(BlockFunctionBody node) => _nodeExits(node.block);
+
+  @override
+  bool visitBreakStatement(BreakStatement node) {
+    _enclosingBlockContainsBreak = true;
+    return false;
+  }
+
+  @override
+  bool visitCascadeExpression(CascadeExpression node) =>
+      _nodeExits(node.target) || _visitExpressions(node.cascadeSections);
+
+  @override
+  bool visitConditionalExpression(ConditionalExpression node) {
+    Expression conditionExpression = node.condition;
+    Expression thenStatement = node.thenExpression;
+    Expression elseStatement = node.elseExpression;
+    // TODO(jwren) Do we want to take constant expressions into account,
+    // evaluate if(false) {} differently than if(<condition>), when <condition>
+    // evaluates to a constant false value?
+    if (_nodeExits(conditionExpression)) {
+      return true;
+    }
+    if (thenStatement == null || elseStatement == null) {
+      return false;
+    }
+    return thenStatement.accept(this) && elseStatement.accept(this);
+  }
+
+  @override
+  bool visitContinueStatement(ContinueStatement node) => false;
+
+  @override
+  bool visitDoStatement(DoStatement node) {
+    bool outerBreakValue = _enclosingBlockContainsBreak;
+    _enclosingBlockContainsBreak = false;
+    try {
+      Expression conditionExpression = node.condition;
+      if (_nodeExits(conditionExpression)) {
+        return true;
+      }
+      // TODO(jwren) Do we want to take all constant expressions into account?
+      if (conditionExpression is BooleanLiteral) {
+        BooleanLiteral booleanLiteral = conditionExpression;
+        // If do {} while (true), and the body doesn't return or the body
+        // doesn't have a break, then return true.
+        bool blockReturns = _nodeExits(node.body);
+        if (booleanLiteral.value &&
+            (blockReturns || !_enclosingBlockContainsBreak)) {
+          return true;
+        }
+      }
+      return false;
+    } finally {
+      _enclosingBlockContainsBreak = outerBreakValue;
+    }
+  }
+
+  @override
+  bool visitEmptyStatement(EmptyStatement node) => false;
+
+  @override
+  bool visitExpressionStatement(ExpressionStatement node) =>
+      _nodeExits(node.expression);
+
+  @override
+  bool visitForEachStatement(ForEachStatement node) {
+    bool outerBreakValue = _enclosingBlockContainsBreak;
+    _enclosingBlockContainsBreak = false;
+    try {
+      return _nodeExits(node.iterable);
+    } finally {
+      _enclosingBlockContainsBreak = outerBreakValue;
+    }
+  }
+
+  @override
+  bool visitForStatement(ForStatement node) {
+    bool outerBreakValue = _enclosingBlockContainsBreak;
+    _enclosingBlockContainsBreak = false;
+    try {
+      if (node.variables != null &&
+          _visitVariableDeclarations(node.variables.variables)) {
+        return true;
+      }
+      if (node.initialization != null && _nodeExits(node.initialization)) {
+        return true;
+      }
+      Expression conditionExpression = node.condition;
+      if (conditionExpression != null && _nodeExits(conditionExpression)) {
+        return true;
+      }
+      if (_visitExpressions(node.updaters)) {
+        return true;
+      }
+      // TODO(jwren) Do we want to take all constant expressions into account?
+      // If for(; true; ) (or for(;;)), and the body doesn't return or the body
+      // doesn't have a break, then return true.
+      bool implicitOrExplictTrue = conditionExpression == null ||
+          (conditionExpression is BooleanLiteral && conditionExpression.value);
+      if (implicitOrExplictTrue) {
+        bool blockReturns = _nodeExits(node.body);
+        if (blockReturns || !_enclosingBlockContainsBreak) {
+          return true;
+        }
+      }
+      return false;
+    } finally {
+      _enclosingBlockContainsBreak = outerBreakValue;
+    }
+  }
+
+  @override
+  bool visitFunctionDeclarationStatement(FunctionDeclarationStatement node) =>
+      false;
+
+  @override
+  bool visitFunctionExpression(FunctionExpression node) => false;
+
+  @override
+  bool visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    if (_nodeExits(node.function)) {
+      return true;
+    }
+    return node.argumentList.accept(this);
+  }
+
+  @override
+  bool visitIdentifier(Identifier node) => false;
+
+  @override
+  bool visitIfStatement(IfStatement node) {
+    Expression conditionExpression = node.condition;
+    Statement thenStatement = node.thenStatement;
+    Statement elseStatement = node.elseStatement;
+    if (_nodeExits(conditionExpression)) {
+      return true;
+    }
+    // TODO(jwren) Do we want to take all constant expressions into account?
+    if (conditionExpression is BooleanLiteral) {
+      BooleanLiteral booleanLiteral = conditionExpression;
+      if (booleanLiteral.value) {
+        // if(true) ...
+        return _nodeExits(thenStatement);
+      } else if (elseStatement != null) {
+        // if (false) ...
+        return _nodeExits(elseStatement);
+      }
+    }
+    if (thenStatement == null || elseStatement == null) {
+      return false;
+    }
+    return _nodeExits(thenStatement) && _nodeExits(elseStatement);
+  }
+
+  @override
+  bool visitIndexExpression(IndexExpression node) {
+    Expression target = node.realTarget;
+    if (_nodeExits(target)) {
+      return true;
+    }
+    if (_nodeExits(node.index)) {
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitInstanceCreationExpression(InstanceCreationExpression node) =>
+      _nodeExits(node.argumentList);
+
+  @override
+  bool visitIsExpression(IsExpression node) => node.expression.accept(this);
+
+  @override
+  bool visitLabel(Label node) => false;
+
+  @override
+  bool visitLabeledStatement(LabeledStatement node) =>
+      node.statement.accept(this);
+
+  @override
+  bool visitLiteral(Literal node) => false;
+
+  @override
+  bool visitMethodInvocation(MethodInvocation node) {
+    Expression target = node.realTarget;
+    if (target != null && target.accept(this)) {
+      return true;
+    }
+    return _nodeExits(node.argumentList);
+  }
+
+  @override
+  bool visitNamedExpression(NamedExpression node) =>
+      node.expression.accept(this);
+
+  @override
+  bool visitParenthesizedExpression(ParenthesizedExpression node) =>
+      node.expression.accept(this);
+
+  @override
+  bool visitPostfixExpression(PostfixExpression node) => false;
+
+  @override
+  bool visitPrefixExpression(PrefixExpression node) => false;
+
+  @override
+  bool visitPropertyAccess(PropertyAccess node) {
+    Expression target = node.realTarget;
+    if (target != null && target.accept(this)) {
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitRethrowExpression(RethrowExpression node) => true;
+
+  @override
+  bool visitReturnStatement(ReturnStatement node) => true;
+
+  @override
+  bool visitSuperExpression(SuperExpression node) => false;
+
+  @override
+  bool visitSwitchCase(SwitchCase node) => _visitStatements(node.statements);
+
+  @override
+  bool visitSwitchDefault(SwitchDefault node) =>
+      _visitStatements(node.statements);
+
+  @override
+  bool visitSwitchStatement(SwitchStatement node) {
+    bool outerBreakValue = _enclosingBlockContainsBreak;
+    _enclosingBlockContainsBreak = false;
+    try {
+      bool hasDefault = false;
+      List<SwitchMember> members = node.members;
+      for (int i = 0; i < members.length; i++) {
+        SwitchMember switchMember = members[i];
+        if (switchMember is SwitchDefault) {
+          hasDefault = true;
+          // If this is the last member and there are no statements, return
+          // false
+          if (switchMember.statements.isEmpty && i + 1 == members.length) {
+            return false;
+          }
+        }
+        // For switch members with no statements, don't visit the children,
+        // otherwise, return false if no return is found in the children
+        // statements.
+        if (!switchMember.statements.isEmpty && !switchMember.accept(this)) {
+          return false;
+        }
+      }
+      // All of the members exit, determine whether there are possible cases
+      // that are not caught by the members.
+      DartType type = node.expression == null ? null : node.expression.bestType;
+      if (type is InterfaceType) {
+        ClassElement element = type.element;
+        if (element != null && element.isEnum) {
+          // If some of the enum values are not covered, then a warning will
+          // have already been generated, so there's no point in generating a
+          // hint.
+          return true;
+        }
+      }
+      return hasDefault;
+    } finally {
+      _enclosingBlockContainsBreak = outerBreakValue;
+    }
+  }
+
+  @override
+  bool visitThisExpression(ThisExpression node) => false;
+
+  @override
+  bool visitThrowExpression(ThrowExpression node) => true;
+
+  @override
+  bool visitTryStatement(TryStatement node) {
+    if (_nodeExits(node.body)) {
+      return true;
+    }
+    Block finallyBlock = node.finallyBlock;
+    if (_nodeExits(finallyBlock)) {
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitTypeName(TypeName node) => false;
+
+  @override
+  bool visitVariableDeclaration(VariableDeclaration node) {
+    Expression initializer = node.initializer;
+    if (initializer != null) {
+      return initializer.accept(this);
+    }
+    return false;
+  }
+
+  @override
+  bool visitVariableDeclarationList(VariableDeclarationList node) =>
+      _visitVariableDeclarations(node.variables);
+
+  @override
+  bool visitVariableDeclarationStatement(VariableDeclarationStatement node) {
+    NodeList<VariableDeclaration> variables = node.variables.variables;
+    for (int i = 0; i < variables.length; i++) {
+      if (variables[i].accept(this)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @override
+  bool visitWhileStatement(WhileStatement node) {
+    bool outerBreakValue = _enclosingBlockContainsBreak;
+    _enclosingBlockContainsBreak = false;
+    try {
+      Expression conditionExpression = node.condition;
+      if (conditionExpression.accept(this)) {
+        return true;
+      }
+      // TODO(jwren) Do we want to take all constant expressions into account?
+      if (conditionExpression is BooleanLiteral) {
+        BooleanLiteral booleanLiteral = conditionExpression;
+        // If while(true), and the body doesn't return or the body doesn't have
+        // a break, then return true.
+        bool blockReturns = node.body.accept(this);
+        if (booleanLiteral.value &&
+            (blockReturns || !_enclosingBlockContainsBreak)) {
+          return true;
+        }
+      }
+      return false;
+    } finally {
+      _enclosingBlockContainsBreak = outerBreakValue;
+    }
+  }
+
+  /**
+   * Return `true` if the given node exits.
+   *
+   * @param node the node being tested
+   * @return `true` if the given node exits
+   */
+  bool _nodeExits(AstNode node) {
+    if (node == null) {
+      return false;
+    }
+    return node.accept(this);
+  }
+
+  bool _visitExpressions(NodeList<Expression> expressions) {
+    for (int i = expressions.length - 1; i >= 0; i--) {
+      if (expressions[i].accept(this)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool _visitStatements(NodeList<Statement> statements) {
+    for (int i = statements.length - 1; i >= 0; i--) {
+      if (statements[i].accept(this)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool _visitVariableDeclarations(
+      NodeList<VariableDeclaration> variableDeclarations) {
+    for (int i = variableDeclarations.length - 1; i >= 0; i--) {
+      if (variableDeclarations[i].accept(this)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the given [node] exits.
+   */
+  static bool exits(AstNode node) {
+    return new ExitDetector()._nodeExits(node);
+  }
+}
+
+/**
+ * Instances of the class `FunctionScope` implement the scope defined by a function.
+ */
+class FunctionScope extends EnclosedScope {
+  final ExecutableElement _functionElement;
+
+  bool _parametersDefined = false;
+
+  /**
+   * Initialize a newly created scope enclosed within another scope.
+   *
+   * @param enclosingScope the scope in which this scope is lexically enclosed
+   * @param functionElement the element representing the type represented by this scope
+   */
+  FunctionScope(Scope enclosingScope, this._functionElement)
+      : super(new EnclosedScope(enclosingScope)) {
+    if (_functionElement == null) {
+      throw new IllegalArgumentException("function element cannot be null");
+    }
+  }
+
+  /**
+   * Define the parameters for the given function in the scope that encloses this function.
+   */
+  void defineParameters() {
+    if (_parametersDefined) {
+      return;
+    }
+    _parametersDefined = true;
+    Scope parameterScope = enclosingScope;
+    for (ParameterElement parameter in _functionElement.parameters) {
+      if (!parameter.isInitializingFormal) {
+        parameterScope.define(parameter);
+      }
+    }
+  }
+}
+
+/**
+ * Instances of the class `FunctionTypeScope` implement the scope defined by a function type
+ * alias.
+ */
+class FunctionTypeScope extends EnclosedScope {
+  final FunctionTypeAliasElement _typeElement;
+
+  bool _parametersDefined = false;
+
+  /**
+   * Initialize a newly created scope enclosed within another scope.
+   *
+   * @param enclosingScope the scope in which this scope is lexically enclosed
+   * @param typeElement the element representing the type alias represented by this scope
+   */
+  FunctionTypeScope(Scope enclosingScope, this._typeElement)
+      : super(new EnclosedScope(enclosingScope)) {
+    _defineTypeParameters();
+  }
+
+  /**
+   * Define the parameters for the function type alias.
+   *
+   * @param typeElement the element representing the type represented by this scope
+   */
+  void defineParameters() {
+    if (_parametersDefined) {
+      return;
+    }
+    _parametersDefined = true;
+    for (ParameterElement parameter in _typeElement.parameters) {
+      define(parameter);
+    }
+  }
+
+  /**
+   * Define the type parameters for the function type alias.
+   *
+   * @param typeElement the element representing the type represented by this scope
+   */
+  void _defineTypeParameters() {
+    Scope typeParameterScope = enclosingScope;
+    for (TypeParameterElement typeParameter in _typeElement.typeParameters) {
+      typeParameterScope.define(typeParameter);
+    }
+  }
+}
+
+/**
+ * A visitor that visits ASTs and fills [UsedImportedElements].
+ */
+class GatherUsedImportedElementsVisitor extends RecursiveAstVisitor {
+  final LibraryElement library;
+  final UsedImportedElements usedElements = new UsedImportedElements();
+
+  GatherUsedImportedElementsVisitor(this.library);
+
+  @override
+  void visitExportDirective(ExportDirective node) {
+    _visitMetadata(node.metadata);
+  }
+
+  @override
+  void visitImportDirective(ImportDirective node) {
+    _visitMetadata(node.metadata);
+  }
+
+  @override
+  void visitLibraryDirective(LibraryDirective node) {
+    _visitMetadata(node.metadata);
+  }
+
+  @override
+  void visitPrefixedIdentifier(PrefixedIdentifier node) {
+    // If the prefixed identifier references some A.B, where A is a library
+    // prefix, then we can lookup the associated ImportDirective in
+    // prefixElementMap and remove it from the unusedImports list.
+    SimpleIdentifier prefixIdentifier = node.prefix;
+    Element element = prefixIdentifier.staticElement;
+    if (element is PrefixElement) {
+      usedElements.prefixes.add(element);
+      return;
+    }
+    // Otherwise, pass the prefixed identifier element and name onto
+    // visitIdentifier.
+    _visitIdentifier(element, prefixIdentifier.name);
+  }
+
+  @override
+  void visitSimpleIdentifier(SimpleIdentifier node) {
+    _visitIdentifier(node.staticElement, node.name);
+  }
+
+  void _visitIdentifier(Element element, String name) {
+    if (element == null) {
+      return;
+    }
+    // If the element is multiply defined then call this method recursively for
+    // each of the conflicting elements.
+    if (element is MultiplyDefinedElement) {
+      MultiplyDefinedElement multiplyDefinedElement = element;
+      for (Element elt in multiplyDefinedElement.conflictingElements) {
+        _visitIdentifier(elt, name);
+      }
+      return;
+    } else if (element is PrefixElement) {
+      usedElements.prefixes.add(element);
+      return;
+    } else if (element.enclosingElement is! CompilationUnitElement) {
+      // Identifiers that aren't a prefix element and whose enclosing element
+      // isn't a CompilationUnit are ignored- this covers the case the
+      // identifier is a relative-reference, a reference to an identifier not
+      // imported by this library.
+      return;
+    }
+    // Ignore if an unknown library.
+    LibraryElement containingLibrary = element.library;
+    if (containingLibrary == null) {
+      return;
+    }
+    // Ignore if a local element.
+    if (library == containingLibrary) {
+      return;
+    }
+    // Remember the element.
+    usedElements.elements.add(element);
+  }
+
+  /**
+   * Given some [NodeList] of [Annotation]s, ensure that the identifiers are visited by
+   * this visitor. Specifically, this covers the cases where AST nodes don't have their identifiers
+   * visited by this visitor, but still need their annotations visited.
+   *
+   * @param annotations the list of annotations to visit
+   */
+  void _visitMetadata(NodeList<Annotation> annotations) {
+    int count = annotations.length;
+    for (int i = 0; i < count; i++) {
+      annotations[i].accept(this);
+    }
+  }
+}
+
+/**
+ * An [AstVisitor] that fills [UsedLocalElements].
+ */
+class GatherUsedLocalElementsVisitor extends RecursiveAstVisitor {
+  final UsedLocalElements usedElements = new UsedLocalElements();
+
+  final LibraryElement _enclosingLibrary;
+  ClassElement _enclosingClass;
+  ExecutableElement _enclosingExec;
+
+  GatherUsedLocalElementsVisitor(this._enclosingLibrary);
+
+  @override
+  visitCatchClause(CatchClause node) {
+    SimpleIdentifier exceptionParameter = node.exceptionParameter;
+    SimpleIdentifier stackTraceParameter = node.stackTraceParameter;
+    if (exceptionParameter != null) {
+      Element element = exceptionParameter.staticElement;
+      usedElements.addCatchException(element);
+      if (stackTraceParameter != null || node.onKeyword == null) {
+        usedElements.addElement(element);
+      }
+    }
+    if (stackTraceParameter != null) {
+      Element element = stackTraceParameter.staticElement;
+      usedElements.addCatchStackTrace(element);
+    }
+    super.visitCatchClause(node);
+  }
+
+  @override
+  visitClassDeclaration(ClassDeclaration node) {
+    ClassElement enclosingClassOld = _enclosingClass;
+    try {
+      _enclosingClass = node.element;
+      super.visitClassDeclaration(node);
+    } finally {
+      _enclosingClass = enclosingClassOld;
+    }
+  }
+
+  @override
+  visitFunctionDeclaration(FunctionDeclaration node) {
+    ExecutableElement enclosingExecOld = _enclosingExec;
+    try {
+      _enclosingExec = node.element;
+      super.visitFunctionDeclaration(node);
+    } finally {
+      _enclosingExec = enclosingExecOld;
+    }
+  }
+
+  @override
+  visitFunctionExpression(FunctionExpression node) {
+    if (node.parent is! FunctionDeclaration) {
+      usedElements.addElement(node.element);
+    }
+    super.visitFunctionExpression(node);
+  }
+
+  @override
+  visitMethodDeclaration(MethodDeclaration node) {
+    ExecutableElement enclosingExecOld = _enclosingExec;
+    try {
+      _enclosingExec = node.element;
+      super.visitMethodDeclaration(node);
+    } finally {
+      _enclosingExec = enclosingExecOld;
+    }
+  }
+
+  @override
+  visitSimpleIdentifier(SimpleIdentifier node) {
+    if (node.inDeclarationContext()) {
+      return;
+    }
+    Element element = node.staticElement;
+    bool isIdentifierRead = _isReadIdentifier(node);
+    if (element is LocalVariableElement) {
+      if (isIdentifierRead) {
+        usedElements.addElement(element);
+      }
+    } else {
+      _useIdentifierElement(node);
+      if (element == null ||
+          element.enclosingElement is ClassElement &&
+              !identical(element, _enclosingExec)) {
+        usedElements.members.add(node.name);
+        if (isIdentifierRead) {
+          usedElements.readMembers.add(node.name);
+        }
+      }
+    }
+  }
+
+  /**
+   * Marks an [Element] of [node] as used in the library.
+   */
+  void _useIdentifierElement(Identifier node) {
+    Element element = node.staticElement;
+    if (element == null) {
+      return;
+    }
+    // check if a local element
+    if (!identical(element.library, _enclosingLibrary)) {
+      return;
+    }
+    // ignore references to an element from itself
+    if (identical(element, _enclosingClass)) {
+      return;
+    }
+    if (identical(element, _enclosingExec)) {
+      return;
+    }
+    // ignore places where the element is not actually used
+    if (node.parent is TypeName) {
+      if (element is ClassElement) {
+        AstNode parent2 = node.parent.parent;
+        if (parent2 is IsExpression) {
+          return;
+        }
+        if (parent2 is VariableDeclarationList) {
+          return;
+        }
+      }
+    }
+    // OK
+    usedElements.addElement(element);
+  }
+
+  static bool _isReadIdentifier(SimpleIdentifier node) {
+    // not reading at all
+    if (!node.inGetterContext()) {
+      return false;
+    }
+    // check if useless reading
+    AstNode parent = node.parent;
+    if (parent.parent is ExpressionStatement &&
+        (parent is PrefixExpression ||
+            parent is PostfixExpression ||
+            parent is AssignmentExpression && parent.leftHandSide == node)) {
+      // v++;
+      // ++v;
+      // v += 2;
+      return false;
+    }
+    // OK
+    return true;
+  }
+}
+
+/**
+ * Instances of the class `HintGenerator` traverse a library's worth of dart code at a time to
+ * generate hints over the set of sources.
+ *
+ * See [HintCode].
+ */
+class HintGenerator {
+  final List<CompilationUnit> _compilationUnits;
+
+  final InternalAnalysisContext _context;
+
+  final AnalysisErrorListener _errorListener;
+
+  LibraryElement _library;
+
+  GatherUsedImportedElementsVisitor _usedImportedElementsVisitor;
+
+  bool _enableDart2JSHints = false;
+
+  /**
+   * The inheritance manager used to find overridden methods.
+   */
+  InheritanceManager _manager;
+
+  GatherUsedLocalElementsVisitor _usedLocalElementsVisitor;
+
+  HintGenerator(this._compilationUnits, this._context, this._errorListener) {
+    _library = _compilationUnits[0].element.library;
+    _usedImportedElementsVisitor =
+        new GatherUsedImportedElementsVisitor(_library);
+    _enableDart2JSHints = _context.analysisOptions.dart2jsHint;
+    _manager = new InheritanceManager(_compilationUnits[0].element.library);
+    _usedLocalElementsVisitor = new GatherUsedLocalElementsVisitor(_library);
+  }
+
+  void generateForLibrary() {
+    PerformanceStatistics.hints.makeCurrentWhile(() {
+      for (CompilationUnit unit in _compilationUnits) {
+        CompilationUnitElement element = unit.element;
+        if (element != null) {
+          _generateForCompilationUnit(unit, element.source);
+        }
+      }
+      CompilationUnit definingUnit = _compilationUnits[0];
+      ErrorReporter definingUnitErrorReporter =
+          new ErrorReporter(_errorListener, definingUnit.element.source);
+      {
+        ImportsVerifier importsVerifier = new ImportsVerifier();
+        importsVerifier.addImports(definingUnit);
+        importsVerifier
+            .removeUsedElements(_usedImportedElementsVisitor.usedElements);
+        importsVerifier.generateDuplicateImportHints(definingUnitErrorReporter);
+        importsVerifier.generateUnusedImportHints(definingUnitErrorReporter);
+      }
+      _library.accept(new UnusedLocalElementsVerifier(
+          _errorListener, _usedLocalElementsVisitor.usedElements));
+    });
+  }
+
+  void _generateForCompilationUnit(CompilationUnit unit, Source source) {
+    ErrorReporter errorReporter = new ErrorReporter(_errorListener, source);
+    unit.accept(_usedImportedElementsVisitor);
+    // dead code analysis
+    unit.accept(new DeadCodeVerifier(errorReporter));
+    unit.accept(_usedLocalElementsVisitor);
+    // dart2js analysis
+    if (_enableDart2JSHints) {
+      unit.accept(new Dart2JSVerifier(errorReporter));
+    }
+    // Dart best practices
+    unit.accept(
+        new BestPracticesVerifier(errorReporter, _context.typeProvider));
+    unit.accept(new OverrideVerifier(errorReporter, _manager));
+    // Find to-do comments
+    new ToDoFinder(errorReporter).findIn(unit);
+    // pub analysis
+    // TODO(danrubel/jwren) Commented out until bugs in the pub verifier are
+    // fixed
+    //    unit.accept(new PubVerifier(context, errorReporter));
+  }
+}
+
+/**
+ * Instances of the class {@code HtmlTagInfo} record information about the tags used in an HTML
+ * file.
+ */
+class HtmlTagInfo {
+  /**
+   * An array containing all of the tags used in the HTML file.
+   */
+  List<String> allTags;
+
+  /**
+   * A table mapping the id's defined in the HTML file to an array containing the names of tags with
+   * that identifier.
+   */
+  HashMap<String, String> idToTagMap;
+
+  /**
+   * A table mapping the classes defined in the HTML file to an array containing the names of tags
+   * with that class.
+   */
+  HashMap<String, List<String>> classToTagsMap;
+
+  /**
+   * Initialize a newly created information holder to hold the given information about the tags in
+   * an HTML file.
+   *
+   * @param allTags an array containing all of the tags used in the HTML file
+   * @param idToTagMap a table mapping the id's defined in the HTML file to an array containing the
+   *          names of tags with that identifier
+   * @param classToTagsMap a table mapping the classes defined in the HTML file to an array
+   *          containing the names of tags with that class
+   */
+  HtmlTagInfo(List<String> allTags, HashMap<String, String> idToTagMap,
+      HashMap<String, List<String>> classToTagsMap) {
+    this.allTags = allTags;
+    this.idToTagMap = idToTagMap;
+    this.classToTagsMap = classToTagsMap;
+  }
+
+  /**
+   * Return an array containing the tags that have the given class, or {@code null} if there are no
+   * such tags.
+   *
+   * @return an array containing the tags that have the given class
+   */
+  List<String> getTagsWithClass(String identifier) {
+    return classToTagsMap[identifier];
+  }
+
+  /**
+   * Return the tag that has the given identifier, or {@code null} if there is no such tag (the
+   * identifier is not defined).
+   *
+   * @return the tag that has the given identifier
+   */
+  String getTagWithId(String identifier) {
+    return idToTagMap[identifier];
+  }
+}
+
+/**
+ * Instances of the class {@code HtmlTagInfoBuilder} gather information about the tags used in one
+ * or more HTML structures.
+ */
+class HtmlTagInfoBuilder implements ht.XmlVisitor {
+  /**
+   * The name of the 'id' attribute.
+   */
+  static final String ID_ATTRIBUTE = "id";
+
+  /**
+   * The name of the 'class' attribute.
+   */
+  static final String ID_CLASS = "class";
+
+  /**
+   * A set containing all of the tag names used in the HTML.
+   */
+  HashSet<String> tagSet = new HashSet<String>();
+
+  /**
+   * A table mapping the id's that are defined to the tag name with that id.
+   */
+  HashMap<String, String> idMap = new HashMap<String, String>();
+
+  /**
+   * A table mapping the classes that are defined to a set of the tag names with that class.
+   */
+  HashMap<String, HashSet<String>> classMap =
+      new HashMap<String, HashSet<String>>();
+
+  /**
+   * Initialize a newly created HTML tag info builder.
+   */
+  HtmlTagInfoBuilder();
+
+  /**
+   * Create a tag information holder holding all of the information gathered about the tags in the
+   * HTML structures that were visited.
+   *
+   * @return the information gathered about the tags in the visited HTML structures
+   */
+  HtmlTagInfo getTagInfo() {
+    List<String> allTags = tagSet.toList();
+    HashMap<String, List<String>> classToTagsMap =
+        new HashMap<String, List<String>>();
+    classMap.forEach((String key, Set<String> tags) {
+      classToTagsMap[key] = tags.toList();
+    });
+    return new HtmlTagInfo(allTags, idMap, classToTagsMap);
+  }
+
+  @override
+  visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) {
+    visitXmlTagNode(node);
+  }
+
+  @override
+  visitHtmlUnit(ht.HtmlUnit node) {
+    node.visitChildren(this);
+  }
+
+  @override
+  visitXmlAttributeNode(ht.XmlAttributeNode node) {}
+
+  @override
+  visitXmlTagNode(ht.XmlTagNode node) {
+    node.visitChildren(this);
+    String tagName = node.tag;
+    tagSet.add(tagName);
+    for (ht.XmlAttributeNode attribute in node.attributes) {
+      String attributeName = attribute.name;
+      if (attributeName == ID_ATTRIBUTE) {
+        String attributeValue = attribute.text;
+        if (attributeValue != null) {
+          String tag = idMap[attributeValue];
+          if (tag == null) {
+            idMap[attributeValue] = tagName;
+          } else {
+//            reportError(HtmlWarningCode.MULTIPLY_DEFINED_ID, valueToken);
+          }
+        }
+      } else if (attributeName == ID_CLASS) {
+        String attributeValue = attribute.text;
+        if (attributeValue != null) {
+          HashSet<String> tagList = classMap[attributeValue];
+          if (tagList == null) {
+            tagList = new HashSet<String>();
+            classMap[attributeValue] = tagList;
+          } else {
+//            reportError(HtmlWarningCode.MULTIPLY_DEFINED_ID, valueToken);
+          }
+          tagList.add(tagName);
+        }
+      }
+    }
+  }
+
+//  /**
+//   * Report an error with the given error code at the given location. Use the given arguments to
+//   * compose the error message.
+//   *
+//   * @param errorCode the error code of the error to be reported
+//   * @param offset the offset of the first character to be highlighted
+//   * @param length the number of characters to be highlighted
+//   * @param arguments the arguments used to compose the error message
+//   */
+//  private void reportError(ErrorCode errorCode, Token token, Object... arguments) {
+//    errorListener.onError(new AnalysisError(
+//        htmlElement.getSource(),
+//        token.getOffset(),
+//        token.getLength(),
+//        errorCode,
+//        arguments));
+//  }
+//
+//  /**
+//   * Report an error with the given error code at the given location. Use the given arguments to
+//   * compose the error message.
+//   *
+//   * @param errorCode the error code of the error to be reported
+//   * @param offset the offset of the first character to be highlighted
+//   * @param length the number of characters to be highlighted
+//   * @param arguments the arguments used to compose the error message
+//   */
+//  private void reportError(ErrorCode errorCode, int offset, int length, Object... arguments) {
+//    errorListener.onError(new AnalysisError(
+//        htmlElement.getSource(),
+//        offset,
+//        length,
+//        errorCode,
+//        arguments));
+//  }
+}
+
+/**
+ * Instances of the class `HtmlUnitBuilder` build an element model for a single HTML unit.
+ */
+class HtmlUnitBuilder implements ht.XmlVisitor<Object> {
+  static String _SRC = "src";
+
+  /**
+   * The analysis context in which the element model will be built.
+   */
+  final InternalAnalysisContext _context;
+
+  /**
+   * The error listener to which errors will be reported.
+   */
+  RecordingErrorListener _errorListener;
+
+  /**
+   * The HTML element being built.
+   */
+  HtmlElementImpl _htmlElement;
+
+  /**
+   * The elements in the path from the HTML unit to the current tag node.
+   */
+  List<ht.XmlTagNode> _parentNodes;
+
+  /**
+   * The script elements being built.
+   */
+  List<HtmlScriptElement> _scripts;
+
+  /**
+   * A set of the libraries that were resolved while resolving the HTML unit.
+   */
+  Set<Library> _resolvedLibraries = new HashSet<Library>();
+
+  /**
+   * Initialize a newly created HTML unit builder.
+   *
+   * @param context the analysis context in which the element model will be built
+   */
+  HtmlUnitBuilder(this._context) {
+    this._errorListener = new RecordingErrorListener();
+  }
+
+  /**
+   * Return the listener to which analysis errors will be reported.
+   *
+   * @return the listener to which analysis errors will be reported
+   */
+  RecordingErrorListener get errorListener => _errorListener;
+
+  /**
+   * Return an array containing information about all of the libraries that were resolved.
+   *
+   * @return an array containing the libraries that were resolved
+   */
+  Set<Library> get resolvedLibraries => _resolvedLibraries;
+
+  /**
+   * Build the HTML element for the given source.
+   *
+   * @param source the source describing the compilation unit
+   * @param unit the AST structure representing the HTML
+   * @throws AnalysisException if the analysis could not be performed
+   */
+  HtmlElementImpl buildHtmlElement(Source source, ht.HtmlUnit unit) {
+    HtmlElementImpl result = new HtmlElementImpl(_context, source.shortName);
+    result.source = source;
+    _htmlElement = result;
+    unit.accept(this);
+    _htmlElement = null;
+    unit.element = result;
+    return result;
+  }
+
+  @override
+  Object visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) {
+    if (_parentNodes.contains(node)) {
+      return _reportCircularity(node);
+    }
+    _parentNodes.add(node);
+    try {
+      Source htmlSource = _htmlElement.source;
+      ht.XmlAttributeNode scriptAttribute = _getScriptSourcePath(node);
+      String scriptSourcePath =
+          scriptAttribute == null ? null : scriptAttribute.text;
+      if (node.attributeEnd.type == ht.TokenType.GT &&
+          scriptSourcePath == null) {
+        EmbeddedHtmlScriptElementImpl script =
+            new EmbeddedHtmlScriptElementImpl(node);
+        try {
+          LibraryResolver resolver = new LibraryResolver(_context);
+          LibraryElementImpl library =
+              resolver.resolveEmbeddedLibrary(htmlSource, node.script, true);
+          script.scriptLibrary = library;
+          _resolvedLibraries.addAll(resolver.resolvedLibraries);
+          _errorListener.addAll(resolver.errorListener);
+        } on AnalysisException catch (exception, stackTrace) {
+          //TODO (danrubel): Handle or forward the exception
+          AnalysisEngine.instance.logger.logError(
+              "Could not resolve script tag",
+              new CaughtException(exception, stackTrace));
+        }
+        node.scriptElement = script;
+        _scripts.add(script);
+      } else {
+        ExternalHtmlScriptElementImpl script =
+            new ExternalHtmlScriptElementImpl(node);
+        if (scriptSourcePath != null) {
+          try {
+            scriptSourcePath = Uri.encodeFull(scriptSourcePath);
+            // Force an exception to be thrown if the URI is invalid so that we
+            // can report the problem.
+            parseUriWithException(scriptSourcePath);
+            Source scriptSource =
+                _context.sourceFactory.resolveUri(htmlSource, scriptSourcePath);
+            script.scriptSource = scriptSource;
+            if (!_context.exists(scriptSource)) {
+              _reportValueError(HtmlWarningCode.URI_DOES_NOT_EXIST,
+                  scriptAttribute, [scriptSourcePath]);
+            }
+          } on URISyntaxException {
+            _reportValueError(HtmlWarningCode.INVALID_URI, scriptAttribute,
+                [scriptSourcePath]);
+          }
+        }
+        node.scriptElement = script;
+        _scripts.add(script);
+      }
+    } finally {
+      _parentNodes.remove(node);
+    }
+    return null;
+  }
+
+  @override
+  Object visitHtmlUnit(ht.HtmlUnit node) {
+    _parentNodes = new List<ht.XmlTagNode>();
+    _scripts = new List<HtmlScriptElement>();
+    try {
+      node.visitChildren(this);
+      _htmlElement.scripts = new List.from(_scripts);
+    } finally {
+      _scripts = null;
+      _parentNodes = null;
+    }
+    return null;
+  }
+
+  @override
+  Object visitXmlAttributeNode(ht.XmlAttributeNode node) => null;
+
+  @override
+  Object visitXmlTagNode(ht.XmlTagNode node) {
+    if (_parentNodes.contains(node)) {
+      return _reportCircularity(node);
+    }
+    _parentNodes.add(node);
+    try {
+      node.visitChildren(this);
+    } finally {
+      _parentNodes.remove(node);
+    }
+    return null;
+  }
+
+  /**
+   * Return the first source attribute for the given tag node, or `null` if it does not exist.
+   *
+   * @param node the node containing attributes
+   * @return the source attribute contained in the given tag
+   */
+  ht.XmlAttributeNode _getScriptSourcePath(ht.XmlTagNode node) {
+    for (ht.XmlAttributeNode attribute in node.attributes) {
+      if (attribute.name == _SRC) {
+        return attribute;
+      }
+    }
+    return null;
+  }
+
+  Object _reportCircularity(ht.XmlTagNode node) {
+    //
+    // This should not be possible, but we have an error report that suggests
+    // that it happened at least once. This code will guard against infinite
+    // recursion and might help us identify the cause of the issue.
+    //
+    StringBuffer buffer = new StringBuffer();
+    buffer.write("Found circularity in XML nodes: ");
+    bool first = true;
+    for (ht.XmlTagNode pathNode in _parentNodes) {
+      if (first) {
+        first = false;
+      } else {
+        buffer.write(", ");
+      }
+      String tagName = pathNode.tag;
+      if (identical(pathNode, node)) {
+        buffer.write("*");
+        buffer.write(tagName);
+        buffer.write("*");
+      } else {
+        buffer.write(tagName);
+      }
+    }
+    AnalysisEngine.instance.logger.logError(buffer.toString());
+    return null;
+  }
+
+  /**
+   * Report an error with the given error code at the given location. Use the given arguments to
+   * compose the error message.
+   *
+   * @param errorCode the error code of the error to be reported
+   * @param offset the offset of the first character to be highlighted
+   * @param length the number of characters to be highlighted
+   * @param arguments the arguments used to compose the error message
+   */
+  void _reportErrorForOffset(
+      ErrorCode errorCode, int offset, int length, List<Object> arguments) {
+    _errorListener.onError(new AnalysisError.con2(
+        _htmlElement.source, offset, length, errorCode, arguments));
+  }
+
+  /**
+   * Report an error with the given error code at the location of the value of the given attribute.
+   * Use the given arguments to compose the error message.
+   *
+   * @param errorCode the error code of the error to be reported
+   * @param offset the offset of the first character to be highlighted
+   * @param length the number of characters to be highlighted
+   * @param arguments the arguments used to compose the error message
+   */
+  void _reportValueError(ErrorCode errorCode, ht.XmlAttributeNode attribute,
+      List<Object> arguments) {
+    int offset = attribute.valueToken.offset + 1;
+    int length = attribute.valueToken.length - 2;
+    _reportErrorForOffset(errorCode, offset, length, arguments);
+  }
+}
+
+/**
+ * Instances of the class `ImplicitConstructorBuilder` are used to build
+ * implicit constructors for mixin applications, and to check for errors
+ * related to super constructor calls in class declarations with mixins.
+ *
+ * The visitor methods don't directly build the implicit constructors or check
+ * for errors, since they don't in general visit the classes in the proper
+ * order to do so correctly.  Instead, they pass closures to
+ * ImplicitConstructorBuilderCallback to inform it of the computations to be
+ * done and their ordering dependencies.
+ */
+class ImplicitConstructorBuilder extends SimpleElementVisitor {
+  final AnalysisErrorListener errorListener;
+
+  /**
+   * Callback to receive the computations to be performed.
+   */
+  final ImplicitConstructorBuilderCallback _callback;
+
+  /**
+   * Initialize a newly created visitor to build implicit constructors.
+   *
+   * The visit methods will pass closures to [_callback] to indicate what
+   * computation needs to be performed, and its dependency order.
+   */
+  ImplicitConstructorBuilder(this.errorListener, this._callback);
+
+  @override
+  void visitClassElement(ClassElementImpl classElement) {
+    classElement.mixinErrorsReported = false;
+    if (classElement.isTypedef) {
+      _visitClassTypeAlias(classElement);
+    } else {
+      _visitClassDeclaration(classElement);
+    }
+  }
+
+  @override
+  void visitCompilationUnitElement(CompilationUnitElement element) {
+    element.types.forEach(visitClassElement);
+  }
+
+  @override
+  void visitLibraryElement(LibraryElement element) {
+    element.units.forEach(visitCompilationUnitElement);
+  }
+
+  /**
+   * Create an implicit constructor that is copied from the given constructor, but that is in the
+   * given class.
+   *
+   * @param classType the class in which the implicit constructor is defined
+   * @param explicitConstructor the constructor on which the implicit constructor is modeled
+   * @param parameterTypes the types to be replaced when creating parameters
+   * @param argumentTypes the types with which the parameters are to be replaced
+   * @return the implicit constructor that was created
+   */
+  ConstructorElement _createImplicitContructor(InterfaceType classType,
+      ConstructorElement explicitConstructor, List<DartType> parameterTypes,
+      List<DartType> argumentTypes) {
+    ConstructorElementImpl implicitConstructor =
+        new ConstructorElementImpl(explicitConstructor.name, -1);
+    implicitConstructor.synthetic = true;
+    implicitConstructor.redirectedConstructor = explicitConstructor;
+    implicitConstructor.const2 = explicitConstructor.isConst;
+    implicitConstructor.returnType = classType;
+    List<ParameterElement> explicitParameters = explicitConstructor.parameters;
+    int count = explicitParameters.length;
+    if (count > 0) {
+      List<ParameterElement> implicitParameters =
+          new List<ParameterElement>(count);
+      for (int i = 0; i < count; i++) {
+        ParameterElement explicitParameter = explicitParameters[i];
+        ParameterElementImpl implicitParameter =
+            new ParameterElementImpl(explicitParameter.name, -1);
+        implicitParameter.const3 = explicitParameter.isConst;
+        implicitParameter.final2 = explicitParameter.isFinal;
+        implicitParameter.parameterKind = explicitParameter.parameterKind;
+        implicitParameter.synthetic = true;
+        implicitParameter.type =
+            explicitParameter.type.substitute2(argumentTypes, parameterTypes);
+        implicitParameters[i] = implicitParameter;
+      }
+      implicitConstructor.parameters = implicitParameters;
+    }
+    FunctionTypeImpl type = new FunctionTypeImpl.con1(implicitConstructor);
+    type.typeArguments = classType.typeArguments;
+    implicitConstructor.type = type;
+    return implicitConstructor;
+  }
+
+  /**
+   * Find all the constructors that should be forwarded from the given
+   * [superType], to the class or mixin application [classElement],
+   * and pass information about them to [callback].
+   *
+   * Return true if some constructors were considered.  (A false return value
+   * can only happen if the supeclass is a built-in type, in which case it
+   * can't be used as a mixin anyway).
+   */
+  bool _findForwardedConstructors(ClassElementImpl classElement,
+      InterfaceType superType, void callback(
+          ConstructorElement explicitConstructor, List<DartType> parameterTypes,
+          List<DartType> argumentTypes)) {
+    ClassElement superclassElement = superType.element;
+    List<ConstructorElement> constructors = superclassElement.constructors;
+    int count = constructors.length;
+    if (count == 0) {
+      return false;
+    }
+    List<DartType> parameterTypes =
+        TypeParameterTypeImpl.getTypes(superType.typeParameters);
+    List<DartType> argumentTypes = _getArgumentTypes(superType, parameterTypes);
+    for (int i = 0; i < count; i++) {
+      ConstructorElement explicitConstructor = constructors[i];
+      if (!explicitConstructor.isFactory &&
+          classElement.isSuperConstructorAccessible(explicitConstructor)) {
+        callback(explicitConstructor, parameterTypes, argumentTypes);
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Return a list of argument types that corresponds to the [parameterTypes]
+   * and that are derived from the type arguments of the given [superType].
+   */
+  List<DartType> _getArgumentTypes(
+      InterfaceType superType, List<DartType> parameterTypes) {
+    DynamicTypeImpl dynamic = DynamicTypeImpl.instance;
+    int parameterCount = parameterTypes.length;
+    List<DartType> types = new List<DartType>(parameterCount);
+    if (superType == null) {
+      types = new List<DartType>.filled(parameterCount, dynamic);
+    } else {
+      List<DartType> typeArguments = superType.typeArguments;
+      int argumentCount = math.min(typeArguments.length, parameterCount);
+      for (int i = 0; i < argumentCount; i++) {
+        types[i] = typeArguments[i];
+      }
+      for (int i = argumentCount; i < parameterCount; i++) {
+        types[i] = dynamic;
+      }
+    }
+    return types;
+  }
+
+  void _visitClassDeclaration(ClassElementImpl classElement) {
+    DartType superType = classElement.supertype;
+    if (superType != null && classElement.mixins.isNotEmpty) {
+      // We don't need to build any implicitly constructors for the mixin
+      // application (since there isn't an explicit element for it), but we
+      // need to verify that they _could_ be built.
+      if (superType is! InterfaceType) {
+        TypeProvider typeProvider = classElement.context.typeProvider;
+        superType = typeProvider.objectType;
+      }
+      ClassElement superElement = superType.element;
+      if (superElement != null) {
+        _callback(classElement, superElement, () {
+          bool constructorFound = false;
+          void callback(ConstructorElement explicitConstructor,
+              List<DartType> parameterTypes, List<DartType> argumentTypes) {
+            constructorFound = true;
+          }
+          if (_findForwardedConstructors(classElement, superType, callback) &&
+              !constructorFound) {
+            SourceRange withRange = classElement.withClauseRange;
+            errorListener.onError(new AnalysisError.con2(classElement.source,
+                withRange.offset, withRange.length,
+                CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS,
+                [superElement.name]));
+            classElement.mixinErrorsReported = true;
+          }
+        });
+      }
+    }
+  }
+
+  void _visitClassTypeAlias(ClassElementImpl classElement) {
+    InterfaceType superType = classElement.supertype;
+    if (superType is InterfaceType) {
+      ClassElement superElement = superType.element;
+      _callback(classElement, superElement, () {
+        List<ConstructorElement> implicitConstructors =
+            new List<ConstructorElement>();
+        void callback(ConstructorElement explicitConstructor,
+            List<DartType> parameterTypes, List<DartType> argumentTypes) {
+          implicitConstructors.add(_createImplicitContructor(classElement.type,
+              explicitConstructor, parameterTypes, argumentTypes));
+        }
+        if (_findForwardedConstructors(classElement, superType, callback)) {
+          if (implicitConstructors.isEmpty) {
+            errorListener.onError(new AnalysisError.con2(classElement.source,
+                classElement.nameOffset, classElement.name.length,
+                CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS,
+                [superElement.name]));
+          } else {
+            classElement.constructors = implicitConstructors;
+          }
+        }
+      });
+    }
+  }
+}
+
+/**
+ * An instance of this class is capable of running ImplicitConstructorBuilder
+ * over all classes in a library cycle.
+ */
+class ImplicitConstructorComputer {
+  /**
+   * Directed graph of dependencies between classes that need to have their
+   * implicit constructors computed.  Each edge in the graph points from a
+   * derived class to its superclass.  Implicit constructors will be computed
+   * for the superclass before they are compute for the derived class.
+   */
+  DirectedGraph<ClassElement> _dependencies = new DirectedGraph<ClassElement>();
+
+  /**
+   * Map from ClassElement to the function which will compute the class's
+   * implicit constructors.
+   */
+  Map<ClassElement, VoidFunction> _computations =
+      new HashMap<ClassElement, VoidFunction>();
+
+  /**
+   * Add the given [libraryElement] to the list of libraries which need to have
+   * implicit constructors built for them.
+   */
+  void add(AnalysisErrorListener errorListener, LibraryElement libraryElement) {
+    libraryElement
+        .accept(new ImplicitConstructorBuilder(errorListener, _defer));
+  }
+
+  /**
+   * Compute the implicit constructors for all compilation units that have been
+   * passed to [add].
+   */
+  void compute() {
+    List<List<ClassElement>> topologicalSort =
+        _dependencies.computeTopologicalSort();
+    for (List<ClassElement> classesInCycle in topologicalSort) {
+      // Note: a cycle could occur if there is a loop in the inheritance graph.
+      // Such loops are forbidden by Dart but could occur in the analysis of
+      // incorrect code.  If this happens, we simply visit the classes
+      // constituting the loop in any order.
+      for (ClassElement classElement in classesInCycle) {
+        VoidFunction computation = _computations[classElement];
+        if (computation != null) {
+          computation();
+        }
+      }
+    }
+  }
+
+  /**
+   * Defer execution of [computation], which builds implicit constructors for
+   * [classElement], until after implicit constructors have been built for
+   * [superclassElement].
+   */
+  void _defer(ClassElement classElement, ClassElement superclassElement,
+      void computation()) {
+    assert(!_computations.containsKey(classElement));
+    _computations[classElement] = computation;
+    _dependencies.addEdge(classElement, superclassElement);
+  }
+}
+
+/**
+ * Instances of the class `ImplicitLabelScope` represent the scope statements
+ * that can be the target of unlabeled break and continue statements.
+ */
+class ImplicitLabelScope {
+  /**
+   * The implicit label scope associated with the top level of a function.
+   */
+  static const ImplicitLabelScope ROOT = const ImplicitLabelScope._(null, null);
+
+  /**
+   * The implicit label scope enclosing this implicit label scope.
+   */
+  final ImplicitLabelScope outerScope;
+
+  /**
+   * The statement that acts as a target for break and/or continue statements
+   * at this scoping level.
+   */
+  final Statement statement;
+
+  /**
+   * Private constructor.
+   */
+  const ImplicitLabelScope._(this.outerScope, this.statement);
+
+  /**
+   * Get the statement which should be the target of an unlabeled `break` or
+   * `continue` statement, or `null` if there is no appropriate target.
+   */
+  Statement getTarget(bool isContinue) {
+    if (outerScope == null) {
+      // This scope represents the toplevel of a function body, so it doesn't
+      // match either break or continue.
+      return null;
+    }
+    if (isContinue && statement is SwitchStatement) {
+      return outerScope.getTarget(isContinue);
+    }
+    return statement;
+  }
+
+  /**
+   * Initialize a newly created scope to represent a switch statement or loop
+   * nested within the current scope.  [statement] is the statement associated
+   * with the newly created scope.
+   */
+  ImplicitLabelScope nest(Statement statement) =>
+      new ImplicitLabelScope._(this, statement);
+}
+
+/**
+ * Instances of the class `ImportsVerifier` visit all of the referenced libraries in the
+ * source code verifying that all of the imports are used, otherwise a
+ * [HintCode.UNUSED_IMPORT] is generated with
+ * [generateUnusedImportHints].
+ *
+ * While this class does not yet have support for an "Organize Imports" action, this logic built up
+ * in this class could be used for such an action in the future.
+ */
+class ImportsVerifier /*extends RecursiveAstVisitor<Object>*/ {
+  /**
+   * A list of [ImportDirective]s that the current library imports, as identifiers are visited
+   * by this visitor and an import has been identified as being used by the library, the
+   * [ImportDirective] is removed from this list. After all the sources in the library have
+   * been evaluated, this list represents the set of unused imports.
+   *
+   * See [ImportsVerifier.generateUnusedImportErrors].
+   */
+  final List<ImportDirective> _unusedImports = <ImportDirective>[];
+
+  /**
+   * After the list of [unusedImports] has been computed, this list is a proper subset of the
+   * unused imports that are listed more than once.
+   */
+  final List<ImportDirective> _duplicateImports = <ImportDirective>[];
+
+  /**
+   * This is a map between the set of [LibraryElement]s that the current library imports, and
+   * a list of [ImportDirective]s that imports the library. In cases where the current library
+   * imports a library with a single directive (such as `import lib1.dart;`), the library
+   * element will map to a list of one [ImportDirective], which will then be removed from the
+   * [unusedImports] list. In cases where the current library imports a library with multiple
+   * directives (such as `import lib1.dart; import lib1.dart show C;`), the
+   * [LibraryElement] will be mapped to a list of the import directives, and the namespace
+   * will need to be used to compute the correct [ImportDirective] being used, see
+   * [namespaceMap].
+   */
+  final HashMap<LibraryElement, List<ImportDirective>> _libraryMap =
+      new HashMap<LibraryElement, List<ImportDirective>>();
+
+  /**
+   * In cases where there is more than one import directive per library element, this mapping is
+   * used to determine which of the multiple import directives are used by generating a
+   * [Namespace] for each of the imports to do lookups in the same way that they are done from
+   * the [ElementResolver].
+   */
+  final HashMap<ImportDirective, Namespace> _namespaceMap =
+      new HashMap<ImportDirective, Namespace>();
+
+  /**
+   * This is a map between prefix elements and the import directives from which they are derived. In
+   * cases where a type is referenced via a prefix element, the import directive can be marked as
+   * used (removed from the unusedImports) by looking at the resolved `lib` in `lib.X`,
+   * instead of looking at which library the `lib.X` resolves.
+   *
+   * TODO (jwren) Since multiple [ImportDirective]s can share the same [PrefixElement],
+   * it is possible to have an unreported unused import in situations where two imports use the same
+   * prefix and at least one import directive is used.
+   */
+  final HashMap<PrefixElement, List<ImportDirective>> _prefixElementMap =
+      new HashMap<PrefixElement, List<ImportDirective>>();
+
+  void addImports(CompilationUnit node) {
+    for (Directive directive in node.directives) {
+      if (directive is ImportDirective) {
+        ImportDirective importDirective = directive;
+        LibraryElement libraryElement = importDirective.uriElement;
+        if (libraryElement != null) {
+          _unusedImports.add(importDirective);
+          //
+          // Initialize prefixElementMap
+          //
+          if (importDirective.asKeyword != null) {
+            SimpleIdentifier prefixIdentifier = importDirective.prefix;
+            if (prefixIdentifier != null) {
+              Element element = prefixIdentifier.staticElement;
+              if (element is PrefixElement) {
+                PrefixElement prefixElementKey = element;
+                List<ImportDirective> list =
+                    _prefixElementMap[prefixElementKey];
+                if (list == null) {
+                  list = new List<ImportDirective>();
+                  _prefixElementMap[prefixElementKey] = list;
+                }
+                list.add(importDirective);
+              }
+              // TODO (jwren) Can the element ever not be a PrefixElement?
+            }
+          }
+          //
+          // Initialize libraryMap: libraryElement -> importDirective
+          //
+          _putIntoLibraryMap(libraryElement, importDirective);
+          //
+          // For this new addition to the libraryMap, also recursively add any
+          // exports from the libraryElement.
+          //
+          _addAdditionalLibrariesForExports(
+              libraryElement, importDirective, new List<LibraryElement>());
+        }
+      }
+    }
+    if (_unusedImports.length > 1) {
+      // order the list of unusedImports to find duplicates in faster than
+      // O(n^2) time
+      List<ImportDirective> importDirectiveArray =
+          new List<ImportDirective>.from(_unusedImports);
+      importDirectiveArray.sort(ImportDirective.COMPARATOR);
+      ImportDirective currentDirective = importDirectiveArray[0];
+      for (int i = 1; i < importDirectiveArray.length; i++) {
+        ImportDirective nextDirective = importDirectiveArray[i];
+        if (ImportDirective.COMPARATOR(currentDirective, nextDirective) == 0) {
+          // Add either the currentDirective or nextDirective depending on which
+          // comes second, this guarantees that the first of the duplicates
+          // won't be highlighted.
+          if (currentDirective.offset < nextDirective.offset) {
+            _duplicateImports.add(nextDirective);
+          } else {
+            _duplicateImports.add(currentDirective);
+          }
+        }
+        currentDirective = nextDirective;
+      }
+    }
+  }
+
+  /**
+   * Any time after the defining compilation unit has been visited by this visitor, this method can
+   * be called to report an [HintCode.DUPLICATE_IMPORT] hint for each of the import directives
+   * in the [duplicateImports] list.
+   *
+   * @param errorReporter the error reporter to report the set of [HintCode.DUPLICATE_IMPORT]
+   *          hints to
+   */
+  void generateDuplicateImportHints(ErrorReporter errorReporter) {
+    for (ImportDirective duplicateImport in _duplicateImports) {
+      errorReporter.reportErrorForNode(
+          HintCode.DUPLICATE_IMPORT, duplicateImport.uri);
+    }
+  }
+
+  /**
+   * After all of the compilation units have been visited by this visitor, this method can be called
+   * to report an [HintCode.UNUSED_IMPORT] hint for each of the import directives in the
+   * [unusedImports] list.
+   *
+   * @param errorReporter the error reporter to report the set of [HintCode.UNUSED_IMPORT]
+   *          hints to
+   */
+  void generateUnusedImportHints(ErrorReporter errorReporter) {
+    for (ImportDirective unusedImport in _unusedImports) {
+      // Check that the import isn't dart:core
+      ImportElement importElement = unusedImport.element;
+      if (importElement != null) {
+        LibraryElement libraryElement = importElement.importedLibrary;
+        if (libraryElement != null && libraryElement.isDartCore) {
+          continue;
+        }
+      }
+      errorReporter.reportErrorForNode(
+          HintCode.UNUSED_IMPORT, unusedImport.uri);
+    }
+  }
+
+  /**
+   * Remove elements from [_unusedImports] using the given [usedElements].
+   */
+  void removeUsedElements(UsedImportedElements usedElements) {
+    // Stop if all the imports are known to be used.
+    if (_unusedImports.isEmpty) {
+      return;
+    }
+    // Process import prefixes.
+    for (PrefixElement prefix in usedElements.prefixes) {
+      List<ImportDirective> importDirectives = _prefixElementMap[prefix];
+      if (importDirectives != null) {
+        for (ImportDirective importDirective in importDirectives) {
+          _unusedImports.remove(importDirective);
+        }
+      }
+    }
+    // Process top-level elements.
+    for (Element element in usedElements.elements) {
+      // Stop if all the imports are known to be used.
+      if (_unusedImports.isEmpty) {
+        return;
+      }
+      // Prepare import directives for this library.
+      LibraryElement library = element.library;
+      List<ImportDirective> importsLibrary = _libraryMap[library];
+      if (importsLibrary == null) {
+        continue;
+      }
+      // If there is only one import directive for this library, then it must be
+      // the directive that this element is imported with, remove it from the
+      // unusedImports list.
+      if (importsLibrary.length == 1) {
+        ImportDirective usedImportDirective = importsLibrary[0];
+        _unusedImports.remove(usedImportDirective);
+        continue;
+      }
+      // Otherwise, find import directives using namespaces.
+      String name = element.displayName;
+      for (ImportDirective importDirective in importsLibrary) {
+        Namespace namespace = _computeNamespace(importDirective);
+        if (namespace != null && namespace.get(name) != null) {
+          _unusedImports.remove(importDirective);
+        }
+      }
+    }
+  }
+
+  /**
+   * Recursively add any exported library elements into the [libraryMap].
+   */
+  void _addAdditionalLibrariesForExports(LibraryElement library,
+      ImportDirective importDirective, List<LibraryElement> exportPath) {
+    if (exportPath.contains(library)) {
+      return;
+    }
+    exportPath.add(library);
+    for (LibraryElement exportedLibraryElt in library.exportedLibraries) {
+      _putIntoLibraryMap(exportedLibraryElt, importDirective);
+      _addAdditionalLibrariesForExports(
+          exportedLibraryElt, importDirective, exportPath);
+    }
+  }
+
+  /**
+   * Lookup and return the [Namespace] from the [namespaceMap], if the map does not
+   * have the computed namespace, compute it and cache it in the map. If the import directive is not
+   * resolved or is not resolvable, `null` is returned.
+   *
+   * @param importDirective the import directive used to compute the returned namespace
+   * @return the computed or looked up [Namespace]
+   */
+  Namespace _computeNamespace(ImportDirective importDirective) {
+    Namespace namespace = _namespaceMap[importDirective];
+    if (namespace == null) {
+      // If the namespace isn't in the namespaceMap, then compute and put it in
+      // the map.
+      ImportElement importElement = importDirective.element;
+      if (importElement != null) {
+        NamespaceBuilder builder = new NamespaceBuilder();
+        namespace = builder.createImportNamespaceForDirective(importElement);
+        _namespaceMap[importDirective] = namespace;
+      }
+    }
+    return namespace;
+  }
+
+  /**
+   * The [libraryMap] is a mapping between a library elements and a list of import
+   * directives, but when adding these mappings into the [libraryMap], this method can be
+   * used to simply add the mapping between the library element an an import directive without
+   * needing to check to see if a list needs to be created.
+   */
+  void _putIntoLibraryMap(
+      LibraryElement libraryElement, ImportDirective importDirective) {
+    List<ImportDirective> importList = _libraryMap[libraryElement];
+    if (importList == null) {
+      importList = new List<ImportDirective>();
+      _libraryMap[libraryElement] = importList;
+    }
+    importList.add(importDirective);
+  }
+}
+
+/**
+ * Instances of the class `InheritanceManager` manage the knowledge of where class members
+ * (methods, getters & setters) are inherited from.
+ */
+class InheritanceManager {
+  /**
+   * The [LibraryElement] that is managed by this manager.
+   */
+  LibraryElement _library;
+
+  /**
+   * This is a mapping between each [ClassElement] and a map between the [String] member
+   * names and the associated [ExecutableElement] in the mixin and superclass chain.
+   */
+  HashMap<ClassElement, MemberMap> _classLookup;
+
+  /**
+   * This is a mapping between each [ClassElement] and a map between the [String] member
+   * names and the associated [ExecutableElement] in the interface set.
+   */
+  HashMap<ClassElement, MemberMap> _interfaceLookup;
+
+  /**
+   * A map between each visited [ClassElement] and the set of [AnalysisError]s found on
+   * the class element.
+   */
+  HashMap<ClassElement, HashSet<AnalysisError>> _errorsInClassElement =
+      new HashMap<ClassElement, HashSet<AnalysisError>>();
+
+  /**
+   * Initialize a newly created inheritance manager.
+   *
+   * @param library the library element context that the inheritance mappings are being generated
+   */
+  InheritanceManager(LibraryElement library) {
+    this._library = library;
+    _classLookup = new HashMap<ClassElement, MemberMap>();
+    _interfaceLookup = new HashMap<ClassElement, MemberMap>();
+  }
+
+  /**
+   * Set the new library element context.
+   *
+   * @param library the new library element
+   */
+  void set libraryElement(LibraryElement library) {
+    this._library = library;
+  }
+
+  /**
+   * Return the set of [AnalysisError]s found on the passed [ClassElement], or
+   * `null` if there are none.
+   *
+   * @param classElt the class element to query
+   * @return the set of [AnalysisError]s found on the passed [ClassElement], or
+   *         `null` if there are none
+   */
+  HashSet<AnalysisError> getErrors(ClassElement classElt) =>
+      _errorsInClassElement[classElt];
+
+  /**
+   * Get and return a mapping between the set of all string names of the members inherited from the
+   * passed [ClassElement] superclass hierarchy, and the associated [ExecutableElement].
+   *
+   * @param classElt the class element to query
+   * @return a mapping between the set of all members inherited from the passed [ClassElement]
+   *         superclass hierarchy, and the associated [ExecutableElement]
+   */
+  MemberMap getMapOfMembersInheritedFromClasses(ClassElement classElt) =>
+      _computeClassChainLookupMap(classElt, new HashSet<ClassElement>());
+
+  /**
+   * Get and return a mapping between the set of all string names of the members inherited from the
+   * passed [ClassElement] interface hierarchy, and the associated [ExecutableElement].
+   *
+   * @param classElt the class element to query
+   * @return a mapping between the set of all string names of the members inherited from the passed
+   *         [ClassElement] interface hierarchy, and the associated [ExecutableElement].
+   */
+  MemberMap getMapOfMembersInheritedFromInterfaces(ClassElement classElt) =>
+      _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>());
+
+  /**
+   * Given some [ClassElement] and some member name, this returns the
+   * [ExecutableElement] that the class inherits from the mixins,
+   * superclasses or interfaces, that has the member name, if no member is inherited `null` is
+   * returned.
+   *
+   * @param classElt the class element to query
+   * @param memberName the name of the executable element to find and return
+   * @return the inherited executable element with the member name, or `null` if no such
+   *         member exists
+   */
+  ExecutableElement lookupInheritance(
+      ClassElement classElt, String memberName) {
+    if (memberName == null || memberName.isEmpty) {
+      return null;
+    }
+    ExecutableElement executable = _computeClassChainLookupMap(
+        classElt, new HashSet<ClassElement>()).get(memberName);
+    if (executable == null) {
+      return _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>())
+          .get(memberName);
+    }
+    return executable;
+  }
+
+  /**
+   * Given some [ClassElement] and some member name, this returns the
+   * [ExecutableElement] that the class either declares itself, or
+   * inherits, that has the member name, if no member is inherited `null` is returned.
+   *
+   * @param classElt the class element to query
+   * @param memberName the name of the executable element to find and return
+   * @return the inherited executable element with the member name, or `null` if no such
+   *         member exists
+   */
+  ExecutableElement lookupMember(ClassElement classElt, String memberName) {
+    ExecutableElement element = _lookupMemberInClass(classElt, memberName);
+    if (element != null) {
+      return element;
+    }
+    return lookupInheritance(classElt, memberName);
+  }
+
+  /**
+   * Given some [InterfaceType] and some member name, this returns the
+   * [FunctionType] of the [ExecutableElement] that the
+   * class either declares itself, or inherits, that has the member name, if no member is inherited
+   * `null` is returned. The returned [FunctionType] has all type
+   * parameters substituted with corresponding type arguments from the given [InterfaceType].
+   *
+   * @param interfaceType the interface type to query
+   * @param memberName the name of the executable element to find and return
+   * @return the member's function type, or `null` if no such member exists
+   */
+  FunctionType lookupMemberType(
+      InterfaceType interfaceType, String memberName) {
+    ExecutableElement iteratorMember =
+        lookupMember(interfaceType.element, memberName);
+    if (iteratorMember == null) {
+      return null;
+    }
+    return substituteTypeArgumentsInMemberFromInheritance(
+        iteratorMember.type, memberName, interfaceType);
+  }
+
+  /**
+   * Determine the set of methods which is overridden by the given class member. If no member is
+   * inherited, an empty list is returned. If one of the inherited members is a
+   * [MultiplyInheritedExecutableElement], then it is expanded into its constituent inherited
+   * elements.
+   *
+   * @param classElt the class to query
+   * @param memberName the name of the class member to query
+   * @return a list of overridden methods
+   */
+  List<ExecutableElement> lookupOverrides(
+      ClassElement classElt, String memberName) {
+    List<ExecutableElement> result = new List<ExecutableElement>();
+    if (memberName == null || memberName.isEmpty) {
+      return result;
+    }
+    List<MemberMap> interfaceMaps =
+        _gatherInterfaceLookupMaps(classElt, new HashSet<ClassElement>());
+    if (interfaceMaps != null) {
+      for (MemberMap interfaceMap in interfaceMaps) {
+        ExecutableElement overriddenElement = interfaceMap.get(memberName);
+        if (overriddenElement != null) {
+          if (overriddenElement is MultiplyInheritedExecutableElement) {
+            MultiplyInheritedExecutableElement multiplyInheritedElement =
+                overriddenElement;
+            for (ExecutableElement element
+                in multiplyInheritedElement.inheritedElements) {
+              result.add(element);
+            }
+          } else {
+            result.add(overriddenElement);
+          }
+        }
+      }
+    }
+    return result;
+  }
+
+  /**
+   * This method takes some inherited [FunctionType], and resolves all the parameterized types
+   * in the function type, dependent on the class in which it is being overridden.
+   *
+   * @param baseFunctionType the function type that is being overridden
+   * @param memberName the name of the member, this is used to lookup the inheritance path of the
+   *          override
+   * @param definingType the type that is overriding the member
+   * @return the passed function type with any parameterized types substituted
+   */
+  FunctionType substituteTypeArgumentsInMemberFromInheritance(
+      FunctionType baseFunctionType, String memberName,
+      InterfaceType definingType) {
+    // if the baseFunctionType is null, or does not have any parameters,
+    // return it.
+    if (baseFunctionType == null ||
+        baseFunctionType.typeArguments.length == 0) {
+      return baseFunctionType;
+    }
+    // First, generate the path from the defining type to the overridden member
+    Queue<InterfaceType> inheritancePath = new Queue<InterfaceType>();
+    _computeInheritancePath(inheritancePath, definingType, memberName);
+    if (inheritancePath == null || inheritancePath.isEmpty) {
+      // TODO(jwren) log analysis engine error
+      return baseFunctionType;
+    }
+    FunctionType functionTypeToReturn = baseFunctionType;
+    // loop backward through the list substituting as we go:
+    while (!inheritancePath.isEmpty) {
+      InterfaceType lastType = inheritancePath.removeLast();
+      List<DartType> parameterTypes = lastType.element.type.typeArguments;
+      List<DartType> argumentTypes = lastType.typeArguments;
+      functionTypeToReturn =
+          functionTypeToReturn.substitute2(argumentTypes, parameterTypes);
+    }
+    return functionTypeToReturn;
+  }
+
+  /**
+   * Compute and return a mapping between the set of all string names of the members inherited from
+   * the passed [ClassElement] superclass hierarchy, and the associated
+   * [ExecutableElement].
+   *
+   * @param classElt the class element to query
+   * @param visitedClasses a set of visited classes passed back into this method when it calls
+   *          itself recursively
+   * @return a mapping between the set of all string names of the members inherited from the passed
+   *         [ClassElement] superclass hierarchy, and the associated [ExecutableElement]
+   */
+  MemberMap _computeClassChainLookupMap(
+      ClassElement classElt, HashSet<ClassElement> visitedClasses) {
+    MemberMap resultMap = _classLookup[classElt];
+    if (resultMap != null) {
+      return resultMap;
+    } else {
+      resultMap = new MemberMap();
+    }
+    ClassElement superclassElt = null;
+    InterfaceType supertype = classElt.supertype;
+    if (supertype != null) {
+      superclassElt = supertype.element;
+    } else {
+      // classElt is Object
+      _classLookup[classElt] = resultMap;
+      return resultMap;
+    }
+    if (superclassElt != null) {
+      if (!visitedClasses.contains(superclassElt)) {
+        visitedClasses.add(superclassElt);
+        try {
+          resultMap = new MemberMap.con2(
+              _computeClassChainLookupMap(superclassElt, visitedClasses));
+          //
+          // Substitute the super types down the hierarchy.
+          //
+          _substituteTypeParametersDownHierarchy(supertype, resultMap);
+          //
+          // Include the members from the superclass in the resultMap.
+          //
+          _recordMapWithClassMembers(resultMap, supertype, false);
+        } finally {
+          visitedClasses.remove(superclassElt);
+        }
+      } else {
+        // This case happens only when the superclass was previously visited and
+        // not in the lookup, meaning this is meant to shorten the compute for
+        // recursive cases.
+        _classLookup[superclassElt] = resultMap;
+        return resultMap;
+      }
+    }
+    //
+    // Include the members from the mixins in the resultMap.  If there are
+    // multiple mixins, visit them in the order listed so that methods in later
+    // mixins will overwrite identically-named methods in earlier mixins.
+    //
+    List<InterfaceType> mixins = classElt.mixins;
+    for (InterfaceType mixin in mixins) {
+      ClassElement mixinElement = mixin.element;
+      if (mixinElement != null) {
+        if (!visitedClasses.contains(mixinElement)) {
+          visitedClasses.add(mixinElement);
+          try {
+            MemberMap map = new MemberMap.con2(
+                _computeClassChainLookupMap(mixinElement, visitedClasses));
+            //
+            // Substitute the super types down the hierarchy.
+            //
+            _substituteTypeParametersDownHierarchy(mixin, map);
+            //
+            // Include the members from the superclass in the resultMap.
+            //
+            _recordMapWithClassMembers(map, mixin, false);
+            //
+            // Add the members from map into result map.
+            //
+            for (int j = 0; j < map.size; j++) {
+              String key = map.getKey(j);
+              ExecutableElement value = map.getValue(j);
+              if (key != null) {
+                if (resultMap.get(key) == null ||
+                    (resultMap.get(key) != null && !_isAbstract(value))) {
+                  resultMap.put(key, value);
+                }
+              }
+            }
+          } finally {
+            visitedClasses.remove(mixinElement);
+          }
+        } else {
+          // This case happens only when the superclass was previously visited
+          // and not in the lookup, meaning this is meant to shorten the compute
+          // for recursive cases.
+          _classLookup[mixinElement] = resultMap;
+          return resultMap;
+        }
+      }
+    }
+    _classLookup[classElt] = resultMap;
+    return resultMap;
+  }
+
+  /**
+   * Compute and return the inheritance path given the context of a type and a member that is
+   * overridden in the inheritance path (for which the type is in the path).
+   *
+   * @param chain the inheritance path that is built up as this method calls itself recursively,
+   *          when this method is called an empty [LinkedList] should be provided
+   * @param currentType the current type in the inheritance path
+   * @param memberName the name of the member that is being looked up the inheritance path
+   */
+  void _computeInheritancePath(Queue<InterfaceType> chain,
+      InterfaceType currentType, String memberName) {
+    // TODO (jwren) create a public version of this method which doesn't require
+    // the initial chain to be provided, then provided tests for this
+    // functionality in InheritanceManagerTest
+    chain.add(currentType);
+    ClassElement classElt = currentType.element;
+    InterfaceType supertype = classElt.supertype;
+    // Base case- reached Object
+    if (supertype == null) {
+      // Looked up the chain all the way to Object, return null.
+      // This should never happen.
+      return;
+    }
+    // If we are done, return the chain
+    // We are not done if this is the first recursive call on this method.
+    if (chain.length != 1) {
+      // We are done however if the member is in this classElt
+      if (_lookupMemberInClass(classElt, memberName) != null) {
+        return;
+      }
+    }
+    // Mixins- note that mixins call lookupMemberInClass, not lookupMember
+    List<InterfaceType> mixins = classElt.mixins;
+    for (int i = mixins.length - 1; i >= 0; i--) {
+      ClassElement mixinElement = mixins[i].element;
+      if (mixinElement != null) {
+        ExecutableElement elt = _lookupMemberInClass(mixinElement, memberName);
+        if (elt != null) {
+          // this is equivalent (but faster than) calling this method
+          // recursively
+          // (return computeInheritancePath(chain, mixins[i], memberName);)
+          chain.add(mixins[i]);
+          return;
+        }
+      }
+    }
+    // Superclass
+    ClassElement superclassElt = supertype.element;
+    if (lookupMember(superclassElt, memberName) != null) {
+      _computeInheritancePath(chain, supertype, memberName);
+      return;
+    }
+    // Interfaces
+    List<InterfaceType> interfaces = classElt.interfaces;
+    for (InterfaceType interfaceType in interfaces) {
+      ClassElement interfaceElement = interfaceType.element;
+      if (interfaceElement != null &&
+          lookupMember(interfaceElement, memberName) != null) {
+        _computeInheritancePath(chain, interfaceType, memberName);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Compute and return a mapping between the set of all string names of the members inherited from
+   * the passed [ClassElement] interface hierarchy, and the associated
+   * [ExecutableElement].
+   *
+   * @param classElt the class element to query
+   * @param visitedInterfaces a set of visited classes passed back into this method when it calls
+   *          itself recursively
+   * @return a mapping between the set of all string names of the members inherited from the passed
+   *         [ClassElement] interface hierarchy, and the associated [ExecutableElement]
+   */
+  MemberMap _computeInterfaceLookupMap(
+      ClassElement classElt, HashSet<ClassElement> visitedInterfaces) {
+    MemberMap resultMap = _interfaceLookup[classElt];
+    if (resultMap != null) {
+      return resultMap;
+    }
+    List<MemberMap> lookupMaps =
+        _gatherInterfaceLookupMaps(classElt, visitedInterfaces);
+    if (lookupMaps == null) {
+      resultMap = new MemberMap();
+    } else {
+      HashMap<String, List<ExecutableElement>> unionMap =
+          _unionInterfaceLookupMaps(lookupMaps);
+      resultMap = _resolveInheritanceLookup(classElt, unionMap);
+    }
+    _interfaceLookup[classElt] = resultMap;
+    return resultMap;
+  }
+
+  /**
+   * Collect a list of interface lookup maps whose elements correspond to all of the classes
+   * directly above [classElt] in the class hierarchy (the direct superclass if any, all
+   * mixins, and all direct superinterfaces). Each item in the list is the interface lookup map
+   * returned by [computeInterfaceLookupMap] for the corresponding super, except with type
+   * parameters appropriately substituted.
+   *
+   * @param classElt the class element to query
+   * @param visitedInterfaces a set of visited classes passed back into this method when it calls
+   *          itself recursively
+   * @return `null` if there was a problem (such as a loop in the class hierarchy) or if there
+   *         are no classes above this one in the class hierarchy. Otherwise, a list of interface
+   *         lookup maps.
+   */
+  List<MemberMap> _gatherInterfaceLookupMaps(
+      ClassElement classElt, HashSet<ClassElement> visitedInterfaces) {
+    InterfaceType supertype = classElt.supertype;
+    ClassElement superclassElement =
+        supertype != null ? supertype.element : null;
+    List<InterfaceType> mixins = classElt.mixins;
+    List<InterfaceType> interfaces = classElt.interfaces;
+    // Recursively collect the list of mappings from all of the interface types
+    List<MemberMap> lookupMaps = new List<MemberMap>();
+    //
+    // Superclass element
+    //
+    if (superclassElement != null) {
+      if (!visitedInterfaces.contains(superclassElement)) {
+        try {
+          visitedInterfaces.add(superclassElement);
+          //
+          // Recursively compute the map for the super type.
+          //
+          MemberMap map =
+              _computeInterfaceLookupMap(superclassElement, visitedInterfaces);
+          map = new MemberMap.con2(map);
+          //
+          // Substitute the super type down the hierarchy.
+          //
+          _substituteTypeParametersDownHierarchy(supertype, map);
+          //
+          // Add any members from the super type into the map as well.
+          //
+          _recordMapWithClassMembers(map, supertype, true);
+          lookupMaps.add(map);
+        } finally {
+          visitedInterfaces.remove(superclassElement);
+        }
+      } else {
+        return null;
+      }
+    }
+    //
+    // Mixin elements
+    //
+    for (int i = mixins.length - 1; i >= 0; i--) {
+      InterfaceType mixinType = mixins[i];
+      ClassElement mixinElement = mixinType.element;
+      if (mixinElement != null) {
+        if (!visitedInterfaces.contains(mixinElement)) {
+          try {
+            visitedInterfaces.add(mixinElement);
+            //
+            // Recursively compute the map for the mixin.
+            //
+            MemberMap map =
+                _computeInterfaceLookupMap(mixinElement, visitedInterfaces);
+            map = new MemberMap.con2(map);
+            //
+            // Substitute the mixin type down the hierarchy.
+            //
+            _substituteTypeParametersDownHierarchy(mixinType, map);
+            //
+            // Add any members from the mixin type into the map as well.
+            //
+            _recordMapWithClassMembers(map, mixinType, true);
+            lookupMaps.add(map);
+          } finally {
+            visitedInterfaces.remove(mixinElement);
+          }
+        } else {
+          return null;
+        }
+      }
+    }
+    //
+    // Interface elements
+    //
+    for (InterfaceType interfaceType in interfaces) {
+      ClassElement interfaceElement = interfaceType.element;
+      if (interfaceElement != null) {
+        if (!visitedInterfaces.contains(interfaceElement)) {
+          try {
+            visitedInterfaces.add(interfaceElement);
+            //
+            // Recursively compute the map for the interfaces.
+            //
+            MemberMap map =
+                _computeInterfaceLookupMap(interfaceElement, visitedInterfaces);
+            map = new MemberMap.con2(map);
+            //
+            // Substitute the supertypes down the hierarchy
+            //
+            _substituteTypeParametersDownHierarchy(interfaceType, map);
+            //
+            // And add any members from the interface into the map as well.
+            //
+            _recordMapWithClassMembers(map, interfaceType, true);
+            lookupMaps.add(map);
+          } finally {
+            visitedInterfaces.remove(interfaceElement);
+          }
+        } else {
+          return null;
+        }
+      }
+    }
+    if (lookupMaps.length == 0) {
+      return null;
+    }
+    return lookupMaps;
+  }
+
+  /**
+   * Given some [ClassElement], this method finds and returns the [ExecutableElement] of
+   * the passed name in the class element. Static members, members in super types and members not
+   * accessible from the current library are not considered.
+   *
+   * @param classElt the class element to query
+   * @param memberName the name of the member to lookup in the class
+   * @return the found [ExecutableElement], or `null` if no such member was found
+   */
+  ExecutableElement _lookupMemberInClass(
+      ClassElement classElt, String memberName) {
+    List<MethodElement> methods = classElt.methods;
+    for (MethodElement method in methods) {
+      if (memberName == method.name &&
+          method.isAccessibleIn(_library) &&
+          !method.isStatic) {
+        return method;
+      }
+    }
+    List<PropertyAccessorElement> accessors = classElt.accessors;
+    for (PropertyAccessorElement accessor in accessors) {
+      if (memberName == accessor.name &&
+          accessor.isAccessibleIn(_library) &&
+          !accessor.isStatic) {
+        return accessor;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Record the passed map with the set of all members (methods, getters and setters) in the type
+   * into the passed map.
+   *
+   * @param map some non-`null` map to put the methods and accessors from the passed
+   *          [ClassElement] into
+   * @param type the type that will be recorded into the passed map
+   * @param doIncludeAbstract `true` if abstract members will be put into the map
+   */
+  void _recordMapWithClassMembers(
+      MemberMap map, InterfaceType type, bool doIncludeAbstract) {
+    List<MethodElement> methods = type.methods;
+    for (MethodElement method in methods) {
+      if (method.isAccessibleIn(_library) &&
+          !method.isStatic &&
+          (doIncludeAbstract || !method.isAbstract)) {
+        map.put(method.name, method);
+      }
+    }
+    List<PropertyAccessorElement> accessors = type.accessors;
+    for (PropertyAccessorElement accessor in accessors) {
+      if (accessor.isAccessibleIn(_library) &&
+          !accessor.isStatic &&
+          (doIncludeAbstract || !accessor.isAbstract)) {
+        map.put(accessor.name, accessor);
+      }
+    }
+  }
+
+  /**
+   * This method is used to report errors on when they are found computing inheritance information.
+   * See [ErrorVerifier.checkForInconsistentMethodInheritance] to see where these generated
+   * error codes are reported back into the analysis engine.
+   *
+   * @param classElt the location of the source for which the exception occurred
+   * @param offset the offset of the location of the error
+   * @param length the length of the location of the error
+   * @param errorCode the error code to be associated with this error
+   * @param arguments the arguments used to build the error message
+   */
+  void _reportError(ClassElement classElt, int offset, int length,
+      ErrorCode errorCode, List<Object> arguments) {
+    HashSet<AnalysisError> errorSet = _errorsInClassElement[classElt];
+    if (errorSet == null) {
+      errorSet = new HashSet<AnalysisError>();
+      _errorsInClassElement[classElt] = errorSet;
+    }
+    errorSet.add(new AnalysisError.con2(
+        classElt.source, offset, length, errorCode, arguments));
+  }
+
+  /**
+   * Given the set of methods defined by classes above [classElt] in the class hierarchy,
+   * apply the appropriate inheritance rules to determine those methods inherited by or overridden
+   * by [classElt]. Also report static warnings
+   * [StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE] and
+   * [StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD] if appropriate.
+   *
+   * @param classElt the class element to query.
+   * @param unionMap a mapping from method name to the set of unique (in terms of signature) methods
+   *          defined in superclasses of [classElt].
+   * @return the inheritance lookup map for [classElt].
+   */
+  MemberMap _resolveInheritanceLookup(ClassElement classElt,
+      HashMap<String, List<ExecutableElement>> unionMap) {
+    MemberMap resultMap = new MemberMap();
+    unionMap.forEach((String key, List<ExecutableElement> list) {
+      int numOfEltsWithMatchingNames = list.length;
+      if (numOfEltsWithMatchingNames == 1) {
+        //
+        // Example: class A inherits only 1 method named 'm'.
+        // Since it is the only such method, it is inherited.
+        // Another example: class A inherits 2 methods named 'm' from 2
+        // different interfaces, but they both have the same signature, so it is
+        // the method inherited.
+        //
+        resultMap.put(key, list[0]);
+      } else {
+        //
+        // Then numOfEltsWithMatchingNames > 1, check for the warning cases.
+        //
+        bool allMethods = true;
+        bool allSetters = true;
+        bool allGetters = true;
+        for (ExecutableElement executableElement in list) {
+          if (executableElement is PropertyAccessorElement) {
+            allMethods = false;
+            if (executableElement.isSetter) {
+              allGetters = false;
+            } else {
+              allSetters = false;
+            }
+          } else {
+            allGetters = false;
+            allSetters = false;
+          }
+        }
+        //
+        // If there isn't a mixture of methods with getters, then continue,
+        // otherwise create a warning.
+        //
+        if (allMethods || allGetters || allSetters) {
+          //
+          // Compute the element whose type is the subtype of all of the other
+          // types.
+          //
+          List<ExecutableElement> elements = new List.from(list);
+          List<FunctionType> executableElementTypes =
+              new List<FunctionType>(numOfEltsWithMatchingNames);
+          for (int i = 0; i < numOfEltsWithMatchingNames; i++) {
+            executableElementTypes[i] = elements[i].type;
+          }
+          List<int> subtypesOfAllOtherTypesIndexes = new List<int>();
+          for (int i = 0; i < numOfEltsWithMatchingNames; i++) {
+            FunctionType subtype = executableElementTypes[i];
+            if (subtype == null) {
+              continue;
+            }
+            bool subtypeOfAllTypes = true;
+            for (int j = 0;
+                j < numOfEltsWithMatchingNames && subtypeOfAllTypes;
+                j++) {
+              if (i != j) {
+                if (!subtype.isSubtypeOf(executableElementTypes[j])) {
+                  subtypeOfAllTypes = false;
+                  break;
+                }
+              }
+            }
+            if (subtypeOfAllTypes) {
+              subtypesOfAllOtherTypesIndexes.add(i);
+            }
+          }
+          //
+          // The following is split into three cases determined by the number of
+          // elements in subtypesOfAllOtherTypes
+          //
+          if (subtypesOfAllOtherTypesIndexes.length == 1) {
+            //
+            // Example: class A inherited only 2 method named 'm'.
+            // One has the function type '() -> dynamic' and one has the
+            // function type '([int]) -> dynamic'. Since the second method is a
+            // subtype of all the others, it is the inherited method.
+            // Tests: InheritanceManagerTest.
+            // test_getMapOfMembersInheritedFromInterfaces_union_oneSubtype_*
+            //
+            resultMap.put(key, elements[subtypesOfAllOtherTypesIndexes[0]]);
+          } else {
+            if (subtypesOfAllOtherTypesIndexes.isEmpty) {
+              //
+              // Determine if the current class has a method or accessor with
+              // the member name, if it does then then this class does not
+              // "inherit" from any of the supertypes. See issue 16134.
+              //
+              bool classHasMember = false;
+              if (allMethods) {
+                classHasMember = classElt.getMethod(key) != null;
+              } else {
+                List<PropertyAccessorElement> accessors = classElt.accessors;
+                for (int i = 0; i < accessors.length; i++) {
+                  if (accessors[i].name == key) {
+                    classHasMember = true;
+                  }
+                }
+              }
+              //
+              // Example: class A inherited only 2 method named 'm'.
+              // One has the function type '() -> int' and one has the function
+              // type '() -> String'. Since neither is a subtype of the other,
+              // we create a warning, and have this class inherit nothing.
+              //
+              if (!classHasMember) {
+                String firstTwoFuntionTypesStr =
+                    "${executableElementTypes[0]}, ${executableElementTypes[1]}";
+                _reportError(classElt, classElt.nameOffset,
+                    classElt.displayName.length,
+                    StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE, [
+                  key,
+                  firstTwoFuntionTypesStr
+                ]);
+              }
+            } else {
+              //
+              // Example: class A inherits 2 methods named 'm'.
+              // One has the function type '(int) -> dynamic' and one has the
+              // function type '(num) -> dynamic'. Since they are both a subtype
+              // of the other, a synthetic function '(dynamic) -> dynamic' is
+              // inherited.
+              // Tests: test_getMapOfMembersInheritedFromInterfaces_
+              // union_multipleSubtypes_*
+              //
+              List<ExecutableElement> elementArrayToMerge =
+                  new List<ExecutableElement>(
+                      subtypesOfAllOtherTypesIndexes.length);
+              for (int i = 0; i < elementArrayToMerge.length; i++) {
+                elementArrayToMerge[i] =
+                    elements[subtypesOfAllOtherTypesIndexes[i]];
+              }
+              ExecutableElement mergedExecutableElement =
+                  _computeMergedExecutableElement(elementArrayToMerge);
+              resultMap.put(key, mergedExecutableElement);
+            }
+          }
+        } else {
+          _reportError(classElt, classElt.nameOffset,
+              classElt.displayName.length,
+              StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD,
+              [key]);
+        }
+      }
+    });
+    return resultMap;
+  }
+
+  /**
+   * Loop through all of the members in some [MemberMap], performing type parameter
+   * substitutions using a passed supertype.
+   *
+   * @param superType the supertype to substitute into the members of the [MemberMap]
+   * @param map the MemberMap to perform the substitutions on
+   */
+  void _substituteTypeParametersDownHierarchy(
+      InterfaceType superType, MemberMap map) {
+    for (int i = 0; i < map.size; i++) {
+      ExecutableElement executableElement = map.getValue(i);
+      if (executableElement is MethodMember) {
+        executableElement =
+            MethodMember.from(executableElement as MethodMember, superType);
+        map.setValue(i, executableElement);
+      } else if (executableElement is PropertyAccessorMember) {
+        executableElement = PropertyAccessorMember.from(
+            executableElement as PropertyAccessorMember, superType);
+        map.setValue(i, executableElement);
+      }
+    }
+  }
+
+  /**
+   * Union all of the [lookupMaps] together into a single map, grouping the ExecutableElements
+   * into a list where none of the elements are equal where equality is determined by having equal
+   * function types. (We also take note too of the kind of the element: ()->int and () -> int may
+   * not be equal if one is a getter and the other is a method.)
+   *
+   * @param lookupMaps the maps to be unioned together.
+   * @return the resulting union map.
+   */
+  HashMap<String, List<ExecutableElement>> _unionInterfaceLookupMaps(
+      List<MemberMap> lookupMaps) {
+    HashMap<String, List<ExecutableElement>> unionMap =
+        new HashMap<String, List<ExecutableElement>>();
+    for (MemberMap lookupMap in lookupMaps) {
+      int lookupMapSize = lookupMap.size;
+      for (int i = 0; i < lookupMapSize; i++) {
+        // Get the string key, if null, break.
+        String key = lookupMap.getKey(i);
+        if (key == null) {
+          break;
+        }
+        // Get the list value out of the unionMap
+        List<ExecutableElement> list = unionMap[key];
+        // If we haven't created such a map for this key yet, do create it and
+        // put the list entry into the unionMap.
+        if (list == null) {
+          list = new List<ExecutableElement>();
+          unionMap[key] = list;
+        }
+        // Fetch the entry out of this lookupMap
+        ExecutableElement newExecutableElementEntry = lookupMap.getValue(i);
+        if (list.isEmpty) {
+          // If the list is empty, just the new value
+          list.add(newExecutableElementEntry);
+        } else {
+          // Otherwise, only add the newExecutableElementEntry if it isn't
+          // already in the list, this covers situation where a class inherits
+          // two methods (or two getters) that are identical.
+          bool alreadyInList = false;
+          bool isMethod1 = newExecutableElementEntry is MethodElement;
+          for (ExecutableElement executableElementInList in list) {
+            bool isMethod2 = executableElementInList is MethodElement;
+            if (isMethod1 == isMethod2 &&
+                executableElementInList.type ==
+                    newExecutableElementEntry.type) {
+              alreadyInList = true;
+              break;
+            }
+          }
+          if (!alreadyInList) {
+            list.add(newExecutableElementEntry);
+          }
+        }
+      }
+    }
+    return unionMap;
+  }
+
+  /**
+   * Given some array of [ExecutableElement]s, this method creates a synthetic element as
+   * described in 8.1.1:
+   *
+   * Let <i>numberOfPositionals</i>(<i>f</i>) denote the number of positional parameters of a
+   * function <i>f</i>, and let <i>numberOfRequiredParams</i>(<i>f</i>) denote the number of
+   * required parameters of a function <i>f</i>. Furthermore, let <i>s</i> denote the set of all
+   * named parameters of the <i>m<sub>1</sub>, &hellip;, m<sub>k</sub></i>. Then let
+   * * <i>h = max(numberOfPositionals(m<sub>i</sub>)),</i>
+   * * <i>r = min(numberOfRequiredParams(m<sub>i</sub>)), for all <i>i</i>, 1 <= i <= k.</i>
+   * Then <i>I</i> has a method named <i>n</i>, with <i>r</i> required parameters of type
+   * <b>dynamic</b>, <i>h</i> positional parameters of type <b>dynamic</b>, named parameters
+   * <i>s</i> of type <b>dynamic</b> and return type <b>dynamic</b>.
+   *
+   */
+  static ExecutableElement _computeMergedExecutableElement(
+      List<ExecutableElement> elementArrayToMerge) {
+    int h = _getNumOfPositionalParameters(elementArrayToMerge[0]);
+    int r = _getNumOfRequiredParameters(elementArrayToMerge[0]);
+    Set<String> namedParametersList = new HashSet<String>();
+    for (int i = 1; i < elementArrayToMerge.length; i++) {
+      ExecutableElement element = elementArrayToMerge[i];
+      int numOfPositionalParams = _getNumOfPositionalParameters(element);
+      if (h < numOfPositionalParams) {
+        h = numOfPositionalParams;
+      }
+      int numOfRequiredParams = _getNumOfRequiredParameters(element);
+      if (r > numOfRequiredParams) {
+        r = numOfRequiredParams;
+      }
+      namedParametersList.addAll(_getNamedParameterNames(element));
+    }
+    return _createSyntheticExecutableElement(elementArrayToMerge,
+        elementArrayToMerge[0].displayName, r, h - r,
+        new List.from(namedParametersList));
+  }
+
+  /**
+   * Used by [computeMergedExecutableElement] to actually create the
+   * synthetic element.
+   *
+   * @param elementArrayToMerge the array used to create the synthetic element
+   * @param name the name of the method, getter or setter
+   * @param numOfRequiredParameters the number of required parameters
+   * @param numOfPositionalParameters the number of positional parameters
+   * @param namedParameters the list of [String]s that are the named parameters
+   * @return the created synthetic element
+   */
+  static ExecutableElement _createSyntheticExecutableElement(
+      List<ExecutableElement> elementArrayToMerge, String name,
+      int numOfRequiredParameters, int numOfPositionalParameters,
+      List<String> namedParameters) {
+    DynamicTypeImpl dynamicType = DynamicTypeImpl.instance;
+    SimpleIdentifier nameIdentifier = new SimpleIdentifier(
+        new sc.StringToken(sc.TokenType.IDENTIFIER, name, 0));
+    ExecutableElementImpl executable;
+    if (elementArrayToMerge[0] is MethodElement) {
+      MultiplyInheritedMethodElementImpl unionedMethod =
+          new MultiplyInheritedMethodElementImpl(nameIdentifier);
+      unionedMethod.inheritedElements = elementArrayToMerge;
+      executable = unionedMethod;
+    } else {
+      MultiplyInheritedPropertyAccessorElementImpl unionedPropertyAccessor =
+          new MultiplyInheritedPropertyAccessorElementImpl(nameIdentifier);
+      unionedPropertyAccessor.getter =
+          (elementArrayToMerge[0] as PropertyAccessorElement).isGetter;
+      unionedPropertyAccessor.setter =
+          (elementArrayToMerge[0] as PropertyAccessorElement).isSetter;
+      unionedPropertyAccessor.inheritedElements = elementArrayToMerge;
+      executable = unionedPropertyAccessor;
+    }
+    int numOfParameters = numOfRequiredParameters +
+        numOfPositionalParameters +
+        namedParameters.length;
+    List<ParameterElement> parameters =
+        new List<ParameterElement>(numOfParameters);
+    int i = 0;
+    for (int j = 0; j < numOfRequiredParameters; j++, i++) {
+      ParameterElementImpl parameter = new ParameterElementImpl("", 0);
+      parameter.type = dynamicType;
+      parameter.parameterKind = ParameterKind.REQUIRED;
+      parameters[i] = parameter;
+    }
+    for (int k = 0; k < numOfPositionalParameters; k++, i++) {
+      ParameterElementImpl parameter = new ParameterElementImpl("", 0);
+      parameter.type = dynamicType;
+      parameter.parameterKind = ParameterKind.POSITIONAL;
+      parameters[i] = parameter;
+    }
+    for (int m = 0; m < namedParameters.length; m++, i++) {
+      ParameterElementImpl parameter =
+          new ParameterElementImpl(namedParameters[m], 0);
+      parameter.type = dynamicType;
+      parameter.parameterKind = ParameterKind.NAMED;
+      parameters[i] = parameter;
+    }
+    executable.returnType = dynamicType;
+    executable.parameters = parameters;
+    FunctionTypeImpl methodType = new FunctionTypeImpl.con1(executable);
+    executable.type = methodType;
+    return executable;
+  }
+
+  /**
+   * Given some [ExecutableElement], return the list of named parameters.
+   */
+  static List<String> _getNamedParameterNames(
+      ExecutableElement executableElement) {
+    List<String> namedParameterNames = new List<String>();
+    List<ParameterElement> parameters = executableElement.parameters;
+    for (int i = 0; i < parameters.length; i++) {
+      ParameterElement parameterElement = parameters[i];
+      if (parameterElement.parameterKind == ParameterKind.NAMED) {
+        namedParameterNames.add(parameterElement.name);
+      }
+    }
+    return namedParameterNames;
+  }
+
+  /**
+   * Given some [ExecutableElement] return the number of parameters of the specified kind.
+   */
+  static int _getNumOfParameters(
+      ExecutableElement executableElement, ParameterKind parameterKind) {
+    int parameterCount = 0;
+    List<ParameterElement> parameters = executableElement.parameters;
+    for (int i = 0; i < parameters.length; i++) {
+      ParameterElement parameterElement = parameters[i];
+      if (parameterElement.parameterKind == parameterKind) {
+        parameterCount++;
+      }
+    }
+    return parameterCount;
+  }
+
+  /**
+   * Given some [ExecutableElement] return the number of positional parameters.
+   *
+   * Note: by positional we mean [ParameterKind.REQUIRED] or [ParameterKind.POSITIONAL].
+   */
+  static int _getNumOfPositionalParameters(
+          ExecutableElement executableElement) =>
+      _getNumOfParameters(executableElement, ParameterKind.REQUIRED) +
+          _getNumOfParameters(executableElement, ParameterKind.POSITIONAL);
+
+  /**
+   * Given some [ExecutableElement] return the number of required parameters.
+   */
+  static int _getNumOfRequiredParameters(ExecutableElement executableElement) =>
+      _getNumOfParameters(executableElement, ParameterKind.REQUIRED);
+
+  /**
+   * Given some [ExecutableElement] returns `true` if it is an abstract member of a
+   * class.
+   *
+   * @param executableElement some [ExecutableElement] to evaluate
+   * @return `true` if the given element is an abstract member of a class
+   */
+  static bool _isAbstract(ExecutableElement executableElement) {
+    if (executableElement is MethodElement) {
+      return executableElement.isAbstract;
+    } else if (executableElement is PropertyAccessorElement) {
+      return executableElement.isAbstract;
+    }
+    return false;
+  }
+}
+
+/**
+ * This enum holds one of four states of a field initialization state through a constructor
+ * signature, not initialized, initialized in the field declaration, initialized in the field
+ * formal, and finally, initialized in the initializers list.
+ */
+class INIT_STATE extends Enum<INIT_STATE> {
+  static const INIT_STATE NOT_INIT = const INIT_STATE('NOT_INIT', 0);
+
+  static const INIT_STATE INIT_IN_DECLARATION =
+      const INIT_STATE('INIT_IN_DECLARATION', 1);
+
+  static const INIT_STATE INIT_IN_FIELD_FORMAL =
+      const INIT_STATE('INIT_IN_FIELD_FORMAL', 2);
+
+  static const INIT_STATE INIT_IN_INITIALIZERS =
+      const INIT_STATE('INIT_IN_INITIALIZERS', 3);
+
+  static const List<INIT_STATE> values = const [
+    NOT_INIT,
+    INIT_IN_DECLARATION,
+    INIT_IN_FIELD_FORMAL,
+    INIT_IN_INITIALIZERS
+  ];
+
+  const INIT_STATE(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * Instances of the class `LabelScope` represent a scope in which a single label is defined.
+ */
+class LabelScope {
+  /**
+   * The label scope enclosing this label scope.
+   */
+  final LabelScope _outerScope;
+
+  /**
+   * The label defined in this scope.
+   */
+  final String _label;
+
+  /**
+   * The element to which the label resolves.
+   */
+  final LabelElement element;
+
+  /**
+   * The AST node to which the label resolves.
+   */
+  final AstNode node;
+
+  /**
+   * Initialize a newly created scope to represent the label [_label].
+   * [_outerScope] is the scope enclosing the new label scope.  [node] is the
+   * AST node the label resolves to.  [element] is the element the label
+   * resolves to.
+   */
+  LabelScope(this._outerScope, this._label, this.node, this.element);
+
+  /**
+   * Return the LabelScope which defines [targetLabel], or `null` if it is not
+   * defined in this scope.
+   */
+  LabelScope lookup(String targetLabel) {
+    if (_label == targetLabel) {
+      return this;
+    } else if (_outerScope != null) {
+      return _outerScope.lookup(targetLabel);
+    } else {
+      return null;
+    }
+  }
+}
+
+/**
+ * Instances of the class `Library` represent the data about a single library during the
+ * resolution of some (possibly different) library. They are not intended to be used except during
+ * the resolution process.
+ */
+class Library {
+  /**
+   * An empty list that can be used to initialize lists of libraries.
+   */
+  static const List<Library> _EMPTY_ARRAY = const <Library>[];
+
+  /**
+   * The prefix of a URI using the dart-ext scheme to reference a native code library.
+   */
+  static String _DART_EXT_SCHEME = "dart-ext:";
+
+  /**
+   * The analysis context in which this library is being analyzed.
+   */
+  final InternalAnalysisContext _analysisContext;
+
+  /**
+   * The inheritance manager which is used for this member lookups in this library.
+   */
+  InheritanceManager _inheritanceManager;
+
+  /**
+   * The listener to which analysis errors will be reported.
+   */
+  final AnalysisErrorListener _errorListener;
+
+  /**
+   * The source specifying the defining compilation unit of this library.
+   */
+  final Source librarySource;
+
+  /**
+   * The library element representing this library.
+   */
+  LibraryElementImpl _libraryElement;
+
+  /**
+   * A list containing all of the libraries that are imported into this library.
+   */
+  List<Library> _importedLibraries = _EMPTY_ARRAY;
+
+  /**
+   * A table mapping URI-based directive to the actual URI value.
+   */
+  HashMap<UriBasedDirective, String> _directiveUris =
+      new HashMap<UriBasedDirective, String>();
+
+  /**
+   * A flag indicating whether this library explicitly imports core.
+   */
+  bool explicitlyImportsCore = false;
+
+  /**
+   * A list containing all of the libraries that are exported from this library.
+   */
+  List<Library> _exportedLibraries = _EMPTY_ARRAY;
+
+  /**
+   * A table mapping the sources for the compilation units in this library to their corresponding
+   * AST structures.
+   */
+  HashMap<Source, CompilationUnit> _astMap =
+      new HashMap<Source, CompilationUnit>();
+
+  /**
+   * The library scope used when resolving elements within this library's compilation units.
+   */
+  LibraryScope _libraryScope;
+
+  /**
+   * Initialize a newly created data holder that can maintain the data associated with a library.
+   *
+   * @param analysisContext the analysis context in which this library is being analyzed
+   * @param errorListener the listener to which analysis errors will be reported
+   * @param librarySource the source specifying the defining compilation unit of this library
+   */
+  Library(this._analysisContext, this._errorListener, this.librarySource) {
+    this._libraryElement =
+        _analysisContext.getLibraryElement(librarySource) as LibraryElementImpl;
+  }
+
+  /**
+   * Return an array of the [CompilationUnit]s that make up the library. The first unit is
+   * always the defining unit.
+   *
+   * @return an array of the [CompilationUnit]s that make up the library. The first unit is
+   *         always the defining unit
+   */
+  List<CompilationUnit> get compilationUnits {
+    List<CompilationUnit> unitArrayList = new List<CompilationUnit>();
+    unitArrayList.add(definingCompilationUnit);
+    for (Source source in _astMap.keys.toSet()) {
+      if (librarySource != source) {
+        unitArrayList.add(getAST(source));
+      }
+    }
+    return unitArrayList;
+  }
+
+  /**
+   * Return a collection containing the sources for the compilation units in this library, including
+   * the defining compilation unit.
+   *
+   * @return the sources for the compilation units in this library
+   */
+  Set<Source> get compilationUnitSources => _astMap.keys.toSet();
+
+  /**
+   * Return the AST structure associated with the defining compilation unit for this library.
+   *
+   * @return the AST structure associated with the defining compilation unit for this library
+   * @throws AnalysisException if an AST structure could not be created for the defining compilation
+   *           unit
+   */
+  CompilationUnit get definingCompilationUnit => getAST(librarySource);
+
+  /**
+   * Set the libraries that are exported by this library to be those in the given array.
+   *
+   * @param exportedLibraries the libraries that are exported by this library
+   */
+  void set exportedLibraries(List<Library> exportedLibraries) {
+    this._exportedLibraries = exportedLibraries;
+  }
+
+  /**
+   * Return an array containing the libraries that are exported from this library.
+   *
+   * @return an array containing the libraries that are exported from this library
+   */
+  List<Library> get exports => _exportedLibraries;
+
+  /**
+   * Set the libraries that are imported into this library to be those in the given array.
+   *
+   * @param importedLibraries the libraries that are imported into this library
+   */
+  void set importedLibraries(List<Library> importedLibraries) {
+    this._importedLibraries = importedLibraries;
+  }
+
+  /**
+   * Return an array containing the libraries that are imported into this library.
+   *
+   * @return an array containing the libraries that are imported into this library
+   */
+  List<Library> get imports => _importedLibraries;
+
+  /**
+   * Return an array containing the libraries that are either imported or exported from this
+   * library.
+   *
+   * @return the libraries that are either imported or exported from this library
+   */
+  List<Library> get importsAndExports {
+    HashSet<Library> libraries = new HashSet<Library>();
+    for (Library library in _importedLibraries) {
+      libraries.add(library);
+    }
+    for (Library library in _exportedLibraries) {
+      libraries.add(library);
+    }
+    return new List.from(libraries);
+  }
+
+  /**
+   * Return the inheritance manager for this library.
+   *
+   * @return the inheritance manager for this library
+   */
+  InheritanceManager get inheritanceManager {
+    if (_inheritanceManager == null) {
+      return _inheritanceManager = new InheritanceManager(_libraryElement);
+    }
+    return _inheritanceManager;
+  }
+
+  /**
+   * Return the library element representing this library, creating it if necessary.
+   *
+   * @return the library element representing this library
+   */
+  LibraryElementImpl get libraryElement {
+    if (_libraryElement == null) {
+      try {
+        _libraryElement = _analysisContext
+            .computeLibraryElement(librarySource) as LibraryElementImpl;
+      } on AnalysisException catch (exception, stackTrace) {
+        AnalysisEngine.instance.logger.logError(
+            "Could not compute library element for ${librarySource.fullName}",
+            new CaughtException(exception, stackTrace));
+      }
+    }
+    return _libraryElement;
+  }
+
+  /**
+   * Set the library element representing this library to the given library element.
+   *
+   * @param libraryElement the library element representing this library
+   */
+  void set libraryElement(LibraryElementImpl libraryElement) {
+    this._libraryElement = libraryElement;
+    if (_inheritanceManager != null) {
+      _inheritanceManager.libraryElement = libraryElement;
+    }
+  }
+
+  /**
+   * Return the library scope used when resolving elements within this library's compilation units.
+   *
+   * @return the library scope used when resolving elements within this library's compilation units
+   */
+  LibraryScope get libraryScope {
+    if (_libraryScope == null) {
+      _libraryScope = new LibraryScope(_libraryElement, _errorListener);
+    }
+    return _libraryScope;
+  }
+
+  /**
+   * Return the AST structure associated with the given source.
+   *
+   * @param source the source representing the compilation unit whose AST is to be returned
+   * @return the AST structure associated with the given source
+   * @throws AnalysisException if an AST structure could not be created for the compilation unit
+   */
+  CompilationUnit getAST(Source source) {
+    CompilationUnit unit = _astMap[source];
+    if (unit == null) {
+      unit = _analysisContext.computeResolvableCompilationUnit(source);
+      _astMap[source] = unit;
+    }
+    return unit;
+  }
+
+  /**
+   * Return the result of resolving the URI of the given URI-based directive against the URI of the
+   * library, or `null` if the URI is not valid. If the URI is not valid, report the error.
+   *
+   * @param directive the directive which URI should be resolved
+   * @return the result of resolving the URI against the URI of the library
+   */
+  Source getSource(UriBasedDirective directive) {
+    StringLiteral uriLiteral = directive.uri;
+    if (uriLiteral is StringInterpolation) {
+      _errorListener.onError(new AnalysisError.con2(librarySource,
+          uriLiteral.offset, uriLiteral.length,
+          CompileTimeErrorCode.URI_WITH_INTERPOLATION));
+      return null;
+    }
+    String uriContent = uriLiteral.stringValue.trim();
+    _directiveUris[directive] = uriContent;
+    uriContent = Uri.encodeFull(uriContent);
+    if (directive is ImportDirective &&
+        uriContent.startsWith(_DART_EXT_SCHEME)) {
+      _libraryElement.hasExtUri = true;
+      return null;
+    }
+    try {
+      parseUriWithException(uriContent);
+      Source source =
+          _analysisContext.sourceFactory.resolveUri(librarySource, uriContent);
+      if (!_analysisContext.exists(source)) {
+        _errorListener.onError(new AnalysisError.con2(librarySource,
+            uriLiteral.offset, uriLiteral.length,
+            CompileTimeErrorCode.URI_DOES_NOT_EXIST, [uriContent]));
+      }
+      return source;
+    } on URISyntaxException {
+      _errorListener.onError(new AnalysisError.con2(librarySource,
+          uriLiteral.offset, uriLiteral.length,
+          CompileTimeErrorCode.INVALID_URI, [uriContent]));
+    }
+    return null;
+  }
+
+  /**
+   * Returns the URI value of the given directive.
+   */
+  String getUri(UriBasedDirective directive) => _directiveUris[directive];
+
+  /**
+   * Set the AST structure associated with the defining compilation unit for this library to the
+   * given AST structure.
+   *
+   * @param unit the AST structure associated with the defining compilation unit for this library
+   */
+  void setDefiningCompilationUnit(CompilationUnit unit) {
+    _astMap[librarySource] = unit;
+  }
+
+  @override
+  String toString() => librarySource.shortName;
+}
+
+/**
+ * Instances of the class `LibraryElementBuilder` build an element model for a single library.
+ */
+class LibraryElementBuilder {
+  /**
+   * The analysis context in which the element model will be built.
+   */
+  final InternalAnalysisContext _analysisContext;
+
+  /**
+   * The listener to which errors will be reported.
+   */
+  final AnalysisErrorListener _errorListener;
+
+  /**
+   * Initialize a newly created library element builder.
+   *
+   * @param analysisContext the analysis context in which the element model will be built
+   * @param errorListener the listener to which errors will be reported
+   */
+  LibraryElementBuilder(this._analysisContext, this._errorListener);
+
+  /**
+   * Build the library element for the given library.
+   *
+   * @param library the library for which an element model is to be built
+   * @return the library element that was built
+   * @throws AnalysisException if the analysis could not be performed
+   */
+  LibraryElementImpl buildLibrary(Library library) {
+    CompilationUnitBuilder builder = new CompilationUnitBuilder();
+    Source librarySource = library.librarySource;
+    CompilationUnit definingCompilationUnit = library.definingCompilationUnit;
+    CompilationUnitElementImpl definingCompilationUnitElement =
+        builder.buildCompilationUnit(librarySource, definingCompilationUnit);
+    NodeList<Directive> directives = definingCompilationUnit.directives;
+    LibraryIdentifier libraryNameNode = null;
+    bool hasPartDirective = false;
+    FunctionElement entryPoint =
+        _findEntryPoint(definingCompilationUnitElement);
+    List<Directive> directivesToResolve = new List<Directive>();
+    List<CompilationUnitElementImpl> sourcedCompilationUnits =
+        new List<CompilationUnitElementImpl>();
+    for (Directive directive in directives) {
+      //
+      // We do not build the elements representing the import and export
+      // directives at this point. That is not done until we get to
+      // LibraryResolver.buildDirectiveModels() because we need the
+      // LibraryElements for the referenced libraries, which might not exist at
+      // this point (due to the possibility of circular references).
+      //
+      if (directive is LibraryDirective) {
+        if (libraryNameNode == null) {
+          libraryNameNode = directive.name;
+          directivesToResolve.add(directive);
+        }
+      } else if (directive is PartDirective) {
+        PartDirective partDirective = directive;
+        StringLiteral partUri = partDirective.uri;
+        Source partSource = partDirective.source;
+        if (_analysisContext.exists(partSource)) {
+          hasPartDirective = true;
+          CompilationUnit partUnit = library.getAST(partSource);
+          CompilationUnitElementImpl part =
+              builder.buildCompilationUnit(partSource, partUnit);
+          part.uriOffset = partUri.offset;
+          part.uriEnd = partUri.end;
+          part.uri = partDirective.uriContent;
+          //
+          // Validate that the part contains a part-of directive with the same
+          // name as the library.
+          //
+          String partLibraryName =
+              _getPartLibraryName(partSource, partUnit, directivesToResolve);
+          if (partLibraryName == null) {
+            _errorListener.onError(new AnalysisError.con2(librarySource,
+                partUri.offset, partUri.length,
+                CompileTimeErrorCode.PART_OF_NON_PART, [partUri.toSource()]));
+          } else if (libraryNameNode == null) {
+            // TODO(brianwilkerson) Collect the names declared by the part.
+            // If they are all the same then we can use that name as the
+            // inferred name of the library and present it in a quick-fix.
+            // partLibraryNames.add(partLibraryName);
+          } else if (libraryNameNode.name != partLibraryName) {
+            _errorListener.onError(new AnalysisError.con2(librarySource,
+                partUri.offset, partUri.length,
+                StaticWarningCode.PART_OF_DIFFERENT_LIBRARY, [
+              libraryNameNode.name,
+              partLibraryName
+            ]));
+          }
+          if (entryPoint == null) {
+            entryPoint = _findEntryPoint(part);
+          }
+          directive.element = part;
+          sourcedCompilationUnits.add(part);
+        }
+      }
+    }
+    if (hasPartDirective && libraryNameNode == null) {
+      _errorListener.onError(new AnalysisError.con1(librarySource,
+          ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART));
+    }
+    //
+    // Create and populate the library element.
+    //
+    LibraryElementImpl libraryElement = new LibraryElementImpl.forNode(
+        _analysisContext.getContextFor(librarySource), libraryNameNode);
+    libraryElement.definingCompilationUnit = definingCompilationUnitElement;
+    if (entryPoint != null) {
+      libraryElement.entryPoint = entryPoint;
+    }
+    int sourcedUnitCount = sourcedCompilationUnits.length;
+    libraryElement.parts = sourcedCompilationUnits;
+    for (Directive directive in directivesToResolve) {
+      directive.element = libraryElement;
+    }
+    library.libraryElement = libraryElement;
+    if (sourcedUnitCount > 0) {
+      _patchTopLevelAccessors(libraryElement);
+    }
+    return libraryElement;
+  }
+
+  /**
+   * Build the library element for the given library.  The resulting element is
+   * stored in the [ResolvableLibrary] structure.
+   *
+   * @param library the library for which an element model is to be built
+   * @throws AnalysisException if the analysis could not be performed
+   */
+  void buildLibrary2(ResolvableLibrary library) {
+    CompilationUnitBuilder builder = new CompilationUnitBuilder();
+    Source librarySource = library.librarySource;
+    CompilationUnit definingCompilationUnit = library.definingCompilationUnit;
+    CompilationUnitElementImpl definingCompilationUnitElement =
+        builder.buildCompilationUnit(librarySource, definingCompilationUnit);
+    NodeList<Directive> directives = definingCompilationUnit.directives;
+    LibraryIdentifier libraryNameNode = null;
+    bool hasPartDirective = false;
+    FunctionElement entryPoint =
+        _findEntryPoint(definingCompilationUnitElement);
+    List<Directive> directivesToResolve = new List<Directive>();
+    List<CompilationUnitElementImpl> sourcedCompilationUnits =
+        new List<CompilationUnitElementImpl>();
+    for (Directive directive in directives) {
+      //
+      // We do not build the elements representing the import and export
+      // directives at this point. That is not done until we get to
+      // LibraryResolver.buildDirectiveModels() because we need the
+      // LibraryElements for the referenced libraries, which might not exist at
+      // this point (due to the possibility of circular references).
+      //
+      if (directive is LibraryDirective) {
+        if (libraryNameNode == null) {
+          libraryNameNode = directive.name;
+          directivesToResolve.add(directive);
+        }
+      } else if (directive is PartDirective) {
+        PartDirective partDirective = directive;
+        StringLiteral partUri = partDirective.uri;
+        Source partSource = partDirective.source;
+        if (_analysisContext.exists(partSource)) {
+          hasPartDirective = true;
+          CompilationUnit partUnit = library.getAST(partSource);
+          if (partUnit != null) {
+            CompilationUnitElementImpl part =
+                builder.buildCompilationUnit(partSource, partUnit);
+            part.uriOffset = partUri.offset;
+            part.uriEnd = partUri.end;
+            part.uri = partDirective.uriContent;
+            //
+            // Validate that the part contains a part-of directive with the same
+            // name as the library.
+            //
+            String partLibraryName =
+                _getPartLibraryName(partSource, partUnit, directivesToResolve);
+            if (partLibraryName == null) {
+              _errorListener.onError(new AnalysisError.con2(librarySource,
+                  partUri.offset, partUri.length,
+                  CompileTimeErrorCode.PART_OF_NON_PART, [partUri.toSource()]));
+            } else if (libraryNameNode == null) {
+              // TODO(brianwilkerson) Collect the names declared by the part.
+              // If they are all the same then we can use that name as the
+              // inferred name of the library and present it in a quick-fix.
+              // partLibraryNames.add(partLibraryName);
+            } else if (libraryNameNode.name != partLibraryName) {
+              _errorListener.onError(new AnalysisError.con2(librarySource,
+                  partUri.offset, partUri.length,
+                  StaticWarningCode.PART_OF_DIFFERENT_LIBRARY, [
+                libraryNameNode.name,
+                partLibraryName
+              ]));
+            }
+            if (entryPoint == null) {
+              entryPoint = _findEntryPoint(part);
+            }
+            directive.element = part;
+            sourcedCompilationUnits.add(part);
+          }
+        }
+      }
+    }
+    if (hasPartDirective && libraryNameNode == null) {
+      _errorListener.onError(new AnalysisError.con1(librarySource,
+          ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART));
+    }
+    //
+    // Create and populate the library element.
+    //
+    LibraryElementImpl libraryElement = new LibraryElementImpl.forNode(
+        _analysisContext.getContextFor(librarySource), libraryNameNode);
+    libraryElement.definingCompilationUnit = definingCompilationUnitElement;
+    if (entryPoint != null) {
+      libraryElement.entryPoint = entryPoint;
+    }
+    int sourcedUnitCount = sourcedCompilationUnits.length;
+    libraryElement.parts = sourcedCompilationUnits;
+    for (Directive directive in directivesToResolve) {
+      directive.element = libraryElement;
+    }
+    library.libraryElement = libraryElement;
+    if (sourcedUnitCount > 0) {
+      _patchTopLevelAccessors(libraryElement);
+    }
+  }
+
+  /**
+   * Add all of the non-synthetic getters and setters defined in the given compilation unit that
+   * have no corresponding accessor to one of the given collections.
+   *
+   * @param getters the map to which getters are to be added
+   * @param setters the list to which setters are to be added
+   * @param unit the compilation unit defining the accessors that are potentially being added
+   */
+  void _collectAccessors(HashMap<String, PropertyAccessorElement> getters,
+      List<PropertyAccessorElement> setters, CompilationUnitElement unit) {
+    for (PropertyAccessorElement accessor in unit.accessors) {
+      if (accessor.isGetter) {
+        if (!accessor.isSynthetic && accessor.correspondingSetter == null) {
+          getters[accessor.displayName] = accessor;
+        }
+      } else {
+        if (!accessor.isSynthetic && accessor.correspondingGetter == null) {
+          setters.add(accessor);
+        }
+      }
+    }
+  }
+
+  /**
+   * Search the top-level functions defined in the given compilation unit for the entry point.
+   *
+   * @param element the compilation unit to be searched
+   * @return the entry point that was found, or `null` if the compilation unit does not define
+   *         an entry point
+   */
+  FunctionElement _findEntryPoint(CompilationUnitElementImpl element) {
+    for (FunctionElement function in element.functions) {
+      if (function.isEntryPoint) {
+        return function;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the name of the library that the given part is declared to be a part of, or `null`
+   * if the part does not contain a part-of directive.
+   *
+   * @param partSource the source representing the part
+   * @param partUnit the AST structure of the part
+   * @param directivesToResolve a list of directives that should be resolved to the library being
+   *          built
+   * @return the name of the library that the given part is declared to be a part of
+   */
+  String _getPartLibraryName(Source partSource, CompilationUnit partUnit,
+      List<Directive> directivesToResolve) {
+    for (Directive directive in partUnit.directives) {
+      if (directive is PartOfDirective) {
+        directivesToResolve.add(directive);
+        LibraryIdentifier libraryName = directive.libraryName;
+        if (libraryName != null) {
+          return libraryName.name;
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Look through all of the compilation units defined for the given library, looking for getters
+   * and setters that are defined in different compilation units but that have the same names. If
+   * any are found, make sure that they have the same variable element.
+   *
+   * @param libraryElement the library defining the compilation units to be processed
+   */
+  void _patchTopLevelAccessors(LibraryElementImpl libraryElement) {
+    HashMap<String, PropertyAccessorElement> getters =
+        new HashMap<String, PropertyAccessorElement>();
+    List<PropertyAccessorElement> setters = new List<PropertyAccessorElement>();
+    _collectAccessors(getters, setters, libraryElement.definingCompilationUnit);
+    for (CompilationUnitElement unit in libraryElement.parts) {
+      _collectAccessors(getters, setters, unit);
+    }
+    for (PropertyAccessorElement setter in setters) {
+      PropertyAccessorElement getter = getters[setter.displayName];
+      if (getter != null) {
+        PropertyInducingElementImpl variable =
+            getter.variable as PropertyInducingElementImpl;
+        variable.setter = setter;
+        (setter as PropertyAccessorElementImpl).variable = variable;
+      }
+    }
+  }
+}
+
+/**
+ * Instances of the class `LibraryImportScope` represent the scope containing all of the names
+ * available from imported libraries.
+ */
+class LibraryImportScope extends Scope {
+  /**
+   * The element representing the library in which this scope is enclosed.
+   */
+  final LibraryElement _definingLibrary;
+
+  /**
+   * The listener that is to be informed when an error is encountered.
+   */
+  final AnalysisErrorListener errorListener;
+
+  /**
+   * A list of the namespaces representing the names that are available in this scope from imported
+   * libraries.
+   */
+  List<Namespace> _importedNamespaces;
+
+  /**
+   * Initialize a newly created scope representing the names imported into the given library.
+   *
+   * @param definingLibrary the element representing the library that imports the names defined in
+   *          this scope
+   * @param errorListener the listener that is to be informed when an error is encountered
+   */
+  LibraryImportScope(this._definingLibrary, this.errorListener) {
+    _createImportedNamespaces();
+  }
+
+  @override
+  void define(Element element) {
+    if (!Scope.isPrivateName(element.displayName)) {
+      super.define(element);
+    }
+  }
+
+  @override
+  Source getSource(AstNode node) {
+    Source source = super.getSource(node);
+    if (source == null) {
+      source = _definingLibrary.definingCompilationUnit.source;
+    }
+    return source;
+  }
+
+  @override
+  Element internalLookup(
+      Identifier identifier, String name, LibraryElement referencingLibrary) {
+    Element foundElement = localLookup(name, referencingLibrary);
+    if (foundElement != null) {
+      return foundElement;
+    }
+    for (int i = 0; i < _importedNamespaces.length; i++) {
+      Namespace nameSpace = _importedNamespaces[i];
+      Element element = nameSpace.get(name);
+      if (element != null) {
+        if (foundElement == null) {
+          foundElement = element;
+        } else if (!identical(foundElement, element)) {
+          foundElement = MultiplyDefinedElementImpl.fromElements(
+              _definingLibrary.context, foundElement, element);
+        }
+      }
+    }
+    if (foundElement is MultiplyDefinedElementImpl) {
+      foundElement = _removeSdkElements(
+          identifier, name, foundElement as MultiplyDefinedElementImpl);
+    }
+    if (foundElement is MultiplyDefinedElementImpl) {
+      String foundEltName = foundElement.displayName;
+      List<Element> conflictingMembers = foundElement.conflictingElements;
+      int count = conflictingMembers.length;
+      List<String> libraryNames = new List<String>(count);
+      for (int i = 0; i < count; i++) {
+        libraryNames[i] = _getLibraryName(conflictingMembers[i]);
+      }
+      libraryNames.sort();
+      errorListener.onError(new AnalysisError.con2(getSource(identifier),
+          identifier.offset, identifier.length,
+          StaticWarningCode.AMBIGUOUS_IMPORT, [
+        foundEltName,
+        StringUtilities.printListOfQuotedNames(libraryNames)
+      ]));
+      return foundElement;
+    }
+    if (foundElement != null) {
+      defineNameWithoutChecking(name, foundElement);
+    }
+    return foundElement;
+  }
+
+  /**
+   * Create all of the namespaces associated with the libraries imported into this library. The
+   * names are not added to this scope, but are stored for later reference.
+   *
+   * @param definingLibrary the element representing the library that imports the libraries for
+   *          which namespaces will be created
+   */
+  void _createImportedNamespaces() {
+    NamespaceBuilder builder = new NamespaceBuilder();
+    List<ImportElement> imports = _definingLibrary.imports;
+    int count = imports.length;
+    _importedNamespaces = new List<Namespace>(count);
+    for (int i = 0; i < count; i++) {
+      _importedNamespaces[i] =
+          builder.createImportNamespaceForDirective(imports[i]);
+    }
+  }
+
+  /**
+   * Returns the name of the library that defines given element.
+   *
+   * @param element the element to get library name
+   * @return the name of the library that defines given element
+   */
+  String _getLibraryName(Element element) {
+    if (element == null) {
+      return StringUtilities.EMPTY;
+    }
+    LibraryElement library = element.library;
+    if (library == null) {
+      return StringUtilities.EMPTY;
+    }
+    List<ImportElement> imports = _definingLibrary.imports;
+    int count = imports.length;
+    for (int i = 0; i < count; i++) {
+      if (identical(imports[i].importedLibrary, library)) {
+        return library.definingCompilationUnit.displayName;
+      }
+    }
+    List<String> indirectSources = new List<String>();
+    for (int i = 0; i < count; i++) {
+      LibraryElement importedLibrary = imports[i].importedLibrary;
+      if (importedLibrary != null) {
+        for (LibraryElement exportedLibrary
+            in importedLibrary.exportedLibraries) {
+          if (identical(exportedLibrary, library)) {
+            indirectSources
+                .add(importedLibrary.definingCompilationUnit.displayName);
+          }
+        }
+      }
+    }
+    int indirectCount = indirectSources.length;
+    StringBuffer buffer = new StringBuffer();
+    buffer.write(library.definingCompilationUnit.displayName);
+    if (indirectCount > 0) {
+      buffer.write(" (via ");
+      if (indirectCount > 1) {
+        indirectSources.sort();
+        buffer.write(StringUtilities.printListOfQuotedNames(indirectSources));
+      } else {
+        buffer.write(indirectSources[0]);
+      }
+      buffer.write(")");
+    }
+    return buffer.toString();
+  }
+
+  /**
+   * Given a collection of elements (captured by the [foundElement]) that the
+   * [identifier] (with the given [name]) resolved to, remove from the list all
+   * of the names defined in the SDK and return the element(s) that remain.
+   */
+  Element _removeSdkElements(Identifier identifier, String name,
+      MultiplyDefinedElementImpl foundElement) {
+    List<Element> conflictingElements = foundElement.conflictingElements;
+    List<Element> nonSdkElements = new List<Element>();
+    Element sdkElement = null;
+    for (Element member in conflictingElements) {
+      if (member.library.isInSdk) {
+        sdkElement = member;
+      } else {
+        nonSdkElements.add(member);
+      }
+    }
+    if (sdkElement != null && nonSdkElements.length > 0) {
+      String sdkLibName = _getLibraryName(sdkElement);
+      String otherLibName = _getLibraryName(nonSdkElements[0]);
+      errorListener.onError(new AnalysisError.con2(getSource(identifier),
+          identifier.offset, identifier.length,
+          StaticWarningCode.CONFLICTING_DART_IMPORT, [
+        name,
+        sdkLibName,
+        otherLibName
+      ]));
+    }
+    if (nonSdkElements.length == conflictingElements.length) {
+      // None of the members were removed
+      return foundElement;
+    } else if (nonSdkElements.length == 1) {
+      // All but one member was removed
+      return nonSdkElements[0];
+    } else if (nonSdkElements.length == 0) {
+      // All members were removed
+      AnalysisEngine.instance.logger
+          .logInformation("Multiply defined SDK element: $foundElement");
+      return foundElement;
+    }
+    return new MultiplyDefinedElementImpl(
+        _definingLibrary.context, nonSdkElements);
+  }
+}
+
+/**
+ * Instances of the class `LibraryResolver` are used to resolve one or more mutually dependent
+ * libraries within a single context.
+ */
+class LibraryResolver {
+  /**
+   * The analysis context in which the libraries are being analyzed.
+   */
+  final InternalAnalysisContext analysisContext;
+
+  /**
+   * The listener to which analysis errors will be reported, this error listener is either
+   * references [recordingErrorListener], or it unions the passed
+   * [AnalysisErrorListener] with the [recordingErrorListener].
+   */
+  RecordingErrorListener _errorListener;
+
+  /**
+   * A source object representing the core library (dart:core).
+   */
+  Source _coreLibrarySource;
+
+  /**
+   * A Source object representing the async library (dart:async).
+   */
+  Source _asyncLibrarySource;
+
+  /**
+   * The object representing the core library.
+   */
+  Library _coreLibrary;
+
+  /**
+   * The object representing the async library.
+   */
+  Library _asyncLibrary;
+
+  /**
+   * The object used to access the types from the core library.
+   */
+  TypeProvider _typeProvider;
+
+  /**
+   * A table mapping library sources to the information being maintained for those libraries.
+   */
+  HashMap<Source, Library> _libraryMap = new HashMap<Source, Library>();
+
+  /**
+   * A collection containing the libraries that are being resolved together.
+   */
+  Set<Library> _librariesInCycles;
+
+  /**
+   * Initialize a newly created library resolver to resolve libraries within the given context.
+   *
+   * @param analysisContext the analysis context in which the library is being analyzed
+   */
+  LibraryResolver(this.analysisContext) {
+    this._errorListener = new RecordingErrorListener();
+    _coreLibrarySource =
+        analysisContext.sourceFactory.forUri(DartSdk.DART_CORE);
+    _asyncLibrarySource =
+        analysisContext.sourceFactory.forUri(DartSdk.DART_ASYNC);
+  }
+
+  /**
+   * Return the listener to which analysis errors will be reported.
+   *
+   * @return the listener to which analysis errors will be reported
+   */
+  RecordingErrorListener get errorListener => _errorListener;
+
+  /**
+   * Return an array containing information about all of the libraries that were resolved.
+   *
+   * @return an array containing the libraries that were resolved
+   */
+  Set<Library> get resolvedLibraries => _librariesInCycles;
+
+  /**
+   * The object used to access the types from the core library.
+   */
+  TypeProvider get typeProvider => _typeProvider;
+
+  /**
+   * Create an object to represent the information about the library defined by the compilation unit
+   * with the given source.
+   *
+   * @param librarySource the source of the library's defining compilation unit
+   * @return the library object that was created
+   * @throws AnalysisException if the library source is not valid
+   */
+  Library createLibrary(Source librarySource) {
+    Library library =
+        new Library(analysisContext, _errorListener, librarySource);
+    _libraryMap[librarySource] = library;
+    return library;
+  }
+
+  /**
+   * Resolve the library specified by the given source in the given context. The library is assumed
+   * to be embedded in the given source.
+   *
+   * @param librarySource the source specifying the defining compilation unit of the library to be
+   *          resolved
+   * @param unit the compilation unit representing the embedded library
+   * @param fullAnalysis `true` if a full analysis should be performed
+   * @return the element representing the resolved library
+   * @throws AnalysisException if the library could not be resolved for some reason
+   */
+  LibraryElement resolveEmbeddedLibrary(
+      Source librarySource, CompilationUnit unit, bool fullAnalysis) {
+    //
+    // Create the objects representing the library being resolved and the core
+    // library.
+    //
+    Library targetLibrary = _createLibraryWithUnit(librarySource, unit);
+    _coreLibrary = _libraryMap[_coreLibrarySource];
+    if (_coreLibrary == null) {
+      // This will only happen if the library being analyzed is the core
+      // library.
+      _coreLibrary = createLibrary(_coreLibrarySource);
+      if (_coreLibrary == null) {
+        LibraryResolver2.missingCoreLibrary(
+            analysisContext, _coreLibrarySource);
+      }
+    }
+    _asyncLibrary = _libraryMap[_asyncLibrarySource];
+    if (_asyncLibrary == null) {
+      // This will only happen if the library being analyzed is the async
+      // library.
+      _asyncLibrary = createLibrary(_asyncLibrarySource);
+      if (_asyncLibrary == null) {
+        LibraryResolver2.missingAsyncLibrary(
+            analysisContext, _asyncLibrarySource);
+      }
+    }
+    //
+    // Compute the set of libraries that need to be resolved together.
+    //
+    _computeEmbeddedLibraryDependencies(targetLibrary, unit);
+    _librariesInCycles = _computeLibrariesInCycles(targetLibrary);
+    //
+    // Build the element models representing the libraries being resolved.
+    // This is done in three steps:
+    //
+    // 1. Build the basic element models without making any connections
+    //    between elements other than the basic parent/child relationships.
+    //    This includes building the elements representing the libraries.
+    // 2. Build the elements for the import and export directives. This
+    //    requires that we have the elements built for the referenced
+    //    libraries, but because of the possibility of circular references
+    //    needs to happen after all of the library elements have been created.
+    // 3. Build the rest of the type model by connecting superclasses, mixins,
+    //    and interfaces. This requires that we be able to compute the names
+    //    visible in the libraries being resolved, which in turn requires that
+    //    we have resolved the import directives.
+    //
+    _buildElementModels();
+    LibraryElement coreElement = _coreLibrary.libraryElement;
+    if (coreElement == null) {
+      throw new AnalysisException("Could not resolve dart:core");
+    }
+    LibraryElement asyncElement = _asyncLibrary.libraryElement;
+    if (asyncElement == null) {
+      throw new AnalysisException("Could not resolve dart:async");
+    }
+    _buildDirectiveModels();
+    _typeProvider = new TypeProviderImpl(coreElement, asyncElement);
+    _buildTypeAliases();
+    _buildTypeHierarchies();
+    //
+    // Perform resolution and type analysis.
+    //
+    // TODO(brianwilkerson) Decide whether we want to resolve all of the
+    // libraries or whether we want to only resolve the target library.
+    // The advantage to resolving everything is that we have already done part
+    // of the work so we'll avoid duplicated effort. The disadvantage of
+    // resolving everything is that we might do extra work that we don't
+    // really care about. Another possibility is to add a parameter to this
+    // method and punt the decision to the clients.
+    //
+    //if (analyzeAll) {
+    resolveReferencesAndTypes();
+    //} else {
+    //  resolveReferencesAndTypes(targetLibrary);
+    //}
+    _performConstantEvaluation();
+    return targetLibrary.libraryElement;
+  }
+
+  /**
+   * Resolve the library specified by the given source in the given context.
+   *
+   * Note that because Dart allows circular imports between libraries, it is possible that more than
+   * one library will need to be resolved. In such cases the error listener can receive errors from
+   * multiple libraries.
+   *
+   * @param librarySource the source specifying the defining compilation unit of the library to be
+   *          resolved
+   * @param fullAnalysis `true` if a full analysis should be performed
+   * @return the element representing the resolved library
+   * @throws AnalysisException if the library could not be resolved for some reason
+   */
+  LibraryElement resolveLibrary(Source librarySource, bool fullAnalysis) {
+    //
+    // Create the object representing the library being resolved and compute
+    // the dependency relationship.  Note that all libraries depend implicitly
+    // on core, and we inject an ersatz dependency on async, so once this is
+    // done the core and async library elements will have been created.
+    //
+    Library targetLibrary = createLibrary(librarySource);
+    _computeLibraryDependencies(targetLibrary);
+    _coreLibrary = _libraryMap[_coreLibrarySource];
+    _asyncLibrary = _libraryMap[_asyncLibrarySource];
+    //
+    // Compute the set of libraries that need to be resolved together.
+    //
+    _librariesInCycles = _computeLibrariesInCycles(targetLibrary);
+    //
+    // Build the element models representing the libraries being resolved.
+    // This is done in three steps:
+    //
+    // 1. Build the basic element models without making any connections
+    //    between elements other than the basic parent/child relationships.
+    //    This includes building the elements representing the libraries, but
+    //    excludes members defined in enums.
+    // 2. Build the elements for the import and export directives. This
+    //    requires that we have the elements built for the referenced
+    //    libraries, but because of the possibility of circular references
+    //    needs to happen after all of the library elements have been created.
+    // 3. Build the members in enum declarations.
+    // 4. Build the rest of the type model by connecting superclasses, mixins,
+    //    and interfaces. This requires that we be able to compute the names
+    //    visible in the libraries being resolved, which in turn requires that
+    //    we have resolved the import directives.
+    //
+    _buildElementModels();
+    LibraryElement coreElement = _coreLibrary.libraryElement;
+    if (coreElement == null) {
+      throw new AnalysisException("Could not resolve dart:core");
+    }
+    LibraryElement asyncElement = _asyncLibrary.libraryElement;
+    if (asyncElement == null) {
+      throw new AnalysisException("Could not resolve dart:async");
+    }
+    _buildDirectiveModels();
+    _typeProvider = new TypeProviderImpl(coreElement, asyncElement);
+    _buildEnumMembers();
+    _buildTypeAliases();
+    _buildTypeHierarchies();
+    _buildImplicitConstructors();
+    //
+    // Perform resolution and type analysis.
+    //
+    // TODO(brianwilkerson) Decide whether we want to resolve all of the
+    // libraries or whether we want to only resolve the target library. The
+    // advantage to resolving everything is that we have already done part of
+    // the work so we'll avoid duplicated effort. The disadvantage of
+    // resolving everything is that we might do extra work that we don't
+    // really care about. Another possibility is to add a parameter to this
+    // method and punt the decision to the clients.
+    //
+    //if (analyzeAll) {
+    resolveReferencesAndTypes();
+    //} else {
+    //  resolveReferencesAndTypes(targetLibrary);
+    //}
+    _performConstantEvaluation();
+    return targetLibrary.libraryElement;
+  }
+
+  /**
+   * Resolve the identifiers and perform type analysis in the libraries in the current cycle.
+   *
+   * @throws AnalysisException if any of the identifiers could not be resolved or if any of the
+   *           libraries could not have their types analyzed
+   */
+  void resolveReferencesAndTypes() {
+    for (Library library in _librariesInCycles) {
+      _resolveReferencesAndTypesInLibrary(library);
+    }
+  }
+
+  /**
+   * Add a dependency to the given map from the referencing library to the referenced library.
+   *
+   * @param dependencyMap the map to which the dependency is to be added
+   * @param referencingLibrary the library that references the referenced library
+   * @param referencedLibrary the library referenced by the referencing library
+   */
+  void _addDependencyToMap(HashMap<Library, List<Library>> dependencyMap,
+      Library referencingLibrary, Library referencedLibrary) {
+    List<Library> dependentLibraries = dependencyMap[referencedLibrary];
+    if (dependentLibraries == null) {
+      dependentLibraries = new List<Library>();
+      dependencyMap[referencedLibrary] = dependentLibraries;
+    }
+    dependentLibraries.add(referencingLibrary);
+  }
+
+  /**
+   * Given a library that is part of a cycle that includes the root library, add to the given set of
+   * libraries all of the libraries reachable from the root library that are also included in the
+   * cycle.
+   *
+   * @param library the library to be added to the collection of libraries in cycles
+   * @param librariesInCycle a collection of the libraries that are in the cycle
+   * @param dependencyMap a table mapping libraries to the collection of libraries from which those
+   *          libraries are referenced
+   */
+  void _addLibrariesInCycle(Library library, Set<Library> librariesInCycle,
+      HashMap<Library, List<Library>> dependencyMap) {
+    if (librariesInCycle.add(library)) {
+      List<Library> dependentLibraries = dependencyMap[library];
+      if (dependentLibraries != null) {
+        for (Library dependentLibrary in dependentLibraries) {
+          _addLibrariesInCycle(
+              dependentLibrary, librariesInCycle, dependencyMap);
+        }
+      }
+    }
+  }
+
+  /**
+   * Add the given library, and all libraries reachable from it that have not already been visited,
+   * to the given dependency map.
+   *
+   * @param library the library currently being added to the dependency map
+   * @param dependencyMap the dependency map being computed
+   * @param visitedLibraries the libraries that have already been visited, used to prevent infinite
+   *          recursion
+   */
+  void _addToDependencyMap(Library library,
+      HashMap<Library, List<Library>> dependencyMap,
+      Set<Library> visitedLibraries) {
+    if (visitedLibraries.add(library)) {
+      bool asyncFound = false;
+      for (Library referencedLibrary in library.importsAndExports) {
+        _addDependencyToMap(dependencyMap, library, referencedLibrary);
+        _addToDependencyMap(referencedLibrary, dependencyMap, visitedLibraries);
+        if (identical(referencedLibrary, _asyncLibrary)) {
+          asyncFound = true;
+        }
+      }
+      if (!library.explicitlyImportsCore && !identical(library, _coreLibrary)) {
+        _addDependencyToMap(dependencyMap, library, _coreLibrary);
+      }
+      if (!asyncFound && !identical(library, _asyncLibrary)) {
+        _addDependencyToMap(dependencyMap, library, _asyncLibrary);
+        _addToDependencyMap(_asyncLibrary, dependencyMap, visitedLibraries);
+      }
+    }
+  }
+
+  /**
+   * Build the element model representing the combinators declared by the given directive.
+   *
+   * @param directive the directive that declares the combinators
+   * @return an array containing the import combinators that were built
+   */
+  List<NamespaceCombinator> _buildCombinators(NamespaceDirective directive) {
+    List<NamespaceCombinator> combinators = new List<NamespaceCombinator>();
+    for (Combinator combinator in directive.combinators) {
+      if (combinator is HideCombinator) {
+        HideElementCombinatorImpl hide = new HideElementCombinatorImpl();
+        hide.hiddenNames = _getIdentifiers(combinator.hiddenNames);
+        combinators.add(hide);
+      } else {
+        ShowElementCombinatorImpl show = new ShowElementCombinatorImpl();
+        show.offset = combinator.offset;
+        show.end = combinator.end;
+        show.shownNames =
+            _getIdentifiers((combinator as ShowCombinator).shownNames);
+        combinators.add(show);
+      }
+    }
+    return combinators;
+  }
+
+  /**
+   * Every library now has a corresponding [LibraryElement], so it is now possible to resolve
+   * the import and export directives.
+   *
+   * @throws AnalysisException if the defining compilation unit for any of the libraries could not
+   *           be accessed
+   */
+  void _buildDirectiveModels() {
+    for (Library library in _librariesInCycles) {
+      HashMap<String, PrefixElementImpl> nameToPrefixMap =
+          new HashMap<String, PrefixElementImpl>();
+      List<ImportElement> imports = new List<ImportElement>();
+      List<ExportElement> exports = new List<ExportElement>();
+      for (Directive directive in library.definingCompilationUnit.directives) {
+        if (directive is ImportDirective) {
+          ImportDirective importDirective = directive;
+          String uriContent = importDirective.uriContent;
+          if (DartUriResolver.isDartExtUri(uriContent)) {
+            library.libraryElement.hasExtUri = true;
+          }
+          Source importedSource = importDirective.source;
+          if (importedSource != null) {
+            // The imported source will be null if the URI in the import
+            // directive was invalid.
+            Library importedLibrary = _libraryMap[importedSource];
+            if (importedLibrary != null) {
+              ImportElementImpl importElement =
+                  new ImportElementImpl(directive.offset);
+              StringLiteral uriLiteral = importDirective.uri;
+              importElement.uriOffset = uriLiteral.offset;
+              importElement.uriEnd = uriLiteral.end;
+              importElement.uri = uriContent;
+              importElement.deferred = importDirective.deferredKeyword != null;
+              importElement.combinators = _buildCombinators(importDirective);
+              LibraryElement importedLibraryElement =
+                  importedLibrary.libraryElement;
+              if (importedLibraryElement != null) {
+                importElement.importedLibrary = importedLibraryElement;
+              }
+              SimpleIdentifier prefixNode = directive.prefix;
+              if (prefixNode != null) {
+                importElement.prefixOffset = prefixNode.offset;
+                String prefixName = prefixNode.name;
+                PrefixElementImpl prefix = nameToPrefixMap[prefixName];
+                if (prefix == null) {
+                  prefix = new PrefixElementImpl.forNode(prefixNode);
+                  nameToPrefixMap[prefixName] = prefix;
+                }
+                importElement.prefix = prefix;
+                prefixNode.staticElement = prefix;
+              }
+              directive.element = importElement;
+              imports.add(importElement);
+              if (analysisContext.computeKindOf(importedSource) !=
+                  SourceKind.LIBRARY) {
+                ErrorCode errorCode = (importElement.isDeferred
+                    ? StaticWarningCode.IMPORT_OF_NON_LIBRARY
+                    : CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY);
+                _errorListener.onError(new AnalysisError.con2(
+                    library.librarySource, uriLiteral.offset, uriLiteral.length,
+                    errorCode, [uriLiteral.toSource()]));
+              }
+            }
+          }
+        } else if (directive is ExportDirective) {
+          ExportDirective exportDirective = directive;
+          Source exportedSource = exportDirective.source;
+          if (exportedSource != null) {
+            // The exported source will be null if the URI in the export
+            // directive was invalid.
+            Library exportedLibrary = _libraryMap[exportedSource];
+            if (exportedLibrary != null) {
+              ExportElementImpl exportElement =
+                  new ExportElementImpl(directive.offset);
+              StringLiteral uriLiteral = exportDirective.uri;
+              exportElement.uriOffset = uriLiteral.offset;
+              exportElement.uriEnd = uriLiteral.end;
+              exportElement.uri = exportDirective.uriContent;
+              exportElement.combinators = _buildCombinators(exportDirective);
+              LibraryElement exportedLibraryElement =
+                  exportedLibrary.libraryElement;
+              if (exportedLibraryElement != null) {
+                exportElement.exportedLibrary = exportedLibraryElement;
+              }
+              directive.element = exportElement;
+              exports.add(exportElement);
+              if (analysisContext.computeKindOf(exportedSource) !=
+                  SourceKind.LIBRARY) {
+                _errorListener.onError(new AnalysisError.con2(
+                    library.librarySource, uriLiteral.offset, uriLiteral.length,
+                    CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY,
+                    [uriLiteral.toSource()]));
+              }
+            }
+          }
+        }
+      }
+      Source librarySource = library.librarySource;
+      if (!library.explicitlyImportsCore &&
+          _coreLibrarySource != librarySource) {
+        ImportElementImpl importElement = new ImportElementImpl(-1);
+        importElement.importedLibrary = _coreLibrary.libraryElement;
+        importElement.synthetic = true;
+        imports.add(importElement);
+      }
+      LibraryElementImpl libraryElement = library.libraryElement;
+      libraryElement.imports = imports;
+      libraryElement.exports = exports;
+      if (libraryElement.entryPoint == null) {
+        Namespace namespace = new NamespaceBuilder()
+            .createExportNamespaceForLibrary(libraryElement);
+        Element element = namespace.get(FunctionElement.MAIN_FUNCTION_NAME);
+        if (element is FunctionElement) {
+          libraryElement.entryPoint = element;
+        }
+      }
+    }
+  }
+
+  /**
+   * Build element models for all of the libraries in the current cycle.
+   *
+   * @throws AnalysisException if any of the element models cannot be built
+   */
+  void _buildElementModels() {
+    for (Library library in _librariesInCycles) {
+      LibraryElementBuilder builder =
+          new LibraryElementBuilder(analysisContext, errorListener);
+      LibraryElementImpl libraryElement = builder.buildLibrary(library);
+      library.libraryElement = libraryElement;
+    }
+  }
+
+  /**
+   * Build the members in enum declarations. This cannot be done while building the rest of the
+   * element model because it depends on being able to access core types, which cannot happen until
+   * the rest of the element model has been built (when resolving the core library).
+   *
+   * @throws AnalysisException if any of the enum members could not be built
+   */
+  void _buildEnumMembers() {
+    PerformanceStatistics.resolve.makeCurrentWhile(() {
+      for (Library library in _librariesInCycles) {
+        for (Source source in library.compilationUnitSources) {
+          EnumMemberBuilder builder = new EnumMemberBuilder(_typeProvider);
+          library.getAST(source).accept(builder);
+        }
+      }
+    });
+  }
+
+  /**
+   * Finish steps that the [buildTypeHierarchies] could not perform, see
+   * [ImplicitConstructorBuilder].
+   *
+   * @throws AnalysisException if any of the type hierarchies could not be resolved
+   */
+  void _buildImplicitConstructors() {
+    PerformanceStatistics.resolve.makeCurrentWhile(() {
+      ImplicitConstructorComputer computer = new ImplicitConstructorComputer();
+      for (Library library in _librariesInCycles) {
+        computer.add(_errorListener, library.libraryElement);
+      }
+      computer.compute();
+    });
+  }
+
+  /**
+   * Resolve the types referenced by function type aliases across all of the function type aliases
+   * defined in the current cycle.
+   *
+   * @throws AnalysisException if any of the function type aliases could not be resolved
+   */
+  void _buildTypeAliases() {
+    PerformanceStatistics.resolve.makeCurrentWhile(() {
+      List<LibraryResolver_TypeAliasInfo> typeAliases =
+          new List<LibraryResolver_TypeAliasInfo>();
+      for (Library library in _librariesInCycles) {
+        for (Source source in library.compilationUnitSources) {
+          CompilationUnit ast = library.getAST(source);
+          for (CompilationUnitMember member in ast.declarations) {
+            if (member is FunctionTypeAlias) {
+              typeAliases.add(
+                  new LibraryResolver_TypeAliasInfo(library, source, member));
+            }
+          }
+        }
+      }
+      // TODO(brianwilkerson) We need to sort the type aliases such that all
+      // aliases referenced by an alias T are resolved before we resolve T.
+      for (LibraryResolver_TypeAliasInfo info in typeAliases) {
+        TypeResolverVisitor visitor = new TypeResolverVisitor.con1(
+            info._library, info._source, _typeProvider);
+        info._typeAlias.accept(visitor);
+      }
+    });
+  }
+
+  /**
+   * Resolve the type hierarchy across all of the types declared in the libraries in the current
+   * cycle.
+   *
+   * @throws AnalysisException if any of the type hierarchies could not be resolved
+   */
+  void _buildTypeHierarchies() {
+    PerformanceStatistics.resolve.makeCurrentWhile(() {
+      for (Library library in _librariesInCycles) {
+        for (Source source in library.compilationUnitSources) {
+          TypeResolverVisitorFactory typeResolverVisitorFactory =
+              analysisContext.typeResolverVisitorFactory;
+          TypeResolverVisitor visitor = (typeResolverVisitorFactory == null)
+              ? new TypeResolverVisitor.con1(library, source, _typeProvider)
+              : typeResolverVisitorFactory(library, source, _typeProvider);
+          library.getAST(source).accept(visitor);
+        }
+      }
+    });
+  }
+
+  /**
+   * Compute a dependency map of libraries reachable from the given library. A dependency map is a
+   * table that maps individual libraries to a list of the libraries that either import or export
+   * those libraries.
+   *
+   * This map is used to compute all of the libraries involved in a cycle that include the root
+   * library. Given that we only add libraries that are reachable from the root library, when we
+   * work backward we are guaranteed to only get libraries in the cycle.
+   *
+   * @param library the library currently being added to the dependency map
+   */
+  HashMap<Library, List<Library>> _computeDependencyMap(Library library) {
+    HashMap<Library, List<Library>> dependencyMap =
+        new HashMap<Library, List<Library>>();
+    _addToDependencyMap(library, dependencyMap, new HashSet<Library>());
+    return dependencyMap;
+  }
+
+  /**
+   * Recursively traverse the libraries reachable from the given library, creating instances of the
+   * class [Library] to represent them, and record the references in the library objects.
+   *
+   * @param library the library to be processed to find libraries that have not yet been traversed
+   * @throws AnalysisException if some portion of the library graph could not be traversed
+   */
+  void _computeEmbeddedLibraryDependencies(
+      Library library, CompilationUnit unit) {
+    Source librarySource = library.librarySource;
+    HashSet<Source> exportedSources = new HashSet<Source>();
+    HashSet<Source> importedSources = new HashSet<Source>();
+    for (Directive directive in unit.directives) {
+      if (directive is ExportDirective) {
+        Source exportSource = _resolveSource(librarySource, directive);
+        if (exportSource != null) {
+          exportedSources.add(exportSource);
+        }
+      } else if (directive is ImportDirective) {
+        Source importSource = _resolveSource(librarySource, directive);
+        if (importSource != null) {
+          importedSources.add(importSource);
+        }
+      }
+    }
+    _computeLibraryDependenciesFromDirectives(library,
+        new List.from(importedSources), new List.from(exportedSources));
+  }
+
+  /**
+   * Return a collection containing all of the libraries reachable from the given library that are
+   * contained in a cycle that includes the given library.
+   *
+   * @param library the library that must be included in any cycles whose members are to be returned
+   * @return all of the libraries referenced by the given library that have a circular reference
+   *         back to the given library
+   */
+  Set<Library> _computeLibrariesInCycles(Library library) {
+    HashMap<Library, List<Library>> dependencyMap =
+        _computeDependencyMap(library);
+    Set<Library> librariesInCycle = new HashSet<Library>();
+    _addLibrariesInCycle(library, librariesInCycle, dependencyMap);
+    return librariesInCycle;
+  }
+
+  /**
+   * Recursively traverse the libraries reachable from the given library, creating instances of the
+   * class [Library] to represent them, and record the references in the library objects.
+   *
+   * @param library the library to be processed to find libraries that have not yet been traversed
+   * @throws AnalysisException if some portion of the library graph could not be traversed
+   */
+  void _computeLibraryDependencies(Library library) {
+    Source librarySource = library.librarySource;
+    _computeLibraryDependenciesFromDirectives(library,
+        analysisContext.computeImportedLibraries(librarySource),
+        analysisContext.computeExportedLibraries(librarySource));
+  }
+
+  /**
+   * Recursively traverse the libraries reachable from the given library, creating instances of the
+   * class [Library] to represent them, and record the references in the library objects.
+   *
+   * @param library the library to be processed to find libraries that have not yet been traversed
+   * @param importedSources an array containing the sources that are imported into the given library
+   * @param exportedSources an array containing the sources that are exported from the given library
+   * @throws AnalysisException if some portion of the library graph could not be traversed
+   */
+  void _computeLibraryDependenciesFromDirectives(Library library,
+      List<Source> importedSources, List<Source> exportedSources) {
+    List<Library> importedLibraries = new List<Library>();
+    bool explicitlyImportsCore = false;
+    bool importsAsync = false;
+    for (Source importedSource in importedSources) {
+      if (importedSource == _coreLibrarySource) {
+        explicitlyImportsCore = true;
+      }
+      if (importedSource == _asyncLibrarySource) {
+        importsAsync = true;
+      }
+      Library importedLibrary = _libraryMap[importedSource];
+      if (importedLibrary == null) {
+        importedLibrary = _createLibraryOrNull(importedSource);
+        if (importedLibrary != null) {
+          _computeLibraryDependencies(importedLibrary);
+        }
+      }
+      if (importedLibrary != null) {
+        importedLibraries.add(importedLibrary);
+      }
+    }
+    library.importedLibraries = importedLibraries;
+    List<Library> exportedLibraries = new List<Library>();
+    for (Source exportedSource in exportedSources) {
+      Library exportedLibrary = _libraryMap[exportedSource];
+      if (exportedLibrary == null) {
+        exportedLibrary = _createLibraryOrNull(exportedSource);
+        if (exportedLibrary != null) {
+          _computeLibraryDependencies(exportedLibrary);
+        }
+      }
+      if (exportedLibrary != null) {
+        exportedLibraries.add(exportedLibrary);
+      }
+    }
+    library.exportedLibraries = exportedLibraries;
+    library.explicitlyImportsCore = explicitlyImportsCore;
+    if (!explicitlyImportsCore && _coreLibrarySource != library.librarySource) {
+      Library importedLibrary = _libraryMap[_coreLibrarySource];
+      if (importedLibrary == null) {
+        importedLibrary = _createLibraryOrNull(_coreLibrarySource);
+        if (importedLibrary != null) {
+          _computeLibraryDependencies(importedLibrary);
+        }
+      }
+    }
+    if (!importsAsync && _asyncLibrarySource != library.librarySource) {
+      Library importedLibrary = _libraryMap[_asyncLibrarySource];
+      if (importedLibrary == null) {
+        importedLibrary = _createLibraryOrNull(_asyncLibrarySource);
+        if (importedLibrary != null) {
+          _computeLibraryDependencies(importedLibrary);
+        }
+      }
+    }
+  }
+
+  /**
+   * Create an object to represent the information about the library defined by the compilation unit
+   * with the given source. Return the library object that was created, or `null` if the
+   * source is not valid.
+   *
+   * @param librarySource the source of the library's defining compilation unit
+   * @return the library object that was created
+   */
+  Library _createLibraryOrNull(Source librarySource) {
+    if (!analysisContext.exists(librarySource)) {
+      return null;
+    }
+    Library library =
+        new Library(analysisContext, _errorListener, librarySource);
+    _libraryMap[librarySource] = library;
+    return library;
+  }
+
+  /**
+   * Create an object to represent the information about the library defined by the compilation unit
+   * with the given source.
+   *
+   * @param librarySource the source of the library's defining compilation unit
+   * @param unit the compilation unit that defines the library
+   * @return the library object that was created
+   * @throws AnalysisException if the library source is not valid
+   */
+  Library _createLibraryWithUnit(Source librarySource, CompilationUnit unit) {
+    Library library =
+        new Library(analysisContext, _errorListener, librarySource);
+    library.setDefiningCompilationUnit(unit);
+    _libraryMap[librarySource] = library;
+    return library;
+  }
+
+  /**
+   * Return an array containing the lexical identifiers associated with the nodes in the given list.
+   *
+   * @param names the AST nodes representing the identifiers
+   * @return the lexical identifiers associated with the nodes in the list
+   */
+  List<String> _getIdentifiers(NodeList<SimpleIdentifier> names) {
+    int count = names.length;
+    List<String> identifiers = new List<String>(count);
+    for (int i = 0; i < count; i++) {
+      identifiers[i] = names[i].name;
+    }
+    return identifiers;
+  }
+
+  /**
+   * Compute a value for all of the constants in the libraries being analyzed.
+   */
+  void _performConstantEvaluation() {
+    PerformanceStatistics.resolve.makeCurrentWhile(() {
+      ConstantValueComputer computer = new ConstantValueComputer(
+          _typeProvider, analysisContext.declaredVariables);
+      for (Library library in _librariesInCycles) {
+        for (Source source in library.compilationUnitSources) {
+          try {
+            CompilationUnit unit = library.getAST(source);
+            if (unit != null) {
+              computer.add(unit);
+            }
+          } on AnalysisException catch (exception, stackTrace) {
+            AnalysisEngine.instance.logger.logError(
+                "Internal Error: Could not access AST for ${source.fullName} during constant evaluation",
+                new CaughtException(exception, stackTrace));
+          }
+        }
+      }
+      computer.computeValues();
+      // As a temporary workaround for issue 21572, run ConstantVerifier now.
+      // TODO(paulberry): remove this workaround once issue 21572 is fixed.
+      for (Library library in _librariesInCycles) {
+        for (Source source in library.compilationUnitSources) {
+          try {
+            CompilationUnit unit = library.getAST(source);
+            ErrorReporter errorReporter =
+                new ErrorReporter(_errorListener, source);
+            ConstantVerifier constantVerifier = new ConstantVerifier(
+                errorReporter, library.libraryElement, _typeProvider);
+            unit.accept(constantVerifier);
+          } on AnalysisException catch (exception, stackTrace) {
+            AnalysisEngine.instance.logger.logError(
+                "Internal Error: Could not access AST for ${source.fullName} "
+                "during constant verification",
+                new CaughtException(exception, stackTrace));
+          }
+        }
+      }
+    });
+  }
+
+  /**
+   * Resolve the identifiers and perform type analysis in the given library.
+   *
+   * @param library the library to be resolved
+   * @throws AnalysisException if any of the identifiers could not be resolved or if the types in
+   *           the library cannot be analyzed
+   */
+  void _resolveReferencesAndTypesInLibrary(Library library) {
+    PerformanceStatistics.resolve.makeCurrentWhile(() {
+      for (Source source in library.compilationUnitSources) {
+        CompilationUnit ast = library.getAST(source);
+        ast.accept(
+            new VariableResolverVisitor.con1(library, source, _typeProvider));
+        ResolverVisitorFactory visitorFactory =
+            analysisContext.resolverVisitorFactory;
+        ResolverVisitor visitor = visitorFactory != null
+            ? visitorFactory(library, source, _typeProvider)
+            : new ResolverVisitor.con1(library, source, _typeProvider);
+        ast.accept(visitor);
+      }
+    });
+  }
+
+  /**
+   * Return the result of resolving the URI of the given URI-based directive against the URI of the
+   * given library, or `null` if the URI is not valid.
+   *
+   * @param librarySource the source representing the library containing the directive
+   * @param directive the directive which URI should be resolved
+   * @return the result of resolving the URI against the URI of the library
+   */
+  Source _resolveSource(Source librarySource, UriBasedDirective directive) {
+    StringLiteral uriLiteral = directive.uri;
+    if (uriLiteral is StringInterpolation) {
+      return null;
+    }
+    String uriContent = uriLiteral.stringValue.trim();
+    if (uriContent == null || uriContent.isEmpty) {
+      return null;
+    }
+    uriContent = Uri.encodeFull(uriContent);
+    return analysisContext.sourceFactory.resolveUri(librarySource, uriContent);
+  }
+}
+
+/**
+ * Instances of the class `LibraryResolver` are used to resolve one or more mutually dependent
+ * libraries within a single context.
+ */
+class LibraryResolver2 {
+  /**
+   * The analysis context in which the libraries are being analyzed.
+   */
+  final InternalAnalysisContext analysisContext;
+
+  /**
+   * The listener to which analysis errors will be reported, this error listener is either
+   * references [recordingErrorListener], or it unions the passed
+   * [AnalysisErrorListener] with the [recordingErrorListener].
+   */
+  RecordingErrorListener _errorListener;
+
+  /**
+   * A source object representing the core library (dart:core).
+   */
+  Source _coreLibrarySource;
+
+  /**
+   * A source object representing the async library (dart:async).
+   */
+  Source _asyncLibrarySource;
+
+  /**
+   * The object representing the core library.
+   */
+  ResolvableLibrary _coreLibrary;
+
+  /**
+   * The object representing the async library.
+   */
+  ResolvableLibrary _asyncLibrary;
+
+  /**
+   * The object used to access the types from the core library.
+   */
+  TypeProvider _typeProvider;
+
+  /**
+   * A table mapping library sources to the information being maintained for those libraries.
+   */
+  HashMap<Source, ResolvableLibrary> _libraryMap =
+      new HashMap<Source, ResolvableLibrary>();
+
+  /**
+   * A collection containing the libraries that are being resolved together.
+   */
+  List<ResolvableLibrary> _librariesInCycle;
+
+  /**
+   * Initialize a newly created library resolver to resolve libraries within the given context.
+   *
+   * @param analysisContext the analysis context in which the library is being analyzed
+   */
+  LibraryResolver2(this.analysisContext) {
+    this._errorListener = new RecordingErrorListener();
+    _coreLibrarySource =
+        analysisContext.sourceFactory.forUri(DartSdk.DART_CORE);
+    _asyncLibrarySource =
+        analysisContext.sourceFactory.forUri(DartSdk.DART_ASYNC);
+  }
+
+  /**
+   * Return the listener to which analysis errors will be reported.
+   *
+   * @return the listener to which analysis errors will be reported
+   */
+  RecordingErrorListener get errorListener => _errorListener;
+
+  /**
+   * Return an array containing information about all of the libraries that were resolved.
+   *
+   * @return an array containing the libraries that were resolved
+   */
+  List<ResolvableLibrary> get resolvedLibraries => _librariesInCycle;
+
+  /**
+   * Resolve the library specified by the given source in the given context.
+   *
+   * Note that because Dart allows circular imports between libraries, it is possible that more than
+   * one library will need to be resolved. In such cases the error listener can receive errors from
+   * multiple libraries.
+   *
+   * @param librarySource the source specifying the defining compilation unit of the library to be
+   *          resolved
+   * @param fullAnalysis `true` if a full analysis should be performed
+   * @return the element representing the resolved library
+   * @throws AnalysisException if the library could not be resolved for some reason
+   */
+  LibraryElement resolveLibrary(
+      Source librarySource, List<ResolvableLibrary> librariesInCycle) {
+    //
+    // Build the map of libraries that are known.
+    //
+    this._librariesInCycle = librariesInCycle;
+    _libraryMap = _buildLibraryMap();
+    ResolvableLibrary targetLibrary = _libraryMap[librarySource];
+    _coreLibrary = _libraryMap[_coreLibrarySource];
+    _asyncLibrary = _libraryMap[_asyncLibrarySource];
+    //
+    // Build the element models representing the libraries being resolved.
+    // This is done in three steps:
+    //
+    // 1. Build the basic element models without making any connections
+    //    between elements other than the basic parent/child relationships.
+    //    This includes building the elements representing the libraries, but
+    //    excludes members defined in enums.
+    // 2. Build the elements for the import and export directives. This
+    //    requires that we have the elements built for the referenced
+    //    libraries, but because of the possibility of circular references
+    //    needs to happen after all of the library elements have been created.
+    // 3. Build the members in enum declarations.
+    // 4. Build the rest of the type model by connecting superclasses, mixins,
+    //    and interfaces. This requires that we be able to compute the names
+    //    visible in the libraries being resolved, which in turn requires that
+    //    we have resolved the import directives.
+    //
+    _buildElementModels();
+    LibraryElement coreElement = _coreLibrary.libraryElement;
+    if (coreElement == null) {
+      missingCoreLibrary(analysisContext, _coreLibrarySource);
+    }
+    LibraryElement asyncElement = _asyncLibrary.libraryElement;
+    if (asyncElement == null) {
+      missingAsyncLibrary(analysisContext, _asyncLibrarySource);
+    }
+    _buildDirectiveModels();
+    _typeProvider = new TypeProviderImpl(coreElement, asyncElement);
+    _buildEnumMembers();
+    _buildTypeAliases();
+    _buildTypeHierarchies();
+    _buildImplicitConstructors();
+    //
+    // Perform resolution and type analysis.
+    //
+    // TODO(brianwilkerson) Decide whether we want to resolve all of the
+    // libraries or whether we want to only resolve the target library. The
+    // advantage to resolving everything is that we have already done part of
+    // the work so we'll avoid duplicated effort. The disadvantage of
+    // resolving everything is that we might do extra work that we don't
+    // really care about. Another possibility is to add a parameter to this
+    // method and punt the decision to the clients.
+    //
+    //if (analyzeAll) {
+    _resolveReferencesAndTypes();
+    //} else {
+    //  resolveReferencesAndTypes(targetLibrary);
+    //}
+    _performConstantEvaluation();
+    return targetLibrary.libraryElement;
+  }
+
+  /**
+   * Build the element model representing the combinators declared by the given directive.
+   *
+   * @param directive the directive that declares the combinators
+   * @return an array containing the import combinators that were built
+   */
+  List<NamespaceCombinator> _buildCombinators(NamespaceDirective directive) {
+    List<NamespaceCombinator> combinators = new List<NamespaceCombinator>();
+    for (Combinator combinator in directive.combinators) {
+      if (combinator is HideCombinator) {
+        HideElementCombinatorImpl hide = new HideElementCombinatorImpl();
+        hide.hiddenNames = _getIdentifiers(combinator.hiddenNames);
+        combinators.add(hide);
+      } else {
+        ShowElementCombinatorImpl show = new ShowElementCombinatorImpl();
+        show.offset = combinator.offset;
+        show.end = combinator.end;
+        show.shownNames =
+            _getIdentifiers((combinator as ShowCombinator).shownNames);
+        combinators.add(show);
+      }
+    }
+    return combinators;
+  }
+
+  /**
+   * Every library now has a corresponding [LibraryElement], so it is now possible to resolve
+   * the import and export directives.
+   *
+   * @throws AnalysisException if the defining compilation unit for any of the libraries could not
+   *           be accessed
+   */
+  void _buildDirectiveModels() {
+    for (ResolvableLibrary library in _librariesInCycle) {
+      HashMap<String, PrefixElementImpl> nameToPrefixMap =
+          new HashMap<String, PrefixElementImpl>();
+      List<ImportElement> imports = new List<ImportElement>();
+      List<ExportElement> exports = new List<ExportElement>();
+      for (Directive directive in library.definingCompilationUnit.directives) {
+        if (directive is ImportDirective) {
+          ImportDirective importDirective = directive;
+          String uriContent = importDirective.uriContent;
+          if (DartUriResolver.isDartExtUri(uriContent)) {
+            library.libraryElement.hasExtUri = true;
+          }
+          Source importedSource = importDirective.source;
+          if (importedSource != null &&
+              analysisContext.exists(importedSource)) {
+            // The imported source will be null if the URI in the import
+            // directive was invalid.
+            ResolvableLibrary importedLibrary = _libraryMap[importedSource];
+            if (importedLibrary != null) {
+              ImportElementImpl importElement =
+                  new ImportElementImpl(directive.offset);
+              StringLiteral uriLiteral = importDirective.uri;
+              if (uriLiteral != null) {
+                importElement.uriOffset = uriLiteral.offset;
+                importElement.uriEnd = uriLiteral.end;
+              }
+              importElement.uri = uriContent;
+              importElement.deferred = importDirective.deferredKeyword != null;
+              importElement.combinators = _buildCombinators(importDirective);
+              LibraryElement importedLibraryElement =
+                  importedLibrary.libraryElement;
+              if (importedLibraryElement != null) {
+                importElement.importedLibrary = importedLibraryElement;
+              }
+              SimpleIdentifier prefixNode = directive.prefix;
+              if (prefixNode != null) {
+                importElement.prefixOffset = prefixNode.offset;
+                String prefixName = prefixNode.name;
+                PrefixElementImpl prefix = nameToPrefixMap[prefixName];
+                if (prefix == null) {
+                  prefix = new PrefixElementImpl.forNode(prefixNode);
+                  nameToPrefixMap[prefixName] = prefix;
+                }
+                importElement.prefix = prefix;
+                prefixNode.staticElement = prefix;
+              }
+              directive.element = importElement;
+              imports.add(importElement);
+              if (analysisContext.computeKindOf(importedSource) !=
+                  SourceKind.LIBRARY) {
+                ErrorCode errorCode = (importElement.isDeferred
+                    ? StaticWarningCode.IMPORT_OF_NON_LIBRARY
+                    : CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY);
+                _errorListener.onError(new AnalysisError.con2(
+                    library.librarySource, uriLiteral.offset, uriLiteral.length,
+                    errorCode, [uriLiteral.toSource()]));
+              }
+            }
+          }
+        } else if (directive is ExportDirective) {
+          ExportDirective exportDirective = directive;
+          Source exportedSource = exportDirective.source;
+          if (exportedSource != null &&
+              analysisContext.exists(exportedSource)) {
+            // The exported source will be null if the URI in the export
+            // directive was invalid.
+            ResolvableLibrary exportedLibrary = _libraryMap[exportedSource];
+            if (exportedLibrary != null) {
+              ExportElementImpl exportElement =
+                  new ExportElementImpl(directive.offset);
+              StringLiteral uriLiteral = exportDirective.uri;
+              if (uriLiteral != null) {
+                exportElement.uriOffset = uriLiteral.offset;
+                exportElement.uriEnd = uriLiteral.end;
+              }
+              exportElement.uri = exportDirective.uriContent;
+              exportElement.combinators = _buildCombinators(exportDirective);
+              LibraryElement exportedLibraryElement =
+                  exportedLibrary.libraryElement;
+              if (exportedLibraryElement != null) {
+                exportElement.exportedLibrary = exportedLibraryElement;
+              }
+              directive.element = exportElement;
+              exports.add(exportElement);
+              if (analysisContext.computeKindOf(exportedSource) !=
+                  SourceKind.LIBRARY) {
+                _errorListener.onError(new AnalysisError.con2(
+                    library.librarySource, uriLiteral.offset, uriLiteral.length,
+                    CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY,
+                    [uriLiteral.toSource()]));
+              }
+            }
+          }
+        }
+      }
+      Source librarySource = library.librarySource;
+      if (!library.explicitlyImportsCore &&
+          _coreLibrarySource != librarySource) {
+        ImportElementImpl importElement = new ImportElementImpl(-1);
+        importElement.importedLibrary = _coreLibrary.libraryElement;
+        importElement.synthetic = true;
+        imports.add(importElement);
+      }
+      LibraryElementImpl libraryElement = library.libraryElement;
+      libraryElement.imports = imports;
+      libraryElement.exports = exports;
+      if (libraryElement.entryPoint == null) {
+        Namespace namespace = new NamespaceBuilder()
+            .createExportNamespaceForLibrary(libraryElement);
+        Element element = namespace.get(FunctionElement.MAIN_FUNCTION_NAME);
+        if (element is FunctionElement) {
+          libraryElement.entryPoint = element;
+        }
+      }
+    }
+  }
+
+  /**
+   * Build element models for all of the libraries in the current cycle.
+   *
+   * @throws AnalysisException if any of the element models cannot be built
+   */
+  void _buildElementModels() {
+    for (ResolvableLibrary library in _librariesInCycle) {
+      LibraryElementBuilder builder =
+          new LibraryElementBuilder(analysisContext, errorListener);
+      builder.buildLibrary2(library);
+    }
+  }
+
+  /**
+   * Build the members in enum declarations. This cannot be done while building the rest of the
+   * element model because it depends on being able to access core types, which cannot happen until
+   * the rest of the element model has been built (when resolving the core library).
+   *
+   * @throws AnalysisException if any of the enum members could not be built
+   */
+  void _buildEnumMembers() {
+    PerformanceStatistics.resolve.makeCurrentWhile(() {
+      for (ResolvableLibrary library in _librariesInCycle) {
+        for (Source source in library.compilationUnitSources) {
+          EnumMemberBuilder builder = new EnumMemberBuilder(_typeProvider);
+          library.getAST(source).accept(builder);
+        }
+      }
+    });
+  }
+
+  /**
+   * Finish steps that the [buildTypeHierarchies] could not perform, see
+   * [ImplicitConstructorBuilder].
+   *
+   * @throws AnalysisException if any of the type hierarchies could not be resolved
+   */
+  void _buildImplicitConstructors() {
+    PerformanceStatistics.resolve.makeCurrentWhile(() {
+      ImplicitConstructorComputer computer = new ImplicitConstructorComputer();
+      for (ResolvableLibrary library in _librariesInCycle) {
+        computer.add(_errorListener, library.libraryElement);
+      }
+      computer.compute();
+    });
+  }
+
+  HashMap<Source, ResolvableLibrary> _buildLibraryMap() {
+    HashMap<Source, ResolvableLibrary> libraryMap =
+        new HashMap<Source, ResolvableLibrary>();
+    int libraryCount = _librariesInCycle.length;
+    for (int i = 0; i < libraryCount; i++) {
+      ResolvableLibrary library = _librariesInCycle[i];
+      library.errorListener = _errorListener;
+      libraryMap[library.librarySource] = library;
+      List<ResolvableLibrary> dependencies = library.importsAndExports;
+      int dependencyCount = dependencies.length;
+      for (int j = 0; j < dependencyCount; j++) {
+        ResolvableLibrary dependency = dependencies[j];
+        //dependency.setErrorListener(errorListener);
+        libraryMap[dependency.librarySource] = dependency;
+      }
+    }
+    return libraryMap;
+  }
+
+  /**
+   * Resolve the types referenced by function type aliases across all of the function type aliases
+   * defined in the current cycle.
+   *
+   * @throws AnalysisException if any of the function type aliases could not be resolved
+   */
+  void _buildTypeAliases() {
+    PerformanceStatistics.resolve.makeCurrentWhile(() {
+      List<LibraryResolver2_TypeAliasInfo> typeAliases =
+          new List<LibraryResolver2_TypeAliasInfo>();
+      for (ResolvableLibrary library in _librariesInCycle) {
+        for (ResolvableCompilationUnit unit
+            in library.resolvableCompilationUnits) {
+          for (CompilationUnitMember member
+              in unit.compilationUnit.declarations) {
+            if (member is FunctionTypeAlias) {
+              typeAliases.add(new LibraryResolver2_TypeAliasInfo(
+                  library, unit.source, member));
+            }
+          }
+        }
+      }
+      // TODO(brianwilkerson) We need to sort the type aliases such that all
+      // aliases referenced by an alias T are resolved before we resolve T.
+      for (LibraryResolver2_TypeAliasInfo info in typeAliases) {
+        TypeResolverVisitor visitor = new TypeResolverVisitor.con4(
+            info._library, info._source, _typeProvider);
+        info._typeAlias.accept(visitor);
+      }
+    });
+  }
+
+  /**
+   * Resolve the type hierarchy across all of the types declared in the libraries in the current
+   * cycle.
+   *
+   * @throws AnalysisException if any of the type hierarchies could not be resolved
+   */
+  void _buildTypeHierarchies() {
+    PerformanceStatistics.resolve.makeCurrentWhile(() {
+      for (ResolvableLibrary library in _librariesInCycle) {
+        for (ResolvableCompilationUnit unit
+            in library.resolvableCompilationUnits) {
+          Source source = unit.source;
+          CompilationUnit ast = unit.compilationUnit;
+          TypeResolverVisitor visitor =
+              new TypeResolverVisitor.con4(library, source, _typeProvider);
+          ast.accept(visitor);
+        }
+      }
+    });
+  }
+
+  /**
+   * Return an array containing the lexical identifiers associated with the nodes in the given list.
+   *
+   * @param names the AST nodes representing the identifiers
+   * @return the lexical identifiers associated with the nodes in the list
+   */
+  List<String> _getIdentifiers(NodeList<SimpleIdentifier> names) {
+    int count = names.length;
+    List<String> identifiers = new List<String>(count);
+    for (int i = 0; i < count; i++) {
+      identifiers[i] = names[i].name;
+    }
+    return identifiers;
+  }
+
+  /**
+   * Compute a value for all of the constants in the libraries being analyzed.
+   */
+  void _performConstantEvaluation() {
+    PerformanceStatistics.resolve.makeCurrentWhile(() {
+      ConstantValueComputer computer = new ConstantValueComputer(
+          _typeProvider, analysisContext.declaredVariables);
+      for (ResolvableLibrary library in _librariesInCycle) {
+        for (ResolvableCompilationUnit unit
+            in library.resolvableCompilationUnits) {
+          CompilationUnit ast = unit.compilationUnit;
+          if (ast != null) {
+            computer.add(ast);
+          }
+        }
+      }
+      computer.computeValues();
+      // As a temporary workaround for issue 21572, run ConstantVerifier now.
+      // TODO(paulberry): remove this workaround once issue 21572 is fixed.
+      for (ResolvableLibrary library in _librariesInCycle) {
+        for (ResolvableCompilationUnit unit
+            in library.resolvableCompilationUnits) {
+          CompilationUnit ast = unit.compilationUnit;
+          ErrorReporter errorReporter =
+              new ErrorReporter(_errorListener, unit.source);
+          ConstantVerifier constantVerifier = new ConstantVerifier(
+              errorReporter, library.libraryElement, _typeProvider);
+          ast.accept(constantVerifier);
+        }
+      }
+    });
+  }
+
+  /**
+   * Resolve the identifiers and perform type analysis in the libraries in the current cycle.
+   *
+   * @throws AnalysisException if any of the identifiers could not be resolved or if any of the
+   *           libraries could not have their types analyzed
+   */
+  void _resolveReferencesAndTypes() {
+    for (ResolvableLibrary library in _librariesInCycle) {
+      _resolveReferencesAndTypesInLibrary(library);
+    }
+  }
+
+  /**
+   * Resolve the identifiers and perform type analysis in the given library.
+   *
+   * @param library the library to be resolved
+   * @throws AnalysisException if any of the identifiers could not be resolved or if the types in
+   *           the library cannot be analyzed
+   */
+  void _resolveReferencesAndTypesInLibrary(ResolvableLibrary library) {
+    PerformanceStatistics.resolve.makeCurrentWhile(() {
+      for (ResolvableCompilationUnit unit
+          in library.resolvableCompilationUnits) {
+        Source source = unit.source;
+        CompilationUnit ast = unit.compilationUnit;
+        ast.accept(
+            new VariableResolverVisitor.con3(library, source, _typeProvider));
+        ResolverVisitor visitor =
+            new ResolverVisitor.con4(library, source, _typeProvider);
+        ast.accept(visitor);
+      }
+    });
+  }
+
+  /**
+   * Report that the async library could not be resolved in the given
+   * [analysisContext] and throw an exception.  [asyncLibrarySource] is the source
+   * representing the async library.
+   */
+  static void missingAsyncLibrary(
+      AnalysisContext analysisContext, Source asyncLibrarySource) {
+    throw new AnalysisException("Could not resolve dart:async");
+  }
+
+  /**
+   * Report that the core library could not be resolved in the given analysis context and throw an
+   * exception.
+   *
+   * @param analysisContext the analysis context in which the failure occurred
+   * @param coreLibrarySource the source representing the core library
+   * @throws AnalysisException always
+   */
+  static void missingCoreLibrary(
+      AnalysisContext analysisContext, Source coreLibrarySource) {
+    throw new AnalysisException("Could not resolve dart:core");
+  }
+}
+
+/**
+ * Instances of the class `TypeAliasInfo` hold information about a [TypeAlias].
+ */
+class LibraryResolver2_TypeAliasInfo {
+  final ResolvableLibrary _library;
+
+  final Source _source;
+
+  final FunctionTypeAlias _typeAlias;
+
+  /**
+   * Initialize a newly created information holder with the given information.
+   *
+   * @param library the library containing the type alias
+   * @param source the source of the file containing the type alias
+   * @param typeAlias the type alias being remembered
+   */
+  LibraryResolver2_TypeAliasInfo(this._library, this._source, this._typeAlias);
+}
+
+/**
+ * Instances of the class `TypeAliasInfo` hold information about a [TypeAlias].
+ */
+class LibraryResolver_TypeAliasInfo {
+  final Library _library;
+
+  final Source _source;
+
+  final FunctionTypeAlias _typeAlias;
+
+  /**
+   * Initialize a newly created information holder with the given information.
+   *
+   * @param library the library containing the type alias
+   * @param source the source of the file containing the type alias
+   * @param typeAlias the type alias being remembered
+   */
+  LibraryResolver_TypeAliasInfo(this._library, this._source, this._typeAlias);
+}
+
+/**
+ * Instances of the class `LibraryScope` implement a scope containing all of the names defined
+ * in a given library.
+ */
+class LibraryScope extends EnclosedScope {
+  /**
+   * Initialize a newly created scope representing the names defined in the given library.
+   *
+   * @param definingLibrary the element representing the library represented by this scope
+   * @param errorListener the listener that is to be informed when an error is encountered
+   */
+  LibraryScope(
+      LibraryElement definingLibrary, AnalysisErrorListener errorListener)
+      : super(new LibraryImportScope(definingLibrary, errorListener)) {
+    _defineTopLevelNames(definingLibrary);
+  }
+
+  @override
+  AnalysisError getErrorForDuplicate(Element existing, Element duplicate) {
+    if (existing is PrefixElement) {
+      // TODO(scheglov) consider providing actual 'nameOffset' from the
+      // synthetic accessor
+      int offset = duplicate.nameOffset;
+      if (duplicate is PropertyAccessorElement) {
+        PropertyAccessorElement accessor = duplicate;
+        if (accessor.isSynthetic) {
+          offset = accessor.variable.nameOffset;
+        }
+      }
+      return new AnalysisError.con2(duplicate.source, offset,
+          duplicate.displayName.length,
+          CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER,
+          [existing.displayName]);
+    }
+    return super.getErrorForDuplicate(existing, duplicate);
+  }
+
+  /**
+   * Add to this scope all of the public top-level names that are defined in the given compilation
+   * unit.
+   *
+   * @param compilationUnit the compilation unit defining the top-level names to be added to this
+   *          scope
+   */
+  void _defineLocalNames(CompilationUnitElement compilationUnit) {
+    for (PropertyAccessorElement element in compilationUnit.accessors) {
+      define(element);
+    }
+    for (ClassElement element in compilationUnit.enums) {
+      define(element);
+    }
+    for (FunctionElement element in compilationUnit.functions) {
+      define(element);
+    }
+    for (FunctionTypeAliasElement element
+        in compilationUnit.functionTypeAliases) {
+      define(element);
+    }
+    for (ClassElement element in compilationUnit.types) {
+      define(element);
+    }
+  }
+
+  /**
+   * Add to this scope all of the names that are explicitly defined in the given library.
+   *
+   * @param definingLibrary the element representing the library that defines the names in this
+   *          scope
+   */
+  void _defineTopLevelNames(LibraryElement definingLibrary) {
+    for (PrefixElement prefix in definingLibrary.prefixes) {
+      define(prefix);
+    }
+    _defineLocalNames(definingLibrary.definingCompilationUnit);
+    for (CompilationUnitElement compilationUnit in definingLibrary.parts) {
+      _defineLocalNames(compilationUnit);
+    }
+  }
+}
+
+/**
+ * This class is used to replace uses of `HashMap<String, ExecutableElement>` which are not as
+ * performant as this class.
+ */
+class MemberMap {
+  /**
+   * The current size of this map.
+   */
+  int _size = 0;
+
+  /**
+   * The array of keys.
+   */
+  List<String> _keys;
+
+  /**
+   * The array of ExecutableElement values.
+   */
+  List<ExecutableElement> _values;
+
+  /**
+   * Default constructor.
+   */
+  MemberMap() : this.con1(10);
+
+  /**
+   * This constructor takes an initial capacity of the map.
+   *
+   * @param initialCapacity the initial capacity
+   */
+  MemberMap.con1(int initialCapacity) {
+    _initArrays(initialCapacity);
+  }
+
+  /**
+   * Copy constructor.
+   */
+  MemberMap.con2(MemberMap memberMap) {
+    _initArrays(memberMap._size + 5);
+    for (int i = 0; i < memberMap._size; i++) {
+      _keys[i] = memberMap._keys[i];
+      _values[i] = memberMap._values[i];
+    }
+    _size = memberMap._size;
+  }
+
+  /**
+   * The size of the map.
+   *
+   * @return the size of the map.
+   */
+  int get size => _size;
+
+  /**
+   * Given some key, return the ExecutableElement value from the map, if the key does not exist in
+   * the map, `null` is returned.
+   *
+   * @param key some key to look up in the map
+   * @return the associated ExecutableElement value from the map, if the key does not exist in the
+   *         map, `null` is returned
+   */
+  ExecutableElement get(String key) {
+    for (int i = 0; i < _size; i++) {
+      if (_keys[i] != null && _keys[i] == key) {
+        return _values[i];
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Get and return the key at the specified location. If the key/value pair has been removed from
+   * the set, then `null` is returned.
+   *
+   * @param i some non-zero value less than size
+   * @return the key at the passed index
+   * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passed index is less than
+   *        zero or greater than or equal to the capacity of the arrays
+   */
+  String getKey(int i) => _keys[i];
+
+  /**
+   * Get and return the ExecutableElement at the specified location. If the key/value pair has been
+   * removed from the set, then then `null` is returned.
+   *
+   * @param i some non-zero value less than size
+   * @return the key at the passed index
+   * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passed index is less than
+   *        zero or greater than or equal to the capacity of the arrays
+   */
+  ExecutableElement getValue(int i) => _values[i];
+
+  /**
+   * Given some key/value pair, store the pair in the map. If the key exists already, then the new
+   * value overrides the old value.
+   *
+   * @param key the key to store in the map
+   * @param value the ExecutableElement value to store in the map
+   */
+  void put(String key, ExecutableElement value) {
+    // If we already have a value with this key, override the value
+    for (int i = 0; i < _size; i++) {
+      if (_keys[i] != null && _keys[i] == key) {
+        _values[i] = value;
+        return;
+      }
+    }
+    // If needed, double the size of our arrays and copy values over in both
+    // arrays
+    if (_size == _keys.length) {
+      int newArrayLength = _size * 2;
+      List<String> keys_new_array = new List<String>(newArrayLength);
+      List<ExecutableElement> values_new_array =
+          new List<ExecutableElement>(newArrayLength);
+      for (int i = 0; i < _size; i++) {
+        keys_new_array[i] = _keys[i];
+      }
+      for (int i = 0; i < _size; i++) {
+        values_new_array[i] = _values[i];
+      }
+      _keys = keys_new_array;
+      _values = values_new_array;
+    }
+    // Put new value at end of array
+    _keys[_size] = key;
+    _values[_size] = value;
+    _size++;
+  }
+
+  /**
+   * Given some [String] key, this method replaces the associated key and value pair with
+   * `null`. The size is not decremented with this call, instead it is expected that the users
+   * check for `null`.
+   *
+   * @param key the key of the key/value pair to remove from the map
+   */
+  void remove(String key) {
+    for (int i = 0; i < _size; i++) {
+      if (_keys[i] == key) {
+        _keys[i] = null;
+        _values[i] = null;
+        return;
+      }
+    }
+  }
+
+  /**
+   * Sets the ExecutableElement at the specified location.
+   *
+   * @param i some non-zero value less than size
+   * @param value the ExecutableElement value to store in the map
+   */
+  void setValue(int i, ExecutableElement value) {
+    _values[i] = value;
+  }
+
+  /**
+   * Initializes [keys] and [values].
+   */
+  void _initArrays(int initialCapacity) {
+    _keys = new List<String>(initialCapacity);
+    _values = new List<ExecutableElement>(initialCapacity);
+  }
+}
+
+/**
+ * Instances of the class `Namespace` implement a mapping of identifiers to the elements
+ * represented by those identifiers. Namespaces are the building blocks for scopes.
+ */
+class Namespace {
+  /**
+   * An empty namespace.
+   */
+  static Namespace EMPTY = new Namespace(new HashMap<String, Element>());
+
+  /**
+   * A table mapping names that are defined in this namespace to the element representing the thing
+   * declared with that name.
+   */
+  final HashMap<String, Element> _definedNames;
+
+  /**
+   * Initialize a newly created namespace to have the given defined names.
+   *
+   * @param definedNames the mapping from names that are defined in this namespace to the
+   *          corresponding elements
+   */
+  Namespace(this._definedNames);
+
+  /**
+   * Return a table containing the same mappings as those defined by this namespace.
+   *
+   * @return a table containing the same mappings as those defined by this namespace
+   */
+  Map<String, Element> get definedNames =>
+      new HashMap<String, Element>.from(_definedNames);
+
+  /**
+   * Return the element in this namespace that is available to the containing scope using the given
+   * name.
+   *
+   * @param name the name used to reference the
+   * @return the element represented by the given identifier
+   */
+  Element get(String name) => _definedNames[name];
+}
+
+/**
+ * Instances of the class `NamespaceBuilder` are used to build a `Namespace`. Namespace
+ * builders are thread-safe and re-usable.
+ */
+class NamespaceBuilder {
+  /**
+   * Create a namespace representing the export namespace of the given [ExportElement].
+   *
+   * @param element the export element whose export namespace is to be created
+   * @return the export namespace that was created
+   */
+  Namespace createExportNamespaceForDirective(ExportElement element) {
+    LibraryElement exportedLibrary = element.exportedLibrary;
+    if (exportedLibrary == null) {
+      //
+      // The exported library will be null if the URI does not reference a valid
+      // library.
+      //
+      return Namespace.EMPTY;
+    }
+    HashMap<String, Element> definedNames =
+        _createExportMapping(exportedLibrary, new HashSet<LibraryElement>());
+    definedNames = _applyCombinators(definedNames, element.combinators);
+    return new Namespace(definedNames);
+  }
+
+  /**
+   * Create a namespace representing the export namespace of the given library.
+   *
+   * @param library the library whose export namespace is to be created
+   * @return the export namespace that was created
+   */
+  Namespace createExportNamespaceForLibrary(LibraryElement library) =>
+      new Namespace(
+          _createExportMapping(library, new HashSet<LibraryElement>()));
+
+  /**
+   * Create a namespace representing the import namespace of the given library.
+   *
+   * @param library the library whose import namespace is to be created
+   * @return the import namespace that was created
+   */
+  Namespace createImportNamespaceForDirective(ImportElement element) {
+    LibraryElement importedLibrary = element.importedLibrary;
+    if (importedLibrary == null) {
+      //
+      // The imported library will be null if the URI does not reference a valid
+      // library.
+      //
+      return Namespace.EMPTY;
+    }
+    HashMap<String, Element> definedNames =
+        _createExportMapping(importedLibrary, new HashSet<LibraryElement>());
+    definedNames = _applyCombinators(definedNames, element.combinators);
+    definedNames = _applyPrefix(definedNames, element.prefix);
+    return new Namespace(definedNames);
+  }
+
+  /**
+   * Create a namespace representing the public namespace of the given library.
+   *
+   * @param library the library whose public namespace is to be created
+   * @return the public namespace that was created
+   */
+  Namespace createPublicNamespaceForLibrary(LibraryElement library) {
+    HashMap<String, Element> definedNames = new HashMap<String, Element>();
+    _addPublicNames(definedNames, library.definingCompilationUnit);
+    for (CompilationUnitElement compilationUnit in library.parts) {
+      _addPublicNames(definedNames, compilationUnit);
+    }
+    return new Namespace(definedNames);
+  }
+
+  /**
+   * Add all of the names in the given namespace to the given mapping table.
+   *
+   * @param definedNames the mapping table to which the names in the given namespace are to be added
+   * @param namespace the namespace containing the names to be added to this namespace
+   */
+  void _addAllFromNamespace(
+      Map<String, Element> definedNames, Namespace namespace) {
+    if (namespace != null) {
+      definedNames.addAll(namespace.definedNames);
+    }
+  }
+
+  /**
+   * Add the given element to the given mapping table if it has a publicly visible name.
+   *
+   * @param definedNames the mapping table to which the public name is to be added
+   * @param element the element to be added
+   */
+  void _addIfPublic(Map<String, Element> definedNames, Element element) {
+    String name = element.name;
+    if (name != null && !Scope.isPrivateName(name)) {
+      definedNames[name] = element;
+    }
+  }
+
+  /**
+   * Add to the given mapping table all of the public top-level names that are defined in the given
+   * compilation unit.
+   *
+   * @param definedNames the mapping table to which the public names are to be added
+   * @param compilationUnit the compilation unit defining the top-level names to be added to this
+   *          namespace
+   */
+  void _addPublicNames(Map<String, Element> definedNames,
+      CompilationUnitElement compilationUnit) {
+    for (PropertyAccessorElement element in compilationUnit.accessors) {
+      _addIfPublic(definedNames, element);
+    }
+    for (ClassElement element in compilationUnit.enums) {
+      _addIfPublic(definedNames, element);
+    }
+    for (FunctionElement element in compilationUnit.functions) {
+      _addIfPublic(definedNames, element);
+    }
+    for (FunctionTypeAliasElement element
+        in compilationUnit.functionTypeAliases) {
+      _addIfPublic(definedNames, element);
+    }
+    for (ClassElement element in compilationUnit.types) {
+      _addIfPublic(definedNames, element);
+    }
+  }
+
+  /**
+   * Apply the given combinators to all of the names in the given mapping table.
+   *
+   * @param definedNames the mapping table to which the namespace operations are to be applied
+   * @param combinators the combinators to be applied
+   */
+  HashMap<String, Element> _applyCombinators(
+      HashMap<String, Element> definedNames,
+      List<NamespaceCombinator> combinators) {
+    for (NamespaceCombinator combinator in combinators) {
+      if (combinator is HideElementCombinator) {
+        _hide(definedNames, combinator.hiddenNames);
+      } else if (combinator is ShowElementCombinator) {
+        definedNames = _show(definedNames, combinator.shownNames);
+      } else {
+        // Internal error.
+        AnalysisEngine.instance.logger
+            .logError("Unknown type of combinator: ${combinator.runtimeType}");
+      }
+    }
+    return definedNames;
+  }
+
+  /**
+   * Apply the given prefix to all of the names in the table of defined names.
+   *
+   * @param definedNames the names that were defined before this operation
+   * @param prefixElement the element defining the prefix to be added to the names
+   */
+  HashMap<String, Element> _applyPrefix(
+      HashMap<String, Element> definedNames, PrefixElement prefixElement) {
+    if (prefixElement != null) {
+      String prefix = prefixElement.name;
+      HashMap<String, Element> newNames = new HashMap<String, Element>();
+      definedNames.forEach((String name, Element element) {
+        newNames["$prefix.$name"] = element;
+      });
+      return newNames;
+    } else {
+      return definedNames;
+    }
+  }
+
+  /**
+   * Create a mapping table representing the export namespace of the given library.
+   *
+   * @param library the library whose public namespace is to be created
+   * @param visitedElements a set of libraries that do not need to be visited when processing the
+   *          export directives of the given library because all of the names defined by them will
+   *          be added by another library
+   * @return the mapping table that was created
+   */
+  HashMap<String, Element> _createExportMapping(
+      LibraryElement library, HashSet<LibraryElement> visitedElements) {
+    // Check if the export namespace has been already computed.
+    {
+      Namespace exportNamespace = library.exportNamespace;
+      if (exportNamespace != null) {
+        return exportNamespace.definedNames;
+      }
+    }
+    // TODO(scheglov) Remove this after switching to the new task model.
+    visitedElements.add(library);
+    try {
+      HashMap<String, Element> definedNames = new HashMap<String, Element>();
+      for (ExportElement element in library.exports) {
+        LibraryElement exportedLibrary = element.exportedLibrary;
+        if (exportedLibrary != null &&
+            !visitedElements.contains(exportedLibrary)) {
+          //
+          // The exported library will be null if the URI does not reference a
+          // valid library.
+          //
+          HashMap<String, Element> exportedNames =
+              _createExportMapping(exportedLibrary, visitedElements);
+          exportedNames = _applyCombinators(exportedNames, element.combinators);
+          definedNames.addAll(exportedNames);
+        }
+      }
+      _addAllFromNamespace(definedNames,
+          (library.context as InternalAnalysisContext)
+              .getPublicNamespace(library));
+      return definedNames;
+    } finally {
+      visitedElements.remove(library);
+    }
+  }
+
+  /**
+   * Hide all of the given names by removing them from the given collection of defined names.
+   *
+   * @param definedNames the names that were defined before this operation
+   * @param hiddenNames the names to be hidden
+   */
+  void _hide(HashMap<String, Element> definedNames, List<String> hiddenNames) {
+    for (String name in hiddenNames) {
+      definedNames.remove(name);
+      definedNames.remove("$name=");
+    }
+  }
+
+  /**
+   * Show only the given names by removing all other names from the given collection of defined
+   * names.
+   *
+   * @param definedNames the names that were defined before this operation
+   * @param shownNames the names to be shown
+   */
+  HashMap<String, Element> _show(
+      HashMap<String, Element> definedNames, List<String> shownNames) {
+    HashMap<String, Element> newNames = new HashMap<String, Element>();
+    for (String name in shownNames) {
+      Element element = definedNames[name];
+      if (element != null) {
+        newNames[name] = element;
+      }
+      String setterName = "$name=";
+      element = definedNames[setterName];
+      if (element != null) {
+        newNames[setterName] = element;
+      }
+    }
+    return newNames;
+  }
+}
+
+/**
+ * Instances of the class `OverrideVerifier` visit all of the declarations in a compilation
+ * unit to verify that if they have an override annotation it is being used correctly.
+ */
+class OverrideVerifier extends RecursiveAstVisitor<Object> {
+  /**
+   * The error reporter used to report errors.
+   */
+  final ErrorReporter _errorReporter;
+
+  /**
+   * The inheritance manager used to find overridden methods.
+   */
+  final InheritanceManager _manager;
+
+  /**
+   * Initialize a newly created verifier to look for inappropriate uses of the override annotation.
+   *
+   * @param errorReporter the error reporter used to report errors
+   * @param manager the inheritance manager used to find overridden methods
+   */
+  OverrideVerifier(this._errorReporter, this._manager);
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    ExecutableElement element = node.element;
+    if (_isOverride(element)) {
+      if (_getOverriddenMember(element) == null) {
+        if (element is MethodElement) {
+          _errorReporter.reportErrorForNode(
+              HintCode.OVERRIDE_ON_NON_OVERRIDING_METHOD, node.name);
+        } else if (element is PropertyAccessorElement) {
+          if (element.isGetter) {
+            _errorReporter.reportErrorForNode(
+                HintCode.OVERRIDE_ON_NON_OVERRIDING_GETTER, node.name);
+          } else {
+            _errorReporter.reportErrorForNode(
+                HintCode.OVERRIDE_ON_NON_OVERRIDING_SETTER, node.name);
+          }
+        }
+      }
+    }
+    return super.visitMethodDeclaration(node);
+  }
+
+  /**
+   * Return the member that overrides the given member.
+   *
+   * @param member the member that overrides the returned member
+   * @return the member that overrides the given member
+   */
+  ExecutableElement _getOverriddenMember(ExecutableElement member) {
+    LibraryElement library = member.library;
+    if (library == null) {
+      return null;
+    }
+    ClassElement classElement =
+        member.getAncestor((element) => element is ClassElement);
+    if (classElement == null) {
+      return null;
+    }
+    return _manager.lookupInheritance(classElement, member.name);
+  }
+
+  /**
+   * Return `true` if the given element has an override annotation associated with it.
+   *
+   * @param element the element being tested
+   * @return `true` if the element has an override annotation associated with it
+   */
+  bool _isOverride(Element element) => element != null && element.isOverride;
+}
+
+/**
+ * Instances of the class `PubVerifier` traverse an AST structure looking for deviations from
+ * pub best practices.
+ */
+class PubVerifier extends RecursiveAstVisitor<Object> {
+//  static String _PUBSPEC_YAML = "pubspec.yaml";
+
+  /**
+   * The analysis context containing the sources to be analyzed
+   */
+  final AnalysisContext _context;
+
+  /**
+   * The error reporter by which errors will be reported.
+   */
+  final ErrorReporter _errorReporter;
+
+  PubVerifier(this._context, this._errorReporter);
+
+  @override
+  Object visitImportDirective(ImportDirective directive) {
+    return null;
+  }
+
+//  /**
+//   * This verifies that the passed file import directive is not contained in a source inside a
+//   * package "lib" directory hierarchy referencing a source outside that package "lib" directory
+//   * hierarchy.
+//   *
+//   * @param uriLiteral the import URL (not `null`)
+//   * @param path the file path being verified (not `null`)
+//   * @return `true` if and only if an error code is generated on the passed node
+//   * See [PubSuggestionCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE].
+//   */
+//  bool
+//      _checkForFileImportInsideLibReferencesFileOutside(StringLiteral uriLiteral,
+//      String path) {
+//    Source source = _getSource(uriLiteral);
+//    String fullName = _getSourceFullName(source);
+//    if (fullName != null) {
+//      int pathIndex = 0;
+//      int fullNameIndex = fullName.length;
+//      while (pathIndex < path.length &&
+//          StringUtilities.startsWith3(path, pathIndex, 0x2E, 0x2E, 0x2F)) {
+//        fullNameIndex = JavaString.lastIndexOf(fullName, '/', fullNameIndex);
+//        if (fullNameIndex < 4) {
+//          return false;
+//        }
+//        // Check for "/lib" at a specified place in the fullName
+//        if (StringUtilities.startsWith4(
+//            fullName,
+//            fullNameIndex - 4,
+//            0x2F,
+//            0x6C,
+//            0x69,
+//            0x62)) {
+//          String relativePubspecPath =
+//              path.substring(0, pathIndex + 3) +
+//              _PUBSPEC_YAML;
+//          Source pubspecSource =
+//              _context.sourceFactory.resolveUri(source, relativePubspecPath);
+//          if (_context.exists(pubspecSource)) {
+//            // Files inside the lib directory hierarchy should not reference
+//            // files outside
+//            _errorReporter.reportErrorForNode(
+//                HintCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE,
+//                uriLiteral);
+//          }
+//          return true;
+//        }
+//        pathIndex += 3;
+//      }
+//    }
+//    return false;
+//  }
+
+//  /**
+//   * This verifies that the passed file import directive is not contained in a source outside a
+//   * package "lib" directory hierarchy referencing a source inside that package "lib" directory
+//   * hierarchy.
+//   *
+//   * @param uriLiteral the import URL (not `null`)
+//   * @param path the file path being verified (not `null`)
+//   * @return `true` if and only if an error code is generated on the passed node
+//   * See [PubSuggestionCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE].
+//   */
+//  bool
+//      _checkForFileImportOutsideLibReferencesFileInside(StringLiteral uriLiteral,
+//      String path) {
+//    if (StringUtilities.startsWith4(path, 0, 0x6C, 0x69, 0x62, 0x2F)) {
+//      if (_checkForFileImportOutsideLibReferencesFileInsideAtIndex(
+//          uriLiteral,
+//          path,
+//          0)) {
+//        return true;
+//      }
+//    }
+//    int pathIndex =
+//        StringUtilities.indexOf5(path, 0, 0x2F, 0x6C, 0x69, 0x62, 0x2F);
+//    while (pathIndex != -1) {
+//      if (_checkForFileImportOutsideLibReferencesFileInsideAtIndex(
+//          uriLiteral,
+//          path,
+//          pathIndex + 1)) {
+//        return true;
+//      }
+//      pathIndex =
+//          StringUtilities.indexOf5(path, pathIndex + 4, 0x2F, 0x6C, 0x69, 0x62, 0x2F);
+//    }
+//    return false;
+//  }
+
+//  bool
+//      _checkForFileImportOutsideLibReferencesFileInsideAtIndex(StringLiteral uriLiteral,
+//      String path, int pathIndex) {
+//    Source source = _getSource(uriLiteral);
+//    String relativePubspecPath = path.substring(0, pathIndex) + _PUBSPEC_YAML;
+//    Source pubspecSource =
+//        _context.sourceFactory.resolveUri(source, relativePubspecPath);
+//    if (!_context.exists(pubspecSource)) {
+//      return false;
+//    }
+//    String fullName = _getSourceFullName(source);
+//    if (fullName != null) {
+//      if (StringUtilities.indexOf5(fullName, 0, 0x2F, 0x6C, 0x69, 0x62, 0x2F) <
+//          0) {
+//        // Files outside the lib directory hierarchy should not reference files
+//        // inside ... use package: url instead
+//        _errorReporter.reportErrorForNode(
+//            HintCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE,
+//            uriLiteral);
+//        return true;
+//      }
+//    }
+//    return false;
+//  }
+
+//  /**
+//   * This verifies that the passed package import directive does not contain ".."
+//   *
+//   * @param uriLiteral the import URL (not `null`)
+//   * @param path the path to be validated (not `null`)
+//   * @return `true` if and only if an error code is generated on the passed node
+//   * See [PubSuggestionCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT].
+//   */
+//  bool _checkForPackageImportContainsDotDot(StringLiteral uriLiteral,
+//      String path) {
+//    if (StringUtilities.startsWith3(path, 0, 0x2E, 0x2E, 0x2F) ||
+//        StringUtilities.indexOf4(path, 0, 0x2F, 0x2E, 0x2E, 0x2F) >= 0) {
+//      // Package import should not to contain ".."
+//      _errorReporter.reportErrorForNode(
+//          HintCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT,
+//          uriLiteral);
+//      return true;
+//    }
+//    return false;
+//  }
+
+//  /**
+//   * Answer the source associated with the compilation unit containing the given AST node.
+//   *
+//   * @param node the node (not `null`)
+//   * @return the source or `null` if it could not be determined
+//   */
+//  Source _getSource(AstNode node) {
+//    Source source = null;
+//    CompilationUnit unit = node.getAncestor((node) => node is CompilationUnit);
+//    if (unit != null) {
+//      CompilationUnitElement element = unit.element;
+//      if (element != null) {
+//        source = element.source;
+//      }
+//    }
+//    return source;
+//  }
+
+//  /**
+//   * Answer the full name of the given source. The returned value will have all
+//   * [File.separatorChar] replace by '/'.
+//   *
+//   * @param source the source
+//   * @return the full name or `null` if it could not be determined
+//   */
+//  String _getSourceFullName(Source source) {
+//    if (source != null) {
+//      String fullName = source.fullName;
+//      if (fullName != null) {
+//        return fullName.replaceAll(r'\', '/');
+//      }
+//    }
+//    return null;
+//  }
+}
+
+/**
+ * Kind of the redirecting constructor.
+ */
+class RedirectingConstructorKind extends Enum<RedirectingConstructorKind> {
+  static const RedirectingConstructorKind CONST =
+      const RedirectingConstructorKind('CONST', 0);
+
+  static const RedirectingConstructorKind NORMAL =
+      const RedirectingConstructorKind('NORMAL', 1);
+
+  static const List<RedirectingConstructorKind> values = const [CONST, NORMAL];
+
+  const RedirectingConstructorKind(String name, int ordinal)
+      : super(name, ordinal);
+}
+
+/**
+ * A `ResolvableLibrary` represents a single library during the resolution of
+ * some (possibly different) library. They are not intended to be used except
+ * during the resolution process.
+ */
+class ResolvableLibrary {
+  /**
+   * An empty array that can be used to initialize lists of libraries.
+   */
+  static List<ResolvableLibrary> _EMPTY_ARRAY = new List<ResolvableLibrary>(0);
+
+  /**
+   * The next artificial hash code.
+   */
+  static int _NEXT_HASH_CODE = 0;
+
+  /**
+   * The artifitial hash code for this object.
+   */
+  final int _hashCode = _nextHashCode();
+
+  /**
+   * The source specifying the defining compilation unit of this library.
+   */
+  final Source librarySource;
+
+  /**
+   * A list containing all of the libraries that are imported into this library.
+   */
+  List<ResolvableLibrary> _importedLibraries = _EMPTY_ARRAY;
+
+  /**
+   * A flag indicating whether this library explicitly imports core.
+   */
+  bool explicitlyImportsCore = false;
+
+  /**
+   * An array containing all of the libraries that are exported from this library.
+   */
+  List<ResolvableLibrary> _exportedLibraries = _EMPTY_ARRAY;
+
+  /**
+   * An array containing the compilation units that comprise this library. The
+   * defining compilation unit is always first.
+   */
+  List<ResolvableCompilationUnit> _compilationUnits;
+
+  /**
+   * The library element representing this library.
+   */
+  LibraryElementImpl _libraryElement;
+
+  /**
+   * The listener to which analysis errors will be reported.
+   */
+  AnalysisErrorListener _errorListener;
+
+  /**
+   * The inheritance manager which is used for member lookups in this library.
+   */
+  InheritanceManager _inheritanceManager;
+
+  /**
+   * The library scope used when resolving elements within this library's compilation units.
+   */
+  LibraryScope _libraryScope;
+
+  /**
+   * Initialize a newly created data holder that can maintain the data associated with a library.
+   *
+   * @param librarySource the source specifying the defining compilation unit of this library
+   * @param errorListener the listener to which analysis errors will be reported
+   */
+  ResolvableLibrary(this.librarySource);
+
+  /**
+   * Return an array of the [CompilationUnit]s that make up the library. The first unit is
+   * always the defining unit.
+   *
+   * @return an array of the [CompilationUnit]s that make up the library. The first unit is
+   *         always the defining unit
+   */
+  List<CompilationUnit> get compilationUnits {
+    int count = _compilationUnits.length;
+    List<CompilationUnit> units = new List<CompilationUnit>(count);
+    for (int i = 0; i < count; i++) {
+      units[i] = _compilationUnits[i].compilationUnit;
+    }
+    return units;
+  }
+
+  /**
+   * Return an array containing the sources for the compilation units in this library, including the
+   * defining compilation unit.
+   *
+   * @return the sources for the compilation units in this library
+   */
+  List<Source> get compilationUnitSources {
+    int count = _compilationUnits.length;
+    List<Source> sources = new List<Source>(count);
+    for (int i = 0; i < count; i++) {
+      sources[i] = _compilationUnits[i].source;
+    }
+    return sources;
+  }
+
+  /**
+   * Return the AST structure associated with the defining compilation unit for this library.
+   *
+   * @return the AST structure associated with the defining compilation unit for this library
+   * @throws AnalysisException if an AST structure could not be created for the defining compilation
+   *           unit
+   */
+  CompilationUnit get definingCompilationUnit =>
+      _compilationUnits[0].compilationUnit;
+
+  /**
+   * Set the listener to which analysis errors will be reported to be the given listener.
+   *
+   * @param errorListener the listener to which analysis errors will be reported
+   */
+  void set errorListener(AnalysisErrorListener errorListener) {
+    this._errorListener = errorListener;
+  }
+
+  /**
+   * Set the libraries that are exported by this library to be those in the given array.
+   *
+   * @param exportedLibraries the libraries that are exported by this library
+   */
+  void set exportedLibraries(List<ResolvableLibrary> exportedLibraries) {
+    this._exportedLibraries = exportedLibraries;
+  }
+
+  /**
+   * Return an array containing the libraries that are exported from this library.
+   *
+   * @return an array containing the libraries that are exported from this library
+   */
+  List<ResolvableLibrary> get exports => _exportedLibraries;
+
+  @override
+  int get hashCode => _hashCode;
+
+  /**
+   * Set the libraries that are imported into this library to be those in the given array.
+   *
+   * @param importedLibraries the libraries that are imported into this library
+   */
+  void set importedLibraries(List<ResolvableLibrary> importedLibraries) {
+    this._importedLibraries = importedLibraries;
+  }
+
+  /**
+   * Return an array containing the libraries that are imported into this library.
+   *
+   * @return an array containing the libraries that are imported into this library
+   */
+  List<ResolvableLibrary> get imports => _importedLibraries;
+
+  /**
+   * Return an array containing the libraries that are either imported or exported from this
+   * library.
+   *
+   * @return the libraries that are either imported or exported from this library
+   */
+  List<ResolvableLibrary> get importsAndExports {
+    HashSet<ResolvableLibrary> libraries = new HashSet<ResolvableLibrary>();
+    for (ResolvableLibrary library in _importedLibraries) {
+      libraries.add(library);
+    }
+    for (ResolvableLibrary library in _exportedLibraries) {
+      libraries.add(library);
+    }
+    return new List.from(libraries);
+  }
+
+  /**
+   * Return the inheritance manager for this library.
+   *
+   * @return the inheritance manager for this library
+   */
+  InheritanceManager get inheritanceManager {
+    if (_inheritanceManager == null) {
+      return _inheritanceManager = new InheritanceManager(_libraryElement);
+    }
+    return _inheritanceManager;
+  }
+
+  /**
+   * Return the library element representing this library, creating it if necessary.
+   *
+   * @return the library element representing this library
+   */
+  LibraryElementImpl get libraryElement => _libraryElement;
+
+  /**
+   * Set the library element representing this library to the given library element.
+   *
+   * @param libraryElement the library element representing this library
+   */
+  void set libraryElement(LibraryElementImpl libraryElement) {
+    this._libraryElement = libraryElement;
+    if (_inheritanceManager != null) {
+      _inheritanceManager.libraryElement = libraryElement;
+    }
+  }
+
+  /**
+   * Return the library scope used when resolving elements within this library's compilation units.
+   *
+   * @return the library scope used when resolving elements within this library's compilation units
+   */
+  LibraryScope get libraryScope {
+    if (_libraryScope == null) {
+      _libraryScope = new LibraryScope(_libraryElement, _errorListener);
+    }
+    return _libraryScope;
+  }
+
+  /**
+   * Return an array containing the compilation units that comprise this library. The defining
+   * compilation unit is always first.
+   *
+   * @return the compilation units that comprise this library
+   */
+  List<ResolvableCompilationUnit> get resolvableCompilationUnits =>
+      _compilationUnits;
+
+  /**
+   * Set the compilation unit in this library to the given compilation units. The defining
+   * compilation unit must be the first element of the array.
+   *
+   * @param units the compilation units in this library
+   */
+  void set resolvableCompilationUnits(List<ResolvableCompilationUnit> units) {
+    _compilationUnits = units;
+  }
+
+  /**
+   * Return the AST structure associated with the given source, or `null` if the source does
+   * not represent a compilation unit that is included in this library.
+   *
+   * @param source the source representing the compilation unit whose AST is to be returned
+   * @return the AST structure associated with the given source
+   * @throws AnalysisException if an AST structure could not be created for the compilation unit
+   */
+  CompilationUnit getAST(Source source) {
+    int count = _compilationUnits.length;
+    for (int i = 0; i < count; i++) {
+      if (_compilationUnits[i].source == source) {
+        return _compilationUnits[i].compilationUnit;
+      }
+    }
+    return null;
+  }
+
+  @override
+  String toString() => librarySource.shortName;
+
+  static int _nextHashCode() {
+    int next = (_NEXT_HASH_CODE + 1) & 0xFFFFFF;
+    _NEXT_HASH_CODE = next;
+    return next;
+  }
+}
+
+/**
+ * The enumeration `ResolverErrorCode` defines the error codes used for errors
+ * detected by the resolver. The convention for this class is for the name of
+ * the error code to indicate the problem that caused the error to be generated
+ * and for the error message to explain what is wrong and, when appropriate, how
+ * the problem can be corrected.
+ */
+class ResolverErrorCode extends ErrorCode {
+  static const ResolverErrorCode BREAK_LABEL_ON_SWITCH_MEMBER =
+      const ResolverErrorCode('BREAK_LABEL_ON_SWITCH_MEMBER',
+          "Break label resolves to case or default statement");
+
+  static const ResolverErrorCode CONTINUE_LABEL_ON_SWITCH =
+      const ResolverErrorCode('CONTINUE_LABEL_ON_SWITCH',
+          "A continue label resolves to switch, must be loop or switch member");
+
+  static const ResolverErrorCode MISSING_LIBRARY_DIRECTIVE_WITH_PART =
+      const ResolverErrorCode('MISSING_LIBRARY_DIRECTIVE_WITH_PART',
+          "Libraries that have parts must have a library directive");
+
+  /**
+   * Initialize a newly created error code to have the given [name]. The message
+   * associated with the error will be created from the given [message]
+   * template. The correction associated with the error will be created from the
+   * given [correction] template.
+   */
+  const ResolverErrorCode(String name, String message, [String correction])
+      : super(name, message, correction);
+
+  @override
+  ErrorSeverity get errorSeverity => type.severity;
+
+  @override
+  ErrorType get type => ErrorType.COMPILE_TIME_ERROR;
+}
+
+/**
+ * Instances of the class `ResolverVisitor` are used to resolve the nodes within a single
+ * compilation unit.
+ */
+class ResolverVisitor extends ScopedVisitor {
+  /**
+   * The manager for the inheritance mappings.
+   */
+  InheritanceManager _inheritanceManager;
+
+  /**
+   * The object used to resolve the element associated with the current node.
+   */
+  ElementResolver elementResolver;
+
+  /**
+   * The object used to compute the type associated with the current node.
+   */
+  StaticTypeAnalyzer typeAnalyzer;
+
+  /**
+   * The class element representing the class containing the current node,
+   * or `null` if the current node is not contained in a class.
+   */
+  ClassElement enclosingClass = null;
+
+  /**
+   * The class declaration representing the class containing the current node, or `null` if
+   * the current node is not contained in a class.
+   */
+  ClassDeclaration _enclosingClassDeclaration = null;
+
+  /**
+   * The function type alias representing the function type containing the current node, or
+   * `null` if the current node is not contained in a function type alias.
+   */
+  FunctionTypeAlias _enclosingFunctionTypeAlias = null;
+
+  /**
+   * The element representing the function containing the current node, or `null` if the
+   * current node is not contained in a function.
+   */
+  ExecutableElement _enclosingFunction = null;
+
+  /**
+   * The [Comment] before a [FunctionDeclaration] or a [MethodDeclaration] that
+   * cannot be resolved where we visited it, because it should be resolved in the scope of the body.
+   */
+  Comment _commentBeforeFunction = null;
+
+  /**
+   * The object keeping track of which elements have had their types overridden.
+   */
+  TypeOverrideManager _overrideManager = new TypeOverrideManager();
+
+  /**
+   * The object keeping track of which elements have had their types promoted.
+   */
+  TypePromotionManager _promoteManager = new TypePromotionManager();
+
+  /**
+   * A comment before a function should be resolved in the context of the
+   * function. But when we incrementally resolve a comment, we don't want to
+   * resolve the whole function.
+   *
+   * So, this flag is set to `true`, when just context of the function should
+   * be built and the comment resolved.
+   */
+  bool resolveOnlyCommentInFunctionBody = false;
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
+   *
+   * @param library the library containing the compilation unit being resolved
+   * @param source the source representing the compilation unit being visited
+   * @param typeProvider the object used to access the types from the core library
+   */
+  ResolverVisitor.con1(
+      Library library, Source source, TypeProvider typeProvider,
+      {StaticTypeAnalyzer typeAnalyzer,
+      StaticTypeAnalyzerFactory typeAnalyzerFactory})
+      : super.con1(library, source, typeProvider) {
+    this._inheritanceManager = library.inheritanceManager;
+    this.elementResolver = new ElementResolver(this);
+    this.typeAnalyzer = typeAnalyzer != null
+        ? typeAnalyzer
+        : (typeAnalyzerFactory != null
+            ? typeAnalyzerFactory(this)
+            : new StaticTypeAnalyzer(this));
+  }
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
+   *
+   * @param definingLibrary the element for the library containing the compilation unit being
+   *          visited
+   * @param source the source representing the compilation unit being visited
+   * @param typeProvider the object used to access the types from the core library
+   * @param errorListener the error listener that will be informed of any errors that are found
+   *          during resolution
+   */
+  ResolverVisitor.con2(LibraryElement definingLibrary, Source source,
+      TypeProvider typeProvider, InheritanceManager inheritanceManager,
+      AnalysisErrorListener errorListener)
+      : super.con2(definingLibrary, source, typeProvider, errorListener) {
+    this._inheritanceManager = inheritanceManager;
+    this.elementResolver = new ElementResolver(this);
+    this.typeAnalyzer = new StaticTypeAnalyzer(this);
+  }
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in an AST node.
+   *
+   * @param definingLibrary the element for the library containing the node being visited
+   * @param source the source representing the compilation unit containing the node being visited
+   * @param typeProvider the object used to access the types from the core library
+   * @param nameScope the scope used to resolve identifiers in the node that will first be visited
+   * @param errorListener the error listener that will be informed of any errors that are found
+   *          during resolution
+   */
+  ResolverVisitor.con3(LibraryElement definingLibrary, Source source,
+      TypeProvider typeProvider, Scope nameScope,
+      AnalysisErrorListener errorListener)
+      : super.con3(
+          definingLibrary, source, typeProvider, nameScope, errorListener) {
+    this._inheritanceManager = new InheritanceManager(definingLibrary);
+    this.elementResolver = new ElementResolver(this);
+    this.typeAnalyzer = new StaticTypeAnalyzer(this);
+  }
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
+   *
+   * @param library the library containing the compilation unit being resolved
+   * @param source the source representing the compilation unit being visited
+   * @param typeProvider the object used to access the types from the core library
+   */
+  ResolverVisitor.con4(
+      ResolvableLibrary library, Source source, TypeProvider typeProvider)
+      : super.con4(library, source, typeProvider) {
+    this._inheritanceManager = library.inheritanceManager;
+    this.elementResolver = new ElementResolver(this);
+    this.typeAnalyzer = new StaticTypeAnalyzer(this);
+  }
+
+  /**
+   * Return the element representing the function containing the current node, or `null` if
+   * the current node is not contained in a function.
+   *
+   * @return the element representing the function containing the current node
+   */
+  ExecutableElement get enclosingFunction => _enclosingFunction;
+
+  /**
+   * Return the object keeping track of which elements have had their types overridden.
+   *
+   * @return the object keeping track of which elements have had their types overridden
+   */
+  TypeOverrideManager get overrideManager => _overrideManager;
+
+  /**
+   * Return the object keeping track of which elements have had their types promoted.
+   *
+   * @return the object keeping track of which elements have had their types promoted
+   */
+  TypePromotionManager get promoteManager => _promoteManager;
+
+  /**
+   * Return the propagated element associated with the given expression whose type can be
+   * overridden, or `null` if there is no element whose type can be overridden.
+   *
+   * @param expression the expression with which the element is associated
+   * @return the element associated with the given expression
+   */
+  VariableElement getOverridablePropagatedElement(Expression expression) {
+    Element element = null;
+    if (expression is SimpleIdentifier) {
+      element = expression.propagatedElement;
+    } else if (expression is PrefixedIdentifier) {
+      element = expression.propagatedElement;
+    } else if (expression is PropertyAccess) {
+      element = expression.propertyName.propagatedElement;
+    }
+    if (element is VariableElement) {
+      return element;
+    }
+    return null;
+  }
+
+  /**
+   * Return the static element associated with the given expression whose type can be overridden, or
+   * `null` if there is no element whose type can be overridden.
+   *
+   * @param expression the expression with which the element is associated
+   * @return the element associated with the given expression
+   */
+  VariableElement getOverridableStaticElement(Expression expression) {
+    Element element = null;
+    if (expression is SimpleIdentifier) {
+      element = expression.staticElement;
+    } else if (expression is PrefixedIdentifier) {
+      element = expression.staticElement;
+    } else if (expression is PropertyAccess) {
+      element = expression.propertyName.staticElement;
+    }
+    if (element is VariableElement) {
+      return element;
+    }
+    return null;
+  }
+
+  /**
+   * Return the static element associated with the given expression whose type can be promoted, or
+   * `null` if there is no element whose type can be promoted.
+   *
+   * @param expression the expression with which the element is associated
+   * @return the element associated with the given expression
+   */
+  VariableElement getPromotionStaticElement(Expression expression) {
+    while (expression is ParenthesizedExpression) {
+      expression = (expression as ParenthesizedExpression).expression;
+    }
+    if (expression is! SimpleIdentifier) {
+      return null;
+    }
+    SimpleIdentifier identifier = expression as SimpleIdentifier;
+    Element element = identifier.staticElement;
+    if (element is! VariableElement) {
+      return null;
+    }
+    ElementKind kind = element.kind;
+    if (kind == ElementKind.LOCAL_VARIABLE) {
+      return element as VariableElement;
+    }
+    if (kind == ElementKind.PARAMETER) {
+      return element as VariableElement;
+    }
+    return null;
+  }
+
+  /**
+   * Prepares this [ResolverVisitor] to using it for incremental resolution.
+   */
+  void initForIncrementalResolution() {
+    _overrideManager.enterScope();
+  }
+
+  /**
+   * If it is appropriate to do so, override the current type of the static and propagated elements
+   * associated with the given expression with the given type. Generally speaking, it is appropriate
+   * if the given type is more specific than the current type.
+   *
+   * @param expression the expression used to access the static and propagated elements whose types
+   *          might be overridden
+   * @param potentialType the potential type of the elements
+   * @param allowPrecisionLoss see @{code overrideVariable} docs
+   */
+  void overrideExpression(
+      Expression expression, DartType potentialType, bool allowPrecisionLoss) {
+    VariableElement element = getOverridableStaticElement(expression);
+    if (element != null) {
+      DartType newBestType =
+          overrideVariable(element, potentialType, allowPrecisionLoss);
+      recordPropagatedTypeIfBetter(expression, newBestType);
+    }
+    element = getOverridablePropagatedElement(expression);
+    if (element != null) {
+      overrideVariable(element, potentialType, allowPrecisionLoss);
+    }
+  }
+
+  /**
+   * If it is appropriate to do so, override the current type of the given element with the given
+   * type.
+   *
+   * @param element the element whose type might be overridden
+   * @param potentialType the potential type of the element
+   * @param allowPrecisionLoss true if `potentialType` is allowed to be less precise than the
+   *          current best type
+   *
+   * Return a new better [DartType], or `null` if [potentialType] is not better
+   * than the current [element] type.
+   */
+  DartType overrideVariable(VariableElement element, DartType potentialType,
+      bool allowPrecisionLoss) {
+    if (potentialType == null || potentialType.isBottom) {
+      return null;
+    }
+    DartType currentType = _overrideManager.getBestType(element);
+
+    if (potentialType == currentType) {
+      return null;
+    }
+
+    // If we aren't allowing precision loss then the third and fourth conditions
+    // check that we aren't losing precision.
+    //
+    // Let [C] be the current type and [P] be the potential type.  When we
+    // aren't allowing precision loss -- which is the case for is-checks -- we
+    // check that [! (C << P)] or  [P << C]. The second check, that [P << C], is
+    // analogous to part of the Dart Language Spec rule for type promotion under
+    // is-checks (in the analogy [T] is [P] and [S] is [C]):
+    //
+    //   An is-expression of the form [v is T] shows that [v] has type [T] iff
+    //   [T] is more specific than the type [S] of the expression [v] and both
+    //   [T != dynamic] and [S != dynamic].
+    //
+    // It also covers an important case that is not applicable in the spec:
+    // for union types, we want an is-check to promote from an union type to
+    // (a subtype of) any of its members.
+    //
+    // The first check, that [! (C << P)], covers the case where [P] and [C] are
+    // unrelated types; This case is not addressed in the spec for static types.
+    if (currentType == null ||
+        allowPrecisionLoss ||
+        !currentType.isMoreSpecificThan(potentialType) ||
+        potentialType.isMoreSpecificThan(currentType)) {
+      // TODO(scheglov) type propagation for instance/top-level fields
+      // was disabled because it depends on the order or visiting.
+      // If both field and its client are in the same unit, and we visit
+      // the client before the field, then propagated type is not set yet.
+//      if (element is PropertyInducingElement) {
+//        PropertyInducingElement variable = element;
+//        if (!variable.isConst && !variable.isFinal) {
+//          return;
+//        }
+//        (variable as PropertyInducingElementImpl).propagatedType =
+//            potentialType;
+//      }
+      _overrideManager.setType(element, potentialType);
+      return potentialType;
+    }
+    return null;
+  }
+
+  /**
+   * If the given [type] is valid, strongly more specific than the
+   * existing static type of the given [expression], record it as a propagated
+   * type of the given [expression]. Otherwise, reset it to `null`.
+   *
+   * If [hasOldPropagatedType] is `true` then the existing propagated type
+   * should also is checked.
+   */
+  void recordPropagatedTypeIfBetter(Expression expression, DartType type,
+      [bool hasOldPropagatedType = false]) {
+    // Ensure that propagated type invalid.
+    if (type == null || type.isDynamic || type.isBottom) {
+      if (!hasOldPropagatedType) {
+        expression.propagatedType = null;
+      }
+      return;
+    }
+    // Ensure that propagated type is more specific than the static type.
+    DartType staticType = expression.staticType;
+    if (type == staticType || !type.isMoreSpecificThan(staticType)) {
+      expression.propagatedType = null;
+      return;
+    }
+    // Ensure that the new propagated type is more specific than the old one.
+    if (hasOldPropagatedType) {
+      DartType oldPropagatedType = expression.propagatedType;
+      if (oldPropagatedType != null &&
+          !type.isMoreSpecificThan(oldPropagatedType)) {
+        return;
+      }
+    }
+    // OK
+    expression.propagatedType = type;
+  }
+
+  @override
+  Object visitAnnotation(Annotation node) {
+    AstNode parent = node.parent;
+    if (identical(parent, _enclosingClassDeclaration) ||
+        identical(parent, _enclosingFunctionTypeAlias)) {
+      return null;
+    }
+    return super.visitAnnotation(node);
+  }
+
+  @override
+  Object visitAsExpression(AsExpression node) {
+    super.visitAsExpression(node);
+    // Since an as-statement doesn't actually change the type, we don't
+    // let it affect the propagated type when it would result in a loss
+    // of precision.
+    overrideExpression(node.expression, node.type.type, false);
+    return null;
+  }
+
+  @override
+  Object visitAssertStatement(AssertStatement node) {
+    super.visitAssertStatement(node);
+    _propagateTrueState(node.condition);
+    return null;
+  }
+
+  @override
+  Object visitBinaryExpression(BinaryExpression node) {
+    sc.TokenType operatorType = node.operator.type;
+    Expression leftOperand = node.leftOperand;
+    Expression rightOperand = node.rightOperand;
+    if (operatorType == sc.TokenType.AMPERSAND_AMPERSAND) {
+      safelyVisit(leftOperand);
+      if (rightOperand != null) {
+        _overrideManager.enterScope();
+        try {
+          _promoteManager.enterScope();
+          try {
+            _propagateTrueState(leftOperand);
+            // Type promotion.
+            _promoteTypes(leftOperand);
+            _clearTypePromotionsIfPotentiallyMutatedIn(leftOperand);
+            _clearTypePromotionsIfPotentiallyMutatedIn(rightOperand);
+            _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated(
+                rightOperand);
+            // Visit right operand.
+            rightOperand.accept(this);
+          } finally {
+            _promoteManager.exitScope();
+          }
+        } finally {
+          _overrideManager.exitScope();
+        }
+      }
+    } else if (operatorType == sc.TokenType.BAR_BAR) {
+      safelyVisit(leftOperand);
+      if (rightOperand != null) {
+        _overrideManager.enterScope();
+        try {
+          _propagateFalseState(leftOperand);
+          rightOperand.accept(this);
+        } finally {
+          _overrideManager.exitScope();
+        }
+      }
+    } else {
+      safelyVisit(leftOperand);
+      safelyVisit(rightOperand);
+    }
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  @override
+  Object visitBlockFunctionBody(BlockFunctionBody node) {
+    safelyVisit(_commentBeforeFunction);
+    _overrideManager.enterScope();
+    try {
+      super.visitBlockFunctionBody(node);
+    } finally {
+      _overrideManager.exitScope();
+    }
+    return null;
+  }
+
+  @override
+  Object visitBreakStatement(BreakStatement node) {
+    //
+    // We do not visit the label because it needs to be visited in the context
+    // of the statement.
+    //
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  @override
+  Object visitClassDeclaration(ClassDeclaration node) {
+    //
+    // Resolve the metadata in the library scope.
+    //
+    if (node.metadata != null) {
+      node.metadata.accept(this);
+    }
+    _enclosingClassDeclaration = node;
+    //
+    // Continue the class resolution.
+    //
+    ClassElement outerType = enclosingClass;
+    try {
+      enclosingClass = node.element;
+      typeAnalyzer.thisType =
+          enclosingClass == null ? null : enclosingClass.type;
+      super.visitClassDeclaration(node);
+      node.accept(elementResolver);
+      node.accept(typeAnalyzer);
+    } finally {
+      typeAnalyzer.thisType = outerType == null ? null : outerType.type;
+      enclosingClass = outerType;
+      _enclosingClassDeclaration = null;
+    }
+    return null;
+  }
+
+  /**
+   * Implementation of this method should be synchronized with
+   * [visitClassDeclaration].
+   */
+  visitClassDeclarationIncrementally(ClassDeclaration node) {
+    //
+    // Resolve the metadata in the library scope.
+    //
+    if (node.metadata != null) {
+      node.metadata.accept(this);
+    }
+    _enclosingClassDeclaration = node;
+    //
+    // Continue the class resolution.
+    //
+    enclosingClass = node.element;
+    typeAnalyzer.thisType = enclosingClass == null ? null : enclosingClass.type;
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+  }
+
+  @override
+  Object visitComment(Comment node) {
+    if (node.parent is FunctionDeclaration ||
+        node.parent is ConstructorDeclaration ||
+        node.parent is MethodDeclaration) {
+      if (!identical(node, _commentBeforeFunction)) {
+        _commentBeforeFunction = node;
+        return null;
+      }
+    }
+    super.visitComment(node);
+    _commentBeforeFunction = null;
+    return null;
+  }
+
+  @override
+  Object visitCommentReference(CommentReference node) {
+    //
+    // We do not visit the identifier because it needs to be visited in the
+    // context of the reference.
+    //
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  @override
+  Object visitCompilationUnit(CompilationUnit node) {
+    //
+    // TODO(brianwilkerson) The goal of the code below is to visit the
+    // declarations in such an order that we can infer type information for
+    // top-level variables before we visit references to them. This is better
+    // than making no effort, but still doesn't completely satisfy that goal
+    // (consider for example "final var a = b; final var b = 0;"; we'll infer a
+    // type of 'int' for 'b', but not for 'a' because of the order of the
+    // visits). Ideally we would create a dependency graph, but that would
+    // require references to be resolved, which they are not.
+    //
+    _overrideManager.enterScope();
+    try {
+      NodeList<Directive> directives = node.directives;
+      int directiveCount = directives.length;
+      for (int i = 0; i < directiveCount; i++) {
+        directives[i].accept(this);
+      }
+      NodeList<CompilationUnitMember> declarations = node.declarations;
+      int declarationCount = declarations.length;
+      for (int i = 0; i < declarationCount; i++) {
+        CompilationUnitMember declaration = declarations[i];
+        if (declaration is! ClassDeclaration) {
+          declaration.accept(this);
+        }
+      }
+      for (int i = 0; i < declarationCount; i++) {
+        CompilationUnitMember declaration = declarations[i];
+        if (declaration is ClassDeclaration) {
+          declaration.accept(this);
+        }
+      }
+    } finally {
+      _overrideManager.exitScope();
+    }
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  @override
+  Object visitConditionalExpression(ConditionalExpression node) {
+    Expression condition = node.condition;
+    safelyVisit(condition);
+    Expression thenExpression = node.thenExpression;
+    if (thenExpression != null) {
+      _overrideManager.enterScope();
+      try {
+        _promoteManager.enterScope();
+        try {
+          _propagateTrueState(condition);
+          // Type promotion.
+          _promoteTypes(condition);
+          _clearTypePromotionsIfPotentiallyMutatedIn(thenExpression);
+          _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated(
+              thenExpression);
+          // Visit "then" expression.
+          thenExpression.accept(this);
+        } finally {
+          _promoteManager.exitScope();
+        }
+      } finally {
+        _overrideManager.exitScope();
+      }
+    }
+    Expression elseExpression = node.elseExpression;
+    if (elseExpression != null) {
+      _overrideManager.enterScope();
+      try {
+        _propagateFalseState(condition);
+        elseExpression.accept(this);
+      } finally {
+        _overrideManager.exitScope();
+      }
+    }
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    bool thenIsAbrupt = _isAbruptTerminationExpression(thenExpression);
+    bool elseIsAbrupt = _isAbruptTerminationExpression(elseExpression);
+    if (elseIsAbrupt && !thenIsAbrupt) {
+      _propagateTrueState(condition);
+      _propagateState(thenExpression);
+    } else if (thenIsAbrupt && !elseIsAbrupt) {
+      _propagateFalseState(condition);
+      _propagateState(elseExpression);
+    }
+    return null;
+  }
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    ExecutableElement outerFunction = _enclosingFunction;
+    try {
+      _enclosingFunction = node.element;
+      super.visitConstructorDeclaration(node);
+    } finally {
+      _enclosingFunction = outerFunction;
+    }
+    ConstructorElementImpl constructor = node.element;
+    constructor.constantInitializers =
+        new ConstantAstCloner().cloneNodeList(node.initializers);
+    return null;
+  }
+
+  @override
+  Object visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+    //
+    // We visit the expression, but do not visit the field name because it needs
+    // to be visited in the context of the constructor field initializer node.
+    //
+    safelyVisit(node.expression);
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  @override
+  Object visitConstructorName(ConstructorName node) {
+    //
+    // We do not visit either the type name, because it won't be visited anyway,
+    // or the name, because it needs to be visited in the context of the
+    // constructor name.
+    //
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  @override
+  Object visitContinueStatement(ContinueStatement node) {
+    //
+    // We do not visit the label because it needs to be visited in the context
+    // of the statement.
+    //
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  @override
+  Object visitDoStatement(DoStatement node) {
+    _overrideManager.enterScope();
+    try {
+      super.visitDoStatement(node);
+    } finally {
+      _overrideManager.exitScope();
+    }
+    // TODO(brianwilkerson) If the loop can only be exited because the condition
+    // is false, then propagateFalseState(node.getCondition());
+    return null;
+  }
+
+  @override
+  Object visitEmptyFunctionBody(EmptyFunctionBody node) {
+    safelyVisit(_commentBeforeFunction);
+    if (resolveOnlyCommentInFunctionBody) {
+      return null;
+    }
+    return super.visitEmptyFunctionBody(node);
+  }
+
+  @override
+  Object visitEnumDeclaration(EnumDeclaration node) {
+    //
+    // Resolve the metadata in the library scope.
+    //
+    if (node.metadata != null) {
+      node.metadata.accept(this);
+    }
+    //
+    // There is nothing else to do because everything else was resolved by the
+    // element builder.
+    //
+    return null;
+  }
+
+  @override
+  Object visitExpressionFunctionBody(ExpressionFunctionBody node) {
+    safelyVisit(_commentBeforeFunction);
+    if (resolveOnlyCommentInFunctionBody) {
+      return null;
+    }
+    _overrideManager.enterScope();
+    try {
+      super.visitExpressionFunctionBody(node);
+    } finally {
+      _overrideManager.exitScope();
+    }
+    return null;
+  }
+
+  @override
+  Object visitFieldDeclaration(FieldDeclaration node) {
+    _overrideManager.enterScope();
+    try {
+      super.visitFieldDeclaration(node);
+    } finally {
+      Map<VariableElement, DartType> overrides =
+          _overrideManager.captureOverrides(node.fields);
+      _overrideManager.exitScope();
+      _overrideManager.applyOverrides(overrides);
+    }
+    return null;
+  }
+
+  @override
+  Object visitForEachStatement(ForEachStatement node) {
+    _overrideManager.enterScope();
+    try {
+      super.visitForEachStatement(node);
+    } finally {
+      _overrideManager.exitScope();
+    }
+    return null;
+  }
+
+  @override
+  void visitForEachStatementInScope(ForEachStatement node) {
+    //
+    // We visit the iterator before the loop variable because the loop variable
+    // cannot be in scope while visiting the iterator.
+    //
+    Expression iterable = node.iterable;
+    safelyVisit(iterable);
+    DeclaredIdentifier loopVariable = node.loopVariable;
+    SimpleIdentifier identifier = node.identifier;
+    safelyVisit(loopVariable);
+    safelyVisit(identifier);
+    Statement body = node.body;
+    if (body != null) {
+      _overrideManager.enterScope();
+      try {
+        if (loopVariable != null && iterable != null) {
+          LocalVariableElement loopElement = loopVariable.element;
+          if (loopElement != null) {
+            DartType iteratorElementType = _getIteratorElementType(iterable);
+            overrideVariable(loopElement, iteratorElementType, true);
+            _recordPropagatedType(loopVariable.identifier, iteratorElementType);
+          }
+        } else if (identifier != null && iterable != null) {
+          Element identifierElement = identifier.staticElement;
+          if (identifierElement is VariableElement) {
+            DartType iteratorElementType = _getIteratorElementType(iterable);
+            overrideVariable(identifierElement, iteratorElementType, true);
+            _recordPropagatedType(identifier, iteratorElementType);
+          }
+        }
+        visitStatementInScope(body);
+      } finally {
+        _overrideManager.exitScope();
+      }
+    }
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+  }
+
+  @override
+  Object visitForStatement(ForStatement node) {
+    _overrideManager.enterScope();
+    try {
+      super.visitForStatement(node);
+    } finally {
+      _overrideManager.exitScope();
+    }
+    return null;
+  }
+
+  @override
+  void visitForStatementInScope(ForStatement node) {
+    safelyVisit(node.variables);
+    safelyVisit(node.initialization);
+    safelyVisit(node.condition);
+    _overrideManager.enterScope();
+    try {
+      _propagateTrueState(node.condition);
+      visitStatementInScope(node.body);
+      node.updaters.accept(this);
+    } finally {
+      _overrideManager.exitScope();
+    }
+    // TODO(brianwilkerson) If the loop can only be exited because the condition
+    // is false, then propagateFalseState(condition);
+  }
+
+  @override
+  Object visitFunctionDeclaration(FunctionDeclaration node) {
+    ExecutableElement outerFunction = _enclosingFunction;
+    try {
+      SimpleIdentifier functionName = node.name;
+      _enclosingFunction = functionName.staticElement as ExecutableElement;
+      super.visitFunctionDeclaration(node);
+    } finally {
+      _enclosingFunction = outerFunction;
+    }
+    return null;
+  }
+
+  @override
+  Object visitFunctionExpression(FunctionExpression node) {
+    ExecutableElement outerFunction = _enclosingFunction;
+    try {
+      _enclosingFunction = node.element;
+      _overrideManager.enterScope();
+      try {
+        super.visitFunctionExpression(node);
+      } finally {
+        _overrideManager.exitScope();
+      }
+    } finally {
+      _enclosingFunction = outerFunction;
+    }
+    return null;
+  }
+
+  @override
+  Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    safelyVisit(node.function);
+    node.accept(elementResolver);
+    _inferFunctionExpressionsParametersTypes(node.argumentList);
+    safelyVisit(node.argumentList);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  @override
+  Object visitFunctionTypeAlias(FunctionTypeAlias node) {
+    // Resolve the metadata in the library scope.
+    if (node.metadata != null) {
+      node.metadata.accept(this);
+    }
+    FunctionTypeAlias outerAlias = _enclosingFunctionTypeAlias;
+    _enclosingFunctionTypeAlias = node;
+    try {
+      super.visitFunctionTypeAlias(node);
+    } finally {
+      _enclosingFunctionTypeAlias = outerAlias;
+    }
+    return null;
+  }
+
+  @override
+  Object visitHideCombinator(HideCombinator node) => null;
+
+  @override
+  Object visitIfStatement(IfStatement node) {
+    Expression condition = node.condition;
+    safelyVisit(condition);
+    Map<VariableElement, DartType> thenOverrides =
+        new HashMap<VariableElement, DartType>();
+    Statement thenStatement = node.thenStatement;
+    if (thenStatement != null) {
+      _overrideManager.enterScope();
+      try {
+        _promoteManager.enterScope();
+        try {
+          _propagateTrueState(condition);
+          // Type promotion.
+          _promoteTypes(condition);
+          _clearTypePromotionsIfPotentiallyMutatedIn(thenStatement);
+          _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated(
+              thenStatement);
+          // Visit "then".
+          visitStatementInScope(thenStatement);
+        } finally {
+          _promoteManager.exitScope();
+        }
+      } finally {
+        thenOverrides = _overrideManager.captureLocalOverrides();
+        _overrideManager.exitScope();
+      }
+    }
+    Map<VariableElement, DartType> elseOverrides =
+        new HashMap<VariableElement, DartType>();
+    Statement elseStatement = node.elseStatement;
+    if (elseStatement != null) {
+      _overrideManager.enterScope();
+      try {
+        _propagateFalseState(condition);
+        visitStatementInScope(elseStatement);
+      } finally {
+        elseOverrides = _overrideManager.captureLocalOverrides();
+        _overrideManager.exitScope();
+      }
+    }
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    // Join overrides.
+    bool thenIsAbrupt = _isAbruptTerminationStatement(thenStatement);
+    bool elseIsAbrupt = _isAbruptTerminationStatement(elseStatement);
+    if (elseIsAbrupt && !thenIsAbrupt) {
+      _propagateTrueState(condition);
+      _overrideManager.applyOverrides(thenOverrides);
+    } else if (thenIsAbrupt && !elseIsAbrupt) {
+      _propagateFalseState(condition);
+      _overrideManager.applyOverrides(elseOverrides);
+    } else if (!thenIsAbrupt && !elseIsAbrupt) {
+      List<Map<VariableElement, DartType>> perBranchOverrides =
+          new List<Map<VariableElement, DartType>>();
+      perBranchOverrides.add(thenOverrides);
+      perBranchOverrides.add(elseOverrides);
+      _overrideManager.mergeOverrides(perBranchOverrides);
+    }
+    return null;
+  }
+
+  @override
+  Object visitLabel(Label node) => null;
+
+  @override
+  Object visitLibraryIdentifier(LibraryIdentifier node) => null;
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    ExecutableElement outerFunction = _enclosingFunction;
+    try {
+      _enclosingFunction = node.element;
+      super.visitMethodDeclaration(node);
+    } finally {
+      _enclosingFunction = outerFunction;
+    }
+    return null;
+  }
+
+  @override
+  Object visitMethodInvocation(MethodInvocation node) {
+    //
+    // We visit the target and argument list, but do not visit the method name
+    // because it needs to be visited in the context of the invocation.
+    //
+    safelyVisit(node.target);
+    node.accept(elementResolver);
+    _inferFunctionExpressionsParametersTypes(node.argumentList);
+    safelyVisit(node.argumentList);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  @override
+  Object visitNode(AstNode node) {
+    node.visitChildren(this);
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  @override
+  Object visitPrefixedIdentifier(PrefixedIdentifier node) {
+    //
+    // We visit the prefix, but do not visit the identifier because it needs to
+    // be visited in the context of the prefix.
+    //
+    safelyVisit(node.prefix);
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  @override
+  Object visitPropertyAccess(PropertyAccess node) {
+    //
+    // We visit the target, but do not visit the property name because it needs
+    // to be visited in the context of the property access node.
+    //
+    safelyVisit(node.target);
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  @override
+  Object visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    //
+    // We visit the argument list, but do not visit the optional identifier
+    // because it needs to be visited in the context of the constructor
+    // invocation.
+    //
+    safelyVisit(node.argumentList);
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  @override
+  Object visitShowCombinator(ShowCombinator node) => null;
+
+  @override
+  Object visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    //
+    // We visit the argument list, but do not visit the optional identifier
+    // because it needs to be visited in the context of the constructor
+    // invocation.
+    //
+    safelyVisit(node.argumentList);
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  @override
+  Object visitSwitchCase(SwitchCase node) {
+    _overrideManager.enterScope();
+    try {
+      super.visitSwitchCase(node);
+    } finally {
+      _overrideManager.exitScope();
+    }
+    return null;
+  }
+
+  @override
+  Object visitSwitchDefault(SwitchDefault node) {
+    _overrideManager.enterScope();
+    try {
+      super.visitSwitchDefault(node);
+    } finally {
+      _overrideManager.exitScope();
+    }
+    return null;
+  }
+
+  @override
+  Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+    _overrideManager.enterScope();
+    try {
+      super.visitTopLevelVariableDeclaration(node);
+    } finally {
+      Map<VariableElement, DartType> overrides =
+          _overrideManager.captureOverrides(node.variables);
+      _overrideManager.exitScope();
+      _overrideManager.applyOverrides(overrides);
+    }
+    return null;
+  }
+
+  @override
+  Object visitTypeName(TypeName node) => null;
+
+  @override
+  Object visitVariableDeclaration(VariableDeclaration node) {
+    super.visitVariableDeclaration(node);
+    if (node.element.isConst && node.initializer != null) {
+      (node.element as ConstVariableElement).constantInitializer =
+          new ConstantAstCloner().cloneNode(node.initializer);
+    }
+    return null;
+  }
+
+  @override
+  Object visitWhileStatement(WhileStatement node) {
+    // Note: since we don't call the base class, we have to maintain
+    // _implicitLabelScope ourselves.
+    ImplicitLabelScope outerImplicitScope = _implicitLabelScope;
+    try {
+      _implicitLabelScope = _implicitLabelScope.nest(node);
+      Expression condition = node.condition;
+      safelyVisit(condition);
+      Statement body = node.body;
+      if (body != null) {
+        _overrideManager.enterScope();
+        try {
+          _propagateTrueState(condition);
+          visitStatementInScope(body);
+        } finally {
+          _overrideManager.exitScope();
+        }
+      }
+    } finally {
+      _implicitLabelScope = outerImplicitScope;
+    }
+    // TODO(brianwilkerson) If the loop can only be exited because the condition
+    // is false, then propagateFalseState(condition);
+    node.accept(elementResolver);
+    node.accept(typeAnalyzer);
+    return null;
+  }
+
+  /**
+   * Checks each promoted variable in the current scope for compliance with the following
+   * specification statement:
+   *
+   * If the variable <i>v</i> is accessed by a closure in <i>s<sub>1</sub></i> then the variable
+   * <i>v</i> is not potentially mutated anywhere in the scope of <i>v</i>.
+   */
+  void _clearTypePromotionsIfAccessedInClosureAndProtentiallyMutated(
+      AstNode target) {
+    for (Element element in _promoteManager.promotedElements) {
+      if ((element as VariableElementImpl).isPotentiallyMutatedInScope) {
+        if (_isVariableAccessedInClosure(element, target)) {
+          _promoteManager.setType(element, null);
+        }
+      }
+    }
+  }
+
+  /**
+   * Checks each promoted variable in the current scope for compliance with the following
+   * specification statement:
+   *
+   * <i>v</i> is not potentially mutated in <i>s<sub>1</sub></i> or within a closure.
+   */
+  void _clearTypePromotionsIfPotentiallyMutatedIn(AstNode target) {
+    for (Element element in _promoteManager.promotedElements) {
+      if (_isVariablePotentiallyMutatedIn(element, target)) {
+        _promoteManager.setType(element, null);
+      }
+    }
+  }
+
+  /**
+   * The given expression is the expression used to compute the iterator for a for-each statement.
+   * Attempt to compute the type of objects that will be assigned to the loop variable and return
+   * that type. Return `null` if the type could not be determined.
+   *
+   * @param iterator the iterator for a for-each statement
+   * @return the type of objects that will be assigned to the loop variable
+   */
+  DartType _getIteratorElementType(Expression iteratorExpression) {
+    DartType expressionType = iteratorExpression.bestType;
+    if (expressionType is InterfaceType) {
+      InterfaceType interfaceType = expressionType;
+      FunctionType iteratorFunction =
+          _inheritanceManager.lookupMemberType(interfaceType, "iterator");
+      if (iteratorFunction == null) {
+        // TODO(brianwilkerson) Should we report this error?
+        return null;
+      }
+      DartType iteratorType = iteratorFunction.returnType;
+      if (iteratorType is InterfaceType) {
+        InterfaceType iteratorInterfaceType = iteratorType;
+        FunctionType currentFunction = _inheritanceManager.lookupMemberType(
+            iteratorInterfaceType, "current");
+        if (currentFunction == null) {
+          // TODO(brianwilkerson) Should we report this error?
+          return null;
+        }
+        return currentFunction.returnType;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * If given "mayBeClosure" is [FunctionExpression] without explicit parameters types and its
+   * required type is [FunctionType], then infer parameters types from [FunctionType].
+   */
+  void _inferFunctionExpressionParametersTypes(
+      Expression mayBeClosure, DartType mayByFunctionType) {
+    // prepare closure
+    if (mayBeClosure is! FunctionExpression) {
+      return;
+    }
+    FunctionExpression closure = mayBeClosure as FunctionExpression;
+    // prepare expected closure type
+    if (mayByFunctionType is! FunctionType) {
+      return;
+    }
+    FunctionType expectedClosureType = mayByFunctionType as FunctionType;
+    // If the expectedClosureType is not more specific than the static type,
+    // return.
+    DartType staticClosureType =
+        (closure.element != null ? closure.element.type : null) as DartType;
+    if (staticClosureType != null &&
+        !expectedClosureType.isMoreSpecificThan(staticClosureType)) {
+      return;
+    }
+    // set propagated type for the closure
+    closure.propagatedType = expectedClosureType;
+    // set inferred types for parameters
+    NodeList<FormalParameter> parameters = closure.parameters.parameters;
+    List<ParameterElement> expectedParameters = expectedClosureType.parameters;
+    for (int i = 0;
+        i < parameters.length && i < expectedParameters.length;
+        i++) {
+      FormalParameter parameter = parameters[i];
+      ParameterElement element = parameter.element;
+      DartType currentType = _overrideManager.getBestType(element);
+      // may be override the type
+      DartType expectedType = expectedParameters[i].type;
+      if (currentType == null || expectedType.isMoreSpecificThan(currentType)) {
+        _overrideManager.setType(element, expectedType);
+      }
+    }
+  }
+
+  /**
+   * Try to infer types of parameters of the [FunctionExpression] arguments.
+   */
+  void _inferFunctionExpressionsParametersTypes(ArgumentList argumentList) {
+    for (Expression argument in argumentList.arguments) {
+      ParameterElement parameter = argument.propagatedParameterElement;
+      if (parameter == null) {
+        parameter = argument.staticParameterElement;
+      }
+      if (parameter != null) {
+        _inferFunctionExpressionParametersTypes(argument, parameter.type);
+      }
+    }
+  }
+
+  /**
+   * Return `true` if the given expression terminates abruptly (that is, if any expression
+   * following the given expression will not be reached).
+   *
+   * @param expression the expression being tested
+   * @return `true` if the given expression terminates abruptly
+   */
+  bool _isAbruptTerminationExpression(Expression expression) {
+    // TODO(brianwilkerson) This needs to be significantly improved. Ideally we
+    // would eventually turn this into a method on Expression that returns a
+    // termination indication (normal, abrupt with no exception, abrupt with an
+    // exception).
+    while (expression is ParenthesizedExpression) {
+      expression = (expression as ParenthesizedExpression).expression;
+    }
+    return expression is ThrowExpression || expression is RethrowExpression;
+  }
+
+  /**
+   * Return `true` if the given statement terminates abruptly (that is, if any statement
+   * following the given statement will not be reached).
+   *
+   * @param statement the statement being tested
+   * @return `true` if the given statement terminates abruptly
+   */
+  bool _isAbruptTerminationStatement(Statement statement) {
+    // TODO(brianwilkerson) This needs to be significantly improved. Ideally we
+    // would eventually turn this into a method on Statement that returns a
+    // termination indication (normal, abrupt with no exception, abrupt with an
+    // exception).
+    //
+    // collinsn: it is unsound to assume that [break] and [continue] are
+    // "abrupt". See: https://code.google.com/p/dart/issues/detail?id=19929#c4
+    // (tests are included in TypePropagationTest.java).
+    // In general, the difficulty is loopy control flow.
+    //
+    // In the presence of exceptions things become much more complicated, but
+    // while we only use this to propagate at [if]-statement join points,
+    // checking for [return] may work well enough in the common case.
+    if (statement is ReturnStatement) {
+      return true;
+    } else if (statement is ExpressionStatement) {
+      return _isAbruptTerminationExpression(statement.expression);
+    } else if (statement is Block) {
+      NodeList<Statement> statements = statement.statements;
+      int size = statements.length;
+      if (size == 0) {
+        return false;
+      }
+
+      // This last-statement-is-return heuristic is unsound for adversarial
+      // code, but probably works well in the common case:
+      //
+      //   var x = 123;
+      //   var c = true;
+      //   L: if (c) {
+      //     x = "hello";
+      //     c = false;
+      //     break L;
+      //     return;
+      //   }
+      //   print(x);
+      //
+      // Unsound to assume that [x = "hello";] never executed after the
+      // if-statement. Of course, a dead-code analysis could point out that
+      // [return] here is dead.
+      return _isAbruptTerminationStatement(statements[size - 1]);
+    }
+    return false;
+  }
+
+  /**
+   * Return `true` if the given variable is accessed within a closure in the given
+   * [AstNode] and also mutated somewhere in variable scope. This information is only
+   * available for local variables (including parameters).
+   *
+   * @param variable the variable to check
+   * @param target the [AstNode] to check within
+   * @return `true` if this variable is potentially mutated somewhere in the given ASTNode
+   */
+  bool _isVariableAccessedInClosure(Element variable, AstNode target) {
+    _ResolverVisitor_isVariableAccessedInClosure visitor =
+        new _ResolverVisitor_isVariableAccessedInClosure(variable);
+    target.accept(visitor);
+    return visitor.result;
+  }
+
+  /**
+   * Return `true` if the given variable is potentially mutated somewhere in the given
+   * [AstNode]. This information is only available for local variables (including parameters).
+   *
+   * @param variable the variable to check
+   * @param target the [AstNode] to check within
+   * @return `true` if this variable is potentially mutated somewhere in the given ASTNode
+   */
+  bool _isVariablePotentiallyMutatedIn(Element variable, AstNode target) {
+    _ResolverVisitor_isVariablePotentiallyMutatedIn visitor =
+        new _ResolverVisitor_isVariablePotentiallyMutatedIn(variable);
+    target.accept(visitor);
+    return visitor.result;
+  }
+
+  /**
+   * If it is appropriate to do so, promotes the current type of the static element associated with
+   * the given expression with the given type. Generally speaking, it is appropriate if the given
+   * type is more specific than the current type.
+   *
+   * @param expression the expression used to access the static element whose types might be
+   *          promoted
+   * @param potentialType the potential type of the elements
+   */
+  void _promote(Expression expression, DartType potentialType) {
+    VariableElement element = getPromotionStaticElement(expression);
+    if (element != null) {
+      // may be mutated somewhere in closure
+      if (element.isPotentiallyMutatedInClosure) {
+        return;
+      }
+      // prepare current variable type
+      DartType type = _promoteManager.getType(element);
+      if (type == null) {
+        type = expression.staticType;
+      }
+      // Declared type should not be "dynamic".
+      if (type == null || type.isDynamic) {
+        return;
+      }
+      // Promoted type should not be "dynamic".
+      if (potentialType == null || potentialType.isDynamic) {
+        return;
+      }
+      // Promoted type should be more specific than declared.
+      if (!potentialType.isMoreSpecificThan(type)) {
+        return;
+      }
+      // Do promote type of variable.
+      _promoteManager.setType(element, potentialType);
+    }
+  }
+
+  /**
+   * Promotes type information using given condition.
+   */
+  void _promoteTypes(Expression condition) {
+    if (condition is BinaryExpression) {
+      BinaryExpression binary = condition;
+      if (binary.operator.type == sc.TokenType.AMPERSAND_AMPERSAND) {
+        Expression left = binary.leftOperand;
+        Expression right = binary.rightOperand;
+        _promoteTypes(left);
+        _promoteTypes(right);
+        _clearTypePromotionsIfPotentiallyMutatedIn(right);
+      }
+    } else if (condition is IsExpression) {
+      IsExpression is2 = condition;
+      if (is2.notOperator == null) {
+        _promote(is2.expression, is2.type.type);
+      }
+    } else if (condition is ParenthesizedExpression) {
+      _promoteTypes(condition.expression);
+    }
+  }
+
+  /**
+   * Propagate any type information that results from knowing that the given condition will have
+   * been evaluated to 'false'.
+   *
+   * @param condition the condition that will have evaluated to 'false'
+   */
+  void _propagateFalseState(Expression condition) {
+    if (condition is BinaryExpression) {
+      BinaryExpression binary = condition;
+      if (binary.operator.type == sc.TokenType.BAR_BAR) {
+        _propagateFalseState(binary.leftOperand);
+        _propagateFalseState(binary.rightOperand);
+      }
+    } else if (condition is IsExpression) {
+      IsExpression is2 = condition;
+      if (is2.notOperator != null) {
+        // Since an is-statement doesn't actually change the type, we don't
+        // let it affect the propagated type when it would result in a loss
+        // of precision.
+        overrideExpression(is2.expression, is2.type.type, false);
+      }
+    } else if (condition is PrefixExpression) {
+      PrefixExpression prefix = condition;
+      if (prefix.operator.type == sc.TokenType.BANG) {
+        _propagateTrueState(prefix.operand);
+      }
+    } else if (condition is ParenthesizedExpression) {
+      _propagateFalseState(condition.expression);
+    }
+  }
+
+  /**
+   * Propagate any type information that results from knowing that the given expression will have
+   * been evaluated without altering the flow of execution.
+   *
+   * @param expression the expression that will have been evaluated
+   */
+  void _propagateState(Expression expression) {
+    // TODO(brianwilkerson) Implement this.
+  }
+
+  /**
+   * Propagate any type information that results from knowing that the given condition will have
+   * been evaluated to 'true'.
+   *
+   * @param condition the condition that will have evaluated to 'true'
+   */
+  void _propagateTrueState(Expression condition) {
+    if (condition is BinaryExpression) {
+      BinaryExpression binary = condition;
+      if (binary.operator.type == sc.TokenType.AMPERSAND_AMPERSAND) {
+        _propagateTrueState(binary.leftOperand);
+        _propagateTrueState(binary.rightOperand);
+      }
+    } else if (condition is IsExpression) {
+      IsExpression is2 = condition;
+      if (is2.notOperator == null) {
+        // Since an is-statement doesn't actually change the type, we don't
+        // let it affect the propagated type when it would result in a loss
+        // of precision.
+        overrideExpression(is2.expression, is2.type.type, false);
+      }
+    } else if (condition is PrefixExpression) {
+      PrefixExpression prefix = condition;
+      if (prefix.operator.type == sc.TokenType.BANG) {
+        _propagateFalseState(prefix.operand);
+      }
+    } else if (condition is ParenthesizedExpression) {
+      _propagateTrueState(condition.expression);
+    }
+  }
+
+  /**
+   * Record that the propagated type of the given node is the given type.
+   *
+   * @param expression the node whose type is to be recorded
+   * @param type the propagated type of the node
+   */
+  void _recordPropagatedType(Expression expression, DartType type) {
+    if (type != null && !type.isDynamic) {
+      expression.propagatedType = type;
+    }
+  }
+}
+
+/**
+ * The abstract class `Scope` defines the behavior common to name scopes used by the resolver
+ * to determine which names are visible at any given point in the code.
+ */
+abstract class Scope {
+  /**
+   * The prefix used to mark an identifier as being private to its library.
+   */
+  static int PRIVATE_NAME_PREFIX = 0x5F;
+
+  /**
+   * The suffix added to the declared name of a setter when looking up the setter. Used to
+   * disambiguate between a getter and a setter that have the same name.
+   */
+  static String SETTER_SUFFIX = "=";
+
+  /**
+   * The name used to look up the method used to implement the unary minus operator. Used to
+   * disambiguate between the unary and binary operators.
+   */
+  static String UNARY_MINUS = "unary-";
+
+  /**
+   * A table mapping names that are defined in this scope to the element representing the thing
+   * declared with that name.
+   */
+  HashMap<String, Element> _definedNames = new HashMap<String, Element>();
+
+  /**
+   * A flag indicating whether there are any names defined in this scope.
+   */
+  bool _hasName = false;
+
+  /**
+   * Return the scope in which this scope is lexically enclosed.
+   *
+   * @return the scope in which this scope is lexically enclosed
+   */
+  Scope get enclosingScope => null;
+
+  /**
+   * Return the listener that is to be informed when an error is encountered.
+   *
+   * @return the listener that is to be informed when an error is encountered
+   */
+  AnalysisErrorListener get errorListener;
+
+  /**
+   * Add the given element to this scope. If there is already an element with the given name defined
+   * in this scope, then an error will be generated and the original element will continue to be
+   * mapped to the name. If there is an element with the given name in an enclosing scope, then a
+   * warning will be generated but the given element will hide the inherited element.
+   *
+   * @param element the element to be added to this scope
+   */
+  void define(Element element) {
+    String name = _getName(element);
+    if (name != null && !name.isEmpty) {
+      if (_definedNames.containsKey(name)) {
+        errorListener
+            .onError(getErrorForDuplicate(_definedNames[name], element));
+      } else {
+        _definedNames[name] = element;
+        _hasName = true;
+      }
+    }
+  }
+
+  /**
+   * Add the given element to this scope without checking for duplication or hiding.
+   *
+   * @param name the name of the element to be added
+   * @param element the element to be added to this scope
+   */
+  void defineNameWithoutChecking(String name, Element element) {
+    _definedNames[name] = element;
+    _hasName = true;
+  }
+
+  /**
+   * Add the given element to this scope without checking for duplication or hiding.
+   *
+   * @param element the element to be added to this scope
+   */
+  void defineWithoutChecking(Element element) {
+    _definedNames[_getName(element)] = element;
+    _hasName = true;
+  }
+
+  /**
+   * Return the error code to be used when reporting that a name being defined locally conflicts
+   * with another element of the same name in the local scope.
+   *
+   * @param existing the first element to be declared with the conflicting name
+   * @param duplicate another element declared with the conflicting name
+   * @return the error code used to report duplicate names within a scope
+   */
+  AnalysisError getErrorForDuplicate(Element existing, Element duplicate) {
+    // TODO(brianwilkerson) Customize the error message based on the types of
+    // elements that share the same name.
+    // TODO(jwren) There are 4 error codes for duplicate, but only 1 is being
+    // generated.
+    Source source = duplicate.source;
+    return new AnalysisError.con2(source, duplicate.nameOffset,
+        duplicate.displayName.length, CompileTimeErrorCode.DUPLICATE_DEFINITION,
+        [existing.displayName]);
+  }
+
+  /**
+   * Return the source that contains the given identifier, or the source associated with this scope
+   * if the source containing the identifier could not be determined.
+   *
+   * @param identifier the identifier whose source is to be returned
+   * @return the source that contains the given identifier
+   */
+  Source getSource(AstNode node) {
+    CompilationUnit unit = node.getAncestor((node) => node is CompilationUnit);
+    if (unit != null) {
+      CompilationUnitElement unitElement = unit.element;
+      if (unitElement != null) {
+        return unitElement.source;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the element with which the given name is associated, or `null` if the name is not
+   * defined within this scope.
+   *
+   * @param identifier the identifier node to lookup element for, used to report correct kind of a
+   *          problem and associate problem with
+   * @param name the name associated with the element to be returned
+   * @param referencingLibrary the library that contains the reference to the name, used to
+   *          implement library-level privacy
+   * @return the element with which the given name is associated
+   */
+  Element internalLookup(
+      Identifier identifier, String name, LibraryElement referencingLibrary);
+
+  /**
+   * Return the element with which the given name is associated, or `null` if the name is not
+   * defined within this scope. This method only returns elements that are directly defined within
+   * this scope, not elements that are defined in an enclosing scope.
+   *
+   * @param name the name associated with the element to be returned
+   * @param referencingLibrary the library that contains the reference to the name, used to
+   *          implement library-level privacy
+   * @return the element with which the given name is associated
+   */
+  Element localLookup(String name, LibraryElement referencingLibrary) {
+    if (_hasName) {
+      return _definedNames[name];
+    }
+    return null;
+  }
+
+  /**
+   * Return the element with which the given identifier is associated, or `null` if the name
+   * is not defined within this scope.
+   *
+   * @param identifier the identifier associated with the element to be returned
+   * @param referencingLibrary the library that contains the reference to the name, used to
+   *          implement library-level privacy
+   * @return the element with which the given identifier is associated
+   */
+  Element lookup(Identifier identifier, LibraryElement referencingLibrary) =>
+      internalLookup(identifier, identifier.name, referencingLibrary);
+
+  /**
+   * Return the name that will be used to look up the given element.
+   *
+   * @param element the element whose look-up name is to be returned
+   * @return the name that will be used to look up the given element
+   */
+  String _getName(Element element) {
+    if (element is MethodElement) {
+      MethodElement method = element;
+      if (method.name == "-" && method.parameters.length == 0) {
+        return UNARY_MINUS;
+      }
+    }
+    return element.name;
+  }
+
+  /**
+   * Return `true` if the given name is a library-private name.
+   *
+   * @param name the name being tested
+   * @return `true` if the given name is a library-private name
+   */
+  static bool isPrivateName(String name) =>
+      name != null && StringUtilities.startsWithChar(name, PRIVATE_NAME_PREFIX);
+}
+
+/**
+ * The abstract class `ScopedVisitor` maintains name and label scopes as an AST structure is
+ * being visited.
+ */
+abstract class ScopedVisitor extends UnifyingAstVisitor<Object> {
+  /**
+   * The element for the library containing the compilation unit being visited.
+   */
+  LibraryElement _definingLibrary;
+
+  /**
+   * The source representing the compilation unit being visited.
+   */
+  final Source source;
+
+  /**
+   * The error listener that will be informed of any errors that are found during resolution.
+   */
+  AnalysisErrorListener _errorListener;
+
+  /**
+   * The scope used to resolve identifiers.
+   */
+  Scope nameScope;
+
+  /**
+   * The object used to access the types from the core library.
+   */
+  final TypeProvider typeProvider;
+
+  /**
+   * The scope used to resolve unlabeled `break` and `continue` statements.
+   */
+  ImplicitLabelScope _implicitLabelScope = ImplicitLabelScope.ROOT;
+
+  /**
+   * The scope used to resolve labels for `break` and `continue` statements, or
+   * `null` if no labels have been defined in the current context.
+   */
+  LabelScope labelScope;
+
+  /**
+   * The class containing the AST nodes being visited,
+   * or `null` if we are not in the scope of a class.
+   */
+  ClassElement enclosingClass;
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
+   *
+   * @param library the library containing the compilation unit being resolved
+   * @param source the source representing the compilation unit being visited
+   * @param typeProvider the object used to access the types from the core library
+   */
+  ScopedVisitor.con1(Library library, this.source, this.typeProvider) {
+    this._definingLibrary = library.libraryElement;
+    LibraryScope libraryScope = library.libraryScope;
+    this._errorListener = libraryScope.errorListener;
+    this.nameScope = libraryScope;
+  }
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
+   *
+   * @param definingLibrary the element for the library containing the compilation unit being
+   *          visited
+   * @param source the source representing the compilation unit being visited
+   * @param typeProvider the object used to access the types from the core library
+   * @param errorListener the error listener that will be informed of any errors that are found
+   *          during resolution
+   */
+  ScopedVisitor.con2(LibraryElement definingLibrary, this.source,
+      this.typeProvider, AnalysisErrorListener errorListener) {
+    this._definingLibrary = definingLibrary;
+    this._errorListener = errorListener;
+    this.nameScope = new LibraryScope(definingLibrary, errorListener);
+  }
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
+   *
+   * @param definingLibrary the element for the library containing the compilation unit being
+   *          visited
+   * @param source the source representing the compilation unit being visited
+   * @param typeProvider the object used to access the types from the core library
+   * @param nameScope the scope used to resolve identifiers in the node that will first be visited
+   * @param errorListener the error listener that will be informed of any errors that are found
+   *          during resolution
+   */
+  ScopedVisitor.con3(LibraryElement definingLibrary, this.source,
+      this.typeProvider, Scope nameScope, AnalysisErrorListener errorListener) {
+    this._definingLibrary = definingLibrary;
+    this._errorListener = errorListener;
+    this.nameScope = nameScope;
+  }
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
+   *
+   * @param library the library containing the compilation unit being resolved
+   * @param source the source representing the compilation unit being visited
+   * @param typeProvider the object used to access the types from the core library
+   */
+  ScopedVisitor.con4(
+      ResolvableLibrary library, this.source, this.typeProvider) {
+    this._definingLibrary = library.libraryElement;
+    LibraryScope libraryScope = library.libraryScope;
+    this._errorListener = libraryScope.errorListener;
+    this.nameScope = libraryScope;
+  }
+
+  /**
+   * Return the library element for the library containing the compilation unit being resolved.
+   *
+   * @return the library element for the library containing the compilation unit being resolved
+   */
+  LibraryElement get definingLibrary => _definingLibrary;
+
+  /**
+   * Return the implicit label scope in which the current node is being
+   * resolved.
+   */
+  ImplicitLabelScope get implicitLabelScope => _implicitLabelScope;
+
+  /**
+   * Replaces the current [Scope] with the enclosing [Scope].
+   *
+   * @return the enclosing [Scope].
+   */
+  Scope popNameScope() {
+    nameScope = nameScope.enclosingScope;
+    return nameScope;
+  }
+
+  /**
+   * Pushes a new [Scope] into the visitor.
+   *
+   * @return the new [Scope].
+   */
+  Scope pushNameScope() {
+    Scope newScope = new EnclosedScope(nameScope);
+    nameScope = newScope;
+    return nameScope;
+  }
+
+  /**
+   * Report an error with the given error code and arguments.
+   *
+   * @param errorCode the error code of the error to be reported
+   * @param node the node specifying the location of the error
+   * @param arguments the arguments to the error, used to compose the error message
+   */
+  void reportErrorForNode(ErrorCode errorCode, AstNode node,
+      [List<Object> arguments]) {
+    _errorListener.onError(new AnalysisError.con2(
+        source, node.offset, node.length, errorCode, arguments));
+  }
+
+  /**
+   * Report an error with the given error code and arguments.
+   *
+   * @param errorCode the error code of the error to be reported
+   * @param offset the offset of the location of the error
+   * @param length the length of the location of the error
+   * @param arguments the arguments to the error, used to compose the error message
+   */
+  void reportErrorForOffset(ErrorCode errorCode, int offset, int length,
+      [List<Object> arguments]) {
+    _errorListener.onError(
+        new AnalysisError.con2(source, offset, length, errorCode, arguments));
+  }
+
+  /**
+   * Report an error with the given error code and arguments.
+   *
+   * @param errorCode the error code of the error to be reported
+   * @param token the token specifying the location of the error
+   * @param arguments the arguments to the error, used to compose the error message
+   */
+  void reportErrorForToken(ErrorCode errorCode, sc.Token token,
+      [List<Object> arguments]) {
+    _errorListener.onError(new AnalysisError.con2(
+        source, token.offset, token.length, errorCode, arguments));
+  }
+
+  /**
+   * Visit the given AST node if it is not null.
+   *
+   * @param node the node to be visited
+   */
+  void safelyVisit(AstNode node) {
+    if (node != null) {
+      node.accept(this);
+    }
+  }
+
+  @override
+  Object visitBlock(Block node) {
+    Scope outerScope = nameScope;
+    try {
+      EnclosedScope enclosedScope = new EnclosedScope(nameScope);
+      _hideNamesDefinedInBlock(enclosedScope, node);
+      nameScope = enclosedScope;
+      super.visitBlock(node);
+    } finally {
+      nameScope = outerScope;
+    }
+    return null;
+  }
+
+  @override
+  Object visitBlockFunctionBody(BlockFunctionBody node) {
+    ImplicitLabelScope implicitOuterScope = _implicitLabelScope;
+    try {
+      _implicitLabelScope = ImplicitLabelScope.ROOT;
+      super.visitBlockFunctionBody(node);
+    } finally {
+      _implicitLabelScope = implicitOuterScope;
+    }
+    return null;
+  }
+
+  @override
+  Object visitCatchClause(CatchClause node) {
+    SimpleIdentifier exception = node.exceptionParameter;
+    if (exception != null) {
+      Scope outerScope = nameScope;
+      try {
+        nameScope = new EnclosedScope(nameScope);
+        nameScope.define(exception.staticElement);
+        SimpleIdentifier stackTrace = node.stackTraceParameter;
+        if (stackTrace != null) {
+          nameScope.define(stackTrace.staticElement);
+        }
+        super.visitCatchClause(node);
+      } finally {
+        nameScope = outerScope;
+      }
+    } else {
+      super.visitCatchClause(node);
+    }
+    return null;
+  }
+
+  @override
+  Object visitClassDeclaration(ClassDeclaration node) {
+    ClassElement classElement = node.element;
+    Scope outerScope = nameScope;
+    try {
+      if (classElement == null) {
+        AnalysisEngine.instance.logger.logInformation(
+            "Missing element for class declaration ${node.name.name} in ${definingLibrary.source.fullName}",
+            new CaughtException(new AnalysisException(), null));
+        super.visitClassDeclaration(node);
+      } else {
+        ClassElement outerClass = enclosingClass;
+        try {
+          enclosingClass = node.element;
+          nameScope = new TypeParameterScope(nameScope, classElement);
+          visitClassDeclarationInScope(node);
+          nameScope = new ClassScope(nameScope, classElement);
+          visitClassMembersInScope(node);
+        } finally {
+          enclosingClass = outerClass;
+        }
+      }
+    } finally {
+      nameScope = outerScope;
+    }
+    return null;
+  }
+
+  void visitClassDeclarationInScope(ClassDeclaration node) {
+    safelyVisit(node.name);
+    safelyVisit(node.typeParameters);
+    safelyVisit(node.extendsClause);
+    safelyVisit(node.withClause);
+    safelyVisit(node.implementsClause);
+    safelyVisit(node.nativeClause);
+  }
+
+  void visitClassMembersInScope(ClassDeclaration node) {
+    safelyVisit(node.documentationComment);
+    node.metadata.accept(this);
+    node.members.accept(this);
+  }
+
+  @override
+  Object visitClassTypeAlias(ClassTypeAlias node) {
+    Scope outerScope = nameScope;
+    try {
+      ClassElement element = node.element;
+      nameScope =
+          new ClassScope(new TypeParameterScope(nameScope, element), element);
+      super.visitClassTypeAlias(node);
+    } finally {
+      nameScope = outerScope;
+    }
+    return null;
+  }
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    ConstructorElement constructorElement = node.element;
+    Scope outerScope = nameScope;
+    try {
+      if (constructorElement == null) {
+        StringBuffer buffer = new StringBuffer();
+        buffer.write("Missing element for constructor ");
+        buffer.write(node.returnType.name);
+        if (node.name != null) {
+          buffer.write(".");
+          buffer.write(node.name.name);
+        }
+        buffer.write(" in ");
+        buffer.write(definingLibrary.source.fullName);
+        AnalysisEngine.instance.logger.logInformation(buffer.toString(),
+            new CaughtException(new AnalysisException(), null));
+      } else {
+        nameScope = new FunctionScope(nameScope, constructorElement);
+      }
+      super.visitConstructorDeclaration(node);
+    } finally {
+      nameScope = outerScope;
+    }
+    return null;
+  }
+
+  @override
+  Object visitDeclaredIdentifier(DeclaredIdentifier node) {
+    VariableElement element = node.element;
+    if (element != null) {
+      nameScope.define(element);
+    }
+    super.visitDeclaredIdentifier(node);
+    return null;
+  }
+
+  @override
+  Object visitDoStatement(DoStatement node) {
+    ImplicitLabelScope outerImplicitScope = _implicitLabelScope;
+    try {
+      _implicitLabelScope = _implicitLabelScope.nest(node);
+      visitStatementInScope(node.body);
+      safelyVisit(node.condition);
+    } finally {
+      _implicitLabelScope = outerImplicitScope;
+    }
+    return null;
+  }
+
+  @override
+  Object visitForEachStatement(ForEachStatement node) {
+    Scope outerNameScope = nameScope;
+    ImplicitLabelScope outerImplicitScope = _implicitLabelScope;
+    try {
+      nameScope = new EnclosedScope(nameScope);
+      _implicitLabelScope = _implicitLabelScope.nest(node);
+      visitForEachStatementInScope(node);
+    } finally {
+      nameScope = outerNameScope;
+      _implicitLabelScope = outerImplicitScope;
+    }
+    return null;
+  }
+
+  /**
+   * Visit the given statement after it's scope has been created. This replaces the normal call to
+   * the inherited visit method so that ResolverVisitor can intervene when type propagation is
+   * enabled.
+   *
+   * @param node the statement to be visited
+   */
+  void visitForEachStatementInScope(ForEachStatement node) {
+    //
+    // We visit the iterator before the loop variable because the loop variable
+    // cannot be in scope while visiting the iterator.
+    //
+    safelyVisit(node.identifier);
+    safelyVisit(node.iterable);
+    safelyVisit(node.loopVariable);
+    visitStatementInScope(node.body);
+  }
+
+  @override
+  Object visitFormalParameterList(FormalParameterList node) {
+    super.visitFormalParameterList(node);
+    // We finished resolving function signature, now include formal parameters
+    // scope.  Note: we must not do this if the parent is a
+    // FunctionTypedFormalParameter, because in that case we aren't finished
+    // resolving the full function signature, just a part of it.
+    if (nameScope is FunctionScope &&
+        node.parent is! FunctionTypedFormalParameter) {
+      (nameScope as FunctionScope).defineParameters();
+    }
+    if (nameScope is FunctionTypeScope) {
+      (nameScope as FunctionTypeScope).defineParameters();
+    }
+    return null;
+  }
+
+  @override
+  Object visitForStatement(ForStatement node) {
+    Scope outerNameScope = nameScope;
+    ImplicitLabelScope outerImplicitScope = _implicitLabelScope;
+    try {
+      nameScope = new EnclosedScope(nameScope);
+      _implicitLabelScope = _implicitLabelScope.nest(node);
+      visitForStatementInScope(node);
+    } finally {
+      nameScope = outerNameScope;
+      _implicitLabelScope = outerImplicitScope;
+    }
+    return null;
+  }
+
+  /**
+   * Visit the given statement after it's scope has been created. This replaces the normal call to
+   * the inherited visit method so that ResolverVisitor can intervene when type propagation is
+   * enabled.
+   *
+   * @param node the statement to be visited
+   */
+  void visitForStatementInScope(ForStatement node) {
+    safelyVisit(node.variables);
+    safelyVisit(node.initialization);
+    safelyVisit(node.condition);
+    node.updaters.accept(this);
+    visitStatementInScope(node.body);
+  }
+
+  @override
+  Object visitFunctionDeclaration(FunctionDeclaration node) {
+    ExecutableElement functionElement = node.element;
+    if (functionElement != null &&
+        functionElement.enclosingElement is! CompilationUnitElement) {
+      nameScope.define(functionElement);
+    }
+    Scope outerScope = nameScope;
+    try {
+      if (functionElement == null) {
+        AnalysisEngine.instance.logger.logInformation(
+            "Missing element for top-level function ${node.name.name} in ${definingLibrary.source.fullName}",
+            new CaughtException(new AnalysisException(), null));
+      } else {
+        nameScope = new FunctionScope(nameScope, functionElement);
+      }
+      super.visitFunctionDeclaration(node);
+    } finally {
+      nameScope = outerScope;
+    }
+    return null;
+  }
+
+  @override
+  Object visitFunctionExpression(FunctionExpression node) {
+    if (node.parent is FunctionDeclaration) {
+      // We have already created a function scope and don't need to do so again.
+      super.visitFunctionExpression(node);
+    } else {
+      Scope outerScope = nameScope;
+      try {
+        ExecutableElement functionElement = node.element;
+        if (functionElement == null) {
+          StringBuffer buffer = new StringBuffer();
+          buffer.write("Missing element for function ");
+          AstNode parent = node.parent;
+          while (parent != null) {
+            if (parent is Declaration) {
+              Element parentElement = parent.element;
+              buffer.write(parentElement == null
+                  ? "<unknown> "
+                  : "${parentElement.name} ");
+            }
+            parent = parent.parent;
+          }
+          buffer.write("in ");
+          buffer.write(definingLibrary.source.fullName);
+          AnalysisEngine.instance.logger.logInformation(buffer.toString(),
+              new CaughtException(new AnalysisException(), null));
+        } else {
+          nameScope = new FunctionScope(nameScope, functionElement);
+        }
+        super.visitFunctionExpression(node);
+      } finally {
+        nameScope = outerScope;
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitFunctionTypeAlias(FunctionTypeAlias node) {
+    Scope outerScope = nameScope;
+    try {
+      nameScope = new FunctionTypeScope(nameScope, node.element);
+      super.visitFunctionTypeAlias(node);
+    } finally {
+      nameScope = outerScope;
+    }
+    return null;
+  }
+
+  @override
+  Object visitIfStatement(IfStatement node) {
+    safelyVisit(node.condition);
+    visitStatementInScope(node.thenStatement);
+    visitStatementInScope(node.elseStatement);
+    return null;
+  }
+
+  @override
+  Object visitLabeledStatement(LabeledStatement node) {
+    LabelScope outerScope = _addScopesFor(node.labels, node.unlabeled);
+    try {
+      super.visitLabeledStatement(node);
+    } finally {
+      labelScope = outerScope;
+    }
+    return null;
+  }
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    Scope outerScope = nameScope;
+    try {
+      ExecutableElement methodElement = node.element;
+      if (methodElement == null) {
+        AnalysisEngine.instance.logger.logInformation(
+            "Missing element for method ${node.name.name} in ${definingLibrary.source.fullName}",
+            new CaughtException(new AnalysisException(), null));
+      } else {
+        nameScope = new FunctionScope(nameScope, methodElement);
+      }
+      super.visitMethodDeclaration(node);
+    } finally {
+      nameScope = outerScope;
+    }
+    return null;
+  }
+
+  /**
+   * Visit the given statement after it's scope has been created. This is used by ResolverVisitor to
+   * correctly visit the 'then' and 'else' statements of an 'if' statement.
+   *
+   * @param node the statement to be visited
+   */
+  void visitStatementInScope(Statement node) {
+    if (node is Block) {
+      // Don't create a scope around a block because the block will create it's
+      // own scope.
+      visitBlock(node);
+    } else if (node != null) {
+      Scope outerNameScope = nameScope;
+      try {
+        nameScope = new EnclosedScope(nameScope);
+        node.accept(this);
+      } finally {
+        nameScope = outerNameScope;
+      }
+    }
+  }
+
+  @override
+  Object visitSwitchCase(SwitchCase node) {
+    node.expression.accept(this);
+    Scope outerNameScope = nameScope;
+    try {
+      nameScope = new EnclosedScope(nameScope);
+      node.statements.accept(this);
+    } finally {
+      nameScope = outerNameScope;
+    }
+    return null;
+  }
+
+  @override
+  Object visitSwitchDefault(SwitchDefault node) {
+    Scope outerNameScope = nameScope;
+    try {
+      nameScope = new EnclosedScope(nameScope);
+      node.statements.accept(this);
+    } finally {
+      nameScope = outerNameScope;
+    }
+    return null;
+  }
+
+  @override
+  Object visitSwitchStatement(SwitchStatement node) {
+    LabelScope outerScope = labelScope;
+    ImplicitLabelScope outerImplicitScope = _implicitLabelScope;
+    try {
+      _implicitLabelScope = _implicitLabelScope.nest(node);
+      for (SwitchMember member in node.members) {
+        for (Label label in member.labels) {
+          SimpleIdentifier labelName = label.label;
+          LabelElement labelElement = labelName.staticElement as LabelElement;
+          labelScope =
+              new LabelScope(labelScope, labelName.name, member, labelElement);
+        }
+      }
+      super.visitSwitchStatement(node);
+    } finally {
+      labelScope = outerScope;
+      _implicitLabelScope = outerImplicitScope;
+    }
+    return null;
+  }
+
+  @override
+  Object visitVariableDeclaration(VariableDeclaration node) {
+    super.visitVariableDeclaration(node);
+    if (node.parent.parent is! TopLevelVariableDeclaration &&
+        node.parent.parent is! FieldDeclaration) {
+      VariableElement element = node.element;
+      if (element != null) {
+        nameScope.define(element);
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitWhileStatement(WhileStatement node) {
+    safelyVisit(node.condition);
+    ImplicitLabelScope outerImplicitScope = _implicitLabelScope;
+    try {
+      _implicitLabelScope = _implicitLabelScope.nest(node);
+      visitStatementInScope(node.body);
+    } finally {
+      _implicitLabelScope = outerImplicitScope;
+    }
+    return null;
+  }
+
+  /**
+   * Add scopes for each of the given labels.
+   *
+   * @param labels the labels for which new scopes are to be added
+   * @return the scope that was in effect before the new scopes were added
+   */
+  LabelScope _addScopesFor(NodeList<Label> labels, AstNode node) {
+    LabelScope outerScope = labelScope;
+    for (Label label in labels) {
+      SimpleIdentifier labelNameNode = label.label;
+      String labelName = labelNameNode.name;
+      LabelElement labelElement = labelNameNode.staticElement as LabelElement;
+      labelScope = new LabelScope(labelScope, labelName, node, labelElement);
+    }
+    return outerScope;
+  }
+
+  /**
+   * Marks the local declarations of the given [Block] hidden in the enclosing scope.
+   * According to the scoping rules name is hidden if block defines it, but name is defined after
+   * its declaration statement.
+   */
+  void _hideNamesDefinedInBlock(EnclosedScope scope, Block block) {
+    NodeList<Statement> statements = block.statements;
+    int statementCount = statements.length;
+    for (int i = 0; i < statementCount; i++) {
+      Statement statement = statements[i];
+      if (statement is VariableDeclarationStatement) {
+        VariableDeclarationStatement vds = statement;
+        NodeList<VariableDeclaration> variables = vds.variables.variables;
+        int variableCount = variables.length;
+        for (int j = 0; j < variableCount; j++) {
+          scope.hide(variables[j].element);
+        }
+      } else if (statement is FunctionDeclarationStatement) {
+        FunctionDeclarationStatement fds = statement;
+        scope.hide(fds.functionDeclaration.element);
+      }
+    }
+  }
+}
+
+/**
+ * Instances of this class manage the knowledge of what the set of subtypes are for a given type.
+ */
+class SubtypeManager {
+  /**
+   * A map between [ClassElement]s and a set of [ClassElement]s that are subtypes of the
+   * key.
+   */
+  HashMap<ClassElement, HashSet<ClassElement>> _subtypeMap =
+      new HashMap<ClassElement, HashSet<ClassElement>>();
+
+  /**
+   * The set of all [LibraryElement]s that have been visited by the manager. This is used both
+   * to prevent infinite loops in the recursive methods, and also as a marker for the scope of the
+   * libraries visited by this manager.
+   */
+  HashSet<LibraryElement> _visitedLibraries = new HashSet<LibraryElement>();
+
+  /**
+   * Given some [ClassElement], return the set of all subtypes, and subtypes of subtypes.
+   *
+   * @param classElement the class to recursively return the set of subtypes of
+   */
+  HashSet<ClassElement> computeAllSubtypes(ClassElement classElement) {
+    // Ensure that we have generated the subtype map for the library
+    _computeSubtypesInLibrary(classElement.library);
+    // use the subtypeMap to compute the set of all subtypes and subtype's
+    // subtypes
+    HashSet<ClassElement> allSubtypes = new HashSet<ClassElement>();
+    _safelyComputeAllSubtypes(
+        classElement, new HashSet<ClassElement>(), allSubtypes);
+    return allSubtypes;
+  }
+
+  /**
+   * Given some [LibraryElement], visit all of the types in the library, the passed library,
+   * and any imported libraries, will be in the [visitedLibraries] set.
+   *
+   * @param libraryElement the library to visit, it it hasn't been visited already
+   */
+  void ensureLibraryVisited(LibraryElement libraryElement) {
+    _computeSubtypesInLibrary(libraryElement);
+  }
+
+  /**
+   * Given some [ClassElement], this method adds all of the pairs combinations of itself and
+   * all of its supertypes to the [subtypeMap] map.
+   *
+   * @param classElement the class element
+   */
+  void _computeSubtypesInClass(ClassElement classElement) {
+    InterfaceType supertypeType = classElement.supertype;
+    if (supertypeType != null) {
+      ClassElement supertypeElement = supertypeType.element;
+      if (supertypeElement != null) {
+        _putInSubtypeMap(supertypeElement, classElement);
+      }
+    }
+    List<InterfaceType> interfaceTypes = classElement.interfaces;
+    for (InterfaceType interfaceType in interfaceTypes) {
+      ClassElement interfaceElement = interfaceType.element;
+      if (interfaceElement != null) {
+        _putInSubtypeMap(interfaceElement, classElement);
+      }
+    }
+    List<InterfaceType> mixinTypes = classElement.mixins;
+    for (InterfaceType mixinType in mixinTypes) {
+      ClassElement mixinElement = mixinType.element;
+      if (mixinElement != null) {
+        _putInSubtypeMap(mixinElement, classElement);
+      }
+    }
+  }
+
+  /**
+   * Given some [CompilationUnitElement], this method calls
+   * [computeAllSubtypes] on all of the [ClassElement]s in the
+   * compilation unit.
+   *
+   * @param unitElement the compilation unit element
+   */
+  void _computeSubtypesInCompilationUnit(CompilationUnitElement unitElement) {
+    List<ClassElement> classElements = unitElement.types;
+    for (ClassElement classElement in classElements) {
+      _computeSubtypesInClass(classElement);
+    }
+  }
+
+  /**
+   * Given some [LibraryElement], this method calls
+   * [computeAllSubtypes] on all of the [ClassElement]s in the
+   * compilation unit, and itself for all imported and exported libraries. All visited libraries are
+   * added to the [visitedLibraries] set.
+   *
+   * @param libraryElement the library element
+   */
+  void _computeSubtypesInLibrary(LibraryElement libraryElement) {
+    if (libraryElement == null || _visitedLibraries.contains(libraryElement)) {
+      return;
+    }
+    _visitedLibraries.add(libraryElement);
+    _computeSubtypesInCompilationUnit(libraryElement.definingCompilationUnit);
+    List<CompilationUnitElement> parts = libraryElement.parts;
+    for (CompilationUnitElement part in parts) {
+      _computeSubtypesInCompilationUnit(part);
+    }
+    List<LibraryElement> imports = libraryElement.importedLibraries;
+    for (LibraryElement importElt in imports) {
+      _computeSubtypesInLibrary(importElt.library);
+    }
+    List<LibraryElement> exports = libraryElement.exportedLibraries;
+    for (LibraryElement exportElt in exports) {
+      _computeSubtypesInLibrary(exportElt.library);
+    }
+  }
+
+  /**
+   * Add some key/ value pair into the [subtypeMap] map.
+   *
+   * @param supertypeElement the key for the [subtypeMap] map
+   * @param subtypeElement the value for the [subtypeMap] map
+   */
+  void _putInSubtypeMap(
+      ClassElement supertypeElement, ClassElement subtypeElement) {
+    HashSet<ClassElement> subtypes = _subtypeMap[supertypeElement];
+    if (subtypes == null) {
+      subtypes = new HashSet<ClassElement>();
+      _subtypeMap[supertypeElement] = subtypes;
+    }
+    subtypes.add(subtypeElement);
+  }
+
+  /**
+   * Given some [ClassElement] and a [HashSet<ClassElement>], this method recursively
+   * adds all of the subtypes of the [ClassElement] to the passed array.
+   *
+   * @param classElement the type to compute the set of subtypes of
+   * @param visitedClasses the set of class elements that this method has already recursively seen
+   * @param allSubtypes the computed set of subtypes of the passed class element
+   */
+  void _safelyComputeAllSubtypes(ClassElement classElement,
+      HashSet<ClassElement> visitedClasses, HashSet<ClassElement> allSubtypes) {
+    if (!visitedClasses.add(classElement)) {
+      // if this class has already been called on this class element
+      return;
+    }
+    HashSet<ClassElement> subtypes = _subtypeMap[classElement];
+    if (subtypes == null) {
+      return;
+    }
+    for (ClassElement subtype in subtypes) {
+      _safelyComputeAllSubtypes(subtype, visitedClasses, allSubtypes);
+    }
+    allSubtypes.addAll(subtypes);
+  }
+}
+
+/**
+ * Instances of the class `ToDoFinder` find to-do comments in Dart code.
+ */
+class ToDoFinder {
+  /**
+   * The error reporter by which to-do comments will be reported.
+   */
+  final ErrorReporter _errorReporter;
+
+  /**
+   * Initialize a newly created to-do finder to report to-do comments to the given reporter.
+   *
+   * @param errorReporter the error reporter by which to-do comments will be reported
+   */
+  ToDoFinder(this._errorReporter);
+
+  /**
+   * Search the comments in the given compilation unit for to-do comments and report an error for
+   * each.
+   *
+   * @param unit the compilation unit containing the to-do comments
+   */
+  void findIn(CompilationUnit unit) {
+    _gatherTodoComments(unit.beginToken);
+  }
+
+  /**
+   * Search the comment tokens reachable from the given token and create errors for each to-do
+   * comment.
+   *
+   * @param token the head of the list of tokens being searched
+   */
+  void _gatherTodoComments(sc.Token token) {
+    while (token != null && token.type != sc.TokenType.EOF) {
+      sc.Token commentToken = token.precedingComments;
+      while (commentToken != null) {
+        if (commentToken.type == sc.TokenType.SINGLE_LINE_COMMENT ||
+            commentToken.type == sc.TokenType.MULTI_LINE_COMMENT) {
+          _scrapeTodoComment(commentToken);
+        }
+        commentToken = commentToken.next;
+      }
+      token = token.next;
+    }
+  }
+
+  /**
+   * Look for user defined tasks in comments and convert them into info level analysis issues.
+   *
+   * @param commentToken the comment token to analyze
+   */
+  void _scrapeTodoComment(sc.Token commentToken) {
+    JavaPatternMatcher matcher =
+        new JavaPatternMatcher(TodoCode.TODO_REGEX, commentToken.lexeme);
+    if (matcher.find()) {
+      int offset =
+          commentToken.offset + matcher.start() + matcher.group(1).length;
+      int length = matcher.group(2).length;
+      _errorReporter.reportErrorForOffset(
+          TodoCode.TODO, offset, length, [matcher.group(2)]);
+    }
+  }
+}
+
+/**
+ * Instances of the class `TypeOverrideManager` manage the ability to override the type of an
+ * element within a given context.
+ */
+class TypeOverrideManager {
+  /**
+   * The current override scope, or `null` if no scope has been entered.
+   */
+  TypeOverrideManager_TypeOverrideScope currentScope;
+
+  /**
+   * Apply a set of overrides that were previously captured.
+   *
+   * @param overrides the overrides to be applied
+   */
+  void applyOverrides(Map<VariableElement, DartType> overrides) {
+    if (currentScope == null) {
+      throw new IllegalStateException("Cannot apply overrides without a scope");
+    }
+    currentScope.applyOverrides(overrides);
+  }
+
+  /**
+   * Return a table mapping the elements whose type is overridden in the current scope to the
+   * overriding type.
+   *
+   * @return the overrides in the current scope
+   */
+  Map<VariableElement, DartType> captureLocalOverrides() {
+    if (currentScope == null) {
+      throw new IllegalStateException(
+          "Cannot capture local overrides without a scope");
+    }
+    return currentScope.captureLocalOverrides();
+  }
+
+  /**
+   * Return a map from the elements for the variables in the given list that have their types
+   * overridden to the overriding type.
+   *
+   * @param variableList the list of variables whose overriding types are to be captured
+   * @return a table mapping elements to their overriding types
+   */
+  Map<VariableElement, DartType> captureOverrides(
+      VariableDeclarationList variableList) {
+    if (currentScope == null) {
+      throw new IllegalStateException(
+          "Cannot capture overrides without a scope");
+    }
+    return currentScope.captureOverrides(variableList);
+  }
+
+  /**
+   * Enter a new override scope.
+   */
+  void enterScope() {
+    currentScope = new TypeOverrideManager_TypeOverrideScope(currentScope);
+  }
+
+  /**
+   * Exit the current override scope.
+   */
+  void exitScope() {
+    if (currentScope == null) {
+      throw new IllegalStateException("No scope to exit");
+    }
+    currentScope = currentScope._outerScope;
+  }
+
+  /**
+   * Return the best type information available for the given element. If the type of the element
+   * has been overridden, then return the overriding type. Otherwise, return the static type.
+   *
+   * @param element the element for which type information is to be returned
+   * @return the best type information available for the given element
+   */
+  DartType getBestType(VariableElement element) {
+    DartType bestType = getType(element);
+    return bestType == null ? element.type : bestType;
+  }
+
+  /**
+   * Return the overridden type of the given element, or `null` if the type of the element has
+   * not been overridden.
+   *
+   * @param element the element whose type might have been overridden
+   * @return the overridden type of the given element
+   */
+  DartType getType(Element element) {
+    if (currentScope == null) {
+      return null;
+    }
+    return currentScope.getType(element);
+  }
+
+  /**
+   * Update overrides assuming [perBranchOverrides] is the collection of
+   * per-branch overrides for *all* branches flowing into a join point.
+   *
+   * If a variable type in any of branches is not the same as its type before
+   * the branching, then its propagated type is reset to `null`.
+   */
+  void mergeOverrides(List<Map<VariableElement, DartType>> perBranchOverrides) {
+    for (Map<VariableElement, DartType> branch in perBranchOverrides) {
+      branch.forEach((VariableElement variable, DartType branchType) {
+        DartType currentType = currentScope.getType(variable);
+        if (currentType != branchType) {
+          currentScope.resetType(variable);
+        }
+      });
+    }
+  }
+
+  /**
+   * Set the overridden type of the given element to the given type
+   *
+   * @param element the element whose type might have been overridden
+   * @param type the overridden type of the given element
+   */
+  void setType(VariableElement element, DartType type) {
+    if (currentScope == null) {
+      throw new IllegalStateException("Cannot override without a scope");
+    }
+    currentScope.setType(element, type);
+  }
+}
+
+/**
+ * Instances of the class `TypeOverrideScope` represent a scope in which the types of
+ * elements can be overridden.
+ */
+class TypeOverrideManager_TypeOverrideScope {
+  /**
+   * The outer scope in which types might be overridden.
+   */
+  final TypeOverrideManager_TypeOverrideScope _outerScope;
+
+  /**
+   * A table mapping elements to the overridden type of that element.
+   */
+  Map<VariableElement, DartType> _overridenTypes =
+      new HashMap<VariableElement, DartType>();
+
+  /**
+   * Initialize a newly created scope to be an empty child of the given scope.
+   *
+   * @param outerScope the outer scope in which types might be overridden
+   */
+  TypeOverrideManager_TypeOverrideScope(this._outerScope);
+
+  /**
+   * Apply a set of overrides that were previously captured.
+   *
+   * @param overrides the overrides to be applied
+   */
+  void applyOverrides(Map<VariableElement, DartType> overrides) {
+    _overridenTypes.addAll(overrides);
+  }
+
+  /**
+   * Return a table mapping the elements whose type is overridden in the current scope to the
+   * overriding type.
+   *
+   * @return the overrides in the current scope
+   */
+  Map<VariableElement, DartType> captureLocalOverrides() => _overridenTypes;
+
+  /**
+   * Return a map from the elements for the variables in the given list that have their types
+   * overridden to the overriding type.
+   *
+   * @param variableList the list of variables whose overriding types are to be captured
+   * @return a table mapping elements to their overriding types
+   */
+  Map<VariableElement, DartType> captureOverrides(
+      VariableDeclarationList variableList) {
+    Map<VariableElement, DartType> overrides =
+        new HashMap<VariableElement, DartType>();
+    if (variableList.isConst || variableList.isFinal) {
+      for (VariableDeclaration variable in variableList.variables) {
+        VariableElement element = variable.element;
+        if (element != null) {
+          DartType type = _overridenTypes[element];
+          if (type != null) {
+            overrides[element] = type;
+          }
+        }
+      }
+    }
+    return overrides;
+  }
+
+  /**
+   * Return the overridden type of the given element, or `null` if the type of the element
+   * has not been overridden.
+   *
+   * @param element the element whose type might have been overridden
+   * @return the overridden type of the given element
+   */
+  DartType getType(Element element) {
+    if (element is PropertyAccessorElement) {
+      element = (element as PropertyAccessorElement).variable;
+    }
+    DartType type = _overridenTypes[element];
+    if (_overridenTypes.containsKey(element)) {
+      return type;
+    }
+    if (type != null) {
+      return type;
+    } else if (_outerScope != null) {
+      return _outerScope.getType(element);
+    }
+    return null;
+  }
+
+  /**
+   * Clears the overridden type of the given [element].
+   */
+  void resetType(VariableElement element) {
+    _overridenTypes[element] = null;
+  }
+
+  /**
+   * Set the overridden type of the given element to the given type
+   *
+   * @param element the element whose type might have been overridden
+   * @param type the overridden type of the given element
+   */
+  void setType(VariableElement element, DartType type) {
+    _overridenTypes[element] = type;
+  }
+}
+
+/**
+ * Instances of the class `TypeParameterScope` implement the scope defined by the type
+ * parameters in a class.
+ */
+class TypeParameterScope extends EnclosedScope {
+  /**
+   * Initialize a newly created scope enclosed within another scope.
+   *
+   * @param enclosingScope the scope in which this scope is lexically enclosed
+   * @param typeElement the element representing the type represented by this scope
+   */
+  TypeParameterScope(Scope enclosingScope, ClassElement typeElement)
+      : super(enclosingScope) {
+    if (typeElement == null) {
+      throw new IllegalArgumentException("class element cannot be null");
+    }
+    _defineTypeParameters(typeElement);
+  }
+
+  /**
+   * Define the type parameters for the class.
+   *
+   * @param typeElement the element representing the type represented by this scope
+   */
+  void _defineTypeParameters(ClassElement typeElement) {
+    for (TypeParameterElement typeParameter in typeElement.typeParameters) {
+      define(typeParameter);
+    }
+  }
+}
+
+/**
+ * Instances of the class `TypePromotionManager` manage the ability to promote types of local
+ * variables and formal parameters from their declared types based on control flow.
+ */
+class TypePromotionManager {
+  /**
+   * The current promotion scope, or `null` if no scope has been entered.
+   */
+  TypePromotionManager_TypePromoteScope currentScope;
+
+  /**
+   * Returns the elements with promoted types.
+   */
+  Iterable<Element> get promotedElements => currentScope.promotedElements;
+
+  /**
+   * Enter a new promotions scope.
+   */
+  void enterScope() {
+    currentScope = new TypePromotionManager_TypePromoteScope(currentScope);
+  }
+
+  /**
+   * Exit the current promotion scope.
+   */
+  void exitScope() {
+    if (currentScope == null) {
+      throw new IllegalStateException("No scope to exit");
+    }
+    currentScope = currentScope._outerScope;
+  }
+
+  /**
+   * Returns static type of the given variable - declared or promoted.
+   *
+   * @return the static type of the given variable - declared or promoted
+   */
+  DartType getStaticType(VariableElement variable) {
+    DartType staticType = getType(variable);
+    if (staticType == null) {
+      staticType = variable.type;
+    }
+    return staticType;
+  }
+
+  /**
+   * Return the promoted type of the given element, or `null` if the type of the element has
+   * not been promoted.
+   *
+   * @param element the element whose type might have been promoted
+   * @return the promoted type of the given element
+   */
+  DartType getType(Element element) {
+    if (currentScope == null) {
+      return null;
+    }
+    return currentScope.getType(element);
+  }
+
+  /**
+   * Set the promoted type of the given element to the given type.
+   *
+   * @param element the element whose type might have been promoted
+   * @param type the promoted type of the given element
+   */
+  void setType(Element element, DartType type) {
+    if (currentScope == null) {
+      throw new IllegalStateException("Cannot promote without a scope");
+    }
+    currentScope.setType(element, type);
+  }
+}
+
+/**
+ * Instances of the class `TypePromoteScope` represent a scope in which the types of
+ * elements can be promoted.
+ */
+class TypePromotionManager_TypePromoteScope {
+  /**
+   * The outer scope in which types might be promoter.
+   */
+  final TypePromotionManager_TypePromoteScope _outerScope;
+
+  /**
+   * A table mapping elements to the promoted type of that element.
+   */
+  HashMap<Element, DartType> _promotedTypes = new HashMap<Element, DartType>();
+
+  /**
+   * Initialize a newly created scope to be an empty child of the given scope.
+   *
+   * @param outerScope the outer scope in which types might be promoted
+   */
+  TypePromotionManager_TypePromoteScope(this._outerScope);
+
+  /**
+   * Returns the elements with promoted types.
+   */
+  Iterable<Element> get promotedElements => _promotedTypes.keys.toSet();
+
+  /**
+   * Return the promoted type of the given element, or `null` if the type of the element has
+   * not been promoted.
+   *
+   * @param element the element whose type might have been promoted
+   * @return the promoted type of the given element
+   */
+  DartType getType(Element element) {
+    DartType type = _promotedTypes[element];
+    if (type == null && element is PropertyAccessorElement) {
+      type = _promotedTypes[element.variable];
+    }
+    if (type != null) {
+      return type;
+    } else if (_outerScope != null) {
+      return _outerScope.getType(element);
+    }
+    return null;
+  }
+
+  /**
+   * Set the promoted type of the given element to the given type.
+   *
+   * @param element the element whose type might have been promoted
+   * @param type the promoted type of the given element
+   */
+  void setType(Element element, DartType type) {
+    _promotedTypes[element] = type;
+  }
+}
+
+/**
+ * The interface `TypeProvider` defines the behavior of objects that provide access to types
+ * defined by the language.
+ */
+abstract class TypeProvider {
+  /**
+   * Return the type representing the built-in type 'bool'.
+   */
+  InterfaceType get boolType;
+
+  /**
+   * Return the type representing the type 'bottom'.
+   */
+  DartType get bottomType;
+
+  /**
+   * Return the type representing the built-in type 'Deprecated'.
+   */
+  InterfaceType get deprecatedType;
+
+  /**
+   * Return the type representing the built-in type 'double'.
+   */
+  InterfaceType get doubleType;
+
+  /**
+   * Return the type representing the built-in type 'dynamic'.
+   */
+  DartType get dynamicType;
+
+  /**
+   * Return the type representing the built-in type 'Function'.
+   */
+  InterfaceType get functionType;
+
+  /**
+   * Return the type representing 'Future<dynamic>'.
+   */
+  InterfaceType get futureDynamicType;
+
+  /**
+   * Return the type representing 'Future<Null>'.
+   */
+  InterfaceType get futureNullType;
+
+  /**
+   * Return the type representing the built-in type 'Future'.
+   */
+  InterfaceType get futureType;
+
+  /**
+   * Return the type representing the built-in type 'int'.
+   */
+  InterfaceType get intType;
+
+  /**
+   * Return the type representing the type 'Iterable<dynamic>'.
+   */
+  InterfaceType get iterableDynamicType;
+
+  /**
+   * Return the type representing the built-in type 'Iterable'.
+   */
+  InterfaceType get iterableType;
+
+  /**
+   * Return the type representing the built-in type 'List'.
+   */
+  InterfaceType get listType;
+
+  /**
+   * Return the type representing the built-in type 'Map'.
+   */
+  InterfaceType get mapType;
+
+  /**
+   * Return a list containing all of the types that cannot be either extended or
+   * implemented.
+   */
+  List<InterfaceType> get nonSubtypableTypes;
+
+  /**
+   * Return a [DartObjectImpl] representing the `null` object.
+   */
+  DartObjectImpl get nullObject;
+
+  /**
+   * Return the type representing the built-in type 'Null'.
+   */
+  InterfaceType get nullType;
+
+  /**
+   * Return the type representing the built-in type 'num'.
+   */
+  InterfaceType get numType;
+
+  /**
+   * Return the type representing the built-in type 'Object'.
+   */
+  InterfaceType get objectType;
+
+  /**
+   * Return the type representing the built-in type 'StackTrace'.
+   */
+  InterfaceType get stackTraceType;
+
+  /**
+   * Return the type representing 'Stream<dynamic>'.
+   */
+  InterfaceType get streamDynamicType;
+
+  /**
+   * Return the type representing the built-in type 'Stream'.
+   */
+  InterfaceType get streamType;
+
+  /**
+   * Return the type representing the built-in type 'String'.
+   */
+  InterfaceType get stringType;
+
+  /**
+   * Return the type representing the built-in type 'Symbol'.
+   */
+  InterfaceType get symbolType;
+
+  /**
+   * Return the type representing the built-in type 'Type'.
+   */
+  InterfaceType get typeType;
+
+  /**
+   * Return the type representing typenames that can't be resolved.
+   */
+  DartType get undefinedType;
+}
+
+/**
+ * Instances of the class `TypeProviderImpl` provide access to types defined by the language
+ * by looking for those types in the element model for the core library.
+ */
+class TypeProviderImpl implements TypeProvider {
+  /**
+   * The type representing the built-in type 'bool'.
+   */
+  InterfaceType _boolType;
+
+  /**
+   * The type representing the type 'bottom'.
+   */
+  DartType _bottomType;
+
+  /**
+   * The type representing the built-in type 'double'.
+   */
+  InterfaceType _doubleType;
+
+  /**
+   * The type representing the built-in type 'Deprecated'.
+   */
+  InterfaceType _deprecatedType;
+
+  /**
+   * The type representing the built-in type 'dynamic'.
+   */
+  DartType _dynamicType;
+
+  /**
+   * The type representing the built-in type 'Function'.
+   */
+  InterfaceType _functionType;
+
+  /**
+   * The type representing 'Future<dynamic>'.
+   */
+  InterfaceType _futureDynamicType;
+
+  /**
+   * The type representing 'Future<Null>'.
+   */
+  InterfaceType _futureNullType;
+
+  /**
+   * The type representing the built-in type 'Future'.
+   */
+  InterfaceType _futureType;
+
+  /**
+   * The type representing the built-in type 'int'.
+   */
+  InterfaceType _intType;
+
+  /**
+   * The type representing 'Iterable<dynamic>'.
+   */
+  InterfaceType _iterableDynamicType;
+
+  /**
+   * The type representing the built-in type 'Iterable'.
+   */
+  InterfaceType _iterableType;
+
+  /**
+   * The type representing the built-in type 'List'.
+   */
+  InterfaceType _listType;
+
+  /**
+   * The type representing the built-in type 'Map'.
+   */
+  InterfaceType _mapType;
+
+  /**
+   * An shared object representing the value 'null'.
+   */
+  DartObjectImpl _nullObject;
+
+  /**
+   * The type representing the type 'Null'.
+   */
+  InterfaceType _nullType;
+
+  /**
+   * The type representing the built-in type 'num'.
+   */
+  InterfaceType _numType;
+
+  /**
+   * The type representing the built-in type 'Object'.
+   */
+  InterfaceType _objectType;
+
+  /**
+   * The type representing the built-in type 'StackTrace'.
+   */
+  InterfaceType _stackTraceType;
+
+  /**
+   * The type representing 'Stream<dynamic>'.
+   */
+  InterfaceType _streamDynamicType;
+
+  /**
+   * The type representing the built-in type 'Stream'.
+   */
+  InterfaceType _streamType;
+
+  /**
+   * The type representing the built-in type 'String'.
+   */
+  InterfaceType _stringType;
+
+  /**
+   * The type representing the built-in type 'Symbol'.
+   */
+  InterfaceType _symbolType;
+
+  /**
+   * The type representing the built-in type 'Type'.
+   */
+  InterfaceType _typeType;
+
+  /**
+   * The type representing typenames that can't be resolved.
+   */
+  DartType _undefinedType;
+
+  /**
+   * Initialize a newly created type provider to provide the types defined in
+   * the given [coreLibrary] and [asyncLibrary].
+   */
+  TypeProviderImpl(LibraryElement coreLibrary, LibraryElement asyncLibrary) {
+    Namespace coreNamespace =
+        new NamespaceBuilder().createPublicNamespaceForLibrary(coreLibrary);
+    Namespace asyncNamespace =
+        new NamespaceBuilder().createPublicNamespaceForLibrary(asyncLibrary);
+    _initializeFrom(coreNamespace, asyncNamespace);
+  }
+
+  /**
+   * Initialize a newly created type provider to provide the types defined in
+   * the given [Namespace]s.
+   */
+  TypeProviderImpl.forNamespaces(
+      Namespace coreNamespace, Namespace asyncNamespace) {
+    _initializeFrom(coreNamespace, asyncNamespace);
+  }
+
+  @override
+  InterfaceType get boolType => _boolType;
+
+  @override
+  DartType get bottomType => _bottomType;
+
+  @override
+  InterfaceType get deprecatedType => _deprecatedType;
+
+  @override
+  InterfaceType get doubleType => _doubleType;
+
+  @override
+  DartType get dynamicType => _dynamicType;
+
+  @override
+  InterfaceType get functionType => _functionType;
+
+  @override
+  InterfaceType get futureDynamicType => _futureDynamicType;
+
+  @override
+  InterfaceType get futureNullType => _futureNullType;
+
+  @override
+  InterfaceType get futureType => _futureType;
+
+  @override
+  InterfaceType get intType => _intType;
+
+  @override
+  InterfaceType get iterableDynamicType => _iterableDynamicType;
+
+  @override
+  InterfaceType get iterableType => _iterableType;
+
+  @override
+  InterfaceType get listType => _listType;
+
+  @override
+  InterfaceType get mapType => _mapType;
+
+  @override
+  List<InterfaceType> get nonSubtypableTypes => <InterfaceType>[
+    nullType,
+    numType,
+    intType,
+    doubleType,
+    boolType,
+    stringType
+  ];
+
+  @override
+  DartObjectImpl get nullObject {
+    if (_nullObject == null) {
+      _nullObject = new DartObjectImpl(nullType, NullState.NULL_STATE);
+    }
+    return _nullObject;
+  }
+
+  @override
+  InterfaceType get nullType => _nullType;
+
+  @override
+  InterfaceType get numType => _numType;
+
+  @override
+  InterfaceType get objectType => _objectType;
+
+  @override
+  InterfaceType get stackTraceType => _stackTraceType;
+
+  @override
+  InterfaceType get streamDynamicType => _streamDynamicType;
+
+  @override
+  InterfaceType get streamType => _streamType;
+
+  @override
+  InterfaceType get stringType => _stringType;
+
+  @override
+  InterfaceType get symbolType => _symbolType;
+
+  @override
+  InterfaceType get typeType => _typeType;
+
+  @override
+  DartType get undefinedType => _undefinedType;
+
+  /**
+   * Return the type with the given name from the given namespace, or `null` if there is no
+   * class with the given name.
+   *
+   * @param namespace the namespace in which to search for the given name
+   * @param typeName the name of the type being searched for
+   * @return the type that was found
+   */
+  InterfaceType _getType(Namespace namespace, String typeName) {
+    Element element = namespace.get(typeName);
+    if (element == null) {
+      AnalysisEngine.instance.logger
+          .logInformation("No definition of type $typeName");
+      return null;
+    }
+    return (element as ClassElement).type;
+  }
+
+  /**
+   * Initialize the types provided by this type provider from the given
+   * [Namespace]s.
+   */
+  void _initializeFrom(Namespace coreNamespace, Namespace asyncNamespace) {
+    _boolType = _getType(coreNamespace, "bool");
+    _bottomType = BottomTypeImpl.instance;
+    _deprecatedType = _getType(coreNamespace, "Deprecated");
+    _doubleType = _getType(coreNamespace, "double");
+    _dynamicType = DynamicTypeImpl.instance;
+    _functionType = _getType(coreNamespace, "Function");
+    _futureType = _getType(asyncNamespace, "Future");
+    _intType = _getType(coreNamespace, "int");
+    _iterableType = _getType(coreNamespace, "Iterable");
+    _listType = _getType(coreNamespace, "List");
+    _mapType = _getType(coreNamespace, "Map");
+    _nullType = _getType(coreNamespace, "Null");
+    _numType = _getType(coreNamespace, "num");
+    _objectType = _getType(coreNamespace, "Object");
+    _stackTraceType = _getType(coreNamespace, "StackTrace");
+    _streamType = _getType(asyncNamespace, "Stream");
+    _stringType = _getType(coreNamespace, "String");
+    _symbolType = _getType(coreNamespace, "Symbol");
+    _typeType = _getType(coreNamespace, "Type");
+    _undefinedType = UndefinedTypeImpl.instance;
+    _futureDynamicType = _futureType.substitute4(<DartType>[_dynamicType]);
+    _futureNullType = _futureType.substitute4(<DartType>[_nullType]);
+    _iterableDynamicType = _iterableType.substitute4(<DartType>[_dynamicType]);
+    _streamDynamicType = _streamType.substitute4(<DartType>[_dynamicType]);
+  }
+}
+
+/**
+ * Instances of the class `TypeResolverVisitor` are used to resolve the types associated with
+ * the elements in the element model. This includes the types of superclasses, mixins, interfaces,
+ * fields, methods, parameters, and local variables. As a side-effect, this also finishes building
+ * the type hierarchy.
+ */
+class TypeResolverVisitor extends ScopedVisitor {
+  /**
+   * The type representing the type 'dynamic'.
+   */
+  DartType _dynamicType;
+
+  /**
+   * The type representing typenames that can't be resolved.
+   */
+  DartType _undefinedType;
+
+  /**
+   * The flag specifying if currently visited class references 'super' expression.
+   */
+  bool _hasReferenceToSuper = false;
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
+   *
+   * @param library the library containing the compilation unit being resolved
+   * @param source the source representing the compilation unit being visited
+   * @param typeProvider the object used to access the types from the core library
+   */
+  TypeResolverVisitor.con1(
+      Library library, Source source, TypeProvider typeProvider)
+      : super.con1(library, source, typeProvider) {
+    _dynamicType = typeProvider.dynamicType;
+    _undefinedType = typeProvider.undefinedType;
+  }
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
+   *
+   * @param definingLibrary the element for the library containing the compilation unit being
+   *          visited
+   * @param source the source representing the compilation unit being visited
+   * @param typeProvider the object used to access the types from the core library
+   * @param errorListener the error listener that will be informed of any errors that are found
+   *          during resolution
+   */
+  TypeResolverVisitor.con2(LibraryElement definingLibrary, Source source,
+      TypeProvider typeProvider, AnalysisErrorListener errorListener)
+      : super.con2(definingLibrary, source, typeProvider, errorListener) {
+    _dynamicType = typeProvider.dynamicType;
+    _undefinedType = typeProvider.undefinedType;
+  }
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in an AST node.
+   *
+   * @param definingLibrary the element for the library containing the node being visited
+   * @param source the source representing the compilation unit containing the node being visited
+   * @param typeProvider the object used to access the types from the core library
+   * @param nameScope the scope used to resolve identifiers in the node that will first be visited
+   * @param errorListener the error listener that will be informed of any errors that are found
+   *          during resolution
+   */
+  TypeResolverVisitor.con3(LibraryElement definingLibrary, Source source,
+      TypeProvider typeProvider, Scope nameScope,
+      AnalysisErrorListener errorListener)
+      : super.con3(
+          definingLibrary, source, typeProvider, nameScope, errorListener) {
+    _dynamicType = typeProvider.dynamicType;
+    _undefinedType = typeProvider.undefinedType;
+  }
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
+   *
+   * @param library the library containing the compilation unit being resolved
+   * @param source the source representing the compilation unit being visited
+   * @param typeProvider the object used to access the types from the core library
+   */
+  TypeResolverVisitor.con4(
+      ResolvableLibrary library, Source source, TypeProvider typeProvider)
+      : super.con4(library, source, typeProvider) {
+    _dynamicType = typeProvider.dynamicType;
+    _undefinedType = typeProvider.undefinedType;
+  }
+
+  @override
+  Object visitAnnotation(Annotation node) {
+    //
+    // Visit annotations, if the annotation is @proxy, on a class, and "proxy"
+    // resolves to the proxy annotation in dart.core, then create create the
+    // ElementAnnotationImpl and set it as the metadata on the enclosing class.
+    //
+    // Element resolution is done in the ElementResolver, and this work will be
+    // done in the general case for all annotations in the ElementResolver.
+    // The reason we resolve this particular element early is so that
+    // ClassElement.isProxy() returns the correct information during all
+    // phases of the ElementResolver.
+    //
+    super.visitAnnotation(node);
+    Identifier identifier = node.name;
+    if (identifier.name.endsWith(ElementAnnotationImpl.PROXY_VARIABLE_NAME) &&
+        node.parent is ClassDeclaration) {
+      Element element = nameScope.lookup(identifier, definingLibrary);
+      if (element != null &&
+          element.library.isDartCore &&
+          element is PropertyAccessorElement) {
+        // This is the @proxy from dart.core
+        ClassDeclaration classDeclaration = node.parent as ClassDeclaration;
+        ElementAnnotationImpl elementAnnotation =
+            new ElementAnnotationImpl(element);
+        node.elementAnnotation = elementAnnotation;
+        (classDeclaration.element as ClassElementImpl).metadata =
+            <ElementAnnotationImpl>[elementAnnotation];
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitCatchClause(CatchClause node) {
+    super.visitCatchClause(node);
+    SimpleIdentifier exception = node.exceptionParameter;
+    if (exception != null) {
+      // If an 'on' clause is provided the type of the exception parameter is
+      // the type in the 'on' clause. Otherwise, the type of the exception
+      // parameter is 'Object'.
+      TypeName exceptionTypeName = node.exceptionType;
+      DartType exceptionType;
+      if (exceptionTypeName == null) {
+        exceptionType = typeProvider.dynamicType;
+      } else {
+        exceptionType = _getType(exceptionTypeName);
+      }
+      _recordType(exception, exceptionType);
+      Element element = exception.staticElement;
+      if (element is VariableElementImpl) {
+        element.type = exceptionType;
+      } else {
+        // TODO(brianwilkerson) Report the internal error
+      }
+    }
+    SimpleIdentifier stackTrace = node.stackTraceParameter;
+    if (stackTrace != null) {
+      _recordType(stackTrace, typeProvider.stackTraceType);
+      Element element = stackTrace.staticElement;
+      if (element is VariableElementImpl) {
+        element.type = typeProvider.stackTraceType;
+      } else {
+        // TODO(brianwilkerson) Report the internal error
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitClassDeclaration(ClassDeclaration node) {
+    _hasReferenceToSuper = false;
+    super.visitClassDeclaration(node);
+    ClassElementImpl classElement = _getClassElement(node.name);
+    if (classElement != null) {
+      classElement.hasReferenceToSuper = _hasReferenceToSuper;
+    }
+    return null;
+  }
+
+  @override
+  void visitClassDeclarationInScope(ClassDeclaration node) {
+    super.visitClassDeclarationInScope(node);
+    ExtendsClause extendsClause = node.extendsClause;
+    WithClause withClause = node.withClause;
+    ImplementsClause implementsClause = node.implementsClause;
+    ClassElementImpl classElement = _getClassElement(node.name);
+    InterfaceType superclassType = null;
+    if (extendsClause != null) {
+      ErrorCode errorCode = (withClause == null
+          ? CompileTimeErrorCode.EXTENDS_NON_CLASS
+          : CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS);
+      superclassType = _resolveType(extendsClause.superclass, errorCode,
+          CompileTimeErrorCode.EXTENDS_ENUM, errorCode);
+      if (!identical(superclassType, typeProvider.objectType)) {
+        classElement.validMixin = false;
+      }
+    }
+    if (classElement != null) {
+      if (superclassType == null) {
+        InterfaceType objectType = typeProvider.objectType;
+        if (!identical(classElement.type, objectType)) {
+          superclassType = objectType;
+        }
+      }
+      classElement.supertype = superclassType;
+    }
+    _resolve(classElement, withClause, implementsClause);
+    return null;
+  }
+
+  @override
+  void visitClassMembersInScope(ClassDeclaration node) {
+    //
+    // Process field declarations before constructors and methods so that the
+    // types of field formal parameters can be correctly resolved.
+    //
+    List<ClassMember> nonFields = new List<ClassMember>();
+    node.visitChildren(
+        new _TypeResolverVisitor_visitClassMembersInScope(this, nonFields));
+    int count = nonFields.length;
+    for (int i = 0; i < count; i++) {
+      nonFields[i].accept(this);
+    }
+  }
+
+  @override
+  Object visitClassTypeAlias(ClassTypeAlias node) {
+    super.visitClassTypeAlias(node);
+    ErrorCode errorCode = CompileTimeErrorCode.MIXIN_WITH_NON_CLASS_SUPERCLASS;
+    InterfaceType superclassType = _resolveType(node.superclass, errorCode,
+        CompileTimeErrorCode.EXTENDS_ENUM, errorCode);
+    if (superclassType == null) {
+      superclassType = typeProvider.objectType;
+    }
+    ClassElementImpl classElement = _getClassElement(node.name);
+    if (classElement != null) {
+      classElement.supertype = superclassType;
+    }
+    _resolve(classElement, node.withClause, node.implementsClause);
+    return null;
+  }
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    super.visitConstructorDeclaration(node);
+    ExecutableElementImpl element = node.element as ExecutableElementImpl;
+    if (element == null) {
+      ClassDeclaration classNode =
+          node.getAncestor((node) => node is ClassDeclaration);
+      StringBuffer buffer = new StringBuffer();
+      buffer.write("The element for the constructor ");
+      buffer.write(node.name == null ? "<unnamed>" : node.name.name);
+      buffer.write(" in ");
+      if (classNode == null) {
+        buffer.write("<unknown class>");
+      } else {
+        buffer.write(classNode.name.name);
+      }
+      buffer.write(" in ");
+      buffer.write(source.fullName);
+      buffer.write(" was not set while trying to resolve types.");
+      AnalysisEngine.instance.logger.logError(buffer.toString(),
+          new CaughtException(new AnalysisException(), null));
+    } else {
+      ClassElement definingClass = element.enclosingElement as ClassElement;
+      element.returnType = definingClass.type;
+      FunctionTypeImpl type = new FunctionTypeImpl.con1(element);
+      type.typeArguments = definingClass.type.typeArguments;
+      element.type = type;
+    }
+    return null;
+  }
+
+  @override
+  Object visitDeclaredIdentifier(DeclaredIdentifier node) {
+    super.visitDeclaredIdentifier(node);
+    DartType declaredType;
+    TypeName typeName = node.type;
+    if (typeName == null) {
+      declaredType = _dynamicType;
+    } else {
+      declaredType = _getType(typeName);
+    }
+    LocalVariableElementImpl element = node.element as LocalVariableElementImpl;
+    element.type = declaredType;
+    return null;
+  }
+
+  @override
+  Object visitFieldFormalParameter(FieldFormalParameter node) {
+    super.visitFieldFormalParameter(node);
+    Element element = node.identifier.staticElement;
+    if (element is ParameterElementImpl) {
+      ParameterElementImpl parameter = element;
+      FormalParameterList parameterList = node.parameters;
+      if (parameterList == null) {
+        DartType type;
+        TypeName typeName = node.type;
+        if (typeName == null) {
+          type = _dynamicType;
+          if (parameter is FieldFormalParameterElement) {
+            FieldElement fieldElement =
+                (parameter as FieldFormalParameterElement).field;
+            if (fieldElement != null) {
+              type = fieldElement.type;
+            }
+          }
+        } else {
+          type = _getType(typeName);
+        }
+        parameter.type = type;
+      } else {
+        _setFunctionTypedParameterType(parameter, node.type, node.parameters);
+      }
+    } else {
+      // TODO(brianwilkerson) Report this internal error
+    }
+    return null;
+  }
+
+  @override
+  Object visitFunctionDeclaration(FunctionDeclaration node) {
+    super.visitFunctionDeclaration(node);
+    ExecutableElementImpl element = node.element as ExecutableElementImpl;
+    if (element == null) {
+      StringBuffer buffer = new StringBuffer();
+      buffer.write("The element for the top-level function ");
+      buffer.write(node.name);
+      buffer.write(" in ");
+      buffer.write(source.fullName);
+      buffer.write(" was not set while trying to resolve types.");
+      AnalysisEngine.instance.logger.logError(buffer.toString(),
+          new CaughtException(new AnalysisException(), null));
+    }
+    element.returnType = _computeReturnType(node.returnType);
+    FunctionTypeImpl type = new FunctionTypeImpl.con1(element);
+    ClassElement definingClass =
+        element.getAncestor((element) => element is ClassElement);
+    if (definingClass != null) {
+      type.typeArguments = definingClass.type.typeArguments;
+    }
+    element.type = type;
+    return null;
+  }
+
+  @override
+  Object visitFunctionTypeAlias(FunctionTypeAlias node) {
+    FunctionTypeAliasElementImpl element =
+        node.element as FunctionTypeAliasElementImpl;
+    if (element.returnType == null) {
+      // Only visit function type aliases once.
+      super.visitFunctionTypeAlias(node);
+      element.returnType = _computeReturnType(node.returnType);
+    }
+    return null;
+  }
+
+  @override
+  Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+    super.visitFunctionTypedFormalParameter(node);
+    Element element = node.identifier.staticElement;
+    if (element is ParameterElementImpl) {
+      _setFunctionTypedParameterType(element, node.returnType, node.parameters);
+    } else {
+      // TODO(brianwilkerson) Report this internal error
+    }
+    return null;
+  }
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    super.visitMethodDeclaration(node);
+    ExecutableElementImpl element = node.element as ExecutableElementImpl;
+    if (element == null) {
+      ClassDeclaration classNode =
+          node.getAncestor((node) => node is ClassDeclaration);
+      StringBuffer buffer = new StringBuffer();
+      buffer.write("The element for the method ");
+      buffer.write(node.name.name);
+      buffer.write(" in ");
+      if (classNode == null) {
+        buffer.write("<unknown class>");
+      } else {
+        buffer.write(classNode.name.name);
+      }
+      buffer.write(" in ");
+      buffer.write(source.fullName);
+      buffer.write(" was not set while trying to resolve types.");
+      AnalysisEngine.instance.logger.logError(buffer.toString(),
+          new CaughtException(new AnalysisException(), null));
+    }
+    element.returnType = _computeReturnType(node.returnType);
+    FunctionTypeImpl type = new FunctionTypeImpl.con1(element);
+    ClassElement definingClass =
+        element.getAncestor((element) => element is ClassElement);
+    if (definingClass != null) {
+      type.typeArguments = definingClass.type.typeArguments;
+    }
+    element.type = type;
+    if (element is PropertyAccessorElement) {
+      PropertyAccessorElement accessor = element as PropertyAccessorElement;
+      PropertyInducingElementImpl variable =
+          accessor.variable as PropertyInducingElementImpl;
+      if (accessor.isGetter) {
+        variable.type = type.returnType;
+      } else if (variable.type == null) {
+        List<DartType> parameterTypes = type.normalParameterTypes;
+        if (parameterTypes != null && parameterTypes.length > 0) {
+          variable.type = parameterTypes[0];
+        }
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitSimpleFormalParameter(SimpleFormalParameter node) {
+    super.visitSimpleFormalParameter(node);
+    DartType declaredType;
+    TypeName typeName = node.type;
+    if (typeName == null) {
+      declaredType = _dynamicType;
+    } else {
+      declaredType = _getType(typeName);
+    }
+    Element element = node.identifier.staticElement;
+    if (element is ParameterElement) {
+      (element as ParameterElementImpl).type = declaredType;
+    } else {
+      // TODO(brianwilkerson) Report the internal error.
+    }
+    return null;
+  }
+
+  @override
+  Object visitSuperExpression(SuperExpression node) {
+    _hasReferenceToSuper = true;
+    return super.visitSuperExpression(node);
+  }
+
+  @override
+  Object visitTypeName(TypeName node) {
+    super.visitTypeName(node);
+    Identifier typeName = node.name;
+    TypeArgumentList argumentList = node.typeArguments;
+    Element element = nameScope.lookup(typeName, definingLibrary);
+    if (element == null) {
+      //
+      // Check to see whether the type name is either 'dynamic' or 'void',
+      // neither of which are in the name scope and hence will not be found by
+      // normal means.
+      //
+      if (typeName.name == _dynamicType.name) {
+        _setElement(typeName, _dynamicType.element);
+        if (argumentList != null) {
+          // TODO(brianwilkerson) Report this error
+//          reporter.reportError(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, node, dynamicType.getName(), 0, argumentList.getArguments().size());
+        }
+        typeName.staticType = _dynamicType;
+        node.type = _dynamicType;
+        return null;
+      }
+      VoidTypeImpl voidType = VoidTypeImpl.instance;
+      if (typeName.name == voidType.name) {
+        // There is no element for 'void'.
+        if (argumentList != null) {
+          // TODO(brianwilkerson) Report this error
+//          reporter.reportError(StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS, node, voidType.getName(), 0, argumentList.getArguments().size());
+        }
+        typeName.staticType = voidType;
+        node.type = voidType;
+        return null;
+      }
+      //
+      // If not, the look to see whether we might have created the wrong AST
+      // structure for a constructor name. If so, fix the AST structure and then
+      // proceed.
+      //
+      AstNode parent = node.parent;
+      if (typeName is PrefixedIdentifier &&
+          parent is ConstructorName &&
+          argumentList == null) {
+        ConstructorName name = parent;
+        if (name.name == null) {
+          PrefixedIdentifier prefixedIdentifier =
+              typeName as PrefixedIdentifier;
+          SimpleIdentifier prefix = prefixedIdentifier.prefix;
+          element = nameScope.lookup(prefix, definingLibrary);
+          if (element is PrefixElement) {
+            if (parent.parent is InstanceCreationExpression &&
+                (parent.parent as InstanceCreationExpression).isConst) {
+              // If, if this is a const expression, then generate a
+              // CompileTimeErrorCode.CONST_WITH_NON_TYPE error.
+              reportErrorForNode(CompileTimeErrorCode.CONST_WITH_NON_TYPE,
+                  prefixedIdentifier.identifier,
+                  [prefixedIdentifier.identifier.name]);
+            } else {
+              // Else, if this expression is a new expression, report a
+              // NEW_WITH_NON_TYPE warning.
+              reportErrorForNode(StaticWarningCode.NEW_WITH_NON_TYPE,
+                  prefixedIdentifier.identifier,
+                  [prefixedIdentifier.identifier.name]);
+            }
+            _setElement(prefix, element);
+            return null;
+          } else if (element != null) {
+            //
+            // Rewrite the constructor name. The parser, when it sees a
+            // constructor named "a.b", cannot tell whether "a" is a prefix and
+            // "b" is a class name, or whether "a" is a class name and "b" is a
+            // constructor name. It arbitrarily chooses the former, but in this
+            // case was wrong.
+            //
+            name.name = prefixedIdentifier.identifier;
+            name.period = prefixedIdentifier.period;
+            node.name = prefix;
+            typeName = prefix;
+          }
+        }
+      }
+    }
+    // check element
+    bool elementValid = element is! MultiplyDefinedElement;
+    if (elementValid &&
+        element is! ClassElement &&
+        _isTypeNameInInstanceCreationExpression(node)) {
+      SimpleIdentifier typeNameSimple = _getTypeSimpleIdentifier(typeName);
+      InstanceCreationExpression creation =
+          node.parent.parent as InstanceCreationExpression;
+      if (creation.isConst) {
+        if (element == null) {
+          reportErrorForNode(
+              CompileTimeErrorCode.UNDEFINED_CLASS, typeNameSimple, [typeName]);
+        } else {
+          reportErrorForNode(CompileTimeErrorCode.CONST_WITH_NON_TYPE,
+              typeNameSimple, [typeName]);
+        }
+        elementValid = false;
+      } else {
+        if (element != null) {
+          reportErrorForNode(
+              StaticWarningCode.NEW_WITH_NON_TYPE, typeNameSimple, [typeName]);
+          elementValid = false;
+        }
+      }
+    }
+    if (elementValid && element == null) {
+      // We couldn't resolve the type name.
+      // TODO(jwren) Consider moving the check for
+      // CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE from the
+      // ErrorVerifier, so that we don't have two errors on a built in
+      // identifier being used as a class name.
+      // See CompileTimeErrorCodeTest.test_builtInIdentifierAsType().
+      SimpleIdentifier typeNameSimple = _getTypeSimpleIdentifier(typeName);
+      RedirectingConstructorKind redirectingConstructorKind;
+      if (_isBuiltInIdentifier(node) && _isTypeAnnotation(node)) {
+        reportErrorForNode(CompileTimeErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE,
+            typeName, [typeName.name]);
+      } else if (typeNameSimple.name == "boolean") {
+        reportErrorForNode(
+            StaticWarningCode.UNDEFINED_CLASS_BOOLEAN, typeNameSimple, []);
+      } else if (_isTypeNameInCatchClause(node)) {
+        reportErrorForNode(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName,
+            [typeName.name]);
+      } else if (_isTypeNameInAsExpression(node)) {
+        reportErrorForNode(
+            StaticWarningCode.CAST_TO_NON_TYPE, typeName, [typeName.name]);
+      } else if (_isTypeNameInIsExpression(node)) {
+        reportErrorForNode(StaticWarningCode.TYPE_TEST_WITH_UNDEFINED_NAME,
+            typeName, [typeName.name]);
+      } else if ((redirectingConstructorKind =
+              _getRedirectingConstructorKind(node)) !=
+          null) {
+        ErrorCode errorCode = (redirectingConstructorKind ==
+                RedirectingConstructorKind.CONST
+            ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS
+            : StaticWarningCode.REDIRECT_TO_NON_CLASS);
+        reportErrorForNode(errorCode, typeName, [typeName.name]);
+      } else if (_isTypeNameInTypeArgumentList(node)) {
+        reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT,
+            typeName, [typeName.name]);
+      } else {
+        reportErrorForNode(
+            StaticWarningCode.UNDEFINED_CLASS, typeName, [typeName.name]);
+      }
+      elementValid = false;
+    }
+    if (!elementValid) {
+      if (element is MultiplyDefinedElement) {
+        _setElement(typeName, element);
+      } else {
+        _setElement(typeName, _dynamicType.element);
+      }
+      typeName.staticType = _undefinedType;
+      node.type = _undefinedType;
+      return null;
+    }
+    DartType type = null;
+    if (element is ClassElement) {
+      _setElement(typeName, element);
+      type = element.type;
+    } else if (element is FunctionTypeAliasElement) {
+      _setElement(typeName, element);
+      type = element.type;
+    } else if (element is TypeParameterElement) {
+      _setElement(typeName, element);
+      type = element.type;
+      if (argumentList != null) {
+        // Type parameters cannot have type arguments.
+        // TODO(brianwilkerson) Report this error.
+        //      resolver.reportError(ResolverErrorCode.?, keyType);
+      }
+    } else if (element is MultiplyDefinedElement) {
+      List<Element> elements = element.conflictingElements;
+      type = _getTypeWhenMultiplyDefined(elements);
+      if (type != null) {
+        node.type = type;
+      }
+    } else {
+      // The name does not represent a type.
+      RedirectingConstructorKind redirectingConstructorKind;
+      if (_isTypeNameInCatchClause(node)) {
+        reportErrorForNode(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE, typeName,
+            [typeName.name]);
+      } else if (_isTypeNameInAsExpression(node)) {
+        reportErrorForNode(
+            StaticWarningCode.CAST_TO_NON_TYPE, typeName, [typeName.name]);
+      } else if (_isTypeNameInIsExpression(node)) {
+        reportErrorForNode(StaticWarningCode.TYPE_TEST_WITH_NON_TYPE, typeName,
+            [typeName.name]);
+      } else if ((redirectingConstructorKind =
+              _getRedirectingConstructorKind(node)) !=
+          null) {
+        ErrorCode errorCode = (redirectingConstructorKind ==
+                RedirectingConstructorKind.CONST
+            ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS
+            : StaticWarningCode.REDIRECT_TO_NON_CLASS);
+        reportErrorForNode(errorCode, typeName, [typeName.name]);
+      } else if (_isTypeNameInTypeArgumentList(node)) {
+        reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT,
+            typeName, [typeName.name]);
+      } else {
+        AstNode parent = typeName.parent;
+        while (parent is TypeName) {
+          parent = parent.parent;
+        }
+        if (parent is ExtendsClause ||
+            parent is ImplementsClause ||
+            parent is WithClause ||
+            parent is ClassTypeAlias) {
+          // Ignored. The error will be reported elsewhere.
+        } else {
+          reportErrorForNode(
+              StaticWarningCode.NOT_A_TYPE, typeName, [typeName.name]);
+        }
+      }
+      _setElement(typeName, _dynamicType.element);
+      typeName.staticType = _dynamicType;
+      node.type = _dynamicType;
+      return null;
+    }
+    if (argumentList != null) {
+      NodeList<TypeName> arguments = argumentList.arguments;
+      int argumentCount = arguments.length;
+      List<DartType> parameters = _getTypeArguments(type);
+      int parameterCount = parameters.length;
+      List<DartType> typeArguments = new List<DartType>(parameterCount);
+      if (argumentCount == parameterCount) {
+        for (int i = 0; i < parameterCount; i++) {
+          TypeName argumentTypeName = arguments[i];
+          DartType argumentType = _getType(argumentTypeName);
+          if (argumentType == null) {
+            argumentType = _dynamicType;
+          }
+          typeArguments[i] = argumentType;
+        }
+      } else {
+        reportErrorForNode(_getInvalidTypeParametersErrorCode(node), node, [
+          typeName.name,
+          parameterCount,
+          argumentCount
+        ]);
+        for (int i = 0; i < parameterCount; i++) {
+          typeArguments[i] = _dynamicType;
+        }
+      }
+      if (type is InterfaceTypeImpl) {
+        InterfaceTypeImpl interfaceType = type as InterfaceTypeImpl;
+        type = interfaceType.substitute4(typeArguments);
+      } else if (type is FunctionTypeImpl) {
+        FunctionTypeImpl functionType = type as FunctionTypeImpl;
+        type = functionType.substitute3(typeArguments);
+      } else {
+        // TODO(brianwilkerson) Report this internal error.
+      }
+    } else {
+      //
+      // Check for the case where there are no type arguments given for a
+      // parameterized type.
+      //
+      List<DartType> parameters = _getTypeArguments(type);
+      int parameterCount = parameters.length;
+      if (parameterCount > 0) {
+        DynamicTypeImpl dynamicType = DynamicTypeImpl.instance;
+        List<DartType> arguments = new List<DartType>(parameterCount);
+        for (int i = 0; i < parameterCount; i++) {
+          arguments[i] = dynamicType;
+        }
+        type = type.substitute2(arguments, parameters);
+      }
+    }
+    typeName.staticType = type;
+    node.type = type;
+    return null;
+  }
+
+  @override
+  Object visitTypeParameter(TypeParameter node) {
+    super.visitTypeParameter(node);
+    TypeName bound = node.bound;
+    if (bound != null) {
+      TypeParameterElementImpl typeParameter =
+          node.name.staticElement as TypeParameterElementImpl;
+      if (typeParameter != null) {
+        typeParameter.bound = bound.type;
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitVariableDeclaration(VariableDeclaration node) {
+    super.visitVariableDeclaration(node);
+    DartType declaredType;
+    TypeName typeName = (node.parent as VariableDeclarationList).type;
+    if (typeName == null) {
+      declaredType = _dynamicType;
+    } else {
+      declaredType = _getType(typeName);
+    }
+    Element element = node.name.staticElement;
+    if (element is VariableElement) {
+      (element as VariableElementImpl).type = declaredType;
+      if (element is PropertyInducingElement) {
+        PropertyInducingElement variableElement = element;
+        PropertyAccessorElementImpl getter =
+            variableElement.getter as PropertyAccessorElementImpl;
+        getter.returnType = declaredType;
+        FunctionTypeImpl getterType = new FunctionTypeImpl.con1(getter);
+        ClassElement definingClass =
+            element.getAncestor((element) => element is ClassElement);
+        if (definingClass != null) {
+          getterType.typeArguments = definingClass.type.typeArguments;
+        }
+        getter.type = getterType;
+        PropertyAccessorElementImpl setter =
+            variableElement.setter as PropertyAccessorElementImpl;
+        if (setter != null) {
+          List<ParameterElement> parameters = setter.parameters;
+          if (parameters.length > 0) {
+            (parameters[0] as ParameterElementImpl).type = declaredType;
+          }
+          setter.returnType = VoidTypeImpl.instance;
+          FunctionTypeImpl setterType = new FunctionTypeImpl.con1(setter);
+          if (definingClass != null) {
+            setterType.typeArguments = definingClass.type.typeArguments;
+          }
+          setter.type = setterType;
+        }
+      }
+    } else {
+      // TODO(brianwilkerson) Report the internal error.
+    }
+    return null;
+  }
+
+  /**
+   * Given a type name representing the return type of a function, compute the return type of the
+   * function.
+   *
+   * @param returnType the type name representing the return type of the function
+   * @return the return type that was computed
+   */
+  DartType _computeReturnType(TypeName returnType) {
+    if (returnType == null) {
+      return _dynamicType;
+    } else {
+      return returnType.type;
+    }
+  }
+
+  /**
+   * Return the class element that represents the class whose name was provided.
+   *
+   * @param identifier the name from the declaration of a class
+   * @return the class element that represents the class
+   */
+  ClassElementImpl _getClassElement(SimpleIdentifier identifier) {
+    // TODO(brianwilkerson) Seems like we should be using
+    // ClassDeclaration.getElement().
+    if (identifier == null) {
+      // TODO(brianwilkerson) Report this
+      // Internal error: We should never build a class declaration without a
+      // name.
+      return null;
+    }
+    Element element = identifier.staticElement;
+    if (element is! ClassElementImpl) {
+      // TODO(brianwilkerson) Report this
+      // Internal error: Failed to create an element for a class declaration.
+      return null;
+    }
+    return element as ClassElementImpl;
+  }
+
+  /**
+   * Return an array containing all of the elements associated with the parameters in the given
+   * list.
+   *
+   * @param parameterList the list of parameters whose elements are to be returned
+   * @return the elements associated with the parameters
+   */
+  List<ParameterElement> _getElements(FormalParameterList parameterList) {
+    List<ParameterElement> elements = new List<ParameterElement>();
+    for (FormalParameter parameter in parameterList.parameters) {
+      ParameterElement element =
+          parameter.identifier.staticElement as ParameterElement;
+      // TODO(brianwilkerson) Understand why the element would be null.
+      if (element != null) {
+        elements.add(element);
+      }
+    }
+    return elements;
+  }
+
+  /**
+   * The number of type arguments in the given type name does not match the number of parameters in
+   * the corresponding class element. Return the error code that should be used to report this
+   * error.
+   *
+   * @param node the type name with the wrong number of type arguments
+   * @return the error code that should be used to report that the wrong number of type arguments
+   *         were provided
+   */
+  ErrorCode _getInvalidTypeParametersErrorCode(TypeName node) {
+    AstNode parent = node.parent;
+    if (parent is ConstructorName) {
+      parent = parent.parent;
+      if (parent is InstanceCreationExpression) {
+        if (parent.isConst) {
+          return CompileTimeErrorCode.CONST_WITH_INVALID_TYPE_PARAMETERS;
+        } else {
+          return StaticWarningCode.NEW_WITH_INVALID_TYPE_PARAMETERS;
+        }
+      }
+    }
+    return StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS;
+  }
+
+  /**
+   * Checks if the given type name is the target in a redirected constructor.
+   *
+   * @param typeName the type name to analyze
+   * @return some [RedirectingConstructorKind] if the given type name is used as the type in a
+   *         redirected constructor, or `null` otherwise
+   */
+  RedirectingConstructorKind _getRedirectingConstructorKind(TypeName typeName) {
+    AstNode parent = typeName.parent;
+    if (parent is ConstructorName) {
+      ConstructorName constructorName = parent as ConstructorName;
+      parent = constructorName.parent;
+      if (parent is ConstructorDeclaration) {
+        if (identical(parent.redirectedConstructor, constructorName)) {
+          if (parent.constKeyword != null) {
+            return RedirectingConstructorKind.CONST;
+          }
+          return RedirectingConstructorKind.NORMAL;
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the type represented by the given type name.
+   *
+   * @param typeName the type name representing the type to be returned
+   * @return the type represented by the type name
+   */
+  DartType _getType(TypeName typeName) {
+    DartType type = typeName.type;
+    if (type == null) {
+      return _undefinedType;
+    }
+    return type;
+  }
+
+  /**
+   * Return the type arguments associated with the given type.
+   *
+   * @param type the type whole type arguments are to be returned
+   * @return the type arguments associated with the given type
+   */
+  List<DartType> _getTypeArguments(DartType type) {
+    if (type is InterfaceType) {
+      return type.typeArguments;
+    } else if (type is FunctionType) {
+      return type.typeArguments;
+    }
+    return DartType.EMPTY_LIST;
+  }
+
+  /**
+   * Returns the simple identifier of the given (may be qualified) type name.
+   *
+   * @param typeName the (may be qualified) qualified type name
+   * @return the simple identifier of the given (may be qualified) type name.
+   */
+  SimpleIdentifier _getTypeSimpleIdentifier(Identifier typeName) {
+    if (typeName is SimpleIdentifier) {
+      return typeName;
+    } else {
+      return (typeName as PrefixedIdentifier).identifier;
+    }
+  }
+
+  /**
+   * Given the multiple elements to which a single name could potentially be resolved, return the
+   * single interface type that should be used, or `null` if there is no clear choice.
+   *
+   * @param elements the elements to which a single name could potentially be resolved
+   * @return the single interface type that should be used for the type name
+   */
+  InterfaceType _getTypeWhenMultiplyDefined(List<Element> elements) {
+    InterfaceType type = null;
+    for (Element element in elements) {
+      if (element is ClassElement) {
+        if (type != null) {
+          return null;
+        }
+        type = element.type;
+      }
+    }
+    return type;
+  }
+
+  /**
+   * Checks if the given type name is used as the type in an as expression.
+   *
+   * @param typeName the type name to analyzer
+   * @return `true` if the given type name is used as the type in an as expression
+   */
+  bool _isTypeNameInAsExpression(TypeName typeName) {
+    AstNode parent = typeName.parent;
+    if (parent is AsExpression) {
+      AsExpression asExpression = parent;
+      return identical(asExpression.type, typeName);
+    }
+    return false;
+  }
+
+  /**
+   * Checks if the given type name is used as the exception type in a catch clause.
+   *
+   * @param typeName the type name to analyzer
+   * @return `true` if the given type name is used as the exception type in a catch clause
+   */
+  bool _isTypeNameInCatchClause(TypeName typeName) {
+    AstNode parent = typeName.parent;
+    if (parent is CatchClause) {
+      CatchClause catchClause = parent;
+      return identical(catchClause.exceptionType, typeName);
+    }
+    return false;
+  }
+
+  /**
+   * Checks if the given type name is used as the type in an instance creation expression.
+   *
+   * @param typeName the type name to analyzer
+   * @return `true` if the given type name is used as the type in an instance creation
+   *         expression
+   */
+  bool _isTypeNameInInstanceCreationExpression(TypeName typeName) {
+    AstNode parent = typeName.parent;
+    if (parent is ConstructorName &&
+        parent.parent is InstanceCreationExpression) {
+      ConstructorName constructorName = parent;
+      return constructorName != null &&
+          identical(constructorName.type, typeName);
+    }
+    return false;
+  }
+
+  /**
+   * Checks if the given type name is used as the type in an is expression.
+   *
+   * @param typeName the type name to analyzer
+   * @return `true` if the given type name is used as the type in an is expression
+   */
+  bool _isTypeNameInIsExpression(TypeName typeName) {
+    AstNode parent = typeName.parent;
+    if (parent is IsExpression) {
+      IsExpression isExpression = parent;
+      return identical(isExpression.type, typeName);
+    }
+    return false;
+  }
+
+  /**
+   * Checks if the given type name used in a type argument list.
+   *
+   * @param typeName the type name to analyzer
+   * @return `true` if the given type name is in a type argument list
+   */
+  bool _isTypeNameInTypeArgumentList(TypeName typeName) =>
+      typeName.parent is TypeArgumentList;
+
+  /**
+   * Record that the static type of the given node is the given type.
+   *
+   * @param expression the node whose type is to be recorded
+   * @param type the static type of the node
+   */
+  Object _recordType(Expression expression, DartType type) {
+    if (type == null) {
+      expression.staticType = _dynamicType;
+    } else {
+      expression.staticType = type;
+    }
+    return null;
+  }
+
+  /**
+   * Resolve the types in the given with and implements clauses and associate those types with the
+   * given class element.
+   *
+   * @param classElement the class element with which the mixin and interface types are to be
+   *          associated
+   * @param withClause the with clause to be resolved
+   * @param implementsClause the implements clause to be resolved
+   */
+  void _resolve(ClassElementImpl classElement, WithClause withClause,
+      ImplementsClause implementsClause) {
+    if (withClause != null) {
+      List<InterfaceType> mixinTypes = _resolveTypes(withClause.mixinTypes,
+          CompileTimeErrorCode.MIXIN_OF_NON_CLASS,
+          CompileTimeErrorCode.MIXIN_OF_ENUM,
+          CompileTimeErrorCode.MIXIN_OF_NON_CLASS);
+      if (classElement != null) {
+        classElement.mixins = mixinTypes;
+        classElement.withClauseRange =
+            new SourceRange(withClause.offset, withClause.length);
+      }
+    }
+    if (implementsClause != null) {
+      NodeList<TypeName> interfaces = implementsClause.interfaces;
+      List<InterfaceType> interfaceTypes = _resolveTypes(interfaces,
+          CompileTimeErrorCode.IMPLEMENTS_NON_CLASS,
+          CompileTimeErrorCode.IMPLEMENTS_ENUM,
+          CompileTimeErrorCode.IMPLEMENTS_DYNAMIC);
+      if (classElement != null) {
+        classElement.interfaces = interfaceTypes;
+      }
+      // TODO(brianwilkerson) Move the following checks to ErrorVerifier.
+      int count = interfaces.length;
+      List<bool> detectedRepeatOnIndex = new List<bool>.filled(count, false);
+      for (int i = 0; i < detectedRepeatOnIndex.length; i++) {
+        detectedRepeatOnIndex[i] = false;
+      }
+      for (int i = 0; i < count; i++) {
+        TypeName typeName = interfaces[i];
+        if (!detectedRepeatOnIndex[i]) {
+          Element element = typeName.name.staticElement;
+          for (int j = i + 1; j < count; j++) {
+            TypeName typeName2 = interfaces[j];
+            Identifier identifier2 = typeName2.name;
+            String name2 = identifier2.name;
+            Element element2 = identifier2.staticElement;
+            if (element != null && element == element2) {
+              detectedRepeatOnIndex[j] = true;
+              reportErrorForNode(
+                  CompileTimeErrorCode.IMPLEMENTS_REPEATED, typeName2, [name2]);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Return the type specified by the given name.
+   *
+   * @param typeName the type name specifying the type to be returned
+   * @param nonTypeError the error to produce if the type name is defined to be something other than
+   *          a type
+   * @param enumTypeError the error to produce if the type name is defined to be an enum
+   * @param dynamicTypeError the error to produce if the type name is "dynamic"
+   * @return the type specified by the type name
+   */
+  InterfaceType _resolveType(TypeName typeName, ErrorCode nonTypeError,
+      ErrorCode enumTypeError, ErrorCode dynamicTypeError) {
+    DartType type = typeName.type;
+    if (type is InterfaceType) {
+      ClassElement element = type.element;
+      if (element != null && element.isEnum) {
+        reportErrorForNode(enumTypeError, typeName);
+        return null;
+      }
+      return type;
+    }
+    // If the type is not an InterfaceType, then visitTypeName() sets the type
+    // to be a DynamicTypeImpl
+    Identifier name = typeName.name;
+    if (name.name == sc.Keyword.DYNAMIC.syntax) {
+      reportErrorForNode(dynamicTypeError, name, [name.name]);
+    } else {
+      reportErrorForNode(nonTypeError, name, [name.name]);
+    }
+    return null;
+  }
+
+  /**
+   * Resolve the types in the given list of type names.
+   *
+   * @param typeNames the type names to be resolved
+   * @param nonTypeError the error to produce if the type name is defined to be something other than
+   *          a type
+   * @param enumTypeError the error to produce if the type name is defined to be an enum
+   * @param dynamicTypeError the error to produce if the type name is "dynamic"
+   * @return an array containing all of the types that were resolved.
+   */
+  List<InterfaceType> _resolveTypes(NodeList<TypeName> typeNames,
+      ErrorCode nonTypeError, ErrorCode enumTypeError,
+      ErrorCode dynamicTypeError) {
+    List<InterfaceType> types = new List<InterfaceType>();
+    for (TypeName typeName in typeNames) {
+      InterfaceType type =
+          _resolveType(typeName, nonTypeError, enumTypeError, dynamicTypeError);
+      if (type != null) {
+        types.add(type);
+      }
+    }
+    return types;
+  }
+
+  void _setElement(Identifier typeName, Element element) {
+    if (element != null) {
+      if (typeName is SimpleIdentifier) {
+        typeName.staticElement = element;
+      } else if (typeName is PrefixedIdentifier) {
+        PrefixedIdentifier identifier = typeName;
+        identifier.identifier.staticElement = element;
+        SimpleIdentifier prefix = identifier.prefix;
+        Element prefixElement = nameScope.lookup(prefix, definingLibrary);
+        if (prefixElement != null) {
+          prefix.staticElement = prefixElement;
+        }
+      }
+    }
+  }
+
+  /**
+   * Given a parameter element, create a function type based on the given return type and parameter
+   * list and associate the created type with the element.
+   *
+   * @param element the parameter element whose type is to be set
+   * @param returnType the (possibly `null`) return type of the function
+   * @param parameterList the list of parameters to the function
+   */
+  void _setFunctionTypedParameterType(ParameterElementImpl element,
+      TypeName returnType, FormalParameterList parameterList) {
+    List<ParameterElement> parameters = _getElements(parameterList);
+    FunctionTypeAliasElementImpl aliasElement =
+        new FunctionTypeAliasElementImpl.forNode(null);
+    aliasElement.synthetic = true;
+    aliasElement.shareParameters(parameters);
+    aliasElement.returnType = _computeReturnType(returnType);
+    // FunctionTypeAliasElementImpl assumes the enclosing element is a
+    // CompilationUnitElement (because non-synthetic function types can only be
+    // declared at top level), so to avoid breaking things, go find the
+    // compilation unit element.
+    aliasElement.enclosingElement =
+        element.getAncestor((element) => element is CompilationUnitElement);
+    FunctionTypeImpl type = new FunctionTypeImpl.con2(aliasElement);
+    ClassElement definingClass =
+        element.getAncestor((element) => element is ClassElement);
+    if (definingClass != null) {
+      aliasElement.shareTypeParameters(definingClass.typeParameters);
+      type.typeArguments = definingClass.type.typeArguments;
+    } else {
+      FunctionTypeAliasElement alias =
+          element.getAncestor((element) => element is FunctionTypeAliasElement);
+      while (alias != null && alias.isSynthetic) {
+        alias =
+            alias.getAncestor((element) => element is FunctionTypeAliasElement);
+      }
+      if (alias != null) {
+        aliasElement.typeParameters = alias.typeParameters;
+        type.typeArguments = alias.type.typeArguments;
+      } else {
+        type.typeArguments = DartType.EMPTY_LIST;
+      }
+    }
+    element.type = type;
+  }
+
+  /**
+   * @return `true` if the name of the given [TypeName] is an built-in identifier.
+   */
+  static bool _isBuiltInIdentifier(TypeName node) {
+    sc.Token token = node.name.beginToken;
+    return token.type == sc.TokenType.KEYWORD;
+  }
+
+  /**
+   * @return `true` if given [TypeName] is used as a type annotation.
+   */
+  static bool _isTypeAnnotation(TypeName node) {
+    AstNode parent = node.parent;
+    if (parent is VariableDeclarationList) {
+      return identical(parent.type, node);
+    }
+    if (parent is FieldFormalParameter) {
+      return identical(parent.type, node);
+    }
+    if (parent is SimpleFormalParameter) {
+      return identical(parent.type, node);
+    }
+    return false;
+  }
+}
+
+/**
+ * Instances of the class [UnusedLocalElementsVerifier] traverse an element
+ * structure looking for cases of [HintCode.UNUSED_ELEMENT],
+ * [HintCode.UNUSED_FIELD], [HintCode.UNUSED_LOCAL_VARIABLE], etc.
+ */
+class UnusedLocalElementsVerifier extends RecursiveElementVisitor {
+  /**
+   * The error listener to which errors will be reported.
+   */
+  final AnalysisErrorListener _errorListener;
+
+  /**
+   * The elements know to be used.
+   */
+  final UsedLocalElements _usedElements;
+
+  /**
+   * Create a new instance of the [UnusedLocalElementsVerifier].
+   */
+  UnusedLocalElementsVerifier(this._errorListener, this._usedElements);
+
+  @override
+  visitClassElement(ClassElement element) {
+    if (!_isUsedElement(element)) {
+      _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [
+        element.kind.displayName,
+        element.displayName
+      ]);
+    }
+    super.visitClassElement(element);
+  }
+
+  @override
+  visitFieldElement(FieldElement element) {
+    if (!_isReadMember(element)) {
+      _reportErrorForElement(
+          HintCode.UNUSED_FIELD, element, [element.displayName]);
+    }
+    super.visitFieldElement(element);
+  }
+
+  @override
+  visitFunctionElement(FunctionElement element) {
+    if (!_isUsedElement(element)) {
+      _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [
+        element.kind.displayName,
+        element.displayName
+      ]);
+    }
+    super.visitFunctionElement(element);
+  }
+
+  @override
+  visitFunctionTypeAliasElement(FunctionTypeAliasElement element) {
+    if (!_isUsedElement(element)) {
+      _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [
+        element.kind.displayName,
+        element.displayName
+      ]);
+    }
+    super.visitFunctionTypeAliasElement(element);
+  }
+
+  @override
+  visitLocalVariableElement(LocalVariableElement element) {
+    if (!_isUsedElement(element) && !_isNamedUnderscore(element)) {
+      HintCode errorCode;
+      if (_usedElements.isCatchException(element)) {
+        errorCode = HintCode.UNUSED_CATCH_CLAUSE;
+      } else if (_usedElements.isCatchStackTrace(element)) {
+        errorCode = HintCode.UNUSED_CATCH_STACK;
+      } else {
+        errorCode = HintCode.UNUSED_LOCAL_VARIABLE;
+      }
+      _reportErrorForElement(errorCode, element, [element.displayName]);
+    }
+  }
+
+  @override
+  visitMethodElement(MethodElement element) {
+    if (!_isUsedMember(element)) {
+      _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [
+        element.kind.displayName,
+        element.displayName
+      ]);
+    }
+    super.visitMethodElement(element);
+  }
+
+  @override
+  visitPropertyAccessorElement(PropertyAccessorElement element) {
+    if (!_isUsedMember(element)) {
+      _reportErrorForElement(HintCode.UNUSED_ELEMENT, element, [
+        element.kind.displayName,
+        element.displayName
+      ]);
+    }
+    super.visitPropertyAccessorElement(element);
+  }
+
+  bool _isNamedUnderscore(LocalVariableElement element) {
+    String name = element.name;
+    if (name != null) {
+      for (int index = name.length - 1; index >= 0; --index) {
+        if (name.codeUnitAt(index) != 0x5F) {
+          // 0x5F => '_'
+          return false;
+        }
+      }
+      return true;
+    }
+    return false;
+  }
+
+  bool _isReadMember(Element element) {
+    if (element.isPublic) {
+      return true;
+    }
+    if (element.isSynthetic) {
+      return true;
+    }
+    return _usedElements.readMembers.contains(element.displayName);
+  }
+
+  bool _isUsedElement(Element element) {
+    if (element.isSynthetic) {
+      return true;
+    }
+    if (element is LocalVariableElement ||
+        element is FunctionElement && !element.isStatic) {
+      // local variable or function
+    } else {
+      if (element.isPublic) {
+        return true;
+      }
+    }
+    return _usedElements.elements.contains(element);
+  }
+
+  bool _isUsedMember(Element element) {
+    if (element.isPublic) {
+      return true;
+    }
+    if (element.isSynthetic) {
+      return true;
+    }
+    if (_usedElements.members.contains(element.displayName)) {
+      return true;
+    }
+    return _usedElements.elements.contains(element);
+  }
+
+  void _reportErrorForElement(
+      ErrorCode errorCode, Element element, List<Object> arguments) {
+    if (element != null) {
+      _errorListener.onError(new AnalysisError.con2(element.source,
+          element.nameOffset, element.displayName.length, errorCode,
+          arguments));
+    }
+  }
+}
+
+/**
+ * A container with information about used imports prefixes and used imported
+ * elements.
+ */
+class UsedImportedElements {
+  /**
+   * The set of referenced [PrefixElement]s.
+   */
+  final Set<PrefixElement> prefixes = new HashSet<PrefixElement>();
+
+  /**
+   * The set of referenced top-level [Element]s.
+   */
+  final Set<Element> elements = new HashSet<Element>();
+}
+
+/**
+ * A container with sets of used [Element]s.
+ * All these elements are defined in a single compilation unit or a library.
+ */
+class UsedLocalElements {
+  /**
+   * Resolved, locally defined elements that are used or potentially can be
+   * used.
+   */
+  final HashSet<Element> elements = new HashSet<Element>();
+
+  /**
+   * [LocalVariableElement]s that represent exceptions in [CatchClause]s.
+   */
+  final HashSet<LocalVariableElement> catchExceptionElements =
+      new HashSet<LocalVariableElement>();
+
+  /**
+   * [LocalVariableElement]s that represent stack traces in [CatchClause]s.
+   */
+  final HashSet<LocalVariableElement> catchStackTraceElements =
+      new HashSet<LocalVariableElement>();
+
+  /**
+   * Names of resolved or unresolved class members that are referenced in the
+   * library.
+   */
+  final HashSet<String> members = new HashSet<String>();
+
+  /**
+   * Names of resolved or unresolved class members that are read in the
+   * library.
+   */
+  final HashSet<String> readMembers = new HashSet<String>();
+
+  UsedLocalElements();
+
+  factory UsedLocalElements.merge(List<UsedLocalElements> parts) {
+    UsedLocalElements result = new UsedLocalElements();
+    for (UsedLocalElements part in parts) {
+      result.elements.addAll(part.elements);
+      result.catchExceptionElements.addAll(part.catchExceptionElements);
+      result.catchStackTraceElements.addAll(part.catchStackTraceElements);
+      result.members.addAll(part.members);
+      result.readMembers.addAll(part.readMembers);
+    }
+    return result;
+  }
+
+  void addCatchException(LocalVariableElement element) {
+    if (element != null) {
+      catchExceptionElements.add(element);
+    }
+  }
+
+  void addCatchStackTrace(LocalVariableElement element) {
+    if (element != null) {
+      catchStackTraceElements.add(element);
+    }
+  }
+
+  void addElement(Element element) {
+    if (element != null) {
+      elements.add(element);
+    }
+  }
+
+  bool isCatchException(LocalVariableElement element) {
+    return catchExceptionElements.contains(element);
+  }
+
+  bool isCatchStackTrace(LocalVariableElement element) {
+    return catchStackTraceElements.contains(element);
+  }
+}
+
+/**
+ * Instances of the class `VariableResolverVisitor` are used to resolve
+ * [SimpleIdentifier]s to local variables and formal parameters.
+ */
+class VariableResolverVisitor extends ScopedVisitor {
+  /**
+   * The method or function that we are currently visiting, or `null` if we are not inside a
+   * method or function.
+   */
+  ExecutableElement _enclosingFunction;
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
+   *
+   * @param library the library containing the compilation unit being resolved
+   * @param source the source representing the compilation unit being visited
+   * @param typeProvider the object used to access the types from the core library
+   */
+  VariableResolverVisitor.con1(
+      Library library, Source source, TypeProvider typeProvider)
+      : super.con1(library, source, typeProvider);
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in an AST node.
+   *
+   * @param definingLibrary the element for the library containing the node being visited
+   * @param source the source representing the compilation unit containing the node being visited
+   * @param typeProvider the object used to access the types from the core library
+   * @param nameScope the scope used to resolve identifiers in the node that will first be visited
+   * @param errorListener the error listener that will be informed of any errors that are found
+   *          during resolution
+   */
+  VariableResolverVisitor.con2(LibraryElement definingLibrary, Source source,
+      TypeProvider typeProvider, Scope nameScope,
+      AnalysisErrorListener errorListener)
+      : super.con3(
+          definingLibrary, source, typeProvider, nameScope, errorListener);
+
+  /**
+   * Initialize a newly created visitor to resolve the nodes in a compilation unit.
+   *
+   * @param library the library containing the compilation unit being resolved
+   * @param source the source representing the compilation unit being visited
+   * @param typeProvider the object used to access the types from the core library
+   */
+  VariableResolverVisitor.con3(
+      ResolvableLibrary library, Source source, TypeProvider typeProvider)
+      : super.con4(library, source, typeProvider);
+
+  @override
+  Object visitExportDirective(ExportDirective node) => null;
+
+  @override
+  Object visitFunctionDeclaration(FunctionDeclaration node) {
+    ExecutableElement outerFunction = _enclosingFunction;
+    try {
+      _enclosingFunction = node.element;
+      return super.visitFunctionDeclaration(node);
+    } finally {
+      _enclosingFunction = outerFunction;
+    }
+  }
+
+  @override
+  Object visitFunctionExpression(FunctionExpression node) {
+    if (node.parent is! FunctionDeclaration) {
+      ExecutableElement outerFunction = _enclosingFunction;
+      try {
+        _enclosingFunction = node.element;
+        return super.visitFunctionExpression(node);
+      } finally {
+        _enclosingFunction = outerFunction;
+      }
+    } else {
+      return super.visitFunctionExpression(node);
+    }
+  }
+
+  @override
+  Object visitImportDirective(ImportDirective node) => null;
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    ExecutableElement outerFunction = _enclosingFunction;
+    try {
+      _enclosingFunction = node.element;
+      return super.visitMethodDeclaration(node);
+    } finally {
+      _enclosingFunction = outerFunction;
+    }
+  }
+
+  @override
+  Object visitSimpleIdentifier(SimpleIdentifier node) {
+    // Ignore if already resolved - declaration or type.
+    if (node.staticElement != null) {
+      return null;
+    }
+    // Ignore if qualified.
+    AstNode parent = node.parent;
+    if (parent is PrefixedIdentifier && identical(parent.identifier, node)) {
+      return null;
+    }
+    if (parent is PropertyAccess && identical(parent.propertyName, node)) {
+      return null;
+    }
+    if (parent is MethodInvocation &&
+        identical(parent.methodName, node) &&
+        parent.realTarget != null) {
+      return null;
+    }
+    if (parent is ConstructorName) {
+      return null;
+    }
+    if (parent is Label) {
+      return null;
+    }
+    // Prepare VariableElement.
+    Element element = nameScope.lookup(node, definingLibrary);
+    if (element is! VariableElement) {
+      return null;
+    }
+    // Must be local or parameter.
+    ElementKind kind = element.kind;
+    if (kind == ElementKind.LOCAL_VARIABLE) {
+      node.staticElement = element;
+      LocalVariableElementImpl variableImpl =
+          element as LocalVariableElementImpl;
+      if (node.inSetterContext()) {
+        variableImpl.markPotentiallyMutatedInScope();
+        if (element.enclosingElement != _enclosingFunction) {
+          variableImpl.markPotentiallyMutatedInClosure();
+        }
+      }
+    } else if (kind == ElementKind.PARAMETER) {
+      node.staticElement = element;
+      if (node.inSetterContext()) {
+        ParameterElementImpl parameterImpl = element as ParameterElementImpl;
+        parameterImpl.markPotentiallyMutatedInScope();
+        // If we are in some closure, check if it is not the same as where
+        // variable is declared.
+        if (_enclosingFunction != null &&
+            (element.enclosingElement != _enclosingFunction)) {
+          parameterImpl.markPotentiallyMutatedInClosure();
+        }
+      }
+    }
+    return null;
+  }
+}
+
+class _ConstantVerifier_validateInitializerExpression extends ConstantVisitor {
+  final ConstantVerifier verifier;
+
+  List<ParameterElement> parameterElements;
+
+  _ConstantVerifier_validateInitializerExpression(TypeProvider arg0,
+      ErrorReporter arg1, this.verifier, this.parameterElements)
+      : super.con1(arg0, arg1);
+
+  @override
+  DartObjectImpl visitSimpleIdentifier(SimpleIdentifier node) {
+    Element element = node.staticElement;
+    for (ParameterElement parameterElement in parameterElements) {
+      if (identical(parameterElement, element) && parameterElement != null) {
+        DartType type = parameterElement.type;
+        if (type != null) {
+          if (type.isDynamic) {
+            return new DartObjectImpl(
+                verifier._typeProvider.objectType, DynamicState.DYNAMIC_STATE);
+          } else if (type.isSubtypeOf(verifier._boolType)) {
+            return new DartObjectImpl(
+                verifier._typeProvider.boolType, BoolState.UNKNOWN_VALUE);
+          } else if (type.isSubtypeOf(verifier._typeProvider.doubleType)) {
+            return new DartObjectImpl(
+                verifier._typeProvider.doubleType, DoubleState.UNKNOWN_VALUE);
+          } else if (type.isSubtypeOf(verifier._intType)) {
+            return new DartObjectImpl(
+                verifier._typeProvider.intType, IntState.UNKNOWN_VALUE);
+          } else if (type.isSubtypeOf(verifier._numType)) {
+            return new DartObjectImpl(
+                verifier._typeProvider.numType, NumState.UNKNOWN_VALUE);
+          } else if (type.isSubtypeOf(verifier._stringType)) {
+            return new DartObjectImpl(
+                verifier._typeProvider.stringType, StringState.UNKNOWN_VALUE);
+          }
+          //
+          // We don't test for other types of objects (such as List, Map,
+          // Function or Type) because there are no operations allowed on such
+          // types other than '==' and '!=', which means that we don't need to
+          // know the type when there is no specific data about the state of
+          // such objects.
+          //
+        }
+        return new DartObjectImpl(
+            type is InterfaceType ? type : verifier._typeProvider.objectType,
+            GenericState.UNKNOWN_VALUE);
+      }
+    }
+    return super.visitSimpleIdentifier(node);
+  }
+}
+
+class _ElementBuilder_visitClassDeclaration extends UnifyingAstVisitor<Object> {
+  final ElementBuilder builder;
+
+  List<ClassMember> nonFields;
+
+  _ElementBuilder_visitClassDeclaration(this.builder, this.nonFields) : super();
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    nonFields.add(node);
+    return null;
+  }
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    nonFields.add(node);
+    return null;
+  }
+
+  @override
+  Object visitNode(AstNode node) => node.accept(builder);
+}
+
+class _ResolverVisitor_isVariableAccessedInClosure
+    extends RecursiveAstVisitor<Object> {
+  final Element variable;
+
+  bool result = false;
+
+  bool _inClosure = false;
+
+  _ResolverVisitor_isVariableAccessedInClosure(this.variable);
+
+  @override
+  Object visitFunctionExpression(FunctionExpression node) {
+    bool inClosure = this._inClosure;
+    try {
+      this._inClosure = true;
+      return super.visitFunctionExpression(node);
+    } finally {
+      this._inClosure = inClosure;
+    }
+  }
+
+  @override
+  Object visitSimpleIdentifier(SimpleIdentifier node) {
+    if (result) {
+      return null;
+    }
+    if (_inClosure && identical(node.staticElement, variable)) {
+      result = true;
+    }
+    return null;
+  }
+}
+
+class _ResolverVisitor_isVariablePotentiallyMutatedIn
+    extends RecursiveAstVisitor<Object> {
+  final Element variable;
+
+  bool result = false;
+
+  _ResolverVisitor_isVariablePotentiallyMutatedIn(this.variable);
+
+  @override
+  Object visitSimpleIdentifier(SimpleIdentifier node) {
+    if (result) {
+      return null;
+    }
+    if (identical(node.staticElement, variable)) {
+      if (node.inSetterContext()) {
+        result = true;
+      }
+    }
+    return null;
+  }
+}
+
+class _TypeResolverVisitor_visitClassMembersInScope
+    extends UnifyingAstVisitor<Object> {
+  final TypeResolverVisitor TypeResolverVisitor_this;
+
+  List<ClassMember> nonFields;
+
+  _TypeResolverVisitor_visitClassMembersInScope(
+      this.TypeResolverVisitor_this, this.nonFields)
+      : super();
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    nonFields.add(node);
+    return null;
+  }
+
+  @override
+  Object visitExtendsClause(ExtendsClause node) => null;
+
+  @override
+  Object visitImplementsClause(ImplementsClause node) => null;
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    nonFields.add(node);
+    return null;
+  }
+
+  @override
+  Object visitNode(AstNode node) => node.accept(TypeResolverVisitor_this);
+
+  @override
+  Object visitWithClause(WithClause node) => null;
+}
diff --git a/analyzer/lib/src/generated/scanner.dart b/analyzer/lib/src/generated/scanner.dart
new file mode 100644
index 0000000..d7bc236
--- /dev/null
+++ b/analyzer/lib/src/generated/scanner.dart
@@ -0,0 +1,2679 @@
+// 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 engine.scanner;
+
+import 'dart:collection';
+
+import 'error.dart';
+import 'java_engine.dart';
+import 'source.dart';
+
+/**
+ * The opening half of a grouping pair of tokens. This is used for curly
+ * brackets ('{'), parentheses ('('), and square brackets ('[').
+ */
+class BeginToken extends Token {
+  /**
+   * The token that corresponds to this token.
+   */
+  Token endToken;
+
+  /**
+   * Initialize a newly created token to have the given [type] at the given
+   * [offset].
+   */
+  BeginToken(TokenType type, int offset) : super(type, offset) {
+    assert(type == TokenType.OPEN_CURLY_BRACKET ||
+        type == TokenType.OPEN_PAREN ||
+        type == TokenType.OPEN_SQUARE_BRACKET ||
+        type == TokenType.STRING_INTERPOLATION_EXPRESSION);
+  }
+
+  @override
+  Token copy() => new BeginToken(type, offset);
+}
+
+/**
+ * A begin token that is preceded by comments.
+ */
+class BeginTokenWithComment extends BeginToken {
+  /**
+   * The first comment in the list of comments that precede this token.
+   */
+  CommentToken _precedingComment;
+
+  /**
+   * Initialize a newly created token to have the given [type] at the given
+   * [offset] and to be preceded by the comments reachable from the given
+   * [comment].
+   */
+  BeginTokenWithComment(TokenType type, int offset, this._precedingComment)
+      : super(type, offset) {
+    _setCommentParent(_precedingComment);
+  }
+
+  CommentToken get precedingComments => _precedingComment;
+
+  void set precedingComments(CommentToken comment) {
+    _precedingComment = comment;
+    _setCommentParent(_precedingComment);
+  }
+
+  @override
+  void applyDelta(int delta) {
+    super.applyDelta(delta);
+    Token token = precedingComments;
+    while (token != null) {
+      token.applyDelta(delta);
+      token = token.next;
+    }
+  }
+
+  @override
+  Token copy() =>
+      new BeginTokenWithComment(type, offset, copyComments(precedingComments));
+}
+
+/**
+ * A [CharacterReader] that reads a range of characters from another character
+ * reader.
+ */
+class CharacterRangeReader extends CharacterReader {
+  /**
+   * The reader from which the characters are actually being read.
+   */
+  final CharacterReader baseReader;
+
+  /**
+   * The last character to be read.
+   */
+  final int endIndex;
+
+  /**
+   * Initialize a newly created reader to read the characters from the given
+   * [baseReader] between the [startIndex] inclusive to [endIndex] exclusive.
+   */
+  CharacterRangeReader(this.baseReader, int startIndex, this.endIndex) {
+    baseReader.offset = startIndex - 1;
+  }
+
+  @override
+  int get offset => baseReader.offset;
+
+  @override
+  void set offset(int offset) {
+    baseReader.offset = offset;
+  }
+
+  @override
+  int advance() {
+    if (baseReader.offset + 1 >= endIndex) {
+      return -1;
+    }
+    return baseReader.advance();
+  }
+
+  @override
+  String getString(int start, int endDelta) =>
+      baseReader.getString(start, endDelta);
+
+  @override
+  int peek() {
+    if (baseReader.offset + 1 >= endIndex) {
+      return -1;
+    }
+    return baseReader.peek();
+  }
+}
+
+/**
+ * An object used by the scanner to read the characters to be scanned.
+ */
+abstract class CharacterReader {
+  /**
+   * The current offset relative to the beginning of the source. Return the
+   * initial offset if the scanner has not yet scanned the source code, and one
+   * (1) past the end of the source code if the entire source code has been
+   * scanned.
+   */
+  int get offset;
+
+  /**
+   * Set the current offset relative to the beginning of the source to the given
+   * [offset]. The new offset must be between the initial offset and one (1)
+   * past the end of the source code.
+   */
+  void set offset(int offset);
+
+  /**
+   * Advance the current position and return the character at the new current
+   * position.
+   */
+  int advance();
+
+  /**
+   * Return the substring of the source code between the [start] offset and the
+   * modified current position. The current position is modified by adding the
+   * [endDelta], which is the number of characters after the current location to
+   * be included in the string, or the number of characters before the current
+   * location to be excluded if the offset is negative.
+   */
+  String getString(int start, int endDelta);
+
+  /**
+   * Return the character at the current position without changing the current
+   * position.
+   */
+  int peek();
+}
+
+/**
+ * A [CharacterReader] that reads characters from a character sequence.
+ */
+class CharSequenceReader implements CharacterReader {
+  /**
+   * The sequence from which characters will be read.
+   */
+  final String _sequence;
+
+  /**
+   * The number of characters in the string.
+   */
+  int _stringLength = 0;
+
+  /**
+   * The index, relative to the string, of the last character that was read.
+   */
+  int _charOffset = 0;
+
+  /**
+   * Initialize a newly created reader to read the characters in the given
+   * [_sequence].
+   */
+  CharSequenceReader(this._sequence) {
+    this._stringLength = _sequence.length;
+    this._charOffset = -1;
+  }
+
+  @override
+  int get offset => _charOffset;
+
+  @override
+  void set offset(int offset) {
+    _charOffset = offset;
+  }
+
+  @override
+  int advance() {
+    if (_charOffset + 1 >= _stringLength) {
+      return -1;
+    }
+    return _sequence.codeUnitAt(++_charOffset);
+  }
+
+  @override
+  String getString(int start, int endDelta) =>
+      _sequence.substring(start, _charOffset + 1 + endDelta).toString();
+
+  @override
+  int peek() {
+    if (_charOffset + 1 >= _stringLength) {
+      return -1;
+    }
+    return _sequence.codeUnitAt(_charOffset + 1);
+  }
+}
+
+/**
+ * A token representing a comment.
+ */
+class CommentToken extends StringToken {
+  /**
+   * The [Token] that contains this comment.
+   */
+  Token parent;
+
+  /**
+   * Initialize a newly created token to represent a token of the given [type]
+   * with the given [value] at the given [offset].
+   */
+  CommentToken(TokenType type, String value, int offset)
+      : super(type, value, offset);
+
+  @override
+  CommentToken copy() => new CommentToken(type, _value, offset);
+}
+
+/**
+ * A documentation comment token.
+ */
+class DocumentationCommentToken extends CommentToken {
+  /**
+   * The references embedded within the documentation comment.
+   * This list will be empty unless this is a documentation comment that has
+   * references embedded within it.
+   */
+  final List<Token> references = <Token>[];
+
+  /**
+   * Initialize a newly created token to represent a token of the given [type]
+   * with the given [value] at the given [offset].
+   */
+  DocumentationCommentToken(TokenType type, String value, int offset)
+      : super(type, value, offset);
+
+  @override
+  CommentToken copy() => new DocumentationCommentToken(type, _value, offset);
+}
+
+/**
+ * The keywords in the Dart programming language.
+ */
+class Keyword {
+  static const Keyword ASSERT = const Keyword('ASSERT', "assert");
+
+  static const Keyword BREAK = const Keyword('BREAK', "break");
+
+  static const Keyword CASE = const Keyword('CASE', "case");
+
+  static const Keyword CATCH = const Keyword('CATCH', "catch");
+
+  static const Keyword CLASS = const Keyword('CLASS', "class");
+
+  static const Keyword CONST = const Keyword('CONST', "const");
+
+  static const Keyword CONTINUE = const Keyword('CONTINUE', "continue");
+
+  static const Keyword DEFAULT = const Keyword('DEFAULT', "default");
+
+  static const Keyword DO = const Keyword('DO', "do");
+
+  static const Keyword ELSE = const Keyword('ELSE', "else");
+
+  static const Keyword ENUM = const Keyword('ENUM', "enum");
+
+  static const Keyword EXTENDS = const Keyword('EXTENDS', "extends");
+
+  static const Keyword FALSE = const Keyword('FALSE', "false");
+
+  static const Keyword FINAL = const Keyword('FINAL', "final");
+
+  static const Keyword FINALLY = const Keyword('FINALLY', "finally");
+
+  static const Keyword FOR = const Keyword('FOR', "for");
+
+  static const Keyword IF = const Keyword('IF', "if");
+
+  static const Keyword IN = const Keyword('IN', "in");
+
+  static const Keyword IS = const Keyword('IS', "is");
+
+  static const Keyword NEW = const Keyword('NEW', "new");
+
+  static const Keyword NULL = const Keyword('NULL', "null");
+
+  static const Keyword RETHROW = const Keyword('RETHROW', "rethrow");
+
+  static const Keyword RETURN = const Keyword('RETURN', "return");
+
+  static const Keyword SUPER = const Keyword('SUPER', "super");
+
+  static const Keyword SWITCH = const Keyword('SWITCH', "switch");
+
+  static const Keyword THIS = const Keyword('THIS', "this");
+
+  static const Keyword THROW = const Keyword('THROW', "throw");
+
+  static const Keyword TRUE = const Keyword('TRUE', "true");
+
+  static const Keyword TRY = const Keyword('TRY', "try");
+
+  static const Keyword VAR = const Keyword('VAR', "var");
+
+  static const Keyword VOID = const Keyword('VOID', "void");
+
+  static const Keyword WHILE = const Keyword('WHILE', "while");
+
+  static const Keyword WITH = const Keyword('WITH', "with");
+
+  static const Keyword ABSTRACT = const Keyword('ABSTRACT', "abstract", true);
+
+  static const Keyword AS = const Keyword('AS', "as", true);
+
+  static const Keyword DEFERRED = const Keyword('DEFERRED', "deferred", true);
+
+  static const Keyword DYNAMIC = const Keyword('DYNAMIC', "dynamic", true);
+
+  static const Keyword EXPORT = const Keyword('EXPORT', "export", true);
+
+  static const Keyword EXTERNAL = const Keyword('EXTERNAL', "external", true);
+
+  static const Keyword FACTORY = const Keyword('FACTORY', "factory", true);
+
+  static const Keyword GET = const Keyword('GET', "get", true);
+
+  static const Keyword IMPLEMENTS =
+      const Keyword('IMPLEMENTS', "implements", true);
+
+  static const Keyword IMPORT = const Keyword('IMPORT', "import", true);
+
+  static const Keyword LIBRARY = const Keyword('LIBRARY', "library", true);
+
+  static const Keyword OPERATOR = const Keyword('OPERATOR', "operator", true);
+
+  static const Keyword PART = const Keyword('PART', "part", true);
+
+  static const Keyword SET = const Keyword('SET', "set", true);
+
+  static const Keyword STATIC = const Keyword('STATIC', "static", true);
+
+  static const Keyword TYPEDEF = const Keyword('TYPEDEF', "typedef", true);
+
+  static const List<Keyword> values = const [
+    ASSERT,
+    BREAK,
+    CASE,
+    CATCH,
+    CLASS,
+    CONST,
+    CONTINUE,
+    DEFAULT,
+    DO,
+    ELSE,
+    ENUM,
+    EXTENDS,
+    FALSE,
+    FINAL,
+    FINALLY,
+    FOR,
+    IF,
+    IN,
+    IS,
+    NEW,
+    NULL,
+    RETHROW,
+    RETURN,
+    SUPER,
+    SWITCH,
+    THIS,
+    THROW,
+    TRUE,
+    TRY,
+    VAR,
+    VOID,
+    WHILE,
+    WITH,
+    ABSTRACT,
+    AS,
+    DEFERRED,
+    DYNAMIC,
+    EXPORT,
+    EXTERNAL,
+    FACTORY,
+    GET,
+    IMPLEMENTS,
+    IMPORT,
+    LIBRARY,
+    OPERATOR,
+    PART,
+    SET,
+    STATIC,
+    TYPEDEF
+  ];
+
+  /**
+   * A table mapping the lexemes of keywords to the corresponding keyword.
+   */
+  static final Map<String, Keyword> keywords = _createKeywordMap();
+
+  /**
+   * The name of the keyword type.
+   */
+  final String name;
+
+  /**
+   * The lexeme for the keyword.
+   */
+  final String syntax;
+
+  /**
+   * A flag indicating whether the keyword is a pseudo-keyword. Pseudo keywords
+   * can be used as identifiers.
+   */
+  final bool isPseudoKeyword;
+
+  /**
+   * Initialize a newly created keyword to have the given [name] and [syntax].
+   * The keyword is a pseudo-keyword if the [isPseudoKeyword] flag is `true`.
+   */
+  const Keyword(this.name, this.syntax, [this.isPseudoKeyword = false]);
+
+  @override
+  String toString() => name;
+
+  /**
+   * Create a table mapping the lexemes of keywords to the corresponding keyword
+   * and return the table that was created.
+   */
+  static Map<String, Keyword> _createKeywordMap() {
+    LinkedHashMap<String, Keyword> result =
+        new LinkedHashMap<String, Keyword>();
+    for (Keyword keyword in values) {
+      result[keyword.syntax] = keyword;
+    }
+    return result;
+  }
+}
+
+/**
+ * A state in a state machine used to scan keywords.
+ */
+class KeywordState {
+  /**
+   * An empty transition table used by leaf states.
+   */
+  static List<KeywordState> _EMPTY_TABLE = new List<KeywordState>(26);
+
+  /**
+   * The initial state in the state machine.
+   */
+  static final KeywordState KEYWORD_STATE = _createKeywordStateTable();
+
+  /**
+   * A table mapping characters to the states to which those characters will
+   * transition. (The index into the array is the offset from the character
+   * `'a'` to the transitioning character.)
+   */
+  final List<KeywordState> _table;
+
+  /**
+   * The keyword that is recognized by this state, or `null` if this state is
+   * not a terminal state.
+   */
+  Keyword _keyword;
+
+  /**
+   * Initialize a newly created state to have the given transitions and to
+   * recognize the keyword with the given [syntax].
+   */
+  KeywordState(this._table, String syntax) {
+    this._keyword = (syntax == null) ? null : Keyword.keywords[syntax];
+  }
+
+  /**
+   * Return the keyword that was recognized by this state, or `null` if this
+   * state does not recognized a keyword.
+   */
+  Keyword keyword() => _keyword;
+
+  /**
+   * Return the state that follows this state on a transition of the given
+   * [character], or `null` if there is no valid state reachable from this state
+   * with such a transition.
+   */
+  KeywordState next(int character) => _table[character - 0x61];
+
+  /**
+   * Create the next state in the state machine where we have already recognized
+   * the subset of strings in the given array of [strings] starting at the given
+   * [offset] and having the given [length]. All of these strings have a common
+   * prefix and the next character is at the given [start] index.
+   */
+  static KeywordState _computeKeywordStateTable(
+      int start, List<String> strings, int offset, int length) {
+    List<KeywordState> result = new List<KeywordState>(26);
+    assert(length != 0);
+    int chunk = 0x0;
+    int chunkStart = -1;
+    bool isLeaf = false;
+    for (int i = offset; i < offset + length; i++) {
+      if (strings[i].length == start) {
+        isLeaf = true;
+      }
+      if (strings[i].length > start) {
+        int c = strings[i].codeUnitAt(start);
+        if (chunk != c) {
+          if (chunkStart != -1) {
+            result[chunk - 0x61] = _computeKeywordStateTable(
+                start + 1, strings, chunkStart, i - chunkStart);
+          }
+          chunkStart = i;
+          chunk = c;
+        }
+      }
+    }
+    if (chunkStart != -1) {
+      assert(result[chunk - 0x61] == null);
+      result[chunk - 0x61] = _computeKeywordStateTable(
+          start + 1, strings, chunkStart, offset + length - chunkStart);
+    } else {
+      assert(length == 1);
+      return new KeywordState(_EMPTY_TABLE, strings[offset]);
+    }
+    if (isLeaf) {
+      return new KeywordState(result, strings[offset]);
+    } else {
+      return new KeywordState(result, null);
+    }
+  }
+
+  /**
+   * Create and return the initial state in the state machine.
+   */
+  static KeywordState _createKeywordStateTable() {
+    List<Keyword> values = Keyword.values;
+    List<String> strings = new List<String>(values.length);
+    for (int i = 0; i < values.length; i++) {
+      strings[i] = values[i].syntax;
+    }
+    strings.sort();
+    return _computeKeywordStateTable(0, strings, 0, strings.length);
+  }
+}
+
+/**
+ * A token representing a keyword in the language.
+ */
+class KeywordToken extends Token {
+  /**
+   * The keyword being represented by this token.
+   */
+  final Keyword keyword;
+
+  /**
+   * Initialize a newly created token to represent the given [keyword] at the
+   * given [offset].
+   */
+  KeywordToken(this.keyword, int offset) : super(TokenType.KEYWORD, offset);
+
+  @override
+  String get lexeme => keyword.syntax;
+
+  @override
+  Token copy() => new KeywordToken(keyword, offset);
+
+  @override
+  Keyword value() => keyword;
+}
+
+/**
+ * A keyword token that is preceded by comments.
+ */
+class KeywordTokenWithComment extends KeywordToken {
+  /**
+   * The first comment in the list of comments that precede this token.
+   */
+  CommentToken _precedingComment;
+
+  /**
+   * Initialize a newly created token to to represent the given [keyword] at the
+   * given [offset] and to be preceded by the comments reachable from the given
+   * [comment].
+   */
+  KeywordTokenWithComment(Keyword keyword, int offset, this._precedingComment)
+      : super(keyword, offset) {
+    _setCommentParent(_precedingComment);
+  }
+
+  CommentToken get precedingComments => _precedingComment;
+
+  void set precedingComments(CommentToken comment) {
+    _precedingComment = comment;
+    _setCommentParent(_precedingComment);
+  }
+
+  @override
+  void applyDelta(int delta) {
+    super.applyDelta(delta);
+    Token token = precedingComments;
+    while (token != null) {
+      token.applyDelta(delta);
+      token = token.next;
+    }
+  }
+
+  @override
+  Token copy() => new KeywordTokenWithComment(
+      keyword, offset, copyComments(precedingComments));
+}
+
+/**
+ * The class `Scanner` implements a scanner for Dart code.
+ *
+ * The lexical structure of Dart is ambiguous without knowledge of the context
+ * in which a token is being scanned. For example, without context we cannot
+ * determine whether source of the form "<<" should be scanned as a single
+ * left-shift operator or as two left angle brackets. This scanner does not have
+ * any context, so it always resolves such conflicts by scanning the longest
+ * possible token.
+ */
+class Scanner {
+  /**
+   * The source being scanned.
+   */
+  final Source source;
+
+  /**
+   * The reader used to access the characters in the source.
+   */
+  final CharacterReader _reader;
+
+  /**
+   * The error listener that will be informed of any errors that are found
+   * during the scan.
+   */
+  final AnalysisErrorListener _errorListener;
+
+  /**
+   * The flag specifying whether documentation comments should be parsed.
+   */
+  bool _preserveComments = true;
+
+  /**
+   * The token pointing to the head of the linked list of tokens.
+   */
+  Token _tokens;
+
+  /**
+   * The last token that was scanned.
+   */
+  Token _tail;
+
+  /**
+   * The first token in the list of comment tokens found since the last
+   * non-comment token.
+   */
+  Token _firstComment;
+
+  /**
+   * The last token in the list of comment tokens found since the last
+   * non-comment token.
+   */
+  Token _lastComment;
+
+  /**
+   * The index of the first character of the current token.
+   */
+  int _tokenStart = 0;
+
+  /**
+   * A list containing the offsets of the first character of each line in the
+   * source code.
+   */
+  List<int> _lineStarts = new List<int>();
+
+  /**
+   * A list, treated something like a stack, of tokens representing the
+   * beginning of a matched pair. It is used to pair the end tokens with the
+   * begin tokens.
+   */
+  List<BeginToken> _groupingStack = new List<BeginToken>();
+
+  /**
+   * The index of the last item in the [_groupingStack], or `-1` if the stack is
+   * empty.
+   */
+  int _stackEnd = -1;
+
+  /**
+   * A flag indicating whether any unmatched groups were found during the parse.
+   */
+  bool _hasUnmatchedGroups = false;
+
+  /**
+   * A flag indicating whether null-aware operators ('?.', '??', and '??=')
+   * should be tokenized.
+   */
+  bool enableNullAwareOperators = false;
+
+  /**
+   * Initialize a newly created scanner to scan characters from the given
+   * [source]. The given character [_reader] will be used to read the characters
+   * in the source. The given [_errorListener] will be informed of any errors
+   * that are found.
+   */
+  Scanner(this.source, this._reader, this._errorListener) {
+    _tokens = new Token(TokenType.EOF, -1);
+    _tokens.setNext(_tokens);
+    _tail = _tokens;
+    _tokenStart = -1;
+    _lineStarts.add(0);
+  }
+
+  /**
+   * Return the first token in the token stream that was scanned.
+   */
+  Token get firstToken => _tokens.next;
+
+  /**
+   * Return `true` if any unmatched groups were found during the parse.
+   */
+  bool get hasUnmatchedGroups => _hasUnmatchedGroups;
+
+  /**
+   * Return an array containing the offsets of the first character of each line
+   * in the source code.
+   */
+  List<int> get lineStarts => _lineStarts;
+
+  /**
+   * Set whether documentation tokens should be preserved.
+   */
+  void set preserveComments(bool preserveComments) {
+    this._preserveComments = preserveComments;
+  }
+
+  /**
+   * Return the last token that was scanned.
+   */
+  Token get tail => _tail;
+
+  /**
+   * Append the given [token] to the end of the token stream being scanned. This
+   * method is intended to be used by subclasses that copy existing tokens and
+   * should not normally be used because it will fail to correctly associate any
+   * comments with the token being passed in.
+   */
+  void appendToken(Token token) {
+    _tail = _tail.setNext(token);
+  }
+
+  int bigSwitch(int next) {
+    _beginToken();
+    if (next == 0xD) {
+      // '\r'
+      next = _reader.advance();
+      if (next == 0xA) {
+        // '\n'
+        next = _reader.advance();
+      }
+      recordStartOfLine();
+      return next;
+    } else if (next == 0xA) {
+      // '\n'
+      next = _reader.advance();
+      recordStartOfLine();
+      return next;
+    } else if (next == 0x9 || next == 0x20) {
+      // '\t' || ' '
+      return _reader.advance();
+    }
+    if (next == 0x72) {
+      // 'r'
+      int peek = _reader.peek();
+      if (peek == 0x22 || peek == 0x27) {
+        // '"' || "'"
+        int start = _reader.offset;
+        return _tokenizeString(_reader.advance(), start, true);
+      }
+    }
+    if (0x61 <= next && next <= 0x7A) {
+      // 'a'-'z'
+      return _tokenizeKeywordOrIdentifier(next, true);
+    }
+    if ((0x41 <= next && next <= 0x5A) || next == 0x5F || next == 0x24) {
+      // 'A'-'Z' || '_' || '$'
+      return _tokenizeIdentifier(next, _reader.offset, true);
+    }
+    if (next == 0x3C) {
+      // '<'
+      return _tokenizeLessThan(next);
+    }
+    if (next == 0x3E) {
+      // '>'
+      return _tokenizeGreaterThan(next);
+    }
+    if (next == 0x3D) {
+      // '='
+      return _tokenizeEquals(next);
+    }
+    if (next == 0x21) {
+      // '!'
+      return _tokenizeExclamation(next);
+    }
+    if (next == 0x2B) {
+      // '+'
+      return _tokenizePlus(next);
+    }
+    if (next == 0x2D) {
+      // '-'
+      return _tokenizeMinus(next);
+    }
+    if (next == 0x2A) {
+      // '*'
+      return _tokenizeMultiply(next);
+    }
+    if (next == 0x25) {
+      // '%'
+      return _tokenizePercent(next);
+    }
+    if (next == 0x26) {
+      // '&'
+      return _tokenizeAmpersand(next);
+    }
+    if (next == 0x7C) {
+      // '|'
+      return _tokenizeBar(next);
+    }
+    if (next == 0x5E) {
+      // '^'
+      return _tokenizeCaret(next);
+    }
+    if (next == 0x5B) {
+      // '['
+      return _tokenizeOpenSquareBracket(next);
+    }
+    if (next == 0x7E) {
+      // '~'
+      return _tokenizeTilde(next);
+    }
+    if (next == 0x5C) {
+      // '\\'
+      _appendTokenOfType(TokenType.BACKSLASH);
+      return _reader.advance();
+    }
+    if (next == 0x23) {
+      // '#'
+      return _tokenizeTag(next);
+    }
+    if (next == 0x28) {
+      // '('
+      _appendBeginToken(TokenType.OPEN_PAREN);
+      return _reader.advance();
+    }
+    if (next == 0x29) {
+      // ')'
+      _appendEndToken(TokenType.CLOSE_PAREN, TokenType.OPEN_PAREN);
+      return _reader.advance();
+    }
+    if (next == 0x2C) {
+      // ','
+      _appendTokenOfType(TokenType.COMMA);
+      return _reader.advance();
+    }
+    if (next == 0x3A) {
+      // ':'
+      _appendTokenOfType(TokenType.COLON);
+      return _reader.advance();
+    }
+    if (next == 0x3B) {
+      // ';'
+      _appendTokenOfType(TokenType.SEMICOLON);
+      return _reader.advance();
+    }
+    if (next == 0x3F) {
+      // '?'
+      return _tokenizeQuestion();
+    }
+    if (next == 0x5D) {
+      // ']'
+      _appendEndToken(
+          TokenType.CLOSE_SQUARE_BRACKET, TokenType.OPEN_SQUARE_BRACKET);
+      return _reader.advance();
+    }
+    if (next == 0x60) {
+      // '`'
+      _appendTokenOfType(TokenType.BACKPING);
+      return _reader.advance();
+    }
+    if (next == 0x7B) {
+      // '{'
+      _appendBeginToken(TokenType.OPEN_CURLY_BRACKET);
+      return _reader.advance();
+    }
+    if (next == 0x7D) {
+      // '}'
+      _appendEndToken(
+          TokenType.CLOSE_CURLY_BRACKET, TokenType.OPEN_CURLY_BRACKET);
+      return _reader.advance();
+    }
+    if (next == 0x2F) {
+      // '/'
+      return _tokenizeSlashOrComment(next);
+    }
+    if (next == 0x40) {
+      // '@'
+      _appendTokenOfType(TokenType.AT);
+      return _reader.advance();
+    }
+    if (next == 0x22 || next == 0x27) {
+      // '"' || "'"
+      return _tokenizeString(next, _reader.offset, false);
+    }
+    if (next == 0x2E) {
+      // '.'
+      return _tokenizeDotOrNumber(next);
+    }
+    if (next == 0x30) {
+      // '0'
+      return _tokenizeHexOrNumber(next);
+    }
+    if (0x31 <= next && next <= 0x39) {
+      // '1'-'9'
+      return _tokenizeNumber(next);
+    }
+    if (next == -1) {
+      // EOF
+      return -1;
+    }
+    _reportError(ScannerErrorCode.ILLEGAL_CHARACTER, [next]);
+    return _reader.advance();
+  }
+
+  /**
+   * Record the fact that we are at the beginning of a new line in the source.
+   */
+  void recordStartOfLine() {
+    _lineStarts.add(_reader.offset);
+  }
+
+  /**
+   * Record that the source begins on the given [line] and [column] at the
+   * current offset as given by the reader. Both the line and the column are
+   * one-based indexes. The line starts for lines before the given line will not
+   * be correct.
+   *
+   * This method must be invoked at most one time and must be invoked before
+   * scanning begins. The values provided must be sensible. The results are
+   * undefined if these conditions are violated.
+   */
+  void setSourceStart(int line, int column) {
+    int offset = _reader.offset;
+    if (line < 1 || column < 1 || offset < 0 || (line + column - 2) >= offset) {
+      return;
+    }
+    for (int i = 2; i < line; i++) {
+      _lineStarts.add(1);
+    }
+    _lineStarts.add(offset - column + 1);
+  }
+
+  /**
+   * Scan the source code to produce a list of tokens representing the source,
+   * and return the first token in the list of tokens that were produced.
+   */
+  Token tokenize() {
+    int next = _reader.advance();
+    while (next != -1) {
+      next = bigSwitch(next);
+    }
+    _appendEofToken();
+    return firstToken;
+  }
+
+  void _appendBeginToken(TokenType type) {
+    BeginToken token;
+    if (_firstComment == null) {
+      token = new BeginToken(type, _tokenStart);
+    } else {
+      token = new BeginTokenWithComment(type, _tokenStart, _firstComment);
+      _firstComment = null;
+      _lastComment = null;
+    }
+    _tail = _tail.setNext(token);
+    _groupingStack.add(token);
+    _stackEnd++;
+  }
+
+  void _appendCommentToken(TokenType type, String value) {
+    // Ignore comment tokens if client specified that it doesn't need them.
+    if (!_preserveComments) {
+      return;
+    }
+    // OK, remember comment tokens.
+    CommentToken token;
+    if (_isDocumentationComment(value)) {
+      token = new DocumentationCommentToken(type, value, _tokenStart);
+    } else {
+      token = new CommentToken(type, value, _tokenStart);
+    }
+    if (_firstComment == null) {
+      _firstComment = token;
+      _lastComment = _firstComment;
+    } else {
+      _lastComment = _lastComment.setNext(token);
+    }
+  }
+
+  void _appendEndToken(TokenType type, TokenType beginType) {
+    Token token;
+    if (_firstComment == null) {
+      token = new Token(type, _tokenStart);
+    } else {
+      token = new TokenWithComment(type, _tokenStart, _firstComment);
+      _firstComment = null;
+      _lastComment = null;
+    }
+    _tail = _tail.setNext(token);
+    if (_stackEnd >= 0) {
+      BeginToken begin = _groupingStack[_stackEnd];
+      if (begin.type == beginType) {
+        begin.endToken = token;
+        _groupingStack.removeAt(_stackEnd--);
+      }
+    }
+  }
+
+  void _appendEofToken() {
+    Token eofToken;
+    if (_firstComment == null) {
+      eofToken = new Token(TokenType.EOF, _reader.offset + 1);
+    } else {
+      eofToken = new TokenWithComment(
+          TokenType.EOF, _reader.offset + 1, _firstComment);
+      _firstComment = null;
+      _lastComment = null;
+    }
+    // The EOF token points to itself so that there is always infinite
+    // look-ahead.
+    eofToken.setNext(eofToken);
+    _tail = _tail.setNext(eofToken);
+    if (_stackEnd >= 0) {
+      _hasUnmatchedGroups = true;
+      // TODO(brianwilkerson) Fix the ungrouped tokens?
+    }
+  }
+
+  void _appendKeywordToken(Keyword keyword) {
+    if (_firstComment == null) {
+      _tail = _tail.setNext(new KeywordToken(keyword, _tokenStart));
+    } else {
+      _tail = _tail.setNext(
+          new KeywordTokenWithComment(keyword, _tokenStart, _firstComment));
+      _firstComment = null;
+      _lastComment = null;
+    }
+  }
+
+  void _appendStringToken(TokenType type, String value) {
+    if (_firstComment == null) {
+      _tail = _tail.setNext(new StringToken(type, value, _tokenStart));
+    } else {
+      _tail = _tail.setNext(
+          new StringTokenWithComment(type, value, _tokenStart, _firstComment));
+      _firstComment = null;
+      _lastComment = null;
+    }
+  }
+
+  void _appendStringTokenWithOffset(TokenType type, String value, int offset) {
+    if (_firstComment == null) {
+      _tail = _tail.setNext(new StringToken(type, value, _tokenStart + offset));
+    } else {
+      _tail = _tail.setNext(new StringTokenWithComment(
+          type, value, _tokenStart + offset, _firstComment));
+      _firstComment = null;
+      _lastComment = null;
+    }
+  }
+
+  void _appendTokenOfType(TokenType type) {
+    if (_firstComment == null) {
+      _tail = _tail.setNext(new Token(type, _tokenStart));
+    } else {
+      _tail =
+          _tail.setNext(new TokenWithComment(type, _tokenStart, _firstComment));
+      _firstComment = null;
+      _lastComment = null;
+    }
+  }
+
+  void _appendTokenOfTypeWithOffset(TokenType type, int offset) {
+    if (_firstComment == null) {
+      _tail = _tail.setNext(new Token(type, offset));
+    } else {
+      _tail = _tail.setNext(new TokenWithComment(type, offset, _firstComment));
+      _firstComment = null;
+      _lastComment = null;
+    }
+  }
+
+  void _beginToken() {
+    _tokenStart = _reader.offset;
+  }
+
+  /**
+   * Return the beginning token corresponding to a closing brace that was found
+   * while scanning inside a string interpolation expression. Tokens that cannot
+   * be matched with the closing brace will be dropped from the stack.
+   */
+  BeginToken _findTokenMatchingClosingBraceInInterpolationExpression() {
+    while (_stackEnd >= 0) {
+      BeginToken begin = _groupingStack[_stackEnd];
+      if (begin.type == TokenType.OPEN_CURLY_BRACKET ||
+          begin.type == TokenType.STRING_INTERPOLATION_EXPRESSION) {
+        return begin;
+      }
+      _hasUnmatchedGroups = true;
+      _groupingStack.removeAt(_stackEnd--);
+    }
+    //
+    // We should never get to this point because we wouldn't be inside a string
+    // interpolation expression unless we had previously found the start of the
+    // expression.
+    //
+    return null;
+  }
+
+  /**
+   * Report an error at the current offset. The [errorCode] is the error code
+   * indicating the nature of the error. The [arguments] are any arguments
+   * needed to complete the error message
+   */
+  void _reportError(ScannerErrorCode errorCode, [List<Object> arguments]) {
+    _errorListener.onError(new AnalysisError.con2(
+        source, _reader.offset, 1, errorCode, arguments));
+  }
+
+  int _select(int choice, TokenType yesType, TokenType noType) {
+    int next = _reader.advance();
+    if (next == choice) {
+      _appendTokenOfType(yesType);
+      return _reader.advance();
+    } else {
+      _appendTokenOfType(noType);
+      return next;
+    }
+  }
+
+  int _selectWithOffset(
+      int choice, TokenType yesType, TokenType noType, int offset) {
+    int next = _reader.advance();
+    if (next == choice) {
+      _appendTokenOfTypeWithOffset(yesType, offset);
+      return _reader.advance();
+    } else {
+      _appendTokenOfTypeWithOffset(noType, offset);
+      return next;
+    }
+  }
+
+  int _tokenizeAmpersand(int next) {
+    // && &= &
+    next = _reader.advance();
+    if (next == 0x26) {
+      _appendTokenOfType(TokenType.AMPERSAND_AMPERSAND);
+      return _reader.advance();
+    } else if (next == 0x3D) {
+      _appendTokenOfType(TokenType.AMPERSAND_EQ);
+      return _reader.advance();
+    } else {
+      _appendTokenOfType(TokenType.AMPERSAND);
+      return next;
+    }
+  }
+
+  int _tokenizeBar(int next) {
+    // | || |=
+    next = _reader.advance();
+    if (next == 0x7C) {
+      _appendTokenOfType(TokenType.BAR_BAR);
+      return _reader.advance();
+    } else if (next == 0x3D) {
+      _appendTokenOfType(TokenType.BAR_EQ);
+      return _reader.advance();
+    } else {
+      _appendTokenOfType(TokenType.BAR);
+      return next;
+    }
+  }
+
+  int _tokenizeCaret(int next) =>
+      _select(0x3D, TokenType.CARET_EQ, TokenType.CARET);
+
+  int _tokenizeDotOrNumber(int next) {
+    int start = _reader.offset;
+    next = _reader.advance();
+    if (0x30 <= next && next <= 0x39) {
+      return _tokenizeFractionPart(next, start);
+    } else if (0x2E == next) {
+      return _select(
+          0x2E, TokenType.PERIOD_PERIOD_PERIOD, TokenType.PERIOD_PERIOD);
+    } else {
+      _appendTokenOfType(TokenType.PERIOD);
+      return next;
+    }
+  }
+
+  int _tokenizeEquals(int next) {
+    // = == =>
+    next = _reader.advance();
+    if (next == 0x3D) {
+      _appendTokenOfType(TokenType.EQ_EQ);
+      return _reader.advance();
+    } else if (next == 0x3E) {
+      _appendTokenOfType(TokenType.FUNCTION);
+      return _reader.advance();
+    }
+    _appendTokenOfType(TokenType.EQ);
+    return next;
+  }
+
+  int _tokenizeExclamation(int next) {
+    // ! !=
+    next = _reader.advance();
+    if (next == 0x3D) {
+      _appendTokenOfType(TokenType.BANG_EQ);
+      return _reader.advance();
+    }
+    _appendTokenOfType(TokenType.BANG);
+    return next;
+  }
+
+  int _tokenizeExponent(int next) {
+    if (next == 0x2B || next == 0x2D) {
+      next = _reader.advance();
+    }
+    bool hasDigits = false;
+    while (true) {
+      if (0x30 <= next && next <= 0x39) {
+        hasDigits = true;
+      } else {
+        if (!hasDigits) {
+          _reportError(ScannerErrorCode.MISSING_DIGIT);
+        }
+        return next;
+      }
+      next = _reader.advance();
+    }
+  }
+
+  int _tokenizeFractionPart(int next, int start) {
+    bool done = false;
+    bool hasDigit = false;
+    LOOP: while (!done) {
+      if (0x30 <= next && next <= 0x39) {
+        hasDigit = true;
+      } else if (0x65 == next || 0x45 == next) {
+        hasDigit = true;
+        next = _tokenizeExponent(_reader.advance());
+        done = true;
+        continue LOOP;
+      } else {
+        done = true;
+        continue LOOP;
+      }
+      next = _reader.advance();
+    }
+    if (!hasDigit) {
+      _appendStringToken(TokenType.INT, _reader.getString(start, -2));
+      if (0x2E == next) {
+        return _selectWithOffset(0x2E, TokenType.PERIOD_PERIOD_PERIOD,
+            TokenType.PERIOD_PERIOD, _reader.offset - 1);
+      }
+      _appendTokenOfTypeWithOffset(TokenType.PERIOD, _reader.offset - 1);
+      return bigSwitch(next);
+    }
+    _appendStringToken(
+        TokenType.DOUBLE, _reader.getString(start, next < 0 ? 0 : -1));
+    return next;
+  }
+
+  int _tokenizeGreaterThan(int next) {
+    // > >= >> >>=
+    next = _reader.advance();
+    if (0x3D == next) {
+      _appendTokenOfType(TokenType.GT_EQ);
+      return _reader.advance();
+    } else if (0x3E == next) {
+      next = _reader.advance();
+      if (0x3D == next) {
+        _appendTokenOfType(TokenType.GT_GT_EQ);
+        return _reader.advance();
+      } else {
+        _appendTokenOfType(TokenType.GT_GT);
+        return next;
+      }
+    } else {
+      _appendTokenOfType(TokenType.GT);
+      return next;
+    }
+  }
+
+  int _tokenizeHex(int next) {
+    int start = _reader.offset - 1;
+    bool hasDigits = false;
+    while (true) {
+      next = _reader.advance();
+      if ((0x30 <= next && next <= 0x39) ||
+          (0x41 <= next && next <= 0x46) ||
+          (0x61 <= next && next <= 0x66)) {
+        hasDigits = true;
+      } else {
+        if (!hasDigits) {
+          _reportError(ScannerErrorCode.MISSING_HEX_DIGIT);
+        }
+        _appendStringToken(
+            TokenType.HEXADECIMAL, _reader.getString(start, next < 0 ? 0 : -1));
+        return next;
+      }
+    }
+  }
+
+  int _tokenizeHexOrNumber(int next) {
+    int x = _reader.peek();
+    if (x == 0x78 || x == 0x58) {
+      _reader.advance();
+      return _tokenizeHex(x);
+    }
+    return _tokenizeNumber(next);
+  }
+
+  int _tokenizeIdentifier(int next, int start, bool allowDollar) {
+    while ((0x61 <= next && next <= 0x7A) ||
+        (0x41 <= next && next <= 0x5A) ||
+        (0x30 <= next && next <= 0x39) ||
+        next == 0x5F ||
+        (next == 0x24 && allowDollar)) {
+      next = _reader.advance();
+    }
+    _appendStringToken(
+        TokenType.IDENTIFIER, _reader.getString(start, next < 0 ? 0 : -1));
+    return next;
+  }
+
+  int _tokenizeInterpolatedExpression(int next, int start) {
+    _appendBeginToken(TokenType.STRING_INTERPOLATION_EXPRESSION);
+    next = _reader.advance();
+    while (next != -1) {
+      if (next == 0x7D) {
+        BeginToken begin =
+            _findTokenMatchingClosingBraceInInterpolationExpression();
+        if (begin == null) {
+          _beginToken();
+          _appendTokenOfType(TokenType.CLOSE_CURLY_BRACKET);
+          next = _reader.advance();
+          _beginToken();
+          return next;
+        } else if (begin.type == TokenType.OPEN_CURLY_BRACKET) {
+          _beginToken();
+          _appendEndToken(
+              TokenType.CLOSE_CURLY_BRACKET, TokenType.OPEN_CURLY_BRACKET);
+          next = _reader.advance();
+          _beginToken();
+        } else if (begin.type == TokenType.STRING_INTERPOLATION_EXPRESSION) {
+          _beginToken();
+          _appendEndToken(TokenType.CLOSE_CURLY_BRACKET,
+              TokenType.STRING_INTERPOLATION_EXPRESSION);
+          next = _reader.advance();
+          _beginToken();
+          return next;
+        }
+      } else {
+        next = bigSwitch(next);
+      }
+    }
+    return next;
+  }
+
+  int _tokenizeInterpolatedIdentifier(int next, int start) {
+    _appendStringTokenWithOffset(
+        TokenType.STRING_INTERPOLATION_IDENTIFIER, "\$", 0);
+    if ((0x41 <= next && next <= 0x5A) ||
+        (0x61 <= next && next <= 0x7A) ||
+        next == 0x5F) {
+      _beginToken();
+      next = _tokenizeKeywordOrIdentifier(next, false);
+    }
+    _beginToken();
+    return next;
+  }
+
+  int _tokenizeKeywordOrIdentifier(int next, bool allowDollar) {
+    KeywordState state = KeywordState.KEYWORD_STATE;
+    int start = _reader.offset;
+    while (state != null && 0x61 <= next && next <= 0x7A) {
+      state = state.next(next);
+      next = _reader.advance();
+    }
+    if (state == null || state.keyword() == null) {
+      return _tokenizeIdentifier(next, start, allowDollar);
+    }
+    if ((0x41 <= next && next <= 0x5A) ||
+        (0x30 <= next && next <= 0x39) ||
+        next == 0x5F ||
+        next == 0x24) {
+      return _tokenizeIdentifier(next, start, allowDollar);
+    } else if (next < 128) {
+      _appendKeywordToken(state.keyword());
+      return next;
+    } else {
+      return _tokenizeIdentifier(next, start, allowDollar);
+    }
+  }
+
+  int _tokenizeLessThan(int next) {
+    // < <= << <<=
+    next = _reader.advance();
+    if (0x3D == next) {
+      _appendTokenOfType(TokenType.LT_EQ);
+      return _reader.advance();
+    } else if (0x3C == next) {
+      return _select(0x3D, TokenType.LT_LT_EQ, TokenType.LT_LT);
+    } else {
+      _appendTokenOfType(TokenType.LT);
+      return next;
+    }
+  }
+
+  int _tokenizeMinus(int next) {
+    // - -- -=
+    next = _reader.advance();
+    if (next == 0x2D) {
+      _appendTokenOfType(TokenType.MINUS_MINUS);
+      return _reader.advance();
+    } else if (next == 0x3D) {
+      _appendTokenOfType(TokenType.MINUS_EQ);
+      return _reader.advance();
+    } else {
+      _appendTokenOfType(TokenType.MINUS);
+      return next;
+    }
+  }
+
+  int _tokenizeMultiLineComment(int next) {
+    int nesting = 1;
+    next = _reader.advance();
+    while (true) {
+      if (-1 == next) {
+        _reportError(ScannerErrorCode.UNTERMINATED_MULTI_LINE_COMMENT);
+        _appendCommentToken(
+            TokenType.MULTI_LINE_COMMENT, _reader.getString(_tokenStart, 0));
+        return next;
+      } else if (0x2A == next) {
+        next = _reader.advance();
+        if (0x2F == next) {
+          --nesting;
+          if (0 == nesting) {
+            _appendCommentToken(TokenType.MULTI_LINE_COMMENT,
+                _reader.getString(_tokenStart, 0));
+            return _reader.advance();
+          } else {
+            next = _reader.advance();
+          }
+        }
+      } else if (0x2F == next) {
+        next = _reader.advance();
+        if (0x2A == next) {
+          next = _reader.advance();
+          ++nesting;
+        }
+      } else if (next == 0xD) {
+        next = _reader.advance();
+        if (next == 0xA) {
+          next = _reader.advance();
+        }
+        recordStartOfLine();
+      } else if (next == 0xA) {
+        recordStartOfLine();
+        next = _reader.advance();
+      } else {
+        next = _reader.advance();
+      }
+    }
+  }
+
+  int _tokenizeMultiLineRawString(int quoteChar, int start) {
+    int next = _reader.advance();
+    outer: while (next != -1) {
+      while (next != quoteChar) {
+        if (next == -1) {
+          break outer;
+        } else if (next == 0xD) {
+          next = _reader.advance();
+          if (next == 0xA) {
+            next = _reader.advance();
+          }
+          recordStartOfLine();
+        } else if (next == 0xA) {
+          next = _reader.advance();
+          recordStartOfLine();
+        } else {
+          next = _reader.advance();
+        }
+      }
+      next = _reader.advance();
+      if (next == quoteChar) {
+        next = _reader.advance();
+        if (next == quoteChar) {
+          _appendStringToken(TokenType.STRING, _reader.getString(start, 0));
+          return _reader.advance();
+        }
+      }
+    }
+    _reportError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL);
+    _appendStringToken(TokenType.STRING, _reader.getString(start, 0));
+    return _reader.advance();
+  }
+
+  int _tokenizeMultiLineString(int quoteChar, int start, bool raw) {
+    if (raw) {
+      return _tokenizeMultiLineRawString(quoteChar, start);
+    }
+    int next = _reader.advance();
+    while (next != -1) {
+      if (next == 0x24) {
+        _appendStringToken(TokenType.STRING, _reader.getString(start, -1));
+        next = _tokenizeStringInterpolation(start);
+        _beginToken();
+        start = _reader.offset;
+        continue;
+      }
+      if (next == quoteChar) {
+        next = _reader.advance();
+        if (next == quoteChar) {
+          next = _reader.advance();
+          if (next == quoteChar) {
+            _appendStringToken(TokenType.STRING, _reader.getString(start, 0));
+            return _reader.advance();
+          }
+        }
+        continue;
+      }
+      if (next == 0x5C) {
+        next = _reader.advance();
+        if (next == -1) {
+          break;
+        }
+        if (next == 0xD) {
+          next = _reader.advance();
+          if (next == 0xA) {
+            next = _reader.advance();
+          }
+          recordStartOfLine();
+        } else if (next == 0xA) {
+          recordStartOfLine();
+          next = _reader.advance();
+        } else {
+          next = _reader.advance();
+        }
+      } else if (next == 0xD) {
+        next = _reader.advance();
+        if (next == 0xA) {
+          next = _reader.advance();
+        }
+        recordStartOfLine();
+      } else if (next == 0xA) {
+        recordStartOfLine();
+        next = _reader.advance();
+      } else {
+        next = _reader.advance();
+      }
+    }
+    _reportError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL);
+    if (start == _reader.offset) {
+      _appendStringTokenWithOffset(TokenType.STRING, "", 1);
+    } else {
+      _appendStringToken(TokenType.STRING, _reader.getString(start, 0));
+    }
+    return _reader.advance();
+  }
+
+  int _tokenizeMultiply(int next) =>
+      _select(0x3D, TokenType.STAR_EQ, TokenType.STAR);
+
+  int _tokenizeNumber(int next) {
+    int start = _reader.offset;
+    while (true) {
+      next = _reader.advance();
+      if (0x30 <= next && next <= 0x39) {
+        continue;
+      } else if (next == 0x2E) {
+        return _tokenizeFractionPart(_reader.advance(), start);
+      } else if (next == 0x65 || next == 0x45) {
+        return _tokenizeFractionPart(next, start);
+      } else {
+        _appendStringToken(
+            TokenType.INT, _reader.getString(start, next < 0 ? 0 : -1));
+        return next;
+      }
+    }
+  }
+
+  int _tokenizeOpenSquareBracket(int next) {
+    // [ []  []=
+    next = _reader.advance();
+    if (next == 0x5D) {
+      return _select(0x3D, TokenType.INDEX_EQ, TokenType.INDEX);
+    } else {
+      _appendBeginToken(TokenType.OPEN_SQUARE_BRACKET);
+      return next;
+    }
+  }
+
+  int _tokenizePercent(int next) =>
+      _select(0x3D, TokenType.PERCENT_EQ, TokenType.PERCENT);
+
+  int _tokenizePlus(int next) {
+    // + ++ +=
+    next = _reader.advance();
+    if (0x2B == next) {
+      _appendTokenOfType(TokenType.PLUS_PLUS);
+      return _reader.advance();
+    } else if (0x3D == next) {
+      _appendTokenOfType(TokenType.PLUS_EQ);
+      return _reader.advance();
+    } else {
+      _appendTokenOfType(TokenType.PLUS);
+      return next;
+    }
+  }
+
+  int _tokenizeQuestion() {
+    // ? ?. ?? ??=
+    int next = _reader.advance();
+    if (enableNullAwareOperators && next == 0x2E) {
+      // '.'
+      _appendTokenOfType(TokenType.QUESTION_PERIOD);
+      return _reader.advance();
+    } else if (enableNullAwareOperators && next == 0x3F) {
+      // '?'
+      next = _reader.advance();
+      if (next == 0x3D) {
+        // '='
+        _appendTokenOfType(TokenType.QUESTION_QUESTION_EQ);
+        return _reader.advance();
+      } else {
+        _appendTokenOfType(TokenType.QUESTION_QUESTION);
+        return next;
+      }
+    } else {
+      _appendTokenOfType(TokenType.QUESTION);
+      return next;
+    }
+  }
+
+  int _tokenizeSingleLineComment(int next) {
+    while (true) {
+      next = _reader.advance();
+      if (-1 == next) {
+        _appendCommentToken(
+            TokenType.SINGLE_LINE_COMMENT, _reader.getString(_tokenStart, 0));
+        return next;
+      } else if (0xA == next || 0xD == next) {
+        _appendCommentToken(
+            TokenType.SINGLE_LINE_COMMENT, _reader.getString(_tokenStart, -1));
+        return next;
+      }
+    }
+  }
+
+  int _tokenizeSingleLineRawString(int next, int quoteChar, int start) {
+    next = _reader.advance();
+    while (next != -1) {
+      if (next == quoteChar) {
+        _appendStringToken(TokenType.STRING, _reader.getString(start, 0));
+        return _reader.advance();
+      } else if (next == 0xD || next == 0xA) {
+        _reportError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL);
+        _appendStringToken(TokenType.STRING, _reader.getString(start, -1));
+        return _reader.advance();
+      }
+      next = _reader.advance();
+    }
+    _reportError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL);
+    _appendStringToken(TokenType.STRING, _reader.getString(start, 0));
+    return _reader.advance();
+  }
+
+  int _tokenizeSingleLineString(int next, int quoteChar, int start) {
+    while (next != quoteChar) {
+      if (next == 0x5C) {
+        next = _reader.advance();
+      } else if (next == 0x24) {
+        _appendStringToken(TokenType.STRING, _reader.getString(start, -1));
+        next = _tokenizeStringInterpolation(start);
+        _beginToken();
+        start = _reader.offset;
+        continue;
+      }
+      if (next <= 0xD && (next == 0xA || next == 0xD || next == -1)) {
+        _reportError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL);
+        if (start == _reader.offset) {
+          _appendStringTokenWithOffset(TokenType.STRING, "", 1);
+        } else if (next == -1) {
+          _appendStringToken(TokenType.STRING, _reader.getString(start, 0));
+        } else {
+          _appendStringToken(TokenType.STRING, _reader.getString(start, -1));
+        }
+        return _reader.advance();
+      }
+      next = _reader.advance();
+    }
+    _appendStringToken(TokenType.STRING, _reader.getString(start, 0));
+    return _reader.advance();
+  }
+
+  int _tokenizeSlashOrComment(int next) {
+    next = _reader.advance();
+    if (0x2A == next) {
+      return _tokenizeMultiLineComment(next);
+    } else if (0x2F == next) {
+      return _tokenizeSingleLineComment(next);
+    } else if (0x3D == next) {
+      _appendTokenOfType(TokenType.SLASH_EQ);
+      return _reader.advance();
+    } else {
+      _appendTokenOfType(TokenType.SLASH);
+      return next;
+    }
+  }
+
+  int _tokenizeString(int next, int start, bool raw) {
+    int quoteChar = next;
+    next = _reader.advance();
+    if (quoteChar == next) {
+      next = _reader.advance();
+      if (quoteChar == next) {
+        // Multiline string.
+        return _tokenizeMultiLineString(quoteChar, start, raw);
+      } else {
+        // Empty string.
+        _appendStringToken(TokenType.STRING, _reader.getString(start, -1));
+        return next;
+      }
+    }
+    if (raw) {
+      return _tokenizeSingleLineRawString(next, quoteChar, start);
+    } else {
+      return _tokenizeSingleLineString(next, quoteChar, start);
+    }
+  }
+
+  int _tokenizeStringInterpolation(int start) {
+    _beginToken();
+    int next = _reader.advance();
+    if (next == 0x7B) {
+      return _tokenizeInterpolatedExpression(next, start);
+    } else {
+      return _tokenizeInterpolatedIdentifier(next, start);
+    }
+  }
+
+  int _tokenizeTag(int next) {
+    // # or #!.*[\n\r]
+    if (_reader.offset == 0) {
+      if (_reader.peek() == 0x21) {
+        do {
+          next = _reader.advance();
+        } while (next != 0xA && next != 0xD && next > 0);
+        _appendStringToken(
+            TokenType.SCRIPT_TAG, _reader.getString(_tokenStart, 0));
+        return next;
+      }
+    }
+    _appendTokenOfType(TokenType.HASH);
+    return _reader.advance();
+  }
+
+  int _tokenizeTilde(int next) {
+    // ~ ~/ ~/=
+    next = _reader.advance();
+    if (next == 0x2F) {
+      return _select(0x3D, TokenType.TILDE_SLASH_EQ, TokenType.TILDE_SLASH);
+    } else {
+      _appendTokenOfType(TokenType.TILDE);
+      return next;
+    }
+  }
+
+  /**
+   * Checks if [value] is a single-line or multi-line comment.
+   */
+  static bool _isDocumentationComment(String value) {
+    return StringUtilities.startsWith3(value, 0, 0x2F, 0x2F, 0x2F) ||
+        StringUtilities.startsWith3(value, 0, 0x2F, 0x2A, 0x2A);
+  }
+}
+
+/**
+ * The error codes used for errors detected by the scanner.
+ */
+class ScannerErrorCode extends ErrorCode {
+  static const ScannerErrorCode ILLEGAL_CHARACTER =
+      const ScannerErrorCode('ILLEGAL_CHARACTER', "Illegal character {0}");
+
+  static const ScannerErrorCode MISSING_DIGIT =
+      const ScannerErrorCode('MISSING_DIGIT', "Decimal digit expected");
+
+  static const ScannerErrorCode MISSING_HEX_DIGIT =
+      const ScannerErrorCode('MISSING_HEX_DIGIT', "Hexidecimal digit expected");
+
+  static const ScannerErrorCode MISSING_QUOTE =
+      const ScannerErrorCode('MISSING_QUOTE', "Expected quote (' or \")");
+
+  static const ScannerErrorCode UNABLE_GET_CONTENT = const ScannerErrorCode(
+      'UNABLE_GET_CONTENT', "Unable to get content: {0}");
+
+  static const ScannerErrorCode UNTERMINATED_MULTI_LINE_COMMENT =
+      const ScannerErrorCode(
+          'UNTERMINATED_MULTI_LINE_COMMENT', "Unterminated multi-line comment");
+
+  static const ScannerErrorCode UNTERMINATED_STRING_LITERAL =
+      const ScannerErrorCode(
+          'UNTERMINATED_STRING_LITERAL', "Unterminated string literal");
+
+  /**
+   * Initialize a newly created error code to have the given [name]. The message
+   * associated with the error will be created from the given [message]
+   * template. The correction associated with the error will be created from the
+   * given [correction] template.
+   */
+  const ScannerErrorCode(String name, String message, [String correction])
+      : super(name, message, correction);
+
+  @override
+  ErrorSeverity get errorSeverity => ErrorSeverity.ERROR;
+
+  @override
+  ErrorType get type => ErrorType.SYNTACTIC_ERROR;
+}
+
+/**
+ * A token whose value is independent of it's type.
+ */
+class StringToken extends Token {
+  /**
+   * The lexeme represented by this token.
+   */
+  String _value;
+
+  /**
+   * Initialize a newly created token to represent a token of the given [type]
+   * with the given [value] at the given [offset].
+   */
+  StringToken(TokenType type, String value, int offset) : super(type, offset) {
+    this._value = StringUtilities.intern(value);
+  }
+
+  @override
+  String get lexeme => _value;
+
+  @override
+  Token copy() => new StringToken(type, _value, offset);
+
+  @override
+  String value() => _value;
+}
+
+/**
+ * A string token that is preceded by comments.
+ */
+class StringTokenWithComment extends StringToken {
+  /**
+   * The first comment in the list of comments that precede this token.
+   */
+  CommentToken _precedingComment;
+
+  /**
+   * Initialize a newly created token to have the given [type] at the given
+   * [offset] and to be preceded by the comments reachable from the given
+   * [comment].
+   */
+  StringTokenWithComment(
+      TokenType type, String value, int offset, this._precedingComment)
+      : super(type, value, offset) {
+    _setCommentParent(_precedingComment);
+  }
+
+  CommentToken get precedingComments => _precedingComment;
+
+  void set precedingComments(CommentToken comment) {
+    _precedingComment = comment;
+    _setCommentParent(_precedingComment);
+  }
+
+  @override
+  void applyDelta(int delta) {
+    super.applyDelta(delta);
+    Token token = precedingComments;
+    while (token != null) {
+      token.applyDelta(delta);
+      token = token.next;
+    }
+  }
+
+  @override
+  Token copy() => new StringTokenWithComment(
+      type, lexeme, offset, copyComments(precedingComments));
+}
+
+/**
+ * A [CharacterReader] that reads characters from a character sequence, but adds
+ * a delta when reporting the current character offset so that the character
+ * sequence can be a subsequence from a larger sequence.
+ */
+class SubSequenceReader extends CharSequenceReader {
+  /**
+   * The offset from the beginning of the file to the beginning of the source
+   * being scanned.
+   */
+  final int _offsetDelta;
+
+  /**
+   * Initialize a newly created reader to read the characters in the given
+   * [sequence]. The [_offsetDelta] is the offset from the beginning of the file
+   * to the beginning of the source being scanned
+   */
+  SubSequenceReader(String sequence, this._offsetDelta) : super(sequence);
+
+  @override
+  int get offset => _offsetDelta + super.offset;
+
+  @override
+  void set offset(int offset) {
+    super.offset = offset - _offsetDelta;
+  }
+
+  @override
+  String getString(int start, int endDelta) =>
+      super.getString(start - _offsetDelta, endDelta);
+}
+
+/**
+ * A token whose value is independent of it's type.
+ */
+class SyntheticStringToken extends StringToken {
+  /**
+   * Initialize a newly created token to represent a token of the given [type]
+   * with the given [value] at the given [offset].
+   */
+  SyntheticStringToken(TokenType type, String value, int offset)
+      : super(type, value, offset);
+
+  @override
+  bool get isSynthetic => true;
+}
+
+/**
+ * A token that was scanned from the input. Each token knows which tokens
+ * precede and follow it, acting as a link in a doubly linked list of tokens.
+ */
+class Token {
+  /**
+   * The type of the token.
+   */
+  final TokenType type;
+
+  /**
+   * The offset from the beginning of the file to the first character in the
+   * token.
+   */
+  int offset = 0;
+
+  /**
+   * The previous token in the token stream.
+   */
+  Token previous;
+
+  /**
+   * The next token in the token stream.
+   */
+  Token _next;
+
+  /**
+   * Initialize a newly created token to have the given [type] and [offset].
+   */
+  Token(this.type, int offset) {
+    this.offset = offset;
+  }
+
+  /**
+   * Return the offset from the beginning of the file to the character after the
+   * last character of the token.
+   */
+  int get end => offset + length;
+
+  /**
+   * Return `true` if this token represents an operator.
+   */
+  bool get isOperator => type.isOperator;
+
+  /**
+   * Return `true` if this token is a synthetic token. A synthetic token is a
+   * token that was introduced by the parser in order to recover from an error
+   * in the code.
+   */
+  bool get isSynthetic => length == 0;
+
+  /**
+   * Return `true` if this token represents an operator that can be defined by
+   * users.
+   */
+  bool get isUserDefinableOperator => type.isUserDefinableOperator;
+
+  /**
+   * Return the number of characters in the node's source range.
+   */
+  int get length => lexeme.length;
+
+  /**
+   * Return the lexeme that represents this token.
+   */
+  String get lexeme => type.lexeme;
+
+  /**
+   * Return the next token in the token stream.
+   */
+  Token get next => _next;
+
+  /**
+   * Return the first comment in the list of comments that precede this token,
+   * or `null` if there are no comments preceding this token. Additional
+   * comments can be reached by following the token stream using [next] until
+   * `null` is returned.
+   *
+   * For example, if the original contents were "/* one */ /* two */ id", then
+   * the first preceding comment token will have a lexeme of "/* one */" and
+   * the next comment token will have a lexeme of "/* two */".
+   */
+  CommentToken get precedingComments => null;
+
+  /**
+   * Apply (add) the given [delta] to this token's offset.
+   */
+  void applyDelta(int delta) {
+    offset += delta;
+  }
+
+  /**
+   * Return a newly created token that is a copy of this token but that is not a
+   * part of any token stream.
+   */
+  Token copy() => new Token(type, offset);
+
+  /**
+   * Copy a linked list of comment tokens identical to the given comment tokens.
+   */
+  Token copyComments(Token token) {
+    if (token == null) {
+      return null;
+    }
+    Token head = token.copy();
+    Token tail = head;
+    token = token.next;
+    while (token != null) {
+      tail = tail.setNext(token.copy());
+      token = token.next;
+    }
+    return head;
+  }
+
+  /**
+   * Return `true` if this token has any one of the given [types].
+   */
+  bool matchesAny(List<TokenType> types) {
+    for (TokenType type in types) {
+      if (this.type == type) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Set the next token in the token stream to the given [token]. This has the
+   * side-effect of setting this token to be the previous token for the given
+   * token. Return the token that was passed in.
+   */
+  Token setNext(Token token) {
+    _next = token;
+    token.previous = this;
+    return token;
+  }
+
+  /**
+   * Set the next token in the token stream to the given token without changing
+   * which token is the previous token for the given token. Return the token
+   * that was passed in.
+   */
+  Token setNextWithoutSettingPrevious(Token token) {
+    _next = token;
+    return token;
+  }
+
+  @override
+  String toString() => lexeme;
+
+  /**
+   * Return the value of this token. For keyword tokens, this is the keyword
+   * associated with the token, for other tokens it is the lexeme associated
+   * with the token.
+   */
+  Object value() => type.lexeme;
+
+  /**
+   * Sets the `parent` property to `this` for the given [comment] and all the
+   * next tokens.
+   */
+  void _setCommentParent(CommentToken comment) {
+    while (comment != null) {
+      comment.parent = this;
+      comment = comment.next;
+    }
+  }
+
+  /**
+   * Compare the given [tokens] to find the token that appears first in the
+   * source being parsed. That is, return the left-most of all of the tokens.
+   * The list must be non-`null`, but the elements of the list are allowed to be
+   * `null`. Return the token with the smallest offset, or `null` if the list is
+   * empty or if all of the elements of the list are `null`.
+   */
+  static Token lexicallyFirst(List<Token> tokens) {
+    Token first = null;
+    int offset = -1;
+    for (Token token in tokens) {
+      if (token != null && (offset < 0 || token.offset < offset)) {
+        first = token;
+        offset = token.offset;
+      }
+    }
+    return first;
+  }
+}
+
+/**
+ * The classes (or groups) of tokens with a similar use.
+ */
+class TokenClass {
+  /**
+   * A value used to indicate that the token type is not part of any specific
+   * class of token.
+   */
+  static const TokenClass NO_CLASS = const TokenClass('NO_CLASS');
+
+  /**
+   * A value used to indicate that the token type is an additive operator.
+   */
+  static const TokenClass ADDITIVE_OPERATOR =
+      const TokenClass('ADDITIVE_OPERATOR', 13);
+
+  /**
+   * A value used to indicate that the token type is an assignment operator.
+   */
+  static const TokenClass ASSIGNMENT_OPERATOR =
+      const TokenClass('ASSIGNMENT_OPERATOR', 1);
+
+  /**
+   * A value used to indicate that the token type is a bitwise-and operator.
+   */
+  static const TokenClass BITWISE_AND_OPERATOR =
+      const TokenClass('BITWISE_AND_OPERATOR', 11);
+
+  /**
+   * A value used to indicate that the token type is a bitwise-or operator.
+   */
+  static const TokenClass BITWISE_OR_OPERATOR =
+      const TokenClass('BITWISE_OR_OPERATOR', 9);
+
+  /**
+   * A value used to indicate that the token type is a bitwise-xor operator.
+   */
+  static const TokenClass BITWISE_XOR_OPERATOR =
+      const TokenClass('BITWISE_XOR_OPERATOR', 10);
+
+  /**
+   * A value used to indicate that the token type is a cascade operator.
+   */
+  static const TokenClass CASCADE_OPERATOR =
+      const TokenClass('CASCADE_OPERATOR', 2);
+
+  /**
+   * A value used to indicate that the token type is a conditional operator.
+   */
+  static const TokenClass CONDITIONAL_OPERATOR =
+      const TokenClass('CONDITIONAL_OPERATOR', 3);
+
+  /**
+   * A value used to indicate that the token type is an equality operator.
+   */
+  static const TokenClass EQUALITY_OPERATOR =
+      const TokenClass('EQUALITY_OPERATOR', 7);
+
+  /**
+   * A value used to indicate that the token type is an if-null operator.
+   */
+  static const TokenClass IF_NULL_OPERATOR =
+      const TokenClass('IF_NULL_OPERATOR', 4);
+
+  /**
+   * A value used to indicate that the token type is a logical-and operator.
+   */
+  static const TokenClass LOGICAL_AND_OPERATOR =
+      const TokenClass('LOGICAL_AND_OPERATOR', 6);
+
+  /**
+   * A value used to indicate that the token type is a logical-or operator.
+   */
+  static const TokenClass LOGICAL_OR_OPERATOR =
+      const TokenClass('LOGICAL_OR_OPERATOR', 5);
+
+  /**
+   * A value used to indicate that the token type is a multiplicative operator.
+   */
+  static const TokenClass MULTIPLICATIVE_OPERATOR =
+      const TokenClass('MULTIPLICATIVE_OPERATOR', 14);
+
+  /**
+   * A value used to indicate that the token type is a relational operator.
+   */
+  static const TokenClass RELATIONAL_OPERATOR =
+      const TokenClass('RELATIONAL_OPERATOR', 8);
+
+  /**
+   * A value used to indicate that the token type is a shift operator.
+   */
+  static const TokenClass SHIFT_OPERATOR =
+      const TokenClass('SHIFT_OPERATOR', 12);
+
+  /**
+   * A value used to indicate that the token type is a unary operator.
+   */
+  static const TokenClass UNARY_POSTFIX_OPERATOR =
+      const TokenClass('UNARY_POSTFIX_OPERATOR', 16);
+
+  /**
+   * A value used to indicate that the token type is a unary operator.
+   */
+  static const TokenClass UNARY_PREFIX_OPERATOR =
+      const TokenClass('UNARY_PREFIX_OPERATOR', 15);
+
+  /**
+   * The name of the token class.
+   */
+  final String name;
+
+  /**
+   * The precedence of tokens of this class, or `0` if the such tokens do not
+   * represent an operator.
+   */
+  final int precedence;
+
+  const TokenClass(this.name, [this.precedence = 0]);
+
+  @override
+  String toString() => name;
+}
+
+/**
+ * The types of tokens that can be returned by the scanner.
+ */
+class TokenType {
+  /**
+   * The type of the token that marks the end of the input.
+   */
+  static const TokenType EOF = const TokenType_EOF('EOF');
+
+  static const TokenType DOUBLE = const TokenType('DOUBLE');
+
+  static const TokenType HEXADECIMAL = const TokenType('HEXADECIMAL');
+
+  static const TokenType IDENTIFIER = const TokenType('IDENTIFIER');
+
+  static const TokenType INT = const TokenType('INT');
+
+  static const TokenType KEYWORD = const TokenType('KEYWORD');
+
+  static const TokenType MULTI_LINE_COMMENT =
+      const TokenType('MULTI_LINE_COMMENT');
+
+  static const TokenType SCRIPT_TAG = const TokenType('SCRIPT_TAG');
+
+  static const TokenType SINGLE_LINE_COMMENT =
+      const TokenType('SINGLE_LINE_COMMENT');
+
+  static const TokenType STRING = const TokenType('STRING');
+
+  static const TokenType AMPERSAND =
+      const TokenType('AMPERSAND', TokenClass.BITWISE_AND_OPERATOR, "&");
+
+  static const TokenType AMPERSAND_AMPERSAND = const TokenType(
+      'AMPERSAND_AMPERSAND', TokenClass.LOGICAL_AND_OPERATOR, "&&");
+
+  static const TokenType AMPERSAND_EQ =
+      const TokenType('AMPERSAND_EQ', TokenClass.ASSIGNMENT_OPERATOR, "&=");
+
+  static const TokenType AT = const TokenType('AT', TokenClass.NO_CLASS, "@");
+
+  static const TokenType BANG =
+      const TokenType('BANG', TokenClass.UNARY_PREFIX_OPERATOR, "!");
+
+  static const TokenType BANG_EQ =
+      const TokenType('BANG_EQ', TokenClass.EQUALITY_OPERATOR, "!=");
+
+  static const TokenType BAR =
+      const TokenType('BAR', TokenClass.BITWISE_OR_OPERATOR, "|");
+
+  static const TokenType BAR_BAR =
+      const TokenType('BAR_BAR', TokenClass.LOGICAL_OR_OPERATOR, "||");
+
+  static const TokenType BAR_EQ =
+      const TokenType('BAR_EQ', TokenClass.ASSIGNMENT_OPERATOR, "|=");
+
+  static const TokenType COLON =
+      const TokenType('COLON', TokenClass.NO_CLASS, ":");
+
+  static const TokenType COMMA =
+      const TokenType('COMMA', TokenClass.NO_CLASS, ",");
+
+  static const TokenType CARET =
+      const TokenType('CARET', TokenClass.BITWISE_XOR_OPERATOR, "^");
+
+  static const TokenType CARET_EQ =
+      const TokenType('CARET_EQ', TokenClass.ASSIGNMENT_OPERATOR, "^=");
+
+  static const TokenType CLOSE_CURLY_BRACKET =
+      const TokenType('CLOSE_CURLY_BRACKET', TokenClass.NO_CLASS, "}");
+
+  static const TokenType CLOSE_PAREN =
+      const TokenType('CLOSE_PAREN', TokenClass.NO_CLASS, ")");
+
+  static const TokenType CLOSE_SQUARE_BRACKET =
+      const TokenType('CLOSE_SQUARE_BRACKET', TokenClass.NO_CLASS, "]");
+
+  static const TokenType EQ =
+      const TokenType('EQ', TokenClass.ASSIGNMENT_OPERATOR, "=");
+
+  static const TokenType EQ_EQ =
+      const TokenType('EQ_EQ', TokenClass.EQUALITY_OPERATOR, "==");
+
+  static const TokenType FUNCTION =
+      const TokenType('FUNCTION', TokenClass.NO_CLASS, "=>");
+
+  static const TokenType GT =
+      const TokenType('GT', TokenClass.RELATIONAL_OPERATOR, ">");
+
+  static const TokenType GT_EQ =
+      const TokenType('GT_EQ', TokenClass.RELATIONAL_OPERATOR, ">=");
+
+  static const TokenType GT_GT =
+      const TokenType('GT_GT', TokenClass.SHIFT_OPERATOR, ">>");
+
+  static const TokenType GT_GT_EQ =
+      const TokenType('GT_GT_EQ', TokenClass.ASSIGNMENT_OPERATOR, ">>=");
+
+  static const TokenType HASH =
+      const TokenType('HASH', TokenClass.NO_CLASS, "#");
+
+  static const TokenType INDEX =
+      const TokenType('INDEX', TokenClass.UNARY_POSTFIX_OPERATOR, "[]");
+
+  static const TokenType INDEX_EQ =
+      const TokenType('INDEX_EQ', TokenClass.UNARY_POSTFIX_OPERATOR, "[]=");
+
+  static const TokenType IS =
+      const TokenType('IS', TokenClass.RELATIONAL_OPERATOR, "is");
+
+  static const TokenType LT =
+      const TokenType('LT', TokenClass.RELATIONAL_OPERATOR, "<");
+
+  static const TokenType LT_EQ =
+      const TokenType('LT_EQ', TokenClass.RELATIONAL_OPERATOR, "<=");
+
+  static const TokenType LT_LT =
+      const TokenType('LT_LT', TokenClass.SHIFT_OPERATOR, "<<");
+
+  static const TokenType LT_LT_EQ =
+      const TokenType('LT_LT_EQ', TokenClass.ASSIGNMENT_OPERATOR, "<<=");
+
+  static const TokenType MINUS =
+      const TokenType('MINUS', TokenClass.ADDITIVE_OPERATOR, "-");
+
+  static const TokenType MINUS_EQ =
+      const TokenType('MINUS_EQ', TokenClass.ASSIGNMENT_OPERATOR, "-=");
+
+  static const TokenType MINUS_MINUS =
+      const TokenType('MINUS_MINUS', TokenClass.UNARY_PREFIX_OPERATOR, "--");
+
+  static const TokenType OPEN_CURLY_BRACKET =
+      const TokenType('OPEN_CURLY_BRACKET', TokenClass.NO_CLASS, "{");
+
+  static const TokenType OPEN_PAREN =
+      const TokenType('OPEN_PAREN', TokenClass.UNARY_POSTFIX_OPERATOR, "(");
+
+  static const TokenType OPEN_SQUARE_BRACKET = const TokenType(
+      'OPEN_SQUARE_BRACKET', TokenClass.UNARY_POSTFIX_OPERATOR, "[");
+
+  static const TokenType PERCENT =
+      const TokenType('PERCENT', TokenClass.MULTIPLICATIVE_OPERATOR, "%");
+
+  static const TokenType PERCENT_EQ =
+      const TokenType('PERCENT_EQ', TokenClass.ASSIGNMENT_OPERATOR, "%=");
+
+  static const TokenType PERIOD =
+      const TokenType('PERIOD', TokenClass.UNARY_POSTFIX_OPERATOR, ".");
+
+  static const TokenType PERIOD_PERIOD =
+      const TokenType('PERIOD_PERIOD', TokenClass.CASCADE_OPERATOR, "..");
+
+  static const TokenType PLUS =
+      const TokenType('PLUS', TokenClass.ADDITIVE_OPERATOR, "+");
+
+  static const TokenType PLUS_EQ =
+      const TokenType('PLUS_EQ', TokenClass.ASSIGNMENT_OPERATOR, "+=");
+
+  static const TokenType PLUS_PLUS =
+      const TokenType('PLUS_PLUS', TokenClass.UNARY_PREFIX_OPERATOR, "++");
+
+  static const TokenType QUESTION =
+      const TokenType('QUESTION', TokenClass.CONDITIONAL_OPERATOR, "?");
+
+  static const TokenType QUESTION_PERIOD = const TokenType(
+      'QUESTION_PERIOD', TokenClass.UNARY_POSTFIX_OPERATOR, '?.');
+
+  static const TokenType QUESTION_QUESTION =
+      const TokenType('QUESTION_QUESTION', TokenClass.IF_NULL_OPERATOR, '??');
+
+  static const TokenType QUESTION_QUESTION_EQ = const TokenType(
+      'QUESTION_QUESTION_EQ', TokenClass.ASSIGNMENT_OPERATOR, '??=');
+
+  static const TokenType SEMICOLON =
+      const TokenType('SEMICOLON', TokenClass.NO_CLASS, ";");
+
+  static const TokenType SLASH =
+      const TokenType('SLASH', TokenClass.MULTIPLICATIVE_OPERATOR, "/");
+
+  static const TokenType SLASH_EQ =
+      const TokenType('SLASH_EQ', TokenClass.ASSIGNMENT_OPERATOR, "/=");
+
+  static const TokenType STAR =
+      const TokenType('STAR', TokenClass.MULTIPLICATIVE_OPERATOR, "*");
+
+  static const TokenType STAR_EQ =
+      const TokenType('STAR_EQ', TokenClass.ASSIGNMENT_OPERATOR, "*=");
+
+  static const TokenType STRING_INTERPOLATION_EXPRESSION = const TokenType(
+      'STRING_INTERPOLATION_EXPRESSION', TokenClass.NO_CLASS, "\${");
+
+  static const TokenType STRING_INTERPOLATION_IDENTIFIER = const TokenType(
+      'STRING_INTERPOLATION_IDENTIFIER', TokenClass.NO_CLASS, "\$");
+
+  static const TokenType TILDE =
+      const TokenType('TILDE', TokenClass.UNARY_PREFIX_OPERATOR, "~");
+
+  static const TokenType TILDE_SLASH =
+      const TokenType('TILDE_SLASH', TokenClass.MULTIPLICATIVE_OPERATOR, "~/");
+
+  static const TokenType TILDE_SLASH_EQ =
+      const TokenType('TILDE_SLASH_EQ', TokenClass.ASSIGNMENT_OPERATOR, "~/=");
+
+  static const TokenType BACKPING =
+      const TokenType('BACKPING', TokenClass.NO_CLASS, "`");
+
+  static const TokenType BACKSLASH =
+      const TokenType('BACKSLASH', TokenClass.NO_CLASS, "\\");
+
+  static const TokenType PERIOD_PERIOD_PERIOD =
+      const TokenType('PERIOD_PERIOD_PERIOD', TokenClass.NO_CLASS, "...");
+
+  /**
+   * The class of the token.
+   */
+  final TokenClass _tokenClass;
+
+  /**
+   * The name of the token type.
+   */
+  final String name;
+
+  /**
+   * The lexeme that defines this type of token, or `null` if there is more than
+   * one possible lexeme for this type of token.
+   */
+  final String lexeme;
+
+  const TokenType(this.name,
+      [this._tokenClass = TokenClass.NO_CLASS, this.lexeme = null]);
+
+  /**
+   * Return `true` if this type of token represents an additive operator.
+   */
+  bool get isAdditiveOperator => _tokenClass == TokenClass.ADDITIVE_OPERATOR;
+
+  /**
+   * Return `true` if this type of token represents an assignment operator.
+   */
+  bool get isAssignmentOperator =>
+      _tokenClass == TokenClass.ASSIGNMENT_OPERATOR;
+
+  /**
+   * Return `true` if this type of token represents an associative operator. An
+   * associative operator is an operator for which the following equality is
+   * true: `(a * b) * c == a * (b * c)`. In other words, if the result of
+   * applying the operator to multiple operands does not depend on the order in
+   * which those applications occur.
+   *
+   * Note: This method considers the logical-and and logical-or operators to be
+   * associative, even though the order in which the application of those
+   * operators can have an effect because evaluation of the right-hand operand
+   * is conditional.
+   */
+  bool get isAssociativeOperator => this == AMPERSAND ||
+      this == AMPERSAND_AMPERSAND ||
+      this == BAR ||
+      this == BAR_BAR ||
+      this == CARET ||
+      this == PLUS ||
+      this == STAR;
+
+  /**
+   * Return `true` if this type of token represents an equality operator.
+   */
+  bool get isEqualityOperator => _tokenClass == TokenClass.EQUALITY_OPERATOR;
+
+  /**
+   * Return `true` if this type of token represents an increment operator.
+   */
+  bool get isIncrementOperator =>
+      identical(lexeme, "++") || identical(lexeme, "--");
+
+  /**
+   * Return `true` if this type of token represents a multiplicative operator.
+   */
+  bool get isMultiplicativeOperator =>
+      _tokenClass == TokenClass.MULTIPLICATIVE_OPERATOR;
+
+  /**
+   * Return `true` if this token type represents an operator.
+   */
+  bool get isOperator => _tokenClass != TokenClass.NO_CLASS &&
+      this != OPEN_PAREN &&
+      this != OPEN_SQUARE_BRACKET &&
+      this != PERIOD;
+
+  /**
+   * Return `true` if this type of token represents a relational operator.
+   */
+  bool get isRelationalOperator =>
+      _tokenClass == TokenClass.RELATIONAL_OPERATOR;
+
+  /**
+   * Return `true` if this type of token represents a shift operator.
+   */
+  bool get isShiftOperator => _tokenClass == TokenClass.SHIFT_OPERATOR;
+
+  /**
+   * Return `true` if this type of token represents a unary postfix operator.
+   */
+  bool get isUnaryPostfixOperator =>
+      _tokenClass == TokenClass.UNARY_POSTFIX_OPERATOR;
+
+  /**
+   * Return `true` if this type of token represents a unary prefix operator.
+   */
+  bool get isUnaryPrefixOperator =>
+      _tokenClass == TokenClass.UNARY_PREFIX_OPERATOR;
+
+  /**
+   * Return `true` if this token type represents an operator that can be defined
+   * by users.
+   */
+  bool get isUserDefinableOperator => identical(lexeme, "==") ||
+      identical(lexeme, "~") ||
+      identical(lexeme, "[]") ||
+      identical(lexeme, "[]=") ||
+      identical(lexeme, "*") ||
+      identical(lexeme, "/") ||
+      identical(lexeme, "%") ||
+      identical(lexeme, "~/") ||
+      identical(lexeme, "+") ||
+      identical(lexeme, "-") ||
+      identical(lexeme, "<<") ||
+      identical(lexeme, ">>") ||
+      identical(lexeme, ">=") ||
+      identical(lexeme, ">") ||
+      identical(lexeme, "<=") ||
+      identical(lexeme, "<") ||
+      identical(lexeme, "&") ||
+      identical(lexeme, "^") ||
+      identical(lexeme, "|");
+
+  /**
+   * Return the precedence of the token, or `0` if the token does not represent
+   * an operator.
+   */
+  int get precedence => _tokenClass.precedence;
+
+  @override
+  String toString() => name;
+}
+
+class TokenType_EOF extends TokenType {
+  const TokenType_EOF(String name) : super(name, TokenClass.NO_CLASS, "");
+
+  @override
+  String toString() => "-eof-";
+}
+
+/**
+ * A normal token that is preceded by comments.
+ */
+class TokenWithComment extends Token {
+  /**
+   * The first comment in the list of comments that precede this token.
+   */
+  CommentToken _precedingComment;
+
+  /**
+   * Initialize a newly created token to have the given [type] at the given
+   * [offset] and to be preceded by the comments reachable from the given
+   * [comment].
+   */
+  TokenWithComment(TokenType type, int offset, this._precedingComment)
+      : super(type, offset) {
+    _setCommentParent(_precedingComment);
+  }
+
+  CommentToken get precedingComments => _precedingComment;
+
+  void set precedingComments(CommentToken comment) {
+    _precedingComment = comment;
+    _setCommentParent(_precedingComment);
+  }
+
+  @override
+  Token copy() => new TokenWithComment(type, offset, precedingComments);
+}
diff --git a/analyzer/lib/src/generated/sdk.dart b/analyzer/lib/src/generated/sdk.dart
new file mode 100644
index 0000000..81c8a88
--- /dev/null
+++ b/analyzer/lib/src/generated/sdk.dart
@@ -0,0 +1,392 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.sdk;
+
+import 'dart:collection';
+
+import 'ast.dart';
+import 'engine.dart' show AnalysisContext;
+import 'source.dart' show ContentCache, Source, UriKind;
+
+/**
+ * A Dart SDK installed in a specified location.
+ */
+abstract class DartSdk {
+  /**
+   * The short name of the dart SDK 'async' library.
+   */
+  static final String DART_ASYNC = "dart:async";
+
+  /**
+   * The short name of the dart SDK 'core' library.
+   */
+  static final String DART_CORE = "dart:core";
+
+  /**
+   * The short name of the dart SDK 'html' library.
+   */
+  static final String DART_HTML = "dart:html";
+
+  /**
+   * The version number that is returned when the real version number could not
+   * be determined.
+   */
+  static final String DEFAULT_VERSION = "0";
+
+  /**
+   * Return the analysis context used for all of the sources in this [DartSdk].
+   */
+  AnalysisContext get context;
+
+  /**
+   * Return a list containing all of the libraries defined in this SDK.
+   */
+  List<SdkLibrary> get sdkLibraries;
+
+  /**
+   * Return the revision number of this SDK, or `"0"` if the revision number
+   * cannot be discovered.
+   */
+  String get sdkVersion;
+
+  /**
+   * Return a list containing the library URI's for the libraries defined in
+   * this SDK.
+   */
+  List<String> get uris;
+
+  /**
+   * Return a source representing the given 'file:' [uri] if the file is in this
+   * SDK, or `null` if the file is not in this SDK.
+   */
+  Source fromFileUri(Uri uri);
+
+  /**
+   * Return the library representing the library with the given 'dart:' [uri],
+   * or `null` if the given URI does not denote a library in this SDK.
+   */
+  SdkLibrary getSdkLibrary(String uri);
+
+  /**
+   * Return the source representing the library with the given 'dart:' [uri], or
+   * `null` if the given URI does not denote a library in this SDK.
+   */
+  Source mapDartUri(String uri);
+}
+
+/**
+ * A map from Dart library URI's to the [SdkLibraryImpl] representing that
+ * library.
+ */
+class LibraryMap {
+  /**
+   * A table mapping Dart library URI's to the library.
+   */
+  HashMap<String, SdkLibraryImpl> _libraryMap =
+      new HashMap<String, SdkLibraryImpl>();
+
+  /**
+   * Return a list containing all of the sdk libraries in this mapping.
+   */
+  List<SdkLibrary> get sdkLibraries => new List.from(_libraryMap.values);
+
+  /**
+   * Return a list containing the library URI's for which a mapping is available.
+   */
+  List<String> get uris => new List.from(_libraryMap.keys.toSet());
+
+  /**
+   * Return the library with the given 'dart:' [uri], or `null` if the URI does
+   * not map to a library.
+   */
+  SdkLibrary getLibrary(String uri) => _libraryMap[uri];
+
+  /**
+   * Set the library with the given 'dart:' [uri] to the given [library].
+   */
+  void setLibrary(String dartUri, SdkLibraryImpl library) {
+    _libraryMap[dartUri] = library;
+  }
+
+  /**
+   * Return the number of library URI's for which a mapping is available.
+   */
+  int size() => _libraryMap.length;
+}
+
+class SdkLibrariesReader_LibraryBuilder extends RecursiveAstVisitor<Object> {
+  /**
+   * The prefix added to the name of a library to form the URI used in code to
+   * reference the library.
+   */
+  static String _LIBRARY_PREFIX = "dart:";
+
+  /**
+   * The name of the optional parameter used to indicate whether the library is
+   * an implementation library.
+   */
+  static String _IMPLEMENTATION = "implementation";
+
+  /**
+   * The name of the optional parameter used to specify the path used when
+   * compiling for dart2js.
+   */
+  static String _DART2JS_PATH = "dart2jsPath";
+
+  /**
+   * The name of the optional parameter used to indicate whether the library is
+   * documented.
+   */
+  static String _DOCUMENTED = "documented";
+
+  /**
+   * The name of the optional parameter used to specify the category of the
+   * library.
+   */
+  static String _CATEGORY = "category";
+
+  /**
+   * The name of the optional parameter used to specify the platforms on which
+   * the library can be used.
+   */
+  static String _PLATFORMS = "platforms";
+
+  /**
+   * The value of the [PLATFORMS] parameter used to specify that the library can
+   * be used on the VM.
+   */
+  static String _VM_PLATFORM = "VM_PLATFORM";
+
+  /**
+   * A flag indicating whether the dart2js path should be used when it is
+   * available.
+   */
+  final bool _useDart2jsPaths;
+
+  /**
+   * The library map that is populated by visiting the AST structure parsed from
+   * the contents of the libraries file.
+   */
+  LibraryMap _librariesMap = new LibraryMap();
+
+  /**
+   * Initialize a newly created library builder to use the dart2js path if
+   * [_useDart2jsPaths] is `true`.
+   */
+  SdkLibrariesReader_LibraryBuilder(this._useDart2jsPaths);
+
+  /**
+   * Return the library map that was populated by visiting the AST structure
+   * parsed from the contents of the libraries file.
+   */
+  LibraryMap get librariesMap => _librariesMap;
+
+  @override
+  Object visitMapLiteralEntry(MapLiteralEntry node) {
+    String libraryName = null;
+    Expression key = node.key;
+    if (key is SimpleStringLiteral) {
+      libraryName = "$_LIBRARY_PREFIX${key.value}";
+    }
+    Expression value = node.value;
+    if (value is InstanceCreationExpression) {
+      SdkLibraryImpl library = new SdkLibraryImpl(libraryName);
+      List<Expression> arguments = value.argumentList.arguments;
+      for (Expression argument in arguments) {
+        if (argument is SimpleStringLiteral) {
+          library.path = argument.value;
+        } else if (argument is NamedExpression) {
+          String name = argument.name.label.name;
+          Expression expression = argument.expression;
+          if (name == _CATEGORY) {
+            library.category = (expression as SimpleStringLiteral).value;
+          } else if (name == _IMPLEMENTATION) {
+            library.implementation = (expression as BooleanLiteral).value;
+          } else if (name == _DOCUMENTED) {
+            library.documented = (expression as BooleanLiteral).value;
+          } else if (name == _PLATFORMS) {
+            if (expression is SimpleIdentifier) {
+              String identifier = expression.name;
+              if (identifier == _VM_PLATFORM) {
+                library.setVmLibrary();
+              } else {
+                library.setDart2JsLibrary();
+              }
+            }
+          } else if (_useDart2jsPaths && name == _DART2JS_PATH) {
+            if (expression is SimpleStringLiteral) {
+              library.path = expression.value;
+            }
+          }
+        }
+      }
+      _librariesMap.setLibrary(libraryName, library);
+    }
+    return null;
+  }
+}
+
+/**
+ * Represents a single library in the SDK
+ */
+abstract class SdkLibrary {
+  /**
+   * Return the name of the category containing the library.
+   */
+  String get category;
+
+  /**
+   * Return `true` if this library can be compiled to JavaScript by dart2js.
+   */
+  bool get isDart2JsLibrary;
+
+  /**
+   * Return `true` if the library is documented.
+   */
+  bool get isDocumented;
+
+  /**
+   * Return `true` if the library is an implementation library.
+   */
+  bool get isImplementation;
+
+  /**
+   * Return `true` if library is internal can be used only by other SDK libraries.
+   */
+  bool get isInternal;
+
+  /**
+   * Return `true` if this library can be used for both client and server.
+   */
+  bool get isShared;
+
+  /**
+   * Return `true` if this library can be run on the VM.
+   */
+  bool get isVmLibrary;
+
+  /**
+   * Return the path to the file defining the library. The path is relative to
+   * the `lib` directory within the SDK.
+   */
+  String get path;
+
+  /**
+   * Return the short name of the library. This is the URI of the library,
+   * including `dart:`.
+   */
+  String get shortName;
+}
+
+/**
+ * The information known about a single library within the SDK.
+ */
+class SdkLibraryImpl implements SdkLibrary {
+  /**
+   * The bit mask used to access the bit representing the flag indicating
+   * whether a library is intended to work on the dart2js platform.
+   */
+  static int DART2JS_PLATFORM = 1;
+
+  /**
+   * The bit mask used to access the bit representing the flag indicating
+   * whether a library is intended to work on the VM platform.
+   */
+  static int VM_PLATFORM = 2;
+
+  /**
+   * The short name of the library. This is the name used after 'dart:' in a
+   * URI.
+   */
+  String _shortName = null;
+
+  /**
+   * The path to the file defining the library. The path is relative to the
+   * 'lib' directory within the SDK.
+   */
+  String path = null;
+
+  /**
+   * The name of the category containing the library. Unless otherwise specified
+   * in the libraries file all libraries are assumed to be shared between server
+   * and client.
+   */
+  String category = "Shared";
+
+  /**
+   * A flag indicating whether the library is documented.
+   */
+  bool _documented = true;
+
+  /**
+   * A flag indicating whether the library is an implementation library.
+   */
+  bool _implementation = false;
+
+  /**
+   * An encoding of which platforms this library is intended to work on.
+   */
+  int _platforms = 0;
+
+  /**
+   * Initialize a newly created library to represent the library with the given
+   * [name].
+   */
+  SdkLibraryImpl(String name) {
+    this._shortName = name;
+  }
+
+  /**
+   * Set whether the library is documented.
+   */
+  void set documented(bool documented) {
+    this._documented = documented;
+  }
+
+  /**
+   * Set whether the library is an implementation library.
+   */
+  void set implementation(bool implementation) {
+    this._implementation = implementation;
+  }
+
+  @override
+  bool get isDart2JsLibrary => (_platforms & DART2JS_PLATFORM) != 0;
+
+  @override
+  bool get isDocumented => _documented;
+
+  @override
+  bool get isImplementation => _implementation;
+
+  @override
+  bool get isInternal => "Internal" == category;
+
+  @override
+  bool get isShared => category == "Shared";
+
+  @override
+  bool get isVmLibrary => (_platforms & VM_PLATFORM) != 0;
+
+  @override
+  String get shortName => _shortName;
+
+  /**
+   * Record that this library can be compiled to JavaScript by dart2js.
+   */
+  void setDart2JsLibrary() {
+    _platforms |= DART2JS_PLATFORM;
+  }
+
+  /**
+   * Record that this library can be run on the VM.
+   */
+  void setVmLibrary() {
+    _platforms |= VM_PLATFORM;
+  }
+}
diff --git a/analyzer/lib/src/generated/sdk_io.dart b/analyzer/lib/src/generated/sdk_io.dart
new file mode 100644
index 0000000..3a9f239
--- /dev/null
+++ b/analyzer/lib/src/generated/sdk_io.dart
@@ -0,0 +1,572 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.sdk.io;
+
+import 'dart:io';
+
+import 'package:analyzer/src/generated/java_engine.dart';
+
+import 'ast.dart';
+import 'engine.dart';
+import 'error.dart';
+import 'java_core.dart';
+import 'java_engine_io.dart';
+import 'java_io.dart';
+import 'parser.dart';
+import 'scanner.dart';
+import 'sdk.dart';
+import 'source_io.dart';
+
+/**
+ * A Dart SDK installed in a specified directory. Typical Dart SDK layout is
+ * something like...
+ *
+ *     dart-sdk/
+ *        bin/
+ *           dart[.exe]  <-- VM
+ *        lib/
+ *           core/
+ *              core.dart
+ *              ... other core library files ...
+ *           ... other libraries ...
+ *        util/
+ *           ... Dart utilities ...
+ *     Chromium/   <-- Dartium typically exists in a sibling directory
+ */
+class DirectoryBasedDartSdk implements DartSdk {
+  /**
+   * The default SDK, or `null` if the default SDK either has not yet been
+   * created or cannot be created for some reason.
+   */
+  static DirectoryBasedDartSdk _DEFAULT_SDK;
+
+  /**
+   * The name of the directory within the SDK directory that contains
+   * executables.
+   */
+  static String _BIN_DIRECTORY_NAME = "bin";
+
+  /**
+   * The name of the directory on non-Mac that contains dartium.
+   */
+  static String _DARTIUM_DIRECTORY_NAME = "chromium";
+
+  /**
+   * The name of the dart2js executable on non-windows operating systems.
+   */
+  static String _DART2JS_EXECUTABLE_NAME = "dart2js";
+
+  /**
+   * The name of the file containing the dart2js executable on Windows.
+   */
+  static String _DART2JS_EXECUTABLE_NAME_WIN = "dart2js.bat";
+
+  /**
+   * The name of the file containing the Dartium executable on Linux.
+   */
+  static String _DARTIUM_EXECUTABLE_NAME_LINUX = "chrome";
+
+  /**
+   * The name of the file containing the Dartium executable on Macintosh.
+   */
+  static String _DARTIUM_EXECUTABLE_NAME_MAC =
+      "Chromium.app/Contents/MacOS/Chromium";
+
+  /**
+   * The name of the file containing the Dartium executable on Windows.
+   */
+  static String _DARTIUM_EXECUTABLE_NAME_WIN = "Chrome.exe";
+
+  /**
+   * The name of the [System] property whose value is the path to the default
+   * Dart SDK directory.
+   */
+  static String _DEFAULT_DIRECTORY_PROPERTY_NAME = "com.google.dart.sdk";
+
+  /**
+   * The name of the directory within the SDK directory that contains
+   * documentation for the libraries.
+   */
+  static String _DOCS_DIRECTORY_NAME = "docs";
+
+  /**
+   * The suffix added to the name of a library to derive the name of the file
+   * containing the documentation for that library.
+   */
+  static String _DOC_FILE_SUFFIX = "_api.json";
+
+  /**
+   * The name of the directory within the SDK directory that contains the
+   * libraries file.
+   */
+  static String _INTERNAL_DIR = "_internal";
+
+  /**
+   * The name of the directory within the SDK directory that contains the
+   * libraries.
+   */
+  static String _LIB_DIRECTORY_NAME = "lib";
+
+  /**
+   * The name of the libraries file.
+   */
+  static String _LIBRARIES_FILE = "libraries.dart";
+
+  /**
+   * The name of the pub executable on windows.
+   */
+  static String _PUB_EXECUTABLE_NAME_WIN = "pub.bat";
+
+  /**
+   * The name of the pub executable on non-windows operating systems.
+   */
+  static String _PUB_EXECUTABLE_NAME = "pub";
+
+  /**
+   * The name of the file within the SDK directory that contains the version
+   * number of the SDK.
+   */
+  static String _VERSION_FILE_NAME = "version";
+
+  /**
+   * The name of the file containing the VM executable on the Windows operating
+   * system.
+   */
+  static String _VM_EXECUTABLE_NAME_WIN = "dart.exe";
+
+  /**
+   * The name of the file containing the VM executable on non-Windows operating
+   * systems.
+   */
+  static String _VM_EXECUTABLE_NAME = "dart";
+
+  /**
+   * Return the default Dart SDK, or `null` if the directory containing the
+   * default SDK cannot be determined (or does not exist).
+   */
+  static DirectoryBasedDartSdk get defaultSdk {
+    if (_DEFAULT_SDK == null) {
+      JavaFile sdkDirectory = defaultSdkDirectory;
+      if (sdkDirectory == null) {
+        return null;
+      }
+      _DEFAULT_SDK = new DirectoryBasedDartSdk(sdkDirectory);
+    }
+    return _DEFAULT_SDK;
+  }
+
+  /**
+   * Return the default directory for the Dart SDK, or `null` if the directory
+   * cannot be determined (or does not exist). The default directory is provided
+   * by a system property named `com.google.dart.sdk`.
+   */
+  static JavaFile get defaultSdkDirectory {
+    String sdkProperty =
+        JavaSystemIO.getProperty(_DEFAULT_DIRECTORY_PROPERTY_NAME);
+    if (sdkProperty == null) {
+      return null;
+    }
+    JavaFile sdkDirectory = new JavaFile(sdkProperty);
+    if (!sdkDirectory.exists()) {
+      return null;
+    }
+    return sdkDirectory;
+  }
+
+  /**
+   * The [AnalysisContext] which is used for all of the sources in this sdk.
+   */
+  InternalAnalysisContext _analysisContext;
+
+  /**
+   * The directory containing the SDK.
+   */
+  JavaFile _sdkDirectory;
+
+  /**
+   * The revision number of this SDK, or `"0"` if the revision number cannot be
+   * discovered.
+   */
+  String _sdkVersion;
+
+  /**
+   * The file containing the dart2js executable.
+   */
+  JavaFile _dart2jsExecutable;
+
+  /**
+   * The file containing the Dartium executable.
+   */
+  JavaFile _dartiumExecutable;
+
+  /**
+   * The file containing the pub executable.
+   */
+  JavaFile _pubExecutable;
+
+  /**
+   * The file containing the VM executable.
+   */
+  JavaFile _vmExecutable;
+
+  /**
+   * A mapping from Dart library URI's to the library represented by that URI.
+   */
+  LibraryMap _libraryMap;
+
+  /**
+   * Initialize a newly created SDK to represent the Dart SDK installed in the
+   * [sdkDirectory]. The flag [useDart2jsPaths] is `true` if the dart2js path
+   * should be used when it is available
+   */
+  DirectoryBasedDartSdk(JavaFile sdkDirectory, [bool useDart2jsPaths = false]) {
+    this._sdkDirectory = sdkDirectory.getAbsoluteFile();
+    _libraryMap = initialLibraryMap(useDart2jsPaths);
+  }
+
+  @override
+  AnalysisContext get context {
+    if (_analysisContext == null) {
+      _analysisContext = new SdkAnalysisContext();
+      SourceFactory factory = new SourceFactory([new DartUriResolver(this)]);
+      _analysisContext.sourceFactory = factory;
+      List<String> uris = this.uris;
+      ChangeSet changeSet = new ChangeSet();
+      for (String uri in uris) {
+        changeSet.addedSource(factory.forUri(uri));
+      }
+      _analysisContext.applyChanges(changeSet);
+    }
+    return _analysisContext;
+  }
+
+  /**
+   * Return the file containing the dart2js executable, or `null` if it does not
+   * exist.
+   */
+  JavaFile get dart2JsExecutable {
+    if (_dart2jsExecutable == null) {
+      _dart2jsExecutable = _verifyExecutable(new JavaFile.relative(
+          new JavaFile.relative(_sdkDirectory, _BIN_DIRECTORY_NAME),
+          OSUtilities.isWindows()
+              ? _DART2JS_EXECUTABLE_NAME_WIN
+              : _DART2JS_EXECUTABLE_NAME));
+    }
+    return _dart2jsExecutable;
+  }
+
+  /**
+   * Return the name of the file containing the Dartium executable.
+   */
+  String get dartiumBinaryName {
+    if (OSUtilities.isWindows()) {
+      return _DARTIUM_EXECUTABLE_NAME_WIN;
+    } else if (OSUtilities.isMac()) {
+      return _DARTIUM_EXECUTABLE_NAME_MAC;
+    } else {
+      return _DARTIUM_EXECUTABLE_NAME_LINUX;
+    }
+  }
+
+  /**
+   * Return the file containing the Dartium executable, or `null` if it does not
+   * exist.
+   */
+  JavaFile get dartiumExecutable {
+    if (_dartiumExecutable == null) {
+      _dartiumExecutable = _verifyExecutable(
+          new JavaFile.relative(dartiumWorkingDirectory, dartiumBinaryName));
+    }
+    return _dartiumExecutable;
+  }
+
+  /**
+   * Return the directory where dartium can be found (the directory that will be
+   * the working directory is Dartium is invoked without changing the default).
+   */
+  JavaFile get dartiumWorkingDirectory =>
+      getDartiumWorkingDirectory(_sdkDirectory.getParentFile());
+
+  /**
+   * Return the directory containing the SDK.
+   */
+  JavaFile get directory => _sdkDirectory;
+
+  /**
+   * Return the directory containing documentation for the SDK.
+   */
+  JavaFile get docDirectory =>
+      new JavaFile.relative(_sdkDirectory, _DOCS_DIRECTORY_NAME);
+
+  /**
+   * Return `true` if this SDK includes documentation.
+   */
+  bool get hasDocumentation => docDirectory.exists();
+
+  /**
+   * Return `true` if the Dartium binary is available.
+   */
+  bool get isDartiumInstalled => dartiumExecutable != null;
+
+  /**
+   * Return the directory within the SDK directory that contains the libraries.
+   */
+  JavaFile get libraryDirectory =>
+      new JavaFile.relative(_sdkDirectory, _LIB_DIRECTORY_NAME);
+
+  /**
+   * Return the file containing the Pub executable, or `null` if it does not exist.
+   */
+  JavaFile get pubExecutable {
+    if (_pubExecutable == null) {
+      _pubExecutable = _verifyExecutable(new JavaFile.relative(
+          new JavaFile.relative(_sdkDirectory, _BIN_DIRECTORY_NAME), OSUtilities
+              .isWindows() ? _PUB_EXECUTABLE_NAME_WIN : _PUB_EXECUTABLE_NAME));
+    }
+    return _pubExecutable;
+  }
+
+  @override
+  List<SdkLibrary> get sdkLibraries => _libraryMap.sdkLibraries;
+
+  /**
+   * Return the revision number of this SDK, or `"0"` if the revision number
+   * cannot be discovered.
+   */
+  @override
+  String get sdkVersion {
+    if (_sdkVersion == null) {
+      _sdkVersion = DartSdk.DEFAULT_VERSION;
+      JavaFile revisionFile =
+          new JavaFile.relative(_sdkDirectory, _VERSION_FILE_NAME);
+      try {
+        String revision = revisionFile.readAsStringSync();
+        if (revision != null) {
+          _sdkVersion = revision.trim();
+        }
+      } on FileSystemException {
+        // Fall through to return the default.
+      }
+    }
+    return _sdkVersion;
+  }
+
+  @override
+  List<String> get uris => _libraryMap.uris;
+
+  /**
+   * Return the name of the file containing the VM executable.
+   */
+  String get vmBinaryName {
+    if (OSUtilities.isWindows()) {
+      return _VM_EXECUTABLE_NAME_WIN;
+    } else {
+      return _VM_EXECUTABLE_NAME;
+    }
+  }
+
+  /**
+   * Return the file containing the VM executable, or `null` if it does not
+   * exist.
+   */
+  JavaFile get vmExecutable {
+    if (_vmExecutable == null) {
+      _vmExecutable = _verifyExecutable(new JavaFile.relative(
+          new JavaFile.relative(_sdkDirectory, _BIN_DIRECTORY_NAME),
+          vmBinaryName));
+    }
+    return _vmExecutable;
+  }
+
+  @override
+  Source fromFileUri(Uri uri) {
+    JavaFile file = new JavaFile.fromUri(uri);
+    String filePath = file.getAbsolutePath();
+    String libPath = libraryDirectory.getAbsolutePath();
+    if (!filePath.startsWith("$libPath${JavaFile.separator}")) {
+      return null;
+    }
+    filePath = filePath.substring(libPath.length + 1);
+    for (SdkLibrary library in _libraryMap.sdkLibraries) {
+      String libraryPath = library.path;
+      if (filePath.replaceAll('\\', '/') == libraryPath) {
+        String path = library.shortName;
+        try {
+          return new FileBasedSource.con2(parseUriWithException(path), file);
+        } on URISyntaxException catch (exception, stackTrace) {
+          AnalysisEngine.instance.logger.logInformation(
+              "Failed to create URI: $path",
+              new CaughtException(exception, stackTrace));
+          return null;
+        }
+      }
+      libraryPath = new JavaFile(libraryPath).getParent();
+      if (filePath.startsWith("$libraryPath${JavaFile.separator}")) {
+        String path =
+            "${library.shortName}/${filePath.substring(libraryPath.length + 1)}";
+        try {
+          return new FileBasedSource.con2(parseUriWithException(path), file);
+        } on URISyntaxException catch (exception, stackTrace) {
+          AnalysisEngine.instance.logger.logInformation(
+              "Failed to create URI: $path",
+              new CaughtException(exception, stackTrace));
+          return null;
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the directory where dartium can be found (the directory that will be
+   * the working directory if Dartium is invoked without changing the default),
+   * assuming that the Editor was installed in the [installDir].
+   */
+  JavaFile getDartiumWorkingDirectory(JavaFile installDir) =>
+      new JavaFile.relative(installDir, _DARTIUM_DIRECTORY_NAME);
+
+  /**
+   * Return the auxiliary documentation file for the library with the given
+   * [libraryName], or `null` if no such file exists.
+   */
+  JavaFile getDocFileFor(String libraryName) {
+    JavaFile dir = docDirectory;
+    if (!dir.exists()) {
+      return null;
+    }
+    JavaFile libDir = new JavaFile.relative(dir, libraryName);
+    JavaFile docFile =
+        new JavaFile.relative(libDir, "$libraryName$_DOC_FILE_SUFFIX");
+    if (docFile.exists()) {
+      return docFile;
+    }
+    return null;
+  }
+
+  @override
+  SdkLibrary getSdkLibrary(String dartUri) => _libraryMap.getLibrary(dartUri);
+
+  /**
+   * Read all of the configuration files to initialize the library maps. The
+   * flag [useDart2jsPaths] is `true` if the dart2js path should be used when it
+   * is available. Return the initialized library map.
+   */
+  LibraryMap initialLibraryMap(bool useDart2jsPaths) {
+    JavaFile librariesFile = new JavaFile.relative(
+        new JavaFile.relative(libraryDirectory, _INTERNAL_DIR),
+        _LIBRARIES_FILE);
+    try {
+      String contents = librariesFile.readAsStringSync();
+      return new SdkLibrariesReader(useDart2jsPaths).readFromFile(
+          librariesFile, contents);
+    } catch (exception, stackTrace) {
+      AnalysisEngine.instance.logger.logError(
+          "Could not initialize the library map from ${librariesFile.getAbsolutePath()}",
+          new CaughtException(exception, stackTrace));
+      return new LibraryMap();
+    }
+  }
+
+  @override
+  Source mapDartUri(String dartUri) {
+    String libraryName;
+    String relativePath;
+    int index = dartUri.indexOf('/');
+    if (index >= 0) {
+      libraryName = dartUri.substring(0, index);
+      relativePath = dartUri.substring(index + 1);
+    } else {
+      libraryName = dartUri;
+      relativePath = "";
+    }
+    SdkLibrary library = getSdkLibrary(libraryName);
+    if (library == null) {
+      return null;
+    }
+    try {
+      JavaFile file = new JavaFile.relative(libraryDirectory, library.path);
+      if (!relativePath.isEmpty) {
+        file = file.getParentFile();
+        file = new JavaFile.relative(file, relativePath);
+      }
+      return new FileBasedSource.con2(parseUriWithException(dartUri), file);
+    } on URISyntaxException {
+      return null;
+    }
+  }
+
+  /**
+   * Return the given [file] if it exists and is executable, or `null` if it
+   * does not exist or is not executable.
+   */
+  JavaFile _verifyExecutable(JavaFile file) =>
+      file.isExecutable() ? file : null;
+}
+
+/**
+ * An object used to read and parse the libraries file
+ * (dart-sdk/lib/_internal/libraries.dart) for information about the libraries
+ * in an SDK. The library information is represented as a Dart file containing a
+ * single top-level variable whose value is a const map. The keys of the map are
+ * the names of libraries defined in the SDK and the values in the map are info
+ * objects defining the library. For example, a subset of a typical SDK might
+ * have a libraries file that looks like the following:
+ *
+ *     final Map&lt;String, LibraryInfo&gt; LIBRARIES = const &lt;LibraryInfo&gt; {
+ *       // Used by VM applications
+ *       "builtin" : const LibraryInfo(
+ *         "builtin/builtin_runtime.dart",
+ *         category: "Server",
+ *         platforms: VM_PLATFORM),
+ *
+ *       "compiler" : const LibraryInfo(
+ *         "compiler/compiler.dart",
+ *         category: "Tools",
+ *         platforms: 0),
+ *     };
+ */
+class SdkLibrariesReader {
+  /**
+   * A flag indicating whether the dart2js path should be used when it is
+   * available.
+   */
+  final bool _useDart2jsPaths;
+
+  /**
+   * Initialize a newly created library reader to use the dart2js path if
+   * [_useDart2jsPaths] is `true`.
+   */
+  SdkLibrariesReader(this._useDart2jsPaths);
+
+  /**
+   * Return the library map read from the given [file], given that the content
+   * of the file is already known to be [libraryFileContents].
+   */
+  LibraryMap readFromFile(JavaFile file, String libraryFileContents) =>
+      readFromSource(new FileBasedSource.con1(file), libraryFileContents);
+
+  /**
+   * Return the library map read from the given [source], given that the content
+   * of the file is already known to be [libraryFileContents].
+   */
+  LibraryMap readFromSource(Source source, String libraryFileContents) {
+    BooleanErrorListener errorListener = new BooleanErrorListener();
+    Scanner scanner = new Scanner(
+        source, new CharSequenceReader(libraryFileContents), errorListener);
+    Parser parser = new Parser(source, errorListener);
+    CompilationUnit unit = parser.parseCompilationUnit(scanner.tokenize());
+    SdkLibrariesReader_LibraryBuilder libraryBuilder =
+        new SdkLibrariesReader_LibraryBuilder(_useDart2jsPaths);
+    // If any syntactic errors were found then don't try to visit the AST
+    // structure.
+    if (!errorListener.errorReported) {
+      unit.accept(libraryBuilder);
+    }
+    return libraryBuilder.librariesMap;
+  }
+}
diff --git a/analyzer/lib/src/generated/source.dart b/analyzer/lib/src/generated/source.dart
new file mode 100644
index 0000000..d0b5dde
--- /dev/null
+++ b/analyzer/lib/src/generated/source.dart
@@ -0,0 +1,1004 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.source;
+
+import "dart:math" as math;
+import 'dart:collection';
+
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/source/package_map_resolver.dart';
+import 'package:analyzer/task/model.dart';
+
+import 'engine.dart';
+import 'java_core.dart';
+import 'java_engine.dart';
+import 'java_io.dart' show JavaFile;
+import 'sdk.dart' show DartSdk;
+import 'source_io.dart' show FileBasedSource;
+
+/**
+ * A function that is used to visit [ContentCache] entries.
+ */
+typedef void ContentCacheVisitor(String fullPath, int stamp, String contents);
+
+/**
+ * A cache used to override the default content of a [Source].
+ */
+class ContentCache {
+  /**
+   * A table mapping the full path of sources to the contents of those sources.
+   * This is used to override the default contents of a source.
+   */
+  HashMap<String, String> _contentMap = new HashMap<String, String>();
+
+  /**
+   * A table mapping the full path of sources to the modification stamps of
+   * those sources. This is used when the default contents of a source has been
+   * overridden.
+   */
+  HashMap<String, int> _stampMap = new HashMap<String, int>();
+
+  /**
+   * Visit all entries of this cache.
+   */
+  void accept(ContentCacheVisitor visitor) {
+    _contentMap.forEach((String fullPath, String contents) {
+      int stamp = _stampMap[fullPath];
+      visitor(fullPath, stamp, contents);
+    });
+  }
+
+  /**
+   * Return the contents of the given [source], or `null` if this cache does not
+   * override the contents of the source.
+   *
+   * <b>Note:</b> This method is not intended to be used except by
+   * [AnalysisContext.getContents].
+   */
+  String getContents(Source source) => _contentMap[source.fullName];
+
+  /**
+   * Return the modification stamp of the given [source], or `null` if this
+   * cache does not override the contents of the source.
+   *
+   * <b>Note:</b> This method is not intended to be used except by
+   * [AnalysisContext.getModificationStamp].
+   */
+  int getModificationStamp(Source source) => _stampMap[source.fullName];
+
+  /**
+   * Set the contents of the given [source] to the given [contents]. This has
+   * the effect of overriding the default contents of the source. If the
+   * contents are `null` the override is removed so that the default contents
+   * will be returned.
+   */
+  String setContents(Source source, String contents) {
+    String fullName = source.fullName;
+    if (contents == null) {
+      _stampMap.remove(fullName);
+      return _contentMap.remove(fullName);
+    } else {
+      int newStamp = JavaSystem.currentTimeMillis();
+      int oldStamp = _stampMap[fullName];
+      _stampMap[fullName] = newStamp;
+      // Occasionally, if this method is called in rapid succession, the
+      // timestamps are equal. Guard against this by artificially incrementing
+      // the new timestamp.
+      if (newStamp == oldStamp) {
+        _stampMap[fullName] = newStamp + 1;
+      }
+      String oldContent = _contentMap[fullName];
+      _contentMap[fullName] = contents;
+      return oldContent;
+    }
+  }
+}
+
+/**
+ * Instances of the class `DartUriResolver` resolve `dart` URI's.
+ */
+class DartUriResolver extends UriResolver {
+  /**
+   * The name of the `dart` scheme.
+   */
+  static String DART_SCHEME = "dart";
+
+  /**
+   * The prefix of a URI using the dart-ext scheme to reference a native code library.
+   */
+  static String _DART_EXT_SCHEME = "dart-ext:";
+
+  /**
+   * The Dart SDK against which URI's are to be resolved.
+   */
+  final DartSdk _sdk;
+
+  /**
+   * Initialize a newly created resolver to resolve Dart URI's against the given platform within the
+   * given Dart SDK.
+   *
+   * @param sdk the Dart SDK against which URI's are to be resolved
+   */
+  DartUriResolver(this._sdk);
+
+  /**
+   * Return the [DartSdk] against which URIs are to be resolved.
+   *
+   * @return the [DartSdk] against which URIs are to be resolved.
+   */
+  DartSdk get dartSdk => _sdk;
+
+  @override
+  Source resolveAbsolute(Uri uri) {
+    if (!isDartUri(uri)) {
+      return null;
+    }
+    return _sdk.mapDartUri(uri.toString());
+  }
+
+  /**
+   * Return `true` if the given URI is a `dart-ext:` URI.
+   *
+   * @param uriContent the textual representation of the URI being tested
+   * @return `true` if the given URI is a `dart-ext:` URI
+   */
+  static bool isDartExtUri(String uriContent) =>
+      uriContent != null && uriContent.startsWith(_DART_EXT_SCHEME);
+
+  /**
+   * Return `true` if the given URI is a `dart:` URI.
+   *
+   * @param uri the URI being tested
+   * @return `true` if the given URI is a `dart:` URI
+   */
+  static bool isDartUri(Uri uri) => DART_SCHEME == uri.scheme;
+}
+
+class CustomUriResolver extends UriResolver {
+  final Map<String, String> _urlMappings;
+
+  CustomUriResolver(this._urlMappings);
+
+  @override
+  Source resolveAbsolute(Uri uri) {
+    String mapping = _urlMappings[uri.toString()];
+    if (mapping == null) return null;
+
+    Uri fileUri = new Uri.file(mapping);
+    if (!fileUri.isAbsolute) return null;
+
+    JavaFile javaFile = new JavaFile.fromUri(fileUri);
+    return new FileBasedSource.con1(javaFile);
+  }
+}
+
+/**
+ * Instances of the class `LineInfo` encapsulate information about line and column information
+ * within a source file.
+ */
+class LineInfo {
+  /**
+   * An array containing the offsets of the first character of each line in the source code.
+   */
+  final List<int> _lineStarts;
+
+  /**
+   * The zero-based [_lineStarts] index resulting from the last call to
+   * [getLocation].
+   */
+  int _previousLine = 0;
+
+  /**
+   * Initialize a newly created set of line information to represent the data encoded in the given
+   * array.
+   *
+   * @param lineStarts the offsets of the first character of each line in the source code
+   */
+  LineInfo(this._lineStarts) {
+    if (_lineStarts == null) {
+      throw new IllegalArgumentException("lineStarts must be non-null");
+    } else if (_lineStarts.length < 1) {
+      throw new IllegalArgumentException("lineStarts must be non-empty");
+    }
+  }
+
+  /**
+   * Return the location information for the character at the given offset.
+   *
+   * @param offset the offset of the character for which location information is to be returned
+   * @return the location information for the character at the given offset
+   */
+  LineInfo_Location getLocation(int offset) {
+    var min = 0;
+    var max = _lineStarts.length - 1;
+
+    // Subsequent calls to [getLocation] are often for offsets near each other.
+    // To take advantage of that, we cache the index of the line start we found
+    // when this was last called. If the current offset is on that line or
+    // later, we'll skip those early indices completely when searching.
+    if (offset >= _lineStarts[_previousLine]) {
+      min = _previousLine;
+
+      // Before kicking off a full binary search, do a quick check here to see
+      // if the new offset is on that exact line.
+      if (min == _lineStarts.length - 1 || offset < _lineStarts[min + 1]) {
+        return new LineInfo_Location(min + 1, offset - _lineStarts[min] + 1);
+      }
+    }
+
+    // Binary search to fine the line containing this offset.
+    while (min < max) {
+      var midpoint = (max - min + 1) ~/ 2 + min;
+
+      if (_lineStarts[midpoint] > offset) {
+        max = midpoint - 1;
+      } else {
+        min = midpoint;
+      }
+    }
+
+    _previousLine = min;
+
+    return new LineInfo_Location(min + 1, offset - _lineStarts[min] + 1);
+  }
+}
+
+/**
+ * Instances of the class `Location` represent the location of a character as a line and
+ * column pair.
+ */
+class LineInfo_Location {
+  /**
+   * The one-based index of the line containing the character.
+   */
+  final int lineNumber;
+
+  /**
+   * The one-based index of the column containing the character.
+   */
+  final int columnNumber;
+
+  /**
+   * Initialize a newly created location to represent the location of the character at the given
+   * line and column position.
+   *
+   * @param lineNumber the one-based index of the line containing the character
+   * @param columnNumber the one-based index of the column containing the character
+   */
+  LineInfo_Location(this.lineNumber, this.columnNumber);
+}
+
+/**
+ * Instances of interface `LocalSourcePredicate` are used to determine if the given
+ * [Source] is "local" in some sense, so can be updated.
+ */
+abstract class LocalSourcePredicate {
+  /**
+   * Instance of [LocalSourcePredicate] that always returns `false`.
+   */
+  static final LocalSourcePredicate FALSE = new LocalSourcePredicate_FALSE();
+
+  /**
+   * Instance of [LocalSourcePredicate] that always returns `true`.
+   */
+  static final LocalSourcePredicate TRUE = new LocalSourcePredicate_TRUE();
+
+  /**
+   * Instance of [LocalSourcePredicate] that returns `true` for all [Source]s
+   * except of SDK.
+   */
+  static final LocalSourcePredicate NOT_SDK =
+      new LocalSourcePredicate_NOT_SDK();
+
+  /**
+   * Determines if the given [Source] is local.
+   *
+   * @param source the [Source] to analyze
+   * @return `true` if the given [Source] is local
+   */
+  bool isLocal(Source source);
+}
+
+class LocalSourcePredicate_FALSE implements LocalSourcePredicate {
+  @override
+  bool isLocal(Source source) => false;
+}
+
+class LocalSourcePredicate_NOT_SDK implements LocalSourcePredicate {
+  @override
+  bool isLocal(Source source) => source.uriKind != UriKind.DART_URI;
+}
+
+class LocalSourcePredicate_TRUE implements LocalSourcePredicate {
+  @override
+  bool isLocal(Source source) => true;
+}
+
+/**
+ * An implementation of an non-existing [Source].
+ */
+class NonExistingSource extends Source {
+  final String _name;
+
+  final UriKind uriKind;
+
+  NonExistingSource(this._name, this.uriKind);
+
+  @override
+  TimestampedData<String> get contents {
+    throw new UnsupportedOperationException("${_name}does not exist.");
+  }
+
+  @override
+  String get encoding {
+    throw new UnsupportedOperationException("${_name}does not exist.");
+  }
+
+  @override
+  String get fullName => _name;
+
+  @override
+  int get hashCode => _name.hashCode;
+
+  @override
+  bool get isInSystemLibrary => false;
+
+  @override
+  int get modificationStamp => 0;
+
+  @override
+  String get shortName => _name;
+
+  @override
+  Uri get uri => null;
+
+  @override
+  bool operator ==(Object obj) {
+    if (obj is NonExistingSource) {
+      NonExistingSource other = obj;
+      return other.uriKind == uriKind && (other._name == _name);
+    }
+    return false;
+  }
+
+  @override
+  bool exists() => false;
+
+  @override
+  Uri resolveRelativeUri(Uri relativeUri) {
+    throw new UnsupportedOperationException("${_name}does not exist.");
+  }
+}
+
+/**
+ * The interface `Source` defines the behavior of objects representing source code that can be
+ * analyzed by the analysis engine.
+ *
+ * Implementations of this interface need to be aware of some assumptions made by the analysis
+ * engine concerning sources:
+ * * Sources are not required to be unique. That is, there can be multiple instances representing
+ * the same source.
+ * * Sources are long lived. That is, the engine is allowed to hold on to a source for an extended
+ * period of time and that source must continue to report accurate and up-to-date information.
+ * Because of these assumptions, most implementations will not maintain any state but will delegate
+ * to an authoritative system of record in order to implement this API. For example, a source that
+ * represents files on disk would typically query the file system to determine the state of the
+ * file.
+ *
+ * If the instances that implement this API are the system of record, then they will typically be
+ * unique. In that case, sources that are created that represent non-existent files must also be
+ * retained so that if those files are created at a later date the long-lived sources representing
+ * those files will know that they now exist.
+ */
+abstract class Source implements AnalysisTarget {
+  /**
+   * An empty list of sources.
+   */
+  static const List<Source> EMPTY_ARRAY = const <Source>[];
+
+  /**
+   * Get the contents and timestamp of this source.
+   *
+   * Clients should consider using the the method [AnalysisContext.getContents]
+   * because contexts can have local overrides of the content of a source that the source is not
+   * aware of.
+   *
+   * @return the contents and timestamp of the source
+   * @throws Exception if the contents of this source could not be accessed
+   */
+  TimestampedData<String> get contents;
+
+  /**
+   * Return an encoded representation of this source that can be used to create a source that is
+   * equal to this source.
+   *
+   * @return an encoded representation of this source
+   * See [SourceFactory.fromEncoding].
+   */
+  String get encoding;
+
+  @override
+  Source get source => this;
+
+  /**
+   * Return the full (long) version of the name that can be displayed to the user to denote this
+   * source. For example, for a source representing a file this would typically be the absolute path
+   * of the file.
+   *
+   * @return a name that can be displayed to the user to denote this source
+   */
+  String get fullName;
+
+  /**
+   * Return a hash code for this source.
+   *
+   * @return a hash code for this source
+   * See [Object.hashCode].
+   */
+  @override
+  int get hashCode;
+
+  /**
+   * Return `true` if this source is in one of the system libraries.
+   *
+   * @return `true` if this is in a system library
+   */
+  bool get isInSystemLibrary;
+
+  /**
+   * Return the modification stamp for this source, or a negative value if the
+   * source does not exist. A modification stamp is a non-negative integer with
+   * the property that if the contents of the source have not been modified
+   * since the last time the modification stamp was accessed then the same value
+   * will be returned, but if the contents of the source have been modified one
+   * or more times (even if the net change is zero) the stamps will be different.
+   *
+   * Clients should consider using the the method
+   * [AnalysisContext.getModificationStamp] because contexts can have local
+   * overrides of the content of a source that the source is not aware of.
+   */
+  int get modificationStamp;
+
+  /**
+   * Return a short version of the name that can be displayed to the user to denote this source. For
+   * example, for a source representing a file this would typically be the name of the file.
+   *
+   * @return a name that can be displayed to the user to denote this source
+   */
+  String get shortName;
+
+  /**
+   * Return the URI from which this source was originally derived.
+   *
+   * @return the URI from which this source was originally derived
+   */
+  Uri get uri;
+
+  /**
+   * Return the kind of URI from which this source was originally derived. If this source was
+   * created from an absolute URI, then the returned kind will reflect the scheme of the absolute
+   * URI. If it was created from a relative URI, then the returned kind will be the same as the kind
+   * of the source against which the relative URI was resolved.
+   *
+   * @return the kind of URI from which this source was originally derived
+   */
+  UriKind get uriKind;
+
+  /**
+   * Return `true` if the given object is a source that represents the same source code as
+   * this source.
+   *
+   * @param object the object to be compared with this object
+   * @return `true` if the given object is a source that represents the same source code as
+   *         this source
+   * See [Object.==].
+   */
+  @override
+  bool operator ==(Object object);
+
+  /**
+   * Return `true` if this source exists.
+   *
+   * Clients should consider using the the method [AnalysisContext.exists] because
+   * contexts can have local overrides of the content of a source that the source is not aware of
+   * and a source with local content is considered to exist even if there is no file on disk.
+   *
+   * @return `true` if this source exists
+   */
+  bool exists();
+
+  /**
+   * Resolve the relative URI against the URI associated with this source object.
+   *
+   * Note: This method is not intended for public use, it is only visible out of necessity. It is
+   * only intended to be invoked by a [SourceFactory]. Source factories will
+   * only invoke this method if the URI is relative, so implementations of this method are not
+   * required to, and generally do not, verify the argument. The result of invoking this method with
+   * an absolute URI is intentionally left unspecified.
+   *
+   * @param relativeUri the relative URI to be resolved against this source
+   * @return the URI to which given URI was resolved
+   * @throws AnalysisException if the relative URI could not be resolved
+   */
+  Uri resolveRelativeUri(Uri relativeUri);
+}
+
+/**
+ * The interface `ContentReceiver` defines the behavior of objects that can receive the
+ * content of a source.
+ */
+abstract class Source_ContentReceiver {
+  /**
+   * Accept the contents of a source.
+   *
+   * @param contents the contents of the source
+   * @param modificationTime the time at which the contents were last set
+   */
+  void accept(String contents, int modificationTime);
+}
+
+/**
+ * The interface `SourceContainer` is used by clients to define a collection of sources
+ *
+ * Source containers are not used within analysis engine, but can be used by clients to group
+ * sources for the purposes of accessing composite dependency information. For example, the Eclipse
+ * client uses source containers to represent Eclipse projects, which allows it to easily compute
+ * project-level dependencies.
+ */
+abstract class SourceContainer {
+  /**
+   * Determine if the specified source is part of the receiver's collection of sources.
+   *
+   * @param source the source in question
+   * @return `true` if the receiver contains the source, else `false`
+   */
+  bool contains(Source source);
+}
+
+/**
+ * Instances of the class `SourceFactory` resolve possibly relative URI's against an existing
+ * [Source].
+ */
+class SourceFactory {
+  /**
+   * The analysis context that this source factory is associated with.
+   */
+  AnalysisContext context;
+
+  /**
+   * The resolvers used to resolve absolute URI's.
+   */
+  final List<UriResolver> _resolvers;
+
+  /**
+   * The predicate to determine is [Source] is local.
+   */
+  LocalSourcePredicate _localSourcePredicate = LocalSourcePredicate.NOT_SDK;
+
+  /**
+   * Initialize a newly created source factory.
+   *
+   * @param resolvers the resolvers used to resolve absolute URI's
+   */
+  SourceFactory(this._resolvers);
+
+  /**
+   * Return the [DartSdk] associated with this [SourceFactory], or `null` if there
+   * is no such SDK.
+   *
+   * @return the [DartSdk] associated with this [SourceFactory], or `null` if
+   *         there is no such SDK
+   */
+  DartSdk get dartSdk {
+    for (UriResolver resolver in _resolvers) {
+      if (resolver is DartUriResolver) {
+        DartUriResolver dartUriResolver = resolver;
+        return dartUriResolver.dartSdk;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Sets the [LocalSourcePredicate].
+   *
+   * @param localSourcePredicate the predicate to determine is [Source] is local
+   */
+  void set localSourcePredicate(LocalSourcePredicate localSourcePredicate) {
+    this._localSourcePredicate = localSourcePredicate;
+  }
+
+  /// A table mapping package names to paths of directories containing
+  /// the package (or [null] if there is no registered package URI resolver).
+  Map<String, List<Folder>> get packageMap {
+    PackageMapUriResolver resolver = _resolvers.firstWhere(
+        (r) => r is PackageMapUriResolver, orElse: () => null);
+    return resolver != null ? resolver.packageMap : null;
+  }
+
+  /**
+   * Return a source object representing the given absolute URI, or `null` if the URI is not a
+   * valid URI or if it is not an absolute URI.
+   *
+   * @param absoluteUri the absolute URI to be resolved
+   * @return a source object representing the absolute URI
+   */
+  Source forUri(String absoluteUri) {
+    try {
+      Uri uri = parseUriWithException(absoluteUri);
+      if (uri.isAbsolute) {
+        return _internalResolveUri(null, uri);
+      }
+    } catch (exception, stackTrace) {
+      AnalysisEngine.instance.logger.logError(
+          "Could not resolve URI: $absoluteUri",
+          new CaughtException(exception, stackTrace));
+    }
+    return null;
+  }
+
+  /**
+   * Return a source object representing the given absolute URI, or `null` if the URI is not
+   * an absolute URI.
+   *
+   * @param absoluteUri the absolute URI to be resolved
+   * @return a source object representing the absolute URI
+   */
+  Source forUri2(Uri absoluteUri) {
+    if (absoluteUri.isAbsolute) {
+      try {
+        return _internalResolveUri(null, absoluteUri);
+      } on AnalysisException catch (exception, stackTrace) {
+        AnalysisEngine.instance.logger.logError(
+            "Could not resolve URI: $absoluteUri",
+            new CaughtException(exception, stackTrace));
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return a source object that is equal to the source object used to obtain the given encoding.
+   *
+   * @param encoding the encoding of a source object
+   * @return a source object that is described by the given encoding
+   * @throws IllegalArgumentException if the argument is not a valid encoding
+   * See [Source.encoding].
+   */
+  Source fromEncoding(String encoding) {
+    Source source = forUri(encoding);
+    if (source == null) {
+      throw new IllegalArgumentException(
+          "Invalid source encoding: '$encoding'");
+    }
+    return source;
+  }
+
+  /**
+   * Determines if the given [Source] is local.
+   *
+   * @param source the [Source] to analyze
+   * @return `true` if the given [Source] is local
+   */
+  bool isLocalSource(Source source) => _localSourcePredicate.isLocal(source);
+
+  /**
+   * Return a source object representing the URI that results from resolving the given (possibly
+   * relative) contained URI against the URI associated with an existing source object, whether or
+   * not the resulting source exists, or `null` if either the contained URI is invalid or if
+   * it cannot be resolved against the source object's URI.
+   *
+   * @param containingSource the source containing the given URI
+   * @param containedUri the (possibly relative) URI to be resolved against the containing source
+   * @return the source representing the contained URI
+   */
+  Source resolveUri(Source containingSource, String containedUri) {
+    if (containedUri == null || containedUri.isEmpty) {
+      return null;
+    }
+    try {
+      // Force the creation of an escaped URI to deal with spaces, etc.
+      return _internalResolveUri(
+          containingSource, parseUriWithException(containedUri));
+    } catch (exception, stackTrace) {
+      String containingFullName =
+          containingSource != null ? containingSource.fullName : '<null>';
+      AnalysisEngine.instance.logger.logError(
+          "Could not resolve URI ($containedUri) relative to source ($containingFullName)",
+          new CaughtException(exception, stackTrace));
+      return null;
+    }
+  }
+
+  /**
+   * Return an absolute URI that represents the given source, or `null` if a valid URI cannot
+   * be computed.
+   *
+   * @param source the source to get URI for
+   * @return the absolute URI representing the given source
+   */
+  Uri restoreUri(Source source) {
+    for (UriResolver resolver in _resolvers) {
+      Uri uri = resolver.restoreAbsolute(source);
+      if (uri != null) {
+        return uri;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return a source object representing the URI that results from resolving the given (possibly
+   * relative) contained URI against the URI associated with an existing source object, or
+   * `null` if the URI could not be resolved.
+   *
+   * @param containingSource the source containing the given URI
+   * @param containedUri the (possibly relative) URI to be resolved against the containing source
+   * @return the source representing the contained URI
+   * @throws AnalysisException if either the contained URI is invalid or if it cannot be resolved
+   *           against the source object's URI
+   */
+  Source _internalResolveUri(Source containingSource, Uri containedUri) {
+    if (!containedUri.isAbsolute) {
+      if (containingSource == null) {
+        throw new AnalysisException(
+            "Cannot resolve a relative URI without a containing source: $containedUri");
+      }
+      containedUri = containingSource.resolveRelativeUri(containedUri);
+    }
+    for (UriResolver resolver in _resolvers) {
+      Source result = resolver.resolveAbsolute(containedUri);
+      if (result != null) {
+        return result;
+      }
+    }
+    return null;
+  }
+}
+
+/**
+ * The enumeration `SourceKind` defines the different kinds of sources that are known to the
+ * analysis engine.
+ */
+class SourceKind extends Enum<SourceKind> {
+  /**
+   * A source containing HTML. The HTML might or might not contain Dart scripts.
+   */
+  static const SourceKind HTML = const SourceKind('HTML', 0);
+
+  /**
+   * A Dart compilation unit that is not a part of another library. Libraries might or might not
+   * contain any directives, including a library directive.
+   */
+  static const SourceKind LIBRARY = const SourceKind('LIBRARY', 1);
+
+  /**
+   * A Dart compilation unit that is part of another library. Parts contain a part-of directive.
+   */
+  static const SourceKind PART = const SourceKind('PART', 2);
+
+  /**
+   * An unknown kind of source. Used both when it is not possible to identify the kind of a source
+   * and also when the kind of a source is not known without performing a computation and the client
+   * does not want to spend the time to identify the kind.
+   */
+  static const SourceKind UNKNOWN = const SourceKind('UNKNOWN', 3);
+
+  static const List<SourceKind> values = const [HTML, LIBRARY, PART, UNKNOWN];
+
+  const SourceKind(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * A source range defines an [Element]'s source coordinates relative to its [Source].
+ */
+class SourceRange {
+  /**
+   * An empty [SourceRange] with offset `0` and length `0`.
+   */
+  static SourceRange EMPTY = new SourceRange(0, 0);
+
+  /**
+   * The 0-based index of the first character of the source code for this element, relative to the
+   * source buffer in which this element is contained.
+   */
+  final int offset;
+
+  /**
+   * The number of characters of the source code for this element, relative to the source buffer in
+   * which this element is contained.
+   */
+  final int length;
+
+  /**
+   * Initialize a newly created source range using the given offset and the given length.
+   *
+   * @param offset the given offset
+   * @param length the given length
+   */
+  SourceRange(this.offset, this.length);
+
+  /**
+   * @return the 0-based index of the after-last character of the source code for this element,
+   *         relative to the source buffer in which this element is contained.
+   */
+  int get end => offset + length;
+
+  @override
+  int get hashCode => 31 * offset + length;
+
+  @override
+  bool operator ==(Object obj) {
+    if (obj is! SourceRange) {
+      return false;
+    }
+    SourceRange sourceRange = obj as SourceRange;
+    return sourceRange.offset == offset && sourceRange.length == length;
+  }
+
+  /**
+   * @return `true` if <code>x</code> is in [offset, offset + length) interval.
+   */
+  bool contains(int x) => offset <= x && x < offset + length;
+
+  /**
+   * @return `true` if <code>x</code> is in (offset, offset + length) interval.
+   */
+  bool containsExclusive(int x) => offset < x && x < offset + length;
+
+  /**
+   * @return `true` if <code>otherRange</code> covers this [SourceRange].
+   */
+  bool coveredBy(SourceRange otherRange) => otherRange.covers(this);
+
+  /**
+   * @return `true` if this [SourceRange] covers <code>otherRange</code>.
+   */
+  bool covers(SourceRange otherRange) =>
+      offset <= otherRange.offset && otherRange.end <= end;
+
+  /**
+   * @return `true` if this [SourceRange] ends in <code>otherRange</code>.
+   */
+  bool endsIn(SourceRange otherRange) {
+    int thisEnd = end;
+    return otherRange.contains(thisEnd);
+  }
+
+  /**
+   * @return the expanded instance of [SourceRange], which has the same center.
+   */
+  SourceRange getExpanded(int delta) =>
+      new SourceRange(offset - delta, delta + length + delta);
+
+  /**
+   * @return the instance of [SourceRange] with end moved on "delta".
+   */
+  SourceRange getMoveEnd(int delta) => new SourceRange(offset, length + delta);
+
+  /**
+   * @return the expanded translated of [SourceRange], with moved start and the same length.
+   */
+  SourceRange getTranslated(int delta) =>
+      new SourceRange(offset + delta, length);
+
+  /**
+   * @return the minimal [SourceRange] that cover this and the given [SourceRange]s.
+   */
+  SourceRange getUnion(SourceRange other) {
+    int newOffset = math.min(offset, other.offset);
+    int newEnd = math.max(offset + length, other.offset + other.length);
+    return new SourceRange(newOffset, newEnd - newOffset);
+  }
+
+  /**
+   * @return `true` if this [SourceRange] intersects with given.
+   */
+  bool intersects(SourceRange other) {
+    if (other == null) {
+      return false;
+    }
+    if (end <= other.offset) {
+      return false;
+    }
+    if (offset >= other.end) {
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Return `true` if this [SourceRange] starts in the [otherRange].
+   */
+  bool startsIn(SourceRange otherRange) => otherRange.contains(offset);
+
+  @override
+  String toString() => '[offset=$offset, length=$length]';
+}
+
+/**
+ * The enumeration `UriKind` defines the different kinds of URI's that are known to the
+ * analysis engine. These are used to keep track of the kind of URI associated with a given source.
+ */
+class UriKind extends Enum<UriKind> {
+  /**
+   * A 'dart:' URI.
+   */
+  static const UriKind DART_URI = const UriKind('DART_URI', 0, 0x64);
+
+  /**
+   * A 'file:' URI.
+   */
+  static const UriKind FILE_URI = const UriKind('FILE_URI', 1, 0x66);
+
+  /**
+   * A 'package:' URI.
+   */
+  static const UriKind PACKAGE_URI = const UriKind('PACKAGE_URI', 2, 0x70);
+
+  static const List<UriKind> values = const [DART_URI, FILE_URI, PACKAGE_URI];
+
+  /**
+   * The single character encoding used to identify this kind of URI.
+   */
+  final int encoding;
+
+  /**
+   * Initialize a newly created URI kind to have the given encoding.
+   *
+   * @param encoding the single character encoding used to identify this kind of URI.
+   */
+  const UriKind(String name, int ordinal, this.encoding) : super(name, ordinal);
+
+  /**
+   * Return the URI kind represented by the given encoding, or `null` if there is no kind with
+   * the given encoding.
+   *
+   * @param encoding the single character encoding used to identify the URI kind to be returned
+   * @return the URI kind represented by the given encoding
+   */
+  static UriKind fromEncoding(int encoding) {
+    while (true) {
+      if (encoding == 0x64) {
+        return DART_URI;
+      } else if (encoding == 0x66) {
+        return FILE_URI;
+      } else if (encoding == 0x70) {
+        return PACKAGE_URI;
+      }
+      break;
+    }
+    return null;
+  }
+}
+
+/**
+ * The abstract class `UriResolver` defines the behavior of objects that are used to resolve
+ * URI's for a source factory. Subclasses of this class are expected to resolve a single scheme of
+ * absolute URI.
+ */
+abstract class UriResolver {
+  /**
+   * Resolve the given absolute URI. Return a [Source] representing the file to which
+   * it was resolved, whether or not the resulting source exists, or `null` if it could not be
+   * resolved because the URI is invalid.
+   *
+   * @param uri the URI to be resolved
+   * @return a [Source] representing the file to which given URI was resolved
+   */
+  Source resolveAbsolute(Uri uri);
+
+  /**
+   * Return an absolute URI that represents the given source, or `null` if a valid URI cannot
+   * be computed.
+   *
+   * @param source the source to get URI for
+   * @return the absolute URI representing the given source
+   */
+  Uri restoreAbsolute(Source source) => null;
+}
diff --git a/analyzer/lib/src/generated/source_io.dart b/analyzer/lib/src/generated/source_io.dart
new file mode 100644
index 0000000..e885cd8
--- /dev/null
+++ b/analyzer/lib/src/generated/source_io.dart
@@ -0,0 +1,540 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.source.io;
+
+import 'dart:collection';
+
+import 'engine.dart';
+import 'java_core.dart';
+import 'java_engine.dart';
+import 'java_io.dart';
+import 'source.dart';
+
+export 'source.dart';
+
+/**
+ * Instances of the class [DirectoryBasedSourceContainer] represent a source container that
+ * contains all sources within a given directory.
+ */
+class DirectoryBasedSourceContainer implements SourceContainer {
+  /**
+   * The container's path (not `null`).
+   */
+  String _path;
+
+  /**
+   * Construct a container representing the specified directory and containing any sources whose
+   * [Source.fullName] starts with the directory's path. This is a convenience method,
+   * fully equivalent to [DirectoryBasedSourceContainer.con2].
+   *
+   * @param directory the directory (not `null`)
+   */
+  DirectoryBasedSourceContainer.con1(JavaFile directory)
+      : this.con2(directory.getPath());
+
+  /**
+   * Construct a container representing the specified path and containing any sources whose
+   * [Source.fullName] starts with the specified path.
+   *
+   * @param path the path (not `null` and not empty)
+   */
+  DirectoryBasedSourceContainer.con2(String path) {
+    this._path = _appendFileSeparator(path);
+  }
+
+  @override
+  int get hashCode => _path.hashCode;
+
+  /**
+   * Answer the receiver's path, used to determine if a source is contained in the receiver.
+   *
+   * @return the path (not `null`, not empty)
+   */
+  String get path => _path;
+
+  @override
+  bool operator ==(Object obj) =>
+      (obj is DirectoryBasedSourceContainer) && obj.path == path;
+
+  @override
+  bool contains(Source source) => source.fullName.startsWith(_path);
+
+  @override
+  String toString() => "SourceContainer[$_path]";
+
+  /**
+   * Append the system file separator to the given path unless the path already ends with a
+   * separator.
+   *
+   * @param path the path to which the file separator is to be added
+   * @return a path that ends with the system file separator
+   */
+  static String _appendFileSeparator(String path) {
+    if (path == null ||
+        path.length <= 0 ||
+        path.codeUnitAt(path.length - 1) == JavaFile.separatorChar) {
+      return path;
+    }
+    return "$path${JavaFile.separator}";
+  }
+}
+
+/**
+ * Instances of the class `FileBasedSource` implement a source that represents a file.
+ */
+class FileBasedSource extends Source {
+  /**
+   * A function that changes the way that files are read off of disk.
+   */
+  static Function fileReadMode = (String s) => s;
+
+  /**
+   * Map from encoded URI/filepath pair to a unique integer identifier.  This
+   * identifier is used for equality tests and hash codes.
+   *
+   * The URI and filepath are joined into a pair by separating them with an '@'
+   * character.
+   */
+  static final Map<String, int> _idTable = new HashMap<String, int>();
+
+  /**
+   * The URI from which this source was originally derived.
+   */
+  final Uri uri;
+
+  /**
+   * The unique ID associated with this [FileBasedSource].
+   */
+  final int id;
+
+  /**
+   * The file represented by this source.
+   */
+  final JavaFile file;
+
+  /**
+   * The cached absolute path of this source.
+   */
+  String _absolutePath;
+
+  /**
+   * The cached encoding for this source.
+   */
+  String _encoding;
+
+  /**
+   * Initialize a newly created source object.
+   *
+   * @param file the file represented by this source
+   */
+  FileBasedSource.con1(JavaFile file) : this.con2(file.toURI(), file);
+
+  /**
+   * Initialize a newly created source object.
+   *
+   * @param file the file represented by this source
+   * @param uri the URI from which this source was originally derived
+   */
+  FileBasedSource.con2(Uri uri, JavaFile file)
+      : uri = uri,
+        file = file,
+        id = _idTable.putIfAbsent(
+            '$uri@${file.getPath()}', () => _idTable.length);
+
+  @override
+  TimestampedData<String> get contents {
+    return PerformanceStatistics.io.makeCurrentWhile(() {
+      return contentsFromFile;
+    });
+  }
+
+  /**
+   * Get the contents and timestamp of the underlying file.
+   *
+   * Clients should consider using the the method [AnalysisContext.getContents]
+   * because contexts can have local overrides of the content of a source that the source is not
+   * aware of.
+   *
+   * @return the contents of the source paired with the modification stamp of the source
+   * @throws Exception if the contents of this source could not be accessed
+   * See [contents].
+   */
+  TimestampedData<String> get contentsFromFile {
+    return new TimestampedData<String>(
+        file.lastModified(), fileReadMode(file.readAsStringSync()));
+  }
+
+  @override
+  String get encoding {
+    if (_encoding == null) {
+      _encoding = uri.toString();
+    }
+    return _encoding;
+  }
+
+  @override
+  String get fullName {
+    if (_absolutePath == null) {
+      _absolutePath = file.getAbsolutePath();
+    }
+    return _absolutePath;
+  }
+
+  @override
+  int get hashCode => id;
+
+  @override
+  bool get isInSystemLibrary => uri.scheme == DartUriResolver.DART_SCHEME;
+
+  @override
+  int get modificationStamp => file.lastModified();
+
+  @override
+  String get shortName => file.getName();
+
+  @override
+  UriKind get uriKind {
+    String scheme = uri.scheme;
+    if (scheme == PackageUriResolver.PACKAGE_SCHEME) {
+      return UriKind.PACKAGE_URI;
+    } else if (scheme == DartUriResolver.DART_SCHEME) {
+      return UriKind.DART_URI;
+    } else if (scheme == FileUriResolver.FILE_SCHEME) {
+      return UriKind.FILE_URI;
+    }
+    return UriKind.FILE_URI;
+  }
+
+  @override
+  bool operator ==(Object object) =>
+      object is FileBasedSource && id == object.id;
+
+  @override
+  bool exists() => file.isFile();
+
+  @override
+  Uri resolveRelativeUri(Uri containedUri) {
+    try {
+      Uri baseUri = uri;
+      bool isOpaque = uri.isAbsolute && !uri.path.startsWith('/');
+      if (isOpaque) {
+        String scheme = uri.scheme;
+        String part = uri.path;
+        if (scheme == DartUriResolver.DART_SCHEME && part.indexOf('/') < 0) {
+          part = "$part/$part.dart";
+        }
+        baseUri = parseUriWithException("$scheme:/$part");
+      }
+      Uri result = baseUri.resolveUri(containedUri);
+      if (isOpaque) {
+        result = parseUriWithException(
+            "${result.scheme}:${result.path.substring(1)}");
+      }
+      return result;
+    } catch (exception, stackTrace) {
+      throw new AnalysisException(
+          "Could not resolve URI ($containedUri) relative to source ($uri)",
+          new CaughtException(exception, stackTrace));
+    }
+  }
+
+  @override
+  String toString() {
+    if (file == null) {
+      return "<unknown source>";
+    }
+    return file.getAbsolutePath();
+  }
+}
+
+/**
+ * Instances of the class `FileUriResolver` resolve `file` URI's.
+ */
+class FileUriResolver extends UriResolver {
+  /**
+   * The name of the `file` scheme.
+   */
+  static String FILE_SCHEME = "file";
+
+  @override
+  Source resolveAbsolute(Uri uri) {
+    if (!isFileUri(uri)) {
+      return null;
+    }
+    return new FileBasedSource.con2(uri, new JavaFile.fromUri(uri));
+  }
+
+  /**
+   * Return `true` if the given URI is a `file` URI.
+   *
+   * @param uri the URI being tested
+   * @return `true` if the given URI is a `file` URI
+   */
+  static bool isFileUri(Uri uri) => uri.scheme == FILE_SCHEME;
+}
+
+/**
+ * Instances of interface `LocalSourcePredicate` are used to determine if the given
+ * [Source] is "local" in some sense, so can be updated.
+ */
+abstract class LocalSourcePredicate {
+  /**
+   * Instance of [LocalSourcePredicate] that always returns `false`.
+   */
+  static final LocalSourcePredicate FALSE = new LocalSourcePredicate_FALSE();
+
+  /**
+   * Instance of [LocalSourcePredicate] that always returns `true`.
+   */
+  static final LocalSourcePredicate TRUE = new LocalSourcePredicate_TRUE();
+
+  /**
+   * Instance of [LocalSourcePredicate] that returns `true` for all [Source]s
+   * except of SDK.
+   */
+  static final LocalSourcePredicate NOT_SDK =
+      new LocalSourcePredicate_NOT_SDK();
+
+  /**
+   * Determines if the given [Source] is local.
+   *
+   * @param source the [Source] to analyze
+   * @return `true` if the given [Source] is local
+   */
+  bool isLocal(Source source);
+}
+
+class LocalSourcePredicate_FALSE implements LocalSourcePredicate {
+  @override
+  bool isLocal(Source source) => false;
+}
+
+class LocalSourcePredicate_NOT_SDK implements LocalSourcePredicate {
+  @override
+  bool isLocal(Source source) => source.uriKind != UriKind.DART_URI;
+}
+
+class LocalSourcePredicate_TRUE implements LocalSourcePredicate {
+  @override
+  bool isLocal(Source source) => true;
+}
+
+/**
+ * Instances of the class `PackageUriResolver` resolve `package` URI's in the context of
+ * an application.
+ *
+ * For the purposes of sharing analysis, the path to each package under the "packages" directory
+ * should be canonicalized, but to preserve relative links within a package, the remainder of the
+ * path from the package directory to the leaf should not.
+ */
+class PackageUriResolver extends UriResolver {
+  /**
+   * The name of the `package` scheme.
+   */
+  static String PACKAGE_SCHEME = "package";
+
+  /**
+   * Log exceptions thrown with the message "Required key not available" only once.
+   */
+  static bool _CanLogRequiredKeyIoException = true;
+
+  /**
+   * The package directories that `package` URI's are assumed to be relative to.
+   */
+  final List<JavaFile> _packagesDirectories;
+
+  /**
+   * Initialize a newly created resolver to resolve `package` URI's relative to the given
+   * package directories.
+   *
+   * @param packagesDirectories the package directories that `package` URI's are assumed to be
+   *          relative to
+   */
+  PackageUriResolver(this._packagesDirectories) {
+    if (_packagesDirectories.length < 1) {
+      throw new IllegalArgumentException(
+          "At least one package directory must be provided");
+    }
+  }
+
+  /**
+   * If the list of package directories contains one element, return it.
+   * Otherwise raise an exception.  Intended for testing.
+   */
+  String get packagesDirectory_forTesting {
+    int length = _packagesDirectories.length;
+    if (length != 1) {
+      throw new Exception('Expected 1 package directory, found $length');
+    }
+    return _packagesDirectories[0].getPath();
+  }
+
+  /**
+   * Answer the canonical file for the specified package.
+   *
+   * @param packagesDirectory the "packages" directory (not `null`)
+   * @param pkgName the package name (not `null`, not empty)
+   * @param relPath the path relative to the package directory (not `null`, no leading slash,
+   *          but may be empty string)
+   * @return the file (not `null`)
+   */
+  JavaFile getCanonicalFile(
+      JavaFile packagesDirectory, String pkgName, String relPath) {
+    JavaFile pkgDir = new JavaFile.relative(packagesDirectory, pkgName);
+    try {
+      pkgDir = pkgDir.getCanonicalFile();
+    } on JavaIOException catch (exception, stackTrace) {
+      if (!exception.toString().contains("Required key not available")) {
+        AnalysisEngine.instance.logger.logError("Canonical failed: $pkgDir",
+            new CaughtException(exception, stackTrace));
+      } else if (_CanLogRequiredKeyIoException) {
+        _CanLogRequiredKeyIoException = false;
+        AnalysisEngine.instance.logger.logError("Canonical failed: $pkgDir",
+            new CaughtException(exception, stackTrace));
+      }
+    }
+    return new JavaFile.relative(pkgDir, relPath.replaceAll(
+        '/', new String.fromCharCode(JavaFile.separatorChar)));
+  }
+
+  @override
+  Source resolveAbsolute(Uri uri) {
+    if (!isPackageUri(uri)) {
+      return null;
+    }
+    String path = uri.path;
+    if (path == null) {
+      path = uri.path;
+      if (path == null) {
+        return null;
+      }
+    }
+    String pkgName;
+    String relPath;
+    int index = path.indexOf('/');
+    if (index == -1) {
+      // No slash
+      pkgName = path;
+      relPath = "";
+    } else if (index == 0) {
+      // Leading slash is invalid
+      return null;
+    } else {
+      // <pkgName>/<relPath>
+      pkgName = path.substring(0, index);
+      relPath = path.substring(index + 1);
+    }
+    for (JavaFile packagesDirectory in _packagesDirectories) {
+      JavaFile resolvedFile = new JavaFile.relative(packagesDirectory, path);
+      if (resolvedFile.exists()) {
+        JavaFile canonicalFile =
+            getCanonicalFile(packagesDirectory, pkgName, relPath);
+        if (_isSelfReference(packagesDirectory, canonicalFile)) {
+          uri = canonicalFile.toURI();
+        }
+        return new FileBasedSource.con2(uri, canonicalFile);
+      }
+    }
+    return new FileBasedSource.con2(
+        uri, getCanonicalFile(_packagesDirectories[0], pkgName, relPath));
+  }
+
+  @override
+  Uri restoreAbsolute(Source source) {
+    String sourcePath = source.fullName;
+    for (JavaFile packagesDirectory in _packagesDirectories) {
+      List<JavaFile> pkgFolders = packagesDirectory.listFiles();
+      if (pkgFolders != null) {
+        for (JavaFile pkgFolder in pkgFolders) {
+          try {
+            String pkgCanonicalPath = pkgFolder.getCanonicalPath();
+            if (sourcePath.startsWith(pkgCanonicalPath)) {
+              String relPath = sourcePath.substring(pkgCanonicalPath.length);
+              return parseUriWithException(
+                  "$PACKAGE_SCHEME:${pkgFolder.getName()}$relPath");
+            }
+          } catch (e) {}
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * @return `true` if "file" was found in "packagesDir", and it is part of the "lib" folder
+   *         of the application that contains in this "packagesDir".
+   */
+  bool _isSelfReference(JavaFile packagesDir, JavaFile file) {
+    JavaFile rootDir = packagesDir.getParentFile();
+    if (rootDir == null) {
+      return false;
+    }
+    String rootPath = rootDir.getAbsolutePath();
+    String filePath = file.getAbsolutePath();
+    return filePath.startsWith("$rootPath/lib");
+  }
+
+  /**
+   * Return `true` if the given URI is a `package` URI.
+   *
+   * @param uri the URI being tested
+   * @return `true` if the given URI is a `package` URI
+   */
+  static bool isPackageUri(Uri uri) => PACKAGE_SCHEME == uri.scheme;
+}
+
+/**
+ * Instances of the class `RelativeFileUriResolver` resolve `file` URI's.
+ */
+class RelativeFileUriResolver extends UriResolver {
+  /**
+   * The name of the `file` scheme.
+   */
+  static String FILE_SCHEME = "file";
+
+  /**
+   * The directories for the relatvie URI's
+   */
+  final List<JavaFile> _relativeDirectories;
+
+  /**
+   * The root directory for all the source trees
+   */
+  final JavaFile _rootDirectory;
+
+  /**
+   * Initialize a newly created resolver to resolve `file` URI's relative to the given root
+   * directory.
+   */
+  RelativeFileUriResolver(this._rootDirectory, this._relativeDirectories)
+      : super();
+
+  @override
+  Source resolveAbsolute(Uri uri) {
+    String rootPath = _rootDirectory.toURI().path;
+    String uriPath = uri.path;
+    if (uriPath != null && uriPath.startsWith(rootPath)) {
+      String filePath = uri.path.substring(rootPath.length);
+      for (JavaFile dir in _relativeDirectories) {
+        JavaFile file = new JavaFile.relative(dir, filePath);
+        if (file.exists()) {
+          return new FileBasedSource.con2(uri, file);
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return `true` if the given URI is a `file` URI.
+   *
+   * @param uri the URI being tested
+   * @return `true` if the given URI is a `file` URI
+   */
+  static bool isFileUri(Uri uri) => uri.scheme == FILE_SCHEME;
+}
diff --git a/analyzer/lib/src/generated/static_type_analyzer.dart b/analyzer/lib/src/generated/static_type_analyzer.dart
new file mode 100644
index 0000000..b853626
--- /dev/null
+++ b/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -0,0 +1,1956 @@
+// 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 engine.resolver.static_type_analyzer;
+
+import 'dart:collection';
+
+import 'package:analyzer/src/generated/scanner.dart';
+
+import 'ast.dart';
+import 'element.dart';
+import 'java_engine.dart';
+import 'resolver.dart';
+import 'scanner.dart' as sc;
+
+/**
+ * Instances of the class `StaticTypeAnalyzer` perform two type-related tasks. First, they
+ * compute the static type of every expression. Second, they look for any static type errors or
+ * warnings that might need to be generated. The requirements for the type analyzer are:
+ * <ol>
+ * * Every element that refers to types should be fully populated.
+ * * Every node representing an expression should be resolved to the Type of the expression.
+ * </ol>
+ */
+class StaticTypeAnalyzer extends SimpleAstVisitor<Object> {
+  /**
+   * A table mapping HTML tag names to the names of the classes (in 'dart:html') that implement
+   * those tags.
+   */
+  static HashMap<String, String> _HTML_ELEMENT_TO_CLASS_MAP =
+      _createHtmlTagToClassMap();
+
+  /**
+   * The resolver driving the resolution and type analysis.
+   */
+  final ResolverVisitor _resolver;
+
+  /**
+   * The object providing access to the types defined by the language.
+   */
+  TypeProvider _typeProvider;
+
+  /**
+   * The type representing the type 'dynamic'.
+   */
+  DartType _dynamicType;
+
+  /**
+   * The type representing the class containing the nodes being analyzed,
+   * or `null` if the nodes are not within a class.
+   */
+  InterfaceType thisType;
+
+  /**
+   * The object keeping track of which elements have had their types overridden.
+   */
+  TypeOverrideManager _overrideManager;
+
+  /**
+   * The object keeping track of which elements have had their types promoted.
+   */
+  TypePromotionManager _promoteManager;
+
+  /**
+   * A table mapping [ExecutableElement]s to their propagated return types.
+   */
+  HashMap<ExecutableElement, DartType> _propagatedReturnTypes =
+      new HashMap<ExecutableElement, DartType>();
+
+  /**
+   * Initialize a newly created type analyzer.
+   *
+   * @param resolver the resolver driving this participant
+   */
+  StaticTypeAnalyzer(this._resolver) {
+    _typeProvider = _resolver.typeProvider;
+    _dynamicType = _typeProvider.dynamicType;
+    _overrideManager = _resolver.overrideManager;
+    _promoteManager = _resolver.promoteManager;
+  }
+
+  /**
+   * The Dart Language Specification, 12.5: <blockquote>The static type of a string literal is
+   * `String`.</blockquote>
+   */
+  @override
+  Object visitAdjacentStrings(AdjacentStrings node) {
+    _recordStaticType(node, _typeProvider.stringType);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.32: <blockquote>... the cast expression <i>e as T</i> ...
+   *
+   * It is a static warning if <i>T</i> does not denote a type available in the current lexical
+   * scope.
+   *
+   * The static type of a cast expression <i>e as T</i> is <i>T</i>.</blockquote>
+   */
+  @override
+  Object visitAsExpression(AsExpression node) {
+    _recordStaticType(node, _getType(node.type));
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.18: <blockquote>... an assignment <i>a</i> of the form <i>v
+   * = e</i> ...
+   *
+   * It is a static type warning if the static type of <i>e</i> may not be assigned to the static
+   * type of <i>v</i>.
+   *
+   * The static type of the expression <i>v = e</i> is the static type of <i>e</i>.
+   *
+   * ... an assignment of the form <i>C.v = e</i> ...
+   *
+   * It is a static type warning if the static type of <i>e</i> may not be assigned to the static
+   * type of <i>C.v</i>.
+   *
+   * The static type of the expression <i>C.v = e</i> is the static type of <i>e</i>.
+   *
+   * ... an assignment of the form <i>e<sub>1</sub>.v = e<sub>2</sub></i> ...
+   *
+   * Let <i>T</i> be the static type of <i>e<sub>1</sub></i>. It is a static type warning if
+   * <i>T</i> does not have an accessible instance setter named <i>v=</i>. It is a static type
+   * warning if the static type of <i>e<sub>2</sub></i> may not be assigned to <i>T</i>.
+   *
+   * The static type of the expression <i>e<sub>1</sub>.v = e<sub>2</sub></i> is the static type of
+   * <i>e<sub>2</sub></i>.
+   *
+   * ... an assignment of the form <i>e<sub>1</sub>[e<sub>2</sub>] = e<sub>3</sub></i> ...
+   *
+   * The static type of the expression <i>e<sub>1</sub>[e<sub>2</sub>] = e<sub>3</sub></i> is the
+   * static type of <i>e<sub>3</sub></i>.
+   *
+   * A compound assignment of the form <i>v op= e</i> is equivalent to <i>v = v op e</i>. A compound
+   * assignment of the form <i>C.v op= e</i> is equivalent to <i>C.v = C.v op e</i>. A compound
+   * assignment of the form <i>e<sub>1</sub>.v op= e<sub>2</sub></i> is equivalent to <i>((x) => x.v
+   * = x.v op e<sub>2</sub>)(e<sub>1</sub>)</i> where <i>x</i> is a variable that is not used in
+   * <i>e<sub>2</sub></i>. A compound assignment of the form <i>e<sub>1</sub>[e<sub>2</sub>] op=
+   * e<sub>3</sub></i> is equivalent to <i>((a, i) => a[i] = a[i] op e<sub>3</sub>)(e<sub>1</sub>,
+   * e<sub>2</sub>)</i> where <i>a</i> and <i>i</i> are a variables that are not used in
+   * <i>e<sub>3</sub></i>.</blockquote>
+   */
+  @override
+  Object visitAssignmentExpression(AssignmentExpression node) {
+    sc.TokenType operator = node.operator.type;
+    if (operator == sc.TokenType.EQ) {
+      Expression rightHandSide = node.rightHandSide;
+      DartType staticType = _getStaticType(rightHandSide);
+      _recordStaticType(node, staticType);
+      DartType overrideType = staticType;
+      DartType propagatedType = rightHandSide.propagatedType;
+      if (propagatedType != null) {
+        _resolver.recordPropagatedTypeIfBetter(node, propagatedType);
+        overrideType = propagatedType;
+      }
+      _resolver.overrideExpression(node.leftHandSide, overrideType, true);
+    } else if (operator == sc.TokenType.QUESTION_QUESTION_EQ) {
+      // The static type of a compound assignment using ??= is the least upper
+      // bound of the static types of the LHS and RHS.
+      _analyzeLeastUpperBound(node, node.leftHandSide, node.rightHandSide);
+      return null;
+    } else {
+      ExecutableElement staticMethodElement = node.staticElement;
+      DartType staticType = _computeStaticReturnType(staticMethodElement);
+      _recordStaticType(node, staticType);
+      MethodElement propagatedMethodElement = node.propagatedElement;
+      if (!identical(propagatedMethodElement, staticMethodElement)) {
+        DartType propagatedType =
+            _computeStaticReturnType(propagatedMethodElement);
+        _resolver.recordPropagatedTypeIfBetter(node, propagatedType);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 16.29 (Await Expressions):
+   *
+   *   The static type of [the expression "await e"] is flatten(T) where T is
+   *   the static type of e.
+   */
+  @override
+  Object visitAwaitExpression(AwaitExpression node) {
+    DartType staticExpressionType = _getStaticType(node.expression);
+    if (staticExpressionType == null) {
+      // TODO(brianwilkerson) Determine whether this can still happen.
+      staticExpressionType = _dynamicType;
+    }
+    DartType staticType = flattenFutures(_typeProvider, staticExpressionType);
+    _recordStaticType(node, staticType);
+    DartType propagatedExpressionType = node.expression.propagatedType;
+    DartType propagatedType =
+        flattenFutures(_typeProvider, propagatedExpressionType);
+    _resolver.recordPropagatedTypeIfBetter(node, propagatedType);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.20: <blockquote>The static type of a logical boolean
+   * expression is `bool`.</blockquote>
+   *
+   * The Dart Language Specification, 12.21:<blockquote>A bitwise expression of the form
+   * <i>e<sub>1</sub> op e<sub>2</sub></i> is equivalent to the method invocation
+   * <i>e<sub>1</sub>.op(e<sub>2</sub>)</i>. A bitwise expression of the form <i>super op
+   * e<sub>2</sub></i> is equivalent to the method invocation
+   * <i>super.op(e<sub>2</sub>)</i>.</blockquote>
+   *
+   * The Dart Language Specification, 12.22: <blockquote>The static type of an equality expression
+   * is `bool`.</blockquote>
+   *
+   * The Dart Language Specification, 12.23: <blockquote>A relational expression of the form
+   * <i>e<sub>1</sub> op e<sub>2</sub></i> is equivalent to the method invocation
+   * <i>e<sub>1</sub>.op(e<sub>2</sub>)</i>. A relational expression of the form <i>super op
+   * e<sub>2</sub></i> is equivalent to the method invocation
+   * <i>super.op(e<sub>2</sub>)</i>.</blockquote>
+   *
+   * The Dart Language Specification, 12.24: <blockquote>A shift expression of the form
+   * <i>e<sub>1</sub> op e<sub>2</sub></i> is equivalent to the method invocation
+   * <i>e<sub>1</sub>.op(e<sub>2</sub>)</i>. A shift expression of the form <i>super op
+   * e<sub>2</sub></i> is equivalent to the method invocation
+   * <i>super.op(e<sub>2</sub>)</i>.</blockquote>
+   *
+   * The Dart Language Specification, 12.25: <blockquote>An additive expression of the form
+   * <i>e<sub>1</sub> op e<sub>2</sub></i> is equivalent to the method invocation
+   * <i>e<sub>1</sub>.op(e<sub>2</sub>)</i>. An additive expression of the form <i>super op
+   * e<sub>2</sub></i> is equivalent to the method invocation
+   * <i>super.op(e<sub>2</sub>)</i>.</blockquote>
+   *
+   * The Dart Language Specification, 12.26: <blockquote>A multiplicative expression of the form
+   * <i>e<sub>1</sub> op e<sub>2</sub></i> is equivalent to the method invocation
+   * <i>e<sub>1</sub>.op(e<sub>2</sub>)</i>. A multiplicative expression of the form <i>super op
+   * e<sub>2</sub></i> is equivalent to the method invocation
+   * <i>super.op(e<sub>2</sub>)</i>.</blockquote>
+   */
+  @override
+  Object visitBinaryExpression(BinaryExpression node) {
+    if (node.operator.type == TokenType.QUESTION_QUESTION) {
+      // Evaluation of an if-null expresion e of the form e1 ?? e2 is
+      // equivalent to the evaluation of the expression
+      // ((x) => x == null ? e2 : x)(e1).  The static type of e is the least
+      // upper bound of the static type of e1 and the static type of e2.
+      _analyzeLeastUpperBound(node, node.leftOperand, node.rightOperand);
+      return null;
+    }
+    ExecutableElement staticMethodElement = node.staticElement;
+    DartType staticType = _computeStaticReturnType(staticMethodElement);
+    staticType = _refineBinaryExpressionType(node, staticType);
+    _recordStaticType(node, staticType);
+    MethodElement propagatedMethodElement = node.propagatedElement;
+    if (!identical(propagatedMethodElement, staticMethodElement)) {
+      DartType propagatedType =
+          _computeStaticReturnType(propagatedMethodElement);
+      _resolver.recordPropagatedTypeIfBetter(node, propagatedType);
+    }
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.4: <blockquote>The static type of a boolean literal is
+   * bool.</blockquote>
+   */
+  @override
+  Object visitBooleanLiteral(BooleanLiteral node) {
+    _recordStaticType(node, _typeProvider.boolType);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.15.2: <blockquote>A cascaded method invocation expression
+   * of the form <i>e..suffix</i> is equivalent to the expression <i>(t) {t.suffix; return
+   * t;}(e)</i>.</blockquote>
+   */
+  @override
+  Object visitCascadeExpression(CascadeExpression node) {
+    _recordStaticType(node, _getStaticType(node.target));
+    _resolver.recordPropagatedTypeIfBetter(node, node.target.propagatedType);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.19: <blockquote> ... a conditional expression <i>c</i> of
+   * the form <i>e<sub>1</sub> ? e<sub>2</sub> : e<sub>3</sub></i> ...
+   *
+   * It is a static type warning if the type of e<sub>1</sub> may not be assigned to `bool`.
+   *
+   * The static type of <i>c</i> is the least upper bound of the static type of <i>e<sub>2</sub></i>
+   * and the static type of <i>e<sub>3</sub></i>.</blockquote>
+   */
+  @override
+  Object visitConditionalExpression(ConditionalExpression node) {
+    _analyzeLeastUpperBound(node, node.thenExpression, node.elseExpression);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.3: <blockquote>The static type of a literal double is
+   * double.</blockquote>
+   */
+  @override
+  Object visitDoubleLiteral(DoubleLiteral node) {
+    _recordStaticType(node, _typeProvider.doubleType);
+    return null;
+  }
+
+  @override
+  Object visitFunctionDeclaration(FunctionDeclaration node) {
+    FunctionExpression function = node.functionExpression;
+    ExecutableElementImpl functionElement =
+        node.element as ExecutableElementImpl;
+    functionElement.returnType =
+        _computeStaticReturnTypeOfFunctionDeclaration(node);
+    _recordPropagatedTypeOfFunction(functionElement, function.body);
+    _recordStaticType(function, functionElement.type);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.9: <blockquote>The static type of a function literal of the
+   * form <i>(T<sub>1</sub> a<sub>1</sub>, &hellip;, T<sub>n</sub> a<sub>n</sub>, [T<sub>n+1</sub>
+   * x<sub>n+1</sub> = d1, &hellip;, T<sub>n+k</sub> x<sub>n+k</sub> = dk]) => e</i> is
+   * <i>(T<sub>1</sub>, &hellip;, Tn, [T<sub>n+1</sub> x<sub>n+1</sub>, &hellip;, T<sub>n+k</sub>
+   * x<sub>n+k</sub>]) &rarr; T<sub>0</sub></i>, where <i>T<sub>0</sub></i> is the static type of
+   * <i>e</i>. In any case where <i>T<sub>i</sub>, 1 &lt;= i &lt;= n</i>, is not specified, it is
+   * considered to have been specified as dynamic.
+   *
+   * The static type of a function literal of the form <i>(T<sub>1</sub> a<sub>1</sub>, &hellip;,
+   * T<sub>n</sub> a<sub>n</sub>, {T<sub>n+1</sub> x<sub>n+1</sub> : d1, &hellip;, T<sub>n+k</sub>
+   * x<sub>n+k</sub> : dk}) => e</i> is <i>(T<sub>1</sub>, &hellip;, T<sub>n</sub>, {T<sub>n+1</sub>
+   * x<sub>n+1</sub>, &hellip;, T<sub>n+k</sub> x<sub>n+k</sub>}) &rarr; T<sub>0</sub></i>, where
+   * <i>T<sub>0</sub></i> is the static type of <i>e</i>. In any case where <i>T<sub>i</sub>, 1
+   * &lt;= i &lt;= n</i>, is not specified, it is considered to have been specified as dynamic.
+   *
+   * The static type of a function literal of the form <i>(T<sub>1</sub> a<sub>1</sub>, &hellip;,
+   * T<sub>n</sub> a<sub>n</sub>, [T<sub>n+1</sub> x<sub>n+1</sub> = d1, &hellip;, T<sub>n+k</sub>
+   * x<sub>n+k</sub> = dk]) {s}</i> is <i>(T<sub>1</sub>, &hellip;, T<sub>n</sub>, [T<sub>n+1</sub>
+   * x<sub>n+1</sub>, &hellip;, T<sub>n+k</sub> x<sub>n+k</sub>]) &rarr; dynamic</i>. In any case
+   * where <i>T<sub>i</sub>, 1 &lt;= i &lt;= n</i>, is not specified, it is considered to have been
+   * specified as dynamic.
+   *
+   * The static type of a function literal of the form <i>(T<sub>1</sub> a<sub>1</sub>, &hellip;,
+   * T<sub>n</sub> a<sub>n</sub>, {T<sub>n+1</sub> x<sub>n+1</sub> : d1, &hellip;, T<sub>n+k</sub>
+   * x<sub>n+k</sub> : dk}) {s}</i> is <i>(T<sub>1</sub>, &hellip;, T<sub>n</sub>, {T<sub>n+1</sub>
+   * x<sub>n+1</sub>, &hellip;, T<sub>n+k</sub> x<sub>n+k</sub>}) &rarr; dynamic</i>. In any case
+   * where <i>T<sub>i</sub>, 1 &lt;= i &lt;= n</i>, is not specified, it is considered to have been
+   * specified as dynamic.</blockquote>
+   */
+  @override
+  Object visitFunctionExpression(FunctionExpression node) {
+    if (node.parent is FunctionDeclaration) {
+      // The function type will be resolved and set when we visit the parent
+      // node.
+      return null;
+    }
+    ExecutableElementImpl functionElement =
+        node.element as ExecutableElementImpl;
+    functionElement.returnType =
+        _computeStaticReturnTypeOfFunctionExpression(node);
+    _recordPropagatedTypeOfFunction(functionElement, node.body);
+    _recordStaticType(node, node.element.type);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.14.4: <blockquote>A function expression invocation <i>i</i>
+   * has the form <i>e<sub>f</sub>(a<sub>1</sub>, &hellip;, a<sub>n</sub>, x<sub>n+1</sub>:
+   * a<sub>n+1</sub>, &hellip;, x<sub>n+k</sub>: a<sub>n+k</sub>)</i>, where <i>e<sub>f</sub></i> is
+   * an expression.
+   *
+   * It is a static type warning if the static type <i>F</i> of <i>e<sub>f</sub></i> may not be
+   * assigned to a function type.
+   *
+   * If <i>F</i> is not a function type, the static type of <i>i</i> is dynamic. Otherwise the
+   * static type of <i>i</i> is the declared return type of <i>F</i>.</blockquote>
+   */
+  @override
+  Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    DartType functionStaticType = _getStaticType(node.function);
+    DartType staticType;
+    if (functionStaticType is FunctionType) {
+      staticType = functionStaticType.returnType;
+    } else {
+      staticType = _dynamicType;
+    }
+    _recordStaticType(node, staticType);
+    DartType functionPropagatedType = node.function.propagatedType;
+    if (functionPropagatedType is FunctionType) {
+      DartType propagatedType = functionPropagatedType.returnType;
+      _resolver.recordPropagatedTypeIfBetter(node, propagatedType);
+    }
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.29: <blockquote>An assignable expression of the form
+   * <i>e<sub>1</sub>[e<sub>2</sub>]</i> is evaluated as a method invocation of the operator method
+   * <i>[]</i> on <i>e<sub>1</sub></i> with argument <i>e<sub>2</sub></i>.</blockquote>
+   */
+  @override
+  Object visitIndexExpression(IndexExpression node) {
+    if (node.inSetterContext()) {
+      ExecutableElement staticMethodElement = node.staticElement;
+      DartType staticType = _computeArgumentType(staticMethodElement);
+      _recordStaticType(node, staticType);
+      MethodElement propagatedMethodElement = node.propagatedElement;
+      if (!identical(propagatedMethodElement, staticMethodElement)) {
+        DartType propagatedType = _computeArgumentType(propagatedMethodElement);
+        _resolver.recordPropagatedTypeIfBetter(node, propagatedType);
+      }
+    } else {
+      ExecutableElement staticMethodElement = node.staticElement;
+      DartType staticType = _computeStaticReturnType(staticMethodElement);
+      _recordStaticType(node, staticType);
+      MethodElement propagatedMethodElement = node.propagatedElement;
+      if (!identical(propagatedMethodElement, staticMethodElement)) {
+        DartType propagatedType =
+            _computeStaticReturnType(propagatedMethodElement);
+        _resolver.recordPropagatedTypeIfBetter(node, propagatedType);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.11.1: <blockquote>The static type of a new expression of
+   * either the form <i>new T.id(a<sub>1</sub>, &hellip;, a<sub>n</sub>)</i> or the form <i>new
+   * T(a<sub>1</sub>, &hellip;, a<sub>n</sub>)</i> is <i>T</i>.</blockquote>
+   *
+   * The Dart Language Specification, 12.11.2: <blockquote>The static type of a constant object
+   * expression of either the form <i>const T.id(a<sub>1</sub>, &hellip;, a<sub>n</sub>)</i> or the
+   * form <i>const T(a<sub>1</sub>, &hellip;, a<sub>n</sub>)</i> is <i>T</i>. </blockquote>
+   */
+  @override
+  Object visitInstanceCreationExpression(InstanceCreationExpression node) {
+    _recordStaticType(node, node.constructorName.type.type);
+    ConstructorElement element = node.staticElement;
+    if (element != null && "Element" == element.enclosingElement.name) {
+      LibraryElement library = element.library;
+      if (_isHtmlLibrary(library)) {
+        String constructorName = element.name;
+        if ("tag" == constructorName) {
+          DartType returnType = _getFirstArgumentAsTypeWithMap(
+              library, node.argumentList, _HTML_ELEMENT_TO_CLASS_MAP);
+          _resolver.recordPropagatedTypeIfBetter(node, returnType);
+        } else {
+          DartType returnType = _getElementNameAsType(
+              library, constructorName, _HTML_ELEMENT_TO_CLASS_MAP);
+          _resolver.recordPropagatedTypeIfBetter(node, returnType);
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.3: <blockquote>The static type of an integer literal is
+   * `int`.</blockquote>
+   */
+  @override
+  Object visitIntegerLiteral(IntegerLiteral node) {
+    _recordStaticType(node, _typeProvider.intType);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.31: <blockquote>It is a static warning if <i>T</i> does not
+   * denote a type available in the current lexical scope.
+   *
+   * The static type of an is-expression is `bool`.</blockquote>
+   */
+  @override
+  Object visitIsExpression(IsExpression node) {
+    _recordStaticType(node, _typeProvider.boolType);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.6: <blockquote>The static type of a list literal of the
+   * form <i><b>const</b> &lt;E&gt;[e<sub>1</sub>, &hellip;, e<sub>n</sub>]</i> or the form
+   * <i>&lt;E&gt;[e<sub>1</sub>, &hellip;, e<sub>n</sub>]</i> is `List&lt;E&gt;`. The static
+   * type a list literal of the form <i><b>const</b> [e<sub>1</sub>, &hellip;, e<sub>n</sub>]</i> or
+   * the form <i>[e<sub>1</sub>, &hellip;, e<sub>n</sub>]</i> is `List&lt;dynamic&gt;`
+   * .</blockquote>
+   */
+  @override
+  Object visitListLiteral(ListLiteral node) {
+    DartType staticType = _dynamicType;
+    TypeArgumentList typeArguments = node.typeArguments;
+    if (typeArguments != null) {
+      NodeList<TypeName> arguments = typeArguments.arguments;
+      if (arguments != null && arguments.length == 1) {
+        TypeName argumentTypeName = arguments[0];
+        DartType argumentType = _getType(argumentTypeName);
+        if (argumentType != null) {
+          staticType = argumentType;
+        }
+      }
+    }
+    _recordStaticType(
+        node, _typeProvider.listType.substitute4(<DartType>[staticType]));
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.7: <blockquote>The static type of a map literal of the form
+   * <i><b>const</b> &lt;K, V&gt; {k<sub>1</sub>:e<sub>1</sub>, &hellip;,
+   * k<sub>n</sub>:e<sub>n</sub>}</i> or the form <i>&lt;K, V&gt; {k<sub>1</sub>:e<sub>1</sub>,
+   * &hellip;, k<sub>n</sub>:e<sub>n</sub>}</i> is `Map&lt;K, V&gt;`. The static type a map
+   * literal of the form <i><b>const</b> {k<sub>1</sub>:e<sub>1</sub>, &hellip;,
+   * k<sub>n</sub>:e<sub>n</sub>}</i> or the form <i>{k<sub>1</sub>:e<sub>1</sub>, &hellip;,
+   * k<sub>n</sub>:e<sub>n</sub>}</i> is `Map&lt;dynamic, dynamic&gt;`.
+   *
+   * It is a compile-time error if the first type argument to a map literal is not
+   * <i>String</i>.</blockquote>
+   */
+  @override
+  Object visitMapLiteral(MapLiteral node) {
+    DartType staticKeyType = _dynamicType;
+    DartType staticValueType = _dynamicType;
+    TypeArgumentList typeArguments = node.typeArguments;
+    if (typeArguments != null) {
+      NodeList<TypeName> arguments = typeArguments.arguments;
+      if (arguments != null && arguments.length == 2) {
+        TypeName entryKeyTypeName = arguments[0];
+        DartType entryKeyType = _getType(entryKeyTypeName);
+        if (entryKeyType != null) {
+          staticKeyType = entryKeyType;
+        }
+        TypeName entryValueTypeName = arguments[1];
+        DartType entryValueType = _getType(entryValueTypeName);
+        if (entryValueType != null) {
+          staticValueType = entryValueType;
+        }
+      }
+    }
+    _recordStaticType(node, _typeProvider.mapType
+        .substitute4(<DartType>[staticKeyType, staticValueType]));
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.15.1: <blockquote>An ordinary method invocation <i>i</i>
+   * has the form <i>o.m(a<sub>1</sub>, &hellip;, a<sub>n</sub>, x<sub>n+1</sub>: a<sub>n+1</sub>,
+   * &hellip;, x<sub>n+k</sub>: a<sub>n+k</sub>)</i>.
+   *
+   * Let <i>T</i> be the static type of <i>o</i>. It is a static type warning if <i>T</i> does not
+   * have an accessible instance member named <i>m</i>. If <i>T.m</i> exists, it is a static warning
+   * if the type <i>F</i> of <i>T.m</i> may not be assigned to a function type.
+   *
+   * If <i>T.m</i> does not exist, or if <i>F</i> is not a function type, the static type of
+   * <i>i</i> is dynamic. Otherwise the static type of <i>i</i> is the declared return type of
+   * <i>F</i>.</blockquote>
+   *
+   * The Dart Language Specification, 11.15.3: <blockquote>A static method invocation <i>i</i> has
+   * the form <i>C.m(a<sub>1</sub>, &hellip;, a<sub>n</sub>, x<sub>n+1</sub>: a<sub>n+1</sub>,
+   * &hellip;, x<sub>n+k</sub>: a<sub>n+k</sub>)</i>.
+   *
+   * It is a static type warning if the type <i>F</i> of <i>C.m</i> may not be assigned to a
+   * function type.
+   *
+   * If <i>F</i> is not a function type, or if <i>C.m</i> does not exist, the static type of i is
+   * dynamic. Otherwise the static type of <i>i</i> is the declared return type of
+   * <i>F</i>.</blockquote>
+   *
+   * The Dart Language Specification, 11.15.4: <blockquote>A super method invocation <i>i</i> has
+   * the form <i>super.m(a<sub>1</sub>, &hellip;, a<sub>n</sub>, x<sub>n+1</sub>: a<sub>n+1</sub>,
+   * &hellip;, x<sub>n+k</sub>: a<sub>n+k</sub>)</i>.
+   *
+   * It is a static type warning if <i>S</i> does not have an accessible instance member named m. If
+   * <i>S.m</i> exists, it is a static warning if the type <i>F</i> of <i>S.m</i> may not be
+   * assigned to a function type.
+   *
+   * If <i>S.m</i> does not exist, or if <i>F</i> is not a function type, the static type of
+   * <i>i</i> is dynamic. Otherwise the static type of <i>i</i> is the declared return type of
+   * <i>F</i>.</blockquote>
+   */
+  @override
+  Object visitMethodInvocation(MethodInvocation node) {
+    SimpleIdentifier methodNameNode = node.methodName;
+    Element staticMethodElement = methodNameNode.staticElement;
+    // Record types of the variable invoked as a function.
+    if (staticMethodElement is VariableElement) {
+      VariableElement variable = staticMethodElement;
+      DartType staticType = variable.type;
+      _recordStaticType(methodNameNode, staticType);
+      DartType propagatedType = _overrideManager.getType(variable);
+      _resolver.recordPropagatedTypeIfBetter(methodNameNode, propagatedType);
+    }
+    // Record static return type of the static element.
+    DartType staticStaticType = _computeStaticReturnType(staticMethodElement);
+    _recordStaticType(node, staticStaticType);
+    // Record propagated return type of the static element.
+    DartType staticPropagatedType =
+        _computePropagatedReturnType(staticMethodElement);
+    _resolver.recordPropagatedTypeIfBetter(node, staticPropagatedType);
+    // Check for special cases.
+    bool needPropagatedType = true;
+    String methodName = methodNameNode.name;
+    if (methodName == "then") {
+      Expression target = node.realTarget;
+      if (target != null) {
+        DartType targetType = target.bestType;
+        if (_isAsyncFutureType(targetType)) {
+          // Future.then(closure) return type is:
+          // 1) the returned Future type, if the closure returns a Future;
+          // 2) Future<valueType>, if the closure returns a value.
+          NodeList<Expression> arguments = node.argumentList.arguments;
+          if (arguments.length == 1) {
+            // TODO(brianwilkerson) Handle the case where both arguments are
+            // provided.
+            Expression closureArg = arguments[0];
+            if (closureArg is FunctionExpression) {
+              FunctionExpression closureExpr = closureArg;
+              DartType returnType =
+                  _computePropagatedReturnType(closureExpr.element);
+              if (returnType != null) {
+                // prepare the type of the returned Future
+                InterfaceTypeImpl newFutureType;
+                if (_isAsyncFutureType(returnType)) {
+                  newFutureType = returnType as InterfaceTypeImpl;
+                } else {
+                  InterfaceType futureType = targetType as InterfaceType;
+                  newFutureType =
+                      new InterfaceTypeImpl.con1(futureType.element);
+                  newFutureType.typeArguments = <DartType>[returnType];
+                }
+                // set the 'then' invocation type
+                _recordPropagatedType(node, newFutureType);
+                needPropagatedType = false;
+                return null;
+              }
+            }
+          }
+        }
+      }
+    } else if (methodName == "\$dom_createEvent") {
+      Expression target = node.realTarget;
+      if (target != null) {
+        DartType targetType = target.bestType;
+        if (targetType is InterfaceType &&
+            (targetType.name == "HtmlDocument" ||
+                targetType.name == "Document")) {
+          LibraryElement library = targetType.element.library;
+          if (_isHtmlLibrary(library)) {
+            DartType returnType =
+                _getFirstArgumentAsType(library, node.argumentList);
+            if (returnType != null) {
+              _recordPropagatedType(node, returnType);
+              needPropagatedType = false;
+            }
+          }
+        }
+      }
+    } else if (methodName == "query") {
+      Expression target = node.realTarget;
+      if (target == null) {
+        Element methodElement = methodNameNode.bestElement;
+        if (methodElement != null) {
+          LibraryElement library = methodElement.library;
+          if (_isHtmlLibrary(library)) {
+            DartType returnType =
+                _getFirstArgumentAsQuery(library, node.argumentList);
+            if (returnType != null) {
+              _recordPropagatedType(node, returnType);
+              needPropagatedType = false;
+            }
+          }
+        }
+      } else {
+        DartType targetType = target.bestType;
+        if (targetType is InterfaceType &&
+            (targetType.name == "HtmlDocument" ||
+                targetType.name == "Document")) {
+          LibraryElement library = targetType.element.library;
+          if (_isHtmlLibrary(library)) {
+            DartType returnType =
+                _getFirstArgumentAsQuery(library, node.argumentList);
+            if (returnType != null) {
+              _recordPropagatedType(node, returnType);
+              needPropagatedType = false;
+            }
+          }
+        }
+      }
+    } else if (methodName == "\$dom_createElement") {
+      Expression target = node.realTarget;
+      if (target != null) {
+        DartType targetType = target.bestType;
+        if (targetType is InterfaceType &&
+            (targetType.name == "HtmlDocument" ||
+                targetType.name == "Document")) {
+          LibraryElement library = targetType.element.library;
+          if (_isHtmlLibrary(library)) {
+            DartType returnType =
+                _getFirstArgumentAsQuery(library, node.argumentList);
+            if (returnType != null) {
+              _recordPropagatedType(node, returnType);
+              needPropagatedType = false;
+            }
+          }
+        }
+      }
+    } else if (methodName == "JS") {
+      DartType returnType = _getFirstArgumentAsType(
+          _typeProvider.objectType.element.library, node.argumentList);
+      if (returnType != null) {
+        _recordPropagatedType(node, returnType);
+        needPropagatedType = false;
+      }
+    } else if (methodName == "getContext") {
+      Expression target = node.realTarget;
+      if (target != null) {
+        DartType targetType = target.bestType;
+        if (targetType is InterfaceType &&
+            (targetType.name == "CanvasElement")) {
+          NodeList<Expression> arguments = node.argumentList.arguments;
+          if (arguments.length == 1) {
+            Expression argument = arguments[0];
+            if (argument is StringLiteral) {
+              String value = argument.stringValue;
+              if ("2d" == value) {
+                PropertyAccessorElement getter =
+                    targetType.element.getGetter("context2D");
+                if (getter != null) {
+                  DartType returnType = getter.returnType;
+                  if (returnType != null) {
+                    _recordPropagatedType(node, returnType);
+                    needPropagatedType = false;
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+    if (needPropagatedType) {
+      Element propagatedElement = methodNameNode.propagatedElement;
+      // HACK: special case for object methods ([toString]) on dynamic
+      // expressions. More special cases in [visitPrefixedIdentfier].
+      if (propagatedElement == null) {
+        propagatedElement =
+            _typeProvider.objectType.getMethod(methodNameNode.name);
+      }
+      if (!identical(propagatedElement, staticMethodElement)) {
+        // Record static return type of the propagated element.
+        DartType propagatedStaticType =
+            _computeStaticReturnType(propagatedElement);
+        _resolver.recordPropagatedTypeIfBetter(
+            node, propagatedStaticType, true);
+        // Record propagated return type of the propagated element.
+        DartType propagatedPropagatedType =
+            _computePropagatedReturnType(propagatedElement);
+        _resolver.recordPropagatedTypeIfBetter(
+            node, propagatedPropagatedType, true);
+      }
+    }
+    return null;
+  }
+
+  @override
+  Object visitNamedExpression(NamedExpression node) {
+    Expression expression = node.expression;
+    _recordStaticType(node, _getStaticType(expression));
+    _resolver.recordPropagatedTypeIfBetter(node, expression.propagatedType);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.2: <blockquote>The static type of `null` is bottom.
+   * </blockquote>
+   */
+  @override
+  Object visitNullLiteral(NullLiteral node) {
+    _recordStaticType(node, _typeProvider.bottomType);
+    return null;
+  }
+
+  @override
+  Object visitParenthesizedExpression(ParenthesizedExpression node) {
+    Expression expression = node.expression;
+    _recordStaticType(node, _getStaticType(expression));
+    _resolver.recordPropagatedTypeIfBetter(node, expression.propagatedType);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.28: <blockquote>A postfix expression of the form
+   * <i>v++</i>, where <i>v</i> is an identifier, is equivalent to <i>(){var r = v; v = r + 1;
+   * return r}()</i>.
+   *
+   * A postfix expression of the form <i>C.v++</i> is equivalent to <i>(){var r = C.v; C.v = r + 1;
+   * return r}()</i>.
+   *
+   * A postfix expression of the form <i>e1.v++</i> is equivalent to <i>(x){var r = x.v; x.v = r +
+   * 1; return r}(e1)</i>.
+   *
+   * A postfix expression of the form <i>e1[e2]++</i> is equivalent to <i>(a, i){var r = a[i]; a[i]
+   * = r + 1; return r}(e1, e2)</i>
+   *
+   * A postfix expression of the form <i>v--</i>, where <i>v</i> is an identifier, is equivalent to
+   * <i>(){var r = v; v = r - 1; return r}()</i>.
+   *
+   * A postfix expression of the form <i>C.v--</i> is equivalent to <i>(){var r = C.v; C.v = r - 1;
+   * return r}()</i>.
+   *
+   * A postfix expression of the form <i>e1.v--</i> is equivalent to <i>(x){var r = x.v; x.v = r -
+   * 1; return r}(e1)</i>.
+   *
+   * A postfix expression of the form <i>e1[e2]--</i> is equivalent to <i>(a, i){var r = a[i]; a[i]
+   * = r - 1; return r}(e1, e2)</i></blockquote>
+   */
+  @override
+  Object visitPostfixExpression(PostfixExpression node) {
+    Expression operand = node.operand;
+    DartType staticType = _getStaticType(operand);
+    sc.TokenType operator = node.operator.type;
+    if (operator == sc.TokenType.MINUS_MINUS ||
+        operator == sc.TokenType.PLUS_PLUS) {
+      DartType intType = _typeProvider.intType;
+      if (identical(_getStaticType(node.operand), intType)) {
+        staticType = intType;
+      }
+    }
+    _recordStaticType(node, staticType);
+    _resolver.recordPropagatedTypeIfBetter(node, operand.propagatedType);
+    return null;
+  }
+
+  /**
+   * See [visitSimpleIdentifier].
+   */
+  @override
+  Object visitPrefixedIdentifier(PrefixedIdentifier node) {
+    SimpleIdentifier prefixedIdentifier = node.identifier;
+    Element staticElement = prefixedIdentifier.staticElement;
+    DartType staticType = _dynamicType;
+    DartType propagatedType = null;
+    if (staticElement is ClassElement) {
+      if (_isNotTypeLiteral(node)) {
+        staticType = staticElement.type;
+      } else {
+        staticType = _typeProvider.typeType;
+      }
+    } else if (staticElement is FunctionTypeAliasElement) {
+      if (_isNotTypeLiteral(node)) {
+        staticType = staticElement.type;
+      } else {
+        staticType = _typeProvider.typeType;
+      }
+    } else if (staticElement is MethodElement) {
+      staticType = staticElement.type;
+    } else if (staticElement is PropertyAccessorElement) {
+      staticType = _getTypeOfProperty(staticElement, node.prefix.staticType);
+      propagatedType =
+          _getPropertyPropagatedType(staticElement, propagatedType);
+    } else if (staticElement is ExecutableElement) {
+      staticType = staticElement.type;
+    } else if (staticElement is TypeParameterElement) {
+      staticType = staticElement.type;
+    } else if (staticElement is VariableElement) {
+      staticType = staticElement.type;
+    }
+    _recordStaticType(prefixedIdentifier, staticType);
+    _recordStaticType(node, staticType);
+    Element propagatedElement = prefixedIdentifier.propagatedElement;
+    // HACK: special case for object getters ([hashCode] and [runtimeType]) on
+    // dynamic expressions. More special cases in [visitMethodInvocation].
+    if (propagatedElement == null) {
+      propagatedElement =
+          _typeProvider.objectType.getGetter(prefixedIdentifier.name);
+    }
+    if (propagatedElement is ClassElement) {
+      if (_isNotTypeLiteral(node)) {
+        propagatedType = propagatedElement.type;
+      } else {
+        propagatedType = _typeProvider.typeType;
+      }
+    } else if (propagatedElement is FunctionTypeAliasElement) {
+      propagatedType = propagatedElement.type;
+    } else if (propagatedElement is MethodElement) {
+      propagatedType = propagatedElement.type;
+    } else if (propagatedElement is PropertyAccessorElement) {
+      propagatedType =
+          _getTypeOfProperty(propagatedElement, node.prefix.staticType);
+      propagatedType =
+          _getPropertyPropagatedType(propagatedElement, propagatedType);
+    } else if (propagatedElement is ExecutableElement) {
+      propagatedType = propagatedElement.type;
+    } else if (propagatedElement is TypeParameterElement) {
+      propagatedType = propagatedElement.type;
+    } else if (propagatedElement is VariableElement) {
+      propagatedType = propagatedElement.type;
+    }
+    DartType overriddenType = _overrideManager.getType(propagatedElement);
+    if (propagatedType == null ||
+        (overriddenType != null &&
+            overriddenType.isMoreSpecificThan(propagatedType))) {
+      propagatedType = overriddenType;
+    }
+    _resolver.recordPropagatedTypeIfBetter(prefixedIdentifier, propagatedType);
+    _resolver.recordPropagatedTypeIfBetter(node, propagatedType);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.27: <blockquote>A unary expression <i>u</i> of the form
+   * <i>op e</i> is equivalent to a method invocation <i>expression e.op()</i>. An expression of the
+   * form <i>op super</i> is equivalent to the method invocation <i>super.op()<i>.</blockquote>
+   */
+  @override
+  Object visitPrefixExpression(PrefixExpression node) {
+    sc.TokenType operator = node.operator.type;
+    if (operator == sc.TokenType.BANG) {
+      _recordStaticType(node, _typeProvider.boolType);
+    } else {
+      // The other cases are equivalent to invoking a method.
+      ExecutableElement staticMethodElement = node.staticElement;
+      DartType staticType = _computeStaticReturnType(staticMethodElement);
+      if (operator == sc.TokenType.MINUS_MINUS ||
+          operator == sc.TokenType.PLUS_PLUS) {
+        DartType intType = _typeProvider.intType;
+        if (identical(_getStaticType(node.operand), intType)) {
+          staticType = intType;
+        }
+      }
+      _recordStaticType(node, staticType);
+      MethodElement propagatedMethodElement = node.propagatedElement;
+      if (!identical(propagatedMethodElement, staticMethodElement)) {
+        DartType propagatedType =
+            _computeStaticReturnType(propagatedMethodElement);
+        _resolver.recordPropagatedTypeIfBetter(node, propagatedType);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.13: <blockquote> Property extraction allows for a member of
+   * an object to be concisely extracted from the object. If <i>o</i> is an object, and if <i>m</i>
+   * is the name of a method member of <i>o</i>, then
+   * * <i>o.m</i> is defined to be equivalent to: <i>(r<sub>1</sub>, &hellip;, r<sub>n</sub>,
+   * {p<sub>1</sub> : d<sub>1</sub>, &hellip;, p<sub>k</sub> : d<sub>k</sub>}){return
+   * o.m(r<sub>1</sub>, &hellip;, r<sub>n</sub>, p<sub>1</sub>: p<sub>1</sub>, &hellip;,
+   * p<sub>k</sub>: p<sub>k</sub>);}</i> if <i>m</i> has required parameters <i>r<sub>1</sub>,
+   * &hellip;, r<sub>n</sub></i>, and named parameters <i>p<sub>1</sub> &hellip; p<sub>k</sub></i>
+   * with defaults <i>d<sub>1</sub>, &hellip;, d<sub>k</sub></i>.
+   * * <i>(r<sub>1</sub>, &hellip;, r<sub>n</sub>, [p<sub>1</sub> = d<sub>1</sub>, &hellip;,
+   * p<sub>k</sub> = d<sub>k</sub>]){return o.m(r<sub>1</sub>, &hellip;, r<sub>n</sub>,
+   * p<sub>1</sub>, &hellip;, p<sub>k</sub>);}</i> if <i>m</i> has required parameters
+   * <i>r<sub>1</sub>, &hellip;, r<sub>n</sub></i>, and optional positional parameters
+   * <i>p<sub>1</sub> &hellip; p<sub>k</sub></i> with defaults <i>d<sub>1</sub>, &hellip;,
+   * d<sub>k</sub></i>.
+   * Otherwise, if <i>m</i> is the name of a getter member of <i>o</i> (declared implicitly or
+   * explicitly) then <i>o.m</i> evaluates to the result of invoking the getter. </blockquote>
+   *
+   * The Dart Language Specification, 12.17: <blockquote> ... a getter invocation <i>i</i> of the
+   * form <i>e.m</i> ...
+   *
+   * Let <i>T</i> be the static type of <i>e</i>. It is a static type warning if <i>T</i> does not
+   * have a getter named <i>m</i>.
+   *
+   * The static type of <i>i</i> is the declared return type of <i>T.m</i>, if <i>T.m</i> exists;
+   * otherwise the static type of <i>i</i> is dynamic.
+   *
+   * ... a getter invocation <i>i</i> of the form <i>C.m</i> ...
+   *
+   * It is a static warning if there is no class <i>C</i> in the enclosing lexical scope of
+   * <i>i</i>, or if <i>C</i> does not declare, implicitly or explicitly, a getter named <i>m</i>.
+   *
+   * The static type of <i>i</i> is the declared return type of <i>C.m</i> if it exists or dynamic
+   * otherwise.
+   *
+   * ... a top-level getter invocation <i>i</i> of the form <i>m</i>, where <i>m</i> is an
+   * identifier ...
+   *
+   * The static type of <i>i</i> is the declared return type of <i>m</i>.</blockquote>
+   */
+  @override
+  Object visitPropertyAccess(PropertyAccess node) {
+    SimpleIdentifier propertyName = node.propertyName;
+    Element staticElement = propertyName.staticElement;
+    DartType staticType = _dynamicType;
+    if (staticElement is MethodElement) {
+      staticType = staticElement.type;
+    } else if (staticElement is PropertyAccessorElement) {
+      Expression realTarget = node.realTarget;
+      staticType = _getTypeOfProperty(staticElement,
+          realTarget != null ? _getStaticType(realTarget) : null);
+    } else {
+      // TODO(brianwilkerson) Report this internal error.
+    }
+    _recordStaticType(propertyName, staticType);
+    _recordStaticType(node, staticType);
+    Element propagatedElement = propertyName.propagatedElement;
+    DartType propagatedType = _overrideManager.getType(propagatedElement);
+    if (propagatedElement is MethodElement) {
+      propagatedType = propagatedElement.type;
+    } else if (propagatedElement is PropertyAccessorElement) {
+      Expression realTarget = node.realTarget;
+      propagatedType = _getTypeOfProperty(
+          propagatedElement, realTarget != null ? realTarget.bestType : null);
+    } else {
+      // TODO(brianwilkerson) Report this internal error.
+    }
+    _resolver.recordPropagatedTypeIfBetter(propertyName, propagatedType);
+    _resolver.recordPropagatedTypeIfBetter(node, propagatedType);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.9: <blockquote>The static type of a rethrow expression is
+   * bottom.</blockquote>
+   */
+  @override
+  Object visitRethrowExpression(RethrowExpression node) {
+    _recordStaticType(node, _typeProvider.bottomType);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.30: <blockquote>Evaluation of an identifier expression
+   * <i>e</i> of the form <i>id</i> proceeds as follows:
+   *
+   * Let <i>d</i> be the innermost declaration in the enclosing lexical scope whose name is
+   * <i>id</i>. If no such declaration exists in the lexical scope, let <i>d</i> be the declaration
+   * of the inherited member named <i>id</i> if it exists.
+   * * If <i>d</i> is a class or type alias <i>T</i>, the value of <i>e</i> is the unique instance
+   * of class `Type` reifying <i>T</i>.
+   * * If <i>d</i> is a type parameter <i>T</i>, then the value of <i>e</i> is the value of the
+   * actual type argument corresponding to <i>T</i> that was passed to the generative constructor
+   * that created the current binding of this. We are assured that this is well defined, because if
+   * we were in a static member the reference to <i>T</i> would be a compile-time error.
+   * * If <i>d</i> is a library variable then:
+   * * If <i>d</i> is of one of the forms <i>var v = e<sub>i</sub>;</i>, <i>T v =
+   * e<sub>i</sub>;</i>, <i>final v = e<sub>i</sub>;</i>, <i>final T v = e<sub>i</sub>;</i>, and no
+   * value has yet been stored into <i>v</i> then the initializer expression <i>e<sub>i</sub></i> is
+   * evaluated. If, during the evaluation of <i>e<sub>i</sub></i>, the getter for <i>v</i> is
+   * referenced, a CyclicInitializationError is thrown. If the evaluation succeeded yielding an
+   * object <i>o</i>, let <i>r = o</i>, otherwise let <i>r = null</i>. In any case, <i>r</i> is
+   * stored into <i>v</i>. The value of <i>e</i> is <i>r</i>.
+   * * If <i>d</i> is of one of the forms <i>const v = e;</i> or <i>const T v = e;</i> the result
+   * of the getter is the value of the compile time constant <i>e</i>. Otherwise
+   * * <i>e</i> evaluates to the current binding of <i>id</i>.
+   * * If <i>d</i> is a local variable or formal parameter then <i>e</i> evaluates to the current
+   * binding of <i>id</i>.
+   * * If <i>d</i> is a static method, top level function or local function then <i>e</i>
+   * evaluates to the function defined by <i>d</i>.
+   * * If <i>d</i> is the declaration of a static variable or static getter declared in class
+   * <i>C</i>, then <i>e</i> is equivalent to the getter invocation <i>C.id</i>.
+   * * If <i>d</i> is the declaration of a top level getter, then <i>e</i> is equivalent to the
+   * getter invocation <i>id</i>.
+   * * Otherwise, if <i>e</i> occurs inside a top level or static function (be it function,
+   * method, getter, or setter) or variable initializer, evaluation of e causes a NoSuchMethodError
+   * to be thrown.
+   * * Otherwise <i>e</i> is equivalent to the property extraction <i>this.id</i>.
+   * </blockquote>
+   */
+  @override
+  Object visitSimpleIdentifier(SimpleIdentifier node) {
+    Element element = node.staticElement;
+    DartType staticType = _dynamicType;
+    if (element is ClassElement) {
+      if (_isNotTypeLiteral(node)) {
+        staticType = element.type;
+      } else {
+        staticType = _typeProvider.typeType;
+      }
+    } else if (element is FunctionTypeAliasElement) {
+      if (_isNotTypeLiteral(node)) {
+        staticType = element.type;
+      } else {
+        staticType = _typeProvider.typeType;
+      }
+    } else if (element is MethodElement) {
+      staticType = element.type;
+    } else if (element is PropertyAccessorElement) {
+      staticType = _getTypeOfProperty(element, null);
+    } else if (element is ExecutableElement) {
+      staticType = element.type;
+    } else if (element is TypeParameterElement) {
+      staticType = _typeProvider.typeType;
+    } else if (element is VariableElement) {
+      VariableElement variable = element;
+      staticType = _promoteManager.getStaticType(variable);
+    } else if (element is PrefixElement) {
+      return null;
+    } else if (element is DynamicElementImpl) {
+      staticType = _typeProvider.typeType;
+    } else {
+      staticType = _dynamicType;
+    }
+    _recordStaticType(node, staticType);
+    // TODO(brianwilkerson) I think we want to repeat the logic above using the
+    // propagated element to get another candidate for the propagated type.
+    DartType propagatedType = _getPropertyPropagatedType(element, null);
+    if (propagatedType == null) {
+      DartType overriddenType = _overrideManager.getType(element);
+      if (propagatedType == null ||
+          overriddenType != null &&
+              overriddenType.isMoreSpecificThan(propagatedType)) {
+        propagatedType = overriddenType;
+      }
+    }
+    _resolver.recordPropagatedTypeIfBetter(node, propagatedType);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.5: <blockquote>The static type of a string literal is
+   * `String`.</blockquote>
+   */
+  @override
+  Object visitSimpleStringLiteral(SimpleStringLiteral node) {
+    _recordStaticType(node, _typeProvider.stringType);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.5: <blockquote>The static type of a string literal is
+   * `String`.</blockquote>
+   */
+  @override
+  Object visitStringInterpolation(StringInterpolation node) {
+    _recordStaticType(node, _typeProvider.stringType);
+    return null;
+  }
+
+  @override
+  Object visitSuperExpression(SuperExpression node) {
+    if (thisType == null) {
+      // TODO(brianwilkerson) Report this error if it hasn't already been
+      // reported.
+      _recordStaticType(node, _dynamicType);
+    } else {
+      _recordStaticType(node, thisType);
+    }
+    return null;
+  }
+
+  @override
+  Object visitSymbolLiteral(SymbolLiteral node) {
+    _recordStaticType(node, _typeProvider.symbolType);
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.10: <blockquote>The static type of `this` is the
+   * interface of the immediately enclosing class.</blockquote>
+   */
+  @override
+  Object visitThisExpression(ThisExpression node) {
+    if (thisType == null) {
+      // TODO(brianwilkerson) Report this error if it hasn't already been
+      // reported.
+      _recordStaticType(node, _dynamicType);
+    } else {
+      _recordStaticType(node, thisType);
+    }
+    return null;
+  }
+
+  /**
+   * The Dart Language Specification, 12.8: <blockquote>The static type of a throw expression is
+   * bottom.</blockquote>
+   */
+  @override
+  Object visitThrowExpression(ThrowExpression node) {
+    _recordStaticType(node, _typeProvider.bottomType);
+    return null;
+  }
+
+  @override
+  Object visitVariableDeclaration(VariableDeclaration node) {
+    Expression initializer = node.initializer;
+    if (initializer != null) {
+      DartType rightType = initializer.bestType;
+      SimpleIdentifier name = node.name;
+      _resolver.recordPropagatedTypeIfBetter(name, rightType);
+      VariableElement element = name.staticElement as VariableElement;
+      if (element != null) {
+        _resolver.overrideVariable(element, rightType, true);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Set the static (propagated) type of [node] to be the least upper bound
+   * of the static (propagated) types of subexpressions [expr1] and [expr2].
+   */
+  void _analyzeLeastUpperBound(
+      Expression node, Expression expr1, Expression expr2) {
+    DartType staticType1 = _getStaticType(expr1);
+    DartType staticType2 = _getStaticType(expr2);
+    if (staticType1 == null) {
+      // TODO(brianwilkerson) Determine whether this can still happen.
+      staticType1 = _dynamicType;
+    }
+    if (staticType2 == null) {
+      // TODO(brianwilkerson) Determine whether this can still happen.
+      staticType2 = _dynamicType;
+    }
+    DartType staticType = staticType1.getLeastUpperBound(staticType2);
+    if (staticType == null) {
+      staticType = _dynamicType;
+    }
+    _recordStaticType(node, staticType);
+    DartType propagatedType1 = expr1.propagatedType;
+    DartType propagatedType2 = expr2.propagatedType;
+    if (propagatedType1 != null || propagatedType2 != null) {
+      if (propagatedType1 == null) {
+        propagatedType1 = staticType1;
+      }
+      if (propagatedType2 == null) {
+        propagatedType2 = staticType2;
+      }
+      DartType propagatedType =
+          propagatedType1.getLeastUpperBound(propagatedType2);
+      _resolver.recordPropagatedTypeIfBetter(node, propagatedType);
+    }
+  }
+
+  /**
+   * Record that the static type of the given node is the type of the second argument to the method
+   * represented by the given element.
+   *
+   * @param element the element representing the method invoked by the given node
+   */
+  DartType _computeArgumentType(ExecutableElement element) {
+    if (element != null) {
+      List<ParameterElement> parameters = element.parameters;
+      if (parameters != null && parameters.length == 2) {
+        return parameters[1].type;
+      }
+    }
+    return _dynamicType;
+  }
+
+  /**
+   * Compute the propagated return type of the method or function represented by the given element.
+   *
+   * @param element the element representing the method or function invoked by the given node
+   * @return the propagated return type that was computed
+   */
+  DartType _computePropagatedReturnType(Element element) {
+    if (element is ExecutableElement) {
+      return _propagatedReturnTypes[element];
+    }
+    return null;
+  }
+
+  /**
+   * Given a function body, compute the propagated return type of the function. The propagated
+   * return type of functions with a block body is the least upper bound of all
+   * [ReturnStatement] expressions, with an expression body it is the type of the expression.
+   *
+   * @param body the boy of the function whose propagated return type is to be computed
+   * @return the propagated return type that was computed
+   */
+  DartType _computePropagatedReturnTypeOfFunction(FunctionBody body) {
+    if (body is ExpressionFunctionBody) {
+      ExpressionFunctionBody expressionBody = body;
+      return expressionBody.expression.bestType;
+    }
+    if (body is BlockFunctionBody) {
+      _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction visitor =
+          new _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction();
+      body.accept(visitor);
+      return visitor.result;
+    }
+    return null;
+  }
+
+  /**
+   * Compute the static return type of the method or function represented by the given element.
+   *
+   * @param element the element representing the method or function invoked by the given node
+   * @return the static return type that was computed
+   */
+  DartType _computeStaticReturnType(Element element) {
+    if (element is PropertyAccessorElement) {
+      //
+      // This is a function invocation expression disguised as something else.
+      // We are invoking a getter and then invoking the returned function.
+      //
+      FunctionType propertyType = element.type;
+      if (propertyType != null) {
+        DartType returnType = propertyType.returnType;
+        if (returnType.isDartCoreFunction) {
+          return _dynamicType;
+        } else if (returnType is InterfaceType) {
+          MethodElement callMethod = returnType.lookUpMethod(
+              FunctionElement.CALL_METHOD_NAME, _resolver.definingLibrary);
+          if (callMethod != null) {
+            return callMethod.type.returnType;
+          }
+        } else if (returnType is FunctionType) {
+          DartType innerReturnType = returnType.returnType;
+          if (innerReturnType != null) {
+            return innerReturnType;
+          }
+        }
+        if (returnType != null) {
+          return returnType;
+        }
+      }
+    } else if (element is ExecutableElement) {
+      FunctionType type = element.type;
+      if (type != null) {
+        // TODO(brianwilkerson) Figure out the conditions under which the type
+        // is null.
+        return type.returnType;
+      }
+    } else if (element is VariableElement) {
+      VariableElement variable = element;
+      DartType variableType = _promoteManager.getStaticType(variable);
+      if (variableType is FunctionType) {
+        return variableType.returnType;
+      }
+    }
+    return _dynamicType;
+  }
+
+  /**
+   * Given a function declaration, compute the return static type of the function. The return type
+   * of functions with a block body is `dynamicType`, with an expression body it is the type
+   * of the expression.
+   *
+   * @param node the function expression whose static return type is to be computed
+   * @return the static return type that was computed
+   */
+  DartType _computeStaticReturnTypeOfFunctionDeclaration(
+      FunctionDeclaration node) {
+    TypeName returnType = node.returnType;
+    if (returnType == null) {
+      return _dynamicType;
+    }
+    return returnType.type;
+  }
+
+  /**
+   * Given a function expression, compute the return type of the function. The return type of
+   * functions with a block body is `dynamicType`, with an expression body it is the type of
+   * the expression.
+   *
+   * @param node the function expression whose return type is to be computed
+   * @return the return type that was computed
+   */
+  DartType _computeStaticReturnTypeOfFunctionExpression(
+      FunctionExpression node) {
+    FunctionBody body = node.body;
+    if (body.isGenerator) {
+      if (body.isAsynchronous) {
+        return _typeProvider.streamDynamicType;
+      } else {
+        return _typeProvider.iterableDynamicType;
+      }
+    }
+    DartType type;
+    if (body is ExpressionFunctionBody) {
+      type = _getStaticType(body.expression);
+    } else {
+      type = _dynamicType;
+    }
+    if (body.isAsynchronous) {
+      return _typeProvider.futureType
+          .substitute4(<DartType>[flattenFutures(_typeProvider, type)]);
+    } else {
+      return type;
+    }
+  }
+
+  /**
+   * If the given element name can be mapped to the name of a class defined within the given
+   * library, return the type specified by the argument.
+   *
+   * @param library the library in which the specified type would be defined
+   * @param elementName the name of the element for which a type is being sought
+   * @param nameMap an optional map used to map the element name to a type name
+   * @return the type specified by the first argument in the argument list
+   */
+  DartType _getElementNameAsType(LibraryElement library, String elementName,
+      HashMap<String, String> nameMap) {
+    if (elementName != null) {
+      if (nameMap != null) {
+        elementName = nameMap[elementName.toLowerCase()];
+      }
+      ClassElement returnType = library.getType(elementName);
+      if (returnType != null) {
+        return returnType.type;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * If the given argument list contains at least one argument, and if the argument is a simple
+   * string literal, then parse that argument as a query string and return the type specified by the
+   * argument.
+   *
+   * @param library the library in which the specified type would be defined
+   * @param argumentList the list of arguments from which a type is to be extracted
+   * @return the type specified by the first argument in the argument list
+   */
+  DartType _getFirstArgumentAsQuery(
+      LibraryElement library, ArgumentList argumentList) {
+    String argumentValue = _getFirstArgumentAsString(argumentList);
+    if (argumentValue != null) {
+      //
+      // If the query has spaces, full parsing is required because it might be:
+      //   E[text='warning text']
+      //
+      if (StringUtilities.indexOf1(argumentValue, 0, 0x20) >= 0) {
+        return null;
+      }
+      //
+      // Otherwise, try to extract the tag based on
+      // http://www.w3.org/TR/CSS2/selector.html.
+      //
+      String tag = argumentValue;
+      tag = StringUtilities.substringBeforeChar(tag, 0x3A);
+      tag = StringUtilities.substringBeforeChar(tag, 0x5B);
+      tag = StringUtilities.substringBeforeChar(tag, 0x2E);
+      tag = StringUtilities.substringBeforeChar(tag, 0x23);
+      tag = _HTML_ELEMENT_TO_CLASS_MAP[tag.toLowerCase()];
+      ClassElement returnType = library.getType(tag);
+      if (returnType != null) {
+        return returnType.type;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * If the given argument list contains at least one argument, and if the argument is a simple
+   * string literal, return the String value of the argument.
+   *
+   * @param argumentList the list of arguments from which a string value is to be extracted
+   * @return the string specified by the first argument in the argument list
+   */
+  String _getFirstArgumentAsString(ArgumentList argumentList) {
+    NodeList<Expression> arguments = argumentList.arguments;
+    if (arguments.length > 0) {
+      Expression argument = arguments[0];
+      if (argument is SimpleStringLiteral) {
+        return argument.value;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * If the given argument list contains at least one argument, and if the argument is a simple
+   * string literal, and if the value of the argument is the name of a class defined within the
+   * given library, return the type specified by the argument.
+   *
+   * @param library the library in which the specified type would be defined
+   * @param argumentList the list of arguments from which a type is to be extracted
+   * @return the type specified by the first argument in the argument list
+   */
+  DartType _getFirstArgumentAsType(
+          LibraryElement library, ArgumentList argumentList) =>
+      _getFirstArgumentAsTypeWithMap(library, argumentList, null);
+
+  /**
+   * If the given argument list contains at least one argument, and if the argument is a simple
+   * string literal, and if the value of the argument is the name of a class defined within the
+   * given library, return the type specified by the argument.
+   *
+   * @param library the library in which the specified type would be defined
+   * @param argumentList the list of arguments from which a type is to be extracted
+   * @param nameMap an optional map used to map the element name to a type name
+   * @return the type specified by the first argument in the argument list
+   */
+  DartType _getFirstArgumentAsTypeWithMap(LibraryElement library,
+          ArgumentList argumentList, HashMap<String, String> nameMap) =>
+      _getElementNameAsType(
+          library, _getFirstArgumentAsString(argumentList), nameMap);
+
+  /**
+   * Return the propagated type of the given [Element], or `null`.
+   */
+  DartType _getPropertyPropagatedType(Element element, DartType currentType) {
+    if (element is PropertyAccessorElement) {
+      PropertyAccessorElement accessor = element;
+      if (accessor.isGetter) {
+        PropertyInducingElement variable = accessor.variable;
+        DartType propagatedType = variable.propagatedType;
+        if (currentType == null ||
+            propagatedType != null &&
+                propagatedType.isMoreSpecificThan(currentType)) {
+          return propagatedType;
+        }
+      }
+    }
+    return currentType;
+  }
+
+  /**
+   * Return the static type of the given expression.
+   *
+   * @param expression the expression whose type is to be returned
+   * @return the static type of the given expression
+   */
+  DartType _getStaticType(Expression expression) {
+    DartType type = expression.staticType;
+    if (type == null) {
+      // TODO(brianwilkerson) Determine the conditions for which the static type
+      // is null.
+      return _dynamicType;
+    }
+    return type;
+  }
+
+  /**
+   * Return the type represented by the given type name.
+   *
+   * @param typeName the type name representing the type to be returned
+   * @return the type represented by the type name
+   */
+  DartType _getType(TypeName typeName) {
+    DartType type = typeName.type;
+    if (type == null) {
+      //TODO(brianwilkerson) Determine the conditions for which the type is
+      // null.
+      return _dynamicType;
+    }
+    return type;
+  }
+
+  /**
+   * Return the type that should be recorded for a node that resolved to the given accessor.
+   *
+   * @param accessor the accessor that the node resolved to
+   * @param context if the accessor element has context [by being the RHS of a
+   *          [PrefixedIdentifier] or [PropertyAccess]], and the return type of the
+   *          accessor is a parameter type, then the type of the LHS can be used to get more
+   *          specific type information
+   * @return the type that should be recorded for a node that resolved to the given accessor
+   */
+  DartType _getTypeOfProperty(
+      PropertyAccessorElement accessor, DartType context) {
+    FunctionType functionType = accessor.type;
+    if (functionType == null) {
+      // TODO(brianwilkerson) Report this internal error. This happens when we
+      // are analyzing a reference to a property before we have analyzed the
+      // declaration of the property or when the property does not have a
+      // defined type.
+      return _dynamicType;
+    }
+    if (accessor.isSetter) {
+      List<DartType> parameterTypes = functionType.normalParameterTypes;
+      if (parameterTypes != null && parameterTypes.length > 0) {
+        return parameterTypes[0];
+      }
+      PropertyAccessorElement getter = accessor.variable.getter;
+      if (getter != null) {
+        functionType = getter.type;
+        if (functionType != null) {
+          return functionType.returnType;
+        }
+      }
+      return _dynamicType;
+    }
+    DartType returnType = functionType.returnType;
+    if (returnType is TypeParameterType && context is InterfaceType) {
+      // if the return type is a TypeParameter, we try to use the context [that
+      // the function is being called on] to get a more accurate returnType type
+      InterfaceType interfaceTypeContext = context;
+      //      Type[] argumentTypes = interfaceTypeContext.getTypeArguments();
+      List<TypeParameterElement> typeParameterElements =
+          interfaceTypeContext.element != null
+              ? interfaceTypeContext.element.typeParameters
+              : null;
+      if (typeParameterElements != null) {
+        for (int i = 0; i < typeParameterElements.length; i++) {
+          TypeParameterElement typeParameterElement = typeParameterElements[i];
+          if (returnType.name == typeParameterElement.name) {
+            return interfaceTypeContext.typeArguments[i];
+          }
+        }
+        // TODO(jwren) troubleshoot why call to substitute doesn't work
+//        Type[] parameterTypes = TypeParameterTypeImpl.getTypes(parameterElements);
+//        return returnType.substitute(argumentTypes, parameterTypes);
+      }
+    }
+    return returnType;
+  }
+
+  /**
+   * Return `true` if the given [Type] is the `Future` form the 'dart:async'
+   * library.
+   */
+  bool _isAsyncFutureType(DartType type) => type is InterfaceType &&
+      type.name == "Future" &&
+      _isAsyncLibrary(type.element.library);
+
+  /**
+   * Return `true` if the given library is the 'dart:async' library.
+   *
+   * @param library the library being tested
+   * @return `true` if the library is 'dart:async'
+   */
+  bool _isAsyncLibrary(LibraryElement library) => library.name == "dart.async";
+
+  /**
+   * Return `true` if the given library is the 'dart:html' library.
+   *
+   * @param library the library being tested
+   * @return `true` if the library is 'dart:html'
+   */
+  bool _isHtmlLibrary(LibraryElement library) =>
+      library != null && "dart.dom.html" == library.name;
+
+  /**
+   * Return `true` if the given node is not a type literal.
+   *
+   * @param node the node being tested
+   * @return `true` if the given node is not a type literal
+   */
+  bool _isNotTypeLiteral(Identifier node) {
+    AstNode parent = node.parent;
+    return parent is TypeName ||
+        (parent is PrefixedIdentifier &&
+            (parent.parent is TypeName || identical(parent.prefix, node))) ||
+        (parent is PropertyAccess &&
+            identical(parent.target, node) &&
+            parent.operator.type == TokenType.PERIOD) ||
+        (parent is MethodInvocation &&
+            identical(node, parent.target) &&
+            parent.operator.type == TokenType.PERIOD);
+  }
+
+  /**
+   * Record that the propagated type of the given node is the given type.
+   *
+   * @param expression the node whose type is to be recorded
+   * @param type the propagated type of the node
+   */
+  void _recordPropagatedType(Expression expression, DartType type) {
+    if (type != null && !type.isDynamic && !type.isBottom) {
+      expression.propagatedType = type;
+    }
+  }
+
+  /**
+   * Given a function element and its body, compute and record the propagated return type of the
+   * function.
+   *
+   * @param functionElement the function element to record propagated return type for
+   * @param body the boy of the function whose propagated return type is to be computed
+   * @return the propagated return type that was computed, may be `null` if it is not more
+   *         specific than the static return type.
+   */
+  void _recordPropagatedTypeOfFunction(
+      ExecutableElement functionElement, FunctionBody body) {
+    DartType propagatedReturnType =
+        _computePropagatedReturnTypeOfFunction(body);
+    if (propagatedReturnType == null) {
+      return;
+    }
+    // Ignore 'bottom' type.
+    if (propagatedReturnType.isBottom) {
+      return;
+    }
+    // Record only if we inferred more specific type.
+    DartType staticReturnType = functionElement.returnType;
+    if (!propagatedReturnType.isMoreSpecificThan(staticReturnType)) {
+      return;
+    }
+    // OK, do record.
+    _propagatedReturnTypes[functionElement] = propagatedReturnType;
+  }
+
+  /**
+   * Record that the static type of the given node is the given type.
+   *
+   * @param expression the node whose type is to be recorded
+   * @param type the static type of the node
+   */
+  void _recordStaticType(Expression expression, DartType type) {
+    if (type == null) {
+      expression.staticType = _dynamicType;
+    } else {
+      expression.staticType = type;
+    }
+  }
+
+  /**
+   * Attempts to make a better guess for the static type of the given binary expression.
+   *
+   * @param node the binary expression to analyze
+   * @param staticType the static type of the expression as resolved
+   * @return the better type guess, or the same static type as given
+   */
+  DartType _refineBinaryExpressionType(
+      BinaryExpression node, DartType staticType) {
+    sc.TokenType operator = node.operator.type;
+    // bool
+    if (operator == sc.TokenType.AMPERSAND_AMPERSAND ||
+        operator == sc.TokenType.BAR_BAR ||
+        operator == sc.TokenType.EQ_EQ ||
+        operator == sc.TokenType.BANG_EQ) {
+      return _typeProvider.boolType;
+    }
+    DartType intType = _typeProvider.intType;
+    if (_getStaticType(node.leftOperand) == intType) {
+      // int op double
+      if (operator == sc.TokenType.MINUS ||
+          operator == sc.TokenType.PERCENT ||
+          operator == sc.TokenType.PLUS ||
+          operator == sc.TokenType.STAR) {
+        DartType doubleType = _typeProvider.doubleType;
+        if (_getStaticType(node.rightOperand) == doubleType) {
+          return doubleType;
+        }
+      }
+      // int op int
+      if (operator == sc.TokenType.MINUS ||
+          operator == sc.TokenType.PERCENT ||
+          operator == sc.TokenType.PLUS ||
+          operator == sc.TokenType.STAR ||
+          operator == sc.TokenType.TILDE_SLASH) {
+        if (_getStaticType(node.rightOperand) == intType) {
+          staticType = intType;
+        }
+      }
+    }
+    // default
+    return staticType;
+  }
+
+  /**
+   * Implements the function "flatten" defined in the spec:
+   *
+   *   If T = Future<S> then flatten(T) = flatten(S).
+   *
+   *   Otherwise if T <: Future then let S be a type such that T << Future<S>
+   *   and for all R, if T << Future<R> then S << R.  Then flatten(T) = S.
+   *
+   *   In any other circumstance, flatten(T) = T.
+   */
+  static DartType flattenFutures(TypeProvider typeProvider, DartType type) {
+    if (type is InterfaceType) {
+      // Implement the case: "If T = Future<S> then flatten(T) = flatten(S)."
+      if (type.element == typeProvider.futureType.element &&
+          type.typeArguments.length > 0) {
+        return flattenFutures(typeProvider, type.typeArguments[0]);
+      }
+
+      // Implement the case: "Otherwise if T <: Future then let S be a type
+      // such that T << Future<S> and for all R, if T << Future<R> then S << R.
+      // Then flatten(T) = S."
+      //
+      // In other words, given the set of all types R such that T << Future<R>,
+      // let S be the most specific of those types, if any such S exists.
+      //
+      // Since we only care about the most specific type, it is sufficent to
+      // look at the types appearing as a parameter to Future in the type
+      // hierarchy of T.  We don't need to consider the supertypes of those
+      // types, since they are by definition less specific.
+      List<DartType> candidateTypes =
+          _searchTypeHierarchyForFutureParameters(typeProvider, type);
+      DartType flattenResult = _findMostSpecificType(candidateTypes);
+      if (flattenResult != null) {
+        return flattenResult;
+      }
+    }
+
+    // Implement the case: "In any other circumstance, flatten(T) = T."
+    return type;
+  }
+
+  /**
+   * Create a table mapping HTML tag names to the names of the classes (in 'dart:html') that
+   * implement those tags.
+   *
+   * @return the table that was created
+   */
+  static HashMap<String, String> _createHtmlTagToClassMap() {
+    HashMap<String, String> map = new HashMap<String, String>();
+    map["a"] = "AnchorElement";
+    map["area"] = "AreaElement";
+    map["br"] = "BRElement";
+    map["base"] = "BaseElement";
+    map["body"] = "BodyElement";
+    map["button"] = "ButtonElement";
+    map["canvas"] = "CanvasElement";
+    map["content"] = "ContentElement";
+    map["dl"] = "DListElement";
+    map["datalist"] = "DataListElement";
+    map["details"] = "DetailsElement";
+    map["div"] = "DivElement";
+    map["embed"] = "EmbedElement";
+    map["fieldset"] = "FieldSetElement";
+    map["form"] = "FormElement";
+    map["hr"] = "HRElement";
+    map["head"] = "HeadElement";
+    map["h1"] = "HeadingElement";
+    map["h2"] = "HeadingElement";
+    map["h3"] = "HeadingElement";
+    map["h4"] = "HeadingElement";
+    map["h5"] = "HeadingElement";
+    map["h6"] = "HeadingElement";
+    map["html"] = "HtmlElement";
+    map["iframe"] = "IFrameElement";
+    map["img"] = "ImageElement";
+    map["input"] = "InputElement";
+    map["keygen"] = "KeygenElement";
+    map["li"] = "LIElement";
+    map["label"] = "LabelElement";
+    map["legend"] = "LegendElement";
+    map["link"] = "LinkElement";
+    map["map"] = "MapElement";
+    map["menu"] = "MenuElement";
+    map["meter"] = "MeterElement";
+    map["ol"] = "OListElement";
+    map["object"] = "ObjectElement";
+    map["optgroup"] = "OptGroupElement";
+    map["output"] = "OutputElement";
+    map["p"] = "ParagraphElement";
+    map["param"] = "ParamElement";
+    map["pre"] = "PreElement";
+    map["progress"] = "ProgressElement";
+    map["script"] = "ScriptElement";
+    map["select"] = "SelectElement";
+    map["source"] = "SourceElement";
+    map["span"] = "SpanElement";
+    map["style"] = "StyleElement";
+    map["caption"] = "TableCaptionElement";
+    map["td"] = "TableCellElement";
+    map["col"] = "TableColElement";
+    map["table"] = "TableElement";
+    map["tr"] = "TableRowElement";
+    map["textarea"] = "TextAreaElement";
+    map["title"] = "TitleElement";
+    map["track"] = "TrackElement";
+    map["ul"] = "UListElement";
+    map["video"] = "VideoElement";
+    return map;
+  }
+
+  /**
+   * If there is a single type which is at least as specific as all of the
+   * types in [types], return it.  Otherwise return `null`.
+   */
+  static DartType _findMostSpecificType(List<DartType> types) {
+    // The << relation ("more specific than") is a partial ordering on types,
+    // so to find the most specific type of a set, we keep a bucket of the most
+    // specific types seen so far such that no type in the bucket is more
+    // specific than any other type in the bucket.
+    List<DartType> bucket = <DartType>[];
+
+    // Then we consider each type in turn.
+    for (DartType type in types) {
+      // If any existing type in the bucket is more specific than this type,
+      // then we can ignore this type.
+      if (bucket.any((DartType t) => t.isMoreSpecificThan(type))) {
+        continue;
+      }
+      // Otherwise, we need to add this type to the bucket and remove any types
+      // that are less specific than it.
+      bool added = false;
+      int i = 0;
+      while (i < bucket.length) {
+        if (type.isMoreSpecificThan(bucket[i])) {
+          if (added) {
+            if (i < bucket.length - 1) {
+              bucket[i] = bucket.removeLast();
+            } else {
+              bucket.removeLast();
+            }
+          } else {
+            bucket[i] = type;
+            i++;
+            added = true;
+          }
+        } else {
+          i++;
+        }
+      }
+      if (!added) {
+        bucket.add(type);
+      }
+    }
+
+    // Now that we are finished, if there is exactly one type left in the
+    // bucket, it is the most specific type.
+    if (bucket.length == 1) {
+      return bucket[0];
+    }
+
+    // Otherwise, there is no single type that is more specific than the
+    // others.
+    return null;
+  }
+
+  /**
+   * Given a seed type [type], search its class hierarchy for types of the form
+   * Future<R>, and return a list of the resulting R's.
+   */
+  static List<DartType> _searchTypeHierarchyForFutureParameters(
+      TypeProvider typeProvider, InterfaceType type) {
+    List<DartType> result = <DartType>[];
+    HashSet<ClassElement> visitedClasses = new HashSet<ClassElement>();
+    void recurse(InterfaceType type) {
+      if (type.element == typeProvider.futureType.element &&
+          type.typeArguments.length > 0) {
+        result.add(type.typeArguments[0]);
+      }
+      if (visitedClasses.add(type.element)) {
+        if (type.superclass != null) {
+          recurse(type.superclass);
+        }
+        for (InterfaceType interface in type.interfaces) {
+          recurse(interface);
+        }
+        visitedClasses.remove(type.element);
+      }
+    }
+    recurse(type);
+    return result;
+  }
+}
+
+class _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction
+    extends GeneralizingAstVisitor<Object> {
+  DartType result = null;
+
+  _StaticTypeAnalyzer_computePropagatedReturnTypeOfFunction();
+
+  @override
+  Object visitExpression(Expression node) => null;
+
+  @override
+  Object visitReturnStatement(ReturnStatement node) {
+    // prepare this 'return' type
+    DartType type;
+    Expression expression = node.expression;
+    if (expression != null) {
+      type = expression.bestType;
+    } else {
+      type = BottomTypeImpl.instance;
+    }
+    // merge types
+    if (result == null) {
+      result = type;
+    } else {
+      result = result.getLeastUpperBound(type);
+    }
+    return null;
+  }
+}
diff --git a/analyzer/lib/src/generated/testing/ast_factory.dart b/analyzer/lib/src/generated/testing/ast_factory.dart
new file mode 100644
index 0000000..00ee216
--- /dev/null
+++ b/analyzer/lib/src/generated/testing/ast_factory.dart
@@ -0,0 +1,952 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.testing.ast_factory;
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+import 'package:analyzer/src/generated/testing/token_factory.dart';
+import 'package:analyzer/src/generated/utilities_dart.dart';
+
+/**
+ * The class `AstFactory` defines utility methods that can be used to create AST nodes. The
+ * nodes that are created are complete in the sense that all of the tokens that would have been
+ * associated with the nodes by a parser are also created, but the token stream is not constructed.
+ * None of the nodes are resolved.
+ *
+ * The general pattern is for the name of the factory method to be the same as the name of the class
+ * of AST node being created. There are two notable exceptions. The first is for methods creating
+ * nodes that are part of a cascade expression. These methods are all prefixed with 'cascaded'. The
+ * second is places where a shorter name seemed unambiguous and easier to read, such as using
+ * 'identifier' rather than 'prefixedIdentifier', or 'integer' rather than 'integerLiteral'.
+ */
+class AstFactory {
+  static AdjacentStrings adjacentStrings(List<StringLiteral> strings) =>
+      new AdjacentStrings(strings);
+
+  static Annotation annotation(Identifier name) => new Annotation(
+      TokenFactory.tokenFromType(TokenType.AT), name, null, null, null);
+
+  static Annotation annotation2(Identifier name,
+          SimpleIdentifier constructorName, ArgumentList arguments) =>
+      new Annotation(TokenFactory.tokenFromType(TokenType.AT), name,
+          TokenFactory.tokenFromType(TokenType.PERIOD), constructorName,
+          arguments);
+
+  static ArgumentList argumentList([List<Expression> arguments]) =>
+      new ArgumentList(TokenFactory.tokenFromType(TokenType.OPEN_PAREN),
+          arguments, TokenFactory.tokenFromType(TokenType.CLOSE_PAREN));
+
+  static AsExpression asExpression(Expression expression, TypeName type) =>
+      new AsExpression(
+          expression, TokenFactory.tokenFromKeyword(Keyword.AS), type);
+
+  static AssertStatement assertStatement(Expression condition) =>
+      new AssertStatement(TokenFactory.tokenFromKeyword(Keyword.ASSERT),
+          TokenFactory.tokenFromType(TokenType.OPEN_PAREN), condition,
+          TokenFactory.tokenFromType(TokenType.CLOSE_PAREN),
+          TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static AssignmentExpression assignmentExpression(Expression leftHandSide,
+      TokenType operator, Expression rightHandSide) => new AssignmentExpression(
+      leftHandSide, TokenFactory.tokenFromType(operator), rightHandSide);
+
+  static BlockFunctionBody asyncBlockFunctionBody(
+      [List<Statement> statements]) => new BlockFunctionBody(
+      TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "async"), null,
+      block(statements));
+
+  static ExpressionFunctionBody asyncExpressionFunctionBody(
+      Expression expression) => new ExpressionFunctionBody(
+      TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "async"),
+      TokenFactory.tokenFromType(TokenType.FUNCTION), expression,
+      TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static BlockFunctionBody asyncGeneratorBlockFunctionBody(
+      [List<Statement> statements]) => new BlockFunctionBody(
+      TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "async"),
+      TokenFactory.tokenFromType(TokenType.STAR), block(statements));
+
+  static AwaitExpression awaitExpression(Expression expression) =>
+      new AwaitExpression(
+          TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "await"),
+          expression);
+
+  static BinaryExpression binaryExpression(Expression leftOperand,
+      TokenType operator, Expression rightOperand) => new BinaryExpression(
+      leftOperand, TokenFactory.tokenFromType(operator), rightOperand);
+
+  static Block block([List<Statement> statements]) => new Block(
+      TokenFactory.tokenFromType(TokenType.OPEN_CURLY_BRACKET), statements,
+      TokenFactory.tokenFromType(TokenType.CLOSE_CURLY_BRACKET));
+
+  static BlockFunctionBody blockFunctionBody(Block block) =>
+      new BlockFunctionBody(null, null, block);
+
+  static BlockFunctionBody blockFunctionBody2([List<Statement> statements]) =>
+      new BlockFunctionBody(null, null, block(statements));
+
+  static BooleanLiteral booleanLiteral(bool value) => new BooleanLiteral(value
+      ? TokenFactory.tokenFromKeyword(Keyword.TRUE)
+      : TokenFactory.tokenFromKeyword(Keyword.FALSE), value);
+
+  static BreakStatement breakStatement() => new BreakStatement(
+      TokenFactory.tokenFromKeyword(Keyword.BREAK), null,
+      TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static BreakStatement breakStatement2(String label) => new BreakStatement(
+      TokenFactory.tokenFromKeyword(Keyword.BREAK), identifier3(label),
+      TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static IndexExpression cascadedIndexExpression(Expression index) =>
+      new IndexExpression.forCascade(
+          TokenFactory.tokenFromType(TokenType.PERIOD_PERIOD),
+          TokenFactory.tokenFromType(TokenType.OPEN_SQUARE_BRACKET), index,
+          TokenFactory.tokenFromType(TokenType.CLOSE_SQUARE_BRACKET));
+
+  static MethodInvocation cascadedMethodInvocation(String methodName,
+      [List<Expression> arguments]) => new MethodInvocation(null,
+      TokenFactory.tokenFromType(TokenType.PERIOD_PERIOD),
+      identifier3(methodName), argumentList(arguments));
+
+  static PropertyAccess cascadedPropertyAccess(String propertyName) =>
+      new PropertyAccess(null,
+          TokenFactory.tokenFromType(TokenType.PERIOD_PERIOD),
+          identifier3(propertyName));
+
+  static CascadeExpression cascadeExpression(Expression target,
+          [List<Expression> cascadeSections]) =>
+      new CascadeExpression(target, cascadeSections);
+
+  static CatchClause catchClause(String exceptionParameter,
+          [List<Statement> statements]) =>
+      catchClause5(null, exceptionParameter, null, statements);
+
+  static CatchClause catchClause2(
+          String exceptionParameter, String stackTraceParameter,
+          [List<Statement> statements]) =>
+      catchClause5(null, exceptionParameter, stackTraceParameter, statements);
+
+  static CatchClause catchClause3(TypeName exceptionType,
+          [List<Statement> statements]) =>
+      catchClause5(exceptionType, null, null, statements);
+
+  static CatchClause catchClause4(
+          TypeName exceptionType, String exceptionParameter,
+          [List<Statement> statements]) =>
+      catchClause5(exceptionType, exceptionParameter, null, statements);
+
+  static CatchClause catchClause5(TypeName exceptionType,
+      String exceptionParameter, String stackTraceParameter,
+      [List<Statement> statements]) => new CatchClause(exceptionType == null
+          ? null
+          : TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "on"),
+      exceptionType, exceptionParameter == null
+          ? null
+          : TokenFactory.tokenFromKeyword(Keyword.CATCH), exceptionParameter ==
+              null ? null : TokenFactory.tokenFromType(TokenType.OPEN_PAREN),
+      exceptionParameter == null ? null : identifier3(exceptionParameter),
+      stackTraceParameter == null
+          ? null
+          : TokenFactory.tokenFromType(TokenType.COMMA),
+      stackTraceParameter == null ? null : identifier3(stackTraceParameter),
+      exceptionParameter == null
+          ? null
+          : TokenFactory.tokenFromType(TokenType.CLOSE_PAREN),
+      block(statements));
+
+  static ClassDeclaration classDeclaration(Keyword abstractKeyword, String name,
+      TypeParameterList typeParameters, ExtendsClause extendsClause,
+      WithClause withClause, ImplementsClause implementsClause,
+      [List<ClassMember> members]) => new ClassDeclaration(null, null,
+      abstractKeyword == null
+          ? null
+          : TokenFactory.tokenFromKeyword(abstractKeyword),
+      TokenFactory.tokenFromKeyword(Keyword.CLASS), identifier3(name),
+      typeParameters, extendsClause, withClause, implementsClause,
+      TokenFactory.tokenFromType(TokenType.OPEN_CURLY_BRACKET), members,
+      TokenFactory.tokenFromType(TokenType.CLOSE_CURLY_BRACKET));
+
+  static ClassTypeAlias classTypeAlias(String name,
+      TypeParameterList typeParameters, Keyword abstractKeyword,
+      TypeName superclass, WithClause withClause,
+      ImplementsClause implementsClause) => new ClassTypeAlias(null, null,
+      TokenFactory.tokenFromKeyword(Keyword.CLASS), identifier3(name),
+      typeParameters, TokenFactory.tokenFromType(TokenType.EQ),
+      abstractKeyword == null
+          ? null
+          : TokenFactory.tokenFromKeyword(abstractKeyword), superclass,
+      withClause, implementsClause,
+      TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static CompilationUnit compilationUnit() =>
+      compilationUnit8(null, null, null);
+
+  static CompilationUnit compilationUnit2(
+          List<CompilationUnitMember> declarations) =>
+      compilationUnit8(null, null, declarations);
+
+  static CompilationUnit compilationUnit3(List<Directive> directives) =>
+      compilationUnit8(null, directives, null);
+
+  static CompilationUnit compilationUnit4(List<Directive> directives,
+          List<CompilationUnitMember> declarations) =>
+      compilationUnit8(null, directives, declarations);
+
+  static CompilationUnit compilationUnit5(String scriptTag) =>
+      compilationUnit8(scriptTag, null, null);
+
+  static CompilationUnit compilationUnit6(
+          String scriptTag, List<CompilationUnitMember> declarations) =>
+      compilationUnit8(scriptTag, null, declarations);
+
+  static CompilationUnit compilationUnit7(
+          String scriptTag, List<Directive> directives) =>
+      compilationUnit8(scriptTag, directives, null);
+
+  static CompilationUnit compilationUnit8(String scriptTag,
+      List<Directive> directives,
+      List<CompilationUnitMember> declarations) => new CompilationUnit(
+      TokenFactory.tokenFromType(TokenType.EOF),
+      scriptTag == null ? null : AstFactory.scriptTag(scriptTag),
+      directives == null ? new List<Directive>() : directives,
+      declarations == null ? new List<CompilationUnitMember>() : declarations,
+      TokenFactory.tokenFromType(TokenType.EOF));
+
+  static ConditionalExpression conditionalExpression(Expression condition,
+          Expression thenExpression, Expression elseExpression) =>
+      new ConditionalExpression(condition,
+          TokenFactory.tokenFromType(TokenType.QUESTION), thenExpression,
+          TokenFactory.tokenFromType(TokenType.COLON), elseExpression);
+
+  static ConstructorDeclaration constructorDeclaration(Identifier returnType,
+      String name, FormalParameterList parameters,
+      List<ConstructorInitializer> initializers) => new ConstructorDeclaration(
+      null, null, TokenFactory.tokenFromKeyword(Keyword.EXTERNAL), null, null,
+      returnType,
+      name == null ? null : TokenFactory.tokenFromType(TokenType.PERIOD),
+      name == null ? null : identifier3(name), parameters,
+      initializers == null || initializers.isEmpty
+          ? null
+          : TokenFactory.tokenFromType(TokenType.PERIOD), initializers == null
+          ? new List<ConstructorInitializer>()
+          : initializers, null, emptyFunctionBody());
+
+  static ConstructorDeclaration constructorDeclaration2(Keyword constKeyword,
+      Keyword factoryKeyword, Identifier returnType, String name,
+      FormalParameterList parameters, List<ConstructorInitializer> initializers,
+      FunctionBody body) => new ConstructorDeclaration(null, null, null,
+      constKeyword == null ? null : TokenFactory.tokenFromKeyword(constKeyword),
+      factoryKeyword == null
+          ? null
+          : TokenFactory.tokenFromKeyword(factoryKeyword), returnType,
+      name == null ? null : TokenFactory.tokenFromType(TokenType.PERIOD),
+      name == null ? null : identifier3(name), parameters,
+      initializers == null || initializers.isEmpty
+          ? null
+          : TokenFactory.tokenFromType(TokenType.PERIOD), initializers == null
+          ? new List<ConstructorInitializer>()
+          : initializers, null, body);
+
+  static ConstructorFieldInitializer constructorFieldInitializer(
+          bool prefixedWithThis, String fieldName, Expression expression) =>
+      new ConstructorFieldInitializer(
+          prefixedWithThis ? TokenFactory.tokenFromKeyword(Keyword.THIS) : null,
+          prefixedWithThis
+              ? TokenFactory.tokenFromType(TokenType.PERIOD)
+              : null, identifier3(fieldName),
+          TokenFactory.tokenFromType(TokenType.EQ), expression);
+
+  static ConstructorName constructorName(TypeName type, String name) =>
+      new ConstructorName(type,
+          name == null ? null : TokenFactory.tokenFromType(TokenType.PERIOD),
+          name == null ? null : identifier3(name));
+
+  static ContinueStatement continueStatement([String label]) =>
+      new ContinueStatement(TokenFactory.tokenFromKeyword(Keyword.CONTINUE),
+          label == null ? null : identifier3(label),
+          TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static DeclaredIdentifier declaredIdentifier(
+          Keyword keyword, String identifier) =>
+      declaredIdentifier2(keyword, null, identifier);
+
+  static DeclaredIdentifier declaredIdentifier2(
+          Keyword keyword, TypeName type, String identifier) =>
+      new DeclaredIdentifier(null, null,
+          keyword == null ? null : TokenFactory.tokenFromKeyword(keyword), type,
+          identifier3(identifier));
+
+  static DeclaredIdentifier declaredIdentifier3(String identifier) =>
+      declaredIdentifier2(null, null, identifier);
+
+  static DeclaredIdentifier declaredIdentifier4(
+          TypeName type, String identifier) =>
+      declaredIdentifier2(null, type, identifier);
+
+  static DoStatement doStatement(Statement body, Expression condition) =>
+      new DoStatement(TokenFactory.tokenFromKeyword(Keyword.DO), body,
+          TokenFactory.tokenFromKeyword(Keyword.WHILE),
+          TokenFactory.tokenFromType(TokenType.OPEN_PAREN), condition,
+          TokenFactory.tokenFromType(TokenType.CLOSE_PAREN),
+          TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static DoubleLiteral doubleLiteral(double value) =>
+      new DoubleLiteral(TokenFactory.tokenFromString(value.toString()), value);
+
+  static EmptyFunctionBody emptyFunctionBody() =>
+      new EmptyFunctionBody(TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static EmptyStatement emptyStatement() =>
+      new EmptyStatement(TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static EnumDeclaration enumDeclaration(
+          SimpleIdentifier name, List<EnumConstantDeclaration> constants) =>
+      new EnumDeclaration(null, null,
+          TokenFactory.tokenFromKeyword(Keyword.ENUM), name,
+          TokenFactory.tokenFromType(TokenType.OPEN_CURLY_BRACKET), constants,
+          TokenFactory.tokenFromType(TokenType.CLOSE_CURLY_BRACKET));
+
+  static EnumDeclaration enumDeclaration2(
+      String name, List<String> constantNames) {
+    int count = constantNames.length;
+    List<EnumConstantDeclaration> constants =
+        new List<EnumConstantDeclaration>(count);
+    for (int i = 0; i < count; i++) {
+      constants[i] = new EnumConstantDeclaration(
+          null, null, identifier3(constantNames[i]));
+    }
+    return enumDeclaration(identifier3(name), constants);
+  }
+
+  static ExportDirective exportDirective(List<Annotation> metadata, String uri,
+      [List<Combinator> combinators]) => new ExportDirective(null, metadata,
+      TokenFactory.tokenFromKeyword(Keyword.EXPORT), string2(uri), combinators,
+      TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static ExportDirective exportDirective2(String uri,
+          [List<Combinator> combinators]) =>
+      exportDirective(null, uri, combinators);
+
+  static ExpressionFunctionBody expressionFunctionBody(Expression expression) =>
+      new ExpressionFunctionBody(null,
+          TokenFactory.tokenFromType(TokenType.FUNCTION), expression,
+          TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static ExpressionStatement expressionStatement(Expression expression) =>
+      new ExpressionStatement(
+          expression, TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static ExtendsClause extendsClause(TypeName type) =>
+      new ExtendsClause(TokenFactory.tokenFromKeyword(Keyword.EXTENDS), type);
+
+  static FieldDeclaration fieldDeclaration(bool isStatic, Keyword keyword,
+          TypeName type, List<VariableDeclaration> variables) =>
+      new FieldDeclaration(null, null,
+          isStatic ? TokenFactory.tokenFromKeyword(Keyword.STATIC) : null,
+          variableDeclarationList(keyword, type, variables),
+          TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static FieldDeclaration fieldDeclaration2(bool isStatic, Keyword keyword,
+          List<VariableDeclaration> variables) =>
+      fieldDeclaration(isStatic, keyword, null, variables);
+
+  static FieldFormalParameter fieldFormalParameter(
+      Keyword keyword, TypeName type, String identifier,
+      [FormalParameterList parameterList]) => new FieldFormalParameter(null,
+      null, keyword == null ? null : TokenFactory.tokenFromKeyword(keyword),
+      type, TokenFactory.tokenFromKeyword(Keyword.THIS),
+      TokenFactory.tokenFromType(TokenType.PERIOD), identifier3(identifier),
+      parameterList);
+
+  static FieldFormalParameter fieldFormalParameter2(String identifier) =>
+      fieldFormalParameter(null, null, identifier);
+
+  static ForEachStatement forEachStatement(DeclaredIdentifier loopVariable,
+      Expression iterator, Statement body) => new ForEachStatement.con1(null,
+      TokenFactory.tokenFromKeyword(Keyword.FOR),
+      TokenFactory.tokenFromType(TokenType.OPEN_PAREN), loopVariable,
+      TokenFactory.tokenFromKeyword(Keyword.IN), iterator,
+      TokenFactory.tokenFromType(TokenType.CLOSE_PAREN), body);
+
+  static ForEachStatement forEachStatement2(
+          SimpleIdentifier identifier, Expression iterator, Statement body) =>
+      new ForEachStatement.con2(null,
+          TokenFactory.tokenFromKeyword(Keyword.FOR),
+          TokenFactory.tokenFromType(TokenType.OPEN_PAREN), identifier,
+          TokenFactory.tokenFromKeyword(Keyword.IN), iterator,
+          TokenFactory.tokenFromType(TokenType.CLOSE_PAREN), body);
+
+  static FormalParameterList formalParameterList(
+      [List<FormalParameter> parameters]) => new FormalParameterList(
+      TokenFactory.tokenFromType(TokenType.OPEN_PAREN), parameters, null, null,
+      TokenFactory.tokenFromType(TokenType.CLOSE_PAREN));
+
+  static ForStatement forStatement(Expression initialization,
+          Expression condition, List<Expression> updaters, Statement body) =>
+      new ForStatement(TokenFactory.tokenFromKeyword(Keyword.FOR),
+          TokenFactory.tokenFromType(TokenType.OPEN_PAREN), null,
+          initialization, TokenFactory.tokenFromType(TokenType.SEMICOLON),
+          condition, TokenFactory.tokenFromType(TokenType.SEMICOLON), updaters,
+          TokenFactory.tokenFromType(TokenType.CLOSE_PAREN), body);
+
+  static ForStatement forStatement2(VariableDeclarationList variableList,
+          Expression condition, List<Expression> updaters, Statement body) =>
+      new ForStatement(TokenFactory.tokenFromKeyword(Keyword.FOR),
+          TokenFactory.tokenFromType(TokenType.OPEN_PAREN), variableList, null,
+          TokenFactory.tokenFromType(TokenType.SEMICOLON), condition,
+          TokenFactory.tokenFromType(TokenType.SEMICOLON), updaters,
+          TokenFactory.tokenFromType(TokenType.CLOSE_PAREN), body);
+
+  static FunctionDeclaration functionDeclaration(TypeName type, Keyword keyword,
+          String name, FunctionExpression functionExpression) =>
+      new FunctionDeclaration(null, null, null, type,
+          keyword == null ? null : TokenFactory.tokenFromKeyword(keyword),
+          identifier3(name), functionExpression);
+
+  static FunctionDeclarationStatement functionDeclarationStatement(
+          TypeName type, Keyword keyword, String name,
+          FunctionExpression functionExpression) =>
+      new FunctionDeclarationStatement(
+          functionDeclaration(type, keyword, name, functionExpression));
+
+  static FunctionExpression functionExpression() =>
+      new FunctionExpression(formalParameterList(), blockFunctionBody2());
+
+  static FunctionExpression functionExpression2(
+          FormalParameterList parameters, FunctionBody body) =>
+      new FunctionExpression(parameters, body);
+
+  static FunctionExpressionInvocation functionExpressionInvocation(
+          Expression function, [List<Expression> arguments]) =>
+      new FunctionExpressionInvocation(function, argumentList(arguments));
+
+  static FunctionTypedFormalParameter functionTypedFormalParameter(
+      TypeName returnType, String identifier,
+      [List<FormalParameter> parameters]) => new FunctionTypedFormalParameter(
+      null, null, returnType, identifier3(identifier),
+      formalParameterList(parameters));
+
+  static HideCombinator hideCombinator(List<SimpleIdentifier> identifiers) =>
+      new HideCombinator(TokenFactory.tokenFromString("hide"), identifiers);
+
+  static HideCombinator hideCombinator2(List<String> identifiers) =>
+      new HideCombinator(
+          TokenFactory.tokenFromString("hide"), identifierList(identifiers));
+
+  static PrefixedIdentifier identifier(
+          SimpleIdentifier prefix, SimpleIdentifier identifier) =>
+      new PrefixedIdentifier(
+          prefix, TokenFactory.tokenFromType(TokenType.PERIOD), identifier);
+
+  static SimpleIdentifier identifier3(String lexeme) => new SimpleIdentifier(
+      TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, lexeme));
+
+  static PrefixedIdentifier identifier4(
+      String prefix, SimpleIdentifier identifier) => new PrefixedIdentifier(
+      identifier3(prefix), TokenFactory.tokenFromType(TokenType.PERIOD),
+      identifier);
+
+  static PrefixedIdentifier identifier5(String prefix, String identifier) =>
+      new PrefixedIdentifier(identifier3(prefix),
+          TokenFactory.tokenFromType(TokenType.PERIOD),
+          identifier3(identifier));
+
+  static List<SimpleIdentifier> identifierList(List<String> identifiers) {
+    if (identifiers == null) {
+      return null;
+    }
+    return identifiers
+        .map((String identifier) => identifier3(identifier))
+        .toList();
+  }
+
+  static IfStatement ifStatement(
+          Expression condition, Statement thenStatement) =>
+      ifStatement2(condition, thenStatement, null);
+
+  static IfStatement ifStatement2(Expression condition, Statement thenStatement,
+      Statement elseStatement) => new IfStatement(
+      TokenFactory.tokenFromKeyword(Keyword.IF),
+      TokenFactory.tokenFromType(TokenType.OPEN_PAREN), condition,
+      TokenFactory.tokenFromType(TokenType.CLOSE_PAREN), thenStatement,
+      elseStatement == null
+          ? null
+          : TokenFactory.tokenFromKeyword(Keyword.ELSE), elseStatement);
+
+  static ImplementsClause implementsClause(List<TypeName> types) =>
+      new ImplementsClause(
+          TokenFactory.tokenFromKeyword(Keyword.IMPLEMENTS), types);
+
+  static ImportDirective importDirective(
+      List<Annotation> metadata, String uri, bool isDeferred, String prefix,
+      [List<Combinator> combinators]) => new ImportDirective(null, metadata,
+      TokenFactory.tokenFromKeyword(Keyword.IMPORT), string2(uri),
+      !isDeferred ? null : TokenFactory.tokenFromKeyword(Keyword.DEFERRED),
+      prefix == null ? null : TokenFactory.tokenFromKeyword(Keyword.AS),
+      prefix == null ? null : identifier3(prefix), combinators,
+      TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static ImportDirective importDirective2(
+          String uri, bool isDeferred, String prefix,
+          [List<Combinator> combinators]) =>
+      importDirective(null, uri, isDeferred, prefix, combinators);
+
+  static ImportDirective importDirective3(String uri, String prefix,
+          [List<Combinator> combinators]) =>
+      importDirective(null, uri, false, prefix, combinators);
+
+  static IndexExpression indexExpression(Expression array, Expression index) =>
+      new IndexExpression.forTarget(array,
+          TokenFactory.tokenFromType(TokenType.OPEN_SQUARE_BRACKET), index,
+          TokenFactory.tokenFromType(TokenType.CLOSE_SQUARE_BRACKET));
+
+  static InstanceCreationExpression instanceCreationExpression(
+      Keyword keyword, ConstructorName name,
+      [List<Expression> arguments]) => new InstanceCreationExpression(
+      keyword == null ? null : TokenFactory.tokenFromKeyword(keyword), name,
+      argumentList(arguments));
+
+  static InstanceCreationExpression instanceCreationExpression2(
+          Keyword keyword, TypeName type, [List<Expression> arguments]) =>
+      instanceCreationExpression3(keyword, type, null, arguments);
+
+  static InstanceCreationExpression instanceCreationExpression3(
+      Keyword keyword, TypeName type, String identifier,
+      [List<Expression> arguments]) => instanceCreationExpression(keyword,
+          new ConstructorName(type, identifier == null
+                  ? null
+                  : TokenFactory.tokenFromType(TokenType.PERIOD),
+              identifier == null ? null : identifier3(identifier)), arguments);
+
+  static IntegerLiteral integer(int value) => new IntegerLiteral(
+      TokenFactory.tokenFromTypeAndString(TokenType.INT, value.toString()),
+      value);
+
+  static InterpolationExpression interpolationExpression(
+      Expression expression) => new InterpolationExpression(
+      TokenFactory.tokenFromType(TokenType.STRING_INTERPOLATION_EXPRESSION),
+      expression, TokenFactory.tokenFromType(TokenType.CLOSE_CURLY_BRACKET));
+
+  static InterpolationExpression interpolationExpression2(String identifier) =>
+      new InterpolationExpression(
+          TokenFactory.tokenFromType(TokenType.STRING_INTERPOLATION_IDENTIFIER),
+          identifier3(identifier), null);
+
+  static InterpolationString interpolationString(
+          String contents, String value) =>
+      new InterpolationString(TokenFactory.tokenFromString(contents), value);
+
+  static IsExpression isExpression(
+      Expression expression, bool negated, TypeName type) => new IsExpression(
+      expression, TokenFactory.tokenFromKeyword(Keyword.IS),
+      negated ? TokenFactory.tokenFromType(TokenType.BANG) : null, type);
+
+  static Label label(SimpleIdentifier label) =>
+      new Label(label, TokenFactory.tokenFromType(TokenType.COLON));
+
+  static Label label2(String label) => AstFactory.label(identifier3(label));
+
+  static LabeledStatement labeledStatement(
+          List<Label> labels, Statement statement) =>
+      new LabeledStatement(labels, statement);
+
+  static LibraryDirective libraryDirective(
+          List<Annotation> metadata, LibraryIdentifier libraryName) =>
+      new LibraryDirective(null, metadata,
+          TokenFactory.tokenFromKeyword(Keyword.LIBRARY), libraryName,
+          TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static LibraryDirective libraryDirective2(String libraryName) =>
+      libraryDirective(
+          new List<Annotation>(), libraryIdentifier2([libraryName]));
+
+  static LibraryIdentifier libraryIdentifier(
+      List<SimpleIdentifier> components) => new LibraryIdentifier(components);
+
+  static LibraryIdentifier libraryIdentifier2(List<String> components) {
+    return new LibraryIdentifier(identifierList(components));
+  }
+
+  static List list(List<Object> elements) {
+    return elements;
+  }
+
+  static ListLiteral listLiteral([List<Expression> elements]) =>
+      listLiteral2(null, null, elements);
+
+  static ListLiteral listLiteral2(
+      Keyword keyword, TypeArgumentList typeArguments,
+      [List<Expression> elements]) => new ListLiteral(
+      keyword == null ? null : TokenFactory.tokenFromKeyword(keyword),
+      typeArguments, TokenFactory.tokenFromType(TokenType.OPEN_SQUARE_BRACKET),
+      elements, TokenFactory.tokenFromType(TokenType.CLOSE_SQUARE_BRACKET));
+
+  static MapLiteral mapLiteral(Keyword keyword, TypeArgumentList typeArguments,
+      [List<MapLiteralEntry> entries]) => new MapLiteral(
+      keyword == null ? null : TokenFactory.tokenFromKeyword(keyword),
+      typeArguments, TokenFactory.tokenFromType(TokenType.OPEN_CURLY_BRACKET),
+      entries, TokenFactory.tokenFromType(TokenType.CLOSE_CURLY_BRACKET));
+
+  static MapLiteral mapLiteral2([List<MapLiteralEntry> entries]) =>
+      mapLiteral(null, null, entries);
+
+  static MapLiteralEntry mapLiteralEntry(String key, Expression value) =>
+      new MapLiteralEntry(
+          string2(key), TokenFactory.tokenFromType(TokenType.COLON), value);
+
+  static MethodDeclaration methodDeclaration(Keyword modifier,
+      TypeName returnType, Keyword property, Keyword operator,
+      SimpleIdentifier name,
+      FormalParameterList parameters) => new MethodDeclaration(null, null,
+      TokenFactory.tokenFromKeyword(Keyword.EXTERNAL),
+      modifier == null ? null : TokenFactory.tokenFromKeyword(modifier),
+      returnType,
+      property == null ? null : TokenFactory.tokenFromKeyword(property),
+      operator == null ? null : TokenFactory.tokenFromKeyword(operator), name,
+      parameters, emptyFunctionBody());
+
+  static MethodDeclaration methodDeclaration2(Keyword modifier,
+      TypeName returnType, Keyword property, Keyword operator,
+      SimpleIdentifier name, FormalParameterList parameters,
+      FunctionBody body) => new MethodDeclaration(null, null, null,
+      modifier == null ? null : TokenFactory.tokenFromKeyword(modifier),
+      returnType,
+      property == null ? null : TokenFactory.tokenFromKeyword(property),
+      operator == null ? null : TokenFactory.tokenFromKeyword(operator), name,
+      parameters, body);
+
+  static MethodInvocation methodInvocation(Expression target, String methodName,
+      [List<Expression> arguments,
+      TokenType operator = TokenType.PERIOD]) => new MethodInvocation(target,
+      target == null ? null : TokenFactory.tokenFromType(operator),
+      identifier3(methodName), argumentList(arguments));
+
+  static MethodInvocation methodInvocation2(String methodName,
+          [List<Expression> arguments]) =>
+      methodInvocation(null, methodName, arguments);
+
+  static NamedExpression namedExpression(Label label, Expression expression) =>
+      new NamedExpression(label, expression);
+
+  static NamedExpression namedExpression2(
+          String label, Expression expression) =>
+      namedExpression(label2(label), expression);
+
+  static DefaultFormalParameter namedFormalParameter(
+          NormalFormalParameter parameter, Expression expression) =>
+      new DefaultFormalParameter(parameter, ParameterKind.NAMED,
+          expression == null
+              ? null
+              : TokenFactory.tokenFromType(TokenType.COLON), expression);
+
+  static NativeClause nativeClause(String nativeCode) => new NativeClause(
+      TokenFactory.tokenFromString("native"), string2(nativeCode));
+
+  static NativeFunctionBody nativeFunctionBody(String nativeMethodName) =>
+      new NativeFunctionBody(TokenFactory.tokenFromString("native"),
+          string2(nativeMethodName),
+          TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static NullLiteral nullLiteral() =>
+      new NullLiteral(TokenFactory.tokenFromKeyword(Keyword.NULL));
+
+  static ParenthesizedExpression parenthesizedExpression(
+      Expression expression) => new ParenthesizedExpression(
+      TokenFactory.tokenFromType(TokenType.OPEN_PAREN), expression,
+      TokenFactory.tokenFromType(TokenType.CLOSE_PAREN));
+
+  static PartDirective partDirective(List<Annotation> metadata, String url) =>
+      new PartDirective(null, metadata,
+          TokenFactory.tokenFromKeyword(Keyword.PART), string2(url),
+          TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static PartDirective partDirective2(String url) =>
+      partDirective(new List<Annotation>(), url);
+
+  static PartOfDirective partOfDirective(LibraryIdentifier libraryName) =>
+      partOfDirective2(new List<Annotation>(), libraryName);
+
+  static PartOfDirective partOfDirective2(
+          List<Annotation> metadata, LibraryIdentifier libraryName) =>
+      new PartOfDirective(null, metadata,
+          TokenFactory.tokenFromKeyword(Keyword.PART),
+          TokenFactory.tokenFromString("of"), libraryName,
+          TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static DefaultFormalParameter positionalFormalParameter(
+          NormalFormalParameter parameter, Expression expression) =>
+      new DefaultFormalParameter(parameter, ParameterKind.POSITIONAL,
+          expression == null ? null : TokenFactory.tokenFromType(TokenType.EQ),
+          expression);
+
+  static PostfixExpression postfixExpression(
+          Expression expression, TokenType operator) =>
+      new PostfixExpression(expression, TokenFactory.tokenFromType(operator));
+
+  static PrefixExpression prefixExpression(
+          TokenType operator, Expression expression) =>
+      new PrefixExpression(TokenFactory.tokenFromType(operator), expression);
+
+  static PropertyAccess propertyAccess(
+      Expression target, SimpleIdentifier propertyName) => new PropertyAccess(
+      target, TokenFactory.tokenFromType(TokenType.PERIOD), propertyName);
+
+  static PropertyAccess propertyAccess2(Expression target, String propertyName,
+      [TokenType operator = TokenType.PERIOD]) => new PropertyAccess(
+      target, TokenFactory.tokenFromType(operator), identifier3(propertyName));
+
+  static RedirectingConstructorInvocation redirectingConstructorInvocation(
+          [List<Expression> arguments]) =>
+      redirectingConstructorInvocation2(null, arguments);
+
+  static RedirectingConstructorInvocation redirectingConstructorInvocation2(
+          String constructorName, [List<Expression> arguments]) =>
+      new RedirectingConstructorInvocation(
+          TokenFactory.tokenFromKeyword(Keyword.THIS), constructorName == null
+              ? null
+              : TokenFactory.tokenFromType(TokenType.PERIOD),
+          constructorName == null ? null : identifier3(constructorName),
+          argumentList(arguments));
+
+  static RethrowExpression rethrowExpression() =>
+      new RethrowExpression(TokenFactory.tokenFromKeyword(Keyword.RETHROW));
+
+  static ReturnStatement returnStatement() => returnStatement2(null);
+
+  static ReturnStatement returnStatement2(Expression expression) =>
+      new ReturnStatement(TokenFactory.tokenFromKeyword(Keyword.RETURN),
+          expression, TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static ScriptTag scriptTag(String scriptTag) =>
+      new ScriptTag(TokenFactory.tokenFromString(scriptTag));
+
+  static ShowCombinator showCombinator(List<SimpleIdentifier> identifiers) =>
+      new ShowCombinator(TokenFactory.tokenFromString("show"), identifiers);
+
+  static ShowCombinator showCombinator2(List<String> identifiers) =>
+      new ShowCombinator(
+          TokenFactory.tokenFromString("show"), identifierList(identifiers));
+
+  static SimpleFormalParameter simpleFormalParameter(
+          Keyword keyword, String parameterName) =>
+      simpleFormalParameter2(keyword, null, parameterName);
+
+  static SimpleFormalParameter simpleFormalParameter2(
+          Keyword keyword, TypeName type, String parameterName) =>
+      new SimpleFormalParameter(null, null,
+          keyword == null ? null : TokenFactory.tokenFromKeyword(keyword), type,
+          identifier3(parameterName));
+
+  static SimpleFormalParameter simpleFormalParameter3(String parameterName) =>
+      simpleFormalParameter2(null, null, parameterName);
+
+  static SimpleFormalParameter simpleFormalParameter4(
+          TypeName type, String parameterName) =>
+      simpleFormalParameter2(null, type, parameterName);
+
+  static StringInterpolation string([List<InterpolationElement> elements]) =>
+      new StringInterpolation(elements);
+
+  static SimpleStringLiteral string2(String content) => new SimpleStringLiteral(
+      TokenFactory.tokenFromString("'$content'"), content);
+
+  static SuperConstructorInvocation superConstructorInvocation(
+          [List<Expression> arguments]) =>
+      superConstructorInvocation2(null, arguments);
+
+  static SuperConstructorInvocation superConstructorInvocation2(String name,
+      [List<Expression> arguments]) => new SuperConstructorInvocation(
+      TokenFactory.tokenFromKeyword(Keyword.SUPER),
+      name == null ? null : TokenFactory.tokenFromType(TokenType.PERIOD),
+      name == null ? null : identifier3(name), argumentList(arguments));
+
+  static SuperExpression superExpression() =>
+      new SuperExpression(TokenFactory.tokenFromKeyword(Keyword.SUPER));
+
+  static SwitchCase switchCase(
+          Expression expression, List<Statement> statements) =>
+      switchCase2(new List<Label>(), expression, statements);
+
+  static SwitchCase switchCase2(List<Label> labels, Expression expression,
+      List<Statement> statements) => new SwitchCase(labels,
+      TokenFactory.tokenFromKeyword(Keyword.CASE), expression,
+      TokenFactory.tokenFromType(TokenType.COLON), statements);
+
+  static SwitchDefault switchDefault(
+      List<Label> labels, List<Statement> statements) => new SwitchDefault(
+      labels, TokenFactory.tokenFromKeyword(Keyword.DEFAULT),
+      TokenFactory.tokenFromType(TokenType.COLON), statements);
+
+  static SwitchDefault switchDefault2(List<Statement> statements) =>
+      switchDefault(new List<Label>(), statements);
+
+  static SwitchStatement switchStatement(
+      Expression expression, List<SwitchMember> members) => new SwitchStatement(
+      TokenFactory.tokenFromKeyword(Keyword.SWITCH),
+      TokenFactory.tokenFromType(TokenType.OPEN_PAREN), expression,
+      TokenFactory.tokenFromType(TokenType.CLOSE_PAREN),
+      TokenFactory.tokenFromType(TokenType.OPEN_CURLY_BRACKET), members,
+      TokenFactory.tokenFromType(TokenType.CLOSE_CURLY_BRACKET));
+
+  static SymbolLiteral symbolLiteral(List<String> components) {
+    List<Token> identifierList = new List<Token>();
+    for (String component in components) {
+      identifierList.add(
+          TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, component));
+    }
+    return new SymbolLiteral(
+        TokenFactory.tokenFromType(TokenType.HASH), identifierList);
+  }
+
+  static BlockFunctionBody syncBlockFunctionBody(
+      [List<Statement> statements]) => new BlockFunctionBody(
+      TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "sync"), null,
+      block(statements));
+
+  static BlockFunctionBody syncGeneratorBlockFunctionBody(
+      [List<Statement> statements]) => new BlockFunctionBody(
+      TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "sync"),
+      TokenFactory.tokenFromType(TokenType.STAR), block(statements));
+
+  static ThisExpression thisExpression() =>
+      new ThisExpression(TokenFactory.tokenFromKeyword(Keyword.THIS));
+
+  static ThrowExpression throwExpression() => throwExpression2(null);
+
+  static ThrowExpression throwExpression2(Expression expression) =>
+      new ThrowExpression(
+          TokenFactory.tokenFromKeyword(Keyword.THROW), expression);
+
+  static TopLevelVariableDeclaration topLevelVariableDeclaration(
+      Keyword keyword, TypeName type,
+      List<VariableDeclaration> variables) => new TopLevelVariableDeclaration(
+      null, null, variableDeclarationList(keyword, type, variables),
+      TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static TopLevelVariableDeclaration topLevelVariableDeclaration2(
+          Keyword keyword, List<VariableDeclaration> variables) =>
+      new TopLevelVariableDeclaration(null, null,
+          variableDeclarationList(keyword, null, variables),
+          TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static TryStatement tryStatement(Block body, Block finallyClause) =>
+      tryStatement3(body, new List<CatchClause>(), finallyClause);
+
+  static TryStatement tryStatement2(
+          Block body, List<CatchClause> catchClauses) =>
+      tryStatement3(body, catchClauses, null);
+
+  static TryStatement tryStatement3(
+          Block body, List<CatchClause> catchClauses, Block finallyClause) =>
+      new TryStatement(TokenFactory.tokenFromKeyword(Keyword.TRY), body,
+          catchClauses, finallyClause == null
+              ? null
+              : TokenFactory.tokenFromKeyword(Keyword.FINALLY), finallyClause);
+
+  static FunctionTypeAlias typeAlias(TypeName returnType, String name,
+          TypeParameterList typeParameters, FormalParameterList parameters) =>
+      new FunctionTypeAlias(null, null,
+          TokenFactory.tokenFromKeyword(Keyword.TYPEDEF), returnType,
+          identifier3(name), typeParameters, parameters,
+          TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static TypeArgumentList typeArgumentList(List<TypeName> typeNames) {
+    if (typeNames == null || typeNames.length == 0) {
+      return null;
+    }
+    return new TypeArgumentList(TokenFactory.tokenFromType(TokenType.LT),
+        typeNames, TokenFactory.tokenFromType(TokenType.GT));
+  }
+
+  /**
+   * Create a type name whose name has been resolved to the given [element] and
+   * whose type has been resolved to the type of the given element.
+   *
+   * <b>Note:</b> This method does not correctly handle class elements that have
+   * type parameters.
+   */
+  static TypeName typeName(ClassElement element, [List<TypeName> arguments]) {
+    SimpleIdentifier name = identifier3(element.name);
+    name.staticElement = element;
+    TypeName typeName = typeName3(name, arguments);
+    typeName.type = element.type;
+    return typeName;
+  }
+
+  static TypeName typeName3(Identifier name, [List<TypeName> arguments]) =>
+      new TypeName(name, typeArgumentList(arguments));
+
+  static TypeName typeName4(String name, [List<TypeName> arguments]) =>
+      new TypeName(identifier3(name), typeArgumentList(arguments));
+
+  static TypeParameter typeParameter(String name) =>
+      new TypeParameter(null, null, identifier3(name), null, null);
+
+  static TypeParameter typeParameter2(String name, TypeName bound) =>
+      new TypeParameter(null, null, identifier3(name),
+          TokenFactory.tokenFromKeyword(Keyword.EXTENDS), bound);
+
+  static TypeParameterList typeParameterList([List<String> typeNames]) {
+    List<TypeParameter> typeParameters = null;
+    if (typeNames != null && !typeNames.isEmpty) {
+      typeParameters = new List<TypeParameter>();
+      for (String typeName in typeNames) {
+        typeParameters.add(typeParameter(typeName));
+      }
+    }
+    return new TypeParameterList(TokenFactory.tokenFromType(TokenType.LT),
+        typeParameters, TokenFactory.tokenFromType(TokenType.GT));
+  }
+
+  static VariableDeclaration variableDeclaration(String name) =>
+      new VariableDeclaration(identifier3(name), null, null);
+
+  static VariableDeclaration variableDeclaration2(
+      String name, Expression initializer) => new VariableDeclaration(
+      identifier3(name), TokenFactory.tokenFromType(TokenType.EQ), initializer);
+
+  static VariableDeclarationList variableDeclarationList(Keyword keyword,
+          TypeName type, List<VariableDeclaration> variables) =>
+      new VariableDeclarationList(null, null,
+          keyword == null ? null : TokenFactory.tokenFromKeyword(keyword), type,
+          variables);
+
+  static VariableDeclarationList variableDeclarationList2(
+          Keyword keyword, List<VariableDeclaration> variables) =>
+      variableDeclarationList(keyword, null, variables);
+
+  static VariableDeclarationStatement variableDeclarationStatement(
+      Keyword keyword, TypeName type,
+      List<VariableDeclaration> variables) => new VariableDeclarationStatement(
+      variableDeclarationList(keyword, type, variables),
+      TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static VariableDeclarationStatement variableDeclarationStatement2(
+          Keyword keyword, List<VariableDeclaration> variables) =>
+      variableDeclarationStatement(keyword, null, variables);
+
+  static WhileStatement whileStatement(Expression condition, Statement body) =>
+      new WhileStatement(TokenFactory.tokenFromKeyword(Keyword.WHILE),
+          TokenFactory.tokenFromType(TokenType.OPEN_PAREN), condition,
+          TokenFactory.tokenFromType(TokenType.CLOSE_PAREN), body);
+
+  static WithClause withClause(List<TypeName> types) =>
+      new WithClause(TokenFactory.tokenFromKeyword(Keyword.WITH), types);
+
+  static YieldStatement yieldEachStatement(Expression expression) =>
+      new YieldStatement(
+          TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "yield"),
+          TokenFactory.tokenFromType(TokenType.STAR), expression,
+          TokenFactory.tokenFromType(TokenType.SEMICOLON));
+
+  static YieldStatement yieldStatement(
+      Expression expression) => new YieldStatement(
+      TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "yield"), null,
+      expression, TokenFactory.tokenFromType(TokenType.SEMICOLON));
+}
diff --git a/analyzer/lib/src/generated/testing/element_factory.dart b/analyzer/lib/src/generated/testing/element_factory.dart
new file mode 100644
index 0000000..497990c
--- /dev/null
+++ b/analyzer/lib/src/generated/testing/element_factory.dart
@@ -0,0 +1,532 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.testing.element_factory;
+
+import 'dart:collection';
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/constant.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/java_core.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/utilities_dart.dart';
+
+/**
+ * The class `ElementFactory` defines utility methods used to create elements for testing
+ * purposes. The elements that are created are complete in the sense that as much of the element
+ * model as can be created, given the provided information, has been created.
+ */
+class ElementFactory {
+  /**
+   * The element representing the class 'Object'.
+   */
+  static ClassElementImpl _objectElement;
+
+  static ClassElementImpl get object {
+    if (_objectElement == null) {
+      _objectElement = classElement("Object", null);
+    }
+    return _objectElement;
+  }
+
+  static InterfaceType get objectType => object.type;
+
+  static ClassElementImpl classElement(
+      String typeName, InterfaceType superclassType,
+      [List<String> parameterNames]) {
+    ClassElementImpl element = new ClassElementImpl(typeName, 0);
+    element.supertype = superclassType;
+    InterfaceTypeImpl type = new InterfaceTypeImpl.con1(element);
+    element.type = type;
+    if (parameterNames != null) {
+      int count = parameterNames.length;
+      if (count > 0) {
+        List<TypeParameterElementImpl> typeParameters =
+            new List<TypeParameterElementImpl>(count);
+        List<TypeParameterTypeImpl> typeParameterTypes =
+            new List<TypeParameterTypeImpl>(count);
+        for (int i = 0; i < count; i++) {
+          TypeParameterElementImpl typeParameter =
+              new TypeParameterElementImpl(parameterNames[i], 0);
+          typeParameters[i] = typeParameter;
+          typeParameterTypes[i] = new TypeParameterTypeImpl(typeParameter);
+          typeParameter.type = typeParameterTypes[i];
+        }
+        element.typeParameters = typeParameters;
+        type.typeArguments = typeParameterTypes;
+      }
+    }
+    return element;
+  }
+
+  static ClassElementImpl classElement2(String typeName,
+          [List<String> parameterNames]) =>
+      classElement(typeName, objectType, parameterNames);
+
+  static classTypeAlias(String typeName, InterfaceType superclassType,
+      [List<String> parameterNames]) {
+    ClassElementImpl element =
+        classElement(typeName, superclassType, parameterNames);
+    element.typedef = true;
+    return element;
+  }
+
+  static ClassElementImpl classTypeAlias2(String typeName,
+          [List<String> parameterNames]) =>
+      classTypeAlias(typeName, objectType, parameterNames);
+
+  static CompilationUnitElementImpl compilationUnit(String fileName) {
+    Source source = new NonExistingSource(fileName, UriKind.FILE_URI);
+    CompilationUnitElementImpl unit = new CompilationUnitElementImpl(fileName);
+    unit.source = source;
+    return unit;
+  }
+
+  static ConstLocalVariableElementImpl constLocalVariableElement(String name) =>
+      new ConstLocalVariableElementImpl(name, 0);
+
+  static ConstructorElementImpl constructorElement(
+      ClassElement definingClass, String name, bool isConst,
+      [List<DartType> argumentTypes]) {
+    DartType type = definingClass.type;
+    ConstructorElementImpl constructor = name == null
+        ? new ConstructorElementImpl("", -1)
+        : new ConstructorElementImpl(name, 0);
+    constructor.const2 = isConst;
+    if (argumentTypes != null) {
+      int count = argumentTypes.length;
+      List<ParameterElement> parameters = new List<ParameterElement>(count);
+      for (int i = 0; i < count; i++) {
+        ParameterElementImpl parameter = new ParameterElementImpl("a$i", i);
+        parameter.type = argumentTypes[i];
+        parameter.parameterKind = ParameterKind.REQUIRED;
+        parameters[i] = parameter;
+      }
+      constructor.parameters = parameters;
+    } else {
+      constructor.parameters = <ParameterElement>[];
+    }
+    constructor.returnType = type;
+    FunctionTypeImpl constructorType = new FunctionTypeImpl.con1(constructor);
+    constructor.type = constructorType;
+    return constructor;
+  }
+
+  static ConstructorElementImpl constructorElement2(
+          ClassElement definingClass, String name,
+          [List<DartType> argumentTypes]) =>
+      constructorElement(definingClass, name, false, argumentTypes);
+
+  static ClassElementImpl enumElement(
+      TypeProvider typeProvider, String enumName,
+      [List<String> constantNames]) {
+    //
+    // Build the enum.
+    //
+    ClassElementImpl enumElement = new ClassElementImpl(enumName, -1);
+    InterfaceTypeImpl enumType = new InterfaceTypeImpl.con1(enumElement);
+    enumElement.type = enumType;
+    enumElement.supertype = objectType;
+    enumElement.enum2 = true;
+    //
+    // Populate the fields.
+    //
+    List<FieldElement> fields = new List<FieldElement>();
+    InterfaceType intType = typeProvider.intType;
+    InterfaceType stringType = typeProvider.stringType;
+    String indexFieldName = "index";
+    FieldElementImpl indexField = new FieldElementImpl(indexFieldName, -1);
+    indexField.final2 = true;
+    indexField.type = intType;
+    fields.add(indexField);
+    String nameFieldName = "_name";
+    FieldElementImpl nameField = new FieldElementImpl(nameFieldName, -1);
+    nameField.final2 = true;
+    nameField.type = stringType;
+    fields.add(nameField);
+    FieldElementImpl valuesField = new FieldElementImpl("values", -1);
+    valuesField.static = true;
+    valuesField.const3 = true;
+    valuesField.type = typeProvider.listType.substitute4(<DartType>[enumType]);
+    fields.add(valuesField);
+    //
+    // Build the enum constants.
+    //
+    if (constantNames != null) {
+      int constantCount = constantNames.length;
+      for (int i = 0; i < constantCount; i++) {
+        String constantName = constantNames[i];
+        FieldElementImpl constantElement =
+            new ConstFieldElementImpl.con2(constantName, -1);
+        constantElement.static = true;
+        constantElement.const3 = true;
+        constantElement.type = enumType;
+        HashMap<String, DartObjectImpl> fieldMap =
+            new HashMap<String, DartObjectImpl>();
+        fieldMap[indexFieldName] = new DartObjectImpl(intType, new IntState(i));
+        fieldMap[nameFieldName] =
+            new DartObjectImpl(stringType, new StringState(constantName));
+        DartObjectImpl value =
+            new DartObjectImpl(enumType, new GenericState(fieldMap));
+        constantElement.evaluationResult = new EvaluationResultImpl.con1(value);
+        fields.add(constantElement);
+      }
+    }
+    //
+    // Finish building the enum.
+    //
+    enumElement.fields = fields;
+    // Client code isn't allowed to invoke the constructor, so we do not model it.
+    return enumElement;
+  }
+
+  static ExportElementImpl exportFor(LibraryElement exportedLibrary,
+      [List<NamespaceCombinator> combinators = NamespaceCombinator.EMPTY_LIST]) {
+    ExportElementImpl spec = new ExportElementImpl(-1);
+    spec.exportedLibrary = exportedLibrary;
+    spec.combinators = combinators;
+    return spec;
+  }
+
+  static FieldElementImpl fieldElement(
+      String name, bool isStatic, bool isFinal, bool isConst, DartType type) {
+    FieldElementImpl field = new FieldElementImpl(name, 0);
+    field.const3 = isConst;
+    field.final2 = isFinal;
+    field.static = isStatic;
+    field.type = type;
+    PropertyAccessorElementImpl getter =
+        new PropertyAccessorElementImpl.forVariable(field);
+    getter.getter = true;
+    getter.synthetic = true;
+    getter.variable = field;
+    getter.returnType = type;
+    field.getter = getter;
+    FunctionTypeImpl getterType = new FunctionTypeImpl.con1(getter);
+    getter.type = getterType;
+    if (!isConst && !isFinal) {
+      PropertyAccessorElementImpl setter =
+          new PropertyAccessorElementImpl.forVariable(field);
+      setter.setter = true;
+      setter.synthetic = true;
+      setter.variable = field;
+      setter.parameters =
+          <ParameterElement>[requiredParameter2("_$name", type)];
+      setter.returnType = VoidTypeImpl.instance;
+      setter.type = new FunctionTypeImpl.con1(setter);
+      field.setter = setter;
+    }
+    return field;
+  }
+
+  static FieldFormalParameterElementImpl fieldFormalParameter(
+      Identifier name) => new FieldFormalParameterElementImpl(name);
+
+  static FunctionElementImpl functionElement(String functionName) =>
+      functionElement4(functionName, null, null, null, null);
+
+  static FunctionElementImpl functionElement2(
+          String functionName, ClassElement returnElement) =>
+      functionElement3(functionName, returnElement, null, null);
+
+  static FunctionElementImpl functionElement3(String functionName,
+      ClassElement returnElement, List<ClassElement> normalParameters,
+      List<ClassElement> optionalParameters) {
+    // We don't create parameter elements because we don't have parameter names
+    FunctionElementImpl functionElement =
+        new FunctionElementImpl(functionName, 0);
+    FunctionTypeImpl functionType = new FunctionTypeImpl.con1(functionElement);
+    functionElement.type = functionType;
+    // return type
+    if (returnElement == null) {
+      functionElement.returnType = VoidTypeImpl.instance;
+    } else {
+      functionElement.returnType = returnElement.type;
+    }
+    // parameters
+    int normalCount = normalParameters == null ? 0 : normalParameters.length;
+    int optionalCount =
+        optionalParameters == null ? 0 : optionalParameters.length;
+    int totalCount = normalCount + optionalCount;
+    List<ParameterElement> parameters = new List<ParameterElement>(totalCount);
+    for (int i = 0; i < totalCount; i++) {
+      ParameterElementImpl parameter = new ParameterElementImpl("a$i", i);
+      if (i < normalCount) {
+        parameter.type = normalParameters[i].type;
+        parameter.parameterKind = ParameterKind.REQUIRED;
+      } else {
+        parameter.type = optionalParameters[i - normalCount].type;
+        parameter.parameterKind = ParameterKind.POSITIONAL;
+      }
+      parameters[i] = parameter;
+    }
+    functionElement.parameters = parameters;
+    // done
+    return functionElement;
+  }
+
+  static FunctionElementImpl functionElement4(String functionName,
+      ClassElement returnElement, List<ClassElement> normalParameters,
+      List<String> names, List<ClassElement> namedParameters) {
+    FunctionElementImpl functionElement =
+        new FunctionElementImpl(functionName, 0);
+    FunctionTypeImpl functionType = new FunctionTypeImpl.con1(functionElement);
+    functionElement.type = functionType;
+    // parameters
+    int normalCount = normalParameters == null ? 0 : normalParameters.length;
+    int nameCount = names == null ? 0 : names.length;
+    int typeCount = namedParameters == null ? 0 : namedParameters.length;
+    if (names != null && nameCount != typeCount) {
+      throw new IllegalStateException(
+          "The passed String[] and ClassElement[] arrays had different lengths.");
+    }
+    int totalCount = normalCount + nameCount;
+    List<ParameterElement> parameters = new List<ParameterElement>(totalCount);
+    for (int i = 0; i < totalCount; i++) {
+      if (i < normalCount) {
+        ParameterElementImpl parameter = new ParameterElementImpl("a$i", i);
+        parameter.type = normalParameters[i].type;
+        parameter.parameterKind = ParameterKind.REQUIRED;
+        parameters[i] = parameter;
+      } else {
+        ParameterElementImpl parameter =
+            new ParameterElementImpl(names[i - normalCount], i);
+        parameter.type = namedParameters[i - normalCount].type;
+        parameter.parameterKind = ParameterKind.NAMED;
+        parameters[i] = parameter;
+      }
+    }
+    functionElement.parameters = parameters;
+    // return type
+    if (returnElement == null) {
+      functionElement.returnType = VoidTypeImpl.instance;
+    } else {
+      functionElement.returnType = returnElement.type;
+    }
+    return functionElement;
+  }
+
+  static FunctionElementImpl functionElement5(
+          String functionName, List<ClassElement> normalParameters) =>
+      functionElement3(functionName, null, normalParameters, null);
+
+  static FunctionElementImpl functionElement6(String functionName,
+      List<ClassElement> normalParameters,
+      List<ClassElement> optionalParameters) => functionElement3(
+          functionName, null, normalParameters, optionalParameters);
+
+  static FunctionElementImpl functionElement7(String functionName,
+      List<ClassElement> normalParameters, List<String> names,
+      List<ClassElement> namedParameters) => functionElement4(
+          functionName, null, normalParameters, names, namedParameters);
+
+  static FunctionElementImpl functionElementWithParameters(String functionName,
+      DartType returnType, List<ParameterElement> parameters) {
+    FunctionElementImpl functionElement =
+        new FunctionElementImpl(functionName, 0);
+    functionElement.returnType =
+        returnType == null ? VoidTypeImpl.instance : returnType;
+    functionElement.parameters = parameters;
+    FunctionTypeImpl functionType = new FunctionTypeImpl.con1(functionElement);
+    functionElement.type = functionType;
+    return functionElement;
+  }
+
+  static PropertyAccessorElementImpl getterElement(
+      String name, bool isStatic, DartType type) {
+    FieldElementImpl field = new FieldElementImpl(name, -1);
+    field.static = isStatic;
+    field.synthetic = true;
+    field.type = type;
+    PropertyAccessorElementImpl getter =
+        new PropertyAccessorElementImpl.forVariable(field);
+    getter.getter = true;
+    getter.variable = field;
+    getter.returnType = type;
+    field.getter = getter;
+    FunctionTypeImpl getterType = new FunctionTypeImpl.con1(getter);
+    getter.type = getterType;
+    return getter;
+  }
+
+  static HtmlElementImpl htmlUnit(AnalysisContext context, String fileName) {
+    Source source = new NonExistingSource(fileName, UriKind.FILE_URI);
+    HtmlElementImpl unit = new HtmlElementImpl(context, fileName);
+    unit.source = source;
+    return unit;
+  }
+
+  static ImportElementImpl importFor(
+      LibraryElement importedLibrary, PrefixElement prefix,
+      [List<NamespaceCombinator> combinators = NamespaceCombinator.EMPTY_LIST]) {
+    ImportElementImpl spec = new ImportElementImpl(0);
+    spec.importedLibrary = importedLibrary;
+    spec.prefix = prefix;
+    spec.combinators = combinators;
+    return spec;
+  }
+
+  static LibraryElementImpl library(
+      AnalysisContext context, String libraryName) {
+    String fileName = "/$libraryName.dart";
+    CompilationUnitElementImpl unit = compilationUnit(fileName);
+    LibraryElementImpl library =
+        new LibraryElementImpl(context, libraryName, 0);
+    library.definingCompilationUnit = unit;
+    return library;
+  }
+
+  static LocalVariableElementImpl localVariableElement(Identifier name) =>
+      new LocalVariableElementImpl.forNode(name);
+
+  static LocalVariableElementImpl localVariableElement2(String name) =>
+      new LocalVariableElementImpl(name, 0);
+
+  static MethodElementImpl methodElement(String methodName, DartType returnType,
+      [List<DartType> argumentTypes]) {
+    MethodElementImpl method = new MethodElementImpl(methodName, 0);
+    if (argumentTypes == null) {
+      method.parameters = ParameterElement.EMPTY_LIST;
+    } else {
+      int count = argumentTypes.length;
+      List<ParameterElement> parameters = new List<ParameterElement>(count);
+      for (int i = 0; i < count; i++) {
+        ParameterElementImpl parameter = new ParameterElementImpl("a$i", i);
+        parameter.type = argumentTypes[i];
+        parameter.parameterKind = ParameterKind.REQUIRED;
+        parameters[i] = parameter;
+      }
+      method.parameters = parameters;
+    }
+    method.returnType = returnType;
+    FunctionTypeImpl methodType = new FunctionTypeImpl.con1(method);
+    method.type = methodType;
+    return method;
+  }
+
+  static MethodElementImpl methodElementWithParameters(String methodName,
+      List<DartType> typeArguments, DartType returnType,
+      List<ParameterElement> parameters) {
+    MethodElementImpl method = new MethodElementImpl(methodName, 0);
+    method.parameters = parameters;
+    method.returnType = returnType;
+    FunctionTypeImpl methodType = new FunctionTypeImpl.con1(method);
+    methodType.typeArguments = typeArguments;
+    method.type = methodType;
+    return method;
+  }
+
+  static ParameterElementImpl namedParameter(String name) {
+    ParameterElementImpl parameter = new ParameterElementImpl(name, 0);
+    parameter.parameterKind = ParameterKind.NAMED;
+    return parameter;
+  }
+
+  static ParameterElementImpl namedParameter2(String name, DartType type) {
+    ParameterElementImpl parameter = new ParameterElementImpl(name, 0);
+    parameter.parameterKind = ParameterKind.NAMED;
+    parameter.type = type;
+    return parameter;
+  }
+
+  static ParameterElementImpl positionalParameter(String name) {
+    ParameterElementImpl parameter = new ParameterElementImpl(name, 0);
+    parameter.parameterKind = ParameterKind.POSITIONAL;
+    return parameter;
+  }
+
+  static ParameterElementImpl positionalParameter2(String name, DartType type) {
+    ParameterElementImpl parameter = new ParameterElementImpl(name, 0);
+    parameter.parameterKind = ParameterKind.POSITIONAL;
+    parameter.type = type;
+    return parameter;
+  }
+
+  static PrefixElementImpl prefix(String name) =>
+      new PrefixElementImpl(name, 0);
+
+  static ParameterElementImpl requiredParameter(String name) {
+    ParameterElementImpl parameter = new ParameterElementImpl(name, 0);
+    parameter.parameterKind = ParameterKind.REQUIRED;
+    return parameter;
+  }
+
+  static ParameterElementImpl requiredParameter2(String name, DartType type) {
+    ParameterElementImpl parameter = new ParameterElementImpl(name, 0);
+    parameter.parameterKind = ParameterKind.REQUIRED;
+    parameter.type = type;
+    return parameter;
+  }
+
+  static PropertyAccessorElementImpl setterElement(
+      String name, bool isStatic, DartType type) {
+    FieldElementImpl field = new FieldElementImpl(name, -1);
+    field.static = isStatic;
+    field.synthetic = true;
+    field.type = type;
+    PropertyAccessorElementImpl getter =
+        new PropertyAccessorElementImpl.forVariable(field);
+    getter.getter = true;
+    getter.variable = field;
+    getter.returnType = type;
+    field.getter = getter;
+    FunctionTypeImpl getterType = new FunctionTypeImpl.con1(getter);
+    getter.type = getterType;
+    ParameterElementImpl parameter = requiredParameter2("a", type);
+    PropertyAccessorElementImpl setter =
+        new PropertyAccessorElementImpl.forVariable(field);
+    setter.setter = true;
+    setter.synthetic = true;
+    setter.variable = field;
+    setter.parameters = <ParameterElement>[parameter];
+    setter.returnType = VoidTypeImpl.instance;
+    setter.type = new FunctionTypeImpl.con1(setter);
+    field.setter = setter;
+    return setter;
+  }
+
+  static TopLevelVariableElementImpl topLevelVariableElement(Identifier name) =>
+      new TopLevelVariableElementImpl.forNode(name);
+
+  static TopLevelVariableElementImpl topLevelVariableElement2(String name) =>
+      topLevelVariableElement3(name, false, false, null);
+
+  static TopLevelVariableElementImpl topLevelVariableElement3(
+      String name, bool isConst, bool isFinal, DartType type) {
+    TopLevelVariableElementImpl variable =
+        new TopLevelVariableElementImpl(name, -1);
+    variable.const3 = isConst;
+    variable.final2 = isFinal;
+    variable.synthetic = true;
+    PropertyAccessorElementImpl getter =
+        new PropertyAccessorElementImpl.forVariable(variable);
+    getter.getter = true;
+    getter.synthetic = true;
+    getter.variable = variable;
+    getter.returnType = type;
+    variable.getter = getter;
+    FunctionTypeImpl getterType = new FunctionTypeImpl.con1(getter);
+    getter.type = getterType;
+    if (!isConst && !isFinal) {
+      PropertyAccessorElementImpl setter =
+          new PropertyAccessorElementImpl.forVariable(variable);
+      setter.setter = true;
+      setter.static = true;
+      setter.synthetic = true;
+      setter.variable = variable;
+      setter.parameters =
+          <ParameterElement>[requiredParameter2("_$name", type)];
+      setter.returnType = VoidTypeImpl.instance;
+      setter.type = new FunctionTypeImpl.con1(setter);
+      variable.setter = setter;
+    }
+    return variable;
+  }
+}
diff --git a/analyzer/lib/src/generated/testing/html_factory.dart b/analyzer/lib/src/generated/testing/html_factory.dart
new file mode 100644
index 0000000..a8fcb53
--- /dev/null
+++ b/analyzer/lib/src/generated/testing/html_factory.dart
@@ -0,0 +1,65 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.testing.html_factory;
+
+import 'package:analyzer/src/generated/html.dart';
+
+/**
+ * Utility methods to create HTML nodes.
+ */
+class HtmlFactory {
+  static XmlAttributeNode attribute(String name, String value) {
+    Token nameToken = stringToken(name);
+    Token equalsToken = new Token.con1(TokenType.EQ, 0);
+    Token valueToken = stringToken(value);
+    return new XmlAttributeNode(nameToken, equalsToken, valueToken);
+  }
+
+  static Token gtToken() {
+    return new Token.con1(TokenType.GT, 0);
+  }
+
+  static Token ltsToken() {
+    return new Token.con1(TokenType.LT_SLASH, 0);
+  }
+
+  static Token ltToken() {
+    return new Token.con1(TokenType.LT, 0);
+  }
+
+  static HtmlScriptTagNode scriptTag(
+      [List<XmlAttributeNode> attributes = XmlAttributeNode.EMPTY_LIST]) {
+    return new HtmlScriptTagNode(ltToken(), stringToken("script"), attributes,
+        sgtToken(), null, null, null, null);
+  }
+
+  static HtmlScriptTagNode scriptTagWithContent(String contents,
+      [List<XmlAttributeNode> attributes = XmlAttributeNode.EMPTY_LIST]) {
+    Token attributeEnd = gtToken();
+    Token contentToken = stringToken(contents);
+    attributeEnd.setNext(contentToken);
+    Token contentEnd = ltsToken();
+    contentToken.setNext(contentEnd);
+    return new HtmlScriptTagNode(ltToken(), stringToken("script"), attributes,
+        attributeEnd, null, contentEnd, stringToken("script"), gtToken());
+  }
+
+  static Token sgtToken() {
+    return new Token.con1(TokenType.SLASH_GT, 0);
+  }
+
+  static Token stringToken(String value) {
+    return new Token.con2(TokenType.STRING, 0, value);
+  }
+
+  static XmlTagNode tagNode(String name,
+      [List<XmlAttributeNode> attributes = XmlAttributeNode.EMPTY_LIST]) {
+    return new XmlTagNode(ltToken(), stringToken(name), attributes, sgtToken(),
+        null, null, null, null);
+  }
+}
diff --git a/analyzer/lib/src/generated/testing/test_type_provider.dart b/analyzer/lib/src/generated/testing/test_type_provider.dart
new file mode 100644
index 0000000..fe0c24b
--- /dev/null
+++ b/analyzer/lib/src/generated/testing/test_type_provider.dart
@@ -0,0 +1,618 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.testing.test_type_provider;
+
+import 'package:analyzer/src/generated/constant.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/testing/element_factory.dart';
+
+/**
+ * A type provider that can be used by tests without creating the element model
+ * for the core library.
+ */
+class TestTypeProvider implements TypeProvider {
+  /**
+   * The type representing the built-in type 'bool'.
+   */
+  InterfaceType _boolType;
+
+  /**
+   * The type representing the type 'bottom'.
+   */
+  DartType _bottomType;
+
+  /**
+   * The type representing the built-in type 'double'.
+   */
+  InterfaceType _doubleType;
+
+  /**
+   * The type representing the built-in type 'deprecated'.
+   */
+  InterfaceType _deprecatedType;
+
+  /**
+   * The type representing the built-in type 'dynamic'.
+   */
+  DartType _dynamicType;
+
+  /**
+   * The type representing the built-in type 'Function'.
+   */
+  InterfaceType _functionType;
+
+  /**
+   * The type representing 'Future<dynamic>'
+   */
+  InterfaceType _futureDynamicType;
+
+  /**
+   * The type representing 'Future<Null>'
+   */
+  InterfaceType _futureNullType;
+
+  /**
+   * The type representing the built-in type 'Future'
+   */
+  InterfaceType _futureType;
+
+  /**
+   * The type representing the built-in type 'int'.
+   */
+  InterfaceType _intType;
+
+  /**
+   * The type representing 'Iterable<dynamic>'
+   */
+  InterfaceType _iterableDynamicType;
+
+  /**
+   * The type representing the built-in type 'Iterable'.
+   */
+  InterfaceType _iterableType;
+
+  /**
+   * The type representing the built-in type 'Iterator'.
+   */
+  InterfaceType _iteratorType;
+
+  /**
+   * The type representing the built-in type 'List'.
+   */
+  InterfaceType _listType;
+
+  /**
+   * The type representing the built-in type 'Map'.
+   */
+  InterfaceType _mapType;
+
+  /**
+   * An shared object representing the value 'null'.
+   */
+  DartObjectImpl _nullObject;
+
+  /**
+   * The type representing the built-in type 'Null'.
+   */
+  InterfaceType _nullType;
+
+  /**
+   * The type representing the built-in type 'num'.
+   */
+  InterfaceType _numType;
+
+  /**
+   * The type representing the built-in type 'Object'.
+   */
+  InterfaceType _objectType;
+
+  /**
+   * The type representing the built-in type 'StackTrace'.
+   */
+  InterfaceType _stackTraceType;
+
+  /**
+   * The type representing 'Stream<dynamic>'.
+   */
+  InterfaceType _streamDynamicType;
+
+  /**
+   * The type representing the built-in type 'Stream'.
+   */
+  InterfaceType _streamType;
+
+  /**
+   * The type representing the built-in type 'String'.
+   */
+  InterfaceType _stringType;
+
+  /**
+   * The type representing the built-in type 'Symbol'.
+   */
+  InterfaceType _symbolType;
+
+  /**
+   * The type representing the built-in type 'Type'.
+   */
+  InterfaceType _typeType;
+
+  /**
+   * The type representing typenames that can't be resolved.
+   */
+  DartType _undefinedType;
+
+  @override
+  InterfaceType get boolType {
+    if (_boolType == null) {
+      ClassElementImpl boolElement = ElementFactory.classElement2("bool");
+      _boolType = boolElement.type;
+      ConstructorElementImpl fromEnvironment = ElementFactory
+          .constructorElement(boolElement, "fromEnvironment", true);
+      fromEnvironment.parameters = <ParameterElement>[
+        ElementFactory.requiredParameter2("name", stringType),
+        ElementFactory.namedParameter2("defaultValue", _boolType)
+      ];
+      fromEnvironment.factory = true;
+      fromEnvironment.isCycleFree = true;
+      boolElement.constructors = <ConstructorElement>[fromEnvironment];
+    }
+    return _boolType;
+  }
+
+  @override
+  DartType get bottomType {
+    if (_bottomType == null) {
+      _bottomType = BottomTypeImpl.instance;
+    }
+    return _bottomType;
+  }
+
+  @override
+  InterfaceType get deprecatedType {
+    if (_deprecatedType == null) {
+      ClassElementImpl deprecatedElement =
+          ElementFactory.classElement2("Deprecated");
+      deprecatedElement.constructors = <ConstructorElement>[
+        ElementFactory.constructorElement(
+            deprecatedElement, null, true, [stringType])
+      ];
+      _deprecatedType = deprecatedElement.type;
+    }
+    return _deprecatedType;
+  }
+
+  @override
+  InterfaceType get doubleType {
+    if (_doubleType == null) {
+      _initializeNumericTypes();
+    }
+    return _doubleType;
+  }
+
+  @override
+  DartType get dynamicType {
+    if (_dynamicType == null) {
+      _dynamicType = DynamicTypeImpl.instance;
+    }
+    return _dynamicType;
+  }
+
+  @override
+  InterfaceType get functionType {
+    if (_functionType == null) {
+      _functionType = ElementFactory.classElement2("Function").type;
+    }
+    return _functionType;
+  }
+
+  @override
+  InterfaceType get futureDynamicType {
+    if (_futureDynamicType == null) {
+      _futureDynamicType = futureType.substitute4(<DartType>[dynamicType]);
+    }
+    return _futureDynamicType;
+  }
+
+  @override
+  InterfaceType get futureNullType {
+    if (_futureNullType == null) {
+      _futureNullType = futureType.substitute4(<DartType>[nullType]);
+    }
+    return _futureNullType;
+  }
+
+  @override
+  InterfaceType get futureType {
+    if (_futureType == null) {
+      _futureType = ElementFactory.classElement2("Future", ["T"]).type;
+    }
+    return _futureType;
+  }
+
+  @override
+  InterfaceType get intType {
+    if (_intType == null) {
+      _initializeNumericTypes();
+    }
+    return _intType;
+  }
+
+  @override
+  InterfaceType get iterableDynamicType {
+    if (_iterableDynamicType == null) {
+      _iterableDynamicType = iterableType.substitute4(<DartType>[dynamicType]);
+    }
+    return _iterableDynamicType;
+  }
+
+  @override
+  InterfaceType get iterableType {
+    if (_iterableType == null) {
+      ClassElementImpl iterableElement =
+          ElementFactory.classElement2("Iterable", ["E"]);
+      _iterableType = iterableElement.type;
+      DartType eType = iterableElement.typeParameters[0].type;
+      _setAccessors(iterableElement, <PropertyAccessorElement>[
+        ElementFactory.getterElement(
+            "iterator", false, iteratorType.substitute4(<DartType>[eType])),
+        ElementFactory.getterElement("last", false, eType)
+      ]);
+      _propagateTypeArguments(iterableElement);
+    }
+    return _iterableType;
+  }
+
+  InterfaceType get iteratorType {
+    if (_iteratorType == null) {
+      ClassElementImpl iteratorElement =
+          ElementFactory.classElement2("Iterator", ["E"]);
+      _iteratorType = iteratorElement.type;
+      DartType eType = iteratorElement.typeParameters[0].type;
+      _setAccessors(iteratorElement, <PropertyAccessorElement>[
+        ElementFactory.getterElement("current", false, eType)
+      ]);
+      _propagateTypeArguments(iteratorElement);
+    }
+    return _iteratorType;
+  }
+
+  @override
+  InterfaceType get listType {
+    if (_listType == null) {
+      ClassElementImpl listElement =
+          ElementFactory.classElement2("List", ["E"]);
+      listElement.constructors = <ConstructorElement>[
+        ElementFactory.constructorElement2(listElement, null)
+      ];
+      _listType = listElement.type;
+      DartType eType = listElement.typeParameters[0].type;
+      InterfaceType iterableType =
+          this.iterableType.substitute4(<DartType>[eType]);
+      listElement.interfaces = <InterfaceType>[iterableType];
+      _setAccessors(listElement, <PropertyAccessorElement>[
+        ElementFactory.getterElement("length", false, intType)
+      ]);
+      listElement.methods = <MethodElement>[
+        ElementFactory.methodElement("[]", eType, [intType]),
+        ElementFactory.methodElement(
+            "[]=", VoidTypeImpl.instance, [intType, eType]),
+        ElementFactory.methodElement("add", VoidTypeImpl.instance, [eType])
+      ];
+      _propagateTypeArguments(listElement);
+    }
+    return _listType;
+  }
+
+  @override
+  InterfaceType get mapType {
+    if (_mapType == null) {
+      ClassElementImpl mapElement =
+          ElementFactory.classElement2("Map", ["K", "V"]);
+      _mapType = mapElement.type;
+      DartType kType = mapElement.typeParameters[0].type;
+      DartType vType = mapElement.typeParameters[1].type;
+      _setAccessors(mapElement, <PropertyAccessorElement>[
+        ElementFactory.getterElement("length", false, intType)
+      ]);
+      mapElement.methods = <MethodElement>[
+        ElementFactory.methodElement("[]", vType, [objectType]),
+        ElementFactory.methodElement(
+            "[]=", VoidTypeImpl.instance, [kType, vType])
+      ];
+      _propagateTypeArguments(mapElement);
+    }
+    return _mapType;
+  }
+
+  @override
+  List<InterfaceType> get nonSubtypableTypes => <InterfaceType>[
+    nullType,
+    numType,
+    intType,
+    doubleType,
+    boolType,
+    stringType
+  ];
+
+  @override
+  DartObjectImpl get nullObject {
+    if (_nullObject == null) {
+      _nullObject = new DartObjectImpl(nullType, NullState.NULL_STATE);
+    }
+    return _nullObject;
+  }
+
+  @override
+  InterfaceType get nullType {
+    if (_nullType == null) {
+      _nullType = ElementFactory.classElement2("Null").type;
+    }
+    return _nullType;
+  }
+
+  @override
+  InterfaceType get numType {
+    if (_numType == null) {
+      _initializeNumericTypes();
+    }
+    return _numType;
+  }
+
+  @override
+  InterfaceType get objectType {
+    if (_objectType == null) {
+      ClassElementImpl objectElement = ElementFactory.object;
+      _objectType = objectElement.type;
+      objectElement.constructors = <ConstructorElement>[
+        ElementFactory.constructorElement2(objectElement, null)
+      ];
+      objectElement.methods = <MethodElement>[
+        ElementFactory.methodElement("toString", stringType),
+        ElementFactory.methodElement("==", boolType, [_objectType]),
+        ElementFactory.methodElement("noSuchMethod", dynamicType, [dynamicType])
+      ];
+      _setAccessors(objectElement, <PropertyAccessorElement>[
+        ElementFactory.getterElement("hashCode", false, intType),
+        ElementFactory.getterElement("runtimeType", false, typeType)
+      ]);
+    }
+    return _objectType;
+  }
+
+  @override
+  InterfaceType get stackTraceType {
+    if (_stackTraceType == null) {
+      _stackTraceType = ElementFactory.classElement2("StackTrace").type;
+    }
+    return _stackTraceType;
+  }
+
+  @override
+  InterfaceType get streamDynamicType {
+    if (_streamDynamicType == null) {
+      _streamDynamicType = streamType.substitute4(<DartType>[dynamicType]);
+    }
+    return _streamDynamicType;
+  }
+
+  @override
+  InterfaceType get streamType {
+    if (_streamType == null) {
+      _streamType = ElementFactory.classElement2("Stream", ["T"]).type;
+    }
+    return _streamType;
+  }
+
+  @override
+  InterfaceType get stringType {
+    if (_stringType == null) {
+      _stringType = ElementFactory.classElement2("String").type;
+      ClassElementImpl stringElement = _stringType.element as ClassElementImpl;
+      _setAccessors(stringElement, <PropertyAccessorElement>[
+        ElementFactory.getterElement("isEmpty", false, boolType),
+        ElementFactory.getterElement("length", false, intType),
+        ElementFactory.getterElement(
+            "codeUnits", false, listType.substitute4(<DartType>[intType]))
+      ]);
+      stringElement.methods = <MethodElement>[
+        ElementFactory.methodElement("+", _stringType, [_stringType]),
+        ElementFactory.methodElement("toLowerCase", _stringType),
+        ElementFactory.methodElement("toUpperCase", _stringType)
+      ];
+      ConstructorElementImpl fromEnvironment = ElementFactory
+          .constructorElement(stringElement, "fromEnvironment", true);
+      fromEnvironment.parameters = <ParameterElement>[
+        ElementFactory.requiredParameter2("name", stringType),
+        ElementFactory.namedParameter2("defaultValue", _stringType)
+      ];
+      fromEnvironment.factory = true;
+      fromEnvironment.isCycleFree = true;
+      stringElement.constructors = <ConstructorElement>[fromEnvironment];
+    }
+    return _stringType;
+  }
+
+  @override
+  InterfaceType get symbolType {
+    if (_symbolType == null) {
+      ClassElementImpl symbolClass = ElementFactory.classElement2("Symbol");
+      ConstructorElementImpl constructor = ElementFactory.constructorElement(
+          symbolClass, null, true, [stringType]);
+      constructor.factory = true;
+      constructor.isCycleFree = true;
+      symbolClass.constructors = <ConstructorElement>[constructor];
+      _symbolType = symbolClass.type;
+    }
+    return _symbolType;
+  }
+
+  @override
+  InterfaceType get typeType {
+    if (_typeType == null) {
+      _typeType = ElementFactory.classElement2("Type").type;
+    }
+    return _typeType;
+  }
+
+  @override
+  DartType get undefinedType {
+    if (_undefinedType == null) {
+      _undefinedType = UndefinedTypeImpl.instance;
+    }
+    return _undefinedType;
+  }
+
+  /**
+   * Initialize the numeric types. They are created as a group so that we can
+   * (a) create the right hierarchy and (b) add members to them.
+   */
+  void _initializeNumericTypes() {
+    //
+    // Create the type hierarchy.
+    //
+    ClassElementImpl numElement = ElementFactory.classElement2("num");
+    _numType = numElement.type;
+    ClassElementImpl intElement = ElementFactory.classElement("int", _numType);
+    _intType = intElement.type;
+    ClassElementImpl doubleElement =
+        ElementFactory.classElement("double", _numType);
+    _doubleType = doubleElement.type;
+    //
+    // Force the referenced types to be cached.
+    //
+    objectType;
+    boolType;
+    stringType;
+    //
+    // Add the methods.
+    //
+    numElement.methods = <MethodElement>[
+      ElementFactory.methodElement("+", _numType, [_numType]),
+      ElementFactory.methodElement("-", _numType, [_numType]),
+      ElementFactory.methodElement("*", _numType, [_numType]),
+      ElementFactory.methodElement("%", _numType, [_numType]),
+      ElementFactory.methodElement("/", _doubleType, [_numType]),
+      ElementFactory.methodElement("~/", _numType, [_numType]),
+      ElementFactory.methodElement("-", _numType),
+      ElementFactory.methodElement("remainder", _numType, [_numType]),
+      ElementFactory.methodElement("<", _boolType, [_numType]),
+      ElementFactory.methodElement("<=", _boolType, [_numType]),
+      ElementFactory.methodElement(">", _boolType, [_numType]),
+      ElementFactory.methodElement(">=", _boolType, [_numType]),
+      ElementFactory.methodElement("==", _boolType, [_objectType]),
+      ElementFactory.methodElement("isNaN", _boolType),
+      ElementFactory.methodElement("isNegative", _boolType),
+      ElementFactory.methodElement("isInfinite", _boolType),
+      ElementFactory.methodElement("abs", _numType),
+      ElementFactory.methodElement("floor", _numType),
+      ElementFactory.methodElement("ceil", _numType),
+      ElementFactory.methodElement("round", _numType),
+      ElementFactory.methodElement("truncate", _numType),
+      ElementFactory.methodElement("toInt", _intType),
+      ElementFactory.methodElement("toDouble", _doubleType),
+      ElementFactory.methodElement("toStringAsFixed", _stringType, [_intType]),
+      ElementFactory.methodElement(
+          "toStringAsExponential", _stringType, [_intType]),
+      ElementFactory.methodElement(
+          "toStringAsPrecision", _stringType, [_intType]),
+      ElementFactory.methodElement("toRadixString", _stringType, [_intType])
+    ];
+    intElement.methods = <MethodElement>[
+      ElementFactory.methodElement("&", _intType, [_intType]),
+      ElementFactory.methodElement("|", _intType, [_intType]),
+      ElementFactory.methodElement("^", _intType, [_intType]),
+      ElementFactory.methodElement("~", _intType),
+      ElementFactory.methodElement("<<", _intType, [_intType]),
+      ElementFactory.methodElement(">>", _intType, [_intType]),
+      ElementFactory.methodElement("-", _intType),
+      ElementFactory.methodElement("abs", _intType),
+      ElementFactory.methodElement("round", _intType),
+      ElementFactory.methodElement("floor", _intType),
+      ElementFactory.methodElement("ceil", _intType),
+      ElementFactory.methodElement("truncate", _intType),
+      ElementFactory.methodElement("toString", _stringType)
+    ];
+    ConstructorElementImpl fromEnvironment =
+        ElementFactory.constructorElement(intElement, "fromEnvironment", true);
+    fromEnvironment.parameters = <ParameterElement>[
+      ElementFactory.requiredParameter2("name", stringType),
+      ElementFactory.namedParameter2("defaultValue", _intType)
+    ];
+    fromEnvironment.factory = true;
+    fromEnvironment.isCycleFree = true;
+    intElement.constructors = <ConstructorElement>[fromEnvironment];
+    List<FieldElement> fields = <FieldElement>[
+      ElementFactory.fieldElement("NAN", true, false, true, _doubleType),
+      ElementFactory.fieldElement("INFINITY", true, false, true, _doubleType),
+      ElementFactory.fieldElement(
+          "NEGATIVE_INFINITY", true, false, true, _doubleType),
+      ElementFactory.fieldElement(
+          "MIN_POSITIVE", true, false, true, _doubleType),
+      ElementFactory.fieldElement("MAX_FINITE", true, false, true, _doubleType)
+    ];
+    doubleElement.fields = fields;
+    int fieldCount = fields.length;
+    List<PropertyAccessorElement> accessors =
+        new List<PropertyAccessorElement>(fieldCount);
+    for (int i = 0; i < fieldCount; i++) {
+      accessors[i] = fields[i].getter;
+    }
+    doubleElement.accessors = accessors;
+    doubleElement.methods = <MethodElement>[
+      ElementFactory.methodElement("remainder", _doubleType, [_numType]),
+      ElementFactory.methodElement("+", _doubleType, [_numType]),
+      ElementFactory.methodElement("-", _doubleType, [_numType]),
+      ElementFactory.methodElement("*", _doubleType, [_numType]),
+      ElementFactory.methodElement("%", _doubleType, [_numType]),
+      ElementFactory.methodElement("/", _doubleType, [_numType]),
+      ElementFactory.methodElement("~/", _doubleType, [_numType]),
+      ElementFactory.methodElement("-", _doubleType),
+      ElementFactory.methodElement("abs", _doubleType),
+      ElementFactory.methodElement("round", _doubleType),
+      ElementFactory.methodElement("floor", _doubleType),
+      ElementFactory.methodElement("ceil", _doubleType),
+      ElementFactory.methodElement("truncate", _doubleType),
+      ElementFactory.methodElement("toString", _stringType)
+    ];
+  }
+
+  /**
+   * Given a [classElement] representing a class with type parameters, propagate
+   * those type parameters to all of the accessors, methods and constructors
+   * defined for the class.
+   */
+  void _propagateTypeArguments(ClassElementImpl classElement) {
+    List<DartType> typeArguments =
+        TypeParameterTypeImpl.getTypes(classElement.typeParameters);
+    for (PropertyAccessorElement accessor in classElement.accessors) {
+      FunctionTypeImpl functionType = accessor.type as FunctionTypeImpl;
+      functionType.typeArguments = typeArguments;
+    }
+    for (MethodElement method in classElement.methods) {
+      FunctionTypeImpl functionType = method.type as FunctionTypeImpl;
+      functionType.typeArguments = typeArguments;
+    }
+    for (ConstructorElement constructor in classElement.constructors) {
+      FunctionTypeImpl functionType = constructor.type as FunctionTypeImpl;
+      functionType.typeArguments = typeArguments;
+    }
+  }
+
+  /**
+   * Set the accessors for the given class [element] to the given [accessors]
+   * and also set the fields to those that correspond to the accessors.
+   */
+  void _setAccessors(
+      ClassElementImpl element, List<PropertyAccessorElement> accessors) {
+    element.accessors = accessors;
+    element.fields = accessors
+        .map((PropertyAccessorElement accessor) => accessor.variable)
+        .toList();
+  }
+}
diff --git a/analyzer/lib/src/generated/testing/token_factory.dart b/analyzer/lib/src/generated/testing/token_factory.dart
new file mode 100644
index 0000000..e9324be
--- /dev/null
+++ b/analyzer/lib/src/generated/testing/token_factory.dart
@@ -0,0 +1,26 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.testing.token_factory;
+
+import 'package:analyzer/src/generated/scanner.dart';
+
+/**
+ * A set of utility methods that can be used to create tokens.
+ */
+class TokenFactory {
+  static Token tokenFromKeyword(Keyword keyword) =>
+      new KeywordToken(keyword, 0);
+
+  static Token tokenFromString(String lexeme) =>
+      new StringToken(TokenType.STRING, lexeme, 0);
+
+  static Token tokenFromType(TokenType type) => new Token(type, 0);
+
+  static Token tokenFromTypeAndString(TokenType type, String lexeme) =>
+      new StringToken(type, lexeme, 0);
+}
diff --git a/analyzer/lib/src/generated/utilities_collection.dart b/analyzer/lib/src/generated/utilities_collection.dart
new file mode 100644
index 0000000..47dd4f6
--- /dev/null
+++ b/analyzer/lib/src/generated/utilities_collection.dart
@@ -0,0 +1,743 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.utilities.collection;
+
+import "dart:math" as math;
+import 'dart:collection';
+
+import 'java_core.dart';
+import 'scanner.dart' show Token;
+
+/**
+ * The class `BooleanArray` defines methods for operating on integers as if they were arrays
+ * of booleans. These arrays can be indexed by either integers or by enumeration constants.
+ */
+class BooleanArray {
+  /**
+   * Return the value of the element at the given index.
+   *
+   * @param array the array being accessed
+   * @param index the index of the element being accessed
+   * @return the value of the element at the given index
+   * @throws IndexOutOfBoundsException if the index is not between zero (0) and 31, inclusive
+   */
+  static bool get(int array, int index) {
+    _checkIndex(index);
+    return (array & (1 << index)) > 0;
+  }
+
+  /**
+   * Return the value of the element at the given index.
+   *
+   * @param array the array being accessed
+   * @param index the index of the element being accessed
+   * @return the value of the element at the given index
+   * @throws IndexOutOfBoundsException if the index is not between zero (0) and 31, inclusive
+   */
+  static bool getEnum(int array, Enum index) => get(array, index.ordinal);
+
+  /**
+   * Set the value of the element at the given index to the given value.
+   *
+   * @param array the array being modified
+   * @param index the index of the element being set
+   * @param value the value to be assigned to the element
+   * @return the updated value of the array
+   * @throws IndexOutOfBoundsException if the index is not between zero (0) and 31, inclusive
+   */
+  static int set(int array, int index, bool value) {
+    _checkIndex(index);
+    if (value) {
+      return array | (1 << index);
+    } else {
+      return array & ~(1 << index);
+    }
+  }
+
+  /**
+   * Set the value of the element at the given index to the given value.
+   *
+   * @param array the array being modified
+   * @param index the index of the element being set
+   * @param value the value to be assigned to the element
+   * @return the updated value of the array
+   * @throws IndexOutOfBoundsException if the index is not between zero (0) and 31, inclusive
+   */
+  static int setEnum(int array, Enum index, bool value) =>
+      set(array, index.ordinal, value);
+
+  /**
+   * Throw an exception if the index is not within the bounds allowed for an integer-encoded array
+   * of boolean values.
+   *
+   * @throws IndexOutOfBoundsException if the index is not between zero (0) and 31, inclusive
+   */
+  static void _checkIndex(int index) {
+    if (index < 0 || index > 30) {
+      throw new RangeError("Index not between 0 and 30: $index");
+    }
+  }
+}
+
+/**
+ * Instances of the class `DirectedGraph` implement a directed graph in which the nodes are
+ * arbitrary (client provided) objects and edges are represented implicitly. The graph will allow an
+ * edge from any node to any other node, including itself, but will not represent multiple edges
+ * between the same pair of nodes.
+ *
+ * @param N the type of the nodes in the graph
+ */
+class DirectedGraph<N> {
+  /**
+   * The table encoding the edges in the graph. An edge is represented by an entry mapping the head
+   * to a set of tails. Nodes that are not the head of any edge are represented by an entry mapping
+   * the node to an empty set of tails.
+   */
+  HashMap<N, HashSet<N>> _edges = new HashMap<N, HashSet<N>>();
+
+  /**
+   * Return `true` if this graph is empty.
+   *
+   * @return `true` if this graph is empty
+   */
+  bool get isEmpty => _edges.isEmpty;
+
+  /**
+   * Return the number of nodes in this graph.
+   *
+   * @return the number of nodes in this graph
+   */
+  int get nodeCount => _edges.length;
+
+  /**
+   * Return a set of all nodes in the graph.
+   */
+  Set<N> get nodes => _edges.keys.toSet();
+
+  /**
+   * Add an edge from the given head node to the given tail node. Both nodes will be a part of the
+   * graph after this method is invoked, whether or not they were before.
+   *
+   * @param head the node at the head of the edge
+   * @param tail the node at the tail of the edge
+   */
+  void addEdge(N head, N tail) {
+    //
+    // First, ensure that the tail is a node known to the graph.
+    //
+    if (_edges[tail] == null) {
+      _edges[tail] = new HashSet<N>();
+    }
+    //
+    // Then create the edge.
+    //
+    HashSet<N> tails = _edges[head];
+    if (tails == null) {
+      tails = new HashSet<N>();
+      _edges[head] = tails;
+    }
+    tails.add(tail);
+  }
+
+  /**
+   * Add the given node to the set of nodes in the graph.
+   *
+   * @param node the node to be added
+   */
+  void addNode(N node) {
+    HashSet<N> tails = _edges[node];
+    if (tails == null) {
+      _edges[node] = new HashSet<N>();
+    }
+  }
+
+  /**
+   * Run a topological sort of the graph. Since the graph may contain cycles, this results in a list
+   * of strongly connected components rather than a list of nodes. The nodes in each strongly
+   * connected components only have edges that point to nodes in the same component or earlier
+   * components.
+   */
+  List<List<N>> computeTopologicalSort() {
+    DirectedGraph_SccFinder<N> finder = new DirectedGraph_SccFinder<N>(this);
+    return finder.computeTopologicalSort();
+  }
+
+  /**
+   * Return true if the graph contains at least one path from `source` to `destination`.
+   */
+  bool containsPath(N source, N destination) {
+    HashSet<N> nodesVisited = new HashSet<N>();
+    return _containsPathInternal(source, destination, nodesVisited);
+  }
+
+  /**
+   * Return a list of nodes that form a cycle containing the given node. If the node is not part of
+   * this graph, then a list containing only the node itself will be returned.
+   *
+   * @return a list of nodes that form a cycle containing the given node
+   */
+  List<N> findCycleContaining(N node) {
+    if (node == null) {
+      throw new IllegalArgumentException();
+    }
+    DirectedGraph_SccFinder<N> finder = new DirectedGraph_SccFinder<N>(this);
+    return finder.componentContaining(node);
+  }
+
+  /**
+   * Return a set containing the tails of edges that have the given node as their head. The set will
+   * be empty if there are no such edges or if the node is not part of the graph. Clients must not
+   * modify the returned set.
+   *
+   * @param head the node at the head of all of the edges whose tails are to be returned
+   * @return a set containing the tails of edges that have the given node as their head
+   */
+  Set<N> getTails(N head) {
+    HashSet<N> tails = _edges[head];
+    if (tails == null) {
+      return new HashSet<N>();
+    }
+    return tails;
+  }
+
+  /**
+   * Remove all of the given nodes from this graph. As a consequence, any edges for which those
+   * nodes were either a head or a tail will also be removed.
+   *
+   * @param nodes the nodes to be removed
+   */
+  void removeAllNodes(List<N> nodes) {
+    for (N node in nodes) {
+      removeNode(node);
+    }
+  }
+
+  /**
+   * Remove the edge from the given head node to the given tail node. If there was no such edge then
+   * the graph will be unmodified: the number of edges will be the same and the set of nodes will be
+   * the same (neither node will either be added or removed).
+   *
+   * @param head the node at the head of the edge
+   * @param tail the node at the tail of the edge
+   * @return `true` if the graph was modified as a result of this operation
+   */
+  void removeEdge(N head, N tail) {
+    HashSet<N> tails = _edges[head];
+    if (tails != null) {
+      tails.remove(tail);
+    }
+  }
+
+  /**
+   * Remove the given node from this graph. As a consequence, any edges for which that node was
+   * either a head or a tail will also be removed.
+   *
+   * @param node the node to be removed
+   */
+  void removeNode(N node) {
+    _edges.remove(node);
+    for (HashSet<N> tails in _edges.values) {
+      tails.remove(node);
+    }
+  }
+
+  /**
+   * Find one node (referred to as a sink node) that has no outgoing edges (that is, for which there
+   * are no edges that have that node as the head of the edge) and remove it from this graph. Return
+   * the node that was removed, or `null` if there are no such nodes either because the graph
+   * is empty or because every node in the graph has at least one outgoing edge. As a consequence of
+   * removing the node from the graph any edges for which that node was a tail will also be removed.
+   *
+   * @return the sink node that was removed
+   */
+  N removeSink() {
+    N sink = _findSink();
+    if (sink == null) {
+      return null;
+    }
+    removeNode(sink);
+    return sink;
+  }
+
+  bool _containsPathInternal(N source, N destination, HashSet<N> nodesVisited) {
+    if (identical(source, destination)) {
+      return true;
+    }
+    HashSet<N> tails = _edges[source];
+    if (tails != null) {
+      nodesVisited.add(source);
+      for (N tail in tails) {
+        if (!nodesVisited.contains(tail)) {
+          if (_containsPathInternal(tail, destination, nodesVisited)) {
+            return true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Return one node that has no outgoing edges (that is, for which there are no edges that have
+   * that node as the head of the edge), or `null` if there are no such nodes.
+   *
+   * @return a sink node
+   */
+  N _findSink() {
+    for (N key in _edges.keys) {
+      if (_edges[key].isEmpty) return key;
+    }
+    return null;
+  }
+}
+
+/**
+ * Instances of the class `NodeInfo` are used by the [SccFinder] to maintain
+ * information about the nodes that have been examined.
+ *
+ * @param N the type of the nodes corresponding to the entries
+ */
+class DirectedGraph_NodeInfo<N> {
+  /**
+   * The depth of this node.
+   */
+  int index = 0;
+
+  /**
+   * The depth of the first node in a cycle.
+   */
+  int lowlink = 0;
+
+  /**
+   * A flag indicating whether the corresponding node is on the stack. Used to remove the need for
+   * searching a collection for the node each time the question needs to be asked.
+   */
+  bool onStack = false;
+
+  /**
+   * The component that contains the corresponding node.
+   */
+  List<N> component;
+
+  /**
+   * Initialize a newly created information holder to represent a node at the given depth.
+   *
+   * @param depth the depth of the node being represented
+   */
+  DirectedGraph_NodeInfo(int depth) {
+    index = depth;
+    lowlink = depth;
+    onStack = false;
+  }
+}
+
+/**
+ * Instances of the class `SccFinder` implement Tarjan's Algorithm for finding the strongly
+ * connected components in a graph.
+ */
+class DirectedGraph_SccFinder<N> {
+  /**
+   * The graph to work with.
+   */
+  final DirectedGraph<N> _graph;
+
+  /**
+   * The index used to uniquely identify the depth of nodes.
+   */
+  int _index = 0;
+
+  /**
+   * The stack of nodes that are being visited in order to identify components.
+   */
+  List<N> _stack = new List<N>();
+
+  /**
+   * A table mapping nodes to information about the nodes that is used by this algorithm.
+   */
+  HashMap<N, DirectedGraph_NodeInfo<N>> _nodeMap =
+      new HashMap<N, DirectedGraph_NodeInfo<N>>();
+
+  /**
+   * A list of all strongly connected components found, in topological sort order (each node in a
+   * strongly connected component only has edges that point to nodes in the same component or
+   * earlier components).
+   */
+  List<List<N>> _allComponents = new List<List<N>>();
+
+  /**
+   * Initialize a newly created finder.
+   */
+  DirectedGraph_SccFinder(this._graph) : super();
+
+  /**
+   * Return a list containing the nodes that are part of the strongly connected component that
+   * contains the given node.
+   *
+   * @param node the node used to identify the strongly connected component to be returned
+   * @return the nodes that are part of the strongly connected component that contains the given
+   *         node
+   */
+  List<N> componentContaining(N node) => _strongConnect(node).component;
+
+  /**
+   * Run Tarjan's algorithm and return the resulting list of strongly connected components. The
+   * list is in topological sort order (each node in a strongly connected component only has edges
+   * that point to nodes in the same component or earlier components).
+   */
+  List<List<N>> computeTopologicalSort() {
+    for (N node in _graph._edges.keys.toSet()) {
+      DirectedGraph_NodeInfo<N> nodeInfo = _nodeMap[node];
+      if (nodeInfo == null) {
+        _strongConnect(node);
+      }
+    }
+    return _allComponents;
+  }
+
+  /**
+   * Remove and return the top-most element from the stack.
+   *
+   * @return the element that was removed
+   */
+  N _pop() {
+    N node = _stack.removeAt(_stack.length - 1);
+    _nodeMap[node].onStack = false;
+    return node;
+  }
+
+  /**
+   * Add the given node to the stack.
+   *
+   * @param node the node to be added to the stack
+   */
+  void _push(N node) {
+    _nodeMap[node].onStack = true;
+    _stack.add(node);
+  }
+
+  /**
+   * Compute the strongly connected component that contains the given node as well as any
+   * components containing nodes that are reachable from the given component.
+   *
+   * @param v the node from which the search will begin
+   * @return the information about the given node
+   */
+  DirectedGraph_NodeInfo<N> _strongConnect(N v) {
+    //
+    // Set the depth index for v to the smallest unused index
+    //
+    DirectedGraph_NodeInfo<N> vInfo = new DirectedGraph_NodeInfo<N>(_index++);
+    _nodeMap[v] = vInfo;
+    _push(v);
+    //
+    // Consider successors of v
+    //
+    HashSet<N> tails = _graph._edges[v];
+    if (tails != null) {
+      for (N w in tails) {
+        DirectedGraph_NodeInfo<N> wInfo = _nodeMap[w];
+        if (wInfo == null) {
+          // Successor w has not yet been visited; recurse on it
+          wInfo = _strongConnect(w);
+          vInfo.lowlink = math.min(vInfo.lowlink, wInfo.lowlink);
+        } else if (wInfo.onStack) {
+          // Successor w is in stack S and hence in the current SCC
+          vInfo.lowlink = math.min(vInfo.lowlink, wInfo.index);
+        }
+      }
+    }
+    //
+    // If v is a root node, pop the stack and generate an SCC
+    //
+    if (vInfo.lowlink == vInfo.index) {
+      List<N> component = new List<N>();
+      N w;
+      do {
+        w = _pop();
+        component.add(w);
+        _nodeMap[w].component = component;
+      } while (!identical(w, v));
+      _allComponents.add(component);
+    }
+    return vInfo;
+  }
+}
+
+/**
+ * The class `ListUtilities` defines utility methods useful for working with [List
+ ].
+ */
+class ListUtilities {
+  /**
+   * Add all of the elements in the given array to the given list.
+   *
+   * @param list the list to which the elements are to be added
+   * @param elements the elements to be added to the list
+   */
+  static void addAll(List list, List<Object> elements) {
+    int count = elements.length;
+    for (int i = 0; i < count; i++) {
+      list.add(elements[i]);
+    }
+  }
+}
+
+/**
+ * The interface `MapIterator` defines the behavior of objects that iterate over the entries
+ * in a map.
+ *
+ * This interface defines the concept of a current entry and provides methods to access the key and
+ * value in the current entry. When an iterator is first created it will be positioned before the
+ * first entry and there is no current entry until [moveNext] is invoked. When all of the
+ * entries have been accessed there will also be no current entry.
+ *
+ * There is no guarantee made about the order in which the entries are accessible.
+ */
+abstract class MapIterator<K, V> {
+  /**
+   * Return the key associated with the current element.
+   *
+   * @return the key associated with the current element
+   * @throws NoSuchElementException if there is no current element
+   */
+  K get key;
+
+  /**
+   * Return the value associated with the current element.
+   *
+   * @return the value associated with the current element
+   * @throws NoSuchElementException if there is no current element
+   */
+  V get value;
+
+  /**
+   * Set the value associated with the current element to the given value.
+   *
+   * @param newValue the new value to be associated with the current element
+   * @throws NoSuchElementException if there is no current element
+   */
+  void set value(V newValue);
+
+  /**
+   * Advance to the next entry in the map. Return `true` if there is a current element that
+   * can be accessed after this method returns. It is safe to invoke this method even if the
+   * previous invocation returned `false`.
+   *
+   * @return `true` if there is a current element that can be accessed
+   */
+  bool moveNext();
+}
+
+/**
+ * Instances of the class `MultipleMapIterator` implement an iterator that can be used to
+ * sequentially access the entries in multiple maps.
+ */
+class MultipleMapIterator<K, V> implements MapIterator<K, V> {
+  /**
+   * The iterators used to access the entries.
+   */
+  List<MapIterator<K, V>> _iterators;
+
+  /**
+   * The index of the iterator currently being used to access the entries.
+   */
+  int _iteratorIndex = -1;
+
+  /**
+   * The current iterator, or `null` if there is no current iterator.
+   */
+  MapIterator<K, V> _currentIterator;
+
+  /**
+   * Initialize a newly created iterator to return the entries from the given maps.
+   *
+   * @param maps the maps containing the entries to be iterated
+   */
+  MultipleMapIterator(List<Map<K, V>> maps) {
+    int count = maps.length;
+    _iterators = new List<MapIterator>(count);
+    for (int i = 0; i < count; i++) {
+      _iterators[i] = new SingleMapIterator<K, V>(maps[i]);
+    }
+  }
+
+  @override
+  K get key {
+    if (_currentIterator == null) {
+      throw new NoSuchElementException();
+    }
+    return _currentIterator.key;
+  }
+
+  @override
+  V get value {
+    if (_currentIterator == null) {
+      throw new NoSuchElementException();
+    }
+    return _currentIterator.value;
+  }
+
+  @override
+  void set value(V newValue) {
+    if (_currentIterator == null) {
+      throw new NoSuchElementException();
+    }
+    _currentIterator.value = newValue;
+  }
+
+  @override
+  bool moveNext() {
+    if (_iteratorIndex < 0) {
+      if (_iterators.length == 0) {
+        _currentIterator = null;
+        return false;
+      }
+      if (_advanceToNextIterator()) {
+        return true;
+      } else {
+        _currentIterator = null;
+        return false;
+      }
+    }
+    if (_currentIterator.moveNext()) {
+      return true;
+    } else if (_advanceToNextIterator()) {
+      return true;
+    } else {
+      _currentIterator = null;
+      return false;
+    }
+  }
+
+  /**
+   * Under the assumption that there are no more entries that can be returned using the current
+   * iterator, advance to the next iterator that has entries.
+   *
+   * @return `true` if there is a current iterator that has entries
+   */
+  bool _advanceToNextIterator() {
+    _iteratorIndex++;
+    while (_iteratorIndex < _iterators.length) {
+      MapIterator<K, V> iterator = _iterators[_iteratorIndex];
+      if (iterator.moveNext()) {
+        _currentIterator = iterator;
+        return true;
+      }
+      _iteratorIndex++;
+    }
+    return false;
+  }
+}
+
+/**
+ * Instances of the class `SingleMapIterator` implement an iterator that can be used to access
+ * the entries in a single map.
+ */
+class SingleMapIterator<K, V> implements MapIterator<K, V> {
+  /**
+   * The [Map] containing the entries to be iterated over.
+   */
+  final Map<K, V> _map;
+
+  /**
+   * The iterator used to access the entries.
+   */
+  Iterator<K> _keyIterator;
+
+  /**
+   * The current key, or `null` if there is no current key.
+   */
+  K _currentKey;
+
+  /**
+   * The current value.
+   */
+  V _currentValue;
+
+  /**
+   * Initialize a newly created iterator to return the entries from the given map.
+   *
+   * @param map the map containing the entries to be iterated over
+   */
+  SingleMapIterator(this._map) {
+    this._keyIterator = _map.keys.iterator;
+  }
+
+  @override
+  K get key {
+    if (_currentKey == null) {
+      throw new NoSuchElementException();
+    }
+    return _currentKey;
+  }
+
+  @override
+  V get value {
+    if (_currentKey == null) {
+      throw new NoSuchElementException();
+    }
+    return _currentValue;
+  }
+
+  @override
+  void set value(V newValue) {
+    if (_currentKey == null) {
+      throw new NoSuchElementException();
+    }
+    _currentValue = newValue;
+    _map[_currentKey] = newValue;
+  }
+
+  @override
+  bool moveNext() {
+    if (_keyIterator.moveNext()) {
+      _currentKey = _keyIterator.current;
+      _currentValue = _map[_currentKey];
+      return true;
+    } else {
+      _currentKey = null;
+      return false;
+    }
+  }
+
+  /**
+   * Returns a new [SingleMapIterator] instance for the given [Map].
+   */
+  static SingleMapIterator forMap(Map map) => new SingleMapIterator(map);
+}
+
+/**
+ * Instances of the class `TokenMap` map one set of tokens to another set of tokens.
+ */
+class TokenMap {
+  /**
+   * A table mapping tokens to tokens. This should be replaced by a more performant implementation.
+   * One possibility is a pair of parallel arrays, with keys being sorted by their offset and a
+   * cursor indicating where to start searching.
+   */
+  HashMap<Token, Token> _map = new HashMap<Token, Token>();
+
+  /**
+   * Return the token that is mapped to the given token, or `null` if there is no token
+   * corresponding to the given token.
+   *
+   * @param key the token being mapped to another token
+   * @return the token that is mapped to the given token
+   */
+  Token get(Token key) => _map[key];
+
+  /**
+   * Map the key to the value.
+   *
+   * @param key the token being mapped to the value
+   * @param value the token to which the key will be mapped
+   */
+  void put(Token key, Token value) {
+    _map[key] = value;
+  }
+}
diff --git a/analyzer/lib/src/generated/utilities_dart.dart b/analyzer/lib/src/generated/utilities_dart.dart
new file mode 100644
index 0000000..26456a5
--- /dev/null
+++ b/analyzer/lib/src/generated/utilities_dart.dart
@@ -0,0 +1,40 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.utilities.dart;
+
+import 'java_core.dart';
+
+/**
+ * The enumeration `ParameterKind` defines the different kinds of parameters. There are two
+ * basic kinds of parameters: required and optional. Optional parameters are further divided into
+ * two kinds: positional optional and named optional.
+ */
+class ParameterKind extends Enum<ParameterKind> {
+  static const ParameterKind REQUIRED =
+      const ParameterKind('REQUIRED', 0, false);
+
+  static const ParameterKind POSITIONAL =
+      const ParameterKind('POSITIONAL', 1, true);
+
+  static const ParameterKind NAMED = const ParameterKind('NAMED', 2, true);
+
+  static const List<ParameterKind> values = const [REQUIRED, POSITIONAL, NAMED];
+
+  /**
+   * A flag indicating whether this is an optional parameter.
+   */
+  final bool isOptional;
+
+  /**
+   * Initialize a newly created kind with the given state.
+   *
+   * @param isOptional `true` if this is an optional parameter
+   */
+  const ParameterKind(String name, int ordinal, this.isOptional)
+      : super(name, ordinal);
+}
diff --git a/analyzer/lib/src/generated/utilities_general.dart b/analyzer/lib/src/generated/utilities_general.dart
new file mode 100644
index 0000000..e62a781
--- /dev/null
+++ b/analyzer/lib/src/generated/utilities_general.dart
@@ -0,0 +1,155 @@
+// 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.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library engine.utilities.general;
+
+import 'dart:profiler';
+
+/**
+ * Jenkins hash function, optimized for small integers.
+ * Borrowed from sdk/lib/math/jenkins_smi_hash.dart.
+ */
+class JenkinsSmiHash {
+  static int combine(int hash, int value) {
+    hash = 0x1fffffff & (hash + value);
+    hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
+    return hash ^ (hash >> 6);
+  }
+
+  static int finish(int hash) {
+    hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
+    hash = hash ^ (hash >> 11);
+    return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
+  }
+
+  static int hash2(a, b) => finish(combine(combine(0, a), b));
+
+  static int hash4(a, b, c, d) =>
+      finish(combine(combine(combine(combine(0, a), b), c), d));
+}
+
+/**
+ * Helper class for gathering performance statistics.  This class is modeled on
+ * the UserTag class in dart:profiler so that it can interoperate easily with
+ * it.
+ */
+abstract class PerformanceTag {
+  /**
+   * Return a list of all [PerformanceTag]s which have been created.
+   */
+  static List<PerformanceTag> get all => _PerformanceTagImpl.all.toList();
+
+  /**
+   * Return the current [PerformanceTag] for the isolate.
+   */
+  static PerformanceTag get current => _PerformanceTagImpl.current;
+
+  /**
+   * Return the [PerformanceTag] that is initially current.  This is intended
+   * to track time when the system is performing unknown operations.
+   */
+  static PerformanceTag get UNKNOWN => _PerformanceTagImpl.UNKNOWN;
+
+  /**
+   * Create a [PerformanceTag] having the given [label].  A [UserTag] will also
+   * be created, having the same [label], so that performance information can
+   * be queried using the observatory.
+   */
+  factory PerformanceTag(String label) = _PerformanceTagImpl;
+
+  /**
+   * Return the total number of milliseconds that this [PerformanceTag] has
+   * been the current [PerformanceTag] for the isolate.
+   *
+   * This call is safe even if this [PerformanceTag] is current.
+   */
+  int get elapsedMs;
+
+  /**
+   * Return the label for this [PerformanceTag].
+   */
+  String get label;
+
+  /**
+   * Make this the current tag for the isolate, and return the previous tag.
+   */
+  PerformanceTag makeCurrent();
+
+  /**
+   * Make this the current tag for the isolate, run [f], and restore the
+   * previous tag. Returns the result of invoking [f].
+   */
+  makeCurrentWhile(f());
+
+  /**
+   * Reset the total time tracked by all [PerformanceTag]s to zero.
+   */
+  static void reset() {
+    for (_PerformanceTagImpl tag in _PerformanceTagImpl.all) {
+      tag.stopwatch.reset();
+    }
+  }
+}
+
+class _PerformanceTagImpl implements PerformanceTag {
+  /**
+   * The current performance tag for the isolate.
+   */
+  static _PerformanceTagImpl current = UNKNOWN;
+
+  static final _PerformanceTagImpl UNKNOWN = new _PerformanceTagImpl('unknown');
+
+  /**
+   * A list of all performance tags that have been created so far.
+   */
+  static List<_PerformanceTagImpl> all = <_PerformanceTagImpl>[];
+
+  /**
+   * The [UserTag] associated with this [PerformanceTag].
+   */
+  final UserTag userTag;
+
+  /**
+   * Stopwatch tracking the amount of time this [PerformanceTag] has been the
+   * current tag for the isolate.
+   */
+  final Stopwatch stopwatch;
+
+  _PerformanceTagImpl(String label)
+      : userTag = new UserTag(label),
+        stopwatch = new Stopwatch() {
+    all.add(this);
+  }
+
+  @override
+  int get elapsedMs => stopwatch.elapsedMilliseconds;
+
+  @override
+  String get label => userTag.label;
+
+  @override
+  PerformanceTag makeCurrent() {
+    if (identical(this, current)) {
+      return current;
+    }
+    _PerformanceTagImpl previous = current;
+    previous.stopwatch.stop();
+    stopwatch.start();
+    current = this;
+    userTag.makeCurrent();
+    return previous;
+  }
+
+  makeCurrentWhile(f()) {
+    PerformanceTag prevTag = makeCurrent();
+    try {
+      return f();
+    } finally {
+      prevTag.makeCurrent();
+    }
+  }
+}
diff --git a/analyzer/lib/src/generated/visitors.dart b/analyzer/lib/src/generated/visitors.dart
new file mode 100644
index 0000000..defb317
--- /dev/null
+++ b/analyzer/lib/src/generated/visitors.dart
@@ -0,0 +1,770 @@
+// Copyright (c) 2015, 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 engine.ast.visitors;
+
+import 'package:analyzer/src/generated/ast.dart';
+
+/// An [AstVisitor] that delegates calls to visit methods to all [delegates]
+/// before calling [visitChildren].
+class DelegatingAstVisitor<T> implements AstVisitor<T> {
+  Iterable<AstVisitor<T>> _delegates;
+  DelegatingAstVisitor(this._delegates);
+
+  @override
+  T visitAdjacentStrings(AdjacentStrings node) {
+    _delegates.forEach((delegate) => delegate.visitAdjacentStrings(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitAnnotation(Annotation node) {
+    _delegates.forEach((delegate) => delegate.visitAnnotation(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitArgumentList(ArgumentList node) {
+    _delegates.forEach((delegate) => delegate.visitArgumentList(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitAsExpression(AsExpression node) {
+    _delegates.forEach((delegate) => delegate.visitAsExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitAssertStatement(AssertStatement node) {
+    _delegates.forEach((delegate) => delegate.visitAssertStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitAssignmentExpression(AssignmentExpression node) {
+    _delegates.forEach((delegate) => delegate.visitAssignmentExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitAwaitExpression(AwaitExpression node) {
+    _delegates.forEach((delegate) => delegate.visitAwaitExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitBinaryExpression(BinaryExpression node) {
+    _delegates.forEach((delegate) => delegate.visitBinaryExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitBlock(Block node) {
+    _delegates.forEach((delegate) => delegate.visitBlock(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitBlockFunctionBody(BlockFunctionBody node) {
+    _delegates.forEach((delegate) => delegate.visitBlockFunctionBody(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitBooleanLiteral(BooleanLiteral node) {
+    _delegates.forEach((delegate) => delegate.visitBooleanLiteral(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitBreakStatement(BreakStatement node) {
+    _delegates.forEach((delegate) => delegate.visitBreakStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitCascadeExpression(CascadeExpression node) {
+    _delegates.forEach((delegate) => delegate.visitCascadeExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitCatchClause(CatchClause node) {
+    _delegates.forEach((delegate) => delegate.visitCatchClause(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitClassDeclaration(ClassDeclaration node) {
+    _delegates.forEach((delegate) => delegate.visitClassDeclaration(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitClassTypeAlias(ClassTypeAlias node) {
+    _delegates.forEach((delegate) => delegate.visitClassTypeAlias(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitComment(Comment node) {
+    _delegates.forEach((delegate) => delegate.visitComment(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitCommentReference(CommentReference node) {
+    _delegates.forEach((delegate) => delegate.visitCommentReference(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitCompilationUnit(CompilationUnit node) {
+    _delegates.forEach((delegate) => delegate.visitCompilationUnit(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitConditionalExpression(ConditionalExpression node) {
+    _delegates.forEach((delegate) => delegate.visitConditionalExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitConstructorDeclaration(ConstructorDeclaration node) {
+    _delegates
+        .forEach((delegate) => delegate.visitConstructorDeclaration(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+    _delegates
+        .forEach((delegate) => delegate.visitConstructorFieldInitializer(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitConstructorName(ConstructorName node) {
+    _delegates.forEach((delegate) => delegate.visitConstructorName(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitContinueStatement(ContinueStatement node) {
+    _delegates.forEach((delegate) => delegate.visitContinueStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitDeclaredIdentifier(DeclaredIdentifier node) {
+    _delegates.forEach((delegate) => delegate.visitDeclaredIdentifier(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitDefaultFormalParameter(DefaultFormalParameter node) {
+    _delegates
+        .forEach((delegate) => delegate.visitDefaultFormalParameter(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitDoStatement(DoStatement node) {
+    _delegates.forEach((delegate) => delegate.visitDoStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitDoubleLiteral(DoubleLiteral node) {
+    _delegates.forEach((delegate) => delegate.visitDoubleLiteral(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitEmptyFunctionBody(EmptyFunctionBody node) {
+    _delegates.forEach((delegate) => delegate.visitEmptyFunctionBody(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitEmptyStatement(EmptyStatement node) {
+    _delegates.forEach((delegate) => delegate.visitEmptyStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitEnumConstantDeclaration(EnumConstantDeclaration node) {
+    _delegates
+        .forEach((delegate) => delegate.visitEnumConstantDeclaration(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitEnumDeclaration(EnumDeclaration node) {
+    _delegates.forEach((delegate) => delegate.visitEnumDeclaration(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitExportDirective(ExportDirective node) {
+    _delegates.forEach((delegate) => delegate.visitExportDirective(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitExpressionFunctionBody(ExpressionFunctionBody node) {
+    _delegates
+        .forEach((delegate) => delegate.visitExpressionFunctionBody(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitExpressionStatement(ExpressionStatement node) {
+    _delegates.forEach((delegate) => delegate.visitExpressionStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitExtendsClause(ExtendsClause node) {
+    _delegates.forEach((delegate) => delegate.visitExtendsClause(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitFieldDeclaration(FieldDeclaration node) {
+    _delegates.forEach((delegate) => delegate.visitFieldDeclaration(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitFieldFormalParameter(FieldFormalParameter node) {
+    _delegates.forEach((delegate) => delegate.visitFieldFormalParameter(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitForEachStatement(ForEachStatement node) {
+    _delegates.forEach((delegate) => delegate.visitForEachStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitForStatement(ForStatement node) {
+    _delegates.forEach((delegate) => delegate.visitForStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitFormalParameterList(FormalParameterList node) {
+    _delegates.forEach((delegate) => delegate.visitFormalParameterList(node));
+    node.visitChildren(this);
+    return null;
+  }
+  @override
+  T visitFunctionDeclaration(FunctionDeclaration node) {
+    _delegates.forEach((delegate) => delegate.visitFunctionDeclaration(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
+    _delegates.forEach(
+        (delegate) => delegate.visitFunctionDeclarationStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitFunctionExpression(FunctionExpression node) {
+    _delegates.forEach((delegate) => delegate.visitFunctionExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    _delegates.forEach(
+        (delegate) => delegate.visitFunctionExpressionInvocation(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitFunctionTypeAlias(FunctionTypeAlias node) {
+    _delegates.forEach((delegate) => delegate.visitFunctionTypeAlias(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+    _delegates.forEach(
+        (delegate) => delegate.visitFunctionTypedFormalParameter(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitHideCombinator(HideCombinator node) {
+    _delegates.forEach((delegate) => delegate.visitHideCombinator(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitIfStatement(IfStatement node) {
+    _delegates.forEach((delegate) => delegate.visitIfStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitImplementsClause(ImplementsClause node) {
+    _delegates.forEach((delegate) => delegate.visitImplementsClause(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitImportDirective(ImportDirective node) {
+    _delegates.forEach((delegate) => delegate.visitImportDirective(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitIndexExpression(IndexExpression node) {
+    _delegates.forEach((delegate) => delegate.visitIndexExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitInstanceCreationExpression(InstanceCreationExpression node) {
+    _delegates
+        .forEach((delegate) => delegate.visitInstanceCreationExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitIntegerLiteral(IntegerLiteral node) {
+    _delegates.forEach((delegate) => delegate.visitIntegerLiteral(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitInterpolationExpression(InterpolationExpression node) {
+    _delegates
+        .forEach((delegate) => delegate.visitInterpolationExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitInterpolationString(InterpolationString node) {
+    _delegates.forEach((delegate) => delegate.visitInterpolationString(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitIsExpression(IsExpression node) {
+    _delegates.forEach((delegate) => delegate.visitIsExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitLabel(Label node) {
+    _delegates.forEach((delegate) => delegate.visitLabel(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitLabeledStatement(LabeledStatement node) {
+    _delegates.forEach((delegate) => delegate.visitLabeledStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitLibraryDirective(LibraryDirective node) {
+    _delegates.forEach((delegate) => delegate.visitLibraryDirective(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitLibraryIdentifier(LibraryIdentifier node) {
+    _delegates.forEach((delegate) => delegate.visitLibraryIdentifier(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitListLiteral(ListLiteral node) {
+    _delegates.forEach((delegate) => delegate.visitListLiteral(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitMapLiteral(MapLiteral node) {
+    _delegates.forEach((delegate) => delegate.visitMapLiteral(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitMapLiteralEntry(MapLiteralEntry node) {
+    _delegates.forEach((delegate) => delegate.visitMapLiteralEntry(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitMethodDeclaration(MethodDeclaration node) {
+    _delegates.forEach((delegate) => delegate.visitMethodDeclaration(node));
+    node.visitChildren(this);
+    return null;
+  }
+  @override
+  T visitMethodInvocation(MethodInvocation node) {
+    _delegates.forEach((delegate) => delegate.visitMethodInvocation(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitNamedExpression(NamedExpression node) {
+    _delegates.forEach((delegate) => delegate.visitNamedExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitNativeClause(NativeClause node) {
+    _delegates.forEach((delegate) => delegate.visitNativeClause(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitNativeFunctionBody(NativeFunctionBody node) {
+    _delegates.forEach((delegate) => delegate.visitNativeFunctionBody(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitNullLiteral(NullLiteral node) {
+    _delegates.forEach((delegate) => delegate.visitNullLiteral(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitParenthesizedExpression(ParenthesizedExpression node) {
+    _delegates
+        .forEach((delegate) => delegate.visitParenthesizedExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitPartDirective(PartDirective node) {
+    _delegates.forEach((delegate) => delegate.visitPartDirective(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitPartOfDirective(PartOfDirective node) {
+    _delegates.forEach((delegate) => delegate.visitPartOfDirective(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitPostfixExpression(PostfixExpression node) {
+    _delegates.forEach((delegate) => delegate.visitPostfixExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitPrefixExpression(PrefixExpression node) {
+    _delegates.forEach((delegate) => delegate.visitPrefixExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+  @override
+  T visitPrefixedIdentifier(PrefixedIdentifier node) {
+    _delegates.forEach((delegate) => delegate.visitPrefixedIdentifier(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitPropertyAccess(PropertyAccess node) {
+    _delegates.forEach((delegate) => delegate.visitPropertyAccess(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
+    _delegates.forEach(
+        (delegate) => delegate.visitRedirectingConstructorInvocation(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitRethrowExpression(RethrowExpression node) {
+    _delegates.forEach((delegate) => delegate.visitRethrowExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitReturnStatement(ReturnStatement node) {
+    _delegates.forEach((delegate) => delegate.visitReturnStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitScriptTag(ScriptTag node) {
+    _delegates.forEach((delegate) => delegate.visitScriptTag(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitShowCombinator(ShowCombinator node) {
+    _delegates.forEach((delegate) => delegate.visitShowCombinator(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitSimpleFormalParameter(SimpleFormalParameter node) {
+    _delegates.forEach((delegate) => delegate.visitSimpleFormalParameter(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitSimpleIdentifier(SimpleIdentifier node) {
+    _delegates.forEach((delegate) => delegate.visitSimpleIdentifier(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitSimpleStringLiteral(SimpleStringLiteral node) {
+    _delegates.forEach((delegate) => delegate.visitSimpleStringLiteral(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitStringInterpolation(StringInterpolation node) {
+    _delegates.forEach((delegate) => delegate.visitStringInterpolation(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    _delegates
+        .forEach((delegate) => delegate.visitSuperConstructorInvocation(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitSuperExpression(SuperExpression node) {
+    _delegates.forEach((delegate) => delegate.visitSuperExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitSwitchCase(SwitchCase node) {
+    _delegates.forEach((delegate) => delegate.visitSwitchCase(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitSwitchDefault(SwitchDefault node) {
+    _delegates.forEach((delegate) => delegate.visitSwitchDefault(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitSwitchStatement(SwitchStatement node) {
+    _delegates.forEach((delegate) => delegate.visitSwitchStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitSymbolLiteral(SymbolLiteral node) {
+    _delegates.forEach((delegate) => delegate.visitSymbolLiteral(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitThisExpression(ThisExpression node) {
+    _delegates.forEach((delegate) => delegate.visitThisExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitThrowExpression(ThrowExpression node) {
+    _delegates.forEach((delegate) => delegate.visitThrowExpression(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+    _delegates
+        .forEach((delegate) => delegate.visitTopLevelVariableDeclaration(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitTryStatement(TryStatement node) {
+    _delegates.forEach((delegate) => delegate.visitTryStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitTypeArgumentList(TypeArgumentList node) {
+    _delegates.forEach((delegate) => delegate.visitTypeArgumentList(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitTypeName(TypeName node) {
+    _delegates.forEach((delegate) => delegate.visitTypeName(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitTypeParameter(TypeParameter node) {
+    _delegates.forEach((delegate) => delegate.visitTypeParameter(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitTypeParameterList(TypeParameterList node) {
+    _delegates.forEach((delegate) => delegate.visitTypeParameterList(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitVariableDeclaration(VariableDeclaration node) {
+    _delegates.forEach((delegate) => delegate.visitVariableDeclaration(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitVariableDeclarationList(VariableDeclarationList node) {
+    _delegates
+        .forEach((delegate) => delegate.visitVariableDeclarationList(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitVariableDeclarationStatement(VariableDeclarationStatement node) {
+    _delegates.forEach(
+        (delegate) => delegate.visitVariableDeclarationStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitWhileStatement(WhileStatement node) {
+    _delegates.forEach((delegate) => delegate.visitWhileStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitWithClause(WithClause node) {
+    _delegates.forEach((delegate) => delegate.visitWithClause(node));
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
+  T visitYieldStatement(YieldStatement node) {
+    _delegates.forEach((delegate) => delegate.visitYieldStatement(node));
+    node.visitChildren(this);
+    return null;
+  }
+}
diff --git a/analyzer/lib/src/plugin/engine_plugin.dart b/analyzer/lib/src/plugin/engine_plugin.dart
new file mode 100644
index 0000000..acd6318
--- /dev/null
+++ b/analyzer/lib/src/plugin/engine_plugin.dart
@@ -0,0 +1,100 @@
+// Copyright (c) 2015, 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 analyzer.src.plugin.engine_plugin;
+
+import 'package:analyzer/plugin/plugin.dart';
+import 'package:analyzer/plugin/task.dart';
+import 'package:analyzer/src/task/dart.dart';
+import 'package:analyzer/src/task/general.dart';
+import 'package:analyzer/task/model.dart';
+
+/**
+ * A plugin that defines the extension points and extensions that are inherently
+ * defined by the analysis engine.
+ */
+class EnginePlugin implements Plugin {
+  /**
+   * The simple identifier of the extension point that allows plugins to
+   * register new analysis tasks with the analysis engine.
+   */
+  static const String TASK_EXTENSION_POINT = 'task';
+
+  /**
+   * The unique identifier of this plugin.
+   */
+  static const String UNIQUE_IDENTIFIER = 'analysis_engine.core';
+
+  /**
+   * The extension point that allows plugins to register new analysis tasks with
+   * the analysis engine.
+   */
+  ExtensionPoint taskExtensionPoint;
+
+  /**
+   * Initialize a newly created plugin.
+   */
+  EnginePlugin();
+
+  /**
+   * Return a list containing all of the task descriptors that were contributed.
+   */
+  List<TaskDescriptor> get taskDescriptors => taskExtensionPoint.extensions;
+
+  @override
+  String get uniqueIdentifier => UNIQUE_IDENTIFIER;
+
+  @override
+  void registerExtensionPoints(RegisterExtensionPoint registerExtensionPoint) {
+    taskExtensionPoint =
+        registerExtensionPoint(TASK_EXTENSION_POINT, _validateTaskExtension);
+  }
+
+  @override
+  void registerExtensions(RegisterExtension registerExtension) {
+    String taskId = TASK_EXTENSION_POINT_ID;
+    //
+    // Register general tasks.
+    //
+    registerExtension(taskId, GetContentTask.DESCRIPTOR);
+    //
+    // Register Dart tasks.
+    //
+    registerExtension(taskId, BuildClassConstructorsTask.DESCRIPTOR);
+    registerExtension(taskId, BuildCompilationUnitElementTask.DESCRIPTOR);
+    registerExtension(taskId, BuildDirectiveElementsTask.DESCRIPTOR);
+    registerExtension(taskId, BuildEnumMemberElementsTask.DESCRIPTOR);
+    registerExtension(taskId, BuildExportNamespaceTask.DESCRIPTOR);
+    registerExtension(taskId, BuildFunctionTypeAliasesTask.DESCRIPTOR);
+    registerExtension(taskId, BuildLibraryConstructorsTask.DESCRIPTOR);
+    registerExtension(taskId, BuildLibraryElementTask.DESCRIPTOR);
+    registerExtension(taskId, BuildPublicNamespaceTask.DESCRIPTOR);
+    registerExtension(taskId, BuildSourceClosuresTask.DESCRIPTOR);
+    registerExtension(taskId, BuildTypeProviderTask.DESCRIPTOR);
+    registerExtension(taskId, GatherUsedImportedElementsTask.DESCRIPTOR);
+    registerExtension(taskId, GatherUsedLocalElementsTask.DESCRIPTOR);
+    registerExtension(taskId, GenerateHintsTask.DESCRIPTOR);
+    registerExtension(taskId, ParseDartTask.DESCRIPTOR);
+    registerExtension(taskId, ResolveLibraryTypeNamesTask.DESCRIPTOR);
+    registerExtension(taskId, ResolveReferencesTask.DESCRIPTOR);
+    registerExtension(taskId, ResolveUnitTypeNamesTask.DESCRIPTOR);
+    registerExtension(taskId, ResolveVariableReferencesTask.DESCRIPTOR);
+    registerExtension(taskId, ScanDartTask.DESCRIPTOR);
+    registerExtension(taskId, VerifyUnitTask.DESCRIPTOR);
+    //
+    // Register HTML tasks.
+    //
+  }
+
+  /**
+   * Validate the given extension by throwing an [ExtensionError] if it is not a
+   * valid domain.
+   */
+  void _validateTaskExtension(Object extension) {
+    if (extension is! TaskDescriptor) {
+      String id = taskExtensionPoint.uniqueIdentifier;
+      throw new ExtensionError('Extensions to $id must be a TaskDescriptor');
+    }
+  }
+}
diff --git a/analyzer/lib/src/plugin/plugin_impl.dart b/analyzer/lib/src/plugin/plugin_impl.dart
new file mode 100644
index 0000000..5b9e8a2
--- /dev/null
+++ b/analyzer/lib/src/plugin/plugin_impl.dart
@@ -0,0 +1,125 @@
+// Copyright (c) 2015, 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 analyzer.src.plugin.plugin_impl;
+
+import 'dart:collection';
+
+import 'package:analyzer/plugin/plugin.dart';
+
+/**
+ * An object that manages the extension points for a single instance of the
+ * analysis server.
+ */
+class ExtensionManager {
+  /**
+   * A table mapping the id's of extension points to the corresponding
+   * extension points.
+   */
+  Map<String, ExtensionPointImpl> extensionPoints =
+      new HashMap<String, ExtensionPointImpl>();
+
+  /**
+   * Process each of the given [plugins] by allowing them to register extension
+   * points and extensions.
+   *
+   * An [ExtensionError] will be thrown if any of the plugins throws such an
+   * exception while registering with this manager.
+   */
+  void processPlugins(List<Plugin> plugins) {
+    for (Plugin plugin in plugins) {
+      plugin.registerExtensionPoints((String identifier,
+              [ValidateExtension validateExtension]) =>
+          registerExtensionPoint(plugin, identifier, validateExtension));
+    }
+    for (Plugin plugin in plugins) {
+      plugin.registerExtensions(registerExtension);
+    }
+  }
+
+  /**
+   * Register an [extension] to the extension point with the given unique
+   * [identifier].
+   */
+  void registerExtension(String identifier, Object extension) {
+    ExtensionPointImpl extensionPoint = extensionPoints[identifier];
+    if (extensionPoint == null) {
+      throw new ExtensionError(
+          'There is no extension point with the id "$identifier"');
+    }
+    extensionPoint.add(extension);
+  }
+
+  /**
+   * Register an extension point being defined by the given [plugin] with the
+   * given simple [identifier] and [validateExtension].
+   */
+  ExtensionPoint registerExtensionPoint(
+      Plugin plugin, String identifier, ValidateExtension validateExtension) {
+    String uniqueIdentifier = Plugin.buildUniqueIdentifier(plugin, identifier);
+    if (extensionPoints.containsKey(uniqueIdentifier)) {
+      throw new ExtensionError(
+          'There is already an extension point with the id "$identifier"');
+    }
+    ExtensionPointImpl extensionPoint =
+        new ExtensionPointImpl(plugin, identifier, validateExtension);
+    extensionPoints[uniqueIdentifier] = extensionPoint;
+    return extensionPoint;
+  }
+}
+
+/**
+ * A concrete representation of an extension point.
+ */
+class ExtensionPointImpl implements ExtensionPoint {
+  @override
+  final Plugin plugin;
+
+  @override
+  final String simpleIdentifier;
+
+  /**
+   * The function used to validate extensions to this extension point.
+   */
+  final ValidateExtension validateExtension;
+
+  /**
+   * The list of extensions to this extension point.
+   */
+  final List<Object> _extensions = <Object>[];
+
+  /**
+   * Initialize a newly create extension point to belong to the given [plugin]
+   * and have the given [simpleIdentifier]. If [validateExtension] is non-`null`
+   * it will be used to validate extensions associated with this extension
+   * point.
+   */
+  ExtensionPointImpl(
+      this.plugin, this.simpleIdentifier, this.validateExtension);
+
+  /**
+   * Return a list containing all of the extensions that have been registered
+   * for this extension point.
+   */
+  List<Object> get extensions => new UnmodifiableListView(_extensions);
+
+  /**
+   * Return the identifier used to uniquely identify this extension point. The
+   * unique identifier is the identifier for the plugin, followed by a period
+   * (`.`), followed by the [simpleIdentifier] for the extension point.
+   */
+  String get uniqueIdentifier =>
+      Plugin.buildUniqueIdentifier(plugin, simpleIdentifier);
+
+  /**
+   * Validate that the given [extension] is appropriate for this extension
+   * point, and if it is then add it to the list of registered exceptions.
+   */
+  void add(Object extension) {
+    if (validateExtension != null) {
+      validateExtension(extension);
+    }
+    _extensions.add(extension);
+  }
+}
diff --git a/analyzer/lib/src/services/lint.dart b/analyzer/lib/src/services/lint.dart
new file mode 100644
index 0000000..9dd51a0
--- /dev/null
+++ b/analyzer/lib/src/services/lint.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2015, 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 lint;
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/error.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/visitors.dart';
+
+/// Implementers contribute lint warnings via the provided error [reporter].
+abstract class Linter {
+  /// Used to report lint warnings.
+  /// NOTE: this is set by the framework before visit begins.
+  ErrorReporter reporter;
+
+  /// Return a visitor to be passed to compilation units to perform lint
+  /// analysis.
+  /// Lint errors are reported via this [Linter]'s error [reporter].
+  AstVisitor getVisitor();
+}
+
+/// Traverses a library's worth of dart code at a time to generate lint warnings
+/// over the set of sources.
+///
+/// See [LintCode].
+class LintGenerator {
+
+  /// A global container for contributed linters.
+  static final List<Linter> LINTERS = <Linter>[];
+
+  final Iterable<CompilationUnit> _compilationUnits;
+  final AnalysisErrorListener _errorListener;
+  final Iterable<Linter> _linters;
+
+  LintGenerator(this._compilationUnits, this._errorListener,
+      [Iterable<Linter> linters])
+      : _linters = linters != null ? linters : LINTERS;
+
+  void generate() {
+    PerformanceStatistics.lint.makeCurrentWhile(() {
+      _compilationUnits.forEach((cu) {
+        if (cu.element != null) {
+          _generate(cu, cu.element.source);
+        }
+      });
+    });
+  }
+
+  void _generate(CompilationUnit unit, Source source) {
+    ErrorReporter errorReporter = new ErrorReporter(_errorListener, source);
+    _linters.forEach((l) => l.reporter = errorReporter);
+    Iterable<AstVisitor> visitors = _linters.map((l) => l.getVisitor());
+    unit.accept(new DelegatingAstVisitor(visitors.where((v) => v != null)));
+  }
+}
diff --git a/analyzer/lib/src/string_source.dart b/analyzer/lib/src/string_source.dart
new file mode 100644
index 0000000..5ffb332
--- /dev/null
+++ b/analyzer/lib/src/string_source.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2013, 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 analyzer.string_source;
+
+import 'generated/engine.dart' show TimestampedData;
+import 'generated/source.dart';
+
+/// An implementation of [Source] that's based on an in-memory Dart string.
+class StringSource extends Source {
+  final String _contents;
+  final String fullName;
+  final int modificationStamp;
+
+  StringSource(this._contents, this.fullName)
+      : modificationStamp = new DateTime.now().millisecondsSinceEpoch;
+
+  TimestampedData<String> get contents =>
+      new TimestampedData(modificationStamp, _contents);
+
+  String get encoding =>
+      throw new UnsupportedError("StringSource doesn't support " "encoding.");
+
+  int get hashCode => _contents.hashCode ^ fullName.hashCode;
+
+  bool get isInSystemLibrary => false;
+
+  String get shortName => fullName;
+
+  @override
+  Uri get uri =>
+      throw new UnsupportedError("StringSource doesn't support uri.");
+
+  UriKind get uriKind =>
+      throw new UnsupportedError("StringSource doesn't support " "uriKind.");
+
+  bool operator ==(Object object) {
+    if (object is StringSource) {
+      StringSource ssObject = object;
+      return ssObject._contents == _contents && ssObject.fullName == fullName;
+    }
+    return false;
+  }
+
+  bool exists() => true;
+
+  Uri resolveRelativeUri(Uri relativeUri) => throw new UnsupportedError(
+      "StringSource doesn't support resolveRelative.");
+}
diff --git a/analyzer/lib/src/task/dart.dart b/analyzer/lib/src/task/dart.dart
new file mode 100644
index 0000000..b92427e
--- /dev/null
+++ b/analyzer/lib/src/task/dart.dart
@@ -0,0 +1,2586 @@
+// Copyright (c) 2015, 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 analyzer.src.task.dart;
+
+import 'dart:collection';
+import 'dart:math' as math;
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/engine.dart' hide AnalysisTask;
+import 'package:analyzer/src/generated/error.dart';
+import 'package:analyzer/src/generated/error_verifier.dart';
+import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/src/generated/parser.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+import 'package:analyzer/src/generated/sdk.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/task/general.dart';
+import 'package:analyzer/task/dart.dart';
+import 'package:analyzer/task/general.dart';
+import 'package:analyzer/task/model.dart';
+
+/**
+ * The errors produced while resolving a library directives.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ResultDescriptor<List<AnalysisError>> BUILD_DIRECTIVES_ERRORS =
+    new ResultDescriptor<List<AnalysisError>>(
+        'BUILD_DIRECTIVES_ERRORS', AnalysisError.NO_ERRORS,
+        contributesTo: DART_ERRORS);
+
+/**
+ * The errors produced while building function type aliases.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ResultDescriptor<List<AnalysisError>> BUILD_FUNCTION_TYPE_ALIASES_ERRORS =
+    new ResultDescriptor<List<AnalysisError>>(
+        'BUILD_FUNCTION_TYPE_ALIASES_ERRORS', AnalysisError.NO_ERRORS,
+        contributesTo: DART_ERRORS);
+
+/**
+ * The errors produced while building a library element.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ResultDescriptor<List<AnalysisError>> BUILD_LIBRARY_ERRORS =
+    new ResultDescriptor<List<AnalysisError>>(
+        'BUILD_LIBRARY_ERRORS', AnalysisError.NO_ERRORS,
+        contributesTo: DART_ERRORS);
+
+/**
+ * The [ClassElement]s of a [LibrarySpecificUnit].
+ */
+final ListResultDescriptor<ClassElement> CLASS_ELEMENTS =
+    new ListResultDescriptor<ClassElement>('CLASS_ELEMENTS', null);
+
+/**
+ * The element model associated with a single compilation unit.
+ *
+ * The result is only available for targets representing a Dart compilation unit.
+ */
+final ResultDescriptor<CompilationUnitElement> COMPILATION_UNIT_ELEMENT =
+    new ResultDescriptor<CompilationUnitElement>(
+        'COMPILATION_UNIT_ELEMENT', null);
+
+/**
+ * The [ConstructorElement]s of a [ClassElement].
+ */
+final ResultDescriptor<List<ConstructorElement>> CONSTRUCTORS =
+    new ResultDescriptor<List<ConstructorElement>>('CONSTRUCTORS', null);
+
+/**
+ * The errors produced while building a [ClassElement] constructors.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for targets representing a [ClassElement].
+ */
+final ResultDescriptor<List<AnalysisError>> CONSTRUCTORS_ERRORS =
+    new ResultDescriptor<List<AnalysisError>>(
+        'CONSTRUCTORS_ERRORS', AnalysisError.NO_ERRORS);
+
+/**
+ * The sources representing the export closure of a library.
+ * The [Source]s include only library sources, not their units.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ListResultDescriptor<Source> EXPORT_SOURCE_CLOSURE =
+    new ListResultDescriptor<Source>('EXPORT_SOURCE_CLOSURE', null);
+
+/**
+ * The errors produced while generating hints a compilation unit.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for targets representing a Dart compilation unit.
+ */
+final ResultDescriptor<List<AnalysisError>> HINTS =
+    new ResultDescriptor<List<AnalysisError>>(
+        'HINT_ERRORS', AnalysisError.NO_ERRORS, contributesTo: DART_ERRORS);
+
+/**
+ * The sources representing the import closure of a library.
+ * The [Source]s include only library sources, not their units.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ListResultDescriptor<Source> IMPORT_SOURCE_CLOSURE =
+    new ListResultDescriptor<Source>('IMPORT_SOURCE_CLOSURE', null);
+
+/**
+ * The partial [LibraryElement] associated with a library.
+ *
+ * The [LibraryElement] and its [CompilationUnitElement]s are attached to each
+ * other. Directives 'library', 'part' and 'part of' are resolved.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT1 =
+    new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT1', null);
+
+/**
+ * The partial [LibraryElement] associated with a library.
+ *
+ * In addition to [LIBRARY_ELEMENT1] [LibraryElement.imports] and
+ * [LibraryElement.exports] are set.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT2 =
+    new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT2', null);
+
+/**
+ * The partial [LibraryElement] associated with a library.
+ *
+ * In addition to [LIBRARY_ELEMENT2] the [LibraryElement.publicNamespace] is set.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT3 =
+    new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT3', null);
+
+/**
+ * The partial [LibraryElement] associated with a library.
+ *
+ * In addition to [LIBRARY_ELEMENT3] the [LibraryElement.entryPoint] is set,
+ * if the library does not declare one already and one of the exported
+ * libraries exports one.
+ *
+ * Also [LibraryElement.exportNamespace] is set.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT4 =
+    new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT4', null);
+
+/**
+ * The partial [LibraryElement] associated with a library.
+ *
+ * [LIBRARY_ELEMENT4] plus [RESOLVED_UNIT4] for every unit.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT5 =
+    new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT5', null);
+
+/**
+ * The errors produced while parsing a compilation unit.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for targets representing a Dart compilation unit.
+ */
+final ResultDescriptor<List<AnalysisError>> PARSE_ERRORS =
+    new ResultDescriptor<List<AnalysisError>>(
+        'PARSE_ERRORS', AnalysisError.NO_ERRORS, contributesTo: DART_ERRORS);
+
+/**
+ * The errors produced while resolving references.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for targets representing a unit.
+ */
+final ResultDescriptor<List<AnalysisError>> RESOLVE_REFERENCES_ERRORS =
+    new ResultDescriptor<List<AnalysisError>>(
+        'RESOLVE_REFERENCES_ERRORS', AnalysisError.NO_ERRORS,
+        contributesTo: DART_ERRORS);
+
+/**
+ * The errors produced while resolving type names.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ResultDescriptor<List<AnalysisError>> RESOLVE_TYPE_NAMES_ERRORS =
+    new ResultDescriptor<List<AnalysisError>>(
+        'RESOLVE_TYPE_NAMES_ERRORS', AnalysisError.NO_ERRORS,
+        contributesTo: DART_ERRORS);
+
+/**
+ * The partially resolved [CompilationUnit] associated with a unit.
+ *
+ * All declarations bound to the element defined by the declaration.
+ *
+ * The result is only available for targets representing a unit.
+ */
+final ResultDescriptor<CompilationUnit> RESOLVED_UNIT1 =
+    new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT1', null);
+
+/**
+ * The partially resolved [CompilationUnit] associated with a unit.
+ *
+ * All the enum member elements are built.
+ *
+ * The result is only available for targets representing a unit.
+ */
+final ResultDescriptor<CompilationUnit> RESOLVED_UNIT2 =
+    new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT2', null);
+
+/**
+ * The partially resolved [CompilationUnit] associated with a unit.
+ *
+ * All the function type aliases are resolved.
+ *
+ * The result is only available for targets representing a unit.
+ */
+final ResultDescriptor<CompilationUnit> RESOLVED_UNIT3 =
+    new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT3', null);
+
+/**
+ * The partially resolved [CompilationUnit] associated with a unit.
+ *
+ * [RESOLVED_UNIT3] with resolved type names.
+ *
+ * The result is only available for targets representing a unit.
+ */
+final ResultDescriptor<CompilationUnit> RESOLVED_UNIT4 =
+    new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT4', null);
+
+/**
+ * The partially resolved [CompilationUnit] associated with a unit.
+ *
+ * [RESOLVED_UNIT4] plus resolved local variables and formal parameters.
+ *
+ * The result is only available for targets representing a unit.
+ */
+final ResultDescriptor<CompilationUnit> RESOLVED_UNIT5 =
+    new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT5', null);
+
+/**
+ * The errors produced while scanning a compilation unit.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for targets representing a Dart compilation unit.
+ */
+final ResultDescriptor<List<AnalysisError>> SCAN_ERRORS =
+    new ResultDescriptor<List<AnalysisError>>(
+        'SCAN_ERRORS', AnalysisError.NO_ERRORS, contributesTo: DART_ERRORS);
+
+/**
+ * The [TypeProvider] of the context.
+ */
+final ResultDescriptor<TypeProvider> TYPE_PROVIDER =
+    new ResultDescriptor<TypeProvider>('TYPE_PROVIDER', null);
+
+/**
+ * The [UsedImportedElements] of a [LibrarySpecificUnit].
+ */
+final ResultDescriptor<UsedImportedElements> USED_IMPORTED_ELEMENTS =
+    new ResultDescriptor<UsedImportedElements>('USED_IMPORTED_ELEMENTS', null);
+
+/**
+ * The [UsedLocalElements] of a [LibrarySpecificUnit].
+ */
+final ResultDescriptor<UsedLocalElements> USED_LOCAL_ELEMENTS =
+    new ResultDescriptor<UsedLocalElements>('USED_LOCAL_ELEMENTS', null);
+
+/**
+ * The errors produced while verifying a compilation unit.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for targets representing a Dart compilation unit.
+ */
+final ResultDescriptor<List<AnalysisError>> VERIFY_ERRORS =
+    new ResultDescriptor<List<AnalysisError>>(
+        'VERIFY_ERRORS', AnalysisError.NO_ERRORS, contributesTo: DART_ERRORS);
+
+/**
+ * A task that builds implicit constructors for a [ClassElement], or keeps
+ * the existing explicit constructors if the class has them.
+ */
+class BuildClassConstructorsTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the [CONSTRUCTORS] input for the superclass.
+   */
+  static const String SUPER_CONSTRUCTORS = 'SUPER_CONSTRUCTORS';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'BuildConstructorsForClassTask', createTask, buildInputs,
+      <ResultDescriptor>[CONSTRUCTORS, CONSTRUCTORS_ERRORS]);
+
+  BuildClassConstructorsTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    List<AnalysisError> errors = <AnalysisError>[];
+    //
+    // Prepare inputs.
+    //
+    ClassElementImpl classElement = this.target;
+    List<ConstructorElement> superConstructors = inputs[SUPER_CONSTRUCTORS];
+    DartType superType = classElement.supertype;
+    ClassElement superElement = superType.element;
+    //
+    // Shortcut for ClassElement(s) without implicit constructors.
+    //
+    if (superConstructors == null) {
+      outputs[CONSTRUCTORS] = classElement.constructors;
+      outputs[CONSTRUCTORS_ERRORS] = AnalysisError.NO_ERRORS;
+      return;
+    }
+    //
+    // ClassTypeAlias
+    //
+    if (classElement.isTypedef) {
+      List<ConstructorElement> implicitConstructors =
+          new List<ConstructorElement>();
+      void callback(ConstructorElement explicitConstructor,
+          List<DartType> parameterTypes, List<DartType> argumentTypes) {
+        implicitConstructors.add(_createImplicitContructor(classElement.type,
+            explicitConstructor, parameterTypes, argumentTypes));
+      }
+      if (_findForwardedConstructors(classElement, superType, callback)) {
+        if (implicitConstructors.isEmpty) {
+          errors.add(new AnalysisError.con2(classElement.source,
+              classElement.nameOffset, classElement.name.length,
+              CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS,
+              [superElement.name]));
+        } else {
+          classElement.constructors = implicitConstructors;
+        }
+      }
+      outputs[CONSTRUCTORS] = classElement.constructors;
+      outputs[CONSTRUCTORS_ERRORS] = errors;
+    }
+    //
+    // ClassDeclaration
+    //
+    if (!classElement.isTypedef) {
+      bool constructorFound = false;
+      void callback(ConstructorElement explicitConstructor,
+          List<DartType> parameterTypes, List<DartType> argumentTypes) {
+        constructorFound = true;
+      }
+      if (_findForwardedConstructors(classElement, superType, callback) &&
+          !constructorFound) {
+        SourceRange withRange = classElement.withClauseRange;
+        errors.add(new AnalysisError.con2(classElement.source, withRange.offset,
+            withRange.length, CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS,
+            [superElement.name]));
+        classElement.mixinErrorsReported = true;
+      }
+      outputs[CONSTRUCTORS] = classElement.constructors;
+      outputs[CONSTRUCTORS_ERRORS] = errors;
+    }
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given [classElement].
+   */
+  static Map<String, TaskInput> buildInputs(ClassElement classElement) {
+    // TODO(scheglov) Here we implicitly depend on LIBRARY_ELEMENT5, i.e. that
+    // "supertype" for the "classElement" is set.
+    // We need to make it an explicit dependency.
+    DartType superType = classElement.supertype;
+    if (superType is InterfaceType) {
+      if (classElement.isTypedef || classElement.mixins.isNotEmpty) {
+        ClassElement superElement = superType.element;
+        return <String, TaskInput>{
+          SUPER_CONSTRUCTORS: CONSTRUCTORS.of(superElement)
+        };
+      }
+    }
+    // No implicit constructors, no inputs required.
+    return <String, TaskInput>{};
+  }
+
+  /**
+   * Create a [BuildClassConstructorsTask] based on the given
+   * [target] in the given [context].
+   */
+  static BuildClassConstructorsTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new BuildClassConstructorsTask(context, target);
+  }
+
+  /**
+   * Create an implicit constructor that is copied from the given
+   * [explicitConstructor], but that is in the given class.
+   *
+   * [classType] - the class in which the implicit constructor is defined.
+   * [explicitConstructor] - the constructor on which the implicit constructor
+   *    is modeled.
+   * [parameterTypes] - the types to be replaced when creating parameters.
+   * [argumentTypes] - the types with which the parameters are to be replaced.
+   */
+  static ConstructorElement _createImplicitContructor(InterfaceType classType,
+      ConstructorElement explicitConstructor, List<DartType> parameterTypes,
+      List<DartType> argumentTypes) {
+    ConstructorElementImpl implicitConstructor =
+        new ConstructorElementImpl(explicitConstructor.name, -1);
+    implicitConstructor.synthetic = true;
+    implicitConstructor.redirectedConstructor = explicitConstructor;
+    implicitConstructor.const2 = explicitConstructor.isConst;
+    implicitConstructor.returnType = classType;
+    List<ParameterElement> explicitParameters = explicitConstructor.parameters;
+    int count = explicitParameters.length;
+    if (count > 0) {
+      List<ParameterElement> implicitParameters =
+          new List<ParameterElement>(count);
+      for (int i = 0; i < count; i++) {
+        ParameterElement explicitParameter = explicitParameters[i];
+        ParameterElementImpl implicitParameter =
+            new ParameterElementImpl(explicitParameter.name, -1);
+        implicitParameter.const3 = explicitParameter.isConst;
+        implicitParameter.final2 = explicitParameter.isFinal;
+        implicitParameter.parameterKind = explicitParameter.parameterKind;
+        implicitParameter.synthetic = true;
+        implicitParameter.type =
+            explicitParameter.type.substitute2(argumentTypes, parameterTypes);
+        implicitParameters[i] = implicitParameter;
+      }
+      implicitConstructor.parameters = implicitParameters;
+    }
+    FunctionTypeImpl type = new FunctionTypeImpl.con1(implicitConstructor);
+    type.typeArguments = classType.typeArguments;
+    implicitConstructor.type = type;
+    return implicitConstructor;
+  }
+
+  /**
+   * Find all the constructors that should be forwarded from the given
+   * [superType], to the class or mixin application [classElement],
+   * and pass information about them to [callback].
+   *
+   * Return true if some constructors were considered.  (A false return value
+   * can only happen if the supeclass is a built-in type, in which case it
+   * can't be used as a mixin anyway).
+   */
+  static bool _findForwardedConstructors(ClassElementImpl classElement,
+      InterfaceType superType, void callback(
+          ConstructorElement explicitConstructor, List<DartType> parameterTypes,
+          List<DartType> argumentTypes)) {
+    ClassElement superclassElement = superType.element;
+    List<ConstructorElement> constructors = superclassElement.constructors;
+    int count = constructors.length;
+    if (count == 0) {
+      return false;
+    }
+    List<DartType> parameterTypes =
+        TypeParameterTypeImpl.getTypes(superType.typeParameters);
+    List<DartType> argumentTypes = _getArgumentTypes(superType, parameterTypes);
+    for (int i = 0; i < count; i++) {
+      ConstructorElement explicitConstructor = constructors[i];
+      if (!explicitConstructor.isFactory &&
+          classElement.isSuperConstructorAccessible(explicitConstructor)) {
+        callback(explicitConstructor, parameterTypes, argumentTypes);
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Return a list of argument types that corresponds to the [parameterTypes]
+   * and that are derived from the type arguments of the given [superType].
+   */
+  static List<DartType> _getArgumentTypes(
+      InterfaceType superType, List<DartType> parameterTypes) {
+    DynamicTypeImpl dynamic = DynamicTypeImpl.instance;
+    int parameterCount = parameterTypes.length;
+    List<DartType> types = new List<DartType>(parameterCount);
+    if (superType == null) {
+      types = new List<DartType>.filled(parameterCount, dynamic);
+    } else {
+      List<DartType> typeArguments = superType.typeArguments;
+      int argumentCount = math.min(typeArguments.length, parameterCount);
+      for (int i = 0; i < argumentCount; i++) {
+        types[i] = typeArguments[i];
+      }
+      for (int i = argumentCount; i < parameterCount; i++) {
+        types[i] = dynamic;
+      }
+    }
+    return types;
+  }
+}
+
+/**
+ * A task that builds a compilation unit element for a single compilation unit.
+ */
+class BuildCompilationUnitElementTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the input whose value is the line information for the
+   * compilation unit.
+   */
+  static const String LINE_INFO_INPUT_NAME = 'LINE_INFO_INPUT_NAME';
+
+  /**
+   * The name of the input whose value is the AST for the compilation unit.
+   */
+  static const String PARSED_UNIT_INPUT_NAME = 'PARSED_UNIT_INPUT_NAME';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'BuildCompilationUnitElementTask', createTask, buildInputs,
+      <ResultDescriptor>[
+    CLASS_ELEMENTS,
+    COMPILATION_UNIT_ELEMENT,
+    RESOLVED_UNIT1
+  ]);
+
+  /**
+   * Initialize a newly created task to build a compilation unit element for
+   * the given [target] in the given [context].
+   */
+  BuildCompilationUnitElementTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    //
+    // Prepare inputs.
+    //
+    Source source = getRequiredSource();
+    CompilationUnit unit = getRequiredInput(PARSED_UNIT_INPUT_NAME);
+    //
+    // Process inputs.
+    //
+    unit = AstCloner.clone(unit);
+    CompilationUnitBuilder builder = new CompilationUnitBuilder();
+    CompilationUnitElement element = builder.buildCompilationUnit(source, unit);
+    //
+    // Record outputs.
+    //
+    outputs[CLASS_ELEMENTS] = element.types;
+    outputs[COMPILATION_UNIT_ELEMENT] = element;
+    outputs[RESOLVED_UNIT1] = unit;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the given
+   * [target].
+   */
+  static Map<String, TaskInput> buildInputs(LibrarySpecificUnit target) {
+    return <String, TaskInput>{
+      PARSED_UNIT_INPUT_NAME: PARSED_UNIT.of(target.unit)
+    };
+  }
+
+  /**
+   * Create a [BuildCompilationUnitElementTask] based on the given [target] in
+   * the given [context].
+   */
+  static BuildCompilationUnitElementTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new BuildCompilationUnitElementTask(context, target);
+  }
+}
+
+/**
+ * A task that builds imports and export directive elements for a library.
+ */
+class BuildDirectiveElementsTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the input for [RESOLVED_UNIT1] of a library unit.
+   */
+  static const String UNIT_INPUT_NAME = 'UNIT_INPUT_NAME';
+
+  /**
+   * The input with a list of [LIBRARY_ELEMENT3]s of imported libraries.
+   */
+  static const String IMPORTS_LIBRARY_ELEMENT_INPUT_NAME =
+      'IMPORTS_LIBRARY_ELEMENT1_INPUT_NAME';
+
+  /**
+   * The input with a list of [LIBRARY_ELEMENT3]s of exported libraries.
+   */
+  static const String EXPORTS_LIBRARY_ELEMENT_INPUT_NAME =
+      'EXPORTS_LIBRARY_ELEMENT_INPUT_NAME';
+
+  /**
+   * The input with a list of [SOURCE_KIND]s of imported libraries.
+   */
+  static const String IMPORTS_SOURCE_KIND_INPUT_NAME =
+      'IMPORTS_SOURCE_KIND_INPUT_NAME';
+
+  /**
+   * The input with a list of [SOURCE_KIND]s of exported libraries.
+   */
+  static const String EXPORTS_SOURCE_KIND_INPUT_NAME =
+      'EXPORTS_SOURCE_KIND_INPUT_NAME';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'BuildDirectiveElementsTask', createTask, buildInputs, <ResultDescriptor>[
+    LIBRARY_ELEMENT2,
+    BUILD_DIRECTIVES_ERRORS
+  ]);
+
+  BuildDirectiveElementsTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    List<AnalysisError> errors = <AnalysisError>[];
+    //
+    // Prepare inputs.
+    //
+    CompilationUnit libraryUnit = getRequiredInput(UNIT_INPUT_NAME);
+    Map<Source, LibraryElement> importLibraryMap =
+        getRequiredInput(IMPORTS_LIBRARY_ELEMENT_INPUT_NAME);
+    Map<Source, LibraryElement> exportLibraryMap =
+        getRequiredInput(EXPORTS_LIBRARY_ELEMENT_INPUT_NAME);
+    Map<Source, SourceKind> importSourceKindMap =
+        getRequiredInput(IMPORTS_SOURCE_KIND_INPUT_NAME);
+    Map<Source, SourceKind> exportSourceKindMap =
+        getRequiredInput(EXPORTS_SOURCE_KIND_INPUT_NAME);
+    //
+    // Process inputs.
+    //
+    LibraryElementImpl libraryElement = libraryUnit.element.library;
+    Source librarySource = libraryElement.source;
+    //
+    // Resolve directives.
+    //
+    HashMap<String, PrefixElementImpl> nameToPrefixMap =
+        new HashMap<String, PrefixElementImpl>();
+    List<ImportElement> imports = <ImportElement>[];
+    List<ExportElement> exports = <ExportElement>[];
+    bool explicitlyImportsCore = false;
+    for (Directive directive in libraryUnit.directives) {
+      if (directive is ImportDirective) {
+        ImportDirective importDirective = directive;
+        String uriContent = importDirective.uriContent;
+        if (DartUriResolver.isDartExtUri(uriContent)) {
+          libraryElement.hasExtUri = true;
+        }
+        Source importedSource = importDirective.source;
+        if (importedSource != null && context.exists(importedSource)) {
+          // The imported source will be null if the URI in the import
+          // directive was invalid.
+          LibraryElement importedLibrary = importLibraryMap[importedSource];
+          if (importedLibrary != null) {
+            ImportElementImpl importElement =
+                new ImportElementImpl(directive.offset);
+            StringLiteral uriLiteral = importDirective.uri;
+            if (uriLiteral != null) {
+              importElement.uriOffset = uriLiteral.offset;
+              importElement.uriEnd = uriLiteral.end;
+            }
+            importElement.uri = uriContent;
+            importElement.deferred = importDirective.deferredKeyword != null;
+            importElement.combinators = _buildCombinators(importDirective);
+            importElement.importedLibrary = importedLibrary;
+            SimpleIdentifier prefixNode = directive.prefix;
+            if (prefixNode != null) {
+              importElement.prefixOffset = prefixNode.offset;
+              String prefixName = prefixNode.name;
+              PrefixElementImpl prefix = nameToPrefixMap[prefixName];
+              if (prefix == null) {
+                prefix = new PrefixElementImpl.forNode(prefixNode);
+                nameToPrefixMap[prefixName] = prefix;
+              }
+              importElement.prefix = prefix;
+              prefixNode.staticElement = prefix;
+            }
+            directive.element = importElement;
+            imports.add(importElement);
+            if (importSourceKindMap[importedSource] != SourceKind.LIBRARY) {
+              ErrorCode errorCode = (importElement.isDeferred
+                  ? StaticWarningCode.IMPORT_OF_NON_LIBRARY
+                  : CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY);
+              errors.add(new AnalysisError.con2(importedSource,
+                  uriLiteral.offset, uriLiteral.length, errorCode,
+                  [uriLiteral.toSource()]));
+            }
+          }
+        }
+      } else if (directive is ExportDirective) {
+        ExportDirective exportDirective = directive;
+        Source exportedSource = exportDirective.source;
+        if (exportedSource != null && context.exists(exportedSource)) {
+          // The exported source will be null if the URI in the export
+          // directive was invalid.
+          LibraryElement exportedLibrary = exportLibraryMap[exportedSource];
+          if (exportedLibrary != null) {
+            ExportElementImpl exportElement =
+                new ExportElementImpl(directive.offset);
+            StringLiteral uriLiteral = exportDirective.uri;
+            if (uriLiteral != null) {
+              exportElement.uriOffset = uriLiteral.offset;
+              exportElement.uriEnd = uriLiteral.end;
+            }
+            exportElement.uri = exportDirective.uriContent;
+            exportElement.combinators = _buildCombinators(exportDirective);
+            exportElement.exportedLibrary = exportedLibrary;
+            directive.element = exportElement;
+            exports.add(exportElement);
+            if (exportSourceKindMap[exportedSource] != SourceKind.LIBRARY) {
+              errors.add(new AnalysisError.con2(exportedSource,
+                  uriLiteral.offset, uriLiteral.length,
+                  CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY,
+                  [uriLiteral.toSource()]));
+            }
+          }
+        }
+      }
+    }
+    //
+    // Ensure "dart:core" import.
+    //
+    Source coreLibrarySource = context.sourceFactory.forUri(DartSdk.DART_CORE);
+    if (!explicitlyImportsCore && coreLibrarySource != librarySource) {
+      ImportElementImpl importElement = new ImportElementImpl(-1);
+      importElement.importedLibrary = importLibraryMap[coreLibrarySource];
+      importElement.synthetic = true;
+      imports.add(importElement);
+    }
+    //
+    // Populate the library element.
+    //
+    libraryElement.imports = imports;
+    libraryElement.exports = exports;
+    //
+    // Record outputs.
+    //
+    outputs[LIBRARY_ELEMENT2] = libraryElement;
+    outputs[BUILD_DIRECTIVES_ERRORS] = errors;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given library [libSource].
+   */
+  static Map<String, TaskInput> buildInputs(Source libSource) {
+    return <String, TaskInput>{
+      'defining_LIBRARY_ELEMENT1': LIBRARY_ELEMENT1.of(libSource),
+      UNIT_INPUT_NAME:
+          RESOLVED_UNIT1.of(new LibrarySpecificUnit(libSource, libSource)),
+      IMPORTS_LIBRARY_ELEMENT_INPUT_NAME:
+          IMPORTED_LIBRARIES.of(libSource).toMapOf(LIBRARY_ELEMENT1),
+      EXPORTS_LIBRARY_ELEMENT_INPUT_NAME:
+          EXPORTED_LIBRARIES.of(libSource).toMapOf(LIBRARY_ELEMENT1),
+      IMPORTS_SOURCE_KIND_INPUT_NAME:
+          IMPORTED_LIBRARIES.of(libSource).toMapOf(SOURCE_KIND),
+      EXPORTS_SOURCE_KIND_INPUT_NAME:
+          EXPORTED_LIBRARIES.of(libSource).toMapOf(SOURCE_KIND)
+    };
+  }
+
+  /**
+   * Create a [BuildDirectiveElementsTask] based on the given [target] in
+   * the given [context].
+   */
+  static BuildDirectiveElementsTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new BuildDirectiveElementsTask(context, target);
+  }
+
+  /**
+   * Build the element model representing the combinators declared by
+   * the given [directive].
+   */
+  static List<NamespaceCombinator> _buildCombinators(
+      NamespaceDirective directive) {
+    List<NamespaceCombinator> combinators = <NamespaceCombinator>[];
+    for (Combinator combinator in directive.combinators) {
+      if (combinator is ShowCombinator) {
+        ShowElementCombinatorImpl show = new ShowElementCombinatorImpl();
+        show.offset = combinator.offset;
+        show.end = combinator.end;
+        show.shownNames = _getIdentifiers(combinator.shownNames);
+        combinators.add(show);
+      } else if (combinator is HideCombinator) {
+        HideElementCombinatorImpl hide = new HideElementCombinatorImpl();
+        hide.hiddenNames = _getIdentifiers(combinator.hiddenNames);
+        combinators.add(hide);
+      }
+    }
+    return combinators;
+  }
+
+  /**
+   * Return the lexical identifiers associated with the given [identifiers].
+   */
+  static List<String> _getIdentifiers(NodeList<SimpleIdentifier> identifiers) {
+    return identifiers.map((identifier) => identifier.name).toList();
+  }
+}
+
+/**
+ * A task that builds the elements representing the members of enum
+ * declarations.
+ */
+class BuildEnumMemberElementsTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the [TYPE_PROVIDER] input.
+   */
+  static const String TYPE_PROVIDER_INPUT = 'TYPE_PROVIDER_INPUT';
+
+  /**
+   * The name of the [RESOLVED_UNIT1] input.
+   */
+  static const String UNIT_INPUT = 'UNIT_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'BuildEnumMemberElementsTask', createTask, buildInputs,
+      <ResultDescriptor>[RESOLVED_UNIT2]);
+
+  BuildEnumMemberElementsTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    //
+    // Prepare inputs.
+    //
+    TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
+    CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+    //
+    // Record outputs.
+    //
+    EnumMemberBuilder builder = new EnumMemberBuilder(typeProvider);
+    unit.accept(builder);
+    outputs[RESOLVED_UNIT2] = unit;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given [target].
+   */
+  static Map<String, TaskInput> buildInputs(LibrarySpecificUnit target) {
+    return <String, TaskInput>{
+      TYPE_PROVIDER_INPUT: TYPE_PROVIDER.of(AnalysisContextTarget.request),
+      UNIT_INPUT: RESOLVED_UNIT1.of(target)
+    };
+  }
+
+  /**
+   * Create a [BuildEnumMemberElementsTask] based on the given [target] in
+   * the given [context].
+   */
+  static BuildEnumMemberElementsTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new BuildEnumMemberElementsTask(context, target);
+  }
+}
+
+/**
+ * A task that builds [EXPORT_NAMESPACE] and [LIBRARY_ELEMENT4] for a library.
+ */
+class BuildExportNamespaceTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the input for [LIBRARY_ELEMENT3] of a library.
+   */
+  static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'BuildExportNamespaceTask', createTask, buildInputs,
+      <ResultDescriptor>[LIBRARY_ELEMENT4]);
+
+  BuildExportNamespaceTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    LibraryElementImpl library = getRequiredInput(LIBRARY_INPUT);
+    //
+    // Compute export namespace.
+    //
+    ExportNamespaceBuilder builder = new ExportNamespaceBuilder();
+    Namespace namespace = builder.build(library);
+    library.exportNamespace = namespace;
+    //
+    // Update entry point.
+    //
+    if (library.entryPoint == null) {
+      Iterable<Element> exportedElements = namespace.definedNames.values;
+      library.entryPoint = exportedElements.firstWhere(
+          (element) => element is FunctionElement && element.isEntryPoint,
+          orElse: () => null);
+    }
+    //
+    // Record outputs.
+    //
+    outputs[LIBRARY_ELEMENT4] = library;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given library [libSource].
+   */
+  static Map<String, TaskInput> buildInputs(Source libSource) {
+    return <String, TaskInput>{
+      LIBRARY_INPUT: LIBRARY_ELEMENT3.of(libSource),
+      'exportsLibraryPublicNamespace':
+          EXPORT_SOURCE_CLOSURE.of(libSource).toMapOf(LIBRARY_ELEMENT3)
+    };
+  }
+
+  /**
+   * Create a [BuildExportNamespaceTask] based on the given [target] in
+   * the given [context].
+   */
+  static BuildExportNamespaceTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new BuildExportNamespaceTask(context, target);
+  }
+}
+
+/**
+ * A task that builds [RESOLVED_UNIT3] for a unit.
+ */
+class BuildFunctionTypeAliasesTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the [TYPE_PROVIDER] input.
+   */
+  static const String TYPE_PROVIDER_INPUT = 'TYPE_PROVIDER_INPUT';
+
+  /**
+   * The name of the [LIBRARY_ELEMENT4] input.
+   */
+  static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
+
+  /**
+   * The name of the [RESOLVED_UNIT2] input.
+   */
+  static const String UNIT_INPUT = 'UNIT_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'BuildFunctionTypeAliasesTask', createTask, buildInputs,
+      <ResultDescriptor>[BUILD_FUNCTION_TYPE_ALIASES_ERRORS, RESOLVED_UNIT3]);
+
+  BuildFunctionTypeAliasesTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    //
+    // Prepare inputs.
+    //
+    Source source = getRequiredSource();
+    TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
+    CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+    LibraryElement libraryElement = getRequiredInput(LIBRARY_INPUT);
+    //
+    // Resolve FunctionTypeAlias declarations.
+    //
+    TypeResolverVisitor visitor = new TypeResolverVisitor.con2(
+        libraryElement, source, typeProvider, errorListener);
+    for (CompilationUnitMember member in unit.declarations) {
+      if (member is FunctionTypeAlias) {
+        member.accept(visitor);
+      }
+    }
+    //
+    // Record outputs.
+    //
+    outputs[BUILD_FUNCTION_TYPE_ALIASES_ERRORS] = errorListener.errors;
+    outputs[RESOLVED_UNIT3] = unit;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given [target].
+   */
+  static Map<String, TaskInput> buildInputs(LibrarySpecificUnit target) {
+    return <String, TaskInput>{
+      TYPE_PROVIDER_INPUT: TYPE_PROVIDER.of(AnalysisContextTarget.request),
+      'importsExportNamespace':
+          IMPORTED_LIBRARIES.of(target.library).toMapOf(LIBRARY_ELEMENT4),
+      LIBRARY_INPUT: LIBRARY_ELEMENT4.of(target.library),
+      UNIT_INPUT: RESOLVED_UNIT2.of(target)
+    };
+  }
+
+  /**
+   * Create a [BuildFunctionTypeAliasesTask] based on the given [target] in
+   * the given [context].
+   */
+  static BuildFunctionTypeAliasesTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new BuildFunctionTypeAliasesTask(context, target);
+  }
+}
+
+/**
+ * This task finishes building [LIBRARY_ELEMENT] by forcing building
+ * constructors for classes in the defining and part units of a library.
+ */
+class BuildLibraryConstructorsTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the [LIBRARY_ELEMENT5] input.
+   */
+  static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'BuildLibraryConstructorsTask', createTask, buildInputs,
+      <ResultDescriptor>[LIBRARY_ELEMENT]);
+
+  BuildLibraryConstructorsTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    LibraryElement library = getRequiredInput(LIBRARY_INPUT);
+    outputs[LIBRARY_ELEMENT] = library;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given [target].
+   */
+  static Map<String, TaskInput> buildInputs(Source libSource) {
+    return <String, TaskInput>{
+      LIBRARY_INPUT: LIBRARY_ELEMENT5.of(libSource),
+      'resolvedConstructors':
+          CLASS_ELEMENTS.of(libSource).toListOf(CONSTRUCTORS),
+    };
+  }
+
+  /**
+   * Create a [BuildLibraryConstructorsTask] based on the given [target] in
+   * the given [context].
+   */
+  static BuildLibraryConstructorsTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new BuildLibraryConstructorsTask(context, target);
+  }
+}
+
+/**
+ * A task that builds a library element for a Dart library.
+ */
+class BuildLibraryElementTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the input whose value is the defining [RESOLVED_UNIT1].
+   */
+  static const String DEFINING_UNIT_INPUT = 'DEFINING_UNIT_INPUT';
+
+  /**
+   * The name of the input whose value is a list of built [RESOLVED_UNIT1]s
+   * of the parts sourced by a library.
+   */
+  static const String PARTS_UNIT_INPUT = 'PARTS_UNIT_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'BuildLibraryElementTask', createTask, buildInputs, <ResultDescriptor>[
+    BUILD_LIBRARY_ERRORS,
+    CLASS_ELEMENTS,
+    LIBRARY_ELEMENT1,
+    IS_LAUNCHABLE
+  ]);
+
+  /**
+   * The constant used as an unknown common library name in parts.
+   */
+  static const String _UNKNOWN_LIBRARY_NAME = 'unknown-library-name';
+
+  /**
+   * Initialize a newly created task to build a library element for the given
+   * [target] in the given [context].
+   */
+  BuildLibraryElementTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    List<AnalysisError> errors = <AnalysisError>[];
+    //
+    // Prepare inputs.
+    //
+    Source librarySource = getRequiredSource();
+    CompilationUnit definingCompilationUnit =
+        getRequiredInput(DEFINING_UNIT_INPUT);
+    List<CompilationUnit> partUnits = getRequiredInput(PARTS_UNIT_INPUT);
+    //
+    // Process inputs.
+    //
+    CompilationUnitElementImpl definingCompilationUnitElement =
+        definingCompilationUnit.element;
+    Map<Source, CompilationUnit> partUnitMap =
+        new HashMap<Source, CompilationUnit>();
+    for (CompilationUnit partUnit in partUnits) {
+      Source partSource = partUnit.element.source;
+      partUnitMap[partSource] = partUnit;
+    }
+    //
+    // Update "part" directives.
+    //
+    LibraryIdentifier libraryNameNode = null;
+    String partsLibraryName = _UNKNOWN_LIBRARY_NAME;
+    bool hasPartDirective = false;
+    FunctionElement entryPoint =
+        _findEntryPoint(definingCompilationUnitElement);
+    List<Directive> directivesToResolve = <Directive>[];
+    List<CompilationUnitElementImpl> sourcedCompilationUnits =
+        <CompilationUnitElementImpl>[];
+    for (Directive directive in definingCompilationUnit.directives) {
+      if (directive is LibraryDirective) {
+        if (libraryNameNode == null) {
+          libraryNameNode = directive.name;
+          directivesToResolve.add(directive);
+        }
+      } else if (directive is PartDirective) {
+        PartDirective partDirective = directive;
+        StringLiteral partUri = partDirective.uri;
+        Source partSource = partDirective.source;
+        if (context.exists(partSource)) {
+          hasPartDirective = true;
+          CompilationUnit partUnit = partUnitMap[partSource];
+          CompilationUnitElementImpl partElement = partUnit.element;
+          partElement.uriOffset = partUri.offset;
+          partElement.uriEnd = partUri.end;
+          partElement.uri = partDirective.uriContent;
+          //
+          // Validate that the part contains a part-of directive with the same
+          // name as the library.
+          //
+          String partLibraryName =
+              _getPartLibraryName(partSource, partUnit, directivesToResolve);
+          if (partLibraryName == null) {
+            errors.add(new AnalysisError.con2(librarySource, partUri.offset,
+                partUri.length, CompileTimeErrorCode.PART_OF_NON_PART,
+                [partUri.toSource()]));
+          } else if (libraryNameNode == null) {
+            if (partsLibraryName == _UNKNOWN_LIBRARY_NAME) {
+              partsLibraryName = partLibraryName;
+            } else if (partsLibraryName != partLibraryName) {
+              partsLibraryName = null;
+            }
+          } else if (libraryNameNode.name != partLibraryName) {
+            errors.add(new AnalysisError.con2(librarySource, partUri.offset,
+                partUri.length, StaticWarningCode.PART_OF_DIFFERENT_LIBRARY, [
+              libraryNameNode.name,
+              partLibraryName
+            ]));
+          }
+          if (entryPoint == null) {
+            entryPoint = _findEntryPoint(partElement);
+          }
+          directive.element = partElement;
+          sourcedCompilationUnits.add(partElement);
+        }
+      }
+    }
+    if (hasPartDirective && libraryNameNode == null) {
+      AnalysisError error;
+      if (partsLibraryName != _UNKNOWN_LIBRARY_NAME &&
+          partsLibraryName != null) {
+        error = new AnalysisErrorWithProperties.con1(librarySource,
+            ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART)
+          ..setProperty(ErrorProperty.PARTS_LIBRARY_NAME, partsLibraryName);
+      } else {
+        error = new AnalysisError.con1(librarySource,
+            ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART);
+      }
+      errors.add(error);
+    }
+    //
+    // Create and populate the library element.
+    //
+    LibraryElementImpl libraryElement =
+        new LibraryElementImpl.forNode(context, libraryNameNode);
+    libraryElement.definingCompilationUnit = definingCompilationUnitElement;
+    libraryElement.entryPoint = entryPoint;
+    libraryElement.parts = sourcedCompilationUnits;
+    for (Directive directive in directivesToResolve) {
+      directive.element = libraryElement;
+    }
+    if (sourcedCompilationUnits.isNotEmpty) {
+      _patchTopLevelAccessors(libraryElement);
+    }
+    //
+    // Prepare all class elements.
+    //
+    List<ClassElement> classElements = libraryElement.units
+        .map((CompilationUnitElement unitElement) => unitElement.types)
+        .expand((List<ClassElement> unitClassElements) => unitClassElements)
+        .toList();
+    //
+    // Record outputs.
+    //
+    outputs[BUILD_LIBRARY_ERRORS] = errors;
+    outputs[CLASS_ELEMENTS] = classElements;
+    outputs[LIBRARY_ELEMENT1] = libraryElement;
+    outputs[IS_LAUNCHABLE] = entryPoint != null;
+  }
+
+  /**
+   * Add all of the non-synthetic [getters] and [setters] defined in the given
+   * [unit] that have no corresponding accessor to one of the given collections.
+   */
+  void _collectAccessors(Map<String, PropertyAccessorElement> getters,
+      List<PropertyAccessorElement> setters, CompilationUnitElement unit) {
+    for (PropertyAccessorElement accessor in unit.accessors) {
+      if (accessor.isGetter) {
+        if (!accessor.isSynthetic && accessor.correspondingSetter == null) {
+          getters[accessor.displayName] = accessor;
+        }
+      } else {
+        if (!accessor.isSynthetic && accessor.correspondingGetter == null) {
+          setters.add(accessor);
+        }
+      }
+    }
+  }
+
+  /**
+   * Return the top-level [FunctionElement] entry point, or `null` if the given
+   * [element] does not define an entry point.
+   */
+  FunctionElement _findEntryPoint(CompilationUnitElementImpl element) {
+    for (FunctionElement function in element.functions) {
+      if (function.isEntryPoint) {
+        return function;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return the name of the library that the given part is declared to be a
+   * part of, or `null` if the part does not contain a part-of directive.
+   */
+  String _getPartLibraryName(Source partSource, CompilationUnit partUnit,
+      List<Directive> directivesToResolve) {
+    for (Directive directive in partUnit.directives) {
+      if (directive is PartOfDirective) {
+        directivesToResolve.add(directive);
+        LibraryIdentifier libraryName = directive.libraryName;
+        if (libraryName != null) {
+          return libraryName.name;
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Look through all of the compilation units defined for the given [library],
+   * looking for getters and setters that are defined in different compilation
+   * units but that have the same names. If any are found, make sure that they
+   * have the same variable element.
+   */
+  void _patchTopLevelAccessors(LibraryElementImpl library) {
+    HashMap<String, PropertyAccessorElement> getters =
+        new HashMap<String, PropertyAccessorElement>();
+    List<PropertyAccessorElement> setters = <PropertyAccessorElement>[];
+    _collectAccessors(getters, setters, library.definingCompilationUnit);
+    for (CompilationUnitElement unit in library.parts) {
+      _collectAccessors(getters, setters, unit);
+    }
+    for (PropertyAccessorElementImpl setter in setters) {
+      PropertyAccessorElement getter = getters[setter.displayName];
+      if (getter != null) {
+        TopLevelVariableElementImpl variable = getter.variable;
+        TopLevelVariableElementImpl setterVariable = setter.variable;
+        CompilationUnitElementImpl setterUnit = setterVariable.enclosingElement;
+        setterUnit.replaceTopLevelVariable(setterVariable, variable);
+        variable.setter = setter;
+        setter.variable = variable;
+      }
+    }
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the given
+   * [libSource].
+   */
+  static Map<String, TaskInput> buildInputs(Source libSource) {
+    return <String, TaskInput>{
+      DEFINING_UNIT_INPUT:
+          RESOLVED_UNIT1.of(new LibrarySpecificUnit(libSource, libSource)),
+      PARTS_UNIT_INPUT: INCLUDED_PARTS.of(libSource).toList((Source unit) {
+        return RESOLVED_UNIT1.of(new LibrarySpecificUnit(libSource, unit));
+      })
+    };
+  }
+
+  /**
+   * Create a [BuildLibraryElementTask] based on the given [target] in the
+   * given [context].
+   */
+  static BuildLibraryElementTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new BuildLibraryElementTask(context, target);
+  }
+}
+
+/**
+ * A task that builds [PUBLIC_NAMESPACE] for a library.
+ */
+class BuildPublicNamespaceTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the input for [LIBRARY_ELEMENT2] of a library.
+   */
+  static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'BuildPublicNamespaceTask', createTask, buildInputs,
+      <ResultDescriptor>[LIBRARY_ELEMENT3]);
+
+  BuildPublicNamespaceTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    LibraryElementImpl library = getRequiredInput(LIBRARY_INPUT);
+    library.publicNamespace = new PublicNamespaceBuilder().build(library);
+    outputs[LIBRARY_ELEMENT3] = library;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given library [libSource].
+   */
+  static Map<String, TaskInput> buildInputs(Source libSource) {
+    return <String, TaskInput>{LIBRARY_INPUT: LIBRARY_ELEMENT2.of(libSource)};
+  }
+
+  /**
+   * Create a [BuildPublicNamespaceTask] based on the given [target] in
+   * the given [context].
+   */
+  static BuildPublicNamespaceTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new BuildPublicNamespaceTask(context, target);
+  }
+}
+
+/**
+ * A task that builds [IMPORT_SOURCE_CLOSURE] and [EXPORT_SOURCE_CLOSURE] of
+ * a library.
+ */
+class BuildSourceClosuresTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the import closure.
+   */
+  static const String IMPORT_CLOSURE_INPUT = 'IMPORT_CLOSURE_INPUT';
+
+  /**
+   * The name of the export closure.
+   */
+  static const String EXPORT_CLOSURE_INPUT = 'EXPORT_CLOSURE_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'BuildExportSourceClosureTask', createTask, buildInputs,
+      <ResultDescriptor>[
+    IMPORT_SOURCE_CLOSURE,
+    EXPORT_SOURCE_CLOSURE,
+    IS_CLIENT
+  ]);
+
+  BuildSourceClosuresTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    List<Source> importClosure = getRequiredInput(IMPORT_CLOSURE_INPUT);
+    List<Source> exportClosure = getRequiredInput(EXPORT_CLOSURE_INPUT);
+    Source htmlSource = context.sourceFactory.forUri(DartSdk.DART_HTML);
+    //
+    // Record outputs.
+    //
+    outputs[IMPORT_SOURCE_CLOSURE] = importClosure;
+    outputs[EXPORT_SOURCE_CLOSURE] = exportClosure;
+    outputs[IS_CLIENT] = importClosure.contains(htmlSource);
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given library [libSource].
+   */
+  static Map<String, TaskInput> buildInputs(Source libSource) {
+    return <String, TaskInput>{
+      IMPORT_CLOSURE_INPUT: new _ImportSourceClosureTaskInput(libSource),
+      EXPORT_CLOSURE_INPUT: new _ExportSourceClosureTaskInput(libSource)
+    };
+  }
+
+  /**
+   * Create a [BuildSourceClosuresTask] based on the given [target] in
+   * the given [context].
+   */
+  static BuildSourceClosuresTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new BuildSourceClosuresTask(context, target);
+  }
+}
+
+/**
+ * A task that builds [TYPE_PROVIDER] for a context.
+ */
+class BuildTypeProviderTask extends SourceBasedAnalysisTask {
+  /**
+   * The [PUBLIC_NAMESPACE] input of the `dart:core` library.
+   */
+  static const String CORE_INPUT = 'CORE_INPUT';
+
+  /**
+   * The [PUBLIC_NAMESPACE] input of the `dart:async` library.
+   */
+  static const String ASYNC_INPUT = 'ASYNC_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'BuildTypeProviderTask', createTask, buildInputs,
+      <ResultDescriptor>[TYPE_PROVIDER]);
+
+  BuildTypeProviderTask(
+      InternalAnalysisContext context, AnalysisContextTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    LibraryElement coreLibrary = getRequiredInput(CORE_INPUT);
+    LibraryElement asyncLibrary = getRequiredInput(ASYNC_INPUT);
+    Namespace coreNamespace = coreLibrary.publicNamespace;
+    Namespace asyncNamespace = asyncLibrary.publicNamespace;
+    //
+    // Record outputs.
+    //
+    TypeProvider typeProvider =
+        new TypeProviderImpl.forNamespaces(coreNamespace, asyncNamespace);
+    (context as InternalAnalysisContext).typeProvider = typeProvider;
+    outputs[TYPE_PROVIDER] = typeProvider;
+  }
+
+  static Map<String, TaskInput> buildInputs(AnalysisContextTarget target) {
+    SourceFactory sourceFactory = target.context.sourceFactory;
+    Source coreSource = sourceFactory.forUri(DartSdk.DART_CORE);
+    Source asyncSource = sourceFactory.forUri(DartSdk.DART_ASYNC);
+    return <String, TaskInput>{
+      CORE_INPUT: LIBRARY_ELEMENT3.of(coreSource),
+      ASYNC_INPUT: LIBRARY_ELEMENT3.of(asyncSource)
+    };
+  }
+
+  /**
+   * Create a [BuildTypeProviderTask] based on the given [context].
+   */
+  static BuildTypeProviderTask createTask(
+      AnalysisContext context, AnalysisContextTarget target) {
+    return new BuildTypeProviderTask(context, target);
+  }
+}
+
+/**
+ * The helper for building the export [Namespace] of a [LibraryElement].
+ */
+class ExportNamespaceBuilder {
+  /**
+   * Build the export [Namespace] of the given [LibraryElement].
+   */
+  Namespace build(LibraryElement library) {
+    return new Namespace(
+        _createExportMapping(library, new HashSet<LibraryElement>()));
+  }
+
+  /**
+   * Create a mapping table representing the export namespace of the given
+   * [library].
+   *
+   * The given [visitedElements] a set of libraries that do not need to be
+   * visited when processing the export directives of the given library because
+   * all of the names defined by them will be added by another library.
+   */
+  HashMap<String, Element> _createExportMapping(
+      LibraryElement library, HashSet<LibraryElement> visitedElements) {
+    visitedElements.add(library);
+    try {
+      HashMap<String, Element> definedNames = new HashMap<String, Element>();
+      // Add names of the export directives.
+      for (ExportElement element in library.exports) {
+        LibraryElement exportedLibrary = element.exportedLibrary;
+        if (exportedLibrary != null &&
+            !visitedElements.contains(exportedLibrary)) {
+          //
+          // The exported library will be null if the URI does not reference a
+          // valid library.
+          //
+          HashMap<String, Element> exportedNames =
+              _createExportMapping(exportedLibrary, visitedElements);
+          exportedNames = _applyCombinators(exportedNames, element.combinators);
+          definedNames.addAll(exportedNames);
+        }
+      }
+      // Add names of the public namespace.
+      {
+        Namespace publicNamespace = library.publicNamespace;
+        if (publicNamespace != null) {
+          definedNames.addAll(publicNamespace.definedNames);
+        }
+      }
+      return definedNames;
+    } finally {
+      visitedElements.remove(library);
+    }
+  }
+
+  /**
+   * Apply the given [combinators] to all of the names in [definedNames].
+   */
+  static HashMap<String, Element> _applyCombinators(
+      HashMap<String, Element> definedNames,
+      List<NamespaceCombinator> combinators) {
+    for (NamespaceCombinator combinator in combinators) {
+      if (combinator is HideElementCombinator) {
+        _hide(definedNames, combinator.hiddenNames);
+      } else if (combinator is ShowElementCombinator) {
+        definedNames = _show(definedNames, combinator.shownNames);
+      }
+    }
+    return definedNames;
+  }
+
+  /**
+   * Hide all of the [hiddenNames] by removing them from the given
+   * [definedNames].
+   */
+  static void _hide(
+      HashMap<String, Element> definedNames, List<String> hiddenNames) {
+    for (String name in hiddenNames) {
+      definedNames.remove(name);
+      definedNames.remove('$name=');
+    }
+  }
+
+  /**
+   * Show only the given [shownNames] by removing all other names from the given
+   * [definedNames].
+   */
+  static HashMap<String, Element> _show(
+      HashMap<String, Element> definedNames, List<String> shownNames) {
+    HashMap<String, Element> newNames = new HashMap<String, Element>();
+    for (String name in shownNames) {
+      Element element = definedNames[name];
+      if (element != null) {
+        newNames[name] = element;
+      }
+      String setterName = '$name=';
+      element = definedNames[setterName];
+      if (element != null) {
+        newNames[setterName] = element;
+      }
+    }
+    return newNames;
+  }
+}
+
+/**
+ * A task that builds [USED_IMPORTED_ELEMENTS] for a unit.
+ */
+class GatherUsedImportedElementsTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the [RESOLVED_UNIT] input.
+   */
+  static const String UNIT_INPUT = 'UNIT_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'GatherUsedImportedElementsTask', createTask, buildInputs,
+      <ResultDescriptor>[USED_IMPORTED_ELEMENTS]);
+
+  GatherUsedImportedElementsTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+    CompilationUnitElement unitElement = unit.element;
+    LibraryElement libraryElement = unitElement.library;
+    //
+    // Prepare used imported elements.
+    //
+    GatherUsedImportedElementsVisitor visitor =
+        new GatherUsedImportedElementsVisitor(libraryElement);
+    unit.accept(visitor);
+    //
+    // Record outputs.
+    //
+    outputs[USED_IMPORTED_ELEMENTS] = visitor.usedElements;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given [target].
+   */
+  static Map<String, TaskInput> buildInputs(LibrarySpecificUnit target) {
+    return <String, TaskInput>{UNIT_INPUT: RESOLVED_UNIT.of(target)};
+  }
+
+  /**
+   * Create a [GatherUsedImportedElementsTask] based on the given [target] in
+   * the given [context].
+   */
+  static GatherUsedImportedElementsTask createTask(
+      AnalysisContext context, LibrarySpecificUnit target) {
+    return new GatherUsedImportedElementsTask(context, target);
+  }
+}
+
+/**
+ * A task that builds [USED_LOCAL_ELEMENTS] for a unit.
+ */
+class GatherUsedLocalElementsTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the [RESOLVED_UNIT] input.
+   */
+  static const String UNIT_INPUT = 'UNIT_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'GatherUsedLocalElementsTask', createTask, buildInputs,
+      <ResultDescriptor>[USED_LOCAL_ELEMENTS]);
+
+  GatherUsedLocalElementsTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+    CompilationUnitElement unitElement = unit.element;
+    LibraryElement libraryElement = unitElement.library;
+    //
+    // Prepare used local elements.
+    //
+    GatherUsedLocalElementsVisitor visitor =
+        new GatherUsedLocalElementsVisitor(libraryElement);
+    unit.accept(visitor);
+    //
+    // Record outputs.
+    //
+    outputs[USED_LOCAL_ELEMENTS] = visitor.usedElements;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given [target].
+   */
+  static Map<String, TaskInput> buildInputs(LibrarySpecificUnit target) {
+    return <String, TaskInput>{UNIT_INPUT: RESOLVED_UNIT.of(target)};
+  }
+
+  /**
+   * Create a [GatherUsedLocalElementsTask] based on the given [target] in
+   * the given [context].
+   */
+  static GatherUsedLocalElementsTask createTask(
+      AnalysisContext context, LibrarySpecificUnit target) {
+    return new GatherUsedLocalElementsTask(context, target);
+  }
+}
+
+/**
+ * A task that generates [HINTS] for a unit.
+ */
+class GenerateHintsTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the [RESOLVED_UNIT] input.
+   */
+  static const String UNIT_INPUT = 'UNIT_INPUT';
+
+  /**
+   * The name of a list of [USED_LOCAL_ELEMENTS] for each library unit input.
+   */
+  static const String USED_LOCAL_ELEMENTS_INPUT = 'USED_LOCAL_ELEMENTS';
+
+  /**
+   * The name of a list of [USED_IMPORTED_ELEMENTS] for each library unit input.
+   */
+  static const String USED_IMPORTED_ELEMENTS_INPUT = 'USED_IMPORTED_ELEMENTS';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'GenerateHintsTask', createTask, buildInputs, <ResultDescriptor>[HINTS]);
+
+  GenerateHintsTask(InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    Source source = getRequiredSource();
+    ErrorReporter errorReporter = new ErrorReporter(errorListener, source);
+    //
+    // Prepare inputs.
+    //
+    CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+    List<UsedImportedElements> usedImportedElementsList =
+        getRequiredInput(USED_IMPORTED_ELEMENTS_INPUT);
+    List<UsedLocalElements> usedLocalElementsList =
+        getRequiredInput(USED_LOCAL_ELEMENTS_INPUT);
+    CompilationUnitElement unitElement = unit.element;
+    LibraryElement libraryElement = unitElement.library;
+    //
+    // Generate errors.
+    //
+    unit.accept(new DeadCodeVerifier(errorReporter));
+    // Verify imports.
+    {
+      ImportsVerifier verifier = new ImportsVerifier();
+      verifier.addImports(unit);
+      usedImportedElementsList.forEach(verifier.removeUsedElements);
+      verifier.generateDuplicateImportHints(errorReporter);
+      verifier.generateUnusedImportHints(errorReporter);
+    }
+    // Unused local elements.
+    {
+      UsedLocalElements usedElements =
+          new UsedLocalElements.merge(usedLocalElementsList);
+      UnusedLocalElementsVerifier visitor =
+          new UnusedLocalElementsVerifier(errorListener, usedElements);
+      unitElement.accept(visitor);
+    }
+    // Dart2js analysis.
+    if (context.analysisOptions.dart2jsHint) {
+      unit.accept(new Dart2JSVerifier(errorReporter));
+    }
+    // Dart best practices.
+    InheritanceManager inheritanceManager =
+        new InheritanceManager(libraryElement);
+    TypeProvider typeProvider = context.typeProvider;
+    unit.accept(new BestPracticesVerifier(errorReporter, typeProvider));
+    unit.accept(new OverrideVerifier(errorReporter, inheritanceManager));
+    // Find to-do comments.
+    new ToDoFinder(errorReporter).findIn(unit);
+    //
+    // Record outputs.
+    //
+    outputs[HINTS] = errorListener.errors;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given [target].
+   */
+  static Map<String, TaskInput> buildInputs(LibrarySpecificUnit target) {
+    Source libSource = target.library;
+    return <String, TaskInput>{
+      UNIT_INPUT: RESOLVED_UNIT.of(target),
+      USED_LOCAL_ELEMENTS_INPUT: UNITS.of(libSource).toList((unit) {
+        LibrarySpecificUnit target = new LibrarySpecificUnit(libSource, unit);
+        return USED_LOCAL_ELEMENTS.of(target);
+      }),
+      USED_IMPORTED_ELEMENTS_INPUT: UNITS.of(libSource).toList((unit) {
+        LibrarySpecificUnit target = new LibrarySpecificUnit(libSource, unit);
+        return USED_IMPORTED_ELEMENTS.of(target);
+      })
+    };
+  }
+
+  /**
+   * Create a [GenerateHintsTask] based on the given [target] in
+   * the given [context].
+   */
+  static GenerateHintsTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new GenerateHintsTask(context, target);
+  }
+}
+
+/**
+ * A task that parses the content of a Dart file, producing an AST structure.
+ */
+class ParseDartTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the input whose value is the line information produced for the
+   * file.
+   */
+  static const String LINE_INFO_INPUT_NAME = 'LINE_INFO_INPUT_NAME';
+
+  /**
+   * The name of the input whose value is the token stream produced for the file.
+   */
+  static const String TOKEN_STREAM_INPUT_NAME = 'TOKEN_STREAM_INPUT_NAME';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor('ParseDartTask',
+      createTask, buildInputs, <ResultDescriptor>[
+    EXPORTED_LIBRARIES,
+    IMPORTED_LIBRARIES,
+    INCLUDED_PARTS,
+    PARSE_ERRORS,
+    PARSED_UNIT,
+    SOURCE_KIND,
+    UNITS
+  ]);
+
+  /**
+   * Initialize a newly created task to parse the content of the Dart file
+   * associated with the given [target] in the given [context].
+   */
+  ParseDartTask(InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    Source source = getRequiredSource();
+    LineInfo lineInfo = getRequiredInput(LINE_INFO_INPUT_NAME);
+    Token tokenStream = getRequiredInput(TOKEN_STREAM_INPUT_NAME);
+
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    Parser parser = new Parser(source, errorListener);
+    AnalysisOptions options = context.analysisOptions;
+    parser.parseFunctionBodies = options.analyzeFunctionBodiesPredicate(source);
+    CompilationUnit unit = parser.parseCompilationUnit(tokenStream);
+    unit.lineInfo = lineInfo;
+
+    bool hasNonPartOfDirective = false;
+    bool hasPartOfDirective = false;
+    HashSet<Source> exportedSources = new HashSet<Source>();
+    HashSet<Source> importedSources = new HashSet<Source>();
+    HashSet<Source> includedSources = new HashSet<Source>();
+    for (Directive directive in unit.directives) {
+      if (directive is PartOfDirective) {
+        hasPartOfDirective = true;
+      } else {
+        hasNonPartOfDirective = true;
+        if (directive is UriBasedDirective) {
+          Source referencedSource =
+              resolveDirective(context, source, directive, errorListener);
+          if (referencedSource != null) {
+            if (directive is ExportDirective) {
+              exportedSources.add(referencedSource);
+            } else if (directive is ImportDirective) {
+              importedSources.add(referencedSource);
+            } else if (directive is PartDirective) {
+              if (referencedSource != source) {
+                includedSources.add(referencedSource);
+              }
+            } else {
+              throw new AnalysisException(
+                  '$runtimeType failed to handle a ${directive.runtimeType}');
+            }
+          }
+        }
+      }
+    }
+    //
+    // Always include "dart:core" source.
+    //
+    Source coreLibrarySource = context.sourceFactory.forUri(DartSdk.DART_CORE);
+    importedSources.add(coreLibrarySource);
+    //
+    // Compute kind.
+    //
+    SourceKind sourceKind = SourceKind.LIBRARY;
+    if (!hasNonPartOfDirective && hasPartOfDirective) {
+      sourceKind = SourceKind.PART;
+    }
+    //
+    // Record outputs.
+    //
+    outputs[EXPORTED_LIBRARIES] = exportedSources.toList();
+    outputs[IMPORTED_LIBRARIES] = importedSources.toList();
+    outputs[INCLUDED_PARTS] = includedSources.toList();
+    outputs[PARSE_ERRORS] = errorListener.getErrorsForSource(source);
+    outputs[PARSED_UNIT] = unit;
+    outputs[SOURCE_KIND] = sourceKind;
+    outputs[UNITS] = <Source>[source]..addAll(includedSources);
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the given
+   * [source].
+   */
+  static Map<String, TaskInput> buildInputs(Source source) {
+    return <String, TaskInput>{
+      LINE_INFO_INPUT_NAME: LINE_INFO.of(source),
+      TOKEN_STREAM_INPUT_NAME: TOKEN_STREAM.of(source)
+    };
+  }
+
+  /**
+   * Create a [ParseDartTask] based on the given [target] in the given
+   * [context].
+   */
+  static ParseDartTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new ParseDartTask(context, target);
+  }
+
+  /**
+   * Return the result of resolving the URI of the given URI-based [directive]
+   * against the URI of the given library, or `null` if the URI is not valid.
+   *
+   * Resolution is to be performed in the given [context]. Errors should be
+   * reported to the [errorListener].
+   */
+  static Source resolveDirective(AnalysisContext context, Source librarySource,
+      UriBasedDirective directive, AnalysisErrorListener errorListener) {
+    StringLiteral uriLiteral = directive.uri;
+    String uriContent = uriLiteral.stringValue;
+    if (uriContent != null) {
+      uriContent = uriContent.trim();
+      directive.uriContent = uriContent;
+    }
+    UriValidationCode code = directive.validate();
+    if (code == null) {
+      String encodedUriContent = Uri.encodeFull(uriContent);
+      Source source =
+          context.sourceFactory.resolveUri(librarySource, encodedUriContent);
+      directive.source = source;
+      return source;
+    }
+    if (code == UriValidationCode.URI_WITH_DART_EXT_SCHEME) {
+      return null;
+    }
+    if (code == UriValidationCode.URI_WITH_INTERPOLATION) {
+      errorListener.onError(new AnalysisError.con2(librarySource,
+          uriLiteral.offset, uriLiteral.length,
+          CompileTimeErrorCode.URI_WITH_INTERPOLATION));
+      return null;
+    }
+    if (code == UriValidationCode.INVALID_URI) {
+      errorListener.onError(new AnalysisError.con2(librarySource,
+          uriLiteral.offset, uriLiteral.length,
+          CompileTimeErrorCode.INVALID_URI, [uriContent]));
+      return null;
+    }
+    throw new AnalysisException('Failed to handle validation code: $code');
+  }
+}
+
+/**
+ * The helper for building the public [Namespace] of a [LibraryElement].
+ */
+class PublicNamespaceBuilder {
+  final HashMap<String, Element> definedNames = new HashMap<String, Element>();
+
+  /**
+   * Build a public [Namespace] of the given [library].
+   */
+  Namespace build(LibraryElement library) {
+    definedNames.clear();
+    _addPublicNames(library.definingCompilationUnit);
+    library.parts.forEach(_addPublicNames);
+    return new Namespace(definedNames);
+  }
+
+  /**
+   * Add the given [element] if it has a publicly visible name.
+   */
+  void _addIfPublic(Element element) {
+    String name = element.name;
+    if (name != null && !Scope.isPrivateName(name)) {
+      definedNames[name] = element;
+    }
+  }
+
+  /**
+   * Add all of the public top-level names that are defined in the given
+   * [compilationUnit].
+   */
+  void _addPublicNames(CompilationUnitElement compilationUnit) {
+    compilationUnit.accessors.forEach(_addIfPublic);
+    compilationUnit.enums.forEach(_addIfPublic);
+    compilationUnit.functions.forEach(_addIfPublic);
+    compilationUnit.functionTypeAliases.forEach(_addIfPublic);
+    compilationUnit.types.forEach(_addIfPublic);
+  }
+}
+
+/**
+ * An artifitial task that does nothing except to force type names resolution
+ * for the defining and part units of a library.
+ */
+class ResolveLibraryTypeNamesTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the [LIBRARY_ELEMENT4] input.
+   */
+  static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'ResolveLibraryTypeNamesTask', createTask, buildInputs,
+      <ResultDescriptor>[LIBRARY_ELEMENT5]);
+
+  ResolveLibraryTypeNamesTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    LibraryElement library = getRequiredInput(LIBRARY_INPUT);
+    outputs[LIBRARY_ELEMENT5] = library;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given [target].
+   */
+  static Map<String, TaskInput> buildInputs(Source libSource) {
+    return <String, TaskInput>{
+      LIBRARY_INPUT: LIBRARY_ELEMENT4.of(libSource),
+      'resolvedUnits': IMPORT_SOURCE_CLOSURE
+          .of(libSource)
+          .toMapOf(UNITS)
+          .toFlattenList((Source library, Source unit) =>
+              RESOLVED_UNIT4.of(new LibrarySpecificUnit(library, unit)))
+    };
+  }
+
+  /**
+   * Create a [ResolveLibraryTypeNamesTask] based on the given [target] in
+   * the given [context].
+   */
+  static ResolveLibraryTypeNamesTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new ResolveLibraryTypeNamesTask(context, target);
+  }
+}
+
+/**
+ * A task that builds [RESOLVED_UNIT] for a unit.
+ */
+class ResolveReferencesTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the [LIBRARY_ELEMENT] input.
+   */
+  static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
+
+  /**
+   * The name of the [RESOLVED_UNIT5] input.
+   */
+  static const String UNIT_INPUT = 'UNIT_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'ResolveReferencesTask', createTask, buildInputs,
+      <ResultDescriptor>[RESOLVED_UNIT]);
+
+  ResolveReferencesTask(InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    //
+    // Prepare inputs.
+    //
+    LibraryElement libraryElement = getRequiredInput(LIBRARY_INPUT);
+    CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+    CompilationUnitElement unitElement = unit.element;
+    TypeProvider typeProvider = unitElement.context.typeProvider;
+    //
+    // Resolve references.
+    //
+    InheritanceManager inheritanceManager =
+        new InheritanceManager(libraryElement);
+    AstVisitor visitor = new ResolverVisitor.con2(libraryElement,
+        unitElement.source, typeProvider, inheritanceManager, errorListener);
+    unit.accept(visitor);
+    //
+    // Record outputs.
+    //
+    outputs[RESOLVE_REFERENCES_ERRORS] = errorListener.errors;
+    outputs[RESOLVED_UNIT] = unit;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given [target].
+   */
+  static Map<String, TaskInput> buildInputs(LibrarySpecificUnit target) {
+    return <String, TaskInput>{
+      LIBRARY_INPUT: LIBRARY_ELEMENT.of(target.library),
+      UNIT_INPUT: RESOLVED_UNIT5.of(target)
+    };
+  }
+
+  /**
+   * Create a [ResolveReferencesTask] based on the given [target] in
+   * the given [context].
+   */
+  static ResolveReferencesTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new ResolveReferencesTask(context, target);
+  }
+}
+
+/**
+ * A task that builds [RESOLVED_UNIT4] for a unit.
+ */
+class ResolveUnitTypeNamesTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the [RESOLVED_UNIT3] input.
+   */
+  static const String UNIT_INPUT = 'UNIT_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'ResolveUnitTypeNamesTask', createTask, buildInputs, <ResultDescriptor>[
+    RESOLVE_TYPE_NAMES_ERRORS,
+    RESOLVED_UNIT4
+  ]);
+
+  ResolveUnitTypeNamesTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    //
+    // Prepare inputs.
+    //
+    CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+    CompilationUnitElement unitElement = unit.element;
+    //
+    // Resolve TypeName nodes.
+    //
+    TypeResolverVisitor visitor = new TypeResolverVisitor.con2(
+        unitElement.library, unitElement.source, context.typeProvider,
+        errorListener);
+    unit.accept(visitor);
+    //
+    // Record outputs.
+    //
+    outputs[RESOLVE_TYPE_NAMES_ERRORS] = errorListener.errors;
+    outputs[RESOLVED_UNIT4] = unit;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given [target].
+   */
+  static Map<String, TaskInput> buildInputs(LibrarySpecificUnit target) {
+    return <String, TaskInput>{UNIT_INPUT: RESOLVED_UNIT3.of(target)};
+  }
+
+  /**
+   * Create a [ResolveUnitTypeNamesTask] based on the given [target] in
+   * the given [context].
+   */
+  static ResolveUnitTypeNamesTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new ResolveUnitTypeNamesTask(context, target);
+  }
+}
+
+/**
+ * A task that builds [RESOLVED_UNIT5] for a unit.
+ */
+class ResolveVariableReferencesTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the [LIBRARY_ELEMENT] input.
+   */
+  static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
+
+  /**
+   * The name of the [RESOLVED_UNIT4] input.
+   */
+  static const String UNIT_INPUT = 'UNIT_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'ResolveVariableReferencesTask', createTask, buildInputs,
+      <ResultDescriptor>[RESOLVED_UNIT5]);
+
+  ResolveVariableReferencesTask(
+      InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    //
+    // Prepare inputs.
+    //
+    LibraryElement libraryElement = getRequiredInput(LIBRARY_INPUT);
+    CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+    CompilationUnitElement unitElement = unit.element;
+    //
+    // Resolve local variables.
+    //
+    TypeProvider typeProvider = unitElement.context.typeProvider;
+    Scope nameScope = new LibraryScope(libraryElement, errorListener);
+    AstVisitor visitor = new VariableResolverVisitor.con2(libraryElement,
+        unitElement.source, typeProvider, nameScope, errorListener);
+    unit.accept(visitor);
+    //
+    // Record outputs.
+    //
+    outputs[RESOLVED_UNIT5] = unit;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given [target].
+   */
+  static Map<String, TaskInput> buildInputs(LibrarySpecificUnit target) {
+    return <String, TaskInput>{
+      LIBRARY_INPUT: LIBRARY_ELEMENT.of(target.library),
+      UNIT_INPUT: RESOLVED_UNIT4.of(target)
+    };
+  }
+
+  /**
+   * Create a [ResolveVariableReferencesTask] based on the given [target] in
+   * the given [context].
+   */
+  static ResolveVariableReferencesTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new ResolveVariableReferencesTask(context, target);
+  }
+}
+
+/**
+ * A task that scans the content of a file, producing a set of Dart tokens.
+ */
+class ScanDartTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the input whose value is the content of the file.
+   */
+  static const String CONTENT_INPUT_NAME = 'CONTENT_INPUT_NAME';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor('ScanDartTask',
+      createTask, buildInputs, <ResultDescriptor>[
+    LINE_INFO,
+    SCAN_ERRORS,
+    TOKEN_STREAM
+  ]);
+
+  /**
+   * Initialize a newly created task to access the content of the source
+   * associated with the given [target] in the given [context].
+   */
+  ScanDartTask(InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    Source source = getRequiredSource();
+    String content = getRequiredInput(CONTENT_INPUT_NAME);
+
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    Scanner scanner =
+        new Scanner(source, new CharSequenceReader(content), errorListener);
+    scanner.preserveComments = context.analysisOptions.preserveComments;
+    scanner.enableNullAwareOperators =
+        context.analysisOptions.enableNullAwareOperators;
+    outputs[TOKEN_STREAM] = scanner.tokenize();
+    outputs[LINE_INFO] = new LineInfo(scanner.lineStarts);
+    outputs[SCAN_ERRORS] = errorListener.getErrorsForSource(source);
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the given
+   * [source].
+   */
+  static Map<String, TaskInput> buildInputs(Source source) {
+    return <String, TaskInput>{CONTENT_INPUT_NAME: CONTENT.of(source)};
+  }
+
+  /**
+   * Create a [ScanDartTask] based on the given [target] in the given [context].
+   */
+  static ScanDartTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new ScanDartTask(context, target);
+  }
+}
+
+/**
+ * A task that builds [VERIFY_ERRORS] for a unit.
+ */
+class VerifyUnitTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the [RESOLVED_UNIT] input.
+   */
+  static const String UNIT_INPUT = 'UNIT_INPUT';
+
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor('VerifyUnitTask',
+      createTask, buildInputs, <ResultDescriptor>[VERIFY_ERRORS]);
+
+  /**
+   * The [ErrorReporter] to report errors to.
+   */
+  ErrorReporter errorReporter;
+
+  VerifyUnitTask(InternalAnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    Source source = getRequiredSource();
+    errorReporter = new ErrorReporter(errorListener, source);
+    TypeProvider typeProvider = context.typeProvider;
+    //
+    // Prepare inputs.
+    //
+    CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+    CompilationUnitElement unitElement = unit.element;
+    LibraryElement libraryElement = unitElement.library;
+    //
+    // Use the ErrorVerifier to compute errors.
+    //
+    ErrorVerifier errorVerifier = new ErrorVerifier(errorReporter,
+        libraryElement, typeProvider, new InheritanceManager(libraryElement));
+    unit.accept(errorVerifier);
+    //
+    // Record outputs.
+    //
+    outputs[VERIFY_ERRORS] = errorListener.errors;
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the
+   * given [target].
+   */
+  static Map<String, TaskInput> buildInputs(LibrarySpecificUnit target) {
+    return <String, TaskInput>{UNIT_INPUT: RESOLVED_UNIT.of(target)};
+  }
+
+  /**
+   * Create a [VerifyUnitTask] based on the given [target] in
+   * the given [context].
+   */
+  static VerifyUnitTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new VerifyUnitTask(context, target);
+  }
+}
+
+/**
+ * A [TaskInput] whose value is a list of library sources exported directly
+ * or indirectly by the target [Source].
+ */
+class _ExportSourceClosureTaskInput implements TaskInput<List<Source>> {
+  final Source target;
+
+  _ExportSourceClosureTaskInput(this.target);
+
+  @override
+  TaskInputBuilder<List<Source>> createBuilder() =>
+      new _SourceClosureTaskInputBuilder(target, _SourceClosureKind.EXPORT);
+}
+
+/**
+ * The kind of the source closure to build.
+ */
+enum _SourceClosureKind { IMPORT, EXPORT }
+
+/**
+ * A [TaskInput] whose value is a list of library sources imported directly
+ * or indirectly by the target [Source].
+ */
+class _ImportSourceClosureTaskInput implements TaskInput<List<Source>> {
+  final Source target;
+
+  _ImportSourceClosureTaskInput(this.target);
+
+  @override
+  TaskInputBuilder<List<Source>> createBuilder() =>
+      new _SourceClosureTaskInputBuilder(target, _SourceClosureKind.IMPORT);
+}
+
+/**
+ * A [TaskInputBuilder] to build values for [_ImportSourceClosureTaskInput].
+ */
+class _SourceClosureTaskInputBuilder implements TaskInputBuilder<List<Source>> {
+  final _SourceClosureKind kind;
+  final Set<LibraryElement> _libraries = new HashSet<LibraryElement>();
+  final Set<Source> _newSources = new HashSet<Source>();
+
+  Source currentTarget;
+
+  _SourceClosureTaskInputBuilder(Source librarySource, this.kind) {
+    _newSources.add(librarySource);
+  }
+
+  @override
+  ResultDescriptor get currentResult => LIBRARY_ELEMENT2;
+
+  @override
+  void set currentValue(LibraryElement library) {
+    if (_libraries.add(library)) {
+      if (kind == _SourceClosureKind.IMPORT) {
+        for (ImportElement importElement in library.imports) {
+          Source importedSource = importElement.importedLibrary.source;
+          _newSources.add(importedSource);
+        }
+      } else {
+        for (ExportElement exportElement in library.exports) {
+          Source importedSource = exportElement.exportedLibrary.source;
+          _newSources.add(importedSource);
+        }
+      }
+    }
+  }
+
+  @override
+  List<Source> get inputValue {
+    return _libraries.map((LibraryElement library) => library.source).toList();
+  }
+
+  @override
+  bool moveNext() {
+    if (_newSources.isEmpty) {
+      return false;
+    }
+    currentTarget = _newSources.first;
+    _newSources.remove(currentTarget);
+    return true;
+  }
+}
diff --git a/analyzer/lib/src/task/driver.dart b/analyzer/lib/src/task/driver.dart
new file mode 100644
index 0000000..8f9641b
--- /dev/null
+++ b/analyzer/lib/src/task/driver.dart
@@ -0,0 +1,468 @@
+// Copyright (c) 2015, 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 analyzer.src.task.driver;
+
+import 'dart:async';
+import 'dart:collection';
+
+import 'package:analyzer/src/context/cache.dart';
+import 'package:analyzer/src/generated/engine.dart' hide AnalysisTask;
+import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/task/inputs.dart';
+import 'package:analyzer/src/task/manager.dart';
+import 'package:analyzer/task/model.dart';
+
+/**
+ * An object that is used to cause analysis to be performed until all of the
+ * required analysis information has been computed.
+ */
+class AnalysisDriver {
+  /**
+   * The task manager used to figure out how to compute analysis results.
+   */
+  final TaskManager taskManager;
+
+  /**
+   * The context in which analysis is to be performed.
+   */
+  final InternalAnalysisContext context;
+
+  /**
+   * The work order that was previously computed but that has not yet been
+   * completed.
+   */
+  WorkOrder currentWorkOrder;
+
+  /**
+   * The controller that is notified when a task is started.
+   */
+  StreamController<AnalysisTask> _onTaskStartedController;
+
+  /**
+   * The controller that is notified when a task is complete.
+   */
+  StreamController<AnalysisTask> _onTaskCompletedController;
+
+  /**
+   * Initialize a newly created driver to use the tasks know to the given
+   * [taskManager] to perform analysis in the given [context].
+   */
+  AnalysisDriver(this.taskManager, this.context) {
+    _onTaskStartedController = new StreamController.broadcast();
+    _onTaskCompletedController = new StreamController.broadcast();
+  }
+
+  /**
+   * The stream that is notified when a task is complete.
+   */
+  Stream<AnalysisTask> get onTaskCompleted => _onTaskCompletedController.stream;
+
+  /**
+   * The stream that is notified when a task is started.
+   */
+  Stream<AnalysisTask> get onTaskStarted => _onTaskStartedController.stream;
+
+  /**
+   * Perform work until the given [result] has been computed for the given
+   * [target]. Return the last [AnalysisTask] that was performed.
+   */
+  AnalysisTask computeResult(AnalysisTarget target, ResultDescriptor result) {
+    AnalysisTask task;
+    WorkOrder workOrder = createWorkOrderForResult(target, result);
+    if (workOrder != null) {
+      while (workOrder.moveNext()) {
+        task = performWorkItem(workOrder.current);
+      }
+    }
+    return task;
+  }
+
+  /**
+   * Return the work order describing the work that should be getting worked on,
+   * or `null` if there is currently no work to be done.
+   */
+  WorkOrder createNextWorkOrder() {
+    //
+    // TODO(brianwilkerson) This is an inefficient implementation. We need to
+    // port over the concept of the WorkManager to manage the list of sources
+    // for which some work needs to be performed so that we do not waste time
+    // repeatedly looking at the same completed sources to see whether there is
+    // work that needs to be done.
+    //
+    for (AnalysisTarget target in context.priorityTargets) {
+      WorkOrder workOrder = createWorkOrderForTarget(target, true);
+      if (workOrder != null) {
+        return workOrder;
+      }
+    }
+    // TODO(brianwilkerson) Add a third priority, corresponding to
+    // AnalysisContextImpl._pendingFutureSources to support code completion.
+    for (AnalysisTarget target in context.explicitTargets) {
+      WorkOrder workOrder = createWorkOrderForTarget(target, false);
+      if (workOrder != null) {
+        return workOrder;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Create a work order that will produce the given [result] for the given
+   * [target]. Return the work order that was created, or `null` if the result
+   * has already been computed.
+   */
+  WorkOrder createWorkOrderForResult(
+      AnalysisTarget target, ResultDescriptor result) {
+    CacheEntry entry = context.getCacheEntry(target);
+    CacheState state = entry.getState(result);
+    if (state == CacheState.VALID ||
+        state == CacheState.ERROR ||
+        state == CacheState.IN_PROCESS) {
+      return null;
+    }
+    try {
+      return new WorkOrder(taskManager,
+          new WorkItem(context, target, taskManager.findTask(target, result)));
+    } catch (exception, stackTrace) {
+      throw new AnalysisException(
+          'Could not create work order (target = $target; result = $result)',
+          new CaughtException(exception, stackTrace));
+    }
+  }
+
+  /**
+   * Create a work order that will produce the required analysis results for
+   * the given [target]. If [isPriority] is true, then the target is a priority
+   * target. Return the work order that was created, or `null` if there is no
+   * further work that needs to be done for the given target.
+   */
+  WorkOrder createWorkOrderForTarget(AnalysisTarget target, bool isPriority) {
+    for (ResultDescriptor result in taskManager.generalResults) {
+      WorkOrder workOrder = createWorkOrderForResult(target, result);
+      if (workOrder != null) {
+        return workOrder;
+      }
+    }
+    if (isPriority) {
+      for (ResultDescriptor result in taskManager.priorityResults) {
+        WorkOrder workOrder = createWorkOrderForResult(target, result);
+        if (workOrder != null) {
+          return workOrder;
+        }
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Perform the next analysis task, and return `true` if there is more work to
+   * be done in order to compute all of the required analysis information.
+   */
+  bool performAnalysisTask() {
+    //
+    // TODO(brianwilkerson) This implementaiton does not allow us to prioritize
+    // work across contexts. What we need is a way for an external client to ask
+    // to have all priority files analyzed for each context, then ask for normal
+    // files to be analyzed. There are a couple of ways to do this.
+    //
+    // First, we could add a "bool priorityOnly" parameter to this method and
+    // return null here when it is true.
+    //
+    // Second, we could add a concept of a priority order and (externally) run
+    // through the priorities from highest to lowest. That would be a nice
+    // generalization of the previous idea, but it isn't clear that we need the
+    // generality.
+    //
+    // Third, we could move performAnalysisTask and createNextWorkOrder to an
+    // object that knows about all sources in all contexts, so that instead of
+    // the client choosing a context and telling it do to some work, the client
+    // simply says "do some work", and the engine chooses the best thing to do
+    // next regardless of what context it's in.
+    //
+    if (currentWorkOrder == null) {
+      currentWorkOrder = createNextWorkOrder();
+    } else if (currentWorkOrder.moveNext()) {
+      performWorkItem(currentWorkOrder.current);
+    } else {
+      currentWorkOrder = createNextWorkOrder();
+    }
+    return currentWorkOrder != null;
+  }
+
+  /**
+   * Perform the given work item.
+   * Return the performed [AnalysisTask].
+   */
+  AnalysisTask performWorkItem(WorkItem item) {
+    if (item.exception != null) {
+      // Mark all of the results that the task would have computed as being in
+      // ERROR with the exception recorded on the work item.
+      CacheEntry targetEntry = context.getCacheEntry(item.target);
+      targetEntry.setErrorState(item.exception, item.descriptor.results);
+      return null;
+    }
+    // Otherwise, perform the task.
+    AnalysisTask task = item.buildTask();
+    _onTaskStartedController.add(task);
+    task.perform();
+    CacheEntry entry = context.getCacheEntry(task.target);
+    if (task.caughtException == null) {
+      Map<ResultDescriptor, dynamic> outputs = task.outputs;
+      for (ResultDescriptor result in task.descriptor.results) {
+        // TODO(brianwilkerson) We could check here that a value was produced
+        // and throw an exception if not (unless we want to allow null values).
+        entry.setValue(result, outputs[result]);
+      }
+    } else {
+      entry.setErrorState(task.caughtException, item.descriptor.results);
+    }
+    _onTaskCompletedController.add(task);
+    return task;
+  }
+
+  /**
+   * Reset the state of the driver in response to a change in the state of one
+   * or more analysis targets. This will cause any analysis that was currently
+   * in process to be stopped and for analysis to resume based on the new state.
+   */
+  void reset() {
+    currentWorkOrder = null;
+  }
+}
+
+/**
+ * A place to define the behaviors that need to be added to
+ * [InternalAnalysisContext].
+ */
+abstract class ExtendedAnalysisContext implements InternalAnalysisContext {
+  List<AnalysisTarget> get explicitTargets;
+  List<AnalysisTarget> get priorityTargets;
+  void set typeProvider(TypeProvider typeProvider);
+  CacheEntry getCacheEntry(AnalysisTarget target);
+}
+
+/**
+ * An exception indicating that an attempt was made to perform a task on a
+ * target while gathering the inputs to perform the same task for the same
+ * target.
+ */
+class InfiniteTaskLoopException extends AnalysisException {
+  /**
+   * Initialize a newly created exception to represent an attempt to perform
+   * the task for the target represented by the given [item].
+   */
+  InfiniteTaskLoopException(WorkItem item) : super(
+          'Infinite loop while performing task ${item.descriptor.name} for ${item.target}');
+}
+
+/**
+ * A description of a single anaysis task that can be performed to advance
+ * analysis.
+ */
+class WorkItem {
+  /**
+   * The context in which the task will be performed.
+   */
+  final InternalAnalysisContext context;
+
+  /**
+   * The target for which a task is to be performed.
+   */
+  final AnalysisTarget target;
+
+  /**
+   * A description of the task to be performed.
+   */
+  final TaskDescriptor descriptor;
+
+  /**
+   * An iterator used to iterate over the descriptors of the inputs to the task,
+   * or `null` if all of the inputs have been collected and the task can be
+   * created.
+   */
+  TaskInputBuilder builder;
+
+  /**
+   * The inputs to the task that have been computed.
+   */
+  Map<String, dynamic> inputs;
+
+  /**
+   * The exception that was found while trying to populate the inputs. If this
+   * field is non-`null`, then the task cannot be performed and all of the
+   * results that this task would have computed need to be marked as being in
+   * ERROR with this exception.
+   */
+  CaughtException exception = null;
+
+  /**
+   * Initialize a newly created work item to compute the inputs for the task
+   * described by the given descriptor.
+   */
+  WorkItem(this.context, this.target, this.descriptor) {
+    AnalysisTarget actualTarget = identical(
+            target, AnalysisContextTarget.request)
+        ? new AnalysisContextTarget(context)
+        : target;
+    Map<String, TaskInput> inputDescriptors =
+        descriptor.createTaskInputs(actualTarget);
+    builder = new TopLevelTaskInputBuilder(inputDescriptors);
+    if (!builder.moveNext()) {
+      builder = null;
+    }
+    inputs = new HashMap<String, dynamic>();
+  }
+
+  /**
+   * Build the task represented by this work item.
+   */
+  AnalysisTask buildTask() {
+    if (builder != null) {
+      throw new StateError("some inputs have not been computed");
+    }
+    return descriptor.createTask(context, target, inputs);
+  }
+
+  /**
+   * Gather all of the inputs needed to perform the task.
+   *
+   * If at least one of the inputs have not yet been computed, return a work
+   * item that can be used to generate that input to indicate that the caller
+   * should perform the returned item's task before returning to gathering
+   * inputs for this item's task.
+   *
+   * If all of the inputs have been gathered, return `null` to indicate that the
+   * client should build and perform the task. A value of `null` will also be
+   * returned if some of the inputs cannot be computed and the task cannot be
+   * performed. Callers can differentiate between these cases by checking the
+   * [exception] field. If the field is `null`, then the task can be performed;
+   * if the field is non-`null` then the task cannot be performed and all of the
+   * tasks' results should be marked as being in ERROR.
+   */
+  WorkItem gatherInputs(TaskManager taskManager) {
+    while (builder != null) {
+      //
+      // TODO(brianwilkerson) Capture information about which inputs were used
+      // to compute the results. This information can later be used to compute
+      // which results depend on a given result, and hence which results need to
+      // be invalidated when one result is invalidated.
+      //
+      AnalysisTarget inputTarget = builder.currentTarget;
+      ResultDescriptor inputResult = builder.currentResult;
+      CacheEntry inputEntry = context.getCacheEntry(inputTarget);
+      CacheState inputState = inputEntry.getState(inputResult);
+      if (inputState == CacheState.ERROR) {
+        exception = inputEntry.exception;
+        return null;
+      } else if (inputState == CacheState.IN_PROCESS) {
+        //
+        // TODO(brianwilkerson) Implement this case.
+        //
+        // One possibility would be to return a WorkItem that would perform a
+        // no-op task in order to cause us to come back to this work item on the
+        // next iteration. It would be more efficient, in general, to push this
+        // input onto a waiting list and proceed to the next input so that work
+        // could proceed, but given that the only result that can currently be
+        // IN_PROCESS is CONTENT, I don't know that it's worth the extra effort
+        // to implement the general solution at this point.
+        //
+      } else if (inputState != CacheState.VALID) {
+        try {
+          TaskDescriptor descriptor =
+              taskManager.findTask(inputTarget, inputResult);
+          return new WorkItem(context, inputTarget, descriptor);
+        } on AnalysisException catch (exception, stackTrace) {
+          this.exception = new CaughtException(exception, stackTrace);
+          return null;
+        }
+      }
+      builder.currentValue = inputEntry.getValue(inputResult);
+      if (!builder.moveNext()) {
+        inputs = builder.inputValue;
+        builder = null;
+      }
+    }
+    return null;
+  }
+
+  @override
+  String toString() => 'Run $descriptor on $target';
+}
+
+/**
+ * A description of the work to be done to compute a desired analysis result.
+ * The class implements a lazy depth-first traversal of the work item's input.
+ */
+class WorkOrder implements Iterator<WorkItem> {
+  /**
+   * The task manager used to build work items.
+   */
+  final TaskManager taskManager;
+
+  /**
+   * A list containing the work items that are being prepared for being worked.
+   */
+  final List<WorkItem> pendingItems = <WorkItem>[];
+
+  /**
+   * The current work item.
+   */
+  WorkItem currentItem;
+
+  /**
+   * Initialize a newly created work order to compute the result described by
+   * the given work item.
+   */
+  WorkOrder(this.taskManager, WorkItem item) {
+    pendingItems.add(item);
+  }
+
+  @override
+  WorkItem get current {
+    return currentItem;
+  }
+
+  @override
+  bool moveNext() {
+    if (pendingItems.isEmpty) {
+      currentItem = null;
+      return false;
+    }
+    currentItem = pendingItems.removeLast();
+    WorkItem childItem = currentItem.gatherInputs(taskManager);
+    while (childItem != null) {
+      pendingItems.add(currentItem);
+      currentItem = childItem;
+      if (_hasInfiniteTaskLoop()) {
+        currentItem = pendingItems.removeLast();
+        try {
+          throw new InfiniteTaskLoopException(childItem);
+        } on InfiniteTaskLoopException catch (exception, stackTrace) {
+          currentItem.exception = new CaughtException(exception, stackTrace);
+        }
+        return true;
+      }
+      childItem = currentItem.gatherInputs(taskManager);
+    }
+    return true;
+  }
+
+  /**
+   * Check to see whether the current work item is attempting to perform the
+   * same task on the same target as any of the pending work items. If it is,
+   * then throw an [InfiniteTaskLoopException].
+   */
+  bool _hasInfiniteTaskLoop() {
+    TaskDescriptor descriptor = currentItem.descriptor;
+    AnalysisTarget target = currentItem.target;
+    for (WorkItem item in pendingItems) {
+      if (item.descriptor == descriptor && item.target == target) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/analyzer/lib/src/task/general.dart b/analyzer/lib/src/task/general.dart
new file mode 100644
index 0000000..69a808e
--- /dev/null
+++ b/analyzer/lib/src/task/general.dart
@@ -0,0 +1,82 @@
+// Copyright (c) 2015, 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 analyzer.src.task.general;
+
+import 'package:analyzer/src/generated/engine.dart' hide AnalysisTask;
+import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/task/general.dart';
+import 'package:analyzer/task/model.dart';
+
+/**
+ * A task that gets the contents of the source associated with an analysis
+ * target.
+ */
+class GetContentTask extends SourceBasedAnalysisTask {
+  /**
+   * The task descriptor describing this kind of task.
+   */
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor('GetContentTask',
+      createTask, buildInputs, <ResultDescriptor>[CONTENT, MODIFICATION_TIME]);
+
+  /**
+   * Initialize a newly created task to access the content of the source
+   * associated with the given [target] in the given [context].
+   */
+  GetContentTask(AnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  internalPerform() {
+    Source source = getRequiredSource();
+    try {
+      TimestampedData<String> data = context.getContents(source);
+      outputs[CONTENT] = data.data;
+      outputs[MODIFICATION_TIME] = data.modificationTime;
+    } catch (exception, stackTrace) {
+      throw new AnalysisException('Could not get contents of $source',
+          new CaughtException(exception, stackTrace));
+    }
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the given [target].
+   */
+  static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
+    return <String, TaskInput>{};
+  }
+
+  /**
+   * Create a [GetContentTask] based on the given [target] in the given
+   * [context].
+   */
+  static GetContentTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new GetContentTask(context, target);
+  }
+}
+
+/**
+ * A base class for analysis tasks whose target is expected to be a source.
+ */
+abstract class SourceBasedAnalysisTask extends AnalysisTask {
+  /**
+   * Initialize a newly created task to perform analysis within the given
+   * [context] in order to produce results for the given [target].
+   */
+  SourceBasedAnalysisTask(AnalysisContext context, AnalysisTarget target)
+      : super(context, target);
+
+  @override
+  String get description {
+    Source source = target.source;
+    String sourceName = source == null ? '<unknown source>' : source.fullName;
+    return '${descriptor.name} for source $sourceName';
+  }
+}
diff --git a/analyzer/lib/src/task/inputs.dart b/analyzer/lib/src/task/inputs.dart
new file mode 100644
index 0000000..27b48c5
--- /dev/null
+++ b/analyzer/lib/src/task/inputs.dart
@@ -0,0 +1,645 @@
+// Copyright (c) 2015, 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 analyzer.src.task.inputs;
+
+import 'dart:collection';
+
+import 'package:analyzer/task/model.dart';
+
+/**
+ * A function that converts an object of the type [B] into a [TaskInput].
+ * This is used, for example, by a [ListToListTaskInput] to create task inputs
+ * for each value in a list of values.
+ */
+typedef TaskInput<E> GenerateTaskInputs<B, E>(B object);
+
+/**
+ * An input to an [AnalysisTask] that is computed by accessing a single result
+ * defined on a single target.
+ */
+class ListTaskInputImpl<E> extends SimpleTaskInput<List<E>>
+    with ListTaskInputMixin<E> implements ListTaskInput<E> {
+  /**
+   * Initialize a newly created task input that computes the input by accessing
+   * the given [result] associated with the given [target].
+   */
+  ListTaskInputImpl(AnalysisTarget target, ResultDescriptor<List<E>> result)
+      : super(target, result);
+}
+
+/**
+ * A mixin-ready implementation of [ListTaskInput].
+ */
+abstract class ListTaskInputMixin<E> implements ListTaskInput<E> {
+  ListTaskInput /*<V>*/ toList(UnaryFunction<E, dynamic /*<V>*/ > mapper) {
+    return new ListToListTaskInput<E, dynamic /*V*/ >(this, mapper);
+  }
+
+  ListTaskInput /*<V>*/ toListOf(ResultDescriptor /*<V>*/ valueResult) {
+    return (this as ListTaskInputImpl<AnalysisTarget>).toList(valueResult.of);
+  }
+
+  MapTaskInput<E, dynamic /*V*/ > toMap(
+      UnaryFunction<E, dynamic /*<V>*/ > mapper) {
+    return new ListToMapTaskInput<E, dynamic /*V*/ >(this, mapper);
+  }
+
+  MapTaskInput<AnalysisTarget, dynamic /*V*/ > toMapOf(
+      ResultDescriptor /*<V>*/ valueResult) {
+    return (this as ListTaskInputImpl<AnalysisTarget>).toMap(valueResult.of);
+  }
+}
+
+/**
+ * An input to an [AnalysisTask] that is computed by the following steps. First
+ * another (base) task input is used to compute a [List]-valued result. An input
+ * generator function is then used to map each element of that list to a task
+ * input. Finally, each of the task inputs are used to access analysis results,
+ * and the list of the analysis results is used as the input to the task.
+ */
+class ListToListTaskInput<B, E>
+    extends _ListToCollectionTaskInput<B, E, List<E>>
+    with ListTaskInputMixin<E> {
+  /**
+   * Initialize a result accessor to use the given [baseAccessor] to access a
+   * list of values that can be passed to the given [generateTaskInputs] to
+   * generate a list of task inputs that can be used to access the elements of
+   * the input being accessed.
+   */
+  ListToListTaskInput(TaskInput<List<B>> baseAccessor,
+      GenerateTaskInputs<B, E> generateTaskInputs)
+      : super(baseAccessor, generateTaskInputs);
+
+  @override
+  TaskInputBuilder<List<E>> createBuilder() =>
+      new ListToListTaskInputBuilder<B, E>(this);
+}
+
+/**
+ * A [TaskInputBuilder] used to build an input based on a [ListToListTaskInput].
+ */
+class ListToListTaskInputBuilder<B, E>
+    extends _ListToCollectionTaskInputBuilder<B, E, List<E>> {
+  /**
+   * The list of values being built.
+   */
+  List<E> _resultValue;
+
+  /**
+   * Initialize a newly created task input builder that computes the result
+   * specified by the given [input].
+   */
+  ListToListTaskInputBuilder(ListToListTaskInput<B, E> input) : super(input);
+
+  @override
+  void _addResultElement(B baseElement, E resultElement) {
+    _resultValue.add(resultElement);
+  }
+
+  @override
+  void _initResultValue() {
+    _resultValue = <E>[];
+  }
+}
+
+/**
+ * An input to an [AnalysisTask] that is computed by the following steps. First
+ * another (base) task input is used to compute a [List]-valued result. An input
+ * generator function is then used to map each element of that list to a task
+ * input. Finally, each of the task inputs are used to access analysis results,
+ * and the map of the base elements to the analysis results is used as the
+ * input to the task.
+ */
+class ListToMapTaskInput<B, E>
+    extends _ListToCollectionTaskInput<B, E, Map<B, E>>
+    with MapTaskInputMixin<B, E> {
+  /**
+   * Initialize a result accessor to use the given [baseAccessor] to access a
+   * list of values that can be passed to the given [generateTaskInputs] to
+   * generate a list of task inputs that can be used to access the elements of
+   * the input being accessed.
+   */
+  ListToMapTaskInput(TaskInput<List<B>> baseAccessor,
+      GenerateTaskInputs<B, E> generateTaskInputs)
+      : super(baseAccessor, generateTaskInputs);
+
+  @override
+  TaskInputBuilder<Map<B, E>> createBuilder() =>
+      new ListToMapTaskInputBuilder<B, E>(this);
+}
+
+/**
+ * A [TaskInputBuilder] used to build an input based on a [ListToMapTaskInput].
+ */
+class ListToMapTaskInputBuilder<B, E>
+    extends _ListToCollectionTaskInputBuilder<B, E, Map<B, E>> {
+  /**
+   * The map being built.
+   */
+  Map<B, E> _resultValue;
+
+  /**
+   * Initialize a newly created task input builder that computes the result
+   * specified by the given [input].
+   */
+  ListToMapTaskInputBuilder(ListToMapTaskInput<B, E> input) : super(input);
+
+  @override
+  void _addResultElement(B baseElement, E resultElement) {
+    _resultValue[baseElement] = resultElement;
+  }
+
+  @override
+  void _initResultValue() {
+    _resultValue = new HashMap<B, E>();
+  }
+}
+
+/**
+ * A mixin-ready implementation of [MapTaskInput].
+ */
+abstract class MapTaskInputMixin<K, V> implements MapTaskInput<K, V> {
+  TaskInput<List /*<E>*/ > toFlattenList(
+      BinaryFunction<K, dynamic /*element of V*/, dynamic /*<E>*/ > mapper) {
+    return new MapToFlattenListTaskInput<K, dynamic /*element of V*/, dynamic /*E*/ >(
+        this as MapTaskInput<K, List /*<element of V>*/ >, mapper);
+  }
+}
+
+/**
+ * A [TaskInput] that is computed by the following steps.
+ *
+ * First the [base] task input is used to compute a [Map]-valued result.
+ * The values of the [Map] must be [List]s.
+ *
+ * The given [mapper] is used to transform each key / value pair of the [Map]
+ * into task inputs.
+ *
+ * Finally, each of the task inputs are used to access analysis results,
+ * and the list of the results is used as the input.
+ */
+class MapToFlattenListTaskInput<K, V, E> implements TaskInput<List<E>> {
+  final MapTaskInput<K, List<V>> base;
+  final BinaryFunction<K, V, E> mapper;
+
+  MapToFlattenListTaskInput(this.base, this.mapper);
+
+  @override
+  TaskInputBuilder<List> createBuilder() {
+    return new MapToFlattenListTaskInputBuilder<K, V, E>(base, mapper);
+  }
+}
+
+/**
+ * The [TaskInputBuilder] for [MapToFlattenListTaskInput].
+ */
+class MapToFlattenListTaskInputBuilder<K, V, E>
+    implements TaskInputBuilder<List<E>> {
+  final MapTaskInput<K, List<V>> base;
+  final BinaryFunction<K, V, E> mapper;
+
+  TaskInputBuilder currentBuilder;
+  Map<K, List<V>> baseMap;
+  Iterator<K> keyIterator;
+  Iterator<V> valueIterator;
+
+  final List<E> inputValue = <E>[];
+
+  MapToFlattenListTaskInputBuilder(this.base, this.mapper) {
+    currentBuilder = base.createBuilder();
+  }
+
+  @override
+  ResultDescriptor get currentResult {
+    if (currentBuilder == null) {
+      return null;
+    }
+    return currentBuilder.currentResult;
+  }
+
+  AnalysisTarget get currentTarget {
+    if (currentBuilder == null) {
+      return null;
+    }
+    return currentBuilder.currentTarget;
+  }
+
+  @override
+  void set currentValue(Object value) {
+    if (currentBuilder == null) {
+      throw new StateError(
+          'Cannot set the result value when there is no current result');
+    }
+    currentBuilder.currentValue = value;
+  }
+
+  @override
+  bool moveNext() {
+    // Prepare base Map.
+    if (baseMap == null) {
+      if (currentBuilder.moveNext()) {
+        return true;
+      }
+      baseMap = currentBuilder.inputValue;
+      keyIterator = baseMap.keys.iterator;
+      // Done with this builder.
+      currentBuilder = null;
+    }
+    // Prepare the next result value.
+    if (currentBuilder != null) {
+      if (currentBuilder.moveNext()) {
+        return true;
+      }
+      // Add the result value for the current Map key/value.
+      E resultValue = currentBuilder.inputValue;
+      inputValue.add(resultValue);
+      // Done with this builder.
+      currentBuilder = null;
+    }
+    // Move to the next Map value.
+    if (valueIterator != null && valueIterator.moveNext()) {
+      K key = keyIterator.current;
+      V value = valueIterator.current;
+      currentBuilder = mapper(key, value).createBuilder();
+      return moveNext();
+    }
+    // Move to the next Map key.
+    if (keyIterator.moveNext()) {
+      K key = keyIterator.current;
+      valueIterator = baseMap[key].iterator;
+      return moveNext();
+    }
+    // No more Map values/keys to transform.
+    return false;
+  }
+}
+
+/**
+ * An input to an [AnalysisTask] that is computed by accessing a single result
+ * defined on a single target.
+ */
+class SimpleTaskInput<V> implements TaskInput<V> {
+  /**
+   * The target on which the result is defined.
+   */
+  final AnalysisTarget target;
+
+  /**
+   * The result to be accessed.
+   */
+  final ResultDescriptor<V> result;
+
+  /**
+   * Initialize a newly created task input that computes the input by accessing
+   * the given [result] associated with the given [target].
+   */
+  SimpleTaskInput(this.target, this.result);
+
+  @override
+  TaskInputBuilder<V> createBuilder() => new SimpleTaskInputBuilder<V>(this);
+}
+
+/**
+ * A [TaskInputBuilder] used to build an input based on a [SimpleTaskInput].
+ */
+class SimpleTaskInputBuilder<V> implements TaskInputBuilder<V> {
+  /**
+   * The state value indicating that the builder is positioned before the single
+   * result.
+   */
+  static const _BEFORE = -1;
+
+  /**
+   * The state value indicating that the builder is positioned at the single
+   * result.
+   */
+  static const _AT = 0;
+
+  /**
+   * The state value indicating that the builder is positioned after the single
+   * result.
+   */
+  static const _AFTER = 1;
+
+  /**
+   * The input being built.
+   */
+  final SimpleTaskInput<V> input;
+
+  /**
+   * The value of the input being built.
+   */
+  V _resultValue = null;
+
+  /**
+   * The state of the builder.
+   */
+  int _state = _BEFORE;
+
+  /**
+   * A flag indicating whether the result value was explicitly set.
+   */
+  bool _resultSet = false;
+
+  /**
+   * Initialize a newly created task input builder that computes the result
+   * specified by the given [input].
+   */
+  SimpleTaskInputBuilder(this.input);
+
+  @override
+  ResultDescriptor get currentResult => _state == _AT ? input.result : null;
+
+  @override
+  AnalysisTarget get currentTarget => _state == _AT ? input.target : null;
+
+  @override
+  void set currentValue(Object value) {
+    if (_state != _AT) {
+      throw new StateError(
+          'Cannot set the result value when there is no current result');
+    }
+    _resultValue = value as V;
+    _resultSet = true;
+  }
+
+  @override
+  V get inputValue {
+    if (_state != _AFTER) {
+      throw new StateError('Result value has not been created');
+    }
+    return _resultValue;
+  }
+
+  @override
+  bool moveNext() {
+    if (_state == _BEFORE) {
+      _state = _AT;
+      return true;
+    } else {
+      if (!_resultSet) {
+        throw new StateError(
+            'The value of the current result must be set before moving to the next result.');
+      }
+      _state = _AFTER;
+      return false;
+    }
+  }
+}
+
+/**
+ * A [TaskInputBuilder] used to build an input based on one or more other task
+ * inputs. The task inputs to be built are specified by a table mapping the name
+ * of the input to the task used to access the input's value.
+ */
+class TopLevelTaskInputBuilder
+    implements TaskInputBuilder<Map<String, Object>> {
+  /**
+   * The descriptors describing the inputs to be built.
+   */
+  final Map<String, TaskInput> inputDescriptors;
+
+  /**
+   * The names of the inputs. There are the keys from the [inputDescriptors] in
+   * an indexable form.
+   */
+  List<String> inputNames;
+
+  /**
+   * The index of the input name associated with the current result and target.
+   */
+  int nameIndex = -1;
+
+  /**
+   * The builder used to build the current result.
+   */
+  TaskInputBuilder currentBuilder;
+
+  /**
+   * The inputs that are being or have been built. The map will be incomplete
+   * unless the method [moveNext] returns `false`.
+   */
+  final Map<String, Object> inputs = new HashMap<String, Object>();
+
+  /**
+   * Initialize a newly created task input builder to build the inputs described
+   * by the given [inputDescriptors].
+   */
+  TopLevelTaskInputBuilder(this.inputDescriptors) {
+    inputNames = inputDescriptors.keys.toList();
+  }
+
+  @override
+  ResultDescriptor get currentResult {
+    if (currentBuilder == null) {
+      return null;
+    }
+    return currentBuilder.currentResult;
+  }
+
+  @override
+  AnalysisTarget get currentTarget {
+    if (currentBuilder == null) {
+      return null;
+    }
+    return currentBuilder.currentTarget;
+  }
+
+  @override
+  void set currentValue(Object value) {
+    if (currentBuilder == null) {
+      throw new StateError(
+          'Cannot set the result value when there is no current result');
+    }
+    currentBuilder.currentValue = value;
+  }
+
+  @override
+  Map<String, Object> get inputValue {
+    if (nameIndex < inputNames.length) {
+      throw new StateError('Result value has not been created');
+    }
+    return inputs;
+  }
+
+  /**
+   * Assuming that there is a current input, return its name.
+   */
+  String get _currentName => inputNames[nameIndex];
+
+  @override
+  bool moveNext() {
+    if (nameIndex >= inputNames.length) {
+      // We have already computed all of the results, so just return false.
+      return false;
+    }
+    if (nameIndex < 0) {
+      // This is the first time moveNext has been invoked, so we just determine
+      // whether there are any results to be computed.
+      nameIndex = 0;
+    } else {
+      if (currentBuilder.moveNext()) {
+        // We are still working on building the value associated with the
+        // current name.
+        return true;
+      }
+      inputs[_currentName] = currentBuilder.inputValue;
+      nameIndex++;
+    }
+    if (nameIndex >= inputNames.length) {
+      // There is no next value, so we're done.
+      return false;
+    }
+    currentBuilder = inputDescriptors[_currentName].createBuilder();
+    // NOTE: This assumes that every builder will require at least one result
+    // value to be created. If that assumption is every broken, this method will
+    // need to be changed to advance until we find a builder that does require
+    // a result to be computed (or run out of builders).
+    return currentBuilder.moveNext();
+  }
+}
+
+/**
+ * An input to an [AnalysisTask] that is computed by the following steps. First
+ * another (base) task input is used to compute a [List]-valued result. An input
+ * generator function is then used to map each element of that list to a task
+ * input. Finally, each of the task inputs are used to access analysis results,
+ * and a collection of the analysis results is used as the input to the task.
+ */
+abstract class _ListToCollectionTaskInput<B, E, C> implements TaskInput<C> {
+  /**
+   * The accessor used to access the list of elements being mapped.
+   */
+  final TaskInput<List<B>> baseAccessor;
+
+  /**
+   * The function used to convert an element in the list returned by the
+   * [baseAccessor] to a task input.
+   */
+  final GenerateTaskInputs<B, E> generateTaskInputs;
+
+  /**
+   * Initialize a result accessor to use the given [baseAccessor] to access a
+   * list of values that can be passed to the given [generateTaskInputs] to
+   * generate a list of task inputs that can be used to access the elements of
+   * the input being accessed.
+   */
+  _ListToCollectionTaskInput(this.baseAccessor, this.generateTaskInputs);
+}
+
+/**
+ * A [TaskInputBuilder] used to build an [_ListToCollectionTaskInput].
+ */
+abstract class _ListToCollectionTaskInputBuilder<B, E, C>
+    implements TaskInputBuilder<C> {
+  /**
+   * The input being built.
+   */
+  final _ListToCollectionTaskInput<B, E, C> input;
+
+  /**
+   * The builder used to build the current result.
+   */
+  TaskInputBuilder currentBuilder;
+
+  /**
+   * The list of values computed by the [input]'s base accessor.
+   */
+  List<B> _baseList = null;
+
+  /**
+   * The index in the [_baseList] of the value for which a value is currently
+   * being built.
+   */
+  int _baseListIndex = -1;
+
+  /**
+   * The element of the [_baseList] for which a value is currently being built.
+   */
+  B _baseListElement;
+
+  /**
+   * Initialize a newly created task input builder that computes the result
+   * specified by the given [input].
+   */
+  _ListToCollectionTaskInputBuilder(this.input);
+
+  @override
+  ResultDescriptor get currentResult {
+    if (currentBuilder == null) {
+      return null;
+    }
+    return currentBuilder.currentResult;
+  }
+
+  @override
+  AnalysisTarget get currentTarget {
+    if (currentBuilder == null) {
+      return null;
+    }
+    return currentBuilder.currentTarget;
+  }
+
+  @override
+  void set currentValue(Object value) {
+    if (currentBuilder == null) {
+      throw new StateError(
+          'Cannot set the result value when there is no current result');
+    }
+    currentBuilder.currentValue = value;
+  }
+
+  @override
+  C get inputValue {
+    if (currentBuilder != null || _resultValue == null) {
+      throw new StateError('Result value has not been created');
+    }
+    return _resultValue;
+  }
+
+  /**
+   * The list of values being built.
+   */
+  C get _resultValue;
+
+  @override
+  bool moveNext() {
+    if (currentBuilder == null) {
+      if (_resultValue == null) {
+        // This is the first time moveNext has been invoked, so start by
+        // computing the list of values from which the results will be derived.
+        currentBuilder = input.baseAccessor.createBuilder();
+        return currentBuilder.moveNext();
+      } else {
+        // We have already computed all of the results, so just return false.
+        return false;
+      }
+    }
+    if (currentBuilder.moveNext()) {
+      return true;
+    }
+    if (_resultValue == null) {
+      // We have finished computing the list of values from which the results
+      // will be derived.
+      _baseList = currentBuilder.inputValue;
+      _baseListIndex = 0;
+      _initResultValue();
+    } else {
+      // We have finished computing one of the elements in the result list.
+      _addResultElement(_baseListElement, currentBuilder.inputValue);
+      _baseListIndex++;
+    }
+    if (_baseListIndex >= _baseList.length) {
+      currentBuilder = null;
+      return false;
+    }
+    _baseListElement = _baseList[_baseListIndex];
+    currentBuilder = input.generateTaskInputs(_baseListElement).createBuilder();
+    return currentBuilder.moveNext();
+  }
+
+  void _addResultElement(B baseElement, E resultElement);
+  void _initResultValue();
+}
diff --git a/analyzer/lib/src/task/manager.dart b/analyzer/lib/src/task/manager.dart
new file mode 100644
index 0000000..eebc084
--- /dev/null
+++ b/analyzer/lib/src/task/manager.dart
@@ -0,0 +1,114 @@
+// Copyright (c) 2015, 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 analyzer.src.task.manager;
+
+import 'dart:collection';
+
+import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/task/model.dart';
+
+/**
+ * An object that manages the information about the tasks that have been
+ * defined.
+ */
+class TaskManager {
+  /**
+   * A table mapping [ResultDescriptor]s to a list of [TaskDescriptor]s
+   * for the tasks that can be used to compute the result.
+   */
+  Map<ResultDescriptor, List<TaskDescriptor>> taskMap =
+      new HashMap<ResultDescriptor, List<TaskDescriptor>>();
+
+  /**
+   * A list of the results that are to be computed for all sources within an
+   * analysis root.
+   */
+  Set<ResultDescriptor> generalResults = new Set<ResultDescriptor>();
+
+  /**
+   * A list of the results that are to be computed for priority sources.
+   */
+  Set<ResultDescriptor> priorityResults = new Set<ResultDescriptor>();
+
+  /**
+   * Add the given [result] to the list of results that are to be computed for
+   * all sources within an analysis root.
+   */
+  void addGeneralResult(ResultDescriptor result) {
+    generalResults.add(result);
+  }
+
+  /**
+   * Add the given [result] to the list of results that are to be computed for
+   * priority sources.
+   */
+  void addPriorityResult(ResultDescriptor result) {
+    priorityResults.add(result);
+  }
+
+  /**
+   * Add the given [descriptor] to the list of analysis task descriptors that
+   * can be used to compute analysis results.
+   */
+  void addTaskDescriptor(TaskDescriptor descriptor) {
+    descriptor.results.forEach((ResultDescriptor result) {
+      //
+      // Add the result to the task map.
+      //
+      List<TaskDescriptor> descriptors = taskMap[result];
+      if (descriptors == null) {
+        descriptors = <TaskDescriptor>[];
+        taskMap[result] = descriptors;
+      }
+      descriptors.add(descriptor);
+    });
+  }
+
+  /**
+   * Add the task descriptors in the given list of [descriptors] to the list of
+   * analysis task descriptors that can be used to compute analysis results.
+   */
+  void addTaskDescriptors(List<TaskDescriptor> descriptors) {
+    descriptors.forEach(addTaskDescriptor);
+  }
+
+  /**
+   * Find a task that will compute the given [result] for the given [target].
+   */
+  TaskDescriptor findTask(AnalysisTarget target, ResultDescriptor result) {
+    List<TaskDescriptor> descriptors = taskMap[result];
+    if (descriptors == null) {
+      throw new AnalysisException(
+          'No tasks registered to compute $result for $target');
+    }
+    return _findBestTask(descriptors);
+  }
+
+  /**
+   * Remove the given [result] from the list of results that are to be computed
+   * for all sources within an analysis root.
+   */
+  void removeGeneralResult(ResultDescriptor result) {
+    generalResults.remove(result);
+  }
+
+  /**
+   * Remove the given [result] from the list of results that are to be computed
+   * for priority sources.
+   */
+  void removePriorityResult(ResultDescriptor result) {
+    priorityResults.remove(result);
+  }
+
+  /**
+   * Given a list of task [descriptors] that can be used to compute some
+   * unspecified result, return the descriptor that will compute the result with
+   * the least amount of work.
+   */
+  TaskDescriptor _findBestTask(List<TaskDescriptor> descriptors) {
+    // TODO(brianwilkerson) Improve this implementation.
+    return descriptors[0];
+  }
+}
diff --git a/analyzer/lib/src/task/model.dart b/analyzer/lib/src/task/model.dart
new file mode 100644
index 0000000..66ffb9e
--- /dev/null
+++ b/analyzer/lib/src/task/model.dart
@@ -0,0 +1,132 @@
+// Copyright (c) 2015, 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 analyzer.src.task.model;
+
+import 'package:analyzer/src/generated/engine.dart' hide AnalysisTask;
+import 'package:analyzer/src/task/inputs.dart';
+import 'package:analyzer/task/model.dart';
+
+/**
+ * A concrete implementation of a [CompositeResultDescriptor].
+ */
+class CompositeResultDescriptorImpl<V> extends ResultDescriptorImpl<V>
+    implements CompositeResultDescriptor<V> {
+  /**
+   * The results that contribute to this result.
+   */
+  final List<ResultDescriptor<V>> contributors = <ResultDescriptor<V>>[];
+
+  /**
+   * Initialize a newly created composite result to have the given [name].
+   */
+  CompositeResultDescriptorImpl(String name) : super(name, null);
+
+  /**
+   * Record that the given analysis [result] contibutes to this result.
+   */
+  void recordContributor(ResultDescriptor<V> result) {
+    contributors.add(result);
+  }
+}
+
+/**
+ * A concrete implementation of a [ListResultDescriptor].
+ */
+class ListResultDescriptorImpl<E> extends ResultDescriptorImpl<List<E>>
+    implements ListResultDescriptor<E> {
+  /**
+   * Initialize a newly created analysis result to have the given [name] and
+   * [defaultValue]. If a composite result is specified, then this result will
+   * contribute to it.
+   */
+  ListResultDescriptorImpl(String name, List<E> defaultValue,
+      {CompositeResultDescriptor contributesTo})
+      : super(name, defaultValue, contributesTo: contributesTo);
+
+  @override
+  ListTaskInput<E> of(AnalysisTarget target) =>
+      new ListTaskInputImpl<E>(target, this);
+}
+
+/**
+ * A concrete implementation of a [ResultDescriptor].
+ */
+class ResultDescriptorImpl<V> implements ResultDescriptor<V> {
+  /**
+   * The name of the result, used for debugging.
+   */
+  final String name;
+
+  /**
+   * Return the default value for results described by this descriptor.
+   */
+  final V defaultValue;
+
+  /**
+   * Initialize a newly created analysis result to have the given [name] and
+   * [defaultValue]. If a composite result is specified, then this result will
+   * contribute to it.
+   */
+  ResultDescriptorImpl(this.name, this.defaultValue,
+      {CompositeResultDescriptor contributesTo}) {
+    if (contributesTo is CompositeResultDescriptorImpl) {
+      contributesTo.recordContributor(this);
+    }
+  }
+
+  @override
+  TaskInput<V> of(AnalysisTarget target) =>
+      new SimpleTaskInput<V>(target, this);
+
+  @override
+  String toString() => name;
+}
+
+/**
+ * A concrete implementation of a [TaskDescriptor].
+ */
+class TaskDescriptorImpl implements TaskDescriptor {
+  /**
+   * The name of the described task, used for debugging.
+   */
+  final String name;
+
+  /**
+   * The function used to build the analysis task.
+   */
+  final BuildTask buildTask;
+
+  /**
+   * The function used to build the inputs to the task.
+   */
+  @override
+  final CreateTaskInputs createTaskInputs;
+
+  /**
+   * A list of the analysis results that will be computed by the described task.
+   */
+  @override
+  final List<ResultDescriptor> results;
+
+  /**
+   * Initialize a newly created task descriptor to have the given [name] and to
+   * describe a task that takes the inputs built using the given [createTaskInputs],
+   * and produces the given [results]. The [buildTask] will be used to create
+   * the instance of [AnalysisTask] thusly described.
+   */
+  TaskDescriptorImpl(
+      this.name, this.buildTask, this.createTaskInputs, this.results);
+
+  @override
+  AnalysisTask createTask(AnalysisContext context, AnalysisTarget target,
+      Map<String, dynamic> inputs) {
+    AnalysisTask task = buildTask(context, target);
+    task.inputs = inputs;
+    return task;
+  }
+
+  @override
+  String toString() => name;
+}
diff --git a/analyzer/lib/src/task/task_dart.dart b/analyzer/lib/src/task/task_dart.dart
new file mode 100644
index 0000000..5a3491b
--- /dev/null
+++ b/analyzer/lib/src/task/task_dart.dart
@@ -0,0 +1,87 @@
+// 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 engine.task.dart;
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/source.dart';
+
+/**
+ * A `BuildUnitElementTask` builds a compilation unit element for a single
+ * compilation unit.
+ */
+class BuildUnitElementTask extends AnalysisTask {
+  /**
+   * The source for which an element model will be built.
+   */
+  final Source source;
+
+  /**
+   * The source of the library in which an element model will be built.
+   */
+  final Source library;
+
+  /**
+   * The compilation unit from which an element model will be built.
+   */
+  final CompilationUnit unit;
+
+  /**
+   * The element model that was built.
+   */
+  CompilationUnitElement unitElement;
+
+  /**
+   * Initialize a newly created task to build a compilation unit element for
+   * the given [source] in the given [library] based on the compilation [unit]
+   * that was parsed.
+   */
+  BuildUnitElementTask(
+      InternalAnalysisContext context, this.source, this.library, this.unit)
+      : super(context);
+
+  @override
+  String get taskDescription {
+    if (source == null) {
+      return "build the unit element model for null source";
+    }
+    return "build the unit element model for " + source.fullName;
+  }
+
+  @override
+  accept(AnalysisTaskVisitor visitor) {
+    return visitor.visitBuildUnitElementTask(this);
+  }
+
+  /**
+   * Return the compilation unit from which the element model was built.
+   */
+  CompilationUnit getCompilationUnit() {
+    return unit;
+  }
+
+  /**
+   * Return the source that is to be parsed.
+   */
+  Source getSource() {
+    return source;
+  }
+
+  /**
+   * Return the compilation unit element that was produced, or `null` if the
+   * task has not yet been performed or if an exception occurred.
+   */
+  CompilationUnitElement getUnitElement() {
+    return unitElement;
+  }
+
+  @override
+  void internalPerform() {
+    CompilationUnitBuilder builder = new CompilationUnitBuilder();
+    unitElement = builder.buildCompilationUnit(source, unit);
+  }
+}
diff --git a/analyzer/lib/src/util/asserts.dart b/analyzer/lib/src/util/asserts.dart
new file mode 100644
index 0000000..a53e7e6
--- /dev/null
+++ b/analyzer/lib/src/util/asserts.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2015, 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 engine.utilities.asserts;
+
+/**
+ * Ensures that the given [value] is not null.
+ * Otherwise throws an [ArgumentError].
+ * An optional [description] is used in the error message.
+ */
+void notNull(Object value, [String description]) {
+  if (value == null) {
+    if (description == null) {
+      throw new ArgumentError('Must not be null');
+    } else {
+      throw new ArgumentError('Must not be null: $description');
+    }
+  }
+}
diff --git a/analyzer/lib/src/util/lru_map.dart b/analyzer/lib/src/util/lru_map.dart
new file mode 100644
index 0000000..38ef8ff
--- /dev/null
+++ b/analyzer/lib/src/util/lru_map.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2015, 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 engine.utilities.lru_cache;
+
+import 'dart:collection';
+
+/**
+ * This handler is notified when an item is evicted from the cache.
+ */
+typedef EvictionHandler<K, V>(K key, V value);
+
+/**
+ * A hash-table based cache implementation.
+ *
+ * When it reaches the specified number of items, the item that has not been
+ * accessed (both get and put) recently is evicted.
+ */
+class LRUMap<K, V> {
+  final LinkedHashMap<K, V> _map = new LinkedHashMap<K, V>();
+  final int _maxSize;
+  final EvictionHandler _handler;
+
+  LRUMap(this._maxSize, [this._handler]);
+
+  /**
+   * Returns the value for the given [key] or null if [key] is not
+   * in the cache.
+   */
+  V get(K key) {
+    V value = _map.remove(key);
+    if (value != null) {
+      _map[key] = value;
+    }
+    return value;
+  }
+
+  /**
+   * Associates the [key] with the given [value].
+   *
+   * If the cache is full, an item that has not been accessed recently is
+   * evicted.
+   */
+  void put(K key, V value) {
+    _map.remove(key);
+    _map[key] = value;
+    if (_map.length > _maxSize) {
+      K evictedKey = _map.keys.first;
+      V evictedValue = _map.remove(evictedKey);
+      if (_handler != null) {
+        _handler(evictedKey, evictedValue);
+      }
+    }
+  }
+
+  /**
+   * Removes the association for the given [key].
+   */
+  void remove(K key) {
+    _map.remove(key);
+  }
+}
diff --git a/analyzer/lib/src/util/utilities_timing.dart b/analyzer/lib/src/util/utilities_timing.dart
new file mode 100644
index 0000000..7b8e122
--- /dev/null
+++ b/analyzer/lib/src/util/utilities_timing.dart
@@ -0,0 +1,39 @@
+// 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 engine.utilities.timing;
+
+/**
+ * A `CountedStopwatch` is a [Stopwatch] that counts the number of times the
+ * stop method has been invoked.
+ */
+class CountedStopwatch extends Stopwatch {
+  /**
+   * The number of times the [stop] method has been invoked.
+   */
+  int stopCount = 0;
+
+  /**
+   * Initialize a newly created stopwatch.
+   */
+  CountedStopwatch();
+
+  /**
+   * The average number of millisecond that were recorded each time the [start]
+   * and [stop] methods were invoked.
+   */
+  int get averageMilliseconds => elapsedMilliseconds ~/ stopCount;
+
+  @override
+  void reset() {
+    super.reset();
+    stopCount = 0;
+  }
+
+  @override
+  void stop() {
+    super.stop();
+    stopCount++;
+  }
+}
diff --git a/analyzer/lib/task/dart.dart b/analyzer/lib/task/dart.dart
new file mode 100644
index 0000000..a4ac9ee
--- /dev/null
+++ b/analyzer/lib/task/dart.dart
@@ -0,0 +1,167 @@
+// Copyright (c) 2015, 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 analyzer.task.dart;
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/error.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/utilities_general.dart';
+import 'package:analyzer/task/model.dart';
+
+/**
+ * The analysis errors associated with a target.
+ *
+ * The value combines errors represented by multiple other results.
+ */
+// TODO(brianwilkerson) If we want to associate errors with targets smaller than
+// a file, we will need other contribution points to collect them. In which case
+// we might want to rename this and/or document that it applies to files.
+final CompositeResultDescriptor<List<AnalysisError>> DART_ERRORS =
+    new CompositeResultDescriptor<List<AnalysisError>>('DART_ERRORS');
+
+/**
+ * The sources of the libraries that are exported from a library.
+ *
+ * The list will be empty if there are no exported libraries, but will not be
+ * `null`.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ListResultDescriptor<Source> EXPORTED_LIBRARIES =
+    new ListResultDescriptor<Source>('EXPORTED_LIBRARIES', Source.EMPTY_ARRAY);
+
+/**
+ * The sources of the libraries that are imported into a library.
+ *
+ * Not `null`.
+ * The default value is empty.
+ * When computed, this list will always contain at least `dart:core` source.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ListResultDescriptor<Source> IMPORTED_LIBRARIES =
+    new ListResultDescriptor<Source>('IMPORTED_LIBRARIES', Source.EMPTY_ARRAY);
+
+/**
+ * The sources of the parts that are included in a library.
+ *
+ * The list will be empty if there are no parts, but will not be `null`. The
+ * list does *not* include the source for the defining compilation unit.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ListResultDescriptor<Source> INCLUDED_PARTS =
+    new ListResultDescriptor<Source>('INCLUDED_PARTS', Source.EMPTY_ARRAY);
+
+/**
+ * A flag specifying whether a library is dependent on code that is only
+ * available in a client.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ResultDescriptor<bool> IS_CLIENT =
+    new ResultDescriptor<bool>('IS_CLIENT', false);
+
+/**
+ * A flag specifying whether a library is launchable.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ResultDescriptor<bool> IS_LAUNCHABLE =
+    new ResultDescriptor<bool>('IS_LAUNCHABLE', false);
+
+/**
+ * The fully built [LibraryElement] associated with a library.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ResultDescriptor<LibraryElement> LIBRARY_ELEMENT =
+    new ResultDescriptor<LibraryElement>('LIBRARY_ELEMENT', null);
+
+/**
+ * The compilation unit AST produced while parsing a compilation unit.
+ *
+ * The AST structure will not have resolution information associated with it.
+ *
+ * The result is only available for targets representing a Dart compilation unit.
+ */
+final ResultDescriptor<CompilationUnit> PARSED_UNIT =
+    new ResultDescriptor<CompilationUnit>('PARSED_UNIT', null);
+
+/**
+ * The resolved [CompilationUnit] associated with a unit.
+ *
+ * The result is only available for targets representing a unit.
+ */
+final ResultDescriptor<CompilationUnit> RESOLVED_UNIT =
+    new ResultDescriptor<CompilationUnit>('RESOLVED_UNIT', null);
+
+/**
+ * The token stream produced while scanning a compilation unit.
+ *
+ * The value is the first token in the file, or the special end-of-file marker
+ * at the end of the stream if the file does not contain any tokens.
+ *
+ * The result is only available for targets representing a Dart compilation unit.
+ */
+final ResultDescriptor<Token> TOKEN_STREAM =
+    new ResultDescriptor<Token>('TOKEN_STREAM', null);
+
+/**
+ * The sources of the Dart files that a library consists of.
+ *
+ * The list will include the source of the defining unit and [INCLUDED_PARTS].
+ * So, it is never empty or `null`.
+ *
+ * The result is only available for targets representing a Dart library.
+ */
+final ListResultDescriptor<Source> UNITS =
+    new ListResultDescriptor<Source>('UNITS', Source.EMPTY_ARRAY);
+
+/**
+ * A specific compilation unit in a specific library.
+ *
+ * This kind of target is associated with information about a compilation unit
+ * that differs based on the library that the unit is a part of. For example,
+ * the result of resolving a compilation unit depends on the imports, which can
+ * change if a single part is included in more than one library.
+ */
+class LibrarySpecificUnit implements AnalysisTarget {
+  /**
+   * The defining compilation unit of the library in which the [unit]
+   * is analyzed.
+   */
+  final Source library;
+
+  /**
+   * The compilation unit which belongs to the [library].
+   */
+  final Source unit;
+
+  /**
+   * Initialize a newly created target for the [unit] in the [library].
+   */
+  LibrarySpecificUnit(this.library, this.unit);
+
+  @override
+  int get hashCode {
+    return JenkinsSmiHash.combine(library.hashCode, unit.hashCode);
+  }
+
+  @override
+  Source get source => unit;
+
+  @override
+  bool operator ==(other) {
+    return other is LibrarySpecificUnit &&
+        other.library == library &&
+        other.unit == unit;
+  }
+
+  @override
+  String toString() => '$unit in $library';
+}
diff --git a/analyzer/lib/task/general.dart b/analyzer/lib/task/general.dart
new file mode 100644
index 0000000..7cf1b97
--- /dev/null
+++ b/analyzer/lib/task/general.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2015, 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 analyzer.task.general;
+
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/task/model.dart';
+
+/**
+ * The contents of a single file.
+ */
+final ResultDescriptor<String> CONTENT =
+    new ResultDescriptor<String>('CONTENT', null);
+
+/**
+ * The line information for a single file.
+ */
+final ResultDescriptor<LineInfo> LINE_INFO =
+    new ResultDescriptor<LineInfo>('LINE_INFO', null);
+
+/**
+ * The modification time of a file.
+ */
+final ResultDescriptor<int> MODIFICATION_TIME =
+    new ResultDescriptor<int>('MODIFICATION_TIME', -1);
+
+/**
+ * The kind of a [Source].
+ */
+final ResultDescriptor<SourceKind> SOURCE_KIND =
+    new ResultDescriptor<SourceKind>('SOURCE_KIND', SourceKind.UNKNOWN);
diff --git a/analyzer/lib/task/model.dart b/analyzer/lib/task/model.dart
new file mode 100644
index 0000000..973f24e
--- /dev/null
+++ b/analyzer/lib/task/model.dart
@@ -0,0 +1,465 @@
+// Copyright (c) 2015, 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 analyzer.task.model;
+
+import 'dart:collection';
+
+import 'package:analyzer/src/generated/engine.dart' hide AnalysisTask;
+import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/task/model.dart';
+
+/**
+ * A function that converts the given [key] and [value] into a [TaskInput].
+ */
+typedef TaskInput<E> BinaryFunction<K, V, E>(K key, V value);
+
+/**
+ * A function that takes an analysis [context] and an analysis [target] and
+ * returns an analysis task. Such functions are passed to a [TaskDescriptor] to
+ * be used to create the described task.
+ */
+typedef AnalysisTask BuildTask(AnalysisContext context, AnalysisTarget target);
+
+/**
+ * A function that takes the target for which a task will produce results and
+ * returns a map from input names to descriptions of the analysis results needed
+ * by the task in order for the task to be performed. Such functions are passed
+ * to a [TaskDescriptor] to be used to determine the inputs needed by the task.
+ */
+typedef Map<String, TaskInput> CreateTaskInputs(AnalysisTarget target);
+
+/**
+ * A function that converts an object of the type [B] into a [TaskInput].
+ * This is used, for example, by a [ListTaskInput] to create task inputs
+ * for each value in a list of values.
+ */
+typedef TaskInput<E> UnaryFunction<B, E>(B object);
+
+/**
+ * An [AnalysisTarget] wrapper for an [AnalysisContext].
+ */
+class AnalysisContextTarget implements AnalysisTarget {
+  static final AnalysisContextTarget request = new AnalysisContextTarget(null);
+
+  final AnalysisContext context;
+
+  AnalysisContextTarget(this.context);
+
+  @override
+  Source get source => null;
+}
+
+/**
+ * An object with which an analysis result can be associated.
+ *
+ * Clients are allowed to subtype this class when creating new kinds of targets.
+ * Instances of this type are used in hashed data structures, so subtypes are
+ * required to correctly implement [==] and [hashCode].
+ */
+abstract class AnalysisTarget {
+  /**
+   * Return the source associated with this target, or `null` if this target is
+   * not associated with a source.
+   */
+  Source get source;
+}
+
+/**
+ * An object used to compute one or more analysis results associated with a
+ * single target.
+ *
+ * Clients are expected to extend this class when creating new tasks.
+ */
+abstract class AnalysisTask {
+  /**
+   * A table mapping the types of analysis tasks to the number of times each
+   * kind of task has been performed.
+   */
+  static final Map<Type, int> countMap = new HashMap<Type, int>();
+
+  /**
+   * A table mapping the types of analysis tasks to stopwatches used to compute
+   * how much time was spent executing each kind of task.
+   */
+  static final Map<Type, Stopwatch> stopwatchMap =
+      new HashMap<Type, Stopwatch>();
+
+  /**
+   * The context in which the task is to be performed.
+   */
+  final AnalysisContext context;
+
+  /**
+   * The target for which result values are being produced.
+   */
+  final AnalysisTarget target;
+
+  /**
+   * A table mapping input names to input values.
+   */
+  Map<String, dynamic> inputs;
+
+  /**
+   * A table mapping result descriptors whose values are produced by this task
+   * to the values that were produced.
+   */
+  Map<ResultDescriptor, dynamic> outputs =
+      new HashMap<ResultDescriptor, dynamic>();
+
+  /**
+   * The exception that was thrown while performing this task, or `null` if the
+   * task completed successfully.
+   */
+  CaughtException caughtException;
+
+  /**
+   * Initialize a newly created task to perform analysis within the given
+   * [context] in order to produce results for the given [target].
+   */
+  AnalysisTask(this.context, this.target);
+
+  /**
+   * Return a textual description of this task.
+   */
+  String get description;
+
+  /**
+   * Return the descriptor that describes this task.
+   */
+  TaskDescriptor get descriptor;
+
+  /**
+   * Return the value of the input with the given [name]. Throw an exception if
+   * the input value is not defined.
+   */
+  Object getRequiredInput(String name) {
+    if (inputs == null || !inputs.containsKey(name)) {
+      throw new AnalysisException("Could not $description: missing $name");
+    }
+    return inputs[name];
+  }
+
+  /**
+   * Return the source associated with the target. Throw an exception if
+   * the target is not associated with a source.
+   */
+  Source getRequiredSource() {
+    Source source = target.source;
+    if (source == null) {
+      throw new AnalysisException("Could not $description: missing source");
+    }
+    return source;
+  }
+
+  /**
+   * Perform this analysis task, protected by an exception handler.
+   *
+   * This method should throw an [AnalysisException] if an exception occurs
+   * while performing the task. If other kinds of exceptions are thrown they
+   * will be wrapped in an [AnalysisException].
+   *
+   * If no exception is thrown, this method must fully populate the [outputs]
+   * map (have a key/value pair for each result that this task is expected to
+   * produce).
+   */
+  void internalPerform();
+
+  /**
+   * Perform this analysis task. When this method returns, either the [outputs]
+   * map should be fully populated (have a key/value pair for each result that
+   * this task is expected to produce) or the [caughtException] should be set.
+   *
+   * Clients should not override this method.
+   */
+  void perform() {
+    try {
+      _safelyPerform();
+    } on AnalysisException catch (exception, stackTrace) {
+      caughtException = new CaughtException(exception, stackTrace);
+      AnalysisEngine.instance.logger.logInformation(
+          "Task failed: ${description}", caughtException);
+    }
+  }
+
+  @override
+  String toString() => description;
+
+  /**
+   * Perform this analysis task, ensuring that all exceptions are wrapped in an
+   * [AnalysisException].
+   *
+   * Clients should not override this method.
+   */
+  void _safelyPerform() {
+    try {
+      //
+      // Report that this task is being performed.
+      //
+      String contextName = context.name;
+      if (contextName == null) {
+        contextName = 'unnamed';
+      }
+      AnalysisEngine.instance.instrumentationService.logAnalysisTask(
+          contextName, description);
+      //
+      // Gather statistics on the performance of the task.
+      //
+      int count = countMap[runtimeType];
+      countMap[runtimeType] = count == null ? 1 : count + 1;
+      Stopwatch stopwatch = stopwatchMap[runtimeType];
+      if (stopwatch == null) {
+        stopwatch = new Stopwatch();
+        stopwatchMap[runtimeType] = stopwatch;
+      }
+      stopwatch.start();
+      //
+      // Actually perform the task.
+      //
+      try {
+        internalPerform();
+      } finally {
+        stopwatch.stop();
+      }
+    } on AnalysisException {
+      rethrow;
+    } catch (exception, stackTrace) {
+      throw new AnalysisException(
+          'Unexpected exception while performing $description',
+          new CaughtException(exception, stackTrace));
+    }
+  }
+}
+
+/**
+ * A [ResultDescriptor] that denotes an analysis result that is a union of
+ * one or more other results.
+ *
+ * Clients are not expected to subtype this class.
+ */
+abstract class CompositeResultDescriptor<V> extends ResultDescriptor<V> {
+  /**
+   * Initialize a newly created composite result to have the given [name].
+   */
+  factory CompositeResultDescriptor(
+      String name) = CompositeResultDescriptorImpl;
+
+  /**
+   * Return a list containing the descriptors of the results that are unioned
+   * together to compose the value of this result.
+   *
+   * Clients must not modify the returned list.
+   */
+  List<ResultDescriptor<V>> get contributors;
+}
+
+/**
+ * A description of a [List]-based analysis result that can be computed by an
+ * [AnalysisTask].
+ *
+ * Clients are not expected to subtype this class.
+ */
+abstract class ListResultDescriptor<E> implements ResultDescriptor<List<E>> {
+  /**
+   * Initialize a newly created analysis result to have the given [name]. If a
+   * composite result is specified, then this result will contribute to it.
+   */
+  factory ListResultDescriptor(String name, List<E> defaultValue,
+      {CompositeResultDescriptor<List<E>> contributesTo}) = ListResultDescriptorImpl<E>;
+
+  @override
+  ListTaskInput<E> of(AnalysisTarget target);
+}
+
+/**
+ * A description of an input to an [AnalysisTask] that can be used to compute
+ * that input.
+ *
+ * Clients are not expected to subtype this class.
+ */
+abstract class ListTaskInput<E> extends TaskInput<List<E>> {
+  /**
+   * Return a task input that can be used to compute a list whose elements are
+   * the result of passing the elements of this input to the [mapper] function.
+   */
+  ListTaskInput /*<V>*/ toList(UnaryFunction<E, dynamic /*<V>*/ > mapper);
+
+  /**
+   * Return a task input that can be used to compute a list whose elements are
+   * [valueResult]'s associated with those elements.
+   */
+  ListTaskInput /*<V>*/ toListOf(ResultDescriptor /*<V>*/ valueResult);
+
+  /**
+   * Return a task input that can be used to compute a map whose keys are the
+   * elements of this input and whose values are the result of passing the
+   * corresponding key to the [mapper] function.
+   */
+  MapTaskInput<E, dynamic /*V*/ > toMap(
+      UnaryFunction<E, dynamic /*<V>*/ > mapper);
+
+  /**
+   * Return a task input that can be used to compute a map whose keys are the
+   * elements of this input and whose values are the [valueResult]'s associated
+   * with those elements.
+   */
+  MapTaskInput<AnalysisTarget, dynamic /*V*/ > toMapOf(
+      ResultDescriptor /*<V>*/ valueResult);
+}
+
+/**
+ * A description of an input with a [Map] based values.
+ *
+ * Clients are not expected to subtype this class.
+ */
+abstract class MapTaskInput<K, V> extends TaskInput<Map<K, V>> {
+  /**
+   * [V] must be a [List].
+   * Return a task input that can be used to compute a list whose elements are
+   * the result of passing keys [K] and the corresponding elements of [V] to
+   * the [mapper] function.
+   */
+  TaskInput<List /*<E>*/ > toFlattenList(
+      BinaryFunction<K, dynamic /*element of V*/, dynamic /*<E>*/ > mapper);
+}
+
+/**
+ * A description of an analysis result that can be computed by an [AnalysisTask].
+ *
+ * Clients are not expected to subtype this class.
+ */
+abstract class ResultDescriptor<V> {
+  /**
+   * Initialize a newly created analysis result to have the given [name]. If a
+   * composite result is specified, then this result will contribute to it.
+   */
+  factory ResultDescriptor(String name, V defaultValue,
+      {CompositeResultDescriptor<V> contributesTo}) = ResultDescriptorImpl;
+
+  /**
+   * Return the default value for results described by this descriptor.
+   */
+  V get defaultValue;
+
+  /**
+   * Return the name of this descriptor.
+   */
+  String get name;
+
+  /**
+   * Return a task input that can be used to compute this result for the given
+   * [target].
+   */
+  TaskInput<V> of(AnalysisTarget target);
+}
+
+/**
+ * A description of an [AnalysisTask].
+ */
+abstract class TaskDescriptor {
+  /**
+   * Initialize a newly created task descriptor to have the given [name] and to
+   * describe a task that takes the inputs built using the given [inputBuilder],
+   * and produces the given [results]. The [buildTask] will be used to create
+   * the instance of [AnalysisTask] thusly described.
+   */
+  factory TaskDescriptor(String name, BuildTask buildTask,
+      CreateTaskInputs inputBuilder,
+      List<ResultDescriptor> results) = TaskDescriptorImpl;
+
+  /**
+   * Return the builder used to build the inputs to the task.
+   */
+  CreateTaskInputs get createTaskInputs;
+
+  /**
+   * Return the name of the task being described.
+   */
+  String get name;
+
+  /**
+   * Return a list of the analysis results that will be computed by this task.
+   */
+  List<ResultDescriptor> get results;
+
+  /**
+   * Create and return a task that is described by this descriptor that can be
+   * used to compute results based on the given [inputs].
+   */
+  AnalysisTask createTask(AnalysisContext context, AnalysisTarget target,
+      Map<String, dynamic> inputs);
+}
+
+/**
+ * A description of an input to an [AnalysisTask] that can be used to compute
+ * that input.
+ *
+ * Clients are not expected to subtype this class.
+ */
+abstract class TaskInput<V> {
+  /**
+   * Create and return a builder that can be used to build this task input.
+   */
+  TaskInputBuilder<V> createBuilder();
+}
+
+/**
+ * An object used to build the value associated with a single [TaskInput].
+ *
+ * All builders work by requesting one or more results (each result being
+ * associated with a target). The interaction pattern is modeled after the class
+ * [Iterator], in which the method [moveNext] is invoked to move from one result
+ * request to the next. The getters [currentResult] and [currentTarget] are used
+ * to get the result and target of the current request. The value of the result
+ * must be supplied using the [currentValue] setter before [moveNext] can be
+ * invoked to move to the next request. When [moveNext] returns `false`,
+ * indicating that there are no more requests, the method [inputValue] can be
+ * used to access the value of the input that was built.
+ *
+ * Clients are not expected to subtype this class.
+ */
+abstract class TaskInputBuilder<V> {
+  /**
+   * Return the result that needs to be computed, or `null` if [moveNext] has
+   * not been invoked or if the last invocation of [moveNext] returned `false`.
+   */
+  ResultDescriptor get currentResult;
+
+  /**
+   * Return the target for which the result needs to be computed, or `null` if
+   * [moveNext] has not been invoked or if the last invocation of [moveNext]
+   * returned `false`.
+   */
+  AnalysisTarget get currentTarget;
+
+  /**
+   * Set the [value] that was computed for the current result.
+   *
+   * Throws a [StateError] if [moveNext] has not been invoked or if the last
+   * invocation of [moveNext] returned `false`.
+   */
+  void set currentValue(Object value);
+
+  /**
+   * Return the [value] that was computed by this builder.
+   *
+   * Throws a [StateError] if [moveNext] has not been invoked or if the last
+   * invocation of [moveNext] returned `true`.
+   */
+  V get inputValue;
+
+  /**
+   * Move to the next result that needs to be computed in order to build the
+   * inputs for a task. Return `true` if there is another result that needs to
+   * be computed, or `false` if the inputs have been computed.
+   *
+   * It is safe to invoke [moveNext] after it has returned `false`. In this case
+   * [moveNext] has no effect and will again return `false`.
+   *
+   * Throws a [StateError] if the value of the current result has not been
+   * provided using [currentValue].
+   */
+  bool moveNext();
+}
diff --git a/analyzer/pubspec.yaml b/analyzer/pubspec.yaml
new file mode 100644
index 0000000..f562216
--- /dev/null
+++ b/analyzer/pubspec.yaml
@@ -0,0 +1,13 @@
+name: analyzer
+version: 0.25.0+1
+author: Dart Team <misc@dartlang.org>
+description: Static analyzer for Dart.
+homepage: http://www.dartlang.org
+environment:
+  sdk: '>=0.8.10+6 <2.0.0'
+dependencies:
+  path: '>=0.9.0 <2.0.0'
+  watcher: '>=0.9.0 <0.10.0'
+dev_dependencies:
+  typed_mock: '>=0.0.4 <1.0.0'
+  unittest: '>=0.9.0 <0.12.0'
diff --git a/args/lib/args.dart b/args/lib/args.dart
new file mode 100644
index 0000000..562df44
--- /dev/null
+++ b/args/lib/args.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2013, 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 args;
+
+export 'src/arg_parser.dart';
+export 'src/arg_results.dart' hide newArgResults;
+export 'src/option.dart' hide newOption;
diff --git a/args/lib/command_runner.dart b/args/lib/command_runner.dart
new file mode 100644
index 0000000..d728cb0
--- /dev/null
+++ b/args/lib/command_runner.dart
@@ -0,0 +1,382 @@
+// 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 args.command_runner;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:math' as math;
+
+import 'src/arg_parser.dart';
+import 'src/arg_results.dart';
+import 'src/help_command.dart';
+import 'src/usage_exception.dart';
+import 'src/utils.dart';
+
+export 'src/usage_exception.dart';
+
+/// A class for invoking [Commands] based on raw command-line arguments.
+class CommandRunner {
+  /// The name of the executable being run.
+  ///
+  /// Used for error reporting and [usage].
+  final String executableName;
+
+  /// A short description of this executable.
+  final String description;
+
+  /// A single-line template for how to invoke this executable.
+  ///
+  /// Defaults to "$executableName <command> [arguments]". Subclasses can
+  /// override this for a more specific template.
+  String get invocation => "$executableName <command> [arguments]";
+
+  /// Generates a string displaying usage information for the executable.
+  ///
+  /// This includes usage for the global arguments as well as a list of
+  /// top-level commands.
+  String get usage => "$description\n\n$_usageWithoutDescription";
+
+  /// An optional footer for [usage].
+  ///
+  /// If a subclass overrides this to return a string, it will automatically be
+  /// added to the end of [usage].
+  final String usageFooter = null;
+
+  /// Returns [usage] with [description] removed from the beginning.
+  String get _usageWithoutDescription {
+    var usage = '''
+Usage: $invocation
+
+Global options:
+${argParser.usage}
+
+${_getCommandUsage(_commands)}
+
+Run "$executableName help <command>" for more information about a command.''';
+
+    if (usageFooter != null) usage += "\n$usageFooter";
+    return usage;
+  }
+
+  /// An unmodifiable view of all top-level commands defined for this runner.
+  Map<String, Command> get commands => new UnmodifiableMapView(_commands);
+  final _commands = new Map<String, Command>();
+
+  /// The top-level argument parser.
+  ///
+  /// Global options should be registered with this parser; they'll end up
+  /// available via [Command.globalResults]. Commands should be registered with
+  /// [addCommand] rather than directly on the parser.
+  final argParser = new ArgParser();
+
+  CommandRunner(this.executableName, this.description) {
+    argParser.addFlag('help',
+        abbr: 'h', negatable: false, help: 'Print this usage information.');
+    addCommand(new HelpCommand());
+  }
+
+  /// Prints the usage information for this runner.
+  ///
+  /// This is called internally by [run] and can be overridden by subclasses to
+  /// control how output is displayed or integrate with a logging system.
+  void printUsage() => print(usage);
+
+  /// Throws a [UsageException] with [message].
+  void usageException(String message) =>
+      throw new UsageException(message, _usageWithoutDescription);
+
+  /// Adds [Command] as a top-level command to this runner.
+  void addCommand(Command command) {
+    var names = [command.name]..addAll(command.aliases);
+    for (var name in names) {
+      _commands[name] = command;
+      argParser.addCommand(name, command.argParser);
+    }
+    command._runner = this;
+  }
+
+  /// Parses [args] and invokes [Command.run] on the chosen command.
+  ///
+  /// This always returns a [Future] in case the command is asynchronous. The
+  /// [Future] will throw a [UsageError] if [args] was invalid.
+  Future run(Iterable<String> args) =>
+      new Future.sync(() => runCommand(parse(args)));
+
+  /// Parses [args] and returns the result, converting a [FormatException] to a
+  /// [UsageException].
+  ///
+  /// This is notionally a protected method. It may be overridden or called from
+  /// subclasses, but it shouldn't be called externally.
+  ArgResults parse(Iterable<String> args) {
+    try {
+      // TODO(nweiz): if arg parsing fails for a command, print that command's
+      // usage, not the global usage.
+      return argParser.parse(args);
+    } on FormatException catch (error) {
+      usageException(error.message);
+    }
+  }
+
+  /// Runs the command specified by [topLevelResults].
+  ///
+  /// This is notionally a protected method. It may be overridden or called from
+  /// subclasses, but it shouldn't be called externally.
+  ///
+  /// It's useful to override this to handle global flags and/or wrap the entire
+  /// command in a block. For example, you might handle the `--verbose` flag
+  /// here to enable verbose logging before running the command.
+  Future runCommand(ArgResults topLevelResults) {
+    return new Future.sync(() {
+      var argResults = topLevelResults;
+      var commands = _commands;
+      var command;
+      var commandString = executableName;
+
+      while (commands.isNotEmpty) {
+        if (argResults.command == null) {
+          if (argResults.rest.isEmpty) {
+            if (command == null) {
+              // No top-level command was chosen.
+              printUsage();
+              return new Future.value();
+            }
+
+            command.usageException('Missing subcommand for "$commandString".');
+          } else {
+            if (command == null) {
+              usageException(
+                  'Could not find a command named "${argResults.rest[0]}".');
+            }
+
+            command.usageException('Could not find a subcommand named '
+                '"${argResults.rest[0]}" for "$commandString".');
+          }
+        }
+
+        // Step into the command.
+        argResults = argResults.command;
+        command = commands[argResults.name];
+        command._globalResults = topLevelResults;
+        command._argResults = argResults;
+        commands = command._subcommands;
+        commandString += " ${argResults.name}";
+
+        if (argResults['help']) {
+          command.printUsage();
+          return new Future.value();
+        }
+      }
+
+      // Make sure there aren't unexpected arguments.
+      if (!command.takesArguments && argResults.rest.isNotEmpty) {
+        command.usageException(
+            'Command "${argResults.name}" does not take any arguments.');
+      }
+
+      return command.run();
+    });
+  }
+}
+
+/// A single command.
+///
+/// A command is known as a "leaf command" if it has no subcommands and is meant
+/// to be run. Leaf commands must override [run].
+///
+/// A command with subcommands is known as a "branch command" and cannot be run
+/// itself. It should call [addSubcommand] (often from the constructor) to
+/// register subcommands.
+abstract class Command {
+  /// The name of this command.
+  String get name;
+
+  /// A short description of this command.
+  String get description;
+
+  /// A single-line template for how to invoke this command (e.g. `"pub get
+  /// [package]"`).
+  String get invocation {
+    var parents = [name];
+    for (var command = parent; command != null; command = command.parent) {
+      parents.add(command.name);
+    }
+    parents.add(runner.executableName);
+
+    var invocation = parents.reversed.join(" ");
+    return _subcommands.isNotEmpty
+        ? "$invocation <subcommand> [arguments]"
+        : "$invocation [arguments]";
+  }
+
+  /// The command's parent command, if this is a subcommand.
+  ///
+  /// This will be `null` until [Command.addSubcommmand] has been called with
+  /// this command.
+  Command get parent => _parent;
+  Command _parent;
+
+  /// The command runner for this command.
+  ///
+  /// This will be `null` until [CommandRunner.addCommand] has been called with
+  /// this command or one of its parents.
+  CommandRunner get runner {
+    if (parent == null) return _runner;
+    return parent.runner;
+  }
+  CommandRunner _runner;
+
+  /// The parsed global argument results.
+  ///
+  /// This will be `null` until just before [Command.run] is called.
+  ArgResults get globalResults => _globalResults;
+  ArgResults _globalResults;
+
+  /// The parsed argument results for this command.
+  ///
+  /// This will be `null` until just before [Command.run] is called.
+  ArgResults get argResults => _argResults;
+  ArgResults _argResults;
+
+  /// The argument parser for this command.
+  ///
+  /// Options for this command should be registered with this parser (often in
+  /// the constructor); they'll end up available via [argResults]. Subcommands
+  /// should be registered with [addSubcommand] rather than directly on the
+  /// parser.
+  final argParser = new ArgParser();
+
+  /// Generates a string displaying usage information for this command.
+  ///
+  /// This includes usage for the command's arguments as well as a list of
+  /// subcommands, if there are any.
+  String get usage => "$description\n\n$_usageWithoutDescription";
+
+  /// An optional footer for [usage].
+  ///
+  /// If a subclass overrides this to return a string, it will automatically be
+  /// added to the end of [usage].
+  final String usageFooter = null;
+
+  /// Returns [usage] with [description] removed from the beginning.
+  String get _usageWithoutDescription {
+    var buffer = new StringBuffer()
+      ..writeln('Usage: $invocation')
+      ..writeln(argParser.usage);
+
+    if (_subcommands.isNotEmpty) {
+      buffer.writeln();
+      buffer.writeln(_getCommandUsage(_subcommands, isSubcommand: true));
+    }
+
+    buffer.writeln();
+    buffer.write('Run "${runner.executableName} help" to see global options.');
+
+    if (usageFooter != null) {
+      buffer.writeln();
+      buffer.write(usageFooter);
+    }
+
+    return buffer.toString();
+  }
+
+  /// An unmodifiable view of all sublevel commands of this command.
+  Map<String, Command> get subcommands => new UnmodifiableMapView(_subcommands);
+  final _subcommands = new Map<String, Command>();
+
+  /// Whether or not this command should be hidden from help listings.
+  ///
+  /// This is intended to be overridden by commands that want to mark themselves
+  /// hidden.
+  ///
+  /// By default, leaf commands are always visible. Branch commands are visible
+  /// as long as any of their leaf commands are visible.
+  bool get hidden {
+    // Leaf commands are visible by default.
+    if (_subcommands.isEmpty) return false;
+
+    // Otherwise, a command is hidden if all of its subcommands are.
+    return _subcommands.values.every((subcommand) => subcommand.hidden);
+  }
+
+  /// Whether or not this command takes positional arguments in addition to
+  /// options.
+  ///
+  /// If false, [CommandRunner.run] will throw a [UsageException] if arguments
+  /// are provided. Defaults to true.
+  ///
+  /// This is intended to be overridden by commands that don't want to receive
+  /// arguments. It has no effect for branch commands.
+  final takesArguments = true;
+
+  /// Alternate names for this command.
+  ///
+  /// These names won't be used in the documentation, but they will work when
+  /// invoked on the command line.
+  ///
+  /// This is intended to be overridden.
+  final aliases = const <String>[];
+
+  Command() {
+    argParser.addFlag('help',
+        abbr: 'h', negatable: false, help: 'Print this usage information.');
+  }
+
+  /// Runs this command.
+  ///
+  /// If this returns a [Future], [CommandRunner.run] won't complete until the
+  /// returned [Future] does. Otherwise, the return value is ignored.
+  run() {
+    throw new UnimplementedError("Leaf command $this must implement run().");
+  }
+
+  /// Adds [Command] as a subcommand of this.
+  void addSubcommand(Command command) {
+    var names = [command.name]..addAll(command.aliases);
+    for (var name in names) {
+      _subcommands[name] = command;
+      argParser.addCommand(name, command.argParser);
+    }
+    command._parent = this;
+  }
+
+  /// Prints the usage information for this command.
+  ///
+  /// This is called internally by [run] and can be overridden by subclasses to
+  /// control how output is displayed or integrate with a logging system.
+  void printUsage() => print(usage);
+
+  /// Throws a [UsageException] with [message].
+  void usageException(String message) =>
+      throw new UsageException(message, _usageWithoutDescription);
+}
+
+/// Returns a string representation of [commands] fit for use in a usage string.
+///
+/// [isSubcommand] indicates whether the commands should be called "commands" or
+/// "subcommands".
+String _getCommandUsage(Map<String, Command> commands,
+    {bool isSubcommand: false}) {
+  // Don't include aliases.
+  var names =
+      commands.keys.where((name) => !commands[name].aliases.contains(name));
+
+  // Filter out hidden ones, unless they are all hidden.
+  var visible = names.where((name) => !commands[name].hidden);
+  if (visible.isNotEmpty) names = visible;
+
+  // Show the commands alphabetically.
+  names = names.toList()..sort();
+  var length = names.map((name) => name.length).reduce(math.max);
+
+  var buffer =
+      new StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:');
+  for (var name in names) {
+    buffer.writeln();
+    buffer.write('  ${padRight(name, length)}   '
+        '${commands[name].description.split("\n").first}');
+  }
+
+  return buffer.toString();
+}
diff --git a/args/lib/src/arg_parser.dart b/args/lib/src/arg_parser.dart
new file mode 100644
index 0000000..49d6aa6
--- /dev/null
+++ b/args/lib/src/arg_parser.dart
@@ -0,0 +1,149 @@
+// 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 args.src.arg_parser;
+
+import 'dart:collection';
+
+import 'arg_results.dart';
+import 'option.dart';
+import 'parser.dart';
+import 'usage.dart';
+
+/// A class for taking a list of raw command line arguments and parsing out
+/// options and flags from them.
+class ArgParser {
+  final Map<String, Option> _options;
+  final Map<String, ArgParser> _commands;
+
+  /// The options that have been defined for this parser.
+  final Map<String, Option> options;
+
+  /// The commands that have been defined for this parser.
+  final Map<String, ArgParser> commands;
+
+  /// Whether or not this parser parses options that appear after non-option
+  /// arguments.
+  final bool allowTrailingOptions;
+
+  /// Creates a new ArgParser.
+  ///
+  /// If [allowTrailingOptions] is set, the parser will continue parsing even
+  /// after it finds an argument that is neither an option nor a command.
+  /// This allows options to be specified after regular arguments. Defaults to
+  /// `false`.
+  factory ArgParser({bool allowTrailingOptions}) => new ArgParser._(
+      <String, Option>{}, <String, ArgParser>{},
+      allowTrailingOptions: allowTrailingOptions);
+
+  ArgParser._(Map<String, Option> options, Map<String, ArgParser> commands,
+      {bool allowTrailingOptions})
+      : this._options = options,
+        this.options = new UnmodifiableMapView(options),
+        this._commands = commands,
+        this.commands = new UnmodifiableMapView(commands),
+        this.allowTrailingOptions = allowTrailingOptions != null
+            ? allowTrailingOptions
+            : false;
+
+  /// Defines a command.
+  ///
+  /// A command is a named argument which may in turn define its own options and
+  /// subcommands using the given parser. If [parser] is omitted, implicitly
+  /// creates a new one. Returns the parser for the command.
+  ArgParser addCommand(String name, [ArgParser parser]) {
+    // Make sure the name isn't in use.
+    if (_commands.containsKey(name)) {
+      throw new ArgumentError('Duplicate command "$name".');
+    }
+
+    if (parser == null) parser = new ArgParser();
+    _commands[name] = parser;
+    return parser;
+  }
+
+  /// Defines a flag. Throws an [ArgumentError] if:
+  ///
+  /// * There is already an option named [name].
+  /// * There is already an option using abbreviation [abbr].
+  void addFlag(String name, {String abbr, String help, bool defaultsTo: false,
+      bool negatable: true, void callback(bool value), bool hide: false}) {
+    _addOption(name, abbr, help, null, null, null, defaultsTo, callback,
+        OptionType.FLAG, negatable: negatable, hide: hide);
+  }
+
+  /// Defines a value-taking option. Throws an [ArgumentError] if:
+  ///
+  /// * There is already an option with name [name].
+  /// * There is already an option using abbreviation [abbr].
+  /// * [splitCommas] is passed but [allowMultiple] is `false`.
+  void addOption(String name, {String abbr, String help, String valueHelp,
+      List<String> allowed, Map<String, String> allowedHelp, String defaultsTo,
+      void callback(value), bool allowMultiple: false, bool splitCommas,
+      bool hide: false}) {
+    if (!allowMultiple && splitCommas != null) {
+      throw new ArgumentError(
+          'splitCommas may not be set if allowMultiple is false.');
+    }
+
+    _addOption(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo,
+        callback, allowMultiple ? OptionType.MULTIPLE : OptionType.SINGLE,
+        splitCommas: splitCommas, hide: hide);
+  }
+
+  void _addOption(String name, String abbr, String help, String valueHelp,
+      List<String> allowed, Map<String, String> allowedHelp, defaultsTo,
+      void callback(value), OptionType type,
+      {bool negatable: false, bool splitCommas, bool hide: false}) {
+    // Make sure the name isn't in use.
+    if (_options.containsKey(name)) {
+      throw new ArgumentError('Duplicate option "$name".');
+    }
+
+    // Make sure the abbreviation isn't too long or in use.
+    if (abbr != null) {
+      var existing = findByAbbreviation(abbr);
+      if (existing != null) {
+        throw new ArgumentError(
+            'Abbreviation "$abbr" is already used by "${existing.name}".');
+      }
+    }
+
+    _options[name] = newOption(name, abbr, help, valueHelp, allowed,
+        allowedHelp, defaultsTo, callback, type,
+        negatable: negatable, splitCommas: splitCommas, hide: hide);
+  }
+
+  /// Parses [args], a list of command-line arguments, matches them against the
+  /// flags and options defined by this parser, and returns the result.
+  ArgResults parse(List<String> args) =>
+      new Parser(null, this, args.toList(), null, null).parse();
+
+  /// Generates a string displaying usage information for the defined options.
+  ///
+  /// This is basically the help text shown on the command line.
+  @Deprecated("Replaced with get usage. getUsage() will be removed in args 1.0")
+  String getUsage() => new Usage(this).generate();
+
+  /// Generates a string displaying usage information for the defined options.
+  ///
+  /// This is basically the help text shown on the command line.
+  String get usage => new Usage(this).generate();
+
+  /// Get the default value for an option. Useful after parsing to test if the
+  /// user specified something other than the default.
+  getDefault(String option) {
+    if (!options.containsKey(option)) {
+      throw new ArgumentError('No option named $option');
+    }
+    return options[option].defaultValue;
+  }
+
+  /// Finds the option whose abbreviation is [abbr], or `null` if no option has
+  /// that abbreviation.
+  Option findByAbbreviation(String abbr) {
+    return options.values.firstWhere((option) => option.abbreviation == abbr,
+        orElse: () => null);
+  }
+}
diff --git a/args/lib/src/arg_results.dart b/args/lib/src/arg_results.dart
new file mode 100644
index 0000000..6be2de4
--- /dev/null
+++ b/args/lib/src/arg_results.dart
@@ -0,0 +1,96 @@
+// 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 args.src.arg_results;
+
+import 'dart:collection';
+
+import 'arg_parser.dart';
+
+/// Creates a new [ArgResults].
+///
+/// Since [ArgResults] doesn't have a public constructor, this lets [Parser]
+/// get to it. This function isn't exported to the public API of the package.
+ArgResults newArgResults(ArgParser parser, Map<String, dynamic> parsed,
+    String name, ArgResults command, List<String> rest,
+    List<String> arguments) {
+  return new ArgResults._(parser, parsed, name, command, rest, arguments);
+}
+
+/// The results of parsing a series of command line arguments using
+/// [ArgParser.parse()].
+///
+/// Includes the parsed options and any remaining unparsed command line
+/// arguments.
+class ArgResults {
+  /// The [ArgParser] whose options were parsed for these results.
+  final ArgParser _parser;
+
+  /// The option values that were parsed from arguments.
+  final Map<String, dynamic> _parsed;
+
+  /// If these are the results for parsing a command's options, this will be the
+  /// name of the command. For top-level results, this returns `null`.
+  final String name;
+
+  /// The command that was selected, or `null` if none was.
+  ///
+  /// This will contain the options that were selected for that command.
+  final ArgResults command;
+
+  /// The remaining command-line arguments that were not parsed as options or
+  /// flags.
+  ///
+  /// If `--` was used to separate the options from the remaining arguments,
+  /// it will not be included in this list unless parsing stopped before the
+  /// `--` was reached.
+  final List<String> rest;
+
+  /// The original list of arguments that were parsed.
+  final List<String> arguments;
+
+  /// Creates a new [ArgResults].
+  ArgResults._(this._parser, this._parsed, this.name, this.command,
+      List<String> rest, List<String> arguments)
+      : this.rest = new UnmodifiableListView(rest),
+        this.arguments = new UnmodifiableListView(arguments);
+
+  /// Gets the parsed command-line option named [name].
+  operator [](String name) {
+    if (!_parser.options.containsKey(name)) {
+      throw new ArgumentError('Could not find an option named "$name".');
+    }
+
+    return _parser.options[name].getOrDefault(_parsed[name]);
+  }
+
+  /// Get the names of the available options as an [Iterable].
+  ///
+  /// This includes the options whose values were parsed or that have defaults.
+  /// Options that weren't present and have no default will be omitted.
+  Iterable<String> get options {
+    var result = new Set<String>.from(_parsed.keys);
+
+    // Include the options that have defaults.
+    _parser.options.forEach((name, option) {
+      if (option.defaultValue != null) result.add(name);
+    });
+
+    return result;
+  }
+
+  /// Returns `true` if the option with [name] was parsed from an actual
+  /// argument.
+  ///
+  /// Returns `false` if it wasn't provided and the default value or no default
+  /// value would be used instead.
+  bool wasParsed(String name) {
+    var option = _parser.options[name];
+    if (option == null) {
+      throw new ArgumentError('Could not find an option named "$name".');
+    }
+
+    return _parsed.containsKey(name);
+  }
+}
diff --git a/args/lib/src/help_command.dart b/args/lib/src/help_command.dart
new file mode 100644
index 0000000..f477b47
--- /dev/null
+++ b/args/lib/src/help_command.dart
@@ -0,0 +1,53 @@
+// 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 args.help_command;
+
+import '../command_runner.dart';
+
+/// The built-in help command that's added to every [CommandRunner].
+///
+/// This command displays help information for the various subcommands.
+class HelpCommand extends Command {
+  final name = "help";
+  String get description =>
+      "Display help information for ${runner.executableName}.";
+  String get invocation => "${runner.executableName} help [command]";
+
+  void run() {
+    // Show the default help if no command was specified.
+    if (argResults.rest.isEmpty) {
+      runner.printUsage();
+      return;
+    }
+
+    // Walk the command tree to show help for the selected command or
+    // subcommand.
+    var commands = runner.commands;
+    var command = null;
+    var commandString = runner.executableName;
+
+    for (var name in argResults.rest) {
+      if (commands.isEmpty) {
+        command.usageException(
+            'Command "$commandString" does not expect a subcommand.');
+      }
+
+      if (commands[name] == null) {
+        if (command == null) {
+          runner.usageException('Could not find a command named "$name".');
+        }
+
+        command.usageException(
+            'Could not find a subcommand named "$name" for "$commandString".');
+      }
+
+      command = commands[name];
+      commands = command.subcommands;
+      commandString += " $name";
+    }
+
+    command.printUsage();
+  }
+}
diff --git a/args/lib/src/option.dart b/args/lib/src/option.dart
new file mode 100644
index 0000000..07e87f6
--- /dev/null
+++ b/args/lib/src/option.dart
@@ -0,0 +1,134 @@
+// 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 args.src.option;
+
+import 'dart:collection';
+
+/// Creates a new [Option].
+///
+/// Since [Option] doesn't have a public constructor, this lets [ArgParser]
+/// get to it. This function isn't exported to the public API of the package.
+Option newOption(String name, String abbreviation, String help,
+    String valueHelp, List<String> allowed, Map<String, String> allowedHelp,
+    defaultValue, Function callback, OptionType type,
+    {bool negatable, bool splitCommas, bool hide: false}) {
+  return new Option._(name, abbreviation, help, valueHelp, allowed, allowedHelp,
+      defaultValue, callback, type, negatable: negatable,
+      splitCommas: splitCommas, hide: hide);
+}
+
+/// A command-line option. Includes both flags and options which take a value.
+class Option {
+  final String name;
+  final String abbreviation;
+  final List<String> allowed;
+  final defaultValue;
+  final Function callback;
+  final String help;
+  final String valueHelp;
+  final Map<String, String> allowedHelp;
+  final OptionType type;
+  final bool negatable;
+  final bool splitCommas;
+  final bool hide;
+
+  /// Whether the option is boolean-valued flag.
+  bool get isFlag => type == OptionType.FLAG;
+
+  /// Whether the option takes a single value.
+  bool get isSingle => type == OptionType.SINGLE;
+
+  /// Whether the option allows multiple values.
+  bool get isMultiple => type == OptionType.MULTIPLE;
+
+  Option._(this.name, this.abbreviation, this.help, this.valueHelp,
+      List<String> allowed, Map<String, String> allowedHelp, this.defaultValue,
+      this.callback, OptionType type, {this.negatable, bool splitCommas,
+      this.hide: false})
+      : this.allowed = allowed == null
+          ? null
+          : new UnmodifiableListView(allowed),
+        this.allowedHelp = allowedHelp == null
+            ? null
+            : new UnmodifiableMapView(allowedHelp),
+        this.type = type,
+        // If the user doesn't specify [splitCommas], it defaults to true for
+        // multiple options.
+        this.splitCommas = splitCommas == null
+            ? type == OptionType.MULTIPLE
+            : splitCommas {
+    if (name.isEmpty) {
+      throw new ArgumentError('Name cannot be empty.');
+    } else if (name.startsWith('-')) {
+      throw new ArgumentError('Name $name cannot start with "-".');
+    }
+
+    // Ensure name does not contain any invalid characters.
+    if (_invalidChars.hasMatch(name)) {
+      throw new ArgumentError('Name "$name" contains invalid characters.');
+    }
+
+    if (abbreviation != null) {
+      if (abbreviation.length != 1) {
+        throw new ArgumentError('Abbreviation must be null or have length 1.');
+      } else if (abbreviation == '-') {
+        throw new ArgumentError('Abbreviation cannot be "-".');
+      }
+
+      if (_invalidChars.hasMatch(abbreviation)) {
+        throw new ArgumentError('Abbreviation is an invalid character.');
+      }
+    }
+  }
+
+  /// Returns [value] if non-`null`, otherwise returns the default value for
+  /// this option.
+  ///
+  /// For single-valued options, it will be [defaultValue] if set or `null`
+  /// otherwise. For multiple-valued options, it will be an empty list or a
+  /// list containing [defaultValue] if set.
+  dynamic getOrDefault(value) {
+    if (value != null) return value;
+
+    if (!isMultiple) return defaultValue;
+    if (defaultValue != null) return [defaultValue];
+    return [];
+  }
+
+  static final _invalidChars = new RegExp(r'''[ \t\r\n"'\\/]''');
+}
+
+/// What kinds of values an option accepts.
+class OptionType {
+  /// An option that can only be `true` or `false`.
+  ///
+  /// The presence of the option name itself in the argument list means `true`.
+  static const FLAG = const OptionType._("OptionType.FLAG");
+
+  /// An option that takes a single value.
+  ///
+  /// Examples:
+  ///
+  ///     --mode debug
+  ///     -mdebug
+  ///     --mode=debug
+  ///
+  /// If the option is passed more than once, the last one wins.
+  static const SINGLE = const OptionType._("OptionType.SINGLE");
+
+  /// An option that allows multiple values.
+  ///
+  /// Example:
+  ///
+  ///     --output text --output xml
+  ///
+  /// In the parsed [ArgResults], a multiple-valued option will always return
+  /// a list, even if one or no values were passed.
+  static const MULTIPLE = const OptionType._("OptionType.MULTIPLE");
+
+  final String name;
+
+  const OptionType._(this.name);
+}
diff --git a/args/lib/src/parser.dart b/args/lib/src/parser.dart
new file mode 100644
index 0000000..848bb9c
--- /dev/null
+++ b/args/lib/src/parser.dart
@@ -0,0 +1,293 @@
+// Copyright (c) 2013, 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 args.src.parser;
+
+import 'arg_parser.dart';
+import 'arg_results.dart';
+import 'option.dart';
+
+final _SOLO_OPT = new RegExp(r'^-([a-zA-Z0-9])$');
+final _ABBR_OPT = new RegExp(r'^-([a-zA-Z0-9]+)(.*)$');
+final _LONG_OPT = new RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$');
+
+/// The actual argument parsing class.
+///
+/// Unlike [ArgParser] which is really more an "arg grammar", this is the class
+/// that does the parsing and holds the mutable state required during a parse.
+class Parser {
+  /// If parser is parsing a command's options, this will be the name of the
+  /// command. For top-level results, this returns `null`.
+  final String commandName;
+
+  /// The parser for the supercommand of this command parser, or `null` if this
+  /// is the top-level parser.
+  final Parser parent;
+
+  /// The grammar being parsed.
+  final ArgParser grammar;
+
+  /// The arguments being parsed.
+  final List<String> args;
+
+  /// The remaining non-option, non-command arguments.
+  final rest = <String>[];
+
+  /// The accumulated parsed options.
+  final Map<String, dynamic> results = <String, dynamic>{};
+
+  Parser(this.commandName, this.grammar, this.args, this.parent, rest) {
+    if (rest != null) this.rest.addAll(rest);
+  }
+
+  /// The current argument being parsed.
+  String get current => args[0];
+
+  /// Parses the arguments. This can only be called once.
+  ArgResults parse() {
+    var arguments = args.toList();
+    var commandResults = null;
+
+    // Parse the args.
+    while (args.length > 0) {
+      if (current == '--') {
+        // Reached the argument terminator, so stop here.
+        args.removeAt(0);
+        break;
+      }
+
+      // Try to parse the current argument as a command. This happens before
+      // options so that commands can have option-like names.
+      var command = grammar.commands[current];
+      if (command != null) {
+        validate(rest.isEmpty, 'Cannot specify arguments before a command.');
+        var commandName = args.removeAt(0);
+        var commandParser = new Parser(commandName, command, args, this, rest);
+        commandResults = commandParser.parse();
+
+        // All remaining arguments were passed to command so clear them here.
+        rest.clear();
+        break;
+      }
+
+      // Try to parse the current argument as an option. Note that the order
+      // here matters.
+      if (parseSoloOption()) continue;
+      if (parseAbbreviation(this)) continue;
+      if (parseLongOption()) continue;
+
+      // This argument is neither option nor command, so stop parsing unless
+      // the [allowTrailingOptions] option is set.
+      if (!grammar.allowTrailingOptions) break;
+      rest.add(args.removeAt(0));
+    }
+
+    // Invoke the callbacks.
+    grammar.options.forEach((name, option) {
+      if (option.callback == null) return;
+      option.callback(option.getOrDefault(results[name]));
+    });
+
+    // Add in the leftover arguments we didn't parse to the innermost command.
+    rest.addAll(args);
+    args.clear();
+    return newArgResults(
+        grammar, results, commandName, commandResults, rest, arguments);
+  }
+
+  /// Pulls the value for [option] from the second argument in [args].
+  ///
+  /// Validates that there is a valid value there.
+  void readNextArgAsValue(Option option) {
+    // Take the option argument from the next command line arg.
+    validate(args.length > 0, 'Missing argument for "${option.name}".');
+
+    // Make sure it isn't an option itself.
+    validate(!_ABBR_OPT.hasMatch(current) && !_LONG_OPT.hasMatch(current),
+        'Missing argument for "${option.name}".');
+
+    setOption(results, option, current);
+    args.removeAt(0);
+  }
+
+  /// Tries to parse the current argument as a "solo" option, which is a single
+  /// hyphen followed by a single letter.
+  ///
+  /// We treat this differently than collapsed abbreviations (like "-abc") to
+  /// handle the possible value that may follow it.
+  bool parseSoloOption() {
+    var soloOpt = _SOLO_OPT.firstMatch(current);
+    if (soloOpt == null) return false;
+
+    var option = grammar.findByAbbreviation(soloOpt[1]);
+    if (option == null) {
+      // Walk up to the parent command if possible.
+      validate(
+          parent != null, 'Could not find an option or flag "-${soloOpt[1]}".');
+      return parent.parseSoloOption();
+    }
+
+    args.removeAt(0);
+
+    if (option.isFlag) {
+      setFlag(results, option, true);
+    } else {
+      readNextArgAsValue(option);
+    }
+
+    return true;
+  }
+
+  /// Tries to parse the current argument as a series of collapsed abbreviations
+  /// (like "-abc") or a single abbreviation with the value directly attached
+  /// to it (like "-mrelease").
+  bool parseAbbreviation(Parser innermostCommand) {
+    var abbrOpt = _ABBR_OPT.firstMatch(current);
+    if (abbrOpt == null) return false;
+
+    // If the first character is the abbreviation for a non-flag option, then
+    // the rest is the value.
+    var c = abbrOpt[1].substring(0, 1);
+    var first = grammar.findByAbbreviation(c);
+    if (first == null) {
+      // Walk up to the parent command if possible.
+      validate(
+          parent != null, 'Could not find an option with short name "-$c".');
+      return parent.parseAbbreviation(innermostCommand);
+    } else if (!first.isFlag) {
+      // The first character is a non-flag option, so the rest must be the
+      // value.
+      var value = '${abbrOpt[1].substring(1)}${abbrOpt[2]}';
+      setOption(results, first, value);
+    } else {
+      // If we got some non-flag characters, then it must be a value, but
+      // if we got here, it's a flag, which is wrong.
+      validate(abbrOpt[2] == '',
+          'Option "-$c" is a flag and cannot handle value '
+          '"${abbrOpt[1].substring(1)}${abbrOpt[2]}".');
+
+      // Not an option, so all characters should be flags.
+      // We use "innermostCommand" here so that if a parent command parses the
+      // *first* letter, subcommands can still be found to parse the other
+      // letters.
+      for (var i = 0; i < abbrOpt[1].length; i++) {
+        var c = abbrOpt[1].substring(i, i + 1);
+        innermostCommand.parseShortFlag(c);
+      }
+    }
+
+    args.removeAt(0);
+    return true;
+  }
+
+  void parseShortFlag(String c) {
+    var option = grammar.findByAbbreviation(c);
+    if (option == null) {
+      // Walk up to the parent command if possible.
+      validate(
+          parent != null, 'Could not find an option with short name "-$c".');
+      parent.parseShortFlag(c);
+      return;
+    }
+
+    // In a list of short options, only the first can be a non-flag. If
+    // we get here we've checked that already.
+    validate(
+        option.isFlag, 'Option "-$c" must be a flag to be in a collapsed "-".');
+
+    setFlag(results, option, true);
+  }
+
+  /// Tries to parse the current argument as a long-form named option, which
+  /// may include a value like "--mode=release" or "--mode release".
+  bool parseLongOption() {
+    var longOpt = _LONG_OPT.firstMatch(current);
+    if (longOpt == null) return false;
+
+    var name = longOpt[1];
+    var option = grammar.options[name];
+    if (option != null) {
+      args.removeAt(0);
+      if (option.isFlag) {
+        validate(longOpt[3] == null,
+            'Flag option "$name" should not be given a value.');
+
+        setFlag(results, option, true);
+      } else if (longOpt[3] != null) {
+        // We have a value like --foo=bar.
+        setOption(results, option, longOpt[3]);
+      } else {
+        // Option like --foo, so look for the value as the next arg.
+        readNextArgAsValue(option);
+      }
+    } else if (name.startsWith('no-')) {
+      // See if it's a negated flag.
+      name = name.substring('no-'.length);
+      option = grammar.options[name];
+      if (option == null) {
+        // Walk up to the parent command if possible.
+        validate(parent != null, 'Could not find an option named "$name".');
+        return parent.parseLongOption();
+      }
+
+      args.removeAt(0);
+      validate(option.isFlag, 'Cannot negate non-flag option "$name".');
+      validate(option.negatable, 'Cannot negate option "$name".');
+
+      setFlag(results, option, false);
+    } else {
+      // Walk up to the parent command if possible.
+      validate(parent != null, 'Could not find an option named "$name".');
+      return parent.parseLongOption();
+    }
+
+    return true;
+  }
+
+  /// Called during parsing to validate the arguments.
+  ///
+  /// Throws a [FormatException] if [condition] is `false`.
+  void validate(bool condition, String message) {
+    if (!condition) throw new FormatException(message);
+  }
+
+  /// Validates and stores [value] as the value for [option], which must not be
+  /// a flag.
+  void setOption(Map results, Option option, String value) {
+    assert(!option.isFlag);
+
+    if (!option.isMultiple) {
+      _validateAllowed(option, value);
+      results[option.name] = value;
+      return;
+    }
+
+    var list = results.putIfAbsent(option.name, () => []);
+
+    if (option.splitCommas) {
+      for (var element in value.split(",")) {
+        _validateAllowed(option, element);
+        list.add(element);
+      }
+    } else {
+      _validateAllowed(option, value);
+      list.add(value);
+    }
+  }
+
+  /// Validates and stores [value] as the value for [option], which must be a
+  /// flag.
+  void setFlag(Map results, Option option, bool value) {
+    assert(option.isFlag);
+    results[option.name] = value;
+  }
+
+  /// Validates that [value] is allowed as a value of [option].
+  void _validateAllowed(Option option, String value) {
+    if (option.allowed == null) return;
+
+    validate(option.allowed.contains(value),
+        '"$value" is not an allowed value for option "${option.name}".');
+  }
+}
diff --git a/args/lib/src/usage.dart b/args/lib/src/usage.dart
new file mode 100644
index 0000000..11be54b
--- /dev/null
+++ b/args/lib/src/usage.dart
@@ -0,0 +1,243 @@
+// Copyright (c) 2013, 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 args.src.usage;
+
+import 'dart:math';
+
+import '../args.dart';
+
+/// Takes an [ArgParser] and generates a string of usage (i.e. help) text for
+/// its defined options.
+///
+/// Internally, it works like a tabular printer. The output is divided into
+/// three horizontal columns, like so:
+///
+///     -h, --help  Prints the usage information
+///     |  |        |                                 |
+///
+/// It builds the usage text up one column at a time and handles padding with
+/// spaces and wrapping to the next line to keep the cells correctly lined up.
+class Usage {
+  static const NUM_COLUMNS = 3; // Abbreviation, long name, help.
+
+  /// The parser this is generating usage for.
+  final ArgParser args;
+
+  /// The working buffer for the generated usage text.
+  StringBuffer buffer;
+
+  /// The column that the "cursor" is currently on.
+  ///
+  /// If the next call to [write()] is not for this column, it will correctly
+  /// handle advancing to the next column (and possibly the next row).
+  int currentColumn = 0;
+
+  /// The width in characters of each column.
+  List<int> columnWidths;
+
+  /// The number of sequential lines of text that have been written to the last
+  /// column (which shows help info).
+  ///
+  /// We track this so that help text that spans multiple lines can be padded
+  /// with a blank line after it for separation. Meanwhile, sequential options
+  /// with single-line help will be compacted next to each other.
+  int numHelpLines = 0;
+
+  /// How many newlines need to be rendered before the next bit of text can be
+  /// written.
+  ///
+  /// We do this lazily so that the last bit of usage doesn't have dangling
+  /// newlines. We only write newlines right *before* we write some real
+  /// content.
+  int newlinesNeeded = 0;
+
+  Usage(this.args);
+
+  /// Generates a string displaying usage information for the defined options.
+  /// This is basically the help text shown on the command line.
+  String generate() {
+    buffer = new StringBuffer();
+
+    calculateColumnWidths();
+
+    args.options.forEach((name, option) {
+      if (option.hide) return;
+
+      write(0, getAbbreviation(option));
+      write(1, getLongOption(option));
+
+      if (option.help != null) write(2, option.help);
+
+      if (option.allowedHelp != null) {
+        var allowedNames = option.allowedHelp.keys.toList(growable: false);
+        allowedNames.sort();
+        newline();
+        for (var name in allowedNames) {
+          write(1, getAllowedTitle(name));
+          write(2, option.allowedHelp[name]);
+        }
+        newline();
+      } else if (option.allowed != null) {
+        write(2, buildAllowedList(option));
+      } else if (option.defaultValue != null) {
+        if (option.isFlag && option.defaultValue == true) {
+          write(2, '(defaults to on)');
+        } else if (!option.isFlag) {
+          write(2, '(defaults to "${option.defaultValue}")');
+        }
+      }
+
+      // If any given option displays more than one line of text on the right
+      // column (i.e. help, default value, allowed options, etc.) then put a
+      // blank line after it. This gives space where it's useful while still
+      // keeping simple one-line options clumped together.
+      if (numHelpLines > 1) newline();
+    });
+
+    return buffer.toString();
+  }
+
+  String getAbbreviation(Option option) {
+    if (option.abbreviation != null) {
+      return '-${option.abbreviation}, ';
+    } else {
+      return '';
+    }
+  }
+
+  String getLongOption(Option option) {
+    var result;
+    if (option.negatable) {
+      result = '--[no-]${option.name}';
+    } else {
+      result = '--${option.name}';
+    }
+
+    if (option.valueHelp != null) result += "=<${option.valueHelp}>";
+
+    return result;
+  }
+
+  String getAllowedTitle(String allowed) {
+    return '      [$allowed]';
+  }
+
+  void calculateColumnWidths() {
+    int abbr = 0;
+    int title = 0;
+    args.options.forEach((name, option) {
+      if (option.hide) return;
+
+      // Make room in the first column if there are abbreviations.
+      abbr = max(abbr, getAbbreviation(option).length);
+
+      // Make room for the option.
+      title = max(title, getLongOption(option).length);
+
+      // Make room for the allowed help.
+      if (option.allowedHelp != null) {
+        for (var allowed in option.allowedHelp.keys) {
+          title = max(title, getAllowedTitle(allowed).length);
+        }
+      }
+    });
+
+    // Leave a gutter between the columns.
+    title += 4;
+    columnWidths = [abbr, title];
+  }
+
+  void newline() {
+    newlinesNeeded++;
+    currentColumn = 0;
+    numHelpLines = 0;
+  }
+
+  void write(int column, String text) {
+    var lines = text.split('\n');
+
+    // Strip leading and trailing empty lines.
+    while (lines.length > 0 && lines[0].trim() == '') {
+      lines.removeRange(0, 1);
+    }
+
+    while (lines.length > 0 && lines[lines.length - 1].trim() == '') {
+      lines.removeLast();
+    }
+
+    for (var line in lines) {
+      writeLine(column, line);
+    }
+  }
+
+  void writeLine(int column, String text) {
+    // Write any pending newlines.
+    while (newlinesNeeded > 0) {
+      buffer.write('\n');
+      newlinesNeeded--;
+    }
+
+    // Advance until we are at the right column (which may mean wrapping around
+    // to the next line.
+    while (currentColumn != column) {
+      if (currentColumn < NUM_COLUMNS - 1) {
+        buffer.write(padRight('', columnWidths[currentColumn]));
+      } else {
+        buffer.write('\n');
+      }
+      currentColumn = (currentColumn + 1) % NUM_COLUMNS;
+    }
+
+    if (column < columnWidths.length) {
+      // Fixed-size column, so pad it.
+      buffer.write(padRight(text, columnWidths[column]));
+    } else {
+      // The last column, so just write it.
+      buffer.write(text);
+    }
+
+    // Advance to the next column.
+    currentColumn = (currentColumn + 1) % NUM_COLUMNS;
+
+    // If we reached the last column, we need to wrap to the next line.
+    if (column == NUM_COLUMNS - 1) newlinesNeeded++;
+
+    // Keep track of how many consecutive lines we've written in the last
+    // column.
+    if (column == NUM_COLUMNS - 1) {
+      numHelpLines++;
+    } else {
+      numHelpLines = 0;
+    }
+  }
+
+  String buildAllowedList(Option option) {
+    var allowedBuffer = new StringBuffer();
+    allowedBuffer.write('[');
+    bool first = true;
+    for (var allowed in option.allowed) {
+      if (!first) allowedBuffer.write(', ');
+      allowedBuffer.write(allowed);
+      if (allowed == option.defaultValue) {
+        allowedBuffer.write(' (default)');
+      }
+      first = false;
+    }
+    allowedBuffer.write(']');
+    return allowedBuffer.toString();
+  }
+}
+
+/// Pads [source] to [length] by adding spaces at the end.
+String padRight(String source, int length) {
+  final result = new StringBuffer();
+  result.write(source);
+
+  while (result.length < length) {
+    result.write(' ');
+  }
+
+  return result.toString();
+}
diff --git a/args/lib/src/usage_exception.dart b/args/lib/src/usage_exception.dart
new file mode 100644
index 0000000..35cc744
--- /dev/null
+++ b/args/lib/src/usage_exception.dart
@@ -0,0 +1,14 @@
+// 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 args.usage_exception;
+
+class UsageException implements Exception {
+  final String message;
+  final String usage;
+
+  UsageException(this.message, this.usage);
+
+  String toString() => "$message\n\n$usage";
+}
diff --git a/args/lib/src/utils.dart b/args/lib/src/utils.dart
new file mode 100644
index 0000000..dc355df
--- /dev/null
+++ b/args/lib/src/utils.dart
@@ -0,0 +1,9 @@
+// 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 args.utils;
+
+/// Pads [source] to [length] by adding spaces at the end.
+String padRight(String source, int length) =>
+    source + ' ' * (length - source.length);
diff --git a/args/pubspec.yaml b/args/pubspec.yaml
new file mode 100644
index 0000000..4a3b1f6
--- /dev/null
+++ b/args/pubspec.yaml
@@ -0,0 +1,12 @@
+name: args
+version: 0.13.0
+author: "Dart Team <misc@dartlang.org>"
+homepage: https://github.com/dart-lang/args
+description: >
+ Library for defining parsers for parsing raw command-line arguments into
+ a set of options and values using GNU and POSIX style options.
+
+dev_dependencies:
+  unittest: ">=0.11.5 <0.12.0"
+environment:
+  sdk: ">=1.4.0 <2.0.0"
diff --git a/barback/lib/barback.dart b/barback/lib/barback.dart
new file mode 100644
index 0000000..3975758
--- /dev/null
+++ b/barback/lib/barback.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2013, 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 barback;
+
+export 'src/asset/asset.dart';
+export 'src/asset/asset_id.dart';
+export 'src/asset/asset_set.dart';
+export 'src/barback.dart';
+export 'src/build_result.dart';
+export 'src/errors.dart' hide flattenAggregateExceptions;
+export 'src/log.dart';
+export 'src/package_provider.dart';
+export 'src/transformer/aggregate_transform.dart';
+export 'src/transformer/aggregate_transformer.dart';
+export 'src/transformer/barback_settings.dart';
+export 'src/transformer/base_transform.dart';
+export 'src/transformer/declaring_aggregate_transform.dart';
+export 'src/transformer/declaring_aggregate_transformer.dart';
+export 'src/transformer/declaring_transform.dart' hide newDeclaringTransform;
+export 'src/transformer/declaring_transformer.dart';
+export 'src/transformer/lazy_aggregate_transformer.dart';
+export 'src/transformer/lazy_transformer.dart';
+export 'src/transformer/transform.dart' hide newTransform;
+export 'src/transformer/transform_logger.dart';
+export 'src/transformer/transformer.dart';
+export 'src/transformer/transformer_group.dart';
diff --git a/barback/lib/src/asset/asset.dart b/barback/lib/src/asset/asset.dart
new file mode 100644
index 0000000..c0f11ba
--- /dev/null
+++ b/barback/lib/src/asset/asset.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2013, 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 barback.asset.asset;
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:convert';
+
+import 'asset_id.dart';
+import 'internal_asset.dart';
+
+/// A blob of content.
+///
+/// Assets may come from the file system, or as the output of a [Transformer].
+/// They are identified by [AssetId].
+///
+/// Custom implementations of [Asset] are not currently supported.
+abstract class Asset {
+  /// The ID for this asset.
+  final AssetId id;
+
+  factory Asset.fromBytes(AssetId id, List<int> bytes) =>
+      new BinaryAsset(id, bytes);
+
+  factory Asset.fromFile(AssetId id, File file) =>
+      new FileAsset(id, file.path);
+
+  factory Asset.fromString(AssetId id, String content) =>
+      new StringAsset(id, content);
+
+  factory Asset.fromPath(AssetId id, String path) =>
+      new FileAsset(id, path);
+
+  factory Asset.fromStream(AssetId id, Stream<List<int>> stream) =>
+      new StreamAsset(id, stream);
+
+  /// Returns the contents of the asset as a string.
+  ///
+  /// If the asset was created from a [String] the original string is always
+  /// returned and [encoding] is ignored. Otherwise, the binary data of the
+  /// asset is decoded using [encoding], which defaults to [UTF8].
+  Future<String> readAsString({Encoding encoding});
+
+  /// Streams the binary contents of the asset.
+  ///
+  /// If the asset was created from a [String], this returns its UTF-8 encoding.
+  Stream<List<int>> read();
+}
diff --git a/barback/lib/src/asset/asset_forwarder.dart b/barback/lib/src/asset/asset_forwarder.dart
new file mode 100644
index 0000000..03c493c
--- /dev/null
+++ b/barback/lib/src/asset/asset_forwarder.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2013, 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 barback.asset.asset_forwarder;
+
+import 'dart:async';
+
+import 'asset_node.dart';
+
+/// A wrapper for an [AssetNode] that forwards events to a new node.
+///
+/// A forwarder is used when a class wants to forward an [AssetNode] that it
+/// gets as an input, but also wants to have control over when that node is
+/// marked as removed. The forwarder can be closed, thus removing its output
+/// node, without the original node having been removed.
+class AssetForwarder {
+  /// The subscription on the input node.
+  StreamSubscription _subscription;
+
+  /// The controller for the output node.
+  final AssetNodeController _controller;
+
+  /// The node to which events are forwarded.
+  AssetNode get node => _controller.node;
+
+  AssetForwarder(AssetNode node)
+      : _controller = new AssetNodeController.from(node) {
+    if (node.state.isRemoved) return;
+
+    _subscription = node.onStateChange.listen((state) {
+      if (state.isAvailable) {
+        _controller.setAvailable(node.asset);
+      } else if (state.isDirty) {
+        _controller.setDirty();
+      } else {
+        assert(state.isRemoved);
+        close();
+      }
+    });
+  }
+
+  /// Closes the forwarder and marks [node] as removed.
+  void close() {
+    if (_controller.node.state.isRemoved) return;
+    _subscription.cancel();
+    _controller.setRemoved();
+  }
+}
diff --git a/barback/lib/src/asset/asset_id.dart b/barback/lib/src/asset/asset_id.dart
new file mode 100644
index 0000000..adb2247
--- /dev/null
+++ b/barback/lib/src/asset/asset_id.dart
@@ -0,0 +1,111 @@
+// Copyright (c) 2013, 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 barback.asset.asset_id;
+
+import 'package:path/path.dart' as pathos;
+
+/// Identifies an asset within a package.
+class AssetId implements Comparable<AssetId> {
+  /// The name of the package containing this asset.
+  final String package;
+
+  /// The path to the asset relative to the root directory of [package].
+  ///
+  /// Source (i.e. read from disk) and generated (i.e. the output of a
+  /// [Transformer]) assets all have paths. Even intermediate assets that are
+  /// generated and then consumed by later transformations will still have
+  /// a path used to identify it.
+  ///
+  /// Asset paths always use forward slashes as path separators, regardless of
+  /// the host platform.
+  final String path;
+
+  /// Gets the file extension of the asset, if it has one, including the ".".
+  String get extension => pathos.extension(path);
+
+  /// Creates a new AssetId at [path] within [package].
+  ///
+  /// The [path] will be normalized: any backslashes will be replaced with
+  /// forward slashes (regardless of host OS) and "." and ".." will be removed
+  /// where possible.
+  AssetId(this.package, String path)
+      : path = _normalizePath(path);
+
+  /// Parses an [AssetId] string of the form "package|path/to/asset.txt".
+  ///
+  /// The [path] will be normalized: any backslashes will be replaced with
+  /// forward slashes (regardless of host OS) and "." and ".." will be removed
+  /// where possible.
+  factory AssetId.parse(String description) {
+    var parts = description.split("|");
+    if (parts.length != 2) {
+      throw new FormatException('Could not parse "$description".');
+    }
+
+    if (parts[0].isEmpty) {
+      throw new FormatException(
+          'Cannot have empty package name in "$description".');
+    }
+
+    if (parts[1].isEmpty) {
+      throw new FormatException(
+          'Cannot have empty path in "$description".');
+    }
+
+    return new AssetId(parts[0], parts[1]);
+  }
+
+  /// Deserializes an [AssetId] from [data], which must be the result of
+  /// calling [serialize] on an existing [AssetId].
+  ///
+  /// Note that this is intended for communicating ids across isolates and not
+  /// for persistent storage of asset identifiers. There is no guarantee of
+  /// backwards compatibility in serialization form across versions.
+  AssetId.deserialize(data)
+      : package = data[0],
+        path = data[1];
+
+  /// Returns `true` of [other] is an [AssetId] with the same package and path.
+  operator ==(other) =>
+      other is AssetId &&
+      package == other.package &&
+      path == other.path;
+
+  int get hashCode => package.hashCode ^ path.hashCode;
+
+  int compareTo(AssetId other) {
+    var packageComp = package.compareTo(other.package);
+    if (packageComp != 0) return packageComp;
+    return path.compareTo(other.path);
+  }
+
+  /// Returns a new [AssetId] with the same [package] as this one and with the
+  /// [path] extended to include [extension].
+  AssetId addExtension(String extension) =>
+      new AssetId(package, "$path$extension");
+
+  /// Returns a new [AssetId] with the same [package] and [path] as this one
+  /// but with file extension [newExtension].
+  AssetId changeExtension(String newExtension) =>
+      new AssetId(package, pathos.withoutExtension(path) + newExtension);
+
+  String toString() => "$package|$path";
+
+  /// Serializes this [AssetId] to an object that can be sent across isolates
+  /// and passed to [deserialize].
+  serialize() => [package, path];
+}
+
+String _normalizePath(String path) {
+  if (pathos.isAbsolute(path)) {
+    throw new ArgumentError('Asset paths must be relative, but got "$path".');
+  }
+
+  // Normalize path separators so that they are always "/" in the AssetID.
+  path = path.replaceAll(r"\", "/");
+
+  // Collapse "." and "..".
+  return pathos.posix.normalize(path);
+}
\ No newline at end of file
diff --git a/barback/lib/src/asset/asset_node.dart b/barback/lib/src/asset/asset_node.dart
new file mode 100644
index 0000000..45d1b17
--- /dev/null
+++ b/barback/lib/src/asset/asset_node.dart
@@ -0,0 +1,283 @@
+// Copyright (c) 2013, 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 barback.asset.asset_node;
+
+import 'dart:async';
+
+import '../errors.dart';
+import '../graph/transform_node.dart';
+import '../utils.dart';
+import 'asset.dart';
+import 'asset_id.dart';
+
+/// Describes the current state of an asset as part of a transformation graph.
+///
+/// An asset node can be in one of three states (see [AssetState]). It provides
+/// an [onStateChange] stream that emits an event whenever it changes state.
+///
+/// Asset nodes are controlled using [AssetNodeController]s.
+class AssetNode {
+  /// The id of the asset that this node represents.
+  final AssetId id;
+
+  /// The [AssetNode] from which [this] is forwarded.
+  ///
+  /// For nodes that aren't forwarded, this will return [this]. Otherwise, it
+  /// will return the first node in the forwarding chain.
+  ///
+  /// This is used to determine whether two nodes are forwarded from the same
+  /// source.
+  AssetNode get origin => _origin == null ? this : _origin;
+  AssetNode _origin;
+
+  /// The transform that created this asset node.
+  ///
+  /// This is `null` for source assets. It can change if the upstream transform
+  /// that created this asset changes; this change will *not* cause an
+  /// [onStateChange] event.
+  TransformNode get transform => _transform;
+  TransformNode _transform;
+
+  /// The current state of the asset node.
+  AssetState get state => _state;
+  AssetState _state;
+
+  /// The concrete asset that this node represents.
+  ///
+  /// This is null unless [state] is [AssetState.AVAILABLE].
+  Asset get asset => _asset;
+  Asset _asset;
+
+  /// The callback to be called to notify this asset node's creator that the
+  /// concrete asset should be generated.
+  ///
+  /// This is null for non-lazy asset nodes (see [AssetNodeController.lazy]).
+  /// Once this is called, it's set to null and [this] is no longer considered
+  /// lazy.
+  Function _lazyCallback;
+
+  /// Whether this node is lazy, meaning that [force] must be called to
+  /// guarantee that it will eventually become available.
+  bool get isLazy => _lazyCallback != null ||
+      (_origin != null && _origin.isLazy);
+
+  /// A broadcast stream that emits an event whenever the node changes state.
+  ///
+  /// This stream is synchronous to ensure that when a source asset is modified
+  /// or removed, the appropriate portion of the asset graph is dirtied before
+  /// any [Barback.getAssetById] calls emit newly-incorrect values.
+  Stream<AssetState> get onStateChange => _stateChangeController.stream;
+
+  /// This is synchronous so that a source being updated will always be
+  /// propagated through the build graph before anything that depends on it is
+  /// requested.
+  final _stateChangeController =
+      new StreamController<AssetState>.broadcast(sync: true);
+
+  /// Calls [callback] when the node's asset is available.
+  ///
+  /// If the asset is currently available, this calls [callback] synchronously
+  /// to ensure that the asset is still available.
+  ///
+  /// The return value of [callback] is piped to the returned Future. If the
+  /// asset is removed before becoming available, the returned future will throw
+  /// an [AssetNotFoundException].
+  Future whenAvailable(callback(Asset asset)) {
+    return _waitForState((state) => state.isAvailable || state.isRemoved,
+        (state) {
+      if (state.isRemoved) throw new AssetNotFoundException(id);
+      return callback(asset);
+    });
+  }
+
+  /// Calls [callback] when the node's asset is removed.
+  ///
+  /// If the asset is already removed when this is called, it calls [callback]
+  /// synchronously.
+  ///
+  /// The return value of [callback] is piped to the returned Future.
+  Future whenRemoved(callback()) =>
+    _waitForState((state) => state.isRemoved, (_) => callback());
+
+  /// Returns a [Future] that completes when [state] changes from its current
+  /// value to any other value.
+  ///
+  /// The returned [Future] will contain the new state.
+  Future<AssetState> whenStateChanges() {
+    var startState = state;
+    return _waitForState((state) => state != startState, (state) => state);
+  }
+
+  /// Calls [callback] as soon as the node is in a state that matches [test].
+  ///
+  /// [callback] is called synchronously if this is already in such a state.
+  ///
+  /// The return value of [callback] is piped to the returned Future.
+  Future _waitForState(bool test(AssetState state),
+      callback(AssetState state)) {
+    if (test(state)) return syncFuture(() => callback(state));
+    return onStateChange.firstWhere(test).then((_) => callback(state));
+  }
+
+  AssetNode._(this.id, this._transform, this._origin)
+      : _state = AssetState.RUNNING;
+
+  AssetNode._available(Asset asset, this._transform, this._origin)
+      : id = asset.id,
+        _asset = asset,
+        _state = AssetState.AVAILABLE;
+
+  AssetNode._lazy(this.id, this._transform, this._origin, this._lazyCallback)
+      : _state = AssetState.RUNNING;
+
+  /// If [this] is lazy, force it to generate a concrete asset; otherwise, do
+  /// nothing.
+  ///
+  /// See [AssetNodeController.lazy].
+  void force() {
+    if (_origin != null) {
+      _origin.force();
+    } else if (_lazyCallback != null) {
+      _lazyCallback();
+      _lazyCallback = null;
+    }
+  }
+
+  String toString() => "${isLazy ? 'lazy' : state} asset $id";
+}
+
+/// The controller for an [AssetNode].
+///
+/// This controls which state the node is in.
+class AssetNodeController {
+  final AssetNode node;
+
+  /// Creates a controller for a dirty node.
+  AssetNodeController(AssetId id, [TransformNode transform])
+      : node = new AssetNode._(id, transform, null);
+
+  /// Creates a controller for an available node with the given concrete
+  /// [asset].
+  AssetNodeController.available(Asset asset, [TransformNode transform])
+      : node = new AssetNode._available(asset, transform, null);
+
+  /// Creates a controller for a lazy node.
+  ///
+  /// For the most part, this node works like any other dirty node. However, the
+  /// owner of its controller isn't expected to do the work to make it available
+  /// as soon as possible like they would for a non-lazy node. Instead, when its
+  /// value is needed, [callback] will fire to indicate that it should be made
+  /// available as soon as possible.
+  ///
+  /// [callback] is guaranteed to only fire once.
+  AssetNodeController.lazy(AssetId id, void callback(),
+          [TransformNode transform])
+      : node = new AssetNode._lazy(id, transform, null, callback);
+
+  /// Creates a controller for a node whose initial state matches the current
+  /// state of [node].
+  ///
+  /// [AssetNode.origin] of the returned node will automatically be set to
+  /// `node.origin`.
+  ///
+  /// If [node] is lazy, the returned node will also be lazy.
+  AssetNodeController.from(AssetNode node)
+      : node = new AssetNode._(node.id, node.transform, node.origin) {
+    if (node.state.isAvailable) {
+      setAvailable(node.asset);
+    } else if (node.state.isRemoved) {
+      setRemoved();
+    }
+  }
+
+  /// Marks the node as [AssetState.RUNNING].
+  void setDirty() {
+    assert(node._state != AssetState.REMOVED);
+    node._asset = null;
+    node._lazyCallback = null;
+
+    // Don't re-emit a dirty event to avoid cases where we try to dispatch an
+    // event while handling another event (e.g. an output is marked lazy, which
+    // causes it to be forced, which causes it to be marked dirty).
+    if (node._state.isDirty) return;
+    node._state = AssetState.RUNNING;
+    node._stateChangeController.add(AssetState.RUNNING);
+  }
+
+  /// Marks the node as [AssetState.REMOVED].
+  ///
+  /// Once a node is marked as removed, it can't be marked as any other state.
+  /// If a new asset is created with the same id, it will get a new node.
+  void setRemoved() {
+    assert(node._state != AssetState.REMOVED);
+    node._state = AssetState.REMOVED;
+    node._asset = null;
+    node._lazyCallback = null;
+    node._stateChangeController.add(AssetState.REMOVED);
+  }
+
+  /// Marks the node as [AssetState.AVAILABLE] with the given concrete [asset].
+  ///
+  /// It's an error to mark an already-available node as available. It should be
+  /// marked as dirty first.
+  void setAvailable(Asset asset) {
+    assert(asset.id == node.id);
+    assert(node._state != AssetState.REMOVED);
+    assert(node._state != AssetState.AVAILABLE);
+    node._state = AssetState.AVAILABLE;
+    node._asset = asset;
+    node._lazyCallback = null;
+    node._stateChangeController.add(AssetState.AVAILABLE);
+  }
+
+  /// Marks the node as [AssetState.RUNNING] and lazy.
+  ///
+  /// Lazy nodes aren't expected to have their values generated until needed.
+  /// Once it's necessary, [callback] will be called. [callback] is guaranteed
+  /// to be called only once.
+  ///
+  /// See also [AssetNodeController.lazy].
+  void setLazy(void callback()) {
+    assert(node._state != AssetState.REMOVED);
+    node._state = AssetState.RUNNING;
+    node._asset = null;
+    node._lazyCallback = callback;
+    node._stateChangeController.add(AssetState.RUNNING);
+  }
+
+  String toString() => "controller for $node";
+}
+
+// TODO(nweiz): add an error state.
+/// An enum of states that an [AssetNode] can be in.
+class AssetState {
+  /// The node has a concrete asset loaded, available, and up-to-date. The asset
+  /// is accessible via [AssetNode.asset]. An asset can only be marked available
+  /// again from the [AssetState.RUNNING] state.
+  static final AVAILABLE = const AssetState._("available");
+
+  /// The asset is no longer available, possibly for good. A removed asset will
+  /// never enter another state.
+  static final REMOVED = const AssetState._("removed");
+
+  /// The asset will exist in the future (unless it's removed), but the concrete
+  /// asset is not yet available.
+  static final RUNNING = const AssetState._("dirty");
+
+  /// Whether this state is [AssetState.AVAILABLE].
+  bool get isAvailable => this == AssetState.AVAILABLE;
+
+  /// Whether this state is [AssetState.REMOVED].
+  bool get isRemoved => this == AssetState.REMOVED;
+
+  /// Whether this state is [AssetState.RUNNING].
+  bool get isDirty => this == AssetState.RUNNING;
+
+  final String name;
+
+  const AssetState._(this.name);
+
+  String toString() => name;
+}
diff --git a/barback/lib/src/asset/asset_node_set.dart b/barback/lib/src/asset/asset_node_set.dart
new file mode 100644
index 0000000..a1fc17b
--- /dev/null
+++ b/barback/lib/src/asset/asset_node_set.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2013, 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 barback.asset.asset_node_set;
+
+import 'package:collection/collection.dart';
+
+import 'asset_id.dart';
+import 'asset_node.dart';
+
+/// A set of [AssetNode]s that automatically ensures that nodes are removed from
+/// the set as soon as they're marked as [AssetState.REMOVED].
+///
+/// Asset nodes may be accessed by their ids. This means that only one node with
+/// a given id may be stored in the set at a time.
+class AssetNodeSet extends DelegatingSet<AssetNode> {
+  // TODO(nweiz): Use DelegatingMapSet when issue 18705 is fixed.
+  /// A map from asset ids to assets in the set.
+  final _assetsById = new Map<AssetId, AssetNode>();
+
+  AssetNodeSet()
+      : super(new Set());
+
+  /// Returns the asset node in the set with [id], or `null` if none exists.
+  AssetNode operator [](AssetId id) => _assetsById[id];
+
+  bool add(AssetNode node) {
+    if (node.state.isRemoved) return false;
+    node.whenRemoved(() {
+      super.remove(node);
+      _assetsById.remove(node.id);
+    });
+    _assetsById[node.id] = node;
+    return super.add(node);
+  }
+
+  /// Returns whether an asset node with the given [id] is in the set.
+  bool containsId(AssetId id) => _assetsById.containsKey(id);
+
+  void addAll(Iterable<AssetNode> nodes) => nodes.forEach(add);
+}
\ No newline at end of file
diff --git a/barback/lib/src/asset/asset_set.dart b/barback/lib/src/asset/asset_set.dart
new file mode 100644
index 0000000..ac58774
--- /dev/null
+++ b/barback/lib/src/asset/asset_set.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2013, 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 barback.asset.asset_set;
+
+import 'dart:collection';
+
+import 'asset.dart';
+import 'asset_id.dart';
+
+/// A set of [Asset]s with distinct IDs.
+///
+/// This uses the [AssetId] of each asset to determine uniqueness, so no two
+/// assets with the same ID can be in the set.
+class AssetSet extends IterableBase<Asset> {
+  final _assets = new Map<AssetId, Asset>();
+
+  /// The ids of the assets in the set.
+  Iterable<AssetId> get ids => _assets.keys;
+
+  AssetSet();
+
+  /// Creates a new AssetSet from the contents of [other].
+  ///
+  /// If multiple assets in [other] have the same id, the last one takes
+  /// precedence.
+  AssetSet.from(Iterable<Asset> other) {
+    for (var asset in other) {
+      _assets[asset.id] = asset;
+    }
+  }
+
+  Iterator<Asset> get iterator => _assets.values.iterator;
+
+  int get length => _assets.length;
+
+  /// Gets the [Asset] in the set with [id], or returns `null` if no asset with
+  /// that ID is present.
+  Asset operator[](AssetId id) => _assets[id];
+
+  /// Adds [asset] to the set.
+  ///
+  /// If there is already an asset with that ID in the set, it is replaced by
+  /// the new one. Returns [asset].
+  Asset add(Asset asset) {
+    _assets[asset.id] = asset;
+    return asset;
+  }
+
+  /// Adds [assets] to the set.
+  void addAll(Iterable<Asset> assets) {
+    assets.forEach(add);
+  }
+
+  /// Returns `true` if the set contains [asset].
+  bool contains(Asset asset) {
+    var other = _assets[asset.id];
+    return other == asset;
+  }
+
+  /// Returns `true` if the set contains an [Asset] with [id].
+  bool containsId(AssetId id) {
+    return _assets.containsKey(id);
+  }
+
+  /// If the set contains an [Asset] with [id], removes and returns it.
+  Asset removeId(AssetId id) => _assets.remove(id);
+
+  /// Removes all assets from the set.
+  void clear() {
+    _assets.clear();
+  }
+
+  String toString() => _assets.toString();
+}
diff --git a/barback/lib/src/asset/internal_asset.dart b/barback/lib/src/asset/internal_asset.dart
new file mode 100644
index 0000000..dfacba5
--- /dev/null
+++ b/barback/lib/src/asset/internal_asset.dart
@@ -0,0 +1,187 @@
+// Copyright (c) 2013, 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 barback.asset.internal_asset;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:typed_data';
+
+import '../serialize.dart';
+import '../utils.dart';
+import '../utils/file_pool.dart';
+import '../utils/stream_replayer.dart';
+import 'asset.dart';
+import 'asset_id.dart';
+
+/// Serialize an asset to a form that's safe to send across isolates.
+Map serializeAsset(Asset asset) {
+  var id = serializeId(asset.id);
+  if (asset is BinaryAsset) {
+    return {
+      'type': 'binary',
+      'id': id,
+      'contents': asset._contents
+    };
+  } else if (asset is FileAsset) {
+    return {
+      'type': 'file',
+      'id': id,
+      'path': asset._path
+    };
+  } else if (asset is StringAsset) {
+    return {
+      'type': 'string',
+      'id': id,
+      'contents': asset._contents
+    };
+  } else {
+    // [asset] is probably a [StreamAsset], but it's possible that the user has
+    // created a custom subclass, in which case we just serialize the stream
+    // anyway.
+    return {
+      'type': 'stream',
+      'id': id,
+      'stream': serializeStream(asset.read())
+    };
+  }
+}
+
+/// Deserialize an asset from the form returned by [serialize].
+Asset deserializeAsset(Map asset) {
+  var id = deserializeId(asset['id']);
+  switch (asset['type']) {
+    case 'binary': return new BinaryAsset(id, asset['contents']);
+    case 'file': return new FileAsset(id, asset['path']);
+    case 'string': return new StringAsset(id, asset['contents']);
+    case 'stream':
+      return new StreamAsset(id, deserializeStream(asset['stream']));
+    default:
+      throw new FormatException('Unknown asset type "${asset['type']}".');
+  }
+}
+
+/// An asset whose data is stored in a list of bytes.
+class BinaryAsset implements Asset {
+  final AssetId id;
+
+  final Uint8List _contents;
+
+  BinaryAsset(this.id, List<int> contents)
+      : _contents = toUint8List(contents);
+
+  Future<String> readAsString({Encoding encoding}) {
+    if (encoding == null) encoding = UTF8;
+
+    return new Future.value(encoding.decode(_contents));
+  }
+
+  Stream<List<int>> read() => new Future<List<int>>.value(_contents).asStream();
+
+  String toString() {
+    var buffer = new StringBuffer();
+    buffer.write("Bytes [");
+
+    // Don't show the whole list if it's long.
+    if (_contents.length > 11) {
+      for (var i = 0; i < 5; i++) {
+        buffer.write(byteToHex(_contents[i]));
+        buffer.write(" ");
+      }
+
+      buffer.write("...");
+
+      for (var i = _contents.length - 5; i < _contents.length; i++) {
+        buffer.write(" ");
+        buffer.write(byteToHex(_contents[i]));
+      }
+    } else {
+      for (var i = 0; i < _contents.length; i++) {
+        if (i > 0) buffer.write(" ");
+        buffer.write(byteToHex(_contents[i]));
+      }
+    }
+
+    buffer.write("]");
+    return buffer.toString();
+  }
+}
+
+/// An asset backed by a file on the local file system.
+class FileAsset implements Asset {
+  final AssetId id;
+
+  /// Use a [FilePool] to handle reads so we can try to cope with running out
+  /// of file descriptors more gracefully.
+  static final _pool = new FilePool();
+
+  final String _path;
+  FileAsset(this.id, this._path);
+
+  Future<String> readAsString({Encoding encoding}) {
+    if (encoding == null) encoding = UTF8;
+    return _pool.readAsString(_path, encoding);
+  }
+
+  Stream<List<int>> read() => _pool.openRead(_path);
+
+  String toString() => 'File "${_path}"';
+}
+
+/// An asset whose data is stored in a string.
+class StringAsset implements Asset {
+  final AssetId id;
+
+  final String _contents;
+
+  StringAsset(this.id, this._contents);
+
+  Future<String> readAsString({Encoding encoding}) =>
+      new Future.value(_contents);
+
+  Stream<List<int>> read() =>
+      new Future<List<int>>.value(UTF8.encode(_contents)).asStream();
+
+  String toString() {
+    // Don't show the whole string if it's long.
+    var contents = _contents;
+    if (contents.length > 40) {
+      contents = contents.substring(0, 20) + " ... " +
+                 contents.substring(contents.length - 20);
+    }
+
+    contents = _escape(contents);
+    return 'String "$contents"';
+  }
+
+  String _escape(String string) {
+    return string
+        .replaceAll("\"", r'\"')
+        .replaceAll("\n", r"\n")
+        .replaceAll("\r", r"\r")
+        .replaceAll("\t", r"\t");
+  }
+}
+
+/// An asset whose data is available from a stream.
+class StreamAsset implements Asset {
+  final AssetId id;
+
+  /// A stream replayer that records and replays the contents of the input
+  /// stream.
+  final StreamReplayer<List<int>> _replayer;
+
+  StreamAsset(this.id, Stream<List<int>> stream)
+      : _replayer = new StreamReplayer(stream);
+
+  Future<String> readAsString({Encoding encoding}) {
+    if (encoding == null) encoding = UTF8;
+    return _replayer.getReplay().toList()
+        .then((chunks) => encoding.decode(flatten(chunks)));
+  }
+
+  Stream<List<int>> read() => _replayer.getReplay();
+
+  String toString() => "Stream";
+}
diff --git a/barback/lib/src/barback.dart b/barback/lib/src/barback.dart
new file mode 100644
index 0000000..98d1154
--- /dev/null
+++ b/barback/lib/src/barback.dart
@@ -0,0 +1,117 @@
+// Copyright (c) 2013, 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 barback.barback;
+
+import 'dart:async';
+
+import 'asset/asset.dart';
+import 'asset/asset_id.dart';
+import 'asset/asset_set.dart';
+import 'log.dart';
+import 'build_result.dart';
+import 'errors.dart';
+import 'graph/package_graph.dart';
+import 'package_provider.dart';
+import 'transformer/transformer.dart';
+
+/// A general-purpose asynchronous build dependency graph manager.
+///
+/// It consumes source assets (including Dart files) in a set of packages,
+/// runs transformations on them, and then tracks which sources have been
+/// modified and which transformations need to be re-run.
+///
+/// To do this, you give barback a [PackageProvider] which can yield a set of
+/// [Transformer]s and raw source [Asset]s. Then you tell it which input files
+/// have been added or modified by calling [updateSources]. Barback will
+/// automatically wire up the appropriate transformers to those inputs and
+/// start running them asynchronously. If a transformer produces outputs that
+/// can be consumed by other transformers, they will automatically be pipelined
+/// correctly.
+///
+/// You can then request assets (either source or generated) by calling
+/// [getAssetById]. This will wait for any necessary transformations and then
+/// return the asset.
+///
+/// When source files have been modified or removed, tell barback by calling
+/// [updateSources] and [removeSources] as appropriate. Barback will
+/// automatically track which transformations are affected by those changes and
+/// re-run them as needed.
+///
+/// Barback tries to be resilient to errors since assets are often in an
+/// in-progress state. When errors occur, they will be captured and emitted on
+/// the [errors] stream.
+class Barback {
+  /// The graph managed by this instance.
+  final PackageGraph _graph;
+
+  /// A stream that emits a [BuildResult] each time the build is completed,
+  /// whether or not it succeeded.
+  ///
+  /// This will emit a result only once every package's [AssetCascade] has
+  /// finished building.
+  ///
+  /// If an unexpected error in barback itself occurs, it will be emitted
+  /// through this stream's error channel.
+  Stream<BuildResult> get results => _graph.results;
+
+  /// A stream that emits any errors from the graph or the transformers.
+  ///
+  /// This emits errors as they're detected. If an error occurs in one part of
+  /// the graph, unrelated parts will continue building.
+  ///
+  /// This will not emit programming errors from barback itself. Those will be
+  /// emitted through the [results] stream's error channel.
+  Stream get errors => _graph.errors;
+
+  /// The stream of [LogEntry] objects used to report transformer log entries.
+  ///
+  /// If this stream has listeners, then log entries will go to that.
+  /// Otherwise, a default logger will display them.
+  Stream<LogEntry> get log => _graph.log;
+
+  Barback(PackageProvider provider)
+      : _graph = new PackageGraph(provider);
+
+  /// Gets the asset identified by [id].
+  ///
+  /// If [id] is for a generated or transformed asset, this will wait until
+  /// it has been created and return it. If the asset cannot be found, throws
+  /// [AssetNotFoundException].
+  Future<Asset> getAssetById(AssetId id) {
+    return _graph.getAssetNode(id).then((node) {
+      if (node == null) throw new AssetNotFoundException(id);
+      return node.asset;
+    });
+  }
+
+  /// Adds [sources] to the graph's known set of source assets.
+  ///
+  /// Begins applying any transforms that can consume any of the sources. If a
+  /// given source is already known, it is considered modified and all
+  /// transforms that use it will be re-applied.
+  void updateSources(Iterable<AssetId> sources) =>
+      _graph.updateSources(sources);
+
+  /// Removes [removed] from the graph's known set of source assets.
+  void removeSources(Iterable<AssetId> removed) =>
+      _graph.removeSources(removed);
+
+  /// Gets all output assets.
+  ///
+  /// If a build is currently in progress, waits until it completes. The
+  /// returned future will complete with a [BarbackException] if the build is
+  /// not successful.
+  Future<AssetSet> getAllAssets() => _graph.getAllAssets();
+
+  /// Sets the transformer phases for [package]'s assets to [transformers].
+  ///
+  /// To the extent that [transformers] is similar to the previous transformer
+  /// phases for [package], the existing asset graph will be preserved.
+  ///
+  /// Elements of the inner iterable of [transformers] must be [Transformer]s,
+  /// [TransformerGroup]s, or [AggregateTransformer]s.
+  void updateTransformers(String package, Iterable<Iterable> transformers) =>
+      _graph.updateTransformers(package, transformers);
+}
diff --git a/barback/lib/src/build_result.dart b/barback/lib/src/build_result.dart
new file mode 100644
index 0000000..faeb256
--- /dev/null
+++ b/barback/lib/src/build_result.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2013, 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 barback.build_result;
+
+import 'errors.dart';
+import 'utils.dart';
+
+/// An event indicating that the cascade has finished building all assets.
+///
+/// A build can end either in success or failure. If there were no errors during
+/// the build, it's considered to be a success; any errors render it a failure,
+/// although individual assets may still have built successfully.
+class BuildResult {
+  // TODO(rnystrom): Revise how to track error results. Errors can come from
+  // both logs and exceptions. Accumulating them is likely slow and a waste of
+  // memory. If we do want to accumulate them, we should at least unify them
+  // in a single collection (probably of log entries).
+  /// All errors that were thrown during the build.
+  final Set<BarbackException> errors;
+
+  /// `true` if the build succeeded.
+  bool get succeeded => errors.isEmpty;
+
+  BuildResult(Iterable<BarbackException> errors)
+      : errors = flattenAggregateExceptions(errors).toSet();
+
+  /// Creates a build result indicating a successful build.
+  ///
+  /// This equivalent to a build result with no errors.
+  BuildResult.success()
+      : this([]);
+
+  /// Creates a single [BuildResult] that contains all of the errors of
+  /// [results].
+  factory BuildResult.aggregate(Iterable<BuildResult> results) {
+    var errors = unionAll(results.map((result) => result.errors));
+    return new BuildResult(errors);
+  }
+
+  String toString() {
+    if (succeeded) return "success";
+
+    return "errors:\n" + errors.map((error) {
+      var stackTrace = null;
+      if (error is TransformerException || error is AssetLoadException) {
+        stackTrace = error.stackTrace.terse;
+      }
+
+      var msg = new StringBuffer();
+      msg.write(prefixLines(error.toString()));
+      if (stackTrace != null) {
+        msg.write("\n\n");
+        msg.write("Stack chain:\n");
+        msg.write(prefixLines(stackTrace.toString()));
+      }
+      return msg.toString();
+    }).join("\n\n");
+  }
+}
diff --git a/barback/lib/src/errors.dart b/barback/lib/src/errors.dart
new file mode 100644
index 0000000..3488e31
--- /dev/null
+++ b/barback/lib/src/errors.dart
@@ -0,0 +1,192 @@
+// Copyright (c) 2013, 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 barback.errors;
+
+import 'package:stack_trace/stack_trace.dart';
+
+import 'asset/asset_id.dart';
+import 'transformer/wrapping_aggregate_transformer.dart';
+import 'utils.dart';
+
+/// Error thrown when an asset with [id] cannot be found.
+class AssetNotFoundException implements Exception {
+  final AssetId id;
+
+  AssetNotFoundException(this.id);
+
+  String toString() => "Could not find asset $id.";
+}
+
+/// Replaces any occurrences of [AggregateException] in [errors] with the list
+/// of errors it contains.
+Iterable<BarbackException> flattenAggregateExceptions(
+    Iterable<BarbackException> errors) {
+  return errors.expand((error) {
+    if (error is! AggregateException) return [error];
+    return error.errors;
+  });
+}
+
+/// The interface for exceptions from the barback graph or its transformers.
+///
+/// These exceptions are never produced by programming errors in barback.
+abstract class BarbackException implements Exception {
+  /// Takes a collection of [BarbackExceptions] and returns a single exception
+  /// that contains them all.
+  ///
+  /// If [errors] is empty, returns `null`. If it only has one error, that
+  /// error is returned. Otherwise, an [AggregateException] is returned.
+  static BarbackException aggregate(Iterable<BarbackException> errors) {
+    if (errors.isEmpty) return null;
+    if (errors.length == 1) return errors.single;
+    return new AggregateException(errors);
+  }
+}
+
+/// An error that wraps a collection of other [BarbackException]s.
+///
+/// It implicitly flattens any [AggregateException]s that occur in the list of
+/// exceptions it wraps.
+class AggregateException implements BarbackException {
+  final Set<BarbackException> errors;
+
+  AggregateException(Iterable<BarbackException> errors)
+      : errors = flattenAggregateExceptions(errors).toSet();
+
+  String toString() {
+    var buffer = new StringBuffer();
+    buffer.writeln("Multiple errors occurred:\n");
+
+    for (var error in errors) {
+      buffer.writeln(prefixLines(error.toString(),
+                                 prefix: "  ", firstPrefix: "- "));
+    }
+
+    return buffer.toString();
+  }
+}
+
+/// Error thrown when two or more transformers both output an asset with [id].
+class AssetCollisionException implements BarbackException {
+  /// All the transforms that output an asset with [id].
+  ///
+  /// If this only contains a single transform, that indicates that a
+  /// transformer produced an output that collides with a source asset or an
+  /// asset from a previous phase.
+  final Set<TransformInfo> transforms;
+  final AssetId id;
+
+  AssetCollisionException(Iterable<TransformInfo> transforms, this.id)
+      : transforms = new Set.from(transforms);
+
+  String toString() => "Transforms $transforms all emitted asset $id.";
+}
+
+/// Error thrown when a transformer requests an input [id] which cannot be
+/// found.
+class MissingInputException implements BarbackException {
+  /// The transform that requested [id].
+  final TransformInfo transform;
+  final AssetId id;
+
+  MissingInputException(this.transform, this.id);
+
+  String toString() => "Transform $transform tried to load missing input $id.";
+}
+
+/// Error thrown when a transformer outputs an asset to a different package than
+/// the primary input's.
+class InvalidOutputException implements BarbackException {
+  /// The transform that output the asset.
+  final TransformInfo transform;
+  final AssetId id;
+
+  InvalidOutputException(this.transform, this.id);
+
+  String toString() => "Transform $transform emitted $id, which wasn't in the "
+      "same package (${transform.primaryId.package}).";
+}
+
+/// Base class for an error that wraps another.
+abstract class _WrappedException implements BarbackException {
+  /// The wrapped exception.
+  final error;
+  final Chain stackTrace;
+
+  String get message => "$_message: ${getErrorMessage(error)}";
+
+  String get _message;
+
+  _WrappedException(error, StackTrace stackTrace)
+      : this.error = error,
+        this.stackTrace = _getChain(error, stackTrace);
+
+  String toString() {
+    var result = message;
+    if (stackTrace != null) result = "$result\n${stackTrace.terse}";
+    return result;
+  }
+}
+
+/// Returns the stack chain for [error] and [stackTrace].
+Chain _getChain(error, StackTrace stackTrace) {
+  if (error is Error && stackTrace == null) stackTrace = error.stackTrace;
+  if (stackTrace != null) return new Chain.forTrace(stackTrace);
+  return null;
+}
+
+/// Error wrapping an exception thrown by a transform.
+class TransformerException extends _WrappedException {
+  /// The transform that threw the exception.
+  final TransformInfo transform;
+
+  TransformerException(this.transform, error, StackTrace stackTrace)
+      : super(error, stackTrace);
+
+  String get _message => "Transform $transform threw error";
+}
+
+/// Error thrown when a source asset [id] fails to load.
+///
+/// This can be thrown either because the source asset was expected to exist and
+/// did not or because reading it failed somehow.
+class AssetLoadException extends _WrappedException {
+  final AssetId id;
+
+  AssetLoadException(this.id, error, [StackTrace stackTrace])
+      : super(error, stackTrace);
+
+  String get _message => "Failed to load source asset $id";
+}
+
+/// Information about a single transform in the barback graph.
+///
+/// Identifies a single transformation in the barback graph.
+///
+/// A transformation is uniquely identified by the ID of its primary input, and
+/// the transformer that is applied to it.
+class TransformInfo {
+  /// The transformer that's run for this transform.
+  ///
+  /// This may be a [Transformer] or a [WrappingAggregateTransformer]. It may
+  /// also return additional types in the future.
+  final transformer;
+
+  /// The id of this transform's primary asset.
+  final AssetId primaryId;
+
+  TransformInfo(transformer, this.primaryId)
+      : transformer = transformer is WrappingAggregateTransformer ?
+            transformer.transformer : transformer;
+
+  bool operator==(other) =>
+      other is TransformInfo &&
+      other.transformer == transformer &&
+      other.primaryId == primaryId;
+
+  int get hashCode => transformer.hashCode ^ primaryId.hashCode;
+
+  String toString() => "$transformer on $primaryId";
+}
diff --git a/barback/lib/src/graph/asset_cascade.dart b/barback/lib/src/graph/asset_cascade.dart
new file mode 100644
index 0000000..c35d7a2
--- /dev/null
+++ b/barback/lib/src/graph/asset_cascade.dart
@@ -0,0 +1,223 @@
+// Copyright (c) 2013, 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 barback.graph.asset_cascade;
+
+import 'dart:async';
+
+import '../asset/asset.dart';
+import '../asset/asset_id.dart';
+import '../asset/asset_node.dart';
+import '../asset/asset_set.dart';
+import '../errors.dart';
+import '../log.dart';
+import '../transformer/transformer.dart';
+import '../utils.dart';
+import '../utils/cancelable_future.dart';
+import 'node_status.dart';
+import 'node_streams.dart';
+import 'package_graph.dart';
+import 'phase.dart';
+
+/// The asset cascade for an individual package.
+///
+/// This keeps track of which [Transformer]s are applied to which assets, and
+/// re-runs those transformers when their dependencies change. The transformed
+/// asset nodes are accessible via [getAssetNode].
+///
+/// A cascade consists of one or more [Phases], each of which has one or more
+/// [Transformer]s that run in parallel, potentially on the same inputs. The
+/// inputs of the first phase are the source assets for this cascade's package.
+/// The inputs of each successive phase are the outputs of the previous phase,
+/// as well as any assets that haven't yet been transformed.
+class AssetCascade {
+  /// The name of the package whose assets are managed.
+  final String package;
+
+  /// The [PackageGraph] that tracks all [AssetCascade]s for all dependencies of
+  /// the current app.
+  final PackageGraph graph;
+
+  /// The controllers for the [AssetNode]s that provide information about this
+  /// cascade's package's source assets.
+  final _sourceControllerMap = new Map<AssetId, AssetNodeController>();
+
+  /// Futures for source assets that are currently being loaded.
+  ///
+  /// These futures are cancelable so that if an asset is updated after a load
+  /// has been kicked off, the previous load can be ignored in favor of a new
+  /// one.
+  final _loadingSources = new Map<AssetId, CancelableFuture<Asset>>();
+
+  /// The list of phases in this cascade.
+  ///
+  /// This will always contain at least one phase, and the first phase will
+  /// never have any transformers. This ensures that every transformer can
+  /// request inputs from a previous phase.
+  final _phases = <Phase>[];
+
+  /// The subscription to the [Phase.onStatusChange] stream of the last [Phase]
+  /// in [_phases].
+  StreamSubscription _phaseStatusSubscription;
+
+  /// A stream that emits any errors from the cascade or the transformers.
+  ///
+  /// This emits errors as they're detected. If an error occurs in one part of
+  /// the cascade, unrelated parts will continue building.
+  Stream<BarbackException> get errors => _errorsController.stream;
+  final _errorsController =
+      new StreamController<BarbackException>.broadcast(sync: true);
+
+  /// How far along [this] is in processing its assets.
+  NodeStatus get status {
+    // Just check the last phase, since it will check all the previous phases
+    // itself.
+    return _phases.last.status;
+  }
+
+  /// The streams exposed by this cascade.
+  final _streams = new NodeStreams();
+  Stream<LogEntry> get onLog => _streams.onLog;
+  Stream<NodeStatus> get onStatusChange => _streams.onStatusChange;
+
+  /// Returns all currently-available output assets from this cascade.
+  Future<AssetSet> get availableOutputs => new Future.value(new AssetSet.from(
+      _phases.last.availableOutputs.map((node) => node.asset)));
+
+  /// Creates a new [AssetCascade].
+  ///
+  /// It loads source assets within [package] using [provider].
+  AssetCascade(this.graph, this.package) {
+    _addPhase(new Phase(this, package));
+  }
+
+  /// Gets the asset identified by [id].
+  ///
+  /// If [id] is for a generated or transformed asset, this will wait until it
+  /// has been created and return it. This means that the returned asset will
+  /// always be [AssetState.AVAILABLE].
+  ///
+  /// If the asset cannot be found, returns null.
+  Future<AssetNode> getAssetNode(AssetId id) {
+    assert(id.package == package);
+
+    var oldLastPhase = _phases.last;
+    // TODO(rnystrom): Waiting for the entire build to complete is unnecessary
+    // in some cases. Should optimize:
+    // * [id] may be generated before the compilation is finished. We should
+    //   be able to quickly check whether there are any more in-place
+    //   transformations that can be run on it. If not, we can return it early.
+    // * If [id] has never been generated and all active transformers provide
+    //   metadata about the file names of assets it can emit, we can prove that
+    //   none of them can emit [id] and fail early.
+    return oldLastPhase.getOutput(id).then((node) {
+      // The last phase may have changed if [updateSources] was called after
+      // requesting the output. In that case, we want the output from the new
+      // last phase.
+      if (_phases.last == oldLastPhase) return node;
+      return getAssetNode(id);
+    });
+  }
+
+  /// Adds [sources] to the graph's known set of source assets.
+  ///
+  /// Begins applying any transforms that can consume any of the sources. If a
+  /// given source is already known, it is considered modified and all
+  /// transforms that use it will be re-applied.
+  void updateSources(Iterable<AssetId> sources) {
+    for (var id in sources) {
+      var controller = _sourceControllerMap[id];
+      if (controller != null) {
+        controller.setDirty();
+      } else {
+        _sourceControllerMap[id] = new AssetNodeController(id);
+        _phases.first.addInput(_sourceControllerMap[id].node);
+      }
+
+      // If this source was already loading, cancel the old load, since it may
+      // return out-of-date contents for the asset.
+      if (_loadingSources.containsKey(id)) _loadingSources[id].cancel();
+
+      _loadingSources[id] = new CancelableFuture<Asset>(
+          syncFuture(() => graph.provider.getAsset(id)));
+      _loadingSources[id].whenComplete(() {
+        _loadingSources.remove(id);
+      }).then((asset) {
+        var controller = _sourceControllerMap[id].setAvailable(asset);
+      }).catchError((error, stack) {
+        reportError(new AssetLoadException(id, error, stack));
+
+        // TODO(nweiz): propagate error information through asset nodes.
+        _sourceControllerMap.remove(id).setRemoved();
+      });
+    }
+  }
+
+  /// Removes [removed] from the graph's known set of source assets.
+  void removeSources(Iterable<AssetId> removed) {
+    removed.forEach((id) {
+      // If the source was being loaded, cancel that load.
+      if (_loadingSources.containsKey(id)) _loadingSources.remove(id).cancel();
+
+      var controller = _sourceControllerMap.remove(id);
+      // Don't choke if an id is double-removed for some reason.
+      if (controller != null) controller.setRemoved();
+    });
+  }
+
+  /// Sets this cascade's transformer phases to [transformers].
+  ///
+  /// Elements of the inner iterable of [transformers] must be [Transformer]s,
+  /// [TransformerGroup]s, or [AggregateTransformer]s.
+  void updateTransformers(Iterable<Iterable> transformersIterable) {
+    var transformers = transformersIterable.toList();
+
+    // Always preserve a single phase with no transformers at the beginning of
+    // the cascade so that [TransformNode]s in the first populated phase will
+    // have something to request assets from.
+    for (var i = 0; i < transformers.length; i++) {
+      if (_phases.length > i + 1) {
+        _phases[i + 1].updateTransformers(transformers[i]);
+        continue;
+      }
+
+      var phase = _phases.last.addPhase();
+      _addPhase(phase);
+      phase.updateTransformers(transformers[i]);
+    }
+
+    for (var i = transformers.length + 1; i < _phases.length; i++) {
+      _phases[i].remove();
+    }
+    _phases.removeRange(transformers.length + 1, _phases.length);
+
+    _phaseStatusSubscription.cancel();
+    _phaseStatusSubscription = _phases.last.onStatusChange
+        .listen(_streams.changeStatus);
+  }
+
+  /// Force all [LazyTransformer]s' transforms in this cascade to begin
+  /// producing concrete assets.
+  void forceAllTransforms() {
+    for (var phase in _phases) {
+      phase.forceAllTransforms();
+    }
+  }
+
+  void reportError(BarbackException error) {
+    _errorsController.add(error);
+  }
+
+  /// Add [phase] to the end of [_phases] and watch its streams.
+  void _addPhase(Phase phase) {
+    _streams.onLogPool.add(phase.onLog);
+    if (_phaseStatusSubscription != null) _phaseStatusSubscription.cancel();
+    _phaseStatusSubscription =
+        phase.onStatusChange.listen(_streams.changeStatus);
+
+    _phases.add(phase);
+  }
+
+  String toString() => "cascade for $package";
+}
diff --git a/barback/lib/src/graph/group_runner.dart b/barback/lib/src/graph/group_runner.dart
new file mode 100644
index 0000000..f9dcaa1
--- /dev/null
+++ b/barback/lib/src/graph/group_runner.dart
@@ -0,0 +1,89 @@
+// Copyright (c) 2013, 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 barback.graph.group_runner;
+
+import 'dart:async';
+
+import '../asset/asset_node.dart';
+import '../log.dart';
+import '../transformer/transformer_group.dart';
+import '../utils/stream_pool.dart';
+import 'node_status.dart';
+import 'phase.dart';
+
+/// A class that processes all of the phases in a single transformer group.
+///
+/// A group takes many inputs, processes them, and emits many outputs.
+class GroupRunner {
+  /// The group this runner runs.
+  final TransformerGroup _group;
+
+  /// A string describing the location of [this] in the transformer graph.
+  final String _location;
+
+  /// The phases defined by this group.
+  final _phases = new List<Phase>();
+
+  /// How far along [this] is in processing its assets.
+  NodeStatus get status {
+    // Just check the last phase, since it will check all the previous phases
+    // itself.
+    return _phases.last.status;
+  }
+
+  /// A stream that emits an event every time the group's status changes.
+  Stream<NodeStatus> get onStatusChange => _onStatusChange;
+  Stream _onStatusChange;
+
+  /// A stream that emits any new assets emitted by [this].
+  ///
+  /// Assets are emitted synchronously to ensure that any changes are thoroughly
+  /// propagated as soon as they occur.
+  Stream<AssetNode> get onAsset => _onAsset;
+  Stream<AssetNode> _onAsset;
+
+  /// A stream that emits an event whenever any transforms in this group logs
+  /// an entry.
+  Stream<LogEntry> get onLog => _onLogPool.stream;
+  final _onLogPool = new StreamPool<LogEntry>.broadcast();
+
+  GroupRunner(Phase previous, this._group, this._location) {
+    _addPhase(previous.addPhase(_location), []);
+    for (var phase in _group.phases) {
+      _addPhase(_phases.last.addPhase(), phase);
+    }
+
+    _onAsset = _phases.last.onAsset;
+    _onStatusChange = _phases.last.onStatusChange;
+  }
+
+  /// Add a phase with [contents] to [this]'s list of phases.
+  ///
+  /// [contents] should be an inner [Iterable] from a [TransformGroup.phases]
+  /// value.
+  void _addPhase(Phase phase, Iterable contents) {
+    _phases.add(phase);
+    _onLogPool.add(phase.onLog);
+    phase.updateTransformers(contents);
+  }
+
+  /// Force all [LazyTransformer]s' transforms in this group to begin producing
+  /// concrete assets.
+  void forceAllTransforms() {
+    for (var phase in _phases) {
+      phase.forceAllTransforms();
+    }
+  }
+
+  /// Removes this group and all sub-phases within it.
+  void remove() {
+    _onLogPool.close();
+    for (var phase in _phases) {
+      phase.remove();
+    }
+  }
+
+  String toString() => "group in phase $_location for $_group";
+}
diff --git a/barback/lib/src/graph/node_status.dart b/barback/lib/src/graph/node_status.dart
new file mode 100644
index 0000000..a42e523
--- /dev/null
+++ b/barback/lib/src/graph/node_status.dart
@@ -0,0 +1,54 @@
+// 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 barback.graph.node_status;
+
+/// The status of a node in barback's package graph.
+///
+/// A node has three possible statuses: [IDLE], [MATERIALIZING], and [RUNNING].
+/// These are ordered from least dirty to most dirty; the [dirtier] and
+/// [dirtiest] functions make use of this ordering.
+class NodeStatus {
+  /// The node has finished its work and won't do anything else until external
+  /// input causes it to.
+  ///
+  /// For deferred nodes, this may indicate that they're finished declaring
+  /// their outputs and waiting to be forced.
+  static const IDLE = const NodeStatus("idle");
+
+  /// The node has declared its outputs but their concrete values are still
+  /// being generated.
+  ///
+  /// This is only meaningful for nodes that are or contain declaring
+  /// transformers. Note that a lazy transformer that's declared its outputs but
+  /// isn't actively working to generate them is considered [IDLE], not
+  /// [MATERIALIZING].
+  static const MATERIALIZING = const NodeStatus("materializing");
+
+  /// The node is actively working on declaring or generating its outputs.
+  ///
+  /// Declaring transformers are only considered dirty until they're finished
+  /// declaring their outputs; past that point, they're always either
+  /// [MATERIALIZING] or [IDLE]. Non-declaring transformers, by contrast, are
+  /// always either [RUNNING] or [IDLE].
+  static const RUNNING = const NodeStatus("running");
+
+  final String _name;
+
+  /// Returns the dirtiest status in [statuses].
+  static NodeStatus dirtiest(Iterable<NodeStatus> statuses) =>
+      statuses.fold(NodeStatus.IDLE,
+          (status1, status2) => status1.dirtier(status2));
+
+  const NodeStatus(this._name);
+
+  String toString() => _name;
+
+  /// Returns [this] or [other], whichever is dirtier.
+  NodeStatus dirtier(NodeStatus other) {
+    if (this == RUNNING || other == RUNNING) return RUNNING;
+    if (this == MATERIALIZING || other == MATERIALIZING) return MATERIALIZING;
+    return IDLE;
+  }
+}
\ No newline at end of file
diff --git a/barback/lib/src/graph/node_streams.dart b/barback/lib/src/graph/node_streams.dart
new file mode 100644
index 0000000..8344792
--- /dev/null
+++ b/barback/lib/src/graph/node_streams.dart
@@ -0,0 +1,66 @@
+// 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 barback.graph.node_streams;
+
+import 'dart:async';
+
+import '../asset/asset_node.dart';
+import '../log.dart';
+import '../utils/stream_pool.dart';
+import 'node_status.dart';
+
+/// A collection of streams that are common to nodes in barback's package graph.
+class NodeStreams {
+  /// A stream that emits an event every time the node's status changes.
+  ///
+  /// This will emit the new status. It's guaranteed to emit an event only when
+  /// the status changes from the previous value. To ensure this, callers should
+  /// emit status changes using [changeStatus]. The initial status is assumed to
+  /// be [NodeStatus.RUNNING].
+  Stream<NodeStatus> get onStatusChange => _onStatusChangeController.stream;
+  final _onStatusChangeController =
+      new StreamController<NodeStatus>.broadcast(sync: true);
+
+  /// A stream that emits any new assets produced by the node.
+  ///
+  /// Assets are emitted synchronously to ensure that any changes are thoroughly
+  /// propagated as soon as they occur.
+  Stream<AssetNode> get onAsset => onAssetPool.stream;
+  final onAssetPool = new StreamPool<AssetNode>.broadcast();
+  final onAssetController =
+      new StreamController<AssetNode>.broadcast(sync: true);
+
+  /// A stream that emits an event whenever any the node logs an entry.
+  Stream<LogEntry> get onLog => onLogPool.stream;
+  final onLogPool = new StreamPool<LogEntry>.broadcast();
+  final onLogController = new StreamController<LogEntry>.broadcast(sync: true);
+
+  var _previousStatus = NodeStatus.RUNNING;
+
+  /// Whether [this] has been closed.
+  bool get isClosed => onAssetController.isClosed;
+
+  NodeStreams() {
+    onAssetPool.add(onAssetController.stream);
+    onLogPool.add(onLogController.stream);
+  }
+
+  /// Emits a status change notification via [onStatusChange].
+  ///
+  /// This guarantees that a change notification won't be emitted if the status
+  /// didn't actually change.
+  void changeStatus(NodeStatus status) {
+    if (_previousStatus != status) _onStatusChangeController.add(status);
+  }
+
+  /// Closes all the streams.
+  void close() {
+    _onStatusChangeController.close();
+    onAssetController.close();
+    onAssetPool.close();
+    onLogController.close();
+    onLogPool.close();
+  }
+}
diff --git a/barback/lib/src/graph/package_graph.dart b/barback/lib/src/graph/package_graph.dart
new file mode 100644
index 0000000..e66dc44
--- /dev/null
+++ b/barback/lib/src/graph/package_graph.dart
@@ -0,0 +1,282 @@
+// Copyright (c) 2013, 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 barback.graph.package_graph;
+
+import 'dart:async';
+import 'dart:collection';
+
+import '../asset/asset_id.dart';
+import '../asset/asset_node.dart';
+import '../asset/asset_set.dart';
+import '../build_result.dart';
+import '../errors.dart';
+import '../log.dart';
+import '../package_provider.dart';
+import '../utils.dart';
+import 'asset_cascade.dart';
+import 'node_status.dart';
+import 'static_asset_cascade.dart';
+
+/// The collection of [AssetCascade]s for an entire application.
+///
+/// This tracks each package's [AssetCascade] and routes asset requests between
+/// them.
+class PackageGraph {
+  /// The provider that exposes asset and package information.
+  final PackageProvider provider;
+
+  /// The [AssetCascade] for each package.
+  final _cascades = <String, AssetCascade>{};
+
+  /// A stream that emits a [BuildResult] each time the build is completed,
+  /// whether or not it succeeded.
+  ///
+  /// This will emit a result only once every package's [AssetCascade] has
+  /// finished building.
+  ///
+  /// If an unexpected error in barback itself occurs, it will be emitted
+  /// through this stream's error channel.
+  Stream<BuildResult> get results => _resultsController.stream;
+  final _resultsController =
+      new StreamController<BuildResult>.broadcast(sync: true);
+
+  /// A stream that emits any errors from the graph or the transformers.
+  ///
+  /// This emits errors as they're detected. If an error occurs in one part of
+  /// the graph, unrelated parts will continue building.
+  ///
+  /// This will not emit programming errors from barback itself. Those will be
+  /// emitted through the [results] stream's error channel.
+  Stream<BarbackException> get errors => _errors;
+  Stream<BarbackException> _errors;
+
+  /// The stream of [LogEntry] objects used to report transformer log entries.
+  Stream<LogEntry> get log => _logController.stream;
+  final _logController = new StreamController<LogEntry>.broadcast(sync: true);
+
+  /// How far along [this] is in processing its assets.
+  NodeStatus get _status => NodeStatus.dirtiest(
+      _cascades.values.map((cascade) => cascade.status));
+
+  /// Whether a [BuildResult] is scheduled to be emitted on [results] (see
+  /// [_tryScheduleResult]).
+  bool _resultScheduled = false;
+
+  /// The most recent [BuildResult] emitted on [results].
+  BuildResult _lastResult;
+
+  // TODO(nweiz): This can have bogus errors if an error is created and resolved
+  // in the space of one build.
+  /// The errors that have occurred since the current build started.
+  ///
+  /// This will be empty if no build is occurring.
+  final _accumulatedErrors = new Queue<BarbackException>();
+
+  /// The most recent error emitted from a cascade's result stream.
+  ///
+  /// This is used to pipe an unexpected error from a build to the resulting
+  /// [Future] returned by [getAllAssets].
+  var _lastUnexpectedError;
+
+  /// The stack trace for [_lastUnexpectedError].
+  StackTrace _lastUnexpectedErrorTrace;
+
+  /// Creates a new [PackageGraph] that will transform assets in all packages
+  /// made available by [provider].
+  PackageGraph(this.provider) {
+    _inErrorZone(() {
+      for (var package in provider.packages) {
+        var cascade = new AssetCascade(this, package);
+        _cascades[package] = cascade;
+        cascade.onLog.listen(_onLog);
+        cascade.onStatusChange.listen((status) {
+          if (status == NodeStatus.IDLE) _tryScheduleResult();
+        });
+      }
+
+      if (provider is StaticPackageProvider) {
+        StaticPackageProvider staticProvider = provider;
+        for (var package in staticProvider.staticPackages) {
+          if (_cascades.containsKey(package)) {
+            throw new StateError('Package "$package" is in both '
+                'PackageProvider.packages and PackageProvider.staticPackages.');
+          }
+
+          var cascade = new StaticAssetCascade(this, package);
+          _cascades[package] = cascade;
+        }
+      }
+
+      _errors = mergeStreams(_cascades.values.map((cascade) => cascade.errors),
+          broadcast: true);
+      _errors.listen(_accumulatedErrors.add);
+
+      // Make sure a result gets scheduled even if there are no cascades or all
+      // of them are static.
+      if (provider.packages.isEmpty) _tryScheduleResult();
+    });
+  }
+
+  /// Gets the asset node identified by [id].
+  ///
+  /// If [id] is for a generated or transformed asset, this will wait until it
+  /// has been created and return it. This means that the returned asset will
+  /// always be [AssetState.AVAILABLE].
+  ///
+  /// If the asset cannot be found, returns null.
+  Future<AssetNode> getAssetNode(AssetId id) {
+    return _inErrorZone(() {
+      var cascade = _cascades[id.package];
+      if (cascade != null) return cascade.getAssetNode(id);
+      return new Future.value(null);
+    });
+  }
+
+  /// Gets all output assets.
+  ///
+  /// If a build is currently in progress, waits until it completes. The
+  /// returned future will complete with an error if the build is not
+  /// successful.
+  ///
+  /// Any transforms using [LazyTransformer]s will be forced to generate
+  /// concrete outputs, and those outputs will be returned.
+  Future<AssetSet> getAllAssets() {
+    for (var cascade in _cascades.values) {
+      _inErrorZone(() => cascade.forceAllTransforms());
+    }
+
+    if (_status != NodeStatus.IDLE) {
+      // A build is still ongoing, so wait for it to complete and try again.
+      return results.first.then((_) => getAllAssets());
+    }
+
+    // If an unexpected error occurred, complete with that.
+    if (_lastUnexpectedError != null) {
+      var error = _lastUnexpectedError;
+      _lastUnexpectedError = null;
+      return new Future.error(error, _lastUnexpectedErrorTrace);
+    }
+
+    // If the last build completed with an error, complete the future with it.
+    if (!_lastResult.succeeded) {
+      return new Future.error(BarbackException.aggregate(_lastResult.errors));
+    }
+
+    // Otherwise, return all of the final output assets.
+    return Future.wait(_cascades.values.map(
+            (cascade) => cascade.availableOutputs))
+          .then((assetSets) {
+      var assets = unionAll(assetSets.map((assetSet) => assetSet.toSet()));
+      return new Future.value(new AssetSet.from(assets));
+    });
+  }
+
+  /// Adds [sources] to the graph's known set of source assets.
+  ///
+  /// Begins applying any transforms that can consume any of the sources. If a
+  /// given source is already known, it is considered modified and all
+  /// transforms that use it will be re-applied.
+  void updateSources(Iterable<AssetId> sources) {
+    groupBy(sources, (id) => id.package).forEach((package, ids) {
+      var cascade = _cascades[package];
+      if (cascade == null) throw new ArgumentError("Unknown package $package.");
+      _inErrorZone(() => cascade.updateSources(ids));
+    });
+
+    // It's possible for adding sources not to cause any processing. The user
+    // still expects there to be a build, though, so we emit one immediately.
+    _tryScheduleResult();
+  }
+
+  /// Removes [removed] from the graph's known set of source assets.
+  void removeSources(Iterable<AssetId> sources) {
+    groupBy(sources, (id) => id.package).forEach((package, ids) {
+      var cascade = _cascades[package];
+      if (cascade == null) throw new ArgumentError("Unknown package $package.");
+      _inErrorZone(() => cascade.removeSources(ids));
+    });
+
+    // It's possible for removing sources not to cause any processing. The user
+    // still expects there to be a build, though, so we emit one immediately.
+    _tryScheduleResult();
+  }
+
+  void updateTransformers(String package, Iterable<Iterable> transformers) {
+    _inErrorZone(() => _cascades[package].updateTransformers(transformers));
+
+    // It's possible for updating transformers not to cause any processing. The
+    // user still expects there to be a build, though, so we emit one
+    // immediately.
+    _tryScheduleResult();
+  }
+
+  /// A handler for a log entry from an [AssetCascade].
+  void _onLog(LogEntry entry) {
+    if (entry.level == LogLevel.ERROR) {
+      // TODO(nweiz): keep track of stack chain.
+      _accumulatedErrors.add(
+          new TransformerException(entry.transform, entry.message, null));
+    }
+
+    if (_logController.hasListener) {
+      _logController.add(entry);
+    } else if (entry.level != LogLevel.FINE) {
+      // No listeners, so just print entry.
+      var buffer = new StringBuffer();
+      buffer.write("[${entry.level} ${entry.transform}] ");
+
+      if (entry.span != null) {
+        buffer.write(entry.span.message(entry.message));
+      } else {
+        buffer.write(entry.message);
+      }
+
+      print(buffer);
+    }
+  }
+
+  /// If [this] is done processing, schedule a [BuildResult] to be emitted on
+  /// [results].
+  ///
+  /// This schedules the result (as opposed to just emitting one directly on
+  /// [BuildResult]) to ensure that calling multiple functions synchronously
+  /// produces only a single [BuildResult].
+  void _tryScheduleResult() {
+    if (_status != NodeStatus.IDLE) return;
+    if (_resultScheduled) return;
+
+    _resultScheduled = true;
+    newFuture(() {
+      _resultScheduled = false;
+      if (_status != NodeStatus.IDLE) return;
+
+      _lastResult = new BuildResult(_accumulatedErrors);
+      _accumulatedErrors.clear();
+      _resultsController.add(_lastResult);
+    });
+  }
+
+  /// Run [body] in an error-handling [Zone] and pipe any unexpected errors to
+  /// the error channel of [results].
+  ///
+  /// [body] can return a value or a [Future] that will be piped to the returned
+  /// [Future]. If it throws a [BarbackException], that exception will be piped
+  /// to the returned [Future] as well. Any other exceptions will be piped to
+  /// [results].
+  Future _inErrorZone(body()) {
+    var completer = new Completer.sync();
+    runZoned(() {
+      syncFuture(body).then(completer.complete).catchError((error, stackTrace) {
+        if (error is! BarbackException) throw error;
+        completer.completeError(error, stackTrace);
+      });
+    }, onError: (error, stackTrace) {
+      _lastUnexpectedError = error;
+      _lastUnexpectedErrorTrace = stackTrace;
+      _resultsController.addError(error, stackTrace);
+    });
+    return completer.future;
+  }
+}
diff --git a/barback/lib/src/graph/phase.dart b/barback/lib/src/graph/phase.dart
new file mode 100644
index 0000000..4bfc5e1
--- /dev/null
+++ b/barback/lib/src/graph/phase.dart
@@ -0,0 +1,396 @@
+// Copyright (c) 2013, 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 barback.graph.phase;
+
+import 'dart:async';
+
+import '../asset/asset_id.dart';
+import '../asset/asset_node.dart';
+import '../asset/asset_node_set.dart';
+import '../errors.dart';
+import '../log.dart';
+import '../transformer/aggregate_transformer.dart';
+import '../transformer/transformer.dart';
+import '../transformer/transformer_group.dart';
+import '../utils.dart';
+import '../utils/multiset.dart';
+import 'asset_cascade.dart';
+import 'group_runner.dart';
+import 'node_status.dart';
+import 'node_streams.dart';
+import 'phase_forwarder.dart';
+import 'phase_output.dart';
+import 'transformer_classifier.dart';
+
+/// One phase in the ordered series of transformations in an [AssetCascade].
+///
+/// Each phase can access outputs from previous phases and can in turn pass
+/// outputs to later phases. Phases are processed strictly serially. All
+/// transforms in a phase will be complete before moving on to the next phase.
+/// Within a single phase, all transforms will be run in parallel.
+///
+/// Building can be interrupted between phases. For example, a source is added
+/// which starts the background process. Sometime during, say, phase 2 (which
+/// is running asynchronously) that source is modified. When the process queue
+/// goes to advance to phase 3, it will see that modification and start the
+/// waterfall from the beginning again.
+class Phase {
+  /// The cascade that owns this phase.
+  final AssetCascade cascade;
+
+  /// A string describing the location of [this] in the transformer graph.
+  final String _location;
+
+  /// The index of [this] in its parent cascade or group.
+  final int _index;
+
+  /// The groups for this phase.
+  final _groups = new Map<TransformerGroup, GroupRunner>();
+
+  /// The inputs for this phase.
+  ///
+  /// For the first phase, these will be the source assets. For all other
+  /// phases, they will be the outputs from the previous phase.
+  final _inputs = new AssetNodeSet();
+
+  /// The transformer classifiers for this phase.
+  ///
+  /// The keys can be either [Transformer]s or [AggregateTransformer]s.
+  final _classifiers = new Map<dynamic, TransformerClassifier>();
+
+  /// The forwarders for this phase.
+  final _forwarders = new Map<AssetId, PhaseForwarder>();
+
+  /// The outputs for this phase.
+  final _outputs = new Map<AssetId, PhaseOutput>();
+
+  /// The set of all [AssetNode.origin] properties of the input assets for this
+  /// phase.
+  ///
+  /// This is used to determine which assets have been passed unmodified through
+  /// [_classifiers] or [_groups]. It's possible that a given asset was consumed
+  /// by a group and not an individual transformer, and so shouldn't be
+  /// forwarded through the phase as a whole.
+  ///
+  /// In order to detect whether an output has been forwarded through a group or
+  /// a classifier, we must be able to distinguish it from other outputs with
+  /// the same id. To do so, we check if its origin is in [_inputOrigins]. If
+  /// so, it's been forwarded unmodified.
+  final _inputOrigins = new Multiset<AssetNode>();
+
+  /// The streams exposed by this phase.
+  final _streams = new NodeStreams();
+  Stream<NodeStatus> get onStatusChange => _streams.onStatusChange;
+  Stream<AssetNode> get onAsset => _streams.onAsset;
+  Stream<LogEntry> get onLog => _streams.onLog;
+
+  /// How far along [this] is in processing its assets.
+  NodeStatus get status {
+    // Before any transformers are added, the phase should be dirty if and only
+    // if any input is dirty.
+    if (_classifiers.isEmpty && _groups.isEmpty && previous == null) {
+      return _inputs.any((input) => input.state.isDirty) ?
+          NodeStatus.RUNNING : NodeStatus.IDLE;
+    }
+
+    var classifierStatus = NodeStatus.dirtiest(
+        _classifiers.values.map((classifier) => classifier.status));
+    var groupStatus = NodeStatus.dirtiest(
+        _groups.values.map((group) => group.status));
+    return (previous == null ? NodeStatus.IDLE : previous.status)
+        .dirtier(classifierStatus)
+        .dirtier(groupStatus);
+  }
+
+  /// The previous phase in the cascade, or null if this is the first phase.
+  final Phase previous;
+
+  /// The subscription to [previous]'s [onStatusChange] stream.
+  StreamSubscription _previousStatusSubscription;
+
+  /// The subscription to [previous]'s [onAsset] stream.
+  StreamSubscription<AssetNode> _previousOnAssetSubscription;
+
+  final _inputSubscriptions = new Set<StreamSubscription>();
+
+  /// A map of asset ids to completers for [getInput] requests.
+  ///
+  /// If an asset node is requested before it's available, we put a completer in
+  /// this map to wait for the asset to be generated. If it's not generated, the
+  /// completer should complete to `null`.
+  final _pendingOutputRequests = new Map<AssetId, Completer<AssetNode>>();
+
+  /// Returns all currently-available output assets for this phase.
+  Set<AssetNode> get availableOutputs {
+    return _outputs.values
+        .map((output) => output.output)
+        .where((node) => node.state.isAvailable)
+        .toSet();
+  }
+
+  // TODO(nweiz): Rather than passing the cascade and the phase everywhere,
+  // create an interface that just exposes [getInput]. Emit errors via
+  // [AssetNode]s.
+  Phase(AssetCascade cascade, String location)
+      : this._(cascade, location, 0);
+
+  Phase._(this.cascade, this._location, this._index, [this.previous]) {
+    if (previous != null) {
+      _previousOnAssetSubscription = previous.onAsset.listen(addInput);
+      _previousStatusSubscription = previous.onStatusChange
+          .listen((_) => _streams.changeStatus(status));
+    }
+
+    onStatusChange.listen((status) {
+      if (status == NodeStatus.RUNNING) return;
+
+      // All the previous phases have finished declaring or producing their
+      // outputs. If anyone's still waiting for outputs, cut off the wait; we
+      // won't be generating them, at least until a source asset changes.
+      for (var completer in _pendingOutputRequests.values) {
+        completer.complete(null);
+      }
+      _pendingOutputRequests.clear();
+    });
+  }
+
+  /// Adds a new asset as an input for this phase.
+  ///
+  /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase
+  /// will automatically begin determining which transforms can consume it as a
+  /// primary input. The transforms themselves won't be applied until [process]
+  /// is called, however.
+  ///
+  /// This should only be used for brand-new assets or assets that have been
+  /// removed and re-created. The phase will automatically handle updated assets
+  /// using the [AssetNode.onStateChange] stream.
+  void addInput(AssetNode node) {
+    // Each group is one channel along which an asset may be forwarded, as is
+    // each transformer.
+    var forwarder = new PhaseForwarder(
+        node, _classifiers.length, _groups.length);
+    _forwarders[node.id] = forwarder;
+    forwarder.onAsset.listen(_handleOutputWithoutForwarder);
+    if (forwarder.output != null) {
+      _handleOutputWithoutForwarder(forwarder.output);
+    }
+
+    _inputOrigins.add(node.origin);
+    _inputs.add(node);
+    _inputSubscriptions.add(node.onStateChange.listen((state) {
+      if (state.isRemoved) {
+        _inputOrigins.remove(node.origin);
+        _forwarders.remove(node.id).remove();
+      }
+      _streams.changeStatus(status);
+    }));
+
+    for (var classifier in _classifiers.values) {
+      classifier.addInput(node);
+    }
+  }
+
+  // TODO(nweiz): If the output is available when this is called, it's
+  // theoretically possible for it to become unavailable between the call and
+  // the return. If it does so, it won't trigger the rebuilding process. To
+  // avoid this, we should have this and the methods it calls take explicit
+  // callbacks, as in [AssetNode.whenAvailable].
+  /// Gets the asset node for an output [id].
+  ///
+  /// If [id] is for a generated or transformed asset, this will wait until it
+  /// has been created and return it. This means that the returned asset will
+  /// always be [AssetState.AVAILABLE].
+  /// 
+  /// If the output cannot be found, returns null.
+  Future<AssetNode> getOutput(AssetId id) {
+    return syncFuture(() {
+      if (id.package != cascade.package) return cascade.graph.getAssetNode(id);
+      if (_outputs.containsKey(id)) {
+        var output = _outputs[id].output;
+        // If the requested output is available, we can just return it.
+        if (output.state.isAvailable) return output;
+
+        // If the requested output exists but isn't yet available, wait to see
+        // if it becomes available. If it's removed before becoming available,
+        // try again, since it could be generated again.
+        output.force();
+        return output.whenAvailable((_) {
+          return output;
+        }).catchError((error) {
+          if (error is! AssetNotFoundException) throw error;
+          return getOutput(id);
+        });
+      }
+
+      // If this phase and the previous phases are fully declared or done, the
+      // requested output won't be generated and we can safely return null.
+      if (status != NodeStatus.RUNNING) return null;
+
+      // Otherwise, store a completer for the asset node. If it's generated in
+      // the future, we'll complete this completer.
+      var completer = _pendingOutputRequests.putIfAbsent(id,
+          () => new Completer.sync());
+      return completer.future;
+    });
+  }
+
+  /// Set this phase's transformers to [transformers].
+  void updateTransformers(Iterable transformers) {
+    var newTransformers = transformers
+        .where((op) => op is Transformer || op is AggregateTransformer)
+        .toSet();
+    var oldTransformers = _classifiers.keys.toSet();
+    for (var removed in oldTransformers.difference(newTransformers)) {
+      _classifiers.remove(removed).remove();
+    }
+
+    for (var transformer in newTransformers.difference(oldTransformers)) {
+      var classifier = new TransformerClassifier(
+          this, transformer, "$_location.$_index");
+      _classifiers[transformer] = classifier;
+      classifier.onAsset.listen(_handleOutput);
+      _streams.onLogPool.add(classifier.onLog);
+      classifier.onStatusChange.listen((_) => _streams.changeStatus(status));
+      for (var input in _inputs) {
+        classifier.addInput(input);
+      }
+    }
+
+    var newGroups = transformers.where((op) => op is TransformerGroup)
+        .toSet();
+    var oldGroups = _groups.keys.toSet();
+    for (var removed in oldGroups.difference(newGroups)) {
+      _groups.remove(removed).remove();
+    }
+
+    for (var added in newGroups.difference(oldGroups)) {
+      var runner = new GroupRunner(previous, added, "$_location.$_index");
+      _groups[added] = runner;
+      runner.onAsset.listen(_handleOutput);
+      _streams.onLogPool.add(runner.onLog);
+      runner.onStatusChange.listen((_) => _streams.changeStatus(status));
+    }
+
+    for (var forwarder in _forwarders.values) {
+      forwarder.updateTransformers(_classifiers.length, _groups.length);
+    }
+
+    _streams.changeStatus(status);
+  }
+
+  /// Force all [LazyTransformer]s' transforms in this phase to begin producing
+  /// concrete assets.
+  void forceAllTransforms() {
+    for (var classifier in _classifiers.values) {
+      classifier.forceAllTransforms();
+    }
+
+    for (var group in _groups.values) {
+      group.forceAllTransforms();
+    }
+  }
+
+  /// Add a new phase after this one.
+  ///
+  /// The new phase will have a location annotation describing its place in the
+  /// package graph. By default, this annotation will describe it as being
+  /// directly after [this]. If [location] is passed, though, it's described as
+  /// being the first phase in that location.
+  Phase addPhase([String location]) {
+    var index = 0;
+    if (location == null) {
+      location = _location;
+      index = _index + 1;
+    }
+
+    var next = new Phase._(cascade, location, index, this);
+    for (var output in _outputs.values.toList()) {
+      // Remove [output]'s listeners because now they should get the asset from
+      // [next], rather than this phase. Any transforms consuming [output] will
+      // be re-run and will consume the output from the new final phase.
+      output.removeListeners();
+    }
+    return next;
+  }
+
+  /// Mark this phase as removed.
+  ///
+  /// This will remove all the phase's outputs.
+  void remove() {
+    for (var classifier in _classifiers.values.toList()) {
+      classifier.remove();
+    }
+    for (var group in _groups.values) {
+      group.remove();
+    }
+    _streams.close();
+    for (var subscription in _inputSubscriptions) {
+      subscription.cancel();
+    }
+    if (_previousStatusSubscription != null) {
+      _previousStatusSubscription.cancel();
+    }
+    if (_previousOnAssetSubscription != null) {
+      _previousOnAssetSubscription.cancel();
+    }
+  }
+
+  /// Add [asset] as an output of this phase.
+  void _handleOutput(AssetNode asset) {
+    if (_inputOrigins.contains(asset.origin)) {
+      _forwarders[asset.id].addIntermediateAsset(asset);
+    } else {
+      _handleOutputWithoutForwarder(asset);
+    }
+  }
+
+  /// Add [asset] as an output of this phase without checking if it's a
+  /// forwarded asset.
+  void _handleOutputWithoutForwarder(AssetNode asset) {
+    if (_outputs.containsKey(asset.id)) {
+      _outputs[asset.id].add(asset);
+    } else {
+      _outputs[asset.id] = new PhaseOutput(this, asset, "$_location.$_index");
+      _outputs[asset.id].onAsset.listen(_emit,
+          onDone: () => _outputs.remove(asset.id));
+      _emit(_outputs[asset.id].output);
+    }
+
+    var exception = _outputs[asset.id].collisionException;
+    if (exception != null) cascade.reportError(exception);
+  }
+
+  /// Emit [asset] as an output of this phase.
+  ///
+  /// This should be called after [_handleOutput], so that collisions are
+  /// resolved.
+  void _emit(AssetNode asset) {
+    _streams.onAssetController.add(asset);
+    _providePendingAsset(asset);
+  }
+
+  /// Provide an asset to a pending [getOutput] call.
+  void _providePendingAsset(AssetNode asset) {
+    // If anyone's waiting for this asset, provide it to them.
+    var request = _pendingOutputRequests.remove(asset.id);
+    if (request == null) return;
+
+    if (asset.state.isAvailable) {
+      request.complete(asset);
+      return;
+    }
+
+    // A lazy asset may be emitted while still dirty. If so, we wait until it's
+    // either available or removed before trying again to access it.
+    assert(asset.state.isDirty);
+    asset.force();
+    asset.whenStateChanges().then((state) {
+      if (state.isRemoved) return getOutput(asset.id);
+      return asset;
+    }).then(request.complete).catchError(request.completeError);
+  }
+
+  String toString() => "phase $_location.$_index";
+}
diff --git a/barback/lib/src/graph/phase_forwarder.dart b/barback/lib/src/graph/phase_forwarder.dart
new file mode 100644
index 0000000..e70aff3
--- /dev/null
+++ b/barback/lib/src/graph/phase_forwarder.dart
@@ -0,0 +1,134 @@
+// Copyright (c) 2013, 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 barback.graph.phase_forwarder;
+
+import 'dart:async';
+
+import '../asset/asset_node.dart';
+import '../asset/asset_node_set.dart';
+
+/// A class that takes care of forwarding assets within a phase.
+///
+/// Each phase contains one or more channels that process its input assets. Each
+/// non-grouped transformer for that phase is a channel, each [TransformerGroup]
+/// in that phase is another, and the source node is the final channel. For each
+/// input asset, each channel individually decides whether to forward that asset
+/// based on whether that channel uses it. If a channel does decide to forward
+/// an asset, we call that forwarded asset an "intermediate forwarded asset" to
+/// distinguish it from the output of a [PhaseForwarder].
+///
+/// All intermediate assets with a given origin are provided to a single
+/// [PhaseForwarder] via [addIntermediateAsset]. This forwarder then determines
+/// whether all channels in the phase produced intermediate assets. If so, that
+/// means the input asset wasn't consumed by any channel, so the
+/// [PhaseForwarder] forwards it again, producing an output which we'll call the
+/// "final forwarded asset".
+///
+/// A final forwarded asset will be available only if all of the intermediate
+/// forwarded assets are themselves available. If any of the intermediate assets
+/// are dirty, the final asset will also be marked dirty.
+class PhaseForwarder {
+  /// The number of channels to forward, counting the source node.
+  int _numChannels;
+
+  /// The intermediate forwarded assets.
+  final _intermediateAssets = new AssetNodeSet();
+
+  /// The final forwarded asset.
+  ///
+  /// This will be null if the asset is not being forwarded.
+  AssetNode get output =>
+      _outputController == null ? null : _outputController.node;
+  AssetNodeController _outputController;
+
+  /// A stream that emits an event whenever [this] starts producing a final
+  /// forwarded asset.
+  ///
+  /// Whenever this stream emits an event, the value will be identical to
+  /// [output].
+  Stream<AssetNode> get onAsset => _onAssetController.stream;
+  final _onAssetController =
+      new StreamController<AssetNode>.broadcast(sync: true);
+
+  /// Creates a phase forwarder forwarding nodes that come from [node] across
+  /// [numTransformers] transformers and [numGroups] groups.
+  ///
+  /// [node] is passed in explicitly so that it can be forwarded if there are no
+  /// other channels.
+  PhaseForwarder(AssetNode node, int numTransformers, int numGroups)
+      : _numChannels = numTransformers + numGroups + 1 {
+    addIntermediateAsset(node);
+  }
+
+  /// Notify the forwarder that the number of transformer and group channels has
+  /// changed.
+  void updateTransformers(int numTransformers, int numGroups) {
+    // Add one channel for the source node.
+    _numChannels = numTransformers + numGroups + 1;
+    _adjustOutput();
+  }
+
+  /// Adds an intermediate forwarded asset to [this].
+  ///
+  /// [asset] must have the same origin as all other intermediate forwarded
+  /// assets.
+  void addIntermediateAsset(AssetNode asset) {
+    if (_intermediateAssets.isNotEmpty) {
+      assert(asset.origin == _intermediateAssets.first.origin);
+    }
+
+    _intermediateAssets.add(asset);
+    asset.onStateChange.listen((_) => _adjustOutput());
+
+    _adjustOutput();
+  }
+
+  /// Mark this forwarder as removed.
+  ///
+  /// This will remove [output] if it exists.
+  void remove() {
+    if (_outputController != null) {
+      _outputController.setRemoved();
+      _outputController = null;
+    }
+    _onAssetController.close();
+  }
+
+  /// Adjusts [output] to ensure that it accurately reflects the current state
+  /// of the intermediate forwarded assets.
+  void _adjustOutput() {
+    assert(_intermediateAssets.length <= _numChannels);
+    assert(!_intermediateAssets.any((asset) => asset.state.isRemoved));
+
+    // If there are any channels that haven't forwarded an intermediate asset,
+    // we shouldn't forward a final asset. If we are currently, remove
+    // it.
+    if (_intermediateAssets.length < _numChannels) {
+      if (_outputController == null) return;
+      _outputController.setRemoved();
+      _outputController = null;
+      return;
+    }
+
+    // If there isn't a final asset being forwarded yet, we should forward one.
+    // It should be dirty iff any of the intermediate assets are dirty.
+    if (_outputController == null) {
+      var finalAsset = _intermediateAssets.firstWhere(
+          (asset) => asset.state.isDirty,
+          orElse: () => _intermediateAssets.first);
+      _outputController = new AssetNodeController.from(finalAsset);
+      _onAssetController.add(output);
+      return;
+    }
+
+    // If we're already forwarding a final asset, set it dirty iff any of the
+    // intermediate assets are dirty.
+    if (_intermediateAssets.any((asset) => asset.state.isDirty)) {
+      if (!_outputController.node.state.isDirty) _outputController.setDirty();
+    } else if (!_outputController.node.state.isAvailable) {
+      _outputController.setAvailable(_intermediateAssets.first.asset);
+    }
+  }
+}
diff --git a/barback/lib/src/graph/phase_output.dart b/barback/lib/src/graph/phase_output.dart
new file mode 100644
index 0000000..3d6460b
--- /dev/null
+++ b/barback/lib/src/graph/phase_output.dart
@@ -0,0 +1,114 @@
+// Copyright (c) 2013, 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 barback.graph.phase_output;
+
+import 'dart:async';
+import 'dart:collection';
+
+import '../asset/asset_forwarder.dart';
+import '../asset/asset_node.dart';
+import '../errors.dart';
+import 'phase.dart';
+
+/// A class that handles a single output of a phase.
+///
+/// Normally there's only a single [AssetNode] for a phase's output, but it's
+/// possible that multiple transformers in the same phase emit assets with the
+/// same id, causing collisions. This handles those collisions by forwarding the
+/// chronologically first asset.
+///
+/// When the asset being forwarding changes, the old value of [output] will be
+/// marked as removed and a new value will replace it. Users of this class can
+/// be notified of this using [onAsset].
+class PhaseOutput {
+  /// The phase for which this is an output.
+  final Phase _phase;
+
+  /// A string describing the location of [this] in the transformer graph.
+  final String _location;
+
+  /// The asset node for this output.
+  AssetNode get output => _outputForwarder.node;
+  AssetForwarder _outputForwarder;
+
+  /// A stream that emits an [AssetNode] each time this output starts forwarding
+  /// a new asset.
+  Stream<AssetNode> get onAsset => _onAssetController.stream;
+  final _onAssetController =
+      new StreamController<AssetNode>.broadcast(sync: true);
+
+  /// The assets for this output.
+  ///
+  /// If there's no collision, this will only have one element. Otherwise, it
+  /// will be ordered by which asset was added first.
+  final _assets = new Queue<AssetNode>();
+
+  /// The [AssetCollisionException] for this output, or null if there is no
+  /// collision currently.
+  AssetCollisionException get collisionException {
+    if (_assets.length == 1) return null;
+    return new AssetCollisionException(
+        _assets.where((asset) => asset.transform != null)
+            .map((asset) => asset.transform.info),
+        output.id);
+  }
+
+  PhaseOutput(this._phase, AssetNode output, this._location)
+      : _outputForwarder = new AssetForwarder(output) {
+    assert(!output.state.isRemoved);
+    add(output);
+  }
+
+  /// Adds an asset node as an output with this id.
+  void add(AssetNode node) {
+    assert(node.id == output.id);
+    assert(!output.state.isRemoved);
+    _assets.add(node);
+    _watchAsset(node);
+  }
+
+  /// Removes all existing listeners on [output] without actually closing
+  /// [this].
+  ///
+  /// This marks [output] as removed, but immediately replaces it with a new
+  /// [AssetNode] in the same state as the old output. This is used when adding
+  /// a new [Phase] to cause consumers of the prior phase's outputs to be to
+  /// start consuming the new phase's outputs instead.
+  void removeListeners() {
+    _outputForwarder.close();
+    _outputForwarder = new AssetForwarder(_assets.first);
+    _onAssetController.add(output);
+  }
+
+  /// Watches [node] to adjust [_assets] and [output] when it's removed.
+  void _watchAsset(AssetNode node) {
+    node.whenRemoved(() {
+      if (_assets.length == 1) {
+        assert(_assets.single == node);
+        _outputForwarder.close();
+        _onAssetController.close();
+        return;
+      }
+
+      // If there was more than one asset, we're resolving a collision --
+      // possibly partially.
+      var wasFirst = _assets.first == node;
+      _assets.remove(node);
+
+      // If this was the first asset, we replace it with the next asset
+      // (chronologically).
+      if (wasFirst) removeListeners();
+
+      // If there's still a collision, report it. This lets the user know if
+      // they've successfully resolved the collision or not.
+      if (_assets.length > 1) {
+        // TODO(nweiz): report this through the output asset.
+        _phase.cascade.reportError(collisionException);
+      }
+    });
+  }
+
+  String toString() => "phase output in $_location for $output";
+}
diff --git a/barback/lib/src/graph/static_asset_cascade.dart b/barback/lib/src/graph/static_asset_cascade.dart
new file mode 100644
index 0000000..5aab920
--- /dev/null
+++ b/barback/lib/src/graph/static_asset_cascade.dart
@@ -0,0 +1,81 @@
+// 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 barback.graph.static_asset_cascade;
+
+import 'dart:async';
+
+import '../asset/asset_id.dart';
+import '../asset/asset_node.dart';
+import '../asset/asset_set.dart';
+import '../errors.dart';
+import '../log.dart';
+import '../package_provider.dart';
+import 'asset_cascade.dart';
+import 'node_status.dart';
+import 'package_graph.dart';
+
+/// An asset cascade for a static package.
+///
+/// A static package is known to have no transformers and no changes to its
+/// assets. This allows this class to lazily and efficiently provide assets to
+/// the rest of the package graph.
+class StaticAssetCascade implements AssetCascade {
+  final String package;
+
+  final PackageGraph graph;
+
+  /// All sources that have been requested from the provider.
+  final _sources = new Map<AssetId, Future<AssetNode>>();
+
+  StaticAssetCascade(this.graph, this.package);
+
+  Stream<BarbackException> get errors => _errorsController.stream;
+  final _errorsController =
+      new StreamController<BarbackException>.broadcast(sync: true);
+
+  final status = NodeStatus.IDLE;
+
+  final onLog = new StreamController<LogEntry>.broadcast().stream;
+  final onStatusChange = new StreamController<LogEntry>.broadcast().stream;
+
+  Future<AssetSet> get availableOutputs {
+    var provider = graph.provider as StaticPackageProvider;
+    return provider.getAllAssetIds(package).asyncMap(provider.getAsset).toList()
+        .then((assets) => new AssetSet.from(assets));
+  }
+
+  Future<AssetNode> getAssetNode(AssetId id) {
+    return _sources.putIfAbsent(id, () {
+      return graph.provider.getAsset(id).then((asset) {
+        return new AssetNodeController.available(asset).node;
+      }).catchError((error, stackTrace) {
+        if (error is! AssetNotFoundException) {
+          reportError(new AssetLoadException(id, error, stackTrace));
+        }
+
+        // TODO(nweiz): propagate error information through asset nodes.
+        return null;
+      });
+    });
+  }
+
+  void updateSources(Iterable<AssetId> sources) =>
+      throw new UnsupportedError("Static package $package can't be explicitly "
+          "provided sources.");
+
+  void removeSources(Iterable<AssetId> sources) =>
+      throw new UnsupportedError("Static package $package can't be explicitly "
+          "provided sources.");
+
+  void updateTransformers(Iterable<Iterable> transformersIterable) =>
+      throw new UnsupportedError("Static package $package can't have "
+          "transformers.");
+
+  void forceAllTransforms() {}
+
+  void reportError(BarbackException error) => _errorsController.add(error);
+
+  String toString() => "static cascade for $package";
+}
diff --git a/barback/lib/src/graph/transform_node.dart b/barback/lib/src/graph/transform_node.dart
new file mode 100644
index 0000000..44b369e
--- /dev/null
+++ b/barback/lib/src/graph/transform_node.dart
@@ -0,0 +1,849 @@
+// Copyright (c) 2013, 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 barback.graph.transform_node;
+
+import 'dart:async';
+
+import '../asset/asset.dart';
+import '../asset/asset_id.dart';
+import '../asset/asset_node.dart';
+import '../asset/asset_node_set.dart';
+import '../errors.dart';
+import '../log.dart';
+import '../transformer/aggregate_transform.dart';
+import '../transformer/aggregate_transformer.dart';
+import '../transformer/declaring_aggregate_transform.dart';
+import '../transformer/declaring_aggregate_transformer.dart';
+import '../transformer/lazy_aggregate_transformer.dart';
+import '../utils.dart';
+import 'node_status.dart';
+import 'node_streams.dart';
+import 'phase.dart';
+import 'transformer_classifier.dart';
+
+/// Describes a transform on a set of assets and its relationship to the build
+/// dependency graph.
+///
+/// Keeps track of whether it's dirty and needs to be run and which assets it
+/// depends on.
+class TransformNode {
+  /// The aggregate key for this node.
+  final String key;
+
+  /// The [TransformerClassifier] that [this] belongs to.
+  final TransformerClassifier classifier;
+
+  /// The [Phase] that this transform runs in.
+  Phase get phase => classifier.phase;
+
+  /// The [AggregateTransformer] to apply to this node's inputs.
+  final AggregateTransformer transformer;
+
+  /// The primary asset nodes this transform runs on.
+  final _primaries = new AssetNodeSet();
+
+  /// A string describing the location of [this] in the transformer graph.
+  final String _location;
+
+  /// The subscription to the [_primaries]' [AssetNode.onStateChange] streams.
+  final _primarySubscriptions = new Map<AssetId, StreamSubscription>();
+
+  /// The subscription to [phase]'s [Phase.onAsset] stream.
+  StreamSubscription<AssetNode> _phaseAssetSubscription;
+
+  /// The subscription to [phase]'s [Phase.onStatusChange] stream.
+  StreamSubscription<NodeStatus> _phaseStatusSubscription;
+
+  /// How far along [this] is in processing its assets.
+  NodeStatus get status {
+    if (_state == _State.APPLIED || _state == _State.DECLARED) {
+      return NodeStatus.IDLE;
+    }
+
+    if (_declaring && _state != _State.DECLARING &&
+        _state != _State.NEEDS_DECLARE) {
+      return NodeStatus.MATERIALIZING;
+    } else {
+      return NodeStatus.RUNNING;
+    }
+  }
+
+  /// The [TransformInfo] describing this node.
+  ///
+  /// [TransformInfo] is the publicly-visible representation of a transform
+  /// node.
+  TransformInfo get info => new TransformInfo(transformer,
+      new AssetId(phase.cascade.package, key));
+
+  /// Whether this is a declaring transform.
+  ///
+  /// This is usually identical to `transformer is
+  /// DeclaringAggregateTransformer`, but if a declaring and non-lazy
+  /// transformer emits an error during `declareOutputs` it's treated as though
+  /// it wasn't declaring.
+  bool get _declaring => transformer is DeclaringAggregateTransformer &&
+      (_state == _State.DECLARING || _declaredOutputs != null);
+
+  /// Whether this transform has been forced since it last finished applying.
+  ///
+  /// A transform being forced means it should run until it generates outputs
+  /// and is no longer dirty. This is always true for non-declaring
+  /// transformers, since they always need to eagerly generate outputs.
+  bool _forced;
+
+  /// The subscriptions to each secondary input's [AssetNode.onStateChange]
+  /// stream.
+  final _secondarySubscriptions = new Map<AssetId, StreamSubscription>();
+
+  /// The controllers for the asset nodes emitted by this node.
+  final _outputControllers = new Map<AssetId, AssetNodeController>();
+
+  /// The ids of inputs the transformer tried and failed to read last time it
+  /// ran.
+  final _missingInputs = new Set<AssetId>();
+
+  /// The controllers that are used to pass each primary input through [this] if
+  /// it's not consumed or overwritten.
+  ///
+  /// This needs an intervening controller to ensure that the output can be
+  /// marked dirty when determining whether [this] will consume or overwrite it,
+  /// and be marked removed if it does. No pass-through controller will exist
+  /// for primary inputs that are not being passed through.
+  final _passThroughControllers = new Map<AssetId, AssetNodeController>();
+
+  /// The asset node for this transform.
+  final _streams = new NodeStreams();
+  Stream<NodeStatus> get onStatusChange => _streams.onStatusChange;
+  Stream<AssetNode> get onAsset => _streams.onAsset;
+  Stream<LogEntry> get onLog => _streams.onLog;
+
+  /// The current state of [this].
+  var _state = _State.DECLARED;
+
+  /// Whether [this] has been marked as removed.
+  bool get _isRemoved => _streams.onAssetController.isClosed;
+
+  // If [transformer] is declaring but not lazy and [primary] is available, we
+  // can run [apply] even if [force] hasn't been called, since [transformer]
+  // should run eagerly if possible.
+  bool get _canRunDeclaringEagerly =>
+      _declaring && transformer is! LazyAggregateTransformer &&
+      _primaries.every((input) => input.state.isAvailable);
+
+  /// Which primary inputs the most recent run of this transform has declared
+  /// that it consumes.
+  ///
+  /// This starts out `null`, indicating that the transform hasn't declared
+  /// anything yet. This is not meaningful unless [_state] is [_State.APPLIED]
+  /// or [_State.DECLARED].
+  Set<AssetId> _consumedPrimaries;
+
+  /// The set of output ids that [transformer] declared it would emit.
+  ///
+  /// This is only non-null if [transformer] is a
+  /// [DeclaringAggregateTransformer] and its [declareOutputs] has been run
+  /// successfully.
+  Set<AssetId> _declaredOutputs;
+
+  /// The controller for the currently-running
+  /// [DeclaringAggregateTransformer.declareOutputs] call's
+  /// [DeclaringAggregateTransform].
+  ///
+  /// This will be non-`null` when
+  /// [DeclaringAggregateTransformer.declareOutputs] is running. This means that
+  /// it's always non-`null` when [_state] is [_State.DECLARING], sometimes
+  /// non-`null` when it's [_State.NEEDS_DECLARE], and always `null` otherwise.
+  DeclaringAggregateTransformController _declareController;
+
+  /// The controller for the currently-running [AggregateTransformer.apply]
+  /// call's [AggregateTransform].
+  ///
+  /// This will be non-`null` when [AggregateTransform.apply] is running, which
+  /// means that it's always non-`null` when [_state] is [_State.APPLYING] or
+  /// [_State.NEEDS_APPLY], sometimes non-`null` when it's
+  /// [_State.NEEDS_DECLARE], and always `null` otherwise.
+  AggregateTransformController _applyController;
+
+  /// The number of secondary inputs that have been requested but not yet
+  /// produced.
+  int _pendingSecondaryInputs = 0;
+
+  /// A stopwatch that tracks the total time spent in a transformer's `apply`
+  /// function.
+  final _timeInTransformer = new Stopwatch();
+
+  /// A stopwatch that tracks the time in a transformer's `apply` function spent
+  /// waiting for [getInput] calls to complete.
+  final _timeAwaitingInputs = new Stopwatch();
+
+  TransformNode(this.classifier, this.transformer, this.key, this._location) {
+    _forced = transformer is! DeclaringAggregateTransformer;
+
+    _phaseAssetSubscription = phase.previous.onAsset.listen((node) {
+      if (!_missingInputs.contains(node.id)) return;
+      if (_forced) node.force();
+      _dirty();
+    });
+
+    _phaseStatusSubscription = phase.previous.onStatusChange.listen((status) {
+      if (status == NodeStatus.RUNNING) return;
+
+      _maybeFinishDeclareController();
+      _maybeFinishApplyController();
+    });
+
+    classifier.onDoneClassifying.listen((_) {
+      _maybeFinishDeclareController();
+      _maybeFinishApplyController();
+    });
+
+    _run();
+  }
+
+  /// Adds [input] as a primary input for this node.
+  void addPrimary(AssetNode input) {
+    _primaries.add(input);
+    if (_forced) input.force();
+
+    _primarySubscriptions[input.id] = input.onStateChange
+        .listen((_) => _onPrimaryStateChange(input));
+
+    if (_state == _State.DECLARING && !_declareController.isDone) {
+      // If we're running `declareOutputs` and its id stream isn't closed yet,
+      // pass this in as another id.
+      _declareController.addId(input.id);
+      _maybeFinishDeclareController();
+    } else if (_state == _State.APPLYING) {
+      // If we're running `apply`, we need to wait until [input] is available
+      // before we pass it into the stream. If it's available now, great; if
+      // not, [_onPrimaryStateChange] will handle it.
+      if (!input.state.isAvailable) {
+        // If we started running eagerly without being forced, abort that run if
+        // a new unavailable asset comes in.
+        if (input.isLazy && !_forced) _restartRun();
+        return;
+      }
+
+      _onPrimaryStateChange(input);
+      _maybeFinishApplyController();
+    } else {
+      // Otherwise, a new input means we'll need to re-run `declareOutputs`.
+      _restartRun();
+    }
+  }
+
+  /// Marks this transform as removed.
+  ///
+  /// This causes all of the transform's outputs to be marked as removed as
+  /// well. Normally this will be automatically done internally based on events
+  /// from the primary input, but it's possible for a transform to no longer be
+  /// valid even if its primary input still exists.
+  void remove() {
+    _streams.close();
+    _phaseAssetSubscription.cancel();
+    _phaseStatusSubscription.cancel();
+    if (_declareController != null) _declareController.cancel();
+    if (_applyController != null) _applyController.cancel();
+    _clearSecondarySubscriptions();
+    _clearOutputs();
+
+    for (var subscription in _primarySubscriptions.values) {
+      subscription.cancel();
+    }
+    _primarySubscriptions.clear();
+
+    for (var controller in _passThroughControllers.values) {
+      controller.setRemoved();
+    }
+    _passThroughControllers.clear();
+  }
+
+  /// If [this] is deferred, ensures that its concrete outputs will be
+  /// generated.
+  void force() {
+    if (_forced || _state == _State.APPLIED) return;
+    for (var input in _primaries) {
+      input.force();
+    }
+
+    _forced = true;
+    if (_state == _State.DECLARED) _apply();
+  }
+
+  /// Marks this transform as dirty.
+  ///
+  /// Specifically, this should be called when one of the transform's inputs'
+  /// contents change, or when a secondary input is removed. Primary inputs
+  /// being added or removed are handled by [addInput] and
+  /// [_onPrimaryStateChange].
+  void _dirty() {
+    if (_state == _State.DECLARING || _state == _State.NEEDS_DECLARE ||
+        _state == _State.NEEDS_APPLY) {
+      // If we already know that [_apply] needs to be run, there's nothing to do
+      // here.
+      return;
+    }
+
+    if (!_forced && !_canRunDeclaringEagerly) {
+      // [forced] should only ever be false for a declaring transformer.
+      assert(_declaring);
+
+      // If we've finished applying, transition to DECLARED, indicating that we
+      // know what outputs [apply] will emit but we're waiting to emit them
+      // concretely until [force] is called. If we're still applying, we'll
+      // transition to DECLARED once we finish.
+      if (_state == _State.APPLIED) _state = _State.DECLARED;
+      for (var controller in _outputControllers.values) {
+        controller.setLazy(force);
+      }
+      _emitDeclaredOutputs();
+      return;
+    }
+
+    if (_state == _State.APPLIED) {
+      if (_declaredOutputs != null) _emitDeclaredOutputs();
+      _apply();
+    } else if (_state == _State.DECLARED) {
+      _apply();
+    } else {
+      _state = _State.NEEDS_APPLY;
+    }
+  }
+
+  /// The callback called when [input]'s state changes.
+  void _onPrimaryStateChange(AssetNode input) {
+    if (input.state.isRemoved) {
+      _primarySubscriptions.remove(input.id);
+
+      if (_primaries.isEmpty) {
+        // If there are no more primary inputs, there's no more use for this
+        // node in the graph. It will be re-created by its
+        // [TransformerClassifier] if a new input with [key] is added.
+        remove();
+        return;
+      }
+
+      // Any change to the number of primary inputs requires that we re-run the
+      // transformation.
+      _restartRun();
+    } else if (input.state.isAvailable) {
+      if (_state == _State.DECLARED && _canRunDeclaringEagerly) {
+        // If [this] is fully declared but hasn't started applying, this input
+        // becoming available may mean that all inputs are available, in which
+        // case we can run apply eagerly.
+        _apply();
+        return;
+      }
+
+      // If we're not actively passing concrete assets to the transformer, the
+      // distinction between a dirty asset and an available one isn't relevant.
+      if (_state != _State.APPLYING) return;
+
+      if (_applyController.isDone) {
+        // If we get a new asset after we've closed the asset stream, we need to
+        // re-run declare and then apply.
+        _restartRun();
+      } else {
+        // If the new asset comes before the asset stream is done, we can just
+        // pass it to the stream.
+        _applyController.addInput(input.asset);
+        _maybeFinishApplyController();
+      }
+    } else {
+      if (_forced) input.force();
+      if (_state == _State.APPLYING && !_applyController.addedId(input.id) &&
+          (_forced || !input.isLazy)) {
+        // If the input hasn't yet been added to the transform's input stream,
+        // there's no need to consider the transformation dirty. However, if the
+        // input is lazy and we're running eagerly, we need to restart the
+        // transformation.
+        return;
+      }
+      _dirty();
+    }
+  }
+
+  /// Run the entire transformation, including both `declareOutputs` (if
+  /// applicable) and `apply`.
+  void _run() {
+    assert(_state != _State.DECLARING);
+    assert(_state != _State.APPLYING);
+
+    _markOutputsDirty();
+    _declareOutputs(() {
+      if (_forced || _canRunDeclaringEagerly) {
+        _apply();
+      } else {
+        _state = _State.DECLARED;
+        _streams.changeStatus(NodeStatus.IDLE);
+      }
+    });
+  }
+
+  /// Restart the entire transformation, including `declareOutputs` if
+  /// applicable.
+  void _restartRun() {
+    if (_state == _State.DECLARED || _state == _State.APPLIED) {
+      // If we're currently idle, we can restart the transformation immediately.
+      _run();
+      return;
+    }
+
+    // If we're actively running `declareOutputs` or `apply`, cancel the
+    // transforms and transition to `NEEDS_DECLARE`. Once the transformer's
+    // method returns, we'll transition to `DECLARING`.
+    if (_declareController != null) _declareController.cancel();
+    if (_applyController != null) _applyController.cancel();
+    _state = _State.NEEDS_DECLARE;
+  }
+
+  /// Runs [transform.declareOutputs] and emits the resulting assets as dirty
+  /// assets.
+  ///
+  /// Calls [callback] when it's finished. This doesn't return a future so that
+  /// [callback] is called synchronously if there are no outputs to declare. If
+  /// [this] is removed while inputs are being declared, [callback] will not be
+  /// called.
+  void _declareOutputs(void callback()) {
+    if (transformer is! DeclaringAggregateTransformer) {
+      callback();
+      return;
+    }
+
+    _state = _State.DECLARING;
+    var controller = new DeclaringAggregateTransformController(this);
+    _declareController = controller;
+    _streams.onLogPool.add(controller.onLog);
+    for (var primary in _primaries) {
+      controller.addId(primary.id);
+    }
+    _maybeFinishDeclareController();
+
+    syncFuture(() {
+      return (transformer as DeclaringAggregateTransformer)
+          .declareOutputs(controller.transform);
+    }).whenComplete(() {
+      // Cancel the controller here even if `declareOutputs` wasn't interrupted.
+      // Since the declaration is finished, we want to close out the
+      // controller's streams.
+      controller.cancel();
+      _declareController = null;
+    }).then((_) {
+      if (_isRemoved) return;
+      if (_state == _State.NEEDS_DECLARE) {
+        _declareOutputs(callback);
+        return;
+      }
+
+      if (controller.loggedError) {
+        // If `declareOutputs` fails, fall back to treating a declaring
+        // transformer as though it were eager.
+        if (transformer is! LazyAggregateTransformer) _forced = true;
+        callback();
+        return;
+      }
+
+      _consumedPrimaries = controller.consumedPrimaries;
+      _declaredOutputs = controller.outputIds;
+      var invalidIds = _declaredOutputs
+          .where((id) => id.package != phase.cascade.package).toSet();
+      for (var id in invalidIds) {
+        _declaredOutputs.remove(id);
+        // TODO(nweiz): report this as a warning rather than a failing error.
+        phase.cascade.reportError(new InvalidOutputException(info, id));
+      }
+
+      for (var primary in _primaries) {
+        if (_declaredOutputs.contains(primary.id)) continue;
+        _passThrough(primary.id);
+      }
+      _emitDeclaredOutputs();
+      callback();
+    }).catchError((error, stackTrace) {
+      if (_isRemoved) return;
+      if (transformer is! LazyAggregateTransformer) _forced = true;
+      phase.cascade.reportError(_wrapException(error, stackTrace));
+      callback();
+    });
+  }
+
+  /// Emits a dirty asset node for all outputs that were declared by the
+  /// transformer.
+  ///
+  /// This won't emit any outputs for which there already exist output
+  /// controllers. It should only be called for transforms that have declared
+  /// their outputs.
+  void _emitDeclaredOutputs() {
+    assert(_declaredOutputs != null);
+    for (var id in _declaredOutputs) {
+      if (_outputControllers.containsKey(id)) continue;
+      var controller = _forced
+          ? new AssetNodeController(id, this)
+          : new AssetNodeController.lazy(id, force, this);
+      _outputControllers[id] = controller;
+      _streams.onAssetController.add(controller.node);
+    }
+  }
+
+  //// Mark all emitted and passed-through outputs of this transform as dirty.
+  void _markOutputsDirty() {
+    for (var controller in _passThroughControllers.values) {
+      controller.setDirty();
+    }
+    for (var controller in _outputControllers.values) {
+      if (_forced) {
+        controller.setDirty();
+      } else {
+        controller.setLazy(force);
+      }
+    }
+  }
+
+  /// Applies this transform.
+  void _apply() {
+    assert(!_isRemoved);
+
+    _markOutputsDirty();
+    _clearSecondarySubscriptions();
+    _state = _State.APPLYING;
+    _streams.changeStatus(status);
+    _runApply().then((hadError) {
+      if (_isRemoved) return;
+
+      if (_state == _State.DECLARED) return;
+
+      if (_state == _State.NEEDS_DECLARE) {
+        _run();
+        return;
+      }
+
+      // If an input's contents changed while running `apply`, retry unless the
+      // transformer is deferred and hasn't been forced.
+      if (_state == _State.NEEDS_APPLY) {
+        if (_forced || _canRunDeclaringEagerly) {
+          _apply();
+        } else {
+          _state = _State.DECLARED;
+        }
+        return;
+      }
+
+      if (_declaring) _forced = false;
+
+      assert(_state == _State.APPLYING);
+      if (hadError) {
+        _clearOutputs();
+        // If the transformer threw an error, we don't want to emit the
+        // pass-through assets in case they'll be overwritten by the
+        // transformer. However, if the transformer declared that it wouldn't
+        // overwrite or consume a pass-through asset, we can safely emit it.
+        if (_declaredOutputs != null) {
+          for (var input in _primaries) {
+            if (_consumedPrimaries.contains(input.id) ||
+                _declaredOutputs.contains(input.id)) {
+              _consumePrimary(input.id);
+            } else {
+              _passThrough(input.id);
+            }
+          }
+        }
+      }
+
+      _state = _State.APPLIED;
+      _streams.changeStatus(NodeStatus.IDLE);
+    });
+  }
+
+  /// Gets the asset for an input [id].
+  ///
+  /// If an input with [id] cannot be found, throws an [AssetNotFoundException].
+  Future<Asset> getInput(AssetId id) {
+    _timeAwaitingInputs.start();
+    _pendingSecondaryInputs++;
+    return phase.previous.getOutput(id).then((node) {
+      // Throw if the input isn't found. This ensures the transformer's apply
+      // is exited. We'll then catch this and report it through the proper
+      // results stream.
+      if (node == null) {
+        _missingInputs.add(id);
+        throw new AssetNotFoundException(id);
+      }
+
+      _secondarySubscriptions.putIfAbsent(node.id, () {
+        return node.onStateChange.listen((_) => _dirty());
+      });
+
+      return node.asset;
+    }).whenComplete(() {
+      _pendingSecondaryInputs--;
+      if (_pendingSecondaryInputs != 0) _timeAwaitingInputs.stop();
+    });
+  }
+
+  /// Run [AggregateTransformer.apply].
+  ///
+  /// Returns whether or not an error occurred while running the transformer.
+  Future<bool> _runApply() {
+    var controller = new AggregateTransformController(this);
+    _applyController = controller;
+    _streams.onLogPool.add(controller.onLog);
+    for (var primary in _primaries) {
+      if (!primary.state.isAvailable) continue;
+      controller.addInput(primary.asset);
+    }
+    _maybeFinishApplyController();
+
+    return syncFuture(() {
+      _timeInTransformer.reset();
+      _timeAwaitingInputs.reset();
+      _timeInTransformer.start();
+      return transformer.apply(controller.transform);
+    }).whenComplete(() {
+      _timeInTransformer.stop();
+      _timeAwaitingInputs.stop();
+
+      // Cancel the controller here even if `apply` wasn't interrupted. Since
+      // the apply is finished, we want to close out the controller's streams.
+      controller.cancel();
+      _applyController = null;
+    }).then((_) {
+      assert(_state != _State.DECLARED);
+      assert(_state != _State.DECLARING);
+      assert(_state != _State.APPLIED);
+
+      if (!_forced && _primaries.any((node) => !node.state.isAvailable)) {
+        _state = _State.DECLARED;
+        _streams.changeStatus(NodeStatus.IDLE);
+        return false;
+      }
+
+      if (_isRemoved) return false;
+      if (_state == _State.NEEDS_APPLY) return false;
+      if (_state == _State.NEEDS_DECLARE) return false;
+      if (controller.loggedError) return true;
+
+      // If the transformer took long enough, log its duration in fine output.
+      // That way it's not always visible, but users running with "pub serve
+      // --verbose" can see it.
+      var ranLong = _timeInTransformer.elapsed > new Duration(seconds: 1);
+      var ranLongLocally =
+          _timeInTransformer.elapsed - _timeAwaitingInputs.elapsed >
+            new Duration(milliseconds: 200);
+
+      // Report the transformer's timing information if it spent more than 0.2s
+      // doing things other than waiting for its secondary inputs or if it spent
+      // more than 1s in total.
+      if (ranLongLocally || ranLong) {
+        _streams.onLogController.add(new LogEntry(
+            info, info.primaryId, LogLevel.FINE,
+            "Took ${niceDuration(_timeInTransformer.elapsed)} "
+              "(${niceDuration(_timeAwaitingInputs.elapsed)} awaiting "
+              "secondary inputs).",
+            null));
+      }
+
+      _handleApplyResults(controller);
+      return false;
+    }).catchError((error, stackTrace) {
+      // If the transform became dirty while processing, ignore any errors from
+      // it.
+      if (_state == _State.NEEDS_APPLY || _isRemoved) return false;
+
+      // Catch all transformer errors and pipe them to the results stream. This
+      // is so a broken transformer doesn't take down the whole graph.
+      phase.cascade.reportError(_wrapException(error, stackTrace));
+      return true;
+    });
+  }
+
+  /// Handle the results of running [Transformer.apply].
+  ///
+  /// [controller] should be the controller for the [AggegateTransform] passed
+  /// to [AggregateTransformer.apply].
+  void _handleApplyResults(AggregateTransformController controller) {
+    _consumedPrimaries = controller.consumedPrimaries;
+
+    var newOutputs = controller.outputs;
+    // Any ids that are for a different package are invalid.
+    var invalidIds = newOutputs
+        .map((asset) => asset.id)
+        .where((id) => id.package != phase.cascade.package)
+        .toSet();
+    for (var id in invalidIds) {
+      newOutputs.removeId(id);
+      // TODO(nweiz): report this as a warning rather than a failing error.
+      phase.cascade.reportError(new InvalidOutputException(info, id));
+    }
+
+    // Remove outputs that used to exist but don't anymore.
+    for (var id in _outputControllers.keys.toList()) {
+      if (newOutputs.containsId(id)) continue;
+      _outputControllers.remove(id).setRemoved();
+    }
+
+    // Emit or stop emitting pass-through assets between removing and adding
+    // outputs to ensure there are no collisions.
+    for (var id in _primaries.map((node) => node.id)) {
+      if (_consumedPrimaries.contains(id) || newOutputs.containsId(id)) {
+        _consumePrimary(id);
+      } else {
+        _passThrough(id);
+      }
+    }
+
+    // Store any new outputs or new contents for existing outputs.
+    for (var asset in newOutputs) {
+      var controller = _outputControllers[asset.id];
+      if (controller != null) {
+        controller.setAvailable(asset);
+      } else {
+        var controller = new AssetNodeController.available(asset, this);
+        _outputControllers[asset.id] = controller;
+        _streams.onAssetController.add(controller.node);
+      }
+    }
+  }
+
+  /// Cancels all subscriptions to secondary input nodes.
+  void _clearSecondarySubscriptions() {
+    _missingInputs.clear();
+    for (var subscription in _secondarySubscriptions.values) {
+      subscription.cancel();
+    }
+    _secondarySubscriptions.clear();
+  }
+
+  /// Removes all output assets.
+  void _clearOutputs() {
+    // Remove all the previously-emitted assets.
+    for (var controller in _outputControllers.values) {
+      controller.setRemoved();
+    }
+    _outputControllers.clear();
+  }
+
+  /// Emit the pass-through node for the primary input [id] if it's not being
+  /// emitted already.
+  void _passThrough(AssetId id) {
+    assert(!_outputControllers.containsKey(id));
+
+    if (_consumedPrimaries.contains(id)) return;
+    var controller = _passThroughControllers[id];
+    var primary = _primaries[id];
+    if (controller == null) {
+      controller = new AssetNodeController.from(primary);
+      _passThroughControllers[id] = controller;
+      _streams.onAssetController.add(controller.node);
+    } else if (primary.state.isDirty) {
+      controller.setDirty();
+    } else if (!controller.node.state.isAvailable) {
+      controller.setAvailable(primary.asset);
+    }
+  }
+
+  /// Stops emitting the pass-through node for the primary input [id] if it's
+  /// being emitted.
+  void _consumePrimary(AssetId id) {
+    var controller = _passThroughControllers.remove(id);
+    if (controller == null) return;
+    controller.setRemoved();
+  }
+
+  /// If `declareOutputs` is running and all previous phases have declared their
+  /// outputs, mark [_declareController] as done.
+  void _maybeFinishDeclareController() {
+    if (_declareController == null) return;
+    if (classifier.isClassifying) return;
+    if (phase.previous.status == NodeStatus.RUNNING) return;
+    _declareController.done();
+  }
+
+  /// If `apply` is running, all previous phases have declared their outputs,
+  /// and all primary inputs are available and thus have been passed to the
+  /// transformer, mark [_applyController] as done.
+  void _maybeFinishApplyController() {
+    if (_applyController == null) return;
+    if (classifier.isClassifying) return;
+    if (_primaries.any((input) => !input.state.isAvailable)) return;
+    if (phase.previous.status == NodeStatus.RUNNING) return;
+    _applyController.done();
+  }
+
+  BarbackException _wrapException(error, StackTrace stackTrace) {
+    if (error is! AssetNotFoundException) {
+      return new TransformerException(info, error, stackTrace);
+    } else {
+      return new MissingInputException(info, error.id);
+    }
+  }
+
+  String toString() =>
+      "transform node in $_location for $transformer on ${info.primaryId} "
+      "($_state, $status, ${_forced ? '' : 'un'}forced)";
+}
+
+/// The enum of states that [TransformNode] can be in.
+class _State {
+  /// The transform is running [DeclaringAggregateTransformer.declareOutputs].
+  ///
+  /// If the set of primary inputs changes while in this state, it will
+  /// transition to [NEEDS_DECLARE]. If the [TransformNode] is still in this
+  /// state when `declareOutputs` finishes running, it will transition to
+  /// [APPLYING] if the transform is non-lazy and all of its primary inputs are
+  /// available, and [DECLARED] otherwise.
+  ///
+  /// Non-declaring transformers will transition out of this state and into
+  /// [APPLYING] immediately.
+  static const DECLARING = const _State._("declaring outputs");
+
+  /// The transform is running [AggregateTransformer.declareOutputs] or
+  /// [AggregateTransform.apply], but a primary input was added or removed after
+  /// it started, so it will need to re-run `declareOutputs`.
+  ///
+  /// The [TransformNode] will transition to [DECLARING] once `declareOutputs`
+  /// or `apply` finishes running.
+  static const NEEDS_DECLARE = const _State._("needs declare");
+
+  /// The transform is deferred and has run
+  /// [DeclaringAggregateTransformer.declareOutputs] but hasn't yet been forced.
+  ///
+  /// The [TransformNode] will transition to [APPLYING] when one of the outputs
+  /// has been forced or if the transformer is non-lazy and all of its primary
+  /// inputs become available.
+  static const DECLARED = const _State._("declared");
+
+  /// The transform is running [AggregateTransformer.apply].
+  ///
+  /// If an input's contents change or a secondary input is added or removed
+  /// while in this state, the [TransformNode] will transition to [NEEDS_APPLY].
+  /// If a primary input is added or removed, it will transition to
+  /// [NEEDS_DECLARE]. If it's still in this state when `apply` finishes
+  /// running, it will transition to [APPLIED].
+  static const APPLYING = const _State._("applying");
+
+  /// The transform is running [AggregateTransformer.apply], but an input's
+  /// contents changed or a secondary input was added or removed after it
+  /// started, so it will need to re-run `apply`.
+  ///
+  /// If a primary input is added or removed while in this state, the
+  /// [TranformNode] will transition to [NEEDS_DECLARE]. If it's still in this
+  /// state when `apply` finishes running, it will transition to [APPLYING].
+  static const NEEDS_APPLY = const _State._("needs apply");
+
+  /// The transform has finished running [AggregateTransformer.apply], whether
+  /// or not it emitted an error.
+  ///
+  /// If an input's contents change or a secondary input is added or removed,
+  /// the [TransformNode] will transition to [DECLARED] if the transform is
+  /// declaring and [APPLYING] otherwise. If a primary input is added or
+  /// removed, this will transition to [DECLARING].
+  static const APPLIED = const _State._("applied");
+
+  final String name;
+
+  const _State._(this.name);
+
+  String toString() => name;
+}
diff --git a/barback/lib/src/graph/transformer_classifier.dart b/barback/lib/src/graph/transformer_classifier.dart
new file mode 100644
index 0000000..a095e8d
--- /dev/null
+++ b/barback/lib/src/graph/transformer_classifier.dart
@@ -0,0 +1,147 @@
+// 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 barback.graph.transformer_classifier;
+
+import 'dart:async';
+
+import '../asset/asset_forwarder.dart';
+import '../asset/asset_node.dart';
+import '../errors.dart';
+import '../log.dart';
+import '../transformer/aggregate_transformer.dart';
+import '../transformer/wrapping_aggregate_transformer.dart';
+import '../utils.dart';
+import 'node_status.dart';
+import 'node_streams.dart';
+import 'phase.dart';
+import 'transform_node.dart';
+
+/// A class for classifying the primary inputs for a transformer according to
+/// its [AggregateTransformer.classifyPrimary] method.
+///
+/// This is also used for non-aggregate transformers; they're modeled as
+/// aggregate transformers that return the primary path if `isPrimary` is true
+/// and `null` if `isPrimary` is `null`.
+class TransformerClassifier {
+  /// The containing [Phase].
+  final Phase phase;
+
+  /// The [AggregateTransformer] used to classify the inputs.
+  final AggregateTransformer transformer;
+
+  /// A string describing the location of [this] in the transformer graph.
+  final String _location;
+
+  /// The individual transforms for each classiciation key.
+  final _transforms = new Map<Object, TransformNode>();
+
+  /// Forwarders used to pass through assets that aren't used by [transformer].
+  final _passThroughForwarders = new Set<AssetForwarder>();
+
+  /// The streams exposed by this classifier.
+  final _streams = new NodeStreams();
+  Stream get onStatusChange => _streams.onStatusChange;
+  Stream<AssetNode> get onAsset => _streams.onAsset;
+  Stream<LogEntry> get onLog => _streams.onLog;
+
+  /// A broadcast stream that emits an event whenever [this] has finished
+  /// classifying all available inputs.
+  Stream get onDoneClassifying => _onDoneClassifyingController.stream;
+  final _onDoneClassifyingController =
+      new StreamController.broadcast(sync: true);
+
+  /// The number of currently-active calls to [transformer.classifyPrimary].
+  ///
+  /// This is used to determine whether [this] is dirty.
+  var _activeClassifications = 0;
+
+  /// Whether this is currently classifying any inputs.
+  bool get isClassifying => _activeClassifications > 0;
+
+  /// How far along [this] is in processing its assets.
+  NodeStatus get status {
+    if (isClassifying) return NodeStatus.RUNNING;
+    return NodeStatus.dirtiest(
+        _transforms.values.map((transform) => transform.status));
+  }
+
+  TransformerClassifier(this.phase, transformer, this._location)
+      : transformer = transformer is AggregateTransformer ?
+            transformer : new WrappingAggregateTransformer(transformer);
+
+  /// Adds a new asset as an input for this transformer.
+  void addInput(AssetNode input) {
+    _activeClassifications++;
+    syncFuture(() => transformer.classifyPrimary(input.id)).catchError(
+        (error, stackTrace) {
+      if (input.state.isRemoved) return null;
+
+      // Catch all transformer errors and pipe them to the results stream. This
+      // is so a broken transformer doesn't take down the whole graph.
+      var info = new TransformInfo(transformer, input.id);
+      if (error is! AssetNotFoundException) {
+        error = new TransformerException(info, error, stackTrace);
+      } else {
+        error = new MissingInputException(info, error.id);
+      }
+      phase.cascade.reportError(error);
+
+      return null;
+    }).then((key) {
+      if (input.state.isRemoved) return;
+      if (key == null) {
+        var forwarder = new AssetForwarder(input);
+        _passThroughForwarders.add(forwarder);
+        forwarder.node.whenRemoved(
+            () => _passThroughForwarders.remove(forwarder));
+        _streams.onAssetController.add(forwarder.node);
+      } else if (_transforms.containsKey(key)) {
+        _transforms[key].addPrimary(input);
+      } else {
+        var transform = new TransformNode(this, transformer, key, _location);
+        _transforms[key] = transform;
+
+        transform.onStatusChange.listen(
+            (_) => _streams.changeStatus(status),
+            onDone: () {
+          _transforms.remove(transform.key);
+          if (!_streams.isClosed) _streams.changeStatus(status);
+        });
+
+        _streams.onAssetPool.add(transform.onAsset);
+        _streams.onLogPool.add(transform.onLog);
+        transform.addPrimary(input);
+      }
+    }).whenComplete(() {
+      _activeClassifications--;
+      if (_streams.isClosed) return;
+      if (!isClassifying) _onDoneClassifyingController.add(null);
+      _streams.changeStatus(status);
+    });
+  }
+
+  /// Removes this transformer.
+  ///
+  /// This marks all outputs of the transformer as removed.
+  void remove() {
+    _streams.close();
+    _onDoneClassifyingController.close();
+    for (var transform in _transforms.values.toList()) {
+      transform.remove();
+    }
+    for (var forwarder in _passThroughForwarders.toList()) {
+      forwarder.close();
+    }
+  }
+
+  /// Force all deferred transforms to begin producing concrete assets.
+  void forceAllTransforms() {
+    for (var transform in _transforms.values) {
+      transform.force();
+    }
+  }
+
+  String toString() => "classifier in $_location for $transformer";
+}
diff --git a/barback/lib/src/internal_asset.dart b/barback/lib/src/internal_asset.dart
new file mode 100644
index 0000000..ab73901
--- /dev/null
+++ b/barback/lib/src/internal_asset.dart
@@ -0,0 +1,15 @@
+// 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.
+
+/// This library exists so that pub can retain backwards-compatibility with
+/// versions of barback prior to 0.13.1.
+///
+/// In 0.13.1, `lib/src/internal_asset.dart` was moved to
+/// `lib/src/asset/internal_asset.dart. Pub needs to support all versions of
+/// barback back through 0.13.0, though. In order for this to work, it needs to
+/// be able to import "package:barback/src/internal_asset.dart" on all available
+/// barback versions, hence the existence of this library.
+library barback.internal_asset;
+
+export 'asset/internal_asset.dart';
diff --git a/barback/lib/src/log.dart b/barback/lib/src/log.dart
new file mode 100644
index 0000000..f0240a4
--- /dev/null
+++ b/barback/lib/src/log.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2013, 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 barback.log;
+
+import 'package:source_span/source_span.dart';
+
+import 'asset/asset_id.dart';
+import 'errors.dart';
+
+/// The severity of a logged message.
+class LogLevel {
+  static const INFO = const LogLevel("Info");
+  static const FINE = const LogLevel("Fine");
+  static const WARNING = const LogLevel("Warning");
+  static const ERROR = const LogLevel("Error");
+
+  final String name;
+  const LogLevel(this.name);
+
+  String toString() => name;
+}
+
+/// One message logged during a transform.
+class LogEntry {
+  /// The transform that logged the message.
+  final TransformInfo transform;
+
+  /// The asset that the message is associated with.
+  final AssetId assetId;
+
+  final LogLevel level;
+  final String message;
+
+  /// The location that the message pertains to or null if not associated with
+  /// a [SourceSpan].
+  final SourceSpan span;
+
+  LogEntry(this.transform, this.assetId, this.level, this.message, this.span);
+}
diff --git a/barback/lib/src/package_provider.dart b/barback/lib/src/package_provider.dart
new file mode 100644
index 0000000..ce6ee23
--- /dev/null
+++ b/barback/lib/src/package_provider.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2013, 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 barback.package_provider;
+
+import 'dart:async';
+
+import 'asset/asset.dart';
+import 'asset/asset_id.dart';
+
+/// API for locating and accessing packages on disk.
+///
+/// Implemented by pub and provided to barback so that it isn't coupled
+/// directly to pub.
+abstract class PackageProvider {
+  /// The names of all packages that can be provided by this provider.
+  ///
+  /// This is equal to the transitive closure of the entrypoint package
+  /// dependencies.
+  Iterable<String> get packages;
+
+  /// Loads an asset from disk.
+  ///
+  /// This should be re-entrant; it may be called multiple times with the same
+  /// id before the previously returned future has completed.
+  ///
+  /// If no asset with [id] exists, the provider should throw an
+  /// [AssetNotFoundException].
+  Future<Asset> getAsset(AssetId id);
+}
+
+/// A PackageProvider for which some packages are known to be static—that is,
+/// the package has no transformers and its assets won't ever change.
+///
+/// For static packages, rather than telling barback up-front which assets that
+/// package contains via [Barback.updateSources], barback will lazily query the
+/// provider for an asset when it's needed. This is much more efficient.
+abstract class StaticPackageProvider implements PackageProvider {
+  /// The names of all static packages provided by this provider.
+  ///
+  /// This must be disjoint from [packages].
+  Iterable<String> get staticPackages;
+
+  /// Returns all ids of assets in [package].
+  ///
+  /// This is used for [Barback.getAllAssets].
+  Stream<AssetId> getAllAssetIds(String package);
+}
diff --git a/barback/lib/src/serialize.dart b/barback/lib/src/serialize.dart
new file mode 100644
index 0000000..10cc6fe
--- /dev/null
+++ b/barback/lib/src/serialize.dart
@@ -0,0 +1,113 @@
+// Copyright (c) 2013, 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 barback.serialize;
+
+import 'dart:async';
+import 'dart:isolate';
+
+import 'package:stack_trace/stack_trace.dart';
+
+import 'asset/asset_id.dart';
+import 'utils.dart';
+
+/// Converts [id] into a serializable map.
+Map serializeId(AssetId id) => {'package': id.package, 'path': id.path};
+
+/// Converts [stream] into a [SendPort] with which another isolate can request
+/// the data from [stream].
+SendPort serializeStream(Stream stream) {
+  var receivePort = new ReceivePort();
+  receivePort.first.then((sendPort) {
+    stream.listen((data) => sendPort.send({'type': 'data', 'data': data}),
+        onDone: () => sendPort.send({'type': 'done'}),
+        onError: (error, stackTrace) {
+      sendPort.send({
+        'type': 'error',
+        'error': CrossIsolateException.serialize(error, stackTrace)
+      });
+    });
+  });
+
+  return receivePort.sendPort;
+}
+
+/// Converts a serializable map into an [AssetId].
+AssetId deserializeId(Map id) => new AssetId(id['package'], id['path']);
+
+/// Convert a [SendPort] whose opposite is waiting to send us a stream into a
+/// [Stream].
+///
+/// No stream data will actually be sent across the isolate boundary until
+/// someone subscribes to the returned stream.
+Stream deserializeStream(SendPort sendPort) {
+  return callbackStream(() {
+    var receivePort = new ReceivePort();
+    sendPort.send(receivePort.sendPort);
+    return receivePort.transform(
+        const StreamTransformer(_deserializeTransformer));
+  });
+}
+
+/// The body of a [StreamTransformer] that deserializes the values in a stream
+/// sent by [serializeStream].
+StreamSubscription _deserializeTransformer(Stream input, bool cancelOnError) {
+  var subscription;
+  var transformed = input.transform(new StreamTransformer.fromHandlers(
+      handleData: (data, sink) {
+    if (data['type'] == 'data') {
+      sink.add(data['data']);
+    } else if (data['type'] == 'error') {
+      var exception = new CrossIsolateException.deserialize(data['error']);
+      sink.addError(exception, exception.stackTrace);
+    } else {
+      assert(data['type'] == 'done');
+      sink.close();
+      subscription.cancel();
+    }
+  }));
+  subscription = transformed.listen(null, cancelOnError: cancelOnError);
+  return subscription;
+}
+
+/// An exception that was originally raised in another isolate.
+///
+/// Exception objects can't cross isolate boundaries in general, so this class
+/// wraps as much information as can be consistently serialized.
+class CrossIsolateException implements Exception {
+  /// The name of the type of exception thrown.
+  ///
+  /// This is the return value of [error.runtimeType.toString()]. Keep in mind
+  /// that objects in different libraries may have the same type name.
+  final String type;
+
+  /// The exception's message, or its [toString] if it didn't expose a `message`
+  /// property.
+  final String message;
+
+  /// The exception's stack chain, or `null` if no stack chain was available.
+  final Chain stackTrace;
+
+  /// Loads a [CrossIsolateException] from a serialized representation.
+  ///
+  /// [error] should be the result of [CrossIsolateException.serialize].
+  CrossIsolateException.deserialize(Map error)
+      : type = error['type'],
+        message = error['message'],
+        stackTrace = error['stack'] == null ? null :
+            new Chain.parse(error['stack']);
+
+  /// Serializes [error] to an object that can safely be passed across isolate
+  /// boundaries.
+  static Map serialize(error, [StackTrace stack]) {
+    if (stack == null && error is Error) stack = error.stackTrace;
+    return {
+      'type': error.runtimeType.toString(),
+      'message': getErrorMessage(error),
+      'stack': stack == null ? null : new Chain.forTrace(stack).toString()
+    };
+  }
+
+  String toString() => "$message\n$stackTrace";
+}
diff --git a/barback/lib/src/transformer/aggregate_transform.dart b/barback/lib/src/transformer/aggregate_transform.dart
new file mode 100644
index 0000000..6d471fd
--- /dev/null
+++ b/barback/lib/src/transformer/aggregate_transform.dart
@@ -0,0 +1,151 @@
+// 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 barback.transformer.aggregate_transform;
+
+import 'dart:async';
+import 'dart:convert';
+
+import '../asset/asset.dart';
+import '../asset/asset_id.dart';
+import '../asset/asset_set.dart';
+import '../errors.dart';
+import '../graph/transform_node.dart';
+import '../utils.dart';
+import 'base_transform.dart';
+
+/// A transform for [AggregateTransformer]s that provides access to all of their
+/// primary inputs.
+class AggregateTransform extends BaseTransform {
+  final TransformNode _node;
+
+  /// The set of outputs emitted by the transformer.
+  final _outputs = new AssetSet();
+
+  /// The transform key.
+  ///
+  /// This is the key returned by [AggregateTransformer.classifyPrimary] for all
+  /// the assets in this transform.
+  String get key => _node.key;
+
+  /// The package in which this transform is running.
+  String get package => _node.phase.cascade.package;
+
+  /// The stream of primary inputs that will be processed by this transform.
+  ///
+  /// This is exposed as a stream so that the transformer can start working
+  /// before all its inputs are available. The stream is closed not just when
+  /// all inputs are provided, but when barback is confident no more inputs will
+  /// be forthcoming.
+  ///
+  /// A transformer may complete its `apply` method before this stream is
+  /// closed. For example, it may know that each key will only have two inputs
+  /// associated with it, and so use `transform.primaryInputs.take(2)` to access
+  /// only those inputs.
+  Stream<Asset> get primaryInputs => _inputController.stream;
+  final _inputController = new StreamController<Asset>();
+
+  /// The set of all primary inputs that have been emitted by [primaryInputs].
+  ///
+  /// This is populated by the transform's controller so that
+  /// [AggregateTransformController.addedId] synchronously returns the correct
+  /// result after [AggregateTransformController.addInput] is called.
+  final _emittedPrimaryInputs = new AssetSet();
+
+  AggregateTransform._(TransformNode node)
+      : _node = node,
+        super(node);
+
+  /// Gets the asset for an input [id].
+  ///
+  /// If an input with [id] cannot be found, throws an [AssetNotFoundException].
+  Future<Asset> getInput(AssetId id) {
+    if (_emittedPrimaryInputs.containsId(id)) {
+      return syncFuture(() => _emittedPrimaryInputs[id]);
+    } else {
+      return _node.getInput(id);
+    }
+  }
+
+  /// A convenience method to the contents of the input with [id] as a string.
+  ///
+  /// This is equivalent to calling [getInput] followed by [Asset.readAsString].
+  ///
+  /// If the asset was created from a [String] the original string is always
+  /// returned and [encoding] is ignored. Otherwise, the binary data of the
+  /// asset is decoded using [encoding], which defaults to [UTF8].
+  ///
+  /// If an input with [id] cannot be found, throws an [AssetNotFoundException].
+  Future<String> readInputAsString(AssetId id, {Encoding encoding}) {
+    if (encoding == null) encoding = UTF8;
+    return getInput(id).then((input) => input.readAsString(encoding: encoding));
+  }
+
+  /// A convenience method to the contents of the input with [id].
+  ///
+  /// This is equivalent to calling [getInput] followed by [Asset.read].
+  ///
+  /// If the asset was created from a [String], this returns its UTF-8 encoding.
+  ///
+  /// If an input with [id] cannot be found, throws an [AssetNotFoundException].
+  Stream<List<int>> readInput(AssetId id) =>
+      futureStream(getInput(id).then((input) => input.read()));
+
+  /// A convenience method to return whether or not an asset exists.
+  ///
+  /// This is equivalent to calling [getInput] and catching an
+  /// [AssetNotFoundException].
+  Future<bool> hasInput(AssetId id) {
+    return getInput(id).then((_) => true).catchError((error) {
+      if (error is AssetNotFoundException && error.id == id) return false;
+      throw error;
+    });
+  }
+
+  /// Stores [output] as an output created by this transformation.
+  ///
+  /// A transformation can output as many assets as it wants.
+  void addOutput(Asset output) {
+    // TODO(rnystrom): This should immediately throw if an output with that ID
+    // has already been created by this transformer.
+    _outputs.add(output);
+  }
+
+  void consumePrimary(AssetId id) {
+    if (!_emittedPrimaryInputs.containsId(id)) {
+      throw new StateError(
+          "$id can't be consumed because it's not a primary input.");
+    }
+
+    super.consumePrimary(id);
+  }
+}
+
+/// The controller for [AggregateTransform].
+class AggregateTransformController extends BaseTransformController {
+  AggregateTransform get transform => super.transform;
+
+  /// The set of assets that the transformer has emitted.
+  AssetSet get outputs => transform._outputs;
+
+  bool get isDone => transform._inputController.isClosed;
+
+  AggregateTransformController(TransformNode node)
+      : super(new AggregateTransform._(node));
+
+  /// Adds a primary input asset to the [AggregateTransform.primaryInputs]
+  /// stream.
+  void addInput(Asset input) {
+    transform._emittedPrimaryInputs.add(input);
+    transform._inputController.add(input);
+  }
+
+  /// Returns whether an input with the given [id] was added via [addInput].
+  bool addedId(AssetId id) =>
+      transform._emittedPrimaryInputs.containsId(id);
+
+  void done() {
+    transform._inputController.close();
+  }
+}
diff --git a/barback/lib/src/transformer/aggregate_transformer.dart b/barback/lib/src/transformer/aggregate_transformer.dart
new file mode 100644
index 0000000..6873e99
--- /dev/null
+++ b/barback/lib/src/transformer/aggregate_transformer.dart
@@ -0,0 +1,57 @@
+// 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 barback.transformer.aggregate_transformer;
+
+import '../asset/asset_id.dart';
+import 'aggregate_transform.dart';
+
+/// An alternate interface for transformers that want to perform aggregate
+/// transformations on multiple inputs without any individual one of them being
+/// considered "primary".
+///
+/// This is useful for transformers like image spriting, where all the images in
+/// a directory need to be combined into a single image. A normal [Transformer]
+/// can't do this gracefully since when it's running on a single image, it has
+/// no way of knowing what other images exist to request as secondary inputs.
+///
+/// Aggregate transformers work by classifying assets into different groups
+/// based on their ids in [classifyPrimary]. Then [apply] is run once for each
+/// group. For example, a spriting transformer might put each image asset into a
+/// group identified by its directory name. All images in a given directory will
+/// end up in the same group, and they'll all be passed to one [apply] call.
+///
+/// If possible, aggregate transformers should implement
+/// [DeclaringAggregateTransformer] as well to help barback optimize the package
+/// graph.
+abstract class AggregateTransformer {
+  /// Classifies an asset id by returning a key identifying which group the
+  /// asset should be placed in.
+  ///
+  /// All assets for which [classifyPrimary] returns the same key are passed
+  /// together to the same [apply] call.
+  ///
+  /// This may return [Future<String>] or, if it's entirely synchronous,
+  /// [String]. Any string can be used to classify an asset. If possible,
+  /// though, this should return a path-like string to aid in logging.
+  ///
+  /// A return value of `null` indicates that the transformer is not interested
+  /// in an asset. Assets with a key of `null` will not be passed to any [apply]
+  /// call; this is equivalent to [Transformer.isPrimary] returning `false`.
+  classifyPrimary(AssetId id);
+
+  /// Runs this transformer on a group of primary inputs specified by
+  /// [transform].
+  ///
+  /// If this does asynchronous work, it should return a [Future] that completes
+  /// once it's finished.
+  ///
+  /// This may complete before [AggregateTransform.primarInputs] is closed. For
+  /// example, it may know that each key will only have two inputs associated
+  /// with it, and so use `transform.primaryInputs.take(2)` to access only those
+  /// inputs.
+  apply(AggregateTransform transform);
+
+  String toString() => runtimeType.toString().replaceAll("Transformer", "");
+}
diff --git a/barback/lib/src/transformer/barback_settings.dart b/barback/lib/src/transformer/barback_settings.dart
new file mode 100644
index 0000000..5b3f46c
--- /dev/null
+++ b/barback/lib/src/transformer/barback_settings.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2013, 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 barback.transformer.barback_settings;
+
+/// A generic settings object for providing configuration details to
+/// [Transformer]s.
+///
+/// Barback does not specify *how* this is provided to transformers. It is up
+/// to a host application to handle this. (For example, pub passes this to the
+/// transformer's constructor.)
+class BarbackSettings {
+  /// An open-ended map of configuration properties specific to this
+  /// transformer.
+  ///
+  /// The contents of the map should be serializable across isolates, but
+  /// otherwise can contain whatever you want.
+  final Map configuration;
+
+  /// The mode that user is running Barback in.
+  ///
+  /// This will be the same for all transformers in a running instance of
+  /// Barback.
+  final BarbackMode mode;
+
+  BarbackSettings(this.configuration, this.mode);
+}
+
+/// Enum-like class for specifying a mode that transformers may be run in.
+///
+/// Note that this is not a *closed* set of enum values. Host applications may
+/// define their own values for this, so a transformer relying on it should
+/// ensure that it behaves sanely with unknown values.
+class BarbackMode {
+  /// The normal mode used during development.
+  static const DEBUG = const BarbackMode._("debug");
+
+  /// The normal mode used to build an application for deploying to production.
+  static const RELEASE = const BarbackMode._("release");
+
+  /// The name of the mode.
+  ///
+  /// By convention, this is a lowercase string.
+  final String name;
+
+  /// Create a mode named [name].
+  factory BarbackMode(String name) {
+    // Use canonical instances of known names.
+    switch (name) {
+      case "debug": return BarbackMode.DEBUG;
+      case "release": return BarbackMode.RELEASE;
+      default:
+        return new BarbackMode._(name);
+    }
+  }
+
+  const BarbackMode._(this.name);
+
+  String toString() => name;
+}
diff --git a/barback/lib/src/transformer/base_transform.dart b/barback/lib/src/transformer/base_transform.dart
new file mode 100644
index 0000000..fe3ab3b
--- /dev/null
+++ b/barback/lib/src/transformer/base_transform.dart
@@ -0,0 +1,115 @@
+// 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 barback.transformer.base_transform;
+
+import 'dart:async';
+
+import '../asset/asset_id.dart';
+import '../graph/transform_node.dart';
+import '../log.dart';
+import 'transform_logger.dart';
+
+/// The base class for the ephemeral transform objects that are passed to
+/// transformers.
+///
+/// This class provides the transformers with inputs, but its up to the
+/// subclasses to provide a means of emitting outputs.
+abstract class BaseTransform {
+  final TransformNode _node;
+
+  /// The ids of primary inputs that should be consumed.
+  ///
+  /// This is exposed by [BaseTransformController].
+  final _consumedPrimaries = new Set<AssetId>();
+
+  /// Whether the transformer logged an error.
+  ///
+  /// This is exposed via [BaseTransformController].
+  bool _loggedError = false;
+
+  /// The controller for the stream of log entries emitted by the transformer.
+  ///
+  /// This is exposed via [BaseTransformController].
+  ///
+  /// This is synchronous because error logs can cause the transform to fail, so
+  /// we need to ensure that their processing isn't delayed until after the
+  /// transform or build has finished.
+  final _onLogController = new StreamController<LogEntry>.broadcast(sync: true);
+
+  /// A logger so that the [Transformer] can report build details.
+  TransformLogger get logger => _logger;
+  TransformLogger _logger;
+
+  BaseTransform(this._node) {
+    _logger = new TransformLogger((asset, level, message, span) {
+      if (level == LogLevel.ERROR) _loggedError = true;
+
+      // If the log isn't already associated with an asset, use the primary.
+      if (asset == null) asset = _node.info.primaryId;
+      var entry = new LogEntry(_node.info, asset, level, message, span);
+
+      // The log controller can be closed while log entries are still coming in
+      // if the transformer is removed during [apply].
+      if (!_onLogController.isClosed) _onLogController.add(entry);
+    });
+  }
+
+  /// Consume a primary input so that it doesn't get processed by future
+  /// phases or emitted once processing has finished.
+  ///
+  /// Normally each primary input will automatically be forwarded unless the
+  /// transformer overwrites it by emitting an input with the same id. This
+  /// allows the transformer to tell barback not to forward a primary input
+  /// even if it's not overwritten.
+  void consumePrimary(AssetId id) {
+    // TODO(nweiz): throw an error if an id is consumed that wasn't listed as a
+    // primary input.
+    _consumedPrimaries.add(id);
+  }
+}
+
+/// The base class for controllers of subclasses of [BaseTransform].
+///
+/// Controllers are used so that [TransformNode]s can get values from a
+/// [BaseTransform] without exposing getters in the public API.
+abstract class BaseTransformController {
+  /// The [BaseTransform] controlled by this controller.
+  final BaseTransform transform;
+
+  /// The ids of primary inputs that should be consumed.
+  Set<AssetId> get consumedPrimaries => transform._consumedPrimaries;
+
+  /// Whether the transform logged an error.
+  bool get loggedError => transform._loggedError;
+
+  /// The stream of log entries emitted by the transformer during a run.
+  Stream<LogEntry> get onLog => transform._onLogController.stream;
+
+  /// Whether the transform's input or id stream has been closed.
+  ///
+  /// See also [done].
+  bool get isDone;
+
+  BaseTransformController(this.transform);
+
+  /// Mark this transform as finished emitting new inputs or input ids.
+  ///
+  /// This is distinct from [cancel] in that it *doesn't* indicate that the
+  /// transform is finished being used entirely. The transformer may still log
+  /// messages and load secondary inputs. This just indicates that all the
+  /// primary inputs are accounted for.
+  void done();
+
+  /// Mark this transform as canceled.
+  ///
+  /// This will close any streams and release any resources that were allocated
+  /// for the duration of the transformation. Unlike [done], this indicates that
+  /// the transformation is no longer relevant; either it has returned, or
+  /// something external has preemptively invalidated its results.
+  void cancel() {
+    done();
+    transform._onLogController.close();
+  }
+}
diff --git a/barback/lib/src/transformer/declaring_aggregate_transform.dart b/barback/lib/src/transformer/declaring_aggregate_transform.dart
new file mode 100644
index 0000000..cfcb782
--- /dev/null
+++ b/barback/lib/src/transformer/declaring_aggregate_transform.dart
@@ -0,0 +1,105 @@
+// 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 barback.transformer.declaring_aggregate_transform;
+
+import 'dart:async';
+
+import '../asset/asset_id.dart';
+import '../graph/transform_node.dart';
+import '../utils.dart';
+import 'base_transform.dart';
+
+/// A transform for [DeclaringAggregateTransformer]s that allows them to declare
+/// the ids of the outputs they'll generate without generating the concrete
+/// bodies of those outputs.
+class DeclaringAggregateTransform extends BaseTransform {
+  /// The set of output ids declared by the transformer.
+  final _outputIds = new Set<AssetId>();
+
+  /// The transform key.
+  ///
+  /// This is the key returned by [AggregateTransformer.classifyPrimary] for all
+  /// the assets in this transform.
+  final String key;
+
+  /// The package in which this transform is running.
+  final String package;
+
+  /// The stream of primary input ids that have been aggregated for this
+  /// transform.
+  ///
+  /// This is exposed as a stream so that the transformer can start working
+  /// before all its input ids are available. The stream is closed not just when
+  /// all inputs are provided, but when barback is confident no more inputs will
+  /// be forthcoming.
+  ///
+  /// A transformer may complete its `declareOutputs` method before this stream
+  /// is closed. For example, it may know that each key will only have two
+  /// inputs associated with it, and so use `transform.primaryIds.take(2)` to
+  /// access only those inputs' ids.
+  Stream<AssetId> get primaryIds => _primaryIds;
+  Stream<AssetId> _primaryIds;
+
+  /// The controller for [primaryIds].
+  ///
+  /// This is a broadcast controller so that the transform can keep
+  /// [_emittedPrimaryIds] up to date.
+  final _idController = new StreamController<AssetId>.broadcast();
+
+  /// The set of all primary input ids that have been emitted by [primaryIds].
+  final _emittedPrimaryIds = new Set<AssetId>();
+
+  DeclaringAggregateTransform._(TransformNode node)
+      : key = node.key,
+        package = node.phase.cascade.package,
+        super(node) {
+    _idController.stream.listen(_emittedPrimaryIds.add);
+    // [primaryIds] should be a non-broadcast stream.
+    _primaryIds = broadcastToSingleSubscription(_idController.stream);
+  }
+
+  /// Stores [id] as the id of an output that will be created by this
+  /// transformation when it's run.
+  ///
+  /// A transformation can declare as many assets as it wants. If
+  /// [DeclaringTransformer.declareOutputs] declares a given asset id for a
+  /// given input, [Transformer.apply] should emit the corresponding asset as
+  /// well.
+  void declareOutput(AssetId id) {
+    // TODO(nweiz): This should immediately throw if an output with that ID
+    // has already been declared by this transformer.
+    _outputIds.add(id);
+  }
+
+  void consumePrimary(AssetId id) {
+    if (!_emittedPrimaryIds.contains(id)) {
+      throw new StateError(
+          "$id can't be consumed because it's not a primary input.");
+    }
+
+    super.consumePrimary(id);
+  }
+}
+
+/// The controller for [DeclaringAggregateTransform].
+class DeclaringAggregateTransformController extends BaseTransformController {
+  DeclaringAggregateTransform get transform => super.transform;
+
+  /// The set of ids that the transformer declares it will emit.
+  Set<AssetId> get outputIds => transform._outputIds;
+
+  bool get isDone => transform._idController.isClosed;
+
+  DeclaringAggregateTransformController(TransformNode node)
+      : super(new DeclaringAggregateTransform._(node));
+
+  /// Adds a primary input id to the [DeclaringAggregateTransform.primaryIds]
+  /// stream.
+  void addId(AssetId id) => transform._idController.add(id);
+
+  void done() {
+    transform._idController.close();
+  }
+}
diff --git a/barback/lib/src/transformer/declaring_aggregate_transformer.dart b/barback/lib/src/transformer/declaring_aggregate_transformer.dart
new file mode 100644
index 0000000..cef8711
--- /dev/null
+++ b/barback/lib/src/transformer/declaring_aggregate_transformer.dart
@@ -0,0 +1,31 @@
+// 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 barback.transformer.declaring_aggregate_transformer;
+
+import 'declaring_aggregate_transform.dart';
+
+/// An interface for [Transformer]s that can cheaply figure out which assets
+/// they'll emit without doing the work of actually creating those assets.
+///
+/// If a transformer implements this interface, that allows barback to perform
+/// optimizations to make the asset graph work more smoothly.
+abstract class DeclaringAggregateTransformer {
+  /// Declare which assets would be emitted for the primary input ids specified
+  /// by [transform].
+  ///
+  /// This works a little like [AggregateTransformer.apply], with two main
+  /// differences. First, instead of having access to the primary inputs'
+  /// contents, it only has access to their ids. Second, instead of emitting
+  /// [Asset]s, it just emits [AssetId]s through [transform.addOutputId].
+  ///
+  /// If this does asynchronous work, it should return a [Future] that completes
+  /// once it's finished.
+  ///
+  /// This may complete before [DeclaringAggregateTransform.primaryIds] stream
+  /// is closed. For example, it may know that each key will only have two
+  /// inputs associated with it, and so use `transform.primaryIds.take(2)` to
+  /// access only those inputs' ids.
+  declareOutputs(DeclaringAggregateTransform transform);
+}
diff --git a/barback/lib/src/transformer/declaring_transform.dart b/barback/lib/src/transformer/declaring_transform.dart
new file mode 100644
index 0000000..7dff33a
--- /dev/null
+++ b/barback/lib/src/transformer/declaring_transform.dart
@@ -0,0 +1,64 @@
+// 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 barback.transformer.declaring_transform;
+
+import 'dart:async';
+
+import '../asset/asset_id.dart';
+import 'declaring_aggregate_transform.dart';
+import 'transform_logger.dart';
+
+/// Creates a new [DeclaringTransform] wrapping an
+/// [AggregateDeclaringTransform].
+///
+/// Although barback internally works in terms of
+/// [DeclaringAggregateTransformer]s, most transformers only work on individual
+/// primary inputs in isolation. We want to allow those transformers to
+/// implement the more user-friendly [DeclaringTransformer] interface which
+/// takes the more user-friendly [DeclaringTransform] object. This method wraps
+/// the more general [DeclaringAggregateTransform] to return a
+/// [DeclaringTransform] instead.
+Future<DeclaringTransform> newDeclaringTransform(
+    DeclaringAggregateTransform aggregate) {
+  // A wrapped [Transformer] will assign each primary input a unique transform
+  // key, so we can safely get the first asset emitted. We don't want to wait
+  // for the stream to close, since that requires barback to prove that no more
+  // new assets will be generated.
+  return aggregate.primaryIds.first.then((primaryId) => 
+      new DeclaringTransform._(aggregate, primaryId));
+}
+
+/// A transform for [DeclaringTransformer]s that allows them to declare the ids
+/// of the outputs they'll generate without generating the concrete bodies of
+/// those outputs.
+class DeclaringTransform {
+  /// The underlying aggregate transform.
+  final DeclaringAggregateTransform _aggregate;
+
+  final AssetId primaryId;
+
+  /// A logger so that the [Transformer] can report build details.
+  TransformLogger get logger => _aggregate.logger;
+
+  DeclaringTransform._(this._aggregate, this.primaryId);
+
+  /// Stores [id] as the id of an output that will be created by this
+  /// transformation when it's run.
+  ///
+  /// A transformation can declare as many assets as it wants. If
+  /// [DeclaringTransformer.declareOutputs] declareds a given asset id for a
+  /// given input, [Transformer.apply] should emit the corresponding asset as
+  /// well.
+  void declareOutput(AssetId id) => _aggregate.declareOutput(id);
+
+  /// Consume the primary input so that it doesn't get processed by future
+  /// phases or emitted once processing has finished.
+  ///
+  /// Normally the primary input will automatically be forwarded unless the
+  /// transformer overwrites it by emitting an input with the same id. This
+  /// allows the transformer to tell barback not to forward the primary input
+  /// even if it's not overwritten.
+  void consumePrimary() => _aggregate.consumePrimary(primaryId);
+}
diff --git a/barback/lib/src/transformer/declaring_transformer.dart b/barback/lib/src/transformer/declaring_transformer.dart
new file mode 100644
index 0000000..d9e5adc
--- /dev/null
+++ b/barback/lib/src/transformer/declaring_transformer.dart
@@ -0,0 +1,28 @@
+// 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 barback.transformer.declaring_transformer;
+
+import 'dart:async';
+
+import 'declaring_transform.dart';
+
+/// An interface for [Transformer]s that can cheaply figure out which assets
+/// they'll emit without doing the work of actually creating those assets.
+///
+/// If a transformer implements this interface, that allows barback to perform
+/// optimizations to make the asset graph work more smoothly.
+abstract class DeclaringTransformer {
+  /// Declare which assets would be emitted for the primary input id specified
+  /// by [transform].
+  ///
+  /// This works a little like [Transformer.apply], with two main differences.
+  /// First, instead of having access to the primary input's contents, it only
+  /// has access to its id. Second, instead of emitting [Asset]s, it just emits
+  /// [AssetId]s through [transform.addOutputId].
+  ///
+  /// If this does asynchronous work, it should return a [Future] that completes
+  /// once it's finished.
+  declareOutputs(DeclaringTransform transform);
+}
diff --git a/barback/lib/src/transformer/lazy_aggregate_transformer.dart b/barback/lib/src/transformer/lazy_aggregate_transformer.dart
new file mode 100644
index 0000000..891fc1e
--- /dev/null
+++ b/barback/lib/src/transformer/lazy_aggregate_transformer.dart
@@ -0,0 +1,16 @@
+// 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 barback.transformer.lazy_aggregate_transformer;
+
+import 'declaring_aggregate_transformer.dart';
+
+/// An interface for [AggregateTransformer]s that indicates that the
+/// transformer's outputs shouldn't be generated until requested.
+///
+/// The [declareOutputs] method is used to figure out which assets should be
+/// treated as "lazy." Lazy assets will only be forced to be generated if
+/// they're requested by the user or if they're used by a non-declaring
+/// transformer.
+abstract class LazyAggregateTransformer extends DeclaringAggregateTransformer {}
diff --git a/barback/lib/src/transformer/lazy_transformer.dart b/barback/lib/src/transformer/lazy_transformer.dart
new file mode 100644
index 0000000..657b65b
--- /dev/null
+++ b/barback/lib/src/transformer/lazy_transformer.dart
@@ -0,0 +1,16 @@
+// 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 barback.transformer.lazy_transformer;
+
+import 'declaring_transformer.dart';
+
+/// An interface for [Transformer]s that indicates that the transformer's
+/// outputs shouldn't be generated until requested.
+///
+/// The [declareOutputs] method is used to figure out which assets should be
+/// treated as "lazy." Lazy assets will only be forced to be generated if
+/// they're requested by the user or if they're used by a non-declaring
+/// transformer.
+abstract class LazyTransformer extends DeclaringTransformer {}
diff --git a/barback/lib/src/transformer/transform.dart b/barback/lib/src/transformer/transform.dart
new file mode 100644
index 0000000..2da73ee
--- /dev/null
+++ b/barback/lib/src/transformer/transform.dart
@@ -0,0 +1,104 @@
+// 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 barback.transformer.transform;
+
+import 'dart:async';
+import 'dart:convert';
+
+import '../asset/asset.dart';
+import '../asset/asset_id.dart';
+import '../errors.dart';
+import 'aggregate_transform.dart';
+import 'transform_logger.dart';
+
+/// Creates a new [Transform] wrapping an [AggregateTransform].
+///
+/// Although barback internally works in terms of [AggregateTransformer]s, most
+/// transformers only work on individual primary inputs in isolation. We want to
+/// allow those transformers to implement the more user-friendly [Transformer]
+/// interface which takes the more user-friendly [Transform] object. This method
+/// wraps the more general [AggregateTransform] to return a [Transform] instead.
+Future<Transform> newTransform(AggregateTransform aggregate) {
+  // A wrapped [Transformer] will assign each primary input a unique transform
+  // key, so we can safely get the first asset emitted. We don't want to wait
+  // for the stream to close, since that requires barback to prove that no more
+  // new assets will be generated.
+  return aggregate.primaryInputs.first.then((primaryInput) =>
+      new Transform._(aggregate, primaryInput));
+}
+
+/// While a [Transformer] represents a *kind* of transformation, this defines
+/// one specific usage of it on a set of files.
+///
+/// This ephemeral object exists only during an actual transform application to
+/// facilitate communication between the [Transformer] and the code hosting
+/// the transformation. It lets the [Transformer] access inputs and generate
+/// outputs.
+class Transform {
+  /// The underlying aggregate transform.
+  final AggregateTransform _aggregate;
+
+  /// Gets the primary input asset.
+  ///
+  /// While a transformation can use multiple input assets, one must be a
+  /// special "primary" asset. This will be the "entrypoint" or "main" input
+  /// file for a transformation.
+  ///
+  /// For example, with a dart2js transform, the primary input would be the
+  /// entrypoint Dart file. All of the other Dart files that that imports
+  /// would be secondary inputs.
+  final Asset primaryInput;
+
+  /// A logger so that the [Transformer] can report build details.
+  TransformLogger get logger => _aggregate.logger;
+
+  Transform._(this._aggregate, this.primaryInput);
+
+  /// Gets the asset for an input [id].
+  ///
+  /// If an input with [id] cannot be found, throws an [AssetNotFoundException].
+  Future<Asset> getInput(AssetId id) => _aggregate.getInput(id);
+
+  /// A convenience method to the contents of the input with [id] as a string.
+  ///
+  /// This is equivalent to calling [getInput] followed by [Asset.readAsString].
+  ///
+  /// If the asset was created from a [String] the original string is always
+  /// returned and [encoding] is ignored. Otherwise, the binary data of the
+  /// asset is decoded using [encoding], which defaults to [UTF8].
+  ///
+  /// If an input with [id] cannot be found, throws an [AssetNotFoundException].
+  Future<String> readInputAsString(AssetId id, {Encoding encoding}) =>
+      _aggregate.readInputAsString(id, encoding: encoding);
+
+  /// A convenience method to the contents of the input with [id].
+  ///
+  /// This is equivalent to calling [getInput] followed by [Asset.read].
+  ///
+  /// If the asset was created from a [String], this returns its UTF-8 encoding.
+  ///
+  /// If an input with [id] cannot be found, throws an [AssetNotFoundException].
+  Stream<List<int>> readInput(AssetId id) => _aggregate.readInput(id);
+
+  /// A convenience method to return whether or not an asset exists.
+  ///
+  /// This is equivalent to calling [getInput] and catching an
+  /// [AssetNotFoundException].
+  Future<bool> hasInput(AssetId id) => _aggregate.hasInput(id);
+
+  /// Stores [output] as an output created by this transformation.
+  ///
+  /// A transformation can output as many assets as it wants.
+  void addOutput(Asset output) => _aggregate.addOutput(output);
+
+  /// Consume the primary input so that it doesn't get processed by future
+  /// phases or emitted once processing has finished.
+  ///
+  /// Normally the primary input will automatically be forwarded unless the
+  /// transformer overwrites it by emitting an input with the same id. This
+  /// allows the transformer to tell barback not to forward the primary input
+  /// even if it's not overwritten.
+  void consumePrimary() => _aggregate.consumePrimary(primaryInput.id);
+}
diff --git a/barback/lib/src/transformer/transform_logger.dart b/barback/lib/src/transformer/transform_logger.dart
new file mode 100644
index 0000000..5f26a97
--- /dev/null
+++ b/barback/lib/src/transformer/transform_logger.dart
@@ -0,0 +1,71 @@
+// Copyright (c) 2013, 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 barback.transformer.transform_logger;
+
+import 'package:source_span/source_span.dart';
+
+import '../asset/asset_id.dart';
+import '../log.dart';
+
+typedef void LogFunction(AssetId asset, LogLevel level, String message,
+                         SourceSpan span);
+
+/// Object used to report warnings and errors encountered while running a
+/// transformer.
+class TransformLogger {
+  final LogFunction _logFunction;
+
+  TransformLogger(this._logFunction);
+
+  /// Logs an informative message.
+  ///
+  /// If [asset] is provided, the log entry is associated with that asset.
+  /// Otherwise it's associated with the primary input of [transformer]. If
+  /// present, [span] indicates the location in the input asset that caused the
+  /// error.
+  void info(String message, {AssetId asset, SourceSpan span}) {
+    _logFunction(asset, LogLevel.INFO, message, span);
+  }
+
+  /// Logs a message that won't be displayed unless the user is running in
+  /// verbose mode.
+  ///
+  /// If [asset] is provided, the log entry is associated with that asset.
+  /// Otherwise it's associated with the primary input of [transformer]. If
+  /// present, [span] indicates the location in the input asset that caused the
+  /// error.
+  void fine(String message, {AssetId asset, SourceSpan span}) {
+    _logFunction(asset, LogLevel.FINE, message, span);
+  }
+
+  /// Logs a warning message.
+  ///
+  /// If [asset] is provided, the log entry is associated with that asset.
+  /// Otherwise it's associated with the primary input of [transformer]. If
+  /// present, [span] indicates the location in the input asset that caused the
+  /// error.
+  void warning(String message, {AssetId asset, SourceSpan span}) {
+    _logFunction(asset, LogLevel.WARNING, message, span);
+  }
+
+  /// Logs an error message.
+  ///
+  /// If [asset] is provided, the log entry is associated with that asset.
+  /// Otherwise it's associated with the primary input of [transformer]. If
+  /// present, [span] indicates the location in the input asset that caused the
+  /// error.
+  ///
+  /// Logging any errors will cause Barback to consider the transformation to
+  /// have failed, much like throwing an exception. This means that neither the
+  /// primary input nor any outputs emitted by the transformer will be passed on
+  /// to the following phase, and the build will be reported as having failed.
+  ///
+  /// Unlike throwing an exception, this doesn't cause a transformer to stop
+  /// running. This makes it useful in cases where a single input may have
+  /// multiple errors that the user wants to know about.
+  void error(String message, {AssetId asset, SourceSpan span}) {
+    _logFunction(asset, LogLevel.ERROR, message, span);
+  }
+}
diff --git a/barback/lib/src/transformer/transformer.dart b/barback/lib/src/transformer/transformer.dart
new file mode 100644
index 0000000..b40d61d
--- /dev/null
+++ b/barback/lib/src/transformer/transformer.dart
@@ -0,0 +1,93 @@
+// Copyright (c) 2013, 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 barback.transformer.transformer;
+
+import 'dart:async';
+
+import '../asset/asset_id.dart';
+import '../utils.dart';
+import 'transform.dart';
+
+/// A [Transformer] represents a processor that takes in one or more input
+/// assets and uses them to generate one or more output assets.
+///
+/// Dart2js, a SASS->CSS processor, a CSS spriter, and a tool to concatenate
+/// files are all examples of transformers. To define your own transformation
+/// step, extend (or implement) this class.
+///
+/// If possible, transformers should implement [DeclaringTransformer] as well to
+/// help barback optimize the package graph.
+abstract class Transformer {
+  /// Override this to return a space-separated list of file extensions that are
+  /// allowed for the primary inputs to this transformer.
+  ///
+  /// Each extension must begin with a leading `.`.
+  ///
+  /// If you don't override [isPrimary] yourself, it defaults to allowing any
+  /// asset whose extension matches one of the ones returned by this. If you
+  /// don't override [isPrimary] *or* this, it allows all files.
+  String get allowedExtensions => null;
+
+  Transformer() {
+    if (allowedExtensions == null) return;
+
+    var invalidExtensions = allowedExtensions.split(" ")
+        .where((extension) => !extension.startsWith("."))
+        .map((extension) => '"$extension"');
+    if (invalidExtensions.isEmpty) return;
+
+    throw new FormatException('Each extension in $this.allowedExtensions '
+        'must begin with a ".", but ${toSentence(invalidExtensions)} '
+        '${pluralize("doesn't", invalidExtensions.length, plural: "don't")}.');
+  }
+
+  /// Returns `true` if [id] can be a primary input for this transformer.
+  ///
+  /// While a transformer can read from multiple input files, one must be the
+  /// "primary" input. This asset determines whether the transformation should
+  /// be run at all. If the primary input is removed, the transformer will no
+  /// longer be run.
+  ///
+  /// A concrete example is dart2js. When you run dart2js, it will traverse
+  /// all of the imports in your Dart source files and use the contents of all
+  /// of those to generate the final JS. However you still run dart2js "on" a
+  /// single file: the entrypoint Dart file that has your `main()` method.
+  /// This entrypoint file would be the primary input.
+  ///
+  /// If this is not overridden, defaults to allow any asset whose extension
+  /// matches one of the ones returned by [allowedExtensions]. If *that* is
+  /// not overridden, allows all assets.
+  ///
+  /// This may return a `Future<bool>` or, if it's entirely synchronous, a
+  /// `bool`.
+  isPrimary(AssetId id) {
+    // Allow all files if [primaryExtensions] is not overridden.
+    if (allowedExtensions == null) return true;
+
+    for (var extension in allowedExtensions.split(" ")) {
+      if (id.path.endsWith(extension)) return true;
+    }
+
+    return false;
+  }
+
+  /// Run this transformer on the primary input specified by [transform].
+  ///
+  /// The [transform] is used by the [Transformer] for two purposes (in
+  /// addition to accessing the primary input). It can call `getInput()` to
+  /// request additional input assets. It also calls `addOutput()` to provide
+  /// generated assets back to the system. Either can be called multiple times,
+  /// in any order.
+  ///
+  /// In other words, a Transformer's job is to find all inputs for a
+  /// transform, starting at the primary input, then generate all output assets
+  /// and yield them back to the transform.
+  ///
+  /// If this does asynchronous work, it should return a [Future] that completes
+  /// once it's finished.
+  apply(Transform transform);
+
+  String toString() => runtimeType.toString().replaceAll("Transformer", "");
+}
diff --git a/barback/lib/src/transformer/transformer_group.dart b/barback/lib/src/transformer/transformer_group.dart
new file mode 100644
index 0000000..17a2e57
--- /dev/null
+++ b/barback/lib/src/transformer/transformer_group.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2013, 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 barback.transformer.transformer_group;
+
+/// A [TransformerGroup] encapsulates a phased collection of transformers.
+///
+/// A transformer group is defined like a collection of phases, such as you
+/// might pass to [Barback.updateTransformers]. Input assets are transformed by
+/// the first phase, whose results are passed to the second phase, and so on.
+///
+/// However, from the perspective of someone using a [TransformerGroup], it
+/// works just like a [Transformer]. It can be included in phases, and will run
+/// in parallel to other transformers or groups in those phases. Other
+/// transformers and groups will be unable to see any intermediate assets that
+/// are generated by one phase of the group and consumed by another. Phases
+/// after the one containing the group will be able to see its outputs, though.
+class TransformerGroup {
+  /// The phases that comprise this group.
+  ///
+  /// Each element of the inner iterable must be either a [Transformer] or a
+  /// [TransformerGroup].
+  final Iterable<Iterable> phases;
+
+  TransformerGroup(Iterable<Iterable> phases)
+      : this.phases = phases.map((phase) => phase.toList()).toList();
+
+  String toString() => "group of $phases";
+}
diff --git a/barback/lib/src/transformer/wrapping_aggregate_transformer.dart b/barback/lib/src/transformer/wrapping_aggregate_transformer.dart
new file mode 100644
index 0000000..f41d075
--- /dev/null
+++ b/barback/lib/src/transformer/wrapping_aggregate_transformer.dart
@@ -0,0 +1,84 @@
+// 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 barback.transformer.wrapping_aggregate_transformer;
+
+import 'dart:async';
+
+import '../asset/asset_id.dart';
+import '../utils.dart';
+import 'aggregate_transform.dart';
+import 'aggregate_transformer.dart';
+import 'declaring_aggregate_transform.dart';
+import 'declaring_aggregate_transformer.dart';
+import 'declaring_transform.dart';
+import 'declaring_transformer.dart';
+import 'lazy_aggregate_transformer.dart';
+import 'lazy_transformer.dart';
+import 'transform.dart';
+import 'transformer.dart';
+
+/// An [AggregateTransformer] that wraps a non-aggregate [Transformer].
+///
+/// Although barback internally works in terms of [AggregateTransformer]s, most
+/// transformers only work on individual primary inputs in isolation. We want to
+/// allow those transformers to implement the more user-friendly [Transformer]
+/// interface. This class makes that possible.
+class WrappingAggregateTransformer implements AggregateTransformer {
+  /// The wrapped transformer.
+  final Transformer transformer;
+
+  factory WrappingAggregateTransformer(Transformer transformer) {
+    if (transformer is LazyTransformer) {
+      return new _LazyWrappingAggregateTransformer(
+          transformer as LazyTransformer);
+    } else if (transformer is DeclaringTransformer) {
+      return new _DeclaringWrappingAggregateTransformer(
+          transformer as DeclaringTransformer);
+    } else {
+      return new WrappingAggregateTransformer._(transformer);
+    }
+  }
+
+  WrappingAggregateTransformer._(this.transformer);
+
+  Future<String> classifyPrimary(AssetId id) {
+    return syncFuture(() => transformer.isPrimary(id))
+        .then((isPrimary) => isPrimary ? id.path : null);
+  }
+
+  Future apply(AggregateTransform aggregateTransform) {
+    return newTransform(aggregateTransform)
+        .then((transform) => transformer.apply(transform));
+  }
+
+  String toString() => transformer.toString();
+}
+
+/// A wrapper for [DeclaringTransformer]s that implements
+/// [DeclaringAggregateTransformer].
+class _DeclaringWrappingAggregateTransformer
+    extends WrappingAggregateTransformer
+    implements DeclaringAggregateTransformer {
+  final DeclaringTransformer _declaring;
+
+  _DeclaringWrappingAggregateTransformer(DeclaringTransformer transformer)
+      : _declaring = transformer,
+        super._(transformer as Transformer);
+
+  Future declareOutputs(DeclaringAggregateTransform aggregateTransform) {
+    return newDeclaringTransform(aggregateTransform).then((transform) {
+      return (transformer as DeclaringTransformer).declareOutputs(transform);
+    });
+  }
+}
+
+/// A wrapper for [LazyTransformer]s that implements
+/// [LazyAggregateTransformer].
+class _LazyWrappingAggregateTransformer
+    extends _DeclaringWrappingAggregateTransformer
+    implements LazyAggregateTransformer {
+  _LazyWrappingAggregateTransformer(LazyTransformer transformer)
+      : super(transformer);
+}
diff --git a/barback/lib/src/utils.dart b/barback/lib/src/utils.dart
new file mode 100644
index 0000000..7ce6724
--- /dev/null
+++ b/barback/lib/src/utils.dart
@@ -0,0 +1,347 @@
+// Copyright (c) 2013, 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 barback.utils;
+
+import 'dart:async';
+import 'dart:typed_data';
+
+import 'package:stack_trace/stack_trace.dart';
+
+/// A class that represents a value or an error.
+class Fallible<E> {
+  /// Whether [this] has a [value], as opposed to an [error].
+  final bool hasValue;
+
+  /// Whether [this] has an [error], as opposed to a [value].
+  bool get hasError => !hasValue;
+
+  /// The value.
+  ///
+  /// This will be `null` if [this] has an [error].
+  final E _value;
+
+  /// The value.
+  ///
+  /// This will throw a [StateError] if [this] has an [error].
+  E get value {
+    if (hasValue) return _value;
+    throw new StateError("Fallible has no value.\n"
+        "$_error$_stackTraceSuffix");
+  }
+
+  /// The error.
+  ///
+  /// This will be `null` if [this] has a [value].
+  final _error;
+
+  /// The error.
+  ///
+  /// This will throw a [StateError] if [this] has a [value].
+  get error {
+    if (hasError) return _error;
+    throw new StateError("Fallible has no error.");
+  }
+
+  /// The stack trace for [_error].
+  ///
+  /// This will be `null` if [this] has a [value], or if no stack trace was
+  /// provided.
+  final StackTrace _stackTrace;
+
+  /// The stack trace for [error].
+  ///
+  /// This will throw a [StateError] if [this] has a [value].
+  StackTrace get stackTrace {
+    if (hasError) return _stackTrace;
+    throw new StateError("Fallible has no error.");
+  }
+
+  Fallible.withValue(this._value)
+      : _error = null,
+        _stackTrace = null,
+        hasValue = true;
+
+  Fallible.withError(this._error, [this._stackTrace])
+      : _value = null,
+        hasValue = false;
+
+  /// Returns a completed Future with the same value or error as [this].
+  Future toFuture() {
+    if (hasValue) return new Future.value(value);
+    return new Future.error(error, stackTrace);
+  }
+
+  String toString() {
+    if (hasValue) return "Fallible value: $value";
+    return "Fallible error: $error$_stackTraceSuffix";
+  }
+
+  String get _stackTraceSuffix {
+    if (stackTrace == null) return "";
+    return "\nStack trace:\n${new Chain.forTrace(_stackTrace).terse}";
+  }
+}
+
+/// Converts a number in the range [0-255] to a two digit hex string.
+///
+/// For example, given `255`, returns `ff`.
+String byteToHex(int byte) {
+  assert(byte >= 0 && byte <= 255);
+
+  const DIGITS = "0123456789abcdef";
+  return DIGITS[(byte ~/ 16) % 16] + DIGITS[byte % 16];
+}
+
+/// Returns a sentence fragment listing the elements of [iter].
+///
+/// This converts each element of [iter] to a string and separates them with
+/// commas and/or "and" where appropriate.
+String toSentence(Iterable iter) {
+  if (iter.length == 1) return iter.first.toString();
+  return iter.take(iter.length - 1).join(", ") + " and ${iter.last}";
+}
+
+/// Returns [name] if [number] is 1, or the plural of [name] otherwise.
+///
+/// By default, this just adds "s" to the end of [name] to get the plural. If
+/// [plural] is passed, that's used instead.
+String pluralize(String name, int number, {String plural}) {
+  if (number == 1) return name;
+  if (plural != null) return plural;
+  return '${name}s';
+}
+
+/// Converts [input] into a [Uint8List].
+///
+/// If [input] is a [TypedData], this just returns a view on [input].
+Uint8List toUint8List(List<int> input) {
+  if (input is Uint8List) return input;
+  if (input is TypedData) {
+    // TODO(nweiz): remove "as" when issue 11080 is fixed.
+    return new Uint8List.view((input as TypedData).buffer);
+  }
+  return new Uint8List.fromList(input);
+}
+
+/// Group the elements in [iter] by the value returned by [fn].
+///
+/// This returns a map whose keys are the return values of [fn] and whose values
+/// are lists of each element in [iter] for which [fn] returned that key.
+Map<Object, List> groupBy(Iterable iter, fn(element)) {
+  var map = {};
+  for (var element in iter) {
+    var list = map.putIfAbsent(fn(element), () => []);
+    list.add(element);
+  }
+  return map;
+}
+
+/// Flattens nested lists inside an iterable into a single list containing only
+/// non-list elements.
+List flatten(Iterable nested) {
+  var result = [];
+  helper(list) {
+    for (var element in list) {
+      if (element is List) {
+        helper(element);
+      } else {
+        result.add(element);
+      }
+    }
+  }
+  helper(nested);
+  return result;
+}
+
+/// Returns the union of all elements in each set in [sets].
+Set unionAll(Iterable<Set> sets) =>
+  sets.fold(new Set(), (union, set) => union.union(set));
+
+/// Creates a new map from [map] with new keys and values.
+///
+/// The return values of [keyFn] are used as the keys and the return values of
+/// [valueFn] are used as the values for the new map.
+Map mapMap(Map map, keyFn(key, value), valueFn(key, value)) =>
+  new Map.fromIterable(map.keys,
+      key: (key) => keyFn(key, map[key]),
+      value: (key) => valueFn(key, map[key]));
+
+/// Creates a new map from [map] with the same keys.
+///
+/// The return values of [fn] are used as the values for the new map.
+Map mapMapValues(Map map, fn(key, value)) => mapMap(map, (key, _) => key, fn);
+
+/// Creates a new map from [map] with the same keys.
+///
+/// The return values of [fn] are used as the keys for the new map.
+Map mapMapKeys(Map map, fn(key, value)) => mapMap(map, fn, (_, value) => value);
+
+/// Returns whether [set1] has exactly the same elements as [set2].
+bool setEquals(Set set1, Set set2) =>
+  set1.length == set2.length && set1.containsAll(set2);
+
+/// Merges [streams] into a single stream that emits events from all sources.
+///
+/// If [broadcast] is true, this will return a broadcast stream; otherwise, it
+/// will return a buffered stream.
+Stream mergeStreams(Iterable<Stream> streams, {bool broadcast: false}) {
+  streams = streams.toList();
+  var doneCount = 0;
+  // Use a sync stream to preserve the synchrony behavior of the input streams.
+  // If the inputs are sync, then this will be sync as well; if the inputs are
+  // async, then the events we receive will also be async, and forwarding them
+  // sync won't change that.
+  var controller = broadcast ? new StreamController.broadcast(sync: true)
+      : new StreamController(sync: true);
+
+  for (var stream in streams) {
+    stream.listen(
+        controller.add,
+        onError: controller.addError,
+        onDone: () {
+      doneCount++;
+      if (doneCount == streams.length) controller.close();
+    });
+  }
+
+  return controller.stream;
+}
+
+/// Prepends each line in [text] with [prefix]. If [firstPrefix] is passed, the
+/// first line is prefixed with that instead.
+String prefixLines(String text, {String prefix: '| ', String firstPrefix}) {
+  var lines = text.split('\n');
+  if (firstPrefix == null) {
+    return lines.map((line) => '$prefix$line').join('\n');
+  }
+
+  var firstLine = "$firstPrefix${lines.first}";
+  lines = lines.skip(1).map((line) => '$prefix$line').toList();
+  lines.insert(0, firstLine);
+  return lines.join('\n');
+}
+
+/// Returns a [Future] that completes after pumping the event queue [times]
+/// times. By default, this should pump the event queue enough times to allow
+/// any code to run, as long as it's not waiting on some external event.
+Future pumpEventQueue([int times=20]) {
+  if (times == 0) return new Future.value();
+  // We use a delayed future to allow microtask events to finish. The
+  // Future.value or Future() constructors use scheduleMicrotask themselves and
+  // would therefore not wait for microtask callbacks that are scheduled after
+  // invoking this method.
+  return new Future.delayed(Duration.ZERO, () => pumpEventQueue(times - 1));
+}
+
+/// Like `new Future`, but avoids issue 11911 by using `new Future.value` under
+/// the covers.
+// TODO(jmesserly): doc comment changed to due 14601.
+Future newFuture(callback()) => new Future.value().then((_) => callback());
+
+/// Like [Future.sync], but wraps the Future in [Chain.track] as well.
+Future syncFuture(callback()) => Chain.track(new Future.sync(callback));
+
+/// Returns a buffered stream that will emit the same values as the stream
+/// returned by [future] once [future] completes.
+///
+/// If [future] completes to an error, the return value will emit that error and
+/// then close.
+///
+/// If [broadcast] is true, a broadcast stream is returned. This assumes that
+/// the stream returned by [future] will be a broadcast stream as well.
+/// [broadcast] defaults to false.
+Stream futureStream(Future<Stream> future, {bool broadcast: false}) {
+  var subscription;
+  var controller;
+
+  future = future.catchError((e, stackTrace) {
+    // Since [controller] is synchronous, it's likely that emitting an error
+    // will cause it to be cancelled before we call close.
+    if (controller != null) controller.addError(e, stackTrace);
+    if (controller != null) controller.close();
+    controller = null;
+  });
+
+  onListen() {
+    future.then((stream) {
+      if (controller == null) return;
+      subscription = stream.listen(
+          controller.add,
+          onError: controller.addError,
+          onDone: controller.close);
+    });
+  }
+
+  onCancel() {
+    if (subscription != null) subscription.cancel();
+    subscription = null;
+    controller = null;
+  }
+
+  if (broadcast) {
+    controller = new StreamController.broadcast(
+        sync: true, onListen: onListen, onCancel: onCancel);
+  } else {
+    controller = new StreamController(
+        sync: true, onListen: onListen, onCancel: onCancel);
+  }
+  return controller.stream;
+}
+
+/// Returns a [Stream] that will emit the same values as the stream returned by
+/// [callback].
+///
+/// [callback] will only be called when the returned [Stream] gets a subscriber.
+Stream callbackStream(Stream callback()) {
+  var subscription;
+  var controller;
+  controller = new StreamController(onListen: () {
+    subscription = callback().listen(controller.add,
+        onError: controller.addError,
+        onDone: controller.close);
+  },
+      onCancel: () => subscription.cancel(),
+      onPause: () => subscription.pause(),
+      onResume: () => subscription.resume(),
+      sync: true);
+  return controller.stream;
+}
+
+/// Creates a single-subscription stream from a broadcast stream.
+///
+/// The returned stream will enqueue events from [broadcast] until a listener is
+/// attached, then pipe events to that listener.
+Stream broadcastToSingleSubscription(Stream broadcast) {
+  if (!broadcast.isBroadcast) return broadcast;
+
+  // TODO(nweiz): Implement this using a transformer when issues 18588 and 18586
+  // are fixed.
+  var subscription;
+  var controller = new StreamController(onCancel: () => subscription.cancel());
+  subscription = broadcast.listen(controller.add,
+      onError: controller.addError,
+      onDone: controller.close);
+  return controller.stream;
+}
+
+/// A regular expression to match the exception prefix that some exceptions'
+/// [Object.toString] values contain.
+final _exceptionPrefix = new RegExp(r'^([A-Z][a-zA-Z]*)?(Exception|Error): ');
+
+/// Get a string description of an exception.
+///
+/// Many exceptions include the exception class name at the beginning of their
+/// [toString], so we remove that if it exists.
+String getErrorMessage(error) =>
+  error.toString().replaceFirst(_exceptionPrefix, '');
+
+/// Returns a human-friendly representation of [duration].
+String niceDuration(Duration duration) {
+  var result = duration.inMinutes > 0 ? "${duration.inMinutes}:" : "";
+
+  var s = duration.inSeconds % 59;
+  var ms = (duration.inMilliseconds % 1000) ~/ 100;
+  return result + "$s.${ms}s";
+}
diff --git a/barback/lib/src/utils/cancelable_future.dart b/barback/lib/src/utils/cancelable_future.dart
new file mode 100644
index 0000000..64d2b5f
--- /dev/null
+++ b/barback/lib/src/utils/cancelable_future.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2013, 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 barback.utils.cancelable_future;
+
+import 'dart:async';
+
+/// A wrapper for [Future] that can be cancelled.
+///
+/// When this is cancelled, that means it won't complete either successfully or
+/// with an error, regardless of whether the wrapped Future completes.
+/// Cancelling this won't stop whatever code is feeding the wrapped future from
+/// running.
+class CancelableFuture<T> implements Future<T> {
+  bool _canceled = false;
+  final _completer = new Completer<T>.sync();
+
+  CancelableFuture(Future<T> inner) {
+    inner.then((result) {
+      if (_canceled) return;
+      _completer.complete(result);
+    }).catchError((error, stackTrace) {
+      if (_canceled) return;
+      _completer.completeError(error, stackTrace);
+    });
+  }
+
+  Stream<T> asStream() => _completer.future.asStream();
+  Future catchError(Function onError, {bool test(error)}) =>
+    _completer.future.catchError(onError, test: test);
+  Future then(onValue(T value), {Function onError}) =>
+    _completer.future.then(onValue, onError: onError);
+  Future<T> whenComplete(action()) => _completer.future.whenComplete(action);
+  Future timeout(Duration timeLimit, {void onTimeout()}) =>
+    _completer.future.timeout(timeLimit, onTimeout: onTimeout);
+  /// Cancels this future.
+  void cancel() {
+    _canceled = true;
+  }
+}
diff --git a/barback/lib/src/utils/file_pool.dart b/barback/lib/src/utils/file_pool.dart
new file mode 100644
index 0000000..6a23337
--- /dev/null
+++ b/barback/lib/src/utils/file_pool.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2013, 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 barback.utils.file_pool;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:pool/pool.dart';
+import 'package:stack_trace/stack_trace.dart';
+
+import '../utils.dart';
+
+/// Manages a pool of files that are opened for reading to cope with maximum
+/// file descriptor limits.
+///
+/// If a file cannot be opened because too many files are already open, this
+/// will defer the open until a previously opened file is closed and then try
+/// again. If this doesn't succeed after a certain amount of time, the open
+/// will fail and the original "too many files" exception will be thrown.
+class FilePool {
+  /// The underlying pool.
+  ///
+  /// The maximum number of allocated descriptors is based on empirical tests
+  /// that indicate that beyond 32, additional file reads don't provide
+  /// substantial additional throughput.
+  final Pool _pool = new Pool(32, timeout: new Duration(seconds: 60));
+
+  /// Opens the file at [path] for reading.
+  ///
+  /// When the returned stream is listened to, if there are too many files
+  /// open, this will wait for a previously opened file to be closed and then
+  /// try again.
+  Stream<List<int>> openRead(String path) {
+    return futureStream(_pool.request().then((resource) {
+      return Chain.track(new File(path).openRead()).transform(
+          new StreamTransformer.fromHandlers(handleDone: (sink) {
+        sink.close();
+        resource.release();
+      }));
+    }));
+  }
+
+  /// Reads [path] as a string using [encoding].
+  ///
+  /// If there are too many files open and the read fails, this will wait for
+  /// a previously opened file to be closed and then try again.
+  Future<String> readAsString(String path, Encoding encoding) {
+    return _readAsBytes(path).then(encoding.decode);
+  }
+
+  /// Reads [path] as a list of bytes, using [openRead] to retry if there are
+  /// failures.
+  Future<List<int>> _readAsBytes(String path) {
+    var completer = new Completer<List<int>>();
+    var builder = new BytesBuilder();
+
+    openRead(path).listen(builder.add, onDone: () {
+      completer.complete(builder.takeBytes());
+    }, onError: completer.completeError, cancelOnError: true);
+
+    return completer.future;
+  }
+}
diff --git a/barback/lib/src/utils/multiset.dart b/barback/lib/src/utils/multiset.dart
new file mode 100644
index 0000000..20d1c57
--- /dev/null
+++ b/barback/lib/src/utils/multiset.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2013, 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 barback.utils.multiset;
+
+import 'dart:collection';
+
+/// A set of objects where each object can appear multiple times.
+///
+/// Like a set, this has amortized O(1) insertion, removal, and
+/// existence-checking of elements. Counting the number of copies of an element
+/// in the set is also amortized O(1).
+///
+/// Distinct elements retain insertion order. Additional copies of an element
+/// beyond the first are grouped with the original element.
+///
+/// If multiple equal elements are added, only the first actual object is
+/// retained.
+class Multiset<E> extends IterableBase<E> {
+  /// A map from each element in the set to the number of copies of that element
+  /// in the set.
+  final _map = new Map<E, int>();
+
+  Iterator<E> get iterator {
+    return _map.keys.expand((element) {
+      return new Iterable.generate(_map[element], (_) => element);
+    }).iterator;
+  }
+
+  Multiset()
+      : super();
+
+  /// Creates a multi-set and initializes it using the contents of [other].
+  Multiset.from(Iterable<E> other)
+      : super() {
+    other.forEach(add);
+  }
+
+  /// Adds [value] to the set.
+  void add(E value) {
+    _map.putIfAbsent(value, () => 0);
+    _map[value] += 1;
+  }
+
+  /// Removes one copy of [value] from the set.
+  ///
+  /// Returns whether a copy of [value] was removed, regardless of whether more
+  /// copies remain.
+  bool remove(E value) {
+    if (!_map.containsKey(value)) return false;
+
+    _map[value] -= 1;
+    if (_map[value] == 0) _map.remove(value);
+    return true;
+  }
+
+  /// Returns whether [value] is in the set.
+  bool contains(E value) => _map.containsKey(value);
+
+  /// Returns the number of copies of [value] in the set.
+  int count(E value) => _map.containsKey(value) ? _map[value] : 0;
+}
diff --git a/barback/lib/src/utils/stream_pool.dart b/barback/lib/src/utils/stream_pool.dart
new file mode 100644
index 0000000..679672d
--- /dev/null
+++ b/barback/lib/src/utils/stream_pool.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2013, 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 barback.utils.stream_pool;
+
+import 'dart:async';
+
+/// A pool of streams whose events are unified and emitted through a central
+/// stream.
+class StreamPool<T> {
+  /// The stream through which all events from streams in the pool are emitted.
+  Stream<T> get stream => _controller.stream;
+  final StreamController<T> _controller;
+
+  /// Subscriptions to the streams that make up the pool.
+  final _subscriptions = new Map<Stream<T>, StreamSubscription<T>>();
+
+  /// Creates a new stream pool that only supports a single subscriber.
+  ///
+  /// Any events from broadcast streams in the pool will be buffered until a
+  /// listener is subscribed.
+  StreamPool()
+      // Create the controller as sync so that any sync input streams will be
+      // forwarded synchronously. Async input streams will have their asynchrony
+      // preserved, since _controller.add will be called asynchronously.
+      : _controller = new StreamController<T>(sync: true);
+
+  /// Creates a new stream pool where [stream] can be listened to more than
+  /// once.
+  ///
+  /// Any events from buffered streams in the pool will be emitted immediately,
+  /// regardless of whether [stream] has any subscribers.
+  StreamPool.broadcast()
+      // Create the controller as sync so that any sync input streams will be
+      // forwarded synchronously. Async input streams will have their asynchrony
+      // preserved, since _controller.add will be called asynchronously.
+      : _controller = new StreamController<T>.broadcast(sync: true);
+
+  /// Adds [stream] as a member of this pool.
+  ///
+  /// Any events from [stream] will be emitted through [this.stream]. If
+  /// [stream] is sync, they'll be emitted synchronously; if [stream] is async,
+  /// they'll be emitted asynchronously.
+  void add(Stream<T> stream) {
+    if (_subscriptions.containsKey(stream)) return;
+    _subscriptions[stream] = stream.listen(_controller.add,
+        onError: _controller.addError,
+        onDone: () => remove(stream));
+  }
+
+  /// Removes [stream] as a member of this pool.
+  void remove(Stream<T> stream) {
+    var subscription = _subscriptions.remove(stream);
+    if (subscription != null) subscription.cancel();
+  }
+
+  /// Removes all streams from this pool and closes [stream].
+  void close() {
+    for (var subscription in _subscriptions.values) {
+      subscription.cancel();
+    }
+    _subscriptions.clear();
+    _controller.close();
+  }
+}
diff --git a/barback/lib/src/utils/stream_replayer.dart b/barback/lib/src/utils/stream_replayer.dart
new file mode 100644
index 0000000..4a25085
--- /dev/null
+++ b/barback/lib/src/utils/stream_replayer.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2013, 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 barback.utils.stream_replayer;
+
+import 'dart:async';
+import 'dart:collection';
+
+import '../utils.dart';
+
+/// Records the values and errors that are sent through a stream and allows them
+/// to be replayed arbitrarily many times.
+///
+/// This only listens to the wrapped stream when a replayed stream gets a
+/// listener.
+class StreamReplayer<T> {
+  /// The wrapped stream.
+  final Stream<T> _stream;
+
+  /// Whether or not [this] has started listening to [_stream].
+  bool _isSubscribed = false;
+
+  /// Whether or not [_stream] has been closed.
+  bool _isClosed = false;
+
+  /// The buffer of events or errors that have already been emitted by
+  /// [_stream].
+  ///
+  /// Each element is a [Fallible] that's either a value or an error sent
+  /// through the stream.
+  final _buffer = new Queue<Fallible<T>>();
+
+  /// The controllers that are listening for future events from [_stream].
+  final _controllers = new Set<StreamController<T>>();
+
+  StreamReplayer(this._stream);
+
+  /// Returns a stream that replays the values and errors of the input stream.
+  ///
+  /// This stream is a buffered stream.
+  Stream<T> getReplay() {
+    var controller = new StreamController<T>(onListen: _subscribe);
+
+    for (var eventOrError in _buffer) {
+      if (eventOrError.hasValue) {
+        controller.add(eventOrError.value);
+      } else {
+        controller.addError(eventOrError.error, eventOrError.stackTrace);
+      }
+    }
+    if (_isClosed) {
+      controller.close();
+    } else {
+      _controllers.add(controller);
+    }
+    return controller.stream;
+  }
+
+  /// Subscribe to [_stream] if we haven't yet done so.
+  void _subscribe() {
+    if (_isSubscribed || _isClosed) return;
+    _isSubscribed = true;
+
+    _stream.listen((data) {
+      _buffer.add(new Fallible<T>.withValue(data));
+      for (var controller in _controllers) {
+        controller.add(data);
+      }
+    }, onError: (error, [stackTrace]) {
+      _buffer.add(new Fallible<T>.withError(error, stackTrace));
+      for (var controller in _controllers) {
+        controller.addError(error, stackTrace);
+      }
+    }, onDone: () {
+      _isClosed = true;
+      for (var controller in _controllers) {
+        controller.close();
+      }
+      _controllers.clear();
+    });
+  }
+}
diff --git a/barback/pubspec.yaml b/barback/pubspec.yaml
new file mode 100644
index 0000000..ff406e3
--- /dev/null
+++ b/barback/pubspec.yaml
@@ -0,0 +1,34 @@
+name: barback
+
+# Note! This version number is referenced directly in the pub source code in
+# lib/src/barback.dart. Pub implicitly places a version constraint on barback to
+# ensure users only select a version of barback that works with their current
+# version of pub.
+#
+# When the minor or patch version of this is upgraded, you *must* update that
+# version constraint in pub to stay in sync with this.
+version: 0.15.2+4
+
+author: "Dart Team <misc@dartlang.org>"
+homepage: http://github.com/dart-lang/barback
+description: >
+  An asset build system.
+
+  Given a set of input files and a set of transformations (think compilers,
+  preprocessors and the like), will automatically apply the appropriate
+  transforms and generate output files. When inputs are modified, automatically
+  runs the transforms that are affected.
+
+  Runs transforms asynchronously and in parallel when possible to maximize
+  responsiveness.
+dependencies:
+  path: ">=0.9.0 <2.0.0"
+  pool: ">=1.0.0 <2.0.0"
+  source_span: ">=1.0.0 <2.0.0"
+  stack_trace: ">=0.9.1 <2.0.0"
+  collection: ">=0.9.1 <2.0.0"
+dev_dependencies:
+  scheduled_test: ">=0.9.0 <0.11.0"
+  unittest: ">=0.9.0 <0.10.0"
+environment:
+  sdk: ">=1.0.1 <2.0.0"
diff --git a/browser/lib/dart.js b/browser/lib/dart.js
new file mode 100644
index 0000000..f8d686e
--- /dev/null
+++ b/browser/lib/dart.js
@@ -0,0 +1,32 @@
+// Copyright (c) 2013, 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.
+
+(function() {
+// Bootstrap support for Dart scripts on the page as this script.
+if (navigator.userAgent.indexOf('(Dart)') === -1) {
+  // TODO:
+  // - Support in-browser compilation.
+  // - Handle inline Dart scripts.
+
+  // Fall back to compiled JS. Run through all the scripts and
+  // replace them if they have a type that indicate that they source
+  // in Dart code (type="application/dart").
+  var scripts = document.getElementsByTagName("script");
+  var length = scripts.length;
+  for (var i = 0; i < length; ++i) {
+    if (scripts[i].type == "application/dart") {
+      // Remap foo.dart to foo.dart.js.
+      if (scripts[i].src && scripts[i].src != '') {
+        var script = document.createElement('script');
+        script.src = scripts[i].src.replace(/\.dart(?=\?|$)/, '.dart.js');
+        var parent = scripts[i].parentNode;
+        // TODO(vsm): Find a solution for issue 8455 that works with more
+        // than one script.
+        document.currentScript = script;
+        parent.replaceChild(script, scripts[i]);
+      }
+    }
+  }
+}
+})();
diff --git a/browser/lib/interop.js b/browser/lib/interop.js
new file mode 100644
index 0000000..ec02e58
--- /dev/null
+++ b/browser/lib/interop.js
@@ -0,0 +1,9 @@
+// Copyright (c) 2013, 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.
+
+// TODO(jmesserly): remove this script after a deprecation period.
+if (typeof console == "object" && typeof console.warn == "function") {
+  console.warn('<script src="packages/browser/interop.js"> is no longer ' +
+      'needed for dart:js. See http://pub.dartlang.org/packages/browser.');
+}
diff --git a/browser/pubspec.yaml b/browser/pubspec.yaml
new file mode 100644
index 0000000..3bf88f1
--- /dev/null
+++ b/browser/pubspec.yaml
@@ -0,0 +1,8 @@
+name: browser
+version: 0.10.0+2
+author: "Dart Team <misc@dartlang.org>"
+homepage: http://www.dartlang.org
+description: >
+ The bootstrap dart.js script for Dart apps running in the browser.
+environment:
+  sdk: ">=1.3.0-dev.4.1 <2.0.0"
diff --git a/charted/lib/charted.dart b/charted/lib/charted.dart
new file mode 100644
index 0000000..22532a8
--- /dev/null
+++ b/charted/lib/charted.dart
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+/**
+ * A library to create visualizations - implementation/port of D3.js in Dart.
+ */
+library charted;
+
+export 'charts/charts.dart';
+export 'core/utils.dart';
+export 'core/timer.dart';
+export 'core/interpolators.dart';
+export 'layout/layout.dart';
+export 'core/scales.dart';
+export 'selection/selection.dart';
+export 'selection/transition.dart';
+export 'svg/axis.dart';
+export 'svg/shapes.dart';
diff --git a/charted/lib/charts/behaviors/axis_label_tooltip.dart b/charted/lib/charts/behaviors/axis_label_tooltip.dart
new file mode 100644
index 0000000..7ab121e
--- /dev/null
+++ b/charted/lib/charts/behaviors/axis_label_tooltip.dart
@@ -0,0 +1,117 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+/// A behavior that draws marking lines on the chart.
+class AxisLabelTooltip implements ChartBehavior {
+  static const _AXIS_SELECTOR = '.measure-axis-group,.dimension-axis-group';
+
+  SubscriptionsDisposer _disposer = new SubscriptionsDisposer();
+  StreamSubscription _axesChangeSubscription;
+  CartesianArea _area;
+  Element _tooltipRoot;
+
+  math.Rectangle _hostAreaRect;
+  math.Rectangle _renderAreaRect;
+
+  void init(ChartArea area, Selection upper, Selection lower) {
+    if (area is! CartesianArea) return;
+    _area = area;
+    _axesChangeSubscription =
+        _area.onChartAxesUpdated.listen((_) => _subscribe());
+
+    // Axis tooltip requires host to be position: relative.
+    area.host.style.position = 'relative';
+  }
+
+  void dispose() {
+    _disposer.dispose();
+    if (_tooltipRoot != null) _tooltipRoot.remove();
+  }
+
+  void _subscribe() {
+    var elements = _area.host.querySelectorAll(_AXIS_SELECTOR);
+    _disposer.dispose();
+    _disposer.addAll(
+        elements.map((x) => x.onMouseOver.listen(_handleMouseOver)));
+    _disposer.addAll(
+        elements.map((x) => x.onMouseOut.listen(_handleMouseOut)));
+  }
+
+  void _handleMouseOver(MouseEvent e) {
+    Element target = e.target;
+    if (!target.dataset.containsKey('detail')) return;
+    ensureTooltipRoot();
+    ensureRenderAreaRect();
+
+    _tooltipRoot.text = target.dataset['detail'];
+    var position = computeTooltipPosition(
+        target.getBoundingClientRect(),
+        _tooltipRoot.getBoundingClientRect(),
+        _renderAreaRect);
+
+    _tooltipRoot.style
+      ..left = '${position.x}px'
+      ..top = '${position.y}px'
+      ..opacity = '1';
+  }
+
+  void _handleMouseOut(MouseEvent e) {
+    Element target = e.target;
+    if (!target.dataset.containsKey('detail')) return;
+    if (_tooltipRoot != null) {
+      _tooltipRoot.style.opacity = '0';
+    }
+  }
+
+  void ensureTooltipRoot() {
+    if (_tooltipRoot == null) {
+      _tooltipRoot = new Element.tag('div')
+        ..style.position = 'absolute'
+        ..attributes['dir'] = _area.config.isRTL ? 'rtl' : ''
+        ..classes.add('chart-axis-label-tooltip');
+      if (_area.config.isRTL) {
+        _tooltipRoot.classes.add('rtl');
+      } else {
+        _tooltipRoot.classes.remove('rtl');
+      }
+      _area.host.append(_tooltipRoot);
+    }
+  }
+
+  void ensureRenderAreaRect() {
+    var layout = _area.layout;
+    _hostAreaRect = _area.host.getBoundingClientRect();
+    _renderAreaRect = new math.Rectangle(
+        _hostAreaRect.left + layout.chartArea.x + layout.renderArea.x,
+        _hostAreaRect.top + layout.chartArea.y + layout.renderArea.y,
+        layout.renderArea.width, layout.renderArea.height);
+  }
+
+  /// Computes the ideal tooltip position based on orientation.
+  math.Point computeTooltipPosition(math.Rectangle label,
+      math.Rectangle tooltip, math.Rectangle renderArea) {
+    var x = label.left + (label.width - tooltip.width) / 2,
+        y = label.top + (label.height - tooltip.height) / 2;
+
+    if (x + tooltip.width > renderArea.right) {
+      x = renderArea.right - tooltip.width;
+    } else if (x < renderArea.left) {
+      x = renderArea.left;
+    }
+
+    if (y + tooltip.height > renderArea.bottom) {
+      y = renderArea.bottom - tooltip.height;
+    } else if (y < renderArea.top) {
+      y = renderArea.top;
+    }
+
+    return new math.Point(x - _hostAreaRect.left, y - _hostAreaRect.top);
+  }
+}
diff --git a/charted/lib/charts/behaviors/chart_tooltip.dart b/charted/lib/charts/behaviors/chart_tooltip.dart
new file mode 100644
index 0000000..2ff9c84
--- /dev/null
+++ b/charted/lib/charts/behaviors/chart_tooltip.dart
@@ -0,0 +1,199 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+/// Displays tooltip for the values as user moves the mouse pointer over
+/// values in the chart. It displays all the active values in the data row
+/// and use the value in the dimension as the title.
+class ChartTooltip implements ChartBehavior {
+  static const _TOOLTIP_OFFSET = 10;
+  final String orientation;
+  final bool showDimensionValue;
+  final bool showMeasureTotal;
+  final bool showSelectedMeasure;
+
+  ChartArea _area;
+  ChartState _state;
+  Selection _tooltipRoot;
+  SubscriptionsDisposer _disposer = new SubscriptionsDisposer();
+
+  /// Constructs the tooltip.
+  /// If [showDimensionValue] is set, displays the dimension value as title.
+  /// If [showMeasureTotal] is set, displays the total value.
+  ChartTooltip({
+      this.showSelectedMeasure: false,
+      this.showDimensionValue: false,
+      this.showMeasureTotal: false,
+      this.orientation: ORIENTATION_RIGHT});
+
+  /// Sets up listeners for triggering tooltip.
+  void init(ChartArea area, Selection _, Selection __) {
+    _area = area;
+    _state = area.state;
+    _disposer.addAll([
+        area.onValueMouseOver.listen(show),
+        area.onValueMouseOut.listen(hide)
+    ]);
+
+    // Tooltip requires host to be position: relative.
+    area.host.style.position = 'relative';
+
+    var _scope = new SelectionScope.element(_area.host);
+    _scope.append('div')..classed('tooltip');
+    _tooltipRoot = _scope.select('.tooltip');
+  }
+
+  void dispose() {
+    _disposer.dispose();
+    if (_tooltipRoot != null) _tooltipRoot.remove();
+  }
+
+  /// Displays tooltip upon receiving a hover event on an element in chart.
+  show(ChartEvent e) {
+    _tooltipRoot.first
+      ..children.clear()
+      ..attributes['dir'] = _area.config.isRTL ? 'rtl' : '';
+    _tooltipRoot.classed('rtl', _area.config.isRTL);
+
+    // Display dimension value if set in config.
+    if (showDimensionValue) {
+      var column = _area.config.dimensions.elementAt(0),
+          value = _area.data.rows.elementAt(e.row).elementAt(column),
+          formatter = _getFormatterForColumn(column);
+
+      _tooltipRoot.append('div')
+        ..classed('tooltip-title')
+        ..text((formatter != null) ? formatter(value) : value.toString());
+    }
+
+    // Display sum of the values in active row if set in config.
+    if (showMeasureTotal) {
+      var measures = e.series.measures,
+          formatter = _getFormatterForColumn(measures.elementAt(0)),
+          row = _area.data.rows.elementAt(e.row),
+          total = 0;
+      for (int i = 0, len = measures.length; i < len; i++) {
+        total += row.elementAt(measures.elementAt(i));
+      }
+      _tooltipRoot.append('div')
+        ..classed('tooltip-total')
+        ..text((formatter != null) ? formatter(total) : total.toString());
+    }
+
+    // Find the currently selectedMeasures and hoveredMeasures and show
+    // tooltip for them, if none is selected/hovered, show all.
+    var activeMeasures = [];
+    if (showSelectedMeasure) {
+      activeMeasures.addAll(_state.selection);
+      activeMeasures.add(_state.preview);
+      if (activeMeasures.isEmpty) {
+        for (var series in _area.config.series) {
+          activeMeasures.addAll(series.measures);
+        }
+      }
+      activeMeasures.sort();
+    }
+
+    var data = (showSelectedMeasure) ? activeMeasures : e.series.measures;
+
+    // Create the tooltip items base on the number of measures in the series.
+    var items = _tooltipRoot.selectAll('.tooltip-item').
+        data(data);
+    items.enter.append('div')
+        ..classed('tooltip-item')
+        ..classedWithCallback('active', (d, i, c) =>
+            !showSelectedMeasure && (i == e.column));
+
+    // Display the label for the currently active series.
+    var tooltipItems = _tooltipRoot.selectAll('.tooltip-item');
+    tooltipItems.append('div')
+        ..classed('tooltip-item-label')
+        ..textWithCallback((d, i, c) => _area.data.columns.
+            elementAt((showSelectedMeasure) ? d :
+            e.series.measures.elementAt(i)).label);
+
+    // Display the value of the currently active series
+    tooltipItems.append('div')
+      ..classed('tooltip-item-value')
+      ..styleWithCallback('color', (d, i, c) =>
+          _area.theme.getColorForKey(d))
+      ..textWithCallback((d, i, c) {
+      var formatter = _getFormatterForColumn(d),
+          value = _area.data.rows.elementAt(e.row).elementAt(d);
+      return (formatter != null) ? formatter(value) : value.toString();
+    });
+
+    math.Point position = computeTooltipPosition(
+        new math.Point(e.chartX, e.chartY),
+            _tooltipRoot.first.getBoundingClientRect());
+
+    // Set position of the tooltip and display it.
+    _tooltipRoot
+      ..style('left', '${position.x}px')
+      ..style('top', '${position.y}px')
+      ..style('opacity', '1');
+  }
+
+  static String switchPositionDirection(String direction) =>
+      direction == ORIENTATION_LEFT
+          ? ORIENTATION_RIGHT
+          : ORIENTATION_LEFT;
+
+  /// Computes the ideal tooltip position based on orientation.
+  math.Point computeTooltipPosition(
+      math.Point coord, math.Rectangle rect) {
+    var x, y, direction;
+    direction = _area.config.isRTL && _area.config.switchAxesForRTL
+        ? switchPositionDirection(orientation)
+        : orientation;
+
+    if (direction == ORIENTATION_LEFT) {
+      x = coord.x - rect.width - _TOOLTIP_OFFSET;
+      y = coord.y + _TOOLTIP_OFFSET;
+    } else {
+      x = coord.x + _TOOLTIP_OFFSET;
+      y = coord.y + _TOOLTIP_OFFSET;
+    }
+    return boundTooltipPosition(
+        new math.Rectangle(x, y, rect.width, rect.height));
+  }
+
+  /// Positions the tooltip to be inside of the chart boundary.
+  math.Point boundTooltipPosition(math.Rectangle rect) {
+    var hostRect = _area.host.getBoundingClientRect();
+
+    var top = rect.top;
+    var left = rect.left;
+
+    // Checks top and bottom.
+    if (rect.top < 0) {
+      top += (2 * _TOOLTIP_OFFSET);
+    } else if (rect.top + rect.height > hostRect.height) {
+      top -= (rect.height + 2 * _TOOLTIP_OFFSET);
+    }
+
+    // Checks left and right.
+    if (rect.left < 0) {
+      left += (rect.width + 2 * _TOOLTIP_OFFSET);
+    } else if (rect.left + rect.width > hostRect.width) {
+      left -= (rect.width + 2 * _TOOLTIP_OFFSET);
+    }
+
+    return new math.Point(left, top);
+  }
+
+  FormatFunction _getFormatterForColumn(int column) =>
+      _area.data.columns.elementAt(column).formatter;
+
+  hide(ChartEvent e) {
+    if (_tooltipRoot == null) return;
+    _tooltipRoot.style('opacity', '0');
+  }
+}
+
diff --git a/charted/lib/charts/behaviors/line_marker.dart b/charted/lib/charts/behaviors/line_marker.dart
new file mode 100644
index 0000000..78426c0
--- /dev/null
+++ b/charted/lib/charts/behaviors/line_marker.dart
@@ -0,0 +1,114 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+/// A behavior that draws marking lines on the chart.
+class LineMarker implements ChartBehavior {
+  /// Position of the line markers.
+  final Map<int,dynamic> positions;
+
+  /// If true, the markers are drawn above the series
+  final bool drawAboveSeries;
+
+  /// If true, animates (grows from the axis into the chart)
+  final bool animate;
+
+  CartesianArea _area;
+  bool _isLeftAxisPrimary = false;
+  Rect _rect;
+
+  bool _showing;
+  Selection _parent;
+  DataSelection _markers;
+
+  StreamSubscription _axesChangeSubscription;
+
+  LineMarker(this.positions,
+      {this.drawAboveSeries: false, this.animate: false});
+
+  void init(ChartArea area, Selection upper, Selection lower) {
+    if (area is! CartesianArea) return;
+    _area = area;
+    _parent = drawAboveSeries ? upper : lower;
+    _isLeftAxisPrimary = _area.config.isLeftAxisPrimary;
+    _axesChangeSubscription =
+        _area.onChartAxesUpdated.listen((_) => _update());
+    _update();
+  }
+
+  void dispose() {
+    if (_axesChangeSubscription != null) _axesChangeSubscription.cancel();
+    if (_markers != null) _markers.remove();
+  }
+
+  bool _isDimension(int column) => _area.config.dimensions.contains(column);
+
+  String _pathForDimension(int column, bool initial) {
+    assert(_isDimension(column));
+
+    int index;
+    for(index = 0;
+        _area.config.dimensions.elementAt(index) != column; ++index);
+
+    assert(index == 0 || index == 1 && _area.useTwoDimensionAxes);
+
+    var dimensionAtBottom =
+            index == 1 && _isLeftAxisPrimary ||
+            index == 0 && !_isLeftAxisPrimary,
+        scale = _area.dimensionScales.elementAt(index),
+        scaled = scale.scale(positions[column]),
+        theme = _area.theme.dimensionAxisTheme,
+        renderAreaRect = _area.layout.renderArea,
+        left = renderAreaRect.x,
+        right = initial ? left : (left + renderAreaRect.width),
+        bottom = renderAreaRect.y + renderAreaRect.height,
+        top = initial ? bottom : renderAreaRect.y;
+
+    if (scale is OrdinalScale) {
+      var band = scale.rangeBand,
+          bandPadding = theme.axisBandInnerPadding;
+      scaled = scaled - band * bandPadding + _area.theme.defaultStrokeWidth;
+      band = band + 2 * (band * bandPadding - _area.theme.defaultStrokeWidth);
+      return dimensionAtBottom
+          ? 'M ${left + scaled} ${bottom} V ${top} H ${left + scaled + band} V ${bottom} Z'
+          : 'M ${left} ${scaled + band} H ${right} V ${scaled - band} H ${left} Z';
+    } else {
+      return dimensionAtBottom
+          ? 'M ${left + scaled} ${bottom} V ${top}'
+          : 'M ${left} ${scaled} H ${right}';
+    }
+  }
+
+  String _pathForMeasure(int column, bool initial) {
+    throw new UnimplementedError('Measure axis markers');
+  }
+
+  String _getMarkerPath(int column, bool initial) =>
+      _isDimension(column)
+          ? _pathForDimension(column, initial)
+          : _pathForMeasure(column, initial);
+
+  void _update() {
+    if (!_area.isReady) return;
+    _markers = _parent.selectAll('.line-marker').data(positions.keys);
+
+    _markers.enter.append('path').each((d, i, e) {
+      e.classes.add('line-marker');
+      e.attributes['d'] = _getMarkerPath(d, animate);
+    });
+
+    if (animate) {
+      _markers.transition()
+          .attrWithCallback('d', (d, i, e) => _getMarkerPath(d, false));
+    }
+
+    _markers.exit.remove();
+  }
+}
+
diff --git a/charted/lib/charts/behaviors/mouse_tracker.dart b/charted/lib/charts/behaviors/mouse_tracker.dart
new file mode 100644
index 0000000..dd4de0c
--- /dev/null
+++ b/charted/lib/charts/behaviors/mouse_tracker.dart
@@ -0,0 +1,122 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+/// A behavior that tracks mouse pointer and paints a dashed line to
+/// the axes from the current pointer location.
+class MouseTracker implements ChartBehavior {
+  ChartArea _area;
+  Rect _rect;
+
+  Element _markerX;
+  Element _markerY;
+
+  bool _showMarkerX = true;
+  bool _showMarkerY = true;
+  bool _showing;
+
+  Element _lower;
+  Element _upper;
+
+  StreamSubscription _mouseMoveSubscription;
+  StreamSubscription _mouseInSubscription;
+  StreamSubscription _mouseOutSubscription;
+
+  void init(ChartArea area, Selection upper, Selection lower) {
+    _area = area;
+    _lower = lower.first;
+    _upper = upper.first;
+
+    if (area is CartesianArea) {
+      _mouseInSubscription = _area.onMouseOver.listen(_show);
+      _mouseOutSubscription = _area.onMouseOut.listen(_hide);
+    }
+  }
+
+  void dispose() {
+    if (_mouseInSubscription != null) _mouseInSubscription.cancel();
+    if (_mouseOutSubscription != null) _mouseOutSubscription.cancel();
+    if (_mouseMoveSubscription != null) _mouseOutSubscription.cancel();
+    if (_markerX != null) _markerX.remove();
+    if (_markerY != null) _markerY.remove();
+  }
+
+  void _show(ChartEvent e) {
+    if (_mouseMoveSubscription != null) return;
+    _create();
+    _visibility(true);
+    _mouseMoveSubscription = _area.onMouseMove.listen(_update);
+  }
+
+  void _hide(ChartEvent e) {
+    if (_showing != true) return;
+    _visibility(false);
+    _mouseMoveSubscription.cancel();
+    _mouseMoveSubscription = null;
+  }
+
+  void _visibility(bool show) {
+    if (_showing == show) return;
+    var value = show ? 'visible' : 'hidden';
+    if (_markerX != null) {
+      _markerX.style.visibility = value;
+    }
+    if (_markerY != null) {
+      _markerY.style.visibility = value;
+    }
+  }
+
+  bool _isRenderArea(ChartEvent e) =>
+      _rect != null && _rect.contains(e.chartX, e.chartY);
+
+  void _create() {
+    if (_rect == null) {
+      var renderArea = _area.layout.renderArea;
+      _rect = new Rect(
+          renderArea.x, renderArea.y, renderArea.width, renderArea.height);
+    }
+    if (_showMarkerX && _markerX == null) {
+      _markerX = new LineElement();
+      _markerX.attributes
+        ..['x1'] = '0'
+        ..['y1'] = _rect.y.toString()
+        ..['x2'] = '0'
+        ..['y2'] = (_rect.y + _rect.height).toString()
+        ..['class'] = 'axis-marker axis-marker-x';
+      _lower.append(_markerX);
+    }
+    if (_showMarkerY && _markerY == null) {
+      _markerY = new LineElement();
+      _markerY.attributes
+        ..['x1'] = _rect.x.toString()
+        ..['y1'] = '0'
+        ..['x2'] = (_rect.x + _rect.width).toString()
+        ..['y2'] = '0'
+        ..['class'] = 'axis-marker axis-marker-y';
+      _lower.append(_markerY);
+    }
+    _visibility(false);
+  }
+
+  void _update(ChartEvent e) {
+    if (!_isRenderArea(e)) {
+      _visibility(false);
+    } else {
+      _visibility(true);
+      window.requestAnimationFrame((_) {
+        if (_showMarkerX) {
+          _markerX.attributes['transform'] = 'translate(${e.chartX},0)';
+        }
+        if (_showMarkerY) {
+          _markerY.attributes['transform'] = 'translate(0,${e.chartY})';
+        }
+      });
+    }
+  }
+}
diff --git a/charted/lib/charts/cartesian_renderers/bar_chart_renderer.dart b/charted/lib/charts/cartesian_renderers/bar_chart_renderer.dart
new file mode 100644
index 0000000..7f1cf3b
--- /dev/null
+++ b/charted/lib/charts/cartesian_renderers/bar_chart_renderer.dart
@@ -0,0 +1,198 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+class BarChartRenderer extends CartesianRendererBase {
+  static const RADIUS = 2;
+
+  final Iterable<int> dimensionsUsingBand = const[0];
+  final bool alwaysAnimate;
+
+  @override
+  final String name = "bar-rdr";
+
+  BarChartRenderer({this.alwaysAnimate: false});
+
+  /// Returns false if the number of dimension axes on the area is 0.
+  /// Otherwise, the first dimension scale is used to render the chart.
+  @override
+  bool prepare(ChartArea area, ChartSeries series) {
+    _ensureAreaAndSeries(area, series);
+    return area is CartesianArea;
+  }
+
+  @override
+  void draw(Element element, {Future schedulePostRender}) {
+    _ensureReadyToDraw(element);
+
+    var verticalBars = !area.config.isLeftAxisPrimary;
+
+    var measuresCount = series.measures.length,
+        measureScale = area.measureScales(series).first,
+        dimensionScale = area.dimensionScales.first;
+
+    var rows = new List()
+      ..addAll(area.data.rows.map((e) =>
+          new List.generate(
+              measuresCount, (i) => e[series.measures.elementAt(i)])));
+
+    var dimensionVals = area.data.rows.map(
+        (row) => row.elementAt(area.config.dimensions.first)).toList();
+
+    var bars = new OrdinalScale()
+      ..domain = new Range(series.measures.length).toList()
+      ..rangeRoundBands([0, dimensionScale.rangeBand]);
+
+    // Create and update the bar groups.
+
+    var groups = root.selectAll('.bar-rdr-rowgroup').data(rows);
+    var animateBarGroups = alwaysAnimate || !groups.isEmpty;
+
+    groups.enter.append('g')
+      ..classed('bar-rdr-rowgroup')
+      ..attrWithCallback('transform', (d, i, c) => verticalBars ?
+          'translate(${dimensionScale.scale(dimensionVals[i])}, 0)' :
+          'translate(0, ${dimensionScale.scale(dimensionVals[i])})');
+    groups.attrWithCallback('data-row', (d, i, e) => i);
+    groups.exit.remove();
+
+    if (animateBarGroups) {
+      groups.transition()
+        ..attrWithCallback('transform', (d, i, c) => verticalBars ?
+            'translate(${dimensionScale.scale(dimensionVals[i])}, 0)' :
+            'translate(0, ${dimensionScale.scale(dimensionVals[i])})')
+        ..duration(theme.transitionDurationMilliseconds);
+    }
+
+    var barWidth = bars.rangeBand.abs() -
+        theme.defaultSeparatorWidth - theme.defaultStrokeWidth;
+
+    // Create and update the bars
+    // Avoids animation on first render unless alwaysAnimate is set to true.
+
+    var bar = groups.selectAll(
+        '.bar-rdr-bar').dataWithCallback((d, i, c) => rows[i]);
+    var getBarLength = (d) {
+      var scaled = measureScale.scale(d).round() - 1,
+          ht = verticalBars ? rect.height - scaled : scaled;
+      return (ht < 0) ? 0 : ht;
+    };
+    var getBarPos = (d) {
+      num scaled = measureScale.scale(d) - theme.defaultStrokeWidth;
+      return scaled.round();
+    };
+    var buildPath = (d, int i, bool animate) {
+      return verticalBars
+          ? topRoundedRect(
+              bars.scale(i).toInt() + theme.defaultStrokeWidth,
+              animate ? rect.height : getBarPos(d),
+              barWidth, animate ? 0 : getBarLength(d), RADIUS)
+          : rightRoundedRect(
+              1, bars.scale(i).toInt() + theme.defaultStrokeWidth,
+              animate ? 0 : getBarLength(d), barWidth, RADIUS);
+    };
+
+    var enter = bar.enter.appendWithCallback((d, i, e) {
+        var rect = Namespace.createChildElement('path', e),
+            measure = series.measures.elementAt(i),
+            row = int.parse(e.dataset['row']),
+            color = colorForValue(measure, row),
+            style = stylesForValue(measure, row);
+
+        rect.classes.add('bar-rdr-bar ${style.join(" ")}');
+        rect.attributes
+          ..['d'] = buildPath(d, i, animateBarGroups)
+          ..['stroke-width'] = '${theme.defaultStrokeWidth}px'
+          ..['fill'] = color
+          ..['stroke'] = color;
+
+        if (!animateBarGroups) {
+          rect.attributes['data-column'] = '$measure';
+        }
+        return rect;
+      })
+      ..on('click', (d, i, e) => _event(mouseClickController, d, i, e))
+      ..on('mouseover', (d, i, e) => _event(mouseOverController, d, i, e))
+      ..on('mouseout', (d, i, e) => _event(mouseOutController, d, i, e));
+
+    if (animateBarGroups) {
+      bar.each((d, i, e) {
+        var measure = series.measures.elementAt(i),
+            row = int.parse(e.parent.dataset['row']),
+            color = colorForValue(measure, row),
+            styles = stylesForValue(measure, row);
+        e.attributes
+          ..['data-column'] = '$measure'
+          ..['fill'] = color
+          ..['stroke'] = color;
+        e.classes
+          ..removeAll(ChartState.VALUE_CLASS_NAMES)
+          ..addAll(styles);
+      });
+
+      bar.transition()
+        ..attrWithCallback('d', (d, i, e) => buildPath(d, i, false));
+    }
+
+    bar.exit.remove();
+  }
+
+  @override
+  void dispose() {
+    if (root == null) return;
+    root.selectAll('.bar-rdr-rowgroup').remove();
+  }
+
+  @override
+  double get bandInnerPadding {
+    assert(series != null && area != null);
+    var measuresCount = series.measures.length;
+    return measuresCount > 2 ? 1 - (measuresCount / (measuresCount + 1)) :
+        area.theme.dimensionAxisTheme.axisBandInnerPadding;
+  }
+
+  @override
+  double get bandOuterPadding {
+    assert(series != null && area != null);
+    return area.theme.dimensionAxisTheme.axisBandOuterPadding;
+  }
+
+  @override
+  void handleStateChanges(List<ChangeRecord> changes) {
+    var groups = host.querySelectorAll('.bar-rdr-rowgroup');
+    if (groups == null || groups.isEmpty) return;
+
+    for(int i = 0, len = groups.length; i < len; ++i) {
+      var group = groups.elementAt(i),
+          bars = group.querySelectorAll('.bar-rdr-bar'),
+          row = int.parse(group.dataset['row']);
+
+      for(int j = 0, barsCount = bars.length; j < barsCount; ++j) {
+        var bar = bars.elementAt(j),
+            column = int.parse(bar.dataset['column']),
+            color = colorForValue(column, row);
+
+        bar.classes.removeAll(ChartState.VALUE_CLASS_NAMES);
+        bar.classes.addAll(stylesForValue(column, row));
+        bar.attributes
+          ..['fill'] = color
+          ..['stroke'] = color;
+      }
+    }
+  }
+
+  void _event(StreamController controller, data, int index, Element e) {
+    if (controller == null) return;
+    var rowStr = e.parent.dataset['row'];
+    var row = rowStr != null ? int.parse(rowStr) : null;
+    controller.add(
+        new _ChartEvent(scope.event, area,
+            series, row, series.measures.elementAt(index), data));
+  }
+}
diff --git a/charted/lib/charts/cartesian_renderers/bubble_chart_renderer.dart b/charted/lib/charts/cartesian_renderers/bubble_chart_renderer.dart
new file mode 100644
index 0000000..3441d0f
--- /dev/null
+++ b/charted/lib/charts/cartesian_renderers/bubble_chart_renderer.dart
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+
+part of charted.charts;
+
+class BubbleChartRenderer extends CartesianRendererBase {
+  final Iterable<int> dimensionsUsingBand = const[];
+  final double maxBubbleRadius;
+  final bool alwaysAnimate;
+
+  Element _host;
+  Selection _group;
+  SelectionScope _scope;
+
+  @override
+  final String name = "bubble-rdr";
+
+  StreamController<ChartEvent> _mouseOverController;
+  StreamController<ChartEvent> _mouseOutController;
+  StreamController<ChartEvent> _mouseClickController;
+
+  BubbleChartRenderer({
+      this.maxBubbleRadius: 20.0,
+      this.alwaysAnimate: false});
+
+  /*
+   * BubbleChart needs two dimension axes.
+   */
+  @override
+  bool prepare(ChartArea area, ChartSeries series) {
+    _ensureAreaAndSeries(area, series);
+    return area is CartesianArea && area.useTwoDimensionAxes == true;
+  }
+
+  @override
+  void draw(Element element, {Future schedulePostRender}) {
+    assert(series != null && area != null);
+    assert(element != null && element is GElement);
+
+    if (_scope == null) {
+      _host = element;
+      _scope = new SelectionScope.element(element);
+      _group = _scope.selectElements([_host]);
+    }
+
+    var geometry = area.layout.renderArea,
+        measuresCount = series.measures.length,
+        bubbleRadiusScale = area.measureScales(series).first,
+        xDimensionScale = area.dimensionScales.first,
+        yDimensionScale = area.dimensionScales.last,
+        theme = area.theme,
+        bubbleRadiusFactor =
+            maxBubbleRadius / min([geometry.width, geometry.height]);
+
+    String color(i) => theme.getColorForKey(series.measures.elementAt(i));
+
+    // Measure values used to set size of the bubble.
+    var columns = [];
+    for (int m in series.measures) {
+      columns.add(new List.from(
+          area.data.rows.map((Iterable row) => row.elementAt(m))));
+    }
+
+    // Dimension values used to position the bubble.
+    var xDimensionIndex = area.config.dimensions.first,
+        yDimensionIndex = area.config.dimensions.last,
+        xDimensionVals = [],
+        yDimensionVals = [];
+    for (var row in area.data.rows) {
+      xDimensionVals.add(row.elementAt(xDimensionIndex));
+      yDimensionVals.add(row.elementAt(yDimensionIndex));
+    }
+
+    var group = _group.selectAll('.measure-group').data(columns);
+    group.enter.append('g')..classed('measure-group');
+    group.each((d, i, e) {
+      e.style.setProperty('fill', color(i));
+      e.attributes['data-column'] = series.measures.elementAt(i);
+    });
+    group.exit.remove();
+
+    var measures = group.selectAll('.bubble').dataWithCallback(
+        (d, i, e) => columns[i]);
+
+    measures.enter.append('circle')..classed('bubble');
+    measures.each((d, i, e) {
+      e.attributes
+        ..['transform'] = 'translate('
+            '${xDimensionScale.scale(xDimensionVals[i])},'
+            '${yDimensionScale.scale(yDimensionVals[i])})'
+        ..['r'] = '${bubbleRadiusScale.scale(d) * bubbleRadiusFactor}'
+        ..['data-row'] = i;
+    });
+    measures.exit.remove();
+    handleStateChanges([]);
+  }
+
+  @override
+  void dispose() {
+    if (_group == null) return;
+    _group.selectAll('.row-group').remove();
+  }
+
+  @override
+  double get bandInnerPadding => 1.0;
+
+  @override
+  double get bandOuterPadding => area.theme.dimensionAxisTheme.axisOuterPadding;
+
+  @override
+  Extent get extent {
+    assert(series != null && area != null);
+    var rows = area.data.rows,
+        max = rows[0][series.measures.first],
+        min = max;
+
+    rows.forEach((row) {
+      series.measures.forEach((idx) {
+        if (row[idx] > max) max = row[idx];
+        if (row[idx] < min) min = row[idx];
+      });
+    });
+    return new Extent(min, max);
+  }
+
+  @override
+  void handleStateChanges(List<ChangeRecord> changes) {
+    var groups = host.querySelectorAll('.bar-rdr-rowgroup');
+    if (groups == null || groups.isEmpty) return;
+
+    for(int i = 0, len = groups.length; i < len; ++i) {
+      var group = groups.elementAt(i),
+          bars = group.querySelectorAll('.bar-rdr-bar'),
+          row = int.parse(group.dataset['row']);
+
+      for(int j = 0, barsCount = bars.length; j < barsCount; ++j) {
+        var bar = bars.elementAt(j),
+            column = int.parse(bar.dataset['column']),
+            color = colorForValue(column, row);
+
+        bar.classes.removeAll(ChartState.VALUE_CLASS_NAMES);
+        bar.classes.addAll(stylesForValue(column, row));
+        bar.style
+          ..setProperty('fill', color)
+          ..setProperty('stroke', color);
+      }
+    }
+  }
+
+  void _event(StreamController controller, data, int index, Element e) {
+    if (controller == null) return;
+    var rowStr = e.parent.dataset['row'];
+    var row = rowStr != null ? int.parse(rowStr) : null;
+    controller.add(
+        new _ChartEvent(_scope.event, area, series, row, index, data));
+  }
+}
diff --git a/charted/lib/charts/cartesian_renderers/cartesian_base_renderer.dart b/charted/lib/charts/cartesian_renderers/cartesian_base_renderer.dart
new file mode 100644
index 0000000..6002dce
--- /dev/null
+++ b/charted/lib/charts/cartesian_renderers/cartesian_base_renderer.dart
@@ -0,0 +1,262 @@
+/**
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+
+part of charted.charts;
+
+abstract class CartesianRendererBase implements CartesianRenderer {
+  final SubscriptionsDisposer _disposer = new SubscriptionsDisposer();
+
+  CartesianArea area;
+  ChartSeries series;
+  ChartTheme theme;
+  ChartState state;
+  Rect rect;
+
+  List<int> _columnStateCache;
+  List<Iterable<String>> _columnStylesCache;
+
+  final _valueColorCache = new Map<int,String>();
+  final _valueStylesCache = new Map<int,Iterable<String>>();
+
+  Element host;
+  Selection root;
+  SelectionScope scope;
+
+  Pair<int,int> _hovered;
+  Pair<int,int> _highlighted;
+
+  StreamController<ChartEvent> mouseOverController;
+  StreamController<ChartEvent> mouseOutController;
+  StreamController<ChartEvent> mouseClickController;
+
+  void _ensureAreaAndSeries(CartesianArea area, ChartSeries series) {
+    assert(area != null && series != null);
+    assert(this.area == null || this.area == area);
+    if (this.area == null) {
+      if (area.state != null) {
+        this.state = area.state;
+        _disposer.add(this.state.changes.listen((changes) {
+          resetStylesCache();
+          handleStateChanges(changes);
+        }));
+      }
+    }
+    this.area = area;
+    this.series = series;
+  }
+
+  void _ensureReadyToDraw(Element element) {
+    assert(series != null && area != null);
+    assert(element != null && element is GElement);
+
+    if (scope == null) {
+      host = element;
+      scope = new SelectionScope.element(element);
+      root = scope.selectElements([host]);
+    }
+
+    theme = area.theme;
+    rect = area.layout.renderArea;
+    resetStylesCache();
+  }
+
+  void resetStylesCache() {
+    var length = area.data.columns.length;
+    _columnStylesCache = new List(length);
+    _columnStateCache = new List(length);
+    _valueStylesCache.clear();
+    _valueColorCache.clear();
+    _computeColumnStates();
+  }
+
+  /// Override this method to handle state changes.
+  void handleStateChanges(List<ChangeRecord> changes);
+
+  @override
+  Extent get extent {
+    assert(series != null && area != null);
+    var rows = area.data.rows,
+        measures = series.measures,
+        max = rows.isEmpty ? 0 : rows[0][measures.first],
+        min = max;
+
+    for (int i = 0, len = rows.length; i < len; ++i) {
+      var row = rows.elementAt(i);
+      for (int j = 0, jLen = measures.length; j < jLen; ++j) {
+        var measure = measures.elementAt(j);
+        if (row[measure] > max) {
+          max = row[measure];
+        } else if (row[measure] < min) {
+          min = row[measure];
+        }
+      }
+    }
+    return new Extent(min, max);
+  }
+
+  @override
+  Stream<ChartEvent> get onValueMouseOver {
+    if (mouseOverController == null) {
+      mouseOverController = new StreamController.broadcast(sync: true);
+    }
+    return mouseOverController.stream;
+  }
+
+  @override
+  Stream<ChartEvent> get onValueMouseOut {
+    if (mouseOutController == null) {
+      mouseOutController = new StreamController.broadcast(sync: true);
+    }
+    return mouseOutController.stream;
+  }
+
+  @override
+  Stream<ChartEvent> get onValueClick {
+    if (mouseClickController == null) {
+      mouseClickController = new StreamController.broadcast(sync: true);
+    }
+    return mouseClickController.stream;
+  }
+
+  double get bandInnerPadding => 1.0;
+  double get bandOuterPadding =>
+      area.theme.dimensionAxisTheme.axisOuterPadding;
+
+  void _computeColumnStates() {
+    area.config.series.forEach((ChartSeries series) {
+      series.measures.forEach((int column) {
+        if (_columnStateCache[column] != null) return;
+        int flags = 0;
+        if (state != null && area.useRowColoring == false) {
+          if (state.highlights.isNotEmpty) {
+            flags |= (state.highlights.any((x) => x.first == column)
+                ? ChartState.COL_HIGHLIGHTED
+                : ChartState.COL_UNHIGHLIGHTED);
+          }
+          if (state.selection.isNotEmpty) {
+            flags |= (state.isSelected(column)
+                ? ChartState.COL_SELECTED
+                : ChartState.COL_UNSELECTED);
+          }
+          if (!state.isVisible(column)) {
+            flags |= ChartState.COL_HIDDEN;
+          }
+          if (state.preview == column) {
+            flags |= ChartState.COL_PREVIEW;
+          }
+          if (state.hovered != null && state.hovered.first == column) {
+            flags |= ChartState.COL_HOVERED;
+          }
+        }
+        _columnStateCache[column] = flags;
+      });
+    });
+  }
+
+  Iterable<String> stylesForColumn(int column) {
+    if (_columnStylesCache[column] == null) {
+      if (state == null || area.useRowColoring) {
+        _columnStylesCache[column] = const[];
+      } else {
+        var styles = [],
+            flags = _columnStateCache[column];
+
+        if (flags & ChartState.COL_SELECTED != 0) {
+          styles.add(ChartState.COL_SELECTED_CLASS);
+        } else if (flags & ChartState.COL_UNSELECTED != 0){
+          styles.add(ChartState.COL_UNSELECTED_CLASS);
+        }
+
+        if (flags & ChartState.COL_HIGHLIGHTED != 0) {
+          styles.add(ChartState.COL_HIGHLIGHTED_CLASS);
+        } else if (flags & ChartState.COL_UNHIGHLIGHTED != 0) {
+          styles.add(ChartState.COL_UNHIGHLIGHTED_CLASS);
+        }
+
+        if (flags & ChartState.COL_HOVERED != 0) {
+          styles.add(ChartState.COL_HOVERED_CLASS);
+        }
+        if (flags & ChartState.COL_PREVIEW != 0) {
+          styles.add(ChartState.COL_PREVIEW_CLASS);
+        }
+        if (flags & ChartState.COL_HIDDEN != 0) {
+          styles.add(ChartState.COL_HIDDEN_CLASS);
+        }
+
+        _columnStylesCache[column] = styles;
+      }
+    }
+    return _columnStylesCache[column];
+  }
+
+  String colorForColumn(int column) =>
+      theme.getColorForKey(column,
+          _flagsToColorState(_columnStateCache[column]));
+
+  Iterable<String> stylesForValue(int column, int row) {
+    var hash = hash2(column, row);
+    if (_valueStylesCache[hash] == null) {
+      if (state == null) {
+        _valueStylesCache[hash] = const[];
+      } else {
+        var styles = stylesForColumn(column).toList();
+        if (state.highlights.isNotEmpty) {
+          styles.add(state.highlights.any((x) => x.last == row)
+              ? ChartState.VAL_HIGHLIGHTED_CLASS
+              : ChartState.VAL_UNHIGHLIGHTED_CLASS);
+        }
+        if (state.hovered != null && state.hovered.last == row) {
+          styles.add(ChartState.VAL_HOVERED_CLASS);
+        }
+        _valueStylesCache[hash] = styles;
+      }
+    }
+    return _valueStylesCache[hash];
+  }
+
+  String colorForValue(int column, int row) {
+    var hash = hash2(column, row);
+    if (_valueColorCache[hash] == null) {
+      if (state == null) {
+        _valueColorCache[hash] =
+            theme.getColorForKey(area.useRowColoring ? row : column);
+      } else {
+        var flags = _columnStateCache[column];
+        if (state.highlights.isNotEmpty) {
+          flags |= (state.highlights.any((x) => x.last == row)
+              ? ChartState.VAL_HIGHLIGHTED
+              : ChartState.VAL_UNHIGHLIGHTED);
+        }
+        if (state.hovered != null && state.hovered.last == row) {
+          flags |= ChartState.VAL_HOVERED;
+        }
+        _valueColorCache[hash] = theme.getColorForKey(
+            area.useRowColoring ? row : column, _flagsToColorState(flags));
+      }
+    }
+    return _valueColorCache[hash];
+  }
+
+  //
+  // TODO(prsd): Let ChartTheme specify the mapping
+  //
+  int _flagsToColorState(int flags) {
+    // Unselected columns and unhighlighted rows are inactive.
+    if (flags & ChartState.COL_UNSELECTED != 0 ||
+        flags & ChartState.VAL_UNHIGHLIGHTED != 0) {
+      return ChartTheme.STATE_INACTIVE;
+    }
+    // Selected columns and highlighted rows are active.
+    if (flags & ChartState.COL_PREVIEW != 0 ||
+        flags & ChartState.VAL_HOVERED != 0) {
+      return ChartTheme.STATE_ACTIVE;
+    }
+    // All others are normal.
+    return ChartTheme.STATE_NORMAL;
+  }
+}
diff --git a/charted/lib/charts/cartesian_renderers/line_chart_renderer.dart b/charted/lib/charts/cartesian_renderers/line_chart_renderer.dart
new file mode 100644
index 0000000..7349af7
--- /dev/null
+++ b/charted/lib/charts/cartesian_renderers/line_chart_renderer.dart
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+
+part of charted.charts;
+
+class LineChartRenderer extends CartesianRendererBase {
+  final Iterable<int> dimensionsUsingBand = const[];
+  final SubscriptionsDisposer _disposer = new SubscriptionsDisposer();
+  final bool alwaysAnimate;
+
+  List _xPositions = [];
+  Map<int, CircleElement> _measureCircleMap = {};
+  int currentDataIndex = -1;
+
+  @override
+  final String name = "line-rdr";
+
+  LineChartRenderer({this.alwaysAnimate: false});
+
+  /*
+   * Returns false if the number of dimension axes on the area is 0.
+   * Otherwise, the first dimension scale is used to render the chart.
+   */
+  @override
+  bool prepare(ChartArea area, ChartSeries series) {
+    _ensureAreaAndSeries(area, series);
+    _trackPointerInArea();
+    return area is CartesianArea;
+  }
+
+  @override
+  void draw(Element element, {Future schedulePostRender}) {
+    _ensureReadyToDraw(element);
+
+    var measureScale = area.measureScales(series).first,
+        dimensionScale = area.dimensionScales.first;
+
+    // Create lists of values in measure columns.
+    var lines = series.measures.map((column) {
+      return area.data.rows.map((values) => values[column]).toList();
+    }).toList();
+
+    // We only support one dimension axes, so we always use the
+    // first dimension.
+    var x = area.data.rows.map(
+        (row) => row.elementAt(area.config.dimensions.first)).toList();
+
+    var rangeBandOffset =
+        dimensionScale is OrdinalScale ? dimensionScale.rangeBand / 2 : 0;
+
+    _xPositions =
+        x.map((val) => dimensionScale.scale(val) + rangeBandOffset).toList();
+
+    // Add circles that track user's pointer movements.
+    var linePoints = root.selectAll('.line-rdr-point').data(series.measures);
+    linePoints.enter.append('circle').each((d, i, e) {
+      e.classes.add('line-rdr-point');
+      e.attributes['r'] = '4';
+    });
+
+    linePoints.each((d, i, e) {
+      var color = colorForColumn(d);
+      e.attributes
+        ..['r'] = '4'
+        ..['stroke'] = color
+        ..['fill'] = color
+        ..['data-column'] = '$d';
+    });
+
+    linePoints.exit.remove();
+
+    var line = new SvgLine(
+        xValueAccessor: (d, i) => dimensionScale.scale(x[i]) + rangeBandOffset,
+        yValueAccessor: (d, i) => measureScale.scale(d));
+
+    // Add lines and hook up hover and selection events.
+    var svgLines = root.selectAll('.line-rdr-line').data(lines);
+    svgLines.enter.append('path')
+        ..each((d, i, e) {
+          e.classes.add('line-rdr-line');
+          e.attributes['fill'] = 'none';
+        });
+
+    svgLines.each((d, i, e) {
+      var column = series.measures.elementAt(i),
+          color = colorForColumn(column),
+          styles = stylesForColumn(column);
+      e.classes.addAll(styles);
+      e.attributes
+        ..['d'] = line.path(d, i, e)
+        ..['stroke'] = color
+        ..['data-column'] = '$column';
+    });
+
+    svgLines.exit.remove();
+  }
+
+  @override
+  void dispose() {
+    if (root == null) return;
+    root.selectAll('.line-rdr-line').remove();
+    root.selectAll('.line-rdr-point').remove();
+    _disposer.dispose();
+  }
+
+  Selection _getSelectionForColumn(int column) =>
+      root.selectAll('.line-rdr-line[data-column="$column"]');
+
+  @override
+  void handleStateChanges(List<ChangeRecord> changes) {
+    resetStylesCache();
+    for (int i = 0; i < series.measures.length; ++i) {
+      var column = series.measures.elementAt(i),
+          selection = _getSelectionForColumn(column),
+          color = colorForColumn(column),
+          styles = stylesForColumn(column);
+      selection.each((d,i,e) {
+        e.classes
+          ..removeAll(ChartState.COLUMN_CLASS_NAMES)
+          ..addAll(styles);
+      });
+      selection.transition()
+        ..style('stroke', color)
+        ..duration(50);
+    }
+  }
+
+  void _showTrackingCircles(int row) {
+    var yScale = area.measureScales(series).first;
+    root.selectAll('.line-rdr-point').each((d, i, e) {
+      var x = _xPositions[row];
+      var y = yScale.scale(area.data.rows.elementAt(row).elementAt(d));
+      e.attributes
+        ..['cx'] = '$x'
+        ..['cy'] = '$y';
+      e.style.setProperty('opacity', '1');
+    });
+  }
+
+  void _hideTrackingCircles() {
+    root.selectAll('.line-rdr-point').style('opacity', '0.0');
+  }
+
+  int _getNearestRowIndex(double x) {
+    var lastSmallerValue = 0;
+    var chartX = x - area.layout.renderArea.x;
+    for (var i = 0; i < _xPositions.length; i++) {
+      var pos = _xPositions[i];
+      if (pos < chartX) {
+        lastSmallerValue = pos;
+      } else {
+        return i == 0 ? 0 :
+          (chartX - lastSmallerValue <= pos - chartX) ? i - 1 : i;
+      }
+    }
+    return _xPositions.length - 1;
+  }
+
+  void _trackPointerInArea() {
+    _disposer.add(area.onMouseMove.listen((ChartEvent event) {
+      if (area.layout.renderArea.contains(event.chartX, event.chartY)) {
+        var renderAreaX = event.chartX - area.layout.renderArea.x,
+            row = _getNearestRowIndex(event.chartX);
+        window.animationFrame.then((_) => _showTrackingCircles(row));
+      } else {
+        _hideTrackingCircles();
+      }
+    }));
+    _disposer.add(area.onMouseOut.listen((ChartEvent event) {
+      _hideTrackingCircles();
+    }));
+  }
+}
diff --git a/charted/lib/charts/cartesian_renderers/stackedbar_chart_renderer.dart b/charted/lib/charts/cartesian_renderers/stackedbar_chart_renderer.dart
new file mode 100644
index 0000000..cc020cc
--- /dev/null
+++ b/charted/lib/charts/cartesian_renderers/stackedbar_chart_renderer.dart
@@ -0,0 +1,286 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+class StackedBarChartRenderer extends CartesianRendererBase {
+  static const RADIUS = 2;
+
+  final Iterable<int> dimensionsUsingBand = const[0];
+  final bool alwaysAnimate;
+
+  @override
+  final String name = "stack-rdr";
+
+  StackedBarChartRenderer({this.alwaysAnimate: false});
+
+  /// Returns false if the number of dimension axes on the area is 0.
+  /// Otherwise, the first dimension scale is used to render the chart.
+  @override
+  bool prepare(CartesianArea area, ChartSeries series) {
+    _ensureAreaAndSeries(area, series);
+    return true;
+  }
+
+  @override
+  void draw(Element element, {Future schedulePostRender}) {
+    _ensureReadyToDraw(element);
+    var verticalBars = !area.config.isLeftAxisPrimary;
+
+    var measuresCount = series.measures.length,
+        measureScale = area.measureScales(series).first,
+        dimensionScale = area.dimensionScales.first;
+
+    var rows = new List()
+      ..addAll(area.data.rows.map((e) =>
+          new List.generate(measuresCount,
+              (i) => e[series.measures.elementAt(_reverseIdx(i))])));
+
+    var dimensionVals = area.data.rows.map(
+        (row) => row.elementAt(area.config.dimensions.first)).toList();
+
+    var groups = root.selectAll('.stack-rdr-rowgroup').data(rows);
+    var animateBarGroups = alwaysAnimate || !groups.isEmpty;
+    groups.enter.append('g')
+      ..classed('stack-rdr-rowgroup')
+      ..attrWithCallback('transform', (d, i, c) => verticalBars ?
+          'translate(${dimensionScale.scale(dimensionVals[i])}, 0)' :
+          'translate(0, ${dimensionScale.scale(dimensionVals[i])})');
+    groups.attrWithCallback('data-row', (d, i, e) => i);
+    groups.exit.remove();
+
+    if (animateBarGroups) {
+      groups.transition()
+        ..attrWithCallback('transform', (d, i, c) => verticalBars ?
+            'translate(${dimensionScale.scale(dimensionVals[i])}, 0)' :
+            'translate(0, ${dimensionScale.scale(dimensionVals[i])})')
+        ..duration(theme.transitionDurationMilliseconds);
+    }
+
+    var bar = groups.selectAll('.stack-rdr-bar').dataWithCallback((d, i, c) => d);
+
+    // TODO(prsd): Revisit animation and state tracking.
+    var ic = -1,
+        order = 0,
+        prevOffsetVal = new List();
+
+    // Keep track of "y" values.
+    // These are used to insert values in the middle of stack when necessary
+    if (animateBarGroups) {
+      prevOffsetVal.add(0);
+      bar.each((d, i, e) {
+        var offset = e.dataset['offset'],
+            offsetVal = offset != null ? int.parse(offset) : 0;
+        if (i > ic) {
+          prevOffsetVal[prevOffsetVal.length - 1] = offsetVal;
+        } else {
+          prevOffsetVal.add(offsetVal);
+        }
+        ic = i;
+      });
+      ic = 1000000000;
+    }
+
+    var barWidth = dimensionScale.rangeBand - theme.defaultStrokeWidth;
+
+    // Calculate height of each segment in the bar.
+    // Uses prevAllZeroHeight and prevOffset to track previous segments
+    var prevAllZeroHeight = true,
+        prevOffset = 0;
+    var getBarLength = (d, i) {
+      if (!verticalBars) return measureScale.scale(d).round();
+      var retval = rect.height - measureScale.scale(d).round();
+      if (i != 0) {
+        // If previous bars has 0 height, don't offset for spacing
+        // If any of the previous bar has non 0 height, do the offset.
+        retval -= prevAllZeroHeight
+            ? 1
+            : (theme.defaultSeparatorWidth + theme.defaultStrokeWidth);
+        retval += prevOffset;
+      } else {
+        // When rendering next group of bars, reset prevZeroHeight.
+        prevOffset = 0;
+        prevAllZeroHeight = true;
+        retval -= 1; // -1 so bar does not overlap x axis.
+      }
+
+      if (retval <= 0) {
+        prevOffset = prevAllZeroHeight
+            ? 0
+            : theme.defaultSeparatorWidth + theme.defaultStrokeWidth + retval;
+        retval = 0;
+      }
+      prevAllZeroHeight = (retval == 0) && prevAllZeroHeight;
+      return retval;
+    };
+
+    // Initial "y" position of a bar that is being created.
+    // Only used when animateBarGroups is set to true.
+    var getInitialBarPos = (i) {
+      var tempY;
+      if (i <= ic && i > 0) {
+        tempY = prevOffsetVal[order];
+        order++;
+      } else {
+        tempY = verticalBars ? rect.height : 0;
+      }
+      ic = i;
+      return tempY;
+    };
+
+    // Position of a bar in the stack. yPos is used to keep track of the
+    // offset based on previous calls to getBarY
+    var yPos = 0;
+    var getBarPos = (d, i) {
+      if (verticalBars) {
+        if (i == 0) {
+          yPos = measureScale.scale(0).round();
+        }
+        return yPos -= (rect.height - measureScale.scale(d).round());
+      } else {
+        if (i == 0) {
+          // 1 to not overlap the axis line.
+          yPos = 1;
+        }
+        var pos = yPos;
+        yPos += measureScale.scale(d).round();
+        // Check if after adding the height of the bar, if y has changed, if
+        // changed, we offset for space between the bars.
+        if (yPos != pos) {
+          yPos += (theme.defaultSeparatorWidth + theme.defaultStrokeWidth);
+        }
+        return pos;
+      }
+    };
+
+    var barsCount = rows.first.length;
+    var buildPath = (d, int i, bool animate) {
+      var position = animate ? getInitialBarPos(i) : getBarPos(d, i),
+          length = animate ? 0 : getBarLength(d, i),
+          radius = i == barsCount - 1 ? RADIUS : 0,
+          path = verticalBars
+              ? topRoundedRect(0, position, barWidth, length, radius)
+              : rightRoundedRect(position, 0, length, barWidth, radius);
+      return path;
+    };
+
+    var enter = bar.enter.appendWithCallback((d, i, e) {
+          var rect = Namespace.createChildElement('path', e),
+              measure = series.measures.elementAt(_reverseIdx(i)),
+              row = int.parse(e.dataset['row']),
+              color = colorForValue(measure, row),
+              style = stylesForValue(measure, row);
+
+          rect.classes.add('stack-rdr-bar ${style.join(" ")}');
+          rect.attributes
+            ..['d'] = buildPath(d, i, animateBarGroups)
+            ..['stroke-width'] = '${theme.defaultStrokeWidth}px'
+            ..['fill'] = color
+            ..['stroke'] = color;
+
+          if (!animateBarGroups) {
+            rect.attributes['data-column'] = '$measure';
+          }
+          return rect;
+        });
+
+    enter
+      ..on('click', (d, i, e) => _event(mouseClickController, d, i, e))
+      ..on('mouseover', (d, i, e) => _event(mouseOverController, d, i, e))
+      ..on('mouseout', (d, i, e) => _event(mouseOutController, d, i, e));
+
+    if (animateBarGroups) {
+      bar.each((d, i, e) {
+        var measure = series.measures.elementAt(_reverseIdx(i)),
+            row = int.parse(e.parent.dataset['row']),
+            color = colorForValue(measure, row),
+            styles = stylesForValue(measure, row);
+        e.attributes
+          ..['data-column'] = '$measure'
+          ..['fill'] = color
+          ..['stroke'] = color;
+        e.classes
+          ..removeAll(ChartState.VALUE_CLASS_NAMES)
+          ..addAll(styles);
+      });
+
+      bar.transition()
+        ..attrWithCallback('d', (d, i, e) => buildPath(d, i, false));
+    }
+
+    bar.exit.remove();
+  }
+
+  @override
+  void dispose() {
+    if (root == null) return;
+    root.selectAll('.stack-rdr-rowgroup').remove();
+  }
+
+  @override
+  double get bandInnerPadding =>
+      area.theme.dimensionAxisTheme.axisBandInnerPadding;
+
+  @override
+  Extent get extent {
+    assert(area != null && series != null);
+    var rows = area.data.rows,
+    max = rows.isEmpty ? 0 : rows[0][series.measures.first],
+    min = max;
+
+    rows.forEach((row) {
+      if (row[series.measures.first] < min)
+        min = row[series.measures.first];
+
+      var bar = 0;
+      series.measures.forEach((idx) {
+        bar += row[idx];
+      });
+      if (bar > max) max = bar;
+    });
+
+    return new Extent(min, max);
+  }
+
+  @override
+  void handleStateChanges(List<ChangeRecord> changes) {
+    var groups = host.querySelectorAll('.stack-rdr-rowgroup');
+    if (groups == null || groups.isEmpty) return;
+
+    for(int i = 0, len = groups.length; i < len; ++i) {
+      var group = groups.elementAt(i),
+          bars = group.querySelectorAll('.stack-rdr-bar'),
+          row = int.parse(group.dataset['row']);
+
+      for(int j = 0, barsCount = bars.length; j < barsCount; ++j) {
+        var bar = bars.elementAt(j),
+            column = int.parse(bar.dataset['column']),
+            color = colorForValue(column, row);
+
+        bar.classes.removeAll(ChartState.VALUE_CLASS_NAMES);
+        bar.classes.addAll(stylesForValue(column, row));
+        bar.attributes
+          ..['fill'] = color
+          ..['stroke'] = color;
+      }
+    }
+  }
+
+  void _event(StreamController controller, data, int index, Element e) {
+    if (controller == null) return;
+    var rowStr = e.parent.dataset['row'];
+    var row = rowStr != null ? int.parse(rowStr) : null;
+    controller.add(new _ChartEvent(
+        scope.event, area, series, row, _reverseIdx(index), data));
+  }
+
+  // Stacked bar chart renders items from bottom to top (first measure is at
+  // the bottom of the stack). We use [_reversedIdx] instead of index to
+  // match the color and order of what is displayed in the legend.
+  int _reverseIdx(int index) => series.measures.length - 1 - index;
+}
diff --git a/charted/lib/charts/chart_area.dart b/charted/lib/charts/chart_area.dart
new file mode 100644
index 0000000..db5d303
--- /dev/null
+++ b/charted/lib/charts/chart_area.dart
@@ -0,0 +1,165 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+///
+/// Area for rendering cartesian charts. A cartesian chart creates
+/// visualization by carefully placing elements along the dimension
+/// and measure axes (in a 2 dimensional plane).
+///
+/// Some renderers may use additional dimensions that is made visible
+/// by size and color of the rendered elements.
+///
+/// For example:
+/// - A bar-chart draws bars indicating a value along measure axis.
+/// - A bubble-chart where a circle is positioned across two dimension
+///   axes. A bubble-chart may also use color and size of circles to
+///   indicate more dimensions.
+///
+/// In a [CartesianArea], more than one series can be rendered together.
+///
+abstract class CartesianArea implements ChartArea {
+  /// When set to true, [ChartArea] uses both 'x' and 'y' axes for dimensions.
+  /// Examples:
+  ///   - A bar-chart has one dimension axis (typically the 'x' axis)
+  ///   - A bubble-chart has two dimension axis (both 'x' and 'y')
+  bool get useTwoDimensionAxes;
+
+  /// Scales used to render the measure axis of the given [ChartSeries]. Each
+  /// series may use more than one measure scale.
+  ///
+  /// For example, a scatter plot may use different scales for color, shape
+  /// and size of the rendering.
+  Iterable<Scale> measureScales(ChartSeries s);
+
+  /// Scales used to render the dimension axes. The number of scales returned
+  /// is either one or two based on [useTwoDimensions]
+  Iterable<Scale> get dimensionScales;
+
+  /// List of dimensions using a band of space on the axis
+  Iterable<int> get dimensionsUsingBands;
+
+  /// Stream to notify when chart axes get updated.
+  Stream<ChartArea> get onChartAxesUpdated;
+
+  /// Factory method to create an instance of the default implementation
+  /// - [host] must be an Element that has clientHeight and clientWidth
+  ///   properties defined (i.e cannot be inline elements)
+  /// - [data] is an instance of [ChartData]
+  /// - [config] is an implementation of [ChartConfig]
+  /// - If [autoUpdate] is set, chart is updated when data or config
+  ///   change.  When not set, [draw] must be called to update the chart.
+  /// - When [useTwoDimensionAxes] is set, the chart uses both 'x' and 'y'
+  ///   axes as dimensions.
+  factory CartesianArea(
+      dynamic host,
+      ChartData data,
+      ChartConfig config, {
+      bool autoUpdate: false,
+      bool useTwoDimensionAxes: false,
+      bool useRowColoring: false,
+      ChartState state }) =>
+          new _CartesianArea(host, data, config, autoUpdate,
+              useTwoDimensionAxes, useRowColoring, state);
+}
+
+///
+/// Area for rendering layout charts. A layout chart creates visualization by
+/// distributing available space to each measure.
+///
+/// For example:
+/// - A pie-chart distributes a radial area to each measure.
+/// - In a tree-map a rectangular area is distributed to each measure.
+///
+/// In a [LayoutArea], only one series can be rendered and the area does
+/// not have any scales and axes.
+///
+abstract class LayoutArea implements ChartArea {
+  /// Layouts always use coloring by row index and value.
+  @override
+  bool get useRowColoring => true;
+
+  factory LayoutArea(
+      dynamic host,
+      ChartData data,
+      ChartConfig config,
+      bool autoUpdate) =>
+          new _LayoutArea(host, data, config, autoUpdate);
+}
+
+///
+/// Base interface for all implementations of a chart drawing area.
+///
+abstract class ChartArea implements ChartAreaBehaviorSource {
+  /// Data used by the chart. Chart isn't updated till the next call to
+  /// draw function if [autoUpdate] is set to false.
+  ///
+  /// Setting new value to [data] will update chart if [autoUpdate] is set.
+  ChartData data;
+
+  /// Configuration for this chart.  [ChartArea] subscribes to changes on
+  /// [config] and calls draw upon any changes.
+  ///
+  /// Refer to [ChartConfig] for further documentation about which changes
+  /// are added to the stream, which in turn trigger an update on the chart.
+  ChartConfig config;
+
+  /// Theme for this chart. Any changes to [theme] are not applied to the chart
+  /// until it is redrawn. Changes can be forced by calling [draw] function.
+  ChartTheme theme;
+
+  /// When set to true, [ChartArea] subscribes to changes on data and updates
+  /// the chart when [data] or [config] changes. Defaults to false.
+  bool autoUpdate;
+
+  /// When true, [ChartArea] and renderers that support coloring by row,
+  /// use row indices and values to color the chart. Defaults to false.
+  bool get useRowColoring;
+
+  /// Geometry of components in this [ChartArea]
+  ChartAreaLayout get layout;
+
+  /// Host element of the ChartArea
+  Element get host;
+
+  /// True when all components of the chart have been updated - either already
+  /// drawn or are in the process of transitioning in.
+  bool get isReady;
+
+  /// State of the chart - selection and highlights.
+  ChartState get state;
+
+  /// Draw the chart with current data and configuration.
+  /// - If [preRender] is set, [ChartArea] attempts to build all non data
+  ///   dependant elements of the chart.
+  /// - When [schedulePostRender] is not null, non-essential elements/tasks
+  ///   of chart building are post-poned until the future is resolved.
+  void draw({bool preRender: false, Future schedulePostRender});
+
+  /// Force destroy the ChartArea.
+  /// - Clear references to all passed objects and subscriptions.
+  /// - Call dispose on all renderers and behaviors.
+  void dispose();
+}
+
+///
+/// Class representing geometry of the [ChartArea] and various components
+/// that are created by the ChartArea.
+///
+abstract class ChartAreaLayout {
+  /// Sizes of axes by orientation.
+  /// Only valid on [CartesianArea], null otherwise.
+  Map<String, Rect> get axes;
+
+  /// Size of render area.
+  Rect get renderArea => new Rect();
+
+  /// Size of chart area.
+  Rect get chartArea => new Rect();
+}
diff --git a/charted/lib/charts/chart_config.dart b/charted/lib/charts/chart_config.dart
new file mode 100644
index 0000000..6fb6e11
--- /dev/null
+++ b/charted/lib/charts/chart_config.dart
@@ -0,0 +1,94 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+///
+/// Configuration of the chart.
+///
+abstract class ChartConfig {
+  /// List of series rendered on this chart.
+  ///
+  /// If the implementation is observable, setting a new list must broadcast
+  /// a change.  Additionally, if [series] is set to an [ObservableList],
+  /// changes to the list are broadcast too.
+  Iterable<ChartSeries> series;
+
+  /// List of columns that form the dimensions on the chart.
+  ///
+  /// If the implementation is observable, setting a new list must broadcast
+  /// a change. Additionally, if [dimensions] is set to an [ObservableList],
+  /// changes to the list are broadcast too.
+  Iterable<int> dimensions;
+
+  /// Instance of [ChartLegend] that is used to render legend.
+  ChartLegend legend;
+
+  /// Recommended minimum size for the chart
+  Rect minimumSize;
+
+  /// Indicates if the chart has primary dimension on the left axis
+  bool isLeftAxisPrimary = false;
+
+  /// Registers axis configuration for the axis represented by [id].
+  void registerMeasureAxis(String id, ChartAxisConfig axis);
+
+  /// User-set axis configuration for [id], null if not set.
+  ChartAxisConfig getMeasureAxis(String id);
+
+  /// Register axis configuration of the axis used for dimension [column].
+  void registerDimensionAxis(int column, ChartAxisConfig axis);
+
+  /// User set axis configuration for [column], null if not set.
+  ChartAxisConfig getDimensionAxis(int column);
+
+  /// Measure axes ids that are displayed. If not specified, the first two
+  /// measure axes are displayed. If the list is empty, none of the measure
+  /// axes are displayed.
+  Iterable<String> displayedMeasureAxes;
+
+  /// Indicates if the dimension axes should be drawn on this chart. Unless set
+  /// to "false", the axes are rendered.
+  bool renderDimensionAxes;
+
+  /// When set to true, the chart rendering changes to be more suitable for
+  /// scripts that are written from right-to-left.
+  bool isRTL;
+
+  /// Indicate if the horizontal axes and the corresponding scales should
+  /// switch direction too.
+  /// Example: Time scale on the X axis would progress from right to left.
+  bool switchAxesForRTL;
+
+  /// Factory method to create an instance of the default implementation
+  factory ChartConfig(
+      Iterable<ChartSeries> series, Iterable<int> dimensions) = _ChartConfig;
+}
+
+///
+/// [ChangeRecord] that is used to notify changes to [ChartConfig].
+/// Currently, changes to list of dimensions and list of series are monitored.
+///
+class ChartConfigChangeRecord implements ChangeRecord {
+  const ChartConfigChangeRecord();
+}
+
+///
+/// Configuration for an axis
+///
+class ChartAxisConfig {
+  /// Title for the axis
+  String title;
+
+  /// Scale to be used with the axis
+  Scale scale;
+
+  /// For a quantitative scale, values at which ticks should be displayed.
+  /// When not specified, the ticks are based on the type of [scale] used.
+  Iterable tickValues;
+}
diff --git a/charted/lib/charts/chart_data.dart b/charted/lib/charts/chart_data.dart
new file mode 100644
index 0000000..9c1b4f2
--- /dev/null
+++ b/charted/lib/charts/chart_data.dart
@@ -0,0 +1,121 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+///
+/// Interface to be implemented by data providers to give tabular access to
+/// data for chart renderers.
+///
+abstract class ChartData {
+  /// Read-only access to column specs
+  Iterable<ChartColumnSpec> get columns;
+
+  /// Read-only access to rows
+  Iterable<Iterable> get rows;
+
+  /// Create a new instance of [ChartData]'s internal implementation
+  factory ChartData(
+      Iterable<ChartColumnSpec> columns, Iterable<Iterable> rows) = _ChartData;
+}
+
+///
+/// Interface implemented by [ChartData] transformers.
+/// Examples:
+///   [AggregationTransformer] to compute aggregated rows/columns
+///   [FilterTransformer] to filter data
+///   [TransposeTransformer] to convert rows to columns and vice-versa
+///
+abstract class ChartDataTransform {
+  /// Create a new instance of [ChartData] by selecting a subset
+  /// of rows and columns from the current one
+  ChartData transform(ChartData source);
+}
+
+
+///
+/// Implementation of [ChangeRecord], that is used to notify when rows get added
+/// or removed to ChartData
+///
+class ChartRowChangeRecord implements ChangeRecord {
+  /// Changes to the rows - contains all updates to rows since last notification.
+  final List<ListChangeRecord> changes;
+
+  const ChartRowChangeRecord(this.changes);
+}
+
+///
+/// Implementation of [ChangeRecord], that is used to notify changes to
+/// values in [ChartData].
+///
+class ChartValueChangeRecord implements ChangeRecord {
+  /// Row that changed.
+  final int row;
+
+  /// List of changes to data on the row - includes all updates since the
+  /// last change notification.
+  final List<ListChangeRecord> changes;
+
+  const ChartValueChangeRecord(this.row, this.changes);
+}
+
+///
+/// Meta information for each column in ChartData
+///
+class ChartColumnSpec {
+  static const String TYPE_BOOLEAN = 'boolean';
+  static const String TYPE_DATE = 'date';
+  static const String TYPE_NUMBER = 'number';
+  static const String TYPE_STRING = 'string';
+  static const String TYPE_TIMESTAMP = 'timestamp';
+
+  static const List ORDINAL_SCALES = const [ TYPE_STRING ];
+  static const List LINEAR_SCALES = const [ TYPE_NUMBER ];
+  static const List TIME_SCALES = const [ TYPE_DATE, TYPE_TIMESTAMP ];
+
+  /// Formatter for values that belong to this column
+  final FormatFunction formatter;
+
+  /// Label for the column.  Used in legend, tooltips etc;
+  /// When not specified, defaults to empty string.
+  final String label;
+
+  /// Type of data in this column. Used for interpolations, computing
+  /// scales and ranges. When not specified, it is assumed to be "number"
+  /// for measures and "string" for dimensions.
+  final String type;
+
+  /// Indicates if this column requires [OrdinalScale].
+  ///
+  /// If not specified, an ordinal scale is used for string columns and
+  /// quantitative scales are used for others.
+  final bool useOrdinalScale;
+
+  /// Initialize axis scale according to [ChartColumnSpec] type.
+  /// This logic is extracted from [ChartArea] implementation for conveniently
+  /// adding more scale types.
+  Scale createDefaultScale() {
+    if (useOrdinalScale == true) {
+      return new OrdinalScale();
+    }
+    else if (LINEAR_SCALES.contains(type)) {
+      return new LinearScale();
+    }
+    else if (TIME_SCALES.contains(type)) {
+      return new TimeScale();
+    }
+    return null;
+  }
+
+  ChartColumnSpec({this.label, String type : TYPE_NUMBER,
+      this.formatter, bool useOrdinalScale})
+      : useOrdinalScale =
+            useOrdinalScale == true ||
+            useOrdinalScale == null && ORDINAL_SCALES.contains(type),
+        type = type;
+}
diff --git a/charted/lib/charts/chart_events.dart b/charted/lib/charts/chart_events.dart
new file mode 100644
index 0000000..9eb3a36
--- /dev/null
+++ b/charted/lib/charts/chart_events.dart
@@ -0,0 +1,101 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+///
+/// Interface implemented by a [CartesianRenderer] that supports behaviors.
+///
+abstract class ChartRendererBehaviorSource {
+  /// Stream to notify when a rendered value is clicked.
+  Stream<ChartEvent> get onValueClick;
+
+  /// Stream to notify when user moves mouse over a rendered value
+  Stream<ChartEvent> get onValueMouseOver;
+
+  /// Stream to notify when user moves mouse out of rendered value
+  Stream<ChartEvent> get onValueMouseOut;
+}
+
+///
+/// Interface implemented by a [ChartArea] that supports behaviors.
+///
+abstract class ChartAreaBehaviorSource implements ChartRendererBehaviorSource {
+  /// Stream to notify when a mouse button is pressed on [ChartArea].
+  Stream<ChartEvent> get onMouseDown;
+
+  /// Stream to notify when a pressed mouse button is released on [ChartArea].
+  Stream<ChartEvent> get onMouseUp;
+
+  /// Stream to notify when mouse pointer enters [ChartArea].
+  Stream<ChartEvent> get onMouseOver;
+
+  /// Stream to notify when mouse pointer leaves [ChartArea].
+  Stream<ChartEvent> get onMouseOut;
+
+  /// Stream of events that notify when mouse is moved on [ChartArea].
+  Stream<ChartEvent> get onMouseMove;
+
+  /// A pane that is rendered below all the chart elements - for use with
+  /// behaviors that add elements to chart.
+  Selection get lowerBehaviorPane;
+
+  /// A pane that is rendered above all the chart elements - for use with
+  /// behaviors that add elements to chart.
+  Selection get upperBehaviorPane;
+
+  /// Add a behavior to the chart.
+  void addChartBehavior(ChartBehavior behavior);
+
+  /// Remove a behavior from the chart.
+  void removeChartBehavior(ChartBehavior behavior);
+}
+
+///
+/// Class representing an event emitted by ChartEventSource
+///
+abstract class ChartEvent {
+  /// DOM source event that caused this event
+  Event get source;
+
+  /// ChartSeries if any on which this event occurred
+  ChartSeries get series;
+
+  /// Column in ChartData on which this event occurred
+  int get column;
+
+  /// Row in ChartData on which this event occurred
+  int get row;
+
+  /// Value from ChartData on which the event occurred
+  num get value;
+
+  /// X position relative to the rendered chart
+  num get chartX;
+
+  /// Y position relative to the rendered chart
+  num get chartY;
+}
+
+/// Interface implemented by chart behaviors.
+/// During initialization, the behaviors subscribe to any necessary events and
+/// handle them appropriately.
+abstract class ChartBehavior {
+  /// Called while ChartArea is being initialized.
+  ///  - [area] is the ChartArea on which this behavior is installed
+  ///  - [upperRenderPane] is the Selection that is rendered on top of the
+  ///    chart.  Behaviors can use it to draw any visualization in response
+  ///    to user actions.
+  ///  - [lowerRenderPane] is the Selection that is rendered below the chart.
+  void init(
+      ChartArea area, Selection upperRenderPane, Selection lowerRenderPane);
+
+  /// Clears all DOM created by this behavior, unsubscribes to event listeners
+  /// and clears any state.
+  void dispose();
+}
diff --git a/charted/lib/charts/chart_legend.dart b/charted/lib/charts/chart_legend.dart
new file mode 100644
index 0000000..6d2d3c7
--- /dev/null
+++ b/charted/lib/charts/chart_legend.dart
@@ -0,0 +1,57 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+///
+/// Interface that is implemented by classes that support
+/// displaying legend.
+///
+abstract class ChartLegend {
+  /// Title of the legend, dynamically updates the legend title when set.
+  String title;
+
+  /// Called by [ChartArea] to notify changes to legend.
+  update(Iterable<ChartLegendItem> legend, ChartArea chart);
+
+  /// Called by [ChartArea] to dispose selection listeners.
+  dispose();
+
+  /// Factory to create an instance of the default implementation.
+  factory ChartLegend(Element host,
+      {maxItems: 0, title: '', showValues: false}) =>
+          new _ChartLegend(host, maxItems, showValues, title);
+}
+
+///
+/// Class representing an item in the legend.
+///
+class ChartLegendItem {
+  /// Index of the row/column in [ChartData]. Legend uses column based coloring
+  /// in [CartesianArea] that has useRowColoring set to false and row based
+  /// coloring in all other cases.
+  int index;
+
+  /// HTML color used for the row/column in chart
+  String color;
+
+  /// The label of the item.
+  String label;
+
+  /// Description of the item.
+  String description;
+
+  /// Pre-formatted value to use as value.
+  String value;
+
+  /// List of series that this column is part of
+  Iterable<ChartSeries> series;
+
+  ChartLegendItem({this.index, this.color,
+      this.label, this.description, this.series, this.value});
+}
diff --git a/charted/lib/charts/chart_renderer.dart b/charted/lib/charts/chart_renderer.dart
new file mode 100644
index 0000000..a5b0efb
--- /dev/null
+++ b/charted/lib/charts/chart_renderer.dart
@@ -0,0 +1,69 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+///
+/// Renders the chart on a CartesianArea.
+///
+abstract class CartesianRenderer extends ChartRenderer {
+  /// Returns extent of the series. This extent is used by [ChartArea] to
+  /// set the output range of the corresponding scale/axis of the series.
+  ///
+  /// Extent has valid values only if [prepare] was already called.
+  Extent get extent;
+
+  /// Indicates if this renderer uses range "band" on any of the dimension
+  /// axis. Band is space taken on the dimension axis (if more than a point).
+  ///
+  /// Examples:
+  ///   A bar chart takes up space (width of the bar) on the dimension axis.
+  ///   A line chart does not take any space
+  Iterable<int> get dimensionsUsingBand;
+
+  /// Hint for padding between two bands that [ChartArea] will use for layout.
+  /// This getter is called only for renderers that have [dimensionsUsingBand]
+  /// set to non-empty list.
+  double get bandInnerPadding;
+
+  /// Hint for padding before first and after the last bands
+  /// This getter is called only for renderers that have [dimensionsUsingBand]
+  /// set to non-empty list.
+  double get bandOuterPadding;
+
+  /// Render series data on the passed [host].
+  /// Draw will not be successful if [prepare] was not already called.
+  void draw(Element host, {Future schedulePostRender});
+}
+
+///
+/// Renders layout visualization on a LayoutArea
+///
+abstract class LayoutRenderer extends ChartRenderer {
+  /// Create a layout/visualization from the data. Layout will not be successful
+  /// if [prepare] was not already called.
+  Iterable<ChartLegendItem> layout(Element host, {Future schedulePostRender});
+}
+
+///
+/// Common interface for all renderers in Charted
+///
+abstract class ChartRenderer extends ChartRendererBehaviorSource {
+  /// Name of the renderer.
+  /// The name can only include chars that are allowed in the CSS for selectors.
+  String get name;
+
+  /// Prepare the chart for rendering.
+  /// - [area] represents the [ChartArea] on which the chart is rendered.
+  /// - [series] represents the [ChartSeries] that is rendered
+  bool prepare(ChartArea area, ChartSeries series);
+
+  /// Clears DOM created by this renderer and releases
+  /// references to passed objects.
+  void dispose();
+}
diff --git a/charted/lib/charts/chart_series.dart b/charted/lib/charts/chart_series.dart
new file mode 100644
index 0000000..435bd69
--- /dev/null
+++ b/charted/lib/charts/chart_series.dart
@@ -0,0 +1,73 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+///
+/// A [ChartSeries] represents one or more columns in ChartData that are
+/// rendered together.
+///
+/// Examples:
+/// 1. For bar-chart or line-chart, a series consists of one column
+/// 2. For stacked chart or grouped bar chart, a series has more than columns
+///
+class ChartSeries {
+  /// Name of the series
+  final String name;
+
+  /// Optional Ids of measure axes.
+  ///
+  /// When specified renderers scale the column values against the ranges
+  /// of the given axes. If an axis with a matching Id does not exist in
+  /// [ChartArea] a new axis is created.
+  ///
+  /// When not specified, renderers may use [ChartArea.defaultMeasureAxis]
+  /// where ever necessary.  Refer to the implementation of [CartesianRenderer] for
+  /// more information on defaults and how the measure axes are used.
+  ///
+  /// If the implementation is [Observable] and [measureAxisIds] is set to an
+  /// [ObservableList], changes to the list must be broadcasted.
+  Iterable<String> measureAxisIds;
+
+  /// List of columns in ChartData that are measures of this series.
+  ///
+  /// A series may include more than one measure if the renderer supports it.
+  /// When there are more measures than what the renderer can handle, a renderer
+  /// only renders the first "supported number" of columns. If the number of
+  /// columns is less than the minimum that the renderer supports, the remaining
+  /// measures are assumed to have zeros.
+  ///
+  /// If the implementation is [Observable] and [measures] is set to an
+  /// [ObservableList], changes to the list must be broadcasted.
+  Iterable<int> measures;
+
+  /// Instance of the renderer used to render the series.
+  ///
+  /// [ChartArea] creates a renderer using [ChartRender.create] and uses it
+  /// to compute range of the measure axis and to render the chart.
+  ChartRenderer renderer;
+
+  /// Factory function to create an instance of internal implementation of
+  /// [ChartSeries].
+  factory ChartSeries(String name, Iterable<int> measures,
+      ChartRenderer renderer, { Iterable<String> measureAxisIds : null }) =>
+          new _ChartSeries(name, measures, renderer, measureAxisIds);
+}
+
+
+///
+/// Implementation of [ChangeRecord] that is used to notify changes to
+/// [ChartSeries].  Currently, only changes to measures and measureAxisIds
+/// are supported.
+///
+class ChartSeriesChangeRecord implements ChangeRecord {
+  /// Reference to series that changed
+  final ChartSeries series;
+
+  const ChartSeriesChangeRecord(this.series);
+}
\ No newline at end of file
diff --git a/charted/lib/charts/chart_state.dart b/charted/lib/charts/chart_state.dart
new file mode 100644
index 0000000..39b4f58
--- /dev/null
+++ b/charted/lib/charts/chart_state.dart
@@ -0,0 +1,150 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+///
+/// Model to provide highlight, selection and visibility in a ChartArea.
+/// Selection and visibility
+///
+abstract class ChartState implements ChangeNotifier {
+  static int COL_SELECTED       = 0x001;
+  static int COL_UNSELECTED     = 0x002;
+  static int COL_PREVIEW        = 0x004;
+  static int COL_HIDDEN         = 0x008;
+  static int COL_HIGHLIGHTED    = 0x010;
+  static int COL_UNHIGHLIGHTED  = 0x020;
+  static int COL_HOVERED        = 0x040;
+  static int VAL_HIGHLIGHTED    = 0x080;
+  static int VAL_UNHIGHLIGHTED  = 0x100;
+  static int VAL_HOVERED        = 0x200;
+
+  static const COL_SELECTED_CLASS       = 'col-selected';
+  static const COL_UNSELECTED_CLASS     = 'col-unselected';
+  static const COL_PREVIEW_CLASS        = 'col-previewed';
+  static const COL_HIDDEN_CLASS         = 'col-hidden';
+  static const COL_HIGHLIGHTED_CLASS    = 'col-highlighted';
+  static const COL_UNHIGHLIGHTED_CLASS  = 'col-unhighlighted';
+  static const COL_HOVERED_CLASS        = 'col-hovered';
+  static const VAL_HIGHLIGHTED_CLASS    = 'row-highlighted';
+  static const VAL_UNHIGHLIGHTED_CLASS  = 'row-unhighlighted';
+  static const VAL_HOVERED_CLASS        = 'row-hovered';
+
+  static const COLUMN_CLASS_NAMES = const[
+    COL_SELECTED_CLASS, COL_UNSELECTED_CLASS, COL_PREVIEW_CLASS,
+    COL_HIGHLIGHTED_CLASS, COL_UNHIGHLIGHTED_CLASS, COL_HIDDEN_CLASS,
+    COL_HOVERED_CLASS];
+
+  static const VALUE_CLASS_NAMES = const[
+    COL_SELECTED_CLASS, COL_UNSELECTED_CLASS, COL_PREVIEW_CLASS,
+    COL_HIGHLIGHTED_CLASS, COL_UNHIGHLIGHTED_CLASS, COL_HIDDEN_CLASS,
+    COL_HOVERED_CLASS, VAL_HIGHLIGHTED_CLASS, VAL_UNHIGHLIGHTED_CLASS,
+    VAL_HOVERED_CLASS];
+
+  /// List of selected items.
+  /// - Contains a column on CartesianArea if useRowColoring is false.
+  /// - Row index in all other cases.
+  Iterable<int> get selection;
+
+  /// List of visible items.
+  /// - Contains a column on CartesianArea if useRowColoring is false.
+  /// - Row index in all other cases.
+  Iterable<int> get hidden;
+
+  /// Currently previewed row or column. Hidden items can be previewed
+  /// by hovering on the corresponding label in Legend
+  /// - Contains a column on CartesianArea if useRowColoring is false.
+  /// - Row index in all other cases.
+  int preview;
+
+  /// Currently highlighted value, if any, represented as column and row.
+  /// Highlight is result of a click on certain value.
+  Iterable<Pair<int,int>> highlights;
+
+  /// Currently hovered value, if any, represented as column and row.
+  /// Hover is result of mouse moving over a certian value in chart.
+  Pair<int,int> hovered;
+
+  /// Ensure that a row or column is visible.
+  bool unhide(int id);
+
+  /// Ensure that a row or column is invisible.
+  bool hide(int id);
+
+  /// Returns current visibility of a row or column.
+  bool isVisible(int id);
+
+  /// Select a row or column.
+  bool select(int id);
+
+  /// Unselect a row or column.
+  bool unselect(int id);
+
+  /// Returns current selection state of a row or column.
+  bool isSelected(int id);
+
+  /// Select a row or column.
+  bool highlight(int column, int row);
+
+  /// Unselect a row or column.
+  bool unhighlight(int column, int row);
+
+  /// Returns current selection state of a row or column.
+  bool isHighlighted(int column, int row);
+
+  factory ChartState({bool isMultiSelect: false}) =>
+      new _ChartState(isMultiSelect: isMultiSelect);
+}
+
+///
+/// Implementation of [ChangeRecord], that is used to notify changes to
+/// values in [ChartData].
+///
+class ChartSelectionChangeRecord implements ChangeRecord {
+  final int add;
+  final int remove;
+  const ChartSelectionChangeRecord({this.add, this.remove});
+}
+
+///
+/// Implementation of [ChangeRecord], that is used to notify changes to
+/// values in [ChartData].
+///
+class ChartVisibilityChangeRecord implements ChangeRecord {
+  final int unhide;
+  final int hide;
+  const ChartVisibilityChangeRecord({this.unhide, this.hide});
+}
+
+///
+/// Implementation of [ChangeRecord], that is used to notify changes to
+/// values in [ChartData].
+///
+class ChartHighlightChangeRecord implements ChangeRecord {
+  final Pair<int,int> remove;
+  final Pair<int,int> add;
+  const ChartHighlightChangeRecord({this.add, this.remove});
+}
+
+///
+/// Implementation of [ChangeRecord], that is used to notify changes to
+/// values in [ChartData].
+///
+class ChartHoverChangeRecord implements ChangeRecord {
+  final Pair<int,int> hovered;
+  const ChartHoverChangeRecord(this.hovered);
+}
+
+///
+/// Implementation of [ChangeRecord], that is used to notify changes to
+/// values in [ChartData].
+///
+class ChartPreviewChangeRecord implements ChangeRecord {
+  final int previewed;
+  const ChartPreviewChangeRecord(this.previewed);
+}
diff --git a/charted/lib/charts/chart_theme.dart b/charted/lib/charts/chart_theme.dart
new file mode 100644
index 0000000..785a950
--- /dev/null
+++ b/charted/lib/charts/chart_theme.dart
@@ -0,0 +1,137 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+/// Theme used to render the chart area, specifically colors and axes.
+///
+/// Typical implementations of ChartTheme also implement theme interfaces
+/// used by the renderers, tooltips, legends and any other behaviors.
+abstract class ChartTheme {
+  static ChartTheme current = new QuantumChartTheme();
+
+  /// Column/series when it is disabled, possibly because another is active
+  static const int STATE_INACTIVE = 0;
+
+  /// Column/Series that is normal
+  static const int STATE_NORMAL = 1;
+
+  /// Column/series that is active, possibly by a click
+  static const int STATE_ACTIVE = 2;
+
+  /// Color that can be used for key.
+  /// For a given input key, the output is always the same.
+  String getColorForKey(key, [int state]);
+
+  /// Color for overflow and other items.
+  /// For example, the collect all bucket used by pie-chart.
+  String getOtherColor([int state]);
+
+  /// Width of the separator between two chart elements.
+  /// Used to separate pies in pie-chart, bars in grouped and stacked charts.
+  int get defaultSeparatorWidth => 1;
+
+  /// Stroke width used by all shapes.
+  int get defaultStrokeWidth => 2;
+
+  /// Default font for computation of text metrics
+  String get defaultFont;
+
+  /// Easing function for the transition
+  EasingFunction  get transitionEasingType => Transition.defaultEasingType;
+
+  /// Easing mode for the transition
+  EasingModeFunction get transitionEasingMode => Transition.defaultEasingMode;
+
+  /// Total duration of the transition in milli-seconds
+  int get transitionDurationMilliseconds => 250;
+
+  /// Theme passed to the measure axes - only used by cartesian charts
+  ChartAxisTheme get measureAxisTheme => null;
+
+  /// Theme passed to the dimension axes - only used by catesian charts
+  ChartAxisTheme get dimensionAxisTheme => null;
+
+  /// Padding around the rendered chart. Defaults to 10px in all directions
+  AbsoluteRect get padding => const AbsoluteRect(10, 10, 10, 10);
+
+  /// Special filters that can be applied to the shapes.
+  /// In SVG, these may add blurring, drop-shadow and other effects to charts.
+  String get filters => '';
+
+  /// Returns a filter (id/url) for key
+  String getFilterForKey(key, [int state]);
+}
+
+abstract class ChartAxisTheme {
+  /// Treshold for tick length.  Setting [axisTickSize] <= [FILL_RENDER_AREA]
+  /// will make the axis span the entire height/width of the rendering area.
+  static const int FILL_RENDER_AREA = SMALL_INT_MIN;
+
+  /// Number of ticks displayed on the axis - only used when an axis is
+  /// using a quantitative scale.
+  int get axisTickCount;
+
+  /// Size of ticks on the axis. When [measureTickSize] <= [FILL_RENDER_AREA],
+  /// the painted tick will span complete height/width of the rendering area.
+  int get axisTickSize;
+
+  /// Space between axis and label for dimension axes
+  int get axisTickPadding;
+
+  /// Space between the first tick and the measure axes.
+  /// Only used on charts that don't have renderers that use "bands" of space
+  /// on the dimension axes
+  ///
+  /// Represented as a percentage of space between two consecutive ticks. The
+  /// space between two consecutive ticks is also known as the segment size.
+  double get axisOuterPadding;
+
+  /// Space between the two bands in the chart.
+  /// Only used on charts that have renderers that use "bands" of space on the
+  /// dimension axes.
+  ///
+  /// Represented as a percentage of space between two consecutive ticks. The
+  /// space between two consecutive ticks is also known as the segment size.
+  double get axisBandInnerPadding;
+
+  /// Space between the first band and the measure axis.
+  /// Only used on charts that have renderers that use "bands" of space on the
+  /// dimension axes.
+  ///
+  /// Represented as a percentage of space between two consecutive ticks. The
+  /// space between two consecutive ticks is also known as the segment size.
+  double get axisBandOuterPadding;
+
+  /// When set to true, the vertical axes resize to fit the labels.
+  bool get verticalAxisAutoResize => true;
+
+  /// Width of vertical axis when it is not resizing automatically. If
+  /// [autoResizeAxis] is set to true, [verticalAxisWidth] will be used as the
+  /// maximum width of the vertical axis.
+  ///
+  /// Height of vertical axis is automatically computed based on height of the
+  /// visualization.
+  int get verticalAxisWidth => 200;
+
+  /// Height of horizontal axis. Width of horizontal axis is automatically
+  /// computed based on width of the visualization.
+  int get horizontalAxisHeight => 200;
+
+  /// Font used by axis ticks. When specified, axis uses efficient off-screen
+  /// computation of text metrics.
+  ///
+  /// Font string must be of the following form:
+  ///   "bold italic 16px Roboto"
+  ///   "bold 16px Roboto"
+  ///   "italic 16px Roboto"
+  ///   "16px Roboto"
+  ///
+  /// When not specified, SVGTextElement's metrics API will be used.
+  String get ticksFont => null;
+}
diff --git a/charted/lib/charts/charts.dart b/charted/lib/charts/charts.dart
new file mode 100644
index 0000000..f20ad75
--- /dev/null
+++ b/charted/lib/charts/charts.dart
@@ -0,0 +1,75 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+library charted.charts;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:html' show Element, document, window, Event, MouseEvent;
+import 'dart:math' as math;
+import 'dart:svg' hide Rect;
+import 'dart:typed_data';
+
+import 'package:charted/core/text_metrics.dart';
+import 'package:charted/core/utils.dart';
+import 'package:charted/core/scales.dart';
+import 'package:charted/core/interpolators.dart';
+import 'package:charted/layout/layout.dart';
+import 'package:charted/selection/selection.dart';
+import 'package:charted/svg/axis.dart';
+import 'package:charted/svg/shapes.dart';
+import 'package:charted/selection/transition.dart';
+
+import 'package:collection/equality.dart';
+import 'package:logging/logging.dart';
+import 'package:observe/observe.dart';
+import 'package:quiver/core.dart';
+
+part 'chart_area.dart';
+part 'chart_config.dart';
+part 'chart_data.dart';
+part 'chart_events.dart';
+part 'chart_legend.dart';
+part 'chart_renderer.dart';
+part 'chart_series.dart';
+part 'chart_state.dart';
+part 'chart_theme.dart';
+
+part 'behaviors/axis_label_tooltip.dart';
+part 'behaviors/chart_tooltip.dart';
+part 'behaviors/line_marker.dart';
+part 'behaviors/mouse_tracker.dart';
+
+part 'cartesian_renderers/bar_chart_renderer.dart';
+part 'cartesian_renderers/cartesian_base_renderer.dart';
+part 'cartesian_renderers/bubble_chart_renderer.dart';
+part 'cartesian_renderers/line_chart_renderer.dart';
+part 'cartesian_renderers/stackedbar_chart_renderer.dart';
+
+part 'layout_renderers/layout_base_renderer.dart';
+part 'layout_renderers/pie_chart_renderer.dart';
+
+part 'src/cartesian_area_impl.dart';
+part 'src/layout_area_impl.dart';
+part 'src/chart_axis_impl.dart';
+part 'src/chart_config_impl.dart';
+part 'src/chart_data_impl.dart';
+part 'src/chart_events_impl.dart';
+part 'src/chart_legend_impl.dart';
+part 'src/chart_series_impl.dart';
+part 'src/chart_state_impl.dart';
+
+part 'themes/quantum_theme.dart';
+
+part 'data_transformers/aggregation.dart';
+part 'data_transformers/aggregation_item.dart';
+part 'data_transformers/aggregation_transformer.dart';
+part 'data_transformers/filter_transformer.dart';
+part 'data_transformers/transpose_transformer.dart';
+
+final Logger logger = new Logger('charted.charts');
diff --git a/charted/lib/charts/data_transformers/aggregation.dart b/charted/lib/charts/data_transformers/aggregation.dart
new file mode 100644
index 0000000..3a8978f
--- /dev/null
+++ b/charted/lib/charts/data_transformers/aggregation.dart
@@ -0,0 +1,728 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+
+part of charted.charts;
+
+/**
+ * Function callback to filter items in the input
+ */
+typedef bool AggregationFilterFunc(var item);
+
+typedef dynamic FieldAccessor(dynamic item, dynamic key);
+
+
+// TODO(midoringo, prsd): Consider splitting each aggregation type into its own
+// strategy object for readability, maintainability, and scalability.
+/**
+ * Given list of items, dimensions and facts, compute
+ * aggregates (COUNT, SUM, MIN, MAX) for facts at each dimension level.
+ */
+class AggregationModel {
+
+  // Number of aggregations we collect on each fact
+  int _aggregationTypesCount = 0;
+
+  // Currently supported list of aggregations.
+  static final List<String> supportedAggregationTypes =
+      ['sum', 'min', 'max', 'valid'];
+
+  // Computed aggregation types.
+  List<String> computedAggregationTypes;
+
+  // Offsets of aggregations that are computed once per fact per dimension
+  // If an offset is null, it will not be computed
+  int _offsetSum;
+  int _offsetMin;
+  int _offsetMax;
+  int _offsetCnt;
+
+  // Offset of aggregations that one computed once per dimension
+  int _offsetFilteredCount;
+  int _offsetSortedIndex;
+
+  // Number of bits we can use in an integer without making it medium int
+  static const int SMI_BITS = 30;
+
+  // Computed aggregations
+  static const int AGGREGATIONS_BUFFER_LENGTH = 1024 * 1024;
+  Float64List _aggregations;
+
+  // Cache of fact values
+  Float64List _factsCache;
+
+  // Cache of enumerated dimensions
+  List<int> _dimEnumCache;
+
+  // Sorted list of indices (for data in _rows/_dimEnumCache/_factsCache)
+  List<int> _sorted;
+
+  // Enumeration map for dimension values
+  List<Map<dynamic, int>> _dimToIntMap;
+
+  // Sort orders for dimension values
+  List<List<int>> _dimSortOrders;
+
+  // Input
+  List _rows;
+  List _dimFields;
+  List _factFields;
+
+  // When groupBy is called, this value represents the
+  // common prefix of the old and new dimensions list.
+  int _dimPrefixLength = 0;
+
+  // Dimensions mapped to computed aggregates
+  Map<String, int> _dimToAggrMap;
+
+  // null when no filter was applied.
+  // Otherwise, store a bitmap indicating if an item was included.
+  List<int> _filterResults;
+
+  // Cache of entities created for the facts on this aggregation view.
+  Map<String, AggregationItem> _entityCache;
+
+  // List of field names that aggregation items will have.
+  List<String> _itemFieldNamesCache;
+
+  // Walk through the map, by splitting key at '.'
+  final bool walkThroughMap;
+
+  // Map of fieldName to comparator function.
+  final Map<String, Function> comparators;
+
+  // Timing operations
+  static final Logger _logger = new Logger('aggregations');
+  Stopwatch _timeItWatch;
+  String _timeItName;
+
+  FieldAccessor dimensionAccessor;
+  FieldAccessor factsAccessor;
+
+  /**
+   * Create a new [AggregationModel] from a [collection] of items,
+   * list of [dimensions] on which the items are grouped and a list of [facts]
+   * on which aggregations are computed.
+   */
+  AggregationModel(List collection, List dimensions,
+                   List facts,
+                   { List<String> aggregationTypes,
+                     this.walkThroughMap: false,
+                     this.comparators,
+                     this.dimensionAccessor,
+                     this.factsAccessor}) {
+    _init(collection, dimensions, facts, aggregationTypes);
+  }
+
+  void _timeItStart(String name) {
+    _timeItName = name;
+    _timeItWatch = new Stopwatch();
+    _timeItWatch.start();
+  }
+
+  void _timeItEnd() {
+    _timeItWatch.stop();
+    _logger.info('[aggregations/$_timeItName] '
+                 '${_timeItWatch.elapsed.inMilliseconds}ms/${_rows.length}r');
+  }
+
+  List get factFields => _factFields;
+  List get dimensionFields => _dimFields;
+
+  /**
+   * Initialize the view
+   */
+  void _init(List collection, List dimensions,
+             List facts, List<String> aggregationTypes) {
+    if (collection == null) {
+      throw new ArgumentError('Data cannot be empty or null');
+    }
+
+    if (facts == null || facts.isEmpty) {
+      throw new ArgumentError('Facts cannot be empty or null');
+    }
+
+    if (dimensions == null) {
+      dimensions = [];
+    }
+
+    if (dimensionAccessor == null) {
+      dimensionAccessor = _fetch;
+    }
+
+    if (factsAccessor == null) {
+      factsAccessor = _fetch;
+    }
+
+    if (aggregationTypes != null) {
+      Iterable unknownAggregationTypes =
+          aggregationTypes.where((e) => !supportedAggregationTypes.contains(e));
+      if (unknownAggregationTypes.length != 0) {
+        throw new ArgumentError(
+            'Unknown aggregation types: ${unknownAggregationTypes.join(', ')}');
+      }
+    } else {
+      aggregationTypes = ['sum'];
+    }
+
+    // Always adding 'count' for correct computation of average and count.
+    if (!aggregationTypes.contains('valid')) {
+      aggregationTypes.add('valid');
+    }
+
+    _rows = collection;
+    _dimFields = new List.from(dimensions, growable: false);
+    _factFields = new List.from(facts, growable: false);
+    _entityCache = new Map<String, AggregationItem>();
+
+    _createBuffers();
+
+    _aggregationTypesCount = aggregationTypes.length;
+    for (int i = 0; i < _aggregationTypesCount; i++) {
+      switch(aggregationTypes[i]) {
+        case 'sum':
+          _offsetSum = i;
+          break;
+        case 'min':
+          _offsetMin = i;
+          break;
+        case 'max':
+          _offsetMax = i;
+          break;
+        case 'valid':
+          _offsetCnt = i;
+          break;
+      }
+    }
+    computedAggregationTypes = new List.from(aggregationTypes, growable: false);
+
+    // Preprocess the data
+    _preprocess();
+  }
+
+  /**
+   * Re-calculate aggregations based on new dimensions.
+   */
+  void groupBy(List dimensions, [AggregationFilterFunc filter = null]) {
+    if (dimensions == null) {
+      dimensions = [];
+    }
+
+    List savedDimFields = _dimFields;
+    _dimFields = new List.from(dimensions, growable: false);
+
+    _dimPrefixLength = 0;
+    while (_dimPrefixLength < _dimFields.length &&
+           _dimPrefixLength < savedDimFields.length &&
+           savedDimFields[_dimPrefixLength] == _dimFields[_dimPrefixLength]) {
+      ++_dimPrefixLength;
+    }
+
+    _createBuffers();
+    _preprocess(groupBy:true);
+
+    // For groupBy, compute immediately.
+    compute(filter);
+
+    // Ensure that cache represents updated dimensions
+    _updateCachedEntities();
+  }
+
+  /**
+   * Create buffers.
+   * This method is called when the object is being created and when
+   * a groupBy is called to change the dimensions on which
+   * aggregations are computed.
+   */
+  void _createBuffers() {
+    // Create both when object is created and groupBy is called
+    _dimEnumCache = new Int32List(_dimFields.length * _rows.length);
+
+    // Create only when the object is created
+    if (_factsCache == null) {
+      _factsCache = new Float64List((_factFields.length + 1) * _rows.length);
+    }
+
+    // Create only when the object is created
+    if (_filterResults == null) {
+      _filterResults = new List<int>((_rows.length) ~/ SMI_BITS + 1);
+    }
+
+    // Create both when object is created and groupBy is called
+    // Reuse dimension enumerations if possible.
+    var oldDimToInt = _dimToIntMap;
+    _dimToIntMap = new List<Map<dynamic, int>>.generate(_dimFields.length,
+        (i) => i < _dimPrefixLength ? oldDimToInt[i] : new Map<dynamic, int>());
+  }
+
+  /**
+   * Check cache entries
+   * When data is grouped by a new dimensions, entities that were
+   * created prior to the groupBy should be cleared and removed from cache
+   * if they aren't valid anymore.
+   * Update the entities that are valid after the groupBy.
+   */
+  void _updateCachedEntities() {
+    List keys = new List.from(_entityCache.keys, growable: false);
+    keys.forEach((key) {
+      _AggregationItemImpl entity = _entityCache[key];
+      if (entity == null) {
+        _entityCache.remove(key);
+      } else if (entity != null && entity.isValid) {
+        if (key.split(':').length <= _dimPrefixLength) {
+          entity.update();
+        } else {
+          _entityCache.remove(key);
+          entity.clear();
+        }
+      }
+    });
+  }
+
+  final Map<String, List> _parsedKeys = {};
+  /**
+   * Get value from a map-like object
+   */
+  dynamic _fetch(var item, String key) {
+    if (walkThroughMap && key.contains('.')) {
+      return walk(item, key, _parsedKeys);
+    } else {
+      return item[key];
+    }
+  }
+
+  /*
+   * Preprocess Data
+   *  - Enumerate dimension values
+   *  - Create sort orders for dimension values
+   *  - Cache facts in lists
+   */
+  void _preprocess({bool groupBy: false}) {
+
+    _timeItStart('preprocess');
+
+    // Enumerate dimensions...
+    // Cache dimensions and facts.
+
+    List<int> dimensionValCount =
+        new List<int>.generate(_dimFields.length, (idx) => 0);
+
+    int dimensionsCount = _dimFields.length;
+    int factsCount = _factFields.length;
+    int rowCount = _rows.length;
+
+    for (int ri = 0, factsDataOffset = 0, dimDataOffset = 0;
+         ri < rowCount; ++ri, factsDataOffset += factsCount,
+         dimDataOffset += dimensionsCount) {
+      var item = _rows[ri];
+
+      // Cache the fact values in the big buffer, but only
+      // when we are initializing (not when a groupBy was called
+      // after initialization)
+      if (!groupBy) {
+        for (int fi = 0; fi < factsCount; fi++) {
+          var value = factsAccessor(item,_factFields[fi]);
+          _factsCache[factsDataOffset + fi] =
+              (value == null) ? double.NAN : value.toDouble();
+        }
+      }
+
+      // Enumerate the dimension values and cache enumerated rows
+      for (int di = 0; di < dimensionsCount; di++) {
+        var dimensionVal = dimensionAccessor(item, _dimFields[di]);
+        int dimensionValEnum = _dimToIntMap[di][dimensionVal];
+        if (dimensionValEnum == null) {
+          _dimToIntMap[di][dimensionVal] = dimensionValCount[di];
+          dimensionValEnum = dimensionValCount[di]++;
+        }
+        _dimEnumCache[dimDataOffset + di] = dimensionValEnum;
+      }
+    }
+
+    // Sort all dimensions internally
+    // The resulting arrays would be used to sort the entire data
+
+    List oldSortOrders = _dimSortOrders;
+    _dimSortOrders = new List.generate(dimensionsCount, (i) {
+      if (groupBy && i < _dimPrefixLength) {
+        return oldSortOrders[i];
+      }
+
+      List dimensionVals = new List.from(_dimToIntMap[i].keys);
+      List retval = new List(_dimToIntMap[i].length);
+
+      // When a comparator is not specified, our implementation of the
+      // comparator tries to gracefully handle null values.
+      dimensionVals.sort(
+          comparators != null && comparators.containsKey(_dimFields[i]) ?
+              comparators[_dimFields[i]] : _defaultDimComparator);
+
+      for (int si = 0; si < retval.length; ++si) {
+        retval[_dimToIntMap[i][dimensionVals[si]]] = si;
+      }
+      return retval;
+    }, growable: false);
+
+    // Create a list of sorted indices - only if we are not having a full
+    // overlap of dimensionFields.
+    if (!groupBy || _dimPrefixLength != _dimFields.length) {
+      _sorted = new List<int>.generate(_rows.length, (i) => i, growable: false);
+      _sorted.sort(_comparator);
+    }
+
+    // Pre-compute frequently used values
+    _offsetSortedIndex = factsCount * _aggregationTypesCount;
+    _offsetFilteredCount = factsCount * _aggregationTypesCount + 1;
+
+    _timeItEnd();
+  }
+
+  // Ensures that null dimension values don't cause an issue with sorting
+  _defaultDimComparator(Comparable left, Comparable right) =>
+      (left == null && right == null) ? 0 :
+          (left == null) ? -1 :
+              (right == null) ? 1 : left.compareTo(right);
+
+  /*
+   * Given item indices in rows, compare them based
+   * on the sort orders created while preprocessing data.
+   */
+  _comparator(int one, int two) {
+    if (one == two) {
+      return 0;
+    }
+
+    int offsetOne = _dimFields.length * one;
+    int offsetTwo = _dimFields.length * two;
+
+    for (int i = 0; i < _dimFields.length; ++i) {
+      int diff = _dimSortOrders[i][_dimEnumCache[offsetOne + i]] -
+                 _dimSortOrders[i][_dimEnumCache[offsetTwo + i]];
+      if (diff != 0) {
+        return diff;
+      }
+    }
+    return 0;
+  }
+
+  /**
+   * Compute aggregations
+   * If [filter] is not null, it would be used to filter out items that
+   * should not be included in the aggregates.
+   */
+  void compute([AggregationFilterFunc filter = null]) {
+    _timeItStart('compute');
+
+    _dimToAggrMap = new Map<String, int>();
+    _aggregations = new Float64List(AGGREGATIONS_BUFFER_LENGTH);
+    _filterResults = filter == null ?
+        null : new List<int>.filled((_rows.length ~/ SMI_BITS) + 1, 0);
+
+    int rowCount = _rows.length;
+    int dimensionCount = _dimFields.length;
+    int factsCount = _factFields.length;
+
+    // Saves current dimension value to which we are aggregating
+    // Values of dimensions are in even indices (starting at 0) and
+    // location of respective dimension in buffer is in odd indices.
+    List<int> currentDim = new List<int>(dimensionCount * 2);
+    bool reset = true;
+    bool isNewDimension = false;
+    int aggregationSizePerDim = factsCount * _aggregationTypesCount;
+
+    // Reserve the 0th position for top-level aggregations.
+    int currentBufferPos = (factsCount * _aggregationTypesCount + 2);
+    _dimToAggrMap[''] = 0;
+    _aggregations[_offsetSortedIndex] = 0.0;
+
+
+    for (int ri = 0, index = 0, dimensionDataOffset = 0, factsDataOffset = 0;
+         ri < rowCount; ++ri, reset = false) {
+
+      // If filter is not null, check if this row must be included in
+      // the aggregations and mark it accordingly.
+      index = _sorted[ri];
+      if (filter != null) {
+        if (!filter(_rows[index])) {
+          continue;
+        } else {
+          _filterResults[ri ~/ SMI_BITS] |= (1 << (ri % SMI_BITS));
+        }
+      }
+
+      dimensionDataOffset = index * dimensionCount;
+      factsDataOffset = index * factsCount;
+
+      // Update top-level aggregations.
+      _updateAggregationsAt(0, factsDataOffset, ri == 0 ? true : false);
+
+      // See which dimensions get effected by this row.
+      // di => the index of the dimension
+      // ci => index of the cached value in [currentDim]
+      for (int di = 0, ci = 0; di < dimensionCount; ++di, ci += 2) {
+        // If a dimension value changed, then all dimensions that are lower
+        // in the heirarchy change too.
+        if (reset ||
+            currentDim[ci] != _dimEnumCache[dimensionDataOffset + di]) {
+          currentDim[ci] = _dimEnumCache[dimensionDataOffset + di];
+          currentDim[ci + 1] = currentBufferPos;
+
+          // Save location to aggregations position in the buffer
+          _dimToAggrMap[new List.generate(di + 1,
+              (i) => currentDim[2 * i]).join(':')] = currentBufferPos;
+
+          // Store items start position
+          _aggregations[currentBufferPos + _offsetSortedIndex] = ri.toDouble();
+
+          // After the aggregated values, we save the filtered count,
+          // index of the first item (in sorted)
+          currentBufferPos += (aggregationSizePerDim + 2);
+          reset = true;
+          isNewDimension = true;
+        }
+
+        _updateAggregationsAt(currentDim[ci + 1],
+            factsDataOffset, isNewDimension);
+        isNewDimension = false;
+      }
+    }
+
+    _timeItEnd();
+  }
+
+  /**
+   * Helper function that does the actual aggregations.
+   * This function is called once per row per dimension.
+   */
+  _updateAggregationsAt(int aggrDataOffset,
+                        int factsDataOffset, bool isNewDimension) {
+    // Update count.
+    _aggregations[aggrDataOffset + _offsetFilteredCount] += 1;
+
+    // Update aggregation for each of the facts.
+    for (int fi = 0, bufferFactOffset = aggrDataOffset;
+         fi < _factFields.length;
+         bufferFactOffset += _aggregationTypesCount, ++fi) {
+
+      double factValue = _factsCache[factsDataOffset + fi];
+      if (factValue.isNaN) {
+        continue;
+      }
+
+      // Sum
+      if (_offsetSum != null) {
+        _aggregations[bufferFactOffset + _offsetSum] += factValue;
+      }
+
+      // Min
+      if (_offsetMin != null && (isNewDimension || factValue <
+          _aggregations[bufferFactOffset + _offsetMin])) {
+        _aggregations[bufferFactOffset + _offsetMin] = factValue;
+      }
+
+      // Max
+      if (_offsetMax != null && (isNewDimension || factValue >
+          _aggregations[bufferFactOffset + _offsetMax])) {
+        _aggregations[bufferFactOffset + _offsetMax] = factValue;
+      }
+
+      // Count
+      if (_offsetCnt != null) {
+        _aggregations[bufferFactOffset + _offsetCnt]++;
+      }
+    }
+  }
+
+  /*
+   * TODO(prsd):
+   * 1. Implementation of updates and posting updates to entities.
+   *    patchEntity and addToEntity must add listeners on AggregationItems
+   *    and any changes must be propagated to entities.
+   * 2. Updates (add/remove/update) should do their best to update the
+   *    aggregations and then maybe do a delayed recomputation (sort etc;)
+   */
+
+  /**
+   * Update an item.
+   * If aggregates were already computed, they are updated to reflect the
+   * new value and any observers are notified.
+   */
+  void updateItem(dynamic item, String field) {
+    throw new UnimplementedError();
+  }
+
+  /**
+   * Add a new item.
+   * If aggregates were already computed, they are updated to reflect
+   * values on the new item.
+   */
+  void addItem(dynamic item) {
+    throw new UnimplementedError();
+  }
+
+  /**
+   * Remove an existing item.
+   * If aggregates were already computed, they are updated to reflect
+   * facts on the removed item.
+   */
+  void removeItem(dynamic item) {
+    throw new UnimplementedError();
+  }
+
+  /**
+   * Return an [AggregationItem] that represents facts for dimension
+   * represented by [dimension] Only one instance of an entity is created
+   * per dimension (even if this function is called multiple times)
+   *
+   * Callers of this method can observe the returned entity for updates to
+   * aggregations caused by changes to filter or done through add, remove
+   * or modify of items in the collection.
+   */
+  AggregationItem facts(List dimension) {
+    List<int> enumeratedList = new List<int>();
+    for (int i = 0; i < dimension.length; ++i) {
+      enumeratedList.add(_dimToIntMap[i][dimension[i]]);
+    }
+
+    String key = enumeratedList.join(':');
+    AggregationItem item = _entityCache[key];
+
+    if (item == null && _dimToAggrMap.containsKey(key)) {
+      item = new _AggregationItemImpl(this, dimension, key);
+      _entityCache[key] = item;
+    }
+
+    return item;
+  }
+
+  /**
+   * Return a list of values that are present for a dimension field.
+   */
+  List valuesForDimension(dynamic dimensionFieldName) {
+    int di = _dimFields.indexOf(dimensionFieldName);
+    if (di < 0) {
+      return null;
+    }
+    List values = new List.from(_dimToIntMap[di].keys);
+    values.sort(
+        comparators != null && comparators.containsKey(dimensionFieldName) ?
+            comparators[dimensionFieldName] : _defaultDimComparator);
+    return values;
+  }
+}
+
+/**
+ * Parse a path for nested map-like objects.
+ * Caches the parsed key in the passed map.
+ *
+ * Takes map keys of the format:
+ *     "list(key=val;val=m).another(state=good).state"
+ * and outputs:
+ *     ["list", {"key": "val", "val": "m"},
+ *      "another", {"state": "good"}, "state"]
+ */
+List _parseKey(String key, Map parsedKeysCache) {
+  List parts = parsedKeysCache == null ? null : parsedKeysCache[key];
+  if (parts == null && key != null) {
+    parts = new List();
+    if (key.contains(').')) {
+      int start = 0;
+      int cursor = 0;
+      bool inParams = false;
+      List matchKeyVals;
+      Map listMatchingMap = {};
+
+      while (cursor < key.length) {
+        if (!inParams) {
+          cursor = key.indexOf('(', start);
+          if (cursor == -1) {
+            parts.addAll(key.substring(start).split('.'));
+            break;
+          }
+          parts.addAll(key.substring(start, cursor).split('.'));
+          cursor++;
+          start = cursor;
+          inParams = true;
+        } else {
+          cursor = key.indexOf(')', start);
+          if (cursor == -1) {
+            throw new ArgumentError('Invalid field name: $key');
+          }
+          listMatchingMap.clear();
+          matchKeyVals = key.substring(start, cursor).split(';');
+          matchKeyVals.forEach((value) {
+            List keyval = value.split('=');
+            if (keyval.length != 2) {
+              throw new ArgumentError('Invalid field name: ${key}');
+            }
+            listMatchingMap[keyval[0]] = keyval[1];
+          });
+          parts.add(listMatchingMap);
+          cursor += 2;
+          start = cursor;
+          inParams = false;
+        }
+      }
+    } else {
+      parts = key.split('.');
+    }
+    if (parsedKeysCache != null) {
+      parsedKeysCache[key] = parts;
+    }
+  }
+
+  return parts;
+}
+
+/**
+ * Walk a map-like structure that could have list in the path.
+ *
+ * Example:
+ *     Map testMap = {
+ *       "first": "firstval",
+ *       "list": [
+ *         { "val": "m",
+ *           "key": "val",
+ *           "another": [
+ *             { 'state': 'good' },
+ *             { 'state': 'bad' }
+ *           ]
+ *         },
+ *         { "val": "m", "key": "invalid" },
+ *         { "val": "o" }
+ *       ]
+ *     };
+ *
+ *  For the above map:
+ *     walk(testMap, "list(key=val;val=m).another(state=good).state");
+ *  outputs:
+ *     good
+ */
+dynamic walk(initial, String key, Map parsedKeyCache) {
+  List parts = _parseKey(key, parsedKeyCache);
+  return parts.fold(initial, (current, part) {
+    if (current == null) {
+      return null;
+    } else if (current is List && part is Map) {
+      for (int i = 0; i < current.length; i++) {
+        bool match = true;
+        part.forEach((key, val) {
+          if ((key.contains('.') &&
+               walk(part, key, parsedKeyCache).toString() != val) ||
+              part[key] != val) {
+            match = false;
+          }
+        });
+        if (match) {
+          return current[i];
+        }
+      }
+    } else {
+      return current[part];
+    }
+  });
+}
diff --git a/charted/lib/charts/data_transformers/aggregation_item.dart b/charted/lib/charts/data_transformers/aggregation_item.dart
new file mode 100644
index 0000000..b6c3480
--- /dev/null
+++ b/charted/lib/charts/data_transformers/aggregation_item.dart
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+
+part of charted.charts;
+
+/**
+ * AggregationItem is created by [AggregationModel] to make access to facts
+ * observable. Users must use AggregationItem.isValid before trying to access
+ * the aggregations.
+ */
+abstract class AggregationItem extends ChangeNotifier {
+  /**
+   * List of dimension fields in effect
+   */
+  List<String> dimensions;
+
+  /**
+   * Check if this entity is valid.
+   * Currently the only case where an entity becomes invalid
+   * is when a groupBy is called on the model.
+   */
+  bool get isValid;
+
+  /**
+   * Fetch the fact from AggregationModel and return it
+   * Currently takes keys in the form of "sum(spend)", where sum is
+   * the aggregation type and spend is fact's field name.
+   *
+   * Currently, "sum", "count", "min", "max", "avg", "valid" and "avgOfValid"
+   * are supported as the operators.
+   */
+
+  operator[](String key);
+
+  /**
+   * Check if we support a given key.
+   */
+  bool containsKey(String key);
+
+  /**
+   * List of valid field names for this entity.
+   * It's the combined list of accessors for individual items, items in
+   * the next dimension and all possible facts defined on the view.
+   */
+  Iterable<String> get fieldNames;
+}
+
+/*
+ * Implementation of AggregationItem
+ * Instances of _AggregationItemImpl are created only by AggregationModel
+ */
+class _AggregationItemImpl extends ChangeNotifier implements AggregationItem {
+  static final List<String> derivedAggregationTypes = ['count', 'avg'];
+
+  AggregationModel model;
+  List<String> dimensions;
+
+  String _key;
+
+  int _length;
+  int _factsOffset;
+
+  /*
+   * Currently entities are created only when they have valid aggregations
+   */
+  _AggregationItemImpl(this.model, this.dimensions, this._key) {
+    if (model == null) {
+      throw new ArgumentError('Model cannot be null');
+    }
+    if (_key == null) {
+      _key = '';
+    }
+
+    // facts + list of items + list of children (drilldown)
+    _length =  model._aggregationTypesCount * model._factFields.length + 2;
+    _factsOffset = model._dimToAggrMap[_key];
+  }
+
+  /**
+   * _dimToAggrMap got updated on the model, update ourselves accordingly
+   */
+  void update() {
+    _factsOffset = model._dimToAggrMap[_key];
+  }
+
+  /*
+   * Mark this entity as invalid.
+   */
+  void clear() {
+    _factsOffset = null;
+  }
+
+  bool get isValid => _factsOffset != null;
+
+  dynamic operator[](String key) {
+    if (!isValid) {
+      throw new StateError('Entity is not valid anymore');
+    }
+
+    int argPos = key.indexOf('(');
+    if (argPos == -1) {
+      return _nonAggregationMember(key);
+    }
+
+    String aggrFunc = key.substring(0, argPos);
+    int aggrFuncIndex = model.computedAggregationTypes.indexOf(aggrFunc);
+    if (aggrFuncIndex == -1 && !derivedAggregationTypes.contains(aggrFunc)) {
+      throw new ArgumentError('Unknown aggregation method: ${aggrFunc}');
+    }
+
+    String factName = key.substring(argPos + 1, key.lastIndexOf(')'));
+    int factIndex = model._factFields.indexOf(factName);
+
+    // Try parsing int if every element in factFields is int.
+    if (model._factFields.every((e) => e is int)) {
+      factIndex = model._factFields.indexOf(int.parse(factName,
+          onError: (e) {
+            throw new ArgumentError('Type of factFields are int but factName' +
+                'contains non int value');
+          }));
+    }
+    if (factIndex == -1) {
+      throw new ArgumentError('Model not configured for ${factName}');
+    }
+
+    int offset = _factsOffset + factIndex * model._aggregationTypesCount;
+    // No items for the corresponding fact, so return null.
+    if (aggrFunc != 'count' && aggrFunc != 'avg' &&
+        model._aggregations[offset + model._offsetCnt].toInt() == 0) {
+      return null;
+    }
+
+    if (aggrFuncIndex != -1) {
+      return model._aggregations[offset + aggrFuncIndex];
+    } else if (aggrFunc == 'count') {
+      return model._aggregations[_factsOffset +
+                                 model._offsetFilteredCount].toInt();
+    } else if (aggrFunc == 'avg') {
+      return model._aggregations[offset + model._offsetSum] /
+             model._aggregations[_factsOffset + model._offsetFilteredCount].
+          toInt();
+    }  else if (aggrFunc == 'avgOfValid') {
+      return model._aggregations[offset + model._offsetSum] /
+          model._aggregations[offset + model._offsetCnt].toInt();
+    }
+    return null;
+  }
+
+  dynamic _nonAggregationMember(String key) {
+    if (key == 'items') {
+      return new _AggregationItemsIterator(model, dimensions, _key);
+    }
+    if (key == 'aggregations') {
+      return _lowerAggregations();
+    }
+    return null;
+  }
+
+  List<AggregationItem> _lowerAggregations() {
+    List<AggregationItem> aggregations = new List<AggregationItem>();
+    if (dimensions.length == model._dimFields.length) {
+      return aggregations;
+    }
+
+    var lowerDimensionField = model._dimFields[dimensions.length];
+    List lowerVals = model.valuesForDimension(lowerDimensionField);
+
+    lowerVals.forEach((name) {
+      List lowerDims = new List.from(dimensions)..add(name);
+      AggregationItem entity = model.facts(lowerDims);
+      if (entity != null) {
+        aggregations.add(entity);
+      }
+    });
+
+    return aggregations;
+  }
+
+  bool containsKey(String key) => fieldNames.contains(key);
+
+  Iterable<String> get fieldNames {
+    if (!isValid) {
+      throw new StateError('Entity is not valid anymore');
+    }
+
+    if (model._itemFieldNamesCache == null) {
+      List<String> cache = new List<String>.from(['items', 'children']);
+      model._factFields.forEach((var name) {
+        AggregationModel.supportedAggregationTypes.forEach((String aggrType) {
+          cache.add('${aggrType}(${name})');
+        });
+      });
+      model._itemFieldNamesCache = cache;
+    }
+    return model._itemFieldNamesCache;
+  }
+
+  /*
+   * TODO(prsd): Implementation of [Observable]
+   */
+  Stream<List<ChangeRecord>> get changes {
+    throw new UnimplementedError();
+  }
+}
+
+class _AggregationItemsIterator implements Iterator {
+  final AggregationModel model;
+  List<String> dimensions;
+  String key;
+
+  int _current;
+  int _counter = 0;
+
+  int _start;
+  int _count;
+  int _endOfRows;
+
+  _AggregationItemsIterator(this.model, List<String> this.dimensions,
+                           String this.key) {
+    int offset = model._dimToAggrMap[key];
+    if (offset != null) {
+      int factsEndOffset = offset +
+          model._factFields.length * model._aggregationTypesCount;
+      _start = model._aggregations[factsEndOffset].toInt();
+      _count = model._aggregations[factsEndOffset + 1].toInt();
+      _endOfRows = model._rows.length;
+    }
+  }
+
+  bool moveNext() {
+    if (_current == null) {
+      _current = _start;
+    } else {
+      ++_current;
+    }
+
+    if (++_counter > _count) {
+      return false;
+    }
+
+    /*
+     * If model had a filter applied, then check if _current points to a
+     * filtered-in row, else skip till we find one.
+     * Also, make sure (even if something else went wrong) we don't go
+     * beyond the number of items in the model.
+     */
+    if (this.model._filterResults != null) {
+      while ((this.model._filterResults[_current ~/ AggregationModel.SMI_BITS] &
+              (1 << _current % AggregationModel.SMI_BITS)) == 0 &&
+          _current <= _endOfRows) {
+        ++_current;
+      }
+    }
+    return (_current < _endOfRows);
+  }
+
+  get current {
+    if (_current == null || _counter > _count) {
+      return null;
+    }
+    return model._rows[model._sorted[_current]];
+  }
+}
diff --git a/charted/lib/charts/data_transformers/aggregation_transformer.dart b/charted/lib/charts/data_transformers/aggregation_transformer.dart
new file mode 100644
index 0000000..8199175
--- /dev/null
+++ b/charted/lib/charts/data_transformers/aggregation_transformer.dart
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+
+part of charted.charts;
+
+/**
+ * Transforms the ChartData base on the specified dimension columns and facts
+ * columns indices. The values in the facts columns will be aggregated by the
+ * tree heirarcy generated by the dimension columns.  Expand and Collapse
+ * methods may be called to display different levels of aggragation.
+ *
+ * The output ChartData produced by transform() will contain only columns in the
+ * original ChartData that were specified in dimensions or facts column indices.
+ * The output column will be re-ordered first by the indices specified in the
+ * dimension column indices then by the facts column indices.  The data in the
+ * cells of each row will also follow this rule.
+ */
+class AggregationTransformer extends ChangeNotifier
+    implements ChartDataTransform, ChartData {
+
+  static const String AGGREGATION_TYPE_SUM = 'sum';
+  static const String AGGREGATION_TYPE_MIN = 'min';
+  static const String AGGREGATION_TYPE_MAX = 'max';
+  static const String AGGREGATION_TYPE_VALID = 'valid';
+  final SubscriptionsDisposer _dataSubscriptions = new SubscriptionsDisposer();
+  final Set<List> _expandedSet = new Set();
+  Iterable<ChartColumnSpec> columns;
+  ObservableList<Iterable> rows = new ObservableList();
+  List<int> _dimensionColumnIndices;
+  List<int> _factsColumnIndices;
+  String _aggregationType;
+  AggregationModel _model;
+  bool _expandAllDimension = false;
+  List _selectedColumns = [];
+  FieldAccessor _indexFieldAccessor = (List row, int index) => row[index];
+  ChartData _data;
+
+  AggregationTransformer(this._dimensionColumnIndices,
+      this._factsColumnIndices,
+      [String aggregationType = AGGREGATION_TYPE_SUM]) {
+    _aggregationType = aggregationType;
+  }
+
+  /**
+   * Transforms the ChartData base on the specified dimension columns and facts
+   * columns, aggregation type and currently expanded dimensions.
+   */
+  ChartData transform(ChartData data) {
+    assert(data.columns.length > max(_dimensionColumnIndices));
+    assert(data.columns.length > max(_factsColumnIndices));
+    _data = data;
+    _registerListeners();
+    _transform();
+    return this;
+  }
+
+  /** Registers listeners if data.rows or data.columns are Observable. */
+  _registerListeners() {
+    _dataSubscriptions.dispose();
+
+    if(_data is Observable) {
+      var observable = (_data as Observable);
+      _dataSubscriptions.add(observable.changes.listen((records) {
+        _transform();
+
+        // NOTE: Currently we're only passing the first change because the chart
+        // area just draw with the updated data.  When we add partial update
+        // to chart area, we'll need to handle this better.
+        notifyChange(records.first);
+      }));
+    }
+  }
+
+  /**
+   * Performs the filter transform with _data.  This is called on transform and
+   * onChange if the input ChartData is Observable.
+   */
+  _transform() {
+    _model = new AggregationModel(_data.rows, _dimensionColumnIndices,
+        _factsColumnIndices, aggregationTypes: [_aggregationType],
+        dimensionAccessor: _indexFieldAccessor,
+        factsAccessor: _indexFieldAccessor);
+    _model.compute();
+
+    // If user called expandAll prior to model initiation, do it now.
+    if (_expandAllDimension) {
+      expandAll();
+    }
+
+    _selectedColumns.clear();
+    _selectedColumns.addAll(_dimensionColumnIndices);
+    _selectedColumns.addAll(_factsColumnIndices);
+
+    // Process rows.
+    rows.clear();
+    var transformedRows = <Iterable>[];
+    for (var value in _model.valuesForDimension(_dimensionColumnIndices[0])) {
+      _generateAggregatedRow(transformedRows, [value]);
+    }
+    rows.addAll(transformedRows);
+
+    // Process columns.
+    columns = new List<ChartColumnSpec>.generate(_selectedColumns.length, (index) =>
+        _data.columns.elementAt(_selectedColumns[index]));
+  }
+
+  /**
+   * Fills the aggregatedRows List with data base on the set of expanded values
+   * recursively.  Currently when a dimension is expanded, rows are
+   * generated for its children but not for itself.  If we want to change the
+   * logic to include itself, just move the expand check around the else clause
+   * and always write a row of data whether it's expanded or not.
+   */
+  _generateAggregatedRow(List<Iterable> aggregatedRows, List dimensionValues) {
+    var entity = _model.facts(dimensionValues);
+    var dimensionLevel = dimensionValues.length - 1;
+    var currentDimValue = dimensionValues.last;
+
+    // Dimension is not expanded at this level.  Generate data rows and fill int
+    // value base on whether the column is dimension column or facts column.
+    if (!_isExpanded(dimensionValues) ||
+        dimensionValues.length == _dimensionColumnIndices.length) {
+      aggregatedRows.add(new List.generate(_selectedColumns.length, (index) {
+
+        // Dimension column.
+        if (index < _dimensionColumnIndices.length) {
+          if (index < dimensionLevel) {
+            // If column index is in a higher level, write parent value.
+            return dimensionValues[0];
+          } else if (index == dimensionLevel) {
+            // If column index is at current level, write value.
+            return dimensionValues.last;
+          } else {
+            // If column Index is in a lower level, write empty string.
+            return '';
+          }
+        } else {
+          // Write aggregated value for facts column.
+          return entity['${_aggregationType}(${_selectedColumns[index]})'];
+        }
+      }));
+    } else {
+      // Dimension is expanded, process each child dimension in the expanded
+      // dimension.
+      for (AggregationItem childAggregation in entity['aggregations']) {
+        _generateAggregatedRow(aggregatedRows, childAggregation.dimensions);
+      }
+    }
+  }
+
+  /**
+   * Expands a specific dimension and optionally expands all of its parent
+   * dimensions.
+   */
+  void expand(List dimension, [bool expandParent = true]) {
+    _expandAllDimension = false;
+    _expandedSet.add(dimension);
+    if (expandParent && dimension.length > 1) {
+      Function eq = const ListEquality().equals;
+      var dim = dimension.take(dimension.length - 1).toList();
+      if (!_expandedSet.any((e) => eq(e, dim))) {
+        expand(dim);
+      }
+    }
+  }
+
+  /**
+   * Collapses a specific dimension and optionally collapse all of its
+   * Children dimensions.
+   */
+  void collapse(List dimension, [bool collapseChildren = true]) {
+    _expandAllDimension = false;
+    if (collapseChildren) {
+      Function eq = const ListEquality().equals;
+      // Doing this because _expandedSet.where doesn't work.
+      var collapseList = [];
+      for (List dim in _expandedSet) {
+        if (eq(dim.take(dimension.length).toList(), dimension)) {
+          collapseList.add(dim);
+        }
+      }
+      _expandedSet.removeAll(collapseList);
+    } else {
+      _expandedSet.remove(dimension);
+    }
+  }
+
+  /** Expands all dimensions. */
+  void expandAll() {
+    if (_model != null) {
+      for (var value in _model.valuesForDimension(_dimensionColumnIndices[0])) {
+        _expandAll([value]);
+      }
+      _expandAllDimension = false;
+    } else {
+      _expandAllDimension = true;
+    }
+  }
+
+  void _expandAll(value) {
+    var entity = _model.facts(value);
+    _expandedSet.add(value);
+    for (AggregationItem childAggregation in entity['aggregations']) {
+      _expandAll(childAggregation.dimensions);
+    }
+  }
+
+  /** Collapses all dimensions. */
+  void collapseAll() {
+    _expandAllDimension = false;
+    _expandedSet.clear();
+  }
+
+  /** Tests if specific dimension is expanded. */
+  bool _isExpanded(List dimension) {
+    Function eq = const ListEquality().equals;
+    return _expandedSet.any((e) => eq(e, dimension));
+  }
+}
diff --git a/charted/lib/charts/data_transformers/filter_transformer.dart b/charted/lib/charts/data_transformers/filter_transformer.dart
new file mode 100644
index 0000000..65794be
--- /dev/null
+++ b/charted/lib/charts/data_transformers/filter_transformer.dart
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+
+part of charted.charts;
+
+typedef bool FilterFunction(dynamic value);
+
+/**
+ * Transforms the ChartData base on the specified FilterDefinitions.  Each row
+ * of data will be tested by passing the value at target column to the filter
+ * function.  If filter function returns false, the row will be filtered out.
+ * This transformer does not modify the column part of the input ChartData.
+ */
+class FilterTransformer extends ChangeNotifier
+    implements ChartDataTransform, ChartData {
+  final SubscriptionsDisposer _dataSubscriptions = new SubscriptionsDisposer();
+  Iterable<ChartColumnSpec> columns;
+  ObservableList<Iterable> rows = new ObservableList();
+  List<FilterDefinition> filterFunctions;
+  ChartData _data;
+
+  FilterTransformer(this.filterFunctions);
+
+  /**
+   * Transforms the input data with the list of [FilterDefinition] specified in
+   * the constructor.  If the rows and columns are ObservableList in the data,
+   * changes in rows and columns in input data will trigger tranform to be
+   * performed again to update the output rows and columns.
+   */
+  ChartData transform(ChartData data) {
+    _data = data;
+    _registerListeners();
+    _transform();
+    return this;
+  }
+
+  /** Registers listeners if data.rows or data.columns are Observable. */
+  _registerListeners() {
+    _dataSubscriptions.dispose();
+
+    if(_data is Observable) {
+      var observable = (_data as Observable);
+      _dataSubscriptions.add(observable.changes.listen((records) {
+        _transform();
+
+        // NOTE: Currently we're only passing the first change because the chart
+        // area just draw with the updated data.  When we add partial update
+        // to chart area, we'll need to handle this better.
+        notifyChange(records.first);
+      }));
+    }
+  }
+
+  /**
+   * Performs the filter transform with _data.  This is called on transform and
+   * onChange if the input ChartData is Observable.
+   */
+  _transform() {
+    columns = _data.columns;
+    rows.clear();
+
+    for (var row in _data.rows) {
+      // Add the row if each value in target column passes the filter function.
+      if (filterFunctions.every((e) =>
+          e.filterFunc(row.elementAt(e.targetColumn)))) {
+        rows.add(row);
+      }
+    }
+  }
+}
+
+class FilterDefinition {
+  final FilterFunction filterFunc;
+  final int targetColumn;
+  FilterDefinition(this.targetColumn, this.filterFunc);
+}
diff --git a/charted/lib/charts/data_transformers/transpose_transformer.dart b/charted/lib/charts/data_transformers/transpose_transformer.dart
new file mode 100644
index 0000000..e3acbce
--- /dev/null
+++ b/charted/lib/charts/data_transformers/transpose_transformer.dart
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+
+part of charted.charts;
+
+/**
+ * Transforms the ChartData by transposing the columns and rows.  A label column
+ * index in the original data will need to be specified (default to 0), all
+ * values in the specified label column will be used as the label for the
+ * transformed data, all the labels in the original Chart data columns will be
+ * populated in the label column as values of that column.
+ *
+ * All values in the data except for the data in the label column must have the
+ * same type; All columns except for the lable column must have the same
+ * formatter if a formatter exist for columns.
+ */
+class TransposeTransformer extends ChangeNotifier
+    implements ChartDataTransform, ChartData {
+  final SubscriptionsDisposer _dataSubscriptions = new SubscriptionsDisposer();
+  ObservableList<ChartColumnSpec> columns = new ObservableList();
+  ObservableList<Iterable> rows = new ObservableList();
+
+  // If specified, this values of this column in the input chart data will be
+  // used as labels of the transposed column label.  Defaults to first column.
+  int _labelColumn;
+  ChartData _data;
+
+  TransposeTransformer([this._labelColumn = 0]);
+
+  /**
+   * Transforms the input data with the specified label column in the
+   * constructor.  If the ChartData is Observable, changes fired by the input
+   * data will trigger tranform to be performed again to update the output rows
+   * and columns.
+   */
+  ChartData transform(ChartData data) {
+    _data = data;
+    _registerListeners();
+    _transform();
+    return this;
+  }
+
+  /** Registers listeners if input ChartData is Observable. */
+  _registerListeners() {
+    _dataSubscriptions.dispose();
+
+    if(_data is Observable) {
+      var observable = (_data as Observable);
+      _dataSubscriptions.add(observable.changes.listen((records) {
+        _transform();
+
+        // NOTE: Currently we're only passing the first change because the chart
+        // area just draw with the updated data.  When we add partial update
+        // to chart area, we'll need to handle this better.
+        notifyChange(records.first);
+      }));
+    }
+  }
+
+  /**
+   * Performs the transpose transform with _data.  This is called on transform
+   * and on changes if ChartData is Observable.
+   */
+  _transform() {
+    // Assert all columns are of the same type and formatter, excluding the
+    // label column.
+    var type;
+    var formatter;
+    for (var i = 0; i < _data.columns.length; i++) {
+      if (i != _labelColumn) {
+        if (type == null) {
+          type = _data.columns.elementAt(i).type;
+        } else {
+          assert(type == _data.columns.elementAt(i).type);
+        }
+        if (formatter == null) {
+          formatter = _data.columns.elementAt(i).formatter;
+        } else {
+          assert(formatter == _data.columns.elementAt(i).formatter);
+        }
+      }
+    }
+
+    columns.clear();
+    rows.clear();
+    rows.addAll(new List<Iterable>.generate(_data.columns.length - 1, (i) => []));
+
+    // Populate the transposed rows' data, excluding the label column, visit
+    // each value in the original data once.
+    var columnLabels = [];
+    for (var row in _data.rows) {
+      for (var i = 0; i < row.length; i++) {
+        var columnOffset = (i < _labelColumn) ? 0 : 1;
+        if (i != _labelColumn) {
+          (rows.elementAt(i - columnOffset) as List).add(row.elementAt(i));
+        } else {
+          columnLabels.add(row.elementAt(i));
+        }
+      }
+    }
+
+    // Transpose the ColumnSpec's label into the column where the original
+    // column that is used as the new label.
+    for (var i = 0; i < rows.length; i++) {
+      var columnOffset = (i < _labelColumn) ? 0 : 1;
+      (rows.elementAt(i) as List).insert(_labelColumn,
+          _data.columns.elementAt(i + columnOffset).label);
+
+    }
+
+    // Construct new ColumnSpaces base on the label column.
+    for (var label in columnLabels) {
+      columns.add(new ChartColumnSpec(type: type, label: label,
+          formatter: formatter));
+    }
+    columns.insert(_labelColumn,
+        new ChartColumnSpec(type: ChartColumnSpec.TYPE_STRING, label:
+        _data.columns.elementAt(_labelColumn).label));
+  }
+}
diff --git a/charted/lib/charts/layout_renderers/layout_base_renderer.dart b/charted/lib/charts/layout_renderers/layout_base_renderer.dart
new file mode 100644
index 0000000..484613a
--- /dev/null
+++ b/charted/lib/charts/layout_renderers/layout_base_renderer.dart
@@ -0,0 +1,78 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+abstract class LayoutRendererBase implements LayoutRenderer {
+  LayoutArea area;
+  ChartSeries series;
+  ChartTheme theme;
+  Rect rect;
+
+  Element host;
+  Selection root;
+  SelectionScope scope;
+
+  StreamController<ChartEvent> mouseOverController;
+  StreamController<ChartEvent> mouseOutController;
+  StreamController<ChartEvent> mouseClickController;
+
+  void _ensureAreaAndSeries(ChartArea area, ChartSeries series) {
+    assert(area != null && series != null);
+    this.area = area;
+    this.series = series;
+  }
+
+  void _ensureReadyToDraw(Element element) {
+    assert(series != null && area != null);
+    assert(element != null && element is GElement);
+
+    if (scope == null) {
+      host = element;
+      scope = new SelectionScope.element(element);
+      root = scope.selectElements([host]);
+    }
+
+    theme = area.theme;
+    rect = area.layout.renderArea;
+  }
+
+  @override
+  void dispose() {
+    if (root == null) return;
+    root.selectAll('.row-group').remove();
+  }
+
+  @override
+  Stream<ChartEvent> get onValueMouseOver {
+    if (mouseOverController == null) {
+      mouseOverController = new StreamController.broadcast(sync: true);
+    }
+    return mouseOverController.stream;
+  }
+
+  @override
+  Stream<ChartEvent> get onValueMouseOut {
+    if (mouseOutController == null) {
+      mouseOutController = new StreamController.broadcast(sync: true);
+    }
+    return mouseOutController.stream;
+  }
+
+  @override
+  Stream<ChartEvent> get onValueClick {
+    if (mouseClickController == null) {
+      mouseClickController = new StreamController.broadcast(sync: true);
+    }
+    return mouseClickController.stream;
+  }
+
+  /// Get a color using the theme's ordinal scale of colors
+  String colorForKey(i, [int state = ChartTheme.STATE_NORMAL]) =>
+      area.theme.getColorForKey(series.measures.elementAt(i), state);
+}
diff --git a/charted/lib/charts/layout_renderers/pie_chart_renderer.dart b/charted/lib/charts/layout_renderers/pie_chart_renderer.dart
new file mode 100644
index 0000000..2e6f43a
--- /dev/null
+++ b/charted/lib/charts/layout_renderers/pie_chart_renderer.dart
@@ -0,0 +1,137 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+class PieChartRenderer extends LayoutRendererBase {
+  static const STATS_PERCENTAGE = 'percentage-only';
+  static const STATS_VALUE = 'value-only';
+  static const STATS_VALUE_PERCENTAGE = 'value-percentage';
+
+  final Iterable<int> dimensionsUsingBand = const[];
+  final String statsMode;
+  final num innerRadiusRatio;
+  final int maxSliceCount;
+  final String otherItemsLabel;
+  final String otherItemsColor;
+  final showLabels;
+
+  @override
+  final String name = "pie-rdr";
+
+  final List<ChartLegendItem> _legend = [];
+
+  PieChartRenderer({
+      num innerRadiusRatio: 0,
+      bool showLabels,
+      this.statsMode: STATS_PERCENTAGE,
+      this.maxSliceCount: SMALL_INT_MAX,
+      this.otherItemsLabel: 'Other',
+      this.otherItemsColor: '#EEEEEE'})
+      : showLabels = showLabels == null ? innerRadiusRatio == 0 : showLabels,
+        innerRadiusRatio = innerRadiusRatio;
+
+  /// Returns false if the number of dimension axes != 0. Pie chart can only
+  /// be rendered on areas with no axes.
+  @override
+  bool prepare(ChartArea area, ChartSeries series) {
+    _ensureAreaAndSeries(area, series);
+    return area is LayoutArea;
+  }
+
+  @override
+  Iterable<ChartLegendItem> layout(
+      Element element, {Future schedulePostRender}) {
+    _ensureReadyToDraw(element);
+
+    var radius = math.min(rect.width, rect.height) / 2;
+    root.attr('transform', 'translate(${rect.width / 2}, ${rect.height / 2})');
+
+    // Pick only items that are valid - non-null and don't have null value
+    var measure = series.measures.first,
+        dimension = area.config.dimensions.first,
+        rows = area.data.rows.where(
+            (x) => x != null && x[measure] != null).toList();
+
+    rows.sort((a, b) => b[measure].compareTo(a[measure]));
+
+    // Limit items to the passed maxSliceCount
+    var otherRow;
+    if (rows.length > maxSliceCount) {
+      var displayed = rows.take(maxSliceCount).toList();
+      var otherItemsValue = 0;
+      for (int i = displayed.length; i < rows.length; ++i) {
+        otherItemsValue += rows.elementAt(i)[measure];
+      }
+      otherRow = new List(rows.first.length)
+        ..[dimension] = otherItemsLabel
+        ..[measure] = otherItemsValue;
+      rows = displayed..add(otherRow);
+    } else {
+      otherRow = null;
+    }
+
+    if (area.config.isRTL) {
+      rows = rows.reversed.toList();
+    }
+
+    var data = (new PieLayout()..accessor = (d, i) => d[measure]).layout(rows),
+        arc = new SvgArc(
+            innerRadiusCallback: (d, i, e) => innerRadiusRatio * radius,
+            outerRadiusCallback: (d, i, e) => radius);
+
+    var pie = root.selectAll('.pie-path').data(data);
+    var colorForData = (Iterable row) =>
+        row.hashCode == otherRow.hashCode
+            ? theme.getOtherColor()
+            : theme.getColorForKey(row.elementAt(dimension));
+
+    pie.enter.append('path').each((d, i, e) {
+      e.classes.add('pie-path');
+      e.attributes
+        ..['fill'] = colorForData(d.data)
+        ..['d'] = arc.path(d, i, host)
+        ..['stroke-width'] = '1px'
+        ..['stroke'] = '#ffffff';
+      e.append(
+          Namespace.createChildElement('text', e)
+            ..classes.add('pie-label'));
+    });
+
+    pie
+      ..on('click', (d, i, e) => _event(mouseClickController, d, i, e))
+      ..on('mouseover', (d, i, e) => _event(mouseOverController, d, i, e))
+      ..on('mouseout', (d, i, e) => _event(mouseOutController, d, i, e));
+
+    pie.exit.remove();
+
+    _legend.clear();
+    var items = new List.generate(data.length, (i) {
+      SvgArcData d = data.elementAt(i);
+      Iterable row = d.data;
+      return new ChartLegendItem(color: colorForData(row),
+          label: row.elementAt(dimension), series: [series],
+          value: '${(((d.endAngle - d.startAngle) * 50) / math.PI).toStringAsFixed(2)}%');
+    });
+    return _legend..addAll(area.config.isRTL ? items.reversed : items);
+  }
+
+  @override
+  void dispose() {
+    if (root == null) return;
+    root.selectAll('.pie-path').remove();
+  }
+
+  void _event(StreamController controller, data, int index, Element e) {
+     if (controller == null) return;
+     var rowStr = e.parent.dataset['row'];
+     var row = rowStr != null ? int.parse(rowStr) : null;
+     controller.add(
+         new _ChartEvent(scope.event, area, series, row, index, data.value));
+   }
+}
diff --git a/charted/lib/charts/src/cartesian_area_impl.dart b/charted/lib/charts/src/cartesian_area_impl.dart
new file mode 100644
index 0000000..154b33b
--- /dev/null
+++ b/charted/lib/charts/src/cartesian_area_impl.dart
@@ -0,0 +1,740 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+/// Displays either one or two dimension axes and zero or more measure axis.
+/// The number of measure axes displayed is zero in charts like bubble chart
+/// which contain two dimension axes.
+class _CartesianArea implements CartesianArea {
+  /// Default identifiers used by the measure axes
+  static const MEASURE_AXIS_IDS = const['_default'];
+
+  /// Orientations used by measure axes. First, when "x" axis is the primary
+  /// and the only dimension. Second, when "y" axis is the primary and the only
+  /// dimension.
+  static const MEASURE_AXIS_ORIENTATIONS = const[
+    const[ORIENTATION_LEFT, ORIENTATION_RIGHT],
+    const[ORIENTATION_BOTTOM, ORIENTATION_TOP]
+  ];
+
+  /// Orientations used by the dimension axes. First, when "x" is the
+  /// primary dimension and the last one for cases where "y" axis is primary
+  /// dimension.
+  static const DIMENSION_AXIS_ORIENTATIONS = const[
+    const[ORIENTATION_BOTTOM, ORIENTATION_LEFT],
+    const[ORIENTATION_LEFT, ORIENTATION_BOTTOM]
+  ];
+
+  /// Mapping of measure axis Id to it's axis.
+  final _measureAxes = new LinkedHashMap<String, _ChartAxis>();
+
+  /// Mapping of dimension column index to it's axis.
+  final _dimensionAxes = new LinkedHashMap<int, _ChartAxis>();
+
+  /// Disposer for all change stream subscriptions related to data.
+  final _dataEventsDisposer = new SubscriptionsDisposer();
+
+  /// Disposer for all change stream subscriptions related to config.
+  final _configEventsDisposer = new SubscriptionsDisposer();
+
+  @override
+  final Element host;
+
+  @override
+  final bool useTwoDimensionAxes;
+
+  @override
+  final bool useRowColoring;
+
+  /// Indicates whether any renderers need bands on primary dimension
+  final List<int> dimensionsUsingBands = [];
+
+  @override
+  final ChartState state;
+
+  @override
+  _ChartAreaLayout layout = new _ChartAreaLayout();
+
+  @override
+  Selection upperBehaviorPane;
+
+  @override
+  Selection lowerBehaviorPane;
+
+  @override
+  bool isReady = false;
+
+  @override
+  ChartTheme theme;
+
+  ChartData _data;
+  ChartConfig _config;
+  ObservableList<int> selectedMeasures = new ObservableList();
+  ObservableList<int> hoveredMeasures = new ObservableList();
+  int _dimensionAxesCount;
+  bool _autoUpdate = false;
+
+  SelectionScope _scope;
+  Selection _svg;
+  Selection visualization;
+
+  Iterable<ChartSeries> _series;
+
+  bool _pendingLegendUpdate = false;
+  List<ChartBehavior> _behaviors = new List<ChartBehavior>();
+  Map<ChartSeries, _ChartSeriesInfo> _seriesInfoCache = new Map();
+
+  StreamController<ChartEvent> _valueMouseOverController;
+  StreamController<ChartEvent> _valueMouseOutController;
+  StreamController<ChartEvent> _valueMouseClickController;
+  StreamController<ChartArea> _chartAxesUpdatedController;
+
+  _CartesianArea(
+      this.host,
+      ChartData data,
+      ChartConfig config,
+      bool autoUpdate,
+      this.useTwoDimensionAxes,
+      this.useRowColoring,
+      this.state) : _autoUpdate = autoUpdate {
+    assert(host != null);
+    assert(isNotInline(host));
+
+    this.data = data;
+    this.config = config;
+    theme = new QuantumChartTheme();
+
+    Transition.defaultEasingType = theme.transitionEasingType;
+    Transition.defaultEasingMode = theme.transitionEasingMode;
+    Transition.defaultDurationMilliseconds =
+        theme.transitionDurationMilliseconds;
+  }
+
+  void dispose() {
+    _configEventsDisposer.dispose();
+    _dataEventsDisposer.dispose();
+    _config.legend.dispose();
+
+    if (_valueMouseOverController != null) {
+      _valueMouseOverController.close();
+      _valueMouseOverController = null;
+    }
+    if (_valueMouseOutController != null) {
+      _valueMouseOutController.close();
+      _valueMouseOutController = null;
+    }
+    if (_valueMouseClickController != null) {
+      _valueMouseClickController.close();
+      _valueMouseClickController = null;
+    }
+    if (_chartAxesUpdatedController != null) {
+      _chartAxesUpdatedController.close();
+      _chartAxesUpdatedController = null;
+    }
+  }
+
+  static bool isNotInline(Element e) =>
+      e != null && e.getComputedStyle().display != 'inline';
+
+  /// Set new data for this chart. If [value] is [Observable], subscribes to
+  /// changes and updates the chart when data changes.
+  @override
+  set data(ChartData value) {
+    _data = value;
+    _dataEventsDisposer.dispose();
+
+    if (autoUpdate && _data != null && _data is Observable) {
+      _dataEventsDisposer.add((_data as Observable).changes.listen((_) {
+        draw();
+      }));
+    }
+  }
+
+  @override
+  ChartData get data => _data;
+
+  /// Set new config for this chart. If [value] is [Observable], subscribes to
+  /// changes and updates the chart when series or dimensions change.
+  @override
+  set config(ChartConfig value) {
+    _config = value;
+    _configEventsDisposer.dispose();
+    _pendingLegendUpdate = true;
+
+    if (_config != null && _config is Observable) {
+      _configEventsDisposer.add((_config as Observable).changes.listen((_) {
+        _pendingLegendUpdate = true;
+        draw();
+      }));
+    }
+  }
+
+  @override
+  ChartConfig get config => _config;
+
+  @override
+  set autoUpdate(bool value) {
+    if (_autoUpdate != value) {
+      _autoUpdate = value;
+      this.data = _data;
+      this.config = _config;
+    }
+  }
+
+  @override
+  bool get autoUpdate => _autoUpdate;
+
+  /// Gets measure axis from cache - creates a new instance of _ChartAxis
+  /// if one was not already created for the given [axisId].
+  _ChartAxis _getMeasureAxis(String axisId) {
+    _measureAxes.putIfAbsent(axisId, () {
+      var axisConf = config.getMeasureAxis(axisId),
+          axis = axisConf != null ?
+              new _ChartAxis.withAxisConfig(this, axisConf) :
+                  new _ChartAxis(this);
+      return axis;
+    });
+    return _measureAxes[axisId];
+  }
+
+  /// Gets a dimension axis from cache - creates a new instance of _ChartAxis
+  /// if one was not already created for the given dimension [column].
+  _ChartAxis _getDimensionAxis(int column) {
+    _dimensionAxes.putIfAbsent(column, () {
+      var axisConf = config.getDimensionAxis(column),
+          axis = axisConf != null ?
+              new _ChartAxis.withAxisConfig(this, axisConf) :
+                  new _ChartAxis(this);
+      return axis;
+    });
+    return _dimensionAxes[column];
+  }
+
+  /// All columns rendered by a series must be of the same type.
+  bool _isSeriesValid(ChartSeries s) {
+    var first = data.columns.elementAt(s.measures.first).type;
+    return s.measures.every((i) =>
+        (i < data.columns.length) && data.columns.elementAt(i).type == first);
+  }
+
+  @override
+  Iterable<Scale> get dimensionScales =>
+      config.dimensions.map((int column) => _getDimensionAxis(column).scale);
+
+  @override
+  Iterable<Scale> measureScales(ChartSeries series) {
+    var axisIds = isNullOrEmpty(series.measureAxisIds)
+        ? MEASURE_AXIS_IDS
+        : series.measureAxisIds;
+    return axisIds.map((String id) => _getMeasureAxis(id).scale);
+  }
+
+  /// Computes the size of chart and if changed from the previous time
+  /// size was computed, sets attributes on svg element
+  Rect _computeChartSize() {
+    int width = host.clientWidth,
+        height = host.clientHeight;
+
+    if (config.minimumSize != null) {
+      width = max([width, config.minimumSize.width]);
+      height = max([height, config.minimumSize.height]);
+    }
+
+    AbsoluteRect padding = theme.padding;
+    num paddingLeft = config.isRTL ? padding.end : padding.start;
+    Rect current = new Rect(paddingLeft, padding.top,
+        width - (padding.start + padding.end),
+        height - (padding.top + padding.bottom));
+    if (layout.chartArea == null || layout.chartArea != current) {
+      _svg.attr('width', width.toString());
+      _svg.attr('height', height.toString());
+      layout.chartArea = current;
+
+      var transform = 'translate(${paddingLeft},${padding.top})';
+      visualization.first.attributes['transform'] = transform;
+      lowerBehaviorPane.first.attributes['transform'] = transform;
+      upperBehaviorPane.first.attributes['transform'] = transform;
+    }
+    return layout.chartArea;
+  }
+
+  @override
+  draw({bool preRender:false, Future schedulePostRender}) {
+    assert(data != null && config != null);
+    assert(config.series != null && config.series.isNotEmpty);
+
+    // One time initialization.
+    // Each [ChartArea] has it's own [SelectionScope]
+    if (_scope == null) {
+      _scope = new SelectionScope.element(host);
+      _svg = _scope.append('svg:svg')..classed('chart-canvas');
+      if (!isNullOrEmpty(theme.filters)) {
+        var element = _svg.first,
+            defs = Namespace.createChildElement('defs', element)
+              ..append(new SvgElement.svg(
+                  theme.filters, treeSanitizer: new NullTreeSanitizer()));
+        _svg.first.append(defs);
+      }
+      lowerBehaviorPane = _svg.append('g')..classed('lower-render-pane');
+      visualization = _svg.append('g')..classed('chart-render-pane');
+      upperBehaviorPane = _svg.append('g')..classed('upper-render-pane');
+
+      if (_behaviors.isNotEmpty) {
+        _behaviors.forEach(
+            (b) => b.init(this, upperBehaviorPane, lowerBehaviorPane));
+      }
+    }
+
+    // Compute chart sizes and filter out unsupported series
+    var size = _computeChartSize(),
+        series = config.series.where((s) =>
+            _isSeriesValid(s) && s.renderer.prepare(this, s)),
+        selection = visualization.selectAll('.series-group').
+            data(series, (x) => x.hashCode),
+        axesDomainCompleter = new Completer();
+
+    // Wait till the axes are rendered before rendering series.
+    // In an SVG, z-index is based on the order of nodes in the DOM.
+    axesDomainCompleter.future.then((_) {
+      selection.enter.append('svg:g')..classed('series-group');
+      String transform =
+          'translate(${layout.renderArea.x},${layout.renderArea.y})';
+
+      selection.each((ChartSeries s, _, Element group) {
+        _ChartSeriesInfo info = _seriesInfoCache[s];
+        if (info == null) {
+          info = _seriesInfoCache[s] = new _ChartSeriesInfo(this, s);
+        }
+        info.check();
+        group.attributes['transform'] = transform;
+        (s.renderer as CartesianRenderer)
+            .draw(group, schedulePostRender:schedulePostRender);
+      });
+
+      // A series that was rendered earlier isn't there anymore, remove it
+      selection.exit
+        ..each((ChartSeries s, _, __) {
+          var info = _seriesInfoCache.remove(s);
+          if (info != null) {
+            info.dispose();
+          }
+        })
+        ..remove();
+
+      // Notify on the stream that the chart has been updated.
+      isReady = true;
+      if (_chartAxesUpdatedController != null) {
+        _chartAxesUpdatedController.add(this);
+      }
+    });
+
+    // Save the list of valid series and initialize axes.
+    _series = series;
+    _initAxes(preRender: preRender);
+
+    // Render the chart, now that the axes layer is already in DOM.
+    axesDomainCompleter.complete();
+
+    // Updates the legend if required.
+    _updateLegend();
+  }
+
+  String _orientRTL(String orientation) => orientation;
+  Scale _scaleRTL(Scale scale) => scale;
+
+  /// Initialize the axes - required even if the axes are not being displayed.
+  _initAxes({bool preRender: false}) {
+    Map measureAxisUsers = <String,Iterable<ChartSeries>>{};
+
+    // Create necessary measures axes.
+    // If measure axes were not configured on the series, default is used.
+    _series.forEach((ChartSeries s) {
+      var measureAxisIds = isNullOrEmpty(s.measureAxisIds)
+          ? MEASURE_AXIS_IDS
+          : s.measureAxisIds;
+      measureAxisIds.forEach((axisId) {
+        var axis = _getMeasureAxis(axisId),  // Creates axis if required
+            users = measureAxisUsers[axisId];
+        if (users == null) {
+          measureAxisUsers[axisId] = [s];
+        } else {
+          users.add(s);
+        }
+      });
+    });
+
+    // Now that we know a list of series using each measure axis, configure
+    // the input domain of each axis.
+    measureAxisUsers.forEach((id, listOfSeries) {
+      var sampleCol = listOfSeries.first.measures.first,
+          sampleColSpec = data.columns.elementAt(sampleCol),
+          axis = _getMeasureAxis(id),
+          domain;
+
+      if (sampleColSpec.useOrdinalScale) {
+        throw new UnsupportedError(
+            'Ordinal measure axes are not currently supported.');
+      } else {
+        // Extent is available because [ChartRenderer.prepare] was already
+        // called (when checking for valid series in [draw].
+        Iterable extents = listOfSeries.map((s) => s.renderer.extent).toList();
+        var lowest = min(extents.map((e) => e.min)),
+            highest = max(extents.map((e) => e.max));
+
+        // Use default domain if lowest and highest are the same, right now
+        // lowest is always 0, change to lowest when we make use of it.
+        // TODO(prsd): Allow negative values and non-zero lower values.
+        domain = (highest != 0) ? [0, highest] : [0, 1];
+      }
+      axis.initAxisDomain(sampleCol, false, domain);
+    });
+
+    // Configure dimension axes.
+    int dimensionAxesCount = useTwoDimensionAxes ? 2 : 1;
+    config.dimensions.take(dimensionAxesCount).forEach((int column) {
+       var axis = _getDimensionAxis(column),
+           sampleColumnSpec = data.columns.elementAt(column),
+           values = data.rows.map((row) => row.elementAt(column)),
+           domain;
+
+       if (sampleColumnSpec.useOrdinalScale) {
+         domain = values.map((e) => e.toString()).toList();
+       } else {
+         var extent = new Extent.items(values);
+         domain = [extent.min, extent.max];
+       }
+       axis.initAxisDomain(column, true, domain);
+    });
+
+    // See if any dimensions need "band" on the axis.
+    dimensionsUsingBands.clear();
+    List<bool> usingBands = [false, false];
+    _series.forEach((ChartSeries s) =>
+        (s.renderer as CartesianRenderer).dimensionsUsingBand.forEach((x) {
+      if (x <= 1 && !(usingBands[x])) {
+        usingBands[x] = true;
+        dimensionsUsingBands.add(config.dimensions.elementAt(x));
+      }
+    }));
+
+    // List of measure and dimension axes that are displayed
+    assert(
+        isNullOrEmpty(config.displayedMeasureAxes) ||
+        config.displayedMeasureAxes.length < 2);
+    var measureAxesCount = dimensionAxesCount == 1 ? 2 : 0,
+        displayedMeasureAxes = (isNullOrEmpty(config.displayedMeasureAxes)
+            ? _measureAxes.keys.take(measureAxesCount)
+            : config.displayedMeasureAxes.take(measureAxesCount)).
+                toList(growable: false),
+        displayedDimensionAxes =
+            config.dimensions.take(dimensionAxesCount).toList(growable: false);
+
+    // Compute size of the dimension axes
+    if (config.renderDimensionAxes != false) {
+      var dimensionAxisOrientations = config.isLeftAxisPrimary
+          ? DIMENSION_AXIS_ORIENTATIONS.last
+          : DIMENSION_AXIS_ORIENTATIONS.first;
+      for (int i = 0, len = displayedDimensionAxes.length; i < len; ++i) {
+        var axis = _dimensionAxes[displayedDimensionAxes[i]],
+            orientation = _orientRTL(dimensionAxisOrientations[i]);
+        axis.prepareToDraw(orientation, theme.dimensionAxisTheme);
+        layout._axes[orientation] = axis.size;
+      }
+    }
+
+    // Compute size of the measure axes
+    if (displayedMeasureAxes.isNotEmpty) {
+      var measureAxisOrientations = config.isLeftAxisPrimary
+          ? MEASURE_AXIS_ORIENTATIONS.last
+          : MEASURE_AXIS_ORIENTATIONS.first;
+      displayedMeasureAxes.asMap().forEach((int index, String key) {
+        var axis = _measureAxes[key],
+            orientation = _orientRTL(measureAxisOrientations[index]);
+        axis.prepareToDraw(orientation, theme.measureAxisTheme);
+        layout._axes[orientation] = axis.size;
+      });
+    }
+
+    // Consolidate all the information that we collected into final layout
+    _computeLayout(
+        displayedMeasureAxes.isEmpty && config.renderDimensionAxes == false);
+
+    // Domains for all axes have been taken care of and _ChartAxis ensures
+    // that the scale is initialized on visible axes. Initialize the scale on
+    // all invisible measure scales.
+    if (_measureAxes.length != displayedMeasureAxes.length) {
+      _measureAxes.keys.forEach((String axisId) {
+        if (displayedMeasureAxes.contains(axisId)) return;
+        _getMeasureAxis(axisId).initAxisScale(
+            [layout.renderArea.height, 0], theme.measureAxisTheme);
+      });
+    }
+
+    // Draw the visible measure axes, if any.
+    if (displayedMeasureAxes.isNotEmpty) {
+      var axisGroups = visualization.
+          selectAll('.measure-axis-group').data(displayedMeasureAxes);
+      // Update measure axis (add/remove/update)
+      axisGroups.enter.append('svg:g');
+      axisGroups.each((axisId, index, group) {
+        _getMeasureAxis(axisId).draw(group, _scope, preRender: preRender);
+        group.classes.clear();
+        group.classes.addAll(['measure-axis-group','measure-${index}']);
+      });
+      axisGroups.exit.remove();
+    }
+
+    // Draw the dimension axes, unless asked not to.
+    if (config.renderDimensionAxes != false) {
+      var dimAxisGroups = visualization.
+          selectAll('.dimension-axis-group').data(displayedDimensionAxes);
+      // Update dimension axes (add/remove/update)
+      dimAxisGroups.enter.append('svg:g');
+      dimAxisGroups.each((column, index, group) {
+        _getDimensionAxis(column).draw(group, _scope, preRender: preRender);
+        group.classes.clear();
+        group.classes.addAll(['dimension-axis-group', 'dim-${index}']);
+      });
+      dimAxisGroups.exit.remove();
+    } else {
+      // Initialize scale on invisible axis
+      var dimensionAxisOrientations = config.isLeftAxisPrimary ?
+          DIMENSION_AXIS_ORIENTATIONS.last : DIMENSION_AXIS_ORIENTATIONS.first;
+      for (int i = 0; i < dimensionAxesCount; ++i) {
+        var column = config.dimensions.elementAt(i),
+            axis = _dimensionAxes[column],
+            orientation = dimensionAxisOrientations[i];
+        axis.initAxisScale(orientation == ORIENTATION_LEFT ?
+            [layout.renderArea.height, 0] : [0, layout.renderArea.width],
+            theme.dimensionAxisTheme);
+      };
+    }
+  }
+
+  // Compute chart render area size and positions of all elements
+  _computeLayout(bool notRenderingAxes) {
+    if (notRenderingAxes) {
+      layout.renderArea =
+          new Rect(0, 0, layout.chartArea.height, layout.chartArea.width);
+      return;
+    }
+
+    var top = layout.axes[ORIENTATION_TOP],
+        left = layout.axes[ORIENTATION_LEFT],
+        bottom = layout.axes[ORIENTATION_BOTTOM],
+        right = layout.axes[ORIENTATION_RIGHT];
+
+    var renderAreaHeight = layout.chartArea.height -
+            (top.height + layout.axes[ORIENTATION_BOTTOM].height),
+        renderAreaWidth = layout.chartArea.width -
+            (left.width + layout.axes[ORIENTATION_RIGHT].width);
+
+    layout.renderArea = new Rect(
+        left.width, top.height, renderAreaWidth, renderAreaHeight);
+
+    layout._axes
+      ..[ORIENTATION_TOP] =
+          new Rect(left.width, 0, renderAreaWidth, top.height)
+      ..[ORIENTATION_RIGHT] =
+          new Rect(left.width + renderAreaWidth, top.y,
+              right.width, renderAreaHeight)
+      ..[ORIENTATION_BOTTOM] =
+          new Rect(left.width, top.height + renderAreaHeight,
+              renderAreaWidth, bottom.height)
+      ..[ORIENTATION_LEFT] =
+          new Rect(
+              left.width, top.height, left.width, renderAreaHeight);
+  }
+
+  // Updates the legend, if configuration changed since the last
+  // time the legend was updated.
+  _updateLegend() {
+    if (!_pendingLegendUpdate) return;
+    if (_config == null || _config.legend == null || _series.isEmpty) return;
+
+    var legend = <ChartLegendItem>[];
+    List seriesByColumn =
+        new List.generate(data.columns.length, (_) => new List());
+
+    _series.forEach((s) =>
+        s.measures.forEach((m) => seriesByColumn[m].add(s)));
+
+    seriesByColumn.asMap().forEach((int i, List s) {
+      if (s.length == 0) return;
+      legend.add(new ChartLegendItem(
+          index:i, label:data.columns.elementAt(i).label, series:s,
+          color:theme.getColorForKey(i)));
+    });
+
+    _config.legend.update(legend, this);
+    _pendingLegendUpdate = false;
+  }
+
+  @override
+  Stream<ChartEvent> get onMouseUp =>
+      host.onMouseUp
+          .map((MouseEvent e) => new _ChartEvent(e, this));
+
+  @override
+  Stream<ChartEvent> get onMouseDown =>
+      host.onMouseDown
+          .map((MouseEvent e) => new _ChartEvent(e, this));
+
+  @override
+  Stream<ChartEvent> get onMouseOver =>
+      host.onMouseOver
+          .map((MouseEvent e) => new _ChartEvent(e, this));
+
+  @override
+  Stream<ChartEvent> get onMouseOut =>
+      host.onMouseOut
+          .map((MouseEvent e) => new _ChartEvent(e, this));
+
+  @override
+  Stream<ChartEvent> get onMouseMove =>
+      host.onMouseMove
+          .map((MouseEvent e) => new _ChartEvent(e, this));
+
+  @override
+  Stream<ChartEvent> get onValueClick {
+    if (_valueMouseClickController == null) {
+      _valueMouseClickController = new StreamController.broadcast(sync: true);
+    }
+    return _valueMouseClickController.stream;
+  }
+
+  @override
+  Stream<ChartEvent> get onValueMouseOver {
+    if (_valueMouseOverController == null) {
+      _valueMouseOverController = new StreamController.broadcast(sync: true);
+    }
+    return _valueMouseOverController.stream;
+  }
+
+  @override
+  Stream<ChartEvent> get onValueMouseOut {
+    if (_valueMouseOutController == null) {
+      _valueMouseOutController = new StreamController.broadcast(sync: true);
+    }
+    return _valueMouseOutController.stream;
+  }
+
+  @override
+  Stream<ChartArea> get onChartAxesUpdated {
+    if (_chartAxesUpdatedController == null) {
+      _chartAxesUpdatedController = new StreamController.broadcast(sync: true);
+    }
+    return _chartAxesUpdatedController.stream;
+  }
+
+  @override
+  void addChartBehavior(ChartBehavior behavior) {
+    if (behavior == null || _behaviors.contains(behavior)) return;
+    _behaviors.add(behavior);
+    if (upperBehaviorPane != null && lowerBehaviorPane != null) {
+      behavior.init(this, upperBehaviorPane, lowerBehaviorPane);
+    }
+  }
+
+  @override
+  void removeChartBehavior(ChartBehavior behavior) {
+    if (behavior == null || !_behaviors.contains(behavior)) return;
+    if (upperBehaviorPane != null && lowerBehaviorPane != null) {
+      behavior.dispose();
+    }
+    _behaviors.remove(behavior);
+  }
+}
+
+class _ChartAreaLayout implements ChartAreaLayout {
+  final _axes = <String, Rect>{
+      ORIENTATION_LEFT: const Rect(),
+      ORIENTATION_RIGHT: const Rect(),
+      ORIENTATION_TOP: const Rect(),
+      ORIENTATION_BOTTOM: const Rect()
+    };
+
+  UnmodifiableMapView<String, Rect> _axesView;
+
+  @override
+  get axes => _axesView;
+
+  @override
+  Rect renderArea;
+
+  @override
+  Rect chartArea;
+
+  _ChartAreaLayout() {
+    _axesView = new UnmodifiableMapView(_axes);
+  }
+}
+
+class _ChartSeriesInfo {
+  CartesianRenderer _renderer;
+  SubscriptionsDisposer _disposer = new SubscriptionsDisposer();
+
+  _ChartSeries _series;
+  _CartesianArea _area;
+  _ChartSeriesInfo(this._area, this._series);
+
+  _click(ChartEvent e) {
+    var state = _area.state;
+    if (state != null) {
+      if (state.isHighlighted(e.column, e.row)) {
+        state.unhighlight(e.column, e.row);
+      } else {
+        state.highlight(e.column, e.row);
+      }
+    }
+    if (_area._valueMouseClickController != null) {
+      _area._valueMouseClickController.add(e);
+    }
+  }
+
+  _mouseOver(ChartEvent e) {
+    var state = _area.state;
+    if (state != null) {
+      state.hovered = new Pair(e.column, e.row);
+    }
+    if (_area._valueMouseOverController != null) {
+      _area._valueMouseOverController.add(e);
+    }
+  }
+
+  _mouseOut(ChartEvent e) {
+    var state = _area.state;
+    if (state != null) {
+      var current = state.hovered;
+      if (current != null &&
+          current.first == e.column && current.last == e.row) {
+        state.hovered = null;
+      }
+    }
+    if (_area._valueMouseOutController != null) {
+      _area._valueMouseOutController.add(e);
+    }
+  }
+
+  check() {
+    if (_renderer != _series.renderer) {
+      dispose();
+      try {
+        _disposer.addAll([
+          _series.renderer.onValueClick.listen(_click),
+          _series.renderer.onValueMouseOver.listen(_mouseOver),
+          _series.renderer.onValueMouseOut.listen(_mouseOut)
+        ]);
+      } on UnimplementedError {};
+    }
+    _renderer = _series.renderer;
+  }
+
+  dispose() => _disposer.dispose();
+}
diff --git a/charted/lib/charts/src/chart_axis_impl.dart b/charted/lib/charts/src/chart_axis_impl.dart
new file mode 100644
index 0000000..ca159ec
--- /dev/null
+++ b/charted/lib/charts/src/chart_axis_impl.dart
@@ -0,0 +1,201 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+class _ChartAxis {
+  static const List _VERTICAL_ORIENTATIONS =
+      const [ ORIENTATION_LEFT, ORIENTATION_RIGHT ];
+
+  CartesianArea _area;
+  ChartAxisConfig _config;
+  ChartAxisTheme _theme;
+  SvgAxisTicks _axisTicksPlacement;
+
+  int _column;
+  bool _isDimension;
+  ChartColumnSpec _columnSpec;
+
+  bool _isVertical;
+  String _orientation;
+  Scale _scale;
+  SelectionScope _scope;
+
+  MutableRect size;
+
+  _ChartAxis.withAxisConfig(this._area, this._config);
+  _ChartAxis(this._area);
+
+  void initAxisDomain(int column, bool isDimension, Iterable domain) {
+    _columnSpec = _area.data.columns.elementAt(column);
+    _column = column;
+    _isDimension = isDimension;
+
+    // If we don't have a scale yet, create one.
+    if (scale == null) {
+      _scale = _columnSpec.createDefaultScale();
+    }
+
+    // Sets the domain if not using a custom scale.
+    if (_config == null || (_config != null && _config.scale == null)) {
+      scale.domain = domain;
+      scale.nice = !_isDimension;
+    }
+  }
+
+  void initAxisScale(Iterable range, ChartAxisTheme theme) {
+    assert(scale != null);
+    if (scale is OrdinalScale) {
+      var usingBands = _area.dimensionsUsingBands.contains(_column),
+          innerPadding = usingBands ? theme.axisBandInnerPadding : 1.0,
+          outerPadding = usingBands ?
+              theme.axisBandOuterPadding : theme.axisOuterPadding;
+
+      // This is because when left axis is primary the first data row should
+      // appear on top of the y-axis instead of on bottom.
+      if (_area.config.isLeftAxisPrimary) {
+        range = range.toList().reversed;
+      }
+      (scale as OrdinalScale).
+          rangeRoundBands(range, innerPadding, outerPadding);
+    } else {
+      scale.range = range;
+    }
+  }
+
+  void prepareToDraw(String orientation, ChartAxisTheme theme) {
+    if (orientation == null) orientation = ORIENTATION_BOTTOM;
+    _theme = theme;
+    _orientation = orientation;
+    _isVertical =
+        _orientation == ORIENTATION_LEFT || _orientation == ORIENTATION_RIGHT;
+
+    var layout = _area.layout.chartArea;
+    size = _isVertical
+        ? new MutableRect.size(_theme.verticalAxisWidth, layout.width)
+        : new MutableRect.size(layout.height, _theme.horizontalAxisHeight);
+
+    // Handle auto re-sizing of horizontal axis.
+    if (_isVertical) {
+      var ticks = (_config != null && !isNullOrEmpty(_config.tickValues))
+              ? _config.tickValues
+              : scale.ticks,
+          formatter = _columnSpec.formatter == null
+              ? scale.createTickFormatter()
+              : _columnSpec.formatter,
+          textMetrics = new TextMetrics(fontStyle: theme.ticksFont),
+          formattedTicks = ticks.map((x) => formatter(x)).toList(),
+          shortenedTicks = formattedTicks;
+
+      var width = textMetrics.getLongestTextWidth(formattedTicks).ceil();
+      if (width > theme.verticalAxisWidth) {
+        width = theme.verticalAxisWidth;
+        shortenedTicks = formattedTicks.map(
+            (x) => textMetrics.ellipsizeText(x, width.toDouble())).toList();
+      }
+      if (theme.verticalAxisAutoResize) {
+        size.width =
+            width + _theme.axisTickPadding + math.max(_theme.axisTickSize, 0);
+      }
+      _axisTicksPlacement =
+          new PrecomputedAxisTicks(ticks, formattedTicks, shortenedTicks);
+    }
+  }
+
+  void draw(Element element, SelectionScope scope, {bool preRender: false}) {
+    assert(element != null && element is GElement);
+    assert(scale != null);
+
+    var rect = _area.layout.axes[_orientation],
+        renderAreaRect = _area.layout.renderArea,
+        range =  _isVertical ? [rect.height, 0] : [0, rect.width],
+        className = (_isVertical ? 'vertical-axis': 'horizontal-axis'),
+        innerTickSize = _theme.axisTickSize <= ChartAxisTheme.FILL_RENDER_AREA
+            ? 0 - (_isVertical ? renderAreaRect.width : renderAreaRect.height)
+            : _theme.axisTickSize,
+        tickValues = _config != null && !isNullOrEmpty(_config.tickValues)
+            ? _config.tickValues
+            : null;
+
+    element.attributes['transform'] = 'translate(${rect.x}, ${rect.y})';
+
+    if (!_isVertical) {
+      _axisTicksPlacement =
+          new RotateHorizontalAxisTicks(rect, _config, _theme);
+    }
+
+    initAxisScale(range, _theme);
+    var axis = new SvgAxis(orientation: _orientation,
+        innerTickSize: innerTickSize, outerTickSize: 0,
+        tickPadding: _theme.axisTickPadding,
+        tickFormat: _columnSpec.formatter, tickValues: tickValues,
+        scale: scale);
+
+    axis.create(element, scope,
+        axisTicksBuilder: _axisTicksPlacement, isRTL: _area.config.isRTL);
+  }
+
+  void clear() {
+  }
+
+  // Scale passed through configuration takes precedence
+  Scale get scale =>
+      (_config != null && _config.scale != null) ? _config.scale : _scale;
+
+  set scale(Scale value) => _scale = value;
+}
+
+class PrecomputedAxisTicks implements SvgAxisTicks {
+  final int rotation = 0;
+  final Iterable ticks;
+  final Iterable formattedTicks;
+  final Iterable shortenedTicks;
+  const PrecomputedAxisTicks(
+      this.ticks, this.formattedTicks, this.shortenedTicks);
+  void init(SvgAxis axis) {}
+}
+
+class RotateHorizontalAxisTicks implements SvgAxisTicks {
+  final Rect rect;
+  final ChartAxisConfig config;
+  final ChartAxisTheme theme;
+
+  int rotation = 0;
+  Iterable ticks;
+  Iterable formattedTicks;
+  Iterable shortenedTicks;
+
+  RotateHorizontalAxisTicks(this.rect, this.config, this.theme);
+
+  void init(SvgAxis axis) {
+    assert(
+        axis.orientation == ORIENTATION_BOTTOM ||
+        axis.orientation == ORIENTATION_TOP);
+    assert(theme.ticksFont != null);
+    ticks = axis.tickValues;
+    formattedTicks = ticks.map((x) => axis.tickFormat(x)).toList();
+    shortenedTicks = formattedTicks;
+
+    var range = axis.scale.rangeExtent,
+        textMetrics = new TextMetrics(fontStyle: theme.ticksFont),
+        allowedWidth = (range.max - range.min) ~/ ticks.length,
+        maxLabelWidth = textMetrics.getLongestTextWidth(formattedTicks);
+
+    // Check if we need rotation
+    if (0.90 * allowedWidth < maxLabelWidth) {
+      rotation = 45;
+
+      // Check if we have enough space to render full chart
+      allowedWidth = (1.4142 * rect.height) - (textMetrics.fontSize / 1.4142);
+      if (maxLabelWidth > allowedWidth) {
+        shortenedTicks = formattedTicks.map(
+            (x) => textMetrics.ellipsizeText(x, allowedWidth)).toList();
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/charted/lib/charts/src/chart_config_impl.dart b/charted/lib/charts/src/chart_config_impl.dart
new file mode 100644
index 0000000..8ef9312
--- /dev/null
+++ b/charted/lib/charts/src/chart_config_impl.dart
@@ -0,0 +1,131 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+class _ChartConfig extends ChangeNotifier implements ChartConfig {
+  final Map<String,ChartAxisConfig> _measureAxisRegistry = {};
+  final Map<int,ChartAxisConfig> _dimensionAxisRegistry = {};
+  final SubscriptionsDisposer _disposer = new SubscriptionsDisposer();
+
+  bool _isRTL = false;
+  Iterable<ChartSeries> _series;
+  Iterable<int> _dimensions;
+  StreamSubscription _dimensionsSubscription;
+
+  @override
+  Rect minimumSize = const Rect.size(400, 300);
+
+  @override
+  bool isLeftAxisPrimary = false;
+
+  @override
+  bool autoResizeAxis = true;
+
+  @override
+  ChartLegend legend;
+
+  @override
+  Iterable<String> displayedMeasureAxes;
+
+  @override
+  bool renderDimensionAxes = true;
+
+  @override
+  bool switchAxesForRTL = true;
+
+  _ChartConfig(Iterable<ChartSeries> series, Iterable<int> dimensions) {
+    this.series = series;
+    this.dimensions = dimensions;
+  }
+
+  @override
+  set series(Iterable<ChartSeries> values) {
+    assert(values != null && values.isNotEmpty);
+
+    _disposer.dispose();
+    _series = values;
+    notifyChange(const ChartConfigChangeRecord());
+
+    // Monitor each series for changes on them
+    values.forEach((item) => _disposer.add(item.changes.listen(
+        (_) => notifyChange(const ChartConfigChangeRecord())), item));
+
+    // Monitor series for changes.  When the list changes, update
+    // subscriptions to ChartSeries changes.
+    if (_series is ObservableList) {
+      var observable = _series as ObservableList;
+      _disposer.add(observable.listChanges.listen((records) {
+        records.forEach((record) {
+          record.removed.forEach((value) => _disposer.unsubscribe(value));
+          for (int i = 0; i < record.addedCount; i++) {
+            var added = observable[i + record.index];
+            _disposer.add(added.changes.listen(
+                (_) => notifyChange(const ChartConfigChangeRecord())));
+          }
+        });
+        notifyChange(const ChartConfigChangeRecord());
+      }));
+    }
+  }
+
+  @override
+  Iterable<ChartSeries> get series => _series;
+
+  @override
+  set dimensions(Iterable<int> values) {
+    _dimensions = values;
+
+    if (_dimensionsSubscription != null) {
+      _dimensionsSubscription.cancel();
+      _dimensionsSubscription = null;
+    }
+
+    if (values == null || values.isEmpty) return;
+
+    if (_dimensions is ObservableList) {
+      _dimensionsSubscription =
+          (_dimensions as ObservableList).listChanges.listen(
+              (_) => notifyChange(const ChartConfigChangeRecord()));
+    }
+  }
+
+  @override
+  Iterable<int> get dimensions => _dimensions;
+
+  @override
+  void registerMeasureAxis(String id, ChartAxisConfig config) {
+    assert(config != null);
+    _measureAxisRegistry[id] = config;
+  }
+
+  @override
+  ChartAxisConfig getMeasureAxis(String id) => _measureAxisRegistry[id];
+
+  @override
+  void registerDimensionAxis(int column, ChartAxisConfig config) {
+    assert(config != null);
+    assert(dimensions.contains(column));
+    _dimensionAxisRegistry[column] = config;
+  }
+
+  @override
+  ChartAxisConfig getDimensionAxis(int column) =>
+      _dimensionAxisRegistry[column];
+
+  @override
+  set isRTL(bool value) {
+    if (_isRTL != value && value != null) {
+      _isRTL = value;
+      notifyChange(const ChartConfigChangeRecord());
+    }
+  }
+
+  @override
+  bool get isRTL => _isRTL;
+}
diff --git a/charted/lib/charts/src/chart_data_impl.dart b/charted/lib/charts/src/chart_data_impl.dart
new file mode 100644
index 0000000..561c105
--- /dev/null
+++ b/charted/lib/charts/src/chart_data_impl.dart
@@ -0,0 +1,136 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+class _ChartData extends ChangeNotifier implements ChartData {
+  Iterable<ChartColumnSpec> _columns;
+  Iterable<Iterable> _rows;
+
+  bool _hasObservableRows = false;
+  SubscriptionsDisposer _disposer = new SubscriptionsDisposer();
+
+  _ChartData(Iterable<ChartColumnSpec> columns, Iterable<Iterable> rows) {
+    this.columns = columns;
+    this.rows = rows;
+  }
+
+  set columns(Iterable<ChartColumnSpec> value) {
+    assert(value != null);
+
+    // Create a copy of columns.  We do not currently support
+    // changes to the list of columns.  Any changes to the spec
+    // will be applied at the next ChartBase.draw();
+    this._columns = new List<ChartColumnSpec>.from(value);
+  }
+
+  Iterable<ChartColumnSpec> get columns => _columns;
+
+  set rows(Iterable<Iterable> value) {
+    assert(value != null);
+
+    _rows = value;
+    if (_rows is ObservableList) {
+      _disposer.add(
+          (_rows as ObservableList).listChanges.listen(rowsChanged));
+    }
+
+    if (_rows.every((row) => row is ObservableList)) {
+      _hasObservableRows = true;
+      for (int i = 0; i < _rows.length; i++) {
+        var row = _rows.elementAt(i);
+        _disposer.add(row.listChanges.listen((changes)
+            => _valuesChanged(i, changes)), row);
+      };
+    } else if (_rows is Observable) {
+      logger.info('List of rows is Observable, but not rows themselves!');
+    }
+  }
+
+  Iterable<Iterable> get rows => _rows;
+
+  rowsChanged(List<ListChangeRecord> changes) {
+    if (_rows is! ObservableList) return;
+    notifyChange(new ChartRowChangeRecord(changes));
+
+    if (!_hasObservableRows) return;
+    changes.forEach((ListChangeRecord change) {
+      change.removed.forEach((item) => _disposer.unsubscribe(item));
+
+      for(int i = 0; i < change.addedCount; i++) {
+        var index = change.index + i,
+            row = _rows.elementAt(index);
+
+        if (row is! ObservableList) {
+          logger.severe('A non-observable row was added! '
+              'Changes on this row will not be monitored');
+        } else {
+          _disposer.add(row.listChanges.listen((changes)
+              => _valuesChanged(index, changes)), row);
+        }
+      }
+    });
+  }
+
+  _valuesChanged(int index, List<ListChangeRecord> changes) {
+    if (!_hasObservableRows) return;
+    notifyChange(new ChartValueChangeRecord(index, changes));
+  }
+
+  @override
+  String toString() {
+    var cellDataLength = new List.filled(rows.elementAt(0).length, 0);
+    for (var i = 0; i < columns.length; i++) {
+      if (cellDataLength[i] < columns.elementAt(i).label.toString().length) {
+        cellDataLength[i] = columns.elementAt(i).label.toString().length;
+      }
+    }
+    for (var row in rows) {
+      for (var i = 0; i < row.length; i++) {
+        if (cellDataLength[i] < row.elementAt(i).toString().length) {
+          cellDataLength[i] = row.elementAt(i).toString().length;
+        }
+      }
+    }
+
+    var totalLength = 1;  // 1 for the leading '|'.
+    for (var length in cellDataLength) {
+      // 3 for the leading and trailing ' ' padding and trailing '|'.
+      totalLength += length + 3;
+    }
+
+    // Second pass for building the string buffer and padd each cell with space
+    // according to the difference between cell string length and max length.
+    var strBuffer = new StringBuffer();
+    strBuffer.write('-' * totalLength + '\n');
+    strBuffer.write('|');
+
+    // Process columns.
+    for (var i = 0; i < columns.length; i++) {
+      var label = columns.elementAt(i).label;
+      var lengthDiff = cellDataLength[i] - label.length;
+      strBuffer.write(' ' * lengthDiff + ' ${label} |');
+    }
+    strBuffer.write('\n' + '-' * totalLength + '\n');
+
+    // Process rows.
+    for (var row in rows) {
+      strBuffer.write('|');
+      for (var i = 0; i < row.length; i++) {
+        var data = row.elementAt(i).toString();
+        var lengthDiff = cellDataLength[i] - data.length;
+        strBuffer.write(' ' * lengthDiff + ' ${data} |');
+
+        if (i == row.length - 1) {
+          strBuffer.write('\n' + '-' * totalLength + '\n');
+        }
+      }
+    }
+    return strBuffer.toString();
+  }
+}
diff --git a/charted/lib/charts/src/chart_events_impl.dart b/charted/lib/charts/src/chart_events_impl.dart
new file mode 100644
index 0000000..3080520
--- /dev/null
+++ b/charted/lib/charts/src/chart_events_impl.dart
@@ -0,0 +1,51 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+class _ChartEvent implements ChartEvent {
+  @override
+  final ChartArea area;
+
+  @override
+  final ChartSeries series;
+
+  @override
+  final MouseEvent source;
+
+  @override
+  final int column;
+
+  @override
+  final int row;
+
+  @override
+  final num value;
+
+  @override
+  num scaledX;
+
+  @override
+  num scaledY;
+
+  @override
+  num chartX;
+
+  @override
+  num chartY;
+
+  _ChartEvent(this.source, this.area,
+      [this.series, this.row, this.column, this.value]) {
+    var hostRect = area.host.getBoundingClientRect(),
+        left = area.config.isRTL
+            ? area.theme.padding.end
+            : area.theme.padding.start;
+    chartX = source.client.x - hostRect.left - left;
+    chartY = source.client.y - hostRect.top - area.theme.padding.top;
+  }
+}
diff --git a/charted/lib/charts/src/chart_legend_impl.dart b/charted/lib/charts/src/chart_legend_impl.dart
new file mode 100644
index 0000000..5123cb1
--- /dev/null
+++ b/charted/lib/charts/src/chart_legend_impl.dart
@@ -0,0 +1,203 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+class _ChartLegend implements ChartLegend {
+  static const CLASS_PREFIX = 'chart-legend';
+
+  final Element host;
+  final int visibleItemsCount;
+  final bool showValues;
+  final SubscriptionsDisposer _disposer = new SubscriptionsDisposer();
+
+  String _title;
+  SelectionScope _scope;
+  Selection _root;
+  ChartArea _area;
+
+  Iterable<ChartLegendItem> _items;
+
+  _ChartLegend(this.host, this.visibleItemsCount, this.showValues, String title)
+      : _title = title {
+    assert(host != null);
+  }
+
+  void dispose() {
+    _disposer.dispose();
+  }
+
+  /**
+   * Sets the title of the legend, if the legend is already drawn, updates the
+   * title on the legend as well.
+   */
+  void set title(String title) {
+    _title = title;
+    if (_scope == null) return;
+    _updateTitle();
+  }
+
+  String get title => _title;
+
+  /** Updates the title of the legend. */
+  void _updateTitle() {
+    if (_title.isNotEmpty) {
+      if (_root.select('.chart-legend-heading').length == 0) {
+        _root.select('.chart-legend-heading');
+        _root.append('div')
+          ..classed('chart-legend-heading')
+          ..text(_title);
+      } else {
+        _root.select('.chart-legend-heading').text(_title);
+      }
+    }
+  }
+
+  /** Updates the legend base on a new list of ChartLegendItems. */
+  update(Iterable<ChartLegendItem> items, ChartArea area) {
+    assert(items != null);
+    assert(area == _area || _area == null);
+
+    _area = area;
+    if (_area.state != null) {
+      _disposer.add(_area.state.changes.listen(_handleStateChanges));
+    }
+    if (_scope == null) {
+      _scope = new SelectionScope.element(host);
+      _root = _scope.selectElements([host]);
+    }
+
+    _updateTitle();
+    _items = items;
+    _createLegendItems();
+
+    // Add more item label if there's more items than the max display items.
+    if ((visibleItemsCount > 0) && (visibleItemsCount < items.length)) {
+      _root.select('.chart-legend-more').remove();
+      _root.append('div')
+        ..on('mouseover',
+            (d, i, e) => _displayMoreItem(items.skip(visibleItemsCount)))
+        ..on('mouseleave', (d, i, e) => _hideMoreItem())
+        ..text('${items.length - visibleItemsCount} more...')
+        ..classed('chart-legend-more');
+    }
+  }
+
+  /** Hides extra legend items. */
+  void _hideMoreItem() {
+    var tooltip = _root.select('.chart-legend-more-tooltip');
+    tooltip.style('opacity', '0');
+  }
+
+  // Displays remaining legend items as a tooltip
+  void _displayMoreItem(Iterable<ChartLegendItem> items) {
+    var tooltip = _root.select('.chart-legend-more-tooltip');
+    if (tooltip.isEmpty) {
+      tooltip = _root.select('.chart-legend-more').append('div')
+          ..classed('chart-legend-more-tooltip');
+    }
+    tooltip.style('opacity', '1');
+
+    // _createLegendItems(tooltip, 'chart-legend-more', items);
+  }
+
+  /// Creates legend items starting at the given index.
+  void _createLegendItems() {
+    var state = _area.state,
+        rows = _root.selectAll(
+            '.chart-legend-row').data(_items, (x) => x.hashCode),
+        isFirstRender = rows.length == 0;
+
+    var enter = rows.enter.appendWithCallback((d, i, e) {
+      var row = Namespace.createChildElement('div', e),
+          color = Namespace.createChildElement('div', e)
+            ..className = 'chart-legend-color',
+          label = Namespace.createChildElement('div', e)
+            ..className = 'chart-legend-label',
+          value = showValues ? (Namespace.createChildElement('div', e)
+            ..className = 'chart-legend-value') : null;
+
+      var rowStyles = ['chart-legend-row'];
+
+      // If this is the first time we are adding rows,
+      // Update elements before adding them to the DOM.
+      if (isFirstRender) {
+        if (state != null) {
+          if (d.index == state.preview) {
+            rowStyles.add('chart-legend-hover');
+          }
+          if (state.isSelected(d.index)) {
+            rowStyles.add('chart-legend-selected');
+          }
+        }
+        rowStyles.addAll(
+            d.series.map((ChartSeries x) => 'type-${x.renderer.name}'));
+
+        color.style.setProperty('background-color', d.color);
+        row.append(color);
+        label.text = d.label;
+        row.append(label);
+
+        if (showValues) {
+          value.text = d.value;
+          value.style.setProperty('color', d.color);
+        }
+      }
+      row.classes.addAll(rowStyles);
+      return row;
+    });
+
+    // We have elements in the DOM that need updating.
+    if (!isFirstRender) {
+      rows.each((ChartLegendItem d, i, Element e) {
+        if (state != null) {
+          if (d.index == state.preview) {
+            e.classes.add('chart-legend-hover');
+          } else {
+            e.classes.remove('chart-legend-hover');
+          }
+          if (state.isSelected(d.index)) {
+            e.classes.add('chart-legend-selected');
+          } else {
+            e.classes.remove('chart-legend-selected');
+          }
+        }
+        e.classes.addAll(d.series.map((ChartSeries x) => 'type-${x.renderer.name}'));
+        (e.firstChild as Element).style.setProperty('background-color', d.color);
+        (e.children[1]).innerHtml = d.label;
+        if (showValues) {
+          (e.lastChild as Element)
+            ..innerHtml = d.value
+            ..style.setProperty('color', d.color);
+        }
+      });
+    }
+
+    if (state != null) {
+      enter
+        ..on('mouseover', (d, i, e) => state.preview = d.index)
+        ..on('mouseout', (d, i, e) {
+            if (state.preview == d.index) {
+              state.preview = null;
+            }
+          })
+        ..on('click', (d, i, e) {
+            if (state.isSelected(d.index)) {
+              state.unselect(d.index);
+            } else {
+              state.select(d.index);
+            }
+          });
+    }
+
+    rows.exit.remove();
+  }
+
+  /// Update legend to show chart's selection and visibility.
+  void _handleStateChanges(_) => _createLegendItems();
+}
diff --git a/charted/lib/charts/src/chart_series_impl.dart b/charted/lib/charts/src/chart_series_impl.dart
new file mode 100644
index 0000000..2add4c5
--- /dev/null
+++ b/charted/lib/charts/src/chart_series_impl.dart
@@ -0,0 +1,53 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+class _ChartSeries extends ChangeNotifier implements ChartSeries {
+  final String name;
+
+  Iterable<String> _measureAxisIds;
+  Iterable<int> _measures;
+  ChartRenderer _renderer;
+
+  SubscriptionsDisposer _disposer = new SubscriptionsDisposer();
+
+  _ChartSeries(this.name, Iterable<int> measures, this._renderer,
+      Iterable<String> measureAxisIds) {
+    this.measures = measures;
+    this.measureAxisIds = measureAxisIds;
+  }
+
+  set renderer(ChartRenderer value) {
+    if (value != null && value == _renderer) return;
+    _renderer.dispose();
+    _renderer = value;
+    notifyChange(new ChartSeriesChangeRecord(this));
+  }
+
+  ChartRenderer get renderer => _renderer;
+
+  set measures(Iterable<int> value) {
+    _measures = value;
+
+    if (_measures is ObservableList) {
+      _disposer.add(
+          (_measures as ObservableList).listChanges.listen(_measuresChanged));
+    }
+  }
+
+  Iterable<int> get measures => _measures;
+
+  set measureAxisIds(Iterable<String> value) => _measureAxisIds = value;
+  Iterable<String> get measureAxisIds => _measureAxisIds;
+
+  _measuresChanged(_) {
+    if (_measures is! ObservableList) return;
+    notifyChange(new ChartSeriesChangeRecord(this));
+  }
+}
diff --git a/charted/lib/charts/src/chart_state_impl.dart b/charted/lib/charts/src/chart_state_impl.dart
new file mode 100644
index 0000000..82411d9
--- /dev/null
+++ b/charted/lib/charts/src/chart_state_impl.dart
@@ -0,0 +1,121 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+///
+/// Model that maintains state of each visualization item. Each entry in the
+/// legend is considered one visualization item.
+/// - In [CartesianArea] it is always a column.
+/// - In [LayoutArea] renders choose either columns or rows.
+///
+class _ChartState extends ChangeNotifier implements ChartState {
+  final bool isMultiSelect;
+  final bool isMultiHighlight;
+  final bool isSelectOrHighlight;
+
+  LinkedHashSet<int> hidden = new LinkedHashSet<int>();
+
+  LinkedHashSet<int> selection = new LinkedHashSet<int>();
+  LinkedHashSet<Pair<int,int>> highlights = new LinkedHashSet<Pair<int,int>>();
+
+  Pair<int,int> _hovered;
+  int _preview;
+
+  _ChartState({
+    this.isMultiSelect: false,
+    this.isMultiHighlight: false,
+    this.isSelectOrHighlight: true});
+
+  set hovered(Pair<int,int> value) {
+    if (value != _hovered) {
+      _hovered = value;
+      notifyChange(new ChartHoverChangeRecord(_hovered));
+    }
+    return value;
+  }
+  Pair<int,int> get hovered => _hovered;
+
+  set preview(int value) {
+    if (value != _preview) {
+      _preview = value;
+      notifyChange(new ChartPreviewChangeRecord(_preview));
+    }
+    return value;
+  }
+  int get preview => _preview;
+
+  bool unhide(int id) {
+    if (hidden.contains(id)) {
+      hidden.remove(id);
+      notifyChange(new ChartVisibilityChangeRecord(unhide:id));
+    }
+    return true;
+  }
+
+  bool hide(int id) {
+    if (!hidden.contains(id)) {
+      hidden.add(id);
+      notifyChange(new ChartVisibilityChangeRecord(hide:id));
+    }
+    return false;
+  }
+
+  bool isVisible(int id) => !hidden.contains(id);
+
+  bool select(int id) {
+    if (!selection.contains(id)) {
+      if (!isMultiSelect) {
+        selection.clear();
+      }
+      if (isSelectOrHighlight) {
+        highlights.clear();
+      }
+      selection.add(id);
+      notifyChange(new ChartSelectionChangeRecord(add:id));
+    }
+    return true;
+  }
+
+  bool unselect(int id) {
+    if (selection.contains(id)) {
+      selection.remove(id);
+      notifyChange(new ChartSelectionChangeRecord(remove:id));
+    }
+    return false;
+  }
+
+  bool isSelected(int id) => selection.contains(id);
+
+  bool highlight(int column, int row) {
+    if (!isHighlighted(column, row)) {
+      if (!isMultiHighlight) {
+        highlights.clear();
+      }
+      if (isSelectOrHighlight) {
+        selection.clear();
+      }
+      var item = new Pair(column, row);
+      highlights.add(item);
+      notifyChange(new ChartHighlightChangeRecord(add: item));
+    }
+    return true;
+  }
+
+  bool unhighlight(int column, int row) {
+    if (isHighlighted(column, row)) {
+      var item = new Pair(column, row);
+      highlights.remove(item);
+      notifyChange(new ChartHighlightChangeRecord(remove: item));
+    }
+    return false;
+  }
+
+  bool isHighlighted(int column, int row) =>
+      highlights.any((x) => x.first == column && x.last == row);
+}
diff --git a/charted/lib/charts/src/layout_area_impl.dart b/charted/lib/charts/src/layout_area_impl.dart
new file mode 100644
index 0000000..3f86f71
--- /dev/null
+++ b/charted/lib/charts/src/layout_area_impl.dart
@@ -0,0 +1,317 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.charts;
+
+/// Creates an empty area and provides generic API for interaction with layout
+/// based charts.
+class _LayoutArea implements LayoutArea {
+  /// Disposer for all change stream subscriptions related to data.
+  final _dataEventsDisposer = new SubscriptionsDisposer();
+
+  /// Disposer for all change stream subscriptions related to config.
+  final _configEventsDisposer = new SubscriptionsDisposer();
+
+  @override
+  final Element host;
+
+  @override
+  final bool useRowColoring = true;
+
+  @override
+  _ChartAreaLayout layout = new _ChartAreaLayout();
+
+  @override
+  Selection upperBehaviorPane;
+
+  @override
+  Selection lowerBehaviorPane;
+
+  @override
+  bool isReady = false;
+
+  @override
+  ChartState state;
+
+  @override
+  ChartTheme theme;
+
+  ChartData _data;
+  ChartConfig _config;
+  ObservableList<int> selectedMeasures = new ObservableList();
+  ObservableList<int> hoveredMeasures = new ObservableList();
+  bool _autoUpdate = false;
+
+  SelectionScope _scope;
+  Selection _svg;
+  Selection visualization;
+
+  ChartSeries _series;
+  LayoutRenderer _renderer;
+
+  bool _pendingLegendUpdate = false;
+  List<ChartBehavior> _behaviors = new List<ChartBehavior>();
+
+  SubscriptionsDisposer _rendererDisposer = new SubscriptionsDisposer();
+  StreamController<ChartEvent> _valueMouseOverController;
+  StreamController<ChartEvent> _valueMouseOutController;
+  StreamController<ChartEvent> _valueMouseClickController;
+
+  _LayoutArea(
+      this.host,
+      ChartData data,
+      ChartConfig config,
+      bool autoUpdate) : _autoUpdate = autoUpdate {
+    assert(host != null);
+    assert(isNotInline(host));
+
+    this.data = data;
+    this.config = config;
+    theme = new QuantumChartTheme();
+
+    Transition.defaultEasingType = theme.transitionEasingType;
+    Transition.defaultEasingMode = theme.transitionEasingMode;
+    Transition.defaultDurationMilliseconds =
+        theme.transitionDurationMilliseconds;
+  }
+
+  void dispose() {
+    _configEventsDisposer.dispose();
+    _dataEventsDisposer.dispose();
+    _config.legend.dispose();
+  }
+
+  static bool isNotInline(Element e) =>
+      e != null && e.getComputedStyle().display != 'inline';
+
+  /// Set new data for this chart. If [value] is [Observable], subscribes to
+  /// changes and updates the chart when data changes.
+  @override
+  set data(ChartData value) {
+    _data = value;
+    _dataEventsDisposer.dispose();
+
+    if (autoUpdate && _data != null && _data is Observable) {
+      _dataEventsDisposer.add((_data as Observable).changes.listen((_) {
+        draw();
+      }));
+    }
+  }
+
+  @override
+  ChartData get data => _data;
+
+  /// Set new config for this chart. If [value] is [Observable], subscribes to
+  /// changes and updates the chart when series or dimensions change.
+  @override
+  set config(ChartConfig value) {
+    _config = value;
+    _configEventsDisposer.dispose();
+    _pendingLegendUpdate = true;
+
+    if (_config != null && _config is Observable) {
+      _configEventsDisposer.add((_config as Observable).changes.listen((_) {
+        _pendingLegendUpdate = true;
+        draw();
+      }));
+    }
+  }
+
+  @override
+  ChartConfig get config => _config;
+
+  @override
+  set autoUpdate(bool value) {
+    if (_autoUpdate != value) {
+      _autoUpdate = value;
+      this.data = _data;
+      this.config = _config;
+    }
+  }
+
+  @override
+  bool get autoUpdate => _autoUpdate;
+
+  /// Computes the size of chart and if changed from the previous time
+  /// size was computed, sets attributes on svg element
+  Rect _computeChartSize() {
+    int width = host.clientWidth,
+        height = host.clientHeight;
+
+    if (config.minimumSize != null) {
+      width = max([width, config.minimumSize.width]);
+      height = max([height, config.minimumSize.height]);
+    }
+
+    AbsoluteRect padding = theme.padding;
+    num paddingLeft = config.isRTL ? padding.end : padding.start;
+    Rect current = new Rect(paddingLeft, padding.top,
+        width - (padding.start + padding.end),
+        height - (padding.top + padding.bottom));
+    if (layout.chartArea == null || layout.chartArea != current) {
+      var transform = 'translate(${paddingLeft},${padding.top})';
+
+      visualization.first.attributes['transform'] = transform;
+      lowerBehaviorPane.first.attributes['transform'] = transform;
+      upperBehaviorPane.first.attributes['transform'] = transform;
+
+      _svg.attr('width', width.toString());
+      _svg.attr('height', height.toString());
+      layout.chartArea = current;
+      layout.renderArea = current;
+      layout._axes.clear();
+    }
+
+    return layout.chartArea;
+  }
+
+  @override
+  draw({bool preRender:false, Future schedulePostRender}) {
+    assert(data != null && config != null);
+    assert(config.series != null && config.series.isNotEmpty);
+
+    // One time initialization.
+    // Each [ChartArea] has it's own [SelectionScope]
+    if (_scope == null) {
+      _scope = new SelectionScope.element(host);
+      _svg = _scope.append('svg:svg')..classed('chart-canvas');
+
+      lowerBehaviorPane = _svg.append('g')..classed('lower-render-pane');
+      visualization = _svg.append('g')..classed('chart-render-pane');
+      upperBehaviorPane = _svg.append('g')..classed('upper-render-pane');
+
+      if (_behaviors.isNotEmpty) {
+        _behaviors.forEach(
+            (b) => b.init(this, upperBehaviorPane, lowerBehaviorPane));
+      }
+    }
+
+    // Compute chart sizes and filter out unsupported series
+    var size = _computeChartSize(),
+        series = config.series.firstWhere(
+            (s) => s.renderer.prepare(this, s), orElse: () => null),
+        group = visualization.first.querySelector('.series-group');
+
+    // We need atleast one matching series.
+    assert(series != null);
+
+    // Create a group for rendering, if it was not already done.
+    if (group == null) {
+      group = Namespace.createChildElement('g', visualization.first)
+          ..classes.add('series-group');
+      visualization.first.append(group);
+    }
+
+    // If we previously displayed a series, verify that we are
+    // still using the same renderer.  Otherwise, dispose the older one.
+    if (_renderer != null && series.renderer != _renderer) {
+      _rendererDisposer.dispose();
+    }
+
+    // Save and subscribe to events on the the current renderer.
+    _renderer = series.renderer;
+    try {
+      _rendererDisposer.addAll([
+        _renderer.onValueClick.listen((ChartEvent e) {
+          if (_valueMouseClickController != null) {
+            _valueMouseClickController.add(e);
+          }
+        }),
+        _renderer.onValueMouseOver.listen((ChartEvent e) {
+          if (_valueMouseOverController != null) {
+            _valueMouseOverController.add(e);
+          }
+        }),
+        _renderer.onValueMouseOut.listen((ChartEvent e) {
+          if (_valueMouseOutController != null) {
+            _valueMouseOutController.add(e);
+          }
+        })
+      ]);
+    } on UnimplementedError {};
+
+    Iterable<ChartLegendItem> legend =
+        _renderer.layout(group, schedulePostRender:schedulePostRender);
+
+    // Notify on the stream that the chart has been updated.
+    isReady = true;
+
+    // Save the list of valid series and initialize axes.
+    _series = series;
+
+    // Updates the legend if required.
+    _config.legend.update(legend, this);
+  }
+
+  @override
+  Stream<ChartEvent> get onMouseUp =>
+      host.onMouseUp
+          .map((MouseEvent e) => new _ChartEvent(e, this));
+
+  @override
+  Stream<ChartEvent> get onMouseDown =>
+      host.onMouseDown
+          .map((MouseEvent e) => new _ChartEvent(e, this));
+
+  @override
+  Stream<ChartEvent> get onMouseOver =>
+      host.onMouseOver
+          .map((MouseEvent e) => new _ChartEvent(e, this));
+
+  @override
+  Stream<ChartEvent> get onMouseOut =>
+      host.onMouseOut
+          .map((MouseEvent e) => new _ChartEvent(e, this));
+
+  @override
+  Stream<ChartEvent> get onMouseMove =>
+      host.onMouseMove
+          .map((MouseEvent e) => new _ChartEvent(e, this));
+
+  @override
+  Stream<ChartEvent> get onValueClick {
+    if (_valueMouseClickController == null) {
+      _valueMouseClickController = new StreamController.broadcast(sync: true);
+    }
+    return _valueMouseClickController.stream;
+  }
+
+  @override
+  Stream<ChartEvent> get onValueMouseOver {
+    if (_valueMouseOverController == null) {
+      _valueMouseOverController = new StreamController.broadcast(sync: true);
+    }
+    return _valueMouseOverController.stream;
+  }
+
+  @override
+  Stream<ChartEvent> get onValueMouseOut {
+    if (_valueMouseOutController == null) {
+      _valueMouseOutController = new StreamController.broadcast(sync: true);
+    }
+    return _valueMouseOutController.stream;
+  }
+
+  @override
+  void addChartBehavior(ChartBehavior behavior) {
+    if (behavior == null || _behaviors.contains(behavior)) return;
+    _behaviors.add(behavior);
+    if (upperBehaviorPane != null && lowerBehaviorPane != null) {
+      behavior.init(this, upperBehaviorPane, lowerBehaviorPane);
+    }
+  }
+
+  @override
+  void removeChartBehavior(ChartBehavior behavior) {
+    if (behavior == null || !_behaviors.contains(behavior)) return;
+    if (upperBehaviorPane != null && lowerBehaviorPane != null) {
+      behavior.dispose();
+    }
+    _behaviors.remove(behavior);
+  }
+}
diff --git a/charted/lib/charts/themes/quantum_theme.css b/charted/lib/charts/themes/quantum_theme.css
new file mode 100644
index 0000000..5a7cb1d
--- /dev/null
+++ b/charted/lib/charts/themes/quantum_theme.css
@@ -0,0 +1,273 @@
+
+@import '//fonts.googleapis.com/css?family=Roboto:400,700';
+
+/*
+ * Chart components layout
+ */
+
+.chart-wrapper {
+  font-family: 'Roboto';
+  display: block;
+}
+
+.chart-host-wrapper {
+  display: flex;
+  width: 100%;
+}
+
+.chart-host {
+  display: block;
+  width: 70%;
+}
+
+.chart-legend-host {
+  display: block;
+  width: 30%;
+}
+
+/*
+ * Chart title
+ */
+
+.chart-title-wrapper {
+  margin-bottom: 26px;  /* charts adds an extra 10px */
+}
+
+.chart-title {
+  font-size: 16px;
+  color: #757575;
+  margin-bottom: 8px;
+}
+
+.chart-subtitle {
+  font-size: 14px;
+  color: #BDBDBD;
+}
+
+/*
+ * Styles for the SVG chart rendering
+ */
+
+.chart-canvas {
+  font-family: 'Roboto';
+  display: block;
+}
+
+.chart-axis-label {
+  font-size: 12px;
+  color: #424242;
+}
+
+.chart-axis-label-tooltip {
+  color: white;
+  opacity: 0;
+  transition: opacity .25s;
+  background-color: black;
+  position: absolute;
+  padding: 4px;
+  border-radius: 2px;
+  z-index: 9999;
+}
+
+.chart-axis-text,
+.dimension-axis-group text, .measure-axis-group text {
+  font-size: 12px;
+  fill: #757575;
+  stroke: none;
+}
+
+/*
+ * Styles for the legend.
+ */
+
+.chart-legend-row,
+.chart-legend-more {
+  width: 100%;
+  display: flex;
+  font-size: 14px;
+  margin-bottom: 16px;
+  position: relative;
+  padding-left: 20px;
+  cursor: default;
+}
+
+.chart-legend-title {
+  color: #838383;
+}
+
+.chart-legend-subtitle {
+  color: #C1C1C1;
+}
+
+.chart-legend-selected,
+.chart-legend-hover {
+  color: #434343;
+}
+
+.chart-legend-more-row {
+  white-space: nowrap;
+  display: flex;
+  font-size: 14px;
+  margin-top: 10px;
+}
+
+.chart-legend-more-row:first-child {
+  margin-top: 0px;
+}
+
+.chart-legend-label {
+  flex-basis: 80px;
+  max-width: 120px;
+}
+
+.chart-legend-color,
+.chart-legend-more-color {
+  width: 12px;
+  height: 12px;
+  margin: auto 8px;
+  border-radius: 2px;
+}
+.type-line-rdr > .chart-legend-color {
+  height: 4px;
+}
+.type-pie-rdr > .chart-legend-color {
+  border-radius: 6px;
+}
+
+.chart-legend-more-tooltip {
+  position: absolute;
+  left: 70px;
+  padding: 10px;
+  top: 0px;
+  border-radius: 2px;
+  border: 1px solid #C1C1C1;
+  box-shadow: 0px 2px 1px rgba(0, 0, 0, 0.2);
+  z-index: 9999;
+  background-color: white;
+  font-size: 14px;
+  pointer-events: none;
+  opacity: 0;
+  transition: opacity .4s;
+}
+
+/*
+ * Axes styles
+ */
+
+.tick line, .domain {
+  fill: none;
+  stroke: #E0E0E0;
+  stroke-width: 1px;
+  shape-rendering: crispEdges;
+}
+
+.measure-axis-group .domain {
+  stroke-width: 0;
+}
+
+.dimension-axis-group .domain {
+  stroke: #9E9E9E;
+}
+
+/*
+ * Axis markers
+ */
+
+.axis-marker {
+  stroke: #E0E0E0;
+  stroke-dasharray: 3 3;
+}
+
+
+/*
+ * Styles for LineChartRenderer
+ */
+
+.line-rdr-line {
+  stroke-width: 2px;
+  transition: stroke-width .4s;
+  stroke-linecap: round;
+}
+
+.line-rdr-line.col-selected {
+  stroke-width: 4px;
+}
+
+.line-rdr-line.col-selected,
+.line-rdr-line.col-previewed {
+  filter: url('#active-shadow');
+}
+
+.line-rdr-point {
+  opacity: 0;
+  transition: opacity .2s;
+}
+
+/*
+ * Styles for BarChartRenderer and StackedBarChartRenderer
+ */
+
+.bar-rdr-bar.col-selected,
+.bar-rdr-bar.col-previewed,
+.stack-rdr-bar.col-selected,
+.stack-rdr-bar.col-previewed,
+.bar-rdr-bar.row-hovered,
+.bar-rdr-bar.row-highlighted,
+.stack-rdr-bar.row-hovered,
+.stack-rdr-bar.row-highlighted {
+  filter: url('#active-shadow');
+}
+
+/*
+ * Styles for the tooltip.
+ */
+
+.tooltip {
+  opacity: 0;
+  display: flex;
+  flex-direction: column;
+  position: absolute;
+  border-radius: 2px;
+  border: 1px solid #C1C1C1;
+  padding: 20px;
+  box-shadow: 0px 2px 1px rgba(0, 0, 0, 0.2);
+  z-index: 9999;
+  background-color: white;
+  font-size: 14px;
+  pointer-events: none;
+  transition: opacity .25s;
+}
+
+.tooltip-title {
+  color: #434343;
+  font-weight: bold;
+  margin-bottom: 16px;
+}
+
+.tooltip-total {
+  color: #636363;
+  font-weight: bold;
+  margin-bottom: 16px;
+}
+
+.tooltip-item {
+  white-space: nowrap;
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  color: #838383;
+  margin-bottom: 16px;
+}
+
+.tooltip-item:last-child {
+  margin-bottom: 0px;
+}
+
+.tooltip .tooltip-item-value {
+  margin-left: 20px;
+  margin-right: 0;
+}
+.tooltip.rtl .tooltip-item-value {
+  margin-left: 0;
+  margin-right: 20px;
+}
diff --git a/charted/lib/charts/themes/quantum_theme.dart b/charted/lib/charts/themes/quantum_theme.dart
new file mode 100644
index 0000000..48a1729
--- /dev/null
+++ b/charted/lib/charts/themes/quantum_theme.dart
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+
+part of charted.charts;
+
+class QuantumChartTheme extends ChartTheme {
+  static const List OTHER_COLORS =
+      const['#EEEEEE', '#BDBDBD', '#9E9E9E'];
+
+  static const List<List<String>> COLORS = const[
+    const [ '#C5D9FB', '#4184F3', '#2955C5' ],
+    const [ '#F3C6C2', '#DB4437', '#A52714' ],
+    const [ '#FBE7B1', '#F4B400', '#EF9200' ],
+    const [ '#B6E0CC', '#0F9D58', '#0A7F42' ],
+    const [ '#E0BDE6', '#AA46BB', '#691A99' ],
+    const [ '#B1EAF1', '#00ABC0', '#00828E' ],
+    const [ '#FFCBBB', '#FF6F42', '#E54918' ],
+    const [ '#EFF3C2', '#9D9C23', '#817616' ],
+    const [ '#C4C9E8', '#5B6ABF', '#3848AA' ],
+    const [ '#F7BACF', '#EF6191', '#E81D62' ],
+    const [ '#B1DEDA', '#00786A', '#004C3F' ],
+    const [ '#F38EB0', '#C1175A', '#870D4E' ],
+  ];
+
+  static const List<List<String>> COLORS_ASSIST = const[
+    const [ '#C5D9FB', '#4184F3', '#2955C5' ],
+    const [ '#F3C6C2', '#DB4437', '#A52714' ],
+    const [ '#FBE7B1', '#F4B400', '#EF9200' ],
+    const [ '#B6E0CC', '#0F9D58', '#0A7F42' ],
+    const [ '#E0BDE6', '#AA46BB', '#691A99' ],
+    const [ '#B1EAF1', '#00ABC0', '#00828E' ],
+    const [ '#FFCBBB', '#FF6F42', '#E54918' ],
+    const [ '#EFF3C2', '#9D9C23', '#817616' ]
+  ];
+
+  final OrdinalScale _scale = new OrdinalScale()..range = COLORS;
+
+  /* Implementation of ChartTheme */
+  String getColorForKey(key, [int state = ChartTheme.STATE_NORMAL]) {
+    var result = _scale.scale(key);
+    return (result is List && result.length > state)
+        ? result.elementAt(state)
+        : result;
+  }
+
+  String getOtherColor([int state = ChartTheme.STATE_NORMAL]) =>
+      OTHER_COLORS is List && OTHER_COLORS.length > state
+          ? OTHER_COLORS.elementAt(state)
+          : OTHER_COLORS;
+
+  ChartAxisTheme get measureAxisTheme =>
+      const _QuantumChartAxisTheme(ChartAxisTheme.FILL_RENDER_AREA, 5);
+  ChartAxisTheme get dimensionAxisTheme =>
+      const _QuantumChartAxisTheme(0, 10);
+
+  AbsoluteRect get padding => const AbsoluteRect(10, 10, 0, 0);
+
+  String get defaultFont => '14px Roboto';
+
+  String get filters => '''
+    <filter id="active-shadow" x="-50%" y="-25%" width="200%" height="200%">
+      <feOffset result="offOut" in="SourceGraphic" dx="0" dy="0" />
+      <feColorMatrix result="matrixOut" in="offOut"
+          type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/>
+      <feGaussianBlur result="blurOut" in="matrixOut" stdDeviation="2" />
+      <feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
+    </filter>
+''';
+
+  String getFilterForKey(key, [int state = ChartTheme.STATE_NORMAL]) =>
+      state == ChartTheme.STATE_ACTIVE ? 'url(#active-shadow)' : null;
+}
+
+class _QuantumChartAxisTheme implements ChartAxisTheme {
+  final axisOuterPadding = 0.1;
+  final axisBandInnerPadding = 0.35;
+  final axisBandOuterPadding = 0.175;
+  final axisTickPadding = 6;
+  final axisTickSize;
+  final axisTickCount;
+  final verticalAxisAutoResize = true;
+  final verticalAxisWidth = 75;
+  final horizontalAxisAutoResize = false;
+  final horizontalAxisHeight = 50;
+  final ticksFont = '14px Roboto';
+  const _QuantumChartAxisTheme(this.axisTickSize, this.axisTickCount);
+}
diff --git a/charted/lib/core/interpolators.dart b/charted/lib/core/interpolators.dart
new file mode 100644
index 0000000..0626a6f
--- /dev/null
+++ b/charted/lib/core/interpolators.dart
@@ -0,0 +1,24 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+/// A collection of interpolator generators and easing functions.
+/// 
+/// Interpolators provide intermediate state when transitioning from one
+/// frame to another in an animation.
+/// 
+/// Easing functions indicate progress of an animation to interpolators. 
+/// 
+/// Currently provides interpolator for various types, including basic types
+/// like numbers, colors, strings, transforms and for iterables.
+library charted.core.interpolators;
+
+import 'dart:math' as math;
+import 'package:charted/core/utils.dart';
+
+part 'interpolators/interpolators.dart';
+part 'interpolators/easing.dart';
diff --git a/charted/lib/core/interpolators/easing.dart b/charted/lib/core/interpolators/easing.dart
new file mode 100644
index 0000000..2b4bdec
--- /dev/null
+++ b/charted/lib/core/interpolators/easing.dart
@@ -0,0 +1,137 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.core.interpolators;
+
+const String EASE_TYPE_LINEAR   = 'linear';
+const String EASE_TYPE_POLY     = 'poly';
+const String EASE_TYPE_QUAD     = 'quad';
+const String EASE_TYPE_CUBIC    = 'cubic';
+const String EASE_TYPE_SIN      = 'sin';
+const String EASE_TYPE_EXP      = 'exp';
+const String EASE_TYPE_CIRCLE   = 'circle';
+const String EASE_TYPE_ELASTIC  = 'elastic';
+const String EASE_TYPE_BACK     = 'back';
+const String EASE_TYPE_BOUNCE   = 'bounce';
+
+const String EASE_MODE_IN       = 'in';
+const String EASE_MODE_OUT      = 'out';
+const String EASE_MODE_IN_OUT   = 'in-out';
+const String EASE_MODE_OUT_IN   = 'out-in';
+
+/// [EasingFunction] manipulates progression of an animation.  The returned
+/// value is passed to an [Interpolator] to generate intermediate state.
+typedef num EasingFunction(num t);
+
+/// Alters behavior of the [EasingFunction].  Takes [fn] and returns an
+/// altered [EasingFunction].
+typedef EasingFunction EasingModeFunction(EasingFunction fn);
+
+/// Creates an easing function based on type and mode.
+EasingFunction easingFunctionByName(
+    String type, [String mode = EASE_MODE_IN, List params]) {
+  const Map easingTypes = const {
+    EASE_TYPE_LINEAR: identityFunction,
+    EASE_TYPE_POLY: easePoly,
+    EASE_TYPE_QUAD: easeQuad,
+    EASE_TYPE_CUBIC: easeCubic,
+    EASE_TYPE_SIN: easeSin,
+    EASE_TYPE_EXP: easeExp,
+    EASE_TYPE_CIRCLE: easeCircle,
+    EASE_TYPE_ELASTIC: easeElastic,
+    EASE_TYPE_BACK: easeBack,
+    EASE_TYPE_BOUNCE: easeBounce
+  };
+  
+  const Map easingModes = const {
+    EASE_MODE_IN: identityFunction,
+    EASE_MODE_OUT: reverseEasingFn,
+    EASE_MODE_IN_OUT: reflectEasingFn,
+    EASE_MODE_OUT_IN: reflectReverseEasingFn
+  };
+  
+  const Map customEasingFunctions = const {
+    '$EASE_TYPE_CUBIC-$EASE_MODE_IN_OUT': easeCubicInOut 
+  };
+
+  assert(easingTypes.containsKey(type));
+  assert(easingModes.containsKey(mode));
+  
+  EasingFunction fn;
+  if (customEasingFunctions.containsKey('$type-$mode')) {
+    fn = Function.apply(customEasingFunctions['$type-$mode'], params);
+  } else {
+    fn = Function.apply(easingTypes[type], params);
+    fn = easingModes[mode](fn);
+  }
+  return clampEasingFn(fn);
+}
+
+
+/// Clamps transition progress to stay between 0.0 and 1.0
+EasingFunction clampEasingFn(EasingFunction f) =>
+    (t) => t <= 0 ? 0 : t >= 1 ? 1 : f(t);
+
+
+//
+// Implementation of easing modes.
+//
+
+EasingFunction reverseEasingFn(EasingFunction f) =>
+    (t) => 1 - f(1 - t);
+
+EasingFunction reflectEasingFn(EasingFunction f) =>
+    (t) => .5 * (t < .5 ? f(2 * t) : (2 - f(2 - 2 * t)));
+
+EasingFunction reflectReverseEasingFn(EasingFunction f) =>
+    reflectEasingFn(reverseEasingFn(f));
+
+
+//
+// Implementation of easing function generators.
+//
+
+EasingFunction easePoly([e = 1]) => (t) => math.pow(t, e);
+
+EasingFunction easeElastic([a = 1, p = 0.45]) {
+  var s = p / 2 * math.PI * math.asin(1 / a);
+  return (t) => 1 + a * math.pow(2, -10 * t) *
+      math.sin((t - s) * 2 * math.PI / p);
+}
+
+EasingFunction easeBack([s = 1.70158]) =>
+    (num t) => t * t * ((s + 1) * t - s);
+
+EasingFunction easeQuad() => (num t) => t * t;
+
+EasingFunction easeCubic() => (num t) => t * t * t;
+
+EasingFunction easeCubicInOut() =>
+    (num t) {
+      if (t <= 0) return 0;
+      if (t >= 1) return 1;
+      var t2 = t * t,
+          t3 = t2 * t;
+      return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
+    };
+
+EasingFunction easeSin() =>
+    (num t) => 1 - math.cos(t * math.PI / 2);
+
+EasingFunction easeExp() =>
+    (num t) => math.pow(2, 10 * (t - 1));
+
+EasingFunction easeCircle() =>
+    (num t) => 1 - math.sqrt(1 - t * t);
+
+EasingFunction easeBounce() =>
+    (num t) =>  t < 1 / 2.75 ?
+        7.5625 * t * t : t < 2 / 2.75 ?
+            7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ?
+                7.5625 * (t -= 2.25 / 2.75) * t + .9375
+                    : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
diff --git a/charted/lib/core/interpolators/interpolators.dart b/charted/lib/core/interpolators/interpolators.dart
new file mode 100644
index 0000000..0de80ee
--- /dev/null
+++ b/charted/lib/core/interpolators/interpolators.dart
@@ -0,0 +1,392 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.core.interpolators;
+
+/// [Interpolator] accepts [t], such that 0.0 < t < 1.0 and returns
+/// a value in a pre-defined range.
+typedef Interpolator(num t);
+
+/// [InterpolatorGenerator] accepts two parameters [a], [b] and returns an
+/// [Interpolator] for transitioning from [a] to [b]
+typedef Interpolator InterpolatorGenerator(a, b);
+
+/// List of registered interpolators - [createInterpolatorFromRegistry]
+/// iterates through this list from backwards and the first non-null
+/// interpolate function is returned to the caller.
+List<InterpolatorGenerator> _interpolators = [ createInterpolatorByType ];
+
+/// Returns a default interpolator between values [a] and [b]. Unless
+/// more interpolators are added, one of the internal implementations are
+/// selected by the type of [a] and [b].
+Interpolator createInterpolatorFromRegistry(a, b) {
+  var fn, i = _interpolators.length;
+  while (--i >= 0 && fn == null) {
+    fn = _interpolators[i](a, b);
+  }
+  return fn;
+}
+
+/// Creates an interpolator based on the type of [a] and [b].
+/// 
+/// Usage note: Use this method only when type of [a] and [b] are not known.
+///     When used, this function will prevent tree shaking of all built-in
+///     interpolators.
+Interpolator createInterpolatorByType(a, b) =>
+    (a is List && b is List) ? createListInterpolator(a, b) :
+    (a is Map && b is Map) ? createMapInterpolator(a, b) :
+    (a is String && b is String) ? createStringInterpolator(a, b) :
+    (a is num && b is num) ? createNumberInterpolator(a, b) :
+    (a is Color && b is Color) ? createRgbColorInterpolator(a, b) :
+    (t) => (t <= 0.5) ? a : b;
+
+
+//
+// Implementations of InterpolatorGenerator
+//
+
+/// Generate a numeric interpolator between numbers [a] and [b]
+Interpolator createNumberInterpolator(num a, num b) {
+  b -= a;
+  return (t) => a + b * t;
+}
+
+/// Generate a rounded number interpolator between numbers [a] and [b]
+Interpolator createRoundedNumberInterpolator(num a, num b) {
+  b -= a;
+  return (t) => (a + b * t).round();
+}
+
+
+/// Generate an interpolator between two strings [a] and [b].
+///
+/// The interpolator will interpolate all the number pairs in both strings
+/// that have same number of numeric parts.  The function assumes the non
+/// number part of the string to be identical and would use string [b] for
+/// merging the non numeric part of the strings.
+///
+/// Eg: Interpolate between $100.0 and $150.0
+Interpolator createStringInterpolator(String a, String b) {
+  if (a == null || b == null) return (t) => b;
+
+  // See if both A and B represent colors as RGB or HEX strings.
+  // If yes, use color interpolators
+  if (Color.isRgbColorString(a) && Color.isRgbColorString(b)) {
+    return createRgbColorInterpolator(
+        new Color.fromRgbString(a), new Color.fromRgbString(b));
+  }
+
+  // See if both A and B represent colors as HSL strings.
+  // If yes, use color interpolators.
+  if (Color.isHslColorString(a) && Color.isHslColorString(b)) {
+    return createHslColorInterpolator(
+        new Color.fromHslString(a), new Color.fromHslString(b));
+  }
+  
+  var numberRegEx =
+          new RegExp(r'[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?'),
+      numMatchesInA = numberRegEx.allMatches(a),
+      numMatchesInB = numberRegEx.allMatches(b),
+      stringParts = [],
+      numberPartsInA = [],
+      numberPartsInB = [],
+      interpolators = [],
+      s0 = 0;
+
+  numberPartsInA.addAll(numMatchesInA.map((m) => m.group(0)));
+
+  for (Match m in numMatchesInB) {
+    stringParts.add(b.substring(s0, m.start));
+    numberPartsInB.add(m.group(0));
+    s0 = m.end;
+  }
+
+  if (s0 < b.length) stringParts.add(b.substring(s0));
+
+  int numberLength = math.min(numberPartsInA.length, numberPartsInB.length);
+  int maxLength = math.max(numberPartsInA.length, numberPartsInB.length);
+  for (var i = 0; i < numberLength; i++) {
+    interpolators.add(createNumberInterpolator(num.parse(numberPartsInA[i]),
+        num.parse(numberPartsInB[i])));
+  }
+  if (numberPartsInA.length < numberPartsInB.length) {
+    for (var i = numberLength; i < maxLength; i++) {
+      interpolators.add(createNumberInterpolator(num.parse(numberPartsInB[i]),
+        num.parse(numberPartsInB[i])));
+    }
+  }
+
+  return (t) {
+    StringBuffer sb = new StringBuffer();
+    for (var i = 0; i < stringParts.length; i++) {
+      sb.write(stringParts[i]);
+      if (interpolators.length > i) {
+        sb.write(interpolators[i](t));
+      }
+    }
+    return sb.toString();
+  };
+}
+
+/// Generate an interpolator for RGB values.
+Interpolator createRgbColorInterpolator(Color a, Color b) {
+  if (a == null || b == null) return (t) => b;
+  var ar = a.r,
+      ag = a.g,
+      ab = a.b,
+      br = b.r - ar,
+      bg = b.g - ag,
+      bb = b.b - ab;
+
+  return (t) => new Color.fromRgba((ar + br * t).round(),
+      (ag + bg * t).round(), (ab + bb * t).round(), 1.0).toRgbaString();
+}
+
+/// Generate an interpolator using HSL color system converted to Hex string.
+Interpolator createHslColorInterpolator(Color a, Color b) {
+  if (a == null || b == null) return (t) => b;
+  var ah = a.h,
+      as = a.s,
+      al = a.l,
+      bh = b.h - ah,
+      bs = b.s - as,
+      bl = b.l - al;
+
+  return (t) => new Color.fromHsla((ah + bh * t).round(),
+      (as + bs * t).round(), (al + bl * t).round(), 1.0).toHslaString();
+}
+
+/// Generates an interpolator to interpolate each element between lists
+/// [a] and [b] using registered interpolators.
+Interpolator createListInterpolator(List a, List b) {
+  if (a == null || b == null) return (t) => b;
+  var x = [],
+      aLength = a.length,
+      numInterpolated = b.length,
+      n0 = math.min(aLength, numInterpolated),
+      output = new List.filled(math.max(aLength, numInterpolated), null),
+      i;
+
+  for (i = 0; i < n0; i++) x.add(createInterpolatorFromRegistry(a[i], b[i]));
+  for (; i < aLength; ++i) output[i] = a[i];
+  for (; i < numInterpolated; ++i) output[i] = b[i];
+
+  return (t) {
+    for (i = 0; i < n0; ++i) output[i] = x[i](t);
+    return output;
+  };
+}
+
+/// Generates an interpolator to interpolate each value on [a] to [b] using
+/// registered interpolators.
+Interpolator createMapInterpolator(Map a, Map b) {
+  if (a == null || b == null) return (t) => b;
+  var interpolatorsMap = new Map(),
+      output = new Map(),
+      aKeys = a.keys.toList(),
+      bKeys = b.keys.toList();
+
+  aKeys.forEach((k) {
+    if (b[k] != null) {
+      interpolatorsMap[k] = (createInterpolatorFromRegistry(a[k], b[k]));
+    } else {
+      output[k] = a[k];
+    }
+  });
+
+  bKeys.forEach((k) {
+    if (output[k] == null) {
+      output[k] = b[k];
+    }
+  });
+
+  return (t) {
+    interpolatorsMap.forEach((k, v) => output[k] = v(t));
+    return output;
+  };
+}
+
+/// Returns the interpolator that interpolators two transform strings
+/// [a] and [b] by their translate, rotate, scale and skewX parts.
+Interpolator createTransformInterpolator(String a, String b) {
+  if (a == null || b == null) return (t) => b;
+  var numRegExStr = r'[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?',
+      numberRegEx = new RegExp(numRegExStr),
+      translateRegEx =
+          new RegExp(r'translate\(' + '$numRegExStr,$numRegExStr' + r'\)'),
+      scaleRegEx =
+          new RegExp(r'scale\(' + numRegExStr + r',' + numRegExStr + r'\)'),
+      rotateRegEx = new RegExp(r'rotate\(' + numRegExStr + r'(deg)?\)'),
+      skewRegEx = new RegExp(r'skewX\(' + numRegExStr + r'(deg)?\)'),
+
+      translateA = translateRegEx.firstMatch(a),
+      scaleA = scaleRegEx.firstMatch(a),
+      rotateA = rotateRegEx.firstMatch(a),
+      skewA = skewRegEx.firstMatch(a),
+
+      translateB = translateRegEx.firstMatch(b),
+      scaleB = scaleRegEx.firstMatch(b),
+      rotateB = rotateRegEx.firstMatch(b),
+      skewB = skewRegEx.firstMatch(b);
+
+  var numSetA = [],
+      numSetB = [],
+      tempStr, match;
+
+  // translate
+  if (translateA != null) {
+    tempStr = a.substring(translateA.start, translateA.end);
+    match = numberRegEx.allMatches(tempStr);
+    for (Match m in match) {
+      numSetA.add(num.parse(m.group(0)));
+    }
+  } else {
+    numSetA.addAll(const[0, 0]);
+  }
+
+  if (translateB != null) {
+    tempStr = b.substring(translateB.start, translateB.end);
+    match = numberRegEx.allMatches(tempStr);
+    for (Match m in match) {
+      numSetB.add(num.parse(m.group(0)));
+    }
+  } else {
+    numSetB.addAll(const[0, 0]);
+  }
+
+  // scale
+  if (scaleA != null) {
+    tempStr = a.substring(scaleA.start, scaleA.end);
+    match = numberRegEx.allMatches(tempStr);
+    for (Match m in match) {
+      numSetA.add(num.parse(m.group(0)));
+    }
+  } else {
+    numSetA.addAll(const[1, 1]);
+  }
+
+  if (scaleB != null) {
+    tempStr = b.substring(scaleB.start, scaleB.end);
+    match = numberRegEx.allMatches(tempStr);
+    for (Match m in match) {
+      numSetB.add(num.parse(m.group(0)));
+    }
+  } else {
+    numSetB.addAll(const[1, 1]);
+  }
+
+  // rotate
+  if (rotateA != null) {
+    tempStr = a.substring(rotateA.start, rotateA.end);
+    match = numberRegEx.firstMatch(tempStr);
+    numSetA.add(num.parse(match.group(0)));
+  } else {
+    numSetA.add(0);
+  }
+
+  if (rotateB != null) {
+    tempStr = b.substring(rotateB.start, rotateB.end);
+    match = numberRegEx.firstMatch(tempStr);
+    numSetB.add(num.parse(match.group(0)));
+  } else {
+    numSetB.add(0);
+  }
+
+  // rotate < 180 degree
+  if (numSetA[4] != numSetB[4]) {
+    if (numSetA[4] - numSetB[4] > 180) {
+      numSetB[4] += 360;
+    } else if (numSetB[4] - numSetA[4] > 180) {
+      numSetA[4] += 360;
+    }
+  }
+
+  // skew
+  if (skewA != null) {
+    tempStr = a.substring(skewA.start, skewA.end);
+    match = numberRegEx.firstMatch(tempStr);
+    numSetA.add(num.parse(match.group(0)));
+  } else {
+    numSetA.add(0);
+  }
+
+  if (skewB != null) {
+    tempStr = b.substring(skewB.start, skewB.end);
+    match = numberRegEx.firstMatch(tempStr);
+    numSetB.add(num.parse(match.group(0)));
+  } else {
+    numSetB.add(0);
+  }
+
+  return (t) {
+    return
+        'translate(${createNumberInterpolator(numSetA[0], numSetB[0])(t)},'
+            '${createNumberInterpolator(numSetA[1], numSetB[1])(t)})'
+        'scale(${createNumberInterpolator(numSetA[2], numSetB[2])(t)},'
+            '${createNumberInterpolator(numSetA[3], numSetB[3])(t)})'
+        'rotate(${createNumberInterpolator(numSetA[4], numSetB[4])(t)})'
+        'skewX(${createNumberInterpolator(numSetA[5], numSetB[5])(t)})';
+  };
+}
+
+/// Returns the interpolator that interpolators zoom list [a] to [b]. Zoom
+/// lists are described by triple elements [ux0, uy0, w0] and [ux1, uy1, w1].
+Interpolator createZoomInterpolator(List a, List b) {
+  if (a == null || b == null) return (t) => b;
+  assert(a.length == b.length && a.length == 3);
+
+  var sqrt2 = math.SQRT2,
+      param2 = 2,
+      param4 = 4;
+
+  var ux0 = a[0], uy0 = a[1], w0 = a[2],
+      ux1 = b[0], uy1 = b[1], w1 = b[2];
+
+  var dx = ux1 - ux0,
+      dy = uy1 - uy0,
+      d2 = dx * dx + dy * dy,
+      d1 = math.sqrt(d2),
+      b0 = (w1 * w1 - w0 * w0 + param4 * d2) / (2 * w0 * param2 * d1),
+      b1 = (w1 * w1 - w0 * w0 - param4 * d2) / (2 * w1 * param2 * d1),
+      r0 = math.log(math.sqrt(b0 * b0 + 1) - b0),
+      r1 = math.log(math.sqrt(b1 * b1 + 1) - b1),
+      dr = r1 - r0,
+      S = ((!dr.isNaN) ? dr : math.log(w1 / w0)) / sqrt2;
+
+  return (t) {
+    var s = t * S;
+    if (!dr.isNaN) {
+      // General case.
+      var coshr0 = cosh(r0),
+          u = w0 / (param2 * d1) * (coshr0 * tanh(sqrt2 * s + r0) - sinh(r0));
+      return [
+        ux0 + u * dx,
+        uy0 + u * dy,
+        w0 * coshr0 / cosh(sqrt2 * s + r0)
+      ];
+    }
+    // Special case for u0 ~= u1.
+    return [
+      ux0 + t * dx,
+      uy0 + t * dy,
+      w0 * math.exp(sqrt2 * s)
+    ];
+  };
+}
+
+/// Reverse interpolator for a number.
+Interpolator uninterpolateNumber(num a, num b) {
+  b = 1 / (b - a);
+  return (x) => (x - a) * b;
+}
+
+/// Reverse interpolator for a clamped number.
+Interpolator uninterpolateClamp(num a, num b) {
+  b = 1 / (b - a);
+  return (x) => math.max(0, math.min(1, (x - a) * b));
+}
diff --git a/charted/lib/core/scales.dart b/charted/lib/core/scales.dart
new file mode 100644
index 0000000..fa61fc5
--- /dev/null
+++ b/charted/lib/core/scales.dart
@@ -0,0 +1,233 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+/// Collection of scales for use by charts. The role of scales is to map the
+/// input domain to an output range.
+///
+/// Charted supports two types of scales:
+///   - Quantitative, where the scales use a mathematical function for mapping
+///     the input domain to output range.
+///   - Ordinal, where the input domain is discrete (i.e set of values)
+///
+library charted.core.scales;
+
+import 'dart:math' as math;
+import 'package:charted/core/utils.dart';
+import 'package:charted/core/interpolators.dart';
+import 'package:charted/core/time_interval.dart';
+import 'package:charted/locale/locale.dart';
+import 'package:charted/locale/format.dart';
+
+part 'scales/ordinal_scale.dart';
+part 'scales/linear_scale.dart';
+part 'scales/log_scale.dart';
+part 'scales/time_scale.dart';
+
+typedef num RoundFunction(num value);
+
+/// Minimum common interface supported by all scales. [QuantitativeScale] and
+/// [OrdinalScale] contain the interface for their respective types.
+abstract class Scale {
+  /// Given a [value] in the input domain, map it to the range.
+  /// On [QuantitativeScale]s both parameter and return values are numbers.
+  dynamic scale(value);
+
+  /// Given a [value] in the output range, return value in the input domain
+  /// that maps to the value.
+  /// On [QuantitativeScale]s both parameter and return values are numbers.
+  dynamic invert(value);
+
+  /// Input domain used by this scale.
+  Iterable domain;
+
+  /// Output range used by this scale.
+  Iterable range;
+
+  /// Maximum and minimum values of the scale's output range.
+  Extent get rangeExtent;
+
+  /// Creates tick values over the input domain.
+  Iterable get ticks;
+
+  /// Creates a formatter that is suitable for formatting the ticks.
+  /// For ordinal scale, the returned function is an identity function.
+  FormatFunction createTickFormatter([String format]);
+
+  /// Creates a clone of this scale.
+  Scale clone();
+
+  /// Suggested number of ticks on this scale.
+  /// Note: This property is only valid on quantitative scales.
+  int ticksCount;
+
+  /// Indicates if the current scale is using niced values for ticks.
+  /// Note: This property is only valid on quantitative scales.
+  bool nice;
+
+  /// Indicates if output range is clamped.  When clamp is not true, any input
+  /// value that is not within the input domain may result in a value that is
+  /// outside the output range.
+  /// Note: This property is only valid on quantitative scales.
+  bool clamp;
+
+  /// Indicates that the scaled values must be rounded to the nearest
+  /// integer.  Helps avoid anti-aliasing artifacts in the visualizations.
+  /// Note: This property is only valid on quantitative scales.
+  bool rounded;
+}
+
+/// Minimum common interface supported by scales whose input domain
+/// contains discreet values (Ordinal scales).
+abstract class OrdinalScale extends Scale {
+  factory OrdinalScale() = _OrdinalScale;
+
+  /// Amount of space that each value in the domain gets from the range. A band
+  /// is available only after [rangeBands] or [rangeRoundBands] is called by
+  /// the user. A bar-chart could use this space as width of a bar.
+  num get rangeBand;
+
+  /// Maps each value on the domain to a single point on output range.  When a
+  /// non-zero value is specified, [padding] space is left unused on both ends
+  /// of the range.
+  void rangePoints(Iterable range, [double padding]);
+
+  /// Maps each value on the domain to a band in the output range.  When a
+  /// non-zero value is specified, [padding] space is left between each bands
+  /// and [outerPadding] space is left unused at both ends of the range.
+  void rangeBands(Iterable range, [double padding, double outerPadding]);
+
+  /// Similar to [rangeBands] but ensures that each band starts and ends on a
+  /// pixel boundary - helps avoid anti-aliasing artifacts.
+  void rangeRoundBands(Iterable range, [double padding, double outerPadding]);
+}
+
+class RoundingFunctions extends Pair<RoundFunction,RoundFunction> {
+  RoundingFunctions(RoundFunction floor, RoundFunction ceil)
+      : super(floor, ceil);
+
+  factory RoundingFunctions.defaults() =>
+      new RoundingFunctions((x) => x.floor(), (x) => x.ceil());
+
+  factory RoundingFunctions.identity() =>
+      new RoundingFunctions(identityFunction, identityFunction);
+
+  RoundFunction get floor => super.first;
+  RoundFunction get ceil => super.last;
+}
+
+/// Namespacing container for utilities used by scales.
+abstract class ScaleUtils {
+  /// Utility to return extent of sorted [values].
+  static Extent extent(Iterable values) =>
+      values.first < values.last
+          ? new Extent(values.first, values.last)
+          : new Extent(values.last, values.first);
+
+  /// Extends [values] to round numbers based on the given pair of
+  /// floor and ceil functions.  [functions] is a pair of rounding function
+  /// among which the first is used to compute floor of a number and the
+  /// second for ceil of the number.
+  static List nice(List values, RoundingFunctions functions) {
+    if (values.last >= values.first) {
+      values[0] = functions.floor(values.first);
+      values[values.length - 1] = functions.ceil(values.last);
+    } else {
+      values[values.length - 1] = functions.floor(values.last);
+      values[0] = functions.ceil(values.first);
+    }
+    return values;
+  }
+
+  static RoundingFunctions niceStep(num step) => (step > 0)
+      ? new RoundingFunctions(
+          (x) => (x / step).floor() * step, (x) => (x / step).ceil() * step)
+      : new RoundingFunctions.identity();
+
+  /// Returns a Function that given a value x on the domain, returns the
+  /// corrsponding value on the range on a bilinear scale.
+  ///
+  /// @param domain         The domain of the scale.
+  /// @param range          The range of the scale.
+  /// @param uninterpolator The uninterpolator for domain values.
+  /// @param interpolator   The interpolator for range values.
+  static Function bilinearScale(List domain, List range,
+      Function uninterpolator, Function interpolator) {
+    var u = uninterpolator(domain[0], domain[1]),
+        i = interpolator(range[0], range[1]);
+    return (x) => i(u(x));
+  }
+
+  /// Returns a Function that given a value x on the domain, returns the
+  /// corrsponding value on the range on a polylinear scale.
+  ///
+  /// @param domain         The domain of the scale.
+  /// @param range          The range of the scale.
+  /// @param uninterpolator The uninterpolator for domain values.
+  /// @param interpolator   The interpolator for range values.
+  static Function polylinearScale(List domain, List range,
+      Function uninterpolator, Function interpolator) {
+    var u = [],
+        i = [],
+        j = 0,
+        k = math.min(domain.length, range.length) - 1;
+
+    // Handle descending domains.
+    if (domain[k] < domain[0]) {
+      domain = domain.reversed.toList();
+      range = range.reversed.toList();
+    }
+
+    while (++j <= k) {
+      u.add(uninterpolator(domain[j - 1], domain[j]));
+      i.add(interpolator(range[j - 1], range[j]));
+    }
+
+    return (x) {
+      int index = bisect(domain, x, 1, k) - 1;
+      return i[index](u[index](x));
+    };
+  }
+
+  /// Returns the insertion point i for value x such that all values in a[lo:i]
+  /// will be less than x and all values in a[i:hi] will be equal to or greater
+  /// than x.
+  static int bisectLeft(List a, num x, [int lo = 0, int hi = -1]) {
+    if (hi == -1) {
+      hi = a.length;
+    }
+    while (lo < hi) {
+      int mid = ((lo + hi) / 2).floor();
+      if (a[mid] < x) {
+        lo = mid + 1;
+      } else {
+        hi = mid;
+      }
+    }
+    return lo;
+  }
+
+  /// Returns the insertion point i for value x such that all values in a[lo:i]
+  /// will be less than or equalto x and all values in a[i:hi] will be greater
+  /// than x.
+  static int bisectRight(List a, num x, [int lo = 0, int hi = -1]) {
+    if (hi == -1) {
+      hi = a.length;
+    }
+    while (lo < hi) {
+      int mid = ((lo + hi) / 2).floor();
+      if (x < a[mid]) {
+        hi = mid;
+      } else {
+        lo = mid + 1;
+      }
+    }
+    return lo;
+  }
+
+  static Function bisect = bisectRight;
+}
diff --git a/charted/lib/core/scales/linear_scale.dart b/charted/lib/core/scales/linear_scale.dart
new file mode 100644
index 0000000..5aea19a
--- /dev/null
+++ b/charted/lib/core/scales/linear_scale.dart
@@ -0,0 +1,173 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+part of charted.core.scales;
+
+class LinearScale implements Scale {
+  static const defaultDomain = const [0, 1];
+  static const defaultRange = const [0, 1];
+
+  bool _rounded = false;
+  Iterable _domain = defaultDomain;
+  Iterable _range = defaultRange;
+
+  int _ticksCount = 5;
+  bool _clamp = false;
+  bool _nice = false;
+
+  Function _invert;
+  Function _scale;
+
+  LinearScale();
+
+  LinearScale._clone(LinearScale source)
+      : _domain = source._domain.toList(),
+        _range = source._range.toList(),
+        _ticksCount = source._ticksCount,
+        _clamp = source._clamp,
+        _nice = source._nice,
+        _rounded = source._rounded {
+    _reset();
+  }
+
+  void _reset() {
+    if (nice) {
+      _domain = ScaleUtils.nice(
+          _domain, ScaleUtils.niceStep(_linearTickRange().step));
+    }
+
+    Function linear = math.min(_domain.length, _range.length) > 2 ?
+        ScaleUtils.polylinearScale : ScaleUtils.bilinearScale;
+
+    Function uninterpolator = clamp ? uninterpolateClamp : uninterpolateNumber;
+    InterpolatorGenerator interpolator =
+        _rounded ? createRoundedNumberInterpolator : createNumberInterpolator;
+
+    _invert =
+        linear(_range, _domain, uninterpolator, createNumberInterpolator);
+    _scale = linear(_domain, _range, uninterpolator, interpolator);
+  }
+
+  @override
+  set range(Iterable value) {
+    assert(value != null);
+    _range = value;
+    _reset();
+  }
+
+  @override
+  Iterable get range => _range;
+
+  @override
+  set domain(Iterable value) {
+    _domain = value;
+    _reset();
+  }
+
+  @override
+  Iterable get domain => _domain;
+
+  @override
+  set rounded(bool value) {
+    assert(value != null);
+    if (value != null && _rounded != value) {
+      _rounded = value;
+      _reset();
+    }
+  }
+
+  @override
+  bool get rounded => _rounded;
+
+  @override
+  set ticksCount(int value) {
+    assert(value != null);
+    if (value != null && _ticksCount != value) {
+      _ticksCount = value;
+      _reset();
+    }
+  }
+
+  @override
+  int get ticksCount => _ticksCount;
+
+  @override
+  Iterable get ticks => _linearTickRange();
+
+  @override
+  set clamp(bool value) {
+    assert(value != null);
+    if (value != null && _clamp != value) {
+      _clamp = value;
+      _reset();
+    }
+  }
+
+  @override
+  bool get clamp => _clamp;
+
+  @override
+  set nice(bool value) {
+    assert(value != null);
+    if (value != null && _nice != value) {
+      _nice = value;
+      _reset();
+    }
+  }
+
+  @override
+  bool get nice => _nice;
+
+  @override
+  Extent get rangeExtent => ScaleUtils.extent(_range);
+
+  @override
+  num scale(num value) => _scale(value);
+
+  @override
+  num invert(num value) => _invert(value);
+
+  Range _linearTickRange([Extent extent]) {
+    if (extent == null) {
+      extent = ScaleUtils.extent(_domain);
+    }
+    var span = extent.max - extent.min,
+        step =
+            math.pow(10, (math.log(span / _ticksCount) / math.LN10).floor()),
+        err = _ticksCount / span * step;
+
+    // Filter ticks to get closer to the desired count.
+    if (err <= .15) {
+      step *= 10;
+    }
+    else if (err <= .35) {
+      step *= 5;
+    }
+    else if (err <= .75) {
+      step *= 2;
+    }
+
+    return new Range((extent.min / step).ceil() * step,
+        (extent.max / step).floor() * step + step * 0.5, step);
+  }
+
+  @override
+  FormatFunction createTickFormatter([String formatStr]) {
+    int precision(value) {
+      return -(math.log(value) / math.LN10 + .01).floor();
+    }
+    Range tickRange = _linearTickRange();
+    if (formatStr == null) {
+      formatStr = ".${precision(tickRange.step)}f";
+    }
+    NumberFormat formatter = new NumberFormat(new EnUsLocale());
+    return formatter.format(formatStr);
+  }
+
+  @override
+  LinearScale clone() => new LinearScale._clone(this);
+}
diff --git a/charted/lib/core/scales/log_scale.dart b/charted/lib/core/scales/log_scale.dart
new file mode 100644
index 0000000..20fc3a4
--- /dev/null
+++ b/charted/lib/core/scales/log_scale.dart
@@ -0,0 +1,180 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+part of charted.core.scales;
+
+/// Log scale is similar to linear scale, except there's a logarithmic
+/// transform that is applied to the input domain value before the output
+/// range value is computed.
+///
+/// The mapping to the output range value y can be expressed as a function
+/// of the input domain value x: y = m log(x) + b.
+///
+/// As log(0) is negative infinity, a log scale must have either an
+/// exclusively-positive or exclusively-negative domain; the domain must not
+/// include or cross zero.
+class LogScale implements Scale {
+  static const defaultBase = 10;
+  static const defaultDomain = const [1, 10];
+  static final negativeNumbersRoundFunctionsPair =
+      new RoundingFunctions(
+          (x) => -((-x).floor()),
+          (x) => -((-x).ceil()));
+
+  final LinearScale _linear;
+
+  bool _nice = false;
+  int _base = defaultBase;
+  int _ticksCount = 10;
+  bool _positive = true;
+  List _domain = defaultDomain;
+
+  LogScale() : _linear = new LinearScale();
+
+  LogScale._clone(LogScale source)
+      : _linear = source._linear.clone(),
+        _domain = source._domain.toList(),
+        _positive = source._positive,
+        _base = source._base,
+        _nice = source._nice,
+        _ticksCount = source._ticksCount;
+
+  num _log(x) => (_positive ?
+      math.log(x < 0 ? 0 : x) : -math.log(x > 0 ? 0 : -x)) / math.log(base);
+
+  num _pow(x) => _positive ? math.pow(base, x) : -math.pow(base, -x);
+
+  set base(int value) {
+    if (_base != value) {
+      _base = value;
+      _reset();
+    }
+  }
+
+  get base => _base;
+
+  @override
+  num scale(x) => _linear.scale(_log(x));
+
+  @override
+  num invert(x) => _pow(_linear.invert(x));
+
+  @override
+  set domain(Iterable values) {
+    _positive = values.first >= 0;
+    _domain = values;
+    _reset();
+  }
+
+  @override
+  Iterable get domain => _domain;
+
+  @override
+  set range(Iterable newRange) {
+    _linear.range = newRange;
+  }
+
+  @override
+  Iterable get range => _linear.range;
+
+  @override
+  set rounded(bool value) {
+    _linear.rounded = value;
+  }
+
+  @override
+  bool get rounded => _linear.rounded;
+
+  @override
+  set nice(bool value) {
+    if (_nice != value) {
+      _nice = value;
+      _reset();
+    }
+  }
+
+  @override
+  bool get nice => _nice;
+
+  @override
+  set ticksCount(int value) {
+    if (_ticksCount != value) {
+      _ticksCount = value;
+      _reset();
+    }
+  }
+
+  @override
+  int get ticksCount => _ticksCount;
+
+  @override
+  set clamp(bool value) {
+    _linear.clamp = value;
+  }
+
+  @override
+  bool get clamp => _linear.clamp;
+
+  @override
+  Extent get rangeExtent => _linear.rangeExtent;
+
+  _reset() {
+    if (_nice) {
+      var niced = _domain.map((e) => _log(e)).toList();
+      var roundFunctions = _positive
+          ? new RoundingFunctions.defaults()
+          : negativeNumbersRoundFunctionsPair;
+
+      _linear.domain = ScaleUtils.nice(niced, roundFunctions);
+      _domain = niced.map((e) => _pow(e)).toList();
+    } else {
+      _linear.domain = _domain.map((e) => _log(e)).toList();
+    }
+  }
+
+  Iterable get ticks {
+    var extent = ScaleUtils.extent(_domain),
+        ticks = [],
+        u = extent.min,
+        v = extent.max,
+        i = (_log(u)).floor(),
+        j = (_log(v)).ceil(),
+        n = (_base % 1 > 0) ? 2 : _base;
+
+    if ((j - i).isFinite) {
+      if (_positive) {
+        for (; i < j; i++) for (var k = 1; k < n; k++) ticks.add(_pow(i) * k);
+        ticks.add(_pow(i));
+      } else {
+        ticks.add(_pow(i));
+        for (; i++ < j;) for (var k = n - 1; k > 0; k--) ticks.add(_pow(i) * k);
+      }
+      for (i = 0; ticks[i] < u; i++) {} // strip small values
+      for (j = ticks.length; ticks[j - 1] > v; j--) {} // strip big values
+      ticks = ticks.sublist(i, j);
+    }
+    return ticks;
+  }
+
+  FormatFunction createTickFormatter([String formatStr]) {
+    NumberFormat formatter = new NumberFormat(new EnUsLocale());
+    FormatFunction logFormatFunction =
+        formatter.format(formatStr != null ? formatStr : ".0E");
+    var k = math.max(.1, ticksCount / this.ticks.length),
+        e = _positive ? 1e-12 : -1e-12;
+    return (d) {
+      if (_positive) {
+        return d / _pow((_log(d) + e).ceil()) <= k ? logFormatFunction(d) : '';
+      } else {
+        return d / _pow((_log(d) + e).floor()) <= k ? logFormatFunction(d) : '';
+      }
+    };
+  }
+
+  @override
+  LogScale clone() => new LogScale._clone(this);
+}
diff --git a/charted/lib/core/scales/ordinal_scale.dart b/charted/lib/core/scales/ordinal_scale.dart
new file mode 100644
index 0000000..6c135a2
--- /dev/null
+++ b/charted/lib/core/scales/ordinal_scale.dart
@@ -0,0 +1,177 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+part of charted.core.scales;
+
+class _OrdinalScale implements OrdinalScale {
+  final _index = new Map<dynamic, int>();
+
+  List _domain = [];
+  List _range = [];
+  num _rangeBand = 0;
+  Extent _rangeExtent;
+  Function _reset;
+
+  _OrdinalScale();
+
+  _OrdinalScale._clone(_OrdinalScale source)
+      : _domain = source._domain,
+        _range = source._range,
+        _reset = source._reset,
+        _rangeExtent = source._rangeExtent,
+        _rangeBand = source._rangeBand {
+    _index.addAll(source._index);
+  }
+
+  @override
+  scale(dynamic value) {
+    if (!_index.containsKey(value)) {
+      _index[value] = domain.length;
+      _domain.add(value);
+    }
+    return _range.isNotEmpty
+        ? _range.elementAt(_index[value] % _range.length)
+        : 0;
+  }
+
+  @override
+  dynamic invert(num value) {
+    int position = _range.indexOf(value);
+    return position > -1 && position < _domain.length
+        ? _domain[position]
+        : null;
+  }
+
+  @override
+  set domain(Iterable values) {
+    _domain = [];
+    _index.clear();
+
+    for (var i = 0; i < values.length; i++) {
+      var value = values.elementAt(i);
+      if (_index[value] == null) {
+        _index[value] = _domain.length;
+        _domain.add(value);
+      }
+    }
+
+    if (_reset != null) _reset(this);
+  }
+
+  @override
+  Iterable get domain => _domain;
+
+  @override
+  set range(Iterable values) => _setRange(this, values);
+
+  @override
+  Iterable get range => _range;
+
+  @override
+  Extent get rangeExtent => _rangeExtent;
+
+  @override
+  void rangePoints(Iterable range, [double padding = 0.0]) =>
+      _setRangePoints(this, range, padding);
+
+  @override
+  void rangeBands(Iterable range,
+      [double padding = 0.0, double outerPadding]) =>
+          _setRangeBands(this, range, padding,
+              outerPadding == null ? padding : outerPadding);
+
+  @override
+  void rangeRoundBands(Iterable range,
+      [double padding = 0.0, double outerPadding]) =>
+          _setRangeRoundBands(this, range, padding,
+              outerPadding == null ? padding : outerPadding);
+
+  @override
+  num get rangeBand => _rangeBand;
+
+  @override
+  FormatFunction createTickFormatter([String format]) => identityFunction;
+
+  @override
+  Iterable get ticks => _domain;
+
+  @override
+  OrdinalScale clone() => new _OrdinalScale._clone(this);
+
+  List _steps(start, step) =>
+      new Range(domain.length).map((num i) => start + step * i).toList();
+
+  static void _setRange(_OrdinalScale scale, Iterable values) {
+    scale._reset = (_OrdinalScale s) {
+      s._range = values;
+      s._rangeBand = 0;
+      s._rangeExtent = null;
+    };
+    scale._reset(scale);
+  }
+
+  static void _setRangePoints(
+      _OrdinalScale scale, Iterable range, double padding) {
+    scale._reset = (_OrdinalScale s) {
+      var start = range.first,
+          stop = range.last,
+          step = (stop - start) / (s.domain.length - 1 + padding);
+
+      s._range = s._steps(s.domain.length < 2
+          ? (start + stop) / 2
+          : start + step * padding / 2, step);
+      s._rangeBand = 0;
+      s._rangeExtent = new Extent(start, stop);
+    };
+    if (scale.domain.isNotEmpty) {
+      scale._reset(scale);
+    }
+  }
+
+  static void _setRangeBands(_OrdinalScale scale,
+      Iterable range, double padding, double outerPadding) {
+    scale._reset = (_OrdinalScale s) {
+      var start = range.first,
+          stop = range.last,
+          step = (stop - start) / s.domain.length - padding + 2 * outerPadding;
+
+      s._range = s._steps(start + step * outerPadding, step);
+      s._rangeBand = step * (1 - padding);
+      s._rangeExtent = new Extent(start, stop);
+    };
+    if (scale.domain.isNotEmpty){
+      scale._reset(scale);
+    }
+  }
+
+  static void _setRangeRoundBands(_OrdinalScale scale,
+      Iterable range, double padding, double outerPadding) {
+    scale._reset = (_OrdinalScale s) {
+      var start = range.first,
+          stop = range.last,
+          step = ((stop - start) /
+              (s.domain.length - padding + 2 * outerPadding)).floor(),
+          error = stop - start - (s.domain.length - padding) * step;
+
+      s._range = s._steps(start + (error / 2).round(), step);
+      s._rangeBand = (step * (1 - padding)).round();
+      s._rangeExtent = new Extent(start, stop);
+    };
+    if (scale.domain.isNotEmpty) {
+      scale._reset(scale);
+    }
+  }
+
+  //
+  // Properties that are valid only on quantitative scales.
+  //
+
+  bool clamp;
+  bool nice;
+  bool rounded;
+  int ticksCount;
+}
diff --git a/charted/lib/core/scales/time_scale.dart b/charted/lib/core/scales/time_scale.dart
new file mode 100644
index 0000000..8fba314
--- /dev/null
+++ b/charted/lib/core/scales/time_scale.dart
@@ -0,0 +1,185 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.core.scales;
+
+/// TimeScale is a linear scale that operates on time.
+class TimeScale extends LinearScale {
+  static const _scaleSteps = const [
+    1e3,    // 1-second
+    5e3,    // 5-second
+    15e3,   // 15-second
+    3e4,    // 30-second
+    6e4,    // 1-minute
+    3e5,    // 5-minute
+    9e5,    // 15-minute
+    18e5,   // 30-minute
+    36e5,   // 1-hour
+    108e5,  // 3-hour
+    216e5,  // 6-hour
+    432e5,  // 12-hour
+    864e5,  // 1-day
+    1728e5, // 2-day
+    6048e5, // 1-week
+    2592e6, // 1-month
+    7776e6, // 3-month
+    31536e6 // 1-year
+  ];
+
+  static final _scaleLocalMethods = [
+    [TimeInterval.second, 1],
+    [TimeInterval.second, 5],
+    [TimeInterval.second, 15],
+    [TimeInterval.second, 30],
+    [TimeInterval.minute, 1],
+    [TimeInterval.minute, 5],
+    [TimeInterval.minute, 15],
+    [TimeInterval.minute, 30],
+    [TimeInterval.hour,   1],
+    [TimeInterval.hour,   3],
+    [TimeInterval.hour,   6],
+    [TimeInterval.hour,   12],
+    [TimeInterval.day,    1],
+    [TimeInterval.day,    2],
+    [TimeInterval.week,   1],
+    [TimeInterval.month,  1],
+    [TimeInterval.month,  3],
+    [TimeInterval.year,   1]
+  ];
+
+  static TimeFormatFunction _scaleLocalFormat = new TimeFormat().multi([
+      [".%L",   (DateTime d) => d.millisecond > 0],
+      [":%S",   (DateTime d) => d.second > 0],
+      ["%I:%M", (DateTime d) => d.minute > 0],
+      ["%I %p", (DateTime d) => d.hour > 0],
+      ["%a %d", (DateTime d) => (d.weekday % 7) > 0 && d.day != 1],
+      ["%b %d", (DateTime d) => d.day != 1],
+      ["%B",    (DateTime d) => d.month > 1],
+      ["%Y",    (d) => true]
+  ]);
+
+  TimeScale();
+  TimeScale._clone(TimeScale source) : super._clone(source);
+
+  @override
+  scale(dynamic val) =>
+      super.scale(val is DateTime ? val.millisecondsSinceEpoch : val);
+
+  @override
+  set domain(Iterable value) {
+    super.domain = value.map(
+        (d) => d is DateTime ? d.millisecondsSinceEpoch : d).toList();
+  }
+
+  @override
+  FormatFunction createTickFormatter([String format]) => _scaleLocalFormat;
+
+  @override
+  TimeScale clone() => new TimeScale._clone(this);
+
+  List _getTickMethod(Extent extent, int count) {
+    var target  = (extent.max - extent.min) / count,
+        i = ScaleUtils.bisect(_scaleSteps, target);
+
+    return i == _scaleSteps.length
+        ? [ TimeInterval.year, _linearTickRange(
+            new Extent(extent.min / 31536e6, extent.max / 31536e6)).step ]
+        : i == 0
+            ? [ new ScaleMilliSeconds(), _linearTickRange(extent).step ]
+            : _scaleLocalMethods[
+                target / _scaleSteps[i - 1] < _scaleSteps[i] / target ? i - 1 : i];
+  }
+
+  List niceInterval(int ticksCount, [int skip = 1]) {
+    var extent = ScaleUtils.extent(domain),
+        method = _getTickMethod(extent, ticksCount),
+        interval;
+
+    if (method != null) {
+      interval = method[0];
+      skip = method[1];
+    }
+
+    bool skipped(var date) {
+      if (date is DateTime) date = date.millisecondsSinceEpoch;
+      return (interval as TimeInterval)
+          .range(date, date + 1, skip).length == 0;
+    }
+
+    if (skip > 1) {
+      domain = ScaleUtils.nice(domain, new RoundingFunctions(
+        (date) {
+          while (skipped(date = (interval as TimeInterval).floor(date))) {
+            date = new DateTime.fromMillisecondsSinceEpoch(
+                date.millisecondsSinceEpoch - 1);
+          }
+          return date.millisecondsSinceEpoch;
+        },
+        (date) {
+          while (skipped(date = (interval as TimeInterval).ceil(date))) {
+            date = new DateTime.fromMillisecondsSinceEpoch(
+                date.millisecondsSinceEpoch + 1);
+          }
+          return date.millisecondsSinceEpoch;
+        }
+      ));
+    } else {
+      domain = ScaleUtils.nice(
+          domain, new RoundingFunctions(
+              (date) => interval.floor(date).millisecondsSinceEpoch,
+              (date) => interval.ceil(date).millisecondsSinceEpoch));
+    }
+    return domain;
+  }
+
+  @override
+  set nice(bool value) {
+    assert(value != null);
+    if (value != null && _nice != value) {
+      _nice = value;
+      domain = niceInterval(_ticksCount);
+    }
+  }
+
+  List ticksInterval(int ticksCount, [int skip]) {
+    var extent = ScaleUtils.extent(domain),
+        method = _getTickMethod(extent, ticksCount),
+        interval;
+    if (method != null) {
+      interval = method[0];
+      skip = method[1];
+    }
+    return interval.range(extent.min, extent.max + 1, skip < 1 ? 1 : skip);
+  }
+
+  @override
+  List get ticks => ticksInterval(ticksCount);
+}
+
+class ScaleMilliSeconds implements TimeInterval {
+  DateTime _toDateTime(x) {
+    assert (x is int || x is DateTime);
+    return x is num ? new DateTime.fromMillisecondsSinceEpoch(x) : x;
+  }
+  DateTime floor(dynamic val) => _toDateTime(val);
+  DateTime ceil(dynamic val) => _toDateTime(val);
+  DateTime round(dynamic val) => _toDateTime(val);
+
+  DateTime offset(dynamic val, num dt) {
+    assert(val is int || val is DateTime);
+    return new DateTime.fromMillisecondsSinceEpoch(
+        val is int ? val + dt : (val as DateTime).millisecondsSinceEpoch + dt);
+  }
+
+  List range(var t0, var t1, int step) {
+    int start = t0 is DateTime ? t0.millisecondsSinceEpoch : t0,
+        stop = t1 is DateTime ? t1.millisecondsSinceEpoch : t1;
+    return new Range((start / step).ceil() * step, stop, step).map(
+        (d) => new DateTime.fromMillisecondsSinceEpoch(d)).toList();
+  }
+}
diff --git a/charted/lib/core/text_metrics.dart b/charted/lib/core/text_metrics.dart
new file mode 100644
index 0000000..5933594
--- /dev/null
+++ b/charted/lib/core/text_metrics.dart
@@ -0,0 +1,113 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+/// Provides a way to measure rendered text width for clipping
+/// text on tooltips and ticks when they are too long.
+library charted.core.text_metrics;
+
+import "dart:html";
+import "package:charted/core/text_metrics/segmentation.dart";
+
+/// Utilities to measure text width.
+class TextMetrics {
+  static CanvasElement canvas;
+  static CanvasRenderingContext2D context;
+  static TextMetrics instance;
+
+  static const MAX_STRING_LENGTH = 250;
+  static final FONT_SIZE_REGEX = new RegExp("\s?([0-9]+)px\s?");
+
+  final String fontStyle;
+  int fontSize = 16;
+
+  String currentFontStyle;
+
+  factory TextMetrics({String fontStyle}) {
+    if (canvas == null || context == null) {
+      canvas = document.createElement('canvas');
+      context = canvas.getContext('2d');
+    }
+    if (instance == null) {
+      instance = new TextMetrics._internal(fontStyle);
+    }
+    return instance;
+  }
+  TextMetrics._internal(this.fontStyle) {
+    Match match = FONT_SIZE_REGEX.firstMatch(fontStyle);
+    fontSize = int.parse(match.group(1));
+  }
+
+  void setFontStyle(String fontStyle) {
+    if (fontStyle == null) {
+      fontStyle = this.fontStyle;
+    }
+    if (currentFontStyle != fontStyle) {
+      context.font = fontStyle;
+      currentFontStyle = fontStyle;
+    }
+  }
+
+  /// Measure width of [text] in pixels.
+  /// Optionally, uses [fontStyle] instead of using the default style
+  double getTextWidth(String text, {String fontStyle}) {
+    assert(text.length <= MAX_STRING_LENGTH);
+    setFontStyle(fontStyle);
+    return context.measureText(text).width;
+  }
+
+  /// Gets length of the longest string in the given [strings].
+  /// Optionally, uses [fontStyle] instead of using the default style.
+  double getLongestTextWidth(Iterable<String> strings, {String fontStyle}) {
+    setFontStyle(fontStyle);
+    double maxWidth = 0.0;
+    for (int i = 0; i < strings.length; ++i) {
+      assert(strings.elementAt(i).length <= MAX_STRING_LENGTH);
+      double width = context.measureText(strings.elementAt(i)).width;
+      if (width > maxWidth) {
+        maxWidth = width;
+      }
+    }
+
+    return maxWidth;
+  }
+
+  /// Truncates given [text] to fit in [width]. Adds an ellipsis to the
+  /// returned string, if it needed to be truncated.
+  /// Optionally, uses [fontStyle] instead of using the default style.
+  String ellipsizeText(String text, double width, {String fontStyle}) {
+    assert(text.length <= MAX_STRING_LENGTH);
+    setFontStyle(fontStyle);
+    double computedWidth = context.measureText(text).width;
+    if (computedWidth > width) {
+      var indices = graphemeBreakIndices(text);
+      var position = 0,
+          min = 0, max = indices.length - 1, mid,
+          ellipsis = context.measureText('…').width;
+      width = width - ellipsis;
+      while (max >= min) {
+        mid = (min + max) ~/ 2;
+        position = indices[mid];
+        if (context.measureText(text.substring(0, position)).width > width) {
+          max = mid - 1;
+        } else {
+          min = mid + 1;
+        }
+      }
+      text = text.substring(0, indices[max]) + '…';
+    }
+    return text;
+  }
+
+  /// Truncates text in the given [element], which is either a [SvgTextElement]
+  /// or a [SvgTspanElement] to fit in [width]. Appends an ellipsis to the text
+  /// if it had to be truncated.
+  /// Calling this method may force a layout on the document. For better
+  /// performance, use [TextMetrics.ellipsizeText].
+  static ellipsizeTextElement() {
+  }
+}
diff --git a/charted/lib/core/text_metrics/segmentation.dart b/charted/lib/core/text_metrics/segmentation.dart
new file mode 100644
index 0000000..45a0d73
--- /dev/null
+++ b/charted/lib/core/text_metrics/segmentation.dart
@@ -0,0 +1,69 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+library charted.core.text_metrics.segmentation;
+
+import "package:collection/algorithms.dart";
+import "package:charted/core/text_metrics/segmentation_utils.dart";
+import "package:charted/core/text_metrics/segmentation_data.dart";
+
+/// Current unicode version.
+/// Character database available at http://www.unicode.org/Public/7.0.0/ucd/
+const UNICODE_VERSION = '7.0.0';
+
+// Code table based on:
+// http://www.unicode.org/Public/7.0.0/ucd/auxiliary/GraphemeBreakTest.html
+// GRAPHEME_BREAK_TABLE[prevType * TYPE_COUNT + curType] == 1 means break.
+const GRAPHEME_BREAK_TABLE = const[
+    1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
+    1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1,
+    1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1,
+    1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1,
+    1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1,
+    1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1,
+    1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0
+];
+
+/// Get type of a given char code.
+int _typeForRune(int rune) {
+  int count = CODE_POINT_BLOCKS.length ~/ 3;
+  int min = 0;
+  int max = count - 1;
+  while (max >= min) {
+    int mid = (max + min) ~/ 2;
+    int idx = mid * 3;
+    if (CODE_POINT_BLOCKS[idx] <= rune && rune <= CODE_POINT_BLOCKS[idx+1]) {
+      return CODE_POINT_BLOCKS[idx+2]; // Return the found character type
+    }
+    if (CODE_POINT_BLOCKS[idx] > rune) {
+      max = mid - 1;
+    }
+    else if (CODE_POINT_BLOCKS[idx+1] < rune) {
+      min = max + 1;
+    }
+  }
+  return CODE_CATEGORY_OTHER; // Defaults to OTHER.
+}
+
+Iterable<int> graphemeBreakIndices(String s) {
+  List<int> indices = [];
+  int previousType = 0;
+  for (var iter = s.runes.iterator; iter.moveNext();) {
+    int currentType = _typeForRune(iter.current);
+    if (GRAPHEME_BREAK_TABLE[previousType * 12 + currentType] == 1) {
+      indices.add(iter.rawIndex);
+    }
+    previousType = currentType;
+  }
+  return indices;
+}
diff --git a/charted/lib/core/text_metrics/segmentation_data.dart b/charted/lib/core/text_metrics/segmentation_data.dart
new file mode 100644
index 0000000..7198bb7
--- /dev/null
+++ b/charted/lib/core/text_metrics/segmentation_data.dart
@@ -0,0 +1,1204 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+// This is a generated file.
+// Please use tool/build_unicode_segmentation-data.dart to update
+//
+
+/// Code ranges by their types for use with grapheme segmentation
+/// of text in charts.
+library charted.core.text_metrics.segmentation_data;
+
+/// Each line in the following list represents a code range.
+/// Start, End, Type.
+const CODE_POINT_BLOCKS = const[
+  0, 9, 3,
+  10, 10, 2,
+  11, 12, 3,
+  13, 13, 1,
+  14, 31, 3,
+  127, 159, 3,
+  173, 173, 3,
+  768, 879, 4,
+  1155, 1159, 4,
+  1160, 1161, 4,
+  1425, 1469, 4,
+  1471, 1471, 4,
+  1473, 1474, 4,
+  1476, 1477, 4,
+  1479, 1479, 4,
+  1536, 1541, 3,
+  1552, 1562, 4,
+  1564, 1564, 3,
+  1611, 1631, 4,
+  1648, 1648, 4,
+  1750, 1756, 4,
+  1757, 1757, 3,
+  1759, 1764, 4,
+  1767, 1768, 4,
+  1770, 1773, 4,
+  1807, 1807, 3,
+  1809, 1809, 4,
+  1840, 1866, 4,
+  1958, 1968, 4,
+  2027, 2035, 4,
+  2070, 2073, 4,
+  2075, 2083, 4,
+  2085, 2087, 4,
+  2089, 2093, 4,
+  2137, 2139, 4,
+  2276, 2306, 4,
+  2307, 2307, 5,
+  2362, 2362, 4,
+  2363, 2363, 5,
+  2364, 2364, 4,
+  2366, 2368, 5,
+  2369, 2376, 4,
+  2377, 2380, 5,
+  2381, 2381, 4,
+  2382, 2383, 5,
+  2385, 2391, 4,
+  2402, 2403, 4,
+  2433, 2433, 4,
+  2434, 2435, 5,
+  2492, 2492, 4,
+  2494, 2494, 4,
+  2495, 2496, 5,
+  2497, 2500, 4,
+  2503, 2504, 5,
+  2507, 2508, 5,
+  2509, 2509, 4,
+  2519, 2519, 4,
+  2530, 2531, 4,
+  2561, 2562, 4,
+  2563, 2563, 5,
+  2620, 2620, 4,
+  2622, 2624, 5,
+  2625, 2626, 4,
+  2631, 2632, 4,
+  2635, 2637, 4,
+  2641, 2641, 4,
+  2672, 2673, 4,
+  2677, 2677, 4,
+  2689, 2690, 4,
+  2691, 2691, 5,
+  2748, 2748, 4,
+  2750, 2752, 5,
+  2753, 2757, 4,
+  2759, 2760, 4,
+  2761, 2761, 5,
+  2763, 2764, 5,
+  2765, 2765, 4,
+  2786, 2787, 4,
+  2817, 2817, 4,
+  2818, 2819, 5,
+  2876, 2876, 4,
+  2878, 2878, 4,
+  2879, 2879, 4,
+  2880, 2880, 5,
+  2881, 2884, 4,
+  2887, 2888, 5,
+  2891, 2892, 5,
+  2893, 2893, 4,
+  2902, 2902, 4,
+  2903, 2903, 4,
+  2914, 2915, 4,
+  2946, 2946, 4,
+  3006, 3006, 4,
+  3007, 3007, 5,
+  3008, 3008, 4,
+  3009, 3010, 5,
+  3014, 3016, 5,
+  3018, 3020, 5,
+  3021, 3021, 4,
+  3031, 3031, 4,
+  3072, 3072, 4,
+  3073, 3075, 5,
+  3134, 3136, 4,
+  3137, 3140, 5,
+  3142, 3144, 4,
+  3146, 3149, 4,
+  3157, 3158, 4,
+  3170, 3171, 4,
+  3201, 3201, 4,
+  3202, 3203, 5,
+  3260, 3260, 4,
+  3262, 3262, 5,
+  3263, 3263, 4,
+  3264, 3265, 5,
+  3266, 3266, 4,
+  3267, 3268, 5,
+  3270, 3270, 4,
+  3271, 3272, 5,
+  3274, 3275, 5,
+  3276, 3277, 4,
+  3285, 3286, 4,
+  3298, 3299, 4,
+  3329, 3329, 4,
+  3330, 3331, 5,
+  3390, 3390, 4,
+  3391, 3392, 5,
+  3393, 3396, 4,
+  3398, 3400, 5,
+  3402, 3404, 5,
+  3405, 3405, 4,
+  3415, 3415, 4,
+  3426, 3427, 4,
+  3458, 3459, 5,
+  3530, 3530, 4,
+  3535, 3535, 4,
+  3536, 3537, 5,
+  3538, 3540, 4,
+  3542, 3542, 4,
+  3544, 3550, 5,
+  3551, 3551, 4,
+  3570, 3571, 5,
+  3633, 3633, 4,
+  3635, 3635, 5,
+  3636, 3642, 4,
+  3655, 3662, 4,
+  3761, 3761, 4,
+  3763, 3763, 5,
+  3764, 3769, 4,
+  3771, 3772, 4,
+  3784, 3789, 4,
+  3864, 3865, 4,
+  3893, 3893, 4,
+  3895, 3895, 4,
+  3897, 3897, 4,
+  3902, 3903, 5,
+  3953, 3966, 4,
+  3967, 3967, 5,
+  3968, 3972, 4,
+  3974, 3975, 4,
+  3981, 3991, 4,
+  3993, 4028, 4,
+  4038, 4038, 4,
+  4127, 4127, 4,
+  4141, 4144, 4,
+  4142, 4142, 4,
+  4145, 4145, 5,
+  4146, 4151, 4,
+  4153, 4154, 4,
+  4155, 4156, 5,
+  4157, 4158, 4,
+  4182, 4183, 5,
+  4184, 4185, 4,
+  4190, 4192, 4,
+  4209, 4212, 4,
+  4226, 4226, 4,
+  4228, 4228, 5,
+  4229, 4230, 4,
+  4237, 4237, 4,
+  4253, 4253, 4,
+  4259, 4259, 4,
+  4352, 4352, 5,
+  4352, 4447, 6,
+  4352, 4352, 4,
+  4352, 4352, 5,
+  4360, 4360, 5,
+  4363, 4363, 3,
+  4370, 4370, 5,
+  4375, 4375, 4,
+  4376, 4376, 5,
+  4387, 4387, 4,
+  4387, 4387, 5,
+  4397, 4397, 4,
+  4400, 4400, 4,
+  4403, 4403, 5,
+  4403, 4403, 4,
+  4403, 4403, 4,
+  4404, 4404, 4,
+  4405, 4405, 4,
+  4427, 4427, 5,
+  4427, 4427, 4,
+  4427, 4427, 5,
+  4427, 4427, 4,
+  4427, 4427, 4,
+  4428, 4428, 5,
+  4442, 4442, 4,
+  4443, 4443, 5,
+  4448, 4519, 7,
+  4451, 4451, 5,
+  4451, 4451, 4,
+  4458, 4458, 4,
+  4458, 4458, 5,
+  4458, 4458, 4,
+  4459, 4459, 4,
+  4459, 4459, 5,
+  4520, 4607, 8,
+  4957, 4959, 4,
+  5906, 5908, 4,
+  5938, 5940, 4,
+  5970, 5971, 4,
+  6002, 6003, 4,
+  6068, 6069, 4,
+  6070, 6070, 5,
+  6071, 6077, 4,
+  6078, 6085, 5,
+  6086, 6086, 4,
+  6087, 6088, 5,
+  6089, 6099, 4,
+  6109, 6109, 4,
+  6155, 6157, 4,
+  6158, 6158, 3,
+  6313, 6313, 4,
+  6432, 6434, 4,
+  6435, 6438, 5,
+  6439, 6440, 4,
+  6441, 6443, 5,
+  6448, 6449, 5,
+  6450, 6450, 4,
+  6451, 6456, 5,
+  6457, 6459, 4,
+  6581, 6583, 5,
+  6586, 6586, 5,
+  6679, 6680, 4,
+  6681, 6682, 5,
+  6683, 6683, 4,
+  6741, 6741, 5,
+  6742, 6742, 4,
+  6743, 6743, 5,
+  6744, 6750, 4,
+  6752, 6752, 4,
+  6754, 6754, 4,
+  6757, 6764, 4,
+  6765, 6770, 5,
+  6771, 6780, 4,
+  6783, 6783, 4,
+  6832, 6845, 4,
+  6846, 6846, 4,
+  6912, 6915, 4,
+  6916, 6916, 5,
+  6964, 6964, 4,
+  6965, 6965, 5,
+  6966, 6970, 4,
+  6971, 6971, 5,
+  6972, 6972, 4,
+  6973, 6977, 5,
+  6978, 6978, 4,
+  6979, 6980, 5,
+  7019, 7027, 4,
+  7040, 7041, 4,
+  7042, 7042, 5,
+  7073, 7073, 5,
+  7074, 7077, 4,
+  7078, 7079, 5,
+  7080, 7081, 4,
+  7082, 7082, 5,
+  7083, 7085, 4,
+  7142, 7142, 4,
+  7143, 7143, 5,
+  7144, 7145, 4,
+  7146, 7148, 5,
+  7149, 7149, 4,
+  7150, 7150, 5,
+  7151, 7153, 4,
+  7154, 7155, 5,
+  7204, 7211, 5,
+  7212, 7219, 4,
+  7220, 7221, 5,
+  7222, 7223, 4,
+  7376, 7378, 4,
+  7380, 7392, 4,
+  7393, 7393, 5,
+  7394, 7400, 4,
+  7405, 7405, 4,
+  7410, 7411, 5,
+  7412, 7412, 4,
+  7416, 7417, 4,
+  7446, 7446, 4,
+  7446, 7446, 5,
+  7446, 7446, 5,
+  7616, 7669, 4,
+  7676, 7679, 4,
+  8203, 8203, 3,
+  8204, 8205, 4,
+  8206, 8207, 3,
+  8232, 8232, 3,
+  8233, 8233, 3,
+  8234, 8238, 3,
+  8288, 8292, 3,
+  8293, 8293, 3,
+  8294, 8303, 3,
+  8400, 8412, 4,
+  8413, 8416, 4,
+  8417, 8417, 4,
+  8418, 8420, 4,
+  8421, 8432, 4,
+  11503, 11505, 4,
+  11647, 11647, 4,
+  11744, 11775, 4,
+  12330, 12333, 4,
+  12334, 12335, 4,
+  12441, 12442, 4,
+  42607, 42607, 4,
+  42608, 42610, 4,
+  42612, 42621, 4,
+  42655, 42655, 4,
+  42736, 42737, 4,
+  43010, 43010, 4,
+  43014, 43014, 4,
+  43019, 43019, 4,
+  43043, 43044, 5,
+  43045, 43046, 4,
+  43047, 43047, 5,
+  43136, 43137, 5,
+  43188, 43203, 5,
+  43204, 43204, 4,
+  43232, 43249, 4,
+  43302, 43309, 4,
+  43335, 43345, 4,
+  43346, 43347, 5,
+  43360, 43388, 6,
+  43392, 43394, 4,
+  43395, 43395, 5,
+  43443, 43443, 4,
+  43444, 43445, 5,
+  43446, 43449, 4,
+  43450, 43451, 5,
+  43452, 43452, 4,
+  43453, 43456, 5,
+  43493, 43493, 4,
+  43561, 43566, 4,
+  43567, 43568, 5,
+  43569, 43570, 4,
+  43571, 43572, 5,
+  43573, 43574, 4,
+  43587, 43587, 4,
+  43596, 43596, 4,
+  43597, 43597, 5,
+  43644, 43644, 4,
+  43696, 43696, 4,
+  43698, 43700, 4,
+  43703, 43704, 4,
+  43710, 43711, 4,
+  43713, 43713, 4,
+  43755, 43755, 5,
+  43756, 43757, 4,
+  43758, 43759, 5,
+  43765, 43765, 5,
+  43766, 43766, 4,
+  44003, 44004, 5,
+  44005, 44005, 4,
+  44006, 44007, 5,
+  44008, 44008, 4,
+  44009, 44010, 5,
+  44012, 44012, 5,
+  44013, 44013, 4,
+  44032, 44032, 9,
+  44033, 44059, 10,
+  44060, 44060, 9,
+  44061, 44087, 10,
+  44088, 44088, 9,
+  44089, 44115, 10,
+  44116, 44116, 9,
+  44117, 44143, 10,
+  44144, 44144, 9,
+  44145, 44171, 10,
+  44172, 44172, 9,
+  44173, 44199, 10,
+  44200, 44200, 9,
+  44201, 44227, 10,
+  44228, 44228, 9,
+  44229, 44255, 10,
+  44256, 44256, 9,
+  44257, 44283, 10,
+  44284, 44284, 9,
+  44285, 44311, 10,
+  44312, 44312, 9,
+  44313, 44339, 10,
+  44340, 44340, 9,
+  44341, 44367, 10,
+  44368, 44368, 9,
+  44369, 44395, 10,
+  44396, 44396, 9,
+  44397, 44423, 10,
+  44424, 44424, 9,
+  44425, 44451, 10,
+  44452, 44452, 9,
+  44453, 44479, 10,
+  44480, 44480, 9,
+  44481, 44507, 10,
+  44508, 44508, 9,
+  44509, 44535, 10,
+  44536, 44536, 9,
+  44537, 44563, 10,
+  44564, 44564, 9,
+  44565, 44591, 10,
+  44592, 44592, 9,
+  44593, 44619, 10,
+  44620, 44620, 9,
+  44621, 44647, 10,
+  44648, 44648, 9,
+  44649, 44675, 10,
+  44676, 44676, 9,
+  44677, 44703, 10,
+  44704, 44704, 9,
+  44705, 44731, 10,
+  44732, 44732, 9,
+  44733, 44759, 10,
+  44760, 44760, 9,
+  44761, 44787, 10,
+  44788, 44788, 9,
+  44789, 44815, 10,
+  44816, 44816, 9,
+  44817, 44843, 10,
+  44844, 44844, 9,
+  44845, 44871, 10,
+  44872, 44872, 9,
+  44873, 44899, 10,
+  44900, 44900, 9,
+  44901, 44927, 10,
+  44928, 44928, 9,
+  44929, 44955, 10,
+  44956, 44956, 9,
+  44957, 44983, 10,
+  44984, 44984, 9,
+  44985, 45011, 10,
+  45012, 45012, 9,
+  45013, 45039, 10,
+  45040, 45040, 9,
+  45041, 45067, 10,
+  45068, 45068, 9,
+  45069, 45095, 10,
+  45096, 45096, 9,
+  45097, 45123, 10,
+  45124, 45124, 9,
+  45125, 45151, 10,
+  45152, 45152, 9,
+  45153, 45179, 10,
+  45180, 45180, 9,
+  45181, 45207, 10,
+  45208, 45208, 9,
+  45209, 45235, 10,
+  45236, 45236, 9,
+  45237, 45263, 10,
+  45264, 45264, 9,
+  45265, 45291, 10,
+  45292, 45292, 9,
+  45293, 45319, 10,
+  45320, 45320, 9,
+  45321, 45347, 10,
+  45348, 45348, 9,
+  45349, 45375, 10,
+  45376, 45376, 9,
+  45377, 45403, 10,
+  45404, 45404, 9,
+  45405, 45431, 10,
+  45432, 45432, 9,
+  45433, 45459, 10,
+  45460, 45460, 9,
+  45461, 45487, 10,
+  45488, 45488, 9,
+  45489, 45515, 10,
+  45516, 45516, 9,
+  45517, 45543, 10,
+  45544, 45544, 9,
+  45545, 45571, 10,
+  45572, 45572, 9,
+  45573, 45599, 10,
+  45600, 45600, 9,
+  45601, 45627, 10,
+  45628, 45628, 9,
+  45629, 45655, 10,
+  45656, 45656, 9,
+  45657, 45683, 10,
+  45684, 45684, 9,
+  45685, 45711, 10,
+  45712, 45712, 9,
+  45713, 45739, 10,
+  45740, 45740, 9,
+  45741, 45767, 10,
+  45768, 45768, 9,
+  45769, 45795, 10,
+  45796, 45796, 9,
+  45797, 45823, 10,
+  45824, 45824, 9,
+  45825, 45851, 10,
+  45852, 45852, 9,
+  45853, 45879, 10,
+  45880, 45880, 9,
+  45881, 45907, 10,
+  45908, 45908, 9,
+  45909, 45935, 10,
+  45936, 45936, 9,
+  45937, 45963, 10,
+  45964, 45964, 9,
+  45965, 45991, 10,
+  45992, 45992, 9,
+  45993, 46019, 10,
+  46020, 46020, 9,
+  46021, 46047, 10,
+  46048, 46048, 9,
+  46049, 46075, 10,
+  46076, 46076, 9,
+  46077, 46103, 10,
+  46104, 46104, 9,
+  46105, 46131, 10,
+  46132, 46132, 9,
+  46133, 46159, 10,
+  46160, 46160, 9,
+  46161, 46187, 10,
+  46188, 46188, 9,
+  46189, 46215, 10,
+  46216, 46216, 9,
+  46217, 46243, 10,
+  46244, 46244, 9,
+  46245, 46271, 10,
+  46272, 46272, 9,
+  46273, 46299, 10,
+  46300, 46300, 9,
+  46301, 46327, 10,
+  46328, 46328, 9,
+  46329, 46355, 10,
+  46356, 46356, 9,
+  46357, 46383, 10,
+  46384, 46384, 9,
+  46385, 46411, 10,
+  46412, 46412, 9,
+  46413, 46439, 10,
+  46440, 46440, 9,
+  46441, 46467, 10,
+  46468, 46468, 9,
+  46469, 46495, 10,
+  46496, 46496, 9,
+  46497, 46523, 10,
+  46524, 46524, 9,
+  46525, 46551, 10,
+  46552, 46552, 9,
+  46553, 46579, 10,
+  46580, 46580, 9,
+  46581, 46607, 10,
+  46608, 46608, 9,
+  46609, 46635, 10,
+  46636, 46636, 9,
+  46637, 46663, 10,
+  46664, 46664, 9,
+  46665, 46691, 10,
+  46692, 46692, 9,
+  46693, 46719, 10,
+  46720, 46720, 9,
+  46721, 46747, 10,
+  46748, 46748, 9,
+  46749, 46775, 10,
+  46776, 46776, 9,
+  46777, 46803, 10,
+  46804, 46804, 9,
+  46805, 46831, 10,
+  46832, 46832, 9,
+  46833, 46859, 10,
+  46860, 46860, 9,
+  46861, 46887, 10,
+  46888, 46888, 9,
+  46889, 46915, 10,
+  46916, 46916, 9,
+  46917, 46943, 10,
+  46944, 46944, 9,
+  46945, 46971, 10,
+  46972, 46972, 9,
+  46973, 46999, 10,
+  47000, 47000, 9,
+  47001, 47027, 10,
+  47028, 47028, 9,
+  47029, 47055, 10,
+  47056, 47056, 9,
+  47057, 47083, 10,
+  47084, 47084, 9,
+  47085, 47111, 10,
+  47112, 47112, 9,
+  47113, 47139, 10,
+  47140, 47140, 9,
+  47141, 47167, 10,
+  47168, 47168, 9,
+  47169, 47195, 10,
+  47196, 47196, 9,
+  47197, 47223, 10,
+  47224, 47224, 9,
+  47225, 47251, 10,
+  47252, 47252, 9,
+  47253, 47279, 10,
+  47280, 47280, 9,
+  47281, 47307, 10,
+  47308, 47308, 9,
+  47309, 47335, 10,
+  47336, 47336, 9,
+  47337, 47363, 10,
+  47364, 47364, 9,
+  47365, 47391, 10,
+  47392, 47392, 9,
+  47393, 47419, 10,
+  47420, 47420, 9,
+  47421, 47447, 10,
+  47448, 47448, 9,
+  47449, 47475, 10,
+  47476, 47476, 9,
+  47477, 47503, 10,
+  47504, 47504, 9,
+  47505, 47531, 10,
+  47532, 47532, 9,
+  47533, 47559, 10,
+  47560, 47560, 9,
+  47561, 47587, 10,
+  47588, 47588, 9,
+  47589, 47615, 10,
+  47616, 47616, 9,
+  47617, 47643, 10,
+  47644, 47644, 9,
+  47645, 47671, 10,
+  47672, 47672, 9,
+  47673, 47699, 10,
+  47700, 47700, 9,
+  47701, 47727, 10,
+  47728, 47728, 9,
+  47729, 47755, 10,
+  47756, 47756, 9,
+  47757, 47783, 10,
+  47784, 47784, 9,
+  47785, 47811, 10,
+  47812, 47812, 9,
+  47813, 47839, 10,
+  47840, 47840, 9,
+  47841, 47867, 10,
+  47868, 47868, 9,
+  47869, 47895, 10,
+  47896, 47896, 9,
+  47897, 47923, 10,
+  47924, 47924, 9,
+  47925, 47951, 10,
+  47952, 47952, 9,
+  47953, 47979, 10,
+  47980, 47980, 9,
+  47981, 48007, 10,
+  48008, 48008, 9,
+  48009, 48035, 10,
+  48036, 48036, 9,
+  48037, 48063, 10,
+  48064, 48064, 9,
+  48065, 48091, 10,
+  48092, 48092, 9,
+  48093, 48119, 10,
+  48120, 48120, 9,
+  48121, 48147, 10,
+  48148, 48148, 9,
+  48149, 48175, 10,
+  48176, 48176, 9,
+  48177, 48203, 10,
+  48204, 48204, 9,
+  48205, 48231, 10,
+  48232, 48232, 9,
+  48233, 48259, 10,
+  48260, 48260, 9,
+  48261, 48287, 10,
+  48288, 48288, 9,
+  48289, 48315, 10,
+  48316, 48316, 9,
+  48317, 48343, 10,
+  48344, 48344, 9,
+  48345, 48371, 10,
+  48372, 48372, 9,
+  48373, 48399, 10,
+  48400, 48400, 9,
+  48401, 48427, 10,
+  48428, 48428, 9,
+  48429, 48455, 10,
+  48456, 48456, 9,
+  48457, 48483, 10,
+  48484, 48484, 9,
+  48485, 48511, 10,
+  48512, 48512, 9,
+  48513, 48539, 10,
+  48540, 48540, 9,
+  48541, 48567, 10,
+  48568, 48568, 9,
+  48569, 48595, 10,
+  48596, 48596, 9,
+  48597, 48623, 10,
+  48624, 48624, 9,
+  48625, 48651, 10,
+  48652, 48652, 9,
+  48653, 48679, 10,
+  48680, 48680, 9,
+  48681, 48707, 10,
+  48708, 48708, 9,
+  48709, 48735, 10,
+  48736, 48736, 9,
+  48737, 48763, 10,
+  48764, 48764, 9,
+  48765, 48791, 10,
+  48792, 48792, 9,
+  48793, 48819, 10,
+  48820, 48820, 9,
+  48821, 48847, 10,
+  48848, 48848, 9,
+  48849, 48875, 10,
+  48876, 48876, 9,
+  48877, 48903, 10,
+  48904, 48904, 9,
+  48905, 48931, 10,
+  48932, 48932, 9,
+  48933, 48959, 10,
+  48960, 48960, 9,
+  48961, 48987, 10,
+  48988, 48988, 9,
+  48989, 49015, 10,
+  49016, 49016, 9,
+  49017, 49043, 10,
+  49044, 49044, 9,
+  49045, 49071, 10,
+  49072, 49072, 9,
+  49073, 49099, 10,
+  49100, 49100, 9,
+  49101, 49127, 10,
+  49128, 49128, 9,
+  49129, 49155, 10,
+  49156, 49156, 9,
+  49157, 49183, 10,
+  49184, 49184, 9,
+  49185, 49211, 10,
+  49212, 49212, 9,
+  49213, 49239, 10,
+  49240, 49240, 9,
+  49241, 49267, 10,
+  49268, 49268, 9,
+  49269, 49295, 10,
+  49296, 49296, 9,
+  49297, 49323, 10,
+  49324, 49324, 9,
+  49325, 49351, 10,
+  49352, 49352, 9,
+  49353, 49379, 10,
+  49380, 49380, 9,
+  49381, 49407, 10,
+  49408, 49408, 9,
+  49409, 49435, 10,
+  49436, 49436, 9,
+  49437, 49463, 10,
+  49464, 49464, 9,
+  49465, 49491, 10,
+  49492, 49492, 9,
+  49493, 49519, 10,
+  49520, 49520, 9,
+  49521, 49547, 10,
+  49548, 49548, 9,
+  49549, 49575, 10,
+  49576, 49576, 9,
+  49577, 49603, 10,
+  49604, 49604, 9,
+  49605, 49631, 10,
+  49632, 49632, 9,
+  49633, 49659, 10,
+  49660, 49660, 9,
+  49661, 49687, 10,
+  49688, 49688, 9,
+  49689, 49715, 10,
+  49716, 49716, 9,
+  49717, 49743, 10,
+  49744, 49744, 9,
+  49745, 49771, 10,
+  49772, 49772, 9,
+  49773, 49799, 10,
+  49800, 49800, 9,
+  49801, 49827, 10,
+  49828, 49828, 9,
+  49829, 49855, 10,
+  49856, 49856, 9,
+  49857, 49883, 10,
+  49884, 49884, 9,
+  49885, 49911, 10,
+  49912, 49912, 9,
+  49913, 49939, 10,
+  49940, 49940, 9,
+  49941, 49967, 10,
+  49968, 49968, 9,
+  49969, 49995, 10,
+  49996, 49996, 9,
+  49997, 50023, 10,
+  50024, 50024, 9,
+  50025, 50051, 10,
+  50052, 50052, 9,
+  50053, 50079, 10,
+  50080, 50080, 9,
+  50081, 50107, 10,
+  50108, 50108, 9,
+  50109, 50135, 10,
+  50136, 50136, 9,
+  50137, 50163, 10,
+  50164, 50164, 9,
+  50165, 50191, 10,
+  50192, 50192, 9,
+  50193, 50219, 10,
+  50220, 50220, 9,
+  50221, 50247, 10,
+  50248, 50248, 9,
+  50249, 50275, 10,
+  50276, 50276, 9,
+  50277, 50303, 10,
+  50304, 50304, 9,
+  50305, 50331, 10,
+  50332, 50332, 9,
+  50333, 50359, 10,
+  50360, 50360, 9,
+  50361, 50387, 10,
+  50388, 50388, 9,
+  50389, 50415, 10,
+  50416, 50416, 9,
+  50417, 50443, 10,
+  50444, 50444, 9,
+  50445, 50471, 10,
+  50472, 50472, 9,
+  50473, 50499, 10,
+  50500, 50500, 9,
+  50501, 50527, 10,
+  50528, 50528, 9,
+  50529, 50555, 10,
+  50556, 50556, 9,
+  50557, 50583, 10,
+  50584, 50584, 9,
+  50585, 50611, 10,
+  50612, 50612, 9,
+  50613, 50639, 10,
+  50640, 50640, 9,
+  50641, 50667, 10,
+  50668, 50668, 9,
+  50669, 50695, 10,
+  50696, 50696, 9,
+  50697, 50723, 10,
+  50724, 50724, 9,
+  50725, 50751, 10,
+  50752, 50752, 9,
+  50753, 50779, 10,
+  50780, 50780, 9,
+  50781, 50807, 10,
+  50808, 50808, 9,
+  50809, 50835, 10,
+  50836, 50836, 9,
+  50837, 50863, 10,
+  50864, 50864, 9,
+  50865, 50891, 10,
+  50892, 50892, 9,
+  50893, 50919, 10,
+  50920, 50920, 9,
+  50921, 50947, 10,
+  50948, 50948, 9,
+  50949, 50975, 10,
+  50976, 50976, 9,
+  50977, 51003, 10,
+  51004, 51004, 9,
+  51005, 51031, 10,
+  51032, 51032, 9,
+  51033, 51059, 10,
+  51060, 51060, 9,
+  51061, 51087, 10,
+  51088, 51088, 9,
+  51089, 51115, 10,
+  51116, 51116, 9,
+  51117, 51143, 10,
+  51144, 51144, 9,
+  51145, 51171, 10,
+  51172, 51172, 9,
+  51173, 51199, 10,
+  51200, 51200, 9,
+  51201, 51227, 10,
+  51228, 51228, 9,
+  51229, 51255, 10,
+  51256, 51256, 9,
+  51257, 51283, 10,
+  51284, 51284, 9,
+  51285, 51311, 10,
+  51312, 51312, 9,
+  51313, 51339, 10,
+  51340, 51340, 9,
+  51341, 51367, 10,
+  51368, 51368, 9,
+  51369, 51395, 10,
+  51396, 51396, 9,
+  51397, 51423, 10,
+  51424, 51424, 9,
+  51425, 51451, 10,
+  51452, 51452, 9,
+  51453, 51479, 10,
+  51480, 51480, 9,
+  51481, 51507, 10,
+  51508, 51508, 9,
+  51509, 51535, 10,
+  51536, 51536, 9,
+  51537, 51563, 10,
+  51564, 51564, 9,
+  51565, 51591, 10,
+  51592, 51592, 9,
+  51593, 51619, 10,
+  51620, 51620, 9,
+  51621, 51647, 10,
+  51648, 51648, 9,
+  51649, 51675, 10,
+  51676, 51676, 9,
+  51677, 51703, 10,
+  51704, 51704, 9,
+  51705, 51731, 10,
+  51732, 51732, 9,
+  51733, 51759, 10,
+  51760, 51760, 9,
+  51761, 51787, 10,
+  51788, 51788, 9,
+  51789, 51815, 10,
+  51816, 51816, 9,
+  51817, 51843, 10,
+  51844, 51844, 9,
+  51845, 51871, 10,
+  51872, 51872, 9,
+  51873, 51899, 10,
+  51900, 51900, 9,
+  51901, 51927, 10,
+  51928, 51928, 9,
+  51929, 51955, 10,
+  51956, 51956, 9,
+  51957, 51983, 10,
+  51984, 51984, 9,
+  51985, 52011, 10,
+  52012, 52012, 9,
+  52013, 52039, 10,
+  52040, 52040, 9,
+  52041, 52067, 10,
+  52068, 52068, 9,
+  52069, 52095, 10,
+  52096, 52096, 9,
+  52097, 52123, 10,
+  52124, 52124, 9,
+  52125, 52151, 10,
+  52152, 52152, 9,
+  52153, 52179, 10,
+  52180, 52180, 9,
+  52181, 52207, 10,
+  52208, 52208, 9,
+  52209, 52235, 10,
+  52236, 52236, 9,
+  52237, 52263, 10,
+  52264, 52264, 9,
+  52265, 52291, 10,
+  52292, 52292, 9,
+  52293, 52319, 10,
+  52320, 52320, 9,
+  52321, 52347, 10,
+  52348, 52348, 9,
+  52349, 52375, 10,
+  52376, 52376, 9,
+  52377, 52403, 10,
+  52404, 52404, 9,
+  52405, 52431, 10,
+  52432, 52432, 9,
+  52433, 52459, 10,
+  52460, 52460, 9,
+  52461, 52487, 10,
+  52488, 52488, 9,
+  52489, 52515, 10,
+  52516, 52516, 9,
+  52517, 52543, 10,
+  52544, 52544, 9,
+  52545, 52571, 10,
+  52572, 52572, 9,
+  52573, 52599, 10,
+  52600, 52600, 9,
+  52601, 52627, 10,
+  52628, 52628, 9,
+  52629, 52655, 10,
+  52656, 52656, 9,
+  52657, 52683, 10,
+  52684, 52684, 9,
+  52685, 52711, 10,
+  52712, 52712, 9,
+  52713, 52739, 10,
+  52740, 52740, 9,
+  52741, 52767, 10,
+  52768, 52768, 9,
+  52769, 52795, 10,
+  52796, 52796, 9,
+  52797, 52823, 10,
+  52824, 52824, 9,
+  52825, 52851, 10,
+  52852, 52852, 9,
+  52853, 52879, 10,
+  52880, 52880, 9,
+  52881, 52907, 10,
+  52908, 52908, 9,
+  52909, 52935, 10,
+  52936, 52936, 9,
+  52937, 52963, 10,
+  52964, 52964, 9,
+  52965, 52991, 10,
+  52992, 52992, 9,
+  52993, 53019, 10,
+  53020, 53020, 9,
+  53021, 53047, 10,
+  53048, 53048, 9,
+  53049, 53075, 10,
+  53076, 53076, 9,
+  53077, 53103, 10,
+  53104, 53104, 9,
+  53105, 53131, 10,
+  53132, 53132, 9,
+  53133, 53159, 10,
+  53160, 53160, 9,
+  53161, 53187, 10,
+  53188, 53188, 9,
+  53189, 53215, 10,
+  53216, 53216, 9,
+  53217, 53243, 10,
+  53244, 53244, 9,
+  53245, 53271, 10,
+  53272, 53272, 9,
+  53273, 53299, 10,
+  53300, 53300, 9,
+  53301, 53327, 10,
+  53328, 53328, 9,
+  53329, 53355, 10,
+  53356, 53356, 9,
+  53357, 53383, 10,
+  53384, 53384, 9,
+  53385, 53411, 10,
+  53412, 53412, 9,
+  53413, 53439, 10,
+  53440, 53440, 9,
+  53441, 53467, 10,
+  53468, 53468, 9,
+  53469, 53495, 10,
+  53496, 53496, 9,
+  53497, 53523, 10,
+  53524, 53524, 9,
+  53525, 53551, 10,
+  53552, 53552, 9,
+  53553, 53579, 10,
+  53580, 53580, 9,
+  53581, 53607, 10,
+  53608, 53608, 9,
+  53609, 53635, 10,
+  53636, 53636, 9,
+  53637, 53663, 10,
+  53664, 53664, 9,
+  53665, 53691, 10,
+  53692, 53692, 9,
+  53693, 53719, 10,
+  53720, 53720, 9,
+  53721, 53747, 10,
+  53748, 53748, 9,
+  53749, 53775, 10,
+  53776, 53776, 9,
+  53777, 53803, 10,
+  53804, 53804, 9,
+  53805, 53831, 10,
+  53832, 53832, 9,
+  53833, 53859, 10,
+  53860, 53860, 9,
+  53861, 53887, 10,
+  53888, 53888, 9,
+  53889, 53915, 10,
+  53916, 53916, 9,
+  53917, 53943, 10,
+  53944, 53944, 9,
+  53945, 53971, 10,
+  53972, 53972, 9,
+  53973, 53999, 10,
+  54000, 54000, 9,
+  54001, 54027, 10,
+  54028, 54028, 9,
+  54029, 54055, 10,
+  54056, 54056, 9,
+  54057, 54083, 10,
+  54084, 54084, 9,
+  54085, 54111, 10,
+  54112, 54112, 9,
+  54113, 54139, 10,
+  54140, 54140, 9,
+  54141, 54167, 10,
+  54168, 54168, 9,
+  54169, 54195, 10,
+  54196, 54196, 9,
+  54197, 54223, 10,
+  54224, 54224, 9,
+  54225, 54251, 10,
+  54252, 54252, 9,
+  54253, 54279, 10,
+  54280, 54280, 9,
+  54281, 54307, 10,
+  54308, 54308, 9,
+  54309, 54335, 10,
+  54336, 54336, 9,
+  54337, 54363, 10,
+  54364, 54364, 9,
+  54365, 54391, 10,
+  54392, 54392, 9,
+  54393, 54419, 10,
+  54420, 54420, 9,
+  54421, 54447, 10,
+  54448, 54448, 9,
+  54449, 54475, 10,
+  54476, 54476, 9,
+  54477, 54503, 10,
+  54504, 54504, 9,
+  54505, 54531, 10,
+  54532, 54532, 9,
+  54533, 54559, 10,
+  54560, 54560, 9,
+  54561, 54587, 10,
+  54588, 54588, 9,
+  54589, 54615, 10,
+  54616, 54616, 9,
+  54617, 54643, 10,
+  54644, 54644, 9,
+  54645, 54671, 10,
+  54672, 54672, 9,
+  54673, 54699, 10,
+  54700, 54700, 9,
+  54701, 54727, 10,
+  54728, 54728, 9,
+  54729, 54755, 10,
+  54756, 54756, 9,
+  54757, 54783, 10,
+  54784, 54784, 9,
+  54785, 54811, 10,
+  54812, 54812, 9,
+  54813, 54839, 10,
+  54840, 54840, 9,
+  54841, 54867, 10,
+  54868, 54868, 9,
+  54869, 54895, 10,
+  54896, 54896, 9,
+  54897, 54923, 10,
+  54924, 54924, 9,
+  54925, 54951, 10,
+  54952, 54952, 9,
+  54953, 54979, 10,
+  54980, 54980, 9,
+  54981, 55007, 10,
+  55008, 55008, 9,
+  55009, 55035, 10,
+  55036, 55036, 9,
+  55037, 55063, 10,
+  55064, 55064, 9,
+  55065, 55091, 10,
+  55092, 55092, 9,
+  55093, 55119, 10,
+  55120, 55120, 9,
+  55121, 55147, 10,
+  55148, 55148, 9,
+  55149, 55175, 10,
+  55176, 55176, 9,
+  55177, 55203, 10,
+  55216, 55238, 7,
+  55243, 55291, 8,
+  55296, 57343, 3,
+  57344, 57344, 3,
+  57344, 57344, 3,
+  64286, 64286, 4,
+  65024, 65039, 4,
+  65056, 65069, 4,
+  65279, 65279, 3,
+  65438, 65439, 4,
+  65520, 65528, 3,
+  65529, 65531, 3
+];
+
diff --git a/charted/lib/core/text_metrics/segmentation_utils.dart b/charted/lib/core/text_metrics/segmentation_utils.dart
new file mode 100644
index 0000000..5ab995d
--- /dev/null
+++ b/charted/lib/core/text_metrics/segmentation_utils.dart
@@ -0,0 +1,35 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+library charted.core.text_metrics.segmentation_utils;
+
+const CODE_CATEGORY_OTHER = 0;
+
+const CodeUnitCategory = const{
+  'Other': CODE_CATEGORY_OTHER,
+  'CR': 1,
+  'LF': 2,
+  'Control': 3,
+  'Extend': 4,
+  'SpacingMark': 5,
+  'L': 6,
+  'V': 7,
+  'T': 8,
+  'LV': 9,
+  'LVT': 10,
+  'Regional_Indicator': 11
+};
+
+class CodeRange {
+  final int start;
+  final int end;
+  final int codePointType;
+  const CodeRange(this.start, this.end, this.codePointType);
+  toString() => "const CodeRange($start, $end, $codePointType)";
+}
+
diff --git a/charted/lib/core/time_interval.dart b/charted/lib/core/time_interval.dart
new file mode 100644
index 0000000..6385312
--- /dev/null
+++ b/charted/lib/core/time_interval.dart
@@ -0,0 +1,136 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+library charted.core.time_intervals;
+
+typedef DateTime TimeFloorFunction(DateTime val);
+typedef DateTime TimeStepFunction(DateTime val, int offset);
+typedef int TimeToNumberFunction(DateTime val);
+
+class TimeInterval {
+  TimeFloorFunction _floor;
+  TimeStepFunction _step;
+  TimeToNumberFunction _number;
+
+  TimeInterval(this._floor, this._step, this._number);
+
+  DateTime floor(dynamic date) {
+    assert(date is int || date is DateTime);
+    if (date is int) {
+      date = new DateTime.fromMillisecondsSinceEpoch(date) ;
+    }
+    return _floor(date);
+  }
+
+  DateTime round(dynamic date) {
+    DateTime d0 = floor(date),
+             d1 = offset(d0, 1);
+    int ms = date is int ? date : date.millisecondsSinceEpoch;
+    return (ms - d0.millisecondsSinceEpoch < d1.millisecondsSinceEpoch - ms)
+        ? d0
+        : d1;
+  }
+
+  DateTime ceil(dynamic date) => offset(floor(date), 1);
+
+  DateTime offset(DateTime date, int k) => _step(date, k);
+
+  Iterable<DateTime> range(dynamic t0, dynamic t1, int dt) {
+    assert(t0 is int || t0 is DateTime);
+    assert(t1 is int || t1 is DateTime);
+
+    List<DateTime> values = [];
+    if (t1 is int) {
+      t1 = new DateTime.fromMillisecondsSinceEpoch(t1);
+    }
+
+    DateTime time = ceil(t0);
+    if (dt > 1) {
+      while (time.isBefore(t1)) {
+        if ((_number(time) % dt) == 0) {
+          values.add(
+              new DateTime.fromMillisecondsSinceEpoch(
+                  time.millisecondsSinceEpoch));
+        }
+        time = _step(time, 1);
+      }
+    }
+    else {
+      while (time.isBefore(t1)) {
+        values.add(
+            new DateTime.fromMillisecondsSinceEpoch(
+                time.millisecondsSinceEpoch));
+        time = _step(time, 1);
+      }
+    }
+    return values;
+  }
+
+  static TimeInterval second = new TimeInterval(
+      (DateTime date) =>
+          new DateTime.fromMillisecondsSinceEpoch(
+              (date.millisecondsSinceEpoch ~/ 1000) * 1000),
+      (DateTime date, int offset) =>
+          date = new DateTime.fromMillisecondsSinceEpoch(
+              date.millisecondsSinceEpoch + offset * 1000),
+      (DateTime date) => date.second);
+
+  static TimeInterval minute = new TimeInterval(
+      (DateTime date) =>
+          new DateTime.fromMillisecondsSinceEpoch(
+              (date.millisecondsSinceEpoch ~/ 60000) * 60000),
+      (DateTime date, int offset) =>
+          date = new DateTime.fromMillisecondsSinceEpoch(
+              date.millisecondsSinceEpoch + offset * 60000),
+      (DateTime date) => date.minute);
+
+  static TimeInterval hour = new TimeInterval(
+      (DateTime date) =>
+          new DateTime.fromMillisecondsSinceEpoch(
+              (date.millisecondsSinceEpoch ~/ 3600000) * 3600000),
+      (DateTime date, int offset) =>
+          date = new DateTime.fromMillisecondsSinceEpoch(
+              date.millisecondsSinceEpoch + offset * 3600000),
+      (DateTime date) => date.hour);
+
+  static TimeInterval day = new TimeInterval(
+      (DateTime date) =>
+          new DateTime(date.year, date.month, date.day),
+      (DateTime date, int offset) =>
+          new DateTime(date.year, date.month, date.day + offset,
+              date.hour, date.minute, date.second, date.millisecond),
+      (DateTime date) => date.day - 1);
+
+  static TimeInterval week = new TimeInterval(
+      (DateTime date) =>
+          new DateTime(date.year, date.month, date.day - (date.weekday % 7)),
+      (DateTime date, int offset) =>
+          new DateTime(date.year, date.month, date.day + offset * 7,
+              date.hour, date.minute, date.second, date.millisecond ),
+      (DateTime date) {
+        var day = year.floor(date).day;
+        return (dayOfYear(date) +  day % 7) ~/ 7;
+      });
+
+  static TimeInterval month = new TimeInterval(
+      (DateTime date) => new DateTime(date.year, date.month, 1),
+      (DateTime date, num offset) =>
+          new DateTime(date.year, date.month + offset, date.day,
+              date.hour, date.minute, date.second, date.millisecond),
+      (DateTime date) => date.month - 1);
+
+  static TimeInterval year = new TimeInterval(
+      (DateTime date) => new DateTime(date.year),
+      (DateTime date, num offset) =>
+          new DateTime(date.year + offset, date.month, date.day,
+              date.hour, date.minute, date.second, date.millisecond),
+      (DateTime date) => date.year);
+
+  static int dayOfYear(DateTime date) =>
+      date.difference(year.floor(date)).inDays;
+}
diff --git a/charted/lib/core/timer.dart b/charted/lib/core/timer.dart
new file mode 100644
index 0000000..d464e63
--- /dev/null
+++ b/charted/lib/core/timer.dart
@@ -0,0 +1,102 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+/// A [window.requestAnimationFrame] based timer for use with transitions.
+/// Uses [dart.async.Timer] when the time until next timeout is too long.
+library charted.core.timer;
+
+import 'dart:async';
+import 'dart:html' show window;
+import 'dart:collection';
+
+typedef bool TimerCallback(int time);
+
+class AnimationTimer extends LinkedListEntry {
+  static LinkedList<AnimationTimer> _queue = new LinkedList<AnimationTimer>();
+
+  /// true if we are already waiting for window.animationFrame. At any given
+  /// time, only one of _timerScheduled and _animationFrameRequested are set
+  static bool _animationFrameRequested = false;
+
+  /// Instance of currently scheduled timer. At any given time, only one
+  /// of _timerScheduled and _animationFrameRequested are set.
+  static Timer _timerScheduled;
+
+  /// Currently active timer.
+  static AnimationTimer active;
+
+  /// Callback function that is called when the timer is fired.
+  final TimerCallback callback;
+
+  /// Start time of the animation.
+  final int time;
+
+  /// Indicates if the timer is finished (the callback returned true)
+  bool _finished = false;
+
+  /// Schedule a new [callback] to be called [delay] micro-seconds after
+  /// [then] micro-seconds since epoch.
+  AnimationTimer(this.callback, { int delay: 0, int then: null })
+      : time = then == null
+          ? new DateTime.now().millisecondsSinceEpoch + delay
+          : then + delay {
+    _queue.add(this);
+    if (!_animationFrameRequested) {
+      if (_timerScheduled != null) {
+        _timerScheduled.cancel();
+      }
+      _animationFrameRequested = true;
+      window.animationFrame.then(_step);
+    }
+  }
+
+  /// Iterate through all timers, call the callbacks where necessary and
+  /// return milliseconds until next timer.
+  static int flush() {
+    int now = new DateTime.now().millisecondsSinceEpoch;
+    int earliest = null;
+    AnimationTimer timer = _queue.isEmpty ? null : _queue.first;
+
+    while(timer != null) {
+      bool finished = false;
+      AnimationTimer ref = timer;
+
+      if (now > timer.time) {
+        active = timer;
+        finished = timer.callback(now - timer.time);
+      }
+      if (!finished && (earliest == null || timer.time < earliest)) {
+        earliest = timer.time;
+      }
+      timer = timer.next;
+      if (finished) ref.unlink();
+    }
+    active = null;
+    return earliest == null ? earliest : earliest - now;
+  }
+
+  /// Internal timer and animation frame handler.
+  _step([_]) {
+    int delay = flush();
+
+    if (delay == null) {
+      _animationFrameRequested = false;
+    }
+    else if (delay > 24) {
+      if (_timerScheduled != null) {
+        _timerScheduled.cancel();
+      }
+      _timerScheduled = new Timer(new Duration(milliseconds: delay), _step);
+      _animationFrameRequested = false;
+    }
+    else {
+      _animationFrameRequested = true;
+      window.animationFrame.then(_step);
+    }
+  }
+}
diff --git a/charted/lib/core/utils.dart b/charted/lib/core/utils.dart
new file mode 100644
index 0000000..febf9e6
--- /dev/null
+++ b/charted/lib/core/utils.dart
@@ -0,0 +1,50 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+/// A collection of utilities for use by rest of the library and it's users
+library charted.core.utils;
+
+import "dart:html" show Element, NodeTreeSanitizer;
+import "dart:math" as math;
+import "dart:async";
+
+import "package:intl/intl.dart" show BidiFormatter, Bidi, TextDirection;
+import "package:collection/collection.dart";
+import "package:quiver/core.dart";
+
+part 'utils/color.dart';
+part 'utils/disposer.dart';
+part 'utils/lists.dart';
+part 'utils/math.dart';
+part 'utils/namespace.dart';
+part 'utils/object_factory.dart';
+part 'utils/rect.dart';
+part 'utils/bidi_formatter.dart';
+
+const String ORIENTATION_LEFT   = 'left';
+const String ORIENTATION_RIGHT  = 'right';
+const String ORIENTATION_TOP    = 'top';
+const String ORIENTATION_BOTTOM = 'bottom';
+
+/// Identity function that returns the value passed as it's parameter.
+identityFunction(x) => x;
+
+/// Function that formats a value to String.
+typedef String FormatFunction(value);
+
+/// Test if the given String or Iterable, [val] is null or empty
+bool isNullOrEmpty(val) {
+  assert(val == null || val is String || val is Iterable);
+  return val == null || val.isEmpty;
+}
+
+/// An empty tree sanitizer for use with Element.html
+/// This sanitizer must not be used when attaching user input to the DOM.
+class NullTreeSanitizer implements NodeTreeSanitizer {
+  void sanitizeTree(_) {}
+}
diff --git a/charted/lib/core/utils/bidi_formatter.dart b/charted/lib/core/utils/bidi_formatter.dart
new file mode 100644
index 0000000..a072673
--- /dev/null
+++ b/charted/lib/core/utils/bidi_formatter.dart
@@ -0,0 +1,57 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.core.utils;
+
+/// Charts are always drawn with LTR context.
+BidiFormatter _bidiFormatter = new BidiFormatter.LTR();
+
+/// Fix direction of HTML using <span dir="..."> for RTL when required
+fixMarkupDirection(String markup) =>
+    _bidiFormatter.wrapWithSpan(markup, isHtml:true);
+
+/// Fix direction of text using unicode markers for RTL when required
+/// This is a simplified version of BidiFormatter.wrapWithUnicode that
+/// is meant to be used for small labels only (Eg: axis ticks).
+String fixSimpleTextDirection(String text) {
+  TextDirection direction = estimateDirectionOfSimpleText(text);
+  if (TextDirection.RTL == direction) {
+    var result = text;
+    var marker = direction == TextDirection.RTL ? Bidi.RLE : Bidi.LRE;
+    return "${marker}$text${Bidi.PDF}";
+  }
+  return text;
+}
+
+/// Estimates direction of simple text.
+/// This is a simplified version of Bidi.estimateDirectionOfText
+var _spaceRegExp = new RegExp(r'\s+');
+var _digitsRegExp = new RegExp(r'\d');
+TextDirection estimateDirectionOfSimpleText(String text) {
+  var rtlCount = 0,
+      total = 0,
+      hasWeaklyLtr = false,
+      tokens = text.split(_spaceRegExp);
+
+  for (int i = 0, len = tokens.length; i < len; ++i) {
+    var token = tokens.elementAt(i);
+    if (Bidi.startsWithRtl(token)) {
+      rtlCount++;
+      total++;
+    } else if (Bidi.hasAnyLtr(token)) {
+      total++;
+    } else if (_digitsRegExp.hasMatch(token)) {
+      hasWeaklyLtr = true;
+    }
+  }
+  if (total == 0) {
+    return hasWeaklyLtr ? TextDirection.LTR : TextDirection.UNKNOWN;
+  } else {
+    return rtlCount > 0.4 * total ? TextDirection.RTL : TextDirection.LTR;
+  }
+}
diff --git a/charted/lib/core/utils/color.dart b/charted/lib/core/utils/color.dart
new file mode 100644
index 0000000..bac12ec
--- /dev/null
+++ b/charted/lib/core/utils/color.dart
@@ -0,0 +1,307 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.core.utils;
+
+/// Represents a single color for use in visualizations.  Currently, supports
+/// representing and conversion between RGB, RGBA, HSL, HSLA and hex formats.
+class Color {
+  // Internal representation of RGB
+  int _r = 0;
+  int _g = 0;
+  int _b = 0;
+
+  // Internal representation of HSL
+  int _h = 0;   // 0 <= _h <= 360
+  int _s = 0;   // 0 <= _s <= 100
+  int _l = 0;   // 0 <= _l <= 100
+
+  // Alpha value for this color
+  double _a = 0.0;
+
+  // Flags to indicate the color-space for which values are readily available.
+  bool _hasRgbColors = false;
+  bool _hasHslColors = false;
+
+  /// Create an instance from RGB colors.
+  Color.fromRgba(this._r, this._g, this._b, this._a) : _hasRgbColors = true;
+
+  /// Create an instance using a string representing color in RGB color space.
+  /// The input string, [value], can be one of the following formats:
+  /// 
+  ///    #RGB
+  ///    #RRGGBB
+  ///    
+  ///    rgb(R, G, B)
+  ///    rgba(R, G, B, A)
+  /// 
+  /// R, G and B represent intensities of Red, Green and Blue channels and A
+  /// represents the alpha channel (transparency)
+  /// 
+  /// When using these formats:
+  ///     0 <= R,G,B <= 255
+  ///     0 <= A <= 1.0
+  factory Color.fromRgbString(String value) =>
+      isHexColorString(value) ? _fromHexString(value) : _fromRgbString(value); 
+  
+  /// Create an instance from HSL colors.
+  Color.fromHsla(this._h, this._s, this._l, this._a) : _hasHslColors = true;
+
+  /// Create an instance using a string representing color in HSL color space.
+  /// The input string, [value], can be in one of the following formats:
+  /// 
+  ///     hsl(H, S%, L%)
+  ///     hsla(H, S%, L%, A)
+  /// 
+  /// H, S and L represent Hue, Saturation and Luminosity respectively.
+  /// 
+  /// When using these formats:
+  ///     0 <= H <= 360
+  ///     0 <= S,L <= 100
+  ///     0 <= A <= 1.0
+  factory Color.fromHslString(String value) => _fromHslString(value);
+
+  /// Ensures that the RGB values are available. If they aren't already
+  /// available, they are computed from the HSA values.
+  ///
+  /// Based on color.js from Google Closure library
+  void toRgb() {
+    if (_hasRgbColors) return;
+
+    num _hueToRgb(num v1, num v2, num vH) {
+      vH %= 1.0;
+
+      if ((6 * vH) < 1) {
+        return (v1 + (v2 - v1) * 6 * vH);
+      } else if (2 * vH < 1) {
+        return v2;
+      } else if (3 * vH < 2) {
+        return (v1 + (v2 - v1) * ((2 / 3) - vH) * 6);
+      }
+      return v1;
+    }
+
+    final h = _h / 360;
+
+    if (_s == 0) {
+      _r = _g = _b = (_l * 255).round();
+    } else {
+      var temp1 = 0;
+      var temp2 = 0;
+      if (_l < 0.5) {
+        temp2 = _l * (1 + _s);
+      } else {
+        temp2 = _l + _s - (_s * _l);
+      }
+      temp1 = 2 * _l - temp2;
+      _r = (255 * _hueToRgb(temp1, temp2, h + (1 / 3))).round();
+      _g = (255 * _hueToRgb(temp1, temp2, h)).round();
+      _b = (255 * _hueToRgb(temp1, temp2, h - (1 / 3))).round();
+    }
+  }
+
+  /// Ensures that the HSA values are available. If they aren't already
+  /// available, they are computed from the RGB values.
+  ///
+  /// Based on color.js in Google Closure library.
+  void toHsl() {
+    if (_hasHslColors) return;
+
+    final r = _r / 255;
+    final g = _g / 255;
+    final b = _b / 255;
+    final max = math.max(r, math.max(g, b));
+    final min = math.min(r, math.min(g, b));
+
+    double l = (max + min) / 2;
+    double h = 0.0;
+    double s = 0.0;
+
+    // If max and min are equal, the color is gray (h = s = 0)
+    if (max != min) {
+      if (max == r) {
+        h = 60 * (g - b) / (max - min);
+      } else if (max == g) {
+        h = 60 * (b - r) / (max - min) + 120;
+      } else if (max == b) {
+        h = 60 * (r - g) / (max - min) + 240;
+      }
+
+      if (0 < l && l <= 0.5) {
+        s = (max - min) / (2 * l);
+      } else {
+        s = (max - min) / (2 - 2 * l);
+      }
+    }
+
+    _h = (h % 360).floor();
+    _s = (s * 100).floor();
+    _l = (l * 100).floor();
+  }
+
+  /// Returns a hex string of the format '#RRGGBB' representing this color.
+  /// A new string will be returned each time this function is called.
+  String toHexString() {
+    toRgb();
+    return rgbToHexString(_r, _g, _b);
+  }
+
+  /// Returns a string similar to 'rgba(r, g, b, a)' representing this color.
+  /// A new string will be returned each time this function is called.
+  String toRgbaString() {
+    toRgb();
+    return 'rgba($_r,$_g,$_b,$_a)';
+  }
+
+  /// Returns a string similar to 'hsla(h, s, l, a)' representing this color.
+  /// A new string will be returned each time this function is called.
+  String toHslaString() {
+    toHsl();
+    return 'hsla($_h,$_s%,$_l%,$_a)';
+  }
+
+  /// Intensity of red from RGB.
+  /// Computes RGB values if they are not already available.
+  int get r {
+    toRgb();
+    return _r;
+  }
+
+  /// Intensity of green from RGB
+  /// Computes RGB values if they are not already available.
+  int get g {
+    toRgb();
+    return _g;
+  }
+
+  /// Intensity of blue from RGB.
+  /// Computes RGB values if they are not already available.
+  int get b {
+    toRgb();
+    return _b;
+  }
+
+  /// Hue value from HSL representation.
+  /// Computes HSL values if they are not already available.
+  int get h {
+    toHsl();
+    return _h;
+  }
+
+  /// Saturation value from HSL representation.
+  /// Computes HSL values if they are not already available.
+  int get s {
+    toHsl();
+    return _s;
+  }
+
+  /// Luminosity value from HSL representation.
+  /// Computes HSL values if they are not already available.
+  int get l {
+    toHsl();
+    return _l;
+  }
+
+  /// Alpha value used by both RGB and HSL representations.
+  double get a => _a;
+
+  @override
+  String toString() => _hasRgbColors ? toRgbaString() : toHslaString();
+
+  @override
+  int get hashCode => toString().hashCode;
+
+  /// Given RGB values create hex string from it.
+  static String rgbToHexString(int r, int g, int b) {
+    String _hexify(int v) {
+      return v < 0x10
+          ? "0" + math.max(0, v).toInt().toRadixString(16)
+          : math.min(255, v).toInt().toRadixString(16);
+    }
+    return '#${_hexify(r)}${_hexify(g)}${_hexify(b)}';
+  }
+
+  /// RegExp to test if a given string is a hex color string
+  static final RegExp hexColorRegExp =
+      new RegExp(r'^#([0-9a-f]{3}){1,2}$', caseSensitive: false);
+
+  /// Tests if [str] is a hex color
+  static bool isHexColorString(String str) => hexColorRegExp.hasMatch(str);
+
+  /// RegExp to test if a given string is rgb() or rgba() color.
+  static final RegExp rgbaColorRegExp = new RegExp(
+      r'^(rgb|rgba)?\(\d+,\s?\d+,\s?\d+(,\s?(0|1)?(\.\d)?\d*)?\)$',
+      caseSensitive: false);
+
+  /// Tests if [str] is a color represented by rgb() or rgba() or hex string
+  static bool isRgbColorString(String str) =>
+      isHexColorString(str) || rgbaColorRegExp.hasMatch(str);
+
+  /// RegExp to test if a given string is hsl() or hsla() color.
+  static final RegExp hslaColorRegExp = new RegExp(
+      r'^(hsl|hsla)?\(\d+,\s?\d+%,\s?\d+%(,\s?(0|1)?(\.\d)?\d*)?\)$',
+      caseSensitive: false);
+
+  /// Tests if [str] is a color represented by hsl() or hsla()
+  static bool isHslColorString(String str) => hslaColorRegExp.hasMatch(str);
+  
+  /// Create an instance using the passed RGB string.
+  static Color _fromRgbString(String value) {
+    int pos =
+        (value.startsWith('rgb(') || value.startsWith('RGB(')) ? 4 :
+        (value.startsWith('rgba(') || value.startsWith('RGBA(')) ? 5 : 0;
+    if (pos != 0) {
+      final params = value.substring(pos, value.length - 1).split(',');
+      int r = int.parse(params[0]),
+          g = int.parse(params[1]),
+          b = int.parse(params[2]);
+      double a = params.length == 3 ? 1.0 : double.parse(params[3]);
+      return new Color.fromRgba(r, g, b, a);
+    }
+    return new Color.fromRgba(0, 0, 0, 0.0);
+  }
+
+  /// Create an instance using the passed HEX string.
+  /// Assumes that the string starts with a '#' before HEX chars.
+  static Color _fromHexString(String hex) {
+    if (isNullOrEmpty(hex) || (hex.length != 4 && hex.length != 7)) {
+      return new Color.fromRgba(0, 0, 0, 0.0);
+    }
+    int rgb = 0;
+
+    hex = hex.substring(1);
+    if (hex.length == 3) {
+      for (final char in hex) {
+        final val = int.parse(char, radix: 16);
+        rgb = (rgb * 16 + val ) * 16 + val;
+      }
+    }
+    else if (hex.length == 6){
+      rgb = int.parse(hex, radix: 16);
+    }
+
+    return new Color.fromRgba(
+        (rgb & 0xff0000) >> 0x10, (rgb & 0xff00) >> 8, (rgb & 0xff), 1.0);
+  }
+
+  /// Create an instance using the passed HSL color string.
+  static Color _fromHslString(String value) {
+    int pos =
+        (value.startsWith('hsl(') || value.startsWith('HSL(')) ? 4 :
+        (value.startsWith('hsla(') || value.startsWith('HSLA(')) ? 5 : 0;
+    if (pos != 0) {
+      final params = value.substring(pos, value.length - 1).split(',');
+      int h = int.parse(params[0]),
+          s = int.parse(params[1]),
+          l = int.parse(params[2]);
+      double a = params.length == 3 ? 1.0 : double.parse(params[3]);
+      return new Color.fromHsla(h, s, l, a);
+    }
+    return new Color.fromHsla(0, 0, 0, 0.0);
+  }
+}
diff --git a/charted/lib/core/utils/disposer.dart b/charted/lib/core/utils/disposer.dart
new file mode 100644
index 0000000..acc8062
--- /dev/null
+++ b/charted/lib/core/utils/disposer.dart
@@ -0,0 +1,40 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.core.utils;
+
+class SubscriptionsDisposer {
+  List<StreamSubscription> _subscriptions = [];
+  Expando<StreamSubscription> _byObject = new Expando();
+
+  void add(StreamSubscription value, [Object handle]) {
+    if (handle != null) _byObject[handle] = value;
+    _subscriptions.add(value);
+  }
+
+  void addAll(Iterable<StreamSubscription> values, [Object handle]) {
+    for (var subscription in values) {
+      add(subscription, handle);
+    }
+  }
+
+  void unsubscribe(Object handle) {
+    StreamSubscription s = _byObject[handle];
+    if (s != null) {
+      _subscriptions.remove(s);
+      s.cancel();
+    }
+  }
+
+  void dispose() {
+    _subscriptions.forEach((StreamSubscription val) {
+      if (val != null) val.cancel();
+    });
+    _subscriptions = [];
+  }
+}
diff --git a/charted/lib/core/utils/lists.dart b/charted/lib/core/utils/lists.dart
new file mode 100644
index 0000000..d9633f9
--- /dev/null
+++ b/charted/lib/core/utils/lists.dart
@@ -0,0 +1,112 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.core.utils;
+
+/// Returns a sum of all values in the given list of values
+num sum(List values) =>
+    values == null || values.isEmpty ?
+        0: values.fold(0.0, (old, next) => old + next);
+
+/// Returns the smallest number in the given list of values
+num min(Iterable values) =>
+    values == null || values.isEmpty ?
+        null : values.fold(values.elementAt(0), math.min);
+
+/// Returns the largest number in the given list of values
+num max(Iterable values) =>
+    values == null || values.isEmpty ?
+        null : values.fold(values.elementAt(0), math.max);
+
+/// Represents a constant pair of values
+class Pair<T1, T2> {
+  final T1 first;
+  final T2 last;
+  
+  const Pair(this.first, this.last);
+  
+  bool operator==(other) =>
+      other is Pair && first == other.first && last == other.last;
+  
+  int get hashCode => hash2(first, last);
+}
+
+/// Represents a pair of mininum and maximum values in a List.
+class Extent<T> extends Pair<T, T> {
+  final T min;
+  final T max;
+
+  factory Extent.items(Iterable<T> items,
+      [ Comparator compare = Comparable.compare ]) {
+    if (items.length == 0) return new Extent(null, null);
+    var max = items.first,
+        min = items.first;
+    for (var value in items) {
+      if (compare(max, value) < 0) max = value;
+      if (compare(min, value) > 0) min = value;
+    }
+    return new Extent(min, max);
+  }
+
+  const Extent(T min, T max) : min = min, max = max, super(min, max);
+}
+
+/// Iterable representing a range of values containing the start, stop
+/// and each of the step values between them.
+class Range extends DelegatingList<num> {
+  final num start;
+  final num stop;
+  final num step;
+  
+  factory Range.integers(num start, [num stop, num step = 1]) =>
+      new Range(start, stop, step, true);
+
+  factory Range(num start, [num stop, num step = 1, bool integers = false]) {
+    List<num> values = <num>[];
+    
+    if (stop == null) {
+      stop = start;
+      start = 0;
+    }
+
+    if (step == 0 || start < stop && step < 0 || start > stop && step > 0) {
+      throw new ArgumentError('Invalid range.');
+    }
+
+    var k = _integerConversionFactor(step.abs()),
+        i = -1,
+        j;
+
+    start *= k;
+    stop *= k;
+    step *= k;
+
+    if (step < 0) {
+      while ((j = start + step * ++i) > stop) {
+        values.add(integers ? j ~/ k : j / k);
+      }
+    } else {
+      while ((j = start + step * ++i) < stop) {
+        values.add(integers ? j ~/ k : j / k);
+      }
+    }
+    
+    return new Range._internal(start, stop, step, values);
+  }
+  
+  Range._internal(this.start, this.stop, this.step, List values)
+      : super(values);
+
+  static int _integerConversionFactor(num val) {
+    int k = 1;
+    while (val * k % 1 > 0) {
+      k *= 10;
+    }
+    return k;
+  }
+}
diff --git a/charted/lib/core/utils/math.dart b/charted/lib/core/utils/math.dart
new file mode 100644
index 0000000..7b8206c
--- /dev/null
+++ b/charted/lib/core/utils/math.dart
@@ -0,0 +1,48 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.core.utils;
+
+/// Mathematical constant PI
+const double PI = math.PI;
+
+/// PI * 0.5
+const double HALF_PI = PI / 2.0;
+
+/// PI * 2
+/// Ratio constant of a circle's circumference to radius
+const double TAU = PI * 2.0;
+
+/// An arbitrary small possible number
+const double EPSILON = 1e-6;
+
+/// EPSILON * EPSILON, where EPSILON is a arbitrarily small positive number
+const double EPSILON_SQUARE = EPSILON * EPSILON;
+
+/// Maximum value of Dart SMI.
+/// On 32 bit machines, numbers above this have an additional lookup overhead.
+const int SMALL_INT_MAX = (1 << 30) - 1;
+
+/// Minimum value of Dart SMI.
+/// On 32 bit machines, numbers below this have an additional lookup overhead.
+const int SMALL_INT_MIN = -1 * (1 << 30);
+
+/// Hyperbolic cosine.
+num cosh(num x) => ((x = math.exp(x)) + 1 / x) / 2;
+
+/// Hyperbolic sine.
+num sinh(num x) => ((x = math.exp(x)) - 1 / x) / 2;
+
+/// Hyperbolic tangent.
+num tanh(num x) => ((x = math.exp(2 * x)) - 1) / (x + 1);
+
+/// Converts [degrees] to radians.
+num toRadians(num degrees) => degrees * math.PI / 180.0;
+
+/// Converts [radians] to degrees.
+num toDegrees(num radians) => radians * 180.0 / math.PI;
diff --git a/charted/lib/core/utils/namespace.dart b/charted/lib/core/utils/namespace.dart
new file mode 100644
index 0000000..7d1d8d4
--- /dev/null
+++ b/charted/lib/core/utils/namespace.dart
@@ -0,0 +1,65 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.core.utils;
+
+/// Basic namespace handing for Charted - includes utilities to
+/// parse the namespace prefixes and to create DOM elements using a
+/// namespace.
+class Namespace {
+  /// Supported namespace prefixes mapped to their URIs.
+  static const Map<String,String> NS_PREFIXES = const {
+    "svg": "http://www.w3.org/2000/svg",
+    "xhtml": "http://www.w3.org/1999/xhtml",
+    "xlink": "http://www.w3.org/1999/xlink",
+    "xml": "http://www.w3.org/XML/1998/namespace",
+    "xmlns": "http://www.w3.org/2000/xmlns/"
+  };
+
+  /// Create an element from [tag]. If tag is prefixed with a
+  /// supported namespace prefix, the created element will
+  /// have the namespaceUri set to the correct URI.
+  static Element createChildElement(String tag, Element parent) {
+    var separatorIndex = tag.indexOf(':');
+    if (separatorIndex == -1 && parent != null) {
+      return parent.ownerDocument.createElementNS(parent.namespaceUri, tag);
+    }
+    Namespace parsed = new Namespace._internal(tag, separatorIndex);
+    return parsed.namespaceUri == null ?
+        parent.ownerDocument.createElementNS(parent.namespaceUri, tag) :
+        parent.ownerDocument.createElementNS(parsed.namespaceUri,
+            parsed.localName);
+  }
+
+  /// Local part of the Element's tag name.
+  String localName;
+
+  /// Name space URI for the selected namespace.
+  String namespaceUri;
+
+  /// Parses a tag for namespace prefix and local name.
+  /// If a known namespace prefix is found, sets the namespaceUri property
+  /// to the URI of the namespace.
+  factory Namespace(String tagName) =>
+      new Namespace._internal(tagName, tagName.indexOf(':'));
+
+  /// Utility for use by createChildElement and factory constructor.
+  Namespace._internal(String tagName, int separatorIdx) {
+    String prefix = tagName;
+    if (separatorIdx >= 0) {
+      prefix = tagName.substring(0, separatorIdx);
+      localName = tagName.substring(separatorIdx + 1);
+    }
+
+    if (NS_PREFIXES.containsKey(prefix)) {
+      namespaceUri = NS_PREFIXES[prefix];
+    } else {
+      localName = tagName;
+    }
+  }
+}
diff --git a/charted/lib/core/utils/object_factory.dart b/charted/lib/core/utils/object_factory.dart
new file mode 100644
index 0000000..7ddab07
--- /dev/null
+++ b/charted/lib/core/utils/object_factory.dart
@@ -0,0 +1,40 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.core.utils;
+
+typedef T ObjectCreator<T>();
+
+/// Provides a registry and factory service.
+///
+/// Registration:
+/// ObjectFactory.register(“type”, () => { new TypeCreator(); });
+///
+/// Usage:
+/// instance = ObjectFactory.create('type');
+class ObjectFactory<T> {
+  Map<String, ObjectCreator<T>> _components = {};
+
+  /// Register a component [creator] for [name].
+  void register(String name, ObjectCreator<T> creator) {
+    _components[name] = creator;
+  }
+
+  /// Create an instance for [name].
+  T create(String name) {
+    if (!_components.containsKey(name)) {
+      throw new ArgumentError('Element $name not found in ComponentFactory');
+    }
+    var creator = _components[name],
+        instance = creator();
+    if (instance == null) {
+      throw new ArgumentError('Component $name initialization failed.');
+    }
+    return instance;
+  }
+}
diff --git a/charted/lib/core/utils/rect.dart b/charted/lib/core/utils/rect.dart
new file mode 100644
index 0000000..628a4b5
--- /dev/null
+++ b/charted/lib/core/utils/rect.dart
@@ -0,0 +1,62 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.core.utils;
+
+/// Interface representing size and position of an element
+class Rect {
+  final num x;
+  final num y;
+  final num width;
+  final num height;
+
+  const Rect([this.x = 0, this.y = 0, this.width = 0, this.height = 0]);
+  const Rect.size(this.width, this.height) : x = 0, y = 0;
+  const Rect.position(this.x, this.y) : width = 0, height = 0;
+
+  bool operator==(other) =>
+      other is Rect && isSameSizeAs(other) && isSamePositionAs(other);
+
+  bool isSameSizeAs(Rect other) =>
+      other != null && width == other.width && height == other.height;
+
+  bool isSamePositionAs(Rect other) =>
+      other != null && x == other.x && y == other.y;
+
+  bool contains(num otherX, num otherY) =>
+      otherX >= x && otherX <= x + width &&
+      otherY >= y && otherY <= y + height;
+
+  String toString() => '$x, $y, $width, $height';
+}
+
+/// Mutable version of [Rect] class.
+class MutableRect extends Rect {
+  num x;
+  num y;
+  num width;
+  num height;
+
+  MutableRect(this.x, this.y, this.width, this.height);
+  MutableRect.size(this.width, this.height);
+  MutableRect.position(this.x, this.y);
+}
+
+class AbsoluteRect {
+  final num start;
+  final num end;
+  final num top;
+  final num bottom;
+
+  const AbsoluteRect(this.top, this.end, this.bottom, this.start);
+
+  bool operator==(other) =>
+      other is AbsoluteRect &&
+      start == other.start && end == other.end &&
+      top == other.top && bottom == other.bottom;
+}
\ No newline at end of file
diff --git a/charted/lib/layout/layout.dart b/charted/lib/layout/layout.dart
new file mode 100644
index 0000000..9a3b636
--- /dev/null
+++ b/charted/lib/layout/layout.dart
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+/*
+ * TODO(prsd): Document library
+ */
+library charted.layout;
+
+import 'dart:html' show Element;
+import 'package:charted/core/utils.dart';
+import 'package:charted/selection/selection.dart';
+import 'package:charted/svg/shapes.dart' show SvgArcData;
+import 'dart:math' as math;
+
+part 'src/pie_layout.dart';
+part 'src/hierarchy_layout.dart';
+part 'src/treemap_layout.dart';
diff --git a/charted/lib/layout/src/hierarchy_layout.dart b/charted/lib/layout/src/hierarchy_layout.dart
new file mode 100644
index 0000000..873d5f9
--- /dev/null
+++ b/charted/lib/layout/src/hierarchy_layout.dart
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2015 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+part of charted.layout;
+
+typedef int SortFunction(HierarchyNode a, HierarchyNode b);
+typedef List ChildrenAccessor(HierarchyNode node);
+typedef num ValueAccessor(HierarchyNode node);
+
+/**
+ * The hierarchy layout is an abstract layout that is not used directly, but
+ * instead allows code sharing between multiple hierarchical layouts such as:
+ * Cluster, Pack, Partition, Tree, and Treemap layout.
+ */
+abstract class HierarchyLayout<T extends HierarchyNode> {
+  static const ROOT_ROW_INDEX = -1;
+  SortFunction sortFunction = hierarchySort;
+  ChildrenAccessor childrenAccessor = hierarchyChildren;
+  ValueAccessor valueAccessor = hierarchyValue;
+
+  /// Returns the list of HierarchyNode constructed from the given data and
+  /// parentColumn and valueColumn which is used to construct the hierarchy.
+  /// The returned list of nodes contains the hierarchy with root being the
+  /// first element its children in depth first order.
+  List<T> layout(List rows, int parentColumn, int labelColumn,
+      int valueColumn) {
+    Map<int, List> parentMap = {};
+    List<HierarchyNode> nodeList = [];
+    var rootRow;
+    for (var row in rows) {
+      nodeList.add(createNode(row[labelColumn], row[valueColumn], 0));
+    }
+
+    for (var i = 0; i < rows.length; i++) {
+      int parentRow = rows[i][parentColumn];
+      if (parentRow == ROOT_ROW_INDEX) continue;
+      var currentNode = nodeList[i];
+      var parentNode = nodeList[parentRow];
+      (parentNode.children.isEmpty) ? parentNode.children = [currentNode] :
+          parentNode.children.add(currentNode);
+      currentNode.parent = parentNode;
+      currentNode.depth = parentNode.depth + 1;
+      for (var child in currentNode.children) {
+        child.depth += 1;
+      }
+    }
+
+    // Reorder the list so that root is the first element and the list contains
+    // the hierarcy of nodes in depth first order.
+    var hierarchyNodeList = [];
+    var root = nodeList.where((e) => e.depth == 0).elementAt(0);
+    var children = [root];
+    while (children.length > 0) {
+      var node = children.removeLast();
+      children.addAll(node.children);
+      hierarchyNodeList.add(node);
+    }
+
+    return hierarchyNodeList;
+  }
+
+  T createNode(label, value, depth);
+
+  /// Default accessor method for getting the list of children of the node.
+  static List hierarchyChildren(HierarchyNode node) => node.children;
+
+  /// Default accessor method for getting the value of the node.
+  static num hierarchyValue(HierarchyNode node) => node.value;
+
+  /// Default sorting method for comparing node a and b.
+  static int hierarchySort(HierarchyNode a, HierarchyNode b) =>
+      b.value - a.value;
+
+}
+
+abstract class HierarchyNode {
+  /// The parent node, or null for the root.
+  HierarchyNode parent = null;
+
+  /// The list of children nodes, or null for leaf nodes.
+  List children = [];
+
+  /// The label to show for each block of hierarchy
+  String label = '';
+
+  /// The node value, as returned by the value accessor.
+  dynamic value;
+
+  /// The depth of the node, starting at 0 for the root.
+  int depth = 0;
+}
diff --git a/charted/lib/layout/src/pie_layout.dart b/charted/lib/layout/src/pie_layout.dart
new file mode 100644
index 0000000..fcedbd4
--- /dev/null
+++ b/charted/lib/layout/src/pie_layout.dart
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+part of charted.layout;
+
+/**
+ * Utility class to create arc definitions that can be used by the SvgArc
+ * to generate arcs for pie and donut charts
+ */
+class PieLayout {
+  /**
+   * Callback to convert datum to values used for layout
+   * Defaults to [defaultValueAccessor]
+   */
+  SelectionValueAccessor<num> accessor = defaultValueAccessor;
+
+  /**
+   * Callback to get the start angle for the pie. This callback is
+   * called once per list of value (i.e once per call to [layout])
+   * Defaults to [defaultStartAngleCallback]
+   */
+  SelectionCallback<num> startAngleCallback = defaultStartAngleCallback;
+
+  /**
+   * Callback to get the start angle for the pie. This callback is
+   * called once per list of value (i.e once per call to [layout])
+   * Defaults to [defaultEndAngleCallback]
+   */
+  SelectionCallback<num> endAngleCallback = defaultEndAngleCallback;
+
+  /**
+   * Comparator that is used to set the sort order of values. If not
+   * specified, the input order is used.
+   */
+  Comparator<num> compare = null;
+
+  /**
+   * Return a list of SvgArcData objects that could be used to create
+   * arcs in a pie-chart or donut-chart.
+   */
+  List layout(List data, [int ei, Element e]) {
+    var values = new List.generate(data.length,
+            (int i) => accessor(data[i], i)),
+        startAngle = startAngleCallback(data, ei, e),
+        endAngle = endAngleCallback(data, ei, e),
+        total = sum(values),
+        scaleFactor = (endAngle - startAngle) / (total > 0 ? total : 1),
+        index = new Range.integers(values.length).toList(),
+        arcs = new List(data.length);
+
+    if (compare != null) {
+      index.sort((left, right) => compare(data[left], data[right]));
+    }
+
+    int count = 0;
+    index.forEach((i) {
+      endAngle = startAngle + values[i] * scaleFactor;
+      arcs[count++] = new SvgArcData(data[i], values[i], startAngle, endAngle);
+      startAngle = endAngle;
+    });
+
+    return arcs;
+  }
+
+  /** Sets a constant value to start angle of the layout */
+  set startAngle(num value) =>
+      startAngleCallback = toCallback(value);
+
+  /** Sets a constant value to end angle of the layout */
+  set endAngle(num value) =>
+      endAngleCallback = toCallback(value);
+
+  /** Default value accessor */
+  static num defaultValueAccessor(num d, i) => d;
+
+  /** Default start angle callback - returns 0 */
+  static num defaultStartAngleCallback(d, i, _) => 0;
+
+  /** Default end angle calback - returns 2 * PI */
+  static num defaultEndAngleCallback(d, i, _) => 2 * PI;
+}
diff --git a/charted/lib/layout/src/treemap_layout.dart b/charted/lib/layout/src/treemap_layout.dart
new file mode 100644
index 0000000..c2a6c37
--- /dev/null
+++ b/charted/lib/layout/src/treemap_layout.dart
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2015 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+part of charted.layout;
+
+/// PaddingFunction takes a node and generates the padding for the particular
+/// node
+typedef List PaddingFunction(TreeMapNode node);
+
+/**
+ * Utility layout class which recursively subdivides area into rectangles which
+ * can be used to quickly visualize the size of any node in the tree.
+ */
+class TreeMapLayout extends HierarchyLayout {
+  /// Rectangular subdivision; squareness controlled via the target ratio.
+  static const TREEMAP_LAYOUT_SQUARIFY = 0;
+
+  /// Horizontal subdivision.
+  static const TREEMAP_LAYOUT_SLICE= 1;
+
+  /// Vertical subdivision.
+  static const TREEMAP_LAYOUT_DICE = 2;
+
+  /// Alternating between horizontal and vertical subdivision.
+  static const TREEMAP_LAYOUT_SLICE_DICE = 3;
+
+  static const _DEFAULT_PADDING = const [0, 0, 0, 0];
+
+  /// A sticky treemap layout will preserve the relative arrangement of nodes
+  /// across transitions. (not yet implemented)
+  bool _sticky = false;
+  var _stickies;
+
+  /// The available layout size to the specified two-element array of numbers
+  /// representing width and height.
+  List size = [1, 1];
+
+  /// The mode to layout the Treemap.
+  int mode = TREEMAP_LAYOUT_SQUARIFY;
+
+  /// The ration to scale the Treemap.
+  num ratio = .5 * (1 + math.sqrt(5));
+
+  /// The paddingFunction for each node, defaults to return [0, 0, 0, 0].
+  PaddingFunction paddingFunction = (node) => _DEFAULT_PADDING;
+
+  /// TODO(midoringo): Implement sticky related feature.
+  get sticky => _sticky;
+  set sticky (bool sticky) {
+    _sticky = sticky;
+    _stickies = null;
+  }
+
+  // TODO (midoringo): handle the sticky case.
+  @override
+  List<TreeMapNode> layout(List rows, int parentColumn, int labelColumn,
+      int valueColumn) {
+    var nodes = super.layout(rows, parentColumn, labelColumn, valueColumn);
+    var root = nodes[0];
+    root.x = 0;
+    root.y = 0;
+    root.dx = size.first;
+    root.dy = size.last;
+    _scale([root], root.dx * root.dy / root.value);
+    _squarify(root);
+    return nodes;
+  }
+
+  @override
+  TreeMapNode createNode(label, value, depth) {
+    return new TreeMapNode()
+        ..label = label
+        ..value = value
+        ..depth = depth;
+  }
+
+  void _position(List<TreeMapNode> nodes, num length, MutableRect rect,
+      bool flush, num area) {
+    var i = -1;
+    var n = nodes.length;
+    var x = rect.x;
+    var y = rect.y;
+    var v = length > 0 ? (area / length).round() : 0;
+    var o;
+    if (length == rect.width) {
+      if (flush || (v > rect.height)) v = rect.height;
+      for (var node in nodes) {
+        node.x = x;
+        node.y = y;
+        node.dy = v;
+        x += node.dx = math.min(rect.x + rect.width - x, v > 0 ?
+            (node.area / v).round() : 0);
+      }
+      nodes.last.sticky = true;
+      nodes.last.dx += rect.x + rect.width - x;
+      rect.y += v;
+      rect.height -= v;
+    } else {
+      if (flush || (v > rect.width)) v = rect.width;
+      for (var node in nodes) {
+        node.x = x;
+        node.y = y;
+        node.dx = v;
+        y += node.dy = math.min(rect.y + rect.height - y, v > 0 ?
+            (node.area / v).round() : 0);
+      }
+      nodes.last.sticky = false;
+      nodes.last.dy += rect.y + rect.height - y;
+      rect.x += v;
+      rect.width -= v;
+    }
+  }
+
+  /// Applies padding between each nodes.
+  MutableRect _treeMapPad(TreeMapNode node, padding) {
+    var x = node.x + padding[3];
+    var y = node.y + padding.first;
+    var dx = node.dx - padding[1] - padding[3];
+    var dy = node.dy - padding.first - padding[2];
+    if (dx < 0) {
+      x += dx / 2;
+      dx = 0;
+    }
+    if (dy < 0) {
+      y += dy / 2;
+      dy = 0;
+    }
+    return new MutableRect(x, y, dx, dy);
+  }
+
+  /// Scales the node base on it's value and the layout area.
+  void _scale(List<TreeMapNode> children, var factor) {
+    var child, area;
+    for (var child in children) {
+      area = child.value * (factor < 0 ? 0 : factor);
+      child.area = area <= 0 ? 0 : area;
+    }
+  }
+
+  /// Compuetes the most amount of area needed to layout the list of nodes.
+  num _worst(List<TreeMapNode> nodes, num length, num pArea) {
+    var area;
+    var rmax = 0;
+    var rmin = double.INFINITY;
+    for (var node in nodes) {
+      area = node.area;
+      if (area <= 0) continue;
+      if (area < rmin) rmin = area;
+      if (area > rmax) rmax = area;
+    }
+    pArea *= pArea;
+    length *= length;
+    return (pArea > 0) ? math.max(length * rmax * ratio / pArea,
+        pArea / (length * rmin * ratio)) : double.INFINITY;
+  }
+
+  /// Recursively compute each nodes (and its children nodes) position and size
+  /// base on the node's property and layout mode.
+  void _squarify(TreeMapNode node) {
+    var children = node.children;
+    if (children.isNotEmpty) {
+      var rect = _treeMapPad(node, paddingFunction(node));
+      List<TreeMapNode> nodes = [];
+      var area = 0;
+      var remaining = new List.from(children);
+      var child, score, n,
+      best = double.INFINITY,
+      length = (mode == TREEMAP_LAYOUT_SLICE) ? rect.width :
+          (mode == TREEMAP_LAYOUT_DICE) ? rect.height :
+          (mode == TREEMAP_LAYOUT_SLICE_DICE) ? (node.depth & 1 == 1) ?
+              rect.height : rect.width : math.min(rect.width, rect.height);
+      _scale(remaining, rect.width * rect.height / node.value);
+      while ((n = remaining.length) > 0) {
+        var child = remaining[n - 1];
+        nodes.add(child);
+        area += child.area;
+        score = _worst(nodes, length, area);
+        if (mode != TREEMAP_LAYOUT_SQUARIFY || score <= best) {
+          remaining.removeLast();
+          best = score;
+        } else {
+          area -= nodes.removeLast().area;
+          _position(nodes, length, rect, false, area);
+          length = math.min(rect.width, rect.height);
+          nodes.clear();
+          area = 0;
+          best = double.INFINITY;
+        }
+      }
+      if (nodes.isNotEmpty) {
+        _position(nodes, length, rect, true, area);
+        nodes.clear();
+        area = 0;
+      }
+      children.forEach(_squarify);
+    }
+  }
+}
+
+class TreeMapNode extends HierarchyNode {
+  /// The minimum x-coordinate of the node position.
+  num x = 0;
+
+  /// The minimum y-coordinate of the node position.
+  num y = 0;
+
+  /// The x-extent of the node position.
+  num dx = 0;
+
+  /// The y-extent of the node position.
+  num dy = 0;
+
+  /// The area the node should take up.
+  num area = 0;
+
+  /// Attribute for the last node in the row, only used for sticky layout.
+  bool sticky = false;
+}
diff --git a/charted/lib/locale/format.dart b/charted/lib/locale/format.dart
new file mode 100644
index 0000000..64b4396
--- /dev/null
+++ b/charted/lib/locale/format.dart
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+
+library charted.locale.format;
+
+import 'dart:math' as math;
+import 'package:intl/intl.dart';
+import 'package:charted/locale/locale.dart';
+import 'package:charted/core/utils.dart';
+
+part 'format/number_format.dart';
+part 'format/time_format.dart';
+
+typedef String NumberFormatFunction(num x, [int precision]);
+
+/**
+ * Returns a new format function with the given string specifier.
+ * The format specifier is modeled after Python 3.1's built-in format
+ * specification mini-language.
+ *
+ * The general form of a specifier is:
+ * [​[fill]align][sign][symbol][0][width][,][.precision][type]
+ *
+ * @see <a href="http://docs.python.org/release/3.1.3/library/string.html#formatspec">Python format specification mini-language</a>
+ */
+FormatFunction format(String specifier, [Locale locale = null]) {
+  if (locale == null) {
+    locale = new EnUsLocale();
+  }
+  return locale.numberFormat.format(specifier);
+}
+
+
+/*
+ * Class for computing the SI format prefix for the given value.
+ */
+class FormatPrefix {
+  // SI scale units in increments of 1000.
+  static const List unitPrefixes = const
+      ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];
+
+  Function _scale;
+  String _symbol;
+
+  FormatPrefix(num value, [int precision = 0]) {
+    var i = 0;
+    if (value < 0) {
+      value *= -1;
+    }
+    if (precision > 0) {
+      value = _roundToPrecision(value, _formatPrecision(value, precision));
+    }
+
+    // Determining SI scale of the value in increment of 1000.
+    i = 1 + (1e-12 + math.log(value) / math.LN10).floor();
+    i = math.max(-24, math.min(24,
+        ((i - 1) / 3).floor() * 3));
+    i = 8 + (i / 3).floor();
+
+    // Sets the scale and symbol of the value.
+    var k = math.pow(10, (8 - i).abs() * 3);
+    _scale = i > 8 ? (d) => d / k : (d) => d * k;
+    _symbol = unitPrefixes[i];
+  }
+
+  _formatPrecision(num x, num p) {
+    return p - (x != 0 ? (math.log(x) / math.LN10).ceil() : 1);
+  }
+
+  /** Returns the value of x rounded to the nth digit. */
+  _roundToPrecision(num x, num n) {
+    return n != 0 ?
+        (x * (n = math.pow(10, n))).round() / n : x.round();
+  }
+
+  /** Returns the SI prefix for the value. */
+  get symbol => _symbol;
+
+  /** Returns the scale for the value corresponding to the SI prefix. */
+  get scale => _scale;
+}
diff --git a/charted/lib/locale/format/number_format.dart b/charted/lib/locale/format/number_format.dart
new file mode 100644
index 0000000..d1c97a7
--- /dev/null
+++ b/charted/lib/locale/format/number_format.dart
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+part of charted.locale.format;
+
+/**
+ * The number formatter of a given locale.  Applying the locale specific
+ * number format, number grouping and currency symbol, etc..  The format
+ * function in the NumberFormat class is used to format a number by the given
+ * specifier with the number properties of the locale.
+ */
+class NumberFormat {
+
+  // [[fill]align][sign][symbol][0][width][,][.precision][type]
+  static RegExp FORMAT_REGEX =
+      new RegExp(r'(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?'
+      r'(\.-?\d+)?([a-z%])?', caseSensitive: false);
+
+  String localeDecimal;
+  String localeThousands;
+  List localeGrouping;
+  List localeCurrency;
+  Function formatGroup;
+
+  NumberFormat(Locale locale) {
+    localeDecimal = locale.decimal;
+    localeThousands = locale.thousands;
+    localeGrouping = locale.grouping;
+    localeCurrency = locale.currency;
+    formatGroup = (localeGrouping != null) ? (value) {
+      var i = value.length,
+          t = [],
+          j = 0,
+          g = localeGrouping[0];
+      while (i > 0 && g > 0) {
+        if (i - g >= 0) {
+          i = i - g;
+        } else {
+          g = i;
+          i = 0;
+        }
+        var length = (i + g) < value.length ? (i + g) : value.length;
+        t.add(value.substring(i, length));
+        g = localeGrouping[j = (j + 1) % localeGrouping.length];
+      }
+      return t.reversed.join(localeThousands);
+    } : (x) => x;
+  }
+
+  /**
+   * Returns a new format function with the given string specifier. A format
+   * function takes a number as the only argument, and returns a string
+   * representing the formatted number. The format specifier is modeled after
+   * Python 3.1's built-in format specification mini-language. The general form
+   * of a specifier is:
+   * [​[fill]align][sign][symbol][0][width][,][.precision][type].
+   *
+   * @see <a href="http://docs.python.org/release/3.1.3/library/string.html#formatspec">format specification mini-language</a>
+   */
+  FormatFunction format(String specifier) {
+    Match match = FORMAT_REGEX.firstMatch(specifier);
+    var fill = match.group(1) != null ? match.group(1) : ' ',
+        align = match.group(2) != null ? match.group(2) : '>',
+        sign = match.group(3) != null ? match.group(3) : '',
+        symbol = match.group(4) != null ? match.group(4) : '',
+        zfill = match.group(5),
+        width = match.group(6) != null ? int.parse(match.group(6)) : 0,
+        comma = match.group(7) != null,
+        precision = match.group(8) != null ?
+            int.parse(match.group(8).substring(1)) : null,
+        type = match.group(9),
+        scale = 1,
+        prefix = '',
+        suffix = '',
+        integer = false;
+
+    if (zfill != null || fill == '0' && align == '=') {
+      zfill = fill = '0';
+      align = '=';
+      if (comma) {
+        width -= ((width - 1) / 4).floor();
+      }
+    }
+
+    switch (type) {
+      case 'n': comma = true; type = 'g'; break;
+      case '%': scale = 100; suffix = '%'; type = 'f'; break;
+      case 'p': scale = 100; suffix = '%'; type = 'r'; break;
+      case 'b':
+      case 'o':
+      case 'x':
+      case 'X': if (symbol == '#') prefix = '0' + type.toLowerCase(); break;
+      case 'c':
+      case 'd': integer = true; precision = 0; break;
+      case 's': scale = -1; type = 'r'; break;
+    }
+
+    if (symbol == '\$') {
+      prefix = localeCurrency[0];
+      suffix = localeCurrency[1];
+    }
+
+    // If no precision is specified for r, fallback to general notation.
+    if (type == 'r' && precision == null) {
+      type = 'g';
+    }
+
+    // Ensure that the requested precision is in the supported range.
+    if (precision != null) {
+      if (type == 'g') {
+        precision = math.max(1, math.min(21, precision));
+      } else if (type == 'e' || type == 'f') {
+        precision = math.max(0, math.min(20, precision));
+      }
+    }
+
+    NumberFormatFunction formatFunction = _getFormatFunction(type);
+
+    var zcomma = (zfill != null) && comma;
+
+    return (value) {
+      var fullSuffix = suffix;
+
+      // Return the empty string for floats formatted as ints.
+      if (integer && (value % 1) > 0) return '';
+
+      // Convert negative to positive, and record the sign prefix.
+      var negative;
+      if (value < 0 || value == 0 && 1 / value < 0) {
+        value = -value;
+        negative = '-';
+      } else {
+        negative = sign;
+      }
+
+      // Apply the scale, computing it from the value's exponent for si
+      // format.  Preserve the existing suffix, if any, such as the
+      // currency symbol.
+      if (scale < 0) {
+        FormatPrefix unit = new FormatPrefix(value,
+            (precision != null) ? precision : 0);
+        value = unit.scale(value);
+        fullSuffix = unit.symbol + suffix;
+      } else {
+        value *= scale;
+      }
+
+      // Convert to the desired precision.
+      if (precision != null) {
+        value = formatFunction(value, precision);
+      } else {
+        value = formatFunction(value);
+      }
+
+      // Break the value into the integer part (before) and decimal part
+      // (after).
+      var i = value.lastIndexOf('.'),
+          before = i < 0 ? value : value.substring(0, i),
+          after = i < 0 ? '' : localeDecimal + value.substring(i + 1);
+
+      // If the fill character is not '0', grouping is applied before
+      //padding.
+      if (zfill == null && comma) {
+        before = formatGroup(before);
+      }
+
+      var length = prefix.length + before.length + after.length +
+              (zcomma ? 0 : negative.length),
+          padding = length < width ? new List.filled(
+              (length = width - length + 1), '').join(fill) : '';
+
+      // If the fill character is '0', grouping is applied after padding.
+      if (zcomma) {
+        before = formatGroup(padding + before);
+      }
+
+      // Apply prefix.
+      negative += prefix;
+
+      // Rejoin integer and decimal parts.
+      value = before + after;
+
+      // Apply any padding and alignment attributes before returning the string.
+      return (align == '<' ? negative + value + padding
+          : align == '>' ? padding + negative + value
+          : align == '^' ? padding.substring(0, length >>= 1) + negative +
+              value + padding.substring(length)
+          : negative + (zcomma ? value : padding + value)) + fullSuffix;
+    };
+  }
+
+  // Gets the format function by given type.
+  NumberFormatFunction _getFormatFunction(String type) {
+    switch(type) {
+      case 'b':
+        return (num x, [int p = 0]) => x.toInt().toRadixString(2);
+      case 'c':
+        return (num x, [int p = 0]) => new String.fromCharCodes([x]);
+      case 'o':
+        return (num x, [int p = 0]) => x.toInt().toRadixString(8);
+      case 'x':
+        return (num x, [int p = 0]) => x.toInt().toRadixString(16);
+      case 'X':
+        return (num x, [int p = 0]) =>
+            x.toInt().toRadixString(16).toUpperCase();
+      case 'g':
+        return (num x, [int p = 1]) => x.toStringAsPrecision(p);
+      case 'e':
+        return (num x, [int p = 0]) => x.toStringAsExponential(p);
+      case 'f':
+        return (num x, [int p = 0]) => x.toStringAsFixed(p);
+      case 'r':
+      default:
+        return (num x, [int p = 0]) => x.toString();
+    }
+  }
+}
\ No newline at end of file
diff --git a/charted/lib/locale/format/time_format.dart b/charted/lib/locale/format/time_format.dart
new file mode 100644
index 0000000..a5dd8fc
--- /dev/null
+++ b/charted/lib/locale/format/time_format.dart
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+part of charted.locale.format;
+
+typedef String TimeFormatFunction(DateTime date);
+
+//TODO(songrenchu): Document time format; Add test for time format.
+
+class TimeFormat {
+  String _template;
+  String _locale;
+  DateFormat _dateFormat;
+
+  TimeFormat([String template = null, String identifier = 'en_US']) {
+    _template = template;
+    _locale = identifier;
+    if (_template != null)
+      _dateFormat = new DateFormat(_wrapStrptime2ICU(_template), _locale);
+  }
+
+  TimeFormat _getInstance(String template) {
+    return new TimeFormat(template, _locale);
+  }
+
+  String apply(DateTime date) {
+    assert(_dateFormat != null);
+    return _dateFormat.format(date);
+  }
+
+  String toString() => _template;
+
+  DateTime parse(String string) {
+    assert(_dateFormat != null);
+    return _dateFormat.parse(string);
+  }
+
+  TimeFormatFunction multi(List<List> formats) {
+    var n = formats.length,
+        i = -1;
+    while (++i < n)
+      formats[i][0] = _getInstance(formats[i][0] as String);
+    return (var date) {
+      if (date is num) {
+        date = new DateTime.fromMillisecondsSinceEpoch(date.toInt());
+      }
+      var i = 0,
+          f = formats[i];
+      while (f.length < 2 || f[1](date) == false) {
+        i++;
+        if (i < n) f = formats[i];
+      }
+      if (i == n) return null;
+      return f[0].apply(date);
+    };
+  }
+
+  UTCTimeFormat utc([String specifier = null]) {
+    return new UTCTimeFormat(specifier == null ?
+        _template : specifier, _locale);
+  }
+
+  static UTCTimeFormat iso() {
+    return new UTCTimeFormat("%Y-%m-%dT%H:%M:%S.%LZ");
+  }
+
+  static Map timeFormatPads = {"-": "", "_": " ", "0": "0"};
+  // TODO(songrenchu): Cannot fully be equivalent now.
+  static Map timeFormatsTransform = {
+    'a': 'EEE',
+    'A': 'EEEE',
+    'b': 'MMM',
+    'B': 'MMMM',
+    'c': 'EEE MMM d HH:mm:ss yyyy',
+    'd': 'dd',
+    'e': 'd',         // TODO(songrenchu): zero padding not supported
+    'H': 'HH',
+    'I': 'hh',
+    'j': 'DDD',
+    'm': 'MM',
+    'M': 'mm',
+    'L': 'SSS',
+    'p': 'a',
+    'S': 'ss',
+    'U': 'ww',        // TODO(songrenchu): ICU doesn't disdinguish 'U' and 'W',
+                      // and not supported by Dart: DateFormat
+    'w': 'ee',        // TODO(songrenchu): e not supported by Dart: DateFormat
+    'W': 'ww',        // TODO(songrenchu): ICU doesn't disdinguish 'U' and 'W',
+                      // and not supported by Dart: DateFormat
+    'x': 'MM/dd/yyyy',
+    'X': 'HH:mm:ss',
+    'y': 'yy',
+    'Y': 'yyyy',
+    'Z': 'Z',
+    '%': '%'
+  };
+
+  String _wrapStrptime2ICU(String template) {
+    var string = [],
+        i = -1,
+        j = 0,
+        n = template.length,
+        formatPad,
+        tempChar;
+    while (++i < n) {
+      if (template[i] == '%') {
+        string.add(template.substring(j, i));
+        if ((formatPad = timeFormatPads[tempChar = template[++i]]) != null)
+          tempChar = template[++i];
+        if (timeFormatsTransform[tempChar] != null)
+          string.add(timeFormatsTransform[tempChar]);
+        j = i + 1;
+      }
+    }
+    if (j < i)
+      string.add("'" + template.substring(j, i) + "'");
+    return string.join("");
+  }
+}
+
+class UTCTimeFormat extends TimeFormat {
+  UTCTimeFormat(String template, [String identifier = 'en_US']):
+    super(template, identifier);
+
+  UTCTimeFormat _getInstance(String template) {
+    return new UTCTimeFormat(template, _locale);
+  }
+
+  DateTime parse(String string) {
+    assert(_dateFormat != null);
+    return _dateFormat.parseUTC(string);
+  }
+}
\ No newline at end of file
diff --git a/charted/lib/locale/languages/en_us.dart b/charted/lib/locale/languages/en_us.dart
new file mode 100644
index 0000000..53be549
--- /dev/null
+++ b/charted/lib/locale/languages/en_us.dart
@@ -0,0 +1,44 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+part of charted.locale;
+
+// Defines the en_us locale and related format properties.
+class EnUsLocale extends Locale {
+  static EnUsLocale instance;
+
+  factory EnUsLocale() {
+    if (EnUsLocale.instance == null) {
+      EnUsLocale.instance = new EnUsLocale._create();
+    }
+    return EnUsLocale.instance;
+  }
+
+  EnUsLocale._create();
+
+  final identifier = 'en_US';
+  final decimal = '.';
+  final thousands = ',';
+  final grouping = const[3];
+  final currency = const['\$', ''];
+  final dateTime = '%a %b %e %X %Y';
+  final date = '%m/%d/%Y';
+  final time = '%H =>%M =>%S';
+  final periods = const['AM', 'PM'];
+
+  final days = const[
+      'Sunday', 'Monday', 'Tuesday',
+      'Wednesday', 'Thursday', 'Friday', 'Saturday'];
+  final shortDays = const['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
+
+  final months = const[
+      'January', 'February', 'March', 'April', 'May', 'June',
+      'July', 'August', 'September', 'October', 'November', 'December'];
+  final shortMonths = const[
+      'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
+      'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+}
diff --git a/charted/lib/locale/locale.dart b/charted/lib/locale/locale.dart
new file mode 100644
index 0000000..163e8b3
--- /dev/null
+++ b/charted/lib/locale/locale.dart
@@ -0,0 +1,38 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+library charted.locale;
+
+import 'package:charted/locale/format.dart';
+
+part 'languages/en_us.dart';
+
+abstract class Locale {
+  String get identifier;
+  String get decimal;
+  String get thousands;
+  List get grouping;
+  List get currency;
+
+  String get dateTime;
+  String get date;
+  String get time;
+  List get periods;
+
+  List get days;
+  List get shortDays;
+
+  List get months;
+  List get shortMonths;
+
+  Locale();
+
+  NumberFormat get numberFormat => new NumberFormat(this);
+  TimeFormat timeFormat([specifier = null]) =>
+      new TimeFormat(specifier, this.identifier);
+}
diff --git a/charted/lib/selection/selection.dart b/charted/lib/selection/selection.dart
new file mode 100644
index 0000000..0cb9ae5
--- /dev/null
+++ b/charted/lib/selection/selection.dart
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+/*
+ * TODO(prsd): Document library
+ */
+library charted.selection;
+
+import "dart:html" show Element, Event, document;
+import "dart:math" as math;
+import "package:charted/core/utils.dart";
+import "package:charted/selection/transition.dart";
+
+part "src/selection_scope.dart";
+part "src/selection_impl.dart";
+
+/**
+ * Callback to access key value from a given data object. During the process
+ * of binding data to Elements, the key values are used to match Elements
+ * that have previously bound data
+ */
+typedef SelectionKeyFunction(datum);
+
+/**
+ * Callback for all DOM related operations - The first parameter [datum] is
+ * the piece of data associated with the node, [ei] is the index of the
+ * element in it's group and [c] is the context to which the data is
+ * associated to.
+ */
+typedef E SelectionCallback<E>(datum, int index, Element element);
+
+/** Callback used to access a value from a datum */
+typedef E SelectionValueAccessor<E>(datum, int index);
+
+/** Create a ChartedCallback that always returns [val] */
+SelectionCallback toCallback(val) => (datum, index, element) => val;
+
+/** Create a ChartedValueAccessor that always returns [val] */
+SelectionValueAccessor toValueAccessor(val) => (datum, index) => val;
+
+/**
+ * [Selection] is a collection of elements - this collection defines
+ * operators that can be applied on all elements of the collection.
+ *
+ * All operators accept parameters either as a constant value or a callback
+ * function (typically using the named parameters "val" and "fn"). These
+ * operators, when invoked with the callback function, the function is
+ * called once per element and is passed the "data" associated, the "index"
+ * and the element itself.
+ */
+abstract class Selection {
+  /**
+   * Collection of groups - A selection when created by calling [selectAll]
+   * on an existing [Selection], could contain more than one group.
+   */
+  Iterable<SelectionGroup> groups;
+
+  /**
+   * Scope of this selection that manages the element, data associations for
+   * all elements in this selection (and the sub-selections)
+   */
+  SelectionScope get scope;
+
+  /** Indicates if this selection is empty */
+  bool get isEmpty;
+
+  /** Number of elements in this selection */
+  int get length;
+
+  /** First non-null element in this selection, if any. */
+  Element get first;
+
+  /**
+   * Creates and returns a new [Selection] containing the first element
+   * matching [selector] under each element in the current selection.
+   *
+   * If an element does not have a matching descendant, a placeholder is used
+   * in it's position - thus being able to match the indices of elements in
+   * the current and the created sub-selection.
+   *
+   * Any data bound to elements in this selection is inherited by the
+   * selected descendants.
+   */
+  Selection select(String selector);
+
+  /**
+   * Same as [select], but calls [fn] for each non-null element in the
+   * selection (with data associated to the element, index of the element in
+   * it's group and the element itself) to get the selected element that will
+   * be selected.
+   */
+  Selection selectWithCallback(SelectionCallback<Element> fn);
+
+  /**
+   * Creates and returns a new [Selection] containing all elements matching
+   * [selector] under each element in the current selection.
+   *
+   * The resulting [Selection] is nested with elements from current selection
+   * as parents and the selected descendants grouped by elements in the
+   * current selection.  When no descendants match the selector, the
+   * collection of selected elements in a group is empty.
+   *
+   * Data bound to the elements is not automatically inherited by the
+   * selected descendants.
+   */
+  Selection selectAll(String selector);
+
+  /**
+   * Same as [selectAll], but calls [fn] for each non-null element in the
+   * selection (with data associated to the element, index of the element in
+   * it's group and the element itself) to get a collection of selected
+   * elements that will be part of the new selection.
+   */
+  Selection selectAllWithCallback(SelectionCallback<Iterable<Element>> fn);
+
+  /**
+   * Sets the attribute [name] on all elements when [val] is not null.
+   * Removes the attribute when [val] is null.
+   */
+  void attr(String name, val);
+
+  /**
+   * Same as [attr], but calls [fn] for each non-null element in the
+   * selection (with data associated to the element, index of the element in
+   * it's group and the element itself) to get the value of the attribute.
+   */
+  void attrWithCallback(String name, SelectionCallback fn);
+
+  /**
+   * Ensures presence of a class when [val] is true.  Ensures that the class
+   * isn't present if [val] is false.
+   */
+  void classed(String name, [bool val = true]);
+
+  /**
+   * Same as [classed], but calls [fn] for each non-null element in the
+   * selection (with data associated to the element, index of the element in
+   * it's group and the element itself) to get the boolean value that
+   * indicates if the class must be added or removed.
+   */
+  void classedWithCallback(String name, SelectionCallback<bool> fn);
+
+  /** Sets CSS [property] to [val] on all elements in the selection. */
+  void style(String property, val, {String priority});
+
+  /**
+   * Same as [style], but calls [fn] for each non-null element in the
+   * selection (with data associated to the element, index of the element in
+   * it's group and the element itself) to get the value of the property.
+   */
+  void styleWithCallback(String property,
+      SelectionCallback<String> fn, {String priority});
+
+  /**
+   * Sets textContent of all elements in the selection to [val]. A side-effect
+   * of this call is that any children of these elements will not be part of
+   * the DOM anymore.
+   */
+  void text(String val);
+
+  /**
+   * Same as [text], but calls [fn] for each non-null element in
+   * the selection (with data associated to the element, index of the
+   * element in it's group and the element itself) to get the text content
+   */
+  void textWithCallback(SelectionCallback<String> fn);
+
+  /**
+   * Sets innerHtml of all elements in the selection to [val]. A side-effect
+   * of this call is that any children of these elements will not be part of
+   * the DOM anymore.
+   */
+  void html(String val);
+
+  /**
+   * Same as [html], but calls [fn] for each non-null element in
+   * the selection (with data associated to the element, index of the
+   * element in it's group and the element itself) to get the html content
+   */
+  void htmlWithCallback(SelectionCallback<String> fn);
+
+  /**
+   * Appends a new child element to each element in the selection.
+   *
+   * Returns a [Selection] containing the newly created elements. As with
+   * [select], any data bound to the elements in this selection is inherited
+   * by the new elements.
+   */
+  Selection append(String tag);
+
+  /**
+   * Same as [append], but calls [fn] for each non-null element in the
+   * selection (with data associated to the element, index of the element in
+   * it's group and the element itself) to get the element to be appended.
+   */
+  Selection appendWithCallback(SelectionCallback<Element> fn);
+
+  /**
+   * Inserts a child node to each element in the selection before the first
+   * element matching [before] or before the element returned by [beforeFn].
+   *
+   * Returns a [Selection] containing the newly created elements. As with
+   * [select], any data bound to the elements in this selection is inherited
+   * by the new elements.
+   */
+  Selection insert(String tag,
+      {String before, SelectionCallback<Element> beforeFn});
+
+  /**
+   * Same as [insert], but calls [fn] for each non-null element in the
+   * selection (with data associated to the element, index of the element in
+   * it's group and the element itself) to get the element to be inserted.
+   */
+  Selection insertWithCallback(SelectionCallback<Element> fn,
+      {String before, SelectionCallback<Element> beforeFn});
+
+  /** Removes all selected elements from the DOM */
+  void remove();
+
+  /** Calls [fn] on each element in this selection */
+  void each(SelectionCallback fn);
+
+  /**
+   * Adds or removes an event [listener] to each element in the selection for
+   * the specified [type] (Eg: "mouseclick", "mousedown")
+   *
+   * Any existing listener of the same type will be removed.  To register
+   * multiple listener for the same event type, the [type] can be suffixed
+   * with a namespace. (Eg: "mouseclick.foo", "mousedown.bar")
+   *
+   * When [listener] is null, any existing listener of the same type and in
+   * the same namespace will be removed (Eg: Using "mouseclick.foo" as type
+   * will only remove listeners for "mouseclick.foo" and not "mouseclick.bar")
+   *
+   * To remove listeners of an event type in all namespaces, prefix the type
+   * with a "." (Eg: ".mouseclick" will remove "mouseclick.bar",
+   * "mouseclick .foo" and all other mouseclick event listeners)
+   *
+   * To summarize, [type] can be any DOM event type optionally in the format
+   * "event.namespace" where event is the DOM event type and namespace is
+   * used to distinguish between added listeners.
+   *
+   * When [listener] is called, it is passed the current value associated with
+   * the element.  Please note that index passed to the listener contains a
+   * value as it was at the time of adding the listener.
+   */
+  void on(String type, [SelectionCallback listener, bool capture]);
+
+  /**
+   * Associates data with the selected elements.
+   * Computes the enter, update and exit selections.
+   */
+  DataSelection data(Iterable vals, [SelectionKeyFunction keyFn]);
+
+  /**
+   * Same as [data], but calls [fn] for each non-null element in the
+   * selection (with data associated to the element, index of the element in
+   * it's group and the element itself) to get the data to be set on the
+   * current element.
+   */
+  DataSelection dataWithCallback(
+      SelectionCallback<Iterable> fn, [SelectionKeyFunction keyFn]);
+
+  /**
+   * Associates data with all the elements - no join is performed. Unlike
+   * [data], this does not compute the enter, update and exit selections.
+   */
+  void datum(Iterable vals);
+
+  /**
+   * Same as [datum], but calls [fn] for each non-null element in the
+   * selection (with data associated to the element, index of the element in
+   * it's group and the element itself) to get the data to be set on the
+   * current element.
+   */
+  void datumWithCallback(SelectionCallback<Iterable> fn);
+
+  /**
+   * Starts a transition for the current selection. Transitions behave much
+   * like selections, except operators animate smoothly over time rather than
+   * applying instantaneously.
+   */
+  Transition transition();
+}
+
+
+/*
+ * Group of elements in the selection.
+ * Each selection may contain more than one group of elements.
+ */
+abstract class SelectionGroup {
+  Iterable<Element> elements;
+  Element parent;
+}
+
+
+/**
+ * [EnterSelection] is a sub-selection that represents missing elements of a
+ * selection - an element is considered missing when there is data and no
+ * corresponding element in a selection.
+ */
+abstract class EnterSelection {
+  /**
+   * Indicate if this selection is empty
+   * See [Selection.isEmpty] for more information.
+   */
+  bool get isEmpty;
+
+  /** [DataSelection] that corresponds to this selection. */
+  DataSelection get update;
+
+  /**
+   * Appends an element to all elements in this selection and return
+   * [Selection] containing the newly added elements.
+   *
+   * See [Selection.append] for more information.
+   * The new nodes are merged into the [DataSelection]
+   */
+  Selection append(String tag);
+
+  /**
+   * Same as [append] but calls [fn] to get the element to be appended.
+   * See [Selection.appendWithCallback] for more information.
+   */
+  Selection appendWithCallback(SelectionCallback<Element> fn);
+
+  /**
+   * Insert a child node to each element in the selection and return
+   * [Selection] containing the newly added elements.
+   *
+   * See [Selection.insert] for more information.
+   * The new nodes are merged into the [UpdateSelection]
+   */
+  Selection insert(String tag,
+      {String before, SelectionCallback<Element> beforeFn});
+
+  /**
+   * Same as [insert] but calls [fn] to get the element to be inserted.
+   * See [Selection.insertWithCallback] for more information.
+   */
+  Selection insertWithCallback(SelectionCallback<Element> fn,
+      {String before, SelectionCallback<Element> beforeFn});
+
+  /**
+   * For each element in the current selection, select exactly one
+   * descendant and return [Selection] containing the selected elements.
+   *
+   * See [Selection.select] for more information.
+   */
+  Selection select(String selector);
+
+  /**
+   * Same as [select] but calls [fn] to get the element to be inserted.
+   * See [Selection.selectWithCallback] for more information.
+   */
+  Selection selectWithCallback(SelectionCallback<Element> fn);
+}
+
+/*
+ * [ExitSelection] is a sub-selection that represents elements that don't
+ * have data associated to them.
+ */
+abstract class ExitSelection extends Selection {
+  DataSelection get update;
+}
+
+/*
+ * Selection that consists elements in the selection that aren't part of
+ * [EnterSelection] or the [ExitSelection]
+ *
+ * An [UpdateSelection] is only available after data() is attached and is
+ * currently exactly the same as [Selection] itself.
+ */
+abstract class DataSelection extends Selection {
+  /**
+   * A view of the current selection that contains a collection of data
+   * elements which weren't associated with an element in the DOM.
+   */
+  EnterSelection get enter;
+
+  /**
+   * A view of the current selection containing elements that don't have data
+   * associated with them.
+   */
+  ExitSelection get exit;
+}
diff --git a/charted/lib/selection/src/selection_impl.dart b/charted/lib/selection/src/selection_impl.dart
new file mode 100644
index 0000000..b9cf3a4
--- /dev/null
+++ b/charted/lib/selection/src/selection_impl.dart
@@ -0,0 +1,549 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+part of charted.selection;
+
+/**
+ * Implementation of [Selection].
+ * Selections cannot be created directly - they are only created using
+ * the select or selectAll methods on [SelectionScope] and [Selection].
+ */
+class _SelectionImpl implements Selection {
+
+  Iterable<SelectionGroup> groups;
+  SelectionScope scope;
+
+  Transition _transition;
+
+  /**
+   * Creates a new selection.
+   *
+   * When [source] is not specified, the new selection would have exactly
+   * one group with [SelectionScope.root] as it's parent.  Otherwise, one group
+   * per for each non-null element is created with element as it's parent.
+   *
+   * When [selector] is specified, each group contains all elements matching
+   * [selector] and under the group's parent element.  Otherwise, [fn] is
+   * called once per group with parent element's "data", "index" and the
+   * "element" itself passed as parameters.  [fn] must return an iterable of
+   * elements to be used in each group.
+   */
+  _SelectionImpl.all({String selector, SelectionCallback<Iterable<Element>> fn,
+      SelectionScope this.scope, Selection source}) {
+    assert(selector != null || fn != null);
+    assert(source != null || scope != null);
+
+    if (selector != null) {
+      fn = (d, i, c) => c == null ?
+          scope.root.querySelectorAll(selector) :
+          c.querySelectorAll(selector);
+    }
+
+    var tmpGroups = new List<SelectionGroup>();
+    if (source != null) {
+      scope = source.scope;
+      for (int gi = 0; gi < source.groups.length; ++gi) {
+        final g = source.groups.elementAt(gi);
+        for (int ei = 0; ei < g.elements.length; ++ei) {
+          final e = g.elements.elementAt(ei);
+          if (e != null) {
+            tmpGroups.add(
+                new _SelectionGroupImpl(
+                    fn(scope.datum(e), gi, e), parent: e));
+          }
+        }
+      }
+    } else {
+      tmpGroups.add(
+          new _SelectionGroupImpl(fn(null, 0, null), parent: scope.root));
+    }
+    groups = tmpGroups;
+  }
+
+  /**
+   * Same as [all] but only uses the first element matching [selector] when
+   * [selector] is speficied.  Otherwise, call [fn] which must return the
+   * element to be selected.
+   */
+  _SelectionImpl.single({String selector, SelectionCallback<Element> fn,
+      SelectionScope this.scope, Selection source}) {
+    assert(selector != null || fn != null);
+    assert(source != null || scope != null);
+
+    if (selector != null) {
+      fn = (d, i, c) => c == null ?
+          scope.root.querySelector(selector) :
+          c.querySelector(selector);
+    }
+
+    if (source != null) {
+      scope = source.scope;
+      groups = new List<SelectionGroup>.generate(source.groups.length, (gi) {
+        SelectionGroup g = source.groups.elementAt(gi);
+        return new _SelectionGroupImpl(
+            new List.generate(g.elements.length, (ei) {
+          var e = g.elements.elementAt(ei);
+          if (e != null) {
+            var datum = scope.datum(e);
+            var enterElement = fn(datum, ei, e);
+            if (datum != null) {
+              scope.associate(enterElement, datum);
+            }
+            return enterElement;
+          } else {
+            return null;
+          }
+        }), parent: g.parent);
+      });
+    } else {
+      groups = new List<SelectionGroup>.generate(1,
+          (_) => new _SelectionGroupImpl(new List.generate(1,
+              (_) => fn(null, 0, null), growable: false)), growable: false);
+    }
+  }
+
+  /** Creates a selection using the pre-computed list of [SelectionGroup] */
+  _SelectionImpl.selectionGroups(
+      Iterable<SelectionGroup> this.groups, SelectionScope this.scope);
+
+  /**
+   * Creates a selection using the list of elements. All elements will
+   * be part of the same group, with [SelectionScope.root] as the group's parent
+   */
+  _SelectionImpl.elements(Iterable elements, SelectionScope this.scope) {
+    groups = new List<SelectionGroup>()
+        ..add(new _SelectionGroupImpl(elements));
+  }
+
+  /**
+   * Utility to evaluate value of parameters (uses value when given
+   * or invokes a callback to get the value) and calls [action] for
+   * each non-null element in this selection
+   */
+  void _do(SelectionCallback f, Function action) {
+    each((d, i, e) => action(e, f == null ? null : f(scope.datum(e), i, e)));
+  }
+
+  /** Calls a function on each non-null element in the selection */
+  void each(SelectionCallback fn) {
+    if (fn == null) return;
+    for (int gi = 0, gLen = groups.length; gi < gLen; ++gi) {
+      final g = groups.elementAt(gi);
+      for (int ei = 0, eLen = g.elements.length; ei < eLen; ++ei) {
+        final e = g.elements.elementAt(ei);
+        if (e != null) fn(scope.datum(e), ei, e);
+      }
+    }
+  }
+
+  void on(String type, [SelectionCallback listener, bool capture]) {
+    Function getEventHandler(i, e) => (Event event) {
+      var previous = scope.event;
+      scope.event = event;
+      try {
+        listener(scope.datum(e), i, e);
+      } finally {
+        scope.event = previous;
+      }
+    };
+
+    if (!type.startsWith('.')) {
+      if (listener != null) {
+        // Add a listener to each element.
+        each((d, i, Element e){
+          var handlers = scope._listeners[e];
+          if (handlers == null) scope._listeners[e] = handlers = {};
+          handlers[type] = new Pair(getEventHandler(i, e), capture);
+          e.addEventListener(type, handlers[type].first, capture);
+        });
+      } else {
+        // Remove the listener from each element.
+        each((d, i, Element e) {
+          var handlers = scope._listeners[e];
+          if (handlers != null && handlers[type] != null) {
+            e.removeEventListener(
+                type, handlers[type].first, handlers[type].last);
+          }
+        });
+      }
+    } else {
+      // Remove all listeners on the event type (ignoring the namespace)
+      each((d, i, Element e) {
+        var handlers = scope._listeners[e],
+            t = type.substring(1);
+        handlers.forEach((String s, Pair<Function, bool> value) {
+          if (s.split('.')[0] == t) {
+            e.removeEventListener(s, value.first, value.last);
+          }
+        });
+      });
+    }
+  }
+
+  int get length {
+    int retval = 0;
+    each((d, i, e) => retval++);
+    return retval;
+  }
+
+  bool get isEmpty => length == 0;
+
+  /** First non-null element in this selection */
+  Element get first {
+    for (int gi = 0; gi < groups.length; gi++) {
+      SelectionGroup g = groups.elementAt(gi);
+      for (int ei = 0; ei < g.elements.length; ei++) {
+        if (g.elements.elementAt(ei) != null) {
+          return g.elements.elementAt(ei);
+        }
+      }
+    }
+    return null;
+  }
+
+  void attr(String name, val) {
+    assert(name != null && name.isNotEmpty);
+    attrWithCallback(name, toCallback(val));
+  }
+
+  void attrWithCallback(String name, SelectionCallback fn) {
+    assert(fn != null);
+    _do(fn, (e, v) => v == null ?
+        e.attributes.remove(name) : e.attributes[name] = "$v");
+  }
+
+  void classed(String name, [bool val = true]) {
+    assert(name != null && name.isNotEmpty);
+    classedWithCallback(name, toCallback(val));
+  }
+
+  void classedWithCallback(String name, SelectionCallback<bool> fn) {
+    assert(fn != null);
+    _do(fn, (e, v) =>
+        v == false ? e.classes.remove(name) : e.classes.add(name));
+  }
+
+  void style(String property, val, {String priority}) {
+    assert(property != null && property.isNotEmpty);
+    styleWithCallback(property,
+        toCallback(val as String), priority: priority);
+  }
+
+  void styleWithCallback(String property,
+      SelectionCallback<String> fn, {String priority}) {
+    assert(fn != null);
+    _do(fn, (Element e, String v) =>
+        v == null || v.isEmpty ?
+            e.style.removeProperty(property) :
+            e.style.setProperty(property, v, priority));
+  }
+
+  void text(String val) => textWithCallback(toCallback(val));
+
+  void textWithCallback(SelectionCallback<String> fn) {
+    assert(fn != null);
+    _do(fn, (e, v) => e.text = v == null ? '' : v);
+  }
+
+  void html(String val) => htmlWithCallback(toCallback(val));
+
+  void htmlWithCallback(SelectionCallback<String> fn) {
+    assert(fn != null);
+    _do(fn, (e, v) => e.innerHtml = v == null ? '' : v);
+  }
+
+  void remove() => _do(null, (e, _) => e.remove());
+
+  Selection select(String selector) {
+    assert(selector != null && selector.isNotEmpty);
+    return new _SelectionImpl.single(selector: selector, source: this);
+  }
+
+  Selection selectWithCallback(SelectionCallback<Element> fn) {
+    assert(fn != null);
+    return new _SelectionImpl.single(fn: fn, source:this);
+  }
+
+  Selection append(String tag) {
+    assert(tag != null && tag.isNotEmpty);
+    return appendWithCallback(
+        (d, ei, e) => Namespace.createChildElement(tag, e));
+  }
+
+  Selection appendWithCallback(SelectionCallback<Element> fn) {
+    assert(fn != null);
+    return new _SelectionImpl.single(fn: (datum, ei, e) {
+          Element child = fn(datum, ei, e);
+          return child == null ? null : e.append(child);
+        }, source: this);
+  }
+
+  Selection insert(String tag,
+      {String before, SelectionCallback<Element> beforeFn}) {
+    assert(tag != null && tag.isNotEmpty);
+    return insertWithCallback(
+        (d, ei, e) => Namespace.createChildElement(tag, e),
+        before: before, beforeFn: beforeFn);
+  }
+
+  Selection insertWithCallback(SelectionCallback<Element> fn,
+      {String before, SelectionCallback<Element> beforeFn}) {
+    assert(fn != null);
+    beforeFn =
+        before == null ? beforeFn : (d, ei, e) => e.querySelector(before);
+    return new _SelectionImpl.single(
+        fn: (datum, ei, e) {
+          Element child = fn(datum, ei, e);
+          Element before = beforeFn(datum, ei, e);
+          return child == null ? null : e.insertBefore(child, before);
+        },
+        source: this);
+  }
+
+  Selection selectAll(String selector) {
+    assert(selector != null && selector.isNotEmpty);
+    return new _SelectionImpl.all(selector: selector, source: this);
+  }
+
+  Selection selectAllWithCallback(SelectionCallback<Iterable<Element>> fn) {
+    assert(fn != null);
+    return new _SelectionImpl.all(fn: fn, source:this);
+  }
+
+  DataSelection data(Iterable vals, [SelectionKeyFunction keyFn]) {
+    assert(vals != null);
+    return dataWithCallback(toCallback(vals), keyFn);
+  }
+
+  DataSelection dataWithCallback(
+      SelectionCallback<Iterable> fn, [SelectionKeyFunction keyFn]) {
+    assert(fn != null);
+
+    var enterGroups = [],
+        updateGroups = [],
+        exitGroups = [];
+
+    // Create a dummy node to be used with enter() selection.
+    // TODO(prsd): Use virtual DOM. It could be upto 50 times faster than
+    //             using a html element for dummy.
+    Element dummy(val) {
+      var element = new Element.tag('charted-dummy');
+      scope.associate(element, val);
+      return element;
+    };
+
+    // Joins data to all elements in the group.
+    void join(SelectionGroup g, Iterable vals) {
+      final int valuesLength = vals.length;
+      final int elementsLength = g.elements.length;
+
+      // Nodes exiting, entering and updating in this group.
+      // We maintain the nodes at the same index as they currently
+      // are (for exiting) or where they should be (for entering and updating)
+      var update = new List(valuesLength),
+          enter = new List(valuesLength),
+          exit = new List(elementsLength);
+
+      // Use key function to determine DOMElement to data associations.
+      if (keyFn != null) {
+        var keysOnDOM = [],
+            elementsByKey = {},
+            valuesByKey = {};
+
+        // Create a key to DOM element map.
+        // Used later to see if an element already exists for a key.
+        for (int ei = 0, len = elementsLength; ei < len; ++ei) {
+          final e = g.elements.elementAt(ei);
+          var keyValue = keyFn(scope.datum(e));
+          if (elementsByKey.containsKey(keyValue)) {
+            exit[ei] = e;
+          } else {
+            elementsByKey[keyValue] = e;
+          }
+          keysOnDOM.add(keyValue);
+        }
+
+        // Iterate through the values and find values that don't have
+        // corresponding elements in the DOM, collect the entering elements.
+        for (int vi = 0, len = valuesLength; vi < len; ++vi) {
+          final v = vals.elementAt(vi);
+          var keyValue = keyFn(v);
+          Element e = elementsByKey[keyValue];
+          if (e != null) {
+            update[vi] = e;
+            scope.associate(e, v);
+          } else if (!valuesByKey.containsKey(keyValue)) {
+            enter[vi] = dummy(v);
+          }
+          valuesByKey[keyValue] = v;
+          elementsByKey.remove(keyValue);
+        }
+
+        // Iterate through the previously saved keys to
+        // find a list of elements that don't have data anymore.
+        // We don't use elementsByKey.keys() becuase that does not
+        // guarantee the order of returned keys.
+        for (int i = 0, len = elementsLength; i < len; ++i) {
+          if (elementsByKey.containsKey(keysOnDOM[i])) {
+            exit[i] = g.elements.elementAt(i);
+          }
+        }
+      } else {
+        // When we don't have the key function, just use list index as the key
+        int updateElementsCount = math.min(elementsLength, valuesLength);
+        int i = 0;
+
+        // Collect a list of elements getting updated in this group
+        for (int len = updateElementsCount; i < len; ++i) {
+          var e = g.elements.elementAt(i);
+          if (e != null) {
+            scope.associate(e, vals.elementAt(i));
+            update[i] = e;
+          } else {
+            enter[i] = dummy(vals.elementAt(i));
+          }
+        }
+
+        // List of elements newly getting added
+        for (int len = valuesLength; i < len; ++i) {
+          enter[i] = dummy(vals.elementAt(i));
+        }
+
+        // List of elements exiting this group
+        for (int len = elementsLength; i < len; ++i) {
+          exit[i] = g.elements.elementAt(i);
+        }
+      }
+
+      // Create the element groups and set parents from the current group.
+      enterGroups.add(new _SelectionGroupImpl(enter, parent: g.parent));
+      updateGroups.add(new _SelectionGroupImpl(update, parent: g.parent));
+      exitGroups.add(new _SelectionGroupImpl(exit, parent: g.parent));
+    };
+
+    for (int gi = 0; gi < groups.length; ++gi) {
+      final g = groups.elementAt(gi);
+      join(g, fn(scope.datum(g.parent), gi, g.parent));
+    }
+
+    return new _DataSelectionImpl(
+        updateGroups, enterGroups, exitGroups, scope);
+  }
+
+  void datum(Iterable vals) {
+    throw new UnimplementedError();
+  }
+
+  void datumWithCallback(SelectionCallback<Iterable> fn) {
+    throw new UnimplementedError();
+  }
+
+  Transition transition() {
+    return _transition = new Transition(this);
+  }
+}
+
+/* Implementation of [DataSelection] */
+class _DataSelectionImpl extends _SelectionImpl implements DataSelection {
+  EnterSelection enter;
+  ExitSelection exit;
+
+  _DataSelectionImpl(Iterable updated, Iterable entering, Iterable exiting,
+      SelectionScope scope) : super.selectionGroups(updated, scope) {
+    enter = new _EnterSelectionImpl(entering, this);
+    exit = new _ExitSelectionImpl(exiting, this);
+  }
+}
+
+/* Implementation of [EnterSelection] */
+class _EnterSelectionImpl implements EnterSelection {
+  final DataSelection update;
+
+  SelectionScope scope;
+  Iterable<SelectionGroup> groups;
+
+  _EnterSelectionImpl(Iterable this.groups, DataSelection this.update) {
+    scope = update.scope;
+  }
+
+  bool get isEmpty => false;
+
+  Selection insert(String tag,
+      {String before, SelectionCallback<Element> beforeFn}) {
+    assert(tag != null && tag.isNotEmpty);
+    return insertWithCallback(
+        (d, ei, e) => Namespace.createChildElement(tag, e),
+        before: before, beforeFn: beforeFn);
+  }
+
+  Selection insertWithCallback(SelectionCallback<Element> fn,
+      {String before, SelectionCallback<Element> beforeFn}) {
+    assert(fn != null);
+    return selectWithCallback((d, ei, e) {
+      Element child = fn(d, ei, e);
+      e.insertBefore(child, e.querySelector(before));
+      return child;
+    });
+  }
+
+  Selection append(String tag) {
+    assert(tag != null && tag.isNotEmpty);
+    return appendWithCallback(
+        (d, ei, e) => Namespace.createChildElement(tag, e));
+  }
+
+  Selection appendWithCallback(SelectionCallback<Element> fn) {
+    assert(fn != null);
+    return selectWithCallback((datum, ei, e) {
+          Element child = fn(datum, ei, e);
+          e.append(child);
+          return child;
+        });
+  }
+
+  Selection select(String selector) {
+    assert(selector == null && selector.isNotEmpty);
+    return selectWithCallback((d, ei, e) => e.querySelector(selector));
+  }
+
+  Selection selectWithCallback(SelectionCallback<Element> fn) {
+    var subgroups = [],
+        gi = 0;
+    for (int gi = 0, len = groups.length; gi < len; ++gi) {
+      final g = groups.elementAt(gi);
+      final u = update.groups.elementAt(gi);
+      final subgroup = [];
+      for (int ei = 0, eLen = g.elements.length; ei < eLen; ++ei) {
+        final e = g.elements.elementAt(ei);
+        if (e != null) {
+          var datum = scope.datum(e),
+              selected = fn(datum, ei, g.parent);
+          scope.associate(selected, datum);
+          u.elements[ei] = selected;
+          subgroup.add(selected);
+        } else {
+          subgroup.add(null);
+        }
+      }
+      subgroups.add(new _SelectionGroupImpl(subgroup, parent: g.parent));
+    }
+    return new _SelectionImpl.selectionGroups(subgroups, scope);
+  }
+}
+
+/* Implementation of [ExitSelection] */
+class _ExitSelectionImpl extends _SelectionImpl implements ExitSelection {
+  final DataSelection update;
+  _ExitSelectionImpl(Iterable groups, DataSelection update)
+      : update = update, super.selectionGroups(groups, update.scope);
+}
+
+class _SelectionGroupImpl implements SelectionGroup {
+  Iterable<Element> elements;
+  Element parent;
+  _SelectionGroupImpl(this.elements, {this.parent});
+}
diff --git a/charted/lib/selection/src/selection_scope.dart b/charted/lib/selection/src/selection_scope.dart
new file mode 100644
index 0000000..eeb60e2
--- /dev/null
+++ b/charted/lib/selection/src/selection_scope.dart
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+
+part of charted.selection;
+
+/** SelectionScope represents a scope for all the data and DOM operations. */
+class SelectionScope {
+  Expando _associations = new Expando();
+  Expando<Map<String, Pair<Function, bool>>> _listeners = new Expando();
+  Element _root;
+
+  /** Creates a new selection scope with document as the root. */
+  SelectionScope() {
+    _root = document.documentElement;
+  }
+
+  /**
+   * Creates a new selection scope with the first element matching
+   * [selector] as the root.
+   */
+  SelectionScope.selector(String selector) {
+    if (selector == null || selector.isEmpty ){
+      throw new ArgumentError('Selector cannot be empty');
+    }
+    _root = document.querySelector(selector);
+  }
+
+  /**
+   * Creates a new selection scope with the passed [element] as [root].
+   * Charted assumes that the element is already part of the DOM.
+   */
+  SelectionScope.element(Element element) {
+    if (element == null) {
+      throw new ArgumentError('Root element for SelectionScope cannot be null');
+    }
+    _root = element;
+  }
+
+  /**
+   * Returns the [root] element for this scope.
+   * All [Selection]s and elements created using the Charted API,
+   * are created as descendants of root.
+   */
+  Element get root => _root;
+
+  /*
+   * Current event for which a callback is being called.
+   */
+  Event event;
+
+  /** Returns the stored for the given [element]. */
+  datum(Element element) => element == null ? null : _associations[element];
+
+  /** Associates data to the given [element]. */
+  associate(Element element, datum) =>
+      datum != null ? _associations[element] = datum : null;
+
+  /**
+   * Creates a new [Selection] containing the first element matching
+   * [selector].  If no element matches, the resulting selection will
+   * have a null element.
+   */
+  Selection select(String selector) =>
+      new _SelectionImpl.single(selector: selector, scope: this);
+
+  /**
+   * Creates a new [Selection] containing all elements matching [selector].
+   * If no element matches, the resulting selection will not have any
+   * elements in it.
+   */
+  Selection selectAll(String selector) =>
+      new _SelectionImpl.all(selector:selector, scope:this);
+
+  /**
+   * Creates a new [Selection] containing [elements].  Assumes that
+   * all the given elements are descendants of [root] in DOM.
+   */
+  Selection selectElements(List<Element> elements) =>
+      new _SelectionImpl.elements(elements, this);
+
+  /**
+   * Appends a new element to [root] and creates a selection containing
+   * the newly added element.
+   */
+  Selection append(String tag) {
+    var element = Namespace.createChildElement(tag, _root);
+    root.children.add(element);
+
+    return selectElements([element]);
+  }
+}
diff --git a/charted/lib/selection/src/transition_impl.dart b/charted/lib/selection/src/transition_impl.dart
new file mode 100644
index 0000000..6ea65ab
--- /dev/null
+++ b/charted/lib/selection/src/transition_impl.dart
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+part of charted.selection.transition;
+
+class _TransitionImpl implements Transition {
+  SelectionCallback _delay = (d, i, c) => 0;
+  SelectionCallback _duration =
+      (d, i, c) => Transition.defaultDurationMilliseconds;
+  Selection _selection;
+  Map _attrs = {};
+  Map _styles = {};
+  Map _attrTweens = {};
+  Map _styleTweens = {};
+  Map<AnimationTimer, Element> _timerMap = {};
+  Map<Element, List<Map>> _attrMap = {};
+  Map<Element, int> _durationMap = {};
+  bool _interrupted = false;
+  var _timerDelay = 0;
+
+  _TransitionImpl(this._selection, [num delay = 0]) {
+    _transitionNode(delay);
+    _timerDelay = delay;
+  }
+
+  Interpolator ease = clampEasingFn(
+          Transition.defaultEasingMode(Transition.defaultEasingType));
+
+  void delay(int millisecond) {
+    delayWithCallback(toCallback(millisecond));
+  }
+
+  void delayWithCallback(SelectionCallback fn) {
+    _delay = fn;
+  }
+
+  void duration(int millisecond) {
+    durationWithCallback(toCallback(millisecond));
+  }
+
+  void durationWithCallback(SelectionCallback fn) {
+    _duration = fn;
+  }
+
+  void attr(String name, val) {
+    attrWithCallback(name, toCallback(val));
+  }
+
+  void attrWithCallback(String name, SelectionCallback fn) {
+    _attrs[name] = fn;
+  }
+
+  void attrTween(String name, AttrTweenCallback tween) {
+    _attrTweens[name] = tween;
+  }
+
+  void style(String property, String val, [String priority = '']) {
+    styleWithCallback(property, toCallback(val), priority);
+  }
+
+  void styleWithCallback(String property, SelectionCallback<String> fn,
+      [String priority = '']) {
+    _styles[property] = {'callback': fn, 'priority': priority};
+  }
+
+  void styleTween(String property, StyleTweenCallback tween,
+      [String priority]) {
+    _styleTweens[property] = {'callback': tween, 'priority': priority};
+  }
+
+  // Starts a timer that registers all attributes, durations, and delays for the
+  // transition of the current selection.
+  _transitionNode(num delay) {
+    new AnimationTimer((elapsed) {
+      _selection.each((d, i, c) {
+        var tweenList = [];
+        _attrs.forEach((key, value) {
+            tweenList.add(_getAttrInterpolator(c, key, value(d, i, c)));
+        });
+        _attrTweens.forEach((key, value) {
+          tweenList.add((t) => c.setAttribute(key,
+              value(d, i, c.getAttribute(key))(t)));
+        });
+        _styles.forEach((key, value) {
+            tweenList.add(_getStyleInterpolator(c, key,
+                value['callback'](d, i, c), value['priority']));
+        });
+        _styleTweens.forEach((key, value) {
+          tweenList.add((t) => c.style.setProperty(key,
+              value['callback'](d, i,
+              c.style.getPropertyValue(key))(t).toString(), value['priority']));
+        });
+
+        _attrMap[c] = tweenList;
+        _durationMap[c] = _duration(d, i, c);
+        _timerMap[new AnimationTimer(_tick, delay: _delay(d, i, c))] = c;
+      });
+      return true;
+    }, delay: delay);
+  }
+
+  // Returns the correct interpolator function for the old and new attribute.
+  _getAttrInterpolator(Element element, String attrName, newValue) {
+    var attr = element.attributes[attrName];
+    var interpolator = createStringInterpolator(attr, newValue.toString());
+    return (t) => element.setAttribute(attrName, interpolator(t).toString());
+  }
+
+  // Returns the correct interpolator function for the old and new style.
+  _getStyleInterpolator(Element element, String styleName, newValue, priority) {
+    var style = element.style.getPropertyValue(styleName);
+
+    var interpolator = createStringInterpolator(style, newValue.toString());
+
+    return (t) => element.style.setProperty(styleName,
+        interpolator(t).toString(), priority);
+  }
+
+  // Ticks of the transition, this is the callback registered to the
+  // ChartedTimer, called on each animation frame until the transition duration
+  // has been reached.
+  bool _tick(elapsed) {
+    if (_interrupted) {
+      return true;
+    }
+    var activeNode = _timerMap[AnimationTimer.active];
+    var t = elapsed / _durationMap[activeNode];
+    for (Interpolator tween in _attrMap[activeNode]) {
+      tween(ease(t));
+    }
+    return (t >= 1) ? true : false;
+  }
+
+  // Interrupts the transition.
+  void interrupt() {
+    _interrupted = true;
+  }
+
+  Transition select(String selector) {
+    var t = new Transition(_selection.select(selector));
+    t.ease = ease;
+    t.delayWithCallback(_delay);
+    t.durationWithCallback(_duration);
+    return t;
+  }
+
+  Transition selectAll(String selector) {
+    var t = new Transition(_selection.selectAll(selector));
+    t.ease = ease;
+    t.delayWithCallback(_delay);
+    t.durationWithCallback(_duration);
+    return t;
+  }
+
+  Transition transition() {
+    var e = _selection.first;
+    var delay = _delay(_selection.scope.datum(e), 0, e) +
+        _duration(_selection.scope.datum(e), 0, e) + _timerDelay;
+    var t = new _TransitionImpl(_selection, delay);
+    t.ease = ease;
+    t.durationWithCallback(_duration);
+    return t;
+  }
+}
diff --git a/charted/lib/selection/transition.dart b/charted/lib/selection/transition.dart
new file mode 100644
index 0000000..4df2c08
--- /dev/null
+++ b/charted/lib/selection/transition.dart
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://developers.google.com/open-source/licenses/bsd
+ */
+library charted.selection.transition;
+
+import "dart:html" show Element,document;
+import "package:charted/core/timer.dart";
+import "package:charted/selection/selection.dart";
+import "package:charted/core/interpolators.dart";
+
+part 'src/transition_impl.dart';
+
+typedef Interpolator AttrTweenCallback(datum, int ei, String attr);
+typedef Interpolator StyleTweenCallback(datum, int ei, String style);
+
+/**
+ * Transitions are created using the transition operator on a selection.
+ * Transitions start automatically upon creation after a delay which defaults
+ * to zero; however, note that a zero-delay transition actually starts after a
+ * minimal (~17ms) delay, pending the first timer callback.
+ * Transitions have a default duration of 250ms.
+ */
+abstract class Transition {
+
+  /** A settable default easing type */
+  static EasingFunction defaultEasingType = easeCubic();
+
+  /** A settable default easing mode */
+  static EasingModeFunction defaultEasingMode = reflectEasingFn;
+
+  /** A settable default transition duration */
+  static int defaultDurationMilliseconds = 250;
+
+  /** Sets the ease function of the transition, default is cubic-in-out. */
+  Interpolator ease;
+
+  /**
+   * Specifies the transition delay in milliseconds. All elements are given the
+   * same delay. The default delay is 0.
+   */
+  void delay(int millisecond);
+
+  /**
+   * Sets the delay with a ChartedCallback function which would be evaluated for
+   * each selected element (in order), being passed the current datum d, the
+   * current index i, and the current DOM element. The function's return value
+   * is then used to set each element's delay.
+   */
+  void delayWithCallback(SelectionCallback fn);
+
+  /**
+   * Specifies per-element duration in milliseconds. All elements are given the
+   * same duration in millisecond.  The default duration is 250ms.
+   */
+  void duration(int millisecond);
+
+  /**
+   * Sets the duration with a ChartedCallback which would be evaluated for each
+   * selected element (in order), being passed the current datum d, the current
+   * index i, and the current DOM element.  The function's return value is then
+   * used to set each element's duration.
+   */
+  void durationWithCallback(SelectionCallback fn);
+
+  /**
+   * Sets the attribute [name] on all elements when [val] is not null.
+   * Removes the attribute when [val] is null.
+   */
+  void attr(String name, val);
+
+  /**
+   * Same as [attr], but calls [fn] for each non-null element in
+   * the selection (with data associated to the element, index of the
+   * element in it's group and the element itself) to get the value
+   * of the attribute.
+   */
+  void attrWithCallback(String name, SelectionCallback fn);
+
+  /**
+   * Transitions the value of the attribute with the specified name according to
+   * the specified tween function. The starting and ending value of the
+   * transition are determined by tween; the tween function is invoked when the
+   * transition starts on each element, being passed the current datum d, the
+   * current index i and the current attribute value a. The return value of
+   * tween must be an interpolator: a function that maps a parametric value t in
+   * the domain [0,1] to a color, number or arbitrary value.
+   */
+  void attrTween(String name, AttrTweenCallback tween);
+
+  /**
+   * Transitions the value of the CSS style property with the specified name to
+   * the specified value. An optional priority may also be specified, either as
+   * null or the string "important" (without the exclamation point). The
+   * starting value of the transition is the current computed style property
+   * value, and the ending value is the specified value. All elements are
+   * transitioned to the same style property value.
+   */
+  void style(String property, String val, [String priority]);
+
+  /**
+   * Transitions the style with a CartedCallback which would be evaluated for
+   * each selected element (in order), being passed the current datum d and the
+   * current index i, and the current DOM element.
+   * The function's return value is then used to transition each element's
+   * style property.
+   */
+  void styleWithCallback(String property,
+      SelectionCallback<String> fn, [String priority]);
+
+  /**
+   * Transitions the value of the CSS style property with the specified name
+   * according to the specified tween function. An optional priority may also
+   * be specified, either as null or the string "important" (without the
+   * exclamation point). The starting and ending value of the transition are
+   * determined by tween; the tween function is invoked when the transition
+   * starts on each element, being passed the current datum d, the current index
+   * i and the current attribute value a. The return value of tween must be an
+   * interpolator: a function that maps a parametric value t in the domain [0,1]
+   * to a color, number or arbitrary value.
+   */
+  void styleTween(String property, StyleTweenCallback tween, [String priority]);
+
+  /** Interrupts the transition. */
+  void interrupt();
+
+  /**
+   * For each element in the current transition, selects the first descendant
+   * element that matches the specified selector string. If no element matches
+   * the specified selector for the current element, the element at the current
+   * index will be null in the returned selection; operators (with the exception
+   * of data) automatically skip null elements, thereby preserving the index of
+   * the existing selection. If the current element has associated data, this
+   * data is inherited by the returned subselection, and automatically bound to
+   * the newly selected elements. If multiple elements match the selector, only
+   * the first matching element in document traversal order will be selected.
+   */
+  Transition select(String selector);
+
+  /**
+   * For each element in the current transition, selects descendant elements
+   * that match the specified selector string. The returned selection is grouped
+   * by the ancestor node in the current selection. If no element matches the
+   * specified selector for the current element, the group at the current index
+   * will be empty in the returned selection. The subselection does not inherit
+   * data from the current selection; however, if data was previously bound to
+   * the selected elements, that data will be available to operators.
+   */
+  Transition selectAll(String selector);
+
+  /**
+   * Creates a new transition on the same selected elements that starts with
+   * this transition ends. The new transition inherits this transition’s
+   * duration and easing. This can be used to define chained transitions without
+   * needing to listen for "end" events.  Only works when parent delay and
+   * duration are constant.
+   */
+  Transition transition();
+
+  /** Factory method to create an instance of the default implementation */
+  factory Transition(Selection selection) => new _TransitionImpl(selection);
+}
diff --git a/charted/lib/svg/axis.dart b/charted/lib/svg/axis.dart
new file mode 100644
index 0000000..8441113
--- /dev/null
+++ b/charted/lib/svg/axis.dart
@@ -0,0 +1,230 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+library charted.svg.axis;
+
+import 'dart:html' show Element;
+import 'dart:math' as math;
+
+import 'package:charted/core/scales.dart';
+import 'package:charted/core/utils.dart';
+import 'package:charted/selection/selection.dart';
+
+///
+/// [SvgAxis] helps draw chart axes based on a given scale.
+///
+class SvgAxis {
+  /// Store of axis roots mapped to currently used scales
+  static final _scales = new Expando<Scale>();
+
+  /// Orientation of the axis. Defaults to [ORIENTATION_BOTTOM].
+  final String orientation;
+
+  /// Scale used on this axis
+  final Scale scale;
+
+  /// Size of all inner ticks
+  final num innerTickSize;
+
+  /// Size of the outer two ticks
+  final num outerTickSize;
+
+  /// Padding on the ticks
+  final num tickPadding;
+
+  /// List of values to be used on the ticks
+  List _tickValues;
+
+  /// Formatter for the tick labels
+  FormatFunction _tickFormat;
+
+  SvgAxis({
+      this.orientation: ORIENTATION_BOTTOM,
+      this.innerTickSize: 6,
+      this.outerTickSize: 6,
+      this.tickPadding: 3,
+      Iterable tickValues,
+      FormatFunction tickFormat,
+      Scale scale }) : scale = scale == null ? new LinearScale() : scale {
+    _tickFormat = tickFormat == null
+        ? this.scale.createTickFormatter()
+        : tickFormat;
+    _tickValues = isNullOrEmpty(tickValues) ? this.scale.ticks : tickValues;
+  }
+
+  Iterable get tickValues => _tickValues;
+
+  FormatFunction get tickFormat => _tickFormat;
+
+  /// Draw an axis on each non-null element in selection
+  draw(Selection g, {SvgAxisTicks axisTicksBuilder, bool isRTL}) =>
+      g.each((d, i, e) => create(
+          e, g.scope, axisTicksBuilder: axisTicksBuilder, isRTL: isRTL));
+
+  /// Create an axis on [element].
+  create(Element element, SelectionScope scope, {
+      SvgAxisTicks axisTicksBuilder, bool isRTL: false}) {
+
+    var group = scope.selectElements([element]),
+        older = _scales[element],
+        current = _scales[element] = scale.clone(),
+        isInitialRender = older == null;
+
+    var isLeft = orientation == ORIENTATION_LEFT,
+        isRight = !isLeft && orientation == ORIENTATION_RIGHT,
+        isVertical = isLeft || isRight,
+        isBottom = !isVertical && orientation == ORIENTATION_BOTTOM,
+        isTop = !(isVertical || isBottom) && orientation == ORIENTATION_TOP,
+        isHorizontal = !isVertical;
+
+    if (older == null) older = current;
+    if (axisTicksBuilder == null) {
+      axisTicksBuilder = new SvgAxisTicks();
+    }
+    axisTicksBuilder.init(this);
+
+    var values = axisTicksBuilder.ticks,
+        formatted = axisTicksBuilder.formattedTicks,
+        ellipsized = axisTicksBuilder.shortenedTicks;
+
+    var ticks = group.selectAll('.tick').data(values, current.scale),
+        exit = ticks.exit,
+        transform = isVertical ? _yAxisTransform : _xAxisTransform,
+        sign = isTop || isLeft ? -1 : 1,
+        isEllipsized = ellipsized != formatted;
+
+    var enter = ticks.enter.appendWithCallback((d, i, e) {
+      var group = Namespace.createChildElement('g', e)
+        ..classes.add('tick')
+        ..append(Namespace.createChildElement('line',  e))
+        ..append(Namespace.createChildElement('text', e)
+            ..attributes['dy'] =
+                isVertical ? '0.32em' : (isBottom ? '0.71em' : '0'));
+      if (!isInitialRender) {
+        group.style.setProperty('opacity', EPSILON.toString());
+      }
+      return group;
+    });
+
+    // All attributes/styles/classes that may change due to theme and scale.
+    // TODO(prsd): Order elements before updating ticks.
+    ticks.each((d, i, e) {
+      Element line = e.firstChild;
+      Element text = e.lastChild;
+      bool isRTLText = false; // FIXME(prsd)
+
+      if (isHorizontal) {
+        line.attributes['y2'] = (sign * innerTickSize).toString();
+        text.attributes['y'] =
+            (sign * (math.max(innerTickSize, 0) + tickPadding)).toString();
+
+        if (axisTicksBuilder.rotation != 0) {
+          text.attributes
+            ..['transform'] =
+                'rotate(${(isRTL ? -1 : 1) * axisTicksBuilder.rotation})'
+            ..['text-anchor'] = isRTL ? 'end' : 'start';
+        } else {
+          text.attributes
+            ..['transform'] = ''
+            ..['text-anchor'] = 'middle';
+        }
+      } else {
+        line.attributes['x2'] = (sign * innerTickSize).toString();
+        text.attributes
+            ..['x'] = '${sign * (math.max(innerTickSize, 0) + tickPadding)}'
+            ..['text-anchor'] = isLeft
+                ? (isRTLText ? 'start' : 'end')
+                : (isRTLText ? 'end' : 'start');
+      }
+
+      text.text = fixSimpleTextDirection(ellipsized.elementAt(i));
+      text.attributes['data-detail'] = formatted.elementAt(i);
+
+      if (isInitialRender) {
+        var dx = current is OrdinalScale ? current.rangeBand / 2 : 0;
+        e.attributes['transform'] = isHorizontal
+            ? 'translate(${current.scale(d) + dx},0)'
+            : 'translate(0,${current.scale(d) + dx})';
+      } else {
+        e.style.setProperty('opacity', '1.0');
+      }
+    });
+
+    // Transition existing ticks to right positions
+    if (!isInitialRender) {
+      var transformFn;
+      if (current is OrdinalScale && current.rangeBand != 0) {
+        var dx = current.rangeBand / 2;
+        transformFn = (d) => current.scale(d) + dx;
+      } else if (older is OrdinalScale && older.rangeBand != 0) {
+        older = current;
+      } else {
+        transform(ticks, current.scale);
+      }
+
+      transform(enter, transformFn != null ? transformFn : older.scale);
+      transform(ticks, transformFn != null ? transformFn : current.scale);
+    }
+
+    exit.remove();
+
+    // Append the outer domain.
+    var path = element.querySelector('.domain'),
+        tickSize = sign * outerTickSize,
+        range = current.rangeExtent;
+    if (path == null) {
+      path = Namespace.createChildElement('path', element);
+      path.classes.add('domain');
+    }
+    path.attributes['d'] = isLeft || isRight
+        ? 'M${tickSize},${range.min}H0V${range.max}H${tickSize}'
+        : 'M${range.min},${tickSize}V0H${range.max}V${tickSize}';
+    element.append(path);
+  }
+
+  _xAxisTransform(Selection selection, transformFn) {
+    selection.transition()
+      ..attrWithCallback(
+          'transform', (d, i, e) => 'translate(${transformFn(d)},0)');
+  }
+
+  _yAxisTransform(Selection selection, transformFn) {
+    selection.transition()
+      ..attrWithCallback(
+          'transform', (d, i, e) => 'translate(0,${transformFn(d)})');
+  }
+}
+
+/// Interface and the default implementation of [SvgAxisTicks].
+/// SvgAxisTicks provides strategy to handle overlapping ticks on an
+/// axis.  Default implementation assumes that the ticks don't overlap.
+class SvgAxisTicks {
+  bool _ellipsized;
+  int _rotation;
+  List _ticks;
+  List _formattedTicks;
+
+  void init(SvgAxis axis) {
+    _ticks = axis.tickValues;
+    _formattedTicks = _ticks.map((x) => axis.tickFormat(x));
+  }
+
+  /// When non-zero, indicates the angle by which each tick value must be
+  /// rotated to avoid the overlap.
+  int get rotation => _rotation;
+
+  /// List of ticks that will be displayed on the axis.
+  Iterable get ticks => _ticks;
+
+  /// List of formatted ticks values.
+  Iterable get formattedTicks => _formattedTicks;
+
+  /// List of clipped tick values, if they had to be clipped. Must be same
+  /// as the [formattedTicks] if none of the ticks were ellipsized.
+  Iterable get shortenedTicks => _formattedTicks;
+}
diff --git a/charted/lib/svg/shapes.dart b/charted/lib/svg/shapes.dart
new file mode 100644
index 0000000..bf5a3c4
--- /dev/null
+++ b/charted/lib/svg/shapes.dart
@@ -0,0 +1,29 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+library charted.svg.shapes;
+
+import "dart:html" show Element;
+import "dart:math" as math;
+
+import "package:charted/core/utils.dart";
+import "package:charted/core/interpolators.dart";
+import "package:charted/selection/selection.dart";
+
+part 'shapes/arc.dart';
+part 'shapes/line.dart';
+part 'shapes/area.dart';
+part 'shapes/rect.dart';
+
+/// Common interface provided by all shape implementations.
+abstract class SvgShape {
+  /// Generate the path based on the passed data [d], element index [i]
+  /// and the element [e].  This method follows the same signature as the
+  /// other callbacks used by selection API - [ChartedCallback<String>]
+  String path(d, int i, Element e);
+}
diff --git a/charted/lib/svg/shapes/arc.dart b/charted/lib/svg/shapes/arc.dart
new file mode 100644
index 0000000..0cec145
--- /dev/null
+++ b/charted/lib/svg/shapes/arc.dart
@@ -0,0 +1,131 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.svg.shapes;
+
+///
+/// [SvgArc] provides a data-driven way to create path descriptions
+/// that can be used to draw arcs - like those used in pie-charts.
+///
+class SvgArc implements SvgShape {
+  static const _OFFSET = -HALF_PI;
+  static const _MAX = TAU - EPSILON;
+
+  /// [innerRadiusCallback] is called to get inner radius of the arc.
+  /// As with other callbacks, [innerRadiusCallback] is passed data, index
+  /// and element in the context.
+  final SelectionCallback<num> innerRadiusCallback;
+
+  /// [outerRadiusCallback] is called to get outer radius of the arc.
+  /// As with other callbacks, [outerRadiusCallback] is passed data, index
+  /// and element in the context.
+  final SelectionCallback<num> outerRadiusCallback;
+
+  /// [startAngleCallback] is called to get the start angle of the arc.
+  /// As with other callbacks, [startAngleCallback] is passed data, index
+  /// and element in the context.
+  final SelectionCallback<num> startAngleCallback;
+
+  /// [endAngleCallback] is called to get the start angle of the arc.
+  /// As with other callbacks, [endAngleCallback] is passed data, index
+  /// and element in the context.
+  final SelectionCallback<num> endAngleCallback;
+
+  SvgArc({
+      this.innerRadiusCallback : defaultInnerRadiusCallback,
+      this.outerRadiusCallback: defaultOuterRadiusCallback,
+      this.startAngleCallback: defaultStartAngleCallback,
+      this.endAngleCallback: defaultEndAngleCallback
+  });
+
+  String path(d, int i, Element e) {
+    var ir = innerRadiusCallback(d, i, e),
+        or = outerRadiusCallback(d, i, e),
+        start = startAngleCallback(d, i, e) + _OFFSET,
+        end = endAngleCallback(d, i, e) + _OFFSET,
+        sa = math.min(start, end),
+        ea = math.max(start, end),
+        delta = ea - sa;
+
+    if (delta > _MAX) {
+      return ir > 0
+          ? "M0,$or" "A$or,$or 0 1,1 0,-$or" "A$or,$or 0 1,1 0,$or"
+            "M0,$ir" "A$ir,$ir 0 1,0 0,-$ir" "A$ir,$ir 0 1,0 0,$ir" "Z"
+          : "M0,$or" "A$or,$or 0 1,1 0,-$or" "A$or,$or 0 1,1 0,$or" "Z";
+    }
+
+    var ss = math.sin(sa),
+        se = math.sin(ea),
+        cs = math.cos(sa),
+        ce = math.cos(ea),
+        df = delta < PI ? 0 : 1;
+
+    return ir > 0
+        ? "M${or * cs},${or * ss}" "A$or,$or 0 $df,1 ${or * ce},${or * se}"
+          "L${ir * ce},${ir * se}" "A$ir,$ir 0 $df,0 ${ir * cs},${ir * ss}"
+          "Z"
+        : "M${or * cs},${or * ss}" "A$or,$or 0 $df,1 ${or * ce},${or * se}"
+          "L0,0" "Z";
+  }
+
+  List centroid(d, int i, Element e) {
+    var r = (innerRadiusCallback(d, i, e) + outerRadiusCallback(d, i, e)) / 2,
+        a = (startAngleCallback(d, i, e) + endAngleCallback(d, i, e)) / 2 -
+            math.PI / 2;
+    return [math.cos(a) * r, math.sin(a) * r];
+  }
+
+  /// Default [innerRadiusCallback] returns data.innerRadius
+  static num defaultInnerRadiusCallback(d, i, e) =>
+      d is! SvgArcData || d.innerRadius == null ? 0 : d.innerRadius;
+
+  /// Default [outerRadiusCallback] returns data.outerRadius
+  static num defaultOuterRadiusCallback(d, i, e) =>
+      d is! SvgArcData || d.outerRadius == null ? 0 : d.outerRadius;
+
+  /// Default [startAngleCallback] returns data.startAngle
+  static num defaultStartAngleCallback(d, i, e) =>
+      d is! SvgArcData || d.startAngle == null ? 0 : d.startAngle;
+
+  /// Default [endAngleCallback] that returns data.endAngle
+  static num defaultEndAngleCallback(d, i, e) =>
+      d is! SvgArcData || d.endAngle == null ? 0 : d.endAngle;
+}
+
+/// Value type for SvgArc as used by default property accessors in SvgArc
+class SvgArcData {
+  dynamic data;
+  num value;
+  num innerRadius;
+  num outerRadius;
+  num startAngle;
+  num endAngle;
+
+  SvgArcData(this.data, this.value,
+      this.startAngle, this.endAngle, [
+      this.innerRadius = 0, this.outerRadius = 100 ]);
+}
+
+
+/// Returns the interpolator between two [SvgArcData] [a] and [b].
+///
+/// The interpolator will interpolate the older innerRadius and outerDadius with
+/// newer ones, as well as older startAngle and endAngle with newer ones.
+Interpolator interpolateSvgArcData(SvgArcData a, SvgArcData b) {
+  var ast = a.startAngle,
+      aen = a.endAngle,
+      ai = a.innerRadius,
+      ao = a.outerRadius,
+      bst = b.startAngle - ast,
+      ben = b.endAngle - aen,
+      bi = b.innerRadius - ai,
+      bo = b.outerRadius - ao;
+
+  return (t) => new SvgArcData(b.data, b.value,
+    (ast + bst * t), (aen + ben * t), (ai + bi * t), (ao + bo * t));
+}
diff --git a/charted/lib/svg/shapes/area.dart b/charted/lib/svg/shapes/area.dart
new file mode 100644
index 0000000..0152b02
--- /dev/null
+++ b/charted/lib/svg/shapes/area.dart
@@ -0,0 +1,13 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.svg.shapes;
+
+class SvgArea implements SvgShape {
+  String path(d, i, e) => null;
+}
diff --git a/charted/lib/svg/shapes/line.dart b/charted/lib/svg/shapes/line.dart
new file mode 100644
index 0000000..4264544
--- /dev/null
+++ b/charted/lib/svg/shapes/line.dart
@@ -0,0 +1,88 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.svg.shapes;
+
+/// Function to convert a list of points to path.
+typedef String LineInterpolator(Iterable<math.Point> points, int tension);
+
+///
+/// [SvgLine] provides a data-driven way to create path descriptions
+/// that can be used to draw lines.
+///
+class SvgLine implements SvgShape {
+  static const LINE_INTERPOLATOR_LINEAR = 'linear';
+
+  static final LINE_INTERPOLATORS = {
+    LINE_INTERPOLATOR_LINEAR: _linear
+  };
+
+  /// Callback to access/convert datum to x coordinate value.
+  final SelectionValueAccessor<num> xValueAccessor;
+
+  /// Callback to access/convert datum to y coordinate value.
+  final SelectionValueAccessor<num> yValueAccessor;
+
+  /// Callback that is used to determine if a valaue is considered valid.
+  /// If [isDefined] returns false at any time, the value isn't part
+  /// of the line - the line would be split.
+  final SelectionCallback<bool> isDefined;
+
+  /// Interpolator that is used for creating the path.
+  final LineInterpolator interpolator;
+
+  /// Tension of the line, as used by a few interpolators.
+  final int tension;
+
+  SvgLine({
+      this.xValueAccessor: defaultDataToX,
+      this.yValueAccessor: defaultDataToY,
+      this.isDefined: defaultIsDefined,
+      this.tension: 0,
+      String interpolate: LINE_INTERPOLATOR_LINEAR })
+      : interpolator = LINE_INTERPOLATORS[interpolate] {
+    assert(interpolator != null);
+  }
+
+  /// Generates path for drawing a line based in the selected [interpolator]
+  @override
+  String path(data, int index, Element e) {
+    assert(data is Iterable);
+    var segments = new StringBuffer(),
+        points = [];
+    for (int i = 0, len = data.length; i < len; ++i) {
+      final d = data.elementAt(i);
+      if (isDefined(d, i, e)) {
+        points.add(new math.Point(xValueAccessor(d, i), yValueAccessor(d, i)));
+      } else {
+        segments.write('M${interpolator(points, tension)}');
+        points.clear();
+      }
+    }
+    if (points.isNotEmpty) {
+      segments.write('M${interpolator(points, tension)}');
+    }
+    return segments.toString();
+  }
+
+  /// Default implementation of [xValueAccessor].
+  /// Returns the first element if [d] is an iterable, otherwise returns [d].
+  static num defaultDataToX(d, i) => d is Iterable ? d.first : d;
+
+  /// Default implementation of [yValueAccessor].
+  /// Returns the second element if [d] is an iterable, otherwise returns [d].
+  static num defaultDataToY(d, i) => d is Iterable ? d.elementAt(1) : d;
+
+  /// Default implementation of [isDefined].
+  /// Returns true for all non-null values of [d].
+  static bool defaultIsDefined(d, i, e) => d != null;
+
+  /// Linear interpolator.
+  static String _linear(Iterable points, _) =>
+      points.map((pt) => '${pt.x},${pt.y}').join('L');
+}
diff --git a/charted/lib/svg/shapes/rect.dart b/charted/lib/svg/shapes/rect.dart
new file mode 100644
index 0000000..a4c3c56
--- /dev/null
+++ b/charted/lib/svg/shapes/rect.dart
@@ -0,0 +1,32 @@
+//
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd
+//
+
+part of charted.svg.shapes;
+
+/// Draw a rectangle at [x], [y] which is [width] pixels wide and
+/// [height] pixels height.  [topLeft], [topRight], [bottomRight] and
+/// [bottomLeft] are the corner radius at each of the four corners.
+String roundedRect(int x, int y, int width, int height,
+    int topLeft, int topRight, int bottomRight, int bottomLeft) =>
+        'M${x+topLeft},${y} '
+        'L${x+width-topRight},${y} '
+            'Q${x+width},${y} ${x+width},${y+topRight}'
+        'L${x+width},${y+height-bottomRight} '
+            'Q${x+width},${y+height} ${x+width-bottomRight},${y+height}'
+        'L${x+bottomLeft},${y+height} '
+            'Q${x},${y+height} ${x},${y+height-bottomLeft}'
+        'L${x},${y+topLeft} '
+            'Q${x},${y} ${x+topLeft},${y} Z';
+
+/// Draw a rectangle with rounded corners on both corners on the right.
+String rightRoundedRect(int x, int y, int width, int height, int radius) =>
+    roundedRect(x, y, width, height, 0, radius, radius, 0);
+
+/// Draw a rectangle with rounded corners on both corners on the top.
+String topRoundedRect(int x, int y, int width, int height, int radius) =>
+    roundedRect(x, y, width, height, radius, radius, 0, 0);
diff --git a/charted/pubspec.yaml b/charted/pubspec.yaml
new file mode 100644
index 0000000..e43c8a3
--- /dev/null
+++ b/charted/pubspec.yaml
@@ -0,0 +1,24 @@
+name: charted
+version: 0.2.3
+authors:
+- Prasad Sunkari <prsd@google.com>
+- Michael Cheng <midoringo@google.com>
+- Yan Qiao <yqiao@google.com>
+description: Visualization toolkit for Dart - Provides D3 (http://d3js.org) like Selection API, utilities to achieve data driven DOM and an easy-to-use Charting library based on the Selection API.
+homepage: https://github.com/google/charted
+dependencies:
+  browser: any
+  intl: any
+  logging: any
+  observe: any
+  quiver: any
+dev_dependencies:
+  hop: '>=0.27.0'
+  http: any
+  unittest: any
+transformers:
+- $dart2js:
+    minify: true
+    commandLineOptions:
+    - --trust-type-annotations
+    - --trust-primitives
diff --git a/cli_util/lib/cli_util.dart b/cli_util/lib/cli_util.dart
new file mode 100644
index 0000000..683bef7
--- /dev/null
+++ b/cli_util/lib/cli_util.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2015, 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 cli_util;
+
+import 'dart:io';
+
+import 'package:which/which.dart';
+
+/// Return the path to the current Dart SDK. This will return `null` if we are
+/// unable to locate the Dart SDK.
+Directory getSdkDir([List<String> cliArgs]) {
+  // Look for --dart-sdk on the command line.
+  if (cliArgs != null) {
+    int index = cliArgs.indexOf('--dart-sdk');
+
+    if (index != -1 && (index + 1 < cliArgs.length)) {
+      return new Directory(cliArgs[index + 1]);
+    }
+
+    for (String arg in cliArgs) {
+      if (arg.startsWith('--dart-sdk=')) {
+        return new Directory(arg.substring('--dart-sdk='.length));
+      }
+    }
+  }
+
+  // Look in env['DART_SDK']
+  if (Platform.environment['DART_SDK'] != null) {
+    return new Directory(Platform.environment['DART_SDK']);
+  }
+
+  // Look relative to the dart executable.
+  Directory sdkDirectory = new File(Platform.executable).parent.parent;
+  if (_isSdkDir(sdkDirectory)) return sdkDirectory;
+
+  // Try and locate the VM using 'which'.
+  String executable = whichSync('dart', orElse: () => null);
+
+  if (executable != null) {
+    // In case Dart is symlinked (e.g. homebrew on Mac) follow symbolic links.
+    Link link = new Link(executable);
+    if (link.existsSync()) {
+      executable = link.resolveSymbolicLinksSync();
+    }
+
+    File dartVm = new File(executable);
+    Directory dir = dartVm.parent.parent;
+    if (_isSdkDir(dir)) return dir;
+  }
+
+  return null;
+}
+
+bool _isSdkDir(Directory dir) => _joinFile(dir, ['version']).existsSync();
+
+File _joinFile(Directory dir, List<String> files) {
+  String pathFragment = files.join(Platform.pathSeparator);
+  return new File("${dir.path}${Platform.pathSeparator}${pathFragment}");
+}
diff --git a/cli_util/pubspec.yaml b/cli_util/pubspec.yaml
new file mode 100644
index 0000000..80f21e1
--- /dev/null
+++ b/cli_util/pubspec.yaml
@@ -0,0 +1,11 @@
+name: cli_util
+version: 0.0.1+1
+author: Dart Team <misc@dartlang.org>
+description: A library to help in building Dart command-line apps.
+homepage: https://github.com/dart-lang/cli_util
+environment:
+  sdk: '>=1.0.0 <2.0.0'
+dependencies:
+  which: '>=0.1.2 <0.2.0'
+dev_dependencies:
+  unittest: '>=0.11.0 <0.12.0'
diff --git a/code_transformers/lib/assets.dart b/code_transformers/lib/assets.dart
new file mode 100644
index 0000000..4a3516f
--- /dev/null
+++ b/code_transformers/lib/assets.dart
@@ -0,0 +1,150 @@
+// 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.
+
+/// Common methods used by transfomers for dealing with asset IDs.
+library code_transformers.assets;
+
+import 'dart:math' show min, max;
+
+import 'package:barback/barback.dart';
+import 'package:path/path.dart' as path;
+import 'package:source_span/source_span.dart';
+
+import 'messages/build_logger.dart';
+import 'src/messages.dart';
+
+/// Create an [AssetId] for a [url] seen in the [source] asset.
+///
+/// By default this is used to resolve relative urls that occur in HTML assets,
+/// including cross-package urls of the form "packages/foo/bar.html". Dart
+/// "package:" urls are not resolved unless [source] is Dart file (has a .dart
+/// extension).
+///
+/// This function returns null if [url] can't be resolved. We log a warning
+/// message in [logger] if we know the reason why resolution failed. This
+/// happens, for example, when using incorrect paths to reach into another
+/// package, or when [errorsOnAbsolute] is true and the url seems to be
+/// absolute. If [span] is not `null` it is used to provide context for any
+/// warning message(s) generated.
+// TODO(sigmund): delete once this is part of barback (dartbug.com/12610)
+AssetId uriToAssetId(
+    AssetId source, String url, TransformLogger logger, SourceSpan span,
+    {bool errorOnAbsolute: true}) {
+  if (url == null || url == '') return null;
+  var uri = Uri.parse(url);
+  var urlBuilder = path.url;
+  if (uri.host != '' || uri.scheme != '' || urlBuilder.isAbsolute(url)) {
+    if (source.extension == '.dart' && uri.scheme == 'package') {
+      var index = uri.path.indexOf('/');
+      if (index != -1) {
+        return new AssetId(
+            uri.path.substring(0, index), 'lib${uri.path.substring(index)}');
+      }
+    }
+
+    if (errorOnAbsolute) {
+      var msg = NO_ABSOLUTE_PATHS.create({'url': url});
+      logger.warning(logger is BuildLogger ? msg : msg.snippet, span: span);
+    }
+    return null;
+  }
+
+  var targetPath = urlBuilder
+      .normalize(urlBuilder.join(urlBuilder.dirname(source.path), url));
+  var segments = urlBuilder.split(targetPath);
+  var sourceSegments = urlBuilder.split(source.path);
+  assert(sourceSegments.length > 0);
+  var topFolder = sourceSegments[0];
+  var entryFolder = topFolder != 'lib' && topFolder != 'asset';
+
+  // Find the first 'packages/'  or 'assets/' segment:
+  var packagesIndex = segments.indexOf('packages');
+  var assetsIndex = segments.indexOf('assets');
+  var index = (packagesIndex >= 0 && assetsIndex >= 0)
+      ? min(packagesIndex, assetsIndex)
+      : max(packagesIndex, assetsIndex);
+  if (index > -1) {
+    if (entryFolder) {
+      // URLs of the form "packages/foo/bar" seen under entry folders (like
+      // web/, test/, example/, etc) are resolved as an asset in another
+      // package. 'packages' can be used anywhere, there is no need to walk up
+      // where the entrypoint file was.
+      // TODO(sigmund): this needs to change: Only resolve when index == 1 &&
+      // topFolder == segment[0], otherwise give a warning (dartbug.com/17596).
+      return _extractOtherPackageId(index, segments, logger, span);
+    } else if (index == 1 && segments[0] == '..') {
+      // Relative URLs of the form "../../packages/foo/bar" in an asset under
+      // lib/ or asset/ are also resolved as an asset in another package, but we
+      // check that the relative path goes all the way out where the packages
+      // folder lives (otherwise the app would not work in Dartium). Since
+      // [targetPath] has been normalized, "packages" or "assets" should be at
+      // index 1.
+      return _extractOtherPackageId(1, segments, logger, span);
+    } else {
+      var prefix = segments[index];
+      var fixedSegments = [];
+      fixedSegments.addAll(sourceSegments.map((_) => '..'));
+      fixedSegments.addAll(segments.sublist(index));
+      var fixedUrl = urlBuilder.joinAll(fixedSegments);
+      var msg = INVALID_URL_TO_OTHER_PACKAGE
+          .create({'url': url, 'prefix': prefix, 'fixedUrl': fixedUrl});
+      logger.warning(logger is BuildLogger ? msg : msg.snippet, span: span);
+      return null;
+    }
+  }
+
+  // Otherwise, resolve as a path in the same package.
+  return new AssetId(source.package, targetPath);
+}
+
+AssetId _extractOtherPackageId(
+    int index, List segments, TransformLogger logger, SourceSpan span) {
+  if (index >= segments.length) return null;
+  var prefix = segments[index];
+  if (prefix != 'packages' && prefix != 'assets') return null;
+  var folder = prefix == 'packages' ? 'lib' : 'asset';
+  if (segments.length < index + 3) {
+    var msg = INVALID_PREFIX_PATH.create({'prefix': prefix, 'folder': folder});
+    logger.warning(logger is BuildLogger ? msg : msg.snippet, span: span);
+    return null;
+  }
+  return new AssetId(segments[index + 1],
+      path.url.join(folder, path.url.joinAll(segments.sublist(index + 2))));
+}
+
+/// Gets a URI which would be appropriate for importing the file represented by
+/// [assetId].
+///
+/// This function returns null if we cannot determine a uri for [assetId].
+///
+/// Note that [assetId] may represent a non-importable file such as a part.
+String assetIdToUri(AssetId assetId,
+    {TransformLogger logger, SourceSpan span, AssetId from}) {
+  if (!assetId.path.startsWith('lib/')) {
+    // Cannot do absolute imports of non lib-based assets.
+    if (from == null) {
+      if (logger != null) {
+        var msg = UNSPECIFIED_FROM_IN_NON_LIB_ASSET.create({'id': '$assetId'});
+        logger.warning(logger is BuildLogger ? msg : msg.snippet, span: span);
+      }
+      return null;
+    }
+
+    if (assetId.package != from.package) {
+      if (logger != null) {
+        var msg = IMPORT_FROM_DIFFERENT_PACKAGE
+            .create({'toId': '$assetId', 'fromId': '$from'});
+        logger.warning(logger is BuildLogger ? msg : msg.snippet, span: span);
+      }
+      return null;
+    }
+    return new Uri(
+            path: path.url.relative(assetId.path, from: path.url.dirname(from.path)))
+        .toString();
+  }
+
+  return Uri
+      .parse('package:${assetId.package}/${assetId.path.substring(4)}')
+      .toString();
+}
diff --git a/code_transformers/lib/benchmarks.dart b/code_transformers/lib/benchmarks.dart
new file mode 100644
index 0000000..c048457
--- /dev/null
+++ b/code_transformers/lib/benchmarks.dart
@@ -0,0 +1,76 @@
+// 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 code_transformers.benchmarks;
+
+import 'dart:async';
+import 'package:barback/barback.dart';
+import 'src/async_benchmark_base.dart';
+
+/// A benchmark for testing the performance of transformer phases.
+class TransformerBenchmark extends AsyncBenchmarkBase {
+  /// Internal abstraction layer for barback.
+  _BenchmarkHelper _helper;
+
+  /// The transformer phases to be ran.
+  final List<List<Transformer>> transformers;
+
+  /// The files to pass to barback.
+  final Map<AssetId, String> files;
+
+  TransformerBenchmark(this.transformers, this.files);
+
+  @override
+  Future setup() {
+    _helper = new _BenchmarkHelper(transformers, files);
+    return new Future.value();
+  }
+
+  @override
+  Future run() => _helper.run();
+
+  @override
+  teardown() {
+    _helper = null;
+    return new Future.value();
+  }
+}
+
+/// Barback abstraction layer.
+class _BenchmarkHelper implements PackageProvider {
+  /// All the files available.
+  final Map<AssetId, String> _files;
+
+  /// All the packages.
+  final Iterable<String> packages;
+
+  /// Internal instance of barback.
+  Barback _barback;
+
+  /// Subscription to barback results.
+  StreamSubscription<BuildResult> resultSubscription;
+
+  _BenchmarkHelper(
+      List<List<Transformer>> transformers, Map<AssetId, String> files)
+      : _files = files,
+        packages = new Set.from(files.keys.map((assetId) => assetId.package)) {
+    _barback = new Barback(this);
+    for (var package in packages) {
+      _barback.updateTransformers(package, transformers);
+    }
+  }
+
+  /// Look up an [AssetId] in [files] and return an [Asset] for it.
+  @override
+  Future<Asset> getAsset(AssetId id) =>
+      new Future.value(new Asset.fromString(id, _files[id]));
+
+  /// Tells barback which files have changed, and thus anything that depends on
+  /// it on should be computed. Returns a [Future] that completes once some
+  /// results are received.
+  Future run([Iterable<AssetId> assetIds]) {
+    if (assetIds == null) assetIds = _files.keys;
+    _barback.updateSources(assetIds);
+    return _barback.results.first;
+  }
+}
diff --git a/code_transformers/lib/messages/build_logger.dart b/code_transformers/lib/messages/build_logger.dart
new file mode 100644
index 0000000..0e7ecf8
--- /dev/null
+++ b/code_transformers/lib/messages/build_logger.dart
@@ -0,0 +1,155 @@
+// Copyright (c) 2013, 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 code_transformers.messages.messages_logger;
+
+import 'dart:async';
+import 'dart:convert' show JSON;
+
+import 'package:barback/barback.dart';
+import 'package:source_span/source_span.dart';
+
+import 'messages.dart' show Message, MessageId, BuildLogEntry, LogEntryTable;
+
+/// A [TransformLogger] used to track error and warning messages produced during
+/// a build.
+///
+/// This logger records all messages that were logged and then forwards
+/// the calls to an underlying [TransformLogger]. The internal records support
+/// serializing the errors and emiting them to an asset (so they can be
+/// presented to the user in a web-based client), clustering similar messages
+/// together, sorting messages in order of importance, etc.
+///
+/// The logger also supports reporting error messages as warnings. Barback makes
+/// error messages stop the transformation process, which sometimes can surprise
+/// users. Turning errors into warnings is especially useful when used within
+/// `pub serve`, where we would like the transformation to continue as far as it
+/// can. When this flag is turned on, the level is still recorded as an error,
+/// so a web client UI can still highlight their importance.
+// TODO(sigmund): also cluster messages when they are reported on the
+// command-line.
+class BuildLogger implements TransformLogger {
+  /// Underling transform that is currently active. This can be either an
+  /// [AggregateTransform] or [Transform].
+  final _transform;
+
+  /// The primary input id.
+  final AssetId _primaryId;
+
+  /// Logs created during the current transform.
+  final LogEntryTable _logs = new LogEntryTable();
+
+  /// Whether to use `warning` or `error` when forwarding error messages to the
+  /// underlying logger in `_transform.logger`.
+  final bool convertErrorsToWarnings;
+
+  /// Uri prefix to link for additional details. If set, messages logged through
+  /// this logger will contain an additional sentence, telling users to find
+  /// more details at `$detailsUri#packagename_id`.
+  final String detailsUri;
+
+  /// If transform is a [Transform] then [primaryId] will default to the
+  /// primaryInput.id, if it is an [AggregateTransform] then you must pass in
+  /// a [primaryId] to be used, otherwise you will get a runtime error.
+  BuildLogger(transform,
+      {this.convertErrorsToWarnings: false, AssetId primaryId, this.detailsUri})
+      : _transform = transform,
+        _primaryId = primaryId != null ? primaryId : transform.primaryInput.id;
+
+  /// Records a message at the fine level. If [msg] is a [Message] it is
+  /// recorded directly, otherwise it is first converted to a [String].
+  void fine(msg, {AssetId asset, SourceSpan span}) {
+    msg = msg is Message ? msg : new Message.unknown('$msg');
+    _transform.logger.fine(_snippet(msg), asset: asset, span: span);
+    _logs.add(new BuildLogEntry(msg, span, LogLevel.FINE.name));
+  }
+
+  /// Records a message at the info level. If [msg] is a [Message] it is
+  /// recorded directly, otherwise it is first converted to a [String].
+  void info(msg, {AssetId asset, SourceSpan span}) {
+    msg = msg is Message ? msg : new Message.unknown('$msg');
+    _transform.logger.info(_snippet(msg), asset: asset, span: span);
+    _logs.add(new BuildLogEntry(msg, span, LogLevel.INFO.name));
+  }
+
+  /// Records a warning message. If [msg] is a [Message] it is recorded
+  /// directly, otherwise it is first converted to a [String].
+  void warning(msg, {AssetId asset, SourceSpan span}) {
+    msg = msg is Message ? msg : new Message.unknown('$msg');
+    _transform.logger.warning(_snippet(msg), asset: asset, span: span);
+    _logs.add(new BuildLogEntry(msg, span, LogLevel.WARNING.name));
+  }
+
+  /// Records an error message. If [msg] is a [Message] it is recorded
+  /// directly, otherwise it is first converted to a [String].
+  void error(msg, {AssetId asset, SourceSpan span}) {
+    msg = msg is Message ? msg : new Message.unknown('$msg');
+    if (convertErrorsToWarnings) {
+      _transform.logger.warning(_snippet(msg), asset: asset, span: span);
+    } else {
+      _transform.logger.error(_snippet(msg), asset: asset, span: span);
+    }
+    _logs.add(new BuildLogEntry(msg, span, LogLevel.ERROR.name));
+  }
+
+  String _snippet(Message msg) {
+    var s = msg.snippet;
+    if (detailsUri == null) return s;
+    var dot = s.endsWith('.') || s.endsWith('!') || s.endsWith('?') ? '' : '.';
+    var hashTag = '${msg.id.package}_${msg.id.id}';
+    return '$s$dot See $detailsUri#$hashTag for details.';
+  }
+
+  /// Outputs the log data to a JSON serialized file.
+  Future writeOutput() {
+    return _getNextLogAssetId().then((id) {
+      _transform.addOutput(new Asset.fromString(id, JSON.encode(_logs)));
+    });
+  }
+
+  // Each phase outputs a new log file with an incrementing # appended, this
+  // figures out the next # to use.
+  Future<AssetId> _getNextLogAssetId([int nextNumber = 1]) {
+    var nextAssetPath = _primaryId.addExtension('${LOG_EXTENSION}.$nextNumber');
+    return _transform.hasInput(nextAssetPath).then((exists) {
+      if (!exists) return nextAssetPath;
+      return _getNextLogAssetId(++nextNumber);
+    });
+  }
+
+  // Reads all log files for an Asset into [logs].
+  static Future _readLogFilesForAsset(
+      AssetId id, Transform transform, LogEntryTable entries,
+      [nextNumber = 1]) {
+    var nextAssetPath = id.addExtension('${LOG_EXTENSION}.$nextNumber');
+    return transform.hasInput(nextAssetPath).then((exists) {
+      if (!exists) return null;
+      return transform.readInputAsString(nextAssetPath).then((data) {
+        entries.addAll(new LogEntryTable.fromJson(JSON.decode(data)));
+        return _readLogFilesForAsset(id, transform, entries, ++nextNumber);
+      });
+    });
+  }
+
+  /// Combines all existing ._buildLogs.* files into a single ._buildLogs file.
+  /// [transform] may be a [Transform] or [AggregateTransform]. If an
+  /// [AggregateTransform] is passed then [primaryId] must also be passed.
+  static Future combineLogFiles(transform, [AssetId primaryId]) {
+    if (primaryId == null) primaryId = transform.primaryInput.id;
+    var entries = new LogEntryTable();
+    return _readLogFilesForAsset(primaryId, transform, entries).then((_) {
+      return transform.addOutput(new Asset.fromString(
+          primaryId.addExtension(LOG_EXTENSION),
+          JSON.encode(entries.toJson())));
+    });
+  }
+
+  // Reads all logs for an asset and adds them to this loggers log output.
+  Future addLogFilesFromAsset(AssetId id, [int nextNumber = 1]) {
+    return _readLogFilesForAsset(id, _transform, _logs);
+  }
+}
+
+/// Extension used for assets that contained serialized logs.
+const String LOG_EXTENSION = '._buildLogs';
diff --git a/code_transformers/lib/messages/messages.dart b/code_transformers/lib/messages/messages.dart
new file mode 100644
index 0000000..56f3c0e
--- /dev/null
+++ b/code_transformers/lib/messages/messages.dart
@@ -0,0 +1,226 @@
+// 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.
+
+/// Defines messages templates and an adapter for TransformLogger to be able
+/// report error messages from transformers and refer to them in a consistent
+/// manner long term.
+library code_transformers.messages;
+
+// Note: this library purposely doesn't depend on dart:io, dart:html, or barback
+// so it can easily be used both in the transformers and in client-side apps
+// (for example in the log_injector).
+import 'dart:collection' show LinkedHashMap;
+import 'package:source_span/source_span.dart';
+
+/// A globally unique identifier for an error message. This identifier should be
+/// stable, that is, it should never change after it is asigned to a particular
+/// message. That allows us to document our error messages and make them
+/// searchable for prosperity.
+class MessageId implements Comparable {
+  /// Name of the package that declares this message.
+  final String package;
+
+  /// Message identifier number, unique within the package.
+  final int id;
+
+  const MessageId(this.package, this.id);
+
+  static const MessageId NOT_SPECIFIED = const MessageId('unknown', 0);
+
+  /// Serialize this message. We use a string and not a map to encode ids so
+  /// they can be used as keys in JSON maps.
+  String toJson() => toString();
+
+  toString() => '${package}#$id';
+
+  int compareTo(MessageId other) {
+    var res = package.compareTo(other.package);
+    if (res != 0) return res;
+    return id.compareTo(other.id);
+  }
+
+  /// Creates a new [MessageId] from an encoded value produced via [toJson].
+  factory MessageId.fromJson(data) {
+    var index = data.lastIndexOf('#');
+    if (index == -1) throw 'Invalid message id: $data';
+    return new MessageId(
+        data.substring(0, index), int.parse(data.substring(index + 1)));
+  }
+
+  operator ==(MessageId other) => package == other.package && id == other.id;
+  int get hashCode => 31 * package.hashCode + id;
+}
+
+/// An instance of an error message. These are typically produced from a
+/// [MessageTemplate].
+class Message {
+  /// A globally unique identifier for this message.
+  final MessageId id;
+
+  /// A snippet message that is presented to the user.
+  final String snippet;
+
+  const Message(this.id, this.snippet);
+
+  const Message.unknown(this.snippet) : id = MessageId.NOT_SPECIFIED;
+
+  /// Serializes this message to JSON.
+  Map toJson() => {'id': id.toJson(), 'snippet': snippet};
+  String toString() => 'id: $id, snippet: $snippet';
+
+  /// Creates a new [Message] from an encoded value produced via [toJson].
+  factory Message.fromJson(data) =>
+      new Message(new MessageId.fromJson(data['id']), data['snippet']);
+}
+
+/// Template for a message. Templates can include placeholders to indicate
+/// values that are different for each instance of the error. Calling [create]
+/// will generate the actual message, with the placeholders replaced with
+/// values. If there are no placeholders, an instance of [MessageTemplate] is a
+/// valid instance of [Message] as well.
+class MessageTemplate implements Message {
+  /// Unique and stable id for the message.
+  final MessageId id;
+
+  /// Template message with placeholders of the form `%-name-%`.
+  final String snippetTemplate;
+
+  /// This returns the message snippet, only if it the template has no
+  /// placeholders, otherwise this throws an exception. Most messages have no
+  /// placeholder arguments, in those cases, the snippet  can be computed
+  /// without specifying any arguments (exactly like calling `create()` with no
+  /// arguments).
+  String get snippet => _createSnippet();
+
+  /// Short description of the error message, typically used as a title of the
+  /// error message in autogenerated documentation. This should be a single
+  /// phrase, and cannot use placeholders.
+  final String description;
+
+  /// Additional details about this error message. These are used to
+  /// automatically generate documentation.
+  final String details;
+
+  const MessageTemplate(
+      this.id, this.snippetTemplate, this.description, this.details);
+
+  static final _placeholderPattern = new RegExp(r"%-(\w*)-%");
+
+  _createSnippet([Map args = const {}, bool fillUnknowns = false]) {
+    var snippet = snippetTemplate.replaceAllMapped(_placeholderPattern, (m) {
+      var arg = m.group(1);
+      var value = args[arg];
+      if (value != null) return '$value';
+      if (fillUnknowns) return '';
+      throw "missing argument $arg, for error message: $snippetTemplate";
+    });
+    return snippet;
+  }
+
+  create([Map args = const {}, bool fillUnknowns = false]) =>
+      new Message(id, _createSnippet(args, fillUnknowns));
+
+  /// Serializes this message to JSON.
+  Map toJson() => create().toJson();
+  String toString() => '${toJson()}';
+}
+
+/// Represents an actual log entry for a build error message. Including the
+/// actual message, its severity level (warning, error, etc), and a source span
+/// for a code location that is revelant to the message.
+class BuildLogEntry {
+  /// The actual message.
+  final Message message;
+
+  /// Severity level.
+  final String level;
+
+  /// Location associated with this message, if any.
+  final SourceSpan span;
+
+  BuildLogEntry(this.message, this.span, this.level);
+
+  /// Creates a new [BuildLogEntry] from an encoded value produced via [toJson].
+  factory BuildLogEntry.fromJson(Map data) {
+    var spanData = data['span'];
+    var span = null;
+    if (spanData != null) {
+      var locData = spanData['start'];
+      var start = new SourceLocation(locData['offset'],
+          sourceUrl: Uri.parse(locData['url']),
+          line: locData['line'],
+          column: locData['column']);
+      locData = spanData['end'];
+      var end = new SourceLocation(locData['offset'],
+          sourceUrl: Uri.parse(locData['url']),
+          line: locData['line'],
+          column: locData['column']);
+      span = new SourceSpan(start, end, spanData['text']);
+    }
+    return new BuildLogEntry(
+        new Message.fromJson(data['message']), span, data['level']);
+  }
+
+  /// Serializes this log entry to JSON.
+  Map toJson() {
+    var data = {'level': level, 'message': message.toJson(),};
+    if (span != null) {
+      data['span'] = {
+        'start': {
+          'url': span.start.sourceUrl.toString(),
+          'offset': span.start.offset,
+          'line': span.start.line,
+          'column': span.start.column,
+        },
+        'end': {
+          'url': span.end.sourceUrl.toString(),
+          'offset': span.end.offset,
+          'line': span.end.line,
+          'column': span.end.column,
+        },
+        'text': span.text,
+      };
+    }
+    return data;
+  }
+  String toString() => '${toJson()}';
+}
+
+/// A table of entries, that clusters error messages by id.
+class LogEntryTable {
+  final Map<MessageId, List<BuildLogEntry>> entries;
+
+  LogEntryTable() : entries = new LinkedHashMap();
+
+  /// Creates a new [LogEntryTable] from an encoded value produced via [toJson].
+  factory LogEntryTable.fromJson(Map json) {
+    var res = new LogEntryTable();
+    for (String key in json.keys) {
+      var id = new MessageId.fromJson(key);
+      res.entries[id] =
+          json[key].map((v) => new BuildLogEntry.fromJson(v)).toList();
+    }
+    return res;
+  }
+
+  /// Serializes this entire table as JSON.
+  Map toJson() {
+    var res = {};
+    entries.forEach((key, value) {
+      res['$key'] = value.map((e) => e.toJson()).toList();
+    });
+    return res;
+  }
+  String toString() => '${toJson()}';
+
+  void add(BuildLogEntry entry) {
+    entries.putIfAbsent(entry.message.id, () => []).add(entry);
+  }
+  void addAll(LogEntryTable other) {
+    for (var key in other.entries.keys) {
+      var values = entries.putIfAbsent(key, () => []);
+      values.addAll(other.entries[key]);
+    }
+  }
+}
diff --git a/code_transformers/lib/resolver.dart b/code_transformers/lib/resolver.dart
new file mode 100644
index 0000000..faf34db
--- /dev/null
+++ b/code_transformers/lib/resolver.dart
@@ -0,0 +1,11 @@
+// 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.
+
+/// Tools for working with resolved ASTs from Barback transformers.
+library code_transformers.resolver;
+
+export 'src/dart_sdk.dart';
+export 'src/entry_point.dart';
+export 'src/resolver.dart';
+export 'src/resolvers.dart';
diff --git a/code_transformers/lib/src/async_benchmark_base.dart b/code_transformers/lib/src/async_benchmark_base.dart
new file mode 100644
index 0000000..0d9966f
--- /dev/null
+++ b/code_transformers/lib/src/async_benchmark_base.dart
@@ -0,0 +1,71 @@
+// 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 code_transformers.src.async_benchmark_base;
+
+import 'dart:async';
+
+/// An adaptation of [BenchmarkBase] from the `benchmark_harness` package that
+/// works for async benchmarks.
+/// TODO(jakemac): Get this merged into `benchmark_harness`.
+class AsyncBenchmarkBase {
+  // Empty constructor.
+  const AsyncBenchmarkBase();
+
+  // The benchmark code.
+  // This function is not used, if both [warmup] and [exercise] are overwritten.
+  Future run() => new Future.value();
+
+  // Runs a short version of the benchmark. By default invokes [run] once.
+  Future warmup() => run();
+
+  // Exercices the benchmark. By default invokes [run] 10 times.
+  Future exercise({int iterations: 10}) {
+    var i = 0;
+    return Future.doWhile(() {
+      if (i >= iterations) return new Future.value(false);
+      i++;
+      return run().then((_) => true);
+    });
+  }
+
+  // Not measured setup code executed prior to the benchmark runs.
+  Future setup() => new Future.value();
+
+  // Not measures teardown code executed after the benchark runs.
+  Future teardown() => new Future.value();
+
+  // Measures the score for this benchmark by executing it repeatedly until
+  // time minimum has been reached.
+  static Future<double> measureFor(Function f, int minimumMillis) {
+    int minimumMicros = minimumMillis * 1000;
+    int iter = 0;
+    Stopwatch watch = new Stopwatch();
+    watch.start();
+    int elapsed = 0;
+    return Future.doWhile(() {
+      if (elapsed > minimumMicros) return new Future.value(false);
+      return f().then((_) {
+        elapsed = watch.elapsedMicroseconds;
+        iter++;
+        return true;
+      });
+    }).then((_) => elapsed / iter);
+  }
+
+  // Measures the average time to call `run` once and returns it.
+  Future<double> measure({int iterations: 10}) {
+    // Unmeasured setup code.
+    return setup().then((_) {
+      // Warmup for at least 100ms. Discard result.
+      return measureFor(() => warmup(), 100);
+    }).then((_) {
+      // Run the benchmark for at least 2000ms.
+      return measureFor(() => exercise(iterations: iterations), 2000);
+    }).then((result) {
+      // Tear down the test (unmeasured) and return the result divided by the
+      // number of iterations.
+      return teardown().then((_) => result / iterations);
+    });
+  }
+}
diff --git a/code_transformers/lib/src/dart_sdk.dart b/code_transformers/lib/src/dart_sdk.dart
new file mode 100644
index 0000000..5667030
--- /dev/null
+++ b/code_transformers/lib/src/dart_sdk.dart
@@ -0,0 +1,258 @@
+// 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 code_transformers.src.dart_sdk;
+
+import 'dart:io' show Directory;
+
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/java_io.dart';
+import 'package:analyzer/src/generated/sdk.dart';
+import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk;
+import 'package:analyzer/src/generated/source.dart';
+import 'package:cli_util/cli_util.dart' as cli_util;
+
+/// Attempts to provide the current Dart SDK directory.
+///
+/// This will return null if the SDK cannot be found
+///
+/// Note that this may not be correct when executing outside of `pub`.
+String get dartSdkDirectory {
+  Directory sdkDir = cli_util.getSdkDir();
+  return sdkDir != null ? sdkDir.path : null;
+}
+
+/// Sources that are annotated with a source uri, so it is easy to resolve how
+/// to support `Resolver.getImportUri`.
+abstract class UriAnnotatedSource extends Source {
+  Uri get uri;
+}
+
+/// Dart SDK which wraps all Dart sources as [UriAnnotatedSource] to ensure they
+/// are tracked with Uris.
+class DirectoryBasedDartSdkProxy extends DirectoryBasedDartSdk {
+  DirectoryBasedDartSdkProxy(String sdkDirectory)
+      : super(new JavaFile(sdkDirectory));
+
+  Source mapDartUri(String dartUri) =>
+      DartSourceProxy.wrap(super.mapDartUri(dartUri), Uri.parse(dartUri));
+}
+
+/// Dart SDK resolver which wraps all Dart sources to ensure they are tracked
+/// with URIs.
+class DartUriResolverProxy implements DartUriResolver {
+  final DartUriResolver _proxy;
+  DartUriResolverProxy(DartSdk sdk) : _proxy = new DartUriResolver(sdk);
+
+  Source resolveAbsolute(Uri uri) =>
+      DartSourceProxy.wrap(_proxy.resolveAbsolute(uri), uri);
+
+  DartSdk get dartSdk => _proxy.dartSdk;
+
+  Source fromEncoding(UriKind kind, Uri uri) =>
+      throw new UnsupportedError('fromEncoding is not supported');
+
+  Uri restoreAbsolute(Source source) =>
+      throw new UnsupportedError('restoreAbsolute is not supported');
+}
+
+/// Source file for dart: sources which track the sources with dart: URIs.
+///
+/// This is primarily to support [Resolver.getImportUri] for Dart SDK (dart:)
+/// based libraries.
+class DartSourceProxy implements UriAnnotatedSource {
+
+  /// Absolute URI which this source can be imported from
+  final Uri uri;
+
+  /// Underlying source object.
+  final Source _proxy;
+
+  Source get source => this;
+
+  DartSourceProxy(this._proxy, this.uri);
+
+  /// Ensures that [source] is a DartSourceProxy.
+  static DartSourceProxy wrap(Source source, Uri uri) {
+    if (source == null || source is DartSourceProxy) return source;
+    return new DartSourceProxy(source, uri);
+  }
+
+  // Note: to support both analyzer versions <0.22.0 and analyzer >=0.22.0, we
+  // implement both `resolveRelative` and `resolveRelativeUri`. Only one of them
+  // is available at a time in the analyzer package, so we use the `as dynamic`
+  // in these methods to hide warnings for the code that is missing. These APIs
+  // are invoked from the analyzer itself, so we don't expect them to cause
+  // failures.
+  Source resolveRelative(Uri relativeUri) {
+    // Assume that the type can be accessed via this URI, since these
+    // should only be parts for dart core files.
+    return wrap((_proxy as dynamic).resolveRelative(relativeUri), uri);
+  }
+
+  Uri resolveRelativeUri(Uri relativeUri) {
+    return (_proxy as dynamic).resolveRelativeUri(relativeUri);
+  }
+
+  bool exists() => _proxy.exists();
+
+  bool operator ==(Object other) =>
+      (other is DartSourceProxy && _proxy == other._proxy);
+
+  int get hashCode => _proxy.hashCode;
+
+  TimestampedData<String> get contents => _proxy.contents;
+
+  String get encoding => _proxy.encoding;
+
+  String get fullName => _proxy.fullName;
+
+  int get modificationStamp => _proxy.modificationStamp;
+
+  String get shortName => _proxy.shortName;
+
+  UriKind get uriKind => _proxy.uriKind;
+
+  bool get isInSystemLibrary => _proxy.isInSystemLibrary;
+}
+
+/// Dart SDK which contains a mock implementation of the SDK libraries. May be
+/// used to speed up resultion when most of the core libraries is not needed.
+class MockDartSdk implements DartSdk {
+  final Map<Uri, _MockSdkSource> _sources = {};
+  final bool reportMissing;
+  final Map<String, SdkLibrary> _libs = {};
+  final String sdkVersion = '0';
+  List<String> get uris => _sources.keys.map((uri) => '$uri').toList();
+  final AnalysisContext context = new SdkAnalysisContext();
+  DartUriResolver _resolver;
+  DartUriResolver get resolver => _resolver;
+
+  MockDartSdk(Map<String, String> sources, {this.reportMissing}) {
+    sources.forEach((uriString, contents) {
+      var uri = Uri.parse(uriString);
+      _sources[uri] = new _MockSdkSource(uri, contents);
+      _libs[uriString] = new SdkLibraryImpl(uri.path)
+        ..setDart2JsLibrary()
+        ..setVmLibrary();
+    });
+    _resolver = new DartUriResolver(this);
+    context.sourceFactory = new SourceFactory([_resolver]);
+  }
+
+  List<SdkLibrary> get sdkLibraries => _libs.values.toList();
+  SdkLibrary getSdkLibrary(String dartUri) => _libs[dartUri];
+  Source mapDartUri(String dartUri) => _getSource(Uri.parse(dartUri));
+
+  Source fromEncoding(UriKind kind, Uri uri) {
+    if (kind != UriKind.DART_URI) {
+      throw new UnsupportedError('expected dart: uri kind, got $kind.');
+    }
+    return _getSource(uri);
+  }
+
+  Source _getSource(Uri uri) {
+    var src = _sources[uri];
+    if (src == null) {
+      if (reportMissing) print('warning: missing mock for $uri.');
+      _sources[uri] =
+          src = new _MockSdkSource(uri, 'library dart.${uri.path};');
+    }
+    return src;
+  }
+
+  @override
+  Source fromFileUri(Uri uri) {
+    throw new UnsupportedError('MockDartSdk.fromFileUri');
+  }
+}
+
+class _MockSdkSource implements UriAnnotatedSource {
+  /// Absolute URI which this source can be imported from.
+  final Uri uri;
+  final String _contents;
+
+  Source get source => this;
+
+  _MockSdkSource(this.uri, this._contents);
+
+  bool exists() => true;
+
+  int get hashCode => uri.hashCode;
+
+  final int modificationStamp = 1;
+
+  TimestampedData<String> get contents =>
+      new TimestampedData(modificationStamp, _contents);
+
+  String get encoding => "${uriKind.encoding}$uri";
+
+  String get fullName => shortName;
+
+  String get shortName => uri.path;
+
+  UriKind get uriKind => UriKind.DART_URI;
+
+  bool get isInSystemLibrary => true;
+
+  Source resolveRelative(Uri relativeUri) =>
+      throw new UnsupportedError('not expecting relative urls in dart: mocks');
+
+  Uri resolveRelativeUri(Uri relativeUri) =>
+      throw new UnsupportedError('not expecting relative urls in dart: mocks');
+}
+
+/// Sample mock SDK sources.
+final Map<String, String> mockSdkSources = {
+  // The list of types below is derived from types that are used internally by
+  // the resolver (see _initializeFrom in analyzer/src/generated/resolver.dart).
+  'dart:core': '''
+        library dart.core;
+
+        void print(Object o) {}
+
+        class Object {
+          String toString(){}
+        }
+        class Function {}
+        class StackTrace {}
+        class Symbol {}
+        class Type {}
+
+        class String {}
+        class bool {}
+        class num {
+          num operator +(num other) {}
+        }
+        class int extends num {
+          int operator-() {}
+        }
+        class double extends num {}
+        class DateTime {}
+        class Null {}
+
+        class Deprecated {
+          final String expires;
+          const Deprecated(this.expires);
+        }
+        const Object deprecated = const Deprecated("next release");
+        class _Override { const _Override(); }
+        const Object override = const _Override();
+        class _Proxy { const _Proxy(); }
+        const Object proxy = const _Proxy();
+
+        class Iterable<E> {}
+        class List<E> implements Iterable<E> {}
+        class Map<K, V> {}
+        ''',
+  'dart:async': '''
+        class Future<T> {
+          Future then(callback) {}
+        class Stream<T> {}
+  ''',
+  'dart:html': '''
+        library dart.html;
+        class HtmlElement {}
+        ''',
+};
diff --git a/code_transformers/lib/src/delete_file.dart b/code_transformers/lib/src/delete_file.dart
new file mode 100644
index 0000000..8eba05e
--- /dev/null
+++ b/code_transformers/lib/src/delete_file.dart
@@ -0,0 +1,22 @@
+// 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.
+
+/// Transformer that deletes everything that it sees, but only in release mode.
+library code_transformers.src.delete_file;
+
+import 'package:barback/barback.dart';
+
+// Deletes all files supplied in release mode.
+class DeleteFile extends Transformer {
+  BarbackSettings settings;
+
+  DeleteFile.asPlugin(this.settings);
+
+  /// Only apply to files in release mode.
+  isPrimary(_) => settings.mode == BarbackMode.RELEASE;
+
+  apply(Transform transform) {
+    transform.consumePrimary();
+  }
+}
diff --git a/code_transformers/lib/src/entry_point.dart b/code_transformers/lib/src/entry_point.dart
new file mode 100644
index 0000000..21f7acf
--- /dev/null
+++ b/code_transformers/lib/src/entry_point.dart
@@ -0,0 +1,59 @@
+// 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.
+import 'dart:async';
+
+import 'package:analyzer/analyzer.dart' as analyzer;
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:barback/barback.dart';
+
+/// Checks to see if the provided AssetId is a Dart file in a directory which
+/// may contain entry points.
+///
+/// Directories are considered entry points if they are Dart files located in
+/// web/, test/, benchmark/ or example/.
+bool isPossibleDartEntryId(AssetId id) {
+  if (id.extension != '.dart') return false;
+
+  return [
+    'benchmark',
+    'example',
+    'test',
+    'web'
+  ].any((dir) => id.path.startsWith("$dir/"));
+}
+
+/// Checks to see if the provided Asset is possibly a Dart entry point.
+///
+/// Assets are considered entry points if they pass [isPossibleDartEntryId] and
+/// have a main() method.
+///
+/// Because this only analyzes the primary asset this may return true for files
+/// which are not dart entries if the file does not have a main() but does have
+/// parts or exports.
+Future<bool> isPossibleDartEntry(Asset asset) {
+  if (!isPossibleDartEntryId(asset.id)) return new Future.value(false);
+
+  return asset.readAsString().then((contents) {
+    return _couldBeEntrypoint(
+        analyzer.parseCompilationUnit(contents, suppressErrors: true));
+  });
+}
+
+bool _couldBeEntrypoint(CompilationUnit compilationUnit) {
+  // Allow two or fewer arguments so that entrypoints intended for use with
+  // [spawnUri] get counted.
+  var hasMain = compilationUnit.declarations.any(
+      (node) => node is FunctionDeclaration &&
+          node.name.name == "main" &&
+          node.functionExpression.parameters.parameters.length <= 2);
+
+  if (hasMain) return true;
+
+  // If it has an export or a part, assume the worst- that the main could be
+  // in there.
+  // We avoid loading those since this can be run from isPrimaryAsset calls
+  // where we do not have access to other sources.
+  return compilationUnit.directives
+      .any((node) => node is ExportDirective || node is PartDirective);
+}
diff --git a/code_transformers/lib/src/messages.dart b/code_transformers/lib/src/messages.dart
new file mode 100644
index 0000000..a78969a
--- /dev/null
+++ b/code_transformers/lib/src/messages.dart
@@ -0,0 +1,66 @@
+// 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.
+
+/// Contains all warning messages produced by the code_transformers package.
+library code_transformers.src.messages;
+
+import 'package:code_transformers/messages/messages.dart';
+
+const NO_ABSOLUTE_PATHS = const MessageTemplate(
+    const MessageId('code_transformers', 1),
+    'absolute paths not allowed: "%-url-%"', 'Absolute paths not allowed', '''
+The transformers processing your code were trying to resolve a URL and identify
+a file that they correspond to. Currently only relative paths can be resolved.
+''');
+
+const INVALID_URL_TO_OTHER_PACKAGE = const MessageTemplate(
+    const MessageId('code_transformers', 2),
+    'Invalid URL to reach to another package: %-url-%. Path '
+    'reaching to other packages must first reach up all the '
+    'way to the %-prefix-% directory. For example, try changing the URL '
+    'to: %-fixedUrl-%', 'Invalid URL to reach another package', '''
+To reach an asset that belongs to another package, use `package:` URLs in
+Dart code, but in any other language (like HTML or CSS) use relative URLs that
+first go all the way to the `packages/` directory.
+
+The rules for correctly writing these imports are subtle and have a lot of
+special cases. Please review
+<https://www.dartlang.org/polymer/app-directories.html> to learn
+more.
+''');
+
+const INVALID_PREFIX_PATH = const MessageTemplate(
+    const MessageId('code_transformers', 3),
+    'incomplete %-prefix-%/ path. It should have at least 3 '
+    'segments %-prefix-%/name/path_from_name\'s_%-folder-%_dir',
+    'Incomplete URL to asset in another package', '''
+URLs that refer to assets in other packages need to explicitly mention the
+`packages/` directory. In the future this requirement might be removed, but for
+now you must use a canonical URL form for it.
+
+For example, if `packages/a/a.html` needs to import `packages/b/b.html`,
+you might expect a.html to import `../b/b.html`. Instead, it must import
+`../../packages/b/b.html`.
+
+See [issue 15797](http://dartbug.com/15797) and
+<https://www.dartlang.org/polymer/app-directories.html> to learn more.
+''');
+
+const UNSPECIFIED_FROM_IN_NON_LIB_ASSET = const MessageTemplate(
+    const MessageId('code_transformers', 4),
+    'Cannot create URI for %-id-% without specifying where to import it from.',
+    'Missing `from` argument.', '''
+Assets outside of the lib folder can only be imported via relative URIs. Use
+the `from` argument in `assetIdToUri` to specify the location in the same
+package where you intend to import this asset from.
+''');
+
+const IMPORT_FROM_DIFFERENT_PACKAGE = const MessageTemplate(
+    const MessageId('code_transformers', 5),
+    'Not possible to import %-toId-% from %-fromId-%', 'Cannot import asset.',
+    '''
+Assets outside of the lib folder can only be imported via relative URIs from
+assets in the same package. To import an asset from another package, you need to
+move it into the lib folder of your package.
+''');
diff --git a/code_transformers/lib/src/remove_sourcemap_comment.dart b/code_transformers/lib/src/remove_sourcemap_comment.dart
new file mode 100644
index 0000000..70f4b9c
--- /dev/null
+++ b/code_transformers/lib/src/remove_sourcemap_comment.dart
@@ -0,0 +1,31 @@
+// 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.
+
+/// Transformer that removes any sourcemap comments from javascript files.
+library code_transformers.src.remove_sourcemap_comment;
+
+import 'package:barback/barback.dart';
+
+/// Transformer that removes any sourcemap comments from javascript files.
+/// Comments should be on their own line in the form: //# sourceMappingURL=*.map
+class RemoveSourcemapComment extends Transformer {
+  BarbackSettings settings;
+
+  RemoveSourcemapComment.asPlugin(this.settings);
+
+  /// Only apply to files in release mode.
+  isPrimary(_) => settings.mode == BarbackMode.RELEASE;
+
+  apply(Transform transform) {
+    var id = transform.primaryInput.id;
+    return transform.readInputAsString(id).then((file) {
+      if (file.contains(_SOURCE_MAP_COMMENT)) {
+        transform.addOutput(
+            new Asset.fromString(id, file.replaceAll(_SOURCE_MAP_COMMENT, '')));
+      }
+    });
+  }
+}
+
+final RegExp _SOURCE_MAP_COMMENT = new RegExp(r'\n\s*\/\/# sourceMappingURL.*');
diff --git a/code_transformers/lib/src/resolver.dart b/code_transformers/lib/src/resolver.dart
new file mode 100644
index 0000000..7d5c8d0
--- /dev/null
+++ b/code_transformers/lib/src/resolver.dart
@@ -0,0 +1,101 @@
+// 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 code_transformer.src.resolver;
+
+import 'dart:async';
+
+import 'package:analyzer/src/generated/ast.dart' show Expression;
+import 'package:analyzer/src/generated/constant.dart' show EvaluationResult;
+import 'package:analyzer/src/generated/element.dart';
+import 'package:barback/barback.dart';
+import 'package:source_maps/refactor.dart';
+import 'package:source_span/source_span.dart';
+
+/// Class for working with a barback based resolved AST.
+abstract class Resolver {
+  /// Update the status of all the sources referenced by the entry points and
+  /// update the resolved library. If [entryPoints] is omitted, the primary
+  /// asset of [transform] is used as the only entry point.
+  ///
+  /// [release] must be called when done handling this Resolver to allow it
+  /// to be used by later phases.
+  Future<Resolver> resolve(Transform transform, [List<AssetId> entryPoints]);
+
+  /// Release this resolver so it can be updated by following transforms.
+  void release();
+
+  /// Gets the resolved Dart library for an asset, or null if the AST has not
+  /// been resolved.
+  ///
+  /// If the AST has not been resolved then this normally means that the
+  /// transformer hosting this needs to be in an earlier phase.
+  LibraryElement getLibrary(AssetId assetId);
+
+  /// Gets all libraries accessible from the entry point, recursively.
+  ///
+  /// This includes all Dart SDK libraries as well.
+  Iterable<LibraryElement> get libraries;
+
+  /// Finds the first library identified by [libraryName], or null if no
+  /// library can be found.
+  LibraryElement getLibraryByName(String libraryName);
+
+  /// Finds the first library identified by [libraryName], or null if no
+  /// library can be found.
+  ///
+  /// [uri] must be an absolute URI of the form
+  /// `[dart:|package:]path/file.dart`.
+  LibraryElement getLibraryByUri(Uri uri);
+
+  /// Resolves a fully-qualified type name (library_name.ClassName).
+  ///
+  /// This will resolve the first instance of [typeName], because of potential
+  /// library name conflicts the name is not guaranteed to be unique.
+  ClassElement getType(String typeName);
+
+  /// Resolves a fully-qualified top-level library variable
+  /// (library_name.variableName).
+  ///
+  /// This will resolve the first instance of [variableName], because of
+  /// potential library name conflicts the name is not guaranteed to be unique.
+  Element getLibraryVariable(String variableName);
+
+  /// Resolves a fully-qualified top-level library function
+  /// (library_name.functionName).
+  ///
+  /// This will resolve the first instance of [functionName], because of
+  /// potential library name conflicts the name is not guaranteed to be unique.
+  Element getLibraryFunction(String functionName);
+
+  /// Gets the result of evaluating the constant [expression] in the context of
+  /// a [library].
+  EvaluationResult evaluateConstant(
+      LibraryElement library, Expression expression);
+
+  /// Gets an URI appropriate for importing the specified library.
+  ///
+  /// Returns null if the library cannot be imported via an absolute URI or
+  /// from [from] (if provided).
+  Uri getImportUri(LibraryElement lib, {AssetId from});
+
+  /// Get the asset ID of the file containing the asset.
+  AssetId getSourceAssetId(Element element);
+
+  /// Get the source span where the specified element was defined or null if
+  /// the element came from the Dart SDK.
+  SourceSpan getSourceSpan(Element element);
+
+  /// Get a [SourceFile] with the contents of the file that defines [element],
+  /// or null if the element came from the Dart SDK.
+  SourceFile getSourceFile(Element element);
+
+  /// Creates a text edit transaction for the given element if it is able
+  /// to be edited, returns null otherwise.
+  ///
+  /// The transaction contains the entire text of the source file where the
+  /// element originated. If the element was from a library part then the
+  /// source file is the part file rather than the library.
+  TextEditTransaction createTextEditTransaction(Element element);
+}
diff --git a/code_transformers/lib/src/resolver_impl.dart b/code_transformers/lib/src/resolver_impl.dart
new file mode 100644
index 0000000..64e8196
--- /dev/null
+++ b/code_transformers/lib/src/resolver_impl.dart
@@ -0,0 +1,553 @@
+// 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 code_transformer.src.resolver_impl;
+
+import 'dart:async';
+import 'package:analyzer/analyzer.dart' show parseDirectives;
+import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator;
+import 'package:analyzer/src/generated/constant.dart'
+    show ConstantEvaluator, EvaluationResult;
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
+import 'package:analyzer/src/generated/source.dart';
+import 'package:barback/barback.dart';
+import 'package:code_transformers/assets.dart';
+import 'package:path/path.dart' as native_path;
+import 'package:source_maps/refactor.dart';
+import 'package:source_span/source_span.dart';
+
+import 'resolver.dart';
+import 'dart_sdk.dart' show UriAnnotatedSource;
+
+// We should always be using url paths here since it's always Dart/pub code.
+final path = native_path.url;
+
+/// Resolves and updates an AST based on Barback-based assets.
+///
+/// This also provides a handful of useful APIs for traversing and working
+/// with the resolved AST.
+class ResolverImpl implements Resolver {
+  /// Cache of all asset sources currently referenced.
+  final Map<AssetId, _AssetBasedSource> sources = <AssetId, _AssetBasedSource>{
+  };
+
+  final InternalAnalysisContext _context =
+      AnalysisEngine.instance.createAnalysisContext();
+
+  /// Transform for which this is currently updating, or null when not updating.
+  Transform _currentTransform;
+
+  /// The currently resolved entry libraries, or null if nothing is resolved.
+  List<LibraryElement> _entryLibraries;
+  Set<LibraryElement> _libraries;
+
+  /// Future indicating when this resolver is done in the current phase.
+  Future _lastPhaseComplete = new Future.value();
+
+  /// Completer for wrapping up the current phase.
+  Completer _currentPhaseComplete;
+
+  /// Creates a resolver with a given [sdk] implementation for resolving
+  /// `dart:*` imports.
+  ResolverImpl(DartSdk sdk, DartUriResolver dartUriResolver,
+      {AnalysisOptions options}) {
+    if (options == null) {
+      options = new AnalysisOptionsImpl()
+        ..cacheSize = 256 // # of sources to cache ASTs for.
+        ..preserveComments = false
+        ..analyzeFunctionBodies = true;
+    }
+    _context.analysisOptions = options;
+    sdk.context.analysisOptions = options;
+    _context.sourceFactory =
+        new SourceFactory([dartUriResolver, new _AssetUriResolver(this)]);
+  }
+
+  LibraryElement getLibrary(AssetId assetId) {
+    var source = sources[assetId];
+    return source == null ? null : _context.computeLibraryElement(source);
+  }
+
+  Future<Resolver> resolve(Transform transform, [List<AssetId> entryPoints]) {
+    // Can only have one resolve in progress at a time, so chain the current
+    // resolution to be after the last one.
+    var phaseComplete = new Completer();
+    var future = _lastPhaseComplete.whenComplete(() {
+      _currentPhaseComplete = phaseComplete;
+      return _performResolve(transform,
+          entryPoints == null ? [transform.primaryInput.id] : entryPoints);
+    }).then((_) => this);
+    // Advance the lastPhaseComplete to be done when this phase is all done.
+    _lastPhaseComplete = phaseComplete.future;
+    return future;
+  }
+
+  void release() {
+    if (_currentPhaseComplete == null) {
+      throw new StateError('Releasing without current lock.');
+    }
+    _currentPhaseComplete.complete(null);
+    _currentPhaseComplete = null;
+
+    // Clear out libraries since they should not be referenced after release.
+    _entryLibraries = null;
+    _libraries = null;
+    _currentTransform = null;
+  }
+
+  Future _performResolve(Transform transform, List<AssetId> entryPoints) {
+    if (_currentTransform != null) {
+      throw new StateError('Cannot be accessed by concurrent transforms');
+    }
+    _currentTransform = transform;
+
+    // Basic approach is to start at the first file, update it's contents
+    // and see if it changed, then walk all files accessed by it.
+    var visited = new Set<AssetId>();
+    var visiting = new FutureGroup();
+    var toUpdate = [];
+
+    void processAsset(AssetId assetId) {
+      visited.add(assetId);
+
+      visiting.add(transform.readInputAsString(assetId).then((contents) {
+        var source = sources[assetId];
+        if (source == null) {
+          source = new _AssetBasedSource(assetId, this);
+          sources[assetId] = source;
+        }
+        source.updateDependencies(contents);
+        toUpdate.add(new _PendingUpdate(source, contents));
+        source.dependentAssets
+            .where((id) => !visited.contains(id))
+            .forEach(processAsset);
+      }, onError: (e) {
+        var source = sources[assetId];
+        if (source != null && source.exists()) {
+          _context.applyChanges(new ChangeSet()..removedSource(source));
+          sources[assetId].updateContents(null);
+        }
+      }));
+    }
+    entryPoints.forEach(processAsset);
+
+    // Once we have all asset sources updated with the new contents then
+    // resolve everything.
+    return visiting.future.then((_) {
+      var changeSet = new ChangeSet();
+      toUpdate.forEach((pending) => pending.apply(changeSet));
+      var unreachableAssets =
+          sources.keys.toSet().difference(visited).map((id) => sources[id]);
+      for (var unreachable in unreachableAssets) {
+        changeSet.removedSource(unreachable);
+        unreachable.updateContents(null);
+        sources.remove(unreachable.assetId);
+      }
+
+      // Update the analyzer context with the latest sources
+      _context.applyChanges(changeSet);
+      // Force resolve each entry point (the getter will ensure the library is
+      // computed first).
+      _entryLibraries = entryPoints.map((id) {
+        var source = sources[id];
+        if (source == null) return null;
+        return _context.computeLibraryElement(source);
+      }).toList();
+    });
+  }
+
+  Iterable<LibraryElement> get libraries {
+    if (_libraries == null) {
+      // Note: we don't use `lib.visibleLibraries` because that excludes the
+      // exports seen in the entry libraries.
+      _libraries = new Set<LibraryElement>();
+      _entryLibraries.forEach(_collectLibraries);
+    }
+    return _libraries;
+  }
+
+  void _collectLibraries(LibraryElement lib) {
+    if (lib == null || _libraries.contains(lib)) return;
+    _libraries.add(lib);
+    lib.importedLibraries.forEach(_collectLibraries);
+    lib.exportedLibraries.forEach(_collectLibraries);
+  }
+
+  LibraryElement getLibraryByName(String libraryName) =>
+      libraries.firstWhere((l) => l.name == libraryName, orElse: () => null);
+
+  LibraryElement getLibraryByUri(Uri uri) =>
+      libraries.firstWhere((l) => getImportUri(l) == uri, orElse: () => null);
+
+  ClassElement getType(String typeName) {
+    var dotIndex = typeName.lastIndexOf('.');
+    var libraryName = dotIndex == -1 ? '' : typeName.substring(0, dotIndex);
+
+    var className =
+        dotIndex == -1 ? typeName : typeName.substring(dotIndex + 1);
+
+    for (var lib in libraries.where((l) => l.name == libraryName)) {
+      var type = lib.getType(className);
+      if (type != null) return type;
+    }
+    return null;
+  }
+
+  Element getLibraryVariable(String variableName) {
+    var dotIndex = variableName.lastIndexOf('.');
+    var libraryName = dotIndex == -1 ? '' : variableName.substring(0, dotIndex);
+
+    var name =
+        dotIndex == -1 ? variableName : variableName.substring(dotIndex + 1);
+
+    return libraries
+        .where((lib) => lib.name == libraryName)
+        .expand((lib) => lib.units)
+        .expand((unit) => unit.topLevelVariables)
+        .firstWhere((variable) => variable.name == name, orElse: () => null);
+  }
+
+  Element getLibraryFunction(String fnName) {
+    var dotIndex = fnName.lastIndexOf('.');
+    var libraryName = dotIndex == -1 ? '' : fnName.substring(0, dotIndex);
+
+    var name = dotIndex == -1 ? fnName : fnName.substring(dotIndex + 1);
+
+    return libraries
+        .where((lib) => lib.name == libraryName)
+        .expand((lib) => lib.units)
+        .expand((unit) => unit.functions)
+        .firstWhere((fn) => fn.name == name, orElse: () => null);
+  }
+
+  EvaluationResult evaluateConstant(
+      LibraryElement library, Expression expression) {
+    return new ConstantEvaluator(library.source, _context.typeProvider)
+        .evaluate(expression);
+  }
+
+  Uri getImportUri(LibraryElement lib, {AssetId from}) =>
+      _getSourceUri(lib, from: from);
+
+  /// Similar to getImportUri but will get the part URI for parts rather than
+  /// the library URI.
+  Uri _getSourceUri(Element element, {AssetId from}) {
+    var source = element.source;
+    if (source is _AssetBasedSource) {
+      var uriString = assetIdToUri(source.assetId, from: from);
+      return uriString != null ? Uri.parse(uriString) : null;
+    } else if (source is UriAnnotatedSource) {
+      return source.uri;
+    }
+    // Should not be able to encounter any other source types.
+    throw new StateError('Unable to resolve URI for ${source.runtimeType}');
+  }
+
+  AssetId getSourceAssetId(Element element) {
+    var source = element.source;
+    if (source is _AssetBasedSource) return source.assetId;
+    return null;
+  }
+
+  SourceSpan getSourceSpan(Element element) {
+    var sourceFile = getSourceFile(element);
+    if (sourceFile == null) return null;
+    return sourceFile.span(element.node.offset, element.node.end);
+  }
+
+  TextEditTransaction createTextEditTransaction(Element element) {
+    if (element.source is! _AssetBasedSource) return null;
+
+    // Cannot edit unless there is an active transformer.
+    if (_currentTransform == null) return null;
+
+    _AssetBasedSource source = element.source;
+    // Cannot modify assets in other packages.
+    if (source.assetId.package != _currentTransform.primaryInput.id.package) {
+      return null;
+    }
+
+    var sourceFile = getSourceFile(element);
+    if (sourceFile == null) return null;
+
+    return new TextEditTransaction(source.rawContents, sourceFile);
+  }
+
+  /// Gets the SourceFile for the source of the element.
+  SourceFile getSourceFile(Element element) {
+    var assetId = getSourceAssetId(element);
+    if (assetId == null) return null;
+
+    var importUri = _getSourceUri(element);
+    var spanPath = importUri != null ? importUri.toString() : assetId.path;
+    return new SourceFile(sources[assetId].rawContents, url: spanPath);
+  }
+}
+
+/// Implementation of Analyzer's Source for Barback based assets.
+class _AssetBasedSource extends Source {
+
+  /// Asset ID where this source can be found.
+  final AssetId assetId;
+
+  /// The resolver this is being used in.
+  final ResolverImpl _resolver;
+
+  /// Cache of dependent asset IDs, to avoid re-parsing the AST.
+  Iterable<AssetId> _dependentAssets;
+
+  /// The current revision of the file, incremented only when file changes.
+  int _revision = 0;
+
+  /// The file contents.
+  String _contents;
+
+  _AssetBasedSource(this.assetId, this._resolver);
+
+  /// Update the dependencies of this source. This parses [contents] but avoids
+  /// any analyzer resolution.
+  void updateDependencies(String contents) {
+    if (contents == _contents) return;
+    var unit = parseDirectives(contents, suppressErrors: true);
+    _dependentAssets = unit.directives
+        .where((d) => (d is ImportDirective ||
+            d is PartDirective ||
+            d is ExportDirective))
+        .map((d) => _resolve(
+            assetId, d.uri.stringValue, _logger, _getSpan(d, contents)))
+        .where((id) => id != null)
+        .toSet();
+  }
+
+  /// Update the contents of this file with [contents].
+  ///
+  /// Returns true if the contents of this asset have changed.
+  bool updateContents(String contents) {
+    if (contents == _contents) return false;
+    _contents = contents;
+    ++_revision;
+    return true;
+  }
+
+  /// Contents of the file.
+  TimestampedData<String> get contents {
+    if (!exists()) throw new StateError('$assetId does not exist');
+
+    return new TimestampedData<String>(modificationStamp, _contents);
+  }
+
+  /// Contents of the file.
+  String get rawContents => _contents;
+
+  Uri get uri => Uri.parse('asset:${assetId.package}/${assetId.path}');
+
+  /// Logger for the current transform.
+  ///
+  /// Only valid while the resolver is updating assets.
+  TransformLogger get _logger => _resolver._currentTransform.logger;
+
+  /// Gets all imports/parts/exports which resolve to assets (non-Dart files).
+  Iterable<AssetId> get dependentAssets => _dependentAssets;
+
+  bool exists() => _contents != null;
+
+  bool operator ==(Object other) =>
+      other is _AssetBasedSource && assetId == other.assetId;
+
+  int get hashCode => assetId.hashCode;
+
+  void getContentsToReceiver(Source_ContentReceiver receiver) {
+    receiver.accept(rawContents, modificationStamp);
+  }
+
+  String get encoding =>
+      "${uriKind.encoding}${assetId.package}/${assetId.path}";
+
+  String get fullName => assetId.toString();
+
+  int get modificationStamp => _revision;
+
+  String get shortName => path.basename(assetId.path);
+
+  UriKind get uriKind {
+    if (assetId.path.startsWith('lib/')) return UriKind.PACKAGE_URI;
+    return UriKind.FILE_URI;
+  }
+
+  bool get isInSystemLibrary => false;
+
+  Source resolveRelative(Uri relativeUri) {
+    var id = _resolve(assetId, relativeUri.toString(), _logger, null);
+    if (id == null) return null;
+
+    // The entire AST should have been parsed and loaded at this point.
+    var source = _resolver.sources[id];
+    if (source == null) {
+      _logger.error('Could not load asset $id');
+    }
+    return source;
+  }
+
+  Uri resolveRelativeUri(Uri relativeUri) {
+    var id = _resolve(assetId, relativeUri.toString(), _logger, null);
+    if (id == null) return uri.resolveUri(relativeUri);
+
+    // The entire AST should have been parsed and loaded at this point.
+    var source = _resolver.sources[id];
+    if (source == null) {
+      _logger.error('Could not load asset $id');
+    }
+    return source.uri;
+  }
+
+  /// For logging errors.
+  SourceSpan _getSpan(AstNode node, [String contents]) =>
+      _getSourceFile(contents).span(node.offset, node.end);
+  /// For logging errors.
+  SourceFile _getSourceFile([String contents]) {
+    var uri = assetIdToUri(assetId);
+    var path = uri != null ? uri : assetId.path;
+    return new SourceFile(contents != null ? contents : rawContents, url: path);
+  }
+}
+
+/// Implementation of Analyzer's UriResolver for Barback based assets.
+class _AssetUriResolver implements UriResolver {
+  final ResolverImpl _resolver;
+  _AssetUriResolver(this._resolver);
+
+  Source resolveAbsolute(Uri uri) {
+    assert(uri.scheme != 'dart');
+    var assetId;
+    if (uri.scheme == 'asset') {
+      var parts = path.split(uri.path);
+      assetId = new AssetId(parts[0], path.joinAll(parts.skip(1)));
+    } else {
+      assetId = _resolve(null, uri.toString(), logger, null);
+      if (assetId == null) {
+        logger.error('Unable to resolve asset ID for "$uri"');
+        return null;
+      }
+    }
+    var source = _resolver.sources[assetId];
+    // Analyzer expects that sources which are referenced but do not exist yet
+    // still exist, so just make an empty source.
+    if (source == null) {
+      source = new _AssetBasedSource(assetId, _resolver);
+      _resolver.sources[assetId] = source;
+    }
+    return source;
+  }
+
+  Source fromEncoding(UriKind kind, Uri uri) =>
+      throw new UnsupportedError('fromEncoding is not supported');
+
+  Uri restoreAbsolute(Source source) =>
+      throw new UnsupportedError('restoreAbsolute is not supported');
+
+  TransformLogger get logger => _resolver._currentTransform.logger;
+}
+
+/// Get an asset ID for a URL relative to another source asset.
+AssetId _resolve(
+    AssetId source, String url, TransformLogger logger, SourceSpan span) {
+  if (url == null || url == '') return null;
+  var uri = Uri.parse(url);
+
+  // Workaround for dartbug.com/17156- pub transforms package: imports from
+  // files of the transformers package to have absolute /packages/ URIs.
+  if (uri.scheme == '' &&
+      path.isAbsolute(url) &&
+      uri.pathSegments[0] == 'packages') {
+    uri = Uri.parse('package:${uri.pathSegments.skip(1).join(path.separator)}');
+  }
+
+  if (uri.scheme == 'package') {
+    var segments = new List.from(uri.pathSegments);
+    var package = segments[0];
+    segments[0] = 'lib';
+    return new AssetId(package, segments.join(path.separator));
+  }
+  // Dart SDK libraries do not have assets.
+  if (uri.scheme == 'dart') return null;
+
+  return uriToAssetId(source, url, logger, span);
+}
+
+/// A completer that waits until all added [Future]s complete.
+// TODO(blois): Copied from quiver. Remove from here when it gets
+// added to dart:core. (See #6626.)
+class FutureGroup<E> {
+  static const _FINISHED = -1;
+
+  int _pending = 0;
+  Future _failedTask;
+  final Completer<List> _completer = new Completer<List>();
+  final List results = [];
+
+  /** Gets the task that failed, if any. */
+  Future get failedTask => _failedTask;
+
+  /**
+   * Wait for [task] to complete.
+   *
+   * If this group has already been marked as completed, a [StateError] will be
+   * thrown.
+   *
+   * If this group has a [failedTask], new tasks will be ignored, because the
+   * error has already been signaled.
+   */
+  void add(Future task) {
+    if (_failedTask != null) return;
+    if (_pending == _FINISHED) throw new StateError("Future already completed");
+
+    _pending++;
+    var i = results.length;
+    results.add(null);
+    task.then((res) {
+      results[i] = res;
+      if (_failedTask != null) return;
+      _pending--;
+      if (_pending == 0) {
+        _pending = _FINISHED;
+        _completer.complete(results);
+      }
+    }, onError: (e, s) {
+      if (_failedTask != null) return;
+      _failedTask = task;
+      _completer.completeError(e, s);
+    });
+  }
+
+  /**
+   * A Future that completes with a List of the values from all the added
+   * tasks, when they have all completed.
+   *
+   * If any task fails, this Future will receive the error. Only the first
+   * error will be sent to the Future.
+   */
+  Future<List<E>> get future => _completer.future;
+}
+
+/// A pending update to notify the resolver that a [Source] has been added or
+/// changed. This is used by the `_performResolve` algorithm above to apply all
+/// changes after it first discovers the transitive closure of files that are
+/// reachable from the sources.
+class _PendingUpdate {
+  _AssetBasedSource source;
+  String content;
+
+  _PendingUpdate(this.source, this.content);
+
+  void apply(ChangeSet changeSet) {
+    if (!source.updateContents(content)) return;
+    if (source._revision == 1 && source._contents != null) {
+      changeSet.addedSource(source);
+    } else {
+      changeSet.changedSource(source);
+    }
+  }
+}
diff --git a/code_transformers/lib/src/resolvers.dart b/code_transformers/lib/src/resolvers.dart
new file mode 100644
index 0000000..fa2f953
--- /dev/null
+++ b/code_transformers/lib/src/resolvers.dart
@@ -0,0 +1,122 @@
+// 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 code_transformers.src.resolvers;
+
+import 'dart:async';
+import 'package:barback/barback.dart';
+
+import 'package:analyzer/src/generated/engine.dart' show AnalysisOptions;
+import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
+import 'package:analyzer/src/generated/source.dart' show DartUriResolver;
+
+import 'entry_point.dart';
+import 'resolver.dart';
+import 'resolver_impl.dart';
+import 'dart_sdk.dart' hide dartSdkDirectory;
+
+/// Barback-based code resolvers which maintains up-to-date resolved ASTs for
+/// the specified code entry points.
+///
+/// This can used by transformers dependent on resolved ASTs to handle the
+/// resolution of the AST and cache the results between compilations.
+///
+/// If multiple transformers rely on a resolved AST they should (ideally) share
+/// the same Resolvers object to minimize re-parsing the AST.
+class Resolvers {
+  final Map<AssetId, Resolver> _resolvers = {};
+  final DartSdk dartSdk;
+  final DartUriResolver dartUriResolver;
+  final AnalysisOptions options;
+
+  Resolvers.fromSdk(this.dartSdk, this.dartUriResolver, {this.options});
+
+  factory Resolvers(dartSdkDirectory, {AnalysisOptions options}) {
+    var sdk = new DirectoryBasedDartSdkProxy(dartSdkDirectory);
+    var uriResolver = new DartUriResolverProxy(sdk);
+    return new Resolvers.fromSdk(sdk, uriResolver, options: options);
+  }
+
+  factory Resolvers.fromMock(Map<String, String> sources,
+      {bool reportMissing: false, AnalysisOptions options}) {
+    var sdk = new MockDartSdk(sources, reportMissing: reportMissing);
+    return new Resolvers.fromSdk(sdk, sdk.resolver, options: options);
+  }
+
+  /// Get a resolver for [transform]. If provided, this resolves the code
+  /// starting from each of the assets in [entryPoints]. If not, this resolves
+  /// the code starting from `transform.primaryInput.id` by default.
+  ///
+  /// [Resolver.release] must be called once it's done being used, or
+  /// [ResolverTransformer] should be used to automatically release the
+  /// resolver.
+  Future<Resolver> get(Transform transform, [List<AssetId> entryPoints]) {
+    var id = transform.primaryInput.id;
+    var resolver = _resolvers.putIfAbsent(
+        id, () => new ResolverImpl(dartSdk, dartUriResolver, options: options));
+    return resolver.resolve(transform, entryPoints);
+  }
+}
+
+/// Transformer mixin which automatically gets and releases resolvers.
+///
+/// To use mix this class in, set the resolvers field and override
+/// [applyResolver].
+abstract class ResolverTransformer implements Transformer {
+  /// The cache of resolvers- must be set from subclass.
+  Resolvers resolvers;
+
+  /// By default only process prossible entry point assets.
+  ///
+  /// This is only a preliminary check based on the asset ID.
+  Future<bool> isPrimary(assetOrId) {
+    // assetOrId is to handle the transition from Asset to AssetID between
+    // pub 1.3 and 1.4. Once support for 1.3 is dropped this should only
+    // support AssetId.
+    var id = assetOrId is AssetId ? assetOrId : assetOrId.id;
+    return new Future.value(isPossibleDartEntryId(id));
+  }
+
+  /// Check to see if this should apply with the resolver on the provided asset.
+  ///
+  /// By default this will only apply on possible Dart entry points (see
+  /// [isPossibleDartEntry]).
+  Future<bool> shouldApplyResolver(Asset asset) => isPossibleDartEntry(asset);
+
+  /// This provides a default implementation of `Transformer.apply` that will
+  /// get and release resolvers automatically. Internally this:
+  ///   * Gets a resolver associated with the transform primary input.
+  ///   * Does resolution to the code starting from that input.
+  ///   * Calls [applyResolver].
+  ///   * Then releases the resolver.
+  ///
+  /// Use [applyToEntryPoints] instead if you need to override the entry points
+  /// to run the resolver on.
+  Future apply(Transform transform) =>
+      shouldApplyResolver(transform.primaryInput).then((result) {
+    if (result) return applyToEntryPoints(transform);
+  });
+
+  /// Helper function to make it easy to write an `Transformer.apply` method
+  /// that automatically gets and releases the resolver. This is typically used
+  /// as follows:
+  ///
+  ///    Future apply(Transform transform) {
+  ///       var entryPoints = ...; // compute entry points
+  ///       return applyToEntryPoints(transform, entryPoints);
+  ///    }
+  Future applyToEntryPoints(Transform transform, [List<AssetId> entryPoints]) {
+    return resolvers.get(transform, entryPoints).then((resolver) {
+      return new Future(() => applyResolver(transform, resolver)).whenComplete(
+          () {
+        resolver.release();
+      });
+    });
+  }
+
+  /// Invoked when the resolver is ready to be processed.
+  ///
+  /// Return a Future to indicate when apply is completed.
+  applyResolver(Transform transform, Resolver resolver);
+}
diff --git a/code_transformers/lib/src/test_harness.dart b/code_transformers/lib/src/test_harness.dart
new file mode 100644
index 0000000..817fbb3
--- /dev/null
+++ b/code_transformers/lib/src/test_harness.dart
@@ -0,0 +1,166 @@
+// 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.
+
+/// Utilities for creating unit tests of Barback transformers.
+library code_transformers.src.test_harness;
+
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+import 'package:stack_trace/stack_trace.dart';
+import 'package:unittest/unittest.dart';
+
+String idToString(AssetId id) => '${id.package}|${id.path}';
+AssetId idFromString(String s) {
+  int index = s.indexOf('|');
+  return new AssetId(s.substring(0, index), s.substring(index + 1));
+}
+
+/// A helper package provider that has files stored in memory, also wraps
+/// [Barback] to simply our tests.
+class TestHelper implements PackageProvider {
+
+  /// Maps from an asset string identifier of the form 'package|path' to the
+  /// file contents.
+  final Map<String, String> files;
+  final Iterable<String> packages;
+  final List<String> messages;
+  int messagesSeen = 0;
+  bool errorSeen = false;
+
+  Barback barback;
+  var errorSubscription;
+  var resultSubscription;
+  var logSubscription;
+
+  final StringFormatter formatter;
+
+  Future<Asset> getAsset(AssetId id) =>
+      new Future.value(new Asset.fromString(id, files[idToString(id)]));
+
+  TestHelper(List<List<Transformer>> transformers, Map<String, String> files,
+      this.messages, {this.formatter: StringFormatter.noTrailingWhitespace})
+      : files = files,
+        packages = files.keys.map((s) => idFromString(s).package) {
+    barback = new Barback(this);
+    for (var p in packages) {
+      barback.updateTransformers(p, transformers);
+    }
+
+    errorSubscription = barback.errors.listen((e) {
+      var trace = null;
+      if (e is Error) trace = e.stackTrace;
+      if (trace != null) {
+        print(Trace.format(trace));
+      }
+      fail('error running barback: $e');
+    });
+
+    resultSubscription = barback.results.listen((result) {
+      expect(result.succeeded, !errorSeen, reason: "${result.errors}");
+    });
+
+    logSubscription = barback.log.listen((entry) {
+      // Ignore info and fine messages.
+      if (entry.level == LogLevel.INFO || entry.level == LogLevel.FINE) return;
+      if (entry.level == LogLevel.ERROR) errorSeen = true;
+      // We only check messages when an expectation is provided.
+      if (messages == null) return;
+
+      var msg = '${entry.level.name.toLowerCase()}: ${entry.message}';
+      var span = entry.span;
+      var spanInfo = span == null
+          ? ''
+          : ' (${span.sourceUrl} ${span.start.line} ${span.start.column})';
+      expect(messagesSeen, lessThan(messages.length),
+          reason: 'more messages than expected.\nMessage seen: $msg$spanInfo');
+      expect('$msg$spanInfo', messages[messagesSeen++]);
+    });
+  }
+
+  void tearDown() {
+    errorSubscription.cancel();
+    resultSubscription.cancel();
+    logSubscription.cancel();
+  }
+
+  /// Tells barback which files have changed, and thus anything that depends on
+  /// it on should be computed. By default mark all the input files.
+  void run([Iterable<String> paths]) {
+    if (paths == null) paths = files.keys;
+    barback.updateSources(paths.map(idFromString));
+  }
+
+  Future<String> operator [](String assetString) {
+    return barback
+        .getAssetById(idFromString(assetString))
+        .then((asset) => asset.readAsString());
+  }
+
+  Future check(String assetIdString, String content) {
+    return this[assetIdString].then((value) {
+      value = formatter.formatString(value);
+      content = formatter.formatString(content);
+      expect(value, content, reason: 'Final output of $assetIdString differs.');
+    });
+  }
+
+  Future checkAll(Map<String, String> files) {
+    return barback.results.first.then((_) {
+      if (files == null) return null;
+      var futures = [];
+      files.forEach((k, v) {
+        futures.add(check(k, v));
+      });
+      return Future.wait(futures);
+    }).then((_) {
+      // We only check messages when an expectation is provided.
+      if (messages == null) return;
+      expect(
+          messagesSeen, messages.length, reason: 'less messages than expected');
+    });
+  }
+}
+
+class StringFormatter {
+  // Formatting options
+  final bool stripLeadingWhitespace;
+  final bool stripTrailingWhitespace;
+  final bool stripNewlines;
+
+  // Static variations for convenience
+  static const noLeadingWhitespace =
+      const StringFormatter(stripLeadingWhitespace: true);
+
+  static const noTrailingWhitespace =
+      const StringFormatter(stripTrailingWhitespace: true);
+
+  static const noSurroundingWhitespace = const StringFormatter(
+      stripLeadingWhitespace: true, stripTrailingWhitespace: true);
+
+  static const noNewlines = const StringFormatter(stripNewlines: true);
+
+  static const noNewlinesOrSurroundingWhitespace = const StringFormatter(
+      stripLeadingWhitespace: true,
+      stripTrailingWhitespace: true,
+      stripNewlines: true);
+
+  const StringFormatter({this.stripLeadingWhitespace: false,
+      this.stripTrailingWhitespace: false, this.stripNewlines: false});
+
+  String formatString(String str) {
+    if (stripLeadingWhitespace) str = _removeLeadingWhitespace(str);
+    if (stripTrailingWhitespace) str = _removeTrailingWhitespace(str);
+    if (stripNewlines) str = _removeNewlines(str);
+    return str;
+  }
+}
+
+String _removeTrailingWhitespace(String str) => str.splitMapJoin('\n',
+    onNonMatch: (s) => s.replaceAll(new RegExp(r'\s+$'), ''));
+
+String _removeLeadingWhitespace(String str) => str.splitMapJoin('\n',
+    onNonMatch: (s) => s.replaceAll(new RegExp(r'^\s+'), ''));
+
+String _removeNewlines(String str) => str.replaceAll('\n', '');
diff --git a/code_transformers/lib/tests.dart b/code_transformers/lib/tests.dart
new file mode 100644
index 0000000..9cb0a40
--- /dev/null
+++ b/code_transformers/lib/tests.dart
@@ -0,0 +1,64 @@
+// 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.
+
+/// Collection of utilities which are useful for creating unit tests for
+/// Barback transformers.
+library code_transformers.tests;
+
+import 'dart:async' show Future;
+import 'dart:io' show Platform;
+
+import 'package:barback/barback.dart' show Transformer;
+import 'package:path/path.dart' as path;
+import 'package:unittest/unittest.dart';
+
+import 'src/test_harness.dart';
+import 'src/dart_sdk.dart';
+
+export 'src/test_harness.dart' show StringFormatter;
+
+/// Defines a test which invokes [applyTransformers].
+testPhases(String testName, List<List<Transformer>> phases,
+    Map<String, String> inputs, Map<String, String> results,
+    [List<String> messages,
+    StringFormatter formatter = StringFormatter.noTrailingWhitespace]) {
+  test(testName, () => applyTransformers(phases,
+      inputs: inputs,
+      results: results,
+      messages: messages,
+      formatter: formatter));
+}
+
+/// Updates the provided transformers with [inputs] as asset inputs then
+/// validates that [results] were generated.
+///
+/// The keys for inputs and results are 'package_name|lib/file.dart'.
+/// Only files which are specified in results are validated.
+///
+/// If [messages] is non-null then this will validate that only the specified
+/// messages were generated, ignoring info messages.
+Future applyTransformers(List<List<Transformer>> phases,
+    {Map<String, String> inputs: const {}, Map<String, String> results: const {
+}, List<String> messages: const [],
+    StringFormatter formatter: StringFormatter.noTrailingWhitespace}) {
+  var helper = new TestHelper(phases, inputs, messages, formatter: formatter)
+    ..run();
+  return helper.checkAll(results).then((_) => helper.tearDown());
+}
+
+/// Variant of [dartSdkDirectory] which includes additional cases only
+/// typically encountered in Dart's testing environment.
+String get testingDartSdkDirectory {
+  var sdkDir = dartSdkDirectory;
+  if (sdkDir == null) {
+    // If we cannot find the SDK dir, then assume this is being run from Dart's
+    // source directory and this script is the main script.
+    var segments = path.split(path.fromUri(Platform.script));
+    var index = segments.indexOf('pkg');
+    expect(index, greaterThan(0),
+        reason: 'testingDartSdkDirectory is only supported in pkg/ tests');
+    sdkDir = path.joinAll(segments.sublist(0, index)..add('sdk'));
+  }
+  return sdkDir;
+}
diff --git a/code_transformers/pubspec.yaml b/code_transformers/pubspec.yaml
new file mode 100644
index 0000000..e74a164
--- /dev/null
+++ b/code_transformers/pubspec.yaml
@@ -0,0 +1,16 @@
+name: code_transformers
+version: 0.2.9
+author: "Dart Team <misc@dartlang.org>"
+description: Collection of utilities related to creating barback transformers.
+homepage: https://github.com/dart-lang/code-transformers
+dependencies:
+  analyzer: ">=0.15.6 <0.26.0"
+  barback: ">=0.14.2 <0.16.0"
+  cli_util: ">=0.0.1 <0.1.0"
+  path: ">=0.9.0 <2.0.0"
+  source_maps: ">=0.9.4 <0.11.0"
+  source_span: ">=1.0.0 <2.0.0"
+dev_dependencies:
+  unittest: ">=0.10.1 <0.12.0"
+environment:
+  sdk: ">=1.0.0 <2.0.0"
diff --git a/collection/lib/algorithms.dart b/collection/lib/algorithms.dart
new file mode 100644
index 0000000..5ff0bb3
--- /dev/null
+++ b/collection/lib/algorithms.dart
@@ -0,0 +1,301 @@
+// Copyright (c) 2013, 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.
+
+/**
+ * Operations on collections.
+ */
+library dart.pkg.collection.algorithms;
+
+import "dart:math" show Random;
+
+/** Version of [binarySearch] optimized for comparable keys */
+int _comparableBinarySearch(List<Comparable> list, Comparable key) {
+  int min = 0;
+  int max = list.length;
+  while (min < max) {
+    int mid = min + ((max - min) >> 1);
+    var element = list[mid];
+    int comp = element.compareTo(key);
+    if (comp == 0) return mid;
+    if (comp < 0) {
+      min = mid + 1;
+    } else {
+      max = mid;
+    }
+  }
+  return -1;
+}
+
+/**
+ * Returns a position of the [key] in [sortedList], if it is there.
+ *
+ * If the list isn't sorted according to the [compare] function, the result
+ * is unpredictable.
+ *
+ * If [compare] is omitted, it defaults to calling [Comparable.compareTo] on
+ * the objects.
+ *
+ * Returns -1 if [key] is not in the list by default.
+ */
+int binarySearch(List sortedList, var key,
+                 { int compare(var a, var b) }) {
+  if (compare == null) {
+    return _comparableBinarySearch(sortedList, key);
+  }
+  int min = 0;
+  int max = sortedList.length;
+  while (min < max) {
+    int mid = min + ((max - min) >> 1);
+    var element = sortedList[mid];
+    int comp = compare(element, key);
+    if (comp == 0) return mid;
+    if (comp < 0) {
+      min = mid + 1;
+    } else {
+      max = mid;
+    }
+  }
+  return -1;
+}
+
+
+/**
+ * Shuffles a list randomly.
+ *
+ * A sub-range of a list can be shuffled by providing [start] and [end].
+ */
+void shuffle(List list, [int start = 0, int end = null]) {
+  Random random = new Random();
+  if (end == null) end = list.length;
+  int length = end - start;
+  while (length > 1) {
+    int pos = random.nextInt(length);
+    length--;
+    var tmp1 = list[start + pos];
+    list[start + pos] = list[start + length];
+    list[start + length] = tmp1;
+  }
+}
+
+
+/**
+ * Reverses a list, or a part of a list, in-place.
+ */
+void reverse(List list, [int start = 0, int end = null]) {
+  if (end == null) end = list.length;
+  _reverse(list, start, end);
+}
+
+// Internal helper function that assumes valid arguments.
+void _reverse(List list, int start, int end) {
+  for (int i = start, j = end - 1; i < j; i++, j--) {
+    var tmp = list[i];
+    list[i] = list[j];
+    list[j] = tmp;
+  }
+}
+
+/**
+ * Sort a list using insertion sort.
+ *
+ * Insertion sort is a simple sorting algorithm. For `n` elements it does on
+ * the order of `n * log(n)` comparisons but up to `n` squared moves. The
+ * sorting is performed in-place, without using extra memory.
+ *
+ * For short lists the many moves have less impact than the simple algorithm,
+ * and it is often the favored sorting algorithm for short lists.
+ *
+ * This insertion sort is stable: Equal elements end up in the same order
+ * as they started in.
+ */
+void insertionSort(List list,
+                   { int compare(a, b),
+                     int start: 0,
+                     int end: null }) {
+  // If the same method could have both positional and named optional
+  // parameters, this should be (list, [start, end], {compare}).
+  if (end == null) end = list.length;
+  if (compare == null) compare = Comparable.compare;
+  _insertionSort(list, compare, start, end, start + 1);
+}
+
+/**
+ * Internal helper function that assumes arguments correct.
+ *
+ * Assumes that the elements up to [sortedUntil] (not inclusive) are
+ * already sorted. The [sortedUntil] values should always be at least
+ * `start + 1`.
+ */
+void _insertionSort(List list, int compare(a, b), int start, int end,
+                    int sortedUntil) {
+  for (int pos = sortedUntil; pos < end; pos++) {
+    int min = start;
+    int max = pos;
+    var element = list[pos];
+    while (min < max) {
+      int mid = min + ((max - min) >> 1);
+      int comparison = compare(element, list[mid]);
+      if (comparison < 0) {
+        max = mid;
+      } else {
+        min = mid + 1;
+      }
+    }
+    list.setRange(min + 1, pos + 1, list, min);
+    list[min] = element;
+  }
+}
+
+/** Limit below which merge sort defaults to insertion sort. */
+const int _MERGE_SORT_LIMIT = 32;
+
+/**
+ * Sorts a list, or a range of a list, using the merge sort algorithm.
+ *
+ * Merge-sorting works by splitting the job into two parts, sorting each
+ * recursively, and then merging the two sorted parts.
+ *
+ * This takes on the order of `n * log(n)` comparisons and moves to sort
+ * `n` elements, but requires extra space of about the same size as the list
+ * being sorted.
+ *
+ * This merge sort is stable: Equal elements end up in the same order
+ * as they started in.
+ */
+void mergeSort(List list, {int start: 0, int end: null, int compare(a, b)}) {
+  if (end == null) end = list.length;
+  if (compare == null) compare = Comparable.compare;
+  int length = end - start;
+  if (length < 2) return;
+  if (length < _MERGE_SORT_LIMIT) {
+    _insertionSort(list, compare, start, end, start + 1);
+    return;
+  }
+  // Special case the first split instead of directly calling
+  // _mergeSort, because the _mergeSort requires its target to
+  // be different from its source, and it requires extra space
+  // of the same size as the list to sort.
+  // This split allows us to have only half as much extra space,
+  // and it ends up in the original place.
+  int middle = start + ((end - start) >> 1);
+  int firstLength = middle - start;
+  int secondLength = end - middle;
+  // secondLength is always the same as firstLength, or one greater.
+  List scratchSpace = new List(secondLength);
+  _mergeSort(list, compare, middle, end, scratchSpace, 0);
+  int firstTarget = end - firstLength;
+  _mergeSort(list, compare, start, middle, list, firstTarget);
+  _merge(compare,
+         list, firstTarget, end,
+         scratchSpace, 0, secondLength,
+         list, start);
+}
+
+/**
+ * Performs an insertion sort into a potentially different list than the
+ * one containing the original values.
+ *
+ * It will work in-place as well.
+ */
+void _movingInsertionSort(List list, int compare(a, b), int start, int end,
+                          List target, int targetOffset) {
+  int length = end - start;
+  if (length == 0) return;
+  target[targetOffset] = list[start];
+  for (int i = 1; i < length; i++) {
+    var element = list[start + i];
+    int min = targetOffset;
+    int max = targetOffset + i;
+    while (min < max) {
+      int mid = min + ((max - min) >> 1);
+      if (compare(element, target[mid]) < 0) {
+        max = mid;
+      } else {
+        min = mid + 1;
+      }
+    }
+    target.setRange(min + 1, targetOffset + i + 1,
+                    target, min);
+    target[min] = element;
+  }
+}
+
+/**
+ * Sorts [list] from [start] to [end] into [target] at [targetOffset].
+ *
+ * The `target` list must be able to contain the range from `start` to `end`
+ * after `targetOffset`.
+ *
+ * Allows target to be the same list as [list], as long as it's not
+ * overlapping the `start..end` range.
+ */
+void _mergeSort(List list, int compare(a, b), int start, int end,
+                List target, int targetOffset) {
+  int length = end - start;
+  if (length < _MERGE_SORT_LIMIT) {
+    _movingInsertionSort(list, compare, start, end, target, targetOffset);
+    return;
+  }
+  int middle = start + (length >> 1);
+  int firstLength = middle - start;
+  int secondLength = end - middle;
+  // Here secondLength >= firstLength (differs by at most one).
+  int targetMiddle = targetOffset + firstLength;
+  // Sort the second half into the end of the target area.
+  _mergeSort(list, compare, middle, end,
+             target, targetMiddle);
+  // Sort the first half into the end of the source area.
+  _mergeSort(list, compare, start, middle,
+             list, middle);
+  // Merge the two parts into the target area.
+  _merge(compare,
+         list, middle, middle + firstLength,
+         target, targetMiddle, targetMiddle + secondLength,
+         target, targetOffset);
+}
+
+/**
+ * Merges two lists into a target list.
+ *
+ * One of the input lists may be positioned at the end of the target
+ * list.
+ *
+ * For equal object, elements from [firstList] are always preferred.
+ * This allows the merge to be stable if the first list contains elements
+ * that started out earlier than the ones in [secondList]
+ */
+void _merge(int compare(a, b),
+            List firstList, int firstStart, int firstEnd,
+            List secondList, int secondStart, int secondEnd,
+            List target, int targetOffset) {
+  // No empty lists reaches here.
+  assert(firstStart < firstEnd);
+  assert(secondStart < secondEnd);
+  int cursor1 = firstStart;
+  int cursor2 = secondStart;
+  var firstElement = firstList[cursor1++];
+  var secondElement = secondList[cursor2++];
+  while (true) {
+    if (compare(firstElement, secondElement) <= 0) {
+      target[targetOffset++] = firstElement;
+      if (cursor1 == firstEnd) break;  // Flushing second list after loop.
+      firstElement = firstList[cursor1++];
+    } else {
+      target[targetOffset++] = secondElement;
+      if (cursor2 != secondEnd) {
+        secondElement = secondList[cursor2++];
+        continue;
+      }
+      // Second list empties first. Flushing first list here.
+      target[targetOffset++] = firstElement;
+      target.setRange(targetOffset, targetOffset + (firstEnd - cursor1),
+          firstList, cursor1);
+      return;
+    }
+  }
+  // First list empties first. Reached by break above.
+  target[targetOffset++] = secondElement;
+  target.setRange(targetOffset, targetOffset + (secondEnd - cursor2),
+      secondList, cursor2);
+}
diff --git a/collection/lib/collection.dart b/collection/lib/collection.dart
new file mode 100644
index 0000000..45d3867
--- /dev/null
+++ b/collection/lib/collection.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2013, 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.
+
+/**
+ * Exports all the individual parts of the collection-helper library.
+ *
+ * The sub-libraries of this package are:
+ *
+ * - `algorithms.dart`: Algorithms that work on lists (shuffle, binary search
+ *                      and various sorting algorithms).
+ * - `equality.dart`: Different notions of equality of collections.
+ * - `iterable_zip.dart`: Combining multiple iterables into one.
+ * - `priority_queue.dart`: Priority queue type and implementations.
+ * - `wrappers.dart`: Wrapper classes that delegate to a collection object.
+ *                    Includes unmodifiable views of collections.
+ */
+library dart.pkg.collection;
+
+export "algorithms.dart";
+export "equality.dart";
+export "iterable_zip.dart";
+export "priority_queue.dart";
+export "src/canonicalized_map.dart";
+export "src/queue_list.dart";
+export "wrappers.dart";
diff --git a/collection/lib/equality.dart b/collection/lib/equality.dart
new file mode 100644
index 0000000..c6fdafa
--- /dev/null
+++ b/collection/lib/equality.dart
@@ -0,0 +1,419 @@
+// Copyright (c) 2013, 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.
+
+/**
+ * Defines equality relations on collections.
+ */
+library dart.pkg.collection.equality;
+
+import "dart:collection";
+
+const int _HASH_MASK = 0x7fffffff;
+
+/**
+ * A generic equality relation on objects.
+ */
+abstract class Equality<E> {
+  const factory Equality() = DefaultEquality;
+
+  /**
+   * Compare two elements for being equal.
+   *
+   * This should be a proper equality relation.
+   */
+  bool equals(E e1, E e2);
+
+  /**
+   * Get a hashcode of an element.
+   *
+   * The hashcode should be compatible with [equals], so that if
+   * `equals(a, b)` then `hash(a) == hash(b)`.
+   */
+  int hash(E e);
+
+  /**
+   * Test whether an object is a valid argument to [equals] and [hash].
+   *
+   * Some implementations may be restricted to only work on specific types
+   * of objects.
+   */
+  bool isValidKey(Object o);
+}
+
+/**
+ * Equality of objects that compares only the natural equality of the objects.
+ *
+ * This equality uses the objects' own [Object.==] and [Object.hashCode] for
+ * the equality.
+ */
+class DefaultEquality implements Equality {
+  const DefaultEquality();
+  bool equals(Object e1, Object e2) => e1 == e2;
+  int hash(Object e) => e.hashCode;
+  bool isValidKey(Object o) => true;
+}
+
+/**
+ * Equality of objects that compares only the identity of the objects.
+ */
+class IdentityEquality implements Equality {
+  const IdentityEquality();
+  bool equals(Object e1, Object e2) => identical(e1, e2);
+  int hash(Object e) => identityHashCode(e);
+  bool isValidKey(Object o) => true;
+}
+
+/**
+ * Equality on iterables.
+ *
+ * Two iterables are equal if they have the same elements in the same order.
+ */
+class IterableEquality<E> implements Equality<Iterable<E>> {
+  final Equality<E> _elementEquality;
+  const IterableEquality([Equality<E> elementEquality =
+                              const DefaultEquality()])
+      : _elementEquality = elementEquality;
+
+  bool equals(Iterable<E> elements1, Iterable<E> elements2) {
+    if (identical(elements1, elements2)) return true;
+    if (elements1 == null || elements2 == null) return false;
+    Iterator it1 = elements1.iterator;
+    Iterator it2 = elements2.iterator;
+    while (true) {
+      bool hasNext = it1.moveNext();
+      if (hasNext != it2.moveNext()) return false;
+      if (!hasNext) return true;
+      if (!_elementEquality.equals(it1.current, it2.current)) return false;
+    }
+  }
+
+  int hash(Iterable<E> elements) {
+    // Jenkins's one-at-a-time hash function.
+    int hash = 0;
+    for (E element in elements) {
+      int c = _elementEquality.hash(element);
+      hash = (hash + c) & _HASH_MASK;
+      hash = (hash + (hash << 10)) & _HASH_MASK;
+      hash ^= (hash >> 6);
+    }
+    hash = (hash + (hash << 3)) & _HASH_MASK;
+    hash ^= (hash >> 11);
+    hash = (hash + (hash << 15)) & _HASH_MASK;
+    return hash;
+  }
+
+  bool isValidKey(Object o) => o is Iterable<E>;
+}
+
+/**
+ * Equality on lists.
+ *
+ * Two lists are equal if they have the same length and their elements
+ * at each index are equal.
+ *
+ * This is effectively the same as [IterableEquality] except that it
+ * accesses elements by index instead of through iteration.
+ */
+class ListEquality<E> implements Equality<List<E>> {
+  final Equality<E> _elementEquality;
+  const ListEquality([Equality<E> elementEquality = const DefaultEquality()])
+      : _elementEquality = elementEquality;
+
+  bool equals(List<E> e1, List<E> e2) {
+    if (identical(e1, e2)) return true;
+    if (e1 == null || e2 == null) return false;
+    int length = e1.length;
+    if (length != e2.length) return false;
+    for (int i = 0; i < length; i++) {
+      if (!_elementEquality.equals(e1[i], e2[i])) return false;
+    }
+    return true;
+  }
+
+  int hash(List<E> e) {
+    // Jenkins's one-at-a-time hash function.
+    // This code is almost identical to the one in IterableEquality, except
+    // that it uses indexing instead of iterating to get the elements.
+    int hash = 0;
+    for (int i = 0; i < e.length; i++) {
+      int c = _elementEquality.hash(e[i]);
+      hash = (hash + c) & _HASH_MASK;
+      hash = (hash + (hash << 10)) & _HASH_MASK;
+      hash ^= (hash >> 6);
+    }
+    hash = (hash + (hash << 3)) & _HASH_MASK;
+    hash ^= (hash >> 11);
+    hash = (hash + (hash << 15)) & _HASH_MASK;
+    return hash;
+  }
+
+  bool isValidKey(Object o) => o is List<E>;
+}
+
+abstract class _UnorderedEquality<E, T extends Iterable<E>>
+    implements Equality<T> {
+  final Equality<E> _elementEquality;
+
+  const _UnorderedEquality(this._elementEquality);
+
+  bool equals(T e1, T e2) {
+    if (identical(e1, e2)) return true;
+    if (e1 == null || e2 == null) return false;
+    HashMap<E, int> counts = new HashMap(
+        equals: _elementEquality.equals,
+        hashCode: _elementEquality.hash,
+        isValidKey: _elementEquality.isValidKey);
+    int length = 0;
+    for (var e in e1) {
+      int count = counts[e];
+      if (count == null) count = 0;
+      counts[e] = count + 1;
+      length++;
+    }
+    for (var e in e2) {
+      int count = counts[e];
+      if (count == null || count == 0) return false;
+      counts[e] = count - 1;
+      length--;
+    }
+    return length == 0;
+  }
+
+  int hash(T e) {
+    int hash = 0;
+    for (E element in e) {
+      int c = _elementEquality.hash(element);
+      hash = (hash + c) & _HASH_MASK;
+    }
+    hash = (hash + (hash << 3)) & _HASH_MASK;
+    hash ^= (hash >> 11);
+    hash = (hash + (hash << 15)) & _HASH_MASK;
+    return hash;
+  }
+}
+
+/**
+ * Equality of the elements of two iterables without considering order.
+ *
+ * Two iterables are considered equal if they have the same number of elements,
+ * and the elements of one set can be paired with the elements
+ * of the other iterable, so that each pair are equal.
+ */
+class UnorderedIterableEquality<E> extends _UnorderedEquality<E, Iterable<E>> {
+  const UnorderedIterableEquality(
+      [Equality<E> elementEquality = const DefaultEquality()])
+      : super(elementEquality);
+
+  bool isValidKey(Object o) => o is Iterable<E>;
+}
+
+/**
+ * Equality of sets.
+ *
+ * Two sets are considered equal if they have the same number of elements,
+ * and the elements of one set can be paired with the elements
+ * of the other set, so that each pair are equal.
+ *
+ * This equality behaves the same as [UnorderedIterableEquality] except that
+ * it expects sets instead of iterables as arguments.
+ */
+class SetEquality<E> extends _UnorderedEquality<E, Set<E>> {
+  const SetEquality(
+      [Equality<E> elementEquality = const DefaultEquality()])
+      : super(elementEquality);
+
+  bool isValidKey(Object o) => o is Set<E>;
+}
+
+/**
+ *  Internal class used by [MapEquality].
+ *
+ *  The class represents a map entry as a single object,
+ *  using a combined hashCode and equality of the key and value.
+ */
+class _MapEntry {
+  final MapEquality equality;
+  final key;
+  final value;
+  _MapEntry(this.equality, this.key, this.value);
+
+  int get hashCode =>
+      (3 * equality._keyEquality.hash(key) +
+       7 * equality._valueEquality.hash(value)) & _HASH_MASK;
+
+  bool operator==(Object other) {
+    if (other is! _MapEntry) return false;
+    _MapEntry otherEntry = other;
+    return equality._keyEquality.equals(key, otherEntry.key) &&
+           equality._valueEquality.equals(value, otherEntry.value);
+
+  }
+}
+
+/**
+ * Equality on maps.
+ *
+ * Two maps are equal if they have the same number of entries, and if the
+ * entries of the two maps are pairwise equal on both key and value.
+ */
+class MapEquality<K, V> implements Equality<Map<K, V>> {
+  final Equality<K> _keyEquality;
+  final Equality<V> _valueEquality;
+  const MapEquality({ Equality<K> keys : const DefaultEquality(),
+                      Equality<V> values : const DefaultEquality() })
+      : _keyEquality = keys, _valueEquality = values;
+
+  bool equals(Map<K, V> e1, Map<K, V> e2) {
+    if (identical(e1, e2)) return true;
+    if (e1 == null || e2 == null) return false;
+    int length = e1.length;
+    if (length != e2.length) return false;
+    Map<_MapEntry, int> equalElementCounts = new HashMap();
+    for (K key in e1.keys) {
+      _MapEntry entry = new _MapEntry(this, key, e1[key]);
+      int count = equalElementCounts[entry];
+      if (count == null) count = 0;
+      equalElementCounts[entry] = count + 1;
+    }
+    for (K key in e2.keys) {
+      _MapEntry entry = new _MapEntry(this, key, e2[key]);
+      int count = equalElementCounts[entry];
+      if (count == null || count == 0) return false;
+      equalElementCounts[entry] = count - 1;
+    }
+    return true;
+  }
+
+  int hash(Map<K, V> map) {
+    int hash = 0;
+    for (K key in map.keys) {
+      int keyHash = _keyEquality.hash(key);
+      int valueHash = _valueEquality.hash(map[key]);
+      hash = (hash + 3 * keyHash + 7 * valueHash) & _HASH_MASK;
+    }
+    hash = (hash + (hash << 3)) & _HASH_MASK;
+    hash ^= (hash >> 11);
+    hash = (hash + (hash << 15)) & _HASH_MASK;
+    return hash;
+  }
+
+  bool isValidKey(Object o) => o is Map<K, V>;
+}
+
+/**
+ * Combines several equalities into a single equality.
+ *
+ * Tries each equality in order, using [Equality.isValidKey], and returns
+ * the result of the first equality that applies to the argument or arguments.
+ *
+ * For `equals`, the first equality that matches the first argument is used,
+ * and if the second argument of `equals` is not valid for that equality,
+ * it returns false.
+ *
+ * Because the equalities are tried in order, they should generally work on
+ * disjoint types. Otherwise the multi-equality may give inconsistent results
+ * for `equals(e1, e2)` and `equals(e2, e1)`. This can happen if one equality
+ * considers only `e1` a valid key, and not `e2`, but an equality which is
+ * checked later, allows both.
+ */
+class MultiEquality<E> implements Equality<E> {
+  final Iterable<Equality<E>> _equalities;
+
+  const MultiEquality(Iterable<Equality<E>> equalities)
+      : _equalities = equalities;
+
+  bool equals(E e1, E e2) {
+    for (Equality<E> eq in _equalities) {
+      if (eq.isValidKey(e1)) return eq.isValidKey(e2) && eq.equals(e1, e2);
+    }
+    return false;
+  }
+
+  int hash(E e) {
+    for (Equality<E> eq in _equalities) {
+      if (eq.isValidKey(e)) return eq.hash(e);
+    }
+    return -1;
+  }
+
+  bool isValidKey(Object o) {
+    for (Equality<E> eq in _equalities) {
+      if (eq.isValidKey(o)) return true;
+    }
+    return false;
+  }
+}
+
+/**
+ * Deep equality on collections.
+ *
+ * Recognizes lists, sets, iterables and maps and compares their elements using
+ * deep equality as well.
+ *
+ * Non-iterable/map objects are compared using a configurable base equality.
+ *
+ * Works in one of two modes: ordered or unordered.
+ *
+ * In ordered mode, lists and iterables are required to have equal elements
+ * in the same order. In unordered mode, the order of elements in iterables
+ * and lists are not importan.
+ *
+ * A list is only equal to another list, likewise for sets and maps. All other
+ * iterables are compared as iterables only.
+ */
+class DeepCollectionEquality implements Equality {
+  final Equality _base;
+  final bool _unordered;
+  const DeepCollectionEquality([Equality base = const DefaultEquality()])
+      : _base = base, _unordered = false;
+
+  /**
+   * Creates a deep equality on collections where the order of lists and
+   * iterables are not considered important. That is, lists and iterables are
+   * treated as unordered iterables.
+   */
+  const DeepCollectionEquality.unordered(
+      [Equality base = const DefaultEquality()])
+      : _base = base, _unordered = true;
+
+  bool equals(e1, e2) {
+    if (e1 is Set) {
+      if (e2 is! Set) return false;
+      return new SetEquality(this).equals(e1, e2);
+    }
+    if (e1 is Map) {
+      if (e2 is! Map) return false;
+      return new MapEquality(keys: this, values: this).equals(e1, e2);
+    }
+    if (!_unordered) {
+      if (e1 is List) {
+        if (e2 is! List) return false;
+        return new ListEquality(this).equals(e1, e2);
+      }
+      if (e1 is Iterable) {
+        if (e2 is! Iterable) return false;
+        return new IterableEquality(this).equals(e1, e2);
+      }
+    } else if (e1 is Iterable) {
+      if (e2 is! Iterable) return false;
+      if (e1 is List != e2 is List) return false;
+      return new UnorderedIterableEquality(this).equals(e1, e2);
+    }
+    return _base.equals(e1, e2);
+  }
+
+  int hash(Object o) {
+    if (o is Set) return new SetEquality(this).hash(o);
+    if (o is Map) return new MapEquality(keys: this, values: this).hash(o);
+    if (!_unordered) {
+      if (o is List) return new ListEquality(this).hash(o);
+      if (o is Iterable) return new IterableEquality(this).hash(o);
+    } else if (o is Iterable) {
+      return new UnorderedIterableEquality(this).hash(o);
+    }
+    return _base.hash(o);
+  }
+
+  bool isValidKey(Object o) => o is Iterable || o is Map || _base.isValidKey(o);
+}
diff --git a/collection/lib/iterable_zip.dart b/collection/lib/iterable_zip.dart
new file mode 100644
index 0000000..772b07e
--- /dev/null
+++ b/collection/lib/iterable_zip.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2013, 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.
+
+/**
+ * Zipping multiple iterables into one iterable of tuples of values.
+ */
+library dart.pkg.collection.iterable_zip;
+
+import "dart:collection" show IterableBase;
+
+/**
+ * Iterable that iterates over lists of values from other iterables.
+ *
+ * When [iterator] is read, an [Iterator] is created for each [Iterable] in
+ * the [Iterable] passed to the constructor.
+ *
+ * As long as all these iterators have a next value, those next values are
+ * combined into a single list, which becomes the next value of this
+ * [Iterable]'s [Iterator]. As soon as any of the iterators run out,
+ * the zipped iterator also stops.
+ */
+class IterableZip extends IterableBase<List> {
+  final Iterable<Iterable> _iterables;
+  IterableZip(Iterable<Iterable> iterables)
+      : this._iterables = iterables;
+
+  /**
+   * Returns an iterator that combines values of the iterables' iterators
+   * as long as they all have values.
+   */
+  Iterator<List> get iterator {
+    List iterators = _iterables.map((x) => x.iterator).toList(growable: false);
+    // TODO(lrn): Return an empty iterator directly if iterators is empty?
+    return new _IteratorZip(iterators);
+  }
+}
+
+class _IteratorZip implements Iterator<List> {
+  final List<Iterator> _iterators;
+  List _current;
+  _IteratorZip(List iterators) : _iterators = iterators;
+  bool moveNext() {
+    if (_iterators.isEmpty) return false;
+    for (int i = 0; i < _iterators.length; i++) {
+      if (!_iterators[i].moveNext()) {
+        _current = null;
+        return false;
+      }
+    }
+    _current = new List(_iterators.length);
+    for (int i = 0; i < _iterators.length; i++) {
+      _current[i] = _iterators[i].current;
+    }
+    return true;
+  }
+
+  List get current => _current;
+}
diff --git a/collection/lib/priority_queue.dart b/collection/lib/priority_queue.dart
new file mode 100644
index 0000000..efb3239
--- /dev/null
+++ b/collection/lib/priority_queue.dart
@@ -0,0 +1,396 @@
+// 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 dart.pkg.collection.priority_queue;
+
+import "dart:collection" show SplayTreeSet;
+
+/**
+ * A priority queue is a priority based work-list of elements.
+ *
+ * The queue allows adding elements, and removing them again in priority order.
+ */
+abstract class PriorityQueue<E> {
+  /**
+   * Number of elements in the queue.
+   */
+  int get length;
+
+  /**
+   * Whether the queue is empty.
+   */
+  bool get isEmpty;
+
+  /**
+   * Whether the queue has any elements.
+   */
+  bool get isNotEmpty;
+
+  /**
+   * Checks if [object] is in the queue.
+   *
+   * Returns true if the element is found.
+   */
+  bool contains(E object);
+
+  /**
+   * Adds element to the queue.
+   *
+   * The element will become the next to be removed by [removeFirst]
+   * when all elements with higher priority have been removed.
+   */
+  void add(E element);
+
+  /**
+   * Adds all [elements] to the queue.
+   */
+  void addAll(Iterable<E> elements);
+
+  /**
+   * Returns the next element that will be returned by [removeFirst].
+   *
+   * The element is not removed from the queue.
+   *
+   * The queue must not be empty when this method is called.
+   */
+  E get first;
+
+  /**
+   * Removes and returns the element with the highest priority.
+   *
+   * Repeatedly calling this method, without adding element in between,
+   * is guaranteed to return elements in non-decreasing order as, specified by
+   * [comparison].
+   *
+   * The queue must not be empty when this method is called.
+   */
+  E removeFirst();
+
+  /**
+   * Removes an element that compares equal to [element] in the queue.
+   *
+   * Returns true if an element is found and removed,
+   * and false if no equal element is found.
+   */
+  bool remove(E element);
+
+  /**
+   * Removes all the elements from this queue and returns them.
+   *
+   * The returned iterable has no specified order.
+   */
+  Iterable<E> removeAll();
+
+  /**
+   * Removes all the elements from this queue.
+   */
+  void clear();
+
+  /**
+   * Returns a list of the elements of this queue in priority order.
+   *
+   * The queue is not modified.
+   *
+   * The order is the order that the elements would be in if they were
+   * removed from this queue using [removeFirst].
+   */
+  List<E> toList();
+
+  /**
+   * Return a comparator based set using the comparator of this queue.
+   *
+   * The queue is not modified.
+   *
+   * The returned [Set] is currently a [SplayTreeSet],
+   * but this may change as other ordered sets are implemented.
+   *
+   * The set contains all the elements of this queue.
+   * If an element occurs more than once in the queue,
+   * the set will contain it only once.
+   */
+  Set<E> toSet();
+}
+
+/**
+ * Heap based priority queue.
+ *
+ * The elements are kept in a heap structure,
+ * where the element with the highest priority is immediately accessible,
+ * and modifying a single element takes
+ * logarithmic time in the number of elements on average.
+ *
+ * * The [add] and [removeFirst] operations take amortized logarithmic time,
+ *   O(log(n)), but may occasionally take linear time when growing the capacity
+ *   of the heap.
+ * * The [addAll] operation works as doing repeated [add] operations.
+ * * The [first] getter takes constant time, O(1).
+ * * The [clear] and [removeAll] methods also take constant time, O(1).
+ * * The [contains] and [remove] operations may need to search the entire
+ *   queue for the elements, taking O(n) time.
+ * * The [toList] operation effectively sorts the elements, taking O(n*log(n))
+ *   time.
+ * * The [toSet] operation effectively adds each element to the new set, taking
+ *   an expected O(n*log(n)) time.
+ */
+class HeapPriorityQueue<E> implements PriorityQueue<E> {
+  /**
+   * Initial capacity of a queue when created, or when added to after a [clear].
+   *
+   * Number can be any positive value. Picking a size that gives a whole
+   * number of "tree levels" in the heap is only done for aesthetic reasons.
+   */
+  static const int _INITIAL_CAPACITY = 7;
+
+  /**
+   * The comparison being used to compare the priority of elements.
+   */
+  final Comparator comparison;
+
+  /**
+   * List implementation of a heap.
+   */
+  List<E> _queue = new List<E>(_INITIAL_CAPACITY);
+
+  /**
+   * Number of elements in queue.
+   *
+   * The heap is implemented in the first [_length] entries of [_queue].
+   */
+  int _length = 0;
+
+  /**
+   * Create a new priority queue.
+   *
+   * The [comparison] is a [Comparator] used to compare the priority of
+   * elements. An element that compares as less than another element has
+   * a higher priority.
+   *
+   * If [comparison] is omitted, it defaults to [Comparable.compare].
+   */
+  HeapPriorityQueue([int comparison(E e1, E e2)])
+      : comparison = (comparison != null) ? comparison : Comparable.compare;
+
+  void add(E element) {
+    _add(element);
+  }
+
+  void addAll(Iterable<E> elements) {
+    for (E element in elements) {
+      _add(element);
+    }
+  }
+
+  void clear() {
+    _queue = const [];
+    _length = 0;
+  }
+
+  bool contains(E object) {
+    return _locate(object) >= 0;
+  }
+
+  E get first {
+    if (_length == 0) throw new StateError("No such element");
+    return _queue[0];
+  }
+
+  bool get isEmpty => _length == 0;
+
+  bool get isNotEmpty => _length != 0;
+
+  int get length => _length;
+
+  bool remove(E element) {
+    int index = _locate(element);
+    if (index < 0) return false;
+    E last = _removeLast();
+    if (index < _length) {
+      int comp = comparison(last, element);
+      if (comp <= 0) {
+        _bubbleUp(last, index);
+      } else {
+        _bubbleDown(last, index);
+      }
+    }
+    return true;
+  }
+
+  Iterable<E> removeAll() {
+    List<E> result = _queue;
+    int length = _length;
+    _queue = const [];
+    _length = 0;
+    return result.take(length);
+  }
+
+  E removeFirst() {
+    if (_length == 0) throw new StateError("No such element");
+    E result = _queue[0];
+    E last = _removeLast();
+    if (_length > 0) {
+      _bubbleDown(last, 0);
+    }
+    return result;
+  }
+
+  List<E> toList() {
+    List<E> list = new List<E>()..length = _length;
+    list.setRange(0, _length, _queue);
+    list.sort(comparison);
+    return list;
+  }
+
+  Set<E> toSet() {
+    Set<E> set = new SplayTreeSet<E>(comparison);
+    for (int i = 0; i < _length; i++) {
+      set.add(_queue[i]);
+    }
+    return set;
+  }
+
+  /**
+   * Returns some representation of the queue.
+   *
+   * The format isn't significant, and may change in the future.
+   */
+  String toString() {
+    return _queue.take(_length).toString();
+  }
+
+  /**
+   * Add element to the queue.
+   *
+   * Grows the capacity if the backing list is full.
+   */
+  void _add(E element) {
+    if (_length == _queue.length) _grow();
+    _bubbleUp(element, _length++);
+  }
+
+  /**
+   * Find the index of an object in the heap.
+   *
+   * Returns -1 if the object is not found.
+   */
+  int _locate(E object) {
+    if (_length == 0) return -1;
+    // Count positions from one instad of zero. This gives the numbers
+    // some nice properties. For example, all right children are odd,
+    // their left sibling is even, and the parent is found by shifting
+    // right by one.
+    // Valid range for position is [1.._length], inclusive.
+    int position = 1;
+    // Pre-order depth first search, omit child nodes if the current
+    // node has lower priority than [object], because all nodes lower
+    // in the heap will also have lower priority.
+    do {
+      int index = position - 1;
+      E element = _queue[index];
+      int comp = comparison(element, object);
+      if (comp == 0) return index;
+      if (comp < 0) {
+        // Element may be in subtree.
+        // Continue with the left child, if it is there.
+        int leftChildPosition = position * 2;
+        if (leftChildPosition <= _length) {
+          position = leftChildPosition;
+          continue;
+        }
+      }
+      // Find the next right sibling or right ancestor sibling.
+      do {
+        while (position.isOdd) {
+          // While position is a right child, go to the parent.
+          position >>= 1;
+        }
+        // Then go to the right sibling of the left-child.
+        position += 1;
+      } while (position > _length);  // Happens if last element is a left child.
+    } while (position != 1);  // At root again. Happens for right-most element.
+    return -1;
+  }
+
+  E _removeLast() {
+    int newLength = _length - 1;
+    E last = _queue[newLength];
+    _queue[newLength] = null;
+    _length = newLength;
+    return last;
+  }
+
+  /**
+   * Place [element] in heap at [index] or above.
+   *
+   * Put element into the empty cell at `index`.
+   * While the `element` has higher priority than the
+   * parent, swap it with the parent.
+   */
+  void _bubbleUp(E element, int index) {
+    while (index > 0) {
+      int parentIndex = (index - 1) ~/ 2;
+      E parent = _queue[parentIndex];
+      if (comparison(element, parent) > 0) break;
+      _queue[index] = parent;
+      index = parentIndex;
+    }
+    _queue[index] = element;
+  }
+
+  /**
+   * Place [element] in heap at [index] or above.
+   *
+   * Put element into the empty cell at `index`.
+   * While the `element` has lower priority than either child,
+   * swap it with the highest priority child.
+   */
+  void _bubbleDown(E element, int index) {
+    int rightChildIndex = index * 2 + 2;
+    while (rightChildIndex < _length) {
+      int leftChildIndex = rightChildIndex - 1;
+      E leftChild = _queue[leftChildIndex];
+      E rightChild = _queue[rightChildIndex];
+      int comp = comparison(leftChild, rightChild);
+      int minChildIndex;
+      E minChild;
+      if (comp < 0) {
+        minChild = leftChild;
+        minChildIndex = leftChildIndex;
+      } else {
+        minChild = rightChild;
+        minChildIndex = rightChildIndex;
+      }
+      comp = comparison(element, minChild);
+      if (comp <= 0) {
+        _queue[index] = element;
+        return;
+      }
+      _queue[index] = minChild;
+      index = minChildIndex;
+      rightChildIndex = index * 2 + 2;
+    }
+    int leftChildIndex = rightChildIndex - 1;
+    if (leftChildIndex < _length) {
+      E child = _queue[leftChildIndex];
+      int comp = comparison(element, child);
+      if (comp > 0) {
+        _queue[index] = child;
+        index = leftChildIndex;
+      }
+    }
+    _queue[index] = element;
+  }
+
+  /**
+   * Grows the capacity of the list holding the heap.
+   *
+   * Called when the list is full.
+   */
+  void _grow() {
+    int newCapacity = _queue.length * 2 + 1;
+    if (newCapacity < _INITIAL_CAPACITY) newCapacity = _INITIAL_CAPACITY;
+    List<E> newQueue = new List<E>(newCapacity);
+    newQueue.setRange(0, _length, _queue);
+    _queue = newQueue;
+  }
+}
diff --git a/collection/lib/src/canonicalized_map.dart b/collection/lib/src/canonicalized_map.dart
new file mode 100644
index 0000000..f1cd859
--- /dev/null
+++ b/collection/lib/src/canonicalized_map.dart
@@ -0,0 +1,115 @@
+// 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 dart.pkg.collection.canonicalized_map;
+
+import 'dart:collection';
+
+import 'utils.dart';
+
+/**
+ * A map whose keys are converted to canonical values of type `C`.
+ *
+ * This is useful for using case-insensitive String keys, for example. It's more
+ * efficient than a [LinkedHashMap] with a custom equality operator because it
+ * only canonicalizes each key once, rather than doing so for each comparison.
+ *
+ * By default, `null` is allowed as a key. It can be forbidden via the
+ * `isValidKey` parameter.
+ */
+class CanonicalizedMap<C, K, V> implements Map<K, V> {
+  final Function _canonicalize;
+
+  final Function _isValidKeyFn;
+
+  final _base = new Map<C, Pair<K, V>>();
+
+  /**
+   * Creates an empty canonicalized map.
+   *
+   * The [canonicalize] function should return the canonical value for the given
+   * key. Keys with the same canonical value are considered equivalent.
+   *
+   * The [isValidKey] function is called before calling [canonicalize] for
+   * methods that take arbitrary objects. It can be used to filter out keys that
+   * can't be canonicalized.
+   */
+  CanonicalizedMap(C canonicalize(K key), {bool isValidKey(K key)})
+      : _canonicalize = canonicalize,
+        _isValidKeyFn = isValidKey;
+
+  /**
+   * Creates a canonicalized map that is initialized with the key/value pairs of
+   * [other].
+   *
+   * The [canonicalize] function should return the canonical value for the given
+   * key. Keys with the same canonical value are considered equivalent.
+   *
+   * The [isValidKey] function is called before calling [canonicalize] for
+   * methods that take arbitrary objects. It can be used to filter out keys that
+   * can't be canonicalized.
+   */
+  CanonicalizedMap.from(Map<K, V> other, C canonicalize(K key),
+          {bool isValidKey(K key)})
+      : _canonicalize = canonicalize,
+        _isValidKeyFn = isValidKey {
+    addAll(other);
+  }
+
+  V operator [](Object key) {
+    if (!_isValidKey(key)) return null;
+    var pair = _base[_canonicalize(key)];
+    return pair == null ? null : pair.last;
+  }
+
+  void operator []=(K key, V value) {
+    _base[_canonicalize(key)] = new Pair(key, value);
+  }
+
+  void addAll(Map<K, V> other) {
+    other.forEach((key, value) => this[key] = value);
+  }
+
+  void clear() {
+    _base.clear();
+  }
+
+  bool containsKey(Object key) {
+    if (!_isValidKey(key)) return false;
+    return _base.containsKey(_canonicalize(key));
+  }
+
+  bool containsValue(Object value) =>
+      _base.values.any((pair) => pair.last == value);
+
+  void forEach(void f(K key, V value)) {
+    _base.forEach((key, pair) => f(pair.first, pair.last));
+  }
+
+  bool get isEmpty => _base.isEmpty;
+
+  bool get isNotEmpty => _base.isNotEmpty;
+
+  Iterable<K> get keys => _base.values.map((pair) => pair.first);
+
+  int get length => _base.length;
+
+  V putIfAbsent(K key, V ifAbsent()) {
+    return _base.putIfAbsent(_canonicalize(key),
+        () => new Pair(key, ifAbsent())).last;
+  }
+
+  V remove(Object key) {
+    if (!_isValidKey(key)) return null;
+    var pair = _base.remove(_canonicalize(key));
+    return pair == null ? null : pair.last;
+  }
+
+  Iterable<V> get values => _base.values.map((pair) => pair.last);
+
+  String toString() => Maps.mapToString(this);
+
+  bool _isValidKey(Object key) => (key == null || key is K) &&
+      (_isValidKeyFn == null || _isValidKeyFn(key));
+}
diff --git a/collection/lib/src/queue_list.dart b/collection/lib/src/queue_list.dart
new file mode 100644
index 0000000..0ef888f
--- /dev/null
+++ b/collection/lib/src/queue_list.dart
@@ -0,0 +1,231 @@
+// 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.
+
+import 'dart:collection';
+
+/**
+ * A class that efficiently implements both [Queue] and [List].
+ */
+// TODO(nweiz): Currently this code is copied almost verbatim from
+// dart:collection. The only changes are to implement List and to remove methods
+// that are redundant with ListMixin. Remove or simplify it when issue 21330 is
+// fixed.
+class QueueList<E> extends Object with ListMixin<E> implements Queue<E> {
+  static const int _INITIAL_CAPACITY = 8;
+  List<E> _table;
+  int _head;
+  int _tail;
+
+  /**
+   * Create an empty queue.
+   *
+   * If [initialCapacity] is given, prepare the queue for at least that many
+   * elements.
+   */
+  QueueList([int initialCapacity]) : _head = 0, _tail = 0 {
+    if (initialCapacity == null || initialCapacity < _INITIAL_CAPACITY) {
+      initialCapacity = _INITIAL_CAPACITY;
+    } else if (!_isPowerOf2(initialCapacity)) {
+      initialCapacity = _nextPowerOf2(initialCapacity);
+    }
+    assert(_isPowerOf2(initialCapacity));
+    _table = new List<E>(initialCapacity);
+  }
+
+  /**
+   * Create a queue initially containing the elements of [source].
+   */
+  factory QueueList.from(Iterable<E> source) {
+    if (source is List) {
+      int length = source.length;
+      QueueList<E> queue = new QueueList(length + 1);
+      assert(queue._table.length > length);
+      List sourceList = source;
+      queue._table.setRange(0, length, sourceList, 0);
+      queue._tail = length;
+      return queue;
+    } else {
+      return new QueueList<E>()..addAll(source);
+    }
+  }
+
+  // Collection interface.
+
+  void add(E element) {
+    _add(element);
+  }
+
+  void addAll(Iterable<E> elements) {
+    if (elements is List) {
+      List list = elements;
+      int addCount = list.length;
+      int length = this.length;
+      if (length + addCount >= _table.length) {
+        _preGrow(length + addCount);
+        // After preGrow, all elements are at the start of the list.
+        _table.setRange(length, length + addCount, list, 0);
+        _tail += addCount;
+      } else {
+        // Adding addCount elements won't reach _head.
+        int endSpace = _table.length - _tail;
+        if (addCount < endSpace) {
+          _table.setRange(_tail, _tail + addCount, list, 0);
+          _tail += addCount;
+        } else {
+          int preSpace = addCount - endSpace;
+          _table.setRange(_tail, _tail + endSpace, list, 0);
+          _table.setRange(0, preSpace, list, endSpace);
+          _tail = preSpace;
+        }
+      }
+    } else {
+      for (E element in elements) _add(element);
+    }
+  }
+
+  String toString() => IterableBase.iterableToFullString(this, "{", "}");
+
+  // Queue interface.
+
+  void addLast(E element) { _add(element); }
+
+  void addFirst(E element) {
+    _head = (_head - 1) & (_table.length - 1);
+    _table[_head] = element;
+    if (_head == _tail) _grow();
+  }
+
+  E removeFirst() {
+    if (_head == _tail) throw new StateError("No element");
+    E result = _table[_head];
+    _table[_head] = null;
+    _head = (_head + 1) & (_table.length - 1);
+    return result;
+  }
+
+  E removeLast() {
+    if (_head == _tail) throw new StateError("No element");
+    _tail = (_tail - 1) & (_table.length - 1);
+    E result = _table[_tail];
+    _table[_tail] = null;
+    return result;
+  }
+
+  // List interface.
+
+  int get length => (_tail - _head) & (_table.length - 1);
+
+  void set length(int value) {
+    if (value < 0) throw new RangeError("Length $value may not be negative.");
+
+    int delta = value - length;
+    if (delta >= 0) {
+      if (_table.length <= value) {
+        _preGrow(value);
+      }
+      _tail = (_tail + delta) & (_table.length - 1);
+      return;
+    }
+
+    int newTail = _tail + delta; // [delta] is negative.
+    if (newTail >= 0) {
+      _table.fillRange(newTail, _tail, null);
+    } else { 
+      newTail += _table.length;
+      _table.fillRange(0, _tail, null);
+      _table.fillRange(newTail, _table.length, null);
+    }
+    _tail = newTail;
+  }
+
+  E operator [](int index) {
+    if (index < 0 || index >= length) {
+      throw new RangeError("Index $index must be in the range [0..$length).");
+    }
+
+    return _table[(_head + index) & (_table.length - 1)];
+  }
+
+  void operator[]=(int index, E value) {
+    if (index < 0 || index >= length) {
+      throw new RangeError("Index $index must be in the range [0..$length).");
+    }
+
+    _table[(_head + index) & (_table.length - 1)] = value;
+  }
+
+  // Internal helper functions.
+
+  /**
+   * Whether [number] is a power of two.
+   *
+   * Only works for positive numbers.
+   */
+  static bool _isPowerOf2(int number) => (number & (number - 1)) == 0;
+
+  /**
+   * Rounds [number] up to the nearest power of 2.
+   *
+   * If [number] is a power of 2 already, it is returned.
+   *
+   * Only works for positive numbers.
+   */
+  static int _nextPowerOf2(int number) {
+    assert(number > 0);
+    number = (number << 1) - 1;
+    for(;;) {
+      int nextNumber = number & (number - 1);
+      if (nextNumber == 0) return number;
+      number = nextNumber;
+    }
+  }
+
+  /** Adds element at end of queue. Used by both [add] and [addAll]. */
+  void _add(E element) {
+    _table[_tail] = element;
+    _tail = (_tail + 1) & (_table.length - 1);
+    if (_head == _tail) _grow();
+  }
+
+  /**
+   * Grow the table when full.
+   */
+  void _grow() {
+    List<E> newTable = new List<E>(_table.length * 2);
+    int split = _table.length - _head;
+    newTable.setRange(0, split, _table, _head);
+    newTable.setRange(split, split + _head, _table, 0);
+    _head = 0;
+    _tail = _table.length;
+    _table = newTable;
+  }
+
+  int _writeToList(List<E> target) {
+    assert(target.length >= length);
+    if (_head <= _tail) {
+      int length = _tail - _head;
+      target.setRange(0, length, _table, _head);
+      return length;
+    } else {
+      int firstPartSize = _table.length - _head;
+      target.setRange(0, firstPartSize, _table, _head);
+      target.setRange(firstPartSize, firstPartSize + _tail, _table, 0);
+      return _tail + firstPartSize;
+    }
+  }
+
+  /** Grows the table even if it is not full. */
+  void _preGrow(int newElementCount) {
+    assert(newElementCount >= length);
+
+    // Add 1.5x extra room to ensure that there's room for more elements after
+    // expansion.
+    newElementCount += newElementCount >> 1;
+    int newCapacity = _nextPowerOf2(newElementCount);
+    List<E> newTable = new List<E>(newCapacity);
+    _tail = _writeToList(newTable);
+    _table = newTable;
+    _head = 0;
+  }
+}
diff --git a/collection/lib/src/unmodifiable_wrappers.dart b/collection/lib/src/unmodifiable_wrappers.dart
new file mode 100644
index 0000000..72b189c
--- /dev/null
+++ b/collection/lib/src/unmodifiable_wrappers.dart
@@ -0,0 +1,247 @@
+// Copyright (c) 2013, 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.
+
+/**
+ * Wrappers that prevent a List, Set, or Map object from being modified.
+ *
+ * The [Set] and [Map] wrappers allow reading from the wrapped collection,
+ * but prohibit writing.
+ *
+ * The [List] wrapper prevents changes to the length of the wrapped list,
+ * but allows changes to the contents.
+ */
+part of dart.pkg.collection.wrappers;
+
+/**
+ * A fixed-length list.
+ *
+ * A `NonGrowableListView` contains a [List] object and ensures that
+ * its length does not change.
+ * Methods that would change the length of the list,
+ * such as [add] and [remove], throw an [UnsupportedError].
+ * All other methods work directly on the underlying list.
+ *
+ * This class _does_ allow changes to the contents of the wrapped list.
+ * You can, for example, [sort] the list.
+ * Permitted operations defer to the wrapped list.
+ */
+class NonGrowableListView<E> extends DelegatingList<E>
+                             with NonGrowableListMixin<E> {
+  NonGrowableListView(List<E> listBase) : super(listBase);
+}
+
+/**
+ * Mixin class that implements a throwing version of all list operations that
+ * change the List's length.
+ */
+abstract class NonGrowableListMixin<E> implements List<E> {
+  static _throw() {
+    throw new UnsupportedError(
+        "Cannot change the length of a fixed-length list");
+  }
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the length of the list are disallowed.
+   */
+  void set length(int newLength) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the length of the list are disallowed.
+   */
+  bool add(E value) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the length of the list are disallowed.
+   */
+  void addAll(Iterable<E> iterable) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the length of the list are disallowed.
+   */
+  void insert(int index, E element) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the length of the list are disallowed.
+   */
+  void insertAll(int index, Iterable<E> iterable) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the length of the list are disallowed.
+   */
+  bool remove(Object value) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the length of the list are disallowed.
+   */
+  E removeAt(int index) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the length of the list are disallowed.
+   */
+  E removeLast() => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the length of the list are disallowed.
+   */
+  void removeWhere(bool test(E element)) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the length of the list are disallowed.
+   */
+  void retainWhere(bool test(E element)) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the length of the list are disallowed.
+   */
+  void removeRange(int start, int end) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the length of the list are disallowed.
+   */
+  void replaceRange(int start, int end, Iterable<E> iterable) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the length of the list are disallowed.
+   */
+  void clear() => _throw();
+}
+
+/**
+ * An unmodifiable set.
+ *
+ * An UnmodifiableSetView contains a [Set] object and ensures
+ * that it does not change.
+ * Methods that would change the set,
+ * such as [add] and [remove], throw an [UnsupportedError].
+ * Permitted operations defer to the wrapped set.
+ */
+class UnmodifiableSetView<E> extends DelegatingSet<E>
+                             with UnmodifiableSetMixin<E> {
+  UnmodifiableSetView(Set<E> setBase) : super(setBase);
+}
+
+/**
+ * Mixin class that implements a throwing version of all set operations that
+ * change the Set.
+ */
+abstract class UnmodifiableSetMixin<E> implements Set<E> {
+  _throw() {
+    throw new UnsupportedError("Cannot modify an unmodifiable Set");
+  }
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the set are disallowed.
+   */
+  bool add(E value) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the set are disallowed.
+   */
+  void addAll(Iterable<E> elements) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the set are disallowed.
+   */
+  bool remove(Object value) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the set are disallowed.
+   */
+  void removeAll(Iterable elements) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the set are disallowed.
+   */
+  void retainAll(Iterable elements) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the set are disallowed.
+   */
+  void removeWhere(bool test(E element)) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the set are disallowed.
+   */
+  void retainWhere(bool test(E element)) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the set are disallowed.
+   */
+  void clear() => _throw();
+}
+
+/**
+ * An unmodifiable map.
+ *
+ * An UnmodifiableMapView contains a [Map] object and ensures
+ * that it does not change.
+ * Methods that would change the map,
+ * such as [addAll] and [remove], throw an [UnsupportedError].
+ * Permitted operations defer to the wrapped map.
+ */
+class UnmodifiableMapView<K, V> extends DelegatingMap<K, V>
+                                with UnmodifiableMapMixin<K, V> {
+  UnmodifiableMapView(Map<K, V> baseMap) : super(baseMap);
+}
+
+/**
+ * Mixin class that implements a throwing version of all map operations that
+ * change the Map.
+ */
+abstract class UnmodifiableMapMixin<K, V> implements Map<K, V> {
+  static _throw() {
+    throw new UnsupportedError("Cannot modify an unmodifiable Map");
+  }
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the map are disallowed.
+   */
+  void operator []=(K key, V value) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the map are disallowed.
+   */
+  V putIfAbsent(K key, V ifAbsent()) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the map are disallowed.
+   */
+  void addAll(Map<K, V> other) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the map are disallowed.
+   */
+  V remove(K key) => _throw();
+
+  /**
+   * Throws an [UnsupportedError];
+   * operations that change the map are disallowed.
+   */
+  void clear() => _throw();
+}
diff --git a/collection/lib/src/utils.dart b/collection/lib/src/utils.dart
new file mode 100644
index 0000000..c9c7537
--- /dev/null
+++ b/collection/lib/src/utils.dart
@@ -0,0 +1,13 @@
+// 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 dart.pkg.collection.utils;
+
+/// A pair of values.
+class Pair<E, F> {
+  E first;
+  F last;
+
+  Pair(this.first, this.last);
+}
diff --git a/collection/lib/wrappers.dart b/collection/lib/wrappers.dart
new file mode 100644
index 0000000..509a966
--- /dev/null
+++ b/collection/lib/wrappers.dart
@@ -0,0 +1,570 @@
+// Copyright (c) 2013, 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.
+
+/**
+ * Delegating wrappers for [Iterable], [List], [Set], [Queue] and [Map].
+ *
+ * Also adds unmodifiable views for `Set` and `Map`, and a fixed length
+ * view for `List`. The unmodifable list view from `dart:collection` is exported
+ * as well, just for completeness.
+ */
+library dart.pkg.collection.wrappers;
+
+import "dart:collection";
+import "dart:math" show Random;
+
+export "dart:collection" show UnmodifiableListView;
+
+export "src/canonicalized_map.dart";
+part "src/unmodifiable_wrappers.dart";
+
+/**
+ * A base class for delegating iterables.
+ *
+ * Subclasses can provide a [_base] that should be delegated to. Unlike
+ * [DelegatingIterable], this allows the base to be created on demand.
+ */
+abstract class _DelegatingIterableBase<E> implements Iterable<E> {
+  Iterable<E> get _base;
+
+  const _DelegatingIterableBase();
+
+  bool any(bool test(E element)) => _base.any(test);
+
+  bool contains(Object element) => _base.contains(element);
+
+  E elementAt(int index) => _base.elementAt(index);
+
+  bool every(bool test(E element)) => _base.every(test);
+
+  Iterable expand(Iterable f(E element)) => _base.expand(f);
+
+  E get first => _base.first;
+
+  E firstWhere(bool test(E element), {E orElse()}) =>
+      _base.firstWhere(test, orElse: orElse);
+
+  fold(initialValue, combine(previousValue, E element)) =>
+      _base.fold(initialValue, combine);
+
+  void forEach(void f(E element)) => _base.forEach(f);
+
+  bool get isEmpty => _base.isEmpty;
+
+  bool get isNotEmpty => _base.isNotEmpty;
+
+  Iterator<E> get iterator => _base.iterator;
+
+  String join([String separator = ""]) => _base.join(separator);
+
+  E get last => _base.last;
+
+  E lastWhere(bool test(E element), {E orElse()}) =>
+      _base.lastWhere(test, orElse: orElse);
+
+  int get length => _base.length;
+
+  Iterable map(f(E element)) => _base.map(f);
+
+  E reduce(E combine(E value, E element)) => _base.reduce(combine);
+
+  E get single => _base.single;
+
+  E singleWhere(bool test(E element)) => _base.singleWhere(test);
+
+  Iterable<E> skip(int n) => _base.skip(n);
+
+  Iterable<E> skipWhile(bool test(E value)) => _base.skipWhile(test);
+
+  Iterable<E> take(int n) => _base.take(n);
+
+  Iterable<E> takeWhile(bool test(E value)) => _base.takeWhile(test);
+
+  List<E> toList({bool growable: true}) => _base.toList(growable: growable);
+
+  Set<E> toSet() => _base.toSet();
+
+  Iterable<E> where(bool test(E element)) => _base.where(test);
+
+  String toString() => _base.toString();
+}
+
+/**
+ * Creates an [Iterable] that delegates all operations to a base iterable.
+ *
+ * This class can be used hide non-`Iterable` methods of an iterable object,
+ * or it can be extended to add extra functionality on top of an existing
+ * iterable object.
+ */
+class DelegatingIterable<E> extends _DelegatingIterableBase<E> {
+  final Iterable<E> _base;
+
+  /**
+   * Create a wrapper that forwards operations to [base].
+   */
+  const DelegatingIterable(Iterable<E> base) : _base = base;
+}
+
+
+/**
+ * Creates a [List] that delegates all operations to a base list.
+ *
+ * This class can be used hide non-`List` methods of a list object,
+ * or it can be extended to add extra functionality on top of an existing
+ * list object.
+ */
+class DelegatingList<E> extends DelegatingIterable<E> implements List<E> {
+  const DelegatingList(List<E> base) : super(base);
+
+  List<E> get _listBase => _base;
+
+  E operator [](int index) => _listBase[index];
+
+  void operator []=(int index, E value) {
+    _listBase[index] = value;
+  }
+
+  void add(E value) {
+    _listBase.add(value);
+  }
+
+  void addAll(Iterable<E> iterable) {
+    _listBase.addAll(iterable);
+  }
+
+  Map<int, E> asMap() => _listBase.asMap();
+
+  void clear() {
+    _listBase.clear();
+  }
+
+  void fillRange(int start, int end, [E fillValue]) {
+    _listBase.fillRange(start, end, fillValue);
+  }
+
+  Iterable<E> getRange(int start, int end) => _listBase.getRange(start, end);
+
+  int indexOf(E element, [int start = 0]) => _listBase.indexOf(element, start);
+
+  void insert(int index, E element) {
+    _listBase.insert(index, element);
+  }
+
+  void insertAll(int index, Iterable<E> iterable) {
+    _listBase.insertAll(index, iterable);
+  }
+
+  int lastIndexOf(E element, [int start]) =>
+      _listBase.lastIndexOf(element, start);
+
+  void set length(int newLength) {
+    _listBase.length = newLength;
+  }
+
+  bool remove(Object value) => _listBase.remove(value);
+
+  E removeAt(int index) => _listBase.removeAt(index);
+
+  E removeLast() => _listBase.removeLast();
+
+  void removeRange(int start, int end) {
+    _listBase.removeRange(start, end);
+  }
+
+  void removeWhere(bool test(E element)) {
+    _listBase.removeWhere(test);
+  }
+
+  void replaceRange(int start, int end, Iterable<E> iterable) {
+    _listBase.replaceRange(start, end, iterable);
+  }
+
+  void retainWhere(bool test(E element)) {
+    _listBase.retainWhere(test);
+  }
+
+  Iterable<E> get reversed => _listBase.reversed;
+
+  void setAll(int index, Iterable<E> iterable) {
+    _listBase.setAll(index, iterable);
+  }
+
+  void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
+    _listBase.setRange(start, end, iterable, skipCount);
+  }
+
+  void shuffle([Random random]) {
+    _listBase.shuffle(random);
+  }
+
+  void sort([int compare(E a, E b)]) {
+    _listBase.sort(compare);
+  }
+
+  List<E> sublist(int start, [int end]) => _listBase.sublist(start, end);
+}
+
+
+/**
+ * Creates a [Set] that delegates all operations to a base set.
+ *
+ * This class can be used hide non-`Set` methods of a set object,
+ * or it can be extended to add extra functionality on top of an existing
+ * set object.
+ */
+class DelegatingSet<E> extends DelegatingIterable<E> implements Set<E> {
+  const DelegatingSet(Set<E> base) : super(base);
+
+  Set<E> get _setBase => _base;
+
+  bool add(E value) => _setBase.add(value);
+
+  void addAll(Iterable<E> elements) {
+    _setBase.addAll(elements);
+  }
+
+  void clear() {
+    _setBase.clear();
+  }
+
+  bool containsAll(Iterable<Object> other) => _setBase.containsAll(other);
+
+  Set<E> difference(Set<E> other) => _setBase.difference(other);
+
+  Set<E> intersection(Set<Object> other) => _setBase.intersection(other);
+
+  E lookup(E element) => _setBase.lookup(element);
+
+  bool remove(Object value) => _setBase.remove(value);
+
+  void removeAll(Iterable<Object> elements) {
+    _setBase.removeAll(elements);
+  }
+
+  void removeWhere(bool test(E element)) {
+    _setBase.removeWhere(test);
+  }
+
+  void retainAll(Iterable<Object> elements) {
+    _setBase.retainAll(elements);
+  }
+
+  void retainWhere(bool test(E element)) {
+    _setBase.retainWhere(test);
+  }
+
+  Set<E> union(Set<E> other) => _setBase.union(other);
+
+  Set<E> toSet() => new DelegatingSet<E>(_setBase.toSet());
+}
+
+/**
+ * Creates a [Queue] that delegates all operations to a base queue.
+ *
+ * This class can be used hide non-`Queue` methods of a queue object,
+ * or it can be extended to add extra functionality on top of an existing
+ * queue object.
+ */
+class DelegatingQueue<E> extends DelegatingIterable<E> implements Queue<E> {
+  const DelegatingQueue(Queue<E> queue) : super(queue);
+
+  Queue<E> get _baseQueue => _base;
+
+  void add(E value) {
+    _baseQueue.add(value);
+  }
+
+  void addAll(Iterable<E> iterable) {
+    _baseQueue.addAll(iterable);
+  }
+
+  void addFirst(E value) {
+    _baseQueue.addFirst(value);
+  }
+
+  void addLast(E value) {
+    _baseQueue.addLast(value);
+  }
+
+  void clear() {
+    _baseQueue.clear();
+  }
+
+  bool remove(Object object) => _baseQueue.remove(object);
+
+  void removeWhere(bool test(E element)) { _baseQueue.removeWhere(test); }
+
+  void retainWhere(bool test(E element)) { _baseQueue.retainWhere(test); }
+
+  E removeFirst() => _baseQueue.removeFirst();
+
+  E removeLast() => _baseQueue.removeLast();
+}
+
+/**
+ * Creates a [Map] that delegates all operations to a base map.
+ *
+ * This class can be used hide non-`Map` methods of an object that extends
+ * `Map`, or it can be extended to add extra functionality on top of an existing
+ * map object.
+ */
+class DelegatingMap<K, V> implements Map<K, V> {
+  final Map<K, V> _base;
+
+  const DelegatingMap(Map<K, V> base) : _base = base;
+
+  V operator [](Object key) => _base[key];
+
+  void operator []=(K key, V value) {
+    _base[key] = value;
+  }
+
+  void addAll(Map<K, V> other) {
+    _base.addAll(other);
+  }
+
+  void clear() {
+    _base.clear();
+  }
+
+  bool containsKey(Object key) => _base.containsKey(key);
+
+  bool containsValue(Object value) => _base.containsValue(value);
+
+  void forEach(void f(K key, V value)) {
+    _base.forEach(f);
+  }
+
+  bool get isEmpty => _base.isEmpty;
+
+  bool get isNotEmpty => _base.isNotEmpty;
+
+  Iterable<K> get keys => _base.keys;
+
+  int get length => _base.length;
+
+  V putIfAbsent(K key, V ifAbsent()) => _base.putIfAbsent(key, ifAbsent);
+
+  V remove(Object key) => _base.remove(key);
+
+  Iterable<V> get values => _base.values;
+
+  String toString() => _base.toString();
+}
+
+/**
+ * An unmodifiable [Set] view of the keys of a [Map].
+ *
+ * The set delegates all operations to the underlying map.
+ *
+ * A `Map` can only contain each key once, so its keys can always
+ * be viewed as a `Set` without any loss, even if the [Map.keys]
+ * getter only shows an [Iterable] view of the keys.
+ *
+ * Note that [lookup] is not supported for this set.
+ */
+class MapKeySet<E> extends _DelegatingIterableBase<E>
+    with UnmodifiableSetMixin<E> {
+  final Map<E, dynamic> _baseMap;
+
+  MapKeySet(Map<E, dynamic> base) : _baseMap = base;
+
+  Iterable<E> get _base => _baseMap.keys;
+
+  bool contains(Object element) => _baseMap.containsKey(element);
+
+  bool get isEmpty => _baseMap.isEmpty;
+
+  bool get isNotEmpty => _baseMap.isNotEmpty;
+
+  int get length => _baseMap.length;
+
+  String toString() => "{${_base.join(', ')}}";
+
+  bool containsAll(Iterable<Object> other) => other.every(contains);
+
+  /**
+   * Returns a new set with the the elements of [this] that are not in [other].
+   *
+   * That is, the returned set contains all the elements of this [Set] that are
+   * not elements of [other] according to `other.contains`.
+   *
+   * Note that the returned set will use the default equality operation, which
+   * may be different than the equality operation [this] uses.
+   */
+  Set<E> difference(Set<E> other) =>
+      where((element) => !other.contains(element)).toSet();
+
+  /**
+   * Returns a new set which is the intersection between [this] and [other].
+   *
+   * That is, the returned set contains all the elements of this [Set] that are
+   * also elements of [other] according to `other.contains`.
+   *
+   * Note that the returned set will use the default equality operation, which
+   * may be different than the equality operation [this] uses.
+   */
+  Set<E> intersection(Set<Object> other) => where(other.contains).toSet();
+
+  /**
+   * Throws an [UnsupportedError] since there's no corresponding method for
+   * [Map]s.
+   */
+  E lookup(E element) => throw new UnsupportedError(
+      "MapKeySet doesn't support lookup().");
+
+  /**
+   * Returns a new set which contains all the elements of [this] and [other].
+   *
+   * That is, the returned set contains all the elements of this [Set] and all
+   * the elements of [other].
+   *
+   * Note that the returned set will use the default equality operation, which
+   * may be different than the equality operation [this] uses.
+   */
+  Set<E> union(Set<E> other) => toSet()..addAll(other);
+}
+
+/**
+ * Creates a modifiable [Set] view of the values of a [Map].
+ * 
+ * The `Set` view assumes that the keys of the `Map` can be uniquely determined
+ * from the values. The `keyForValue` function passed to the constructor finds
+ * the key for a single value. The `keyForValue` function should be consistent
+ * with equality. If `value1 == value2` then `keyForValue(value1)` and
+ * `keyForValue(value2)` should be considered equal keys by the underlying map,
+ * and vice versa.
+ *
+ * Modifying the set will modify the underlying map based on the key returned by
+ * `keyForValue`.
+ *
+ * If the `Map` contents are not compatible with the `keyForValue` function, the
+ * set will not work consistently, and may give meaningless responses or do
+ * inconsistent updates.
+ *
+ * This set can, for example, be used on a map from database record IDs to the
+ * records. It exposes the records as a set, and allows for writing both
+ * `recordSet.add(databaseRecord)` and `recordMap[id]`.
+ *
+ * Effectively, the map will act as a kind of index for the set.
+ */
+class MapValueSet<K, V> extends _DelegatingIterableBase<V> implements Set<V> {
+  final Map<K, V> _baseMap;
+  final Function _keyForValue;
+
+  /**
+   * Creates a new [MapValueSet] based on [base].
+   *
+   * [keyForValue] returns the key in the map that should be associated with the
+   * given value. The set's notion of equality is identical to the equality of
+   * the return values of [keyForValue].
+   */
+  MapValueSet(Map<K, V> base, K keyForValue(V value))
+      : _baseMap = base,
+        _keyForValue = keyForValue;
+
+  Iterable<V> get _base => _baseMap.values;
+
+  bool contains(Object element) {
+    if (element != null && element is! V) return false;
+    return _baseMap.containsKey(_keyForValue(element));
+  }
+
+  bool get isEmpty => _baseMap.isEmpty;
+
+  bool get isNotEmpty => _baseMap.isNotEmpty;
+
+  int get length => _baseMap.length;
+
+  String toString() => toSet().toString();
+
+  bool add(V value) {
+    K key = _keyForValue(value);
+    bool result = false;
+    _baseMap.putIfAbsent(key, () {
+      result = true;
+      return value;
+    });
+    return result;
+  }
+
+  void addAll(Iterable<V> elements) => elements.forEach(add);
+
+  void clear() => _baseMap.clear();
+
+  bool containsAll(Iterable<Object> other) => other.every(contains);
+
+  /**
+   * Returns a new set with the the elements of [this] that are not in [other].
+   *
+   * That is, the returned set contains all the elements of this [Set] that are
+   * not elements of [other] according to `other.contains`.
+   *
+   * Note that the returned set will use the default equality operation, which
+   * may be different than the equality operation [this] uses.
+   */
+  Set<V> difference(Set<V> other) =>
+      where((element) => !other.contains(element)).toSet();
+
+  /**
+   * Returns a new set which is the intersection between [this] and [other].
+   *
+   * That is, the returned set contains all the elements of this [Set] that are
+   * also elements of [other] according to `other.contains`.
+   *
+   * Note that the returned set will use the default equality operation, which
+   * may be different than the equality operation [this] uses.
+   */
+  Set<V> intersection(Set<Object> other) => where(other.contains).toSet();
+
+  V lookup(V element) => _baseMap[_keyForValue(element)];
+
+  bool remove(Object value) {
+    if (value != null && value is! V) return false;
+    var key = _keyForValue(value);
+    if (!_baseMap.containsKey(key)) return false;
+    _baseMap.remove(key);
+    return true;
+  }
+
+  void removeAll(Iterable<Object> elements) => elements.forEach(remove);
+
+  void removeWhere(bool test(V element)) {
+    var toRemove = [];
+    _baseMap.forEach((key, value) {
+      if (test(value)) toRemove.add(key);
+    });
+    toRemove.forEach(_baseMap.remove);
+  }
+
+  void retainAll(Iterable<Object> elements) {
+    var valuesToRetain = new Set<V>.identity();
+    for (var element in elements) {
+      if (element != null && element is! V) continue;
+      var key = _keyForValue(element);
+      if (!_baseMap.containsKey(key)) continue;
+      valuesToRetain.add(_baseMap[key]);
+    }
+
+    var keysToRemove = [];
+    _baseMap.forEach((k, v) {
+      if (!valuesToRetain.contains(v)) keysToRemove.add(k);
+    });
+    keysToRemove.forEach(_baseMap.remove);
+  }
+
+  void retainWhere(bool test(V element)) =>
+      removeWhere((element) => !test(element));
+
+  /**
+   * Returns a new set which contains all the elements of [this] and [other].
+   *
+   * That is, the returned set contains all the elements of this [Set] and all
+   * the elements of [other].
+   *
+   * Note that the returned set will use the default equality operation, which
+   * may be different than the equality operation [this] uses.
+   */
+  Set<V> union(Set<V> other) => toSet()..addAll(other);
+}
diff --git a/collection/pubspec.yaml b/collection/pubspec.yaml
new file mode 100644
index 0000000..b4396b9
--- /dev/null
+++ b/collection/pubspec.yaml
@@ -0,0 +1,9 @@
+name: collection
+version: 1.1.0
+author: Dart Team <misc@dartlang.org>
+description: Collections and utilities functions and classes related to collections.
+homepage: http://www.dartlang.org
+environment:
+  sdk: '>=1.5.0 <2.0.0'
+dev_dependencies:
+  unittest: '>=0.9.0 <0.11.0'
diff --git a/csslib/lib/css.dart b/csslib/lib/css.dart
new file mode 100644
index 0000000..590e835
--- /dev/null
+++ b/csslib/lib/css.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2013, 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 css;
+
+import 'dart:io';
+
+import 'package:path/path.dart' as path;
+import 'package:source_span/source_span.dart';
+
+import 'parser.dart';
+import 'visitor.dart';
+import 'src/messages.dart';
+import 'src/options.dart';
+
+void main(List<String> arguments) {
+  // TODO(jmesserly): fix this to return a proper exit code
+  var options = PreprocessorOptions.parse(arguments);
+  if (options == null) return;
+
+  messages = new Messages(options: options);
+
+  _time('Total time spent on ${options.inputFile}', () {
+    _compile(options.inputFile, options.verbose);
+  }, true);
+}
+
+void _compile(String inputPath, bool verbose) {
+  var ext = path.extension(inputPath);
+  if (ext != '.css' && ext != '.scss') {
+    messages.error("Please provide a CSS/Sass file", null);
+    return;
+  }
+  try {
+    // Read the file.
+    var filename = path.basename(inputPath);
+    var contents = new File(inputPath).readAsStringSync();
+    var file = new SourceFile(contents, url: path.toUri(inputPath));
+
+    // Parse the CSS.
+    var tree = _time(
+        'Parse $filename', () => new Parser(file, contents).parse(), verbose);
+
+    _time('Analyzer $filename', () => new Analyzer([tree], messages), verbose)
+        .run();
+
+    // Emit the processed CSS.
+    var emitter = new CssPrinter();
+    _time('Codegen $filename', () => emitter.visitTree(tree, pretty: true),
+        verbose);
+
+    // Write the contents to a file.
+    var outPath = path.join(path.dirname(inputPath), '_$filename');
+    new File(outPath).writeAsStringSync(emitter.toString());
+  } catch (e) {
+    messages.error('error processing $inputPath. Original message:\n $e', null);
+  }
+}
+
+_time(String message, callback(), bool printTime) {
+  if (!printTime) return callback();
+  final watch = new Stopwatch();
+  watch.start();
+  var result = callback();
+  watch.stop();
+  final duration = watch.elapsedMilliseconds;
+  _printMessage(message, duration);
+  return result;
+}
+
+void _printMessage(String message, int duration) {
+  var buf = new StringBuffer();
+  buf.write(message);
+  for (int i = message.length; i < 60; i++) buf.write(' ');
+  buf.write(' -- ');
+  if (duration < 10) buf.write(' ');
+  if (duration < 100) buf.write(' ');
+  buf
+    ..write(duration)
+    ..write(' ms');
+  print(buf.toString());
+}
diff --git a/csslib/lib/parser.dart b/csslib/lib/parser.dart
new file mode 100644
index 0000000..9d27e97
--- /dev/null
+++ b/csslib/lib/parser.dart
@@ -0,0 +1,2699 @@
+// Copyright (c) 2012, 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 csslib.parser;
+
+import 'dart:math' as math;
+
+import 'package:source_span/source_span.dart';
+
+import "visitor.dart";
+import 'src/messages.dart';
+import 'src/options.dart';
+
+export 'src/options.dart';
+
+part 'src/analyzer.dart';
+part 'src/polyfill.dart';
+part 'src/property.dart';
+part 'src/token.dart';
+part 'src/tokenizer_base.dart';
+part 'src/tokenizer.dart';
+part 'src/tokenkind.dart';
+
+/** Used for parser lookup ahead (used for nested selectors Less support). */
+class ParserState extends TokenizerState {
+  final Token peekToken;
+  final Token previousToken;
+
+  ParserState(this.peekToken, this.previousToken, Tokenizer tokenizer)
+      : super(tokenizer);
+}
+
+// TODO(jmesserly): this should not be global
+void _createMessages({List<Message> errors, PreprocessorOptions options}) {
+  if (errors == null) errors = [];
+
+  if (options == null) {
+    options = new PreprocessorOptions(useColors: false, inputFile: 'memory');
+  }
+
+  messages = new Messages(options: options, printHandler: errors.add);
+}
+
+/** CSS checked mode enabled. */
+bool get isChecked => messages.options.checked;
+
+// TODO(terry): Remove nested name parameter.
+/** Parse and analyze the CSS file. */
+StyleSheet compile(input, {List<Message> errors, PreprocessorOptions options,
+    bool nested: true, bool polyfill: false, List<StyleSheet> includes: null}) {
+  if (includes == null) {
+    includes = [];
+  }
+
+  var source = _inputAsString(input);
+
+  _createMessages(errors: errors, options: options);
+
+  var file = new SourceFile(source);
+
+  var tree = new _Parser(file, source).parse();
+
+  analyze([tree], errors: errors, options: options);
+
+  if (polyfill) {
+    var processCss = new PolyFill(messages, true);
+    processCss.process(tree, includes: includes);
+  }
+
+  return tree;
+}
+
+/** Analyze the CSS file. */
+void analyze(List<StyleSheet> styleSheets,
+    {List<Message> errors, PreprocessorOptions options}) {
+  _createMessages(errors: errors, options: options);
+  new Analyzer(styleSheets, messages).run();
+}
+
+/**
+ * Parse the [input] CSS stylesheet into a tree. The [input] can be a [String],
+ * or [List<int>] of bytes and returns a [StyleSheet] AST.  The optional
+ * [errors] list will contain each error/warning as a [Message].
+ */
+StyleSheet parse(input, {List<Message> errors, PreprocessorOptions options}) {
+  var source = _inputAsString(input);
+
+  _createMessages(errors: errors, options: options);
+
+  var file = new SourceFile(source);
+  return new _Parser(file, source).parse();
+}
+
+/**
+ * Parse the [input] CSS selector into a tree. The [input] can be a [String],
+ * or [List<int>] of bytes and returns a [StyleSheet] AST.  The optional
+ * [errors] list will contain each error/warning as a [Message].
+ */
+// TODO(jmesserly): should rename "parseSelector" and return Selector
+StyleSheet selector(input, {List<Message> errors}) {
+  var source = _inputAsString(input);
+
+  _createMessages(errors: errors);
+
+  var file = new SourceFile(source);
+  return (new _Parser(file, source)..tokenizer.inSelector = true)
+      .parseSelector();
+}
+
+SelectorGroup parseSelectorGroup(input, {List<Message> errors}) {
+  var source = _inputAsString(input);
+
+  _createMessages(errors: errors);
+
+  var file = new SourceFile(source);
+  return (new _Parser(file, source)
+    // TODO(jmesserly): this fix should be applied to the parser. It's tricky
+    // because by the time the flag is set one token has already been fetched.
+    ..tokenizer.inSelector = true).processSelectorGroup();
+}
+
+String _inputAsString(input) {
+  String source;
+
+  if (input is String) {
+    source = input;
+  } else if (input is List) {
+    // TODO(terry): The parse function needs an "encoding" argument and will
+    //              default to whatever encoding CSS defaults to.
+    //
+    // Here's some info about CSS encodings:
+    // http://www.w3.org/International/questions/qa-css-charset.en.php
+    //
+    // As JMesserly suggests it will probably need a "preparser" html5lib
+    // (encoding_parser.dart) that interprets the bytes as ASCII and scans for
+    // @charset. But for now an "encoding" argument would work.  Often the
+    // HTTP header will indicate the correct encoding.
+    //
+    // See encoding helpers at: package:html5lib/lib/src/char_encodings.dart
+    // These helpers can decode in different formats given an encoding name
+    // (mostly unicode, ascii, windows-1252 which is html5 default encoding).
+    source = new String.fromCharCodes(input as List<int>);
+  } else {
+    // TODO(terry): Support RandomAccessFile using console.
+    throw new ArgumentError("'source' must be a String or "
+        "List<int> (of bytes). RandomAccessFile not supported from this "
+        "simple interface");
+  }
+
+  return source;
+}
+
+// TODO(terry): Consider removing this class when all usages can be eliminated
+//               or replaced with compile API.
+/** Public parsing interface for csslib. */
+class Parser {
+  final _Parser _parser;
+
+  // TODO(jmesserly): having file and text is redundant.
+  Parser(SourceFile file, String text, {int start: 0, String baseUrl})
+      : _parser = new _Parser(file, text, start: start, baseUrl: baseUrl);
+
+  StyleSheet parse() => _parser.parse();
+}
+
+/** A simple recursive descent parser for CSS. */
+class _Parser {
+  final Tokenizer tokenizer;
+
+  /** Base url of CSS file. */
+  final String _baseUrl;
+
+  /**
+   * File containing the source being parsed, used to report errors with
+   * source-span locations.
+   */
+  final SourceFile file;
+
+  Token _previousToken;
+  Token _peekToken;
+
+  _Parser(SourceFile file, String text, {int start: 0, String baseUrl})
+      : this.file = file,
+        _baseUrl = baseUrl,
+        tokenizer = new Tokenizer(file, text, true, start) {
+    _peekToken = tokenizer.next();
+  }
+
+  /** Main entry point for parsing an entire CSS file. */
+  StyleSheet parse() {
+    List<TreeNode> productions = [];
+
+    var start = _peekToken.span;
+    while (!_maybeEat(TokenKind.END_OF_FILE) && !_peekKind(TokenKind.RBRACE)) {
+      // TODO(terry): Need to handle charset.
+      var directive = processDirective();
+      if (directive != null) {
+        productions.add(directive);
+        _maybeEat(TokenKind.SEMICOLON);
+      } else {
+        RuleSet ruleset = processRuleSet();
+        if (ruleset != null) {
+          productions.add(ruleset);
+        } else {
+          break;
+        }
+      }
+    }
+
+    checkEndOfFile();
+
+    return new StyleSheet(productions, _makeSpan(start));
+  }
+
+  /** Main entry point for parsing a simple selector sequence. */
+  StyleSheet parseSelector() {
+    List<TreeNode> productions = [];
+
+    var start = _peekToken.span;
+    while (!_maybeEat(TokenKind.END_OF_FILE) && !_peekKind(TokenKind.RBRACE)) {
+      var selector = processSelector();
+      if (selector != null) {
+        productions.add(selector);
+      }
+    }
+
+    checkEndOfFile();
+
+    return new StyleSheet.selector(productions, _makeSpan(start));
+  }
+
+  /** Generate an error if [file] has not been completely consumed. */
+  void checkEndOfFile() {
+    if (!(_peekKind(TokenKind.END_OF_FILE) ||
+        _peekKind(TokenKind.INCOMPLETE_COMMENT))) {
+      _error('premature end of file unknown CSS', _peekToken.span);
+    }
+  }
+
+  /** Guard to break out of parser when an unexpected end of file is found. */
+  // TODO(jimhug): Failure to call this method can lead to inifinite parser
+  //   loops.  Consider embracing exceptions for more errors to reduce
+  //   the danger here.
+  bool isPrematureEndOfFile() {
+    if (_maybeEat(TokenKind.END_OF_FILE)) {
+      _error('unexpected end of file', _peekToken.span);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // Basic support methods
+  ///////////////////////////////////////////////////////////////////
+  int _peek() {
+    return _peekToken.kind;
+  }
+
+  Token _next({unicodeRange: false}) {
+    _previousToken = _peekToken;
+    _peekToken = tokenizer.next(unicodeRange: unicodeRange);
+    return _previousToken;
+  }
+
+  bool _peekKind(int kind) {
+    return _peekToken.kind == kind;
+  }
+
+  /* Is the next token a legal identifier?  This includes pseudo-keywords. */
+  bool _peekIdentifier() {
+    return TokenKind.isIdentifier(_peekToken.kind);
+  }
+
+  /** Marks the parser/tokenizer look ahead to support Less nested selectors. */
+  ParserState get _mark =>
+      new ParserState(_peekToken, _previousToken, tokenizer);
+
+  /** Restores the parser/tokenizer state to state remembered by _mark. */
+  void _restore(ParserState markedData) {
+    tokenizer.restore(markedData);
+    _peekToken = markedData.peekToken;
+    _previousToken = markedData.previousToken;
+  }
+
+  bool _maybeEat(int kind, {unicodeRange: false}) {
+    if (_peekToken.kind == kind) {
+      _previousToken = _peekToken;
+      _peekToken = tokenizer.next(unicodeRange: unicodeRange);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  void _eat(int kind, {unicodeRange: false}) {
+    if (!_maybeEat(kind, unicodeRange: unicodeRange)) {
+      _errorExpected(TokenKind.kindToString(kind));
+    }
+  }
+
+  void _eatSemicolon() {
+    _eat(TokenKind.SEMICOLON);
+  }
+
+  void _errorExpected(String expected) {
+    var tok = _next();
+    var message;
+    try {
+      message = 'expected $expected, but found $tok';
+    } catch (e) {
+      message = 'parsing error expected $expected';
+    }
+    _error(message, tok.span);
+  }
+
+  void _error(String message, SourceSpan location) {
+    if (location == null) {
+      location = _peekToken.span;
+    }
+    messages.error(message, location);
+  }
+
+  void _warning(String message, SourceSpan location) {
+    if (location == null) {
+      location = _peekToken.span;
+    }
+    messages.warning(message, location);
+  }
+
+  SourceSpan _makeSpan(FileSpan start) {
+    // TODO(terry): there are places where we are creating spans before we eat
+    // the tokens, so using _previousToken is not always valid.
+    // TODO(nweiz): use < rather than compareTo when SourceSpan supports it.
+    if (_previousToken == null || _previousToken.span.compareTo(start) < 0) {
+      return start;
+    }
+    return start.expand(_previousToken.span);
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  // Top level productions
+  ///////////////////////////////////////////////////////////////////
+
+  /**
+   * The media_query_list production below replaces the media_list production
+   * from CSS2 the new grammar is:
+   *
+   *   media_query_list
+   *    : S* [media_query [ ',' S* media_query ]* ]?
+   *   media_query
+   *    : [ONLY | NOT]? S* media_type S* [ AND S* expression ]*
+   *    | expression [ AND S* expression ]*
+   *   media_type
+   *    : IDENT
+   *   expression
+   *    : '(' S* media_feature S* [ ':' S* expr ]? ')' S*
+   *   media_feature
+   *    : IDENT
+   */
+  List<MediaQuery> processMediaQueryList() {
+    var mediaQueries = [];
+
+    bool firstTime = true;
+    var mediaQuery;
+    do {
+      mediaQuery = processMediaQuery(firstTime == true);
+      if (mediaQuery != null) {
+        mediaQueries.add(mediaQuery);
+        firstTime = false;
+        continue;
+      }
+
+      // Any more more media types separated by comma.
+      if (!_maybeEat(TokenKind.COMMA)) break;
+
+      // Yep more media types start again.
+      firstTime = true;
+    } while ((!firstTime && mediaQuery != null) || firstTime);
+
+    return mediaQueries;
+  }
+
+  MediaQuery processMediaQuery([bool startQuery = true]) {
+    // Grammar: [ONLY | NOT]? S* media_type S*
+    //          [ AND S* MediaExpr ]* | MediaExpr [ AND S* MediaExpr ]*
+
+    var start = _peekToken.span;
+
+    // Is it a unary media operator?
+    var op = _peekToken.text;
+    var opLen = op.length;
+    var unaryOp = TokenKind.matchMediaOperator(op, 0, opLen);
+    if (unaryOp != -1) {
+      if (isChecked) {
+        if (startQuery && unaryOp != TokenKind.MEDIA_OP_NOT ||
+            unaryOp != TokenKind.MEDIA_OP_ONLY) {
+          _warning("Only the unary operators NOT and ONLY allowed",
+              _makeSpan(start));
+        }
+        if (!startQuery && unaryOp != TokenKind.MEDIA_OP_AND) {
+          _warning("Only the binary AND operator allowed", _makeSpan(start));
+        }
+      }
+      _next();
+      start = _peekToken.span;
+    }
+
+    var type;
+    if (startQuery && unaryOp != TokenKind.MEDIA_OP_AND) {
+      // Get the media type.
+      if (_peekIdentifier()) type = identifier();
+    }
+
+    var exprs = [];
+
+    if (unaryOp == -1 || unaryOp == TokenKind.MEDIA_OP_AND) {
+      var andOp = false;
+      while (true) {
+        var expr = processMediaExpression(andOp);
+        if (expr == null) break;
+
+        exprs.add(expr);
+        op = _peekToken.text;
+        opLen = op.length;
+        andOp = TokenKind.matchMediaOperator(op, 0, opLen) ==
+            TokenKind.MEDIA_OP_AND;
+        if (!andOp) break;
+        _next();
+      }
+    }
+
+    if (unaryOp != -1 || type != null || exprs.length > 0) {
+      return new MediaQuery(unaryOp, type, exprs, _makeSpan(start));
+    }
+  }
+
+  MediaExpression processMediaExpression([bool andOperator = false]) {
+    var start = _peekToken.span;
+
+    // Grammar: '(' S* media_feature S* [ ':' S* expr ]? ')' S*
+    if (_maybeEat(TokenKind.LPAREN)) {
+      if (_peekIdentifier()) {
+        var feature = identifier(); // Media feature.
+        while (_maybeEat(TokenKind.COLON)) {
+          var startExpr = _peekToken.span;
+          var exprs = processExpr();
+          if (_maybeEat(TokenKind.RPAREN)) {
+            return new MediaExpression(
+                andOperator, feature, exprs, _makeSpan(startExpr));
+          } else if (isChecked) {
+            _warning("Missing parenthesis around media expression",
+                _makeSpan(start));
+            return null;
+          }
+        }
+      } else if (isChecked) {
+        _warning("Missing media feature in media expression", _makeSpan(start));
+        return null;
+      }
+    }
+  }
+
+  /**
+   * Directive grammar:
+   *
+   *  import:             '@import' [string | URI] media_list?
+   *  media:              '@media' media_query_list '{' ruleset '}'
+   *  page:               '@page' [':' IDENT]? '{' declarations '}'
+   *  stylet:             '@stylet' IDENT '{' ruleset '}'
+   *  media_query_list:   IDENT [',' IDENT]
+   *  keyframes:          '@-webkit-keyframes ...' (see grammar below).
+   *  font_face:          '@font-face' '{' declarations '}'
+   *  namespace:          '@namespace name url("xmlns")
+   *  host:               '@host '{' ruleset '}'
+   *  mixin:              '@mixin name [(args,...)] '{' declarations/ruleset '}'
+   *  include:            '@include name [(@arg,@arg1)]
+   *                      '@include name [(@arg...)]
+   *  content             '@content'
+   */
+  processDirective() {
+    var start = _peekToken.span;
+
+    var tokId = processVariableOrDirective();
+    if (tokId is VarDefinitionDirective) return tokId;
+    switch (tokId) {
+      case TokenKind.DIRECTIVE_IMPORT:
+        _next();
+
+        // @import "uri_string" or @import url("uri_string") are identical; only
+        // a url can follow an @import.
+        String importStr;
+        if (_peekIdentifier()) {
+          var func = processFunction(identifier());
+          if (func is UriTerm) {
+            importStr = func.text;
+          }
+        } else {
+          importStr = processQuotedString(false);
+        }
+
+        // Any medias?
+        var medias = processMediaQueryList();
+
+        if (importStr == null) {
+          _error('missing import string', _peekToken.span);
+        }
+
+        return new ImportDirective(importStr.trim(), medias, _makeSpan(start));
+
+      case TokenKind.DIRECTIVE_MEDIA:
+        _next();
+
+        // Any medias?
+        var media = processMediaQueryList();
+
+        List<TreeNode> rulesets = [];
+        if (_maybeEat(TokenKind.LBRACE)) {
+          while (!_maybeEat(TokenKind.END_OF_FILE)) {
+            RuleSet ruleset = processRuleSet();
+            if (ruleset == null) break;
+            rulesets.add(ruleset);
+          }
+
+          if (!_maybeEat(TokenKind.RBRACE)) {
+            _error('expected } after ruleset for @media', _peekToken.span);
+          }
+        } else {
+          _error('expected { after media before ruleset', _peekToken.span);
+        }
+        return new MediaDirective(media, rulesets, _makeSpan(start));
+
+      case TokenKind.DIRECTIVE_HOST:
+        _next();
+
+        List<TreeNode> rulesets = [];
+        if (_maybeEat(TokenKind.LBRACE)) {
+          while (!_maybeEat(TokenKind.END_OF_FILE)) {
+            RuleSet ruleset = processRuleSet();
+            if (ruleset == null) break;
+            rulesets.add(ruleset);
+          }
+
+          if (!_maybeEat(TokenKind.RBRACE)) {
+            _error('expected } after ruleset for @host', _peekToken.span);
+          }
+        } else {
+          _error('expected { after host before ruleset', _peekToken.span);
+        }
+        return new HostDirective(rulesets, _makeSpan(start));
+
+      case TokenKind.DIRECTIVE_PAGE:
+        /*
+         * @page S* IDENT? pseudo_page?
+         *      S* '{' S*
+         *      [ declaration | margin ]?
+         *      [ ';' S* [ declaration | margin ]? ]* '}' S*
+         *
+         * pseudo_page :
+         *      ':' [ "left" | "right" | "first" ]
+         *
+         * margin :
+         *      margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
+         *
+         * margin_sym : @top-left-corner, @top-left, @bottom-left, etc.
+         *
+         * See http://www.w3.org/TR/css3-page/#CSS21
+         */
+        _next();
+
+        // Page name
+        var name;
+        if (_peekIdentifier()) {
+          name = identifier();
+        }
+
+        // Any pseudo page?
+        var pseudoPage;
+        if (_maybeEat(TokenKind.COLON)) {
+          if (_peekIdentifier()) {
+            pseudoPage = identifier();
+            // TODO(terry): Normalize pseudoPage to lowercase.
+            if (isChecked &&
+                !(pseudoPage.name == 'left' ||
+                    pseudoPage.name == 'right' ||
+                    pseudoPage.name == 'first')) {
+              _warning(
+                  "Pseudo page must be left, top or first", pseudoPage.span);
+              return null;
+            }
+          }
+        }
+
+        String pseudoName = pseudoPage is Identifier ? pseudoPage.name : '';
+        String ident = name is Identifier ? name.name : '';
+        return new PageDirective(
+            ident, pseudoName, processMarginsDeclarations(), _makeSpan(start));
+
+      case TokenKind.DIRECTIVE_CHARSET:
+        // @charset S* STRING S* ';'
+        _next();
+
+        var charEncoding = processQuotedString(false);
+        if (isChecked && charEncoding == null) {
+          // Missing character encoding.
+          _warning('missing character encoding string', _makeSpan(start));
+        }
+
+        return new CharsetDirective(charEncoding, _makeSpan(start));
+
+      // TODO(terry): Workaround Dart2js bug continue not implemented in switch
+      //              see https://code.google.com/p/dart/issues/detail?id=8270
+      /*
+      case TokenKind.DIRECTIVE_MS_KEYFRAMES:
+        // TODO(terry): For now only IE 10 (are base level) supports @keyframes,
+        // -moz- has only been optional since Oct 2012 release of Firefox, not
+        // all versions of webkit support @keyframes and opera doesn't yet
+        // support w/o -o- prefix.  Add more warnings for other prefixes when
+        // they become optional.
+        if (isChecked) {
+          _warning('@-ms-keyframes should be @keyframes', _makeSpan(start));
+        }
+        continue keyframeDirective;
+
+      keyframeDirective:
+      */
+      case TokenKind.DIRECTIVE_KEYFRAMES:
+      case TokenKind.DIRECTIVE_WEB_KIT_KEYFRAMES:
+      case TokenKind.DIRECTIVE_MOZ_KEYFRAMES:
+      case TokenKind.DIRECTIVE_O_KEYFRAMES:
+      // TODO(terry): Remove workaround when bug 8270 is fixed.
+      case TokenKind.DIRECTIVE_MS_KEYFRAMES:
+        if (tokId == TokenKind.DIRECTIVE_MS_KEYFRAMES && isChecked) {
+          _warning('@-ms-keyframes should be @keyframes', _makeSpan(start));
+        }
+        // TODO(terry): End of workaround.
+
+        /*  Key frames grammar:
+         *
+         *  @[browser]? keyframes [IDENT|STRING] '{' keyframes-blocks '}';
+         *
+         *  browser: [-webkit-, -moz-, -ms-, -o-]
+         *
+         *  keyframes-blocks:
+         *    [keyframe-selectors '{' declarations '}']* ;
+         *
+         *  keyframe-selectors:
+         *    ['from'|'to'|PERCENTAGE] [',' ['from'|'to'|PERCENTAGE] ]* ;
+         */
+        _next();
+
+        var name;
+        if (_peekIdentifier()) {
+          name = identifier();
+        }
+
+        _eat(TokenKind.LBRACE);
+
+        var keyframe = new KeyFrameDirective(tokId, name, _makeSpan(start));
+
+        do {
+          Expressions selectors = new Expressions(_makeSpan(start));
+
+          do {
+            var term = processTerm();
+
+            // TODO(terry): Only allow from, to and PERCENTAGE ...
+
+            selectors.add(term);
+          } while (_maybeEat(TokenKind.COMMA));
+
+          keyframe.add(new KeyFrameBlock(
+              selectors, processDeclarations(), _makeSpan(start)));
+        } while (!_maybeEat(TokenKind.RBRACE) && !isPrematureEndOfFile());
+
+        return keyframe;
+
+      case TokenKind.DIRECTIVE_FONTFACE:
+        _next();
+        return new FontFaceDirective(processDeclarations(), _makeSpan(start));
+
+      case TokenKind.DIRECTIVE_STYLET:
+        /* Stylet grammar:
+         *
+         *  @stylet IDENT '{'
+         *    ruleset
+         *  '}'
+         */
+        _next();
+
+        var name;
+        if (_peekIdentifier()) {
+          name = identifier();
+        }
+
+        _eat(TokenKind.LBRACE);
+
+        List<TreeNode> productions = [];
+
+        start = _peekToken.span;
+        while (!_maybeEat(TokenKind.END_OF_FILE)) {
+          RuleSet ruleset = processRuleSet();
+          if (ruleset == null) {
+            break;
+          }
+          productions.add(ruleset);
+        }
+
+        _eat(TokenKind.RBRACE);
+
+        return new StyletDirective(name, productions, _makeSpan(start));
+
+      case TokenKind.DIRECTIVE_NAMESPACE:
+        /* Namespace grammar:
+         *
+         * @namespace S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
+         * namespace_prefix : IDENT
+         *
+         */
+        _next();
+
+        var prefix;
+        if (_peekIdentifier()) {
+          prefix = identifier();
+        }
+
+        // The namespace URI can be either a quoted string url("uri_string")
+        // are identical.
+        String namespaceUri;
+        if (_peekIdentifier()) {
+          var func = processFunction(identifier());
+          if (func is UriTerm) {
+            namespaceUri = func.text;
+          }
+        } else {
+          if (prefix != null && prefix.name == 'url') {
+            var func = processFunction(prefix);
+            if (func is UriTerm) {
+              // @namespace url("");
+              namespaceUri = func.text;
+              prefix = null;
+            }
+          } else {
+            namespaceUri = processQuotedString(false);
+          }
+        }
+
+        return new NamespaceDirective(
+            prefix != null ? prefix.name : '', namespaceUri, _makeSpan(start));
+
+      case TokenKind.DIRECTIVE_MIXIN:
+        return processMixin();
+
+      case TokenKind.DIRECTIVE_INCLUDE:
+        return processInclude(_makeSpan(start));
+
+      case TokenKind.DIRECTIVE_CONTENT:
+        // TODO(terry): TBD
+        _warning("@content not implemented.", _makeSpan(start));
+        return null;
+    }
+    return null;
+  }
+
+  /**
+   * Parse the mixin beginning token offset [start]. Returns a [MixinDefinition]
+   * node.
+   *
+   * Mixin grammar:
+   *
+   *  @mixin IDENT [(args,...)] '{'
+   *    [ruleset | property | directive]*
+   *  '}'
+   */
+  MixinDefinition processMixin() {
+    _next();
+
+    var name = identifier();
+
+    List<VarDefinitionDirective> params = [];
+    // Any parameters?
+    if (_maybeEat(TokenKind.LPAREN)) {
+      var mustHaveParam = false;
+      var keepGoing = true;
+      while (keepGoing) {
+        var varDef = processVariableOrDirective(mixinParameter: true);
+        if (varDef is VarDefinitionDirective || varDef is VarDefinition) {
+          params.add(varDef);
+        } else if (mustHaveParam) {
+          _warning("Expecting parameter", _makeSpan(_peekToken.span));
+          keepGoing = false;
+        }
+        if (_maybeEat(TokenKind.COMMA)) {
+          mustHaveParam = true;
+          continue;
+        }
+        keepGoing = !_maybeEat(TokenKind.RPAREN);
+      }
+    }
+
+    _eat(TokenKind.LBRACE);
+
+    List<TreeNode> productions = [];
+    List<TreeNode> declarations = [];
+    var mixinDirective;
+
+    var start = _peekToken.span;
+    while (!_maybeEat(TokenKind.END_OF_FILE)) {
+      var directive = processDirective();
+      if (directive != null) {
+        productions.add(directive);
+        continue;
+      }
+
+      var declGroup = processDeclarations(checkBrace: false);
+      if (declGroup.declarations.any((decl) {
+        return decl is Declaration && decl is! IncludeMixinAtDeclaration;
+      })) {
+        var newDecls = [];
+        productions.forEach((include) {
+          // If declGroup has items that are declarations then we assume
+          // this mixin is a declaration mixin not a top-level mixin.
+          if (include is IncludeDirective) {
+            newDecls.add(new IncludeMixinAtDeclaration(include, include.span));
+          } else {
+            _warning("Error mixing of top-level vs declarations mixins",
+                _makeSpan(include.span));
+          }
+        });
+        declGroup.declarations.insertAll(0, newDecls);
+        productions = [];
+      } else {
+        // Declarations are just @includes make it a list of productions
+        // not a declaration group (anything else is a ruleset).  Make it a
+        // list of productions, not a declaration group.
+        for (var decl in declGroup.declarations) {
+          productions
+              .add(decl is IncludeMixinAtDeclaration ? decl.include : decl);
+        }
+        ;
+        declGroup.declarations.clear();
+      }
+
+      if (declGroup.declarations.isNotEmpty) {
+        if (productions.isEmpty) {
+          mixinDirective = new MixinDeclarationDirective(
+              name.name, params, false, declGroup, _makeSpan(start));
+          break;
+        } else {
+          for (var decl in declGroup.declarations) {
+            productions
+                .add(decl is IncludeMixinAtDeclaration ? decl.include : decl);
+          }
+        }
+      } else {
+        mixinDirective = new MixinRulesetDirective(
+            name.name, params, false, productions, _makeSpan(start));
+        break;
+      }
+    }
+
+    if (productions.isNotEmpty) {
+      mixinDirective = new MixinRulesetDirective(
+          name.name, params, false, productions, _makeSpan(start));
+    }
+
+    _eat(TokenKind.RBRACE);
+
+    return mixinDirective;
+  }
+
+  /**
+   * Returns a VarDefinitionDirective or VarDefinition if a varaible otherwise
+   * return the token id of a directive or -1 if neither.
+   */
+  processVariableOrDirective({bool mixinParameter: false}) {
+    var start = _peekToken.span;
+
+    var tokId = _peek();
+    // Handle case for @ directive (where there's a whitespace between the @
+    // sign and the directive name.  Technically, it's not valid grammar but
+    // a number of CSS tests test for whitespace between @ and name.
+    if (tokId == TokenKind.AT) {
+      _next();
+      tokId = _peek();
+      if (_peekIdentifier()) {
+        // Is it a directive?
+        var directive = _peekToken.text;
+        var directiveLen = directive.length;
+        tokId = TokenKind.matchDirectives(directive, 0, directiveLen);
+        if (tokId == -1) {
+          tokId = TokenKind.matchMarginDirectives(directive, 0, directiveLen);
+        }
+      }
+
+      if (tokId == -1) {
+        if (messages.options.lessSupport) {
+          // Less compatibility:
+          //    @name: value;      =>    var-name: value;       (VarDefinition)
+          //    property: @name;   =>    property: var(name);   (VarUsage)
+          var name;
+          if (_peekIdentifier()) {
+            name = identifier();
+          }
+
+          Expressions exprs;
+          if (mixinParameter && _maybeEat(TokenKind.COLON)) {
+            exprs = processExpr();
+          } else if (!mixinParameter) {
+            _eat(TokenKind.COLON);
+            exprs = processExpr();
+          }
+
+          var span = _makeSpan(start);
+          return new VarDefinitionDirective(
+              new VarDefinition(name, exprs, span), span);
+        } else if (isChecked) {
+          _error('unexpected directive @$_peekToken', _peekToken.span);
+        }
+      }
+    } else if (mixinParameter && _peekToken.kind == TokenKind.VAR_DEFINITION) {
+      _next();
+      var definedName;
+      if (_peekIdentifier()) definedName = identifier();
+
+      Expressions exprs;
+      if (_maybeEat(TokenKind.COLON)) {
+        exprs = processExpr();
+      }
+
+      return new VarDefinition(definedName, exprs, _makeSpan(start));
+    }
+
+    return tokId;
+  }
+
+  IncludeDirective processInclude(SourceSpan span, {bool eatSemiColon: true}) {
+    /* Stylet grammar:
+    *
+     *  @include IDENT [(args,...)];
+     */
+    _next();
+
+    var name;
+    if (_peekIdentifier()) {
+      name = identifier();
+    }
+
+    var params = [];
+
+    // Any parameters?  Parameters can be multiple terms per argument e.g.,
+    // 3px solid yellow, green is two parameters:
+    //    1. 3px solid yellow
+    //    2. green
+    // the first has 3 terms and the second has 1 term.
+    if (_maybeEat(TokenKind.LPAREN)) {
+      var terms = [];
+      var expr;
+      var keepGoing = true;
+      while (keepGoing && (expr = processTerm()) != null) {
+        // VarUsage is returns as a list
+        terms.add(expr is List ? expr[0] : expr);
+        keepGoing = !_peekKind(TokenKind.RPAREN);
+        if (keepGoing) {
+          if (_maybeEat(TokenKind.COMMA)) {
+            params.add(terms);
+            terms = [];
+          }
+        }
+      }
+      params.add(terms);
+      _maybeEat(TokenKind.RPAREN);
+    }
+
+    if (eatSemiColon) {
+      _eat(TokenKind.SEMICOLON);
+    }
+
+    return new IncludeDirective(name.name, params, span);
+  }
+
+  RuleSet processRuleSet([SelectorGroup selectorGroup]) {
+    if (selectorGroup == null) {
+      selectorGroup = processSelectorGroup();
+    }
+    if (selectorGroup != null) {
+      return new RuleSet(
+          selectorGroup, processDeclarations(), selectorGroup.span);
+    }
+  }
+
+  /**
+   * Look ahead to see if what should be a declaration is really a selector.
+   * If it's a selector than it's a nested selector.  This support's Less'
+   * nested selector syntax (requires a look ahead). E.g.,
+   *
+   *    div {
+   *      width : 20px;
+   *      span {
+   *        color: red;
+   *      }
+   *    }
+   *
+   * Two tag name selectors div and span equivalent to:
+   *
+   *    div {
+   *      width: 20px;
+   *    }
+   *    div span {
+   *      color: red;
+   *    }
+   *
+   * Return [:null:] if no selector or [SelectorGroup] if a selector was parsed.
+   */
+  SelectorGroup _nestedSelector() {
+    Messages oldMessages = messages;
+    _createMessages();
+
+    var markedData = _mark;
+
+    // Look a head do we have a nested selector instead of a declaration?
+    SelectorGroup selGroup = processSelectorGroup();
+
+    var nestedSelector = selGroup != null &&
+        _peekKind(TokenKind.LBRACE) &&
+        messages.messages.isEmpty;
+
+    if (!nestedSelector) {
+      // Not a selector so restore the world.
+      _restore(markedData);
+      messages = oldMessages;
+      return null;
+    } else {
+      // Remember any messages from look ahead.
+      oldMessages.mergeMessages(messages);
+      messages = oldMessages;
+      return selGroup;
+    }
+  }
+
+  DeclarationGroup processDeclarations({bool checkBrace: true}) {
+    var start = _peekToken.span;
+
+    if (checkBrace) _eat(TokenKind.LBRACE);
+
+    List decls = [];
+    List dartStyles = []; // List of latest styles exposed to Dart.
+
+    do {
+      var selectorGroup = _nestedSelector();
+      while (selectorGroup != null) {
+        // Nested selector so process as a ruleset.
+        var ruleset = processRuleSet(selectorGroup);
+        decls.add(ruleset);
+        selectorGroup = _nestedSelector();
+      }
+
+      Declaration decl = processDeclaration(dartStyles);
+      if (decl != null) {
+        if (decl.hasDartStyle) {
+          var newDartStyle = decl.dartStyle;
+
+          // Replace or add latest Dart style.
+          bool replaced = false;
+          for (var i = 0; i < dartStyles.length; i++) {
+            var dartStyle = dartStyles[i];
+            if (dartStyle.isSame(newDartStyle)) {
+              dartStyles[i] = newDartStyle;
+              replaced = true;
+              break;
+            }
+          }
+          if (!replaced) {
+            dartStyles.add(newDartStyle);
+          }
+        }
+        decls.add(decl);
+      }
+    } while (_maybeEat(TokenKind.SEMICOLON));
+
+    if (checkBrace) _eat(TokenKind.RBRACE);
+
+    // Fixup declaration to only have dartStyle that are live for this set of
+    // declarations.
+    for (var decl in decls) {
+      if (decl is Declaration) {
+        if (decl.hasDartStyle && dartStyles.indexOf(decl.dartStyle) < 0) {
+          // Dart style not live, ignore these styles in this Declarations.
+          decl.dartStyle = null;
+        }
+      }
+    }
+
+    return new DeclarationGroup(decls, _makeSpan(start));
+  }
+
+  List<DeclarationGroup> processMarginsDeclarations() {
+    List groups = [];
+
+    var start = _peekToken.span;
+
+    _eat(TokenKind.LBRACE);
+
+    List<Declaration> decls = [];
+    List dartStyles = []; // List of latest styles exposed to Dart.
+
+    do {
+      switch (_peek()) {
+        case TokenKind.MARGIN_DIRECTIVE_TOPLEFTCORNER:
+        case TokenKind.MARGIN_DIRECTIVE_TOPLEFT:
+        case TokenKind.MARGIN_DIRECTIVE_TOPCENTER:
+        case TokenKind.MARGIN_DIRECTIVE_TOPRIGHT:
+        case TokenKind.MARGIN_DIRECTIVE_TOPRIGHTCORNER:
+        case TokenKind.MARGIN_DIRECTIVE_BOTTOMLEFTCORNER:
+        case TokenKind.MARGIN_DIRECTIVE_BOTTOMLEFT:
+        case TokenKind.MARGIN_DIRECTIVE_BOTTOMCENTER:
+        case TokenKind.MARGIN_DIRECTIVE_BOTTOMRIGHT:
+        case TokenKind.MARGIN_DIRECTIVE_BOTTOMRIGHTCORNER:
+        case TokenKind.MARGIN_DIRECTIVE_LEFTTOP:
+        case TokenKind.MARGIN_DIRECTIVE_LEFTMIDDLE:
+        case TokenKind.MARGIN_DIRECTIVE_LEFTBOTTOM:
+        case TokenKind.MARGIN_DIRECTIVE_RIGHTTOP:
+        case TokenKind.MARGIN_DIRECTIVE_RIGHTMIDDLE:
+        case TokenKind.MARGIN_DIRECTIVE_RIGHTBOTTOM:
+          // Margin syms processed.
+          //   margin :
+          //      margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
+          //
+          //      margin_sym : @top-left-corner, @top-left, @bottom-left, etc.
+          var marginSym = _peek();
+
+          _next();
+
+          var declGroup = processDeclarations();
+          if (declGroup != null) {
+            groups.add(new MarginGroup(
+                marginSym, declGroup.declarations, _makeSpan(start)));
+          }
+          break;
+        default:
+          Declaration decl = processDeclaration(dartStyles);
+          if (decl != null) {
+            if (decl.hasDartStyle) {
+              var newDartStyle = decl.dartStyle;
+
+              // Replace or add latest Dart style.
+              bool replaced = false;
+              for (var i = 0; i < dartStyles.length; i++) {
+                var dartStyle = dartStyles[i];
+                if (dartStyle.isSame(newDartStyle)) {
+                  dartStyles[i] = newDartStyle;
+                  replaced = true;
+                  break;
+                }
+              }
+              if (!replaced) {
+                dartStyles.add(newDartStyle);
+              }
+            }
+            decls.add(decl);
+          }
+          _maybeEat(TokenKind.SEMICOLON);
+          break;
+      }
+    } while (!_maybeEat(TokenKind.RBRACE) && !isPrematureEndOfFile());
+
+    // Fixup declaration to only have dartStyle that are live for this set of
+    // declarations.
+    for (var decl in decls) {
+      if (decl.hasDartStyle && dartStyles.indexOf(decl.dartStyle) < 0) {
+        // Dart style not live, ignore these styles in this Declarations.
+        decl.dartStyle = null;
+      }
+    }
+
+    if (decls.length > 0) {
+      groups.add(new DeclarationGroup(decls, _makeSpan(start)));
+    }
+
+    return groups;
+  }
+
+  SelectorGroup processSelectorGroup() {
+    List<Selector> selectors = [];
+    var start = _peekToken.span;
+
+    do {
+      Selector selector = processSelector();
+      if (selector != null) {
+        selectors.add(selector);
+      }
+    } while (_maybeEat(TokenKind.COMMA));
+
+    if (selectors.length > 0) {
+      return new SelectorGroup(selectors, _makeSpan(start));
+    }
+  }
+
+  /**
+   * Return list of selectors
+   */
+  Selector processSelector() {
+    var simpleSequences = <SimpleSelectorSequence>[];
+    var start = _peekToken.span;
+    while (true) {
+      // First item is never descendant make sure it's COMBINATOR_NONE.
+      var selectorItem = simpleSelectorSequence(simpleSequences.length == 0);
+      if (selectorItem != null) {
+        simpleSequences.add(selectorItem);
+      } else {
+        break;
+      }
+    }
+
+    if (simpleSequences.length > 0) {
+      return new Selector(simpleSequences, _makeSpan(start));
+    }
+  }
+
+  simpleSelectorSequence(bool forceCombinatorNone) {
+    var start = _peekToken.span;
+    var combinatorType = TokenKind.COMBINATOR_NONE;
+    var thisOperator = false;
+
+    switch (_peek()) {
+      case TokenKind.PLUS:
+        _eat(TokenKind.PLUS);
+        combinatorType = TokenKind.COMBINATOR_PLUS;
+        break;
+      case TokenKind.GREATER:
+        _eat(TokenKind.GREATER);
+        combinatorType = TokenKind.COMBINATOR_GREATER;
+        break;
+      case TokenKind.TILDE:
+        _eat(TokenKind.TILDE);
+        combinatorType = TokenKind.COMBINATOR_TILDE;
+        break;
+      case TokenKind.AMPERSAND:
+        _eat(TokenKind.AMPERSAND);
+        thisOperator = true;
+        break;
+    }
+
+    // Check if WHITESPACE existed between tokens if so we're descendent.
+    if (combinatorType == TokenKind.COMBINATOR_NONE && !forceCombinatorNone) {
+      if (this._previousToken != null &&
+          this._previousToken.end != this._peekToken.start) {
+        combinatorType = TokenKind.COMBINATOR_DESCENDANT;
+      }
+    }
+
+    var span = _makeSpan(start);
+    var simpleSel = thisOperator
+        ? new ElementSelector(new ThisOperator(span), span)
+        : simpleSelector();
+    if (simpleSel == null &&
+        (combinatorType == TokenKind.COMBINATOR_PLUS ||
+            combinatorType == TokenKind.COMBINATOR_GREATER ||
+            combinatorType == TokenKind.COMBINATOR_TILDE)) {
+      // For "+ &", "~ &" or "> &" a selector sequence with no name is needed
+      // so that the & will have a combinator too.  This is needed to
+      // disambiguate selector expressions:
+      //    .foo&:hover     combinator before & is NONE
+      //    .foo &          combinator before & is DESCDENDANT
+      //    .foo > &        combinator before & is GREATER
+      simpleSel = new ElementSelector(new Identifier("", span), span);
+    }
+    if (simpleSel != null) {
+      return new SimpleSelectorSequence(simpleSel, span, combinatorType);
+    }
+  }
+
+  /**
+   * Simple selector grammar:
+   *
+   *    simple_selector_sequence
+   *       : [ type_selector | universal ]
+   *         [ HASH | class | attrib | pseudo | negation ]*
+   *       | [ HASH | class | attrib | pseudo | negation ]+
+   *    type_selector
+   *       : [ namespace_prefix ]? element_name
+   *    namespace_prefix
+   *       : [ IDENT | '*' ]? '|'
+   *    element_name
+   *       : IDENT
+   *    universal
+   *       : [ namespace_prefix ]? '*'
+   *    class
+   *       : '.' IDENT
+   */
+  simpleSelector() {
+    // TODO(terry): Natalie makes a good point parsing of namespace and element
+    //              are essentially the same (asterisk or identifier) other
+    //              than the error message for element.  Should consolidate the
+    //              code.
+    // TODO(terry): Need to handle attribute namespace too.
+    var first;
+    var start = _peekToken.span;
+    switch (_peek()) {
+      case TokenKind.ASTERISK:
+        // Mark as universal namespace.
+        var tok = _next();
+        first = new Wildcard(_makeSpan(tok.span));
+        break;
+      case TokenKind.IDENTIFIER:
+        first = identifier();
+        break;
+      default:
+        // Expecting simple selector.
+        // TODO(terry): Could be a synthesized token like value, etc.
+        if (TokenKind.isKindIdentifier(_peek())) {
+          first = identifier();
+        } else if (_peekKind(TokenKind.SEMICOLON)) {
+          // Can't be a selector if we found a semi-colon.
+          return null;
+        }
+        break;
+    }
+
+    if (_maybeEat(TokenKind.NAMESPACE)) {
+      var element;
+      switch (_peek()) {
+        case TokenKind.ASTERISK:
+          // Mark as universal element
+          var tok = _next();
+          element = new Wildcard(_makeSpan(tok.span));
+          break;
+        case TokenKind.IDENTIFIER:
+          element = identifier();
+          break;
+        default:
+          _error('expected element name or universal(*), but found $_peekToken',
+              _peekToken.span);
+          break;
+      }
+
+      return new NamespaceSelector(
+          first, new ElementSelector(element, element.span), _makeSpan(start));
+    } else if (first != null) {
+      return new ElementSelector(first, _makeSpan(start));
+    } else {
+      // Check for HASH | class | attrib | pseudo | negation
+      return simpleSelectorTail();
+    }
+  }
+
+  bool _anyWhiteSpaceBeforePeekToken(int kind) {
+    if (_previousToken != null &&
+        _peekToken != null &&
+        _previousToken.kind == kind) {
+      // If end of previous token isn't same as the start of peek token then
+      // there's something between these tokens probably whitespace.
+      return _previousToken.end != _peekToken.start;
+    }
+
+    return false;
+  }
+
+  /**
+   * type_selector | universal | HASH | class | attrib | pseudo
+   */
+  simpleSelectorTail() {
+    // Check for HASH | class | attrib | pseudo | negation
+    var start = _peekToken.span;
+    switch (_peek()) {
+      case TokenKind.HASH:
+        _eat(TokenKind.HASH);
+
+        var hasWhiteSpace = false;
+        if (_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) {
+          _warning("Not a valid ID selector expected #id", _makeSpan(start));
+          hasWhiteSpace = true;
+        }
+        if (_peekIdentifier()) {
+          var id = identifier();
+          if (hasWhiteSpace) {
+            // Generate bad selector id (normalized).
+            id.name = " ${id.name}";
+          }
+          return new IdSelector(id, _makeSpan(start));
+        }
+        return null;
+      case TokenKind.DOT:
+        _eat(TokenKind.DOT);
+
+        bool hasWhiteSpace = false;
+        if (_anyWhiteSpaceBeforePeekToken(TokenKind.DOT)) {
+          _warning("Not a valid class selector expected .className",
+              _makeSpan(start));
+          hasWhiteSpace = true;
+        }
+        var id = identifier();
+        if (hasWhiteSpace) {
+          // Generate bad selector class (normalized).
+          id.name = " ${id.name}";
+        }
+        return new ClassSelector(id, _makeSpan(start));
+      case TokenKind.COLON:
+        // :pseudo-class ::pseudo-element
+        return processPseudoSelector(start);
+      case TokenKind.LBRACK:
+        return processAttribute();
+      case TokenKind.DOUBLE:
+        _error('name must start with a alpha character, but found a number',
+            _peekToken.span);
+        _next();
+        break;
+    }
+  }
+
+  processPseudoSelector(FileSpan start) {
+    // :pseudo-class ::pseudo-element
+    // TODO(terry): '::' should be token.
+    _eat(TokenKind.COLON);
+    var pseudoElement = _maybeEat(TokenKind.COLON);
+
+    // TODO(terry): If no identifier specified consider optimizing out the
+    //              : or :: and making this a normal selector.  For now,
+    //              create an empty pseudoName.
+    var pseudoName;
+    if (_peekIdentifier()) {
+      pseudoName = identifier();
+    } else {
+      return null;
+    }
+
+    // Functional pseudo?
+
+    if (_peekToken.kind == TokenKind.LPAREN) {
+      if (!pseudoElement && pseudoName.name.toLowerCase() == 'not') {
+        _eat(TokenKind.LPAREN);
+
+        // Negation :   ':NOT(' S* negation_arg S* ')'
+        var negArg = simpleSelector();
+
+        _eat(TokenKind.RPAREN);
+        return new NegationSelector(negArg, _makeSpan(start));
+      } else {
+        // Special parsing for expressions in pseudo functions.  Minus is used
+        // as operator not identifier.
+        // TODO(jmesserly): we need to flip this before we eat the "(" as the
+        // next token will be fetched when we do that. I think we should try to
+        // refactor so we don't need this boolean; it seems fragile.
+        tokenizer.inSelectorExpression = true;
+        _eat(TokenKind.LPAREN);
+
+        // Handle function expression.
+        var span = _makeSpan(start);
+        var expr = processSelectorExpression();
+
+        tokenizer.inSelectorExpression = false;
+
+        // Used during selector look-a-head if not a SelectorExpression is
+        // bad.
+        if (expr is! SelectorExpression) {
+          _errorExpected("CSS expression");
+          return null;
+        }
+
+        _eat(TokenKind.RPAREN);
+        return (pseudoElement)
+            ? new PseudoElementFunctionSelector(pseudoName, expr, span)
+            : new PseudoClassFunctionSelector(pseudoName, expr, span);
+      }
+    }
+
+    // TODO(terry): Need to handle specific pseudo class/element name and
+    // backward compatible names that are : as well as :: as well as
+    // parameters.  Current, spec uses :: for pseudo-element and : for
+    // pseudo-class.  However, CSS2.1 allows for : to specify old
+    // pseudo-elements (:first-line, :first-letter, :before and :after) any
+    // new pseudo-elements defined would require a ::.
+    return pseudoElement
+        ? new PseudoElementSelector(pseudoName, _makeSpan(start))
+        : new PseudoClassSelector(pseudoName, _makeSpan(start));
+  }
+
+  /**
+   *  In CSS3, the expressions are identifiers, strings, or of the form "an+b".
+   *
+   *    : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
+   *
+   *    num               [0-9]+|[0-9]*\.[0-9]+
+   *    PLUS              '+'
+   *    DIMENSION         {num}{ident}
+   *    NUMBER            {num}
+   */
+  processSelectorExpression() {
+    var start = _peekToken.span;
+
+    var expressions = [];
+
+    Token termToken;
+    var value;
+
+    var keepParsing = true;
+    while (keepParsing) {
+      switch (_peek()) {
+        case TokenKind.PLUS:
+          start = _peekToken.span;
+          termToken = _next();
+          expressions.add(new OperatorPlus(_makeSpan(start)));
+          break;
+        case TokenKind.MINUS:
+          start = _peekToken.span;
+          termToken = _next();
+          expressions.add(new OperatorMinus(_makeSpan(start)));
+          break;
+        case TokenKind.INTEGER:
+          termToken = _next();
+          value = int.parse(termToken.text);
+          break;
+        case TokenKind.DOUBLE:
+          termToken = _next();
+          value = double.parse(termToken.text);
+          break;
+        case TokenKind.SINGLE_QUOTE:
+          value = processQuotedString(false);
+          value = "'${_escapeString(value, single: true)}'";
+          return new LiteralTerm(value, value, _makeSpan(start));
+        case TokenKind.DOUBLE_QUOTE:
+          value = processQuotedString(false);
+          value = '"${_escapeString(value)}"';
+          return new LiteralTerm(value, value, _makeSpan(start));
+        case TokenKind.IDENTIFIER:
+          value = identifier(); // Snarf up the ident we'll remap, maybe.
+          break;
+        default:
+          keepParsing = false;
+      }
+
+      if (keepParsing && value != null) {
+        var unitTerm;
+        // Don't process the dimension if MINUS or PLUS is next.
+        if (_peek() != TokenKind.MINUS && _peek() != TokenKind.PLUS) {
+          unitTerm = processDimension(termToken, value, _makeSpan(start));
+        }
+        if (unitTerm == null) {
+          unitTerm = new LiteralTerm(value, value.name, _makeSpan(start));
+        }
+        expressions.add(unitTerm);
+
+        value = null;
+      }
+    }
+
+    return new SelectorExpression(expressions, _makeSpan(start));
+  }
+
+  //  Attribute grammar:
+  //
+  //  attributes :
+  //    '[' S* IDENT S* [ ATTRIB_MATCHES S* [ IDENT | STRING ] S* ]? ']'
+  //
+  //  ATTRIB_MATCHES :
+  //    [ '=' | INCLUDES | DASHMATCH | PREFIXMATCH | SUFFIXMATCH | SUBSTRMATCH ]
+  //
+  //  INCLUDES:         '~='
+  //
+  //  DASHMATCH:        '|='
+  //
+  //  PREFIXMATCH:      '^='
+  //
+  //  SUFFIXMATCH:      '$='
+  //
+  //  SUBSTRMATCH:      '*='
+  //
+  //
+  AttributeSelector processAttribute() {
+    var start = _peekToken.span;
+
+    if (_maybeEat(TokenKind.LBRACK)) {
+      var attrName = identifier();
+
+      int op;
+      switch (_peek()) {
+        case TokenKind.EQUALS:
+        case TokenKind.INCLUDES: // ~=
+        case TokenKind.DASH_MATCH: // |=
+        case TokenKind.PREFIX_MATCH: // ^=
+        case TokenKind.SUFFIX_MATCH: // $=
+        case TokenKind.SUBSTRING_MATCH: // *=
+          op = _peek();
+          _next();
+          break;
+        default:
+          op = TokenKind.NO_MATCH;
+      }
+
+      var value;
+      if (op != TokenKind.NO_MATCH) {
+        // Operator hit so we require a value too.
+        if (_peekIdentifier()) {
+          value = identifier();
+        } else {
+          value = processQuotedString(false);
+        }
+
+        if (value == null) {
+          _error('expected attribute value string or ident', _peekToken.span);
+        }
+      }
+
+      _eat(TokenKind.RBRACK);
+
+      return new AttributeSelector(attrName, op, value, _makeSpan(start));
+    }
+  }
+
+  //  Declaration grammar:
+  //
+  //  declaration:  property ':' expr prio?
+  //
+  //  property:  IDENT [or IE hacks]
+  //  prio:      !important
+  //  expr:      (see processExpr)
+  //
+  // Here are the ugly IE hacks we need to support:
+  //   property: expr prio? \9; - IE8 and below property, /9 before semi-colon
+  //   *IDENT                   - IE7 or below
+  //   _IDENT                   - IE6 property (automatically a valid ident)
+  //
+  Declaration processDeclaration(List dartStyles) {
+    Declaration decl;
+
+    var start = _peekToken.span;
+
+    // IE7 hack of * before property name if so the property is IE7 or below.
+    var ie7 = _peekKind(TokenKind.ASTERISK);
+    if (ie7) {
+      _next();
+    }
+
+    // IDENT ':' expr '!important'?
+    if (TokenKind.isIdentifier(_peekToken.kind)) {
+      var propertyIdent = identifier();
+
+      var ieFilterProperty = propertyIdent.name.toLowerCase() == 'filter';
+
+      _eat(TokenKind.COLON);
+
+      Expressions exprs = processExpr(ieFilterProperty);
+
+      var dartComposite = _styleForDart(propertyIdent, exprs, dartStyles);
+
+      // Handle !important (prio)
+      var importantPriority = _maybeEat(TokenKind.IMPORTANT);
+
+      decl = new Declaration(
+          propertyIdent, exprs, dartComposite, _makeSpan(start),
+          important: importantPriority, ie7: ie7);
+    } else if (_peekToken.kind == TokenKind.VAR_DEFINITION) {
+      _next();
+      var definedName;
+      if (_peekIdentifier()) definedName = identifier();
+
+      _eat(TokenKind.COLON);
+
+      Expressions exprs = processExpr();
+
+      decl = new VarDefinition(definedName, exprs, _makeSpan(start));
+    } else if (_peekToken.kind == TokenKind.DIRECTIVE_INCLUDE) {
+      // @include mixinName in the declaration area.
+      var span = _makeSpan(start);
+      var include = processInclude(span, eatSemiColon: false);
+      decl = new IncludeMixinAtDeclaration(include, span);
+    } else if (_peekToken.kind == TokenKind.DIRECTIVE_EXTEND) {
+      var simpleSequences = <TreeNode>[];
+
+      _next();
+      var span = _makeSpan(start);
+      var selector = simpleSelector();
+      if (selector == null) {
+        _warning("@extends expecting simple selector name", span);
+      } else {
+        simpleSequences.add(selector);
+      }
+      if (_peekKind(TokenKind.COLON)) {
+        var pseudoSelector = processPseudoSelector(_peekToken.span);
+        if (pseudoSelector is PseudoElementSelector ||
+            pseudoSelector is PseudoClassSelector) {
+          simpleSequences.add(pseudoSelector);
+        } else {
+          _warning("not a valid selector", span);
+        }
+      }
+      decl = new ExtendDeclaration(simpleSequences, span);
+    }
+
+    return decl;
+  }
+
+  /** List of styles exposed to the Dart UI framework. */
+  static const int _fontPartFont = 0;
+  static const int _fontPartVariant = 1;
+  static const int _fontPartWeight = 2;
+  static const int _fontPartSize = 3;
+  static const int _fontPartFamily = 4;
+  static const int _fontPartStyle = 5;
+  static const int _marginPartMargin = 6;
+  static const int _marginPartLeft = 7;
+  static const int _marginPartTop = 8;
+  static const int _marginPartRight = 9;
+  static const int _marginPartBottom = 10;
+  static const int _lineHeightPart = 11;
+  static const int _borderPartBorder = 12;
+  static const int _borderPartLeft = 13;
+  static const int _borderPartTop = 14;
+  static const int _borderPartRight = 15;
+  static const int _borderPartBottom = 16;
+  static const int _borderPartWidth = 17;
+  static const int _borderPartLeftWidth = 18;
+  static const int _borderPartTopWidth = 19;
+  static const int _borderPartRightWidth = 20;
+  static const int _borderPartBottomWidth = 21;
+  static const int _heightPart = 22;
+  static const int _widthPart = 23;
+  static const int _paddingPartPadding = 24;
+  static const int _paddingPartLeft = 25;
+  static const int _paddingPartTop = 26;
+  static const int _paddingPartRight = 27;
+  static const int _paddingPartBottom = 28;
+
+  static const Map<String, int> _stylesToDart = const {
+    'font': _fontPartFont,
+    'font-family': _fontPartFamily,
+    'font-size': _fontPartSize,
+    'font-style': _fontPartStyle,
+    'font-variant': _fontPartVariant,
+    'font-weight': _fontPartWeight,
+    'line-height': _lineHeightPart,
+    'margin': _marginPartMargin,
+    'margin-left': _marginPartLeft,
+    'margin-right': _marginPartRight,
+    'margin-top': _marginPartTop,
+    'margin-bottom': _marginPartBottom,
+    'border': _borderPartBorder,
+    'border-left': _borderPartLeft,
+    'border-right': _borderPartRight,
+    'border-top': _borderPartTop,
+    'border-bottom': _borderPartBottom,
+    'border-width': _borderPartWidth,
+    'border-left-width': _borderPartLeftWidth,
+    'border-top-width': _borderPartTopWidth,
+    'border-right-width': _borderPartRightWidth,
+    'border-bottom-width': _borderPartBottomWidth,
+    'height': _heightPart,
+    'width': _widthPart,
+    'padding': _paddingPartPadding,
+    'padding-left': _paddingPartLeft,
+    'padding-top': _paddingPartTop,
+    'padding-right': _paddingPartRight,
+    'padding-bottom': _paddingPartBottom
+  };
+
+  static const Map<String, int> _nameToFontWeight = const {
+    'bold': FontWeight.bold,
+    'normal': FontWeight.normal
+  };
+
+  static int _findStyle(String styleName) => _stylesToDart[styleName];
+
+  DartStyleExpression _styleForDart(
+      Identifier property, Expressions exprs, List dartStyles) {
+    var styleType = _findStyle(property.name.toLowerCase());
+    if (styleType != null) {
+      return buildDartStyleNode(styleType, exprs, dartStyles);
+    }
+  }
+
+  FontExpression _mergeFontStyles(FontExpression fontExpr, List dartStyles) {
+    // Merge all font styles for this class selector.
+    for (var dartStyle in dartStyles) {
+      if (dartStyle.isFont) {
+        fontExpr = new FontExpression.merge(dartStyle, fontExpr);
+      }
+    }
+
+    return fontExpr;
+  }
+
+  DartStyleExpression buildDartStyleNode(
+      int styleType, Expressions exprs, List dartStyles) {
+    switch (styleType) {
+      /*
+       * Properties in order:
+       *
+       *   font-style font-variant font-weight font-size/line-height font-family
+       *
+       * The font-size and font-family values are required. If other values are
+       * missing; a default, if it exist, will be used.
+       */
+      case _fontPartFont:
+        var processor = new ExpressionsProcessor(exprs);
+        return _mergeFontStyles(processor.processFont(), dartStyles);
+      case _fontPartFamily:
+        var processor = new ExpressionsProcessor(exprs);
+
+        try {
+          return _mergeFontStyles(processor.processFontFamily(), dartStyles);
+        } catch (fontException) {
+          _error(fontException, _peekToken.span);
+        }
+        break;
+      case _fontPartSize:
+        var processor = new ExpressionsProcessor(exprs);
+        return _mergeFontStyles(processor.processFontSize(), dartStyles);
+      case _fontPartStyle:
+        /* Possible style values:
+         *   normal [default]
+         *   italic
+         *   oblique
+         *   inherit
+         */
+        // TODO(terry): TBD
+        break;
+      case _fontPartVariant:
+        /* Possible variant values:
+         *   normal  [default]
+         *   small-caps
+         *   inherit
+         */
+        // TODO(terry): TBD
+        break;
+      case _fontPartWeight:
+        /* Possible weight values:
+         *   normal [default]
+         *   bold
+         *   bolder
+         *   lighter
+         *   100 - 900
+         *   inherit
+         */
+        // TODO(terry): Only 'normal', 'bold', or values of 100-900 supoorted
+        //              need to handle bolder, lighter, and inherit.  See
+        //              https://github.com/dart-lang/csslib/issues/1
+        var expr = exprs.expressions[0];
+        if (expr is NumberTerm) {
+          var fontExpr = new FontExpression(expr.span, weight: expr.value);
+          return _mergeFontStyles(fontExpr, dartStyles);
+        } else if (expr is LiteralTerm) {
+          int weight = _nameToFontWeight[expr.value.toString()];
+          if (weight != null) {
+            var fontExpr = new FontExpression(expr.span, weight: weight);
+            return _mergeFontStyles(fontExpr, dartStyles);
+          }
+        }
+        break;
+      case _lineHeightPart:
+        if (exprs.expressions.length == 1) {
+          var expr = exprs.expressions[0];
+          if (expr is UnitTerm) {
+            UnitTerm unitTerm = expr;
+            // TODO(terry): Need to handle other units and LiteralTerm normal
+            //              See https://github.com/dart-lang/csslib/issues/2.
+            if (unitTerm.unit == TokenKind.UNIT_LENGTH_PX ||
+                unitTerm.unit == TokenKind.UNIT_LENGTH_PT) {
+              var fontExpr = new FontExpression(expr.span,
+                  lineHeight: new LineHeight(expr.value, inPixels: true));
+              return _mergeFontStyles(fontExpr, dartStyles);
+            } else if (isChecked) {
+              _warning("Unexpected unit for line-height", expr.span);
+            }
+          } else if (expr is NumberTerm) {
+            var fontExpr = new FontExpression(expr.span,
+                lineHeight: new LineHeight(expr.value, inPixels: false));
+            return _mergeFontStyles(fontExpr, dartStyles);
+          } else if (isChecked) {
+            _warning("Unexpected value for line-height", expr.span);
+          }
+        }
+        break;
+      case _marginPartMargin:
+        return new MarginExpression.boxEdge(exprs.span, processFourNums(exprs));
+      case _borderPartBorder:
+        for (var expr in exprs.expressions) {
+          var v = marginValue(expr);
+          if (v != null) {
+            final box = new BoxEdge.uniform(v);
+            return new BorderExpression.boxEdge(exprs.span, box);
+          }
+        }
+        break;
+      case _borderPartWidth:
+        var v = marginValue(exprs.expressions[0]);
+        if (v != null) {
+          final box = new BoxEdge.uniform(v);
+          return new BorderExpression.boxEdge(exprs.span, box);
+        }
+        break;
+      case _paddingPartPadding:
+        return new PaddingExpression.boxEdge(
+            exprs.span, processFourNums(exprs));
+      case _marginPartLeft:
+      case _marginPartTop:
+      case _marginPartRight:
+      case _marginPartBottom:
+      case _borderPartLeft:
+      case _borderPartTop:
+      case _borderPartRight:
+      case _borderPartBottom:
+      case _borderPartLeftWidth:
+      case _borderPartTopWidth:
+      case _borderPartRightWidth:
+      case _borderPartBottomWidth:
+      case _heightPart:
+      case _widthPart:
+      case _paddingPartLeft:
+      case _paddingPartTop:
+      case _paddingPartRight:
+      case _paddingPartBottom:
+        if (exprs.expressions.length > 0) {
+          return processOneNumber(exprs, styleType);
+        }
+        break;
+      default:
+        // Don't handle it.
+        return null;
+    }
+  }
+
+  // TODO(terry): Look at handling width of thin, thick, etc. any none numbers
+  //              to convert to a number.
+  DartStyleExpression processOneNumber(Expressions exprs, int part) {
+    var value = marginValue(exprs.expressions[0]);
+    if (value != null) {
+      switch (part) {
+        case _marginPartLeft:
+          return new MarginExpression(exprs.span, left: value);
+        case _marginPartTop:
+          return new MarginExpression(exprs.span, top: value);
+        case _marginPartRight:
+          return new MarginExpression(exprs.span, right: value);
+        case _marginPartBottom:
+          return new MarginExpression(exprs.span, bottom: value);
+        case _borderPartLeft:
+        case _borderPartLeftWidth:
+          return new BorderExpression(exprs.span, left: value);
+        case _borderPartTop:
+        case _borderPartTopWidth:
+          return new BorderExpression(exprs.span, top: value);
+        case _borderPartRight:
+        case _borderPartRightWidth:
+          return new BorderExpression(exprs.span, right: value);
+        case _borderPartBottom:
+        case _borderPartBottomWidth:
+          return new BorderExpression(exprs.span, bottom: value);
+        case _heightPart:
+          return new HeightExpression(exprs.span, value);
+        case _widthPart:
+          return new WidthExpression(exprs.span, value);
+        case _paddingPartLeft:
+          return new PaddingExpression(exprs.span, left: value);
+        case _paddingPartTop:
+          return new PaddingExpression(exprs.span, top: value);
+        case _paddingPartRight:
+          return new PaddingExpression(exprs.span, right: value);
+        case _paddingPartBottom:
+          return new PaddingExpression(exprs.span, bottom: value);
+      }
+    }
+  }
+
+  /**
+   * Margins are of the format:
+   *
+   *   top,right,bottom,left      (4 parameters)
+   *   top,right/left, bottom     (3 parameters)
+   *   top/bottom,right/left      (2 parameters)
+   *   top/right/bottom/left      (1 parameter)
+   *
+   * The values of the margins can be a unit or unitless or auto.
+   */
+  BoxEdge processFourNums(Expressions exprs) {
+    num top;
+    num right;
+    num bottom;
+    num left;
+
+    int totalExprs = exprs.expressions.length;
+    switch (totalExprs) {
+      case 1:
+        top = marginValue(exprs.expressions[0]);
+        right = top;
+        bottom = top;
+        left = top;
+        break;
+      case 2:
+        top = marginValue(exprs.expressions[0]);
+        bottom = top;
+        right = marginValue(exprs.expressions[1]);
+        left = right;
+        break;
+      case 3:
+        top = marginValue(exprs.expressions[0]);
+        right = marginValue(exprs.expressions[1]);
+        left = right;
+        bottom = marginValue(exprs.expressions[2]);
+        break;
+      case 4:
+        top = marginValue(exprs.expressions[0]);
+        right = marginValue(exprs.expressions[1]);
+        bottom = marginValue(exprs.expressions[2]);
+        left = marginValue(exprs.expressions[3]);
+        break;
+      default:
+        return null;
+    }
+
+    return new BoxEdge.clockwiseFromTop(top, right, bottom, left);
+  }
+
+  // TODO(terry): Need to handle auto.
+  marginValue(var exprTerm) {
+    if (exprTerm is UnitTerm || exprTerm is NumberTerm) {
+      return exprTerm.value;
+    }
+  }
+
+  //  Expression grammar:
+  //
+  //  expression:   term [ operator? term]*
+  //
+  //  operator:     '/' | ','
+  //  term:         (see processTerm)
+  //
+  Expressions processExpr([bool ieFilter = false]) {
+    var start = _peekToken.span;
+    var expressions = new Expressions(_makeSpan(start));
+
+    var keepGoing = true;
+    var expr;
+    while (keepGoing && (expr = processTerm(ieFilter)) != null) {
+      var op;
+
+      var opStart = _peekToken.span;
+
+      switch (_peek()) {
+        case TokenKind.SLASH:
+          op = new OperatorSlash(_makeSpan(opStart));
+          break;
+        case TokenKind.COMMA:
+          op = new OperatorComma(_makeSpan(opStart));
+          break;
+        case TokenKind.BACKSLASH:
+          // Backslash outside of string; detected IE8 or older signaled by \9 at
+          // end of an expression.
+          var ie8Start = _peekToken.span;
+
+          _next();
+          if (_peekKind(TokenKind.INTEGER)) {
+            var numToken = _next();
+            var value = int.parse(numToken.text);
+            if (value == 9) {
+              op = new IE8Term(_makeSpan(ie8Start));
+            } else if (isChecked) {
+              _warning(
+                  "\$value is not valid in an expression", _makeSpan(start));
+            }
+          }
+          break;
+      }
+
+      if (expr != null) {
+        if (expr is List) {
+          expr.forEach((exprItem) {
+            expressions.add(exprItem);
+          });
+        } else {
+          expressions.add(expr);
+        }
+      } else {
+        keepGoing = false;
+      }
+
+      if (op != null) {
+        expressions.add(op);
+        if (op is IE8Term) {
+          keepGoing = false;
+        } else {
+          _next();
+        }
+      }
+    }
+
+    return expressions;
+  }
+
+  static const int MAX_UNICODE = 0x10FFFF;
+
+  //  Term grammar:
+  //
+  //  term:
+  //    unary_operator?
+  //    [ term_value ]
+  //    | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor
+  //
+  //  term_value:
+  //    NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* |
+  //    TIME S* | FREQ S* | function
+  //
+  //  NUMBER:       {num}
+  //  PERCENTAGE:   {num}%
+  //  LENGTH:       {num}['px' | 'cm' | 'mm' | 'in' | 'pt' | 'pc']
+  //  EMS:          {num}'em'
+  //  EXS:          {num}'ex'
+  //  ANGLE:        {num}['deg' | 'rad' | 'grad']
+  //  TIME:         {num}['ms' | 's']
+  //  FREQ:         {num}['hz' | 'khz']
+  //  function:     IDENT '(' expr ')'
+  //
+  processTerm([bool ieFilter = false]) {
+    var start = _peekToken.span;
+    Token t; // token for term's value
+    var value; // value of term (numeric values)
+
+    var unary = "";
+    switch (_peek()) {
+      case TokenKind.HASH:
+        this._eat(TokenKind.HASH);
+        if (!_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) {
+          String hexText;
+          if (_peekKind(TokenKind.INTEGER)) {
+            String hexText1 = _peekToken.text;
+            _next();
+            if (_peekIdentifier()) {
+              hexText = '$hexText1${identifier().name}';
+            } else {
+              hexText = hexText1;
+            }
+          } else if (_peekIdentifier()) {
+            hexText = identifier().name;
+          }
+          if (hexText != null) {
+            return _parseHex(hexText, _makeSpan(start));
+          }
+        }
+
+        if (isChecked) {
+          _warning("Expected hex number", _makeSpan(start));
+        }
+        // Construct the bad hex value with a #<space>number.
+        return _parseHex(" ${processTerm().text}", _makeSpan(start));
+      case TokenKind.INTEGER:
+        t = _next();
+        value = int.parse("${unary}${t.text}");
+        break;
+      case TokenKind.DOUBLE:
+        t = _next();
+        value = double.parse("${unary}${t.text}");
+        break;
+      case TokenKind.SINGLE_QUOTE:
+        value = processQuotedString(false);
+        value = "'${_escapeString(value, single: true)}'";
+        return new LiteralTerm(value, value, _makeSpan(start));
+      case TokenKind.DOUBLE_QUOTE:
+        value = processQuotedString(false);
+        value = '"${_escapeString(value)}"';
+        return new LiteralTerm(value, value, _makeSpan(start));
+      case TokenKind.LPAREN:
+        _next();
+
+        GroupTerm group = new GroupTerm(_makeSpan(start));
+
+        var term;
+        do {
+          term = processTerm();
+          if (term != null && term is LiteralTerm) {
+            group.add(term);
+          }
+        } while (term != null &&
+            !_maybeEat(TokenKind.RPAREN) &&
+            !isPrematureEndOfFile());
+
+        return group;
+      case TokenKind.LBRACK:
+        _next();
+
+        var term = processTerm();
+        if (!(term is NumberTerm)) {
+          _error('Expecting a positive number', _makeSpan(start));
+        }
+
+        _eat(TokenKind.RBRACK);
+
+        return new ItemTerm(term.value, term.text, _makeSpan(start));
+      case TokenKind.IDENTIFIER:
+        var nameValue = identifier(); // Snarf up the ident we'll remap, maybe.
+
+        if (!ieFilter && _maybeEat(TokenKind.LPAREN)) {
+          // FUNCTION
+          return processFunction(nameValue);
+        }
+        if (ieFilter) {
+          if (_maybeEat(TokenKind.COLON) &&
+              nameValue.name.toLowerCase() == 'progid') {
+            // IE filter:progid:
+            return processIEFilter(start);
+          } else {
+            // Handle filter:<name> where name is any filter e.g., alpha, chroma,
+            // Wave, blur, etc.
+            return processIEFilter(start);
+          }
+        }
+
+        // TODO(terry): Need to have a list of known identifiers today only
+        //              'from' is special.
+        if (nameValue.name == 'from') {
+          return new LiteralTerm(nameValue, nameValue.name, _makeSpan(start));
+        }
+
+        // What kind of identifier is it, named color?
+        var colorEntry = TokenKind.matchColorName(nameValue.name);
+        if (colorEntry == null) {
+          if (isChecked) {
+            var propName = nameValue.name;
+            var errMsg = TokenKind.isPredefinedName(propName)
+                ? "Improper use of property value ${propName}"
+                : "Unknown property value ${propName}";
+            _warning(errMsg, _makeSpan(start));
+          }
+          return new LiteralTerm(nameValue, nameValue.name, _makeSpan(start));
+        }
+
+        // Yes, process the color as an RGB value.
+        var rgbColor =
+            TokenKind.decimalToHex(TokenKind.colorValue(colorEntry), 6);
+        return _parseHex(rgbColor, _makeSpan(start));
+      case TokenKind.UNICODE_RANGE:
+        var first;
+        var second;
+        var firstNumber;
+        var secondNumber;
+        _eat(TokenKind.UNICODE_RANGE, unicodeRange: true);
+        if (_maybeEat(TokenKind.HEX_INTEGER, unicodeRange: true)) {
+          first = _previousToken.text;
+          firstNumber = int.parse('0x$first');
+          if (firstNumber > MAX_UNICODE) {
+            _error("unicode range must be less than 10FFFF", _makeSpan(start));
+          }
+          if (_maybeEat(TokenKind.MINUS, unicodeRange: true)) {
+            if (_maybeEat(TokenKind.HEX_INTEGER, unicodeRange: true)) {
+              second = _previousToken.text;
+              secondNumber = int.parse('0x$second');
+              if (secondNumber > MAX_UNICODE) {
+                _error(
+                    "unicode range must be less than 10FFFF", _makeSpan(start));
+              }
+              if (firstNumber > secondNumber) {
+                _error("unicode first range can not be greater than last",
+                    _makeSpan(start));
+              }
+            }
+          }
+        } else if (_maybeEat(TokenKind.HEX_RANGE, unicodeRange: true)) {
+          first = _previousToken.text;
+        }
+
+        return new UnicodeRangeTerm(first, second, _makeSpan(start));
+      case TokenKind.AT:
+        if (messages.options.lessSupport) {
+          _next();
+
+          var expr = processExpr();
+          if (isChecked && expr.expressions.length > 1) {
+            _error("only @name for Less syntax", _peekToken.span);
+          }
+
+          var param = expr.expressions[0];
+          var varUsage = new VarUsage(param.text, [], _makeSpan(start));
+          expr.expressions[0] = varUsage;
+          return expr.expressions;
+        }
+        break;
+    }
+
+    return processDimension(t, value, _makeSpan(start));
+  }
+
+  /** Process all dimension units. */
+  LiteralTerm processDimension(Token t, var value, SourceSpan span) {
+    LiteralTerm term;
+    var unitType = this._peek();
+
+    switch (unitType) {
+      case TokenKind.UNIT_EM:
+        term = new EmTerm(value, t.text, span);
+        _next(); // Skip the unit
+        break;
+      case TokenKind.UNIT_EX:
+        term = new ExTerm(value, t.text, span);
+        _next(); // Skip the unit
+        break;
+      case TokenKind.UNIT_LENGTH_PX:
+      case TokenKind.UNIT_LENGTH_CM:
+      case TokenKind.UNIT_LENGTH_MM:
+      case TokenKind.UNIT_LENGTH_IN:
+      case TokenKind.UNIT_LENGTH_PT:
+      case TokenKind.UNIT_LENGTH_PC:
+        term = new LengthTerm(value, t.text, span, unitType);
+        _next(); // Skip the unit
+        break;
+      case TokenKind.UNIT_ANGLE_DEG:
+      case TokenKind.UNIT_ANGLE_RAD:
+      case TokenKind.UNIT_ANGLE_GRAD:
+      case TokenKind.UNIT_ANGLE_TURN:
+        term = new AngleTerm(value, t.text, span, unitType);
+        _next(); // Skip the unit
+        break;
+      case TokenKind.UNIT_TIME_MS:
+      case TokenKind.UNIT_TIME_S:
+        term = new TimeTerm(value, t.text, span, unitType);
+        _next(); // Skip the unit
+        break;
+      case TokenKind.UNIT_FREQ_HZ:
+      case TokenKind.UNIT_FREQ_KHZ:
+        term = new FreqTerm(value, t.text, span, unitType);
+        _next(); // Skip the unit
+        break;
+      case TokenKind.PERCENT:
+        term = new PercentageTerm(value, t.text, span);
+        _next(); // Skip the %
+        break;
+      case TokenKind.UNIT_FRACTION:
+        term = new FractionTerm(value, t.text, span);
+        _next(); // Skip the unit
+        break;
+      case TokenKind.UNIT_RESOLUTION_DPI:
+      case TokenKind.UNIT_RESOLUTION_DPCM:
+      case TokenKind.UNIT_RESOLUTION_DPPX:
+        term = new ResolutionTerm(value, t.text, span, unitType);
+        _next(); // Skip the unit
+        break;
+      case TokenKind.UNIT_CH:
+        term = new ChTerm(value, t.text, span, unitType);
+        _next(); // Skip the unit
+        break;
+      case TokenKind.UNIT_REM:
+        term = new RemTerm(value, t.text, span, unitType);
+        _next(); // Skip the unit
+        break;
+      case TokenKind.UNIT_VIEWPORT_VW:
+      case TokenKind.UNIT_VIEWPORT_VH:
+      case TokenKind.UNIT_VIEWPORT_VMIN:
+      case TokenKind.UNIT_VIEWPORT_VMAX:
+        term = new ViewportTerm(value, t.text, span, unitType);
+        _next(); // Skip the unit
+        break;
+      default:
+        if (value != null && t != null) {
+          term = (value is Identifier)
+              ? new LiteralTerm(value, value.name, span)
+              : new NumberTerm(value, t.text, span);
+        }
+        break;
+    }
+
+    return term;
+  }
+
+  String processQuotedString([bool urlString = false]) {
+    var start = _peekToken.span;
+
+    // URI term sucks up everything inside of quotes(' or ") or between parens
+    var stopToken = urlString ? TokenKind.RPAREN : -1;
+
+    // Note: disable skipping whitespace tokens inside a string.
+    // TODO(jmesserly): the layering here feels wrong.
+    var skipWhitespace = tokenizer._skipWhitespace;
+    tokenizer._skipWhitespace = false;
+
+    switch (_peek()) {
+      case TokenKind.SINGLE_QUOTE:
+        stopToken = TokenKind.SINGLE_QUOTE;
+        _next(); // Skip the SINGLE_QUOTE.
+        start = _peekToken.span;
+        break;
+      case TokenKind.DOUBLE_QUOTE:
+        stopToken = TokenKind.DOUBLE_QUOTE;
+        _next(); // Skip the DOUBLE_QUOTE.
+        start = _peekToken.span;
+        break;
+      default:
+        if (urlString) {
+          if (_peek() == TokenKind.LPAREN) {
+            _next(); // Skip the LPAREN.
+            start = _peekToken.span;
+          }
+          stopToken = TokenKind.RPAREN;
+        } else {
+          _error('unexpected string', _makeSpan(start));
+        }
+        break;
+    }
+
+    // Gobble up everything until we hit our stop token.
+    var stringValue = new StringBuffer();
+    while (_peek() != stopToken && _peek() != TokenKind.END_OF_FILE) {
+      stringValue.write(_next().text);
+    }
+
+    tokenizer._skipWhitespace = skipWhitespace;
+
+    // All characters between quotes is the string.
+    if (stopToken != TokenKind.RPAREN) {
+      _next(); // Skip the SINGLE_QUOTE or DOUBLE_QUOTE;
+    }
+
+    return stringValue.toString();
+  }
+
+  // TODO(terry): Should probably understand IE's non-standard filter syntax to
+  //              fully support calc, var(), etc.
+  /**
+   * IE's filter property breaks CSS value parsing.  IE's format can be:
+   *
+   *    filter: progid:DXImageTransform.MS.gradient(Type=0, Color='#9d8b83');
+   *
+   * We'll just parse everything after the 'progid:' look for the left paren
+   * then parse to the right paren ignoring everything in between.
+   */
+  processIEFilter(FileSpan startAfterProgidColon) {
+    var parens = 0;
+
+    while (_peek() != TokenKind.END_OF_FILE) {
+      switch (_peek()) {
+        case TokenKind.LPAREN:
+          _eat(TokenKind.LPAREN);
+          parens++;
+          break;
+        case TokenKind.RPAREN:
+          _eat(TokenKind.RPAREN);
+          if (--parens == 0) {
+            var tok = tokenizer.makeIEFilter(
+                startAfterProgidColon.start.offset, _peekToken.start);
+            return new LiteralTerm(tok.text, tok.text, tok.span);
+          }
+          break;
+        default:
+          _eat(_peek());
+      }
+    }
+  }
+
+  //  Function grammar:
+  //
+  //  function:     IDENT '(' expr ')'
+  //
+  processFunction(Identifier func) {
+    var start = _peekToken.span;
+
+    var name = func.name;
+
+    switch (name) {
+      case 'url':
+        // URI term sucks up everything inside of quotes(' or ") or between parens
+        var urlParam = processQuotedString(true);
+
+        // TODO(terry): Better error messge and checking for mismatched quotes.
+        if (_peek() == TokenKind.END_OF_FILE) {
+          _error("problem parsing URI", _peekToken.span);
+        }
+
+        if (_peek() == TokenKind.RPAREN) {
+          _next();
+        }
+
+        return new UriTerm(urlParam, _makeSpan(start));
+      case 'calc':
+        // TODO(terry): Implement expression handling...
+        break;
+      case 'var':
+        // TODO(terry): Consider handling var in IE specific filter/progid.  This
+        //              will require parsing entire IE specific syntax e.g.,
+        //              param = value or progid:com_id, etc. for example:
+        //
+        //    var-blur: Blur(Add = 0, Direction = 225, Strength = 10);
+        //    var-gradient: progid:DXImageTransform.Microsoft.gradient"
+        //      (GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
+        var expr = processExpr();
+        if (!_maybeEat(TokenKind.RPAREN)) {
+          _error("problem parsing var expected ), ", _peekToken.span);
+        }
+        if (isChecked &&
+            expr.expressions.where((e) => e is OperatorComma).length > 1) {
+          _error("too many parameters to var()", _peekToken.span);
+        }
+
+        var paramName = expr.expressions[0].text;
+
+        // [0] - var name, [1] - OperatorComma, [2] - default value.
+        var defaultValues =
+            expr.expressions.length >= 3 ? expr.expressions.sublist(2) : [];
+        return new VarUsage(paramName, defaultValues, _makeSpan(start));
+      default:
+        var expr = processExpr();
+        if (!_maybeEat(TokenKind.RPAREN)) {
+          _error("problem parsing function expected ), ", _peekToken.span);
+        }
+
+        return new FunctionTerm(name, name, expr, _makeSpan(start));
+    }
+
+    return null;
+  }
+
+  Identifier identifier() {
+    var tok = _next();
+
+    if (!TokenKind.isIdentifier(tok.kind) &&
+        !TokenKind.isKindIdentifier(tok.kind)) {
+      if (isChecked) {
+        _warning('expected identifier, but found $tok', tok.span);
+      }
+      return new Identifier("", _makeSpan(tok.span));
+    }
+
+    return new Identifier(tok.text, _makeSpan(tok.span));
+  }
+
+  // TODO(terry): Move this to base <= 36 and into shared code.
+  static int _hexDigit(int c) {
+    if (c >= 48 /*0*/ && c <= 57 /*9*/) {
+      return c - 48;
+    } else if (c >= 97 /*a*/ && c <= 102 /*f*/) {
+      return c - 87;
+    } else if (c >= 65 /*A*/ && c <= 70 /*F*/) {
+      return c - 55;
+    } else {
+      return -1;
+    }
+  }
+
+  HexColorTerm _parseHex(String hexText, SourceSpan span) {
+    var hexValue = 0;
+
+    for (var i = 0; i < hexText.length; i++) {
+      var digit = _hexDigit(hexText.codeUnitAt(i));
+      if (digit < 0) {
+        _warning('Bad hex number', span);
+        return new HexColorTerm(new BAD_HEX_VALUE(), hexText, span);
+      }
+      hexValue = (hexValue << 4) + digit;
+    }
+
+    // Make 3 character hex value #RRGGBB => #RGB iff:
+    // high/low nibble of RR is the same, high/low nibble of GG is the same and
+    // high/low nibble of BB is the same.
+    if (hexText.length == 6 &&
+        hexText[0] == hexText[1] &&
+        hexText[2] == hexText[3] &&
+        hexText[4] == hexText[5]) {
+      hexText = '${hexText[0]}${hexText[2]}${hexText[4]}';
+    } else if (hexText.length == 4 &&
+        hexText[0] == hexText[1] &&
+        hexText[2] == hexText[3]) {
+      hexText = '${hexText[0]}${hexText[2]}';
+    } else if (hexText.length == 2 && hexText[0] == hexText[1]) {
+      hexText = '${hexText[0]}';
+    }
+    return new HexColorTerm(hexValue, hexText, span);
+  }
+}
+
+class ExpressionsProcessor {
+  final Expressions _exprs;
+  int _index = 0;
+
+  ExpressionsProcessor(this._exprs);
+
+  // TODO(terry): Only handles ##px unit.
+  FontExpression processFontSize() {
+    /* font-size[/line-height]
+     *
+     * Possible size values:
+     *   xx-small
+     *   small
+     *   medium [default]
+     *   large
+     *   x-large
+     *   xx-large
+     *   smaller
+     *   larger
+     *   ##length in px, pt, etc.
+     *   ##%, percent of parent elem's font-size
+     *   inherit
+     */
+    LengthTerm size;
+    LineHeight lineHt;
+    var nextIsLineHeight = false;
+    for (; _index < _exprs.expressions.length; _index++) {
+      var expr = _exprs.expressions[_index];
+      if (size == null && expr is LengthTerm) {
+        // font-size part.
+        size = expr;
+      } else if (size != null) {
+        if (expr is OperatorSlash) {
+          // LineHeight could follow?
+          nextIsLineHeight = true;
+        } else if (nextIsLineHeight && expr is LengthTerm) {
+          assert(expr.unit == TokenKind.UNIT_LENGTH_PX);
+          lineHt = new LineHeight(expr.value, inPixels: true);
+          nextIsLineHeight = false;
+          _index++;
+          break;
+        } else {
+          break;
+        }
+      } else {
+        break;
+      }
+    }
+
+    return new FontExpression(_exprs.span, size: size, lineHeight: lineHt);
+  }
+
+  FontExpression processFontFamily() {
+    var family = <String>[];
+
+    /* Possible family values:
+     * font-family: arial, Times new roman ,Lucida Sans Unicode,Courier;
+     * font-family: "Times New Roman", arial, Lucida Sans Unicode, Courier;
+     */
+    var moreFamilies = false;
+
+    for (; _index < _exprs.expressions.length; _index++) {
+      Expression expr = _exprs.expressions[_index];
+      if (expr is LiteralTerm) {
+        if (family.length == 0 || moreFamilies) {
+          // It's font-family now.
+          family.add(expr.toString());
+          moreFamilies = false;
+        } else if (isChecked) {
+          messages.warning('Only font-family can be a list', _exprs.span);
+        }
+      } else if (expr is OperatorComma && family.length > 0) {
+        moreFamilies = true;
+      } else {
+        break;
+      }
+    }
+
+    return new FontExpression(_exprs.span, family: family);
+  }
+
+  FontExpression processFont() {
+    // Process all parts of the font expression.
+    FontExpression fontSize;
+    FontExpression fontFamily;
+    for (; _index < _exprs.expressions.length; _index++) {
+      // Order is font-size font-family
+      if (fontSize == null) {
+        fontSize = processFontSize();
+      }
+      if (fontFamily == null) {
+        fontFamily = processFontFamily();
+      }
+      //TODO(terry): Handle font-weight, font-style, and font-variant. See
+      //               https://github.com/dart-lang/csslib/issues/3
+      //               https://github.com/dart-lang/csslib/issues/4
+      //               https://github.com/dart-lang/csslib/issues/5
+    }
+
+    return new FontExpression(_exprs.span,
+        size: fontSize.font.size,
+        lineHeight: fontSize.font.lineHeight,
+        family: fontFamily.font.family);
+  }
+}
+
+/**
+ * Escapes [text] for use in a CSS string.
+ * [single] specifies single quote `'` vs double quote `"`.
+ */
+String _escapeString(String text, {bool single: false}) {
+  StringBuffer result = null;
+
+  for (int i = 0; i < text.length; i++) {
+    var code = text.codeUnitAt(i);
+    String replace = null;
+    switch (code) {
+      case 34 /*'"'*/ :
+        if (!single) replace = r'\"';
+        break;
+      case 39 /*"'"*/ :
+        if (single) replace = r"\'";
+        break;
+    }
+
+    if (replace != null && result == null) {
+      result = new StringBuffer(text.substring(0, i));
+    }
+
+    if (result != null) result.write(replace != null ? replace : text[i]);
+  }
+
+  return result == null ? text : result.toString();
+}
diff --git a/csslib/lib/src/analyzer.dart b/csslib/lib/src/analyzer.dart
new file mode 100644
index 0000000..4fdd833
--- /dev/null
+++ b/csslib/lib/src/analyzer.dart
@@ -0,0 +1,1015 @@
+// Copyright (c) 2012, 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.
+
+part of csslib.parser;
+
+// TODO(terry): Add optimizing phase to remove duplicated selectors in the same
+//              selector group (e.g., .btn, .btn { color: red; }).  Also, look
+//              at simplifying selectors expressions too (much harder).
+// TODO(terry): Detect invalid directive usage.  All @imports must occur before
+//              all rules other than @charset directive.  Any @import directive
+//              after any non @charset or @import directive are ignored. e.g.,
+//                  @import "a.css";
+//                  div { color: red; }
+//                  @import "b.css";
+//              becomes:
+//                  @import "a.css";
+//                  div { color: red; }
+// <http://www.w3.org/TR/css3-syntax/#at-rules>
+
+/**
+ * Analysis phase will validate/fixup any new CSS feature or any SASS style
+ * feature.
+ */
+class Analyzer {
+  final List<StyleSheet> _styleSheets;
+  final Messages _messages;
+
+  Analyzer(this._styleSheets, this._messages);
+
+  // TODO(terry): Currently each feature walks the AST each time.  Once we have
+  //               our complete feature set consider benchmarking the cost and
+  //               possibly combine in one walk.
+  void run() {
+    // Expand top-level @include.
+    _styleSheets.forEach(
+        (styleSheet) => TopLevelIncludes.expand(_messages, _styleSheets));
+
+    // Expand @include in declarations.
+    _styleSheets.forEach(
+        (styleSheet) => DeclarationIncludes.expand(_messages, _styleSheets));
+
+    // Remove all @mixin and @include
+    _styleSheets.forEach((styleSheet) => MixinsAndIncludes.remove(styleSheet));
+
+    // Expand any nested selectors using selector desendant combinator to
+    // signal CSS inheritance notation.
+    _styleSheets.forEach((styleSheet) => new ExpandNestedSelectors()
+      ..visitStyleSheet(styleSheet)
+      ..flatten(styleSheet));
+
+    // Expand any @extend.
+    _styleSheets.forEach((styleSheet) {
+      var allExtends = new AllExtends()..visitStyleSheet(styleSheet);
+      new InheritExtends(_messages, allExtends)..visitStyleSheet(styleSheet);
+    });
+  }
+}
+
+/**
+ * Traverse all rulesets looking for nested ones.  If a ruleset is in a
+ * declaration group (implies nested selector) then generate new ruleset(s) at
+ * level 0 of CSS using selector inheritance syntax (flattens the nesting).
+ *
+ * How the AST works for a rule [RuleSet] and nested rules.  First of all a
+ * CSS rule [RuleSet] consist of a selector and a declaration e.g.,
+ *
+ *    selector {
+ *      declaration
+ *    }
+ *
+ * AST structure of a [RuleSet] is:
+ *
+ *    RuleSet
+ *       SelectorGroup
+ *         List<Selector>
+ *            List<SimpleSelectorSequence>
+ *              Combinator      // +, >, ~, DESCENDENT, or NONE
+ *              SimpleSelector  // class, id, element, namespace, attribute
+ *        DeclarationGroup
+ *          List                // Declaration or RuleSet
+ *
+ * For the simple rule:
+ *
+ *    div + span { color: red; }
+ *
+ * the AST [RuleSet] is:
+ *
+ *    RuleSet
+ *       SelectorGroup
+ *         List<Selector>
+ *          [0]
+ *            List<SimpleSelectorSequence>
+ *              [0] Combinator = COMBINATOR_NONE
+ *                  ElementSelector (name = div)
+ *              [1] Combinator = COMBINATOR_PLUS
+ *                  ElementSelector (name = span)
+ *        DeclarationGroup
+ *          List                // Declarations or RuleSets
+ *            [0]
+ *              Declaration (property = color, expression = red)
+ *
+ * Usually a SelectorGroup contains 1 Selector.  Consider the selectors:
+ *
+ *    div { color: red; }
+ *    a { color: red; }
+ *
+ * are equivalent to
+ *
+ *    div, a { color : red; }
+ *
+ * In the above the RuleSet would have a SelectorGroup with 2 selectors e.g.,
+ *
+ *    RuleSet
+ *       SelectorGroup
+ *         List<Selector>
+ *          [0]
+ *            List<SimpleSelectorSequence>
+ *              [0] Combinator = COMBINATOR_NONE
+ *                  ElementSelector (name = div)
+ *          [1]
+ *            List<SimpleSelectorSequence>
+ *              [0] Combinator = COMBINATOR_NONE
+ *                  ElementSelector (name = a)
+ *        DeclarationGroup
+ *          List                // Declarations or RuleSets
+ *            [0]
+ *              Declaration (property = color, expression = red)
+ *
+ * For a nested rule e.g.,
+ *
+ *    div {
+ *      color : blue;
+ *      a { color : red; }
+ *    }
+ *
+ * Would map to the follow CSS rules:
+ *
+ *    div { color: blue; }
+ *    div a { color: red; }
+ *
+ * The AST for the former nested rule is:
+ *
+ *    RuleSet
+ *       SelectorGroup
+ *         List<Selector>
+ *          [0]
+ *            List<SimpleSelectorSequence>
+ *              [0] Combinator = COMBINATOR_NONE
+ *                  ElementSelector (name = div)
+ *        DeclarationGroup
+ *          List                // Declarations or RuleSets
+ *            [0]
+ *              Declaration (property = color, expression = blue)
+ *            [1]
+ *              RuleSet
+ *                SelectorGroup
+ *                  List<Selector>
+ *                    [0]
+ *                      List<SimpleSelectorSequence>
+ *                        [0] Combinator = COMBINATOR_NONE
+ *                            ElementSelector (name = a)
+ *                DeclarationGroup
+ *                  List                // Declarations or RuleSets
+ *                    [0]
+ *                      Declaration (property = color, expression = red)
+ *
+ * Nested rules is a terse mechanism to describe CSS inheritance.  The analyzer
+ * will flatten and expand the nested rules to it's flatten strucure.  Using the
+ * all parent [RuleSets] (selector expressions) and applying each nested
+ * [RuleSet] to the list of [Selectors] in a [SelectorGroup].
+ *
+ * Then result is a style sheet where all nested rules have been flatten and
+ * expanded.
+ */
+class ExpandNestedSelectors extends Visitor {
+  /** Parent [RuleSet] if a nested rule otherwise [:null:]. */
+  RuleSet _parentRuleSet;
+
+  /** Top-most rule if nested rules. */
+  SelectorGroup _topLevelSelectorGroup;
+
+  /** SelectorGroup at each nesting level. */
+  SelectorGroup _nestedSelectorGroup;
+
+  /** Declaration (sans the nested selectors). */
+  DeclarationGroup _flatDeclarationGroup;
+
+  /** Each nested selector get's a flatten RuleSet. */
+  List<RuleSet> _expandedRuleSets = [];
+
+  /** Maping of a nested rule set to the fully expanded list of RuleSet(s). */
+  final Map<RuleSet, List<RuleSet>> _expansions = new Map();
+
+  void visitRuleSet(RuleSet node) {
+    final oldParent = _parentRuleSet;
+
+    var oldNestedSelectorGroups = _nestedSelectorGroup;
+
+    if (_nestedSelectorGroup == null) {
+      // Create top-level selector (may have nested rules).
+      final newSelectors = node.selectorGroup.selectors.toList();
+      _topLevelSelectorGroup = new SelectorGroup(newSelectors, node.span);
+      _nestedSelectorGroup = _topLevelSelectorGroup;
+    } else {
+      // Generate new selector groups from the nested rules.
+      _nestedSelectorGroup = _mergeToFlatten(node);
+    }
+
+    _parentRuleSet = node;
+
+    super.visitRuleSet(node);
+
+    _parentRuleSet = oldParent;
+
+    // Remove nested rules; they're all flatten and in the _expandedRuleSets.
+    node.declarationGroup.declarations
+        .removeWhere((declaration) => declaration is RuleSet);
+
+    _nestedSelectorGroup = oldNestedSelectorGroups;
+
+    // If any expandedRuleSets and we're back at the top-level rule set then
+    // there were nested rule set(s).
+    if (_parentRuleSet == null) {
+      if (!_expandedRuleSets.isEmpty) {
+        // Remember ruleset to replace with these flattened rulesets.
+        _expansions[node] = _expandedRuleSets;
+        _expandedRuleSets = [];
+      }
+      assert(_flatDeclarationGroup == null);
+      assert(_nestedSelectorGroup == null);
+    }
+  }
+
+  /**
+   * Build up the list of all inherited sequences from the parent selector
+   * [node] is the current nested selector and it's parent is the last entry in
+   * the [_nestedSelectorGroup].
+   */
+  SelectorGroup _mergeToFlatten(RuleSet node) {
+    // Create a new SelectorGroup for this nesting level.
+    var nestedSelectors = _nestedSelectorGroup.selectors;
+    var selectors = node.selectorGroup.selectors;
+
+    // Create a merged set of previous parent selectors and current selectors.
+    var newSelectors = [];
+    for (Selector selector in selectors) {
+      for (Selector nestedSelector in nestedSelectors) {
+        var seq = _mergeNestedSelector(nestedSelector.simpleSelectorSequences,
+            selector.simpleSelectorSequences);
+        newSelectors.add(new Selector(seq, node.span));
+      }
+    }
+
+    return new SelectorGroup(newSelectors, node.span);
+  }
+
+  /**
+   * Merge the nested selector sequences [current] to the [parent] sequences or
+   * substitue any & with the parent selector.
+   */
+  List<SimpleSelectorSequence> _mergeNestedSelector(
+      List<SimpleSelectorSequence> parent,
+      List<SimpleSelectorSequence> current) {
+
+    // If any & operator then the parent selector will be substituted otherwise
+    // the parent selector is pre-pended to the current selector.
+    var hasThis = current.any((s) => s.simpleSelector.isThis);
+
+    var newSequence = [];
+
+    if (!hasThis) {
+      // If no & in the sector group then prefix with the parent selector.
+      newSequence.addAll(parent);
+      newSequence.addAll(_convertToDescendentSequence(current));
+    } else {
+      for (var sequence in current) {
+        if (sequence.simpleSelector.isThis) {
+          // Substitue the & with the parent selector and only use a combinator
+          // descendant if & is prefix by a sequence with an empty name e.g.,
+          // "... + &", "&", "... ~ &", etc.
+          var hasPrefix = !newSequence.isEmpty &&
+              !newSequence.last.simpleSelector.name.isEmpty;
+          newSequence.addAll(
+              hasPrefix ? _convertToDescendentSequence(parent) : parent);
+        } else {
+          newSequence.add(sequence);
+        }
+      }
+    }
+
+    return newSequence;
+  }
+
+  /**
+   * Return selector sequences with first sequence combinator being a
+   * descendant.  Used for nested selectors when the parent selector needs to
+   * be prefixed to a nested selector or to substitute the this (&) with the
+   * parent selector.
+   */
+  List<SimpleSelectorSequence> _convertToDescendentSequence(
+      List<SimpleSelectorSequence> sequences) {
+    if (sequences.isEmpty) return sequences;
+
+    var newSequences = [];
+    var first = sequences.first;
+    newSequences.add(new SimpleSelectorSequence(
+        first.simpleSelector, first.span, TokenKind.COMBINATOR_DESCENDANT));
+    newSequences.addAll(sequences.skip(1));
+
+    return newSequences;
+  }
+
+  void visitDeclarationGroup(DeclarationGroup node) {
+    var span = node.span;
+
+    var currentGroup = new DeclarationGroup([], span);
+
+    var oldGroup = _flatDeclarationGroup;
+    _flatDeclarationGroup = currentGroup;
+
+    var expandedLength = _expandedRuleSets.length;
+
+    super.visitDeclarationGroup(node);
+
+    // We're done with the group.
+    _flatDeclarationGroup = oldGroup;
+
+    // No nested rule to process it's a top-level rule.
+    if (_nestedSelectorGroup == _topLevelSelectorGroup) return;
+
+    // If flatten selector's declaration is empty skip this selector, no need
+    // to emit an empty nested selector.
+    if (currentGroup.declarations.isEmpty) return;
+
+    var selectorGroup = _nestedSelectorGroup;
+
+    // Build new rule set from the nested selectors and declarations.
+    var newRuleSet = new RuleSet(selectorGroup, currentGroup, span);
+
+    // Place in order so outer-most rule is first.
+    if (expandedLength == _expandedRuleSets.length) {
+      _expandedRuleSets.add(newRuleSet);
+    } else {
+      _expandedRuleSets.insert(expandedLength, newRuleSet);
+    }
+  }
+
+  // Record all declarations in a nested selector (Declaration, VarDefinition
+  // and MarginGroup) but not the nested rule in the Declaration.
+
+  void visitDeclaration(Declaration node) {
+    if (_parentRuleSet != null) {
+      _flatDeclarationGroup.declarations.add(node);
+    }
+    super.visitDeclaration(node);
+  }
+
+  void visitVarDefinition(VarDefinition node) {
+    if (_parentRuleSet != null) {
+      _flatDeclarationGroup.declarations.add(node);
+    }
+    super.visitVarDefinition(node);
+  }
+
+  void visitExtendDeclaration(ExtendDeclaration node) {
+    if (_parentRuleSet != null) {
+      _flatDeclarationGroup.declarations.add(node);
+    }
+    super.visitExtendDeclaration(node);
+  }
+
+  void visitMarginGroup(MarginGroup node) {
+    if (_parentRuleSet != null) {
+      _flatDeclarationGroup.declarations.add(node);
+    }
+    super.visitMarginGroup(node);
+  }
+
+  /**
+   * Replace the rule set that contains nested rules with the flatten rule sets.
+   */
+  void flatten(StyleSheet styleSheet) {
+    // TODO(terry): Iterate over topLevels instead of _expansions it's already
+    //              a map (this maybe quadratic).
+    _expansions.forEach((RuleSet ruleSet, List<RuleSet> newRules) {
+      var index = styleSheet.topLevels.indexOf(ruleSet);
+      if (index == -1) {
+        // Check any @media directives for nested rules and replace them.
+        var found = _MediaRulesReplacer.replace(styleSheet, ruleSet, newRules);
+        assert(found);
+      } else {
+        styleSheet.topLevels.insertAll(index + 1, newRules);
+      }
+    });
+    _expansions.clear();
+  }
+}
+
+class _MediaRulesReplacer extends Visitor {
+  RuleSet _ruleSet;
+  List<RuleSet> _newRules;
+  bool _foundAndReplaced = false;
+
+  /**
+   * Look for the [ruleSet] inside of an @media directive; if found then replace
+   * with the [newRules].  If [ruleSet] is found and replaced return true.
+   */
+  static bool replace(
+      StyleSheet styleSheet, RuleSet ruleSet, List<RuleSet> newRules) {
+    var visitor = new _MediaRulesReplacer(ruleSet, newRules);
+    visitor.visitStyleSheet(styleSheet);
+    return visitor._foundAndReplaced;
+  }
+
+  _MediaRulesReplacer(this._ruleSet, this._newRules);
+
+  visitMediaDirective(MediaDirective node) {
+    var index = node.rulesets.indexOf(_ruleSet);
+    if (index != -1) {
+      node.rulesets.insertAll(index + 1, _newRules);
+      _foundAndReplaced = true;
+    }
+  }
+}
+
+/**
+ * Expand all @include at the top-level the ruleset(s) associated with the
+ * mixin.
+ */
+class TopLevelIncludes extends Visitor {
+  StyleSheet _styleSheet;
+  final Messages _messages;
+  /** Map of variable name key to it's definition. */
+  final Map<String, MixinDefinition> map = new Map<String, MixinDefinition>();
+  MixinDefinition currDef;
+
+  static void expand(Messages messages, List<StyleSheet> styleSheets) {
+    new TopLevelIncludes(messages, styleSheets);
+  }
+
+  bool _anyRulesets(MixinRulesetDirective def) =>
+      def.rulesets.any((rule) => rule is RuleSet);
+
+  TopLevelIncludes(this._messages, List<StyleSheet> styleSheets) {
+    for (var styleSheet in styleSheets) {
+      visitTree(styleSheet);
+    }
+  }
+
+  void visitStyleSheet(StyleSheet ss) {
+    _styleSheet = ss;
+    super.visitStyleSheet(ss);
+    _styleSheet = null;
+  }
+
+  void visitIncludeDirective(IncludeDirective node) {
+    if (map.containsKey(node.name)) {
+      var mixinDef = map[node.name];
+      if (mixinDef is MixinRulesetDirective) {
+        _TopLevelIncludeReplacer.replace(
+            _messages, _styleSheet, node, mixinDef.rulesets);
+      } else if (currDef is MixinRulesetDirective && _anyRulesets(currDef)) {
+        // currDef is MixinRulesetDirective
+        MixinRulesetDirective mixinRuleset = currDef;
+        int index = mixinRuleset.rulesets.indexOf(node as dynamic);
+        mixinRuleset.rulesets.replaceRange(index, index + 1, [new NoOp()]);
+        _messages.warning(
+            'Using declaration mixin ${node.name} as top-level mixin',
+            node.span);
+      }
+    } else {
+      if (currDef is MixinRulesetDirective) {
+        MixinRulesetDirective rulesetDirect = currDef as MixinRulesetDirective;
+        var index = 0;
+        rulesetDirect.rulesets.forEach((entry) {
+          if (entry == node) {
+            rulesetDirect.rulesets.replaceRange(index, index + 1, [new NoOp()]);
+            _messages.warning('Undefined mixin ${node.name}', node.span);
+          }
+          index++;
+        });
+      }
+    }
+    super.visitIncludeDirective(node);
+  }
+
+  void visitMixinRulesetDirective(MixinRulesetDirective node) {
+    currDef = node;
+
+    super.visitMixinRulesetDirective(node);
+
+    // Replace with latest top-level mixin definition.
+    map[node.name] = node;
+    currDef = null;
+  }
+
+  void visitMixinDeclarationDirective(MixinDeclarationDirective node) {
+    currDef = node;
+
+    super.visitMixinDeclarationDirective(node);
+
+    // Replace with latest mixin definition.
+    map[node.name] = node;
+    currDef = null;
+  }
+}
+
+/** @include as a top-level with ruleset(s). */
+class _TopLevelIncludeReplacer extends Visitor {
+  final Messages _messages;
+  final IncludeDirective _include;
+  final List<RuleSet> _newRules;
+  bool _foundAndReplaced = false;
+
+  /**
+   * Look for the [ruleSet] inside of an @media directive; if found then replace
+   * with the [newRules].  If [ruleSet] is found and replaced return true.
+   */
+  static bool replace(Messages messages, StyleSheet styleSheet,
+      IncludeDirective include, List<RuleSet> newRules) {
+    var visitor = new _TopLevelIncludeReplacer(messages, include, newRules);
+    visitor.visitStyleSheet(styleSheet);
+    return visitor._foundAndReplaced;
+  }
+
+  _TopLevelIncludeReplacer(this._messages, this._include, this._newRules);
+
+  visitStyleSheet(StyleSheet node) {
+    var index = node.topLevels.indexOf(_include);
+    if (index != -1) {
+      node.topLevels.insertAll(index + 1, _newRules);
+      node.topLevels.replaceRange(index, index + 1, [new NoOp()]);
+      _foundAndReplaced = true;
+    }
+    super.visitStyleSheet(node);
+  }
+
+  void visitMixinRulesetDirective(MixinRulesetDirective node) {
+    var index = node.rulesets.indexOf(_include as dynamic);
+    if (index != -1) {
+      node.rulesets.insertAll(index + 1, _newRules);
+      // Only the resolve the @include once.
+      node.rulesets.replaceRange(index, index + 1, [new NoOp()]);
+      _foundAndReplaced = true;
+    }
+    super.visitMixinRulesetDirective(node);
+  }
+}
+
+/**
+ * Utility function to match an include to a list of either Declarations or
+ * RuleSets, depending on type of mixin (ruleset or declaration).  The include
+ * can be an include in a declaration or an include directive (top-level).
+ */
+int _findInclude(List list, var node) {
+  IncludeDirective matchNode =
+      (node is IncludeMixinAtDeclaration) ? node.include : node;
+
+  var index = 0;
+  for (var item in list) {
+    var includeNode = (item is IncludeMixinAtDeclaration) ? item.include : item;
+    if (includeNode == matchNode) return index;
+    index++;
+  }
+  return -1;
+}
+
+/**
+ * Stamp out a mixin with the defined args substituted with the user's
+ * parameters.
+ */
+class CallMixin extends Visitor {
+  final MixinDefinition mixinDef;
+  List _definedArgs;
+  Expressions _currExpressions;
+  int _currIndex = -1;
+
+  final varUsages = new Map<String, Map<Expressions, Set<int>>>();
+
+  /** Only var defs with more than one expression (comma separated). */
+  final Map<String, VarDefinition> varDefs;
+
+  CallMixin(this.mixinDef, [this.varDefs]) {
+    if (mixinDef is MixinRulesetDirective) {
+      visitMixinRulesetDirective(mixinDef);
+    } else {
+      visitMixinDeclarationDirective(mixinDef);
+    }
+  }
+
+  /**
+   * Given a mixin's defined arguments return a cloned mixin defintion that has
+   * replaced all defined arguments with user's supplied VarUsages.
+   */
+  MixinDefinition transform(List callArgs) {
+    // TODO(terry): Handle default arguments and varArgs.
+    // Transform mixin with callArgs.
+    for (var index = 0; index < _definedArgs.length; index++) {
+      var definedArg = _definedArgs[index];
+      VarDefinition varDef;
+      if (definedArg is VarDefinition) {
+        varDef = definedArg;
+      } else if (definedArg is VarDefinitionDirective) {
+        VarDefinitionDirective varDirective = definedArg;
+        varDef = varDirective.def;
+      }
+      var callArg = callArgs[index];
+
+      // Is callArg a var definition with multi-args (expressions > 1).
+      var defArgs = _varDefsAsCallArgs(callArg);
+      if (defArgs.isNotEmpty) {
+        // Replace call args with the var def parameters.
+        callArgs.insertAll(index, defArgs);
+        callArgs.removeAt(index + defArgs.length);
+        callArg = callArgs[index];
+      }
+
+      var expressions = varUsages[varDef.definedName];
+      expressions.forEach((k, v) {
+        for (var usagesIndex in v) {
+          k.expressions.replaceRange(usagesIndex, usagesIndex + 1, callArg);
+        }
+      });
+    }
+
+    // Clone the mixin
+    return mixinDef.clone();
+  }
+
+  /** Rip apart var def with multiple parameters. */
+  List<List<TreeNode>> _varDefsAsCallArgs(var callArg) {
+    var defArgs = [];
+    if (callArg is List && callArg[0] is VarUsage) {
+      var varDef = varDefs[callArg[0].name];
+      var expressions = varDef.expression.expressions;
+      assert(expressions.length > 1);
+      for (var expr in expressions) {
+        if (expr is! OperatorComma) {
+          defArgs.add([expr]);
+        }
+      }
+    }
+    return defArgs;
+  }
+
+  void visitExpressions(Expressions node) {
+    var oldExpressions = _currExpressions;
+    var oldIndex = _currIndex;
+
+    _currExpressions = node;
+    for (_currIndex = 0; _currIndex < node.expressions.length; _currIndex++) {
+      node.expressions[_currIndex].visit(this);
+    }
+
+    _currIndex = oldIndex;
+    _currExpressions = oldExpressions;
+  }
+
+  void _addExpression(Map<Expressions, Set<int>> expressions) {
+    var indexSet = new Set<int>();
+    indexSet.add(_currIndex);
+    expressions[_currExpressions] = indexSet;
+  }
+
+  void visitVarUsage(VarUsage node) {
+    assert(_currIndex != -1);
+    assert(_currExpressions != null);
+    if (varUsages.containsKey(node.name)) {
+      Map<Expressions, Set<int>> expressions = varUsages[node.name];
+      Set<int> allIndexes = expressions[_currExpressions];
+      if (allIndexes == null) {
+        _addExpression(expressions);
+      } else {
+        allIndexes.add(_currIndex);
+      }
+    } else {
+      var newExpressions = new Map<Expressions, Set<int>>();
+      _addExpression(newExpressions);
+      varUsages[node.name] = newExpressions;
+    }
+    super.visitVarUsage(node);
+  }
+
+  void visitMixinDeclarationDirective(MixinDeclarationDirective node) {
+    _definedArgs = node.definedArgs;
+    super.visitMixinDeclarationDirective(node);
+  }
+
+  void visitMixinRulesetDirective(MixinRulesetDirective node) {
+    _definedArgs = node.definedArgs;
+    super.visitMixinRulesetDirective(node);
+  }
+}
+
+/** Expand all @include inside of a declaration associated with a mixin. */
+class DeclarationIncludes extends Visitor {
+  StyleSheet _styleSheet;
+  final Messages _messages;
+  /** Map of variable name key to it's definition. */
+  final Map<String, MixinDefinition> map = new Map<String, MixinDefinition>();
+  /** Cache of mixin called with parameters. */
+  final Map<String, CallMixin> callMap = new Map<String, CallMixin>();
+  MixinDefinition currDef;
+  DeclarationGroup currDeclGroup;
+
+  /** Var definitions with more than 1 expression. */
+  final Map<String, VarDefinition> varDefs = new Map<String, VarDefinition>();
+
+  static void expand(Messages messages, List<StyleSheet> styleSheets) {
+    new DeclarationIncludes(messages, styleSheets);
+  }
+
+  DeclarationIncludes(this._messages, List<StyleSheet> styleSheets) {
+    for (var styleSheet in styleSheets) {
+      visitTree(styleSheet);
+    }
+  }
+
+  bool _allIncludes(rulesets) =>
+      rulesets.every((rule) => rule is IncludeDirective || rule is NoOp);
+
+  CallMixin _createCallDeclMixin(MixinDefinition mixinDef) {
+    callMap.putIfAbsent(mixinDef.name,
+        () => callMap[mixinDef.name] = new CallMixin(mixinDef, varDefs));
+    return callMap[mixinDef.name];
+  }
+
+  void visitStyleSheet(StyleSheet ss) {
+    _styleSheet = ss;
+    super.visitStyleSheet(ss);
+    _styleSheet = null;
+  }
+
+  void visitDeclarationGroup(DeclarationGroup node) {
+    currDeclGroup = node;
+    super.visitDeclarationGroup(node);
+    currDeclGroup = null;
+  }
+
+  void visitIncludeMixinAtDeclaration(IncludeMixinAtDeclaration node) {
+    if (map.containsKey(node.include.name)) {
+      var mixinDef = map[node.include.name];
+
+      // Fix up any mixin that is really a Declaration but has includes.
+      if (mixinDef is MixinRulesetDirective) {
+        if (!_allIncludes(mixinDef.rulesets) && currDeclGroup != null) {
+          var index = _findInclude(currDeclGroup.declarations, node);
+          if (index != -1) {
+            currDeclGroup.declarations.replaceRange(
+                index, index + 1, [new NoOp()]);
+          }
+          _messages.warning(
+              "Using top-level mixin ${node.include.name} as a declaration",
+              node.span);
+        } else {
+          // We're a list of @include(s) inside of a mixin ruleset - convert
+          // to a list of IncludeMixinAtDeclaration(s).
+          var origRulesets = mixinDef.rulesets;
+          var rulesets = [];
+          if (origRulesets.every((ruleset) => ruleset is IncludeDirective)) {
+            origRulesets.forEach((ruleset) {
+              rulesets
+                  .add(new IncludeMixinAtDeclaration(ruleset, ruleset.span));
+            });
+            _IncludeReplacer.replace(_styleSheet, node, rulesets);
+          }
+        }
+      }
+
+      if (mixinDef.definedArgs.length > 0 && node.include.args.length > 0) {
+        var callMixin = _createCallDeclMixin(mixinDef);
+        mixinDef = callMixin.transform(node.include.args);
+      }
+
+      if (mixinDef is MixinDeclarationDirective) {
+        _IncludeReplacer.replace(
+            _styleSheet, node, mixinDef.declarations.declarations);
+      }
+    } else {
+      _messages.warning("Undefined mixin ${node.include.name}", node.span);
+    }
+
+    super.visitIncludeMixinAtDeclaration(node);
+  }
+
+  void visitIncludeDirective(IncludeDirective node) {
+    if (map.containsKey(node.name)) {
+      var mixinDef = map[node.name];
+      if (currDef is MixinDeclarationDirective &&
+          mixinDef is MixinDeclarationDirective) {
+        _IncludeReplacer.replace(
+            _styleSheet, node, mixinDef.declarations.declarations);
+      } else if (currDef is MixinDeclarationDirective) {
+        var decls =
+            (currDef as MixinDeclarationDirective).declarations.declarations;
+        var index = _findInclude(decls, node);
+        if (index != -1) {
+          decls.replaceRange(index, index + 1, [new NoOp()]);
+        }
+      }
+    }
+
+    super.visitIncludeDirective(node);
+  }
+
+  void visitMixinRulesetDirective(MixinRulesetDirective node) {
+    currDef = node;
+
+    super.visitMixinRulesetDirective(node);
+
+    // Replace with latest top-level mixin definition.
+    map[node.name] = node;
+    currDef = null;
+  }
+
+  void visitMixinDeclarationDirective(MixinDeclarationDirective node) {
+    currDef = node;
+
+    super.visitMixinDeclarationDirective(node);
+
+    // Replace with latest mixin definition.
+    map[node.name] = node;
+    currDef = null;
+  }
+
+  void visitVarDefinition(VarDefinition node) {
+    // Only record var definitions that have multiple expressions (comma
+    // separated for mixin parameter substitution.
+    var exprs = (node.expression as Expressions).expressions;
+    if (exprs.length > 1) {
+      varDefs[node.definedName] = node;
+    }
+    super.visitVarDefinition(node);
+  }
+
+  void visitVarDefinitionDirective(VarDefinitionDirective node) {
+    visitVarDefinition(node.def);
+  }
+}
+
+/** @include as a top-level with ruleset(s). */
+class _IncludeReplacer extends Visitor {
+  final _include;
+  final List<Declaration> _newDeclarations;
+  bool _foundAndReplaced = false;
+
+  /**
+   * Look for the [ruleSet] inside of a @media directive; if found then replace
+   * with the [newRules].
+   */
+  static void replace(
+      StyleSheet ss, var include, List<Declaration> newDeclarations) {
+    var visitor = new _IncludeReplacer(include, newDeclarations);
+    visitor.visitStyleSheet(ss);
+  }
+
+  _IncludeReplacer(this._include, this._newDeclarations);
+
+  void visitDeclarationGroup(DeclarationGroup node) {
+    var index = _findInclude(node.declarations, _include);
+    if (index != -1) {
+      node.declarations.insertAll(index + 1, _newDeclarations);
+      // Change @include to NoOp so it's processed only once.
+      node.declarations.replaceRange(index, index + 1, [new NoOp()]);
+      _foundAndReplaced = true;
+    }
+    super.visitDeclarationGroup(node);
+  }
+}
+
+/**
+ * Remove all @mixin and @include and any NoOp used as placeholder for @include.
+ */
+class MixinsAndIncludes extends Visitor {
+  static void remove(StyleSheet styleSheet) {
+    new MixinsAndIncludes()..visitStyleSheet(styleSheet);
+  }
+
+  bool _nodesToRemove(node) =>
+      node is IncludeDirective || node is MixinDefinition || node is NoOp;
+
+  void visitStyleSheet(StyleSheet ss) {
+    var index = ss.topLevels.length;
+    while (--index >= 0) {
+      if (_nodesToRemove(ss.topLevels[index])) {
+        ss.topLevels.removeAt(index);
+      }
+    }
+    super.visitStyleSheet(ss);
+  }
+
+  void visitDeclarationGroup(DeclarationGroup node) {
+    var index = node.declarations.length;
+    while (--index >= 0) {
+      if (_nodesToRemove(node.declarations[index])) {
+        node.declarations.removeAt(index);
+      }
+    }
+    super.visitDeclarationGroup(node);
+  }
+}
+
+/** Find all @extend to create inheritance. */
+class AllExtends extends Visitor {
+  final Map<String, List<SelectorGroup>> inherits =
+      new Map<String, List<SelectorGroup>>();
+
+  SelectorGroup _currSelectorGroup;
+  int _currDeclIndex;
+  List<int> _extendsToRemove = [];
+
+  void visitRuleSet(RuleSet node) {
+    var oldSelectorGroup = _currSelectorGroup;
+    _currSelectorGroup = node.selectorGroup;
+
+    super.visitRuleSet(node);
+
+    _currSelectorGroup = oldSelectorGroup;
+  }
+
+  void visitExtendDeclaration(ExtendDeclaration node) {
+    var inheritName = "";
+    for (var selector in node.selectors) {
+      inheritName += selector.toString();
+    }
+    if (inherits.containsKey(inheritName)) {
+      inherits[inheritName].add(_currSelectorGroup);
+    } else {
+      inherits[inheritName] = [_currSelectorGroup];
+    }
+
+    // Remove this @extend
+    _extendsToRemove.add(_currDeclIndex);
+
+    super.visitExtendDeclaration(node);
+  }
+
+  void visitDeclarationGroup(DeclarationGroup node) {
+    var oldDeclIndex = _currDeclIndex;
+
+    var decls = node.declarations;
+    for (_currDeclIndex = 0; _currDeclIndex < decls.length; _currDeclIndex++) {
+      decls[_currDeclIndex].visit(this);
+    }
+
+    if (_extendsToRemove.isNotEmpty) {
+      var removeTotal = _extendsToRemove.length - 1;
+      for (var index = removeTotal; index >= 0; index--) {
+        decls.removeAt(_extendsToRemove[index]);
+      }
+      _extendsToRemove.clear();
+    }
+
+    _currDeclIndex = oldDeclIndex;
+  }
+}
+
+// TODO(terry): Need to handle merging selector sequences
+// TODO(terry): Need to handle @extend-Only selectors.
+// TODO(terry): Need to handle !optional glag.
+/**
+ * Changes any selector that matches @extend.
+ */
+class InheritExtends extends Visitor {
+  final Messages _messages;
+  final AllExtends _allExtends;
+
+  InheritExtends(this._messages, this._allExtends);
+
+  void visitSelectorGroup(SelectorGroup node) {
+    for (var selectorsIndex = 0;
+        selectorsIndex < node.selectors.length;
+        selectorsIndex++) {
+      var selectors = node.selectors[selectorsIndex];
+      var isLastNone = false;
+      var selectorName = "";
+      for (var index = 0;
+          index < selectors.simpleSelectorSequences.length;
+          index++) {
+        var simpleSeq = selectors.simpleSelectorSequences[index];
+        var namePart = simpleSeq.simpleSelector.toString();
+        selectorName = (isLastNone) ? (selectorName + namePart) : namePart;
+        List<SelectorGroup> matches = _allExtends.inherits[selectorName];
+        if (matches != null) {
+          for (var match in matches) {
+            // Create a new group.
+            var newSelectors = selectors.clone();
+            var newSeq = match.selectors[0].clone();
+            if (isLastNone) {
+              // Add the inherited selector.
+              node.selectors.add(newSeq);
+            } else {
+              // Replace the selector sequence to the left of the pseudo class
+              // or pseudo element.
+
+              // Make new selector seq combinator the same as the original.
+              var orgCombinator =
+                  newSelectors.simpleSelectorSequences[index].combinator;
+              newSeq.simpleSelectorSequences[0].combinator = orgCombinator;
+
+              newSelectors.simpleSelectorSequences.replaceRange(
+                  index, index + 1, newSeq.simpleSelectorSequences);
+              node.selectors.add(newSelectors);
+            }
+            isLastNone = false;
+          }
+        } else {
+          isLastNone = simpleSeq.isCombinatorNone;
+        }
+      }
+    }
+    super.visitSelectorGroup(node);
+  }
+}
diff --git a/csslib/lib/src/css_printer.dart b/csslib/lib/src/css_printer.dart
new file mode 100644
index 0000000..125b5ae
--- /dev/null
+++ b/csslib/lib/src/css_printer.dart
@@ -0,0 +1,516 @@
+// Copyright (c) 2013, 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.
+
+part of csslib.visitor;
+
+/**
+ * Visitor that produces a formatted string representation of the CSS tree.
+ */
+class CssPrinter extends Visitor {
+  StringBuffer _buff = new StringBuffer();
+  bool prettyPrint = true;
+
+  /**
+   * Walk the [tree] Stylesheet. [pretty] if true emits line breaks, extra
+   * spaces, friendly property values, etc., if false emits compacted output.
+   */
+  void visitTree(StyleSheet tree, {bool pretty: false}) {
+    prettyPrint = pretty;
+    _buff = new StringBuffer();
+    visitStyleSheet(tree);
+  }
+
+  /** Appends [str] to the output buffer. */
+  void emit(String str) {
+    _buff.write(str);
+  }
+
+  /** Returns the output buffer. */
+  String toString() => _buff.toString().trim();
+
+  String get _newLine => prettyPrint ? '\n' : ' ';
+  String get _sp => prettyPrint ? ' ' : '';
+
+  // TODO(terry): When adding obfuscation we'll need isOptimized (compact w/
+  //              obufuscation) and have isTesting (compact no obfuscation) and
+  //              isCompact would be !prettyPrint.  We'll need another boolean
+  //              flag for obfuscation.
+  bool get _isTesting => !prettyPrint;
+
+  void visitCssComment(CssComment node) {
+    emit('/* ${node.comment} */');
+  }
+
+  void visitCommentDefinition(CommentDefinition node) {
+    emit('<!-- ${node.comment} -->');
+  }
+
+  void visitMediaExpression(MediaExpression node) {
+    emit(node.andOperator ? ' AND ' : ' ');
+    emit('(${node.mediaFeature}:');
+    visitExpressions(node.exprs);
+    emit(')');
+  }
+
+  void visitMediaQuery(MediaQuery query) {
+    var unary = query.hasUnary ? ' ${query.unary}' : '';
+    var mediaType = query.hasMediaType ? ' ${query.mediaType}' : '';
+    emit('$unary$mediaType');
+    for (var expression in query.expressions) {
+      visitMediaExpression(expression);
+    }
+  }
+
+  void emitMediaQueries(queries) {
+    var queriesLen = queries.length;
+    for (var i = 0; i < queriesLen; i++) {
+      var query = queries[i];
+      if (query.hasMediaType && i > 0) emit(',');
+      visitMediaQuery(query);
+    }
+  }
+
+  void visitMediaDirective(MediaDirective node) {
+    emit(' @media');
+    emitMediaQueries(node.mediaQueries);
+    emit(' {');
+    for (var ruleset in node.rulesets) {
+      ruleset.visit(this);
+    }
+    emit('$_newLine\}');
+  }
+
+  void visitHostDirective(HostDirective node) {
+    emit('\n@host {');
+    for (var ruleset in node.rulesets) {
+      ruleset.visit(this);
+    }
+    emit('$_newLine\}');
+  }
+
+  /**
+   *  @page : pseudoPage {
+   *    decls
+   *  }
+   */
+  void visitPageDirective(PageDirective node) {
+    emit('$_newLine@page');
+    if (node.hasIdent || node.hasPseudoPage) {
+      if (node.hasIdent) emit(' ');
+      emit(node._ident);
+      emit(node.hasPseudoPage ? ':${node._pseudoPage}' : '');
+    }
+    emit(' ');
+
+    var declsMargin = node._declsMargin;
+    var declsMarginLength = declsMargin.length;
+    for (var i = 0; i < declsMarginLength; i++) {
+      if (i > 0) emit(_newLine);
+      emit('{$_newLine');
+      declsMargin[i].visit(this);
+      emit('}');
+    }
+  }
+
+  /** @charset "charset encoding" */
+  void visitCharsetDirective(CharsetDirective node) {
+    emit('$_newLine@charset "${node.charEncoding}";');
+  }
+
+  void visitImportDirective(ImportDirective node) {
+    bool isStartingQuote(String ch) => ('\'"'.indexOf(ch[0]) >= 0);
+
+    if (_isTesting) {
+      // Emit assuming url() was parsed; most suite tests use url function.
+      emit(' @import url(${node.import})');
+    } else if (isStartingQuote(node.import)) {
+      emit(' @import ${node.import}');
+    } else {
+      // url(...) isn't needed only a URI can follow an @import directive; emit
+      // url as a string.
+      emit(' @import "${node.import}"');
+    }
+    emitMediaQueries(node.mediaQueries);
+    emit(';');
+  }
+
+  void visitKeyFrameDirective(KeyFrameDirective node) {
+    emit('$_newLine${node.keyFrameName} ');
+    node.name.visit(this);
+    emit('$_sp{$_newLine');
+    for (final block in node._blocks) {
+      block.visit(this);
+    }
+    emit('}');
+  }
+
+  void visitFontFaceDirective(FontFaceDirective node) {
+    emit('$_newLine@font-face ');
+    emit('$_sp{$_newLine');
+    node._declarations.visit(this);
+    emit('}');
+  }
+
+  void visitKeyFrameBlock(KeyFrameBlock node) {
+    emit('$_sp$_sp');
+    node._blockSelectors.visit(this);
+    emit('$_sp{$_newLine');
+    node._declarations.visit(this);
+    emit('$_sp$_sp}$_newLine');
+  }
+
+  void visitStyletDirective(StyletDirective node) {
+    emit('/* @stylet export as ${node.dartClassName} */\n');
+  }
+
+  void visitNamespaceDirective(NamespaceDirective node) {
+    bool isStartingQuote(String ch) => ('\'"'.indexOf(ch) >= 0);
+
+    if (isStartingQuote(node._uri)) {
+      emit(' @namespace ${node.prefix}"${node._uri}"');
+    } else {
+      if (_isTesting) {
+        // Emit exactly was we parsed.
+        emit(' @namespace ${node.prefix}url(${node._uri})');
+      } else {
+        // url(...) isn't needed only a URI can follow a:
+        //    @namespace prefix directive.
+        emit(' @namespace ${node.prefix}${node._uri}');
+      }
+    }
+    emit(';');
+  }
+
+  void visitVarDefinitionDirective(VarDefinitionDirective node) {
+    visitVarDefinition(node.def);
+    emit(';$_newLine');
+  }
+
+  void visitMixinRulesetDirective(MixinRulesetDirective node) {
+    emit('@mixin ${node.name} {');
+    for (var ruleset in node.rulesets) {
+      ruleset.visit(this);
+    }
+    emit('}');
+  }
+
+  void visitMixinDeclarationDirective(MixinDeclarationDirective node) {
+    emit('@mixin ${node.name} {\n');
+    visitDeclarationGroup(node.declarations);
+    emit('}');
+  }
+
+  /**
+   * Added optional newLine for handling @include at top-level vs/ inside of
+   * a declaration group.
+   */
+  void visitIncludeDirective(IncludeDirective node, [bool topLevel = true]) {
+    if (topLevel) emit(_newLine);
+    emit('@include ${node.name}');
+    emit(';');
+  }
+
+  void visitContentDirective(ContentDirective node) {
+    // TODO(terry): TBD
+  }
+
+  void visitRuleSet(RuleSet node) {
+    emit("$_newLine");
+    node._selectorGroup.visit(this);
+    emit(" {$_newLine");
+    node._declarationGroup.visit(this);
+    emit("}");
+  }
+
+  void visitDeclarationGroup(DeclarationGroup node) {
+    var declarations = node.declarations;
+    var declarationsLength = declarations.length;
+    for (var i = 0; i < declarationsLength; i++) {
+      if (i > 0) emit(_newLine);
+      emit("$_sp$_sp");
+      declarations[i].visit(this);
+      emit(";");
+    }
+    if (declarationsLength > 0) emit(_newLine);
+  }
+
+  void visitMarginGroup(MarginGroup node) {
+    var margin_sym_name =
+        TokenKind.idToValue(TokenKind.MARGIN_DIRECTIVES, node.margin_sym);
+
+    emit("@$margin_sym_name {$_newLine");
+
+    visitDeclarationGroup(node);
+
+    emit("}$_newLine");
+  }
+
+  void visitDeclaration(Declaration node) {
+    String importantAsString() => node.important ? '$_sp!important' : '';
+
+    emit("${node.property}: ");
+    node._expression.visit(this);
+
+    emit("${importantAsString()}");
+  }
+
+  void visitVarDefinition(VarDefinition node) {
+    emit("var-${node.definedName}: ");
+    node._expression.visit(this);
+  }
+
+  void visitIncludeMixinAtDeclaration(IncludeMixinAtDeclaration node) {
+    // Don't emit a new line we're inside of a declaration group.
+    visitIncludeDirective(node.include, false);
+  }
+
+  void visitExtendDeclaration(ExtendDeclaration node) {
+    emit("@extend ");
+    for (var selector in node.selectors) {
+      selector.visit(this);
+    }
+  }
+
+  void visitSelectorGroup(SelectorGroup node) {
+    var selectors = node.selectors;
+    var selectorsLength = selectors.length;
+    for (var i = 0; i < selectorsLength; i++) {
+      if (i > 0) emit(',$_sp');
+      selectors[i].visit(this);
+    }
+  }
+
+  void visitSimpleSelectorSequence(SimpleSelectorSequence node) {
+    emit('${node._combinatorToString}');
+    node.simpleSelector.visit(this);
+  }
+
+  void visitSimpleSelector(SimpleSelector node) {
+    emit(node.name);
+  }
+
+  void visitNamespaceSelector(NamespaceSelector node) {
+    emit(node.toString());
+  }
+
+  void visitElementSelector(ElementSelector node) {
+    emit(node.toString());
+  }
+
+  void visitAttributeSelector(AttributeSelector node) {
+    emit(node.toString());
+  }
+
+  void visitIdSelector(IdSelector node) {
+    emit(node.toString());
+  }
+
+  void visitClassSelector(ClassSelector node) {
+    emit(node.toString());
+  }
+
+  void visitPseudoClassSelector(PseudoClassSelector node) {
+    emit(node.toString());
+  }
+
+  void visitPseudoElementSelector(PseudoElementSelector node) {
+    emit(node.toString());
+  }
+
+  void visitPseudoClassFunctionSelector(PseudoClassFunctionSelector node) {
+    emit(":${node.name}(");
+    node.expression.visit(this);
+    emit(')');
+  }
+
+  void visitPseudoElementFunctionSelector(PseudoElementFunctionSelector node) {
+    emit("::${node.name}(");
+    node.expression.visit(this);
+    emit(')');
+  }
+
+  void visitNegationSelector(NegationSelector node) {
+    emit(':not(');
+    node.negationArg.visit(this);
+    emit(')');
+  }
+
+  void visitSelectorExpression(SelectorExpression node) {
+    var expressions = node.expressions;
+    var expressionsLength = expressions.length;
+    for (var i = 0; i < expressionsLength; i++) {
+      // Add space seperator between terms without an operator.
+      var expression = expressions[i];
+      expression.visit(this);
+    }
+  }
+
+  void visitUnicodeRangeTerm(UnicodeRangeTerm node) {
+    if (node.hasSecond) {
+      emit("U+${node.first}-${node.second}");
+    } else {
+      emit("U+${node.first}");
+    }
+  }
+
+  void visitLiteralTerm(LiteralTerm node) {
+    emit(node.text);
+  }
+
+  void visitHexColorTerm(HexColorTerm node) {
+    var mappedName;
+    if (_isTesting && (node.value is! BAD_HEX_VALUE)) {
+      mappedName = TokenKind.hexToColorName(node.value);
+    }
+    if (mappedName == null) {
+      mappedName = '#${node.text}';
+    }
+
+    emit(mappedName);
+  }
+
+  void visitNumberTerm(NumberTerm node) {
+    visitLiteralTerm(node);
+  }
+
+  void visitUnitTerm(UnitTerm node) {
+    emit(node.toString());
+  }
+
+  void visitLengthTerm(LengthTerm node) {
+    emit(node.toString());
+  }
+
+  void visitPercentageTerm(PercentageTerm node) {
+    emit('${node.text}%');
+  }
+
+  void visitEmTerm(EmTerm node) {
+    emit('${node.text}em');
+  }
+
+  void visitExTerm(ExTerm node) {
+    emit('${node.text}ex');
+  }
+
+  void visitAngleTerm(AngleTerm node) {
+    emit(node.toString());
+  }
+
+  void visitTimeTerm(TimeTerm node) {
+    emit(node.toString());
+  }
+
+  void visitFreqTerm(FreqTerm node) {
+    emit(node.toString());
+  }
+
+  void visitFractionTerm(FractionTerm node) {
+    emit('${node.text}fr');
+  }
+
+  void visitUriTerm(UriTerm node) {
+    emit('url("${node.text}")');
+  }
+
+  void visitResolutionTerm(ResolutionTerm node) {
+    emit(node.toString());
+  }
+
+  void visitViewportTerm(ViewportTerm node) {
+    emit(node.toString());
+  }
+
+  void visitFunctionTerm(FunctionTerm node) {
+    // TODO(terry): Optimize rgb to a hexcolor.
+    emit('${node.text}(');
+    node._params.visit(this);
+    emit(')');
+  }
+
+  void visitGroupTerm(GroupTerm node) {
+    emit('(');
+    var terms = node._terms;
+    var termsLength = terms.length;
+    for (var i = 0; i < termsLength; i++) {
+      if (i > 0) emit('$_sp');
+      terms[i].visit(this);
+    }
+    emit(')');
+  }
+
+  void visitItemTerm(ItemTerm node) {
+    emit('[${node.text}]');
+  }
+
+  void visitIE8Term(IE8Term node) {
+    visitLiteralTerm(node);
+  }
+
+  void visitOperatorSlash(OperatorSlash node) {
+    emit('/');
+  }
+
+  void visitOperatorComma(OperatorComma node) {
+    emit(',');
+  }
+
+  void visitOperatorPlus(OperatorPlus node) {
+    emit('+');
+  }
+
+  void visitOperatorMinus(OperatorMinus node) {
+    emit('-');
+  }
+
+  void visitVarUsage(VarUsage node) {
+    emit('var(${node.name}');
+    if (!node.defaultValues.isEmpty) {
+      emit(',');
+      for (var defaultValue in node.defaultValues) {
+        emit(' ');
+        defaultValue.visit(this);
+      }
+    }
+    emit(')');
+  }
+
+  void visitExpressions(Expressions node) {
+    var expressions = node.expressions;
+    var expressionsLength = expressions.length;
+    for (var i = 0; i < expressionsLength; i++) {
+      // Add space seperator between terms without an operator.
+      // TODO(terry): Should have a BinaryExpression to solve this problem.
+      var expression = expressions[i];
+      if (i > 0 &&
+          !(expression is OperatorComma || expression is OperatorSlash)) {
+        emit(' ');
+      }
+      expression.visit(this);
+    }
+  }
+
+  void visitBinaryExpression(BinaryExpression node) {
+    // TODO(terry): TBD
+    throw UnimplementedError;
+  }
+
+  void visitUnaryExpression(UnaryExpression node) {
+    // TODO(terry): TBD
+    throw UnimplementedError;
+  }
+
+  void visitIdentifier(Identifier node) {
+    emit(node.name);
+  }
+
+  void visitWildcard(Wildcard node) {
+    emit('*');
+  }
+
+  void visitDartStyleExpression(DartStyleExpression node) {
+    // TODO(terry): TBD
+    throw UnimplementedError;
+  }
+}
diff --git a/csslib/lib/src/messages.dart b/csslib/lib/src/messages.dart
new file mode 100644
index 0000000..595bf6c
--- /dev/null
+++ b/csslib/lib/src/messages.dart
@@ -0,0 +1,130 @@
+// Copyright (c) 2012, 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 csslib.src.messages;
+
+import 'package:logging/logging.dart' show Level;
+import 'package:source_span/source_span.dart';
+
+import 'options.dart';
+
+// TODO(terry): Remove the global messages, use some object that tracks
+//              compilation state.
+
+/** The global [Messages] for tracking info/warnings/messages. */
+Messages messages;
+
+// Color constants used for generating messages.
+final String GREEN_COLOR = '\u001b[32m';
+final String RED_COLOR = '\u001b[31m';
+final String MAGENTA_COLOR = '\u001b[35m';
+final String NO_COLOR = '\u001b[0m';
+
+/** Map between error levels and their display color. */
+final Map<Level, String> _ERROR_COLORS = (() {
+  var colorsMap = new Map<Level, String>();
+  colorsMap[Level.SEVERE] = RED_COLOR;
+  colorsMap[Level.WARNING] = MAGENTA_COLOR;
+  colorsMap[Level.INFO] = GREEN_COLOR;
+  return colorsMap;
+})();
+
+/** Map between error levels and their friendly name. */
+final Map<Level, String> _ERROR_LABEL = (() {
+  var labels = new Map<Level, String>();
+  labels[Level.SEVERE] = 'error';
+  labels[Level.WARNING] = 'warning';
+  labels[Level.INFO] = 'info';
+  return labels;
+})();
+
+/** A single message from the compiler. */
+class Message {
+  final Level level;
+  final String message;
+  final SourceSpan span;
+  final bool useColors;
+
+  Message(this.level, this.message, {SourceSpan span, bool useColors: false})
+      : this.span = span,
+        this.useColors = useColors;
+
+  String toString() {
+    var output = new StringBuffer();
+    bool colors = useColors && _ERROR_COLORS.containsKey(level);
+    var levelColor = colors ? _ERROR_COLORS[level] : null;
+    if (colors) output.write(levelColor);
+    output
+      ..write(_ERROR_LABEL[level])
+      ..write(' ');
+    if (colors) output.write(NO_COLOR);
+
+    if (span == null) {
+      output.write(message);
+    } else {
+      output.write('on ');
+      output.write(span.message(message, color: levelColor));
+    }
+
+    return output.toString();
+  }
+}
+
+typedef void PrintHandler(Message obj);
+
+/**
+ * This class tracks and prints information, warnings, and errors emitted by the
+ * compiler.
+ */
+class Messages {
+  /** Called on every error. Set to blank function to supress printing. */
+  final PrintHandler printHandler;
+
+  final PreprocessorOptions options;
+
+  final List<Message> messages = <Message>[];
+
+  Messages({PreprocessorOptions options, this.printHandler: print})
+      : options = options != null ? options : new PreprocessorOptions();
+
+  /** Report a compile-time CSS error. */
+  void error(String message, SourceSpan span) {
+    var msg = new Message(Level.SEVERE, message,
+        span: span, useColors: options.useColors);
+
+    messages.add(msg);
+
+    printHandler(msg);
+  }
+
+  /** Report a compile-time CSS warning. */
+  void warning(String message, SourceSpan span) {
+    if (options.warningsAsErrors) {
+      error(message, span);
+    } else {
+      var msg = new Message(Level.WARNING, message,
+          span: span, useColors: options.useColors);
+
+      messages.add(msg);
+    }
+  }
+
+  /** Report and informational message about what the compiler is doing. */
+  void info(String message, SourceSpan span) {
+    var msg = new Message(Level.INFO, message,
+        span: span, useColors: options.useColors);
+
+    messages.add(msg);
+
+    if (options.verbose) printHandler(msg);
+  }
+
+  /** Merge [newMessages] to this message lsit. */
+  void mergeMessages(Messages newMessages) {
+    messages.addAll(newMessages.messages);
+    newMessages.messages
+        .where((message) => message.level == Level.SEVERE || options.verbose)
+        .forEach(printHandler);
+  }
+}
diff --git a/csslib/lib/src/options.dart b/csslib/lib/src/options.dart
new file mode 100644
index 0000000..95e8a2f
--- /dev/null
+++ b/csslib/lib/src/options.dart
@@ -0,0 +1,111 @@
+// Copyright (c) 2012, 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 csslib.src.options;
+
+import 'package:args/args.dart';
+
+class PreprocessorOptions {
+  /** Generate polyfill code (e.g., var, etc.) */
+  final bool polyfill;
+
+  /** Report warnings as errors. */
+  final bool warningsAsErrors;
+
+  /** Throw an exception on warnings (not used by command line tool). */
+  final bool throwOnWarnings;
+
+  /** Throw an exception on errors (not used by command line tool). */
+  final bool throwOnErrors;
+
+  /** True to show informational messages. The `--verbose` flag. */
+  final bool verbose;
+
+  /** True to show warning messages for bad CSS.  The '--checked' flag. */
+  final bool checked;
+
+  // TODO(terry): Add mixin support and nested rules.
+  /**
+   * Subset of Less commands enabled; disable with '--no-less'.
+   * Less syntax supported:
+   * - @name at root level statically defines variables resolved at compilation
+   * time.  Essentially a directive e.g., @var-name.
+   */
+  final bool lessSupport;
+
+  /** Whether to use colors to print messages on the terminal. */
+  final bool useColors;
+
+  /** File to process by the compiler. */
+  final String inputFile;
+
+  const PreprocessorOptions({this.verbose: false, this.checked: false,
+      this.lessSupport: true, this.warningsAsErrors: false,
+      this.throwOnErrors: false, this.throwOnWarnings: false,
+      this.useColors: true, this.polyfill: false, this.inputFile});
+
+  PreprocessorOptions.fromArgs(ArgResults args)
+      : warningsAsErrors = args['warnings_as_errors'],
+        throwOnWarnings = args['throw_on_warnings'],
+        throwOnErrors = args['throw_on_errors'],
+        verbose = args['verbose'],
+        checked = args['checked'],
+        lessSupport = args['less'],
+        useColors = args['colors'],
+        polyfill = args['polyfill'],
+        inputFile = args.rest.length > 0 ? args.rest[0] : null;
+
+  // tool.dart [options...] <css file>
+  static PreprocessorOptions parse(List<String> arguments) {
+    var parser = new ArgParser()
+      ..addFlag('verbose',
+          abbr: 'v',
+          defaultsTo: false,
+          negatable: false,
+          help: 'Display detail info')
+      ..addFlag('checked',
+          defaultsTo: false,
+          negatable: false,
+          help: 'Validate CSS values invalid value display a warning message')
+      ..addFlag('less',
+          defaultsTo: true,
+          negatable: true,
+          help: 'Supports subset of Less syntax')
+      ..addFlag('suppress_warnings',
+          defaultsTo: true, help: 'Warnings not displayed')
+      ..addFlag('warnings_as_errors',
+          defaultsTo: false, help: 'Warning handled as errors')
+      ..addFlag('throw_on_errors',
+          defaultsTo: false, help: 'Throw on errors encountered')
+      ..addFlag('throw_on_warnings',
+          defaultsTo: false, help: 'Throw on warnings encountered')
+      ..addFlag('colors',
+          defaultsTo: true, help: 'Display errors/warnings in colored text')
+      ..addFlag('polyfill',
+          defaultsTo: false, help: 'Generate polyfill for new CSS features')
+      ..addFlag('help',
+          abbr: 'h',
+          defaultsTo: false,
+          negatable: false,
+          help: 'Displays this help message');
+
+    try {
+      var results = parser.parse(arguments);
+      if (results['help'] || results.rest.length == 0) {
+        showUsage(parser);
+        return null;
+      }
+      return new PreprocessorOptions.fromArgs(results);
+    } on FormatException catch (e) {
+      print(e.message);
+      showUsage(parser);
+      return null;
+    }
+  }
+
+  static showUsage(parser) {
+    print('Usage: css [options...] input.css');
+    print(parser.getUsage());
+  }
+}
diff --git a/csslib/lib/src/polyfill.dart b/csslib/lib/src/polyfill.dart
new file mode 100644
index 0000000..bdd8330
--- /dev/null
+++ b/csslib/lib/src/polyfill.dart
@@ -0,0 +1,256 @@
+// Copyright (c) 2013, 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.
+
+part of csslib.parser;
+
+/**
+ * CSS polyfill emits CSS to be understood by older parsers that which do not
+ * understand (var, calc, etc.).
+ */
+class PolyFill {
+  final Messages _messages;
+  final bool _warningsAsErrors;
+  Map<String, VarDefinition> _allVarDefinitions =
+      new Map<String, VarDefinition>();
+
+  Set<StyleSheet> allStyleSheets = new Set<StyleSheet>();
+
+  /**
+   * [_pseudoElements] list of known pseudo attributes found in HTML, any
+   * CSS pseudo-elements 'name::custom-element' is mapped to the manged name
+   * associated with the pseudo-element key.
+   */
+  PolyFill(this._messages, this._warningsAsErrors);
+
+  /**
+   * Run the analyzer on every file that is a style sheet or any component that
+   * has a style tag.
+   */
+  void process(StyleSheet styleSheet, {List<StyleSheet> includes: null}) {
+    if (includes != null) {
+      processVarDefinitions(includes);
+    }
+    processVars(styleSheet);
+
+    // Remove all var definitions for this style sheet.
+    new _RemoveVarDefinitions().visitTree(styleSheet);
+  }
+
+  /** Process all includes looking for var definitions. */
+  void processVarDefinitions(List<StyleSheet> includes) {
+    for (var include in includes) {
+      _allVarDefinitions = (new _VarDefinitionsIncludes(_allVarDefinitions)
+        ..visitTree(include)).varDefs;
+    }
+  }
+
+  void processVars(StyleSheet styleSheet) {
+    // Build list of all var definitions.
+    var mainStyleSheetVarDefs = (new _VarDefAndUsage(
+        this._messages, _allVarDefinitions)..visitTree(styleSheet)).varDefs;
+
+    // Resolve all definitions to a non-VarUsage (terminal expression).
+    mainStyleSheetVarDefs.forEach((key, value) {
+      for (Expression expr in (value.expression as Expressions).expressions) {
+        mainStyleSheetVarDefs[key] =
+            _findTerminalVarDefinition(_allVarDefinitions, value);
+      }
+    });
+  }
+}
+
+/** Build list of all var definitions in all includes. */
+class _VarDefinitionsIncludes extends Visitor {
+  final Map<String, VarDefinition> varDefs;
+
+  _VarDefinitionsIncludes(this.varDefs);
+
+  void visitTree(StyleSheet tree) {
+    visitStyleSheet(tree);
+  }
+
+  visitVarDefinition(VarDefinition node) {
+    // Replace with latest variable definition.
+    varDefs[node.definedName] = node;
+    super.visitVarDefinition(node);
+  }
+
+  void visitVarDefinitionDirective(VarDefinitionDirective node) {
+    visitVarDefinition(node.def);
+  }
+}
+
+/**
+ * Find var- definitions in a style sheet.
+ * [found] list of known definitions.
+ */
+class _VarDefAndUsage extends Visitor {
+  final Messages _messages;
+  final Map<String, VarDefinition> _knownVarDefs;
+  final Map<String, VarDefinition> varDefs = new Map<String, VarDefinition>();
+
+  VarDefinition currVarDefinition;
+  List<Expression> currentExpressions;
+
+  _VarDefAndUsage(this._messages, this._knownVarDefs);
+
+  void visitTree(StyleSheet tree) {
+    visitStyleSheet(tree);
+  }
+
+  visitVarDefinition(VarDefinition node) {
+    // Replace with latest variable definition.
+    currVarDefinition = node;
+
+    _knownVarDefs[node.definedName] = node;
+    varDefs[node.definedName] = node;
+
+    super.visitVarDefinition(node);
+
+    currVarDefinition = null;
+  }
+
+  void visitVarDefinitionDirective(VarDefinitionDirective node) {
+    visitVarDefinition(node.def);
+  }
+
+  void visitExpressions(Expressions node) {
+    currentExpressions = node.expressions;
+    super.visitExpressions(node);
+    currentExpressions = null;
+  }
+
+  void visitVarUsage(VarUsage node) {
+    if (currVarDefinition != null && currVarDefinition.badUsage) return;
+
+    // Don't process other var() inside of a varUsage.  That implies that the
+    // default is a var() too.  Also, don't process any var() inside of a
+    // varDefinition (they're just place holders until we've resolved all real
+    // usages.
+    var expressions = currentExpressions;
+    var index = expressions.indexOf(node);
+    assert(index >= 0);
+    var def = _knownVarDefs[node.name];
+    if (def != null) {
+      if (def.badUsage) {
+        // Remove any expressions pointing to a bad var definition.
+        expressions.removeAt(index);
+        return;
+      }
+      _resolveVarUsage(currentExpressions, index,
+          _findTerminalVarDefinition(_knownVarDefs, def));
+    } else if (node.defaultValues.any((e) => e is VarUsage)) {
+      // Don't have a VarDefinition need to use default values resolve all
+      // default values.
+      var terminalDefaults = <Expression>[];
+      for (var defaultValue in node.defaultValues) {
+        terminalDefaults.addAll(resolveUsageTerminal(defaultValue));
+      }
+      expressions.replaceRange(index, index + 1, terminalDefaults);
+    } else if (node.defaultValues.isNotEmpty) {
+      // No VarDefinition but default value is a terminal expression; use it.
+      expressions.replaceRange(index, index + 1, node.defaultValues);
+    } else {
+      if (currVarDefinition != null) {
+        currVarDefinition.badUsage = true;
+        var mainStyleSheetDef = varDefs[node.name];
+        if (mainStyleSheetDef != null) {
+          varDefs.remove(currVarDefinition.property);
+        }
+      }
+      // Remove var usage that points at an undefined definition.
+      expressions.removeAt(index);
+      _messages.warning("Variable is not defined.", node.span);
+    }
+
+    var oldExpressions = currentExpressions;
+    currentExpressions = node.defaultValues;
+    super.visitVarUsage(node);
+    currentExpressions = oldExpressions;
+  }
+
+  List<Expression> resolveUsageTerminal(VarUsage usage) {
+    var result = [];
+
+    var varDef = _knownVarDefs[usage.name];
+    var expressions;
+    if (varDef == null) {
+      // VarDefinition not found try the defaultValues.
+      expressions = usage.defaultValues;
+    } else {
+      // Use the VarDefinition found.
+      expressions = (varDef.expression as Expressions).expressions;
+    }
+
+    for (var expr in expressions) {
+      if (expr is VarUsage) {
+        // Get terminal value.
+        result.addAll(resolveUsageTerminal(expr));
+      }
+    }
+
+    // We're at a terminal just return the VarDefinition expression.
+    if (result.isEmpty && varDef != null) {
+      result = (varDef.expression as Expressions).expressions;
+    }
+
+    return result;
+  }
+
+  _resolveVarUsage(List<Expression> expressions, int index, VarDefinition def) {
+    var defExpressions = (def.expression as Expressions).expressions;
+    expressions.replaceRange(index, index + 1, defExpressions);
+  }
+}
+
+/** Remove all var definitions. */
+class _RemoveVarDefinitions extends Visitor {
+  void visitTree(StyleSheet tree) {
+    visitStyleSheet(tree);
+  }
+
+  void visitStyleSheet(StyleSheet ss) {
+    ss.topLevels.removeWhere((e) => e is VarDefinitionDirective);
+    super.visitStyleSheet(ss);
+  }
+
+  void visitDeclarationGroup(DeclarationGroup node) {
+    node.declarations.removeWhere((e) => e is VarDefinition);
+    super.visitDeclarationGroup(node);
+  }
+}
+
+/** Find terminal definition (non VarUsage implies real CSS value). */
+VarDefinition _findTerminalVarDefinition(
+    Map<String, VarDefinition> varDefs, VarDefinition varDef) {
+  var expressions = varDef.expression as Expressions;
+  for (var expr in expressions.expressions) {
+    if (expr is VarUsage) {
+      var usageName = (expr as VarUsage).name;
+      var foundDef = varDefs[usageName];
+
+      // If foundDef is unknown check if defaultValues; if it exist then resolve
+      // to terminal value.
+      if (foundDef == null) {
+        // We're either a VarUsage or terminal definition if in varDefs;
+        // either way replace VarUsage with it's default value because the
+        // VarDefinition isn't found.
+        var defaultValues = (expr as VarUsage).defaultValues;
+        var replaceExprs = expressions.expressions;
+        assert(replaceExprs.length == 1);
+        replaceExprs.replaceRange(0, 1, defaultValues);
+        return varDef;
+      }
+      if (foundDef is VarDefinition) {
+        return _findTerminalVarDefinition(varDefs, foundDef);
+      }
+    } else {
+      // Return real CSS property.
+      return varDef;
+    }
+  }
+
+  // Didn't point to a var definition that existed.
+  return varDef;
+}
diff --git a/csslib/lib/src/property.dart b/csslib/lib/src/property.dart
new file mode 100644
index 0000000..5d6dc14
--- /dev/null
+++ b/csslib/lib/src/property.dart
@@ -0,0 +1,1233 @@
+// Copyright (c) 2012, 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.
+
+/** Representations of CSS styles. */
+
+part of csslib.parser;
+
+// TODO(terry): Prune down this file we do need some of the code in this file
+//              for darker, lighter, how to represent a Font, etc but alot of
+//              the complexity can be removed.
+//              See https://github.com/dart-lang/csslib/issues/7
+
+/**
+ * Base for all style properties (e.g., Color, Font, Border, Margin, etc.)
+ */
+abstract class _StyleProperty {
+  /**
+   * Returns the expression part of a CSS declaration.  Declaration is:
+   *
+   *     property:expression;
+   *
+   * E.g., if property is color then expression could be rgba(255,255,0) the
+   *       CSS declaration would be 'color:rgba(255,255,0);'.
+   *
+   * then _cssExpression would return 'rgba(255,255,0)'.  See
+   * <http://www.w3.org/TR/CSS21/grammar.html>
+   */
+  String get cssExpression;
+}
+
+/**
+ * Base interface for Color, HSL and RGB.
+ */
+abstract class ColorBase {
+  /**
+   * Canonical form for color #rrggbb with alpha blending (0.0 == full
+   * transparency and 1.0 == fully opaque). If _argb length is 6 it's an
+   * rrggbb otherwise it's aarrggbb.
+   */
+  String toHexArgbString();
+
+  /**
+   * Return argb as a value (int).
+   */
+  int get argbValue;
+}
+
+/**
+ * General purpse Color class.  Represent a color as an ARGB value that can be
+ * converted to and from num, hex string, hsl, hsla, rgb, rgba and SVG pre-
+ * defined color constant.
+ */
+class Color implements _StyleProperty, ColorBase {
+  // If _argb length is 6 it's an rrggbb otherwise it's aarrggbb.
+  final String _argb;
+
+  // TODO(terry): Look at reducing Rgba and Hsla classes as factories for
+  //              converting from Color to an Rgba or Hsla for reading only.
+  //              Usefulness of creating an Rgba or Hsla is limited.
+
+  /**
+   * Create a color with an integer representing the rgb value of red, green,
+   * and blue.  The value 0xffffff is the color white #ffffff (CSS style).
+   * The [rgb] value of 0xffd700 would map to #ffd700 or the constant
+   * Color.gold, where ff is red intensity, d7 is green intensity, and 00 is
+   * blue intensity.
+   */
+  Color(int rgb, [num alpha]) : this._argb = Color._rgbToArgbString(rgb, alpha);
+
+  /**
+   * RGB takes three values. The [red], [green], and [blue] parameters are
+   * the intensity of those components where '0' is the least and '256' is the
+   * greatest.
+   *
+   * If [alpha] is provided, it is the level of translucency which ranges from
+   * '0' (completely transparent) to '1.0' (completely opaque).  It will
+   * internally be mapped to an int between '0' and '255' like the other color
+   * components.
+   */
+  Color.createRgba(int red, int green, int blue, [num alpha])
+      : this._argb = Color.convertToHexString(Color._clamp(red, 0, 255),
+          Color._clamp(green, 0, 255), Color._clamp(blue, 0, 255),
+          alpha != null ? Color._clamp(alpha, 0, 1) : alpha);
+
+  /**
+   * Creates a new color from a CSS color string. For more information, see
+   * <https://developer.mozilla.org/en/CSS/color>.
+   */
+  Color.css(String color) : this._argb = Color._convertCssToArgb(color);
+
+  // TODO(jmesserly): I found the use of percents a bit suprising.
+  /**
+   * HSL takes three values.  The [hueDegree] degree on the color wheel; '0' is
+   * the least and '100' is the greatest.  The value '0' or '360' is red, '120'
+   * is green, '240' is blue. Numbers in between reflect different shades.
+   * The [saturationPercent] percentage; where'0' is the least and '100' is the
+   * greatest (100 represents full color).  The [lightnessPercent] percentage;
+   * where'0' is the least and '100' is the greatest.  The value 0 is dark or
+   * black, 100 is light or white and 50 is a medium lightness.
+   *
+   * If [alpha] is provided, it is the level of translucency which ranges from
+   * '0' (completely transparent foreground) to '1.0' (completely opaque
+   * foreground).
+   */
+  Color.createHsla(num hueDegree, num saturationPercent, num lightnessPercent,
+      [num alpha])
+      : this._argb = new Hsla(Color._clamp(hueDegree, 0, 360) / 360,
+          Color._clamp(saturationPercent, 0, 100) / 100,
+          Color._clamp(lightnessPercent, 0, 100) / 100,
+          alpha != null ? Color._clamp(alpha, 0, 1) : alpha).toHexArgbString();
+
+  /**
+   * The hslaRaw takes three values.  The [hue] degree on the color wheel; '0'
+   * is the least and '1' is the greatest.  The value '0' or '1' is red, the
+   * ratio of 120/360 is green, and the ratio of 240/360 is blue.  Numbers in
+   * between reflect different shades.  The [saturation] is a percentage; '0'
+   * is the least and '1' is the greatest.  The value of '1' is equivalent to
+   * 100% (full colour).  The [lightness] is a percentage; '0' is the least and
+   * '1' is the greatest.  The value of '0' is dark (black), the value of '1'
+   * is light (white), and the value of '.50' is a medium lightness.
+   *
+   * The fourth optional parameter is:
+   *   [alpha]      level of translucency range of values is 0..1, zero is a
+   *                completely transparent foreground and 1 is a completely
+   *                opaque foreground.
+   */
+  Color.hslaRaw(num hue, num saturation, num lightness, [num alpha])
+      : this._argb = new Hsla(Color._clamp(hue, 0, 1),
+          Color._clamp(saturation, 0, 1), Color._clamp(lightness, 0, 1),
+          alpha != null ? Color._clamp(alpha, 0, 1) : alpha).toHexArgbString();
+
+  /**
+   * Generate a real constant for pre-defined colors (no leading #).
+   */
+  const Color.hex(this._argb);
+
+  // TODO(jmesserly): this is needed by the example so leave it exposed for now.
+  String toString() => cssExpression;
+
+  // TODO(terry): Regardless of how color is set (rgb, num, css or hsl) we'll
+  //              always return a rgb or rgba loses fidelity when debugging in
+  //              CSS if user uses hsl and would like to edit as hsl, etc.  If
+  //              this is an issue we should keep the original value and not re-
+  //              create the CSS from the normalized value.
+  String get cssExpression {
+    if (_argb.length == 6) {
+      return "#$_argb"; // RGB only, no alpha blending.
+    } else {
+      num alpha = Color.hexToInt(_argb.substring(0, 2));
+      String a = (alpha / 255).toStringAsPrecision(2);
+      int r = Color.hexToInt(_argb.substring(2, 4));
+      int g = Color.hexToInt(_argb.substring(4, 6));
+      int b = Color.hexToInt(_argb.substring(6, 8));
+      return "rgba($r,$g,$b,$a)";
+    }
+  }
+
+  Rgba get rgba {
+    int nextIndex = 0;
+    num a;
+    if (_argb.length == 8) {
+      // Get alpha blending value 0..255
+      int alpha = Color.hexToInt(_argb.substring(nextIndex, nextIndex + 2));
+      // Convert to value from 0..1
+      a = double.parse((alpha / 255).toStringAsPrecision(2));
+      nextIndex += 2;
+    }
+    int r = Color.hexToInt(_argb.substring(nextIndex, nextIndex + 2));
+    nextIndex += 2;
+    int g = Color.hexToInt(_argb.substring(nextIndex, nextIndex + 2));
+    nextIndex += 2;
+    int b = Color.hexToInt(_argb.substring(nextIndex, nextIndex + 2));
+    return new Rgba(r, g, b, a);
+  }
+
+  Hsla get hsla => new Hsla.fromRgba(rgba);
+
+  int get argbValue => Color.hexToInt(_argb);
+
+  bool operator ==(other) => Color.equal(this, other);
+
+  String toHexArgbString() => _argb;
+
+  Color darker(num amount) {
+    Rgba newRgba = Color._createNewTintShadeFromRgba(rgba, -amount);
+    return new Color.hex("${newRgba.toHexArgbString()}");
+  }
+
+  Color lighter(num amount) {
+    Rgba newRgba = Color._createNewTintShadeFromRgba(rgba, amount);
+    return new Color.hex("${newRgba.toHexArgbString()}");
+  }
+
+  static bool equal(ColorBase curr, other) {
+    if (other is Color) {
+      Color o = other;
+      return o.toHexArgbString() == curr.toHexArgbString();
+    } else if (other is Rgba) {
+      Rgba rgb = other;
+      return rgb.toHexArgbString() == curr.toHexArgbString();
+    } else if (other is Hsla) {
+      Hsla hsla = other;
+      return hsla.toHexArgbString() == curr.toHexArgbString();
+    } else {
+      return false;
+    }
+  }
+
+  int get hashCode => _argb.hashCode;
+
+  // Conversion routines:
+
+  static String _rgbToArgbString(int rgba, num alpha) {
+    int a;
+    // If alpha is defined then adjust from 0..1 to 0..255 value, if not set
+    // then a is left as undefined and passed to convertToHexString.
+    if (alpha != null) {
+      a = (Color._clamp(alpha, 0, 1) * 255).round();
+    }
+
+    int r = (rgba & 0xff0000) >> 0x10;
+    int g = (rgba & 0xff00) >> 8;
+    int b = rgba & 0xff;
+
+    return Color.convertToHexString(r, g, b, a);
+  }
+
+  static const int _rgbCss = 1;
+  static const int _rgbaCss = 2;
+  static const int _hslCss = 3;
+  static const int _hslaCss = 4;
+  /**
+   * Parse CSS expressions of the from #rgb, rgb(r,g,b), rgba(r,g,b,a),
+   * hsl(h,s,l), hsla(h,s,l,a) and SVG colors (e.g., darkSlateblue, etc.) and
+   * convert to argb.
+   */
+  static String _convertCssToArgb(String value) {
+    // TODO(terry): Better parser/regex for converting CSS properties.
+    String color = value.trim().replaceAll("\\s", "");
+    if (color[0] == '#') {
+      String v = color.substring(1);
+      Color.hexToInt(v); // Valid hexadecimal, throws if not.
+      return v;
+    } else if (color.length > 0 && color[color.length - 1] == ')') {
+      int type;
+      if (color.indexOf("rgb(") == 0 || color.indexOf("RGB(") == 0) {
+        color = color.substring(4);
+        type = _rgbCss;
+      } else if (color.indexOf("rgba(") == 0 || color.indexOf("RGBA(") == 0) {
+        type = _rgbaCss;
+        color = color.substring(5);
+      } else if (color.indexOf("hsl(") == 0 || color.indexOf("HSL(") == 0) {
+        type = _hslCss;
+        color = color.substring(4);
+      } else if (color.indexOf("hsla(") == 0 || color.indexOf("HSLA(") == 0) {
+        type = _hslaCss;
+        color = color.substring(5);
+      } else {
+        throw new UnsupportedError('CSS property not implemented');
+      }
+
+      color = color.substring(0, color.length - 1); // Strip close paren.
+
+      var args = <num>[];
+      List<String> params = color.split(",");
+      for (String param in params) {
+        args.add(double.parse(param));
+      }
+      switch (type) {
+        case _rgbCss:
+          return Color.convertToHexString(args[0], args[1], args[2]);
+        case _rgbaCss:
+          return Color.convertToHexString(args[0], args[1], args[2], args[3]);
+        case _hslCss:
+          return new Hsla(args[0], args[1], args[2]).toHexArgbString();
+        case _hslaCss:
+          return new Hsla(args[0], args[1], args[2], args[3]).toHexArgbString();
+        default:
+          // Type not defined UnsupportedOperationException should have thrown.
+          assert(true);
+          break;
+      }
+    }
+  }
+
+  static int hexToInt(String hex) => int.parse(hex, radix: 16);
+
+  static String convertToHexString(int r, int g, int b, [num a]) {
+    String rHex = Color._numAs2DigitHex(Color._clamp(r, 0, 255));
+    String gHex = Color._numAs2DigitHex(Color._clamp(g, 0, 255));
+    String bHex = Color._numAs2DigitHex(Color._clamp(b, 0, 255));
+    String aHex = (a != null)
+        ? Color._numAs2DigitHex((Color._clamp(a, 0, 1) * 255).round())
+        : "";
+
+    // TODO(terry) 15.toRadixString(16) return 'F' on Dartium not f as in JS.
+    //             bug: <http://code.google.com/p/dart/issues/detail?id=2670>
+    return "$aHex$rHex$gHex$bHex".toLowerCase();
+  }
+
+  static String _numAs2DigitHex(num v) {
+    // TODO(terry): v.toInt().toRadixString instead of v.toRadixString
+    //              Bug <http://code.google.com/p/dart/issues/detail?id=2671>.
+    String hex = v.toInt().toRadixString(16);
+    if (hex.length == 1) {
+      hex = "0${hex}";
+    }
+    return hex;
+  }
+
+  static num _clamp(num value, num min, num max) =>
+      math.max(math.min(max, value), min);
+
+  /**
+   * Change the tint (make color lighter) or shade (make color darker) of all
+   * parts of [rgba] (r, g and b).  The [amount] is percentage darker between
+   * -1 to 0 for darker and 0 to 1 for lighter; '0' is no change.  The [amount]
+   * will darken or lighten the rgb values; it will not change the alpha value.
+   * If [amount] is outside of the value -1 to +1 then [amount] is changed to
+   * either the min or max direction -1 or 1.
+   *
+   * Darker will approach the color #000000 (black) and lighter will approach
+   * the color #ffffff (white).
+   */
+  static Rgba _createNewTintShadeFromRgba(Rgba rgba, num amount) {
+    int r, g, b;
+    num tintShade = Color._clamp(amount, -1, 1);
+    if (amount < 0 && rgba.r == 255 && rgba.g == 255 && rgba.b == 255) {
+      // TODO(terry): See TODO in _changeTintShadeColor; eliminate this test
+      //              by converting to HSL and adjust lightness although this
+      //              is fastest lighter/darker algorithm.
+      // Darkening white special handling.
+      r = Color._clamp((255 + (255 * tintShade)).round().toInt(), 0, 255);
+      g = Color._clamp((255 + (255 * tintShade)).round().toInt(), 0, 255);
+      b = Color._clamp((255 + (255 * tintShade)).round().toInt(), 0, 255);
+    } else {
+      // All other colors then darkening white go here.
+      r = Color._changeTintShadeColor(rgba.r, tintShade).round().toInt();
+      g = Color._changeTintShadeColor(rgba.g, tintShade).round().toInt();
+      b = Color._changeTintShadeColor(rgba.b, tintShade).round().toInt();
+    }
+    return new Rgba(r, g, b, rgba.a);
+  }
+
+  // TODO(terry): This does an okay lighter/darker; better would be convert to
+  //              HSL then change the lightness.
+  /**
+   * The parameter [v] is the color to change (r, g, or b) in the range '0' to
+   * '255'. The parameter [delta] is a number between '-1' and '1'.  A value
+   * between '-1' and '0' is darker and a value between '0' and '1' is lighter
+   * ('0' imples no change).
+   */
+  static num _changeTintShadeColor(num v, num delta) =>
+      Color._clamp(((1 - delta) * v + (delta * 255)).round(), 0, 255);
+
+  // Predefined CSS colors see <http://www.w3.org/TR/css3-color/>
+  static final Color transparent = const Color.hex("00ffffff"); // Alpha 0.0
+  static final Color aliceBlue = const Color.hex("0f08ff");
+  static final Color antiqueWhite = const Color.hex("0faebd7");
+  static final Color aqua = const Color.hex("00ffff");
+  static final Color aquaMarine = const Color.hex("7fffd4");
+  static final Color azure = const Color.hex("f0ffff");
+  static final Color beige = const Color.hex("f5f5dc");
+  static final Color bisque = const Color.hex("ffe4c4");
+  static final Color black = const Color.hex("000000");
+  static final Color blanchedAlmond = const Color.hex("ffebcd");
+  static final Color blue = const Color.hex("0000ff");
+  static final Color blueViolet = const Color.hex("8a2be2");
+  static final Color brown = const Color.hex("a52a2a");
+  static final Color burlyWood = const Color.hex("deb887");
+  static final Color cadetBlue = const Color.hex("5f9ea0");
+  static final Color chartreuse = const Color.hex("7fff00");
+  static final Color chocolate = const Color.hex("d2691e");
+  static final Color coral = const Color.hex("ff7f50");
+  static final Color cornFlowerBlue = const Color.hex("6495ed");
+  static final Color cornSilk = const Color.hex("fff8dc");
+  static final Color crimson = const Color.hex("dc143c");
+  static final Color cyan = const Color.hex("00ffff");
+  static final Color darkBlue = const Color.hex("00008b");
+  static final Color darkCyan = const Color.hex("008b8b");
+  static final Color darkGoldenRod = const Color.hex("b8860b");
+  static final Color darkGray = const Color.hex("a9a9a9");
+  static final Color darkGreen = const Color.hex("006400");
+  static final Color darkGrey = const Color.hex("a9a9a9");
+  static final Color darkKhaki = const Color.hex("bdb76b");
+  static final Color darkMagenta = const Color.hex("8b008b");
+  static final Color darkOliveGreen = const Color.hex("556b2f");
+  static final Color darkOrange = const Color.hex("ff8c00");
+  static final Color darkOrchid = const Color.hex("9932cc");
+  static final Color darkRed = const Color.hex("8b0000");
+  static final Color darkSalmon = const Color.hex("e9967a");
+  static final Color darkSeaGreen = const Color.hex("8fbc8f");
+  static final Color darkSlateBlue = const Color.hex("483d8b");
+  static final Color darkSlateGray = const Color.hex("2f4f4f");
+  static final Color darkSlateGrey = const Color.hex("2f4f4f");
+  static final Color darkTurquoise = const Color.hex("00ced1");
+  static final Color darkViolet = const Color.hex("9400d3");
+  static final Color deepPink = const Color.hex("ff1493");
+  static final Color deepSkyBlue = const Color.hex("00bfff");
+  static final Color dimGray = const Color.hex("696969");
+  static final Color dimGrey = const Color.hex("696969");
+  static final Color dodgerBlue = const Color.hex("1e90ff");
+  static final Color fireBrick = const Color.hex("b22222");
+  static final Color floralWhite = const Color.hex("fffaf0");
+  static final Color forestGreen = const Color.hex("228b22");
+  static final Color fuchsia = const Color.hex("ff00ff");
+  static final Color gainsboro = const Color.hex("dcdcdc");
+  static final Color ghostWhite = const Color.hex("f8f8ff");
+  static final Color gold = const Color.hex("ffd700");
+  static final Color goldenRod = const Color.hex("daa520");
+  static final Color gray = const Color.hex("808080");
+  static final Color green = const Color.hex("008000");
+  static final Color greenYellow = const Color.hex("adff2f");
+  static final Color grey = const Color.hex("808080");
+  static final Color honeydew = const Color.hex("f0fff0");
+  static final Color hotPink = const Color.hex("ff69b4");
+  static final Color indianRed = const Color.hex("cd5c5c");
+  static final Color indigo = const Color.hex("4b0082");
+  static final Color ivory = const Color.hex("fffff0");
+  static final Color khaki = const Color.hex("f0e68c");
+  static final Color lavender = const Color.hex("e6e6fa");
+  static final Color lavenderBlush = const Color.hex("fff0f5");
+  static final Color lawnGreen = const Color.hex("7cfc00");
+  static final Color lemonChiffon = const Color.hex("fffacd");
+  static final Color lightBlue = const Color.hex("add8e6");
+  static final Color lightCoral = const Color.hex("f08080");
+  static final Color lightCyan = const Color.hex("e0ffff");
+  static final Color lightGoldenRodYellow = const Color.hex("fafad2");
+  static final Color lightGray = const Color.hex("d3d3d3");
+  static final Color lightGreen = const Color.hex("90ee90");
+  static final Color lightGrey = const Color.hex("d3d3d3");
+  static final Color lightPink = const Color.hex("ffb6c1");
+  static final Color lightSalmon = const Color.hex("ffa07a");
+  static final Color lightSeaGreen = const Color.hex("20b2aa");
+  static final Color lightSkyBlue = const Color.hex("87cefa");
+  static final Color lightSlateGray = const Color.hex("778899");
+  static final Color lightSlateGrey = const Color.hex("778899");
+  static final Color lightSteelBlue = const Color.hex("b0c4de");
+  static final Color lightYellow = const Color.hex("ffffe0");
+  static final Color lime = const Color.hex("00ff00");
+  static final Color limeGreen = const Color.hex("32cd32");
+  static final Color linen = const Color.hex("faf0e6");
+  static final Color magenta = const Color.hex("ff00ff");
+  static final Color maroon = const Color.hex("800000");
+  static final Color mediumAquaMarine = const Color.hex("66cdaa");
+  static final Color mediumBlue = const Color.hex("0000cd");
+  static final Color mediumOrchid = const Color.hex("ba55d3");
+  static final Color mediumPurple = const Color.hex("9370db");
+  static final Color mediumSeaGreen = const Color.hex("3cb371");
+  static final Color mediumSlateBlue = const Color.hex("7b68ee");
+  static final Color mediumSpringGreen = const Color.hex("00fa9a");
+  static final Color mediumTurquoise = const Color.hex("48d1cc");
+  static final Color mediumVioletRed = const Color.hex("c71585");
+  static final Color midnightBlue = const Color.hex("191970");
+  static final Color mintCream = const Color.hex("f5fffa");
+  static final Color mistyRose = const Color.hex("ffe4e1");
+  static final Color moccasin = const Color.hex("ffe4b5");
+  static final Color navajoWhite = const Color.hex("ffdead");
+  static final Color navy = const Color.hex("000080");
+  static final Color oldLace = const Color.hex("fdf5e6");
+  static final Color olive = const Color.hex("808000");
+  static final Color oliveDrab = const Color.hex("6b8e23");
+  static final Color orange = const Color.hex("ffa500");
+  static final Color orangeRed = const Color.hex("ff4500");
+  static final Color orchid = const Color.hex("da70d6");
+  static final Color paleGoldenRod = const Color.hex("eee8aa");
+  static final Color paleGreen = const Color.hex("98fb98");
+  static final Color paleTurquoise = const Color.hex("afeeee");
+  static final Color paleVioletRed = const Color.hex("db7093");
+  static final Color papayaWhip = const Color.hex("ffefd5");
+  static final Color peachPuff = const Color.hex("ffdab9");
+  static final Color peru = const Color.hex("cd85ef");
+  static final Color pink = const Color.hex("ffc0cb");
+  static final Color plum = const Color.hex("dda0dd");
+  static final Color powderBlue = const Color.hex("b0e0e6");
+  static final Color purple = const Color.hex("800080");
+  static final Color red = const Color.hex("ff0000");
+  static final Color rosyBrown = const Color.hex("bc8f8f");
+  static final Color royalBlue = const Color.hex("4169e1");
+  static final Color saddleBrown = const Color.hex("8b4513");
+  static final Color salmon = const Color.hex("fa8072");
+  static final Color sandyBrown = const Color.hex("f4a460");
+  static final Color seaGreen = const Color.hex("2e8b57");
+  static final Color seashell = const Color.hex("fff5ee");
+  static final Color sienna = const Color.hex("a0522d");
+  static final Color silver = const Color.hex("c0c0c0");
+  static final Color skyBlue = const Color.hex("87ceeb");
+  static final Color slateBlue = const Color.hex("6a5acd");
+  static final Color slateGray = const Color.hex("708090");
+  static final Color slateGrey = const Color.hex("708090");
+  static final Color snow = const Color.hex("fffafa");
+  static final Color springGreen = const Color.hex("00ff7f");
+  static final Color steelBlue = const Color.hex("4682b4");
+  static final Color tan = const Color.hex("d2b48c");
+  static final Color teal = const Color.hex("008080");
+  static final Color thistle = const Color.hex("d8bfd8");
+  static final Color tomato = const Color.hex("ff6347");
+  static final Color turquoise = const Color.hex("40e0d0");
+  static final Color violet = const Color.hex("ee82ee");
+  static final Color wheat = const Color.hex("f5deb3");
+  static final Color white = const Color.hex("ffffff");
+  static final Color whiteSmoke = const Color.hex("f5f5f5");
+  static final Color yellow = const Color.hex("ffff00");
+  static final Color yellowGreen = const Color.hex("9acd32");
+}
+
+/**
+ * Rgba class for users that want to interact with a color as a RGBA value.
+ */
+class Rgba implements _StyleProperty, ColorBase {
+  // TODO(terry): Consider consolidating rgba to a single 32-bit int, make sure
+  //              it works under JS and Dart VM.
+  final int r;
+  final int g;
+  final int b;
+  final num a;
+
+  Rgba(int red, int green, int blue, [num alpha])
+      : this.r = Color._clamp(red, 0, 255),
+        this.g = Color._clamp(green, 0, 255),
+        this.b = Color._clamp(blue, 0, 255),
+        this.a = (alpha != null) ? Color._clamp(alpha, 0, 1) : alpha;
+
+  factory Rgba.fromString(String hexValue) =>
+      new Color.css("#${Color._convertCssToArgb(hexValue)}").rgba;
+
+  factory Rgba.fromColor(Color color) => color.rgba;
+
+  factory Rgba.fromArgbValue(num value) {
+    return new Rgba(((value.toInt() & 0xff000000) >> 0x18), /* a */
+        ((value.toInt() & 0xff0000) >> 0x10), /* r */
+        ((value.toInt() & 0xff00) >> 8), /* g */
+        ((value.toInt() & 0xff))); /* b */
+  }
+
+  factory Rgba.fromHsla(Hsla hsla) {
+    // Convert to Rgba.
+    // See site <http://easyrgb.com/index.php?X=MATH> for good documentation
+    // and color conversion routines.
+
+    num h = hsla.hue;
+    num s = hsla.saturation;
+    num l = hsla.lightness;
+    num a = hsla.alpha;
+
+    int r;
+    int g;
+    int b;
+
+    if (s == 0) {
+      r = (l * 255).round().toInt();
+      g = r;
+      b = r;
+    } else {
+      num var2;
+
+      if (l < 0.5) {
+        var2 = l * (1 + s);
+      } else {
+        var2 = (l + s) - (s * l);
+      }
+      num var1 = 2 * l - var2;
+
+      r = (255 * Rgba._hueToRGB(var1, var2, h + (1 / 3))).round().toInt();
+      g = (255 * Rgba._hueToRGB(var1, var2, h)).round().toInt();
+      b = (255 * Rgba._hueToRGB(var1, var2, h - (1 / 3))).round().toInt();
+    }
+
+    return new Rgba(r, g, b, a);
+  }
+
+  static num _hueToRGB(num v1, num v2, num vH) {
+    if (vH < 0) {
+      vH += 1;
+    }
+
+    if (vH > 1) {
+      vH -= 1;
+    }
+
+    if ((6 * vH) < 1) {
+      return (v1 + (v2 - v1) * 6 * vH);
+    }
+
+    if ((2 * vH) < 1) {
+      return v2;
+    }
+
+    if ((3 * vH) < 2) {
+      return (v1 + (v2 - v1) * ((2 / 3 - vH) * 6));
+    }
+
+    return v1;
+  }
+
+  bool operator ==(other) => Color.equal(this, other);
+
+  String get cssExpression {
+    if (a == null) {
+      return "#${Color.convertToHexString(r, g, b)}";
+    } else {
+      return "rgba($r,$g,$b,$a)";
+    }
+  }
+
+  String toHexArgbString() => Color.convertToHexString(r, g, b, a);
+
+  int get argbValue {
+    int value = 0;
+    if (a != null) {
+      value = (a.toInt() << 0x18);
+    }
+    value += (r << 0x10);
+    value += (g << 0x08);
+    value += b;
+    return value;
+  }
+
+  Color get color => new Color.createRgba(r, g, b, a);
+  Hsla get hsla => new Hsla.fromRgba(this);
+
+  Rgba darker(num amount) => Color._createNewTintShadeFromRgba(this, -amount);
+  Rgba lighter(num amount) => Color._createNewTintShadeFromRgba(this, amount);
+
+  int get hashCode => toHexArgbString().hashCode;
+}
+
+/**
+ * Hsl class support to interact with a color as a hsl with hue, saturation, and
+ * lightness with optional alpha blending.  The hue is a ratio of 360 degrees
+ * 360° = 1 or 0, (1° == (1/360)), saturation and lightness is a 0..1 fraction
+ * (1 == 100%) and alpha is a 0..1 fraction.
+ */
+class Hsla implements _StyleProperty, ColorBase {
+  final num _h; // Value from 0..1
+  final num _s; // Value from 0..1
+  final num _l; // Value from 0..1
+  final num _a; // Value from 0..1
+
+  /**
+   * [hue] is a 0..1 fraction of 360 degrees (360 == 0).
+   * [saturation] is a 0..1 fraction (100% == 1).
+   * [lightness] is a 0..1 fraction (100% == 1).
+   * [alpha] is a 0..1 fraction, alpha blending between 0..1, 1 == 100% opaque.
+   */
+  Hsla(num hue, num saturation, num lightness, [num alpha])
+      : this._h = (hue == 1) ? 0 : Color._clamp(hue, 0, 1),
+        this._s = Color._clamp(saturation, 0, 1),
+        this._l = Color._clamp(lightness, 0, 1),
+        this._a = (alpha != null) ? Color._clamp(alpha, 0, 1) : alpha;
+
+  factory Hsla.fromString(String hexValue) {
+    Rgba rgba = new Color.css("#${Color._convertCssToArgb(hexValue)}").rgba;
+    return _createFromRgba(rgba.r, rgba.g, rgba.b, rgba.a);
+  }
+
+  factory Hsla.fromColor(Color color) {
+    Rgba rgba = color.rgba;
+    return _createFromRgba(rgba.r, rgba.g, rgba.b, rgba.a);
+  }
+
+  factory Hsla.fromArgbValue(num value) {
+    num a = (value.toInt() & 0xff000000) >> 0x18;
+    int r = (value.toInt() & 0xff0000) >> 0x10;
+    int g = (value.toInt() & 0xff00) >> 8;
+    int b = value.toInt() & 0xff;
+
+    // Convert alpha to 0..1 from (0..255).
+    if (a != null) {
+      a = double.parse((a / 255).toStringAsPrecision(2));
+    }
+
+    return _createFromRgba(r, g, b, a);
+  }
+
+  factory Hsla.fromRgba(Rgba rgba) =>
+      _createFromRgba(rgba.r, rgba.g, rgba.b, rgba.a);
+
+  static Hsla _createFromRgba(num r, num g, num b, num a) {
+    // Convert RGB to hsl.
+    // See site <http://easyrgb.com/index.php?X=MATH> for good documentation
+    // and color conversion routines.
+    r /= 255;
+    g /= 255;
+    b /= 255;
+
+    // Hue, saturation and lightness.
+    num h;
+    num s;
+    num l;
+
+    num minRgb = math.min(r, math.min(g, b));
+    num maxRgb = math.max(r, math.max(g, b));
+    l = (maxRgb + minRgb) / 2;
+    if (l <= 0) {
+      return new Hsla(0, 0, l); // Black;
+    }
+
+    num vm = maxRgb - minRgb;
+    s = vm;
+    if (s > 0) {
+      s /= (l < 0.5) ? (maxRgb + minRgb) : (2 - maxRgb - minRgb);
+    } else {
+      return new Hsla(0, 0, l); // White
+    }
+
+    num r2, g2, b2;
+    r2 = (maxRgb - r) / vm;
+    g2 = (maxRgb - g) / vm;
+    b2 = (maxRgb - b) / vm;
+    if (r == maxRgb) {
+      h = (g == minRgb) ? 5.0 + b2 : 1 - g2;
+    } else if (g == maxRgb) {
+      h = (b == minRgb) ? 1 + r2 : 3 - b2;
+    } else {
+      h = (r == minRgb) ? 3 + g2 : 5 - r2;
+    }
+    h /= 6;
+
+    return new Hsla(h, s, l, a);
+  }
+
+  /**
+   * Returns 0..1 fraction (ratio of 360°, e.g. 1° == 1/360).
+   */
+  num get hue => _h;
+
+  /**
+   * Returns 0..1 fraction (1 == 100%)
+   */
+  num get saturation => _s;
+
+  /**
+   * Returns 0..1 fraction (1 == 100%).
+   */
+  num get lightness => _l;
+
+  /**
+   * Returns number as degrees 0..360.
+   */
+  num get hueDegrees => (_h * 360).round();
+
+  /**
+   * Returns number as percentage 0..100
+   */
+  num get saturationPercentage => (_s * 100).round();
+
+  /**
+   * Returns number as percentage 0..100.
+   */
+  num get lightnessPercentage => (_l * 100).round();
+
+  /**
+   * Returns number as 0..1
+   */
+  num get alpha => _a;
+
+  bool operator ==(other) => Color.equal(this, other);
+
+  String get cssExpression => (_a == null)
+      ? "hsl($hueDegrees,$saturationPercentage,$lightnessPercentage)"
+      : "hsla($hueDegrees,$saturationPercentage,$lightnessPercentage,$_a)";
+
+  String toHexArgbString() => new Rgba.fromHsla(this).toHexArgbString();
+
+  int get argbValue => Color.hexToInt(this.toHexArgbString());
+
+  Color get color => new Color.createHsla(_h, _s, _l, _a);
+  Rgba get rgba => new Rgba.fromHsla(this);
+
+  Hsla darker(num amount) =>
+      new Hsla.fromRgba(new Rgba.fromHsla(this).darker(amount));
+
+  Hsla lighter(num amount) =>
+      new Hsla.fromRgba(new Rgba.fromHsla(this).lighter(amount));
+
+  int get hashCode => toHexArgbString().hashCode;
+}
+
+/** X,Y position. */
+class PointXY implements _StyleProperty {
+  final num x, y;
+  const PointXY(this.x, this.y);
+
+  String get cssExpression {
+    // TODO(terry): TBD
+  }
+}
+
+// TODO(terry): Implement style and color.
+/**
+ * Supports border for measuring with layout.
+ */
+class Border implements _StyleProperty {
+  final int top, left, bottom, right;
+
+  // TODO(terry): Just like CSS, 1-arg -> set all properties, 2-args -> top and
+  //               bottom are first arg, left and right are second, 3-args, and
+  //               4-args -> tlbr or trbl.
+  const Border([this.top, this.left, this.bottom, this.right]);
+
+  // TODO(terry): Consider using Size or width and height.
+  Border.uniform(num amount)
+      : top = amount,
+        left = amount,
+        bottom = amount,
+        right = amount;
+
+  int get width => left + right;
+  int get height => top + bottom;
+
+  String get cssExpression {
+    return (top == left && bottom == right && top == right)
+        ? "${left}px"
+        : "${top != null ? '$top' : '0'}px ${
+      right != null ? '$right' : '0'}px ${
+      bottom != null ? '$bottom' : '0'}px ${
+      left != null ? '$left' : '0'}px";
+  }
+}
+
+/** Font style constants. */
+class FontStyle {
+  /** Font style [normal] default. */
+  static const String normal = "normal";
+  /**
+   * Font style [italic] use explicity crafted italic font otherwise inclined
+   * on the fly like oblique.
+   */
+  static const String italic = "italic";
+  /**
+   * Font style [oblique] is rarely used. The normal style of a font is inclined
+   * on the fly to the right by 8-12 degrees.
+   */
+  static const String oblique = "oblique";
+}
+
+/** Font variant constants. */
+class FontVariant {
+  /** Font style [normal] default. */
+  static const String normal = "normal";
+  /** Font variant [smallCaps]. */
+  static const String smallCaps = "small-caps";
+}
+
+/** Font weight constants values 100, 200, 300, 400, 500, 600, 700, 800, 900. */
+class FontWeight {
+  /** Font weight normal [default] */
+  static const int normal = 400;
+  /** Font weight bold */
+  static const int bold = 700;
+
+  static const int wt100 = 100;
+  static const int wt200 = 200;
+  static const int wt300 = 300;
+  static const int wt400 = 400;
+  static const int wt500 = 500;
+  static const int wt600 = 600;
+  static const int wt700 = 700;
+  static const int wt800 = 800;
+  static const int wt900 = 900;
+}
+
+/** Generic font family names. */
+class FontGeneric {
+  /** Generic family sans-serif font (w/o serifs). */
+  static const String sansSerif = "sans-serif";
+  /** Generic family serif font. */
+  static const String serif = "serif";
+  /** Generic family fixed-width font. */
+  static const monospace = "monospace";
+  /** Generic family emulate handwriting font. */
+  static const String cursive = "cursive";
+  /** Generic family decorative font. */
+  static const String fantasy = "fantasy";
+}
+
+/**
+ * List of most common font families across different platforms.  Use the
+ * collection names in the Font class (e.g., Font.SANS_SERIF, Font.FONT_SERIF,
+ * Font.MONOSPACE, Font.CURSIVE or Font.FANTASY).  These work best on all
+ * platforms using the fonts that best match availability on each platform.
+ * See <http://www.angelfire.com/al4/rcollins/style/fonts.html> for a good
+ * description of fonts available between platforms and browsers.
+ */
+class FontFamily {
+  /** Sans-Serif font for Windows similar to Helvetica on Mac bold/italic. */
+  static const String arial = "arial";
+  /** Sans-Serif font for Windows less common already bolded. */
+  static const String arialBlack = "arial black";
+  /** Sans-Serif font for Mac since 1984, similar to Arial/Helvetica. */
+  static const String geneva = "geneva";
+  /** Sans-Serif font for Windows most readable sans-serif font for displays. */
+  static const String verdana = "verdana";
+  /** Sans-Serif font for Mac since 1984 is identical to Arial. */
+  static const String helvetica = "helvetica";
+
+  /** Serif font for Windows traditional font with “old-style” numerals. */
+  static const String georgia = "georgia";
+  /**
+   * Serif font for Mac. PCs may have the non-scalable Times use Times New
+   * Roman instead.  Times is more compact than Times New Roman.
+   */
+  static const String times = "times";
+  /**
+   * Serif font for Windows most common serif font and default serif font for
+   * most browsers.
+   */
+  static const String timesNewRoman = "times new roman";
+
+  /**
+   * Monospace font for Mac/Windows most common. Scalable on Mac not scalable
+   * on Windows.
+   */
+  static const String courier = "courier";
+  /** Monospace font for Mac/Windows scalable on both platforms. */
+  static const String courierNew = "courier new";
+
+  /** Cursive font for Windows and default cursive font for IE. */
+  static const String comicSansMs = "comic sans ms";
+  /** Cursive font for Mac on Macs 2000 and newer. */
+  static const String textile = "textile";
+  /** Cursive font for older Macs. */
+  static const String appleChancery = "apple chancery";
+  /** Cursive font for some PCs. */
+  static const String zaphChancery = "zaph chancery";
+
+  /** Fantasy font on most Mac/Windows/Linux platforms. */
+  static const String impact = "impact";
+  /** Fantasy font for Windows. */
+  static const String webdings = "webdings";
+}
+
+class LineHeight {
+  final num height;
+  final bool inPixels;
+  const LineHeight(this.height, {this.inPixels: true});
+}
+
+// TODO(terry): Support @font-face fule.
+/**
+ * Font style support for size, family, weight, style, variant, and lineheight.
+ */
+class Font implements _StyleProperty {
+  /** Collection of most common sans-serif fonts in order. */
+  static const List<String> sansSerif = const [
+    FontFamily.arial,
+    FontFamily.verdana,
+    FontFamily.geneva,
+    FontFamily.helvetica,
+    FontGeneric.sansSerif
+  ];
+
+  /** Collection of most common serif fonts in order. */
+  static const List<String> serif = const [
+    FontFamily.georgia,
+    FontFamily.timesNewRoman,
+    FontFamily.times,
+    FontGeneric.serif
+  ];
+  /** Collection of most common monospace fonts in order. */
+  static const List<String> monospace = const [
+    FontFamily.courierNew,
+    FontFamily.courier,
+    FontGeneric.monospace
+  ];
+  /** Collection of most common cursive fonts in order. */
+  static const List<String> cursive = const [
+    FontFamily.textile,
+    FontFamily.appleChancery,
+    FontFamily.zaphChancery,
+    FontGeneric.fantasy
+  ];
+  /** Collection of most common fantasy fonts in order. */
+  static const List<String> fantasy = const [
+    FontFamily.comicSansMs,
+    FontFamily.impact,
+    FontFamily.webdings,
+    FontGeneric.fantasy
+  ];
+
+  // TODO(terry): Should support the values xx-small, small, large, xx-large,
+  //              etc. (mapped to a pixel sized font)?
+  /** Font size in pixels. */
+  final num size;
+
+  // TODO(terry): _family should be an immutable list, wrapper class to do this
+  //              should exist in Dart.
+  /**
+   * Family specifies a list of fonts, the browser will sequentially select the
+   * the first known/supported font.  There are two types of font families the
+   * family-name (e.g., arial, times, courier, etc) or the generic-family (e.g.,
+   * serif, sans-seric, etc.)
+   */
+  final List<String> family;
+
+  /** Font weight from 100, 200, 300, 400, 500, 600, 700, 800, 900 */
+  final int weight;
+
+  /** Style of a font normal, italic, oblique. */
+  final String style;
+
+  /**
+   * Font variant NORMAL (default) or SMALL_CAPS.  Different set of font glyph
+   * lower case letters designed to have to fit within the font-height and
+   * weight of the corresponding lowercase letters.
+   */
+  final String variant;
+
+  final LineHeight lineHeight;
+
+  // TODO(terry): Size and computedLineHeight are in pixels.  Need to figure out
+  //              how to handle in other units (specified in other units) like
+  //              points, inches, etc.  Do we have helpers like Units.Points(12)
+  //              where 12 is in points and that's converted to pixels?
+  // TODO(terry): lineHeight is computed as 1.2 although CSS_RESET is 1.0 we
+  //              need to be consistent some browsers use 1 others 1.2.
+  // TODO(terry): There is a school of thought "Golden Ratio Typography".
+  // Where width to display the text is also important in computing the line
+  // height.  Classic typography suggest the ratio be 1.5.  See
+  // <http://www.pearsonified.com/2011/12/golden-ratio-typography.php> and
+  // <http://meyerweb.com/eric/thoughts/2008/05/06/line-height-abnormal/>.
+  /**
+   * Create a font using [size] of font in pixels, [family] name of font(s)
+   * using [FontFamily], [style] of the font using [FontStyle], [variant] using
+   * [FontVariant], and [lineHeight] extra space (leading) around the font in
+   * pixels, if not specified it's 1.2 the font size.
+   */
+  const Font({this.size, this.family, this.weight, this.style, this.variant,
+      this.lineHeight});
+
+  /**
+   * Merge the two fonts and return the result. See [Style.merge] for
+   * more information.
+   */
+  factory Font.merge(Font a, Font b) {
+    if (a == null) return b;
+    if (b == null) return a;
+    return new Font._merge(a, b);
+  }
+
+  Font._merge(Font a, Font b)
+      : size = _mergeVal(a.size, b.size),
+        family = _mergeVal(a.family, b.family),
+        weight = _mergeVal(a.weight, b.weight),
+        style = _mergeVal(a.style, b.style),
+        variant = _mergeVal(a.variant, b.variant),
+        lineHeight = _mergeVal(a.lineHeight, b.lineHeight);
+
+  /**
+   * Shorthand CSS format for font is:
+   *
+   *    font-style font-variant font-weight font-size/line-height font-family
+   *
+   * The font-size and font-family values are required. If any of the other
+   * values are missing the default value is used.
+   */
+  String get cssExpression {
+    // TODO(jimhug): include variant, style, other options
+    if (weight != null) {
+      // TODO(jacobr): is this really correct for lineHeight?
+      if (lineHeight != null) {
+        return "$weight ${size}px/$lineHeightInPixels $_fontsAsString";
+      }
+      return '$weight ${size}px $_fontsAsString';
+    }
+
+    return '${size}px $_fontsAsString';
+  }
+
+  Font scale(num ratio) => new Font(
+      size: size * ratio,
+      family: family,
+      weight: weight,
+      style: style,
+      variant: variant);
+
+  /**
+   * The lineHeight, provides an indirect means to specify the leading. The
+   * leading is the difference between the font-size height and the (used)
+   * value of line height in pixels.  If lineHeight is not specified it's
+   * automatically computed as 1.2 of the font size.  Firefox is 1.2, Safari is
+   * ~1.2, and CSS suggest a ration from 1 to 1.2 of the font-size when
+   * computing line-height. The Font class constructor has the computation for
+   * _lineHeight.
+   */
+  num get lineHeightInPixels {
+    if (lineHeight != null) {
+      if (lineHeight.inPixels) {
+        return lineHeight.height;
+      } else {
+        return (size != null) ? lineHeight.height * size : null;
+      }
+    } else {
+      return (size != null) ? size * 1.2 : null;
+    }
+  }
+
+  int get hashCode {
+    // TODO(jimhug): Lot's of potential collisions here. List of fonts, etc.
+    return size.toInt() % family[0].hashCode;
+  }
+
+  bool operator ==(other) {
+    if (other is! Font) return false;
+    Font o = other;
+    return o.size == size &&
+        o.family == family &&
+        o.weight == weight &&
+        o.lineHeight == lineHeight &&
+        o.style == style &&
+        o.variant == variant;
+  }
+
+  // TODO(terry): This is fragile should probably just iterate through the list
+  //              of fonts construction the font-family string.
+  /** Return fonts as a comma seperated list sans the square brackets. */
+  String get _fontsAsString {
+    String fonts = family.toString();
+    return fonts.length > 2 ? fonts.substring(1, fonts.length - 1) : "";
+  }
+}
+
+/**
+ * This class stores the sizes of the box edges in the CSS [box model][]. Each
+ * edge area is placed around the sides of the content box. The innermost area
+ * is the [Style.padding] area which has a background and surrounds the content.
+ * The content and padding area is surrounded by the [Style.border], which
+ * itself is surrounded by the transparent [Style.margin]. This box represents
+ * the eges of padding, border, or margin depending on which accessor was used
+ * to retrieve it.
+ *
+ * [box model]: https://developer.mozilla.org/en/CSS/box_model
+ */
+class BoxEdge {
+  /** The size of the left edge, or null if the style has no edge. */
+  final num left;
+
+  /** The size of the top edge, or null if the style has no edge. */
+  final num top;
+
+  /** The size of the right edge, or null if the style has no edge. */
+  final num right;
+
+  /** The size of the bottom edge, or null if the style has no edge. */
+  final num bottom;
+
+  /**
+   * Creates a box edge with the specified [left], [top], [right], and
+   * [bottom] width.
+   */
+  const BoxEdge([this.left, this.top, this.right, this.bottom]);
+
+  /**
+   * Creates a box edge with the specified [top], [right], [bottom], and
+   * [left] width. This matches the typical CSS order:
+   * <https://developer.mozilla.org/en/CSS/margin>
+   * <https://developer.mozilla.org/en/CSS/border-width>
+   * <https://developer.mozilla.org/en/CSS/padding>.
+   */
+  const BoxEdge.clockwiseFromTop(this.top, this.right, this.bottom, this.left);
+
+  /**
+   * This is a helper to creates a box edge with the same [left], [top]
+   * [right], and [bottom] widths.
+   */
+  const BoxEdge.uniform(num size)
+      : top = size,
+        left = size,
+        bottom = size,
+        right = size;
+
+  /**
+   * Takes a possibly null box edge, with possibly null metrics, and fills
+   * them in with 0 instead.
+   */
+  factory BoxEdge.nonNull(BoxEdge other) {
+    if (other == null) return const BoxEdge(0, 0, 0, 0);
+    num left = other.left;
+    num top = other.top;
+    num right = other.right;
+    num bottom = other.bottom;
+    bool make = false;
+    if (left == null) {
+      make = true;
+      left = 0;
+    }
+    if (top == null) {
+      make = true;
+      top = 0;
+    }
+    if (right == null) {
+      make = true;
+      right = 0;
+    }
+    if (bottom == null) {
+      make = true;
+      bottom = 0;
+    }
+    return make ? new BoxEdge(left, top, right, bottom) : other;
+  }
+
+  /**
+   * Merge the two box edge sizes and return the result. See [Style.merge] for
+   * more information.
+   */
+  factory BoxEdge.merge(BoxEdge x, BoxEdge y) {
+    if (x == null) return y;
+    if (y == null) return x;
+    return new BoxEdge._merge(x, y);
+  }
+
+  BoxEdge._merge(BoxEdge x, BoxEdge y)
+      : left = _mergeVal(x.left, y.left),
+        top = _mergeVal(x.top, y.top),
+        right = _mergeVal(x.right, y.right),
+        bottom = _mergeVal(x.bottom, y.bottom);
+
+  /**
+   * The total size of the horizontal edges. Equal to [left] + [right], where
+   * null is interpreted as 0px.
+   */
+  num get width => (left != null ? left : 0) + (right != null ? right : 0);
+
+  /**
+   * The total size of the vertical edges. Equal to [top] + [bottom], where
+   * null is interpreted as 0px.
+   */
+  num get height => (top != null ? top : 0) + (bottom != null ? bottom : 0);
+}
+
+_mergeVal(x, y) => y != null ? y : x;
diff --git a/csslib/lib/src/token.dart b/csslib/lib/src/token.dart
new file mode 100644
index 0000000..e30484d
--- /dev/null
+++ b/csslib/lib/src/token.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2012, 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.
+
+part of csslib.parser;
+
+/**
+ * A single token in the Dart language.
+ */
+class Token {
+  /** A member of [TokenKind] specifying what kind of token this is. */
+  final int kind;
+
+  /** The location where this token was parsed from. */
+  final FileSpan span;
+
+  /** The start offset of this token. */
+  int get start => span.start.offset;
+
+  /** The end offset of this token. */
+  int get end => span.end.offset;
+
+  /** Returns the source text corresponding to this [Token]. */
+  String get text => span.text;
+
+  Token(this.kind, this.span);
+
+  /** Returns a pretty representation of this token for error messages. **/
+  String toString() {
+    var kindText = TokenKind.kindToString(kind);
+    var actualText = text.trim();
+    if (kindText != actualText) {
+      if (actualText.length > 10) {
+        actualText = '${actualText.substring(0, 8)}...';
+      }
+      return '$kindText($actualText)';
+    } else {
+      return kindText;
+    }
+  }
+}
+
+/** A token containing a parsed literal value. */
+class LiteralToken extends Token {
+  var value;
+  LiteralToken(int kind, FileSpan span, this.value) : super(kind, span);
+}
+
+/** A token containing error information. */
+class ErrorToken extends Token {
+  String message;
+  ErrorToken(int kind, FileSpan span, this.message) : super(kind, span);
+}
+
+/**
+ * CSS ident-token.
+ *
+ * See <http://dev.w3.org/csswg/css-syntax/#typedef-ident-token> and
+ * <http://dev.w3.org/csswg/css-syntax/#ident-token-diagram>.
+ */
+class IdentifierToken extends Token {
+  final String text;
+
+  IdentifierToken(this.text, int kind, FileSpan span) : super(kind, span);
+}
diff --git a/csslib/lib/src/tokenizer.dart b/csslib/lib/src/tokenizer.dart
new file mode 100644
index 0000000..f5c5210
--- /dev/null
+++ b/csslib/lib/src/tokenizer.dart
@@ -0,0 +1,456 @@
+// Copyright (c) 2012, 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.
+
+part of csslib.parser;
+
+class Tokenizer extends TokenizerBase {
+  /** U+ prefix for unicode characters. */
+  final UNICODE_U = 'U'.codeUnitAt(0);
+  final UNICODE_LOWER_U = 'u'.codeUnitAt(0);
+  final UNICODE_PLUS = '+'.codeUnitAt(0);
+
+  final QUESTION_MARK = '?'.codeUnitAt(0);
+
+  /** CDATA keyword. */
+  final List CDATA_NAME = 'CDATA'.codeUnits;
+
+  Tokenizer(SourceFile file, String text, bool skipWhitespace, [int index = 0])
+      : super(file, text, skipWhitespace, index);
+
+  Token next({unicodeRange: false}) {
+    // keep track of our starting position
+    _startIndex = _index;
+
+    int ch;
+    ch = _nextChar();
+    switch (ch) {
+      case TokenChar.NEWLINE:
+      case TokenChar.RETURN:
+      case TokenChar.SPACE:
+      case TokenChar.TAB:
+        return finishWhitespace();
+      case TokenChar.END_OF_FILE:
+        return _finishToken(TokenKind.END_OF_FILE);
+      case TokenChar.AT:
+        int peekCh = _peekChar();
+        if (TokenizerHelpers.isIdentifierStart(peekCh)) {
+          var oldIndex = _index;
+          var oldStartIndex = _startIndex;
+
+          _startIndex = _index;
+          ch = _nextChar();
+          finishIdentifier();
+
+          // Is it a directive?
+          int tokId = TokenKind.matchDirectives(
+              _text, _startIndex, _index - _startIndex);
+          if (tokId == -1) {
+            // No, is it a margin directive?
+            tokId = TokenKind.matchMarginDirectives(
+                _text, _startIndex, _index - _startIndex);
+          }
+
+          if (tokId != -1) {
+            return _finishToken(tokId);
+          } else {
+            // Didn't find a CSS directive or margin directive so the @name is
+            // probably the Less definition '@name: value_variable_definition'.
+            _startIndex = oldStartIndex;
+            _index = oldIndex;
+          }
+        }
+        return _finishToken(TokenKind.AT);
+      case TokenChar.DOT:
+        int start = _startIndex; // Start where the dot started.
+        if (maybeEatDigit()) {
+          // looks like a number dot followed by digit(s).
+          Token number = finishNumber();
+          if (number.kind == TokenKind.INTEGER) {
+            // It's a number but it's preceeded by a dot, so make it a double.
+            _startIndex = start;
+            return _finishToken(TokenKind.DOUBLE);
+          } else {
+            // Don't allow dot followed by a double (e.g,  '..1').
+            return _errorToken();
+          }
+        }
+        // It's really a dot.
+        return _finishToken(TokenKind.DOT);
+      case TokenChar.LPAREN:
+        return _finishToken(TokenKind.LPAREN);
+      case TokenChar.RPAREN:
+        return _finishToken(TokenKind.RPAREN);
+      case TokenChar.LBRACE:
+        return _finishToken(TokenKind.LBRACE);
+      case TokenChar.RBRACE:
+        return _finishToken(TokenKind.RBRACE);
+      case TokenChar.LBRACK:
+        return _finishToken(TokenKind.LBRACK);
+      case TokenChar.RBRACK:
+        if (_maybeEatChar(TokenChar.RBRACK) &&
+            _maybeEatChar(TokenChar.GREATER)) {
+          // ]]>
+          return next();
+        }
+        return _finishToken(TokenKind.RBRACK);
+      case TokenChar.HASH:
+        return _finishToken(TokenKind.HASH);
+      case TokenChar.PLUS:
+        if (maybeEatDigit()) return finishNumber();
+        return _finishToken(TokenKind.PLUS);
+      case TokenChar.MINUS:
+        if (inSelectorExpression || unicodeRange) {
+          // If parsing in pseudo function expression then minus is an operator
+          // not part of identifier e.g., interval value range (e.g. U+400-4ff)
+          // or minus operator in selector expression.
+          return _finishToken(TokenKind.MINUS);
+        } else if (maybeEatDigit()) {
+          return finishNumber();
+        } else if (TokenizerHelpers.isIdentifierStart(ch)) {
+          return finishIdentifier();
+        }
+        return _finishToken(TokenKind.MINUS);
+      case TokenChar.GREATER:
+        return _finishToken(TokenKind.GREATER);
+      case TokenChar.TILDE:
+        if (_maybeEatChar(TokenChar.EQUALS)) {
+          return _finishToken(TokenKind.INCLUDES); // ~=
+        }
+        return _finishToken(TokenKind.TILDE);
+      case TokenChar.ASTERISK:
+        if (_maybeEatChar(TokenChar.EQUALS)) {
+          return _finishToken(TokenKind.SUBSTRING_MATCH); // *=
+        }
+        return _finishToken(TokenKind.ASTERISK);
+      case TokenChar.AMPERSAND:
+        return _finishToken(TokenKind.AMPERSAND);
+      case TokenChar.NAMESPACE:
+        if (_maybeEatChar(TokenChar.EQUALS)) {
+          return _finishToken(TokenKind.DASH_MATCH); // |=
+        }
+        return _finishToken(TokenKind.NAMESPACE);
+      case TokenChar.COLON:
+        return _finishToken(TokenKind.COLON);
+      case TokenChar.COMMA:
+        return _finishToken(TokenKind.COMMA);
+      case TokenChar.SEMICOLON:
+        return _finishToken(TokenKind.SEMICOLON);
+      case TokenChar.PERCENT:
+        return _finishToken(TokenKind.PERCENT);
+      case TokenChar.SINGLE_QUOTE:
+        return _finishToken(TokenKind.SINGLE_QUOTE);
+      case TokenChar.DOUBLE_QUOTE:
+        return _finishToken(TokenKind.DOUBLE_QUOTE);
+      case TokenChar.SLASH:
+        if (_maybeEatChar(TokenChar.ASTERISK)) return finishMultiLineComment();
+        return _finishToken(TokenKind.SLASH);
+      case TokenChar.LESS: // <!--
+        if (_maybeEatChar(TokenChar.BANG)) {
+          if (_maybeEatChar(TokenChar.MINUS) &&
+              _maybeEatChar(TokenChar.MINUS)) {
+            return finishMultiLineComment();
+          } else if (_maybeEatChar(TokenChar.LBRACK) &&
+              _maybeEatChar(CDATA_NAME[0]) &&
+              _maybeEatChar(CDATA_NAME[1]) &&
+              _maybeEatChar(CDATA_NAME[2]) &&
+              _maybeEatChar(CDATA_NAME[3]) &&
+              _maybeEatChar(CDATA_NAME[4]) &&
+              _maybeEatChar(TokenChar.LBRACK)) {
+            // <![CDATA[
+            return next();
+          }
+        }
+        return _finishToken(TokenKind.LESS);
+      case TokenChar.EQUALS:
+        return _finishToken(TokenKind.EQUALS);
+      case TokenChar.CARET:
+        if (_maybeEatChar(TokenChar.EQUALS)) {
+          return _finishToken(TokenKind.PREFIX_MATCH); // ^=
+        }
+        return _finishToken(TokenKind.CARET);
+      case TokenChar.DOLLAR:
+        if (_maybeEatChar(TokenChar.EQUALS)) {
+          return _finishToken(TokenKind.SUFFIX_MATCH); // $=
+        }
+        return _finishToken(TokenKind.DOLLAR);
+      case TokenChar.BANG:
+        Token tok = finishIdentifier();
+        return (tok == null) ? _finishToken(TokenKind.BANG) : tok;
+      default:
+        // TODO(jmesserly): this is used for IE8 detection; I'm not sure it's
+        // appropriate outside of a few specific places; certainly shouldn't
+        // be parsed in selectors.
+        if (!inSelector && ch == TokenChar.BACKSLASH) {
+          return _finishToken(TokenKind.BACKSLASH);
+        }
+
+        if (unicodeRange) {
+          // Three types of unicode ranges:
+          //   - single code point (e.g. U+416)
+          //   - interval value range (e.g. U+400-4ff)
+          //   - range where trailing ‘?’ characters imply ‘any digit value’
+          //   (e.g. U+4??)
+          if (maybeEatHexDigit()) {
+            var t = finishHexNumber();
+            // Any question marks then it's a HEX_RANGE not HEX_NUMBER.
+            if (maybeEatQuestionMark()) finishUnicodeRange();
+            return t;
+          } else if (maybeEatQuestionMark()) {
+            // HEX_RANGE U+N???
+            return finishUnicodeRange();
+          } else {
+            return _errorToken();
+          }
+        } else if ((ch == UNICODE_U || ch == UNICODE_LOWER_U) &&
+            (_peekChar() == UNICODE_PLUS)) {
+          // Unicode range: U+uNumber[-U+uNumber]
+          //   uNumber = 0..10FFFF
+          _nextChar(); // Skip +
+          _startIndex = _index; // Starts at the number
+          return _finishToken(TokenKind.UNICODE_RANGE);
+        } else if (varDef(ch)) {
+          return _finishToken(TokenKind.VAR_DEFINITION);
+        } else if (varUsage(ch)) {
+          return _finishToken(TokenKind.VAR_USAGE);
+        } else if (TokenizerHelpers.isIdentifierStart(ch)) {
+          return finishIdentifier();
+        } else if (TokenizerHelpers.isDigit(ch)) {
+          return finishNumber();
+        }
+        return _errorToken();
+    }
+  }
+
+  bool varDef(int ch) {
+    return ch == 'v'.codeUnitAt(0) &&
+        _maybeEatChar('a'.codeUnitAt(0)) &&
+        _maybeEatChar('r'.codeUnitAt(0)) &&
+        _maybeEatChar('-'.codeUnitAt(0));
+  }
+
+  bool varUsage(int ch) {
+    return ch == 'v'.codeUnitAt(0) &&
+        _maybeEatChar('a'.codeUnitAt(0)) &&
+        _maybeEatChar('r'.codeUnitAt(0)) &&
+        (_peekChar() == '-'.codeUnitAt(0));
+  }
+
+  Token _errorToken([String message = null]) {
+    return _finishToken(TokenKind.ERROR);
+  }
+
+  int getIdentifierKind() {
+    // Is the identifier a unit type?
+    int tokId = -1;
+
+    // Don't match units in selectors or selector expressions.
+    if (!inSelectorExpression && !inSelector) {
+      tokId = TokenKind.matchUnits(_text, _startIndex, _index - _startIndex);
+    }
+    if (tokId == -1) {
+      tokId = (_text.substring(_startIndex, _index) == '!important')
+          ? TokenKind.IMPORTANT
+          : -1;
+    }
+
+    return tokId >= 0 ? tokId : TokenKind.IDENTIFIER;
+  }
+
+  Token finishIdentifier() {
+    // If we encounter an escape sequence, remember it so we can post-process
+    // to unescape.
+    var chars = [];
+
+    // backup so we can start with the first character
+    int validateFrom = _index;
+    _index = _startIndex;
+    while (_index < _text.length) {
+      int ch = _text.codeUnitAt(_index);
+
+      // If the previous character was "\" we need to escape. T
+      // http://www.w3.org/TR/CSS21/syndata.html#characters
+      // if followed by hexadecimal digits, create the appropriate character.
+      // otherwise, include the character in the identifier and don't treat it
+      // specially.
+      if (ch == 92 /*\*/) {
+        int startHex = ++_index;
+        eatHexDigits(startHex + 6);
+        if (_index != startHex) {
+          // Parse the hex digits and add that character.
+          chars.add(int.parse('0x' + _text.substring(startHex, _index)));
+
+          if (_index == _text.length) break;
+
+          // if we stopped the hex because of a whitespace char, skip it
+          ch = _text.codeUnitAt(_index);
+          if (_index - startHex != 6 &&
+              (ch == TokenChar.SPACE ||
+                  ch == TokenChar.TAB ||
+                  ch == TokenChar.RETURN ||
+                  ch == TokenChar.NEWLINE)) {
+            _index++;
+          }
+        } else {
+          // not a digit, just add the next character literally
+          if (_index == _text.length) break;
+          chars.add(_text.codeUnitAt(_index++));
+        }
+      } else if (_index < validateFrom ||
+          (inSelectorExpression
+              ? TokenizerHelpers.isIdentifierPartExpr(ch)
+              : TokenizerHelpers.isIdentifierPart(ch))) {
+        chars.add(ch);
+        _index++;
+      } else {
+        // Not an identifier or escaped character.
+        break;
+      }
+    }
+
+    var span = _file.span(_startIndex, _index);
+    var text = new String.fromCharCodes(chars);
+
+    return new IdentifierToken(text, getIdentifierKind(), span);
+  }
+
+  Token finishNumber() {
+    eatDigits();
+
+    if (_peekChar() == 46 /*.*/) {
+      // Handle the case of 1.toString().
+      _nextChar();
+      if (TokenizerHelpers.isDigit(_peekChar())) {
+        eatDigits();
+        return _finishToken(TokenKind.DOUBLE);
+      } else {
+        _index -= 1;
+      }
+    }
+
+    return _finishToken(TokenKind.INTEGER);
+  }
+
+  bool maybeEatDigit() {
+    if (_index < _text.length &&
+        TokenizerHelpers.isDigit(_text.codeUnitAt(_index))) {
+      _index += 1;
+      return true;
+    }
+    return false;
+  }
+
+  Token finishHexNumber() {
+    eatHexDigits(_text.length);
+    return _finishToken(TokenKind.HEX_INTEGER);
+  }
+
+  void eatHexDigits(int end) {
+    end = math.min(end, _text.length);
+    while (_index < end) {
+      if (TokenizerHelpers.isHexDigit(_text.codeUnitAt(_index))) {
+        _index += 1;
+      } else {
+        return;
+      }
+    }
+  }
+
+  bool maybeEatHexDigit() {
+    if (_index < _text.length &&
+        TokenizerHelpers.isHexDigit(_text.codeUnitAt(_index))) {
+      _index += 1;
+      return true;
+    }
+    return false;
+  }
+
+  bool maybeEatQuestionMark() {
+    if (_index < _text.length && _text.codeUnitAt(_index) == QUESTION_MARK) {
+      _index += 1;
+      return true;
+    }
+    return false;
+  }
+
+  void eatQuestionMarks() {
+    while (_index < _text.length) {
+      if (_text.codeUnitAt(_index) == QUESTION_MARK) {
+        _index += 1;
+      } else {
+        return;
+      }
+    }
+  }
+
+  Token finishUnicodeRange() {
+    eatQuestionMarks();
+    return _finishToken(TokenKind.HEX_RANGE);
+  }
+
+  Token finishMultiLineComment() {
+    while (true) {
+      int ch = _nextChar();
+      if (ch == 0) {
+        return _finishToken(TokenKind.INCOMPLETE_COMMENT);
+      } else if (ch == 42 /*'*'*/) {
+        if (_maybeEatChar(47 /*'/'*/)) {
+          if (_skipWhitespace) {
+            return next();
+          } else {
+            return _finishToken(TokenKind.COMMENT);
+          }
+        }
+      } else if (ch == TokenChar.MINUS) {
+        /* Check if close part of Comment Definition --> (CDC). */
+        if (_maybeEatChar(TokenChar.MINUS)) {
+          if (_maybeEatChar(TokenChar.GREATER)) {
+            if (_skipWhitespace) {
+              return next();
+            } else {
+              return _finishToken(TokenKind.HTML_COMMENT);
+            }
+          }
+        }
+      }
+    }
+    return _errorToken();
+  }
+}
+
+/** Static helper methods. */
+class TokenizerHelpers {
+  static bool isIdentifierStart(int c) {
+    return isIdentifierStartExpr(c) || c == 45 /*-*/;
+  }
+
+  static bool isDigit(int c) {
+    return (c >= 48 /*0*/ && c <= 57 /*9*/);
+  }
+
+  static bool isHexDigit(int c) {
+    return (isDigit(c) ||
+        (c >= 97 /*a*/ && c <= 102 /*f*/) ||
+        (c >= 65 /*A*/ && c <= 70 /*F*/));
+  }
+
+  static bool isIdentifierPart(int c) {
+    return isIdentifierPartExpr(c) || c == 45 /*-*/;
+  }
+
+  /** Pseudo function expressions identifiers can't have a minus sign. */
+  static bool isIdentifierStartExpr(int c) {
+    return ((c >= 97 /*a*/ && c <= 122 /*z*/) ||
+        (c >= 65 /*A*/ && c <= 90 /*Z*/) ||
+        // Note: Unicode 10646 chars U+00A0 or higher are allowed, see:
+        // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+        // http://www.w3.org/TR/CSS21/syndata.html#characters
+        // Also, escaped character should be allowed.
+        c == 95 /*_*/ || c >= 0xA0 || c == 92 /*\*/);
+  }
+
+  /** Pseudo function expressions identifiers can't have a minus sign. */
+  static bool isIdentifierPartExpr(int c) {
+    return (isIdentifierStartExpr(c) || isDigit(c));
+  }
+}
diff --git a/csslib/lib/src/tokenizer_base.dart b/csslib/lib/src/tokenizer_base.dart
new file mode 100644
index 0000000..7999a8f
--- /dev/null
+++ b/csslib/lib/src/tokenizer_base.dart
@@ -0,0 +1,437 @@
+// Copyright (c) 2012, 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.
+// Generated by scripts/tokenizer_gen.py.
+
+part of csslib.parser;
+
+/** Tokenizer state to support look ahead for Less' nested selectors. */
+class TokenizerState {
+  final int index;
+  final int startIndex;
+  final bool inSelectorExpression;
+  final bool inSelector;
+
+  TokenizerState(TokenizerBase base)
+      : index = base._index,
+        startIndex = base._startIndex,
+        inSelectorExpression = base.inSelectorExpression,
+        inSelector = base.inSelector;
+}
+
+/**
+ * The base class for our tokenizer. The hand coded parts are in this file, with
+ * the generated parts in the subclass Tokenizer.
+ */
+abstract class TokenizerBase {
+  final SourceFile _file;
+  final String _text;
+
+  bool _skipWhitespace;
+
+  /**
+   * Changes tokenization when in a pseudo function expression.  If true then
+   * minus signs are handled as operators instead of identifiers.
+   */
+  bool inSelectorExpression = false;
+
+  /**
+   * Changes tokenization when in selectors. If true, it prevents identifiers
+   * from being treated as units. This would break things like ":lang(fr)" or
+   * the HTML (unknown) tag name "px", which is legal to use in a selector.
+   */
+  // TODO(jmesserly): is this a problem elsewhere? "fr" for example will be
+  // processed as a "fraction" unit token, preventing it from working in
+  // places where an identifier is expected. This was breaking selectors like:
+  //     :lang(fr)
+  // The assumption that "fr" always means fraction (and similar issue with
+  // other units) doesn't seem valid. We probably should defer this
+  // analysis until we reach places in the parser where units are expected.
+  // I'm not sure this is tokenizing as described in the specs:
+  //     http://dev.w3.org/csswg/css-syntax/
+  //     http://dev.w3.org/csswg/selectors4/
+  bool inSelector = false;
+
+  int _index = 0;
+  int _startIndex = 0;
+
+  static const String _CDATA_START = '<![CDATA[';
+  static const String _CDATA_END = ']]>';
+
+  TokenizerBase(this._file, this._text, this._skipWhitespace,
+      [this._index = 0]);
+
+  Token next();
+  int getIdentifierKind();
+
+  /** Snapshot of Tokenizer scanning state. */
+  TokenizerState get mark => new TokenizerState(this);
+
+  /** Restore Tokenizer scanning state. */
+  void restore(TokenizerState markedData) {
+    _index = markedData.index;
+    _startIndex = markedData.startIndex;
+    inSelectorExpression = markedData.inSelectorExpression;
+    inSelector = markedData.inSelector;
+  }
+
+  int _nextChar() {
+    if (_index < _text.length) {
+      return _text.codeUnitAt(_index++);
+    } else {
+      return 0;
+    }
+  }
+
+  int _peekChar() {
+    if (_index < _text.length) {
+      return _text.codeUnitAt(_index);
+    } else {
+      return 0;
+    }
+  }
+
+  bool _maybeEatChar(int ch) {
+    if (_index < _text.length) {
+      if (_text.codeUnitAt(_index) == ch) {
+        _index++;
+        return true;
+      } else {
+        return false;
+      }
+    } else {
+      return false;
+    }
+  }
+
+  String _tokenText() {
+    if (_index < _text.length) {
+      return _text.substring(_startIndex, _index);
+    } else {
+      return _text.substring(_startIndex, _text.length);
+    }
+  }
+
+  Token _finishToken(int kind) {
+    return new Token(kind, _file.span(_startIndex, _index));
+  }
+
+  Token _errorToken([String message = null]) {
+    return new ErrorToken(
+        TokenKind.ERROR, _file.span(_startIndex, _index), message);
+  }
+
+  Token finishWhitespace() {
+    _index--;
+    while (_index < _text.length) {
+      final ch = _text.codeUnitAt(_index++);
+      if (ch == TokenChar.SPACE ||
+          ch == TokenChar.TAB ||
+          ch == TokenChar.RETURN) {
+        // do nothing
+      } else if (ch == TokenChar.NEWLINE) {
+        if (!_skipWhitespace) {
+          return _finishToken(TokenKind.WHITESPACE); // note the newline?
+        }
+      } else {
+        _index--;
+        if (_skipWhitespace) {
+          return next();
+        } else {
+          return _finishToken(TokenKind.WHITESPACE);
+        }
+      }
+    }
+    return _finishToken(TokenKind.END_OF_FILE);
+  }
+
+  Token finishMultiLineComment() {
+    int nesting = 1;
+    do {
+      int ch = _nextChar();
+      if (ch == 0) {
+        return _errorToken();
+      } else if (ch == TokenChar.ASTERISK) {
+        if (_maybeEatChar(TokenChar.SLASH)) {
+          nesting--;
+        }
+      } else if (ch == TokenChar.SLASH) {
+        if (_maybeEatChar(TokenChar.ASTERISK)) {
+          nesting++;
+        }
+      }
+    } while (nesting > 0);
+
+    if (_skipWhitespace) {
+      return next();
+    } else {
+      return _finishToken(TokenKind.COMMENT);
+    }
+  }
+
+  void eatDigits() {
+    while (_index < _text.length) {
+      if (TokenizerHelpers.isDigit(_text.codeUnitAt(_index))) {
+        _index++;
+      } else {
+        return;
+      }
+    }
+  }
+
+  static int _hexDigit(int c) {
+    if (c >= 48 /*0*/ && c <= 57 /*9*/) {
+      return c - 48;
+    } else if (c >= 97 /*a*/ && c <= 102 /*f*/) {
+      return c - 87;
+    } else if (c >= 65 /*A*/ && c <= 70 /*F*/) {
+      return c - 55;
+    } else {
+      return -1;
+    }
+  }
+
+  int readHex([int hexLength]) {
+    int maxIndex;
+    if (hexLength == null) {
+      maxIndex = _text.length - 1;
+    } else {
+      // TODO(jimhug): What if this is too long?
+      maxIndex = _index + hexLength;
+      if (maxIndex >= _text.length) return -1;
+    }
+    var result = 0;
+    while (_index < maxIndex) {
+      final digit = _hexDigit(_text.codeUnitAt(_index));
+      if (digit == -1) {
+        if (hexLength == null) {
+          return result;
+        } else {
+          return -1;
+        }
+      }
+      _hexDigit(_text.codeUnitAt(_index));
+      // Multiply by 16 rather than shift by 4 since that will result in a
+      // correct value for numbers that exceed the 32 bit precision of JS
+      // 'integers'.
+      // TODO: Figure out a better solution to integer truncation. Issue 638.
+      result = (result * 16) + digit;
+      _index++;
+    }
+
+    return result;
+  }
+
+  Token finishNumber() {
+    eatDigits();
+
+    if (_peekChar() == TokenChar.DOT) {
+      // Handle the case of 1.toString().
+      _nextChar();
+      if (TokenizerHelpers.isDigit(_peekChar())) {
+        eatDigits();
+        return finishNumberExtra(TokenKind.DOUBLE);
+      } else {
+        _index--;
+      }
+    }
+
+    return finishNumberExtra(TokenKind.INTEGER);
+  }
+
+  Token finishNumberExtra(int kind) {
+    if (_maybeEatChar(101 /*e*/) || _maybeEatChar(69 /*E*/)) {
+      kind = TokenKind.DOUBLE;
+      _maybeEatChar(TokenKind.MINUS);
+      _maybeEatChar(TokenKind.PLUS);
+      eatDigits();
+    }
+    if (_peekChar() != 0 && TokenizerHelpers.isIdentifierStart(_peekChar())) {
+      _nextChar();
+      return _errorToken("illegal character in number");
+    }
+
+    return _finishToken(kind);
+  }
+
+  Token _makeStringToken(List<int> buf, bool isPart) {
+    final s = new String.fromCharCodes(buf);
+    final kind = isPart ? TokenKind.STRING_PART : TokenKind.STRING;
+    return new LiteralToken(kind, _file.span(_startIndex, _index), s);
+  }
+
+  Token makeIEFilter(int start, int end) {
+    var filter = _text.substring(start, end);
+    return new LiteralToken(TokenKind.STRING, _file.span(start, end), filter);
+  }
+
+  Token _makeRawStringToken(bool isMultiline) {
+    var s;
+    if (isMultiline) {
+      // Skip initial newline in multiline strings
+      int start = _startIndex + 4;
+      if (_text[start] == '\n') start++;
+      s = _text.substring(start, _index - 3);
+    } else {
+      s = _text.substring(_startIndex + 2, _index - 1);
+    }
+    return new LiteralToken(
+        TokenKind.STRING, _file.span(_startIndex, _index), s);
+  }
+
+  Token finishMultilineString(int quote) {
+    var buf = <int>[];
+    while (true) {
+      int ch = _nextChar();
+      if (ch == 0) {
+        return _errorToken();
+      } else if (ch == quote) {
+        if (_maybeEatChar(quote)) {
+          if (_maybeEatChar(quote)) {
+            return _makeStringToken(buf, false);
+          }
+          buf.add(quote);
+        }
+        buf.add(quote);
+      } else if (ch == TokenChar.BACKSLASH) {
+        var escapeVal = readEscapeSequence();
+        if (escapeVal == -1) {
+          return _errorToken("invalid hex escape sequence");
+        } else {
+          buf.add(escapeVal);
+        }
+      } else {
+        buf.add(ch);
+      }
+    }
+  }
+
+  Token _finishOpenBrace() {
+    return _finishToken(TokenKind.LBRACE);
+  }
+
+  Token _finishCloseBrace() {
+    return _finishToken(TokenKind.RBRACE);
+  }
+
+  Token finishString(int quote) {
+    if (_maybeEatChar(quote)) {
+      if (_maybeEatChar(quote)) {
+        // skip an initial newline
+        _maybeEatChar(TokenChar.NEWLINE);
+        return finishMultilineString(quote);
+      } else {
+        return _makeStringToken(new List<int>(), false);
+      }
+    }
+    return finishStringBody(quote);
+  }
+
+  Token finishRawString(int quote) {
+    if (_maybeEatChar(quote)) {
+      if (_maybeEatChar(quote)) {
+        return finishMultilineRawString(quote);
+      } else {
+        return _makeStringToken(<int>[], false);
+      }
+    }
+    while (true) {
+      int ch = _nextChar();
+      if (ch == quote) {
+        return _makeRawStringToken(false);
+      } else if (ch == 0) {
+        return _errorToken();
+      }
+    }
+  }
+
+  Token finishMultilineRawString(int quote) {
+    while (true) {
+      int ch = _nextChar();
+      if (ch == 0) {
+        return _errorToken();
+      } else if (ch == quote && _maybeEatChar(quote) && _maybeEatChar(quote)) {
+        return _makeRawStringToken(true);
+      }
+    }
+  }
+
+  Token finishStringBody(int quote) {
+    var buf = new List<int>();
+    while (true) {
+      int ch = _nextChar();
+      if (ch == quote) {
+        return _makeStringToken(buf, false);
+      } else if (ch == 0) {
+        return _errorToken();
+      } else if (ch == TokenChar.BACKSLASH) {
+        var escapeVal = readEscapeSequence();
+        if (escapeVal == -1) {
+          return _errorToken("invalid hex escape sequence");
+        } else {
+          buf.add(escapeVal);
+        }
+      } else {
+        buf.add(ch);
+      }
+    }
+  }
+
+  int readEscapeSequence() {
+    final ch = _nextChar();
+    int hexValue;
+    switch (ch) {
+      case 110 /*n*/ :
+        return TokenChar.NEWLINE;
+      case 114 /*r*/ :
+        return TokenChar.RETURN;
+      case 102 /*f*/ :
+        return TokenChar.FF;
+      case 98 /*b*/ :
+        return TokenChar.BACKSPACE;
+      case 116 /*t*/ :
+        return TokenChar.TAB;
+      case 118 /*v*/ :
+        return TokenChar.FF;
+      case 120 /*x*/ :
+        hexValue = readHex(2);
+        break;
+      case 117 /*u*/ :
+        if (_maybeEatChar(TokenChar.LBRACE)) {
+          hexValue = readHex();
+          if (!_maybeEatChar(TokenChar.RBRACE)) {
+            return -1;
+          }
+        } else {
+          hexValue = readHex(4);
+        }
+        break;
+      default:
+        return ch;
+    }
+
+    if (hexValue == -1) return -1;
+
+    // According to the Unicode standard the high and low surrogate halves
+    // used by UTF-16 (U+D800 through U+DFFF) and values above U+10FFFF
+    // are not legal Unicode values.
+    if (hexValue < 0xD800 || hexValue > 0xDFFF && hexValue <= 0xFFFF) {
+      return hexValue;
+    } else if (hexValue <= 0x10FFFF) {
+      messages.error('unicode values greater than 2 bytes not implemented yet',
+          _file.span(_startIndex, _startIndex + 1));
+      return -1;
+    } else {
+      return -1;
+    }
+  }
+
+  Token finishDot() {
+    if (TokenizerHelpers.isDigit(_peekChar())) {
+      eatDigits();
+      return finishNumberExtra(TokenKind.DOUBLE);
+    } else {
+      return _finishToken(TokenKind.DOT);
+    }
+  }
+}
diff --git a/csslib/lib/src/tokenkind.dart b/csslib/lib/src/tokenkind.dart
new file mode 100644
index 0000000..8e976bf
--- /dev/null
+++ b/csslib/lib/src/tokenkind.dart
@@ -0,0 +1,816 @@
+// Copyright (c) 2012, 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.
+
+part of csslib.parser;
+
+// TODO(terry): Need to be consistent with tokens either they're ASCII tokens
+//              e.g., ASTERISK or they're CSS e.g., PSEUDO, COMBINATOR_*.
+class TokenKind {
+  // Common shared tokens used in TokenizerBase.
+  static const int UNUSED = 0; // Unused place holder...
+  static const int END_OF_FILE = 1; // EOF
+  static const int LPAREN = 2; // (
+  static const int RPAREN = 3; // )
+  static const int LBRACK = 4; // [
+  static const int RBRACK = 5; // ]
+  static const int LBRACE = 6; // {
+  static const int RBRACE = 7; // }
+  static const int DOT = 8; // .
+  static const int SEMICOLON = 9; // ;
+
+  // Unique tokens for CSS.
+  static const int AT = 10; // @
+  static const int HASH = 11; // #
+  static const int PLUS = 12; // +
+  static const int GREATER = 13; // >
+  static const int TILDE = 14; // ~
+  static const int ASTERISK = 15; // *
+  static const int NAMESPACE = 16; // |
+  static const int COLON = 17; // :
+  static const int PRIVATE_NAME = 18; // _ prefix private class or id
+  static const int COMMA = 19; // ,
+  static const int SPACE = 20;
+  static const int TAB = 21; // /t
+  static const int NEWLINE = 22; // /n
+  static const int RETURN = 23; // /r
+  static const int PERCENT = 24; // %
+  static const int SINGLE_QUOTE = 25; // '
+  static const int DOUBLE_QUOTE = 26; // "
+  static const int SLASH = 27; // /
+  static const int EQUALS = 28; // =
+  static const int CARET = 30; // ^
+  static const int DOLLAR = 31; // $
+  static const int LESS = 32; // <
+  static const int BANG = 33; // !
+  static const int MINUS = 34; // -
+  static const int BACKSLASH = 35; // \
+  static const int AMPERSAND = 36; // &
+
+  // WARNING: Tokens from this point and above must have the corresponding ASCII
+  //          character in the TokenChar list at the bottom of this file.  The
+  //          order of the above tokens should be the same order as TokenChar.
+
+  /** [TokenKind] representing integer tokens. */
+  static const int INTEGER = 60;
+
+  /** [TokenKind] representing hex integer tokens. */
+  static const int HEX_INTEGER = 61;
+
+  /** [TokenKind] representing double tokens. */
+  static const int DOUBLE = 62;
+
+  /** [TokenKind] representing whitespace tokens. */
+  static const int WHITESPACE = 63;
+
+  /** [TokenKind] representing comment tokens. */
+  static const int COMMENT = 64;
+
+  /** [TokenKind] representing error tokens. */
+  static const int ERROR = 65;
+
+  /** [TokenKind] representing incomplete string tokens. */
+  static const int INCOMPLETE_STRING = 66;
+
+  /** [TokenKind] representing incomplete comment tokens. */
+  static const int INCOMPLETE_COMMENT = 67;
+
+  static const int VAR_DEFINITION = 400; // var-NNN-NNN
+  static const int VAR_USAGE = 401; // var(NNN-NNN [,default])
+
+  // Synthesized Tokens (no character associated with TOKEN).
+  static const int STRING = 500;
+  static const int STRING_PART = 501;
+  static const int NUMBER = 502;
+  static const int HEX_NUMBER = 503;
+  static const int HTML_COMMENT = 504; // <!--
+  static const int IMPORTANT = 505; // !important
+  static const int CDATA_START = 506; // <![CDATA[
+  static const int CDATA_END = 507; // ]]>
+  // U+uNumber[-U+uNumber]
+  // uNumber = 0..10FFFF | ?[?]*
+  static const int UNICODE_RANGE = 508;
+  static const int HEX_RANGE = 509; // ? in the hex range
+  static const int IDENTIFIER = 511;
+
+  // Uniquely synthesized tokens for CSS.
+  static const int SELECTOR_EXPRESSION = 512;
+  static const int COMBINATOR_NONE = 513;
+  static const int COMBINATOR_DESCENDANT = 514; // Space combinator
+  static const int COMBINATOR_PLUS = 515; // + combinator
+  static const int COMBINATOR_GREATER = 516; // > combinator
+  static const int COMBINATOR_TILDE = 517; // ~ combinator
+
+  static const int UNARY_OP_NONE = 518; // No unary operator present.
+
+  // Attribute match types:
+  static const int INCLUDES = 530; // '~='
+  static const int DASH_MATCH = 531; // '|='
+  static const int PREFIX_MATCH = 532; // '^='
+  static const int SUFFIX_MATCH = 533; // '$='
+  static const int SUBSTRING_MATCH = 534; // '*='
+  static const int NO_MATCH = 535; // No operator.
+
+  // Unit types:
+  static const int UNIT_EM = 600;
+  static const int UNIT_EX = 601;
+  static const int UNIT_LENGTH_PX = 602;
+  static const int UNIT_LENGTH_CM = 603;
+  static const int UNIT_LENGTH_MM = 604;
+  static const int UNIT_LENGTH_IN = 605;
+  static const int UNIT_LENGTH_PT = 606;
+  static const int UNIT_LENGTH_PC = 607;
+  static const int UNIT_ANGLE_DEG = 608;
+  static const int UNIT_ANGLE_RAD = 609;
+  static const int UNIT_ANGLE_GRAD = 610;
+  static const int UNIT_ANGLE_TURN = 611;
+  static const int UNIT_TIME_MS = 612;
+  static const int UNIT_TIME_S = 613;
+  static const int UNIT_FREQ_HZ = 614;
+  static const int UNIT_FREQ_KHZ = 615;
+  static const int UNIT_PERCENT = 616;
+  static const int UNIT_FRACTION = 617;
+  static const int UNIT_RESOLUTION_DPI = 618;
+  static const int UNIT_RESOLUTION_DPCM = 619;
+  static const int UNIT_RESOLUTION_DPPX = 620;
+  static const int UNIT_CH = 621; // Measure of "0" U+0030 glyph.
+  static const int UNIT_REM = 622; // computed value ‘font-size’ on root elem.
+  static const int UNIT_VIEWPORT_VW = 623;
+  static const int UNIT_VIEWPORT_VH = 624;
+  static const int UNIT_VIEWPORT_VMIN = 625;
+  static const int UNIT_VIEWPORT_VMAX = 626;
+
+  // Directives (@nnnn)
+  static const int DIRECTIVE_NONE = 640;
+  static const int DIRECTIVE_IMPORT = 641;
+  static const int DIRECTIVE_MEDIA = 642;
+  static const int DIRECTIVE_PAGE = 643;
+  static const int DIRECTIVE_CHARSET = 644;
+  static const int DIRECTIVE_STYLET = 645;
+  static const int DIRECTIVE_KEYFRAMES = 646;
+  static const int DIRECTIVE_WEB_KIT_KEYFRAMES = 647;
+  static const int DIRECTIVE_MOZ_KEYFRAMES = 648;
+  static const int DIRECTIVE_MS_KEYFRAMES = 649;
+  static const int DIRECTIVE_O_KEYFRAMES = 650;
+  static const int DIRECTIVE_FONTFACE = 651;
+  static const int DIRECTIVE_NAMESPACE = 652;
+  static const int DIRECTIVE_HOST = 653;
+  static const int DIRECTIVE_MIXIN = 654;
+  static const int DIRECTIVE_INCLUDE = 655;
+  static const int DIRECTIVE_CONTENT = 656;
+  static const int DIRECTIVE_EXTEND = 657;
+
+  // Media query operators
+  static const int MEDIA_OP_ONLY = 665; // Unary.
+  static const int MEDIA_OP_NOT = 666; // Unary.
+  static const int MEDIA_OP_AND = 667; // Binary.
+
+  // Directives inside of a @page (margin sym).
+  static const int MARGIN_DIRECTIVE_TOPLEFTCORNER = 670;
+  static const int MARGIN_DIRECTIVE_TOPLEFT = 671;
+  static const int MARGIN_DIRECTIVE_TOPCENTER = 672;
+  static const int MARGIN_DIRECTIVE_TOPRIGHT = 673;
+  static const int MARGIN_DIRECTIVE_TOPRIGHTCORNER = 674;
+  static const int MARGIN_DIRECTIVE_BOTTOMLEFTCORNER = 675;
+  static const int MARGIN_DIRECTIVE_BOTTOMLEFT = 676;
+  static const int MARGIN_DIRECTIVE_BOTTOMCENTER = 677;
+  static const int MARGIN_DIRECTIVE_BOTTOMRIGHT = 678;
+  static const int MARGIN_DIRECTIVE_BOTTOMRIGHTCORNER = 679;
+  static const int MARGIN_DIRECTIVE_LEFTTOP = 680;
+  static const int MARGIN_DIRECTIVE_LEFTMIDDLE = 681;
+  static const int MARGIN_DIRECTIVE_LEFTBOTTOM = 682;
+  static const int MARGIN_DIRECTIVE_RIGHTTOP = 683;
+  static const int MARGIN_DIRECTIVE_RIGHTMIDDLE = 684;
+  static const int MARGIN_DIRECTIVE_RIGHTBOTTOM = 685;
+
+  // Simple selector type.
+  static const int CLASS_NAME = 700; // .class
+  static const int ELEMENT_NAME = 701; // tagName
+  static const int HASH_NAME = 702; // #elementId
+  static const int ATTRIBUTE_NAME = 703; // [attrib]
+  static const int PSEUDO_ELEMENT_NAME = 704; // ::pseudoElement
+  static const int PSEUDO_CLASS_NAME = 705; // :pseudoClass
+  static const int NEGATION = 706; // NOT
+
+  static const List<Map<int, String>> _DIRECTIVES = const [
+    const {'type': TokenKind.DIRECTIVE_IMPORT, 'value': 'import'},
+    const {'type': TokenKind.DIRECTIVE_MEDIA, 'value': 'media'},
+    const {'type': TokenKind.DIRECTIVE_PAGE, 'value': 'page'},
+    const {'type': TokenKind.DIRECTIVE_CHARSET, 'value': 'charset'},
+    const {'type': TokenKind.DIRECTIVE_STYLET, 'value': 'stylet'},
+    const {'type': TokenKind.DIRECTIVE_KEYFRAMES, 'value': 'keyframes'},
+    const {
+      'type': TokenKind.DIRECTIVE_WEB_KIT_KEYFRAMES,
+      'value': '-webkit-keyframes'
+    },
+    const {
+      'type': TokenKind.DIRECTIVE_MOZ_KEYFRAMES,
+      'value': '-moz-keyframes'
+    },
+    const {'type': TokenKind.DIRECTIVE_MS_KEYFRAMES, 'value': '-ms-keyframes'},
+    const {'type': TokenKind.DIRECTIVE_O_KEYFRAMES, 'value': '-o-keyframes'},
+    const {'type': TokenKind.DIRECTIVE_FONTFACE, 'value': 'font-face'},
+    const {'type': TokenKind.DIRECTIVE_NAMESPACE, 'value': 'namespace'},
+    const {'type': TokenKind.DIRECTIVE_HOST, 'value': 'host'},
+    const {'type': TokenKind.DIRECTIVE_MIXIN, 'value': 'mixin'},
+    const {'type': TokenKind.DIRECTIVE_INCLUDE, 'value': 'include'},
+    const {'type': TokenKind.DIRECTIVE_CONTENT, 'value': 'content'},
+    const {'type': TokenKind.DIRECTIVE_EXTEND, 'value': 'extend'},
+  ];
+
+  static const List<Map<int, String>> MEDIA_OPERATORS = const [
+    const {'type': TokenKind.MEDIA_OP_ONLY, 'value': 'only'},
+    const {'type': TokenKind.MEDIA_OP_NOT, 'value': 'not'},
+    const {'type': TokenKind.MEDIA_OP_AND, 'value': 'and'},
+  ];
+
+  static const List<Map<int, String>> MARGIN_DIRECTIVES = const [
+    const {
+      'type': TokenKind.MARGIN_DIRECTIVE_TOPLEFTCORNER,
+      'value': 'top-left-corner'
+    },
+    const {'type': TokenKind.MARGIN_DIRECTIVE_TOPLEFT, 'value': 'top-left'},
+    const {'type': TokenKind.MARGIN_DIRECTIVE_TOPCENTER, 'value': 'top-center'},
+    const {'type': TokenKind.MARGIN_DIRECTIVE_TOPRIGHT, 'value': 'top-right'},
+    const {
+      'type': TokenKind.MARGIN_DIRECTIVE_TOPRIGHTCORNER,
+      'value': 'top-right-corner'
+    },
+    const {
+      'type': TokenKind.MARGIN_DIRECTIVE_BOTTOMLEFTCORNER,
+      'value': 'bottom-left-corner'
+    },
+    const {
+      'type': TokenKind.MARGIN_DIRECTIVE_BOTTOMLEFT,
+      'value': 'bottom-left'
+    },
+    const {
+      'type': TokenKind.MARGIN_DIRECTIVE_BOTTOMCENTER,
+      'value': 'bottom-center'
+    },
+    const {
+      'type': TokenKind.MARGIN_DIRECTIVE_BOTTOMRIGHT,
+      'value': 'bottom-right'
+    },
+    const {
+      'type': TokenKind.MARGIN_DIRECTIVE_BOTTOMRIGHTCORNER,
+      'value': 'bottom-right-corner'
+    },
+    const {'type': TokenKind.MARGIN_DIRECTIVE_LEFTTOP, 'value': 'left-top'},
+    const {
+      'type': TokenKind.MARGIN_DIRECTIVE_LEFTMIDDLE,
+      'value': 'left-middle'
+    },
+    const {
+      'type': TokenKind.MARGIN_DIRECTIVE_LEFTBOTTOM,
+      'value': 'right-bottom'
+    },
+    const {'type': TokenKind.MARGIN_DIRECTIVE_RIGHTTOP, 'value': 'right-top'},
+    const {
+      'type': TokenKind.MARGIN_DIRECTIVE_RIGHTMIDDLE,
+      'value': 'right-middle'
+    },
+    const {
+      'type': TokenKind.MARGIN_DIRECTIVE_RIGHTBOTTOM,
+      'value': 'right-bottom'
+    },
+  ];
+
+  static const List<Map> _UNITS = const [
+    const {'unit': TokenKind.UNIT_EM, 'value': 'em'},
+    const {'unit': TokenKind.UNIT_EX, 'value': 'ex'},
+    const {'unit': TokenKind.UNIT_LENGTH_PX, 'value': 'px'},
+    const {'unit': TokenKind.UNIT_LENGTH_CM, 'value': 'cm'},
+    const {'unit': TokenKind.UNIT_LENGTH_MM, 'value': 'mm'},
+    const {'unit': TokenKind.UNIT_LENGTH_IN, 'value': 'in'},
+    const {'unit': TokenKind.UNIT_LENGTH_PT, 'value': 'pt'},
+    const {'unit': TokenKind.UNIT_LENGTH_PC, 'value': 'pc'},
+    const {'unit': TokenKind.UNIT_ANGLE_DEG, 'value': 'deg'},
+    const {'unit': TokenKind.UNIT_ANGLE_RAD, 'value': 'rad'},
+    const {'unit': TokenKind.UNIT_ANGLE_GRAD, 'value': 'grad'},
+    const {'unit': TokenKind.UNIT_ANGLE_TURN, 'value': 'turn'},
+    const {'unit': TokenKind.UNIT_TIME_MS, 'value': 'ms'},
+    const {'unit': TokenKind.UNIT_TIME_S, 'value': 's'},
+    const {'unit': TokenKind.UNIT_FREQ_HZ, 'value': 'hz'},
+    const {'unit': TokenKind.UNIT_FREQ_KHZ, 'value': 'khz'},
+    const {'unit': TokenKind.UNIT_FRACTION, 'value': 'fr'},
+    const {'unit': TokenKind.UNIT_RESOLUTION_DPI, 'value': 'dpi'},
+    const {'unit': TokenKind.UNIT_RESOLUTION_DPCM, 'value': 'dpcm'},
+    const {'unit': TokenKind.UNIT_RESOLUTION_DPPX, 'value': 'dppx'},
+    const {'unit': TokenKind.UNIT_CH, 'value': 'ch'},
+    const {'unit': TokenKind.UNIT_REM, 'value': 'rem'},
+    const {'unit': TokenKind.UNIT_VIEWPORT_VW, 'value': 'vw'},
+    const {'unit': TokenKind.UNIT_VIEWPORT_VH, 'value': 'vh'},
+    const {'unit': TokenKind.UNIT_VIEWPORT_VMIN, 'value': 'vmin'},
+    const {'unit': TokenKind.UNIT_VIEWPORT_VMAX, 'value': 'vmax'},
+  ];
+
+  // Some more constants:
+  static const int ASCII_UPPER_A = 65; // ASCII value for uppercase A
+  static const int ASCII_UPPER_Z = 90; // ASCII value for uppercase Z
+
+  // Extended color keywords:
+  static const List<Map> _EXTENDED_COLOR_NAMES = const [
+    const {'name': 'aliceblue', 'value': 0xF08FF},
+    const {'name': 'antiquewhite', 'value': 0xFAEBD7},
+    const {'name': 'aqua', 'value': 0x00FFFF},
+    const {'name': 'aquamarine', 'value': 0x7FFFD4},
+    const {'name': 'azure', 'value': 0xF0FFFF},
+    const {'name': 'beige', 'value': 0xF5F5DC},
+    const {'name': 'bisque', 'value': 0xFFE4C4},
+    const {'name': 'black', 'value': 0x000000},
+    const {'name': 'blanchedalmond', 'value': 0xFFEBCD},
+    const {'name': 'blue', 'value': 0x0000FF},
+    const {'name': 'blueviolet', 'value': 0x8A2BE2},
+    const {'name': 'brown', 'value': 0xA52A2A},
+    const {'name': 'burlywood', 'value': 0xDEB887},
+    const {'name': 'cadetblue', 'value': 0x5F9EA0},
+    const {'name': 'chartreuse', 'value': 0x7FFF00},
+    const {'name': 'chocolate', 'value': 0xD2691E},
+    const {'name': 'coral', 'value': 0xFF7F50},
+    const {'name': 'cornflowerblue', 'value': 0x6495ED},
+    const {'name': 'cornsilk', 'value': 0xFFF8DC},
+    const {'name': 'crimson', 'value': 0xDC143C},
+    const {'name': 'cyan', 'value': 0x00FFFF},
+    const {'name': 'darkblue', 'value': 0x00008B},
+    const {'name': 'darkcyan', 'value': 0x008B8B},
+    const {'name': 'darkgoldenrod', 'value': 0xB8860B},
+    const {'name': 'darkgray', 'value': 0xA9A9A9},
+    const {'name': 'darkgreen', 'value': 0x006400},
+    const {'name': 'darkgrey', 'value': 0xA9A9A9},
+    const {'name': 'darkkhaki', 'value': 0xBDB76B},
+    const {'name': 'darkmagenta', 'value': 0x8B008B},
+    const {'name': 'darkolivegreen', 'value': 0x556B2F},
+    const {'name': 'darkorange', 'value': 0xFF8C00},
+    const {'name': 'darkorchid', 'value': 0x9932CC},
+    const {'name': 'darkred', 'value': 0x8B0000},
+    const {'name': 'darksalmon', 'value': 0xE9967A},
+    const {'name': 'darkseagreen', 'value': 0x8FBC8F},
+    const {'name': 'darkslateblue', 'value': 0x483D8B},
+    const {'name': 'darkslategray', 'value': 0x2F4F4F},
+    const {'name': 'darkslategrey', 'value': 0x2F4F4F},
+    const {'name': 'darkturquoise', 'value': 0x00CED1},
+    const {'name': 'darkviolet', 'value': 0x9400D3},
+    const {'name': 'deeppink', 'value': 0xFF1493},
+    const {'name': 'deepskyblue', 'value': 0x00BFFF},
+    const {'name': 'dimgray', 'value': 0x696969},
+    const {'name': 'dimgrey', 'value': 0x696969},
+    const {'name': 'dodgerblue', 'value': 0x1E90FF},
+    const {'name': 'firebrick', 'value': 0xB22222},
+    const {'name': 'floralwhite', 'value': 0xFFFAF0},
+    const {'name': 'forestgreen', 'value': 0x228B22},
+    const {'name': 'fuchsia', 'value': 0xFF00FF},
+    const {'name': 'gainsboro', 'value': 0xDCDCDC},
+    const {'name': 'ghostwhite', 'value': 0xF8F8FF},
+    const {'name': 'gold', 'value': 0xFFD700},
+    const {'name': 'goldenrod', 'value': 0xDAA520},
+    const {'name': 'gray', 'value': 0x808080},
+    const {'name': 'green', 'value': 0x008000},
+    const {'name': 'greenyellow', 'value': 0xADFF2F},
+    const {'name': 'grey', 'value': 0x808080},
+    const {'name': 'honeydew', 'value': 0xF0FFF0},
+    const {'name': 'hotpink', 'value': 0xFF69B4},
+    const {'name': 'indianred', 'value': 0xCD5C5C},
+    const {'name': 'indigo', 'value': 0x4B0082},
+    const {'name': 'ivory', 'value': 0xFFFFF0},
+    const {'name': 'khaki', 'value': 0xF0E68C},
+    const {'name': 'lavender', 'value': 0xE6E6FA},
+    const {'name': 'lavenderblush', 'value': 0xFFF0F5},
+    const {'name': 'lawngreen', 'value': 0x7CFC00},
+    const {'name': 'lemonchiffon', 'value': 0xFFFACD},
+    const {'name': 'lightblue', 'value': 0xADD8E6},
+    const {'name': 'lightcoral', 'value': 0xF08080},
+    const {'name': 'lightcyan', 'value': 0xE0FFFF},
+    const {'name': 'lightgoldenrodyellow', 'value': 0xFAFAD2},
+    const {'name': 'lightgray', 'value': 0xD3D3D3},
+    const {'name': 'lightgreen', 'value': 0x90EE90},
+    const {'name': 'lightgrey', 'value': 0xD3D3D3},
+    const {'name': 'lightpink', 'value': 0xFFB6C1},
+    const {'name': 'lightsalmon', 'value': 0xFFA07A},
+    const {'name': 'lightseagreen', 'value': 0x20B2AA},
+    const {'name': 'lightskyblue', 'value': 0x87CEFA},
+    const {'name': 'lightslategray', 'value': 0x778899},
+    const {'name': 'lightslategrey', 'value': 0x778899},
+    const {'name': 'lightsteelblue', 'value': 0xB0C4DE},
+    const {'name': 'lightyellow', 'value': 0xFFFFE0},
+    const {'name': 'lime', 'value': 0x00FF00},
+    const {'name': 'limegreen', 'value': 0x32CD32},
+    const {'name': 'linen', 'value': 0xFAF0E6},
+    const {'name': 'magenta', 'value': 0xFF00FF},
+    const {'name': 'maroon', 'value': 0x800000},
+    const {'name': 'mediumaquamarine', 'value': 0x66CDAA},
+    const {'name': 'mediumblue', 'value': 0x0000CD},
+    const {'name': 'mediumorchid', 'value': 0xBA55D3},
+    const {'name': 'mediumpurple', 'value': 0x9370DB},
+    const {'name': 'mediumseagreen', 'value': 0x3CB371},
+    const {'name': 'mediumslateblue', 'value': 0x7B68EE},
+    const {'name': 'mediumspringgreen', 'value': 0x00FA9A},
+    const {'name': 'mediumturquoise', 'value': 0x48D1CC},
+    const {'name': 'mediumvioletred', 'value': 0xC71585},
+    const {'name': 'midnightblue', 'value': 0x191970},
+    const {'name': 'mintcream', 'value': 0xF5FFFA},
+    const {'name': 'mistyrose', 'value': 0xFFE4E1},
+    const {'name': 'moccasin', 'value': 0xFFE4B5},
+    const {'name': 'navajowhite', 'value': 0xFFDEAD},
+    const {'name': 'navy', 'value': 0x000080},
+    const {'name': 'oldlace', 'value': 0xFDF5E6},
+    const {'name': 'olive', 'value': 0x808000},
+    const {'name': 'olivedrab', 'value': 0x6B8E23},
+    const {'name': 'orange', 'value': 0xFFA500},
+    const {'name': 'orangered', 'value': 0xFF4500},
+    const {'name': 'orchid', 'value': 0xDA70D6},
+    const {'name': 'palegoldenrod', 'value': 0xEEE8AA},
+    const {'name': 'palegreen', 'value': 0x98FB98},
+    const {'name': 'paleturquoise', 'value': 0xAFEEEE},
+    const {'name': 'palevioletred', 'value': 0xDB7093},
+    const {'name': 'papayawhip', 'value': 0xFFEFD5},
+    const {'name': 'peachpuff', 'value': 0xFFDAB9},
+    const {'name': 'peru', 'value': 0xCD853F},
+    const {'name': 'pink', 'value': 0xFFC0CB},
+    const {'name': 'plum', 'value': 0xDDA0DD},
+    const {'name': 'powderblue', 'value': 0xB0E0E6},
+    const {'name': 'purple', 'value': 0x800080},
+    const {'name': 'red', 'value': 0xFF0000},
+    const {'name': 'rosybrown', 'value': 0xBC8F8F},
+    const {'name': 'royalblue', 'value': 0x4169E1},
+    const {'name': 'saddlebrown', 'value': 0x8B4513},
+    const {'name': 'salmon', 'value': 0xFA8072},
+    const {'name': 'sandybrown', 'value': 0xF4A460},
+    const {'name': 'seagreen', 'value': 0x2E8B57},
+    const {'name': 'seashell', 'value': 0xFFF5EE},
+    const {'name': 'sienna', 'value': 0xA0522D},
+    const {'name': 'silver', 'value': 0xC0C0C0},
+    const {'name': 'skyblue', 'value': 0x87CEEB},
+    const {'name': 'slateblue', 'value': 0x6A5ACD},
+    const {'name': 'slategray', 'value': 0x708090},
+    const {'name': 'slategrey', 'value': 0x708090},
+    const {'name': 'snow', 'value': 0xFFFAFA},
+    const {'name': 'springgreen', 'value': 0x00FF7F},
+    const {'name': 'steelblue', 'value': 0x4682B4},
+    const {'name': 'tan', 'value': 0xD2B48C},
+    const {'name': 'teal', 'value': 0x008080},
+    const {'name': 'thistle', 'value': 0xD8BFD8},
+    const {'name': 'tomato', 'value': 0xFF6347},
+    const {'name': 'turquoise', 'value': 0x40E0D0},
+    const {'name': 'violet', 'value': 0xEE82EE},
+    const {'name': 'wheat', 'value': 0xF5DEB3},
+    const {'name': 'white', 'value': 0xFFFFFF},
+    const {'name': 'whitesmoke', 'value': 0xF5F5F5},
+    const {'name': 'yellow', 'value': 0xFFFF00},
+    const {'name': 'yellowgreen', 'value': 0x9ACD32},
+  ];
+
+  // TODO(terry): Should used Dart mirroring for parameter values and types
+  //              especially for enumeration (e.g., counter's second parameter
+  //              is list-style-type which is an enumerated list for ordering
+  //              of a list 'circle', 'decimal', 'lower-roman', 'square', etc.
+  //              see http://www.w3schools.com/cssref/pr_list-style-type.asp
+  //              for list of possible values.
+
+  // List of valid CSS functions:
+  static const List<Map<String, Object>> _FUNCTIONS = const [
+    const {'name': 'counter', 'info': const {'params': 2, 'expr': false}},
+    const {'name': 'attr', 'info': const {'params': 1, 'expr': false}},
+    const {'name': 'calc', 'info': const {'params': 1, 'expr': true}},
+    const {'name': 'min', 'info': const {'params': 2, 'expr': true}},
+    const {'name': 'max', 'info': const {'params': 2, 'expr': true}},
+
+    // 2D functions:
+    const {'name': 'translateX', 'info': const {'params': 1, 'expr': false}},
+    const {'name': 'translateY', 'info': const {'params': 1, 'expr': false}},
+    const {'name': 'translate', 'info': const {'params': 2, 'expr': false}},
+    const {'name': 'rotate', 'info': const {'params': 1, 'expr': false}},
+    const {'name': 'scaleX', 'info': const {'params': 1, 'expr': false}},
+    const {'name': 'scaleY', 'info': const {'params': 1, 'expr': false}},
+    const {'name': 'scale', 'info': const {'params': 2, 'expr': false}},
+    const {'name': 'skewX', 'info': const {'params': 1, 'expr': false}},
+    const {'name': 'skewY', 'info': const {'params': 1, 'expr': false}},
+    const {'name': 'skew', 'info': const {'params': 2, 'expr': false}},
+    const {'name': 'matrix', 'info': const {'params': 6, 'expr': false}},
+
+    // 3D functions:
+    const {'name': 'matrix3d', 'info': const {'params': 16, 'expr': false}},
+    const {'name': 'translate3d', 'info': const {'params': 3, 'expr': false}},
+    const {'name': 'translateZ', 'info': const {'params': 1, 'expr': false}},
+    const {'name': 'scale3d', 'info': const {'params': 3, 'expr': false}},
+    const {'name': 'scaleZ', 'info': const {'params': 1, 'expr': false}},
+    const {'name': 'rotate3d', 'info': const {'params': 3, 'expr': false}},
+    const {'name': 'rotateX', 'info': const {'params': 1, 'expr': false}},
+    const {'name': 'rotateY', 'info': const {'params': 1, 'expr': false}},
+    const {'name': 'rotateZ', 'info': const {'params': 1, 'expr': false}},
+    const {'name': 'perspective', 'info': const {'params': 1, 'expr': false}},
+  ];
+
+  /**
+   * Check if name is a pre-defined CSS name.  Used by error handler to report
+   * if name is unknown or used improperly.
+   */
+  static bool isPredefinedName(String name) {
+    var nameLen = name.length;
+    // TODO(terry): Add more pre-defined names (hidden, bolder, inherit, etc.).
+    if (matchUnits(name, 0, nameLen) == -1 ||
+        matchDirectives(name, 0, nameLen) == -1 ||
+        matchMarginDirectives(name, 0, nameLen) == -1 ||
+        matchColorName(name) == null) {
+      return false;
+    }
+
+    return true;
+  }
+
+  /** Return the token that matches the unit ident found. */
+  static int matchList(
+      var identList, String tokenField, String text, int offset, int length) {
+    for (final entry in identList) {
+      String ident = entry['value'];
+
+      if (length == ident.length) {
+        int idx = offset;
+        bool match = true;
+        for (int i = 0; i < ident.length; i++) {
+          int identChar = ident.codeUnitAt(i);
+          int char = text.codeUnitAt(idx++);
+          // Compare lowercase to lowercase then check if char is uppercase.
+          match = match &&
+              (char == identChar ||
+                  ((char >= ASCII_UPPER_A && char <= ASCII_UPPER_Z) &&
+                      (char + 32) == identChar));
+          if (!match) {
+            break;
+          }
+        }
+
+        if (match) {
+          // Completely matched; return the token for this unit.
+          return entry[tokenField];
+        }
+      }
+    }
+
+    return -1; // Not a unit token.
+  }
+
+  /** Return the token that matches the unit ident found. */
+  static int matchUnits(String text, int offset, int length) {
+    return matchList(_UNITS, 'unit', text, offset, length);
+  }
+
+  /** Return the token that matches the directive name found. */
+  static int matchDirectives(String text, int offset, int length) {
+    return matchList(_DIRECTIVES, 'type', text, offset, length);
+  }
+
+  /** Return the token that matches the margin directive name found. */
+  static int matchMarginDirectives(String text, int offset, int length) {
+    return matchList(MARGIN_DIRECTIVES, 'type', text, offset, length);
+  }
+
+  /** Return the token that matches the media operator found. */
+  static int matchMediaOperator(String text, int offset, int length) {
+    return matchList(MEDIA_OPERATORS, 'type', text, offset, length);
+  }
+
+  static String idToValue(var identList, int tokenId) {
+    for (var entry in identList) {
+      if (tokenId == entry['type']) {
+        return entry['value'];
+      }
+    }
+
+    return null;
+  }
+
+  /** Return the unit token as its pretty name. */
+  static String unitToString(int unitTokenToFind) {
+    if (unitTokenToFind == TokenKind.PERCENT) {
+      return '%';
+    } else {
+      for (final entry in _UNITS) {
+        int unit = entry['unit'];
+        if (unit == unitTokenToFind) {
+          return entry['value'];
+        }
+      }
+    }
+
+    return '<BAD UNIT>'; // Not a unit token.
+  }
+
+  /**
+   * Match color name, case insensitive match and return the associated color
+   * entry from _EXTENDED_COLOR_NAMES list, return [:null:] if not found.
+   */
+  static Map matchColorName(String text) {
+    var name = text.toLowerCase();
+    return _EXTENDED_COLOR_NAMES.firstWhere((e) => e['name'] == name,
+        orElse: () => null);
+  }
+
+  /** Return RGB value as [int] from a color entry in _EXTENDED_COLOR_NAMES. */
+  static int colorValue(Map entry) {
+    assert(entry != null);
+    return entry['value'];
+  }
+
+  static String hexToColorName(hexValue) {
+    for (final entry in _EXTENDED_COLOR_NAMES) {
+      if (entry['value'] == hexValue) {
+        return entry['name'];
+      }
+    }
+
+    return null;
+  }
+
+  static String decimalToHex(int number, [int minDigits = 1]) {
+    final String _HEX_DIGITS = '0123456789abcdef';
+
+    List<String> result = new List<String>();
+
+    int dividend = number >> 4;
+    int remain = number % 16;
+    result.add(_HEX_DIGITS[remain]);
+    while (dividend != 0) {
+      remain = dividend % 16;
+      dividend >>= 4;
+      result.add(_HEX_DIGITS[remain]);
+    }
+
+    StringBuffer invertResult = new StringBuffer();
+    int paddings = minDigits - result.length;
+    while (paddings-- > 0) {
+      invertResult.write('0');
+    }
+    for (int i = result.length - 1; i >= 0; i--) {
+      invertResult.write(result[i]);
+    }
+
+    return invertResult.toString();
+  }
+
+  static String kindToString(int kind) {
+    switch (kind) {
+      case TokenKind.UNUSED:
+        return "ERROR";
+      case TokenKind.END_OF_FILE:
+        return "end of file";
+      case TokenKind.LPAREN:
+        return "(";
+      case TokenKind.RPAREN:
+        return ")";
+      case TokenKind.LBRACK:
+        return "[";
+      case TokenKind.RBRACK:
+        return "]";
+      case TokenKind.LBRACE:
+        return "{";
+      case TokenKind.RBRACE:
+        return "}";
+      case TokenKind.DOT:
+        return ".";
+      case TokenKind.SEMICOLON:
+        return ";";
+      case TokenKind.AT:
+        return "@";
+      case TokenKind.HASH:
+        return "#";
+      case TokenKind.PLUS:
+        return "+";
+      case TokenKind.GREATER:
+        return ">";
+      case TokenKind.TILDE:
+        return "~";
+      case TokenKind.ASTERISK:
+        return "*";
+      case TokenKind.NAMESPACE:
+        return "|";
+      case TokenKind.COLON:
+        return ":";
+      case TokenKind.PRIVATE_NAME:
+        return "_";
+      case TokenKind.COMMA:
+        return ",";
+      case TokenKind.SPACE:
+        return " ";
+      case TokenKind.TAB:
+        return "\t";
+      case TokenKind.NEWLINE:
+        return "\n";
+      case TokenKind.RETURN:
+        return "\r";
+      case TokenKind.PERCENT:
+        return "%";
+      case TokenKind.SINGLE_QUOTE:
+        return "'";
+      case TokenKind.DOUBLE_QUOTE:
+        return "\"";
+      case TokenKind.SLASH:
+        return "/";
+      case TokenKind.EQUALS:
+        return '=';
+      case TokenKind.CARET:
+        return '^';
+      case TokenKind.DOLLAR:
+        return '\$';
+      case TokenKind.LESS:
+        return '<';
+      case TokenKind.BANG:
+        return '!';
+      case TokenKind.MINUS:
+        return '-';
+      case TokenKind.BACKSLASH:
+        return '\\';
+      default:
+        throw "Unknown TOKEN";
+    }
+  }
+
+  static bool isKindIdentifier(int kind) {
+    switch (kind) {
+      // Synthesized tokens.
+      case TokenKind.DIRECTIVE_IMPORT:
+      case TokenKind.DIRECTIVE_MEDIA:
+      case TokenKind.DIRECTIVE_PAGE:
+      case TokenKind.DIRECTIVE_CHARSET:
+      case TokenKind.DIRECTIVE_STYLET:
+      case TokenKind.DIRECTIVE_KEYFRAMES:
+      case TokenKind.DIRECTIVE_WEB_KIT_KEYFRAMES:
+      case TokenKind.DIRECTIVE_MOZ_KEYFRAMES:
+      case TokenKind.DIRECTIVE_MS_KEYFRAMES:
+      case TokenKind.DIRECTIVE_O_KEYFRAMES:
+      case TokenKind.DIRECTIVE_FONTFACE:
+      case TokenKind.DIRECTIVE_NAMESPACE:
+      case TokenKind.DIRECTIVE_HOST:
+      case TokenKind.DIRECTIVE_MIXIN:
+      case TokenKind.DIRECTIVE_INCLUDE:
+      case TokenKind.DIRECTIVE_CONTENT:
+      case TokenKind.UNIT_EM:
+      case TokenKind.UNIT_EX:
+      case TokenKind.UNIT_LENGTH_PX:
+      case TokenKind.UNIT_LENGTH_CM:
+      case TokenKind.UNIT_LENGTH_MM:
+      case TokenKind.UNIT_LENGTH_IN:
+      case TokenKind.UNIT_LENGTH_PT:
+      case TokenKind.UNIT_LENGTH_PC:
+      case TokenKind.UNIT_ANGLE_DEG:
+      case TokenKind.UNIT_ANGLE_RAD:
+      case TokenKind.UNIT_ANGLE_GRAD:
+      case TokenKind.UNIT_TIME_MS:
+      case TokenKind.UNIT_TIME_S:
+      case TokenKind.UNIT_FREQ_HZ:
+      case TokenKind.UNIT_FREQ_KHZ:
+      case TokenKind.UNIT_FRACTION:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  static bool isIdentifier(int kind) {
+    return kind == IDENTIFIER;
+  }
+}
+
+// Note: these names should match TokenKind names
+class TokenChar {
+  static const int UNUSED = -1;
+  static const int END_OF_FILE = 0;
+  static const int LPAREN = 0x28; // "(".codeUnitAt(0)
+  static const int RPAREN = 0x29; // ")".codeUnitAt(0)
+  static const int LBRACK = 0x5b; // "[".codeUnitAt(0)
+  static const int RBRACK = 0x5d; // "]".codeUnitAt(0)
+  static const int LBRACE = 0x7b; // "{".codeUnitAt(0)
+  static const int RBRACE = 0x7d; // "}".codeUnitAt(0)
+  static const int DOT = 0x2e; // ".".codeUnitAt(0)
+  static const int SEMICOLON = 0x3b; // ";".codeUnitAt(0)
+  static const int AT = 0x40; // "@".codeUnitAt(0)
+  static const int HASH = 0x23; // "#".codeUnitAt(0)
+  static const int PLUS = 0x2b; // "+".codeUnitAt(0)
+  static const int GREATER = 0x3e; // ">".codeUnitAt(0)
+  static const int TILDE = 0x7e; // "~".codeUnitAt(0)
+  static const int ASTERISK = 0x2a; // "*".codeUnitAt(0)
+  static const int NAMESPACE = 0x7c; // "|".codeUnitAt(0)
+  static const int COLON = 0x3a; // ":".codeUnitAt(0)
+  static const int PRIVATE_NAME = 0x5f; // "_".codeUnitAt(0)
+  static const int COMMA = 0x2c; // ",".codeUnitAt(0)
+  static const int SPACE = 0x20; // " ".codeUnitAt(0)
+  static const int TAB = 0x9; // "\t".codeUnitAt(0)
+  static const int NEWLINE = 0xa; // "\n".codeUnitAt(0)
+  static const int RETURN = 0xd; // "\r".codeUnitAt(0)
+  static const int BACKSPACE = 0x8; // "/b".codeUnitAt(0)
+  static const int FF = 0xc; // "/f".codeUnitAt(0)
+  static const int VT = 0xb; // "/v".codeUnitAt(0)
+  static const int PERCENT = 0x25; // "%".codeUnitAt(0)
+  static const int SINGLE_QUOTE = 0x27; // "'".codeUnitAt(0)
+  static const int DOUBLE_QUOTE = 0x22; // '"'.codeUnitAt(0)
+  static const int SLASH = 0x2f; // "/".codeUnitAt(0)
+  static const int EQUALS = 0x3d; // "=".codeUnitAt(0)
+  static const int OR = 0x7c; // "|".codeUnitAt(0)
+  static const int CARET = 0x5e; // "^".codeUnitAt(0)
+  static const int DOLLAR = 0x24; // "\$".codeUnitAt(0)
+  static const int LESS = 0x3c; // "<".codeUnitAt(0)
+  static const int BANG = 0x21; // "!".codeUnitAt(0)
+  static const int MINUS = 0x2d; // "-".codeUnitAt(0)
+  static const int BACKSLASH = 0x5c; // "\".codeUnitAt(0)
+  static const int AMPERSAND = 0x26; // "&".codeUnitAt(0)
+}
diff --git a/csslib/lib/src/tree.dart b/csslib/lib/src/tree.dart
new file mode 100644
index 0000000..5dad435
--- /dev/null
+++ b/csslib/lib/src/tree.dart
@@ -0,0 +1,1425 @@
+// Copyright (c) 2012, 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.
+
+part of csslib.visitor;
+
+/////////////////////////////////////////////////////////////////////////
+// CSS specific types:
+/////////////////////////////////////////////////////////////////////////
+
+class Identifier extends TreeNode {
+  String name;
+
+  Identifier(this.name, SourceSpan span) : super(span);
+
+  Identifier clone() => new Identifier(name, span);
+
+  visit(VisitorBase visitor) => visitor.visitIdentifier(this);
+
+  String toString() => name;
+}
+
+class Wildcard extends TreeNode {
+  Wildcard(SourceSpan span) : super(span);
+  Wildcard clone() => new Wildcard(span);
+  visit(VisitorBase visitor) => visitor.visitWildcard(this);
+
+  String get name => '*';
+}
+
+class ThisOperator extends TreeNode {
+  ThisOperator(SourceSpan span) : super(span);
+  ThisOperator clone() => new ThisOperator(span);
+  visit(VisitorBase visitor) => visitor.visitThisOperator(this);
+
+  String get name => '&';
+}
+
+class Negation extends TreeNode {
+  Negation(SourceSpan span) : super(span);
+  Negation clone() => new Negation(span);
+  visit(VisitorBase visitor) => visitor.visitNegation(this);
+
+  String get name => 'not';
+}
+
+// /*  ....   */
+class CssComment extends TreeNode {
+  final String comment;
+
+  CssComment(this.comment, SourceSpan span) : super(span);
+  CssComment clone() => new CssComment(comment, span);
+  visit(VisitorBase visitor) => visitor.visitCssComment(this);
+}
+
+// CDO/CDC (Comment Definition Open <!-- and Comment Definition Close -->).
+class CommentDefinition extends CssComment {
+  CommentDefinition(String comment, SourceSpan span) : super(comment, span);
+  CommentDefinition clone() => new CommentDefinition(comment, span);
+  visit(VisitorBase visitor) => visitor.visitCommentDefinition(this);
+}
+
+class SelectorGroup extends TreeNode {
+  final List<Selector> selectors;
+
+  SelectorGroup(this.selectors, SourceSpan span) : super(span);
+
+  SelectorGroup clone() => new SelectorGroup(selectors, span);
+
+  visit(VisitorBase visitor) => visitor.visitSelectorGroup(this);
+}
+
+class Selector extends TreeNode {
+  final List<SimpleSelectorSequence> simpleSelectorSequences;
+
+  Selector(this.simpleSelectorSequences, SourceSpan span) : super(span);
+
+  void add(SimpleSelectorSequence seq) => simpleSelectorSequences.add(seq);
+
+  int get length => simpleSelectorSequences.length;
+
+  Selector clone() {
+    var simpleSequences =
+        simpleSelectorSequences.map((ss) => ss.clone()).toList();
+
+    return new Selector(simpleSequences, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitSelector(this);
+}
+
+class SimpleSelectorSequence extends TreeNode {
+  /** +, >, ~, NONE */
+  int combinator;
+  final SimpleSelector simpleSelector;
+
+  SimpleSelectorSequence(this.simpleSelector, SourceSpan span,
+      [int combinator = TokenKind.COMBINATOR_NONE])
+      : combinator = combinator,
+        super(span);
+
+  bool get isCombinatorNone => combinator == TokenKind.COMBINATOR_NONE;
+  bool get isCombinatorPlus => combinator == TokenKind.COMBINATOR_PLUS;
+  bool get isCombinatorGreater => combinator == TokenKind.COMBINATOR_GREATER;
+  bool get isCombinatorTilde => combinator == TokenKind.COMBINATOR_TILDE;
+  bool get isCombinatorDescendant =>
+      combinator == TokenKind.COMBINATOR_DESCENDANT;
+
+  String get _combinatorToString => isCombinatorDescendant
+      ? ' '
+      : isCombinatorPlus
+          ? ' + '
+          : isCombinatorGreater ? ' > ' : isCombinatorTilde ? ' ~ ' : '';
+
+  SimpleSelectorSequence clone() =>
+      new SimpleSelectorSequence(simpleSelector, span, combinator);
+
+  visit(VisitorBase visitor) => visitor.visitSimpleSelectorSequence(this);
+
+  String toString() => simpleSelector.name;
+}
+
+/* All other selectors (element, #id, .class, attribute, pseudo, negation,
+ * namespace, *) are derived from this selector.
+ */
+abstract class SimpleSelector extends TreeNode {
+  final _name; // Wildcard, ThisOperator, Identifier, Negation, others?
+
+  SimpleSelector(this._name, SourceSpan span) : super(span);
+
+  String get name => _name.name;
+
+  bool get isWildcard => _name is Wildcard;
+
+  bool get isThis => _name is ThisOperator;
+
+  visit(VisitorBase visitor) => visitor.visitSimpleSelector(this);
+}
+
+// element name
+class ElementSelector extends SimpleSelector {
+  ElementSelector(name, SourceSpan span) : super(name, span);
+  visit(VisitorBase visitor) => visitor.visitElementSelector(this);
+
+  ElementSelector clone() => new ElementSelector(_name, span);
+
+  String toString() => name;
+}
+
+// namespace|element
+class NamespaceSelector extends SimpleSelector {
+  final _namespace; // null, Wildcard or Identifier
+
+  NamespaceSelector(this._namespace, var name, SourceSpan span)
+      : super(name, span);
+
+  String get namespace =>
+      _namespace is Wildcard ? '*' : _namespace == null ? '' : _namespace.name;
+
+  bool get isNamespaceWildcard => _namespace is Wildcard;
+
+  SimpleSelector get nameAsSimpleSelector => _name;
+
+  NamespaceSelector clone() => new NamespaceSelector(_namespace, "", span);
+
+  visit(VisitorBase visitor) => visitor.visitNamespaceSelector(this);
+
+  String toString() => "$namespace|${nameAsSimpleSelector.name}";
+}
+
+// [attr op value]
+class AttributeSelector extends SimpleSelector {
+  final int _op;
+  final _value;
+
+  AttributeSelector(Identifier name, this._op, this._value, SourceSpan span)
+      : super(name, span);
+
+  int get operatorKind => _op;
+
+  get value => _value;
+
+  String matchOperator() {
+    switch (_op) {
+      case TokenKind.EQUALS:
+        return '=';
+      case TokenKind.INCLUDES:
+        return '~=';
+      case TokenKind.DASH_MATCH:
+        return '|=';
+      case TokenKind.PREFIX_MATCH:
+        return '^=';
+      case TokenKind.SUFFIX_MATCH:
+        return '\$=';
+      case TokenKind.SUBSTRING_MATCH:
+        return '*=';
+      case TokenKind.NO_MATCH:
+        return '';
+    }
+  }
+
+  // Return the TokenKind for operator used by visitAttributeSelector.
+  String matchOperatorAsTokenString() {
+    switch (_op) {
+      case TokenKind.EQUALS:
+        return 'EQUALS';
+      case TokenKind.INCLUDES:
+        return 'INCLUDES';
+      case TokenKind.DASH_MATCH:
+        return 'DASH_MATCH';
+      case TokenKind.PREFIX_MATCH:
+        return 'PREFIX_MATCH';
+      case TokenKind.SUFFIX_MATCH:
+        return 'SUFFIX_MATCH';
+      case TokenKind.SUBSTRING_MATCH:
+        return 'SUBSTRING_MATCH';
+    }
+  }
+
+  String valueToString() {
+    if (_value != null) {
+      if (_value is Identifier) {
+        return _value.name;
+      } else {
+        return '"${_value}"';
+      }
+    } else {
+      return '';
+    }
+  }
+
+  AttributeSelector clone() => new AttributeSelector(_name, _op, _value, span);
+
+  visit(VisitorBase visitor) => visitor.visitAttributeSelector(this);
+
+  String toString() => "[$name${matchOperator()}${valueToString()}]";
+}
+
+// #id
+class IdSelector extends SimpleSelector {
+  IdSelector(Identifier name, SourceSpan span) : super(name, span);
+  IdSelector clone() => new IdSelector(_name, span);
+  visit(VisitorBase visitor) => visitor.visitIdSelector(this);
+
+  String toString() => "#$_name";
+}
+
+// .class
+class ClassSelector extends SimpleSelector {
+  ClassSelector(Identifier name, SourceSpan span) : super(name, span);
+  ClassSelector clone() => new ClassSelector(_name, span);
+  visit(VisitorBase visitor) => visitor.visitClassSelector(this);
+
+  String toString() => ".$_name";
+}
+
+// :pseudoClass
+class PseudoClassSelector extends SimpleSelector {
+  PseudoClassSelector(Identifier name, SourceSpan span) : super(name, span);
+  visit(VisitorBase visitor) => visitor.visitPseudoClassSelector(this);
+
+  PseudoClassSelector clone() => new PseudoClassSelector(_name, span);
+
+  String toString() => ":$name";
+}
+
+// ::pseudoElement
+class PseudoElementSelector extends SimpleSelector {
+  PseudoElementSelector(Identifier name, SourceSpan span) : super(name, span);
+  visit(VisitorBase visitor) => visitor.visitPseudoElementSelector(this);
+
+  PseudoElementSelector clone() => new PseudoElementSelector(_name, span);
+
+  String toString() => "::$name";
+}
+
+// :pseudoClassFunction(expression)
+class PseudoClassFunctionSelector extends PseudoClassSelector {
+  final SelectorExpression expression;
+
+  PseudoClassFunctionSelector(Identifier name, this.expression, SourceSpan span)
+      : super(name, span);
+
+  PseudoClassFunctionSelector clone() =>
+      new PseudoClassFunctionSelector(_name, expression, span);
+
+  visit(VisitorBase visitor) => visitor.visitPseudoClassFunctionSelector(this);
+}
+
+// ::pseudoElementFunction(expression)
+class PseudoElementFunctionSelector extends PseudoElementSelector {
+  final SelectorExpression expression;
+
+  PseudoElementFunctionSelector(
+      Identifier name, this.expression, SourceSpan span)
+      : super(name, span);
+
+  PseudoElementFunctionSelector clone() =>
+      new PseudoElementFunctionSelector(_name, expression, span);
+
+  visit(VisitorBase visitor) =>
+      visitor.visitPseudoElementFunctionSelector(this);
+}
+
+class SelectorExpression extends TreeNode {
+  final List<Expression> expressions;
+
+  SelectorExpression(this.expressions, SourceSpan span) : super(span);
+
+  SelectorExpression clone() {
+    return new SelectorExpression(
+        expressions.map((e) => e.clone()).toList(), span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitSelectorExpression(this);
+}
+
+// :NOT(negation_arg)
+class NegationSelector extends SimpleSelector {
+  final SimpleSelector negationArg;
+
+  NegationSelector(this.negationArg, SourceSpan span)
+      : super(new Negation(span), span);
+
+  NegationSelector clone() => new NegationSelector(negationArg, span);
+
+  visit(VisitorBase visitor) => visitor.visitNegationSelector(this);
+}
+
+class NoOp extends TreeNode {
+  NoOp() : super(null);
+
+  NoOp clone() => new NoOp();
+
+  visit(VisitorBase visitor) => visitor.visitNoOp(this);
+}
+
+class StyleSheet extends TreeNode {
+  /**
+   * Contains charset, ruleset, directives (media, page, etc.), and selectors.
+   */
+  final List<TreeNode> topLevels;
+
+  StyleSheet(this.topLevels, SourceSpan span) : super(span) {
+    for (final node in topLevels) {
+      assert(node is TopLevelProduction || node is Directive);
+    }
+  }
+
+  /** Selectors only in this tree. */
+  StyleSheet.selector(this.topLevels, SourceSpan span) : super(span);
+
+  StyleSheet clone() {
+    var clonedTopLevels = topLevels.map((e) => e.clone()).toList();
+    return new StyleSheet(clonedTopLevels, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitStyleSheet(this);
+}
+
+class TopLevelProduction extends TreeNode {
+  TopLevelProduction(SourceSpan span) : super(span);
+  TopLevelProduction clone() => new TopLevelProduction(span);
+  visit(VisitorBase visitor) => visitor.visitTopLevelProduction(this);
+}
+
+class RuleSet extends TopLevelProduction {
+  final SelectorGroup _selectorGroup;
+  final DeclarationGroup _declarationGroup;
+
+  RuleSet(this._selectorGroup, this._declarationGroup, SourceSpan span)
+      : super(span);
+
+  SelectorGroup get selectorGroup => _selectorGroup;
+  DeclarationGroup get declarationGroup => _declarationGroup;
+
+  RuleSet clone() {
+    var cloneSelectorGroup = _selectorGroup.clone();
+    var cloneDeclarationGroup = _declarationGroup.clone();
+    return new RuleSet(cloneSelectorGroup, cloneDeclarationGroup, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitRuleSet(this);
+}
+
+class Directive extends TreeNode {
+  Directive(SourceSpan span) : super(span);
+
+  bool get isBuiltIn => true; // Known CSS directive?
+  bool get isExtension => false; // SCSS extension?
+
+  Directive clone() => new Directive(span);
+  visit(VisitorBase visitor) => visitor.visitDirective(this);
+}
+
+class ImportDirective extends Directive {
+  /** import name specified. */
+  final String import;
+
+  /** Any media queries for this import. */
+  final List<MediaQuery> mediaQueries;
+
+  ImportDirective(this.import, this.mediaQueries, SourceSpan span)
+      : super(span);
+
+  ImportDirective clone() {
+    var cloneMediaQueries = [];
+    for (var mediaQuery in mediaQueries) {
+      cloneMediaQueries.add(mediaQuery.clone());
+    }
+    return new ImportDirective(import, cloneMediaQueries, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitImportDirective(this);
+}
+
+/**
+ *  MediaExpression grammar:
+ *    '(' S* media_feature S* [ ':' S* expr ]? ')' S*
+ */
+class MediaExpression extends TreeNode {
+  final bool andOperator;
+  final Identifier _mediaFeature;
+  final Expressions exprs;
+
+  MediaExpression(
+      this.andOperator, this._mediaFeature, this.exprs, SourceSpan span)
+      : super(span);
+
+  String get mediaFeature => _mediaFeature.name;
+
+  MediaExpression clone() {
+    var clonedExprs = exprs.clone();
+    return new MediaExpression(andOperator, _mediaFeature, clonedExprs, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitMediaExpression(this);
+}
+
+/**
+ * MediaQuery grammar:
+ *    : [ONLY | NOT]? S* media_type S* [ AND S* media_expression ]*
+ *    | media_expression [ AND S* media_expression ]*
+ *   media_type
+ *    : IDENT
+ *   media_expression
+ *    : '(' S* media_feature S* [ ':' S* expr ]? ')' S*
+ *   media_feature
+ *    : IDENT
+ */
+class MediaQuery extends TreeNode {
+  /** not, only or no operator. */
+  final int _mediaUnary;
+  final Identifier _mediaType;
+  final List<MediaExpression> expressions;
+
+  MediaQuery(
+      this._mediaUnary, this._mediaType, this.expressions, SourceSpan span)
+      : super(span);
+
+  bool get hasMediaType => _mediaType != null;
+  String get mediaType => _mediaType.name;
+
+  bool get hasUnary => _mediaUnary != -1;
+  String get unary =>
+      TokenKind.idToValue(TokenKind.MEDIA_OPERATORS, _mediaUnary).toUpperCase();
+
+  MediaQuery clone() {
+    var cloneExpressions = [];
+    for (var expr in expressions) {
+      cloneExpressions.add(expr.clone());
+    }
+    return new MediaQuery(_mediaUnary, _mediaType, cloneExpressions, span);
+  }
+  visit(VisitorBase visitor) => visitor.visitMediaQuery(this);
+}
+
+class MediaDirective extends Directive {
+  final List<MediaQuery> mediaQueries;
+  final List<RuleSet> rulesets;
+
+  MediaDirective(this.mediaQueries, this.rulesets, SourceSpan span)
+      : super(span);
+
+  MediaDirective clone() {
+    var cloneQueries = [];
+    for (var mediaQuery in mediaQueries) {
+      cloneQueries.add(mediaQuery.clone());
+    }
+    var cloneRulesets = [];
+    for (var ruleset in rulesets) {
+      cloneRulesets.add(ruleset.clone());
+    }
+    return new MediaDirective(cloneQueries, cloneRulesets, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitMediaDirective(this);
+}
+
+class HostDirective extends Directive {
+  final List<RuleSet> rulesets;
+
+  HostDirective(this.rulesets, SourceSpan span) : super(span);
+
+  HostDirective clone() {
+    var cloneRulesets = [];
+    for (var ruleset in rulesets) {
+      cloneRulesets.add(ruleset.clone());
+    }
+    return new HostDirective(cloneRulesets, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitHostDirective(this);
+}
+
+class PageDirective extends Directive {
+  final String _ident;
+  final String _pseudoPage;
+  final List<DeclarationGroup> _declsMargin;
+
+  PageDirective(
+      this._ident, this._pseudoPage, this._declsMargin, SourceSpan span)
+      : super(span);
+
+  PageDirective clone() {
+    var cloneDeclsMargin = [];
+    for (var declMargin in _declsMargin) {
+      cloneDeclsMargin.add(declMargin.clone());
+    }
+    return new PageDirective(_ident, _pseudoPage, cloneDeclsMargin, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitPageDirective(this);
+
+  bool get hasIdent => _ident != null && _ident.length > 0;
+  bool get hasPseudoPage => _pseudoPage != null && _pseudoPage.length > 0;
+}
+
+class CharsetDirective extends Directive {
+  final String charEncoding;
+
+  CharsetDirective(this.charEncoding, SourceSpan span) : super(span);
+  CharsetDirective clone() => new CharsetDirective(charEncoding, span);
+  visit(VisitorBase visitor) => visitor.visitCharsetDirective(this);
+}
+
+class KeyFrameDirective extends Directive {
+  /*
+   * Either @keyframe or keyframe prefixed with @-webkit-, @-moz-, @-ms-, @-o-.
+   */
+  final int _keyframeName;
+  final name;
+  final List<KeyFrameBlock> _blocks;
+
+  KeyFrameDirective(this._keyframeName, this.name, SourceSpan span)
+      : _blocks = [],
+        super(span);
+
+  add(KeyFrameBlock block) {
+    _blocks.add(block);
+  }
+
+  String get keyFrameName {
+    switch (_keyframeName) {
+      case TokenKind.DIRECTIVE_KEYFRAMES:
+      case TokenKind.DIRECTIVE_MS_KEYFRAMES:
+        return '@keyframes';
+      case TokenKind.DIRECTIVE_WEB_KIT_KEYFRAMES:
+        return '@-webkit-keyframes';
+      case TokenKind.DIRECTIVE_MOZ_KEYFRAMES:
+        return '@-moz-keyframes';
+      case TokenKind.DIRECTIVE_O_KEYFRAMES:
+        return '@-o-keyframes';
+    }
+  }
+
+  KeyFrameDirective clone() {
+    var cloneBlocks = [];
+    for (var block in _blocks) {
+      cloneBlocks.add(block.clone());
+    }
+    return new KeyFrameDirective(_keyframeName, cloneBlocks, span);
+  }
+  visit(VisitorBase visitor) => visitor.visitKeyFrameDirective(this);
+}
+
+class KeyFrameBlock extends Expression {
+  final Expressions _blockSelectors;
+  final DeclarationGroup _declarations;
+
+  KeyFrameBlock(this._blockSelectors, this._declarations, SourceSpan span)
+      : super(span);
+
+  KeyFrameBlock clone() =>
+      new KeyFrameBlock(_blockSelectors.clone(), _declarations.clone(), span);
+  visit(VisitorBase visitor) => visitor.visitKeyFrameBlock(this);
+}
+
+class FontFaceDirective extends Directive {
+  final DeclarationGroup _declarations;
+
+  FontFaceDirective(this._declarations, SourceSpan span) : super(span);
+
+  FontFaceDirective clone() =>
+      new FontFaceDirective(_declarations.clone(), span);
+  visit(VisitorBase visitor) => visitor.visitFontFaceDirective(this);
+}
+
+class StyletDirective extends Directive {
+  final String dartClassName;
+  final List<RuleSet> rulesets;
+
+  StyletDirective(this.dartClassName, this.rulesets, SourceSpan span)
+      : super(span);
+
+  bool get isBuiltIn => false;
+  bool get isExtension => true;
+
+  StyletDirective clone() {
+    var cloneRulesets = [];
+    for (var ruleset in rulesets) {
+      cloneRulesets.add(ruleset.clone());
+    }
+    return new StyletDirective(dartClassName, cloneRulesets, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitStyletDirective(this);
+}
+
+class NamespaceDirective extends Directive {
+  /** Namespace prefix. */
+  final String _prefix;
+
+  /** URI associated with this namespace. */
+  final String _uri;
+
+  NamespaceDirective(this._prefix, this._uri, SourceSpan span) : super(span);
+
+  NamespaceDirective clone() => new NamespaceDirective(_prefix, _uri, span);
+
+  visit(VisitorBase visitor) => visitor.visitNamespaceDirective(this);
+
+  String get prefix => _prefix.length > 0 ? '$_prefix ' : '';
+}
+
+/** To support Less syntax @name: expression */
+class VarDefinitionDirective extends Directive {
+  final VarDefinition def;
+
+  VarDefinitionDirective(this.def, SourceSpan span) : super(span);
+
+  VarDefinitionDirective clone() =>
+      new VarDefinitionDirective(def.clone(), span);
+
+  visit(VisitorBase visitor) => visitor.visitVarDefinitionDirective(this);
+}
+
+class MixinDefinition extends Directive {
+  final String name;
+  final List definedArgs;
+  final bool varArgs;
+
+  MixinDefinition(this.name, this.definedArgs, this.varArgs, SourceSpan span)
+      : super(span);
+
+  MixinDefinition clone() {
+    var cloneDefinedArgs = [];
+    for (var definedArg in definedArgs) {
+      cloneDefinedArgs.add(definedArg.clone());
+    }
+    return new MixinDefinition(name, cloneDefinedArgs, varArgs, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitMixinDefinition(this);
+}
+
+/** Support a Sass @mixin. See http://sass-lang.com for description. */
+class MixinRulesetDirective extends MixinDefinition {
+  final List<RuleSet> rulesets;
+
+  MixinRulesetDirective(String name, List<VarDefinitionDirective> args,
+      bool varArgs, this.rulesets, SourceSpan span)
+      : super(name, args, varArgs, span);
+
+  MixinRulesetDirective clone() {
+    var clonedArgs = [];
+    for (var arg in definedArgs) {
+      clonedArgs.add(arg.clone());
+    }
+    var clonedRulesets = [];
+    for (var ruleset in rulesets) {
+      clonedRulesets.add(ruleset.clone());
+    }
+    return new MixinRulesetDirective(
+        name, clonedArgs, varArgs, clonedRulesets, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitMixinRulesetDirective(this);
+}
+
+class MixinDeclarationDirective extends MixinDefinition {
+  final DeclarationGroup declarations;
+
+  MixinDeclarationDirective(String name, List<VarDefinitionDirective> args,
+      bool varArgs, this.declarations, SourceSpan span)
+      : super(name, args, varArgs, span);
+
+  MixinDeclarationDirective clone() {
+    var clonedArgs = [];
+    for (var arg in definedArgs) {
+      clonedArgs.add(arg.clone());
+    }
+    return new MixinDeclarationDirective(
+        name, clonedArgs, varArgs, declarations.clone(), span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitMixinDeclarationDirective(this);
+}
+
+/** To support consuming a SASS mixin @include. */
+class IncludeDirective extends Directive {
+  final String name;
+  final List<List<TreeNode>> args;
+
+  IncludeDirective(this.name, this.args, SourceSpan span) : super(span);
+
+  IncludeDirective clone() {
+    var cloneArgs = [];
+    for (var arg in args) {
+      for (var term in arg) {
+        cloneArgs.add(term.clone());
+      }
+    }
+    return new IncludeDirective(name, cloneArgs, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitIncludeDirective(this);
+}
+
+/** To support SASS @content. */
+class ContentDirective extends Directive {
+  ContentDirective(SourceSpan span) : super(span);
+
+  visit(VisitorBase visitor) => visitor.visitContentDirective(this);
+}
+
+class Declaration extends TreeNode {
+  final Identifier _property;
+  final Expression _expression;
+  /** Style exposed to Dart. */
+  dynamic dartStyle;
+  final bool important;
+
+  /**
+   * IE CSS hacks that can only be read by a particular IE version.
+   *   7 implies IE 7 or older property (e.g., *background: blue;)
+   *   Note:  IE 8 or older property (e.g., background: green\9;) is handled
+   *          by IE8Term in declaration expression handling.
+   *   Note:  IE 6 only property with a leading underscore is a valid IDENT
+   *          since an ident can start with underscore (e.g., _background: red;)
+   */
+  final bool isIE7;
+
+  Declaration(this._property, this._expression, this.dartStyle, SourceSpan span,
+      {important: false, ie7: false})
+      : this.important = important,
+        this.isIE7 = ie7,
+        super(span);
+
+  String get property => isIE7 ? '*${_property.name}' : _property.name;
+  Expression get expression => _expression;
+
+  bool get hasDartStyle => dartStyle != null;
+
+  Declaration clone() => new Declaration(
+      _property.clone(), _expression.clone(), dartStyle, span,
+      important: important);
+
+  visit(VisitorBase visitor) => visitor.visitDeclaration(this);
+}
+
+// TODO(terry): Consider 2 kinds of VarDefinitions static at top-level and
+//              dynamic when in a declaration.  Currently, Less syntax
+//              '@foo: expression' and 'var-foo: expression' in a declaration
+//              are statically resolved. Better solution, if @foo or var-foo
+//              are top-level are then statically resolved and var-foo in a
+//              declaration group (surrounded by a selector) would be dynamic.
+class VarDefinition extends Declaration {
+  bool badUsage = false;
+
+  VarDefinition(Identifier definedName, Expression expr, SourceSpan span)
+      : super(definedName, expr, null, span);
+
+  String get definedName => _property.name;
+
+  VarDefinition clone() => new VarDefinition(
+      _property.clone(), expression != null ? expression.clone() : null, span);
+
+  visit(VisitorBase visitor) => visitor.visitVarDefinition(this);
+}
+
+/**
+ * Node for usage of @include mixin[(args,...)] found in a declaration group
+ * instead of at a ruleset (toplevel) e.g.,
+ * div {
+ *   @include mixin1;
+ * }
+ */
+class IncludeMixinAtDeclaration extends Declaration {
+  final IncludeDirective include;
+
+  IncludeMixinAtDeclaration(this.include, SourceSpan span)
+      : super(null, null, null, span);
+
+  IncludeMixinAtDeclaration clone() =>
+      new IncludeMixinAtDeclaration(include.clone(), span);
+
+  visit(VisitorBase visitor) => visitor.visitIncludeMixinAtDeclaration(this);
+}
+
+class ExtendDeclaration extends Declaration {
+  final List<TreeNode> selectors;
+
+  ExtendDeclaration(this.selectors, SourceSpan span)
+      : super(null, null, null, span);
+
+  ExtendDeclaration clone() {
+    var newSelector = selectors.map((s) => s.clone()).toList();
+    return new ExtendDeclaration(newSelector, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitExtendDeclaration(this);
+}
+
+class DeclarationGroup extends TreeNode {
+  /** Can be either Declaration or RuleSet (if nested selector). */
+  final List declarations;
+
+  DeclarationGroup(this.declarations, SourceSpan span) : super(span);
+
+  DeclarationGroup clone() {
+    var clonedDecls = declarations.map((d) => d.clone()).toList();
+    return new DeclarationGroup(clonedDecls, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitDeclarationGroup(this);
+}
+
+class MarginGroup extends DeclarationGroup {
+  final int margin_sym; // TokenType for for @margin sym.
+
+  MarginGroup(this.margin_sym, List<Declaration> decls, SourceSpan span)
+      : super(decls, span);
+  MarginGroup clone() =>
+      new MarginGroup(margin_sym, super.clone() as dynamic, span);
+  visit(VisitorBase visitor) => visitor.visitMarginGroup(this);
+}
+
+class VarUsage extends Expression {
+  final String name;
+  final List<Expression> defaultValues;
+
+  VarUsage(this.name, this.defaultValues, SourceSpan span) : super(span);
+
+  VarUsage clone() {
+    var clonedValues = [];
+    for (var expr in defaultValues) {
+      clonedValues.add(expr.clone());
+    }
+    return new VarUsage(name, clonedValues, span);
+  }
+
+  visit(VisitorBase visitor) => visitor.visitVarUsage(this);
+}
+
+class OperatorSlash extends Expression {
+  OperatorSlash(SourceSpan span) : super(span);
+  OperatorSlash clone() => new OperatorSlash(span);
+  visit(VisitorBase visitor) => visitor.visitOperatorSlash(this);
+}
+
+class OperatorComma extends Expression {
+  OperatorComma(SourceSpan span) : super(span);
+  OperatorComma clone() => new OperatorComma(span);
+  visit(VisitorBase visitor) => visitor.visitOperatorComma(this);
+}
+
+class OperatorPlus extends Expression {
+  OperatorPlus(SourceSpan span) : super(span);
+  OperatorPlus clone() => new OperatorPlus(span);
+  visit(VisitorBase visitor) => visitor.visitOperatorPlus(this);
+}
+
+class OperatorMinus extends Expression {
+  OperatorMinus(SourceSpan span) : super(span);
+  OperatorMinus clone() => new OperatorMinus(span);
+  visit(VisitorBase visitor) => visitor.visitOperatorMinus(this);
+}
+
+class UnicodeRangeTerm extends Expression {
+  final String first;
+  final String second;
+
+  UnicodeRangeTerm(this.first, this.second, SourceSpan span) : super(span);
+
+  bool get hasSecond => second != null;
+
+  UnicodeRangeTerm clone() => new UnicodeRangeTerm(first, second, span);
+
+  visit(VisitorBase visitor) => visitor.visitUnicodeRangeTerm(this);
+}
+
+class LiteralTerm extends Expression {
+  // TODO(terry): value and text fields can be made final once all CSS resources
+  //              are copied/symlink'd in the build tool and UriVisitor in
+  //              web_ui is removed.
+  dynamic value;
+  String text;
+
+  LiteralTerm(this.value, this.text, SourceSpan span) : super(span);
+
+  LiteralTerm clone() => new LiteralTerm(value, text, span);
+
+  visit(VisitorBase visitor) => visitor.visitLiteralTerm(this);
+}
+
+class NumberTerm extends LiteralTerm {
+  NumberTerm(value, String t, SourceSpan span) : super(value, t, span);
+  NumberTerm clone() => new NumberTerm(value, text, span);
+  visit(VisitorBase visitor) => visitor.visitNumberTerm(this);
+}
+
+class UnitTerm extends LiteralTerm {
+  final int unit;
+
+  UnitTerm(value, String t, SourceSpan span, this.unit) : super(value, t, span);
+
+  UnitTerm clone() => new UnitTerm(value, text, span, unit);
+
+  visit(VisitorBase visitor) => visitor.visitUnitTerm(this);
+
+  String unitToString() => TokenKind.unitToString(unit);
+
+  String toString() => '$text${unitToString()}';
+}
+
+class LengthTerm extends UnitTerm {
+  LengthTerm(value, String t, SourceSpan span,
+      [int unit = TokenKind.UNIT_LENGTH_PX])
+      : super(value, t, span, unit) {
+    assert(this.unit == TokenKind.UNIT_LENGTH_PX ||
+        this.unit == TokenKind.UNIT_LENGTH_CM ||
+        this.unit == TokenKind.UNIT_LENGTH_MM ||
+        this.unit == TokenKind.UNIT_LENGTH_IN ||
+        this.unit == TokenKind.UNIT_LENGTH_PT ||
+        this.unit == TokenKind.UNIT_LENGTH_PC);
+  }
+  LengthTerm clone() => new LengthTerm(value, text, span, unit);
+  visit(VisitorBase visitor) => visitor.visitLengthTerm(this);
+}
+
+class PercentageTerm extends LiteralTerm {
+  PercentageTerm(value, String t, SourceSpan span) : super(value, t, span);
+  PercentageTerm clone() => new PercentageTerm(value, text, span);
+  visit(VisitorBase visitor) => visitor.visitPercentageTerm(this);
+}
+
+class EmTerm extends LiteralTerm {
+  EmTerm(value, String t, SourceSpan span) : super(value, t, span);
+  EmTerm clone() => new EmTerm(value, text, span);
+  visit(VisitorBase visitor) => visitor.visitEmTerm(this);
+}
+
+class ExTerm extends LiteralTerm {
+  ExTerm(value, String t, SourceSpan span) : super(value, t, span);
+  ExTerm clone() => new ExTerm(value, text, span);
+  visit(VisitorBase visitor) => visitor.visitExTerm(this);
+}
+
+class AngleTerm extends UnitTerm {
+  AngleTerm(var value, String t, SourceSpan span,
+      [int unit = TokenKind.UNIT_LENGTH_PX])
+      : super(value, t, span, unit) {
+    assert(this.unit == TokenKind.UNIT_ANGLE_DEG ||
+        this.unit == TokenKind.UNIT_ANGLE_RAD ||
+        this.unit == TokenKind.UNIT_ANGLE_GRAD ||
+        this.unit == TokenKind.UNIT_ANGLE_TURN);
+  }
+
+  AngleTerm clone() => new AngleTerm(value, text, span, unit);
+  visit(VisitorBase visitor) => visitor.visitAngleTerm(this);
+}
+
+class TimeTerm extends UnitTerm {
+  TimeTerm(var value, String t, SourceSpan span,
+      [int unit = TokenKind.UNIT_LENGTH_PX])
+      : super(value, t, span, unit) {
+    assert(this.unit == TokenKind.UNIT_ANGLE_DEG ||
+        this.unit == TokenKind.UNIT_TIME_MS ||
+        this.unit == TokenKind.UNIT_TIME_S);
+  }
+
+  TimeTerm clone() => new TimeTerm(value, text, span, unit);
+  visit(VisitorBase visitor) => visitor.visitTimeTerm(this);
+}
+
+class FreqTerm extends UnitTerm {
+  FreqTerm(var value, String t, SourceSpan span,
+      [int unit = TokenKind.UNIT_LENGTH_PX])
+      : super(value, t, span, unit) {
+    assert(unit == TokenKind.UNIT_FREQ_HZ || unit == TokenKind.UNIT_FREQ_KHZ);
+  }
+
+  FreqTerm clone() => new FreqTerm(value, text, span, unit);
+  visit(VisitorBase visitor) => visitor.visitFreqTerm(this);
+}
+
+class FractionTerm extends LiteralTerm {
+  FractionTerm(var value, String t, SourceSpan span) : super(value, t, span);
+
+  FractionTerm clone() => new FractionTerm(value, text, span);
+  visit(VisitorBase visitor) => visitor.visitFractionTerm(this);
+}
+
+class UriTerm extends LiteralTerm {
+  UriTerm(String value, SourceSpan span) : super(value, value, span);
+
+  UriTerm clone() => new UriTerm(value, span);
+  visit(VisitorBase visitor) => visitor.visitUriTerm(this);
+}
+
+class ResolutionTerm extends UnitTerm {
+  ResolutionTerm(var value, String t, SourceSpan span,
+      [int unit = TokenKind.UNIT_LENGTH_PX])
+      : super(value, t, span, unit) {
+    assert(unit == TokenKind.UNIT_RESOLUTION_DPI ||
+        unit == TokenKind.UNIT_RESOLUTION_DPCM ||
+        unit == TokenKind.UNIT_RESOLUTION_DPPX);
+  }
+
+  ResolutionTerm clone() => new ResolutionTerm(value, text, span, unit);
+  visit(VisitorBase visitor) => visitor.visitResolutionTerm(this);
+}
+
+class ChTerm extends UnitTerm {
+  ChTerm(var value, String t, SourceSpan span,
+      [int unit = TokenKind.UNIT_LENGTH_PX])
+      : super(value, t, span, unit) {
+    assert(unit == TokenKind.UNIT_CH);
+  }
+
+  ChTerm clone() => new ChTerm(value, text, span, unit);
+  visit(VisitorBase visitor) => visitor.visitChTerm(this);
+}
+
+class RemTerm extends UnitTerm {
+  RemTerm(var value, String t, SourceSpan span,
+      [int unit = TokenKind.UNIT_LENGTH_PX])
+      : super(value, t, span, unit) {
+    assert(unit == TokenKind.UNIT_REM);
+  }
+
+  RemTerm clone() => new RemTerm(value, text, span, unit);
+  visit(VisitorBase visitor) => visitor.visitRemTerm(this);
+}
+
+class ViewportTerm extends UnitTerm {
+  ViewportTerm(var value, String t, SourceSpan span,
+      [int unit = TokenKind.UNIT_LENGTH_PX])
+      : super(value, t, span, unit) {
+    assert(unit == TokenKind.UNIT_VIEWPORT_VW ||
+        unit == TokenKind.UNIT_VIEWPORT_VH ||
+        unit == TokenKind.UNIT_VIEWPORT_VMIN ||
+        unit == TokenKind.UNIT_VIEWPORT_VMAX);
+  }
+
+  ViewportTerm clone() => new ViewportTerm(value, text, span, unit);
+  visit(VisitorBase visitor) => visitor.visitViewportTerm(this);
+}
+
+/** Type to signal a bad hex value for HexColorTerm.value. */
+class BAD_HEX_VALUE {}
+
+class HexColorTerm extends LiteralTerm {
+  HexColorTerm(var value, String t, SourceSpan span) : super(value, t, span);
+
+  HexColorTerm clone() => new HexColorTerm(value, text, span);
+  visit(VisitorBase visitor) => visitor.visitHexColorTerm(this);
+}
+
+class FunctionTerm extends LiteralTerm {
+  final Expressions _params;
+
+  FunctionTerm(var value, String t, this._params, SourceSpan span)
+      : super(value, t, span);
+
+  FunctionTerm clone() => new FunctionTerm(value, text, _params.clone(), span);
+  visit(VisitorBase visitor) => visitor.visitFunctionTerm(this);
+}
+
+/**
+ * A "\9" was encountered at the end of the expression and before a semi-colon.
+ * This is an IE trick to ignore a property or value except by IE 8 and older
+ * browsers.
+ */
+class IE8Term extends LiteralTerm {
+  IE8Term(SourceSpan span) : super('\\9', '\\9', span);
+  IE8Term clone() => new IE8Term(span);
+  visit(VisitorBase visitor) => visitor.visitIE8Term(this);
+}
+
+class GroupTerm extends Expression {
+  final List<LiteralTerm> _terms;
+
+  GroupTerm(SourceSpan span)
+      : _terms = [],
+        super(span);
+
+  void add(LiteralTerm term) {
+    _terms.add(term);
+  }
+
+  GroupTerm clone() => new GroupTerm(span);
+  visit(VisitorBase visitor) => visitor.visitGroupTerm(this);
+}
+
+class ItemTerm extends NumberTerm {
+  ItemTerm(var value, String t, SourceSpan span) : super(value, t, span);
+
+  ItemTerm clone() => new ItemTerm(value, text, span);
+  visit(VisitorBase visitor) => visitor.visitItemTerm(this);
+}
+
+class Expressions extends Expression {
+  final List<Expression> expressions = [];
+
+  Expressions(SourceSpan span) : super(span);
+
+  void add(Expression expression) {
+    expressions.add(expression);
+  }
+
+  Expressions clone() {
+    var clonedExprs = new Expressions(span);
+    for (var expr in expressions) {
+      clonedExprs.add(expr.clone());
+    }
+    return clonedExprs;
+  }
+  visit(VisitorBase visitor) => visitor.visitExpressions(this);
+}
+
+class BinaryExpression extends Expression {
+  final Token op;
+  final Expression x;
+  final Expression y;
+
+  BinaryExpression(this.op, this.x, this.y, SourceSpan span) : super(span);
+
+  BinaryExpression clone() =>
+      new BinaryExpression(op, x.clone(), y.clone(), span);
+  visit(VisitorBase visitor) => visitor.visitBinaryExpression(this);
+}
+
+class UnaryExpression extends Expression {
+  final Token op;
+  final Expression self;
+
+  UnaryExpression(this.op, this.self, SourceSpan span) : super(span);
+
+  UnaryExpression clone() => new UnaryExpression(op, self.clone(), span);
+  visit(VisitorBase visitor) => visitor.visitUnaryExpression(this);
+}
+
+abstract class DartStyleExpression extends TreeNode {
+  static const int unknownType = 0;
+  static const int fontStyle = 1;
+  static const int marginStyle = 2;
+  static const int borderStyle = 3;
+  static const int paddingStyle = 4;
+  static const int heightStyle = 5;
+  static const int widthStyle = 6;
+
+  final int _styleType;
+  int priority;
+
+  DartStyleExpression(this._styleType, SourceSpan span) : super(span);
+
+  /*
+   * Merges give 2 DartStyleExpression (or derived from DartStyleExpression,
+   * e.g., FontExpression, etc.) will merge if the two expressions are of the
+   * same property name (implies same exact type e.g, FontExpression).
+   */
+  merged(DartStyleExpression newDartExpr);
+
+  bool get isUnknown => _styleType == 0 || _styleType == null;
+  bool get isFont => _styleType == fontStyle;
+  bool get isMargin => _styleType == marginStyle;
+  bool get isBorder => _styleType == borderStyle;
+  bool get isPadding => _styleType == paddingStyle;
+  bool get isHeight => _styleType == heightStyle;
+  bool get isWidth => _styleType == widthStyle;
+  bool get isBoxExpression => isMargin || isBorder || isPadding;
+
+  bool isSame(DartStyleExpression other) => this._styleType == other._styleType;
+
+  visit(VisitorBase visitor) => visitor.visitDartStyleExpression(this);
+}
+
+class FontExpression extends DartStyleExpression {
+  final Font font;
+
+  //   font-style font-variant font-weight font-size/line-height font-family
+  // TODO(terry): Only px/pt for now need to handle all possible units to
+  //              support calc expressions on units.
+  FontExpression(SourceSpan span, {dynamic size, List<String> family,
+      int weight, String style, String variant, LineHeight lineHeight})
+      : font = new Font(
+          size: size is LengthTerm ? size.value : size,
+          family: family,
+          weight: weight,
+          style: style,
+          variant: variant,
+          lineHeight: lineHeight),
+        super(DartStyleExpression.fontStyle, span);
+
+  FontExpression merged(DartStyleExpression newFontExpr) {
+    if (newFontExpr is FontExpression && this.isFont && newFontExpr.isFont) {
+      return new FontExpression.merge(this, newFontExpr);
+    }
+    return null;
+  }
+
+  /**
+   * Merge the two FontExpression and return the result.
+   */
+  factory FontExpression.merge(FontExpression x, FontExpression y) {
+    return new FontExpression._merge(x, y, y.span);
+  }
+
+  FontExpression._merge(FontExpression x, FontExpression y, SourceSpan span)
+      : font = new Font.merge(x.font, y.font),
+        super(DartStyleExpression.fontStyle, span);
+
+  FontExpression clone() => new FontExpression(span,
+      size: font.size,
+      family: font.family,
+      weight: font.weight,
+      style: font.style,
+      variant: font.variant,
+      lineHeight: font.lineHeight);
+
+  visit(VisitorBase visitor) => visitor.visitFontExpression(this);
+}
+
+abstract class BoxExpression extends DartStyleExpression {
+  final BoxEdge box;
+
+  BoxExpression(int styleType, SourceSpan span, this.box)
+      : super(styleType, span);
+
+  visit(VisitorBase visitor) => visitor.visitBoxExpression(this);
+
+  String get formattedBoxEdge {
+    if (box.top == box.left && box.top == box.bottom && box.top == box.right) {
+      return '.uniform(${box.top})';
+    } else {
+      var left = box.left == null ? 0 : box.left;
+      var top = box.top == null ? 0 : box.top;
+      var right = box.right == null ? 0 : box.right;
+      var bottom = box.bottom == null ? 0 : box.bottom;
+      return '.clockwiseFromTop($top,$right,$bottom,$left)';
+    }
+  }
+}
+
+class MarginExpression extends BoxExpression {
+  // TODO(terry): Does auto for margin need to be exposed to Dart UI framework?
+  /** Margin expression ripped apart. */
+  MarginExpression(SourceSpan span, {num top, num right, num bottom, num left})
+      : super(DartStyleExpression.marginStyle, span,
+          new BoxEdge(left, top, right, bottom));
+
+  MarginExpression.boxEdge(SourceSpan span, BoxEdge box)
+      : super(DartStyleExpression.marginStyle, span, box);
+
+  merged(DartStyleExpression newMarginExpr) {
+    if (newMarginExpr is MarginExpression &&
+        this.isMargin &&
+        newMarginExpr.isMargin) {
+      return new MarginExpression.merge(this, newMarginExpr);
+    }
+
+    return null;
+  }
+
+  /**
+   * Merge the two MarginExpressions and return the result.
+   */
+  factory MarginExpression.merge(MarginExpression x, MarginExpression y) {
+    return new MarginExpression._merge(x, y, y.span);
+  }
+
+  MarginExpression._merge(
+      MarginExpression x, MarginExpression y, SourceSpan span)
+      : super(x._styleType, span, new BoxEdge.merge(x.box, y.box));
+
+  MarginExpression clone() => new MarginExpression(span,
+      top: box.top, right: box.right, bottom: box.bottom, left: box.left);
+
+  visit(VisitorBase visitor) => visitor.visitMarginExpression(this);
+}
+
+class BorderExpression extends BoxExpression {
+  /** Border expression ripped apart. */
+  BorderExpression(SourceSpan span, {num top, num right, num bottom, num left})
+      : super(DartStyleExpression.borderStyle, span,
+          new BoxEdge(left, top, right, bottom));
+
+  BorderExpression.boxEdge(SourceSpan span, BoxEdge box)
+      : super(DartStyleExpression.borderStyle, span, box);
+
+  merged(DartStyleExpression newBorderExpr) {
+    if (newBorderExpr is BorderExpression &&
+        this.isBorder &&
+        newBorderExpr.isBorder) {
+      return new BorderExpression.merge(this, newBorderExpr);
+    }
+
+    return null;
+  }
+
+  /**
+   * Merge the two BorderExpression and return the result.
+   */
+  factory BorderExpression.merge(BorderExpression x, BorderExpression y) {
+    return new BorderExpression._merge(x, y, y.span);
+  }
+
+  BorderExpression._merge(
+      BorderExpression x, BorderExpression y, SourceSpan span)
+      : super(DartStyleExpression.borderStyle, span,
+          new BoxEdge.merge(x.box, y.box));
+
+  BorderExpression clone() => new BorderExpression(span,
+      top: box.top, right: box.right, bottom: box.bottom, left: box.left);
+
+  visit(VisitorBase visitor) => visitor.visitBorderExpression(this);
+}
+
+class HeightExpression extends DartStyleExpression {
+  final height;
+
+  HeightExpression(SourceSpan span, this.height)
+      : super(DartStyleExpression.heightStyle, span);
+
+  merged(DartStyleExpression newHeightExpr) {
+    if (newHeightExpr is DartStyleExpression &&
+        this.isHeight &&
+        newHeightExpr.isHeight) {
+      return newHeightExpr;
+    }
+
+    return null;
+  }
+
+  HeightExpression clone() => new HeightExpression(span, height);
+  visit(VisitorBase visitor) => visitor.visitHeightExpression(this);
+}
+
+class WidthExpression extends DartStyleExpression {
+  final width;
+
+  WidthExpression(SourceSpan span, this.width)
+      : super(DartStyleExpression.widthStyle, span);
+
+  merged(DartStyleExpression newWidthExpr) {
+    if (newWidthExpr is WidthExpression &&
+        this.isWidth &&
+        newWidthExpr.isWidth) {
+      return newWidthExpr;
+    }
+
+    return null;
+  }
+
+  WidthExpression clone() => new WidthExpression(span, width);
+  visit(VisitorBase visitor) => visitor.visitWidthExpression(this);
+}
+
+class PaddingExpression extends BoxExpression {
+  /** Padding expression ripped apart. */
+  PaddingExpression(SourceSpan span, {num top, num right, num bottom, num left})
+      : super(DartStyleExpression.paddingStyle, span,
+          new BoxEdge(left, top, right, bottom));
+
+  PaddingExpression.boxEdge(SourceSpan span, BoxEdge box)
+      : super(DartStyleExpression.paddingStyle, span, box);
+
+  merged(DartStyleExpression newPaddingExpr) {
+    if (newPaddingExpr is PaddingExpression &&
+        this.isPadding &&
+        newPaddingExpr.isPadding) {
+      return new PaddingExpression.merge(this, newPaddingExpr);
+    }
+
+    return null;
+  }
+
+  /**
+   * Merge the two PaddingExpression and return the result.
+   */
+  factory PaddingExpression.merge(PaddingExpression x, PaddingExpression y) {
+    return new PaddingExpression._merge(x, y, y.span);
+  }
+
+  PaddingExpression._merge(
+      PaddingExpression x, PaddingExpression y, SourceSpan span)
+      : super(DartStyleExpression.paddingStyle, span,
+          new BoxEdge.merge(x.box, y.box));
+
+  PaddingExpression clone() => new PaddingExpression(span,
+      top: box.top, right: box.right, bottom: box.bottom, left: box.left);
+  visit(VisitorBase visitor) => visitor.visitPaddingExpression(this);
+}
diff --git a/csslib/lib/src/tree_base.dart b/csslib/lib/src/tree_base.dart
new file mode 100644
index 0000000..095b493
--- /dev/null
+++ b/csslib/lib/src/tree_base.dart
@@ -0,0 +1,97 @@
+// Copyright (c) 2012, 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.
+
+part of csslib.visitor;
+
+/**
+ * The base type for all nodes in a CSS abstract syntax tree.
+ */
+abstract class TreeNode {
+  /** The source code this [TreeNode] represents. */
+  final SourceSpan span;
+
+  TreeNode(this.span);
+
+  TreeNode clone();
+
+  /** Classic double-dispatch visitor for implementing passes. */
+  void visit(VisitorBase visitor);
+
+  /** A multiline string showing the node and its children. */
+  String toDebugString() {
+    var to = new TreeOutput();
+    var tp = new _TreePrinter(to, true);
+    this.visit(tp);
+    return to.buf.toString();
+  }
+}
+
+/** The base type for expressions. */
+abstract class Expression extends TreeNode {
+  Expression(SourceSpan span) : super(span);
+}
+
+/** Simple class to provide a textual dump of trees for debugging. */
+class TreeOutput {
+  int depth = 0;
+  final StringBuffer buf = new StringBuffer();
+  VisitorBase printer;
+
+  void write(String s) {
+    for (int i = 0; i < depth; i++) {
+      buf.write(' ');
+    }
+    buf.write(s);
+  }
+
+  void writeln(String s) {
+    write(s);
+    buf.write('\n');
+  }
+
+  void heading(String name, [span]) {
+    write(name);
+    if (span != null) {
+      buf.write('  (${span.message('')})');
+    }
+    buf.write('\n');
+  }
+
+  String toValue(value) {
+    if (value == null) return 'null';
+    else if (value is Identifier) return value.name;
+    else return value.toString();
+  }
+
+  void writeNode(String label, TreeNode node) {
+    write('${label}: ');
+    depth += 1;
+    if (node != null) node.visit(printer);
+    else writeln('null');
+    depth -= 1;
+  }
+
+  void writeValue(String label, value) {
+    var v = toValue(value);
+    writeln('${label}: ${v}');
+  }
+
+  void writeNodeList(String label, List<TreeNode> list) {
+    writeln('${label} [');
+    if (list != null) {
+      depth += 1;
+      for (var node in list) {
+        if (node != null) {
+          node.visit(printer);
+        } else {
+          writeln('null');
+        }
+      }
+      depth -= 1;
+      writeln(']');
+    }
+  }
+
+  String toString() => buf.toString();
+}
diff --git a/csslib/lib/src/tree_printer.dart b/csslib/lib/src/tree_printer.dart
new file mode 100644
index 0000000..030a868
--- /dev/null
+++ b/csslib/lib/src/tree_printer.dart
@@ -0,0 +1,558 @@
+// Copyright (c) 2013, 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.
+
+part of csslib.visitor;
+
+// TODO(terry): Enable class for debug only; when conditional imports enabled.
+
+/** Helper function to dump the CSS AST. */
+String treeToDebugString(StyleSheet styleSheet, [bool useSpan = false]) {
+  var to = new TreeOutput();
+  new _TreePrinter(to, useSpan)..visitTree(styleSheet);
+  return to.toString();
+}
+
+/** Tree dump for debug output of the CSS AST. */
+class _TreePrinter extends Visitor {
+  final TreeOutput output;
+  final bool useSpan;
+  _TreePrinter(this.output, this.useSpan) {
+    output.printer = this;
+  }
+
+  void visitTree(StyleSheet tree) => visitStylesheet(tree);
+
+  void heading(String heading, node) {
+    if (useSpan) {
+      output.heading(heading, node.span);
+    } else {
+      output.heading(heading);
+    }
+  }
+
+  void visitStylesheet(StyleSheet node) {
+    heading('Stylesheet', node);
+    output.depth++;
+    super.visitStyleSheet(node);
+    output.depth--;
+  }
+
+  void visitTopLevelProduction(TopLevelProduction node) {
+    heading('TopLevelProduction', node);
+  }
+
+  void visitDirective(Directive node) {
+    heading('Directive', node);
+  }
+
+  void visitCssComment(CssComment node) {
+    heading('Comment', node);
+    output.depth++;
+    output.writeValue('comment value', node.comment);
+    output.depth--;
+  }
+
+  void visitCommentDefinition(CommentDefinition node) {
+    heading('CommentDefinition (CDO/CDC)', node);
+    output.depth++;
+    output.writeValue('comment value', node.comment);
+    output.depth--;
+  }
+
+  void visitMediaExpression(MediaExpression node) {
+    heading('MediaExpression', node);
+    output.writeValue('feature', node.mediaFeature);
+    if (node.andOperator) output.writeValue('AND operator', '');
+    visitExpressions(node.exprs);
+  }
+
+  void visitMediaQueries(MediaQuery query) {
+    output.heading('MediaQueries');
+    output.writeValue('unary', query.unary);
+    output.writeValue('media type', query.mediaType);
+    output.writeNodeList('media expressions', query.expressions);
+  }
+
+  void visitMediaDirective(MediaDirective node) {
+    heading('MediaDirective', node);
+    output.depth++;
+    output.writeNodeList('media queries', node.mediaQueries);
+    output.writeNodeList('rule sets', node.rulesets);
+    super.visitMediaDirective(node);
+    output.depth--;
+  }
+
+  void visitPageDirective(PageDirective node) {
+    heading('PageDirective', node);
+    output.depth++;
+    output.writeValue('pseudo page', node._pseudoPage);
+    super.visitPageDirective(node);
+    output.depth;
+  }
+
+  void visitCharsetDirective(CharsetDirective node) {
+    heading('Charset Directive', node);
+    output.writeValue('charset encoding', node.charEncoding);
+  }
+
+  void visitImportDirective(ImportDirective node) {
+    heading('ImportDirective', node);
+    output.depth++;
+    output.writeValue('import', node.import);
+    super.visitImportDirective(node);
+    output.writeNodeList('media', node.mediaQueries);
+    output.depth--;
+  }
+
+  void visitContentDirective(ContentDirective node) {
+    print("ContentDirective not implemented");
+  }
+
+  void visitKeyFrameDirective(KeyFrameDirective node) {
+    heading('KeyFrameDirective', node);
+    output.depth++;
+    output.writeValue('keyframe', node.keyFrameName);
+    output.writeValue('name', node.name);
+    output.writeNodeList('blocks', node._blocks);
+    output.depth--;
+  }
+
+  void visitKeyFrameBlock(KeyFrameBlock node) {
+    heading('KeyFrameBlock', node);
+    output.depth++;
+    super.visitKeyFrameBlock(node);
+    output.depth--;
+  }
+
+  void visitFontFaceDirective(FontFaceDirective node) {
+    // TODO(terry): To Be Implemented
+  }
+
+  void visitStyletDirective(StyletDirective node) {
+    heading('StyletDirective', node);
+    output.writeValue('dartClassName', node.dartClassName);
+    output.depth++;
+    output.writeNodeList('rulesets', node.rulesets);
+    output.depth--;
+  }
+
+  void visitNamespaceDirective(NamespaceDirective node) {
+    heading('NamespaceDirective', node);
+    output.depth++;
+    output.writeValue('prefix', node._prefix);
+    output.writeValue('uri', node._uri);
+    output.depth--;
+  }
+
+  void visitVarDefinitionDirective(VarDefinitionDirective node) {
+    heading('Less variable definition', node);
+    output.depth++;
+    visitVarDefinition(node.def);
+    output.depth--;
+  }
+
+  void visitMixinRulesetDirective(MixinRulesetDirective node) {
+    heading('Mixin top-level ${node.name}', node);
+    output.writeNodeList('parameters', node.definedArgs);
+    output.depth++;
+    _visitNodeList(node.rulesets);
+    output.depth--;
+  }
+
+  void visitMixinDeclarationDirective(MixinDeclarationDirective node) {
+    heading('Mixin declaration ${node.name}', node);
+    output.writeNodeList('parameters', node.definedArgs);
+    output.depth++;
+    visitDeclarationGroup(node.declarations);
+    output.depth--;
+  }
+
+  /**
+   * Added optional newLine for handling @include at top-level vs/ inside of
+   * a declaration group.
+   */
+  void visitIncludeDirective(IncludeDirective node) {
+    heading('IncludeDirective ${node.name}', node);
+    var flattened = node.args.expand((e) => e).toList();
+    output.writeNodeList('parameters', flattened);
+  }
+
+  void visitIncludeMixinAtDeclaration(IncludeMixinAtDeclaration node) {
+    heading('IncludeMixinAtDeclaration ${node.include.name}', node);
+    output.depth++;
+    visitIncludeDirective(node.include);
+    output.depth--;
+  }
+
+  void visitExtendDeclaration(ExtendDeclaration node) {
+    heading('ExtendDeclaration', node);
+    output.depth++;
+    _visitNodeList(node.selectors);
+    output.depth--;
+  }
+
+  void visitRuleSet(RuleSet node) {
+    heading('Ruleset', node);
+    output.depth++;
+    super.visitRuleSet(node);
+    output.depth--;
+  }
+
+  void visitDeclarationGroup(DeclarationGroup node) {
+    heading('DeclarationGroup', node);
+    output.depth++;
+    output.writeNodeList('declarations', node.declarations);
+    output.depth--;
+  }
+
+  void visitMarginGroup(MarginGroup node) {
+    heading('MarginGroup', node);
+    output.depth++;
+    output.writeValue('@directive', node.margin_sym);
+    output.writeNodeList('declarations', node.declarations);
+    output.depth--;
+  }
+
+  void visitDeclaration(Declaration node) {
+    heading('Declaration', node);
+    output.depth++;
+    if (node.isIE7) output.write('IE7 property');
+    output.write('property');
+    super.visitDeclaration(node);
+    output.writeNode('expression', node._expression);
+    if (node.important) {
+      output.writeValue('!important', 'true');
+    }
+    output.depth--;
+  }
+
+  void visitVarDefinition(VarDefinition node) {
+    heading('Var', node);
+    output.depth++;
+    output.write('defintion');
+    super.visitVarDefinition(node);
+    output.writeNode('expression', node._expression);
+    output.depth--;
+  }
+
+  void visitSelectorGroup(SelectorGroup node) {
+    heading('Selector Group', node);
+    output.depth++;
+    output.writeNodeList('selectors', node.selectors);
+    output.depth--;
+  }
+
+  void visitSelector(Selector node) {
+    heading('Selector', node);
+    output.depth++;
+    output.writeNodeList(
+        'simpleSelectorsSequences', node.simpleSelectorSequences);
+    output.depth--;
+  }
+
+  void visitSimpleSelectorSequence(SimpleSelectorSequence node) {
+    heading('SimpleSelectorSequence', node);
+    output.depth++;
+    if (node.isCombinatorNone) {
+      output.writeValue('combinator', "NONE");
+    } else if (node.isCombinatorDescendant) {
+      output.writeValue('combinator', "descendant");
+    } else if (node.isCombinatorPlus) {
+      output.writeValue('combinator', "+");
+    } else if (node.isCombinatorGreater) {
+      output.writeValue('combinator', ">");
+    } else if (node.isCombinatorTilde) {
+      output.writeValue('combinator', "~");
+    } else {
+      output.writeValue('combinator', "ERROR UNKNOWN");
+    }
+
+    super.visitSimpleSelectorSequence(node);
+
+    output.depth--;
+  }
+
+  void visitNamespaceSelector(NamespaceSelector node) {
+    heading('Namespace Selector', node);
+    output.depth++;
+
+    super.visitNamespaceSelector(node);
+
+    visitSimpleSelector(node.nameAsSimpleSelector);
+    output.depth--;
+  }
+
+  void visitElementSelector(ElementSelector node) {
+    heading('Element Selector', node);
+    output.depth++;
+    super.visitElementSelector(node);
+    output.depth--;
+  }
+
+  void visitAttributeSelector(AttributeSelector node) {
+    heading('AttributeSelector', node);
+    output.depth++;
+    super.visitAttributeSelector(node);
+    String tokenStr = node.matchOperatorAsTokenString();
+    output.writeValue('operator', '${node.matchOperator()} (${tokenStr})');
+    output.writeValue('value', node.valueToString());
+    output.depth--;
+  }
+
+  void visitIdSelector(IdSelector node) {
+    heading('Id Selector', node);
+    output.depth++;
+    super.visitIdSelector(node);
+    output.depth--;
+  }
+
+  void visitClassSelector(ClassSelector node) {
+    heading('Class Selector', node);
+    output.depth++;
+    super.visitClassSelector(node);
+    output.depth--;
+  }
+
+  void visitPseudoClassSelector(PseudoClassSelector node) {
+    heading('Pseudo Class Selector', node);
+    output.depth++;
+    super.visitPseudoClassSelector(node);
+    output.depth--;
+  }
+
+  void visitPseudoElementSelector(PseudoElementSelector node) {
+    heading('Pseudo Element Selector', node);
+    output.depth++;
+    super.visitPseudoElementSelector(node);
+    output.depth--;
+  }
+
+  void visitPseudoClassFunctionSelector(PseudoClassFunctionSelector node) {
+    heading('Pseudo Class Function Selector', node);
+    output.depth++;
+    visitSelectorExpression(node.expression);
+    super.visitPseudoClassFunctionSelector(node);
+    output.depth--;
+  }
+
+  void visitPseudoElementFunctionSelector(PseudoElementFunctionSelector node) {
+    heading('Pseudo Element Function Selector', node);
+    output.depth++;
+    visitSelectorExpression(node.expression);
+    super.visitPseudoElementFunctionSelector(node);
+    output.depth--;
+  }
+
+  void visitSelectorExpression(SelectorExpression node) {
+    heading('Selector Expression', node);
+    output.depth++;
+    output.writeNodeList('expressions', node.expressions);
+    output.depth--;
+  }
+
+  void visitNegationSelector(NegationSelector node) {
+    super.visitNegationSelector(node);
+    output.depth++;
+    heading('Negation Selector', node);
+    output.writeNode('Negation arg', node.negationArg);
+    output.depth--;
+  }
+
+  void visitUnicodeRangeTerm(UnicodeRangeTerm node) {
+    heading('UnicodeRangeTerm', node);
+    output.depth++;
+    output.writeValue('1st value', node.first);
+    output.writeValue('2nd value', node.second);
+    output.depth--;
+  }
+
+  void visitLiteralTerm(LiteralTerm node) {
+    heading('LiteralTerm', node);
+    output.depth++;
+    output.writeValue('value', node.text);
+    output.depth--;
+  }
+
+  void visitHexColorTerm(HexColorTerm node) {
+    heading('HexColorTerm', node);
+    output.depth++;
+    output.writeValue('hex value', node.text);
+    output.writeValue('decimal value', node.value);
+    output.depth--;
+  }
+
+  void visitNumberTerm(NumberTerm node) {
+    heading('NumberTerm', node);
+    output.depth++;
+    output.writeValue('value', node.text);
+    output.depth--;
+  }
+
+  void visitUnitTerm(UnitTerm node) {
+    output.depth++;
+    output.writeValue('value', node.text);
+    output.writeValue('unit', node.unitToString());
+    output.depth--;
+  }
+
+  void visitLengthTerm(LengthTerm node) {
+    heading('LengthTerm', node);
+    super.visitLengthTerm(node);
+  }
+
+  void visitPercentageTerm(PercentageTerm node) {
+    heading('PercentageTerm', node);
+    output.depth++;
+    super.visitPercentageTerm(node);
+    output.depth--;
+  }
+
+  void visitEmTerm(EmTerm node) {
+    heading('EmTerm', node);
+    output.depth++;
+    super.visitEmTerm(node);
+    output.depth--;
+  }
+
+  void visitExTerm(ExTerm node) {
+    heading('ExTerm', node);
+    output.depth++;
+    super.visitExTerm(node);
+    output.depth--;
+  }
+
+  void visitAngleTerm(AngleTerm node) {
+    heading('AngleTerm', node);
+    super.visitAngleTerm(node);
+  }
+
+  void visitTimeTerm(TimeTerm node) {
+    heading('TimeTerm', node);
+    super.visitTimeTerm(node);
+  }
+
+  void visitFreqTerm(FreqTerm node) {
+    heading('FreqTerm', node);
+    super.visitFreqTerm(node);
+  }
+
+  void visitFractionTerm(FractionTerm node) {
+    heading('FractionTerm', node);
+    output.depth++;
+    super.visitFractionTerm(node);
+    output.depth--;
+  }
+
+  void visitUriTerm(UriTerm node) {
+    heading('UriTerm', node);
+    output.depth++;
+    super.visitUriTerm(node);
+    output.depth--;
+  }
+
+  void visitFunctionTerm(FunctionTerm node) {
+    heading('FunctionTerm', node);
+    output.depth++;
+    super.visitFunctionTerm(node);
+    output.depth--;
+  }
+
+  void visitGroupTerm(GroupTerm node) {
+    heading('GroupTerm', node);
+    output.depth++;
+    output.writeNodeList('grouped terms', node._terms);
+    output.depth--;
+  }
+
+  void visitItemTerm(ItemTerm node) {
+    heading('ItemTerm', node);
+    super.visitItemTerm(node);
+  }
+
+  void visitIE8Term(IE8Term node) {
+    heading('IE8Term', node);
+    visitLiteralTerm(node);
+  }
+
+  void visitOperatorSlash(OperatorSlash node) {
+    heading('OperatorSlash', node);
+  }
+
+  void visitOperatorComma(OperatorComma node) {
+    heading('OperatorComma', node);
+  }
+
+  void visitOperatorPlus(OperatorPlus node) {
+    heading('OperatorPlus', node);
+  }
+
+  void visitOperatorMinus(OperatorMinus node) {
+    heading('OperatorMinus', node);
+  }
+
+  void visitVarUsage(VarUsage node) {
+    heading('Var', node);
+    output.depth++;
+    output.write('usage ${node.name}');
+    output.writeNodeList('default values', node.defaultValues);
+    output.depth--;
+  }
+
+  void visitExpressions(Expressions node) {
+    heading('Expressions', node);
+    output.depth++;
+    output.writeNodeList('expressions', node.expressions);
+    output.depth--;
+  }
+
+  void visitBinaryExpression(BinaryExpression node) {
+    heading('BinaryExpression', node);
+    // TODO(terry): TBD
+  }
+
+  void visitUnaryExpression(UnaryExpression node) {
+    heading('UnaryExpression', node);
+    // TODO(terry): TBD
+  }
+
+  void visitIdentifier(Identifier node) {
+    heading('Identifier(${output.toValue(node.name)})', node);
+  }
+
+  void visitWildcard(Wildcard node) {
+    heading('Wildcard(*)', node);
+  }
+
+  void visitDartStyleExpression(DartStyleExpression node) {
+    heading('DartStyleExpression', node);
+  }
+
+  void visitFontExpression(FontExpression node) {
+    heading('Dart Style FontExpression', node);
+  }
+
+  void visitBoxExpression(BoxExpression node) {
+    heading('Dart Style BoxExpression', node);
+  }
+
+  void visitMarginExpression(MarginExpression node) {
+    heading('Dart Style MarginExpression', node);
+  }
+
+  void visitBorderExpression(BorderExpression node) {
+    heading('Dart Style BorderExpression', node);
+  }
+
+  void visitHeightExpression(HeightExpression node) {
+    heading('Dart Style HeightExpression', node);
+  }
+
+  void visitPaddingExpression(PaddingExpression node) {
+    heading('Dart Style PaddingExpression', node);
+  }
+
+  void visitWidthExpression(WidthExpression node) {
+    heading('Dart Style WidthExpression', node);
+  }
+}
diff --git a/csslib/lib/src/validate.dart b/csslib/lib/src/validate.dart
new file mode 100644
index 0000000..d45cd95
--- /dev/null
+++ b/csslib/lib/src/validate.dart
@@ -0,0 +1,125 @@
+// Copyright (c) 2012, 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 csslib.src.validate;
+
+import 'package:csslib/visitor.dart';
+import 'package:source_span/source_span.dart';
+
+/** Can be thrown on any Css runtime problem includes source location. */
+class CssSelectorException extends SourceSpanException {
+  CssSelectorException(String message, [SourceSpan span])
+      : super(message, span);
+}
+
+List<String> classes = [];
+List<String> ids = [];
+
+class Validate {
+  static int _classNameCheck(var selector, int matches) {
+    if (selector.isCombinatorDescendant() ||
+        (selector.isCombinatorNone() && matches == 0)) {
+      if (matches < 0) {
+        String tooMany = selector.simpleSelector.toString();
+        throw new CssSelectorException(
+            'Can not mix Id selector with class selector(s). Id '
+            'selector must be singleton too many starting at $tooMany');
+      }
+
+      return matches + 1;
+    } else {
+      String error = selector.toString();
+      throw new CssSelectorException(
+          'Selectors can not have combinators (>, +, or ~) before $error');
+    }
+  }
+
+  static int _elementIdCheck(var selector, int matches) {
+    if (selector.isCombinatorNone() && matches == 0) {
+      // Perfect just one element id returns matches of -1.
+      return -1;
+    } else if (selector.isCombinatorDescendant()) {
+      String tooMany = selector.simpleSelector.toString();
+      throw new CssSelectorException(
+          'Use of Id selector must be singleton starting at $tooMany');
+    } else {
+      String error = selector.simpleSelector.toString();
+      throw new CssSelectorException(
+          'Selectors can not have combinators (>, +, or ~) before $error');
+    }
+  }
+
+  // Validate the @{css expression} only .class and #elementId are valid inside
+  // of @{...}.
+  static template(List<Selector> selectors) {
+    var errorSelector; // signal which selector didn't match.
+    bool found = false; // signal if a selector is matched.
+    int matches = 0; // < 0 IdSelectors, > 0 ClassSelector
+
+    // At most one selector group (any number of simple selector sequences).
+    assert(selectors.length <= 1);
+
+    for (final sels in selectors) {
+      for (final selector in sels.simpleSelectorSequences) {
+        found = false;
+        var simpleSelector = selector.simpleSelector;
+        if (simpleSelector is ClassSelector) {
+          // Any class name starting with an underscore is a private class name
+          // that doesn't have to match the world of known classes.
+          if (!simpleSelector.name.startsWith('_')) {
+            // TODO(terry): For now iterate through all classes look for faster
+            //              mechanism hash map, etc.
+            for (final className in classes) {
+              if (selector.simpleSelector.name == className) {
+                matches = _classNameCheck(selector, matches);
+                found = true; // .class found.
+                break;
+              }
+              for (final className2 in classes) {
+                print(className2);
+              }
+            }
+          } else {
+            // Don't check any class name that is prefixed with an underscore.
+            // However, signal as found and bump up matches; it's a valid class
+            // name.
+            matches = _classNameCheck(selector, matches);
+            found = true; // ._class are always okay.
+          }
+        } else if (simpleSelector is IdSelector) {
+          // Any element id starting with an underscore is a private element id
+          // that doesn't have to match the world of known elemtn ids.
+          if (!simpleSelector.name.startsWith('_')) {
+            for (final id in ids) {
+              if (simpleSelector.name == id) {
+                matches = _elementIdCheck(selector, matches);
+                found = true; // #id found.
+                break;
+              }
+            }
+          } else {
+            // Don't check any element ID that is prefixed with an underscore.
+            // Signal as found and bump up matches; it's a valid element ID.
+            matches = _elementIdCheck(selector, matches);
+            found = true; // #_id are always okay
+          }
+        } else {
+          String badSelector = simpleSelector.toString();
+          throw new CssSelectorException(
+              'Invalid template selector $badSelector');
+        }
+
+        if (!found) {
+          String unknownName = simpleSelector.toString();
+          throw new CssSelectorException('Unknown selector name $unknownName');
+        }
+      }
+    }
+
+    // Every selector must match.
+    Selector selector = selectors[0];
+    assert((matches >= 0 ? matches : -matches) ==
+        selector.simpleSelectorSequences.length);
+  }
+}
diff --git a/csslib/lib/visitor.dart b/csslib/lib/visitor.dart
new file mode 100644
index 0000000..fa0f8d2
--- /dev/null
+++ b/csslib/lib/visitor.dart
@@ -0,0 +1,455 @@
+// Copyright (c) 2012, 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 csslib.visitor;
+
+import 'package:source_span/source_span.dart';
+import 'parser.dart';
+
+part 'src/css_printer.dart';
+part 'src/tree.dart';
+part 'src/tree_base.dart';
+part 'src/tree_printer.dart';
+
+abstract class VisitorBase {
+  void visitCssComment(CssComment node);
+  void visitCommentDefinition(CommentDefinition node);
+  void visitStyleSheet(StyleSheet node);
+  void visitNoOp(NoOp node);
+  void visitTopLevelProduction(TopLevelProduction node);
+  void visitDirective(Directive node);
+  void visitMediaExpression(MediaExpression node);
+  void visitMediaQuery(MediaQuery node);
+  void visitMediaDirective(MediaDirective node);
+  void visitHostDirective(HostDirective node);
+  void visitPageDirective(PageDirective node);
+  void visitCharsetDirective(CharsetDirective node);
+  void visitImportDirective(ImportDirective node);
+  void visitKeyFrameDirective(KeyFrameDirective node);
+  void visitKeyFrameBlock(KeyFrameBlock node);
+  void visitFontFaceDirective(FontFaceDirective node);
+  void visitStyletDirective(StyletDirective node);
+  void visitNamespaceDirective(NamespaceDirective node);
+  void visitVarDefinitionDirective(VarDefinitionDirective node);
+  void visitMixinDefinition(MixinDefinition node);
+  void visitMixinRulesetDirective(MixinRulesetDirective node);
+  void visitMixinDeclarationDirective(MixinDeclarationDirective node);
+  void visitIncludeDirective(IncludeDirective node);
+  void visitContentDirective(ContentDirective node);
+
+  void visitRuleSet(RuleSet node);
+  void visitDeclarationGroup(DeclarationGroup node);
+  void visitMarginGroup(MarginGroup node);
+  void visitDeclaration(Declaration node);
+  void visitVarDefinition(VarDefinition node);
+  void visitIncludeMixinAtDeclaration(IncludeMixinAtDeclaration node);
+  void visitExtendDeclaration(ExtendDeclaration node);
+  void visitSelectorGroup(SelectorGroup node);
+  void visitSelector(Selector node);
+  void visitSimpleSelectorSequence(SimpleSelectorSequence node);
+  void visitSimpleSelector(SimpleSelector node);
+  void visitElementSelector(ElementSelector node);
+  void visitNamespaceSelector(NamespaceSelector node);
+  void visitAttributeSelector(AttributeSelector node);
+  void visitIdSelector(IdSelector node);
+  void visitClassSelector(ClassSelector node);
+  void visitPseudoClassSelector(PseudoClassSelector node);
+  void visitPseudoElementSelector(PseudoElementSelector node);
+  void visitPseudoClassFunctionSelector(PseudoClassFunctionSelector node);
+  void visitPseudoElementFunctionSelector(PseudoElementFunctionSelector node);
+  void visitNegationSelector(NegationSelector node);
+  void visitSelectorExpression(SelectorExpression node);
+
+  void visitUnicodeRangeTerm(UnicodeRangeTerm node);
+  void visitLiteralTerm(LiteralTerm node);
+  void visitHexColorTerm(HexColorTerm node);
+  void visitNumberTerm(NumberTerm node);
+  void visitUnitTerm(UnitTerm node);
+  void visitLengthTerm(LengthTerm node);
+  void visitPercentageTerm(PercentageTerm node);
+  void visitEmTerm(EmTerm node);
+  void visitExTerm(ExTerm node);
+  void visitAngleTerm(AngleTerm node);
+  void visitTimeTerm(TimeTerm node);
+  void visitFreqTerm(FreqTerm node);
+  void visitFractionTerm(FractionTerm node);
+  void visitUriTerm(UriTerm node);
+  void visitResolutionTerm(ResolutionTerm node);
+  void visitChTerm(ChTerm node);
+  void visitRemTerm(RemTerm node);
+  void visitViewportTerm(ViewportTerm node);
+  void visitFunctionTerm(FunctionTerm node);
+  void visitGroupTerm(GroupTerm node);
+  void visitItemTerm(ItemTerm node);
+  void visitIE8Term(IE8Term node);
+  void visitOperatorSlash(OperatorSlash node);
+  void visitOperatorComma(OperatorComma node);
+  void visitOperatorPlus(OperatorPlus node);
+  void visitOperatorMinus(OperatorMinus node);
+  void visitVarUsage(VarUsage node);
+
+  void visitExpressions(Expressions node);
+  void visitBinaryExpression(BinaryExpression node);
+  void visitUnaryExpression(UnaryExpression node);
+
+  void visitIdentifier(Identifier node);
+  void visitWildcard(Wildcard node);
+  void visitThisOperator(ThisOperator node);
+  void visitNegation(Negation node);
+
+  void visitDartStyleExpression(DartStyleExpression node);
+  void visitFontExpression(FontExpression node);
+  void visitBoxExpression(BoxExpression node);
+  void visitMarginExpression(MarginExpression node);
+  void visitBorderExpression(BorderExpression node);
+  void visitHeightExpression(HeightExpression node);
+  void visitPaddingExpression(PaddingExpression node);
+  void visitWidthExpression(WidthExpression node);
+}
+
+/** Base vistor class for the style sheet AST. */
+class Visitor implements VisitorBase {
+  /** Helper function to walk a list of nodes. */
+  void _visitNodeList(List<TreeNode> list) {
+    // Don't use iterable otherwise the list can't grow while using Visitor.
+    // It certainly can't have items deleted before the index being iterated
+    // but items could be added after the index.
+    for (var index = 0; index < list.length; index++) {
+      list[index].visit(this);
+    }
+  }
+
+  void visitTree(StyleSheet tree) => visitStyleSheet(tree);
+
+  void visitStyleSheet(StyleSheet ss) {
+    _visitNodeList(ss.topLevels);
+  }
+
+  void visitNoOp(NoOp node) {}
+
+  void visitTopLevelProduction(TopLevelProduction node) {}
+
+  void visitDirective(Directive node) {}
+
+  void visitCssComment(CssComment node) {}
+
+  void visitCommentDefinition(CommentDefinition node) {}
+
+  void visitMediaExpression(MediaExpression node) {
+    visitExpressions(node.exprs);
+  }
+
+  void visitMediaQuery(MediaQuery node) {
+    for (var mediaExpr in node.expressions) {
+      visitMediaExpression(mediaExpr);
+    }
+  }
+
+  void visitMediaDirective(MediaDirective node) {
+    for (var mediaQuery in node.mediaQueries) {
+      visitMediaQuery(mediaQuery);
+    }
+    for (var ruleset in node.rulesets) {
+      visitRuleSet(ruleset);
+    }
+  }
+
+  void visitHostDirective(HostDirective node) {
+    for (var ruleset in node.rulesets) {
+      visitRuleSet(ruleset);
+    }
+  }
+
+  void visitPageDirective(PageDirective node) {
+    for (var declGroup in node._declsMargin) {
+      if (declGroup is MarginGroup) {
+        visitMarginGroup(declGroup);
+      } else {
+        visitDeclarationGroup(declGroup);
+      }
+    }
+  }
+
+  void visitCharsetDirective(CharsetDirective node) {}
+
+  void visitImportDirective(ImportDirective node) {
+    for (var mediaQuery in node.mediaQueries) {
+      visitMediaQuery(mediaQuery);
+    }
+  }
+
+  void visitKeyFrameDirective(KeyFrameDirective node) {
+    visitIdentifier(node.name);
+    _visitNodeList(node._blocks);
+  }
+
+  void visitKeyFrameBlock(KeyFrameBlock node) {
+    visitExpressions(node._blockSelectors);
+    visitDeclarationGroup(node._declarations);
+  }
+
+  void visitFontFaceDirective(FontFaceDirective node) {
+    visitDeclarationGroup(node._declarations);
+  }
+
+  void visitStyletDirective(StyletDirective node) {
+    _visitNodeList(node.rulesets);
+  }
+
+  void visitNamespaceDirective(NamespaceDirective node) {}
+
+  void visitVarDefinitionDirective(VarDefinitionDirective node) {
+    visitVarDefinition(node.def);
+  }
+
+  void visitMixinRulesetDirective(MixinRulesetDirective node) {
+    _visitNodeList(node.rulesets);
+  }
+
+  void visitMixinDefinition(MixinDefinition node) {}
+
+  void visitMixinDeclarationDirective(MixinDeclarationDirective node) {
+    visitDeclarationGroup(node.declarations);
+  }
+
+  void visitIncludeDirective(IncludeDirective node) {
+    for (var index = 0; index < node.args.length; index++) {
+      var param = node.args[index];
+      _visitNodeList(param);
+    }
+  }
+
+  void visitContentDirective(ContentDirective node) {
+    // TODO(terry): TBD
+  }
+
+  void visitRuleSet(RuleSet node) {
+    visitSelectorGroup(node._selectorGroup);
+    visitDeclarationGroup(node._declarationGroup);
+  }
+
+  void visitDeclarationGroup(DeclarationGroup node) {
+    _visitNodeList(node.declarations);
+  }
+
+  void visitMarginGroup(MarginGroup node) => visitDeclarationGroup(node);
+
+  void visitDeclaration(Declaration node) {
+    visitIdentifier(node._property);
+    if (node._expression != null) node._expression.visit(this);
+  }
+
+  void visitVarDefinition(VarDefinition node) {
+    visitIdentifier(node._property);
+    if (node._expression != null) node._expression.visit(this);
+  }
+
+  void visitIncludeMixinAtDeclaration(IncludeMixinAtDeclaration node) {
+    visitIncludeDirective(node.include);
+  }
+
+  void visitExtendDeclaration(ExtendDeclaration node) {
+    _visitNodeList(node.selectors);
+  }
+
+  void visitSelectorGroup(SelectorGroup node) {
+    _visitNodeList(node.selectors);
+  }
+
+  void visitSelector(Selector node) {
+    _visitNodeList(node.simpleSelectorSequences);
+  }
+
+  void visitSimpleSelectorSequence(SimpleSelectorSequence node) {
+    node.simpleSelector.visit(this);
+  }
+
+  void visitSimpleSelector(SimpleSelector node) => node._name.visit(this);
+
+  void visitNamespaceSelector(NamespaceSelector node) {
+    if (node._namespace != null) node._namespace.visit(this);
+    if (node.nameAsSimpleSelector != null) {
+      node.nameAsSimpleSelector.visit(this);
+    }
+  }
+
+  void visitElementSelector(ElementSelector node) => visitSimpleSelector(node);
+
+  void visitAttributeSelector(AttributeSelector node) {
+    visitSimpleSelector(node);
+  }
+
+  void visitIdSelector(IdSelector node) => visitSimpleSelector(node);
+
+  void visitClassSelector(ClassSelector node) => visitSimpleSelector(node);
+
+  void visitPseudoClassSelector(PseudoClassSelector node) =>
+      visitSimpleSelector(node);
+
+  void visitPseudoElementSelector(PseudoElementSelector node) =>
+      visitSimpleSelector(node);
+
+  void visitPseudoClassFunctionSelector(PseudoClassFunctionSelector node) =>
+      visitSimpleSelector(node);
+
+  void visitPseudoElementFunctionSelector(PseudoElementFunctionSelector node) =>
+      visitSimpleSelector(node);
+
+  void visitNegationSelector(NegationSelector node) =>
+      visitSimpleSelector(node);
+
+  void visitSelectorExpression(SelectorExpression node) {
+    _visitNodeList(node.expressions);
+  }
+
+  void visitUnicodeRangeTerm(UnicodeRangeTerm node) {}
+
+  void visitLiteralTerm(LiteralTerm node) {}
+
+  void visitHexColorTerm(HexColorTerm node) {}
+
+  void visitNumberTerm(NumberTerm node) {}
+
+  void visitUnitTerm(UnitTerm node) {}
+
+  void visitLengthTerm(LengthTerm node) {
+    visitUnitTerm(node);
+  }
+
+  void visitPercentageTerm(PercentageTerm node) {
+    visitLiteralTerm(node);
+  }
+
+  void visitEmTerm(EmTerm node) {
+    visitLiteralTerm(node);
+  }
+
+  void visitExTerm(ExTerm node) {
+    visitLiteralTerm(node);
+  }
+
+  void visitAngleTerm(AngleTerm node) {
+    visitUnitTerm(node);
+  }
+
+  void visitTimeTerm(TimeTerm node) {
+    visitUnitTerm(node);
+  }
+
+  void visitFreqTerm(FreqTerm node) {
+    visitUnitTerm(node);
+  }
+
+  void visitFractionTerm(FractionTerm node) {
+    visitLiteralTerm(node);
+  }
+
+  void visitUriTerm(UriTerm node) {
+    visitLiteralTerm(node);
+  }
+
+  void visitResolutionTerm(ResolutionTerm node) {
+    visitUnitTerm(node);
+  }
+
+  void visitChTerm(ChTerm node) {
+    visitUnitTerm(node);
+  }
+
+  void visitRemTerm(RemTerm node) {
+    visitUnitTerm(node);
+  }
+
+  void visitViewportTerm(ViewportTerm node) {
+    visitUnitTerm(node);
+  }
+
+  void visitFunctionTerm(FunctionTerm node) {
+    visitLiteralTerm(node);
+    visitExpressions(node._params);
+  }
+
+  void visitGroupTerm(GroupTerm node) {
+    for (var term in node._terms) {
+      term.visit(this);
+    }
+  }
+
+  void visitItemTerm(ItemTerm node) {
+    visitNumberTerm(node);
+  }
+
+  void visitIE8Term(IE8Term node) {}
+
+  void visitOperatorSlash(OperatorSlash node) {}
+
+  void visitOperatorComma(OperatorComma node) {}
+
+  void visitOperatorPlus(OperatorPlus node) {}
+
+  void visitOperatorMinus(OperatorMinus node) {}
+
+  void visitVarUsage(VarUsage node) {
+    _visitNodeList(node.defaultValues);
+  }
+
+  void visitExpressions(Expressions node) {
+    _visitNodeList(node.expressions);
+  }
+
+  void visitBinaryExpression(BinaryExpression node) {
+    // TODO(terry): TBD
+    throw UnimplementedError;
+  }
+
+  void visitUnaryExpression(UnaryExpression node) {
+    // TODO(terry): TBD
+    throw UnimplementedError;
+  }
+
+  void visitIdentifier(Identifier node) {}
+
+  void visitWildcard(Wildcard node) {}
+
+  void visitThisOperator(ThisOperator node) {}
+
+  void visitNegation(Negation node) {}
+
+  void visitDartStyleExpression(DartStyleExpression node) {}
+
+  void visitFontExpression(FontExpression node) {
+    // TODO(terry): TBD
+    throw UnimplementedError;
+  }
+
+  void visitBoxExpression(BoxExpression node) {
+    // TODO(terry): TBD
+    throw UnimplementedError;
+  }
+
+  void visitMarginExpression(MarginExpression node) {
+    // TODO(terry): TBD
+    throw UnimplementedError;
+  }
+
+  void visitBorderExpression(BorderExpression node) {
+    // TODO(terry): TBD
+    throw UnimplementedError;
+  }
+
+  void visitHeightExpression(HeightExpression node) {
+    // TODO(terry): TB
+    throw UnimplementedError;
+  }
+
+  void visitPaddingExpression(PaddingExpression node) {
+    // TODO(terry): TBD
+    throw UnimplementedError;
+  }
+
+  void visitWidthExpression(WidthExpression node) {
+    // TODO(terry): TBD
+    throw UnimplementedError;
+  }
+}
diff --git a/csslib/pubspec.yaml b/csslib/pubspec.yaml
new file mode 100644
index 0000000..5ef264a
--- /dev/null
+++ b/csslib/pubspec.yaml
@@ -0,0 +1,15 @@
+name: csslib
+version: 0.12.0
+author: Polymer.dart Team <web-ui-dev@dartlang.org>
+description: A library for parsing CSS.
+homepage: https://github.com/dart-lang/csslib
+environment:
+  sdk: '>=1.1.0 <2.0.0'
+dependencies:
+  args: '>=0.9.0 <0.14.0'
+  logging: '>=0.9.0 <0.10.0'
+  path: '>=0.9.0 <2.0.0'
+  source_span: '>=1.0.0 <2.0.0'
+dev_dependencies:
+  browser: '>=0.9.0 <0.11.0'
+  unittest: '>=0.9.0 <0.12.0'
diff --git a/dart_style/lib/dart_style.dart b/dart_style/lib/dart_style.dart
new file mode 100644
index 0000000..6ecc7c3
--- /dev/null
+++ b/dart_style/lib/dart_style.dart
@@ -0,0 +1,9 @@
+// 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 dart_style;
+
+export 'src/dart_formatter.dart';
+export 'src/formatter_exception.dart';
+export 'src/source_code.dart';
diff --git a/dart_style/lib/src/chunk.dart b/dart_style/lib/src/chunk.dart
new file mode 100644
index 0000000..307cd95
--- /dev/null
+++ b/dart_style/lib/src/chunk.dart
@@ -0,0 +1,372 @@
+// 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 dart_style.src.chunk;
+
+import 'debug.dart';
+
+/// Tracks where a selection start or end point may appear in some piece of
+/// text.
+abstract class Selection {
+  /// The chunk of text.
+  String get text;
+
+  /// The offset from the beginning of [text] where the selection starts, or
+  /// `null` if the selection does not start within this chunk.
+  int get selectionStart => _selectionStart;
+  int _selectionStart;
+
+  /// The offset from the beginning of [text] where the selection ends, or
+  /// `null` if the selection does not start within this chunk.
+  int get selectionEnd => _selectionEnd;
+  int _selectionEnd;
+
+  /// Sets [selectionStart] to be [start] characters into [text].
+  void startSelection(int start) {
+    _selectionStart = start;
+  }
+
+  /// Sets [selectionStart] to be [fromEnd] characters from the end of [text].
+  void startSelectionFromEnd(int fromEnd) {
+    _selectionStart = text.length - fromEnd;
+  }
+
+  /// Sets [selectionEnd] to be [end] characters into [text].
+  void endSelection(int end) {
+    _selectionEnd = end;
+  }
+
+  /// Sets [selectionEnd] to be [fromEnd] characters from the end of [text].
+  void endSelectionFromEnd(int fromEnd) {
+    _selectionEnd = text.length - fromEnd;
+  }
+}
+
+/// A chunk of non-breaking output text terminated by a hard or soft newline.
+///
+/// Chunks are created by [LineWriter] and fed into [LineSplitter]. Each
+/// contains some text, along with the data needed to tell how the next line
+/// should be formatted and how desireable it is to split after the chunk.
+///
+/// Line splitting after chunks comes in a few different forms.
+///
+/// *   A "hard" split is a mandatory newline. The formatted output will contain
+///     at least one newline after the chunk's text.
+/// *   A "soft" split is a discretionary newline. If a line doesn't fit within
+///     the page width, one or more soft splits may be turned into newlines to
+///     wrap the line to fit within the bounds. If a soft split is not turned
+///     into a newline, it may instead appear as a space or zero-length string
+///     in the output, depending on [spaceWhenUnsplit].
+/// *   A "double" split expands to two newlines. In other words, it leaves a
+///     blank line in the output. Hard or soft splits may be doubled. This is
+///     determined by [isDouble].
+///
+/// A split controls the leading spacing of the subsequent line, both
+/// block-based [indent] and expression-wrapping-based [nesting].
+class Chunk extends Selection {
+  /// The literal text output for the chunk.
+  String get text => _text;
+  String _text;
+
+  /// The indentation level of the line following this chunk.
+  ///
+  /// Note that this is not a relative indentation *offset*. It's the full
+  /// indentation. When a chunk is newly created from text, this is `null` to
+  /// indicate that the chunk has no splitting information yet.
+  int get indent => _indent;
+  int _indent = null;
+
+  /// The number of levels of expression nesting following this chunk.
+  ///
+  /// This is used to determine how much to increase the indentation when a
+  /// line starts after this chunk. A single statement may be indented multiple
+  /// times if the splits occur in more deeply nested expressions, for example:
+  ///
+  ///     // 40 columns                           |
+  ///     someFunctionName(argument, argument,
+  ///         argument, anotherFunction(argument,
+  ///             argument));
+  int nesting = -1;
+
+  /// Whether or not the chunk occurs inside an expression.
+  ///
+  /// Splits within expressions must take into account how deeply nested they
+  /// are to determine the indentation of subsequent lines. "Statement level"
+  /// splits that occur between statements or in the top-level of a unit only
+  /// take the main indent level into account.
+  bool get isInExpression => nesting != -1;
+
+  /// Whether it's valid to add more text to this chunk or not.
+  ///
+  /// Chunks are built up by adding text and then "capped off" by having their
+  /// split information set by calling [handleSplit]. Once the latter has been
+  /// called, no more text should be added to the chunk since it would appear
+  /// *before* the split.
+  bool get canAddText => _indent == null;
+
+  /// The [SplitParam] that determines if this chunk is being used as a split
+  /// or not.
+  ///
+  /// Multiple splits may share a [SplitParam] because they are part of the
+  /// same [Multisplit], in which case they are split or unsplit in unison.
+  ///
+  /// This is `null` for hard splits.
+  SplitParam get param => _param;
+  SplitParam _param;
+
+  /// Whether this chunk is always followed by a newline or whether the line
+  /// splitter may choose to keep the next chunk on the same line.
+  bool get isHardSplit => _indent != null && _param == null;
+
+  /// Whether this chunk may cause a newline depending on line splitting.
+  bool get isSoftSplit => _indent != null && _param != null;
+
+  /// `true` if an extra blank line should be output after this chunk if it's
+  /// split.
+  bool get isDouble => _isDouble;
+  bool _isDouble = false;
+
+  /// Whether this chunk should append an extra space if it's a soft split and
+  /// is left unsplit.
+  ///
+  /// This is `true`, for example, in a chunk that ends with a ",".
+  bool get spaceWhenUnsplit => _spaceWhenUnsplit;
+  bool _spaceWhenUnsplit = false;
+
+  /// Creates a new chunk starting with [_text].
+  Chunk(this._text);
+
+  /// Discard the split for the chunk and put it back into the state where more
+  /// text can be appended.
+  void allowText() {
+    _indent = null;
+  }
+
+  /// Append [text] to the end of the split's text.
+  void appendText(String text) {
+    assert(canAddText);
+
+    _text += text;
+  }
+
+  /// Forces this soft split to become a hard split.
+  ///
+  /// This is called on the soft splits of a [Multisplit] when it ends up
+  /// containing some other hard split.
+  void harden() {
+    assert(_param != null);
+    _param = null;
+  }
+
+  /// Finishes off this chunk with the given split information.
+  ///
+  /// This may be called multiple times on the same split since the splits
+  /// produced by walking the source and the splits coming from comments and
+  /// preserved whitespace often overlap. When that happens, this has logic to
+  /// combine that information into a single split.
+  void applySplit(int indent, int nesting, SplitParam param,
+      {bool spaceWhenUnsplit, bool isDouble}) {
+    if (spaceWhenUnsplit == null) spaceWhenUnsplit = false;
+    if (isDouble == null) isDouble = false;
+
+    if (isHardSplit || param == null) {
+      // A hard split always wins.
+      _param = null;
+    } else if (_indent == null) {
+      // If the chunk hasn't been initialized yet, just inherit the param.
+      _param = param;
+    }
+
+    // Last newline settings win.
+    _indent = indent;
+    this.nesting = nesting;
+    _spaceWhenUnsplit = spaceWhenUnsplit;
+
+    // Preserve a blank line.
+    _isDouble = _isDouble || isDouble;
+  }
+
+  String toString() {
+    var parts = [];
+
+    if (text.isNotEmpty) parts.add("${Color.bold}$text${Color.none}");
+
+    if (_indent != 0 && _indent != null) parts.add("indent:$_indent");
+    if (nesting != -1) parts.add("nest:$nesting");
+    if (spaceWhenUnsplit) parts.add("space");
+    if (_isDouble) parts.add("double");
+
+    if (_indent == null) {
+      parts.add("(no split info)");
+    } else if (isHardSplit) {
+      parts.add("hard");
+    } else {
+      var param = "p$_param";
+
+      if (_param.cost != Cost.normal) param += " \$${_param.cost}";
+
+      if (_param.implies.isNotEmpty) {
+        var impliedIds = _param.implies.map(
+            (param) => "p${param.id}").join(" ");
+        param += " -> $impliedIds";
+      }
+
+      parts.add(param);
+    }
+
+    return parts.join(" ");
+  }
+}
+
+/// Constants for the cost heuristics used to determine which set of splits is
+/// most desirable.
+class Cost {
+  /// The smallest cost.
+  ///
+  /// This isn't zero because we want to ensure all splitting has *some* cost,
+  /// otherwise, the formatter won't try to keep things on one line at all.
+  /// Almost all splits and spans use this. Greater costs tend to come from a
+  /// greater number of nested spans.
+  static const normal = 1;
+
+  /// Splitting after a "=" both for assignment and initialization.
+  static const assignment = 2;
+
+  /// Splitting before the first argument when it happens to be a function
+  /// expression with a block body.
+  static const firstBlockArgument = 2;
+
+  /// The series of positional arguments.
+  static const positionalArguments = 2;
+
+  /// Splitting inside the brackets of a list with only one element.
+  static const singleElementList = 2;
+
+  /// The cost of a single character that goes past the page limit.
+  ///
+  /// This cost is high to ensure any solution that fits in the page is
+  /// preferred over one that does not.
+  static const overflowChar = 1000;
+}
+
+/// Controls whether or not one or more soft split [Chunk]s are split.
+///
+/// When [LineSplitter] tries to split a line to fit within its page width, it
+/// does so by trying different combinations of parameters to see which set of
+/// active ones yields the best result.
+class SplitParam {
+  static int _nextId = 0;
+
+  /// A semi-unique numeric indentifier for the param.
+  ///
+  /// This is useful for debugging and also speeds up using the param in hash
+  /// sets. Ids are *semi*-unique because they may wrap around in long running
+  /// processes. Since params are equal based on their identity, this is
+  /// innocuous and prevents ids from growing without bound.
+  final int id = _nextId = (_nextId + 1) & 0x0fffffff;
+
+  /// The cost of this param when split.
+  final int cost;
+
+  /// The other [SplitParam]s that are "implied" by this one.
+  ///
+  /// Implication means that if the splitter chooses to split this param, it
+  /// must also split all of its implied ones (transitively). Implication is
+  /// one-way. If A implies B, it's fine to split B without splitting A.
+  final implies = <SplitParam>[];
+
+  /// Creates a new [SplitParam].
+  SplitParam([int cost])
+      : cost = cost != null ? cost : Cost.normal;
+
+  String toString() => "$id";
+
+  int get hashCode => id.hashCode;
+
+  bool operator ==(other) => identical(this, other);
+}
+
+/// Delimits a range of chunks that must end up on the same line to avoid an
+/// additional cost.
+///
+/// These are used to encourage the line splitter to try to keep things
+/// together, like parameter lists and binary operator expressions.
+class Span {
+  /// Index of the first chunk contained in this span.
+  int get start => _start;
+  int _start;
+
+  /// Index of the last chunk contained in this span.
+  int get end => _end;
+  int _end;
+
+  /// The cost applied when the span is split across multiple lines or `null`
+  /// if the span is for a multisplit.
+  final int cost;
+
+  Span(this._start, this.cost);
+
+  /// Marks this span as ending at [end].
+  void close(int end) {
+    assert(_end == null);
+    _end = end;
+  }
+
+  String toString() {
+    var result = "Span($start";
+
+    if (end != null) {
+      result += " - $end";
+    } else {
+      result += "...";
+    }
+
+    if (cost != null) result += " \$$cost";
+
+    return result + ")";
+  }
+
+  /// Shifts the indexes of the chunk down by [offset].
+  ///
+  /// This is used when a prefix of the chunk list gets pulled off by the
+  /// [LineWriter] after it gets formatted as a line. The remaining spans need
+  /// to have their indices shifted to account for the removed chunks.
+  ///
+  /// Returns `true` if the span has shifted all the way off the front and
+  /// should just be discarded.
+  bool shift(int offset) {
+    if (end != null && end < offset) return true;
+
+    _start -= offset;
+    if (_end != null) _end -= offset;
+
+    return false;
+  }
+}
+
+/// A comment in the source, with a bit of information about the surrounding
+/// whitespace.
+class SourceComment extends Selection {
+  /// The text of the comment, including `//`, `/*`, and `*/`.
+  final String text;
+
+  /// The number of newlines between the comment or token preceding this comment
+  /// and the beginning of this one.
+  ///
+  /// Will be zero if the comment is a trailing one.
+  final int linesBefore;
+
+  /// Whether this comment is a line comment.
+  final bool isLineComment;
+
+  /// Whether this comment starts at column one in the source.
+  ///
+  /// Comments that start at the start of the line will not be indented in the
+  /// output. This way, commented out chunks of code do not get erroneously
+  /// re-indented.
+  final bool isStartOfLine;
+
+  SourceComment(this.text, this.linesBefore,
+      {this.isLineComment, this.isStartOfLine});
+}
diff --git a/dart_style/lib/src/dart_formatter.dart b/dart_style/lib/src/dart_formatter.dart
new file mode 100644
index 0000000..a6effcc
--- /dev/null
+++ b/dart_style/lib/src/dart_formatter.dart
@@ -0,0 +1,114 @@
+// 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 dart_style.src.dart_formatter;
+
+import 'package:analyzer/src/string_source.dart';
+import 'package:analyzer/src/generated/parser.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+import 'package:analyzer/src/generated/source.dart';
+
+import 'error_listener.dart';
+import 'source_code.dart';
+import 'source_visitor.dart';
+
+/// Dart source code formatter.
+class DartFormatter {
+  /// The string that newlines should use.
+  ///
+  /// If not explicitly provided, this is inferred from the source text. If the
+  /// first newline is `\r\n` (Windows), it will use that. Otherwise, it uses
+  /// Unix-style line endings (`\n`).
+  String lineEnding;
+
+  /// The number of characters allowed in a single line.
+  final int pageWidth;
+
+  /// The number of levels of indentation to prefix the output lines with.
+  final int indent;
+
+  /// Creates a new formatter for Dart code.
+  ///
+  /// If [lineEnding] is given, that will be used for any newlines in the
+  /// output. Otherwise, the line separator will be inferred from the line
+  /// endings in the source file.
+  ///
+  /// If [indent] is given, that many levels of indentation will be prefixed
+  /// before each resulting line in the output.
+  DartFormatter({this.lineEnding, int pageWidth, this.indent: 0})
+      : this.pageWidth = (pageWidth == null) ? 80 : pageWidth;
+
+  /// Formats the given [source] string containing an entire Dart compilation
+  /// unit.
+  ///
+  /// If [uri] is given, it is a [String] or [Uri] used to identify the file
+  /// being formatted in error messages.
+  String format(String source, {uri}) {
+    if (uri == null) {
+      uri = "<unknown>";
+    } else if (uri is Uri) {
+      uri = uri.toString();
+    } else if (uri is String) {
+      // Do nothing.
+    } else {
+      throw new ArgumentError("uri must be `null`, a Uri, or a String.");
+    }
+
+    return formatSource(
+        new SourceCode(source, uri: uri, isCompilationUnit: true)).text;
+  }
+
+  /// Formats the given [source] string containing a single Dart statement.
+  String formatStatement(String source) {
+    return formatSource(new SourceCode(source, isCompilationUnit: false)).text;
+  }
+
+  /// Formats the given [source].
+  ///
+  /// Returns a new [SourceCode] containing the formatted code and the resulting
+  /// selection, if any.
+  SourceCode formatSource(SourceCode source) {
+    var errorListener = new ErrorListener();
+
+    // Tokenize the source.
+    var reader = new CharSequenceReader(source.text);
+    var stringSource = new StringSource(source.text, source.uri);
+    var scanner = new Scanner(stringSource, reader, errorListener);
+    var startToken = scanner.tokenize();
+    var lineInfo = new LineInfo(scanner.lineStarts);
+
+    // Infer the line ending if not given one. Do it here since now we know
+    // where the lines start.
+    if (lineEnding == null) {
+      // If the first newline is "\r\n", use that. Otherwise, use "\n".
+      if (scanner.lineStarts.length > 1 &&
+          scanner.lineStarts[1] >= 2 &&
+          source.text[scanner.lineStarts[1] - 2] == '\r') {
+        lineEnding = "\r\n";
+      } else {
+        lineEnding = "\n";
+      }
+    }
+
+    errorListener.throwIfErrors();
+
+    // Parse it.
+    var parser = new Parser(stringSource, errorListener);
+    parser.parseAsync = true;
+    parser.parseEnum = true;
+
+    var node;
+    if (source.isCompilationUnit) {
+      node = parser.parseCompilationUnit(startToken);
+    } else {
+      node = parser.parseStatement(startToken);
+    }
+
+    errorListener.throwIfErrors();
+
+    // Format it.
+    var visitor = new SourceVisitor(this, lineInfo, source);
+    return visitor.run(node);
+  }
+}
diff --git a/dart_style/lib/src/debug.dart b/dart_style/lib/src/debug.dart
new file mode 100644
index 0000000..09fff05
--- /dev/null
+++ b/dart_style/lib/src/debug.dart
@@ -0,0 +1,113 @@
+// 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.
+
+/// Internal debugging utilities.
+library dart_style.src.debug;
+
+import 'chunk.dart';
+import 'line_prefix.dart';
+import 'line_splitter.dart';
+
+/// Set this to `true` to turn out diagnostic output while formatting.
+bool debugFormatter = false;
+
+bool useAnsiColors = false;
+
+const unicodeSection = "\u00a7";
+const unicodeMidDot = "\u00b7";
+
+/// Constants for ANSI color escape codes.
+class Color {
+  static final cyan = _color("\u001b[36m");
+  static final gray = _color("\u001b[1;30m");
+  static final green = _color("\u001b[32m");
+  static final red = _color("\u001b[31m");
+  static final magenta = _color("\u001b[35m");
+  static final none = _color("\u001b[0m");
+  static final noColor = _color("\u001b[39m");
+  static final bold = _color("\u001b[1m");
+}
+
+/// Prints [chunks] to stdout, one chunk per line, with detailed information
+/// about each chunk.
+void dumpChunks(List<Chunk> chunks) {
+  var i = 0;
+  for (var chunk in chunks) {
+    print("$i: $chunk");
+    i++;
+  }
+}
+
+/// Prints [chunks] to stdout as a single line with non-printing chunks made
+/// visible.
+void dumpLine(List<Chunk> chunks,
+    [int indent = 0, LinePrefix prefix, Set<SplitParam> splits]) {
+  if (prefix == null) prefix = new LinePrefix();
+  if (splits == null) splits = new Set();
+
+  var buffer = new StringBuffer()
+    ..write(Color.gray)
+    ..write("| " * prefix.getNextLineIndent(chunks, indent))
+    ..write(Color.none);
+
+  for (var i = prefix.length; i < chunks.length; i++) {
+    var chunk = chunks[i];
+
+    buffer.write(chunk.text);
+
+    if (chunk.isSoftSplit) {
+      var color = splits.contains(chunk.param) ? Color.green : Color.gray;
+
+      buffer.write("$color$unicodeSection${chunk.param.cost}");
+      if (chunk.nesting != -1) buffer.write(":${chunk.nesting}");
+      buffer.write("${Color.none}");
+    } else if (chunk.isHardSplit) {
+      buffer.write("${Color.magenta}\\n${"->" * chunk.indent}${Color.none}");
+    }
+  }
+
+  print(buffer);
+}
+
+/// Convert the line to a [String] representation.
+///
+/// It will determine how best to split it into multiple lines of output and
+/// return a single string that may contain one or more newline characters.
+void dumpLines(List<Chunk> chunks,
+    [int indent = 0, LinePrefix prefix, SplitSet splits]) {
+  if (prefix == null) prefix = new LinePrefix();
+  if (splits == null) splits = new SplitSet();
+
+  var buffer = new StringBuffer()
+    ..write(Color.gray)
+    ..write("| " * indent)
+    ..write(Color.none);
+
+  for (var i = prefix.length; i < chunks.length - 1; i++) {
+    var chunk = chunks[i];
+    buffer.write(chunk.text);
+
+    if (splits.shouldSplitAt(i)) {
+      for (var j = 0; j < (chunk.isDouble ? 2 : 1); j++) {
+        buffer.writeln();
+
+        indent = chunk.indent + splits.getNesting(i);
+        buffer
+          ..write(Color.gray)
+          ..write("| " * indent)
+          ..write(Color.none);
+      }
+
+      // Should have a valid set of splits when we get here.
+      assert(indent != invalidSplits);
+    } else {
+      if (chunk.spaceWhenUnsplit) buffer.write(" ");
+    }
+  }
+
+  buffer.write(chunks.last.text);
+  print(buffer);
+}
+
+String _color(String ansiEscape) => useAnsiColors ? ansiEscape : "";
diff --git a/dart_style/lib/src/error_listener.dart b/dart_style/lib/src/error_listener.dart
new file mode 100644
index 0000000..0c0be61
--- /dev/null
+++ b/dart_style/lib/src/error_listener.dart
@@ -0,0 +1,25 @@
+// 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 dart_style.src.error_listener;
+
+import 'package:analyzer/analyzer.dart';
+
+import 'formatter_exception.dart';
+
+/// A simple [AnalysisErrorListener] that just collects the reported errors.
+class ErrorListener implements AnalysisErrorListener {
+  final _errors = <AnalysisError>[];
+
+  void onError(AnalysisError error) {
+    _errors.add(error);
+  }
+
+  /// Throws a [FormatterException] if any errors have been reported.
+  void throwIfErrors() {
+    if (_errors.isEmpty) return;
+
+    throw new FormatterException(_errors);
+  }
+}
diff --git a/dart_style/lib/src/formatter_exception.dart b/dart_style/lib/src/formatter_exception.dart
new file mode 100644
index 0000000..7ccc353
--- /dev/null
+++ b/dart_style/lib/src/formatter_exception.dart
@@ -0,0 +1,37 @@
+// 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 dart_style.src.formatter_exception;
+
+import 'package:analyzer/analyzer.dart';
+import 'package:source_span/source_span.dart';
+
+/// Thrown when one or more errors occurs while parsing the code to be
+/// formatted.
+class FormatterException implements Exception {
+  /// The [AnalysisError]s that occurred.
+  final List<AnalysisError> errors;
+
+  /// Creates a new FormatterException with an optional error [message].
+  const FormatterException(this.errors);
+
+  /// Creates a human-friendly representation of the analysis errors.
+  String message() {
+    var buffer = new StringBuffer();
+    buffer.writeln("Could not format because the source could not be parsed:");
+
+    for (var error in errors) {
+      var file = new SourceFile(error.source.contents.data,
+          url: error.source.fullName);
+
+      var span = file.span(error.offset, error.offset + error.length);
+      if (buffer.isNotEmpty) buffer.writeln();
+      buffer.write(span.message(error.message, color: true));
+    }
+
+    return buffer.toString();
+  }
+
+  String toString() => message();
+}
diff --git a/dart_style/lib/src/formatter_options.dart b/dart_style/lib/src/formatter_options.dart
new file mode 100644
index 0000000..3869e8a
--- /dev/null
+++ b/dart_style/lib/src/formatter_options.dart
@@ -0,0 +1,119 @@
+// 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 dart_style.src.formatter_options;
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'source_code.dart';
+
+/// Global options that affect how the formatter produces and uses its outputs.
+class FormatterOptions {
+  /// The [OutputReporter] used to show the formatting results.
+  final OutputReporter reporter;
+
+  /// The number of columns that formatted output should be constrained to fit
+  /// within.
+  final int pageWidth;
+
+  /// Whether symlinks should be traversed when formatting a directory.
+  final bool followLinks;
+
+  FormatterOptions(this.reporter,
+      {this.pageWidth: 80, this.followLinks: false});
+}
+
+/// How the formatter reports the results it produces.
+abstract class OutputReporter {
+  /// Prints only the names of files whose contents are different from their
+  /// formatted version.
+  static final dryRun = new _DryRunReporter();
+
+  /// Prints the formatted results of each file to stdout.
+  static final print = new _PrintReporter();
+
+  /// Prints the formatted result and selection info of each file to stdout as
+  /// a JSON map.
+  static final printJson = new _PrintJsonReporter();
+
+  /// Overwrites each file with its formatted result.
+  static final overwrite = new _OverwriteReporter();
+
+  /// Describe the directory whose contents are about to be processed.
+  void showDirectory(String path) {}
+
+  /// Describe the symlink at [path] that wasn't followed.
+  void showSkippedLink(String path) {}
+
+  /// Describe the hidden file at [path] that wasn't processed.
+  void showHiddenFile(String path) {}
+
+  /// Describe the processed file at [path] whose formatted result is [output].
+  ///
+  /// If the contents of the file are the same as the formatted output,
+  /// [changed] will be false.
+  void showFile(File file, String label, SourceCode output, {bool changed});
+}
+
+/// Prints only the names of files whose contents are different from their
+/// formatted version.
+class _DryRunReporter extends OutputReporter {
+  void showFile(File file, String label, SourceCode output, {bool changed}) {
+    // Only show the changed files.
+    if (changed) print(label);
+  }
+}
+
+/// Prints the formatted results of each file to stdout.
+class _PrintReporter extends OutputReporter {
+  void showDirectory(String path) {
+    print("Formatting directory $path:");
+  }
+
+  void showSkippedLink(String path) {
+    print("Skipping link $path");
+  }
+
+  void showHiddenFile(String path) {
+    print("Skipping hidden file $path");
+  }
+
+  void showFile(File file, String label, SourceCode output, {bool changed}) {
+    // Don't add an extra newline.
+    stdout.write(output.text);
+  }
+}
+
+/// Prints the formatted result and selection info of each file to stdout as a
+/// JSON map.
+class _PrintJsonReporter extends OutputReporter {
+  void showFile(File file, String label, SourceCode output, {bool changed}) {
+    // TODO(rnystrom): Put an empty selection in here to remain compatible with
+    // the old formatter. Since there's no way to pass a selection on the
+    // command line, this will never be used, which is why it's hard-coded to
+    // -1, -1. If we add support for passing in a selection, put the real
+    // result here.
+    print(JSON.encode({
+      "path": label,
+      "source": output.text,
+      "selection": {
+        "offset": output.selectionStart != null ? output.selectionStart : -1,
+        "length": output.selectionLength != null ? output.selectionLength : -1
+      }
+    }));
+  }
+}
+
+/// Overwrites each file with its formatted result.
+class _OverwriteReporter extends _PrintReporter {
+  void showFile(File file, String label, SourceCode output, {bool changed}) {
+    if (changed) {
+      file.writeAsStringSync(output.text);
+      print("Formatted $label");
+    } else {
+      print("Unchanged $label");
+    }
+  }
+}
diff --git a/dart_style/lib/src/io.dart b/dart_style/lib/src/io.dart
new file mode 100644
index 0000000..e7c8555
--- /dev/null
+++ b/dart_style/lib/src/io.dart
@@ -0,0 +1,71 @@
+// 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 dart_style.src.io;
+
+import 'dart:io';
+
+import 'package:path/path.dart' as p;
+
+import 'dart_formatter.dart';
+import 'formatter_options.dart';
+import 'formatter_exception.dart';
+import 'source_code.dart';
+
+/// Runs the formatter on every .dart file in [path] (and its subdirectories),
+/// and replaces them with their formatted output.
+///
+/// Returns `true` if successful or `false` if an error occurred in any of the
+/// files.
+bool processDirectory(FormatterOptions options, Directory directory) {
+  options.reporter.showDirectory(directory.path);
+
+  var success = true;
+  for (var entry in directory.listSync(
+      recursive: true, followLinks: options.followLinks)) {
+    var relative = p.relative(entry.path, from: directory.path);
+
+    if (entry is Link) {
+      options.reporter.showSkippedLink(relative);
+      continue;
+    }
+
+    if (entry is! File || !entry.path.endsWith(".dart")) continue;
+
+    // If the path is in a subdirectory starting with ".", ignore it.
+    if (p.split(relative).any((part) => part.startsWith("."))) {
+      options.reporter.showHiddenFile(relative);
+      continue;
+    }
+
+    if (!processFile(options, entry, label: relative)) success = false;
+  }
+
+  return success;
+}
+
+/// Runs the formatter on [file].
+///
+/// Returns `true` if successful or `false` if an error occurred.
+bool processFile(FormatterOptions options, File file, {String label}) {
+  if (label == null) label = file.path;
+
+  var formatter = new DartFormatter(pageWidth: options.pageWidth);
+  try {
+    var source = new SourceCode(file.readAsStringSync(), uri: file.path);
+    var output = formatter.formatSource(source);
+    options.reporter.showFile(file, label, output,
+        changed: source.text != output.text);
+    return true;
+  } on FormatterException catch (err) {
+    stderr.writeln(err.message());
+  } catch (err, stack) {
+    stderr.writeln('''Hit a bug in the formatter when formatting $label.
+Please report at: github.com/dart-lang/dart_style/issues
+$err
+$stack''');
+  }
+
+  return false;
+}
diff --git a/dart_style/lib/src/line_prefix.dart b/dart_style/lib/src/line_prefix.dart
new file mode 100644
index 0000000..f11bad0
--- /dev/null
+++ b/dart_style/lib/src/line_prefix.dart
@@ -0,0 +1,141 @@
+// 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 dart_style.src.line_printer;
+
+import 'chunk.dart';
+import 'nesting.dart';
+
+/// A prefix of a series of chunks, which in turn can be considered a key to
+/// describe the suffix of the remaining chunks that follows it.
+///
+/// This is used by the splitter to memoize suffixes whose best splits have
+/// previously been calculated. For each unique [LinePrefix], there will be a
+/// single set of best splits for the remainder of the line following it.
+class LinePrefix {
+  /// The number of chunks in the prefix.
+  ///
+  /// The suffix is the remaining chunks starting at index [length].
+  final int length;
+
+  /// The [SplitParam]s for params that appear both in the prefix and suffix
+  /// and have not been set.
+  ///
+  /// This is used to ensure that we honor the decisions already made in the
+  /// prefix when processing the suffix. It only includes params that appear in
+  /// the suffix to avoid storing information about irrelevant params. This is
+  /// critical to ensure we keep prefixes simple to maximize the reuse we get
+  /// from the memoization table.
+  ///
+  /// This does *not* include params that appear only in the suffix. In other
+  /// words, it only includes params that have deliberately been chosen to not
+  /// be set, not params we simply haven't considered yet.
+  final Set<SplitParam> unsplitParams;
+
+  /// The [SplitParam]s for params that appear both in the prefix and suffix
+  /// and have been set.
+  ///
+  /// This is used to ensure that we honor the decisions already made in the
+  /// prefix when processing the suffix. It only includes params that appear in
+  /// the suffix to avoid storing information about irrelevant params. This is
+  /// critical to ensure we keep prefixes simple to maximize the reuse we get
+  /// from the memoization table.
+  final Set<SplitParam> splitParams;
+
+  /// The nested expressions in the prefix that are still open at the beginning
+  /// of the suffix.
+  ///
+  /// For example, if the line is `outer(inner(argument))`, and the prefix is
+  /// `outer(inner(`, the nesting stack will be two levels deep.
+  final NestingStack _nesting;
+
+  /// The depth of indentation caused expression nesting.
+  int get nestingIndent => _nesting.indent;
+
+  /// Creates a new zero-length prefix whose suffix is the entire line.
+  LinePrefix([int length = 0])
+      : this._(length, new Set(), new Set(), new NestingStack());
+
+  LinePrefix._(this.length, this.unsplitParams, this.splitParams,
+      this._nesting) {
+    assert(_nesting != null);
+  }
+
+  bool operator ==(other) {
+    if (other is! LinePrefix) return false;
+
+    if (length != other.length) return false;
+    if (_nesting != other._nesting) return false;
+
+    if (unsplitParams.length != other.unsplitParams.length) {
+      return false;
+    }
+
+    if (splitParams.length != other.splitParams.length) {
+      return false;
+    }
+
+    for (var param in unsplitParams) {
+      if (!other.unsplitParams.contains(param)) return false;
+    }
+
+    for (var param in splitParams) {
+      if (!other.splitParams.contains(param)) return false;
+    }
+
+    return true;
+  }
+
+  int get hashCode => length.hashCode ^ _nesting.hashCode;
+
+  /// Create zero or more new [LinePrefix]es starting from the same nesting
+  /// stack as this one but expanded to [length].
+  ///
+  /// The nesting of the chunk immediately preceding the suffix modifies the
+  /// new prefix's nesting stack.
+  ///
+  /// [unsplitParams] is the set of [SplitParam]s in the new prefix that the
+  /// splitter decided to *not* split (including unsplit ones also in this
+  /// prefix). [splitParams] is likewise the set that have been chosen to be
+  /// split.
+  ///
+  /// Returns an empty iterable if the new split chunk results in an invalid
+  /// prefix. See [NestingStack.applySplit] for details.
+  Iterable<LinePrefix> expand(List<Chunk> chunks, Set<SplitParam> unsplitParams,
+      Set<SplitParam> splitParams, int length) {
+    var split = chunks[length - 1];
+
+    if (!split.isInExpression) {
+      return [
+        new LinePrefix._(length, unsplitParams, splitParams,
+            new NestingStack())
+      ];
+    }
+
+    return _nesting.applySplit(split).map((nesting) =>
+        new LinePrefix._(
+            length, unsplitParams, splitParams, nesting));
+  }
+
+  /// Gets the leading indentation of the newline that immediately follows
+  /// this prefix.
+  ///
+  /// Takes into account the indentation of the previous split and any
+  /// additional indentation from wrapped nested expressions.
+  int getNextLineIndent(List<Chunk> chunks, int indent) {
+    // TODO(rnystrom): This could be cached at construction time, which may be
+    // faster.
+    // Get the initial indentation of the line immediately after the prefix,
+    // ignoring any extra indentation caused by nested expressions.
+    if (length > 0) {
+      indent = chunks[length - 1].indent;
+    }
+
+    return indent + _nesting.indent;
+  }
+
+  String toString() =>
+      "LinePrefix(length $length, nesting $_nesting, "
+      "unsplit $unsplitParams, split $splitParams)";
+}
diff --git a/dart_style/lib/src/line_splitter.dart b/dart_style/lib/src/line_splitter.dart
new file mode 100644
index 0000000..3291e28
--- /dev/null
+++ b/dart_style/lib/src/line_splitter.dart
@@ -0,0 +1,543 @@
+// 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 dart_style.src.line_splitter;
+
+import 'dart:math' as math;
+
+import 'chunk.dart';
+import 'debug.dart';
+import 'line_prefix.dart';
+
+/// The number of spaces in a single level of indentation.
+const spacesPerIndent = 2;
+
+/// The number of indentation levels in a single level of expression nesting.
+const indentsPerNest = 2;
+
+/// Cost or indent value used to indication no solution could be found.
+const invalidSplits = -1;
+
+// TODO(rnystrom): This needs to be updated to take into account how it works
+// now.
+/// Takes a series of [Chunk]s and determines the best way to split them into
+/// lines of output that fit within the page width (if possible).
+///
+/// Trying all possible combinations is exponential in the number of
+/// [SplitParam]s and expression nesting levels both of which can be quite large
+/// with things like long method chains containing function literals, lots of
+/// named parameters, etc. To tame that, this uses dynamic programming. The
+/// basic process is:
+///
+/// Given a suffix of the entire line, we walk over the tokens keeping track
+/// of any splits we find until we fill the page width (or run out of line).
+/// If we reached the end of the line without crossing the page width, we're
+/// fine and the suffix is good as it is.
+///
+/// If we went over, at least one of those splits must be applied to keep the
+/// suffix in bounds. For each of those splits, we split at that point and
+/// apply the same algorithm for the remainder of the line. We get the results
+/// of all of those, choose the one with the lowest cost, and
+/// that's the best solution.
+///
+/// The fact that this recurses while only removing a small prefix from the
+/// line (the chunks before the first split), means this is exponential.
+/// Thankfully, though, the best set of splits for a suffix of the line depends
+/// only on:
+///
+///  -   The starting position of the suffix.
+///
+///  -   The set of expression nesting levels currently being split up to that
+///      point.
+///
+///      For example, consider the following:
+///
+///          outer(inner(argument1, argument2, argument3));
+///
+///      If the suffix we are considering is "argument2, ..." then we need to
+///      know if we previously split after "outer(", "inner(", or both. The
+///      answer determines how much leading indentation "argument2, ..." will
+///      have.
+///
+/// Thus, whenever we calculate an ideal set of splits for some suffix, we
+/// memoize it. When later recursive calls descend to that suffix again, we can
+/// reuse it.
+class LineSplitter {
+  /// The string used for newlines.
+  final String _lineEnding;
+
+  /// The number of characters allowed in a single line.
+  final int _pageWidth;
+
+  /// The list of chunks being split.
+  final List<Chunk> _chunks;
+
+  /// The set of spans that wrap around [_chunks].
+  final List<Span> _spans;
+
+  /// The leading indentation at the beginning of the first line.
+  final int _indent;
+
+  /// Memoization table for the best set of splits for the remainder of the
+  /// line following a given prefix.
+  final _bestSplits = <LinePrefix, SplitSet>{};
+
+  /// Creates a new splitter that tries to fit a series of chunks within a
+  /// given page width.
+  LineSplitter(this._lineEnding, this._pageWidth, this._chunks, this._spans,
+      this._indent) {
+    assert(_chunks.isNotEmpty);
+  }
+
+  /// Convert the line to a [String] representation.
+  ///
+  /// It will determine how best to split it into multiple lines of output and
+  /// return a single string that may contain one or more newline characters.
+  ///
+  /// Returns a two-element list. The first element will be an [int] indicating
+  /// where in [buffer] the selection start point should appear if it was
+  /// contained in the formatted list of chunks. Otherwise it will be `null`.
+  /// Likewise, the second element will be non-`null` if the selection endpoint
+  /// is within the list of chunks.
+  List<int> apply(StringBuffer buffer) {
+    if (debugFormatter) dumpLine(_chunks, _indent);
+
+    var nestingDepth = _flattenNestingLevels();
+
+    // Hack. The formatter doesn't handle formatting very deeply nested code
+    // well. It can make performance spiral into a pit of sadness. Fortunately,
+    // we only tend to see expressions pathologically deeply nested in
+    // generated code that isn't read by humans much anyway. To avoid burning
+    // too much time on these, harden any splits containing more than a certain
+    // level of nesting.
+    //
+    // The number here was chosen empirically based on formatting the repo. It
+    // was picked to get the best performance while affecting the minimum amount
+    // of results.
+    // TODO(rnystrom): Do something smarter.
+    if (nestingDepth > 9) {
+      for (var chunk in _chunks) {
+        if (chunk.param != null && nestingDepth - chunk.nesting > 9) {
+          chunk.harden();
+        }
+      }
+    }
+
+    var splits = _findBestSplits(new LinePrefix());
+
+    var selection = [null, null];
+
+    // Write each chunk and the split after it.
+    buffer.write(" " * (_indent * spacesPerIndent));
+    for (var i = 0; i < _chunks.length; i++) {
+      var chunk = _chunks[i];
+
+      // If this chunk contains one of the selection markers, tell the writer
+      // where it ended up in the final output.
+      if (chunk.selectionStart != null) {
+        selection[0] = buffer.length + chunk.selectionStart;
+      }
+
+      if (chunk.selectionEnd != null) {
+        selection[1] = buffer.length + chunk.selectionEnd;
+      }
+
+      buffer.write(chunk.text);
+
+      if (i == _chunks.length - 1) {
+        // Don't write trailing whitespace after the last chunk.
+      } else if (splits.shouldSplitAt(i)) {
+        buffer.write(_lineEnding);
+        if (chunk.isDouble) buffer.write(_lineEnding);
+
+        var indent = chunk.indent + splits.getNesting(i);
+        buffer.write(" " * (indent * spacesPerIndent));
+
+        // Should have a valid set of splits when we get here.
+        assert(indent != invalidSplits);
+      } else {
+        if (chunk.spaceWhenUnsplit) buffer.write(" ");
+      }
+    }
+
+    return selection;
+  }
+
+  /// Removes any unused nesting levels from the chunks.
+  ///
+  /// The line splitter considers every possible combination of mapping
+  /// indentation to nesting levels when trying to find the best solution. For
+  /// example, it may assign 4 spaces of indentation to level 1, 8 spaces to
+  /// level 3, etc.
+  ///
+  /// It's fairly common for a nesting level to not actually appear at the
+  /// boundary of a chunk. The source visitor may enter more than one level of
+  /// nesting at a point where a split cannot happen. In that case, there's no
+  /// point in trying to assign an indentation level to that nesting level. It
+  /// will never be used because no line will begin at that level of
+  /// indentation.
+  ///
+  /// Worse, if the splitter *does* consider these levels, it can dramatically
+  /// increase solving time. To avoid that, this renumbers all of the nesting
+  /// levels in the chunks to not have any of these unused gaps.
+  ///
+  /// Returns the number of distinct nesting levels remaining after flattening.
+  /// This may be zero if the chunks have no nesting (i.e. just statement-level
+  /// indentation).
+  int _flattenNestingLevels() {
+    var nestingLevels = _chunks
+        .map((chunk) => chunk.nesting)
+        .where((nesting) => nesting != -1)
+        .toSet()
+        .toList();
+    nestingLevels.sort();
+
+    var nestingMap = {-1: -1};
+    for (var i = 0; i < nestingLevels.length; i++) {
+      nestingMap[nestingLevels[i]] = i;
+    }
+
+    for (var chunk in _chunks) {
+      chunk.nesting = nestingMap[chunk.nesting];
+    }
+
+    return nestingLevels.length;
+  }
+
+  /// Finds the best set of splits to apply to the remainder of the line
+  /// following [prefix].
+  SplitSet _findBestSplits(LinePrefix prefix) {
+    // Use the memoized result if we have it.
+    if (_bestSplits.containsKey(prefix)) return _bestSplits[prefix];
+
+    var indent = prefix.getNextLineIndent(_chunks, _indent);
+
+    var bestSplits;
+    var lowestCost;
+
+    // If there are no required splits, consider not splitting any of the soft
+    // splits (if there are any) as one possible solution.
+    if (!_suffixContainsHardSplits(prefix)) {
+      var splits = new SplitSet();
+      var cost = _evaluateCost(prefix, indent, splits);
+
+      if (cost != invalidSplits) {
+        bestSplits = splits;
+        lowestCost = cost;
+
+        // If we fit the whole suffix without any splitting, that's going to be
+        // the best solution, so don't bother trying any others.
+        if (cost < Cost.overflowChar) {
+          _bestSplits[prefix] = bestSplits;
+          return bestSplits;
+        }
+      }
+    }
+
+    // For each split in the suffix, calculate the best cost where that is the
+    // first split applied. This recurses so that for each split, we consider
+    // all of the possible sets of splits *after* it and determine the best
+    // cost subset out of all of those.
+    var skippedParams = new Set();
+
+    var length = indent * spacesPerIndent;
+
+    // Don't consider the last chunk, since there's no point in splitting on it.
+    for (var i = prefix.length; i < _chunks.length - 1; i++) {
+      var split = _chunks[i];
+
+      // We must skip over this chunk if it cannot be split.
+      if (_canSplit(prefix, split, skippedParams)) {
+        var splitParams = _getSplitParams(prefix, i, split);
+
+        // Find all the params we did *not* split in the prefix that appear in
+        // the suffix so we can ensure they aren't split there either.
+        var unsplitParams = prefix.unsplitParams.toSet();
+        for (var param in skippedParams) {
+          if (_suffixContainsParam(i, param)) unsplitParams.add(param);
+        }
+
+        // Create new prefixes that go all the way up to the split. There can be
+        // multiple solutions here since there are different ways to handle a
+        // jump in nesting depth.
+        var longerPrefixes = prefix.expand(
+          _chunks, unsplitParams, splitParams, i + 1);
+
+        for (var longerPrefix in longerPrefixes) {
+          // Given the nesting stack for this split, see what we can do with the
+          // rest of the line.
+          var remaining = _findBestSplits(longerPrefix);
+
+          // If it wasn't possible to split the suffix given this nesting stack,
+          // skip it.
+          if (remaining == null) continue;
+
+          var splits = remaining.add(i, longerPrefix.nestingIndent);
+          var cost = _evaluateCost(prefix, indent, splits);
+
+          // If the suffix is invalid (because of a mis-matching multisplit),
+          // skip it.
+          if (cost == invalidSplits) continue;
+
+          if (lowestCost == null ||
+              cost < lowestCost ||
+              (cost == lowestCost && splits.weight > bestSplits.weight)) {
+            lowestCost = cost;
+            bestSplits = splits;
+          }
+        }
+      }
+
+      // If we go past the end of the page and we've already found a solution
+      // that fits, then no other solution that involves overflowing will beat
+      // that, so stop.
+      length += split.text.length;
+      if (split.spaceWhenUnsplit) length++;
+      if (length > _pageWidth &&
+          lowestCost != null &&
+          lowestCost < Cost.overflowChar) {
+        break;
+      }
+
+      // If we can't leave this split unsplit (because it's hard or has a
+      // param that the prefix already forced to split), then stop.
+      if (split.isHardSplit) break;
+      if (prefix.splitParams.contains(split.param)) break;
+
+      skippedParams.add(split.param);
+    }
+
+    _bestSplits[prefix] = bestSplits;
+
+    return bestSplits;
+  }
+
+  /// Gets whether the splitter can split [chunk] given [prefix] and
+  /// [skippedParams] which come before it.
+  ///
+  /// This returns `false` if the prefix or skipped params imply that this
+  /// chunk's param must also not be applied.
+  bool _canSplit(LinePrefix prefix, Chunk chunk,
+      Set<SplitParam> skippedParams) {
+    // Can always split on a hard split.
+    if (chunk.param == null) return true;
+
+    // If we didn't split the param in the prefix, we can't split on the same
+    // param in the suffix.
+    if (prefix.unsplitParams.contains(chunk.param)) return false;
+
+    // If we already skipped over the chunk's param,
+    // have to skip over it on this chunk too.
+    if (skippedParams.contains(chunk.param)) return false;
+
+    isParamSkipped(param) {
+      if (skippedParams.contains(param)) return false;
+
+      // If any param implied by this one is skipped, then splitting on the
+      // starting param would imply it should be split, which violates that,
+      // so don't allow the root one to be split.
+      for (var implied in param.implies) {
+        if (!isParamSkipped(implied)) return false;
+      }
+
+      return true;
+    }
+
+    return isParamSkipped(chunk.param);
+  }
+
+  /// Get the set of params we have forced to split in [prefix] (including
+  /// [split] which is also forced to split) that also appear in the suffix.
+  ///
+  /// We rebuild the set from scratch so that splits that no longer appear in
+  /// the shorter suffix are discarded. This helps keep the set small in the
+  /// prefix, which maximizes the memoization hits.
+  Set<SplitParam> _getSplitParams(LinePrefix prefix, int index, Chunk split) {
+    var splitParams = new Set();
+
+    addParam(param) {
+      if (_suffixContainsParam(index, param)) splitParams.add(param);
+
+      // Recurse into the params that are implied by this one.
+      param.implies.forEach(addParam);
+    }
+
+    prefix.splitParams.forEach(addParam);
+
+    // Consider this split too.
+    if (split.param != null) addParam(split.param);
+
+    return splitParams;
+  }
+
+  /// Gets whether the suffix after [prefix] contains any mandatory splits.
+  ///
+  /// This includes both hard splits and splits that depend on params that were
+  /// set in the prefix.
+  bool _suffixContainsHardSplits(LinePrefix prefix) {
+    for (var i = prefix.length; i < _chunks.length - 1; i++) {
+      if (_chunks[i].isHardSplit || (_chunks[i].isSoftSplit &&
+              prefix.splitParams.contains(_chunks[i].param))) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /// Gets whether the suffix of the line after index [split] contains a soft
+  /// split using [param].
+  bool _suffixContainsParam(int split, SplitParam param) {
+    if (param == null) return false;
+
+    // TODO(rnystrom): Consider caching the set of params that appear at every
+    // suffix.
+    for (var i = split + 1; i < _chunks.length; i++) {
+      if (_chunks[i].isSoftSplit && _chunks[i].param == param) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /// Evaluates the cost (i.e. the relative "badness") of splitting the line
+  /// into [lines] physical lines based on the current set of params.
+  int _evaluateCost(LinePrefix prefix, int indent, SplitSet splits) {
+    assert(splits != null);
+
+    // Calculate the length of each line and apply the cost of any spans that
+    // get split.
+    var cost = 0;
+    var length = indent * spacesPerIndent;
+
+    var params = new Set();
+
+    var splitIndexes = [];
+
+    endLine() {
+      // Punish lines that went over the length. We don't rule these out
+      // completely because it may be that the only solution still goes over
+      // (for example with long string literals).
+      if (length > _pageWidth) {
+        cost += (length - _pageWidth) * Cost.overflowChar;
+      }
+    }
+
+    for (var i = prefix.length; i < _chunks.length; i++) {
+      var chunk = _chunks[i];
+
+      length += chunk.text.length;
+
+      if (i < _chunks.length - 1) {
+        if (splits.shouldSplitAt(i)) {
+          endLine();
+          splitIndexes.add(i);
+
+          if (chunk.param != null && !params.contains(chunk.param)) {
+            // Don't double-count params if multiple splits share the same
+            // param.
+            // TODO(rnystrom): Is this needed? Can we actually let splits that
+            // share a param accumulate cost?
+            params.add(chunk.param);
+            cost += chunk.param.cost;
+          }
+
+          // Start the new line.
+          length = (chunk.indent + splits.getNesting(i)) * spacesPerIndent;
+        } else {
+          if (chunk.spaceWhenUnsplit) length++;
+        }
+      }
+    }
+
+    // See which spans got split. We avoid iterators here for performance.
+    for (var i = 0; i < _spans.length; i++) {
+      var span = _spans[i];
+      for (var j = 0; j < splitIndexes.length; j++) {
+        var index = splitIndexes[j];
+
+        // If the split is contained within a span (and is not the tail end of
+        // it), the span got split.
+        if (index >= span.start && index < span.end) {
+          cost += span.cost;
+          break;
+        }
+      }
+    }
+
+    // Finish the last line.
+    endLine();
+
+    return cost;
+  }
+}
+
+/// An immutable, persistent set of enabled soft split [Chunk]s.
+///
+/// For each chunk, this tracks if it has been split and, if so, what the
+/// chosen level of expression nesting is for the following line.
+///
+/// Internally, this uses a sparse parallel list where each element corresponds
+/// to the nesting level of the chunk at that index in the chunk list, or `null`
+/// if there is no active split there. This had about a 10% perf improvement
+/// over using a [Set] of splits or a persistent linked list of split
+/// index/nesting pairs.
+class SplitSet {
+  List<int> _splitNesting;
+
+  /// Creates a new empty split set.
+  SplitSet() : this._(const []);
+
+  SplitSet._(this._splitNesting);
+
+  /// Returns a new [SplitSet] containing the union of this one and the split
+  /// at [splitIndex] with [nestingIndent].
+  SplitSet add(int splitIndex, int nestingIndent) {
+    var newNesting = new List(math.max(splitIndex + 1, _splitNesting.length));
+    newNesting.setAll(0, _splitNesting);
+    newNesting[splitIndex] = nestingIndent;
+
+    return new SplitSet._(newNesting);
+  }
+
+  /// Returns `true` if the chunk at [splitIndex] should be split.
+  bool shouldSplitAt(int splitIndex) =>
+      splitIndex < _splitNesting.length && _splitNesting[splitIndex] != null;
+
+  /// Gets the nesting level of the split chunk at [splitIndex].
+  int getNesting(int splitIndex) => _splitNesting[splitIndex];
+
+  /// Determines the "weight" of the set.
+  ///
+  /// This is the sum of the positions where splits occur. Having more splits
+  /// increases weight but, more importantly, having a split closer to the end
+  /// increases its weight.
+  ///
+  /// This is used to break a tie when two [SplitSets] have the same cost. When
+  /// that occurs, we prefer splits later in the line since that keeps most
+  /// code towards the top lines. This occurs frequently in argument lists.
+  /// Since every argument split has the same cost, a long argument list can be
+  /// split in two a number of equal-cost ways. The weight is used to select
+  /// the one that puts the most arguments on the first line(s).
+  int get weight {
+    var result = 0;
+    for (var i = 0; i < _splitNesting.length; i++) {
+      if (_splitNesting[i] != null) result += i;
+    }
+
+    return result;
+  }
+
+  String toString() {
+    var result = [];
+    for (var i = 0; i < _splitNesting.length; i++) {
+      if (_splitNesting[i] != null) {
+        result.add("$i:${_splitNesting[i]}");
+      }
+    }
+
+    return result.join(" ");
+  }
+}
diff --git a/dart_style/lib/src/line_writer.dart b/dart_style/lib/src/line_writer.dart
new file mode 100644
index 0000000..3169aaa
--- /dev/null
+++ b/dart_style/lib/src/line_writer.dart
@@ -0,0 +1,769 @@
+// 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 dart_style.src.source_writer;
+
+import 'dart_formatter.dart';
+import 'chunk.dart';
+import 'debug.dart';
+import 'line_splitter.dart';
+import 'multisplit.dart';
+import 'source_code.dart';
+import 'whitespace.dart';
+
+/// Takes the incremental serialized output of [SourceVisitor]--the source text
+/// along with any comments and preserved whitespace--and produces a coherent
+/// series of [Chunk]s which can then be split into physical lines.
+///
+/// Keeps track of leading indentation, expression nesting, and all of the hairy
+/// code required to seamlessly integrate existing comments into the pure
+/// output produced by [SourceVisitor].
+class LineWriter {
+  final DartFormatter _formatter;
+
+  final SourceCode _source;
+
+  final _buffer = new StringBuffer();
+
+  final _chunks = <Chunk>[];
+
+  /// The whitespace that should be written to [_chunks] before the next
+  ///  non-whitespace token or `null` if no whitespace is pending.
+  ///
+  /// This ensures that changes to indentation and nesting also apply to the
+  /// most recent split, even if the visitor "creates" the split before changing
+  /// indentation or nesting.
+  Whitespace _pendingWhitespace;
+
+  /// The number of newlines that need to be written to [_buffer] before the
+  /// next line can be output.
+  ///
+  /// Where [_pendingWhitespace] buffers between the [SourceVisitor] and
+  /// [_chunks], this buffers between the [LineWriter] and [_buffer]. It ensures
+  /// we don't have trailing newlines in the output.
+  int _bufferedNewlines = 0;
+
+  /// The indentation at the beginning of the current complete line being
+  /// written.
+  int _beginningIndent;
+
+  /// The nested stack of multisplits that are currently being written.
+  ///
+  /// If a hard newline appears in the middle of a multisplit, then the
+  /// multisplit itself must be split. For example, a collection can either be
+  /// single line:
+  ///
+  ///    [all, on, one, line];
+  ///
+  /// or multi-line:
+  ///
+  ///    [
+  ///      one,
+  ///      item,
+  ///      per,
+  ///      line
+  ///    ]
+  ///
+  /// Collections can also contain function expressions, which have blocks which
+  /// in turn force a newline in the middle of the collection. When that
+  /// happens, we need to force all surrounding collections to be multi-line.
+  /// This tracks them so we can do that.
+  final _multisplits = <Multisplit>[];
+
+  /// The nested stack of spans that are currently being written.
+  final _openSpans = <Span>[];
+
+  /// All of the spans that have been created and closed.
+  final _spans = <Span>[];
+
+  /// The current indentation and nesting levels.
+  ///
+  /// This is tracked as a stack of numbers. Each element in the stack
+  /// represents a level of statement indentation. The number of the element is
+  /// the current expression nesting depth for that statement.
+  ///
+  /// It's stored as a stack because expressions may contain statements which
+  /// in turn contain other expressions. The nesting level of the inner
+  /// expressions are unrelated to the surrounding ones. For example:
+  ///
+  ///     outer(invocation(() {
+  ///       inner(lambda());
+  ///     }));
+  ///
+  /// When writing `inner(lambda())`, we need to track its nesting level. At
+  /// the same time, when the lambda is done, we need to return to the nesting
+  /// level of `outer(invocation(...`.
+  ///
+  /// Start with an implicit entry so that top-level definitions and directives
+  /// can be split.
+  var _indentStack = [-1];
+
+  /// The current indentation, not including expression nesting.
+  int get _indent => _indentStack.length - 1;
+
+  /// The nesting depth of the current inner-most block.
+  int get _nesting => _indentStack.last;
+  void set _nesting(int value) {
+    _indentStack[_indentStack.length - 1] = value;
+  }
+
+  /// When not `null`, the nesting level of the current inner-most block after
+  /// the next token is written.
+  ///
+  /// When the nesting level is increased, we don't want it to take effect until
+  /// after at least one token has been written. That ensures that comments
+  /// appearing before the first token are correctly indented. For example, a
+  /// binary operator expression increases the nesting before the first operand
+  /// to ensure any splits within the left operand are handled correctly. If we
+  /// changed the nesting level immediately, then code like:
+  ///
+  ///     {
+  ///       // comment
+  ///       foo + bar;
+  ///     }
+  ///
+  /// would incorrectly get indented because the line comment adds a split which
+  /// would take the nesting level of the binary operator into account even
+  /// though we haven't written any of its tokens yet.
+  int _pendingNesting;
+
+  /// The index of the "current" chunk being written.
+  ///
+  /// If the last chunk is still being appended to, this is its index.
+  /// Otherwise, it is the index of the next chunk which will be created.
+  int get _currentChunkIndex {
+    if (_chunks.isEmpty) return 0;
+    if (_chunks.last.canAddText) return _chunks.length - 1;
+    return _chunks.length;
+  }
+
+  /// The offset in [_buffer] where the selection starts in the formatted code.
+  ///
+  /// This will be `null` if there is no selection or the writer hasn't reached
+  /// the beginning of the selection yet.
+  int _selectionStart;
+
+  /// The length in [_buffer] of the selection in the formatted code.
+  ///
+  /// This will be `null` if there is no selection or the writer hasn't reached
+  /// the end of the selection yet.
+  int _selectionLength;
+
+  /// Whether there is pending whitespace that depends on the number of
+  /// newlines in the source.
+  ///
+  /// This is used to avoid calculating the newlines between tokens unless
+  /// actually needed since doing so is slow when done between every single
+  /// token pair.
+  bool get needsToPreserveNewlines =>
+      _pendingWhitespace == Whitespace.oneOrTwoNewlines ||
+      _pendingWhitespace == Whitespace.spaceOrNewline;
+
+  /// The number of characters of code that can fit in a single line.
+  int get pageWidth => _formatter.pageWidth;
+
+  LineWriter(this._formatter, this._source) {
+    indent(_formatter.indent);
+    _beginningIndent = _formatter.indent;
+  }
+
+  /// Writes [string], the text for a single token, to the output.
+  ///
+  /// By default, this also implicitly adds one level of nesting if we aren't
+  /// currently nested at all. We do this here so that if a comment appears
+  /// after any token within a statement or top-level form and that comment
+  /// leads to splitting, we correctly nest. Even pathological cases like:
+  ///
+  ///
+  ///     import // comment
+  ///         "this_gets_nested.dart";
+  ///
+  /// If we didn't do this here, we'd have to call [nestExpression] after the
+  /// first token of practically every grammar production.
+  void write(String string) {
+    _emitPendingWhitespace();
+    _writeText(string);
+
+    if (_pendingNesting != null) {
+      _nesting = _pendingNesting;
+      _pendingNesting = null;
+    }
+  }
+
+  /// Writes a [WhitespaceChunk] of [type].
+  void writeWhitespace(Whitespace type) {
+    _pendingWhitespace = type;
+  }
+
+  /// Write a soft split with its own param at [cost].
+  ///
+  /// If unsplit, it expands to a space if [space] is `true`.
+  ///
+  /// If [cost] is omitted, defaults to [Cost.normal]. Returns the new param.
+  SplitParam writeSplit({int cost, bool space}) {
+    if (cost == null) cost = Cost.normal;
+
+    var param = new SplitParam(cost);
+    _writeSplit(_indent, _nesting, param, spaceWhenUnsplit: space);
+
+    // If a split inside a multisplit is chosen, this forces the multisplit too.
+    // This ensures that, for example, a split inside a collection literal
+    // forces the collection to also go multiline. Since a multisplit's param
+    // also implies *its* surrounding multisplit, this will split the whole
+    // chain of contained multisplits.
+    if (_multisplits.isNotEmpty && _multisplits.last.param != null) {
+      param.implies.add(_multisplits.last.param);
+    }
+
+    return param;
+  }
+
+  /// Outputs the series of [comments] and associated whitespace that appear
+  /// before [token] (which is not written by this).
+  ///
+  /// The list contains each comment as it appeared in the source between the
+  /// last token written and the next one that's about to be written.
+  ///
+  /// [linesBeforeToken] is number of lines between the last comment (or
+  /// previous token if there are no comments) and the next token.
+  void writeComments(List<SourceComment> comments, int linesBeforeToken,
+      String token) {
+    // Corner case: if we require a blank line, but there exists one between
+    // some of the comments, or after the last one, then we don't need to
+    // enforce one before the first comment. Example:
+    //
+    //     library foo;
+    //     // comment
+    //
+    //     class Bar {}
+    //
+    // Normally, a blank line is required after `library`, but since there is
+    // one after the comment, we don't need one before it. This is mainly so
+    // that commented out directives stick with their preceding group.
+    if (_pendingWhitespace == Whitespace.twoNewlines &&
+        comments.isNotEmpty &&
+        comments.first.linesBefore < 2) {
+      if (linesBeforeToken > 1) {
+        _pendingWhitespace = Whitespace.newline;
+      } else {
+        for (var i = 1; i < comments.length; i++) {
+          if (comments[i].linesBefore > 1) {
+            _pendingWhitespace = Whitespace.newline;
+            break;
+          }
+        }
+      }
+    }
+
+    // Write each comment and the whitespace between them.
+    for (var i = 0; i < comments.length; i++) {
+      var comment = comments[i];
+
+      preserveNewlines(comment.linesBefore);
+
+      // Don't emit a space because we'll handle it below. If we emit it here,
+      // we may get a trailing space if the comment needs a line before it.
+      if (_pendingWhitespace == Whitespace.space) _pendingWhitespace = null;
+      _emitPendingWhitespace();
+
+      if (comment.linesBefore == 0) {
+        // If we're sitting on a split, move the comment before it to adhere it
+        // to the preceding text.
+        if (_shouldMoveCommentBeforeSplit()) {
+          _chunks.last.allowText();
+        }
+
+        // The comment follows other text, so we need to decide if it gets a
+        // space before it or not.
+        if (_needsSpaceBeforeComment(isLineComment: comment.isLineComment)) {
+          _writeText(" ");
+        }
+      } else {
+        // The comment starts a line, so make sure it stays on its own line.
+        _writeHardSplit(nest: true, allowIndent: !comment.isStartOfLine,
+            double: comment.linesBefore > 1);
+      }
+
+      _writeText(comment.text);
+
+      if (comment.selectionStart != null) {
+        startSelectionFromEnd(comment.text.length - comment.selectionStart);
+      }
+
+      if (comment.selectionEnd != null) {
+        endSelectionFromEnd(comment.text.length - comment.selectionEnd);
+      }
+
+      // Make sure there is at least one newline after a line comment and allow
+      // one or two after a block comment that has nothing after it.
+      var linesAfter;
+      if (i < comments.length - 1) {
+        linesAfter = comments[i + 1].linesBefore;
+      } else {
+        linesAfter = linesBeforeToken;
+
+        // Always force a newline after multi-line block comments. Prevents
+        // mistakes like:
+        //
+        //     /**
+        //      * Some doc comment.
+        //      */ someFunction() { ... }
+        if (linesAfter == 0 && comments.last.text.contains("\n")) {
+          linesAfter = 1;
+        }
+      }
+
+      if (linesAfter > 0) _writeHardSplit(nest: true, double: linesAfter > 1);
+    }
+
+    // If the comment has text following it (aside from a grouping character),
+    // it needs a trailing space.
+    if (_needsSpaceAfterLastComment(comments, token)) {
+      _pendingWhitespace = Whitespace.space;
+    }
+
+    preserveNewlines(linesBeforeToken);
+  }
+
+  /// If the current pending whitespace allows some source discretion, pins
+  /// that down given that the source contains [numLines] newlines at that
+  /// point.
+  void preserveNewlines(int numLines) {
+    // If we didn't know how many newlines the user authored between the last
+    // token and this one, now we do.
+    switch (_pendingWhitespace) {
+      case Whitespace.spaceOrNewline:
+        if (numLines > 0) {
+          _pendingWhitespace = Whitespace.nestedNewline;
+        } else {
+          _pendingWhitespace = Whitespace.space;
+        }
+        break;
+
+      case Whitespace.oneOrTwoNewlines:
+        if (numLines > 1) {
+          _pendingWhitespace = Whitespace.twoNewlines;
+        } else {
+          _pendingWhitespace = Whitespace.newline;
+        }
+        break;
+    }
+  }
+
+  /// Increases indentation of the next line by [levels].
+  void indent([int levels = 1]) {
+    while (levels-- > 0) _indentStack.add(-1);
+  }
+
+  /// Decreases indentation of the next line by [levels].
+  void unindent([int levels = 1]) {
+    while (levels-- > 0) _indentStack.removeLast();
+  }
+
+  /// Starts a new span with [cost].
+  ///
+  /// Each call to this needs a later matching call to [endSpan].
+  void startSpan([int cost = Cost.normal]) {
+    _openSpans.add(new Span(_currentChunkIndex, cost));
+  }
+
+  /// Ends the innermost span.
+  void endSpan() {
+    var span = _openSpans.removeLast();
+
+    // If the span was discarded while it was still open, just forget about it.
+    if (span == null) return;
+
+    span.close(_currentChunkIndex);
+
+    // A span that just covers a single chunk can't be split anyway.
+    if (span.start == span.end) return;
+    _spans.add(span);
+  }
+
+  /// Starts a new [Multisplit].
+  ///
+  /// Returns the [SplitParam] for the multisplit.
+  SplitParam startMultisplit({bool separable, int cost}) {
+    var multisplit = new Multisplit(_currentChunkIndex,
+        separable: separable, cost: cost);
+    _multisplits.add(multisplit);
+
+    return multisplit.param;
+  }
+
+  /// Adds a new split point for the current innermost [Multisplit].
+  ///
+  /// If [space] is `true`, the chunk will include a space when unsplit. If
+  /// [nest] is `true`, then this split will take into account expression
+  /// nesting. Otherwise, it will not. Collections do not follow expression
+  /// nesting, while other uses of multisplits generally do.
+  void multisplit({bool nest: false, bool space}) {
+    _writeSplit(_indent, nest ? _nesting : -1, _multisplits.last.param,
+        spaceWhenUnsplit: space);
+  }
+
+  /// Ends the innermost multisplit.
+  void endMultisplit() {
+    var multisplit = _multisplits.removeLast();
+
+    // If this multisplit is contained in another one and they didn't already
+    // get hardened, wire them together: if the inner one chooses to split, it
+    // should force the outer one to split too.
+    if (_multisplits.isNotEmpty &&
+        multisplit.param != null &&
+        _multisplits.last.param != null) {
+      multisplit.param.implies.add(_multisplits.last.param);
+    }
+  }
+
+  /// Pre-emptively forces all of the multisplits to become hard splits.
+  ///
+  /// This is called by [SourceVisitor] when it can determine that a multisplit
+  /// will never be satisfied. Turning it into hard splits lets the writer
+  /// break the output into smaller pieces for the line splitter, which helps
+  /// performance and avoids failing on very large input.
+  ///
+  /// In particular, it's easy for the visitor to know that collections with a
+  /// large number of items must split. Doing that early avoids crashing the
+  /// splitter when it tries to recurse on huge collection literals.
+  void preemptMultisplits() => _handleHardSplit();
+
+  /// Increases the level of expression nesting.
+  ///
+  /// Expressions that are more nested will get increased indentation when split
+  /// if the previous line has a lower level of nesting.
+  void nestExpression() {
+    if (_pendingNesting != null) {
+      _pendingNesting++;
+    } else {
+      _pendingNesting = _nesting + 1;
+    }
+  }
+
+  /// Decreases the level of expression nesting.
+  ///
+  /// Expressions that are more nested will get increased indentation when split
+  /// if the previous line has a lower level of nesting.
+  void unnest() {
+    // By the time the nesting is done, it should have emitted some text and
+    // not be pending anymore.
+    assert(_pendingNesting == null);
+
+    _nesting--;
+  }
+
+  /// Marks the selection starting point as occurring [fromEnd] characters to
+  /// the left of the end of what's currently been written.
+  ///
+  /// It counts backwards from the end because this is called *after* the chunk
+  /// of text containing the selection has been output.
+  void startSelectionFromEnd(int fromEnd) {
+    assert(_chunks.isNotEmpty);
+    _chunks.last.startSelectionFromEnd(fromEnd);
+  }
+
+  /// Marks the selection ending point as occurring [fromEnd] characters to the
+  /// left of the end of what's currently been written.
+  ///
+  /// It counts backwards from the end because this is called *after* the chunk
+  /// of text containing the selection has been output.
+  void endSelectionFromEnd(int fromEnd) {
+    assert(_chunks.isNotEmpty);
+    _chunks.last.endSelectionFromEnd(fromEnd);
+  }
+
+  /// Finishes writing and returns a [SourceCode] containing the final output
+  /// and updated selection, if any.
+  SourceCode end() {
+    if (_chunks.isNotEmpty) _completeLine(_chunks.length);
+
+    // Be a good citizen, end with a newline.
+    if (_source.isCompilationUnit) _buffer.write(_formatter.lineEnding);
+
+    // If we haven't hit the beginning and/or end of the selection yet, they
+    // must be at the very end of the code.
+    if (_source.selectionStart != null) {
+      if (_selectionStart == null) {
+        _selectionStart = _buffer.length;
+      }
+
+      if (_selectionLength == null) {
+        _selectionLength = _buffer.length - _selectionStart;
+      }
+    }
+
+    return new SourceCode(_buffer.toString(),
+        uri: _source.uri,
+        isCompilationUnit: _source.isCompilationUnit,
+        selectionStart: _selectionStart,
+        selectionLength: _selectionLength);
+  }
+
+  /// Writes the current pending [Whitespace] to the output, if any.
+  ///
+  /// This should only be called after source lines have been preserved to turn
+  /// any ambiguous whitespace into a concrete choice.
+  void _emitPendingWhitespace() {
+    if (_pendingWhitespace == null) return;
+    // Output any pending whitespace first now that we know it won't be
+    // trailing.
+    switch (_pendingWhitespace) {
+      case Whitespace.space:
+        _writeText(" ");
+        break;
+
+      case Whitespace.newline:
+        _writeHardSplit();
+        break;
+
+      case Whitespace.nestedNewline:
+        _writeHardSplit(nest: true);
+        break;
+
+      case Whitespace.newlineFlushLeft:
+        _writeHardSplit(allowIndent: false);
+        break;
+
+      case Whitespace.twoNewlines:
+        _writeHardSplit(double: true);
+        break;
+
+      case Whitespace.spaceOrNewline:
+      case Whitespace.oneOrTwoNewlines:
+        // We should have pinned these down before getting here.
+        assert(false);
+        break;
+    }
+
+    _pendingWhitespace = null;
+  }
+
+  /// Returns `true` if the last chunk is a split that should be move after the
+  /// comment that is about to be written.
+  bool _shouldMoveCommentBeforeSplit() {
+    // Not if there is nothing before it.
+    if (_chunks.isEmpty) return false;
+
+    // If the text before the split is an open grouping character, we don't
+    // want to adhere the comment to that.
+    var text = _chunks.last.text;
+    return !text.endsWith("(") && !text.endsWith("[") && !text.endsWith("{");
+  }
+
+  /// Returns `true` if a space should be output between the end of the current
+  /// output and the subsequent comment which is about to be written.
+  ///
+  /// This is only called if the comment is trailing text in the unformatted
+  /// source. In most cases, a space will be output to separate the comment
+  /// from what precedes it. This returns false if:
+  ///
+  /// *   This comment does begin the line in the output even if it didn't in
+  ///     the source.
+  /// *   The comment is a block comment immediately following a grouping
+  ///     character (`(`, `[`, or `{`). This is to allow `foo(/* comment */)`,
+  ///     et. al.
+  bool _needsSpaceBeforeComment({bool isLineComment}) {
+    // Not at the start of the file.
+    if (_chunks.isEmpty) return false;
+
+    // Not at the start of a line.
+    if (!_chunks.last.canAddText) return false;
+
+    var text = _chunks.last.text;
+    if (text.endsWith("\n")) return false;
+
+    // Always put a space before line comments.
+    if (isLineComment) return true;
+
+    // Block comments do not get a space if following a grouping character.
+    return !text.endsWith("(") && !text.endsWith("[") && !text.endsWith("{");
+  }
+
+  /// Returns `true` if a space should be output after the last comment which
+  /// was just written and the token that will be written.
+  bool _needsSpaceAfterLastComment(List<SourceComment> comments, String token) {
+    // Not if there are no comments.
+    if (comments.isEmpty) return false;
+
+    // Not at the beginning of a line.
+    if (!_chunks.last.canAddText) return false;
+
+    // Otherwise, it gets a space if the following token is not a delimiter or
+    // the empty string, for EOF.
+    return token != ")" && token != "]" && token != "}" &&
+           token != "," && token != ";" && token != "";
+  }
+
+  /// Appends a hard split with the current indentation and nesting (the latter
+  /// only if [nest] is `true`).
+  ///
+  /// If [double] is `true`, a double-split (i.e. a blank line) is output.
+  ///
+  /// If [allowIndent] is `false, then the split will always cause the next
+  /// line to be at column zero. Otherwise, it uses the normal indentation and
+  /// nesting behavior.
+  void _writeHardSplit({bool nest: false, bool double: false,
+      bool allowIndent: true}) {
+    // A hard split overrides any other whitespace.
+    _pendingWhitespace = null;
+
+    var indent = _indent;
+    var nesting = nest ? _nesting : -1;
+    if (!allowIndent) {
+      indent = 0;
+      nesting = -1;
+    }
+
+    _writeSplit(indent, nesting, null, isDouble: double);
+  }
+
+  /// Ends the current chunk (if any) with the given split information.
+  void _writeSplit(int indent, int nesting, SplitParam param,
+      {bool isDouble, bool spaceWhenUnsplit}) {
+    if (_chunks.isEmpty) return;
+
+    _chunks.last.applySplit(indent, nesting, param,
+        isDouble: isDouble, spaceWhenUnsplit: spaceWhenUnsplit);
+
+    if (_chunks.last.isHardSplit) _handleHardSplit();
+  }
+
+  /// Writes [text] to either the current chunk or a new one if the current
+  /// chunk is complete.
+  void _writeText(String text) {
+    if (_chunks.isEmpty) {
+      _chunks.add(new Chunk(text));
+    } else if (_chunks.last.canAddText) {
+      _chunks.last.appendText(text);
+    } else {
+      // Since we're about to write some text on the next line, we know the
+      // previous one is fully done being tweaked and merged, so now we can see
+      // if it can be split independently.
+       _checkForCompleteLine(_chunks.length);
+
+      _chunks.add(new Chunk(text));
+    }
+  }
+
+  /// Checks to see if we the first [length] chunks can be processed as a
+  /// single line and processes them if so.
+  ///
+  /// We want to send small lists of chunks to [LineSplitter] for performance.
+  /// We can do that when we know one set of chunks will absolutely not affect
+  /// anything following it. The rule for that is pretty simple: a hard newline
+  /// that is not nested inside an expression.
+  bool _checkForCompleteLine(int length) {
+    if (length == 0) return false;
+
+    // Hang on to the split info so we can reset the writer to start with it.
+    var split = _chunks[length - 1];
+
+    // Can only split on a hard line that is not nested in the middle of an
+    // expression.
+    if (!split.isHardSplit || split.nesting >= 0) return false;
+
+    _completeLine(length);
+
+    // Discard the formatted chunks and any spans contained in them.
+    _chunks.removeRange(0, length);
+    _spans.removeWhere((span) => span.shift(length));
+
+    // Get ready for the next line.
+    _bufferedNewlines = split.isDouble ? 2 : 1;
+    _beginningIndent = split.indent;
+
+    return true;
+  }
+
+  /// Hands off the first [length] chunks to the [LineSplitter] as a single
+  /// logical line to be split.
+  void _completeLine(int length) {
+    assert(_chunks.isNotEmpty);
+
+    // Write the newlines required by the previous line.
+    for (var i = 0; i < _bufferedNewlines; i++) {
+      _buffer.write(_formatter.lineEnding);
+    }
+
+    // If we aren't completing the entire set of chunks, get the subset that we
+    // are completing.
+    var chunks = _chunks;
+    var spans = _spans;
+
+    if (length < _chunks.length) {
+      chunks = chunks.take(length).toList();
+      spans = spans.where((span) => span.start <= length).toList();
+    }
+
+    if (debugFormatter) {
+      dumpChunks(chunks);
+      print(spans.join("\n"));
+    }
+
+    var splitter = new LineSplitter(_formatter.lineEnding, _formatter.pageWidth,
+        chunks, spans, _beginningIndent);
+    var selection = splitter.apply(_buffer);
+
+    if (selection[0] != null) _selectionStart = selection[0];
+    if (selection[1] != null) _selectionLength = selection[1] - _selectionStart;
+  }
+
+  /// Handles open multisplits and spans when a hard line occurs.
+  ///
+  /// Any active separable multisplits will get split in two at this point.
+  /// Other multisplits are forced into the "hard" state. All of their previous
+  /// splits are turned into explicit hard splits and any new splits for that
+  /// multisplit become hard splits too.
+  ///
+  /// All open spans get discarded since they will never be satisfied.
+  void _handleHardSplit() {
+    // Discard all open spans. We don't remove them from the stack so that we
+    // can still correctly count later calls to endSpan().
+    for (var i = 0; i < _openSpans.length; i++) {
+      _openSpans[i] = null;
+    }
+
+    if (_multisplits.isEmpty) return;
+
+    var splitParams = new Set();
+
+    // Add [param] and the transitive closure of its implied params to
+    // [splitParams].
+    traverseParams(param) {
+      splitParams.add(param);
+
+      // Traverse the tree of implied params.
+      param.implies.forEach(traverseParams);
+    }
+
+    for (var multisplit in _multisplits) {
+      // If this multisplit isn't separable or already split, we need to harden
+      // all of its previous splits now.
+      var param = multisplit.harden();
+      if (param != null) traverseParams(param);
+    }
+
+    if (splitParams.isEmpty) return;
+
+    // Take any existing splits for the multisplits and hard split them.
+    for (var i = 0; i < _chunks.length; i++) {
+      var chunk = _chunks[i];
+      if (chunk.param == null) continue;
+
+      if (splitParams.contains(chunk.param)) {
+        chunk.harden();
+
+        // Now that this chunk is a hard split, we may be able to format up to
+        // it as its own line. If so, the chunks will get removed, so reset
+        // the loop counter.
+        if (_checkForCompleteLine(i + 1)) i = -1;
+      } else {
+        // If the chunk isn't hardened, but implies something that is, we can
+        // discard the implication since it is always satisfied now.
+        chunk.param.implies.removeWhere(splitParams.contains);
+      }
+    }
+  }
+}
diff --git a/dart_style/lib/src/multisplit.dart b/dart_style/lib/src/multisplit.dart
new file mode 100644
index 0000000..6ad598d
--- /dev/null
+++ b/dart_style/lib/src/multisplit.dart
@@ -0,0 +1,93 @@
+// 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 dart_style.src.multisplit;
+
+import 'chunk.dart';
+
+/// Handles a series of [Chunks] that all either split or don't split together.
+///
+/// This is used for:
+///
+/// * Map and list literals.
+/// * A series of the same binary operator.
+/// * A series of chained method calls.
+/// * Cascades.
+/// * The beginning and ending of curly brace bodies.
+///
+/// In all of these, either the entire construct will be a single line, or it
+/// will be fully split into multiple lines, with no intermediate states
+/// allowed.
+///
+/// There is still the question of how a multisplit handles an explicit newline
+/// (usually from a function literal subexpression) contained within the
+/// multisplit. There are two variations: separable and inseparable. Most are
+/// the latter.
+///
+/// An inseparable multisplit treats a hard newline as forcing the entire
+/// multisplit to split, like so:
+///
+///     [
+///       () {
+///         // This forces the surrounding list to be split.
+///       }
+///     ]
+///
+/// A separable one breaks the multisplit into two independent multisplits, each
+/// of which may or may not be split based on its own range. For example:
+///
+///     compiler
+///         .somethingLong()
+///         .somethingLong()
+///         .somethingLong((_) {
+///       // The calls above this split because they are long.
+///     }).a().b();
+///     The trailing calls are short enough to not split.
+class Multisplit {
+  /// The index of the first chunk contained by the multisplit.
+  ///
+  /// This is used to determine which chunk range needs to be scanned to look
+  /// for hard newlines to see if the multisplit gets forced.
+  final int startChunk;
+
+  /// The [SplitParam] that controls all of the split chunks.
+  SplitParam get param => _param;
+  SplitParam _param;
+
+  /// `true` if a hard newline has forced this multisplit to be split.
+  bool _isSplit = false;
+
+  final bool _separable;
+
+  Multisplit(this.startChunk, {bool separable, int cost})
+      : _separable = separable != null ? separable : false,
+        _param = new SplitParam(cost);
+
+  /// Handles a hard split occurring in the middle of this multisplit.
+  ///
+  /// If the multisplit is separable, this creates a new param so the previous
+  /// split chunks can vary independently of later ones. Otherwise, it just
+  /// marks this multisplit as being split.
+  ///
+  /// Returns a [SplitParam] for existing splits that should be hardened if this
+  /// splits a non-separable multisplit for the first time. Otherwise, returns
+  /// `null`.
+  SplitParam harden() {
+    if (_isSplit) return null;
+
+    _isSplit = true;
+
+    if (_separable) {
+      _param = new SplitParam(_param.cost);
+
+      // Previous splits may still remain unsplit.
+      return null;
+    } else {
+      // Any other splits created from this multisplit should be hardened now.
+      var oldParam = _param;
+      _param = null;
+      return oldParam;
+    }
+  }
+}
diff --git a/dart_style/lib/src/nesting.dart b/dart_style/lib/src/nesting.dart
new file mode 100644
index 0000000..156f2e9
--- /dev/null
+++ b/dart_style/lib/src/nesting.dart
@@ -0,0 +1,215 @@
+// 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 dart_style.src.nesting;
+
+import 'chunk.dart';
+import 'line_splitter.dart';
+
+/// Maintains a stack of nested expressions that have currently been split.
+///
+/// A single statement may have multiple different levels of indentation based
+/// on the expression nesting level at the point where the line is broken. For
+/// example:
+///
+///     someFunction(argument, argument,
+///         innerFunction(argument,
+///             innermost), argument);
+///
+/// This means that when splitting a line, we need to keep track of the nesting
+/// level of the previous line(s) to determine how far the next line must be
+/// indented.
+///
+/// This class is a persistent collection. Each instance is immutable and
+/// methods to modify it return a new collection.
+class NestingStack {
+  /// The number of visible indentation levels for the current nesting.
+  ///
+  /// This may be less than [_depth] since split lines can skip multiple
+  /// nesting depths.
+  final int indent;
+
+  final NestingStack _parent;
+
+  /// The number of surrounding expression nesting levels.
+  final int _depth;
+
+  NestingStack() : this._(null, -1, 0);
+
+  NestingStack._(this._parent, this._depth, this.indent);
+
+  /// LinePrefixes implement their own value equality to ensure that two
+  /// prefixes with the same nesting stack are considered equal even if the
+  /// nesting occurred from different splits.
+  ///
+  /// For example, consider these two prefixes with `^` marking where splits
+  /// have been applied:
+  ///
+  ///     fn( first, second, ...
+  ///        ^
+  ///     fn( first, second, ...
+  ///               ^
+  ///
+  /// These are equivalent from the view of the suffix because they have the
+  /// same nesting stack, even though the nesting came from different tokens.
+  /// This lets us reuse memoized suffixes more frequently when solving.
+  bool operator ==(other) {
+    if (other is! NestingStack) return false;
+
+    var self = this;
+    while (self != null) {
+      if (self._depth != other._depth) return false;
+      self = self._parent;
+      other = other._parent;
+
+      // They should be the same length.
+      if ((self == null) != (other == null)) return false;
+    }
+
+    return true;
+  }
+
+  int get hashCode {
+    // TODO(rnystrom): Is it worth iterating throught the stack?
+    return indent.hashCode ^ _depth.hashCode;
+  }
+
+  /// Takes this nesting stack and produces all of the new nesting stacks that
+  /// are possible when followed by the nesting level of [split].
+  ///
+  /// This may produce multiple solutions because a non-incremental jump in
+  /// nesting depth can be sliced up multiple ways. Let's say the prefix is:
+  ///
+  ///     first(second(third(...
+  ///
+  /// The current nesting stack is empty (since we're on the first line). How
+  /// do we modify it by taking into account the split after `third(`? The
+  /// simple answer is to just increase the indentation by one level:
+  ///
+  ///     first(second(third(
+  ///         argumentToThird)));
+  ///
+  /// This is correct in most cases, but not all. Consider:
+  ///
+  ///     first(second(third(
+  ///         argumentToThird),
+  ///     argumentToSecond));
+  ///
+  /// Oops! There's no place for `argumentToSecond` to go. To handle that, the
+  /// second line needs to be indented one more level to make room for the later
+  /// line:
+  ///
+  ///     first(second(third(
+  ///             argumentToThird),
+  ///         argumentToSecond));
+  ///
+  /// It's even possible we may need to do:
+  ///
+  ///     first(second(third(
+  ///                 argumentToThird),
+  ///             argumentToSecond),
+  ///         argumentToFirst);
+  ///
+  /// To accommodate those, this returns the list of all possible ways the
+  /// nesting stack can be modified. It generates the in order of best to worst
+  /// so that the line splitter can stop as soon as it finds a working solution.
+  /// "Best" here means it tries fewer levels of indentation first.
+  List<NestingStack> applySplit(Chunk split) {
+    assert(split.isInExpression);
+
+    if (split.nesting == _depth) return [this];
+
+    // If the new split is less nested than we currently are, pop and discard
+    // the previous nesting levels.
+    if (split.nesting < _depth) {
+      // Pop items off the stack until we find the level we are now at.
+      var stack = this;
+      while (stack != null) {
+        if (stack._depth == split.nesting) return [stack];
+        stack = stack._parent;
+      }
+
+      // If we got here, the level wasn't found. That means there is no correct
+      // stack level to pop to, since the stack skips past our indentation
+      // level.
+      return [];
+    }
+
+    // TODO(rnystrom): This eagerly generates all of the nesting stacks even
+    // though LineSplitter._findBestSplits() will early out of looping over
+    // them. Optimize by generating these only as needed.
+
+    // Going deeper, so try every indentating for every subset of expression
+    // nesting levels between the old and new one.
+    return _intermediateDepths(_depth, split.nesting).map((depths) {
+      var result = this;
+
+      for (var depth in depths) {
+        result = new NestingStack._(
+            result, depth, result.indent + indentsPerNest);
+      }
+
+      return new NestingStack._(
+          result, split.nesting, result.indent + indentsPerNest);
+    }).toList();
+  }
+
+  /// Given [min] and [max], generates all of the subsets of numbers in that
+  /// range (exclusive), including the empty set.
+  ///
+  /// This is used for determine what sets of intermediate nesting levels to
+  /// consider when jumping from a shallow nesting level to a much deeper one.
+  /// Subsets are generated in order of increasing length. For example, `(2, 6)`
+  /// yields:
+  ///
+  ///     []
+  ///     [3] [4] [5]
+  ///     [3, 4] [3, 5] [4, 5]
+  ///     [3, 4, 5]
+  ///
+  /// This ensures the splitter prefers solutions that use the least
+  /// indentation.
+  List<List<int>> _intermediateDepths(int min, int max) {
+    assert(min < max);
+
+    var subsets = [[]];
+
+    var lastLengthStart = 0;
+    var lastLengthEnd = subsets.length;
+
+    // Generate subsets in order of increasing length.
+    for (var length = 1; length <= max - min + 1; length++) {
+      // Start with each subset containing one fewer element.
+      for (var i = lastLengthStart; i < lastLengthEnd; i++) {
+        var previousSubset = subsets[i];
+
+        var start = previousSubset.isNotEmpty ? previousSubset.last + 1 : min + 1;
+
+        // Then for each value in the remainer, make a new subset that is the
+        // union of the shorter subset and that value.
+        for (var j = start; j < max; j++) {
+          var subset = previousSubset.toList()..add(j);
+          subsets.add(subset);
+        }
+      }
+
+      // Move on to the next length range.
+      lastLengthStart = lastLengthEnd;
+      lastLengthEnd = subsets.length;
+    }
+
+    return subsets;
+  }
+
+  String toString() {
+    var nesting = this;
+    var levels = [];
+    while (nesting != null) {
+      levels.add("${nesting._depth}:${nesting.indent}");
+      nesting = nesting._parent;
+    }
+
+    return levels.join(" ");
+  }
+}
diff --git a/dart_style/lib/src/source_code.dart b/dart_style/lib/src/source_code.dart
new file mode 100644
index 0000000..795359f
--- /dev/null
+++ b/dart_style/lib/src/source_code.dart
@@ -0,0 +1,81 @@
+// 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 dart_style.src.source_code;
+
+/// Describes a chunk of source code that is to be formatted or has been
+/// formatted.
+class SourceCode {
+  /// The [uri] where the source code is from.
+  ///
+  /// Used in error messages if the code cannot be parsed.
+  final String uri;
+
+  /// The Dart source code text.
+  final String text;
+
+  /// Whether the source is a compilation unit or a bare statement.
+  final bool isCompilationUnit;
+
+  /// The offset in [text] where the selection begins, or `null` if there is
+  /// no selection.
+  final int selectionStart;
+
+  /// The number of selected characters or `null` if there is no selection.
+  final int selectionLength;
+
+  /// Gets the source code before the beginning of the selection.
+  ///
+  /// If there is no selection, returns [text].
+  String get textBeforeSelection {
+    if (selectionStart == null) return text;
+    return text.substring(0, selectionStart);
+  }
+
+  /// Gets the selected source code, if any.
+  ///
+  /// If there is no selection, returns an empty string.
+  String get selectedText {
+    if (selectionStart == null) return "";
+    return text.substring(selectionStart, selectionStart + selectionLength);
+  }
+
+  /// Gets the source code following the selection.
+  ///
+  /// If there is no selection, returns an empty string.
+  String get textAfterSelection {
+    if (selectionStart == null) return "";
+    return text.substring(selectionStart + selectionLength);
+  }
+
+  SourceCode(this.text,
+      {this.uri, this.isCompilationUnit: true, this.selectionStart,
+      this.selectionLength}) {
+    // Must either provide both selection bounds or neither.
+    if ((selectionStart == null) != (selectionLength == null)) {
+      throw new ArgumentError(
+          "Is selectionStart is provided, selectionLength must be too.");
+    }
+
+    if (selectionStart != null) {
+      if (selectionStart < 0) {
+        throw new ArgumentError("selectionStart must be non-negative.");
+      }
+
+      if (selectionStart > text.length) {
+        throw new ArgumentError("selectionStart must be within text.");
+      }
+    }
+
+    if (selectionLength != null) {
+      if (selectionLength < 0) {
+        throw new ArgumentError("selectionLength must be non-negative.");
+      }
+
+      if (selectionStart + selectionLength > text.length) {
+        throw new ArgumentError("selectionLength must end within text.");
+      }
+    }
+  }
+}
diff --git a/dart_style/lib/src/source_visitor.dart b/dart_style/lib/src/source_visitor.dart
new file mode 100644
index 0000000..bbeeafd
--- /dev/null
+++ b/dart_style/lib/src/source_visitor.dart
@@ -0,0 +1,2000 @@
+// 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 dart_style.src.source_visitor;
+
+import 'package:analyzer/analyzer.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+import 'package:analyzer/src/generated/source.dart';
+
+import 'dart_formatter.dart';
+import 'chunk.dart';
+import 'line_writer.dart';
+import 'source_code.dart';
+import 'whitespace.dart';
+
+/// An AST visitor that drives formatting heuristics.
+class SourceVisitor implements AstVisitor {
+  final DartFormatter _formatter;
+
+  /// The writer to which the output lines are written.
+  final LineWriter _writer;
+
+  /// Cached line info for calculating blank lines.
+  LineInfo _lineInfo;
+
+  /// The source being formatted.
+  final SourceCode _source;
+
+  /// `true` if the visitor has written past the beginning of the selection in
+  /// the original source text.
+  bool _passedSelectionStart = false;
+
+  /// `true` if the visitor has written past the end of the selection in the
+  /// original source text.
+  bool _passedSelectionEnd = false;
+
+  /// The character offset of the end of the selection, if there is a selection.
+  ///
+  /// This is calculated and cached by [_findSelectionEnd].
+  int _selectionEnd;
+
+  /// Initialize a newly created visitor to write source code representing
+  /// the visited nodes to the given [writer].
+  SourceVisitor(formatter, this._lineInfo, SourceCode source)
+      : _formatter = formatter,
+        _source = source,
+        _writer = new LineWriter(formatter, source);
+
+  /// Runs the visitor on [node], formatting its contents.
+  ///
+  /// Returns a [SourceCode] containing the resulting formatted source and
+  /// updated selection, if any.
+  ///
+  /// This is the only method that should be called externally. Everything else
+  /// is effectively private.
+  SourceCode run(AstNode node) {
+    visit(node);
+
+    // Output trailing comments.
+    writePrecedingCommentsAndNewlines(node.endToken.next);
+
+    // Finish writing and return the complete result.
+    return _writer.end();
+  }
+
+  visitAdjacentStrings(AdjacentStrings node) {
+    visitNodes(node.strings, between: spaceOrNewline);
+  }
+
+  visitAnnotation(Annotation node) {
+    token(node.atSign);
+    visit(node.name);
+    token(node.period);
+    visit(node.constructorName);
+    visit(node.arguments);
+  }
+
+  /// Visits an argument list.
+  ///
+  /// This is a bit complex to handle the rules for formatting positional and
+  /// named arguments. The goals, in rough order of descending priority are:
+  ///
+  /// 1. Keep everything on the first line.
+  /// 2. Keep the named arguments together on the next line.
+  /// 3. Keep everything together on the second line.
+  /// 4. Split between one or more positional arguments, trying to keep as many
+  ///    on earlier lines as possible.
+  /// 5. Split the named arguments each onto their own line.
+  visitArgumentList(ArgumentList node) {
+    // Don't allow any splitting in an empty argument list.
+    if (node.arguments.isEmpty &&
+        node.rightParenthesis.precedingComments == null) {
+      token(node.leftParenthesis);
+      token(node.rightParenthesis);
+      return;
+    }
+
+    // If there is just one positional argument, it tends to look weird to
+    // split before it, so try not to.
+    var singleArgument = node.arguments.length == 1 &&
+        node.arguments.single is ! NamedExpression;
+    if (singleArgument) _writer.startSpan();
+
+    // Nest around the parentheses in case there are comments before or after
+    // them.
+    _writer.nestExpression();
+
+    token(node.leftParenthesis);
+
+    // Corner case: If the first argument to a method is a block-bodied
+    // function, it looks bad if its parameter list gets wrapped to the next
+    // line. Bump the cost to try to avoid that. This prefers:
+    //
+    //     receiver
+    //         .method()
+    //         .chain((parameter, list) {
+    //       ...
+    //     });
+    //
+    // over:
+    //
+    //     receiver.method().chain(
+    //         (parameter, list) {
+    //       ...
+    //     });
+    // TODO(rnystrom): This causes a function expression's long parameter list
+    // to get split instead, like:
+    //
+    //     receiver.method((longParameter,
+    //         anotherParameter) {
+    //       ...
+    //     });
+    //
+    // Instead of bumping the cost, this should wrap a span around the "("
+    // before the argument list and the function's parameter list. That requires
+    // spans to not strictly be a stack, though, so would be a larger change
+    // than I want to do right now.
+    var cost = Cost.normal;
+    if (node.arguments.isNotEmpty) {
+      var firstArg = node.arguments.first;
+      if (firstArg is FunctionExpression &&
+          firstArg.body is BlockFunctionBody) {
+        cost = Cost.firstBlockArgument;
+      }
+    }
+
+    // Allow splitting after "(".
+    var lastParam = zeroSplit(cost);
+
+    // Try to keep the positional arguments together.
+    _writer.startSpan(Cost.positionalArguments);
+
+    var i = 0;
+    for (; i < node.arguments.length; i++) {
+      var argument = node.arguments[i];
+
+      if (argument is NamedExpression) break;
+
+      visit(argument);
+
+      // Write the trailing comma and split.
+      if (i < node.arguments.length - 1) {
+        token(argument.endToken.next);
+
+        // If there are both positional and named arguments, only try to keep
+        // the positional ones together.
+        if (node.arguments[i + 1] is NamedExpression) _writer.endSpan();
+
+        // Positional arguments split independently.
+        lastParam = split();
+      }
+    }
+
+    // If there are named arguments, write them.
+    if (i < node.arguments.length) {
+      // Named arguments all split together, but not before the first. This
+      // allows all of the named arguments to get pushed to the next line, but
+      // stay together.
+      var multisplitParam = _writer.startMultisplit(separable: true);
+
+      // However, if they *do* all split, we want to split before the first one
+      // too. This disallows:
+      //
+      //     method(first: 1,
+      //         second: 2,
+      //         third: 3);
+      multisplitParam.implies.add(lastParam);
+
+      for (; i < node.arguments.length; i++) {
+        var argument = node.arguments[i];
+
+        visit(argument);
+
+        // Write the trailing comma and split.
+        if (i < node.arguments.length - 1) {
+          token(argument.endToken.next);
+
+          _writer.multisplit(nest: true, space: true);
+        }
+      }
+
+      token(node.rightParenthesis);
+
+      // If there were no positional arguments, the span covers the named ones,
+      // so end it here.
+      if (node.arguments.first is NamedExpression) _writer.endSpan();
+
+      _writer.endMultisplit();
+    } else {
+      token(node.rightParenthesis);
+
+      // Keep the positional span past the ")" to include comments after the
+      // last argument.
+      _writer.endSpan();
+    }
+
+    if (singleArgument) _writer.endSpan();
+    _writer.unnest();
+  }
+
+  visitAsExpression(AsExpression node) {
+    visit(node.expression);
+    space();
+    token(node.asOperator);
+    space();
+    visit(node.type);
+  }
+
+  visitAssertStatement(AssertStatement node) {
+    _simpleStatement(node, () {
+      token(node.keyword);
+      token(node.leftParenthesis);
+      zeroSplit();
+      visit(node.condition);
+      token(node.rightParenthesis);
+    });
+  }
+
+  visitAssignmentExpression(AssignmentExpression node) {
+    visit(node.leftHandSide);
+    space();
+    token(node.operator);
+    split(Cost.assignment);
+    _writer.startSpan();
+    visit(node.rightHandSide);
+    _writer.endSpan();
+  }
+
+  visitAwaitExpression(AwaitExpression node) {
+    token(node.awaitKeyword);
+    space();
+    visit(node.expression);
+  }
+
+  visitBinaryExpression(BinaryExpression node) {
+    _writer.startMultisplit(separable: true);
+    _writer.startSpan();
+    _writer.nestExpression();
+
+    // Note that we have the full precedence table here even though some
+    // operators are not associative and so can never chain. In particular,
+    // Dart does not allow sequences of comparison or equality operators.
+    const operatorPrecedences = const {
+      // Multiplicative.
+      TokenType.STAR: 13,
+      TokenType.SLASH: 13,
+      TokenType.TILDE_SLASH: 13,
+      TokenType.PERCENT: 13,
+
+      // Additive.
+      TokenType.PLUS: 12,
+      TokenType.MINUS: 12,
+
+      // Shift.
+      TokenType.LT_LT: 11,
+      TokenType.GT_GT: 11,
+
+      // "&".
+      TokenType.AMPERSAND: 10,
+
+      // "^".
+      TokenType.CARET: 9,
+
+      // "|".
+      TokenType.BAR: 8,
+
+      // Relational.
+      TokenType.LT: 7,
+      TokenType.GT: 7,
+      TokenType.LT_EQ: 7,
+      TokenType.GT_EQ: 7,
+      // Note: as, is, and is! have the same precedence but are not handled
+      // like regular binary operators since they aren't associative.
+
+      // Equality.
+      TokenType.EQ_EQ: 6,
+      TokenType.BANG_EQ: 6,
+
+      // Logical and.
+      TokenType.AMPERSAND_AMPERSAND: 5,
+
+      // Logical or.
+      TokenType.BAR_BAR: 4,
+    };
+
+    // Flatten out a tree/chain of the same precedence. If we split on this
+    // precedence level, we will break all of them.
+    var precedence = operatorPrecedences[node.operator.type];
+    assert(precedence != null);
+
+    traverse(Expression e) {
+      if (e is BinaryExpression &&
+          operatorPrecedences[e.operator.type] == precedence) {
+        assert(operatorPrecedences[e.operator.type] != null);
+
+        traverse(e.leftOperand);
+
+        space();
+        token(e.operator);
+        _writer.multisplit(space: true, nest: true);
+
+        traverse(e.rightOperand);
+      } else {
+        visit(e);
+      }
+    }
+
+    traverse(node);
+
+    _writer.unnest();
+    _writer.endSpan();
+    _writer.endMultisplit();
+  }
+
+  visitBlock(Block node) {
+    _startBody(node.leftBracket);
+
+    visitNodes(node.statements, between: oneOrTwoNewlines, after: newline);
+
+    _endBody(node.rightBracket);
+  }
+
+  visitBlockFunctionBody(BlockFunctionBody node) {
+    // The "async" or "sync" keyword.
+    token(node.keyword);
+
+    // The "*" in "async*" or "sync*".
+    token(node.star);
+    if (node.keyword != null) space();
+
+    visit(node.block);
+  }
+
+  visitBooleanLiteral(BooleanLiteral node) {
+    token(node.literal);
+  }
+
+  visitBreakStatement(BreakStatement node) {
+    _simpleStatement(node, () {
+      token(node.keyword);
+      visit(node.label, before: space);
+    });
+  }
+
+  visitCascadeExpression(CascadeExpression node) {
+    visit(node.target);
+
+    _writer.indent();
+
+    // If the cascade sections have consistent names they can be broken
+    // normally otherwise they always get their own line.
+    if (_allowInlineCascade(node.cascadeSections)) {
+      _writer.startMultisplit();
+      _writer.multisplit();
+      visitNodes(node.cascadeSections, between: _writer.multisplit);
+      _writer.endMultisplit();
+    } else {
+      newline();
+      visitNodes(node.cascadeSections, between: newline);
+    }
+
+    _writer.unindent();
+  }
+
+  /// Whether a cascade should be allowed to be inline as opposed to one
+  /// expression per line.
+  bool _allowInlineCascade(List<Expression> sections) {
+    if (sections.length < 2) return true;
+
+    var name;
+    // We could be more forgiving about what constitutes sections with
+    // consistent names but for now we require all sections to have the same
+    // method name.
+    for (var expression in sections) {      
+      if (expression is! MethodInvocation) return false;
+      if (name == null) {
+        name = expression.methodName.name;
+      } else if (name != expression.methodName.name) {
+        return false;
+      }
+    }   
+    return true;
+  }
+
+  visitCatchClause(CatchClause node) {
+    token(node.onKeyword, after: space);
+    visit(node.exceptionType);
+
+    if (node.catchKeyword != null) {
+      if (node.exceptionType != null) {
+        space();
+      }
+      token(node.catchKeyword);
+      space();
+      token(node.leftParenthesis);
+      visit(node.exceptionParameter);
+      token(node.comma, after: space);
+      visit(node.stackTraceParameter);
+      token(node.rightParenthesis);
+      space();
+    } else {
+      space();
+    }
+    visit(node.body);
+  }
+
+  visitClassDeclaration(ClassDeclaration node) {
+    visitDeclarationMetadata(node.metadata);
+
+    _writer.nestExpression();
+    modifier(node.abstractKeyword);
+    token(node.classKeyword);
+    space();
+    visit(node.name);
+    visit(node.typeParameters);
+    visit(node.extendsClause);
+    visit(node.withClause);
+    visit(node.implementsClause);
+    visit(node.nativeClause, before: space);
+    space();
+
+    _writer.unnest();
+    _startBody(node.leftBracket);
+
+    visitNodes(node.members, between: oneOrTwoNewlines, after: newline);
+
+    _endBody(node.rightBracket);
+  }
+
+  visitClassTypeAlias(ClassTypeAlias node) {
+    visitDeclarationMetadata(node.metadata);
+
+    _simpleStatement(node, () {
+      modifier(node.abstractKeyword);
+      token(node.keyword);
+      space();
+      visit(node.name);
+      visit(node.typeParameters);
+      space();
+      token(node.equals);
+      space();
+      visit(node.superclass);
+      visit(node.withClause);
+      visit(node.implementsClause);
+    });
+  }
+
+  visitComment(Comment node) => null;
+
+  visitCommentReference(CommentReference node) => null;
+
+  visitCompilationUnit(CompilationUnit node) {
+    visit(node.scriptTag);
+
+    // Put a blank line between the library tag and the other directives.
+    var directives = node.directives;
+    if (directives.isNotEmpty && directives.first is LibraryDirective) {
+      visit(directives.first);
+      twoNewlines();
+
+      directives = directives.skip(1);
+    }
+
+    visitNodes(directives, between: oneOrTwoNewlines);
+    visitNodes(node.declarations,
+        before: twoNewlines, between: oneOrTwoNewlines);
+  }
+
+  visitConditionalExpression(ConditionalExpression node) {
+    _writer.nestExpression();
+    visit(node.condition);
+
+    _writer.startSpan();
+
+    // If we split after one clause in a conditional, always split after both.
+    _writer.startMultisplit();
+    _writer.multisplit(nest: true, space: true);
+    token(node.question);
+    space();
+
+    _writer.nestExpression();
+    visit(node.thenExpression);
+    _writer.unnest();
+
+    _writer.multisplit(nest: true, space: true);
+    token(node.colon);
+    space();
+
+    visit(node.elseExpression);
+
+    _writer.endMultisplit();
+    _writer.endSpan();
+    _writer.unnest();
+  }
+
+  visitConstructorDeclaration(ConstructorDeclaration node) {
+    visitMemberMetadata(node.metadata);
+
+    modifier(node.externalKeyword);
+    modifier(node.constKeyword);
+    modifier(node.factoryKeyword);
+    visit(node.returnType);
+    token(node.period);
+    visit(node.name);
+
+    // Start a multisplit that spans the parameter list. We'll use this for the
+    // split before ":" to ensure that if the parameter list doesn't fit on one
+    // line that the initialization list gets pushed to its own line too.
+    if (node.initializers.length == 1) _writer.startMultisplit();
+
+    _visitBody(node.parameters, node.body, () {
+      // Check for redirects or initializer lists.
+      if (node.redirectedConstructor != null) {
+        _visitConstructorRedirects(node);
+      } else if (node.initializers.isNotEmpty) {
+        _visitConstructorInitializers(node);
+      }
+    });
+  }
+
+  void _visitConstructorInitializers(ConstructorDeclaration node) {
+    _writer.indent(2);
+
+    if (node.initializers.length == 1) {
+      // If there is only a single initializer, allow it on the first line, but
+      // only if the parameter list also fit all one line.
+      _writer.multisplit(space: true);
+      _writer.endMultisplit();
+    } else {
+      newline();
+    }
+
+    token(node.separator); // ":".
+    space();
+
+    for (var i = 0; i < node.initializers.length; i++) {
+      if (i > 0) {
+        // Preceding comma.
+        token(node.initializers[i].beginToken.previous);
+
+        // Indent subsequent fields one more so they line up with the first
+        // field following the ":":
+        //
+        // Foo()
+        //     : first,
+        //       second;
+        if (i == 1) _writer.indent();
+        newline();
+      }
+
+      node.initializers[i].accept(this);
+    }
+
+    // If there were multiple fields, discard their extra indentation.
+    if (node.initializers.length > 1) _writer.unindent();
+
+    _writer.unindent(2);
+  }
+
+  void _visitConstructorRedirects(ConstructorDeclaration node) {
+    token(node.separator /* = */, before: space, after: space);
+    visitCommaSeparatedNodes(node.initializers);
+    visit(node.redirectedConstructor);
+  }
+
+  visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+    token(node.keyword);
+    token(node.period);
+    visit(node.fieldName);
+    space();
+    token(node.equals);
+    space();
+    visit(node.expression);
+  }
+
+  visitConstructorName(ConstructorName node) {
+    visit(node.type);
+    token(node.period);
+    visit(node.name);
+  }
+
+  visitContinueStatement(ContinueStatement node) {
+    _simpleStatement(node, () {
+      token(node.keyword);
+      visit(node.label, before: space);
+    });
+  }
+
+  visitDeclaredIdentifier(DeclaredIdentifier node) {
+    modifier(node.keyword);
+    visit(node.type, after: space);
+    visit(node.identifier);
+  }
+
+  visitDefaultFormalParameter(DefaultFormalParameter node) {
+    visit(node.parameter);
+    if (node.separator != null) {
+      // The '=' separator is preceded by a space.
+      if (node.separator.type == TokenType.EQ) space();
+      token(node.separator);
+      visit(node.defaultValue, before: space);
+    }
+  }
+
+  visitDoStatement(DoStatement node) {
+    _simpleStatement(node, () {
+      token(node.doKeyword);
+      space();
+      visit(node.body);
+      space();
+      token(node.whileKeyword);
+      space();
+      token(node.leftParenthesis);
+      zeroSplit();
+      visit(node.condition);
+      token(node.rightParenthesis);
+    });
+  }
+
+  visitDoubleLiteral(DoubleLiteral node) {
+    token(node.literal);
+  }
+
+  visitEmptyFunctionBody(EmptyFunctionBody node) {
+    token(node.semicolon);
+  }
+
+  visitEmptyStatement(EmptyStatement node) {
+    token(node.semicolon);
+  }
+
+  visitEnumConstantDeclaration(EnumConstantDeclaration node) {
+    visit(node.name);
+  }
+
+  visitEnumDeclaration(EnumDeclaration node) {
+    visitDeclarationMetadata(node.metadata);
+
+    token(node.keyword);
+    space();
+    visit(node.name);
+    space();
+
+    _startBody(node.leftBracket, space: true);
+
+    visitCommaSeparatedNodes(node.constants, between: () {
+      _writer.multisplit(space: true);
+    });
+
+    _endBody(node.rightBracket, space: true);
+  }
+
+  visitExportDirective(ExportDirective node) {
+    visitDeclarationMetadata(node.metadata);
+
+    _simpleStatement(node, () {
+      token(node.keyword);
+      space();
+      visit(node.uri);
+      _visitCombinators(node.combinators);
+    });
+  }
+
+  visitExpressionFunctionBody(ExpressionFunctionBody node) {
+    _simpleStatement(node, () {
+      // The "async" or "sync" keyword.
+      token(node.keyword, after: space);
+
+      // Try to keep the "(...) => " with the start of the body for anonymous
+      // functions.
+      if (_isLambda(node)) _writer.startSpan();
+
+      token(node.functionDefinition); // "=>".
+      split();
+
+      if (_isLambda(node)) _writer.endSpan();
+
+      _writer.startSpan();
+      visit(node.expression);
+      _writer.endSpan();
+    });
+  }
+
+  visitExpressionStatement(ExpressionStatement node) {
+    _simpleStatement(node, () {
+      visit(node.expression);
+    });
+  }
+
+  visitExtendsClause(ExtendsClause node) {
+    split();
+    token(node.keyword);
+    space();
+    visit(node.superclass);
+  }
+
+  visitFieldDeclaration(FieldDeclaration node) {
+    visitMemberMetadata(node.metadata);
+
+    _simpleStatement(node, () {
+      modifier(node.staticKeyword);
+      visit(node.fields);
+    });
+  }
+
+  visitFieldFormalParameter(FieldFormalParameter node) {
+    visitParameterMetadata(node.metadata);
+    token(node.keyword, after: space);
+    visit(node.type, after: space);
+    token(node.thisToken);
+    token(node.period);
+    visit(node.identifier);
+    visit(node.parameters);
+  }
+
+  visitForEachStatement(ForEachStatement node) {
+    _writer.nestExpression();
+    token(node.awaitKeyword, after: space);
+    token(node.forKeyword);
+    space();
+    token(node.leftParenthesis);
+    if (node.loopVariable != null) {
+      visit(node.loopVariable);
+    } else {
+      visit(node.identifier);
+    }
+    split();
+    token(node.inKeyword);
+    space();
+    visit(node.iterable);
+    token(node.rightParenthesis);
+    space();
+    visit(node.body);
+    _writer.unnest();
+  }
+
+  visitFormalParameterList(FormalParameterList node) {
+    _writer.nestExpression();
+    token(node.leftParenthesis);
+
+    // Allow splitting after the "(" in non-empty parameter lists, but not for
+    // lambdas.
+    if ((node.parameters.isNotEmpty ||
+            node.rightParenthesis.precedingComments != null) &&
+        !_isLambda(node)) {
+      zeroSplit();
+    }
+
+    // Try to keep the parameters together.
+    _writer.startSpan();
+
+    var inOptionalParams = false;
+    for (var i = 0; i < node.parameters.length; i++) {
+      var parameter = node.parameters[i];
+      var inFirstOptional =
+          !inOptionalParams && parameter is DefaultFormalParameter;
+
+      // Preceding comma.
+      if (i > 0) token(node.parameters[i - 1].endToken.next);
+
+      // Don't try to keep optional parameters together with mandatory ones.
+      if (inFirstOptional) _writer.endSpan();
+
+      if (i > 0) split();
+
+      if (inFirstOptional) {
+        // Do try to keep optional parameters with each other.
+        _writer.startSpan();
+
+        // "[" or "{" for optional parameters.
+        token(node.leftDelimiter);
+
+        inOptionalParams = true;
+      }
+
+      visit(parameter);
+    }
+
+    // "]" or "}" for optional parameters.
+    token(node.rightDelimiter);
+
+    token(node.rightParenthesis);
+    _writer.unnest();
+    _writer.endSpan();
+  }
+
+  visitForStatement(ForStatement node) {
+    _writer.nestExpression();
+    token(node.forKeyword);
+    space();
+    token(node.leftParenthesis);
+
+    _writer.startMultisplit();
+
+    // The initialization clause.
+    if (node.initialization != null) {
+      visit(node.initialization);
+    } else if (node.variables != null) {
+      // Indent split variables more so they aren't at the same level
+      // as the rest of the loop clauses.
+      _writer.indent(4);
+
+      var declaration = node.variables;
+      visitDeclarationMetadata(declaration.metadata);
+      modifier(declaration.keyword);
+      visit(declaration.type, after: space);
+
+      visitCommaSeparatedNodes(declaration.variables, between: () {
+        _writer.multisplit(space: true, nest: true);
+      });
+
+      _writer.unindent(4);
+    }
+
+    token(node.leftSeparator);
+
+    // The condition clause.
+    if (node.condition != null) _writer.multisplit(nest: true, space: true);
+    visit(node.condition);
+    token(node.rightSeparator);
+
+    // The update clause.
+    if (node.updaters.isNotEmpty) {
+      _writer.multisplit(nest: true, space: true);
+      visitCommaSeparatedNodes(node.updaters,
+          between: () => _writer.multisplit(nest: true, space: true));
+    }
+
+    token(node.rightParenthesis);
+    _writer.endMultisplit();
+    _writer.unnest();
+
+    // The body.
+    if (node.body is! EmptyStatement) space();
+    visit(node.body);
+  }
+
+  visitFunctionDeclaration(FunctionDeclaration node) {
+    visitMemberMetadata(node.metadata);
+
+    _writer.nestExpression();
+    modifier(node.externalKeyword);
+    visit(node.returnType, after: space);
+    modifier(node.propertyKeyword);
+    visit(node.name);
+    visit(node.functionExpression);
+    _writer.unnest();
+  }
+
+  visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
+    visit(node.functionDeclaration);
+  }
+
+  visitFunctionExpression(FunctionExpression node) {
+    _visitBody(node.parameters, node.body);
+  }
+
+  visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+    visit(node.function);
+    visit(node.argumentList);
+  }
+
+  visitFunctionTypeAlias(FunctionTypeAlias node) {
+    visitDeclarationMetadata(node.metadata);
+
+    _simpleStatement(node, () {
+      token(node.keyword);
+      space();
+      visit(node.returnType, after: space);
+      visit(node.name);
+      visit(node.typeParameters);
+      visit(node.parameters);
+    });
+  }
+
+  visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+    visitParameterMetadata(node.metadata);
+    visit(node.returnType, after: space);
+
+    // Try to keep the function's parameters with its name.
+    _writer.startSpan();
+    visit(node.identifier);
+    visit(node.parameters);
+    _writer.endSpan();
+  }
+
+  visitHideCombinator(HideCombinator node) {
+    _visitCombinator(node.keyword, node.hiddenNames);
+  }
+
+  visitIfStatement(IfStatement node) {
+    _writer.nestExpression();
+    token(node.ifKeyword);
+    space();
+    token(node.leftParenthesis);
+    visit(node.condition);
+    token(node.rightParenthesis);
+
+    space();
+    visit(node.thenStatement);
+    _writer.unnest();
+
+    if (node.elseStatement != null) {
+      if (node.thenStatement is Block) {
+        space();
+      } else {
+        // Corner case where an else follows a single-statement then clause.
+        // This is against the style guide, but we still need to handle it. If
+        // it happens, put the else on the next line.
+        newline();
+      }
+
+      token(node.elseKeyword);
+      space();
+      visit(node.elseStatement);
+    }
+  }
+
+  visitImplementsClause(ImplementsClause node) {
+    split();
+    token(node.keyword);
+    space();
+    visitCommaSeparatedNodes(node.interfaces);
+  }
+
+  visitImportDirective(ImportDirective node) {
+    visitDeclarationMetadata(node.metadata);
+
+    _simpleStatement(node, () {
+      token(node.keyword);
+      space();
+      visit(node.uri);
+      token(node.deferredToken, before: space);
+      token(node.asToken, before: split, after: space);
+      visit(node.prefix);
+      _visitCombinators(node.combinators);
+    });
+  }
+
+  visitIndexExpression(IndexExpression node) {
+    if (node.isCascaded) {
+      token(node.period);
+    } else {
+      visit(node.target);
+    }
+
+    _writer.startSpan();
+    token(node.leftBracket);
+    _writer.nestExpression();
+    zeroSplit();
+    visit(node.index);
+    token(node.rightBracket);
+    _writer.unnest();
+    _writer.endSpan();
+  }
+
+  visitInstanceCreationExpression(InstanceCreationExpression node) {
+    _writer.startSpan();
+    token(node.keyword);
+    space();
+    visit(node.constructorName);
+    visit(node.argumentList);
+    _writer.endSpan();
+  }
+
+  visitIntegerLiteral(IntegerLiteral node) {
+    token(node.literal);
+  }
+
+  visitInterpolationExpression(InterpolationExpression node) {
+    token(node.leftBracket);
+    visit(node.expression);
+    token(node.rightBracket);
+  }
+
+  visitInterpolationString(InterpolationString node) {
+    token(node.contents);
+  }
+
+  visitIsExpression(IsExpression node) {
+    visit(node.expression);
+    space();
+    token(node.isOperator);
+    token(node.notOperator);
+    space();
+    visit(node.type);
+  }
+
+  visitLabel(Label node) {
+    visit(node.label);
+    token(node.colon);
+  }
+
+  visitLabeledStatement(LabeledStatement node) {
+    visitNodes(node.labels, between: space, after: space);
+    visit(node.statement);
+  }
+
+  visitLibraryDirective(LibraryDirective node) {
+    visitDeclarationMetadata(node.metadata);
+
+    _simpleStatement(node, () {
+      token(node.keyword);
+      space();
+      visit(node.name);
+    });
+  }
+
+  visitLibraryIdentifier(LibraryIdentifier node) {
+    visit(node.components.first);
+    for (var component in node.components.skip(1)) {
+      token(component.beginToken.previous); // "."
+      visit(component);
+    }
+  }
+
+  visitListLiteral(ListLiteral node) {
+    // Corner case: Splitting inside a list looks bad if there's only one
+    // element, so make those more costly.
+    var cost = node.elements.length <= 1 ? Cost.singleElementList : Cost.normal;
+    _visitCollectionLiteral(
+        node, node.leftBracket, node.elements, node.rightBracket, cost);
+  }
+
+  visitMapLiteral(MapLiteral node) {
+    _visitCollectionLiteral(
+        node, node.leftBracket, node.entries, node.rightBracket);
+  }
+
+  visitMapLiteralEntry(MapLiteralEntry node) {
+    visit(node.key);
+    token(node.separator);
+    split();
+    visit(node.value);
+  }
+
+  visitMethodDeclaration(MethodDeclaration node) {
+    visitMemberMetadata(node.metadata);
+
+    modifier(node.externalKeyword);
+    modifier(node.modifierKeyword);
+    visit(node.returnType, after: space);
+    modifier(node.propertyKeyword);
+    modifier(node.operatorKeyword);
+    visit(node.name);
+
+    _visitBody(node.parameters, node.body);
+  }
+
+  visitMethodInvocation(MethodInvocation node) {
+    // With a chain of method calls like `foo.bar.baz.bang`, they either all
+    // split or none of them do.
+    var startedMultisplit = false;
+
+    // Try to keep the entire method chain one line.
+    _writer.startSpan();
+    _writer.nestExpression();
+
+    // Recursively walk the chain of method calls.
+    var depth = 0;
+    visitInvocation(invocation) {
+      depth++;
+      var hasTarget = true;
+
+      if (invocation.target is MethodInvocation) {
+        visitInvocation(invocation.target);
+      } else if (invocation.period != null) {
+        visit(invocation.target);
+      } else {
+        hasTarget = false;
+      }
+
+      if (hasTarget) {
+        // Don't start the multisplit until after the first target. This
+        // ensures we don't get tripped up by newlines or comments before the
+        // first target.
+        if (!startedMultisplit) {
+          _writer.startMultisplit(separable: true);
+          startedMultisplit = true;
+        }
+
+        _writer.multisplit(nest: true);
+
+        token(invocation.period);
+      }
+
+      visit(invocation.methodName);
+
+      // Stop the multisplit after the last call, but before it's arguments.
+      // That allows unsplit chains where the last argument list wraps, like:
+      //
+      //     foo().bar().baz(
+      //         argument, list);
+      depth--;
+      if (depth == 0 && startedMultisplit) _writer.endMultisplit();
+
+      visit(invocation.argumentList);
+    }
+
+    visitInvocation(node);
+
+    _writer.unnest();
+    _writer.endSpan();
+  }
+
+  visitNamedExpression(NamedExpression node) {
+    visit(node.name);
+    visit(node.expression, before: space);
+  }
+
+  visitNativeClause(NativeClause node) {
+    token(node.keyword);
+    space();
+    visit(node.name);
+  }
+
+  visitNativeFunctionBody(NativeFunctionBody node) {
+    _simpleStatement(node, () {
+      token(node.nativeToken);
+      space();
+      visit(node.stringLiteral);
+    });
+  }
+
+  visitNullLiteral(NullLiteral node) {
+    token(node.literal);
+  }
+
+  visitParenthesizedExpression(ParenthesizedExpression node) {
+    _writer.nestExpression();
+    token(node.leftParenthesis);
+    visit(node.expression);
+    _writer.unnest();
+    token(node.rightParenthesis);
+  }
+
+  visitPartDirective(PartDirective node) {
+    _simpleStatement(node, () {
+      token(node.keyword);
+      space();
+      visit(node.uri);
+    });
+  }
+
+  visitPartOfDirective(PartOfDirective node) {
+    _simpleStatement(node, () {
+      token(node.keyword);
+      space();
+      token(node.ofToken);
+      space();
+      visit(node.libraryName);
+    });
+  }
+
+  visitPostfixExpression(PostfixExpression node) {
+    visit(node.operand);
+    token(node.operator);
+  }
+
+  visitPrefixedIdentifier(PrefixedIdentifier node) {
+    visit(node.prefix);
+    token(node.period);
+    visit(node.identifier);
+  }
+
+  visitPrefixExpression(PrefixExpression node) {
+    token(node.operator);
+
+    // Corner case: put a space between successive "-" operators so we don't
+    // inadvertently turn them into a "--" decrement operator.
+    if (node.operand is PrefixExpression &&
+        (node.operand as PrefixExpression).operator.lexeme == "-") {
+      space();
+    }
+
+    visit(node.operand);
+  }
+
+  visitPropertyAccess(PropertyAccess node) {
+    if (node.isCascaded) {
+      token(node.operator);
+    } else {
+      visit(node.target);
+      token(node.operator);
+    }
+    visit(node.propertyName);
+  }
+
+  visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) {
+    _writer.startSpan();
+
+    token(node.keyword);
+    token(node.period);
+    visit(node.constructorName);
+    visit(node.argumentList);
+
+    _writer.endSpan();
+  }
+
+  visitRethrowExpression(RethrowExpression node) {
+    token(node.keyword);
+  }
+
+  visitReturnStatement(ReturnStatement node) {
+    _simpleStatement(node, () {
+      token(node.keyword);
+      if (node.expression != null) {
+        space();
+        visit(node.expression);
+      }
+    });
+  }
+
+  visitScriptTag(ScriptTag node) {
+    // The lexeme includes the trailing newline. Strip it off since the
+    // formatter ensures it gets a newline after it. Since the script tag must
+    // come at the top of the file, we don't have to worry about preceding
+    // comments or whitespace.
+    _writeText(node.scriptTag.lexeme.trim(), node.offset);
+
+    oneOrTwoNewlines();
+  }
+
+  visitShowCombinator(ShowCombinator node) {
+    _visitCombinator(node.keyword, node.shownNames);
+  }
+
+  visitSimpleFormalParameter(SimpleFormalParameter node) {
+    visitParameterMetadata(node.metadata);
+    modifier(node.keyword);
+    visit(node.type, after: space);
+    visit(node.identifier);
+  }
+
+  visitSimpleIdentifier(SimpleIdentifier node) {
+    token(node.token);
+  }
+
+  visitSimpleStringLiteral(SimpleStringLiteral node) {
+    // Since we output the string literal manually, ensure any preceding
+    // comments are written first.
+    writePrecedingCommentsAndNewlines(node.literal);
+
+    _writeStringLiteral(node.literal.lexeme, node.offset);
+  }
+
+  visitStringInterpolation(StringInterpolation node) {
+    // Since we output the interpolated text manually, ensure we include any
+    // preceding stuff first.
+    writePrecedingCommentsAndNewlines(node.beginToken);
+
+    // Right now, the formatter does not try to do any reformatting of the
+    // contents of interpolated strings. Instead, it treats the entire thing as
+    // a single (possibly multi-line) chunk of text.
+     _writeStringLiteral(
+        _source.text.substring(node.beginToken.offset, node.endToken.end),
+        node.offset);
+  }
+
+  visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+    _writer.startSpan();
+
+    token(node.keyword);
+    token(node.period);
+    visit(node.constructorName);
+    visit(node.argumentList);
+
+    _writer.endSpan();
+  }
+
+  visitSuperExpression(SuperExpression node) {
+    token(node.keyword);
+  }
+
+  visitSwitchCase(SwitchCase node) {
+    visitNodes(node.labels, between: space, after: space);
+    token(node.keyword);
+    space();
+    visit(node.expression);
+    token(node.colon);
+
+    _writer.indent();
+    // TODO(rnystrom): Allow inline cases?
+    newline();
+
+    visitNodes(node.statements, between: oneOrTwoNewlines);
+    _writer.unindent();
+  }
+
+  visitSwitchDefault(SwitchDefault node) {
+    visitNodes(node.labels, between: space, after: space);
+    token(node.keyword);
+    token(node.colon);
+
+    _writer.indent();
+    // TODO(rnystrom): Allow inline cases?
+    newline();
+
+    visitNodes(node.statements, between: oneOrTwoNewlines);
+    _writer.unindent();
+  }
+
+  visitSwitchStatement(SwitchStatement node) {
+    _writer.nestExpression();
+    token(node.keyword);
+    space();
+    token(node.leftParenthesis);
+    zeroSplit();
+    visit(node.expression);
+    token(node.rightParenthesis);
+    space();
+    token(node.leftBracket);
+    _writer.indent();
+    newline();
+
+    visitNodes(node.members, between: oneOrTwoNewlines, after: newline);
+    token(node.rightBracket, before: () {
+      _writer.unindent();
+      newline();
+    });
+    _writer.unnest();
+  }
+
+  visitSymbolLiteral(SymbolLiteral node) {
+    token(node.poundSign);
+    var components = node.components;
+    for (var component in components) {
+      // The '.' separator
+      if (component.previous.lexeme == '.') {
+        token(component.previous);
+      }
+      token(component);
+    }
+  }
+
+  visitThisExpression(ThisExpression node) {
+    token(node.keyword);
+  }
+
+  visitThrowExpression(ThrowExpression node) {
+    token(node.keyword);
+    space();
+    visit(node.expression);
+  }
+
+  visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+    visitDeclarationMetadata(node.metadata);
+
+    _simpleStatement(node, () {
+      visit(node.variables);
+    });
+  }
+
+  visitTryStatement(TryStatement node) {
+    token(node.tryKeyword);
+    space();
+    visit(node.body);
+    visitNodes(node.catchClauses, before: space, between: space);
+    token(node.finallyKeyword, before: space, after: space);
+    visit(node.finallyBlock);
+  }
+
+  visitTypeArgumentList(TypeArgumentList node) {
+    token(node.leftBracket);
+    visitCommaSeparatedNodes(node.arguments);
+    token(node.rightBracket);
+  }
+
+  visitTypeName(TypeName node) {
+    visit(node.name);
+    visit(node.typeArguments);
+  }
+
+  visitTypeParameter(TypeParameter node) {
+    visitParameterMetadata(node.metadata);
+    visit(node.name);
+    token(node.keyword /* extends */, before: space, after: space);
+    visit(node.bound);
+  }
+
+  visitTypeParameterList(TypeParameterList node) {
+    token(node.leftBracket);
+    visitCommaSeparatedNodes(node.typeParameters);
+    token(node.rightBracket);
+  }
+
+  visitVariableDeclaration(VariableDeclaration node) {
+    visit(node.name);
+    if (node.initializer == null) return;
+
+    space();
+    token(node.equals);
+    split(Cost.assignment);
+    _writer.startSpan();
+    visit(node.initializer);
+    _writer.endSpan();
+  }
+
+  visitVariableDeclarationList(VariableDeclarationList node) {
+    visitDeclarationMetadata(node.metadata);
+    modifier(node.keyword);
+    visit(node.type, after: space);
+
+    if (node.variables.length == 1) {
+      visit(node.variables.single);
+      return;
+    }
+
+    // If there are multiple declarations and any of them have initializers,
+    // put them all on their own lines.
+    if (node.variables.any((variable) => variable.initializer != null)) {
+      visit(node.variables.first);
+
+      // Indent variables after the first one to line up past "var".
+      _writer.indent(2);
+
+      for (var variable in node.variables.skip(1)) {
+        token(variable.beginToken.previous); // Comma.
+        newline();
+
+        visit(variable);
+      }
+
+      _writer.unindent(2);
+      return;
+    }
+
+    // Use a multisplit between all of the variables. If there are multiple
+    // declarations, we will try to keep them all on one line. If that isn't
+    // possible, we split after *every* declaration so that each is on its own
+    // line.
+    _writer.startMultisplit();
+    visitCommaSeparatedNodes(node.variables, between: () {
+      _writer.multisplit(space: true, nest: true);
+    });
+    _writer.endMultisplit();
+  }
+
+  visitVariableDeclarationStatement(VariableDeclarationStatement node) {
+    _simpleStatement(node, () {
+      visit(node.variables);
+    });
+  }
+
+  visitWhileStatement(WhileStatement node) {
+    _writer.nestExpression();
+    token(node.keyword);
+    space();
+    token(node.leftParenthesis);
+    zeroSplit();
+    visit(node.condition);
+    token(node.rightParenthesis);
+    if (node.body is! EmptyStatement) space();
+    visit(node.body);
+    _writer.unnest();
+  }
+
+  visitWithClause(WithClause node) {
+    split();
+    token(node.withKeyword);
+    space();
+    visitCommaSeparatedNodes(node.mixinTypes);
+  }
+
+  visitYieldStatement(YieldStatement node) {
+    _simpleStatement(node, () {
+      token(node.yieldKeyword);
+      token(node.star);
+      space();
+      visit(node.expression);
+    });
+  }
+
+  /// Visit a [node], and if not null, optionally preceded or followed by the
+  /// specified functions.
+  void visit(AstNode node, {void before(), void after()}) {
+    if (node == null) return;
+
+    if (before != null) before();
+
+    node.accept(this);
+
+    if (after != null) after();
+  }
+
+  /// Visit metadata annotations on directives and declarations.
+  ///
+  /// These always force the annotations to be on the previous line.
+  void visitDeclarationMetadata(NodeList<Annotation> metadata) {
+    // If there are multiple annotations, they are always on their own lines,
+    // even the last.
+    if (metadata.length > 1) {
+      visitNodes(metadata, between: newline, after: newline);
+    } else {
+      visitNodes(metadata, between: space, after: newline);
+    }
+  }
+
+  /// Visit metadata annotations on members.
+  ///
+  /// These may be on the same line as the member, or on the previous.
+  void visitMemberMetadata(NodeList<Annotation> metadata) {
+    // If there are multiple annotations, they are always on their own lines,
+    // even the last.
+    if (metadata.length > 1) {
+      visitNodes(metadata, between: newline, after: newline);
+    } else {
+      visitNodes(metadata, between: space, after: spaceOrNewline);
+    }
+  }
+
+  /// Visit metadata annotations on parameters and type parameters.
+  ///
+  /// These are always on the same line as the parameter.
+  void visitParameterMetadata(NodeList<Annotation> metadata) {
+    // TODO(rnystrom): Allow splitting after annotations?
+    visitNodes(metadata, between: space, after: space);
+  }
+
+  /// Visit the given function [parameters] followed by its [body], printing a
+  /// space before it if it's not empty.
+  ///
+  /// If [afterParameters] is provided, it is invoked between the parameters
+  /// and body. (It's used for constructor initialization lists.)
+  void _visitBody(FormalParameterList parameters, FunctionBody body,
+      [afterParameters()]) {
+    if (parameters != null) {
+      // If the body is "=>", add an extra level of indentation around the
+      // parameters. This ensures that if they wrap, they wrap more deeply than
+      // the "=>" does, as in:
+      //
+      //     someFunction(parameter,
+      //             parameter, parameter) =>
+      //         "the body";
+      if (body is ExpressionFunctionBody) _writer.nestExpression();
+
+      visit(parameters);
+      if (afterParameters != null) afterParameters();
+
+      if (body is ExpressionFunctionBody) _writer.unnest();
+    }
+
+    if (body is! EmptyFunctionBody) space();
+    visit(body);
+  }
+
+  /// Visit a list of [nodes] if not null, optionally separated and/or preceded
+  /// and followed by the given functions.
+  void visitNodes(Iterable<AstNode> nodes, {before(), between(), after()}) {
+    if (nodes == null || nodes.isEmpty) return;
+
+    if (before != null) before();
+
+    visit(nodes.first);
+    for (var node in nodes.skip(1)) {
+      if (between != null) between();
+      visit(node);
+    }
+
+    if (after != null) after();
+  }
+
+  /// Visit a comma-separated list of [nodes] if not null.
+  void visitCommaSeparatedNodes(Iterable<AstNode> nodes, {between()}) {
+    if (nodes == null || nodes.isEmpty) return;
+
+    if (between == null) between = space;
+
+    var first = true;
+    for (var node in nodes) {
+      if (!first) between();
+      first = false;
+
+      visit(node);
+
+      // The comma after the node.
+      if (node.endToken.next.lexeme == ",") token(node.endToken.next);
+    }
+  }
+
+  /// Visits the collection literal [node] whose body starts with [leftBracket],
+  /// ends with [rightBracket] and contains [elements].
+  void _visitCollectionLiteral(TypedLiteral node, Token leftBracket,
+      Iterable<AstNode> elements, Token rightBracket, [int cost]) {
+    modifier(node.constKeyword);
+    visit(node.typeArguments);
+
+    _startBody(leftBracket, cost: cost);
+
+    // Each list element takes at least 3 characters (one character for the
+    // element, one for the comma, one for the space), so force it to split if
+    // we know that won't fit.
+    if (elements.length > _writer.pageWidth ~/ 3) _writer.preemptMultisplits();
+
+    for (var element in elements) {
+      if (element != elements.first) _writer.multisplit(space: true);
+
+      _writer.nestExpression();
+
+      visit(element);
+
+      // The comma after the element.
+      if (element.endToken.next.lexeme == ",") token(element.endToken.next);
+
+      _writer.unnest();
+    }
+
+    _endBody(rightBracket);
+  }
+
+  /// Visits a list of [combinators] following an "import" or "export"
+  /// directive. Combinators can be split in a few different ways:
+  ///
+  ///     // All on one line:
+  ///     import 'animals.dart' show Ant hide Cat;
+  ///
+  ///     // Wrap before each keyword:
+  ///     import 'animals.dart'
+  ///         show Ant, Baboon
+  ///         hide Cat;
+  ///
+  ///     // Wrap either or both of the name lists:
+  ///     import 'animals.dart'
+  ///         show
+  ///             Ant,
+  ///             Baboon
+  ///         hide Cat;
+  ///
+  /// Multisplits are used here to specifically avoid a few undesirable
+  /// combinations:
+  ///
+  ///     // Wrap list but not keyword:
+  ///     import 'animals.dart' show
+  ///             Ant,
+  ///             Baboon
+  ///         hide Cat;
+  ///
+  ///     // Wrap one keyword but not both:
+  ///     import 'animals.dart'
+  ///         show Ant, Baboon hide Cat;
+  ///
+  /// This ensures that when any wrapping occurs, the keywords are always at
+  /// the beginning of the line.
+  void _visitCombinators(NodeList<Combinator> combinators) {
+    if (combinators.isEmpty) return;
+
+    _writer.startMultisplit();
+    visitNodes(combinators);
+    _writer.endMultisplit();
+  }
+
+  /// Visits a [HideCombinator] or [ShowCombinator] starting with [keyword] and
+  /// containing [names].
+  ///
+  /// This assumes it has been called from within the [Multisplit] created by
+  /// [_visitCombinators].
+  void _visitCombinator(Token keyword, NodeList<SimpleIdentifier> names) {
+    // Allow splitting after the keyword.
+    _writer.multisplit(space: true, nest: true);
+
+    _writer.nestExpression();
+    token(keyword);
+
+    _writer.startMultisplit();
+    _writer.multisplit(nest: true, space: true);
+    visitCommaSeparatedNodes(names,
+        between: () => _writer.multisplit(nest: true, space: true));
+
+    _writer.unnest();
+    _writer.endMultisplit();
+  }
+
+  /// Writes the simple statement or semicolon-delimited top-level declaration.
+  ///
+  /// Handles nesting if a line break occurs in the statement and writes the
+  /// terminating semicolon. Invokes [body] which should write statement itself.
+  void _simpleStatement(AstNode node, body()) {
+    _writer.nestExpression();
+    body();
+
+    // TODO(rnystrom): Can the analyzer move "semicolon" to some shared base
+    // type?
+    token((node as dynamic).semicolon);
+    _writer.unnest();
+  }
+  /// Writes an opening bracket token ("(", "{", "[") and handles indenting and
+  /// starting the multisplit it contains.
+  ///
+  /// If [space] is `true`, then the initial multisplit will use a space if not
+  /// split.
+  void _startBody(Token leftBracket, {int cost, bool space: false}) {
+    token(leftBracket);
+
+    // Indent the body.
+    _writer.startMultisplit(cost: cost);
+    _writer.indent();
+
+    // Split after the bracket.
+    _writer.multisplit(space: space);
+  }
+
+  /// Writes a closing bracket token (")", "}", "]") and handles unindenting
+  /// and ending the multisplit it contains.
+  ///
+  /// Used for blocks, other curly bodies, and collection literals.
+  ///
+  /// If [space] is `true`, then the initial multisplit will use a space if not
+  /// split.
+  void _endBody(Token rightBracket, {bool space: false}) {
+    token(rightBracket, before: () {
+      // Split before the closing bracket character.
+      _writer.unindent();
+      _writer.multisplit(space: space);
+    });
+
+    _writer.endMultisplit();
+  }
+
+  /// Returns `true` if [node] is immediately contained within an anonymous
+  /// [FunctionExpression].
+  bool _isLambda(AstNode node) =>
+      node.parent is FunctionExpression &&
+      node.parent.parent is! FunctionDeclaration;
+
+  /// Writes the string literal [string] to the output.
+  ///
+  /// Splits multiline strings into separate chunks so that the line splitter
+  /// can handle them correctly.
+  void _writeStringLiteral(String string, int offset) {
+    // Split each line of a multiline string into separate chunks.
+    var lines = string.split(_formatter.lineEnding);
+
+    _writeText(lines.first, offset);
+    offset += lines.first.length;
+
+    for (var line in lines.skip(1)) {
+      _writer.writeWhitespace(Whitespace.newlineFlushLeft);
+      offset++;
+      _writeText(line, offset);
+      offset += line.length;
+    }
+  }
+
+  /// Emit the given [modifier] if it's non null, followed by non-breaking
+  /// whitespace.
+  void modifier(Token modifier) {
+    token(modifier, after: space);
+  }
+
+  /// Emit a non-breaking space.
+  void space() {
+    _writer.writeWhitespace(Whitespace.space);
+  }
+
+  /// Emit a single mandatory newline.
+  void newline() {
+    _writer.writeWhitespace(Whitespace.newline);
+  }
+
+  /// Emit a two mandatory newlines.
+  void twoNewlines() {
+    _writer.writeWhitespace(Whitespace.twoNewlines);
+  }
+
+  /// Allow either a single space or newline to be emitted before the next
+  /// non-whitespace token based on whether a newline exists in the source
+  /// between the last token and the next one.
+  void spaceOrNewline() {
+    _writer.writeWhitespace(Whitespace.spaceOrNewline);
+  }
+
+  /// Allow either one or two newlines to be emitted before the next
+  /// non-whitespace token based on whether more than one newline exists in the
+  /// source between the last token and the next one.
+  void oneOrTwoNewlines() {
+    _writer.writeWhitespace(Whitespace.oneOrTwoNewlines);
+  }
+
+  /// Writes a single-space split with the given [cost].
+  ///
+  /// If [cost] is omitted, defaults to [Cost.normal]. Returns the newly created
+  /// [SplitParam].
+  SplitParam split([int cost]) => _writer.writeSplit(cost: cost, space: true);
+
+  /// Writes a split that is the empty string when unsplit.
+  ///
+  /// Returns the newly created [SplitParam].
+  SplitParam zeroSplit([int cost]) => _writer.writeSplit(cost: cost);
+
+  /// Emit [token], along with any comments and formatted whitespace that comes
+  /// before it.
+  ///
+  /// Does nothing if [token] is `null`. If [before] is given, it will be
+  /// executed before the token is outout. Likewise, [after] will be called
+  /// after the token is output.
+  void token(Token token, {before(), after()}) {
+    if (token == null) return;
+
+    writePrecedingCommentsAndNewlines(token);
+
+    if (before != null) before();
+
+    _writeText(token.lexeme, token.offset);
+
+    if (after != null) after();
+  }
+
+  /// Writes all formatted whitespace and comments that appear before [token].
+  void writePrecedingCommentsAndNewlines(Token token) {
+    var comment = token.precedingComments;
+
+    // For performance, avoid calculating newlines between tokens unless
+    // actually needed.
+    if (comment == null) {
+      if (_writer.needsToPreserveNewlines) {
+        _writer.preserveNewlines(_startLine(token) - _endLine(token.previous));
+      }
+      return;
+    }
+
+    var previousLine = _endLine(token.previous);
+
+    // Corner case! The analyzer includes the "\n" in the script tag's lexeme,
+    // which means it appears to be one line later than it is. That causes a
+    // comment following it to appear to be on the same line. Fix that here by
+    // correcting the script tag's line.
+    if (token.previous.type == TokenType.SCRIPT_TAG) previousLine--;
+
+    var tokenLine = _startLine(token);
+
+    var comments = [];
+    while (comment != null) {
+      var commentLine = _startLine(comment);
+
+      // Don't preserve newlines at the top of the file.
+      if (comment == token.precedingComments &&
+          token.previous.type == TokenType.EOF) {
+        previousLine = commentLine;
+      }
+
+      var sourceComment = new SourceComment(comment.toString().trim(),
+          commentLine - previousLine,
+          isLineComment: comment.type == TokenType.SINGLE_LINE_COMMENT,
+          isStartOfLine: _startColumn(comment) == 1);
+
+      // If this comment contains either of the selection endpoints, mark them
+      // in the comment.
+      var start = _getSelectionStartWithin(comment.offset, comment.length);
+      if (start != null) sourceComment.startSelection(start);
+
+      var end = _getSelectionEndWithin(comment.offset, comment.length);
+      if (end != null) sourceComment.endSelection(end);
+
+      comments.add(sourceComment);
+
+      previousLine = _endLine(comment);
+      comment = comment.next;
+    }
+
+    _writer.writeComments(comments, tokenLine - previousLine, token.lexeme);
+  }
+
+  /// Write [text] to the current chunk, given that it starts at [offset] in
+  /// the original source.
+  ///
+  /// Also outputs the selection endpoints if needed.
+  void _writeText(String text, int offset) {
+    _writer.write(text);
+
+    // If this text contains either of the selection endpoints, mark them in
+    // the chunk.
+    var start = _getSelectionStartWithin(offset, text.length);
+    if (start != null) {
+      _writer.startSelectionFromEnd(text.length - start);
+    }
+
+    var end = _getSelectionEndWithin(offset, text.length);
+    if (end != null) {
+      _writer.endSelectionFromEnd(text.length - end);
+    }
+  }
+
+  /// Returns the number of characters past [offset] in the source where the
+  /// selection start appears if it appears before `offset + length`.
+  ///
+  /// Returns `null` if the selection start has already been processed or is
+  /// not within that range.
+  int _getSelectionStartWithin(int offset, int length) {
+    // If there is no selection, do nothing.
+    if (_source.selectionStart == null) return null;
+
+    // If we've already passed it, don't consider it again.
+    if (_passedSelectionStart) return null;
+
+    var start = _source.selectionStart - offset;
+
+    // If it started in whitespace before this text, push it forward to the
+    // beginning of the non-whitespace text.
+    if (start < 0) start = 0;
+
+    // If we haven't reached it yet, don't consider it.
+    if (start >= length) return null;
+
+    // We found it.
+    _passedSelectionStart = true;
+
+    return start;
+  }
+
+  /// Returns the number of characters past [offset] in the source where the
+  /// selection endpoint appears if it appears before `offset + length`.
+  ///
+  /// Returns `null` if the selection endpoint has already been processed or is
+  /// not within that range.
+  int _getSelectionEndWithin(int offset, int length) {
+    // If there is no selection, do nothing.
+    if (_source.selectionLength == null) return null;
+
+    // If we've already passed it, don't consider it again.
+    if (_passedSelectionEnd) return null;
+
+    var end = _findSelectionEnd() - offset;
+
+    // If it started in whitespace before this text, push it forward to the
+    // beginning of the non-whitespace text.
+    if (end < 0) end = 0;
+
+    // If we haven't reached it yet, don't consider it.
+    if (end > length) return null;
+
+    if (end == length && _findSelectionEnd() == _source.selectionStart) {
+      return null;
+    }
+
+    // We found it.
+    _passedSelectionEnd = true;
+
+    return end;
+  }
+
+  /// Calculates the character offset in the source text of the end of the
+  /// selection.
+  ///
+  /// Removes any trailing whitespace from the selection.
+  int _findSelectionEnd() {
+    if (_selectionEnd != null) return _selectionEnd;
+
+    _selectionEnd = _source.selectionStart + _source.selectionLength;
+
+    // If the selection bumps to the end of the source, pin it there.
+    if (_selectionEnd == _source.text.length) return _selectionEnd;
+
+    // Trim off any trailing whitespace. We want the selection to "rubberband"
+    // around the selected non-whitespace tokens since the whitespace will
+    // be munged by the formatter itself.
+    while (_selectionEnd > _source.selectionStart) {
+      // Stop if we hit anything other than space, tab, newline or carriage
+      // return.
+      var char = _source.text.codeUnitAt(_selectionEnd - 1);
+      if (char != 0x20 && char != 0x09 && char != 0x0a && char != 0x0d) {
+        break;
+      }
+
+      _selectionEnd--;
+    }
+
+    return _selectionEnd;
+  }
+
+  /// Gets the 1-based line number that the beginning of [token] lies on.
+  int _startLine(Token token) => _lineInfo.getLocation(token.offset).lineNumber;
+
+  /// Gets the 1-based line number that the end of [token] lies on.
+  int _endLine(Token token) => _lineInfo.getLocation(token.end).lineNumber;
+
+  /// Gets the 1-based column number that the beginning of [token] lies on.
+  int _startColumn(Token token) =>
+      _lineInfo.getLocation(token.offset).columnNumber;
+}
diff --git a/dart_style/lib/src/whitespace.dart b/dart_style/lib/src/whitespace.dart
new file mode 100644
index 0000000..7c95305
--- /dev/null
+++ b/dart_style/lib/src/whitespace.dart
@@ -0,0 +1,51 @@
+// 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 dart_style.src.whitespace;
+
+/// The kind of pending whitespace that has been "written", but not actually
+/// physically output yet.
+///
+/// We defer actually writing whitespace until a non-whitespace token is
+/// encountered to avoid trailing whitespace.
+class Whitespace {
+  /// A single non-breaking space.
+  static const space = const Whitespace._("space");
+
+  /// A single newline.
+  static const newline = const Whitespace._("newline");
+
+  /// A single newline that takes into account the current expression nesting
+  /// for the next line.
+  static const nestedNewline = const Whitespace._("nestedNewline");
+
+  /// A single newline with all indentation eliminated at the beginning of the
+  /// next line.
+  ///
+  /// Used for subsequent lines in a multiline string.
+  static const newlineFlushLeft = const Whitespace._("newlineFlushLeft");
+
+  /// Two newlines, a single blank line of separation.
+  static const twoNewlines = const Whitespace._("twoNewlines");
+
+  /// A space or newline should be output based on whether the current token is
+  /// on the same line as the previous one or not.
+  ///
+  /// In general, we like to avoid using this because it makes the formatter
+  /// less prescriptive over the user's whitespace.
+  static const spaceOrNewline = const Whitespace._("spaceOrNewline");
+
+  /// One or two newlines should be output based on how many newlines are
+  /// present between the next token and the previous one.
+  ///
+  /// In general, we like to avoid using this because it makes the formatter
+  /// less prescriptive over the user's whitespace.
+  static const oneOrTwoNewlines = const Whitespace._("oneOrTwoNewlines");
+
+  final String name;
+
+  const Whitespace._(this.name);
+
+  String toString() => name;
+}
diff --git a/dart_style/pubspec.yaml b/dart_style/pubspec.yaml
new file mode 100644
index 0000000..9f81841
--- /dev/null
+++ b/dart_style/pubspec.yaml
@@ -0,0 +1,23 @@
+name: dart_style
+version: 0.1.8
+author: Dart Team <misc@dartlang.org>
+description: Opinionated, automatic Dart source code formatter.
+homepage: https://github.com/dart-lang/dart_style
+dependencies:
+  analyzer: '>=0.24.0 <0.26.0'
+  args: '>=0.12.1 <0.14.0'
+  path: '>=1.0.0 <2.0.0'
+  source_span: '>=1.0.0 <2.0.0'
+dev_dependencies:
+  browser: '>=0.10.0 <0.11.0'
+  scheduled_test: '>=0.11.2 <0.12.0'
+  unittest: '>=0.11.0 <0.12.0'
+executables:
+  dartfmt: format
+  dartformat: format # Allow the old name for compatibility.
+
+# Tell the bots not to bother building the tests since they don't run in a
+# browser anyway.
+transformers:
+- $dart2js:
+    $exclude: 'test/**'
diff --git a/glob/lib/glob.dart b/glob/lib/glob.dart
new file mode 100644
index 0000000..2d2a83f
--- /dev/null
+++ b/glob/lib/glob.dart
@@ -0,0 +1,179 @@
+// 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 glob;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:path/path.dart' as p;
+
+import 'src/ast.dart';
+import 'src/list_tree.dart';
+import 'src/parser.dart';
+import 'src/utils.dart';
+
+/// Regular expression used to quote globs.
+final _quoteRegExp = new RegExp(r'[*{[?\\}\],\-()]');
+
+/// A glob for matching and listing files and directories.
+///
+/// A glob matches an entire string as a path. Although the glob pattern uses
+/// POSIX syntax, it can match against POSIX, Windows, or URL paths. The format
+/// it expects paths to use is based on the `context` parameter to [new Glob];
+/// it defaults to the current system's syntax.
+///
+/// Paths are normalized before being matched against a glob, so for example the
+/// glob `foo/bar` matches the path `foo/./bar`. A relative glob can match an
+/// absolute path and vice versa; globs and paths are both interpreted as
+/// relative to `context.current`, which defaults to the current working
+/// directory.
+///
+/// When used as a [Pattern], a glob will return either one or zero matches for
+/// a string depending on whether the entire string matches the glob. These
+/// matches don't currently have capture groups, although this may change in the
+/// future.
+class Glob implements Pattern {
+  /// The pattern used to create this glob.
+  final String pattern;
+
+  /// The context in which paths matched against this glob are interpreted.
+  final p.Context context;
+
+  /// If true, a path matches if it matches the glob itself or is recursively
+  /// contained within a directory that matches.
+  final bool recursive;
+
+  /// The parsed AST of the glob.
+  final AstNode _ast;
+
+  ListTree _listTree;
+
+  /// Whether [context]'s current directory is absolute.
+  bool get _contextIsAbsolute {
+    if (_contextIsAbsoluteCache == null) {
+      _contextIsAbsoluteCache = context.isAbsolute(context.current);
+    }
+    return _contextIsAbsoluteCache;
+  }
+  bool _contextIsAbsoluteCache;
+
+  /// Whether [pattern] could match absolute paths.
+  bool get _patternCanMatchAbsolute {
+    if (_patternCanMatchAbsoluteCache == null) {
+      _patternCanMatchAbsoluteCache = _ast.canMatchAbsolute;
+    }
+    return _patternCanMatchAbsoluteCache;
+  }
+  bool _patternCanMatchAbsoluteCache;
+
+  /// Whether [pattern] could match relative paths.
+  bool get _patternCanMatchRelative {
+    if (_patternCanMatchRelativeCache == null) {
+      _patternCanMatchRelativeCache = _ast.canMatchRelative;
+    }
+    return _patternCanMatchRelativeCache;
+  }
+  bool _patternCanMatchRelativeCache;
+
+  /// Returns [contents] with characters that are meaningful in globs
+  /// backslash-escaped.
+  static String quote(String contents) =>
+      contents.replaceAllMapped(_quoteRegExp, (match) => '\\${match[0]}');
+
+  /// Creates a new glob with [pattern].
+  ///
+  /// Paths matched against the glob are interpreted according to [context]. It
+  /// defaults to the system context.
+  ///
+  /// If [recursive] is true, this glob will match and list not only the files
+  /// and directories it explicitly lists, but anything beneath those as well.
+  Glob(String pattern, {p.Context context, bool recursive: false})
+      : this._(
+          pattern,
+          context == null ? p.context : context,
+          recursive);
+
+  // Internal constructor used to fake local variables for [context] and [ast].
+  Glob._(String pattern, p.Context context, bool recursive)
+      : pattern = pattern,
+        context = context,
+        recursive = recursive,
+        _ast = new Parser(pattern + (recursive ? "{,/**}" : ""), context)
+            .parse();
+
+  /// Lists all [FileSystemEntity]s beneath [root] that match the glob.
+  ///
+  /// This works much like [Directory.list], but it only lists directories that
+  /// could contain entities that match the glob. It provides no guarantees
+  /// about the order of the returned entities, although it does guarantee that
+  /// only one entity with a given path will be returned.
+  ///
+  /// [root] defaults to the current working directory.
+  ///
+  /// [followLinks] works the same as for [Directory.list].
+  Stream<FileSystemEntity> list({String root, bool followLinks: true}) {
+    if (context.style != p.style) {
+      throw new StateError("Can't list glob \"$this\"; it matches "
+          "${context.style} paths, but this platform uses ${p.style} paths.");
+    }
+
+    if (_listTree == null) _listTree = new ListTree(_ast);
+    return _listTree.list(root: root, followLinks: followLinks);
+  }
+
+  /// Synchronously lists all [FileSystemEntity]s beneath [root] that match the
+  /// glob.
+  ///
+  /// This works much like [Directory.listSync], but it only lists directories
+  /// that could contain entities that match the glob. It provides no guarantees
+  /// about the order of the returned entities, although it does guarantee that
+  /// only one entity with a given path will be returned.
+  ///
+  /// [root] defaults to the current working directory.
+  ///
+  /// [followLinks] works the same as for [Directory.list].
+  List<FileSystemEntity> listSync({String root, bool followLinks: true}) {
+    if (context.style != p.style) {
+      throw new StateError("Can't list glob \"$this\"; it matches "
+          "${context.style} paths, but this platform uses ${p.style} paths.");
+    }
+
+    if (_listTree == null) _listTree = new ListTree(_ast);
+    return _listTree.listSync(root: root, followLinks: followLinks);
+  }
+
+  /// Returns whether this glob matches [path].
+  bool matches(String path) => matchAsPrefix(path) != null;
+
+  Match matchAsPrefix(String path, [int start = 0]) {
+    // Globs are like anchored RegExps in that they only match entire paths, so
+    // if the match starts anywhere after the first character it can't succeed.
+    if (start != 0) return null;
+
+    if (_patternCanMatchAbsolute &&
+        (_contextIsAbsolute || context.isAbsolute(path))) {
+      var absolutePath = context.normalize(context.absolute(path));
+      if (_ast.matches(toPosixPath(context, absolutePath))) {
+        return new GlobMatch(path, this);
+      }
+    }
+
+    if (_patternCanMatchRelative) {
+      var relativePath = context.relative(path);
+      if (_ast.matches(toPosixPath(context, relativePath))) {
+        return new GlobMatch(path, this);
+      }
+    }
+
+    return null;
+  }
+
+  Iterable<Match> allMatches(String path, [int start = 0]) {
+    var match = matchAsPrefix(path, start);
+    return match == null ? [] : [match];
+  }
+
+  String toString() => pattern;
+}
diff --git a/glob/lib/src/ast.dart b/glob/lib/src/ast.dart
new file mode 100644
index 0000000..1913477
--- /dev/null
+++ b/glob/lib/src/ast.dart
@@ -0,0 +1,372 @@
+// 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 glob.ast;
+
+import 'package:path/path.dart' as p;
+import 'package:collection/collection.dart';
+
+import 'utils.dart';
+
+const _SEPARATOR = 0x2F; // "/"
+
+/// A node in the abstract syntax tree for a glob.
+abstract class AstNode {
+  /// The cached regular expression that this AST was compiled into.
+  RegExp _regExp;
+
+  /// Whether this glob could match an absolute path.
+  ///
+  /// Either this or [canMatchRelative] or both will be true.
+  final bool canMatchAbsolute = false;
+
+  /// Whether this glob could match a relative path.
+  ///
+  /// Either this or [canMatchRelative] or both will be true.
+  final bool canMatchRelative = true;
+
+  /// Returns a new glob with all the options bubbled to the top level.
+  ///
+  /// In particular, this returns a glob AST with two guarantees:
+  ///
+  /// 1. There are no [OptionsNode]s other than the one at the top level.
+  /// 2. It matches the same set of paths as [this].
+  ///
+  /// For example, given the glob `{foo,bar}/{click/clack}`, this would return
+  /// `{foo/click,foo/clack,bar/click,bar/clack}`.
+  OptionsNode flattenOptions() => new OptionsNode([new SequenceNode([this])]);
+
+  /// Returns whether this glob matches [string].
+  bool matches(String string) {
+    if (_regExp == null) _regExp = new RegExp('^${_toRegExp()}\$');
+    return _regExp.hasMatch(string);
+  }
+
+  /// Subclasses should override this to return a regular expression component.
+  String _toRegExp();
+}
+
+/// A sequence of adjacent AST nodes.
+class SequenceNode extends AstNode {
+  /// The nodes in the sequence.
+  final List<AstNode> nodes;
+
+  bool get canMatchAbsolute => nodes.first.canMatchAbsolute;
+  bool get canMatchRelative => nodes.first.canMatchRelative;
+
+  SequenceNode(Iterable<AstNode> nodes)
+      : nodes = nodes.toList();
+
+  OptionsNode flattenOptions() {
+    if (nodes.isEmpty) return new OptionsNode([this]);
+
+    var sequences = nodes.first.flattenOptions().options
+        .map((sequence) => sequence.nodes);
+    for (var node in nodes.skip(1)) {
+      // Concatenate all sequences in the next options node ([nextSequences])
+      // onto all previous sequences ([sequences]).
+      var nextSequences = node.flattenOptions().options;
+      sequences = sequences.expand((sequence) {
+        return nextSequences.map((nextSequence) {
+          return sequence.toList()..addAll(nextSequence.nodes);
+        });
+      });
+    }
+
+    return new OptionsNode(sequences.map((sequence) {
+      // Combine any adjacent LiteralNodes in [sequence].
+      return new SequenceNode(sequence.fold([], (combined, node) {
+        if (combined.isEmpty || combined.last is! LiteralNode ||
+            node is! LiteralNode) {
+          return combined..add(node);
+        }
+
+        combined[combined.length - 1] =
+            new LiteralNode(combined.last.text + node.text);
+        return combined;
+      }));
+    }));
+  }
+
+  /// Splits this glob into components along its path separators.
+  ///
+  /// For example, given the glob `foo/*/*.dart`, this would return three globs:
+  /// `foo`, `*`, and `*.dart`.
+  ///
+  /// Path separators within options nodes are not split. For example,
+  /// `foo/{bar,baz/bang}/qux` will return three globs: `foo`, `{bar,baz/bang}`,
+  /// and `qux`.
+  ///
+  /// [context] is used to determine what absolute roots look like for this
+  /// glob.
+  List<SequenceNode> split(p.Context context) {
+    var componentsToReturn = [];
+    var currentComponent;
+
+    addNode(node) {
+      if (currentComponent == null) currentComponent = [];
+      currentComponent.add(node);
+    }
+
+    finishComponent() {
+      if (currentComponent == null) return;
+      componentsToReturn.add(new SequenceNode(currentComponent));
+      currentComponent = null;
+    }
+
+    for (var node in nodes) {
+      if (node is! LiteralNode || !node.text.contains('/')) {
+        addNode(node);
+        continue;
+      }
+
+      var text = node.text;
+      if (context.style == p.Style.windows) text = text.replaceAll("/", "\\");
+      var components = context.split(text);
+
+      // If the first component is absolute, that means it's a separator (on
+      // Windows some non-separator things are also absolute, but it's invalid
+      // to have "C:" show up in the middle of a path anyway).
+      if (context.isAbsolute(components.first)) {
+        // If this is the first component, it's the root.
+        if (componentsToReturn.isEmpty && currentComponent == null) {
+          var root = components.first;
+          if (context.style == p.Style.windows) {
+            // Above, we switched to backslashes to make [context.split] handle
+            // roots properly. That means that if there is a root, it'll still
+            // have backslashes, where forward slashes are required for globs.
+            // So we switch it back here.
+            root = root.replaceAll("\\", "/");
+          }
+          addNode(new LiteralNode(root));
+        }
+        finishComponent();
+        components = components.skip(1);
+        if (components.isEmpty) continue;
+      }
+
+      // For each component except the last one, add a separate sequence to
+      // [sequences] containing only that component.
+      for (var component in components.take(components.length - 1)) {
+        addNode(new LiteralNode(component));
+        finishComponent();
+      }
+
+      // For the final component, only end its sequence (by adding a new empty
+      // sequence) if it ends with a separator.
+      addNode(new LiteralNode(components.last));
+      if (node.text.endsWith('/')) finishComponent();
+    }
+
+    finishComponent();
+    return componentsToReturn;
+  }
+
+  String _toRegExp() => nodes.map((node) => node._toRegExp()).join();
+
+  bool operator==(Object other) => other is SequenceNode &&
+      const IterableEquality().equals(nodes, other.nodes);
+
+  int get hashCode => const IterableEquality().hash(nodes);
+
+  String toString() => nodes.join();
+}
+
+/// A node matching zero or more non-separator characters.
+class StarNode extends AstNode {
+  StarNode();
+
+  String _toRegExp() => '[^/]*';
+
+  bool operator==(Object other) => other is StarNode;
+
+  int get hashCode => 0;
+
+  String toString() => '*';
+}
+
+/// A node matching zero or more characters that may be separators.
+class DoubleStarNode extends AstNode {
+  /// The path context for the glob.
+  ///
+  /// This is used to determine what absolute paths look like.
+  final p.Context _context;
+
+  DoubleStarNode(this._context);
+
+  String _toRegExp() {
+    // Double star shouldn't match paths with a leading "../", since these paths
+    // wouldn't be listed with this glob. We only check for "../" at the
+    // beginning since the paths are normalized before being checked against the
+    // glob.
+    var buffer = new StringBuffer()..write(r'(?!^(?:\.\./|');
+
+    // A double star at the beginning of the glob also shouldn't match absolute
+    // paths, since those also wouldn't be listed. Which root patterns we look
+    // for depends on the style of path we're matching.
+    if (_context.style == p.Style.posix) {
+      buffer.write(r'/');
+    } else if (_context.style == p.Style.windows) {
+      buffer.write(r'//|[A-Za-z]:/');
+    } else {
+      assert(_context.style == p.Style.url);
+      buffer.write(r'[a-zA-Z][-+.a-zA-Z\d]*://|/');
+    }
+
+    // Use `[^]` rather than `.` so that it matches newlines as well.
+    buffer.write(r'))[^]*');
+
+    return buffer.toString();
+  }
+
+  bool operator==(Object other) => other is DoubleStarNode;
+
+  int get hashCode => 1;
+
+  String toString() => '**';
+}
+
+/// A node matching a single non-separator character.
+class AnyCharNode extends AstNode {
+  AnyCharNode();
+
+  String _toRegExp() => '[^/]';
+
+  bool operator==(Object other) => other is AnyCharNode;
+
+  int get hashCode => 2;
+
+  String toString() => '?';
+}
+
+/// A node matching a single character in a range of options.
+class RangeNode extends AstNode {
+  /// The ranges matched by this node.
+  ///
+  /// The ends of the ranges are unicode code points.
+  final Set<Range> ranges;
+
+  /// Whether this range was negated.
+  final bool negated;
+
+  RangeNode(Iterable<Range> ranges, {this.negated})
+      : ranges = ranges.toSet();
+
+  OptionsNode flattenOptions() {
+    if (negated || ranges.any((range) => !range.isSingleton)) {
+      return super.flattenOptions();
+    }
+
+    // If a range explicitly lists a set of characters, return each character as
+    // a separate expansion.
+    return new OptionsNode(ranges.map((range) {
+      return new SequenceNode([
+        new LiteralNode(new String.fromCharCodes([range.min]))
+      ]);
+    }));
+  }
+
+  String _toRegExp() {
+    var buffer = new StringBuffer();
+
+    var containsSeparator = ranges.any((range) => range.contains(_SEPARATOR));
+    if (!negated && containsSeparator) {
+      // Add `(?!/)` because ranges are never allowed to match separators.
+      buffer.write('(?!/)');
+    }
+
+    buffer.write('[');
+    if (negated) {
+      buffer.write('^');
+      // If the range doesn't itself exclude separators, exclude them ourselves,
+      // since ranges are never allowed to match them.
+      if (!containsSeparator) buffer.write('/');
+    }
+
+    for (var range in ranges) {
+      var start = new String.fromCharCodes([range.min]);
+      buffer.write(regExpQuote(start));
+      if (range.isSingleton) continue;
+      buffer.write('-');
+      buffer.write(regExpQuote(new String.fromCharCodes([range.max])));
+    }
+
+    buffer.write(']');
+    return buffer.toString();
+  }
+
+  bool operator==(Object other) {
+    if (other is! RangeNode) return false;
+    if ((other as RangeNode).negated != negated) return false;
+    return const SetEquality().equals(ranges, (other as RangeNode).ranges);
+  }
+
+  int get hashCode => (negated ? 1 : 3) * const SetEquality().hash(ranges);
+
+  String toString() {
+    var buffer = new StringBuffer()..write('[');
+    for (var range in ranges) {
+      buffer.writeCharCode(range.min);
+      if (range.isSingleton) continue;
+      buffer.write('-');
+      buffer.writeCharCode(range.max);
+    }
+    buffer.write(']');
+    return buffer.toString();
+  }
+}
+
+/// A node that matches one of several options.
+class OptionsNode extends AstNode {
+  /// The options to match.
+  final List<SequenceNode> options;
+
+  bool get canMatchAbsolute => options.any((node) => node.canMatchAbsolute);
+  bool get canMatchRelative => options.any((node) => node.canMatchRelative);
+
+  OptionsNode(Iterable<SequenceNode> options)
+      : options = options.toList();
+
+  OptionsNode flattenOptions() => new OptionsNode(
+      options.expand((option) => option.flattenOptions().options));
+
+  String _toRegExp() =>
+      '(?:${options.map((option) => option._toRegExp()).join("|")})';
+
+  bool operator==(Object other) => other is OptionsNode &&
+      const UnorderedIterableEquality().equals(options, other.options);
+
+  int get hashCode => const UnorderedIterableEquality().hash(options);
+
+  String toString() => '{${options.join(',')}}';
+}
+
+/// A node that matches a literal string.
+class LiteralNode extends AstNode {
+  /// The string to match.
+  final String text;
+
+  /// The path context for the glob.
+  ///
+  /// This is used to determine whether this could match an absolute path.
+  final p.Context _context;
+
+  bool get canMatchAbsolute {
+    var nativeText = _context.style == p.Style.windows ?
+        text.replaceAll('/', '\\') : text;
+    return _context.isAbsolute(nativeText);
+  }
+
+  bool get canMatchRelative => !canMatchAbsolute;
+
+  LiteralNode(this.text, [this._context]);
+
+  String _toRegExp() => regExpQuote(text);
+
+  bool operator==(Object other) => other is LiteralNode && other.text == text;
+
+  int get hashCode => text.hashCode;
+
+  String toString() => text;
+}
diff --git a/glob/lib/src/list_tree.dart b/glob/lib/src/list_tree.dart
new file mode 100644
index 0000000..3667d63
--- /dev/null
+++ b/glob/lib/src/list_tree.dart
@@ -0,0 +1,420 @@
+// 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 glob.list_tree;
+
+import 'dart:io';
+import 'dart:async';
+
+import 'package:path/path.dart' as p;
+
+import 'ast.dart';
+import 'stream_pool.dart';
+import 'utils.dart';
+
+/// The errno for a file or directory not existing on Mac and Linux.
+const _ENOENT = 2;
+
+/// Another errno we see on Windows when trying to list a non-existent
+/// directory.
+const _ENOENT_WIN = 3;
+
+/// A structure built from a glob that efficiently lists filesystem entities
+/// that match that glob.
+///
+/// This structure is designed to list the minimal number of physical
+/// directories necessary to find everything that matches the glob. For example,
+/// for the glob `foo/{bar,baz}/*`, there's no need to list the working
+/// directory or even `foo/`; only `foo/bar` and `foo/baz` should be listed.
+///
+/// This works by creating a tree of [_ListTreeNode]s, each of which corresponds
+/// to a single of directory nesting in the source glob. Each node has child
+/// nodes associated with globs ([_ListTreeNode.children]), as well as its own
+/// glob ([_ListTreeNode._validator]) that indicates which entities within that
+/// node's directory should be returned.
+///
+/// For example, the glob `foo/{*.dart,b*/*.txt}` creates the following tree:
+///
+///     .
+///     '-- "foo" (validator: "*.dart")
+///         '-- "b*" (validator: "*.txt"
+///
+/// If a node doesn't have a validator, we know we don't have to list it
+/// explicitly.
+///
+/// Nodes can also be marked as "recursive", which means they need to be listed
+/// recursively (usually to support `**`). In this case, they will have no
+/// children; instead, their validator will just encompass the globs that would
+/// otherwise be in their children. For example, the glob
+/// `foo/{**.dart,bar/*.txt}` creates a recursive node for `foo` with the
+/// validator `**.dart,bar/*.txt`.
+///
+/// If the glob contains multiple filesystem roots (e.g. `{C:/,D:/}*.dart`),
+/// each root will have its own tree of nodes. Relative globs use `.` as their
+/// root instead.
+class ListTree {
+  /// A map from filesystem roots to the list tree for those roots.
+  ///
+  /// A relative glob will use `.` as its root.
+  final _trees = new Map<String, _ListTreeNode>();
+
+  /// Whether paths listed might overlap.
+  ///
+  /// If they do, we need to filter out overlapping paths.
+  bool _canOverlap;
+
+  ListTree(AstNode glob) {
+    // The first step in constructing a tree from the glob is to simplify the
+    // problem by eliminating options. [glob.flattenOptions] bubbles all options
+    // (and certain ranges) up to the top level of the glob so we can deal with
+    // them one at a time.
+    var options = glob.flattenOptions();
+
+    for (var option in options.options) {
+      // Since each option doesn't include its own options, we can safely split
+      // it into path components.
+      var components = option.split(p.context);
+      var firstNode = components.first.nodes.first;
+      var root = '.';
+
+      // Determine the root for this option, if it's absolute. If it's not, the
+      // root's just ".".
+      if (firstNode is LiteralNode) {
+        var text = firstNode.text;
+        if (Platform.isWindows) text.replaceAll("/", "\\");
+        if (p.isAbsolute(text)) {
+          // If the path is absolute, the root should be the only thing in the
+          // first component.
+          assert(components.first.nodes.length == 1);
+          root = firstNode.text;
+          components.removeAt(0);
+        }
+      }
+
+      _addGlob(root, components);
+    }
+
+    _canOverlap = _computeCanOverlap();
+  }
+
+  /// Add the glob represented by [components] to the tree under [root].
+  void _addGlob(String root, List<AstNode> components) {
+    // The first [parent] represents the root directory itself. It may be null
+    // here if this is the first option with this particular [root]. If so,
+    // we'll create it below.
+    //
+    // As we iterate through [components], [parent] will be set to
+    // progressively more nested nodes.
+    var parent = _trees[root];
+    for (var i = 0; i < components.length; i++) {
+      var component = components[i];
+      var recursive = component.nodes.any((node) => node is DoubleStarNode);
+      var complete = i == components.length - 1;
+
+      // If the parent node for this level of nesting already exists, the new
+      // option will be added to it as additional validator options and/or
+      // additional children.
+      //
+      // If the parent doesn't exist, we'll create it in one of the else
+      // clauses below.
+      if (parent != null) {
+        if (parent.isRecursive || recursive) {
+          // If [component] is recursive, mark [parent] as recursive. This
+          // will cause all of its children to be folded into its validator.
+          // If [parent] was already recursive, this is a no-op.
+          parent.makeRecursive();
+
+          // Add [component] and everything nested beneath it as an option to
+          // [parent]. Since [parent] is recursive, it will recursively list
+          // everything beneath it and filter them with one big glob.
+          parent.addOption(_join(components.sublist(i)));
+          return;
+        } else if (complete) {
+          // If [component] is the last component, add it to [parent]'s
+          // validator but not to its children.
+          parent.addOption(component);
+        } else {
+          // On the other hand if there are more components, add [component]
+          // to [parent]'s children and not its validator. Since we process
+          // each option's components separately, the same component is never
+          // both a validator and a child.
+          if (!parent.children.containsKey(component)) {
+            parent.children[component] = new _ListTreeNode();
+          }
+          parent = parent.children[component];
+        }
+      } else if (recursive) {
+        _trees[root] = new _ListTreeNode.recursive(
+            _join(components.sublist(i)));
+        return;
+      } else if (complete) {
+        _trees[root] = new _ListTreeNode()..addOption(component);
+      } else {
+        _trees[root] = new _ListTreeNode();
+        _trees[root].children[component] = new _ListTreeNode();
+        parent = _trees[root].children[component];
+      }
+    }
+  }
+
+  /// Computes the value for [_canOverlap].
+  bool _computeCanOverlap() {
+    // If this can list a relative path and an absolute path, the former may be
+    // contained within the latter.
+    if (_trees.length > 1 && _trees.containsKey('.')) return true;
+
+    // Otherwise, this can only overlap if the tree beneath any given root could
+    // overlap internally.
+    return _trees.values.any((node) => node.canOverlap);
+  }
+
+  /// List all entities that match this glob beneath [root].
+  Stream<FileSystemEntity> list({String root, bool followLinks: true}) {
+    if (root == null) root = '.';
+    var pool = new StreamPool();
+    for (var rootDir in _trees.keys) {
+      var dir = rootDir == '.' ? root : rootDir;
+      pool.add(_trees[rootDir].list(dir, followLinks: followLinks));
+    }
+    pool.closeWhenEmpty();
+
+    if (!_canOverlap) return pool.stream;
+
+    // TODO(nweiz): Rather than filtering here, avoid double-listing directories
+    // in the first place.
+    var seen = new Set();
+    return pool.stream.where((entity) {
+      if (seen.contains(entity.path)) return false;
+      seen.add(entity.path);
+      return true;
+    });
+  }
+
+  /// Synchronosuly list all entities that match this glob beneath [root].
+  List<FileSystemEntity> listSync({String root, bool followLinks: true}) {
+    if (root == null) root = '.';
+
+    var result = _trees.keys.expand((rootDir) {
+      var dir = rootDir == '.' ? root : rootDir;
+      return _trees[rootDir].listSync(dir, followLinks: followLinks);
+    });
+
+    if (!_canOverlap) return result.toList();
+
+    // TODO(nweiz): Rather than filtering here, avoid double-listing directories
+    // in the first place.
+    var seen = new Set();
+    return result.where((entity) {
+      if (seen.contains(entity.path)) return false;
+      seen.add(entity.path);
+      return true;
+    }).toList();
+  }
+}
+
+/// A single node in a [ListTree].
+class _ListTreeNode {
+  /// This node's child nodes, by their corresponding globs.
+  ///
+  /// Each child node will only be listed on directories that match its glob.
+  ///
+  /// This may be `null`, indicating that this node should be listed
+  /// recursively.
+  Map<SequenceNode, _ListTreeNode> children;
+
+  /// This node's validator.
+  ///
+  /// This determines which entities will ultimately be emitted when [list] is
+  /// called.
+  OptionsNode _validator;
+
+  /// Whether this node is recursive.
+  ///
+  /// A recursive node has no children and is listed recursively.
+  bool get isRecursive => children == null;
+
+  /// Whether this node doesn't itself need to be listed.
+  ///
+  /// If a node has no validator and all of its children are literal filenames,
+  /// there's no need to list its contents. We can just directly traverse into
+  /// its children.
+  bool get _isIntermediate {
+    if (_validator != null) return false;
+    return children.keys.every((sequence) =>
+        sequence.nodes.length == 1 && sequence.nodes.first is LiteralNode);
+  }
+
+  /// Returns whether listing this node might return overlapping results.
+  bool get canOverlap {
+    // A recusive node can never overlap with itself, because it will only ever
+    // involve a single call to [Directory.list] that's then filtered with
+    // [_validator].
+    if (isRecursive) return false;
+
+    // If there's more than one child node and at least one of the children is
+    // dynamic (that is, matches more than just a literal string), there may be
+    // overlap.
+    if (children.length > 1 && children.keys.any((sequence) =>
+          sequence.nodes.length > 1 || sequence.nodes.single is! LiteralNode)) {
+      return true;
+    }
+
+    return children.values.any((node) => node.canOverlap);
+  }
+
+  /// Creates a node with no children and no validator.
+  _ListTreeNode()
+      : children = new Map<SequenceNode, _ListTreeNode>(),
+        _validator = null;
+
+  /// Creates a recursive node the given [validator].
+  _ListTreeNode.recursive(SequenceNode validator)
+      : children = null,
+        _validator = new OptionsNode([validator]);
+
+  /// Transforms this into recursive node, folding all its children into its
+  /// validator.
+  void makeRecursive() {
+    if (isRecursive) return;
+    _validator = new OptionsNode(children.keys.map((sequence) {
+      var child = children[sequence];
+      child.makeRecursive();
+      return _join([sequence, child._validator]);
+    }));
+    children = null;
+  }
+
+  /// Adds [validator] to this node's existing validator.
+  void addOption(SequenceNode validator) {
+    if (_validator == null) {
+      _validator = new OptionsNode([validator]);
+    } else {
+      _validator.options.add(validator);
+    }
+  }
+
+  /// Lists all entities within [dir] matching this node or its children.
+  ///
+  /// This may return duplicate entities. These will be filtered out in
+  /// [ListTree.list].
+  Stream<FileSystemEntity> list(String dir, {bool followLinks: true}) {
+    if (isRecursive) {
+      return new Directory(dir).list(recursive: true, followLinks: followLinks)
+          .where((entity) => _matches(entity.path.substring(dir.length + 1)));
+    }
+
+    var resultPool = new StreamPool();
+
+    // Don't spawn extra [Directory.list] calls when we already know exactly
+    // which subdirectories we're interested in.
+    if (_isIntermediate) {
+      children.forEach((sequence, child) {
+        resultPool.add(child.list(p.join(dir, sequence.nodes.single.text),
+            followLinks: followLinks));
+      });
+      resultPool.closeWhenEmpty();
+      return resultPool.stream;
+    }
+
+    var resultController = new StreamController(sync: true);
+    resultPool.add(resultController.stream);
+    new Directory(dir).list(followLinks: followLinks).listen((entity) {
+      var basename = entity.path.substring(dir.length + 1);
+      if (_matches(basename)) resultController.add(entity);
+
+      children.forEach((sequence, child) {
+        if (entity is! Directory) return;
+        if (!sequence.matches(basename)) return;
+        var stream = child.list(p.join(dir, basename), followLinks: followLinks)
+            .handleError((_) {}, test: (error) {
+          // Ignore errors from directories not existing. We do this here so
+          // that we only ignore warnings below wild cards. For example, the
+          // glob "foo/bar/*/baz" should fail if "foo/bar" doesn't exist but
+          // succeed if "foo/bar/qux/baz" doesn't exist.
+          return error is FileSystemException &&
+              (error.osError.errorCode == _ENOENT ||
+              error.osError.errorCode == _ENOENT_WIN);
+        });
+        resultPool.add(stream);
+      });
+    },
+        onError: resultController.addError,
+        onDone: resultController.close);
+
+    resultPool.closeWhenEmpty();
+    return resultPool.stream;
+  }
+
+  /// Synchronously lists all entities within [dir] matching this node or its
+  /// children.
+  ///
+  /// This may return duplicate entities. These will be filtered out in
+  /// [ListTree.listSync].
+  Iterable<FileSystemEntity> listSync(String dir, {bool followLinks: true}) {
+    if (isRecursive) {
+      return new Directory(dir)
+          .listSync(recursive: true, followLinks: followLinks)
+          .where((entity) => _matches(entity.path.substring(dir.length + 1)));
+    }
+
+    // Don't spawn extra [Directory.listSync] calls when we already know exactly
+    // which subdirectories we're interested in.
+    if (_isIntermediate) {
+      return children.keys.expand((sequence) {
+        return children[sequence].listSync(
+            p.join(dir, sequence.nodes.single.text), followLinks: followLinks);
+      });
+    }
+
+    return new Directory(dir).listSync(followLinks: followLinks)
+        .expand((entity) {
+      var entities = [];
+      var basename = entity.path.substring(dir.length + 1);
+      if (_matches(basename)) entities.add(entity);
+      if (entity is! Directory) return entities;
+
+      entities.addAll(children.keys
+          .where((sequence) => sequence.matches(basename))
+          .expand((sequence) {
+        try {
+          return children[sequence].listSync(
+              p.join(dir, basename), followLinks: followLinks).toList();
+        } on FileSystemException catch (error) {
+          // Ignore errors from directories not existing. We do this here so
+          // that we only ignore warnings below wild cards. For example, the
+          // glob "foo/bar/*/baz" should fail if "foo/bar" doesn't exist but
+          // succeed if "foo/bar/qux/baz" doesn't exist.
+          if (error.osError.errorCode == _ENOENT ||
+              error.osError.errorCode == _ENOENT_WIN) {
+            return const [];
+          } else {
+            rethrow;
+          }
+        }
+      }));
+
+      return entities;
+    });
+  }
+
+  /// Returns whether the native [path] matches [_validator].
+  bool _matches(String path) {
+    if (_validator == null) return false;
+    return _validator.matches(toPosixPath(p.context, path));
+  }
+
+  String toString() => "($_validator) $children";
+}
+
+/// Joins each [components] into a new glob where each component is separated by
+/// a path separator.
+SequenceNode _join(Iterable<AstNode> components) {
+  var componentsList = components.toList();
+  var nodes = [componentsList.removeAt(0)];
+  for (var component in componentsList) {
+    nodes.add(new LiteralNode('/'));
+    nodes.add(component);
+  }
+  return new SequenceNode(nodes);
+}
diff --git a/glob/lib/src/parser.dart b/glob/lib/src/parser.dart
new file mode 100644
index 0000000..5dac146
--- /dev/null
+++ b/glob/lib/src/parser.dart
@@ -0,0 +1,173 @@
+// 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 glob.single_component;
+
+import 'package:path/path.dart' as p;
+import 'package:string_scanner/string_scanner.dart';
+
+import 'ast.dart';
+import 'utils.dart';
+
+const _HYPHEN = 0x2D;
+const _SLASH = 0x2F;
+
+/// A parser for globs.
+class Parser {
+  /// The scanner used to scan the source.
+  final StringScanner _scanner;
+
+  /// The path context for the glob.
+  final p.Context _context;
+
+  Parser(String component, this._context)
+      : _scanner = new StringScanner(component);
+
+  /// Parses an entire glob.
+  SequenceNode parse() => _parseSequence();
+
+  /// Parses a [SequenceNode].
+  ///
+  /// If [inOptions] is true, this is parsing within an [OptionsNode].
+  SequenceNode _parseSequence({bool inOptions: false}) {
+    var nodes = [];
+
+    if (_scanner.isDone) {
+      _scanner.error('expected a glob.', position: 0, length: 0);
+    }
+
+    while (!_scanner.isDone) {
+      if (inOptions && (_scanner.matches(',') || _scanner.matches('}'))) break;
+      nodes.add(_parseNode(inOptions: inOptions));
+    }
+
+    return new SequenceNode(nodes);
+  }
+
+  /// Parses an [AstNode].
+  ///
+  /// If [inOptions] is true, this is parsing within an [OptionsNode].
+  AstNode _parseNode({bool inOptions: false}) {
+    var star = _parseStar();
+    if (star != null) return star;
+
+    var anyChar = _parseAnyChar();
+    if (anyChar != null) return anyChar;
+
+    var range = _parseRange();
+    if (range != null) return range;
+
+    var options = _parseOptions();
+    if (options != null) return options;
+
+    return _parseLiteral(inOptions: inOptions);
+  }
+
+  /// Tries to parse a [StarNode] or a [DoubleStarNode].
+  ///
+  /// Returns `null` if there's not one to parse.
+  AstNode _parseStar() {
+    if (!_scanner.scan('*')) return null;
+    return _scanner.scan('*') ? new DoubleStarNode(_context) : new StarNode();
+  }
+
+  /// Tries to parse an [AnyCharNode].
+  ///
+  /// Returns `null` if there's not one to parse.
+  AstNode _parseAnyChar() {
+    if (!_scanner.scan('?')) return null;
+    return new AnyCharNode();
+  }
+
+  /// Tries to parse an [RangeNode].
+  ///
+  /// Returns `null` if there's not one to parse.
+  AstNode _parseRange() {
+    if (!_scanner.scan('[')) return null;
+    if (_scanner.matches(']')) _scanner.error('unexpected "]".');
+    var negated = _scanner.scan('!') || _scanner.scan('^');
+
+    readRangeChar() {
+      var char = _scanner.readChar();
+      if (negated || char != _SLASH) return char;
+      _scanner.error('"/" may not be used in a range.',
+          position: _scanner.position - 1);
+    }
+
+    var ranges = [];
+    while (!_scanner.scan(']')) {
+      var start = _scanner.position;
+      // Allow a backslash to escape a character.
+      _scanner.scan('\\');
+      var char = readRangeChar();
+
+      if (_scanner.scan('-')) {
+        if (_scanner.matches(']')) {
+          ranges.add(new Range.singleton(char));
+          ranges.add(new Range.singleton(_HYPHEN));
+          continue;
+        }
+
+        // Allow a backslash to escape a character.
+        _scanner.scan('\\');
+
+        var end = readRangeChar();
+
+        if (end < char) {
+          _scanner.error("Range out of order.",
+              position: start,
+              length: _scanner.position - start);
+        }
+        ranges.add(new Range(char, end));
+      } else {
+        ranges.add(new Range.singleton(char));
+      }
+    }
+
+    return new RangeNode(ranges, negated: negated);
+  }
+
+  /// Tries to parse an [OptionsNode].
+  ///
+  /// Returns `null` if there's not one to parse.
+  AstNode _parseOptions() {
+    if (!_scanner.scan('{')) return null;
+    if (_scanner.matches('}')) _scanner.error('unexpected "}".');
+
+    var options = [];
+    do {
+      options.add(_parseSequence(inOptions: true));
+    } while (_scanner.scan(','));
+
+    // Don't allow single-option blocks.
+    if (options.length == 1) _scanner.expect(',');
+    _scanner.expect('}');
+
+    return new OptionsNode(options);
+  }
+
+  /// Parses a [LiteralNode].
+  AstNode _parseLiteral({bool inOptions: false}) {
+    // If we're in an options block, we want to stop parsing as soon as we hit a
+    // comma. Otherwise, commas are fair game for literals.
+    var regExp = new RegExp(
+        inOptions ? r'[^*{[?\\}\],()]*' : r'[^*{[?\\}\]()]*');
+
+    _scanner.scan(regExp);
+    var buffer = new StringBuffer()..write(_scanner.lastMatch[0]);
+
+    while (_scanner.scan('\\')) {
+      buffer.writeCharCode(_scanner.readChar());
+      _scanner.scan(regExp);
+      buffer.write(_scanner.lastMatch[0]);
+    }
+
+    for (var char in const [']', '(', ')']) {
+      if (_scanner.matches(char)) _scanner.error('unexpected "$char"');
+    }
+    if (!inOptions && _scanner.matches('}')) _scanner.error('unexpected "}"');
+
+    return new LiteralNode(buffer.toString(), _context);
+  }
+}
diff --git a/glob/lib/src/stream_pool.dart b/glob/lib/src/stream_pool.dart
new file mode 100644
index 0000000..6417513
--- /dev/null
+++ b/glob/lib/src/stream_pool.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2013, 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 glob.stream_pool;
+
+import 'dart:async';
+
+/// A pool of streams whose events are unified and emitted through a central
+/// stream.
+class StreamPool<T> {
+  /// The stream through which all events from streams in the pool are emitted.
+  Stream<T> get stream => _controller.stream;
+  final StreamController<T> _controller;
+
+  /// Subscriptions to the streams that make up the pool.
+  final _subscriptions = new Map<Stream<T>, StreamSubscription<T>>();
+
+  /// Whether this pool should be closed when it becomes empty.
+  bool _closeWhenEmpty = false;
+
+  /// Creates a new stream pool that only supports a single subscriber.
+  ///
+  /// Any events from broadcast streams in the pool will be buffered until a
+  /// listener is subscribed.
+  StreamPool()
+      // Create the controller as sync so that any sync input streams will be
+      // forwarded synchronously. Async input streams will have their asynchrony
+      // preserved, since _controller.add will be called asynchronously.
+      : _controller = new StreamController<T>(sync: true);
+
+  /// Creates a new stream pool where [stream] can be listened to more than
+  /// once.
+  ///
+  /// Any events from buffered streams in the pool will be emitted immediately,
+  /// regardless of whether [stream] has any subscribers.
+  StreamPool.broadcast()
+      // Create the controller as sync so that any sync input streams will be
+      // forwarded synchronously. Async input streams will have their asynchrony
+      // preserved, since _controller.add will be called asynchronously.
+      : _controller = new StreamController<T>.broadcast(sync: true);
+
+  /// Adds [stream] as a member of this pool.
+  ///
+  /// Any events from [stream] will be emitted through [this.stream]. If
+  /// [stream] is sync, they'll be emitted synchronously; if [stream] is async,
+  /// they'll be emitted asynchronously.
+  void add(Stream<T> stream) {
+    if (_subscriptions.containsKey(stream)) return;
+    _subscriptions[stream] = stream.listen(_controller.add,
+        onError: _controller.addError,
+        onDone: () => remove(stream));
+  }
+
+  /// Removes [stream] as a member of this pool.
+  void remove(Stream<T> stream) {
+    var subscription = _subscriptions.remove(stream);
+    if (subscription != null) subscription.cancel();
+    if (_closeWhenEmpty && _subscriptions.isEmpty) close();
+  }
+
+  /// Removes all streams from this pool and closes [stream].
+  void close() {
+    for (var subscription in _subscriptions.values) {
+      subscription.cancel();
+    }
+    _subscriptions.clear();
+    _controller.close();
+  }
+
+  /// The next time this pool becomes empty, close it.
+  void closeWhenEmpty() {
+    if (_subscriptions.isEmpty) close();
+    _closeWhenEmpty = true;
+  }
+}
diff --git a/glob/lib/src/utils.dart b/glob/lib/src/utils.dart
new file mode 100644
index 0000000..f17f2ed
--- /dev/null
+++ b/glob/lib/src/utils.dart
@@ -0,0 +1,78 @@
+// 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 glob.utils;
+
+import 'package:path/path.dart' as p;
+
+/// A range from [min] to [max], inclusive.
+class Range {
+  /// The minimum value included by the range.
+  final int min;
+
+  /// The maximum value included by the range.
+  final int max;
+
+  /// Whether this range covers only a single number.
+  bool get isSingleton => min == max;
+
+  Range(this.min, this.max);
+
+  /// Returns a range that covers only [value].
+  Range.singleton(int value)
+      : this(value, value);
+
+  /// Whether [this] contains [value].
+  bool contains(int value) => value >= min && value <= max;
+
+  bool operator==(Object other) => other is Range &&
+      other.min == min && other.max == max;
+
+  int get hashCode => 3 * min + 7 * max;
+}
+
+/// An implementation of [Match] constructed by [Glob]s.
+class GlobMatch implements Match {
+  final String input;
+  final Pattern pattern;
+  final int start = 0;
+
+  int get end => input.length;
+  int get groupCount => 0;
+
+  GlobMatch(this.input, this.pattern);
+
+  String operator [](int group) => this.group(group);
+
+  String group(int group) {
+    if (group != 0) throw new RangeError.range(group, 0, 0);
+    return input;
+  }
+
+  List<String> groups(List<int> groupIndices) =>
+      groupIndices.map((index) => group(index)).toList();
+}
+
+final _quote = new RegExp(r"[+*?{}|[\]\\().^$-]");
+
+/// Returns [contents] with characters that are meaningful in regular
+/// expressions backslash-escaped.
+String regExpQuote(String contents) =>
+    contents.replaceAllMapped(_quote, (char) => "\\${char[0]}");
+
+/// Returns [path] with all its separators replaced with forward slashes.
+///
+/// This is useful when converting from Windows paths to globs.
+String separatorToForwardSlash(String path) {
+  if (p.style != p.Style.windows) return path;
+  return path.replaceAll('\\', '/');
+}
+
+/// Returns [path] which follows [context] converted to the POSIX format that
+/// globs match against.
+String toPosixPath(p.Context context, String path) {
+  if (context.style == p.Style.windows) return path.replaceAll('\\', '/');
+  if (context.style == p.Style.url) return Uri.decodeFull(path);
+  return path;
+}
diff --git a/glob/pubspec.yaml b/glob/pubspec.yaml
new file mode 100644
index 0000000..000cfa8
--- /dev/null
+++ b/glob/pubspec.yaml
@@ -0,0 +1,12 @@
+name: glob
+version: 1.0.4
+author: "Dart Team <misc@dartlang.org>"
+homepage: https://github.com/dart-lang/glob
+description: Bash-style filename globbing.
+dependencies:
+  collection: ">=1.1.0 <2.0.0"
+  path: ">=1.0.0 <2.0.0"
+  string_scanner: ">=0.1.0 <0.2.0"
+dev_dependencies:
+  unittest: ">=0.11.0 <0.12.0"
+  scheduled_test: ">=0.11.2 <0.12.0"
diff --git a/html/lib/dom.dart b/html/lib/dom.dart
new file mode 100644
index 0000000..76eb85f
--- /dev/null
+++ b/html/lib/dom.dart
@@ -0,0 +1,998 @@
+/// A simple tree API that results from parsing html. Intended to be compatible
+/// with dart:html, but it is missing many types and APIs.
+library dom;
+
+// TODO(jmesserly): lots to do here. Originally I wanted to generate this using
+// our Blink IDL generator, but another idea is to directly use the excellent
+// http://dom.spec.whatwg.org/ and http://html.spec.whatwg.org/ and just
+// implement that.
+
+import 'dart:collection';
+import 'package:source_span/source_span.dart';
+
+import 'src/constants.dart';
+import 'src/css_class_set.dart';
+import 'src/list_proxy.dart';
+import 'src/query_selector.dart' as query;
+import 'src/token.dart';
+import 'src/tokenizer.dart';
+import 'dom_parsing.dart';
+import 'parser.dart';
+
+export 'src/css_class_set.dart' show CssClassSet;
+
+// TODO(jmesserly): this needs to be replaced by an AttributeMap for attributes
+// that exposes namespace info.
+class AttributeName implements Comparable {
+  /// The namespace prefix, e.g. `xlink`.
+  final String prefix;
+
+  /// The attribute name, e.g. `title`.
+  final String name;
+
+  /// The namespace url, e.g. `http://www.w3.org/1999/xlink`
+  final String namespace;
+
+  const AttributeName(this.prefix, this.name, this.namespace);
+
+  String toString() {
+    // Implement:
+    // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#serializing-html-fragments
+    // If we get here we know we are xml, xmlns, or xlink, because of
+    // [HtmlParser.adjustForeignAttriubtes] is the only place we create
+    // an AttributeName.
+    return prefix != null ? '$prefix:$name' : name;
+  }
+
+  int get hashCode {
+    int h = prefix.hashCode;
+    h = 37 * (h & 0x1FFFFF) + name.hashCode;
+    h = 37 * (h & 0x1FFFFF) + namespace.hashCode;
+    return h & 0x3FFFFFFF;
+  }
+
+  int compareTo(other) {
+    // Not sure about this sort order
+    if (other is! AttributeName) return 1;
+    int cmp = (prefix != null ? prefix : "")
+        .compareTo((other.prefix != null ? other.prefix : ""));
+    if (cmp != 0) return cmp;
+    cmp = name.compareTo(other.name);
+    if (cmp != 0) return cmp;
+    return namespace.compareTo(other.namespace);
+  }
+
+  bool operator ==(x) {
+    if (x is! AttributeName) return false;
+    return prefix == x.prefix && name == x.name && namespace == x.namespace;
+  }
+}
+
+// http://dom.spec.whatwg.org/#parentnode
+abstract class _ParentNode implements Node {
+  // TODO(jmesserly): this is only a partial implementation
+
+  /// Seaches for the first descendant node matching the given selectors, using
+  /// a preorder traversal.
+  ///
+  /// NOTE: Not all selectors from
+  /// [selectors level 4](http://dev.w3.org/csswg/selectors-4/)
+  /// are implemented. For example, nth-child does not implement An+B syntax
+  /// and *-of-type is not implemented. If a selector is not implemented this
+  /// method will throw [UniplmentedError].
+  Element querySelector(String selector) => query.querySelector(this, selector);
+
+  /// Returns all descendant nodes matching the given selectors, using a
+  /// preorder traversal.
+  ///
+  /// NOTE: Not all selectors from
+  /// [selectors level 4](http://dev.w3.org/csswg/selectors-4/)
+  /// are implemented. For example, nth-child does not implement An+B syntax
+  /// and *-of-type is not implemented. If a selector is not implemented this
+  /// method will throw [UniplmentedError].
+  List<Element> querySelectorAll(String selector) =>
+      query.querySelectorAll(this, selector);
+}
+
+// http://dom.spec.whatwg.org/#interface-nonelementparentnode
+abstract class _NonElementParentNode implements _ParentNode {
+  // TODO(jmesserly): could be faster, should throw on invalid id.
+  Element getElementById(String id) => querySelector('#$id');
+}
+
+// This doesn't exist as an interface in the spec, but it's useful to merge
+// common methods from these:
+// http://dom.spec.whatwg.org/#interface-document
+// http://dom.spec.whatwg.org/#element
+abstract class _ElementAndDocument implements _ParentNode {
+  // TODO(jmesserly): could be faster, should throw on invalid tag/class names.
+
+  List<Element> getElementsByTagName(String localName) =>
+      querySelectorAll(localName);
+
+  List<Element> getElementsByClassName(String classNames) => querySelectorAll(
+      classNames.splitMapJoin(' ',
+          onNonMatch: (m) => m.isNotEmpty ? '.$m' : m, onMatch: (m) => ''));
+}
+
+/// Really basic implementation of a DOM-core like Node.
+abstract class Node {
+  static const int ATTRIBUTE_NODE = 2;
+  static const int CDATA_SECTION_NODE = 4;
+  static const int COMMENT_NODE = 8;
+  static const int DOCUMENT_FRAGMENT_NODE = 11;
+  static const int DOCUMENT_NODE = 9;
+  static const int DOCUMENT_TYPE_NODE = 10;
+  static const int ELEMENT_NODE = 1;
+  static const int ENTITY_NODE = 6;
+  static const int ENTITY_REFERENCE_NODE = 5;
+  static const int NOTATION_NODE = 12;
+  static const int PROCESSING_INSTRUCTION_NODE = 7;
+  static const int TEXT_NODE = 3;
+
+  /// The parent of the current node (or null for the document node).
+  Node parentNode;
+
+  /// The parent element of this node.
+  ///
+  /// Returns null if this node either does not have a parent or its parent is
+  /// not an element.
+  Element get parent => parentNode is Element ? parentNode : null;
+
+  // TODO(jmesserly): should move to Element.
+  /// A map holding name, value pairs for attributes of the node.
+  ///
+  /// Note that attribute order needs to be stable for serialization, so we use
+  /// a LinkedHashMap. Each key is a [String] or [AttributeName].
+  LinkedHashMap<dynamic, String> attributes = new LinkedHashMap();
+
+  /// A list of child nodes of the current node. This must
+  /// include all elements but not necessarily other node types.
+  final NodeList nodes = new NodeList._();
+
+  List<Element> _elements;
+
+  // TODO(jmesserly): consider using an Expando for this, and put it in
+  // dom_parsing. Need to check the performance affect.
+  /// The source span of this node, if it was created by the [HtmlParser].
+  FileSpan sourceSpan;
+
+  /// The attribute spans if requested. Otherwise null.
+  LinkedHashMap<dynamic, FileSpan> _attributeSpans;
+  LinkedHashMap<dynamic, FileSpan> _attributeValueSpans;
+
+  Node._() {
+    nodes._parent = this;
+  }
+
+  /// If [sourceSpan] is available, this contains the spans of each attribute.
+  /// The span of an attribute is the entire attribute, including the name and
+  /// quotes (if any). For example, the span of "attr" in `<a attr="value">`
+  /// would be the text `attr="value"`.
+  LinkedHashMap<dynamic, FileSpan> get attributeSpans {
+    _ensureAttributeSpans();
+    return _attributeSpans;
+  }
+
+  /// If [sourceSpan] is available, this contains the spans of each attribute's
+  /// value. Unlike [attributeSpans], this span will inlcude only the value.
+  /// For example, the value span of "attr" in `<a attr="value">` would be the
+  /// text `value`.
+  LinkedHashMap<dynamic, FileSpan> get attributeValueSpans {
+    _ensureAttributeSpans();
+    return _attributeValueSpans;
+  }
+
+  List<Element> get children {
+    if (_elements == null) {
+      _elements = new FilteredElementList(this);
+    }
+    return _elements;
+  }
+
+  /// Returns a copy of this node.
+  ///
+  /// If [deep] is `true`, then all of this node's children and decendents are
+  /// copied as well. If [deep] is `false`, then only this node is copied.
+  Node clone(bool deep);
+
+  int get nodeType;
+
+  // http://domparsing.spec.whatwg.org/#extensions-to-the-element-interface
+  String get _outerHtml {
+    var str = new StringBuffer();
+    _addOuterHtml(str);
+    return str.toString();
+  }
+
+  String get _innerHtml {
+    var str = new StringBuffer();
+    _addInnerHtml(str);
+    return str.toString();
+  }
+
+  // Implemented per: http://dom.spec.whatwg.org/#dom-node-textcontent
+  String get text => null;
+  set text(String value) {}
+
+  void append(Node node) => nodes.add(node);
+
+  Node get firstChild => nodes.isNotEmpty ? nodes[0] : null;
+
+  void _addOuterHtml(StringBuffer str);
+
+  void _addInnerHtml(StringBuffer str) {
+    for (Node child in nodes) child._addOuterHtml(str);
+  }
+
+  Node remove() {
+    // TODO(jmesserly): is parent == null an error?
+    if (parentNode != null) {
+      parentNode.nodes.remove(this);
+    }
+    return this;
+  }
+
+  /// Insert [node] as a child of the current node, before [refNode] in the
+  /// list of child nodes. Raises [UnsupportedOperationException] if [refNode]
+  /// is not a child of the current node. If refNode is null, this adds to the
+  /// end of the list.
+  void insertBefore(Node node, Node refNode) {
+    if (refNode == null) {
+      nodes.add(node);
+    } else {
+      nodes.insert(nodes.indexOf(refNode), node);
+    }
+  }
+
+  /// Replaces this node with another node.
+  Node replaceWith(Node otherNode) {
+    if (parentNode == null) {
+      throw new UnsupportedError('Node must have a parent to replace it.');
+    }
+    parentNode.nodes[parentNode.nodes.indexOf(this)] = otherNode;
+    return this;
+  }
+
+  // TODO(jmesserly): should this be a property or remove?
+  /// Return true if the node has children or text.
+  bool hasContent() => nodes.length > 0;
+
+  /// Move all the children of the current node to [newParent].
+  /// This is needed so that trees that don't store text as nodes move the
+  /// text in the correct way.
+  void reparentChildren(Node newParent) {
+    newParent.nodes.addAll(nodes);
+    nodes.clear();
+  }
+
+  bool hasChildNodes() => !nodes.isEmpty;
+
+  bool contains(Node node) => nodes.contains(node);
+
+  /// Initialize [attributeSpans] using [sourceSpan].
+  void _ensureAttributeSpans() {
+    if (_attributeSpans != null) return;
+
+    _attributeSpans = new LinkedHashMap<dynamic, FileSpan>();
+    _attributeValueSpans = new LinkedHashMap<dynamic, FileSpan>();
+
+    if (sourceSpan == null) return;
+
+    var tokenizer = new HtmlTokenizer(sourceSpan.text,
+        generateSpans: true, attributeSpans: true);
+
+    tokenizer.moveNext();
+    var token = tokenizer.current as StartTagToken;
+
+    if (token.attributeSpans == null) return; // no attributes
+
+    for (var attr in token.attributeSpans) {
+      var offset = sourceSpan.start.offset;
+      _attributeSpans[attr.name] =
+          sourceSpan.file.span(offset + attr.start, offset + attr.end);
+      if (attr.startValue != null) {
+        _attributeValueSpans[attr.name] = sourceSpan.file.span(
+            offset + attr.startValue, offset + attr.endValue);
+      }
+    }
+  }
+
+  _clone(Node shallowClone, bool deep) {
+    if (deep) {
+      for (var child in nodes) {
+        shallowClone.append(child.clone(true));
+      }
+    }
+    return shallowClone;
+  }
+}
+
+class Document extends Node
+    with _ParentNode, _NonElementParentNode, _ElementAndDocument {
+  Document() : super._();
+  factory Document.html(String html) => parse(html);
+
+  int get nodeType => Node.DOCUMENT_NODE;
+
+  // TODO(jmesserly): optmize this if needed
+  Element get documentElement => querySelector('html');
+  Element get head => documentElement.querySelector('head');
+  Element get body => documentElement.querySelector('body');
+
+  /// Returns a fragment of HTML or XML that represents the element and its
+  /// contents.
+  // TODO(jmesserly): this API is not specified in:
+  // <http://domparsing.spec.whatwg.org/> nor is it in dart:html, instead
+  // only Element has outerHtml. However it is quite useful. Should we move it
+  // to dom_parsing, where we keep other custom APIs?
+  String get outerHtml => _outerHtml;
+
+  String toString() => "#document";
+
+  void _addOuterHtml(StringBuffer str) => _addInnerHtml(str);
+
+  Document clone(bool deep) => _clone(new Document(), deep);
+
+  Element createElement(String tag) => new Element.tag(tag);
+
+  // TODO(jmesserly): this is only a partial implementation of:
+  // http://dom.spec.whatwg.org/#dom-document-createelementns
+  Element createElementNS(String namespaceUri, String tag) {
+    if (namespaceUri == '') namespaceUri = null;
+    return new Element._(tag, namespaceUri);
+  }
+
+  DocumentFragment createDocumentFragment() => new DocumentFragment();
+}
+
+class DocumentFragment extends Node with _ParentNode, _NonElementParentNode {
+  DocumentFragment() : super._();
+  factory DocumentFragment.html(String html) => parseFragment(html);
+
+  int get nodeType => Node.DOCUMENT_FRAGMENT_NODE;
+
+  /// Returns a fragment of HTML or XML that represents the element and its
+  /// contents.
+  // TODO(jmesserly): this API is not specified in:
+  // <http://domparsing.spec.whatwg.org/> nor is it in dart:html, instead
+  // only Element has outerHtml. However it is quite useful. Should we move it
+  // to dom_parsing, where we keep other custom APIs?
+  String get outerHtml => _outerHtml;
+
+  String toString() => "#document-fragment";
+
+  DocumentFragment clone(bool deep) => _clone(new DocumentFragment(), deep);
+
+  void _addOuterHtml(StringBuffer str) => _addInnerHtml(str);
+
+  String get text => _getText(this);
+  set text(String value) => _setText(this, value);
+}
+
+class DocumentType extends Node {
+  final String name;
+  final String publicId;
+  final String systemId;
+
+  DocumentType(String name, this.publicId, this.systemId)
+      // Note: once Node.tagName is removed, don't pass "name" to super
+      : name = name,
+        super._();
+
+  int get nodeType => Node.DOCUMENT_TYPE_NODE;
+
+  String toString() {
+    if (publicId != null || systemId != null) {
+      // TODO(jmesserly): the html5 serialization spec does not add these. But
+      // it seems useful, and the parser can handle it, so for now keeping it.
+      var pid = publicId != null ? publicId : '';
+      var sid = systemId != null ? systemId : '';
+      return '<!DOCTYPE $name "$pid" "$sid">';
+    } else {
+      return '<!DOCTYPE $name>';
+    }
+  }
+
+  void _addOuterHtml(StringBuffer str) {
+    str.write(toString());
+  }
+
+  DocumentType clone(bool deep) => new DocumentType(name, publicId, systemId);
+}
+
+class Text extends Node {
+  /// The text node's data, stored as either a String or StringBuffer.
+  /// We support storing a StringBuffer here to support fast [appendData].
+  /// It will flatten back to a String on read.
+  var _data;
+
+  Text(String data) : _data = data != null ? data : '', super._();
+
+  int get nodeType => Node.TEXT_NODE;
+
+  String get data => _data = _data.toString();
+  set data(String value) {
+    _data = value != null ? value : '';
+  }
+
+  String toString() => '"$data"';
+
+  void _addOuterHtml(StringBuffer str) => writeTextNodeAsHtml(str, this);
+
+  Text clone(bool deep) => new Text(data);
+
+  void appendData(String data) {
+    if (_data is! StringBuffer) _data = new StringBuffer(_data);
+    StringBuffer sb = _data;
+    sb.write(data);
+  }
+
+  String get text => data;
+  set text(String value) {
+    data = value;
+  }
+}
+
+// TODO(jmesserly): Elements should have a pointer back to their document
+class Element extends Node with _ParentNode, _ElementAndDocument {
+  final String namespaceUri;
+
+  /// The [local name](http://dom.spec.whatwg.org/#concept-element-local-name)
+  /// of this element.
+  final String localName;
+
+  Element._(this.localName, [this.namespaceUri]) : super._();
+
+  Element.tag(this.localName)
+      : namespaceUri = Namespaces.html,
+        super._();
+
+  static final _START_TAG_REGEXP = new RegExp('<(\\w+)');
+
+  static final _CUSTOM_PARENT_TAG_MAP = const {
+    'body': 'html',
+    'head': 'html',
+    'caption': 'table',
+    'td': 'tr',
+    'colgroup': 'table',
+    'col': 'colgroup',
+    'tr': 'tbody',
+    'tbody': 'table',
+    'tfoot': 'table',
+    'thead': 'table',
+    'track': 'audio',
+  };
+
+  // TODO(jmesserly): this is from dart:html _ElementFactoryProvider...
+  // TODO(jmesserly): have a look at fixing some things in dart:html, in
+  // particular: is the parent tag map complete? Is it faster without regexp?
+  // TODO(jmesserly): for our version we can do something smarter in the parser.
+  // All we really need is to set the correct parse state.
+  factory Element.html(String html) {
+
+    // TODO(jacobr): this method can be made more robust and performant.
+    // 1) Cache the dummy parent elements required to use innerHTML rather than
+    //    creating them every call.
+    // 2) Verify that the html does not contain leading or trailing text nodes.
+    // 3) Verify that the html does not contain both <head> and <body> tags.
+    // 4) Detatch the created element from its dummy parent.
+    String parentTag = 'div';
+    String tag;
+    final match = _START_TAG_REGEXP.firstMatch(html);
+    if (match != null) {
+      tag = match.group(1).toLowerCase();
+      if (_CUSTOM_PARENT_TAG_MAP.containsKey(tag)) {
+        parentTag = _CUSTOM_PARENT_TAG_MAP[tag];
+      }
+    }
+
+    var fragment = parseFragment(html, container: parentTag);
+    Element element;
+    if (fragment.children.length == 1) {
+      element = fragment.children[0];
+    } else if (parentTag == 'html' && fragment.children.length == 2) {
+      // You'll always get a head and a body when starting from html.
+      element = fragment.children[tag == 'head' ? 0 : 1];
+    } else {
+      throw new ArgumentError('HTML had ${fragment.children.length} '
+          'top level elements but 1 expected');
+    }
+    element.remove();
+    return element;
+  }
+
+  int get nodeType => Node.ELEMENT_NODE;
+
+  // TODO(jmesserly): we can make this faster
+  Element get previousElementSibling {
+    if (parentNode == null) return null;
+    var siblings = parentNode.nodes;
+    for (int i = siblings.indexOf(this) - 1; i >= 0; i--) {
+      var s = siblings[i];
+      if (s is Element) return s;
+    }
+    return null;
+  }
+
+  Element get nextElementSibling {
+    if (parentNode == null) return null;
+    var siblings = parentNode.nodes;
+    for (int i = siblings.indexOf(this) + 1; i < siblings.length; i++) {
+      var s = siblings[i];
+      if (s is Element) return s;
+    }
+    return null;
+  }
+
+  String toString() {
+    var prefix = Namespaces.getPrefix(namespaceUri);
+    return "<${prefix == null ? '' : '$prefix '}$localName>";
+  }
+
+  String get text => _getText(this);
+  set text(String value) => _setText(this, value);
+
+  /// Returns a fragment of HTML or XML that represents the element and its
+  /// contents.
+  String get outerHtml => _outerHtml;
+
+  /// Returns a fragment of HTML or XML that represents the element's contents.
+  /// Can be set, to replace the contents of the element with nodes parsed from
+  /// the given string.
+  String get innerHtml => _innerHtml;
+  // TODO(jmesserly): deprecate in favor of:
+  // <https://api.dartlang.org/apidocs/channels/stable/#dart-dom-html.Element@id_setInnerHtml>
+  set innerHtml(String value) {
+    nodes.clear();
+    // TODO(jmesserly): should be able to get the same effect by adding the
+    // fragment directly.
+    nodes.addAll(parseFragment(value, container: localName).nodes);
+  }
+
+  void _addOuterHtml(StringBuffer str) {
+    // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#serializing-html-fragments
+    // Element is the most complicated one.
+    str.write('<');
+    str.write(_getSerializationPrefix(namespaceUri));
+    str.write(localName);
+
+    if (attributes.length > 0) {
+      attributes.forEach((key, v) {
+        // Note: AttributeName.toString handles serialization of attribute
+        // namespace, if needed.
+        str.write(' ');
+        str.write(key);
+        str.write('="');
+        str.write(htmlSerializeEscape(v, attributeMode: true));
+        str.write('"');
+      });
+    }
+
+    str.write('>');
+
+    if (nodes.length > 0) {
+      if (localName == 'pre' ||
+          localName == 'textarea' ||
+          localName == 'listing') {
+        final first = nodes[0];
+        if (first is Text && first.data.startsWith('\n')) {
+          // These nodes will remove a leading \n at parse time, so if we still
+          // have one, it means we started with two. Add it back.
+          str.write('\n');
+        }
+      }
+
+      _addInnerHtml(str);
+    }
+
+    // void elements must not have an end tag
+    // http://dev.w3.org/html5/markup/syntax.html#void-elements
+    if (!isVoidElement(localName)) str.write('</$localName>');
+  }
+
+  static String _getSerializationPrefix(String uri) {
+    if (uri == null ||
+        uri == Namespaces.html ||
+        uri == Namespaces.mathml ||
+        uri == Namespaces.svg) {
+      return '';
+    }
+    var prefix = Namespaces.getPrefix(uri);
+    // TODO(jmesserly): the spec doesn't define "qualified name".
+    // I'm not sure if this is correct, but it should parse reasonably.
+    return prefix == null ? '' : '$prefix:';
+  }
+
+  Element clone(bool deep) {
+    var result = new Element._(localName, namespaceUri)
+      ..attributes = new LinkedHashMap.from(attributes);
+    return _clone(result, deep);
+  }
+
+  // http://dom.spec.whatwg.org/#dom-element-id
+  String get id {
+    var result = attributes['id'];
+    return result != null ? result : '';
+  }
+
+  set id(String value) {
+    attributes['id'] = '$value';
+  }
+
+  // http://dom.spec.whatwg.org/#dom-element-classname
+  String get className {
+    var result = attributes['class'];
+    return result != null ? result : '';
+  }
+
+  set className(String value) {
+    attributes['class'] = '$value';
+  }
+
+  /**
+   * The set of CSS classes applied to this element.
+   *
+   * This set makes it easy to add, remove or toggle the classes applied to
+   * this element.
+   *
+   *     element.classes.add('selected');
+   *     element.classes.toggle('isOnline');
+   *     element.classes.remove('selected');
+   */
+  CssClassSet get classes => new ElementCssClassSet(this);
+}
+
+class Comment extends Node {
+  String data;
+
+  Comment(this.data) : super._();
+
+  int get nodeType => Node.COMMENT_NODE;
+
+  String toString() => "<!-- $data -->";
+
+  void _addOuterHtml(StringBuffer str) {
+    str.write("<!--$data-->");
+  }
+
+  Comment clone(bool deep) => new Comment(data);
+
+  String get text => data;
+  set text(String value) {
+    this.data = value;
+  }
+}
+
+// TODO(jmesserly): fix this to extend one of the corelib classes if possible.
+// (The requirement to remove the node from the old node list makes it tricky.)
+// TODO(jmesserly): is there any way to share code with the _NodeListImpl?
+class NodeList extends ListProxy<Node> {
+  // Note: this is conceptually final, but because of circular reference
+  // between Node and NodeList we initialize it after construction.
+  Node _parent;
+
+  NodeList._();
+
+  Node get first => this[0];
+
+  Node _setParent(Node node) {
+    // Note: we need to remove the node from its previous parent node, if any,
+    // before updating its parent pointer to point at our parent.
+    node.remove();
+    node.parentNode = _parent;
+    return node;
+  }
+
+  void add(Node value) {
+    if (value is DocumentFragment) {
+      addAll(value.nodes);
+    } else {
+      super.add(_setParent(value));
+    }
+  }
+
+  void addLast(Node value) => add(value);
+
+  void addAll(Iterable<Node> collection) {
+    // Note: we need to be careful if collection is another NodeList.
+    // In particular:
+    //   1. we need to copy the items before updating their parent pointers,
+    //     _flattenDocFragments does a copy internally.
+    //   2. we should update parent pointers in reverse order. That way they
+    //      are removed from the original NodeList (if any) from the end, which
+    //      is faster.
+    var list = _flattenDocFragments(collection);
+    for (var node in list.reversed) _setParent(node);
+    super.addAll(list);
+  }
+
+  void insert(int index, Node value) {
+    if (value is DocumentFragment) {
+      insertAll(index, value.nodes);
+    } else {
+      super.insert(index, _setParent(value));
+    }
+  }
+
+  Node removeLast() => super.removeLast()..parentNode = null;
+
+  Node removeAt(int i) => super.removeAt(i)..parentNode = null;
+
+  void clear() {
+    for (var node in this) node.parentNode = null;
+    super.clear();
+  }
+
+  void operator []=(int index, Node value) {
+    if (value is DocumentFragment) {
+      removeAt(index);
+      insertAll(index, value.nodes);
+    } else {
+      this[index].parentNode = null;
+      super[index] = _setParent(value);
+    }
+  }
+
+  // TODO(jmesserly): These aren't implemented in DOM _NodeListImpl, see
+  // http://code.google.com/p/dart/issues/detail?id=5371
+  void setRange(int start, int rangeLength, List<Node> from,
+      [int startFrom = 0]) {
+    if (from is NodeList) {
+      // Note: this is presumed to make a copy
+      from = from.sublist(startFrom, startFrom + rangeLength);
+    }
+    // Note: see comment in [addAll]. We need to be careful about the order of
+    // operations if [from] is also a NodeList.
+    for (int i = rangeLength - 1; i >= 0; i--) {
+      this[start + i] = from[startFrom + i];
+    }
+  }
+
+  void replaceRange(int start, int end, Iterable<Node> newContents) {
+    removeRange(start, end);
+    insertAll(start, newContents);
+  }
+
+  void removeRange(int start, int rangeLength) {
+    for (int i = start; i < rangeLength; i++) this[i].parentNode = null;
+    super.removeRange(start, rangeLength);
+  }
+
+  void removeWhere(bool test(Element e)) {
+    for (var node in where(test)) {
+      node.parentNode = null;
+    }
+    super.removeWhere(test);
+  }
+
+  void retainWhere(bool test(Element e)) {
+    for (var node in where((n) => !test(n))) {
+      node.parentNode = null;
+    }
+    super.retainWhere(test);
+  }
+
+  void insertAll(int index, Iterable<Node> collection) {
+    // Note: we need to be careful how we copy nodes. See note in addAll.
+    var list = _flattenDocFragments(collection);
+    for (var node in list.reversed) _setParent(node);
+    super.insertAll(index, list);
+  }
+
+  _flattenDocFragments(Iterable<Node> collection) {
+    // Note: this function serves two purposes:
+    //  * it flattens document fragments
+    //  * it creates a copy of [collections] when `collection is NodeList`.
+    var result = [];
+    for (var node in collection) {
+      if (node is DocumentFragment) {
+        result.addAll(node.nodes);
+      } else {
+        result.add(node);
+      }
+    }
+    return result;
+  }
+}
+
+/// An indexable collection of a node's descendants in the document tree,
+/// filtered so that only elements are in the collection.
+// TODO(jmesserly): this was copied from dart:html
+// TODO(jmesserly): "implements List<Element>" is a workaround for analyzer bug.
+class FilteredElementList extends IterableBase<Element> with ListMixin<Element>
+    implements List<Element> {
+  final Node _node;
+  final List<Node> _childNodes;
+
+  /// Creates a collection of the elements that descend from a node.
+  ///
+  /// Example usage:
+  ///
+  ///     var filteredElements = new FilteredElementList(query("#container"));
+  ///     // filteredElements is [a, b, c].
+  FilteredElementList(Node node)
+      : _childNodes = node.nodes,
+        _node = node;
+
+  // We can't memoize this, since it's possible that children will be messed
+  // with externally to this class.
+  //
+  // TODO(nweiz): we don't always need to create a new list. For example
+  // forEach, every, any, ... could directly work on the _childNodes.
+  List<Element> get _filtered =>
+      new List<Element>.from(_childNodes.where((n) => n is Element));
+
+  void forEach(void f(Element element)) {
+    _filtered.forEach(f);
+  }
+
+  void operator []=(int index, Element value) {
+    this[index].replaceWith(value);
+  }
+
+  void set length(int newLength) {
+    final len = this.length;
+    if (newLength >= len) {
+      return;
+    } else if (newLength < 0) {
+      throw new ArgumentError("Invalid list length");
+    }
+
+    removeRange(newLength, len);
+  }
+
+  String join([String separator = ""]) => _filtered.join(separator);
+
+  void add(Element value) {
+    _childNodes.add(value);
+  }
+
+  void addAll(Iterable<Element> iterable) {
+    for (Element element in iterable) {
+      add(element);
+    }
+  }
+
+  bool contains(Element element) {
+    return element is Element && _childNodes.contains(element);
+  }
+
+  Iterable<Element> get reversed => _filtered.reversed;
+
+  void sort([int compare(Element a, Element b)]) {
+    throw new UnsupportedError('TODO(jacobr): should we impl?');
+  }
+
+  void setRange(int start, int end, Iterable<Element> iterable,
+      [int skipCount = 0]) {
+    throw new UnimplementedError();
+  }
+
+  void fillRange(int start, int end, [Element fillValue]) {
+    throw new UnimplementedError();
+  }
+
+  void replaceRange(int start, int end, Iterable<Element> iterable) {
+    throw new UnimplementedError();
+  }
+
+  void removeRange(int start, int end) {
+    _filtered.sublist(start, end).forEach((el) => el.remove());
+  }
+
+  void clear() {
+    // Currently, ElementList#clear clears even non-element nodes, so we follow
+    // that behavior.
+    _childNodes.clear();
+  }
+
+  Element removeLast() {
+    final result = this.last;
+    if (result != null) {
+      result.remove();
+    }
+    return result;
+  }
+
+  Iterable map(f(Element element)) => _filtered.map(f);
+  Iterable<Element> where(bool f(Element element)) => _filtered.where(f);
+  Iterable expand(Iterable f(Element element)) => _filtered.expand(f);
+
+  void insert(int index, Element value) {
+    _childNodes.insert(index, value);
+  }
+
+  void insertAll(int index, Iterable<Element> iterable) {
+    _childNodes.insertAll(index, iterable);
+  }
+
+  Element removeAt(int index) {
+    final result = this[index];
+    result.remove();
+    return result;
+  }
+
+  bool remove(Object element) {
+    if (element is! Element) return false;
+    for (int i = 0; i < length; i++) {
+      Element indexElement = this[i];
+      if (identical(indexElement, element)) {
+        indexElement.remove();
+        return true;
+      }
+    }
+    return false;
+  }
+
+  Element reduce(Element combine(Element value, Element element)) {
+    return _filtered.reduce(combine);
+  }
+
+  dynamic fold(dynamic initialValue,
+      dynamic combine(dynamic previousValue, Element element)) {
+    return _filtered.fold(initialValue, combine);
+  }
+
+  bool every(bool f(Element element)) => _filtered.every(f);
+  bool any(bool f(Element element)) => _filtered.any(f);
+  List<Element> toList({bool growable: true}) =>
+      new List<Element>.from(this, growable: growable);
+  Set<Element> toSet() => new Set<Element>.from(this);
+  Element firstWhere(bool test(Element value), {Element orElse()}) {
+    return _filtered.firstWhere(test, orElse: orElse);
+  }
+
+  Element lastWhere(bool test(Element value), {Element orElse()}) {
+    return _filtered.lastWhere(test, orElse: orElse);
+  }
+
+  Element singleWhere(bool test(Element value)) {
+    return _filtered.singleWhere(test);
+  }
+
+  Element elementAt(int index) {
+    return this[index];
+  }
+
+  bool get isEmpty => _filtered.isEmpty;
+  int get length => _filtered.length;
+  Element operator [](int index) => _filtered[index];
+  Iterator<Element> get iterator => _filtered.iterator;
+  List<Element> sublist(int start, [int end]) => _filtered.sublist(start, end);
+  Iterable<Element> getRange(int start, int end) =>
+      _filtered.getRange(start, end);
+  int indexOf(Element element, [int start = 0]) =>
+      _filtered.indexOf(element, start);
+
+  int lastIndexOf(Element element, [int start = null]) {
+    if (start == null) start = length - 1;
+    return _filtered.lastIndexOf(element, start);
+  }
+
+  Element get first => _filtered.first;
+
+  Element get last => _filtered.last;
+
+  Element get single => _filtered.single;
+}
+
+// http://dom.spec.whatwg.org/#dom-node-textcontent
+// For Element and DocumentFragment
+String _getText(Node node) =>
+    (new _ConcatTextVisitor()..visit(node)).toString();
+
+void _setText(Node node, String value) {
+  node.nodes.clear();
+  node.append(new Text(value));
+}
+
+class _ConcatTextVisitor extends TreeVisitor {
+  final _str = new StringBuffer();
+
+  String toString() => _str.toString();
+
+  visitText(Text node) {
+    _str.write(node.data);
+  }
+}
diff --git a/html/lib/dom_parsing.dart b/html/lib/dom_parsing.dart
new file mode 100644
index 0000000..5575dcd
--- /dev/null
+++ b/html/lib/dom_parsing.dart
@@ -0,0 +1,203 @@
+/// This library contains extra APIs that aren't in the DOM, but are useful
+/// when interacting with the parse tree.
+library dom_parsing;
+
+import 'dom.dart';
+import 'src/constants.dart' show rcdataElements;
+
+/// A simple tree visitor for the DOM nodes.
+class TreeVisitor {
+  visit(Node node) {
+    switch (node.nodeType) {
+      case Node.ELEMENT_NODE:
+        return visitElement(node);
+      case Node.TEXT_NODE:
+        return visitText(node);
+      case Node.COMMENT_NODE:
+        return visitComment(node);
+      case Node.DOCUMENT_FRAGMENT_NODE:
+        return visitDocumentFragment(node);
+      case Node.DOCUMENT_NODE:
+        return visitDocument(node);
+      case Node.DOCUMENT_TYPE_NODE:
+        return visitDocumentType(node);
+      default:
+        throw new UnsupportedError('DOM node type ${node.nodeType}');
+    }
+  }
+
+  visitChildren(Node node) {
+    // Allow for mutations (remove works) while iterating.
+    for (var child in node.nodes.toList()) visit(child);
+  }
+
+  /// The fallback handler if the more specific visit method hasn't been
+  /// overriden. Only use this from a subclass of [TreeVisitor], otherwise
+  /// call [visit] instead.
+  visitNodeFallback(Node node) => visitChildren(node);
+
+  visitDocument(Document node) => visitNodeFallback(node);
+
+  visitDocumentType(DocumentType node) => visitNodeFallback(node);
+
+  visitText(Text node) => visitNodeFallback(node);
+
+  // TODO(jmesserly): visit attributes.
+  visitElement(Element node) => visitNodeFallback(node);
+
+  visitComment(Comment node) => visitNodeFallback(node);
+
+  visitDocumentFragment(DocumentFragment node) => visitNodeFallback(node);
+}
+
+/// Converts the DOM tree into an HTML string with code markup suitable for
+/// displaying the HTML's source code with CSS colors for different parts of the
+/// markup. See also [CodeMarkupVisitor].
+String htmlToCodeMarkup(Node node) {
+  return (new CodeMarkupVisitor()..visit(node)).toString();
+}
+
+/// Converts the DOM tree into an HTML string with code markup suitable for
+/// displaying the HTML's source code with CSS colors for different parts of the
+/// markup. See also [htmlToCodeMarkup].
+class CodeMarkupVisitor extends TreeVisitor {
+  final StringBuffer _str;
+
+  CodeMarkupVisitor() : _str = new StringBuffer();
+
+  String toString() => _str.toString();
+
+  visitDocument(Document node) {
+    _str.write("<pre>");
+    visitChildren(node);
+    _str.write("</pre>");
+  }
+
+  visitDocumentType(DocumentType node) {
+    _str.write('<code class="markup doctype">&lt;!DOCTYPE ${node.name}>'
+        '</code>');
+  }
+
+  visitText(Text node) {
+    writeTextNodeAsHtml(_str, node);
+  }
+
+  visitElement(Element node) {
+    final tag = node.localName;
+    _str.write('&lt;<code class="markup element-name">$tag</code>');
+    if (node.attributes.length > 0) {
+      node.attributes.forEach((key, v) {
+        v = htmlSerializeEscape(v, attributeMode: true);
+        _str.write(' <code class="markup attribute-name">$key</code>'
+            '=<code class="markup attribute-value">"$v"</code>');
+      });
+    }
+    if (node.nodes.length > 0) {
+      _str.write(">");
+      visitChildren(node);
+    } else if (isVoidElement(tag)) {
+      _str.write(">");
+      return;
+    }
+    _str.write('&lt;/<code class="markup element-name">$tag</code>>');
+  }
+
+  visitComment(Comment node) {
+    var data = htmlSerializeEscape(node.data);
+    _str.write('<code class="markup comment">&lt;!--${data}--></code>');
+  }
+}
+
+// TODO(jmesserly): reconcile this with dart:web htmlEscape.
+// This one might be more useful, as it is HTML5 spec compliant.
+/// Escapes [text] for use in the
+/// [HTML fragment serialization algorithm][1]. In particular, as described
+/// in the [specification][2]:
+///
+/// - Replace any occurrence of the `&` character by the string `&amp;`.
+/// - Replace any occurrences of the U+00A0 NO-BREAK SPACE character by the
+///   string `&nbsp;`.
+/// - If the algorithm was invoked in [attributeMode], replace any occurrences
+///   of the `"` character by the string `&quot;`.
+/// - If the algorithm was not invoked in [attributeMode], replace any
+///   occurrences of the `<` character by the string `&lt;`, and any occurrences
+///   of the `>` character by the string `&gt;`.
+///
+/// [1]: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#serializing-html-fragments
+/// [2]: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#escapingString
+String htmlSerializeEscape(String text, {bool attributeMode: false}) {
+  // TODO(jmesserly): is it faster to build up a list of codepoints?
+  // StringBuffer seems cleaner assuming Dart can unbox 1-char strings.
+  StringBuffer result = null;
+  for (int i = 0; i < text.length; i++) {
+    var ch = text[i];
+    String replace = null;
+    switch (ch) {
+      case '&':
+        replace = '&amp;';
+        break;
+      case '\u00A0' /*NO-BREAK SPACE*/ :
+        replace = '&nbsp;';
+        break;
+      case '"':
+        if (attributeMode) replace = '&quot;';
+        break;
+      case '<':
+        if (!attributeMode) replace = '&lt;';
+        break;
+      case '>':
+        if (!attributeMode) replace = '&gt;';
+        break;
+    }
+    if (replace != null) {
+      if (result == null) result = new StringBuffer(text.substring(0, i));
+      result.write(replace);
+    } else if (result != null) {
+      result.write(ch);
+    }
+  }
+
+  return result != null ? result.toString() : text;
+}
+
+/// Returns true if this tag name is a void element.
+/// This method is useful to a pretty printer, because void elements must not
+/// have an end tag.
+/// See also: <http://dev.w3.org/html5/markup/syntax.html#void-elements>.
+bool isVoidElement(String tagName) {
+  switch (tagName) {
+    case "area":
+    case "base":
+    case "br":
+    case "col":
+    case "command":
+    case "embed":
+    case "hr":
+    case "img":
+    case "input":
+    case "keygen":
+    case "link":
+    case "meta":
+    case "param":
+    case "source":
+    case "track":
+    case "wbr":
+      return true;
+  }
+  return false;
+}
+
+/// Serialize text node according to:
+/// <http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#html-fragment-serialization-algorithm>
+void writeTextNodeAsHtml(StringBuffer str, Text node) {
+  // Don't escape text for certain elements, notably <script>.
+  final parent = node.parentNode;
+  if (parent is Element) {
+    var tag = parent.localName;
+    if (rcdataElements.contains(tag) || tag == 'plaintext') {
+      str.write(node.data);
+      return;
+    }
+  }
+  str.write(htmlSerializeEscape(node.data));
+}
diff --git a/html/lib/parser.dart b/html/lib/parser.dart
new file mode 100644
index 0000000..d81f888
--- /dev/null
+++ b/html/lib/parser.dart
@@ -0,0 +1,3791 @@
+/// This library has a parser for HTML5 documents, that lets you parse HTML
+/// easily from a script or server side application:
+///
+///     import 'package:html/parser.dart' show parse;
+///     import 'package:html/dom.dart';
+///     main() {
+///       var document = parse(
+///           '<body>Hello world! <a href="www.html5rocks.com">HTML5 rocks!');
+///       print(document.outerHtml);
+///     }
+///
+/// The resulting document you get back has a DOM-like API for easy tree
+/// traversal and manipulation.
+library parser;
+
+import 'dart:collection';
+import 'dart:math';
+import 'package:source_span/source_span.dart';
+
+import 'src/treebuilder.dart';
+import 'src/constants.dart';
+import 'src/encoding_parser.dart';
+import 'src/token.dart';
+import 'src/tokenizer.dart';
+import 'src/utils.dart';
+import 'dom.dart';
+
+/// Parse the [input] html5 document into a tree. The [input] can be
+/// a [String], [List<int>] of bytes or an [HtmlTokenizer].
+///
+/// If [input] is not a [HtmlTokenizer], you can optionally specify the file's
+/// [encoding], which must be a string. If specified that encoding will be
+/// used regardless of any BOM or later declaration (such as in a meta element).
+///
+/// Set [generateSpans] if you want to generate [SourceSpan]s, otherwise the
+/// [Node.sourceSpan] property will be `null`. When using [generateSpans] you
+/// can additionally pass [sourceUrl] to indicate where the [input] was
+/// extracted from.
+Document parse(input,
+    {String encoding, bool generateSpans: false, String sourceUrl}) {
+  var p = new HtmlParser(input,
+      encoding: encoding, generateSpans: generateSpans, sourceUrl: sourceUrl);
+  return p.parse();
+}
+
+/// Parse the [input] html5 document fragment into a tree. The [input] can be
+/// a [String], [List<int>] of bytes or an [HtmlTokenizer]. The [container]
+/// element can optionally be specified, otherwise it defaults to "div".
+///
+/// If [input] is not a [HtmlTokenizer], you can optionally specify the file's
+/// [encoding], which must be a string. If specified, that encoding will be used,
+/// regardless of any BOM or later declaration (such as in a meta element).
+///
+/// Set [generateSpans] if you want to generate [SourceSpan]s, otherwise the
+/// [Node.sourceSpan] property will be `null`. When using [generateSpans] you can
+/// additionally pass [sourceUrl] to indicate where the [input] was extracted
+/// from.
+DocumentFragment parseFragment(input, {String container: "div", String encoding,
+    bool generateSpans: false, String sourceUrl}) {
+  var p = new HtmlParser(input,
+      encoding: encoding, generateSpans: generateSpans, sourceUrl: sourceUrl);
+  return p.parseFragment(container);
+}
+
+/// Parser for HTML, which generates a tree structure from a stream of
+/// (possibly malformed) characters.
+class HtmlParser {
+  /// Raise an exception on the first error encountered.
+  final bool strict;
+
+  /// True to generate [SourceSpan]s for the [Node.sourceSpan] property.
+  final bool generateSpans;
+
+  final HtmlTokenizer tokenizer;
+
+  final TreeBuilder tree;
+
+  final List<ParseError> errors = <ParseError>[];
+
+  String container;
+
+  bool firstStartTag = false;
+
+  // TODO(jmesserly): use enum?
+  /// "quirks" / "limited quirks" / "no quirks"
+  String compatMode = "no quirks";
+
+  /// innerHTML container when parsing document fragment.
+  String innerHTML;
+
+  Phase phase;
+
+  Phase lastPhase;
+
+  Phase originalPhase;
+
+  Phase beforeRCDataPhase;
+
+  bool framesetOK;
+
+  // These fields hold the different phase singletons. At any given time one
+  // of them will be active.
+  InitialPhase _initialPhase;
+  BeforeHtmlPhase _beforeHtmlPhase;
+  BeforeHeadPhase _beforeHeadPhase;
+  InHeadPhase _inHeadPhase;
+  AfterHeadPhase _afterHeadPhase;
+  InBodyPhase _inBodyPhase;
+  TextPhase _textPhase;
+  InTablePhase _inTablePhase;
+  InTableTextPhase _inTableTextPhase;
+  InCaptionPhase _inCaptionPhase;
+  InColumnGroupPhase _inColumnGroupPhase;
+  InTableBodyPhase _inTableBodyPhase;
+  InRowPhase _inRowPhase;
+  InCellPhase _inCellPhase;
+  InSelectPhase _inSelectPhase;
+  InSelectInTablePhase _inSelectInTablePhase;
+  InForeignContentPhase _inForeignContentPhase;
+  AfterBodyPhase _afterBodyPhase;
+  InFramesetPhase _inFramesetPhase;
+  AfterFramesetPhase _afterFramesetPhase;
+  AfterAfterBodyPhase _afterAfterBodyPhase;
+  AfterAfterFramesetPhase _afterAfterFramesetPhase;
+
+  /// Create an HtmlParser and configure the [tree] builder and [strict] mode.
+  /// The [input] can be a [String], [List<int>] of bytes or an [HtmlTokenizer].
+  ///
+  /// If [input] is not a [HtmlTokenizer], you can specify a few more arguments.
+  ///
+  /// The [encoding] must be a string that indicates the encoding. If specified,
+  /// that encoding will be used, regardless of any BOM or later declaration
+  /// (such as in a meta element).
+  ///
+  /// Set [parseMeta] to false if you want to disable parsing the meta element.
+  ///
+  /// Set [lowercaseElementName] or [lowercaseAttrName] to false to disable the
+  /// automatic conversion of element and attribute names to lower case. Note
+  /// that standard way to parse HTML is to lowercase, which is what the browser
+  /// DOM will do if you request [Node.outerHTML], for example.
+  HtmlParser(input, {String encoding, bool parseMeta: true,
+      bool lowercaseElementName: true, bool lowercaseAttrName: true,
+      this.strict: false, bool generateSpans: false, String sourceUrl,
+      TreeBuilder tree})
+      : generateSpans = generateSpans,
+        tree = tree != null ? tree : new TreeBuilder(true),
+        tokenizer = (input is HtmlTokenizer
+            ? input
+            : new HtmlTokenizer(input,
+                encoding: encoding,
+                parseMeta: parseMeta,
+                lowercaseElementName: lowercaseElementName,
+                lowercaseAttrName: lowercaseAttrName,
+                generateSpans: generateSpans,
+                sourceUrl: sourceUrl)) {
+    tokenizer.parser = this;
+    _initialPhase = new InitialPhase(this);
+    _beforeHtmlPhase = new BeforeHtmlPhase(this);
+    _beforeHeadPhase = new BeforeHeadPhase(this);
+    _inHeadPhase = new InHeadPhase(this);
+    // TODO(jmesserly): html5lib did not implement the no script parsing mode
+    // More information here:
+    // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#scripting-flag
+    // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#parsing-main-inheadnoscript
+    // "inHeadNoscript": new InHeadNoScriptPhase(this);
+    _afterHeadPhase = new AfterHeadPhase(this);
+    _inBodyPhase = new InBodyPhase(this);
+    _textPhase = new TextPhase(this);
+    _inTablePhase = new InTablePhase(this);
+    _inTableTextPhase = new InTableTextPhase(this);
+    _inCaptionPhase = new InCaptionPhase(this);
+    _inColumnGroupPhase = new InColumnGroupPhase(this);
+    _inTableBodyPhase = new InTableBodyPhase(this);
+    _inRowPhase = new InRowPhase(this);
+    _inCellPhase = new InCellPhase(this);
+    _inSelectPhase = new InSelectPhase(this);
+    _inSelectInTablePhase = new InSelectInTablePhase(this);
+    _inForeignContentPhase = new InForeignContentPhase(this);
+    _afterBodyPhase = new AfterBodyPhase(this);
+    _inFramesetPhase = new InFramesetPhase(this);
+    _afterFramesetPhase = new AfterFramesetPhase(this);
+    _afterAfterBodyPhase = new AfterAfterBodyPhase(this);
+    _afterAfterFramesetPhase = new AfterAfterFramesetPhase(this);
+  }
+
+  bool get innerHTMLMode => innerHTML != null;
+
+  /// Parse an html5 document into a tree.
+  /// After parsing, [errors] will be populated with parse errors, if any.
+  Document parse() {
+    innerHTML = null;
+    _parse();
+    return tree.getDocument();
+  }
+
+  /// Parse an html5 document fragment into a tree.
+  /// Pass a [container] to change the type of the containing element.
+  /// After parsing, [errors] will be populated with parse errors, if any.
+  DocumentFragment parseFragment([String container = "div"]) {
+    if (container == null) throw new ArgumentError('container');
+    innerHTML = container.toLowerCase();
+    _parse();
+    return tree.getFragment();
+  }
+
+  void _parse() {
+    reset();
+
+    while (true) {
+      try {
+        mainLoop();
+        break;
+      } on ReparseException catch (e) {
+        // Note: this happens if we start parsing but the character encoding
+        // changes. So we should only need to restart very early in the parse.
+        reset();
+      }
+    }
+  }
+
+  void reset() {
+    tokenizer.reset();
+
+    tree.reset();
+    firstStartTag = false;
+    errors.clear();
+    // "quirks" / "limited quirks" / "no quirks"
+    compatMode = "no quirks";
+
+    if (innerHTMLMode) {
+      if (cdataElements.contains(innerHTML)) {
+        tokenizer.state = tokenizer.rcdataState;
+      } else if (rcdataElements.contains(innerHTML)) {
+        tokenizer.state = tokenizer.rawtextState;
+      } else if (innerHTML == 'plaintext') {
+        tokenizer.state = tokenizer.plaintextState;
+      } else {
+        // state already is data state
+        // tokenizer.state = tokenizer.dataState;
+      }
+      phase = _beforeHtmlPhase;
+      _beforeHtmlPhase.insertHtmlElement();
+      resetInsertionMode();
+    } else {
+      phase = _initialPhase;
+    }
+
+    lastPhase = null;
+    beforeRCDataPhase = null;
+    framesetOK = true;
+  }
+
+  bool isHTMLIntegrationPoint(Element element) {
+    if (element.localName == "annotation-xml" &&
+        element.namespaceUri == Namespaces.mathml) {
+      var enc = element.attributes["encoding"];
+      if (enc != null) enc = asciiUpper2Lower(enc);
+      return enc == "text/html" || enc == "application/xhtml+xml";
+    } else {
+      return htmlIntegrationPointElements
+          .contains(new Pair(element.namespaceUri, element.localName));
+    }
+  }
+
+  bool isMathMLTextIntegrationPoint(Element element) {
+    return mathmlTextIntegrationPointElements
+        .contains(new Pair(element.namespaceUri, element.localName));
+  }
+
+  bool inForeignContent(Token token, int type) {
+    if (tree.openElements.length == 0) return false;
+
+    var node = tree.openElements.last;
+    if (node.namespaceUri == tree.defaultNamespace) return false;
+
+    if (isMathMLTextIntegrationPoint(node)) {
+      if (type == TokenKind.startTag &&
+          (token as StartTagToken).name != "mglyph" &&
+          (token as StartTagToken).name != "malignmark") {
+        return false;
+      }
+      if (type == TokenKind.characters || type == TokenKind.spaceCharacters) {
+        return false;
+      }
+    }
+
+    if (node.localName == "annotation-xml" &&
+        type == TokenKind.startTag &&
+        (token as StartTagToken).name == "svg") {
+      return false;
+    }
+
+    if (isHTMLIntegrationPoint(node)) {
+      if (type == TokenKind.startTag ||
+          type == TokenKind.characters ||
+          type == TokenKind.spaceCharacters) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  void mainLoop() {
+    while (tokenizer.moveNext()) {
+      var token = tokenizer.current;
+      var newToken = token;
+      int type;
+      while (newToken != null) {
+        type = newToken.kind;
+
+        // Note: avoid "is" test here, see http://dartbug.com/4795
+        if (type == TokenKind.parseError) {
+          ParseErrorToken error = newToken;
+          parseError(error.span, error.data, error.messageParams);
+          newToken = null;
+        } else {
+          Phase phase_ = phase;
+          if (inForeignContent(token, type)) {
+            phase_ = _inForeignContentPhase;
+          }
+
+          switch (type) {
+            case TokenKind.characters:
+              newToken = phase_.processCharacters(newToken);
+              break;
+            case TokenKind.spaceCharacters:
+              newToken = phase_.processSpaceCharacters(newToken);
+              break;
+            case TokenKind.startTag:
+              newToken = phase_.processStartTag(newToken);
+              break;
+            case TokenKind.endTag:
+              newToken = phase_.processEndTag(newToken);
+              break;
+            case TokenKind.comment:
+              newToken = phase_.processComment(newToken);
+              break;
+            case TokenKind.doctype:
+              newToken = phase_.processDoctype(newToken);
+              break;
+          }
+        }
+      }
+
+      if (token is StartTagToken) {
+        if (token.selfClosing && !token.selfClosingAcknowledged) {
+          parseError(token.span, "non-void-element-with-trailing-solidus", {
+            "name": token.name
+          });
+        }
+      }
+    }
+
+    // When the loop finishes it's EOF
+    var reprocess = true;
+    var reprocessPhases = [];
+    while (reprocess) {
+      reprocessPhases.add(phase);
+      reprocess = phase.processEOF();
+      if (reprocess) {
+        assert(!reprocessPhases.contains(phase));
+      }
+    }
+  }
+
+  /// The last span available. Used for EOF errors if we don't have something
+  /// better.
+  SourceSpan get _lastSpan {
+    if (tokenizer.stream.fileInfo == null) return null;
+    var pos = tokenizer.stream.position;
+    return tokenizer.stream.fileInfo.location(pos).pointSpan();
+  }
+
+  void parseError(SourceSpan span, String errorcode,
+      [Map datavars = const {}]) {
+    if (!generateSpans && span == null) {
+      span = _lastSpan;
+    }
+
+    var err = new ParseError(errorcode, span, datavars);
+    errors.add(err);
+    if (strict) throw err;
+  }
+
+  void adjustMathMLAttributes(StartTagToken token) {
+    var orig = token.data.remove("definitionurl");
+    if (orig != null) {
+      token.data["definitionURL"] = orig;
+    }
+  }
+
+  void adjustSVGAttributes(StartTagToken token) {
+    final replacements = const {
+      "attributename": "attributeName",
+      "attributetype": "attributeType",
+      "basefrequency": "baseFrequency",
+      "baseprofile": "baseProfile",
+      "calcmode": "calcMode",
+      "clippathunits": "clipPathUnits",
+      "contentscripttype": "contentScriptType",
+      "contentstyletype": "contentStyleType",
+      "diffuseconstant": "diffuseConstant",
+      "edgemode": "edgeMode",
+      "externalresourcesrequired": "externalResourcesRequired",
+      "filterres": "filterRes",
+      "filterunits": "filterUnits",
+      "glyphref": "glyphRef",
+      "gradienttransform": "gradientTransform",
+      "gradientunits": "gradientUnits",
+      "kernelmatrix": "kernelMatrix",
+      "kernelunitlength": "kernelUnitLength",
+      "keypoints": "keyPoints",
+      "keysplines": "keySplines",
+      "keytimes": "keyTimes",
+      "lengthadjust": "lengthAdjust",
+      "limitingconeangle": "limitingConeAngle",
+      "markerheight": "markerHeight",
+      "markerunits": "markerUnits",
+      "markerwidth": "markerWidth",
+      "maskcontentunits": "maskContentUnits",
+      "maskunits": "maskUnits",
+      "numoctaves": "numOctaves",
+      "pathlength": "pathLength",
+      "patterncontentunits": "patternContentUnits",
+      "patterntransform": "patternTransform",
+      "patternunits": "patternUnits",
+      "pointsatx": "pointsAtX",
+      "pointsaty": "pointsAtY",
+      "pointsatz": "pointsAtZ",
+      "preservealpha": "preserveAlpha",
+      "preserveaspectratio": "preserveAspectRatio",
+      "primitiveunits": "primitiveUnits",
+      "refx": "refX",
+      "refy": "refY",
+      "repeatcount": "repeatCount",
+      "repeatdur": "repeatDur",
+      "requiredextensions": "requiredExtensions",
+      "requiredfeatures": "requiredFeatures",
+      "specularconstant": "specularConstant",
+      "specularexponent": "specularExponent",
+      "spreadmethod": "spreadMethod",
+      "startoffset": "startOffset",
+      "stddeviation": "stdDeviation",
+      "stitchtiles": "stitchTiles",
+      "surfacescale": "surfaceScale",
+      "systemlanguage": "systemLanguage",
+      "tablevalues": "tableValues",
+      "targetx": "targetX",
+      "targety": "targetY",
+      "textlength": "textLength",
+      "viewbox": "viewBox",
+      "viewtarget": "viewTarget",
+      "xchannelselector": "xChannelSelector",
+      "ychannelselector": "yChannelSelector",
+      "zoomandpan": "zoomAndPan"
+    };
+    for (var originalName in token.data.keys.toList()) {
+      var svgName = replacements[originalName];
+      if (svgName != null) {
+        token.data[svgName] = token.data.remove(originalName);
+      }
+    }
+  }
+
+  void adjustForeignAttributes(StartTagToken token) {
+    // TODO(jmesserly): I don't like mixing non-string objects with strings in
+    // the Node.attributes Map. Is there another solution?
+    final replacements = const {
+      "xlink:actuate":
+          const AttributeName("xlink", "actuate", Namespaces.xlink),
+      "xlink:arcrole":
+          const AttributeName("xlink", "arcrole", Namespaces.xlink),
+      "xlink:href": const AttributeName("xlink", "href", Namespaces.xlink),
+      "xlink:role": const AttributeName("xlink", "role", Namespaces.xlink),
+      "xlink:show": const AttributeName("xlink", "show", Namespaces.xlink),
+      "xlink:title": const AttributeName("xlink", "title", Namespaces.xlink),
+      "xlink:type": const AttributeName("xlink", "type", Namespaces.xlink),
+      "xml:base": const AttributeName("xml", "base", Namespaces.xml),
+      "xml:lang": const AttributeName("xml", "lang", Namespaces.xml),
+      "xml:space": const AttributeName("xml", "space", Namespaces.xml),
+      "xmlns": const AttributeName(null, "xmlns", Namespaces.xmlns),
+      "xmlns:xlink": const AttributeName("xmlns", "xlink", Namespaces.xmlns)
+    };
+
+    for (var originalName in token.data.keys.toList()) {
+      var foreignName = replacements[originalName];
+      if (foreignName != null) {
+        token.data[foreignName] = token.data.remove(originalName);
+      }
+    }
+  }
+
+  void resetInsertionMode() {
+    // The name of this method is mostly historical. (It's also used in the
+    // specification.)
+    for (var node in tree.openElements.reversed) {
+      var nodeName = node.localName;
+      bool last = node == tree.openElements[0];
+      if (last) {
+        assert(innerHTMLMode);
+        nodeName = innerHTML;
+      }
+      // Check for conditions that should only happen in the innerHTML
+      // case
+      switch (nodeName) {
+        case "select":
+        case "colgroup":
+        case "head":
+        case "html":
+          assert(innerHTMLMode);
+          break;
+      }
+      if (!last && node.namespaceUri != tree.defaultNamespace) {
+        continue;
+      }
+      switch (nodeName) {
+        case "select":
+          phase = _inSelectPhase;
+          return;
+        case "td":
+          phase = _inCellPhase;
+          return;
+        case "th":
+          phase = _inCellPhase;
+          return;
+        case "tr":
+          phase = _inRowPhase;
+          return;
+        case "tbody":
+          phase = _inTableBodyPhase;
+          return;
+        case "thead":
+          phase = _inTableBodyPhase;
+          return;
+        case "tfoot":
+          phase = _inTableBodyPhase;
+          return;
+        case "caption":
+          phase = _inCaptionPhase;
+          return;
+        case "colgroup":
+          phase = _inColumnGroupPhase;
+          return;
+        case "table":
+          phase = _inTablePhase;
+          return;
+        case "head":
+          phase = _inBodyPhase;
+          return;
+        case "body":
+          phase = _inBodyPhase;
+          return;
+        case "frameset":
+          phase = _inFramesetPhase;
+          return;
+        case "html":
+          phase = _beforeHeadPhase;
+          return;
+      }
+    }
+    phase = _inBodyPhase;
+  }
+
+  /// Generic RCDATA/RAWTEXT Parsing algorithm
+  /// [contentType] - RCDATA or RAWTEXT
+  void parseRCDataRawtext(Token token, String contentType) {
+    assert(contentType == "RAWTEXT" || contentType == "RCDATA");
+
+    tree.insertElement(token);
+
+    if (contentType == "RAWTEXT") {
+      tokenizer.state = tokenizer.rawtextState;
+    } else {
+      tokenizer.state = tokenizer.rcdataState;
+    }
+
+    originalPhase = phase;
+    phase = _textPhase;
+  }
+}
+
+/// Base class for helper object that implements each phase of processing.
+class Phase {
+  // Order should be (they can be omitted):
+  // * EOF
+  // * Comment
+  // * Doctype
+  // * SpaceCharacters
+  // * Characters
+  // * StartTag
+  //   - startTag* methods
+  // * EndTag
+  //   - endTag* methods
+
+  final HtmlParser parser;
+
+  final TreeBuilder tree;
+
+  Phase(HtmlParser parser)
+      : parser = parser,
+        tree = parser.tree;
+
+  bool processEOF() {
+    throw new UnimplementedError();
+  }
+
+  Token processComment(CommentToken token) {
+    // For most phases the following is correct. Where it's not it will be
+    // overridden.
+    tree.insertComment(token, tree.openElements.last);
+    return null;
+  }
+
+  Token processDoctype(DoctypeToken token) {
+    parser.parseError(token.span, "unexpected-doctype");
+    return null;
+  }
+
+  Token processCharacters(CharactersToken token) {
+    tree.insertText(token.data, token.span);
+    return null;
+  }
+
+  Token processSpaceCharacters(SpaceCharactersToken token) {
+    tree.insertText(token.data, token.span);
+    return null;
+  }
+
+  Token processStartTag(StartTagToken token) {
+    throw new UnimplementedError();
+  }
+
+  Token startTagHtml(StartTagToken token) {
+    if (parser.firstStartTag == false && token.name == "html") {
+      parser.parseError(token.span, "non-html-root");
+    }
+    // XXX Need a check here to see if the first start tag token emitted is
+    // this token... If it's not, invoke parser.parseError().
+    token.data.forEach((attr, value) {
+      tree.openElements[0].attributes.putIfAbsent(attr, () => value);
+    });
+    parser.firstStartTag = false;
+    return null;
+  }
+
+  Token processEndTag(EndTagToken token) {
+    throw new UnimplementedError();
+  }
+
+  /// Helper method for popping openElements.
+  void popOpenElementsUntil(String name) {
+    var node = tree.openElements.removeLast();
+    while (node.localName != name) {
+      node = tree.openElements.removeLast();
+    }
+  }
+}
+
+class InitialPhase extends Phase {
+  InitialPhase(parser) : super(parser);
+
+  Token processSpaceCharacters(SpaceCharactersToken token) {
+    return null;
+  }
+
+  Token processComment(CommentToken token) {
+    tree.insertComment(token, tree.document);
+    return null;
+  }
+
+  Token processDoctype(DoctypeToken token) {
+    var name = token.name;
+    String publicId = token.publicId;
+    var systemId = token.systemId;
+    var correct = token.correct;
+
+    if ((name != "html" ||
+        publicId != null ||
+        systemId != null && systemId != "about:legacy-compat")) {
+      parser.parseError(token.span, "unknown-doctype");
+    }
+
+    if (publicId == null) {
+      publicId = "";
+    }
+
+    tree.insertDoctype(token);
+
+    if (publicId != "") {
+      publicId = asciiUpper2Lower(publicId);
+    }
+
+    if (!correct || token.name != "html" || startsWithAny(publicId, const [
+      "+//silmaril//dtd html pro v0r11 19970101//",
+      "-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
+      "-//as//dtd html 3.0 aswedit + extensions//",
+      "-//ietf//dtd html 2.0 level 1//",
+      "-//ietf//dtd html 2.0 level 2//",
+      "-//ietf//dtd html 2.0 strict level 1//",
+      "-//ietf//dtd html 2.0 strict level 2//",
+      "-//ietf//dtd html 2.0 strict//",
+      "-//ietf//dtd html 2.0//",
+      "-//ietf//dtd html 2.1e//",
+      "-//ietf//dtd html 3.0//",
+      "-//ietf//dtd html 3.2 final//",
+      "-//ietf//dtd html 3.2//",
+      "-//ietf//dtd html 3//",
+      "-//ietf//dtd html level 0//",
+      "-//ietf//dtd html level 1//",
+      "-//ietf//dtd html level 2//",
+      "-//ietf//dtd html level 3//",
+      "-//ietf//dtd html strict level 0//",
+      "-//ietf//dtd html strict level 1//",
+      "-//ietf//dtd html strict level 2//",
+      "-//ietf//dtd html strict level 3//",
+      "-//ietf//dtd html strict//",
+      "-//ietf//dtd html//",
+      "-//metrius//dtd metrius presentational//",
+      "-//microsoft//dtd internet explorer 2.0 html strict//",
+      "-//microsoft//dtd internet explorer 2.0 html//",
+      "-//microsoft//dtd internet explorer 2.0 tables//",
+      "-//microsoft//dtd internet explorer 3.0 html strict//",
+      "-//microsoft//dtd internet explorer 3.0 html//",
+      "-//microsoft//dtd internet explorer 3.0 tables//",
+      "-//netscape comm. corp.//dtd html//",
+      "-//netscape comm. corp.//dtd strict html//",
+      "-//o'reilly and associates//dtd html 2.0//",
+      "-//o'reilly and associates//dtd html extended 1.0//",
+      "-//o'reilly and associates//dtd html extended relaxed 1.0//",
+      "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//",
+      "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//",
+      "-//spyglass//dtd html 2.0 extended//",
+      "-//sq//dtd html 2.0 hotmetal + extensions//",
+      "-//sun microsystems corp.//dtd hotjava html//",
+      "-//sun microsystems corp.//dtd hotjava strict html//",
+      "-//w3c//dtd html 3 1995-03-24//",
+      "-//w3c//dtd html 3.2 draft//",
+      "-//w3c//dtd html 3.2 final//",
+      "-//w3c//dtd html 3.2//",
+      "-//w3c//dtd html 3.2s draft//",
+      "-//w3c//dtd html 4.0 frameset//",
+      "-//w3c//dtd html 4.0 transitional//",
+      "-//w3c//dtd html experimental 19960712//",
+      "-//w3c//dtd html experimental 970421//",
+      "-//w3c//dtd w3 html//",
+      "-//w3o//dtd w3 html 3.0//",
+      "-//webtechs//dtd mozilla html 2.0//",
+      "-//webtechs//dtd mozilla html//"
+    ]) ||
+        const [
+      "-//w3o//dtd w3 html strict 3.0//en//",
+      "-/w3c/dtd html 4.0 transitional/en",
+      "html"
+    ].contains(publicId) ||
+        startsWithAny(publicId, const [
+      "-//w3c//dtd html 4.01 frameset//",
+      "-//w3c//dtd html 4.01 transitional//"
+    ]) &&
+            systemId == null ||
+        systemId != null &&
+            systemId.toLowerCase() ==
+                "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
+      parser.compatMode = "quirks";
+    } else if (startsWithAny(publicId, const [
+      "-//w3c//dtd xhtml 1.0 frameset//",
+      "-//w3c//dtd xhtml 1.0 transitional//"
+    ]) ||
+        startsWithAny(publicId, const [
+      "-//w3c//dtd html 4.01 frameset//",
+      "-//w3c//dtd html 4.01 transitional//"
+    ]) &&
+        systemId != null) {
+      parser.compatMode = "limited quirks";
+    }
+    parser.phase = parser._beforeHtmlPhase;
+    return null;
+  }
+
+  void anythingElse() {
+    parser.compatMode = "quirks";
+    parser.phase = parser._beforeHtmlPhase;
+  }
+
+  Token processCharacters(CharactersToken token) {
+    parser.parseError(token.span, "expected-doctype-but-got-chars");
+    anythingElse();
+    return token;
+  }
+
+  Token processStartTag(StartTagToken token) {
+    parser.parseError(
+        token.span, "expected-doctype-but-got-start-tag", {"name": token.name});
+    anythingElse();
+    return token;
+  }
+
+  Token processEndTag(EndTagToken token) {
+    parser.parseError(
+        token.span, "expected-doctype-but-got-end-tag", {"name": token.name});
+    anythingElse();
+    return token;
+  }
+
+  bool processEOF() {
+    parser.parseError(parser._lastSpan, "expected-doctype-but-got-eof");
+    anythingElse();
+    return true;
+  }
+}
+
+class BeforeHtmlPhase extends Phase {
+  BeforeHtmlPhase(parser) : super(parser);
+
+  // helper methods
+  void insertHtmlElement() {
+    tree.insertRoot(new StartTagToken("html", data: {}));
+    parser.phase = parser._beforeHeadPhase;
+  }
+
+  // other
+  bool processEOF() {
+    insertHtmlElement();
+    return true;
+  }
+
+  Token processComment(CommentToken token) {
+    tree.insertComment(token, tree.document);
+    return null;
+  }
+
+  Token processSpaceCharacters(SpaceCharactersToken token) {
+    return null;
+  }
+
+  Token processCharacters(CharactersToken token) {
+    insertHtmlElement();
+    return token;
+  }
+
+  Token processStartTag(StartTagToken token) {
+    if (token.name == "html") {
+      parser.firstStartTag = true;
+    }
+    insertHtmlElement();
+    return token;
+  }
+
+  Token processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "head":
+      case "body":
+      case "html":
+      case "br":
+        insertHtmlElement();
+        return token;
+      default:
+        parser.parseError(
+            token.span, "unexpected-end-tag-before-html", {"name": token.name});
+        return null;
+    }
+  }
+}
+
+class BeforeHeadPhase extends Phase {
+  BeforeHeadPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case 'html':
+        return startTagHtml(token);
+      case 'head':
+        return startTagHead(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "head":
+      case "body":
+      case "html":
+      case "br":
+        return endTagImplyHead(token);
+      default:
+        return endTagOther(token);
+    }
+  }
+
+  bool processEOF() {
+    startTagHead(new StartTagToken("head", data: {}));
+    return true;
+  }
+
+  Token processSpaceCharacters(SpaceCharactersToken token) {
+    return null;
+  }
+
+  Token processCharacters(CharactersToken token) {
+    startTagHead(new StartTagToken("head", data: {}));
+    return token;
+  }
+
+  Token startTagHtml(StartTagToken token) {
+    return parser._inBodyPhase.processStartTag(token);
+  }
+
+  void startTagHead(StartTagToken token) {
+    tree.insertElement(token);
+    tree.headPointer = tree.openElements.last;
+    parser.phase = parser._inHeadPhase;
+  }
+
+  Token startTagOther(StartTagToken token) {
+    startTagHead(new StartTagToken("head", data: {}));
+    return token;
+  }
+
+  Token endTagImplyHead(EndTagToken token) {
+    startTagHead(new StartTagToken("head", data: {}));
+    return token;
+  }
+
+  void endTagOther(EndTagToken token) {
+    parser.parseError(
+        token.span, "end-tag-after-implied-root", {"name": token.name});
+  }
+}
+
+class InHeadPhase extends Phase {
+  InHeadPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case "html":
+        return startTagHtml(token);
+      case "title":
+        return startTagTitle(token);
+      case "noscript":
+      case "noframes":
+      case "style":
+        return startTagNoScriptNoFramesStyle(token);
+      case "script":
+        return startTagScript(token);
+      case "base":
+      case "basefont":
+      case "bgsound":
+      case "command":
+      case "link":
+        return startTagBaseLinkCommand(token);
+      case "meta":
+        return startTagMeta(token);
+      case "head":
+        return startTagHead(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "head":
+        return endTagHead(token);
+      case "br":
+      case "html":
+      case "body":
+        return endTagHtmlBodyBr(token);
+      default:
+        return endTagOther(token);
+    }
+  }
+
+  // the real thing
+  bool processEOF() {
+    anythingElse();
+    return true;
+  }
+
+  Token processCharacters(CharactersToken token) {
+    anythingElse();
+    return token;
+  }
+
+  Token startTagHtml(StartTagToken token) {
+    return parser._inBodyPhase.processStartTag(token);
+  }
+
+  void startTagHead(StartTagToken token) {
+    parser.parseError(token.span, "two-heads-are-not-better-than-one");
+  }
+
+  void startTagBaseLinkCommand(StartTagToken token) {
+    tree.insertElement(token);
+    tree.openElements.removeLast();
+    token.selfClosingAcknowledged = true;
+  }
+
+  void startTagMeta(StartTagToken token) {
+    tree.insertElement(token);
+    tree.openElements.removeLast();
+    token.selfClosingAcknowledged = true;
+
+    var attributes = token.data;
+    if (!parser.tokenizer.stream.charEncodingCertain) {
+      var charset = attributes["charset"];
+      var content = attributes["content"];
+      if (charset != null) {
+        parser.tokenizer.stream.changeEncoding(charset);
+      } else if (content != null) {
+        var data = new EncodingBytes(content);
+        var codec = new ContentAttrParser(data).parse();
+        parser.tokenizer.stream.changeEncoding(codec);
+      }
+    }
+  }
+
+  void startTagTitle(StartTagToken token) {
+    parser.parseRCDataRawtext(token, "RCDATA");
+  }
+
+  void startTagNoScriptNoFramesStyle(StartTagToken token) {
+    // Need to decide whether to implement the scripting-disabled case
+    parser.parseRCDataRawtext(token, "RAWTEXT");
+  }
+
+  void startTagScript(StartTagToken token) {
+    tree.insertElement(token);
+    parser.tokenizer.state = parser.tokenizer.scriptDataState;
+    parser.originalPhase = parser.phase;
+    parser.phase = parser._textPhase;
+  }
+
+  Token startTagOther(StartTagToken token) {
+    anythingElse();
+    return token;
+  }
+
+  void endTagHead(EndTagToken token) {
+    var node = parser.tree.openElements.removeLast();
+    assert(node.localName == "head");
+    parser.phase = parser._afterHeadPhase;
+  }
+
+  Token endTagHtmlBodyBr(EndTagToken token) {
+    anythingElse();
+    return token;
+  }
+
+  void endTagOther(EndTagToken token) {
+    parser.parseError(token.span, "unexpected-end-tag", {"name": token.name});
+  }
+
+  void anythingElse() {
+    endTagHead(new EndTagToken("head"));
+  }
+}
+
+// XXX If we implement a parser for which scripting is disabled we need to
+// implement this phase.
+//
+// class InHeadNoScriptPhase extends Phase {
+
+class AfterHeadPhase extends Phase {
+  AfterHeadPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case "html":
+        return startTagHtml(token);
+      case "body":
+        return startTagBody(token);
+      case "frameset":
+        return startTagFrameset(token);
+      case "base":
+      case "basefont":
+      case "bgsound":
+      case "link":
+      case "meta":
+      case "noframes":
+      case "script":
+      case "style":
+      case "title":
+        return startTagFromHead(token);
+      case "head":
+        return startTagHead(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "body":
+      case "html":
+      case "br":
+        return endTagHtmlBodyBr(token);
+      default:
+        return endTagOther(token);
+    }
+  }
+
+  bool processEOF() {
+    anythingElse();
+    return true;
+  }
+
+  Token processCharacters(CharactersToken token) {
+    anythingElse();
+    return token;
+  }
+
+  Token startTagHtml(StartTagToken token) {
+    return parser._inBodyPhase.processStartTag(token);
+  }
+
+  void startTagBody(StartTagToken token) {
+    parser.framesetOK = false;
+    tree.insertElement(token);
+    parser.phase = parser._inBodyPhase;
+  }
+
+  void startTagFrameset(StartTagToken token) {
+    tree.insertElement(token);
+    parser.phase = parser._inFramesetPhase;
+  }
+
+  void startTagFromHead(StartTagToken token) {
+    parser.parseError(token.span, "unexpected-start-tag-out-of-my-head", {
+      "name": token.name
+    });
+    tree.openElements.add(tree.headPointer);
+    parser._inHeadPhase.processStartTag(token);
+    for (var node in tree.openElements.reversed) {
+      if (node.localName == "head") {
+        tree.openElements.remove(node);
+        break;
+      }
+    }
+  }
+
+  void startTagHead(StartTagToken token) {
+    parser.parseError(token.span, "unexpected-start-tag", {"name": token.name});
+  }
+
+  Token startTagOther(StartTagToken token) {
+    anythingElse();
+    return token;
+  }
+
+  Token endTagHtmlBodyBr(EndTagToken token) {
+    anythingElse();
+    return token;
+  }
+
+  void endTagOther(EndTagToken token) {
+    parser.parseError(token.span, "unexpected-end-tag", {"name": token.name});
+  }
+
+  void anythingElse() {
+    tree.insertElement(new StartTagToken("body", data: {}));
+    parser.phase = parser._inBodyPhase;
+    parser.framesetOK = true;
+  }
+}
+
+typedef Token TokenProccessor(Token token);
+
+class InBodyPhase extends Phase {
+  bool dropNewline = false;
+
+  // http://www.whatwg.org/specs/web-apps/current-work///parsing-main-inbody
+  // the really-really-really-very crazy mode
+  InBodyPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case "html":
+        return startTagHtml(token);
+      case "base":
+      case "basefont":
+      case "bgsound":
+      case "command":
+      case "link":
+      case "meta":
+      case "noframes":
+      case "script":
+      case "style":
+      case "title":
+        return startTagProcessInHead(token);
+      case "body":
+        return startTagBody(token);
+      case "frameset":
+        return startTagFrameset(token);
+      case "address":
+      case "article":
+      case "aside":
+      case "blockquote":
+      case "center":
+      case "details":
+      case "details":
+      case "dir":
+      case "div":
+      case "dl":
+      case "fieldset":
+      case "figcaption":
+      case "figure":
+      case "footer":
+      case "header":
+      case "hgroup":
+      case "menu":
+      case "nav":
+      case "ol":
+      case "p":
+      case "section":
+      case "summary":
+      case "ul":
+        return startTagCloseP(token);
+      // headingElements
+      case "h1":
+      case "h2":
+      case "h3":
+      case "h4":
+      case "h5":
+      case "h6":
+        return startTagHeading(token);
+      case "pre":
+      case "listing":
+        return startTagPreListing(token);
+      case "form":
+        return startTagForm(token);
+      case "li":
+      case "dd":
+      case "dt":
+        return startTagListItem(token);
+      case "plaintext":
+        return startTagPlaintext(token);
+      case "a":
+        return startTagA(token);
+      case "b":
+      case "big":
+      case "code":
+      case "em":
+      case "font":
+      case "i":
+      case "s":
+      case "small":
+      case "strike":
+      case "strong":
+      case "tt":
+      case "u":
+        return startTagFormatting(token);
+      case "nobr":
+        return startTagNobr(token);
+      case "button":
+        return startTagButton(token);
+      case "applet":
+      case "marquee":
+      case "object":
+        return startTagAppletMarqueeObject(token);
+      case "xmp":
+        return startTagXmp(token);
+      case "table":
+        return startTagTable(token);
+      case "area":
+      case "br":
+      case "embed":
+      case "img":
+      case "keygen":
+      case "wbr":
+        return startTagVoidFormatting(token);
+      case "param":
+      case "source":
+      case "track":
+        return startTagParamSource(token);
+      case "input":
+        return startTagInput(token);
+      case "hr":
+        return startTagHr(token);
+      case "image":
+        return startTagImage(token);
+      case "isindex":
+        return startTagIsIndex(token);
+      case "textarea":
+        return startTagTextarea(token);
+      case "iframe":
+        return startTagIFrame(token);
+      case "noembed":
+      case "noframes":
+      case "noscript":
+        return startTagRawtext(token);
+      case "select":
+        return startTagSelect(token);
+      case "rp":
+      case "rt":
+        return startTagRpRt(token);
+      case "option":
+      case "optgroup":
+        return startTagOpt(token);
+      case "math":
+        return startTagMath(token);
+      case "svg":
+        return startTagSvg(token);
+      case "caption":
+      case "col":
+      case "colgroup":
+      case "frame":
+      case "head":
+      case "tbody":
+      case "td":
+      case "tfoot":
+      case "th":
+      case "thead":
+      case "tr":
+        return startTagMisplaced(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "body":
+        return endTagBody(token);
+      case "html":
+        return endTagHtml(token);
+      case "address":
+      case "article":
+      case "aside":
+      case "blockquote":
+      case "center":
+      case "details":
+      case "dir":
+      case "div":
+      case "dl":
+      case "fieldset":
+      case "figcaption":
+      case "figure":
+      case "footer":
+      case "header":
+      case "hgroup":
+      case "listing":
+      case "menu":
+      case "nav":
+      case "ol":
+      case "pre":
+      case "section":
+      case "summary":
+      case "ul":
+        return endTagBlock(token);
+      case "form":
+        return endTagForm(token);
+      case "p":
+        return endTagP(token);
+      case "dd":
+      case "dt":
+      case "li":
+        return endTagListItem(token);
+      // headingElements
+      case "h1":
+      case "h2":
+      case "h3":
+      case "h4":
+      case "h5":
+      case "h6":
+        return endTagHeading(token);
+      case "a":
+      case "b":
+      case "big":
+      case "code":
+      case "em":
+      case "font":
+      case "i":
+      case "nobr":
+      case "s":
+      case "small":
+      case "strike":
+      case "strong":
+      case "tt":
+      case "u":
+        return endTagFormatting(token);
+      case "applet":
+      case "marquee":
+      case "object":
+        return endTagAppletMarqueeObject(token);
+      case "br":
+        return endTagBr(token);
+      default:
+        return endTagOther(token);
+    }
+  }
+
+  bool isMatchingFormattingElement(Element node1, Element node2) {
+    if (node1.localName != node2.localName ||
+        node1.namespaceUri != node2.namespaceUri) {
+      return false;
+    } else if (node1.attributes.length != node2.attributes.length) {
+      return false;
+    } else {
+      for (var key in node1.attributes.keys) {
+        if (node1.attributes[key] != node2.attributes[key]) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  // helper
+  void addFormattingElement(token) {
+    tree.insertElement(token);
+    var element = tree.openElements.last;
+
+    var matchingElements = [];
+    for (Node node in tree.activeFormattingElements.reversed) {
+      if (node == Marker) {
+        break;
+      } else if (isMatchingFormattingElement(node, element)) {
+        matchingElements.add(node);
+      }
+    }
+
+    assert(matchingElements.length <= 3);
+    if (matchingElements.length == 3) {
+      tree.activeFormattingElements.remove(matchingElements.last);
+    }
+    tree.activeFormattingElements.add(element);
+  }
+
+  // the real deal
+  bool processEOF() {
+    for (var node in tree.openElements.reversed) {
+      switch (node.localName) {
+        case "dd":
+        case "dt":
+        case "li":
+        case "p":
+        case "tbody":
+        case "td":
+        case "tfoot":
+        case "th":
+        case "thead":
+        case "tr":
+        case "body":
+        case "html":
+          continue;
+      }
+      parser.parseError(node.sourceSpan, "expected-closing-tag-but-got-eof");
+      break;
+    }
+    //Stop parsing
+    return false;
+  }
+
+  void processSpaceCharactersDropNewline(StringToken token) {
+    // Sometimes (start of <pre>, <listing>, and <textarea> blocks) we
+    // want to drop leading newlines
+    var data = token.data;
+    dropNewline = false;
+    if (data.startsWith("\n")) {
+      var lastOpen = tree.openElements.last;
+      if (const ["pre", "listing", "textarea"].contains(lastOpen.localName) &&
+          !lastOpen.hasContent()) {
+        data = data.substring(1);
+      }
+    }
+    if (data.length > 0) {
+      tree.reconstructActiveFormattingElements();
+      tree.insertText(data, token.span);
+    }
+  }
+
+  Token processCharacters(CharactersToken token) {
+    if (token.data == "\u0000") {
+      //The tokenizer should always emit null on its own
+      return null;
+    }
+    tree.reconstructActiveFormattingElements();
+    tree.insertText(token.data, token.span);
+    if (parser.framesetOK && !allWhitespace(token.data)) {
+      parser.framesetOK = false;
+    }
+    return null;
+  }
+
+  Token processSpaceCharacters(SpaceCharactersToken token) {
+    if (dropNewline) {
+      processSpaceCharactersDropNewline(token);
+    } else {
+      tree.reconstructActiveFormattingElements();
+      tree.insertText(token.data, token.span);
+    }
+    return null;
+  }
+
+  Token startTagProcessInHead(StartTagToken token) {
+    return parser._inHeadPhase.processStartTag(token);
+  }
+
+  void startTagBody(StartTagToken token) {
+    parser.parseError(token.span, "unexpected-start-tag", {"name": "body"});
+    if (tree.openElements.length == 1 ||
+        tree.openElements[1].localName != "body") {
+      assert(parser.innerHTMLMode);
+    } else {
+      parser.framesetOK = false;
+      token.data.forEach((attr, value) {
+        tree.openElements[1].attributes.putIfAbsent(attr, () => value);
+      });
+    }
+  }
+
+  void startTagFrameset(StartTagToken token) {
+    parser.parseError(token.span, "unexpected-start-tag", {"name": "frameset"});
+    if ((tree.openElements.length == 1 ||
+        tree.openElements[1].localName != "body")) {
+      assert(parser.innerHTMLMode);
+    } else if (parser.framesetOK) {
+      if (tree.openElements[1].parentNode != null) {
+        tree.openElements[1].parentNode.nodes.remove(tree.openElements[1]);
+      }
+      while (tree.openElements.last.localName != "html") {
+        tree.openElements.removeLast();
+      }
+      tree.insertElement(token);
+      parser.phase = parser._inFramesetPhase;
+    }
+  }
+
+  void startTagCloseP(StartTagToken token) {
+    if (tree.elementInScope("p", variant: "button")) {
+      endTagP(new EndTagToken("p"));
+    }
+    tree.insertElement(token);
+  }
+
+  void startTagPreListing(StartTagToken token) {
+    if (tree.elementInScope("p", variant: "button")) {
+      endTagP(new EndTagToken("p"));
+    }
+    tree.insertElement(token);
+    parser.framesetOK = false;
+    dropNewline = true;
+  }
+
+  void startTagForm(StartTagToken token) {
+    if (tree.formPointer != null) {
+      parser.parseError(token.span, "unexpected-start-tag", {"name": "form"});
+    } else {
+      if (tree.elementInScope("p", variant: "button")) {
+        endTagP(new EndTagToken("p"));
+      }
+      tree.insertElement(token);
+      tree.formPointer = tree.openElements.last;
+    }
+  }
+
+  void startTagListItem(StartTagToken token) {
+    parser.framesetOK = false;
+
+    final stopNamesMap = const {
+      "li": const ["li"],
+      "dt": const ["dt", "dd"],
+      "dd": const ["dt", "dd"]
+    };
+    var stopNames = stopNamesMap[token.name];
+    for (var node in tree.openElements.reversed) {
+      if (stopNames.contains(node.localName)) {
+        parser.phase.processEndTag(new EndTagToken(node.localName));
+        break;
+      }
+      if (specialElements.contains(getElementNameTuple(node)) &&
+          !const ["address", "div", "p"].contains(node.localName)) {
+        break;
+      }
+    }
+
+    if (tree.elementInScope("p", variant: "button")) {
+      parser.phase.processEndTag(new EndTagToken("p"));
+    }
+
+    tree.insertElement(token);
+  }
+
+  void startTagPlaintext(StartTagToken token) {
+    if (tree.elementInScope("p", variant: "button")) {
+      endTagP(new EndTagToken("p"));
+    }
+    tree.insertElement(token);
+    parser.tokenizer.state = parser.tokenizer.plaintextState;
+  }
+
+  void startTagHeading(StartTagToken token) {
+    if (tree.elementInScope("p", variant: "button")) {
+      endTagP(new EndTagToken("p"));
+    }
+    if (headingElements.contains(tree.openElements.last.localName)) {
+      parser.parseError(
+          token.span, "unexpected-start-tag", {"name": token.name});
+      tree.openElements.removeLast();
+    }
+    tree.insertElement(token);
+  }
+
+  void startTagA(StartTagToken token) {
+    var afeAElement = tree.elementInActiveFormattingElements("a");
+    if (afeAElement != null) {
+      parser.parseError(token.span, "unexpected-start-tag-implies-end-tag", {
+        "startName": "a",
+        "endName": "a"
+      });
+      endTagFormatting(new EndTagToken("a"));
+      tree.openElements.remove(afeAElement);
+      tree.activeFormattingElements.remove(afeAElement);
+    }
+    tree.reconstructActiveFormattingElements();
+    addFormattingElement(token);
+  }
+
+  void startTagFormatting(StartTagToken token) {
+    tree.reconstructActiveFormattingElements();
+    addFormattingElement(token);
+  }
+
+  void startTagNobr(StartTagToken token) {
+    tree.reconstructActiveFormattingElements();
+    if (tree.elementInScope("nobr")) {
+      parser.parseError(token.span, "unexpected-start-tag-implies-end-tag", {
+        "startName": "nobr",
+        "endName": "nobr"
+      });
+      processEndTag(new EndTagToken("nobr"));
+      // XXX Need tests that trigger the following
+      tree.reconstructActiveFormattingElements();
+    }
+    addFormattingElement(token);
+  }
+
+  Token startTagButton(StartTagToken token) {
+    if (tree.elementInScope("button")) {
+      parser.parseError(token.span, "unexpected-start-tag-implies-end-tag", {
+        "startName": "button",
+        "endName": "button"
+      });
+      processEndTag(new EndTagToken("button"));
+      return token;
+    } else {
+      tree.reconstructActiveFormattingElements();
+      tree.insertElement(token);
+      parser.framesetOK = false;
+    }
+    return null;
+  }
+
+  void startTagAppletMarqueeObject(StartTagToken token) {
+    tree.reconstructActiveFormattingElements();
+    tree.insertElement(token);
+    tree.activeFormattingElements.add(Marker);
+    parser.framesetOK = false;
+  }
+
+  void startTagXmp(StartTagToken token) {
+    if (tree.elementInScope("p", variant: "button")) {
+      endTagP(new EndTagToken("p"));
+    }
+    tree.reconstructActiveFormattingElements();
+    parser.framesetOK = false;
+    parser.parseRCDataRawtext(token, "RAWTEXT");
+  }
+
+  void startTagTable(StartTagToken token) {
+    if (parser.compatMode != "quirks") {
+      if (tree.elementInScope("p", variant: "button")) {
+        processEndTag(new EndTagToken("p"));
+      }
+    }
+    tree.insertElement(token);
+    parser.framesetOK = false;
+    parser.phase = parser._inTablePhase;
+  }
+
+  void startTagVoidFormatting(StartTagToken token) {
+    tree.reconstructActiveFormattingElements();
+    tree.insertElement(token);
+    tree.openElements.removeLast();
+    token.selfClosingAcknowledged = true;
+    parser.framesetOK = false;
+  }
+
+  void startTagInput(StartTagToken token) {
+    var savedFramesetOK = parser.framesetOK;
+    startTagVoidFormatting(token);
+    if (asciiUpper2Lower(token.data["type"]) == "hidden") {
+      //input type=hidden doesn't change framesetOK
+      parser.framesetOK = savedFramesetOK;
+    }
+  }
+
+  void startTagParamSource(StartTagToken token) {
+    tree.insertElement(token);
+    tree.openElements.removeLast();
+    token.selfClosingAcknowledged = true;
+  }
+
+  void startTagHr(StartTagToken token) {
+    if (tree.elementInScope("p", variant: "button")) {
+      endTagP(new EndTagToken("p"));
+    }
+    tree.insertElement(token);
+    tree.openElements.removeLast();
+    token.selfClosingAcknowledged = true;
+    parser.framesetOK = false;
+  }
+
+  void startTagImage(StartTagToken token) {
+    // No really...
+    parser.parseError(token.span, "unexpected-start-tag-treated-as", {
+      "originalName": "image",
+      "newName": "img"
+    });
+    processStartTag(new StartTagToken("img",
+        data: token.data, selfClosing: token.selfClosing));
+  }
+
+  void startTagIsIndex(StartTagToken token) {
+    parser.parseError(token.span, "deprecated-tag", {"name": "isindex"});
+    if (tree.formPointer != null) {
+      return;
+    }
+    var formAttrs = {};
+    var dataAction = token.data["action"];
+    if (dataAction != null) {
+      formAttrs["action"] = dataAction;
+    }
+    processStartTag(new StartTagToken("form", data: formAttrs));
+    processStartTag(new StartTagToken("hr", data: {}));
+    processStartTag(new StartTagToken("label", data: {}));
+    // XXX Localization ...
+    var prompt = token.data["prompt"];
+    if (prompt == null) {
+      prompt = "This is a searchable index. Enter search keywords: ";
+    }
+    processCharacters(new CharactersToken(prompt));
+    var attributes = new LinkedHashMap.from(token.data);
+    attributes.remove('action');
+    attributes.remove('prompt');
+    attributes["name"] = "isindex";
+    processStartTag(new StartTagToken(
+        "input", data: attributes, selfClosing: token.selfClosing));
+    processEndTag(new EndTagToken("label"));
+    processStartTag(new StartTagToken("hr", data: {}));
+    processEndTag(new EndTagToken("form"));
+  }
+
+  void startTagTextarea(StartTagToken token) {
+    tree.insertElement(token);
+    parser.tokenizer.state = parser.tokenizer.rcdataState;
+    dropNewline = true;
+    parser.framesetOK = false;
+  }
+
+  void startTagIFrame(StartTagToken token) {
+    parser.framesetOK = false;
+    startTagRawtext(token);
+  }
+
+  /// iframe, noembed noframes, noscript(if scripting enabled).
+  void startTagRawtext(StartTagToken token) {
+    parser.parseRCDataRawtext(token, "RAWTEXT");
+  }
+
+  void startTagOpt(StartTagToken token) {
+    if (tree.openElements.last.localName == "option") {
+      parser.phase.processEndTag(new EndTagToken("option"));
+    }
+    tree.reconstructActiveFormattingElements();
+    parser.tree.insertElement(token);
+  }
+
+  void startTagSelect(StartTagToken token) {
+    tree.reconstructActiveFormattingElements();
+    tree.insertElement(token);
+    parser.framesetOK = false;
+
+    if (parser._inTablePhase == parser.phase ||
+        parser._inCaptionPhase == parser.phase ||
+        parser._inColumnGroupPhase == parser.phase ||
+        parser._inTableBodyPhase == parser.phase ||
+        parser._inRowPhase == parser.phase ||
+        parser._inCellPhase == parser.phase) {
+      parser.phase = parser._inSelectInTablePhase;
+    } else {
+      parser.phase = parser._inSelectPhase;
+    }
+  }
+
+  void startTagRpRt(StartTagToken token) {
+    if (tree.elementInScope("ruby")) {
+      tree.generateImpliedEndTags();
+      var last = tree.openElements.last;
+      if (last.localName != "ruby") {
+        parser.parseError(last.sourceSpan, 'undefined-error');
+      }
+    }
+    tree.insertElement(token);
+  }
+
+  void startTagMath(StartTagToken token) {
+    tree.reconstructActiveFormattingElements();
+    parser.adjustMathMLAttributes(token);
+    parser.adjustForeignAttributes(token);
+    token.namespace = Namespaces.mathml;
+    tree.insertElement(token);
+    //Need to get the parse error right for the case where the token
+    //has a namespace not equal to the xmlns attribute
+    if (token.selfClosing) {
+      tree.openElements.removeLast();
+      token.selfClosingAcknowledged = true;
+    }
+  }
+
+  void startTagSvg(StartTagToken token) {
+    tree.reconstructActiveFormattingElements();
+    parser.adjustSVGAttributes(token);
+    parser.adjustForeignAttributes(token);
+    token.namespace = Namespaces.svg;
+    tree.insertElement(token);
+    //Need to get the parse error right for the case where the token
+    //has a namespace not equal to the xmlns attribute
+    if (token.selfClosing) {
+      tree.openElements.removeLast();
+      token.selfClosingAcknowledged = true;
+    }
+  }
+
+  /// Elements that should be children of other elements that have a
+  /// different insertion mode; here they are ignored
+  /// "caption", "col", "colgroup", "frame", "frameset", "head",
+  /// "option", "optgroup", "tbody", "td", "tfoot", "th", "thead",
+  /// "tr", "noscript"
+  void startTagMisplaced(StartTagToken token) {
+    parser.parseError(
+        token.span, "unexpected-start-tag-ignored", {"name": token.name});
+  }
+
+  Token startTagOther(StartTagToken token) {
+    tree.reconstructActiveFormattingElements();
+    tree.insertElement(token);
+    return null;
+  }
+
+  void endTagP(EndTagToken token) {
+    if (!tree.elementInScope("p", variant: "button")) {
+      startTagCloseP(new StartTagToken("p", data: {}));
+      parser.parseError(token.span, "unexpected-end-tag", {"name": "p"});
+      endTagP(new EndTagToken("p"));
+    } else {
+      tree.generateImpliedEndTags("p");
+      if (tree.openElements.last.localName != "p") {
+        parser.parseError(token.span, "unexpected-end-tag", {"name": "p"});
+      }
+      popOpenElementsUntil("p");
+    }
+  }
+
+  void endTagBody(EndTagToken token) {
+    if (!tree.elementInScope("body")) {
+      parser.parseError(token.span, 'undefined-error');
+      return;
+    } else if (tree.openElements.last.localName != "body") {
+      for (Element node in slice(tree.openElements, 2)) {
+        switch (node.localName) {
+          case "dd":
+          case "dt":
+          case "li":
+          case "optgroup":
+          case "option":
+          case "p":
+          case "rp":
+          case "rt":
+          case "tbody":
+          case "td":
+          case "tfoot":
+          case "th":
+          case "thead":
+          case "tr":
+          case "body":
+          case "html":
+            continue;
+        }
+        // Not sure this is the correct name for the parse error
+        parser.parseError(token.span, "expected-one-end-tag-but-got-another", {
+          "gotName": "body",
+          "expectedName": node.localName
+        });
+        break;
+      }
+    }
+    parser.phase = parser._afterBodyPhase;
+  }
+
+  Token endTagHtml(EndTagToken token) {
+    //We repeat the test for the body end tag token being ignored here
+    if (tree.elementInScope("body")) {
+      endTagBody(new EndTagToken("body"));
+      return token;
+    }
+    return null;
+  }
+
+  void endTagBlock(EndTagToken token) {
+    //Put us back in the right whitespace handling mode
+    if (token.name == "pre") {
+      dropNewline = false;
+    }
+    var inScope = tree.elementInScope(token.name);
+    if (inScope) {
+      tree.generateImpliedEndTags();
+    }
+    if (tree.openElements.last.localName != token.name) {
+      parser.parseError(token.span, "end-tag-too-early", {"name": token.name});
+    }
+    if (inScope) {
+      popOpenElementsUntil(token.name);
+    }
+  }
+
+  void endTagForm(EndTagToken token) {
+    var node = tree.formPointer;
+    tree.formPointer = null;
+    if (node == null || !tree.elementInScope(node)) {
+      parser.parseError(token.span, "unexpected-end-tag", {"name": "form"});
+    } else {
+      tree.generateImpliedEndTags();
+      if (tree.openElements.last != node) {
+        parser.parseError(
+            token.span, "end-tag-too-early-ignored", {"name": "form"});
+      }
+      tree.openElements.remove(node);
+    }
+  }
+
+  void endTagListItem(EndTagToken token) {
+    var variant;
+    if (token.name == "li") {
+      variant = "list";
+    } else {
+      variant = null;
+    }
+    if (!tree.elementInScope(token.name, variant: variant)) {
+      parser.parseError(token.span, "unexpected-end-tag", {"name": token.name});
+    } else {
+      tree.generateImpliedEndTags(token.name);
+      if (tree.openElements.last.localName != token.name) {
+        parser.parseError(
+            token.span, "end-tag-too-early", {"name": token.name});
+      }
+      popOpenElementsUntil(token.name);
+    }
+  }
+
+  void endTagHeading(EndTagToken token) {
+    for (var item in headingElements) {
+      if (tree.elementInScope(item)) {
+        tree.generateImpliedEndTags();
+        break;
+      }
+    }
+    if (tree.openElements.last.localName != token.name) {
+      parser.parseError(token.span, "end-tag-too-early", {"name": token.name});
+    }
+
+    for (var item in headingElements) {
+      if (tree.elementInScope(item)) {
+        var node = tree.openElements.removeLast();
+        while (!headingElements.contains(node.localName)) {
+          node = tree.openElements.removeLast();
+        }
+        break;
+      }
+    }
+  }
+
+  /// The much-feared adoption agency algorithm.
+  endTagFormatting(EndTagToken token) {
+    // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#adoptionAgency
+    // TODO(jmesserly): the comments here don't match the numbered steps in the
+    // updated spec. This needs a pass over it to verify that it still matches.
+    // In particular the html5lib Python code skiped "step 4", I'm not sure why.
+    // XXX Better parseError messages appreciated.
+    int outerLoopCounter = 0;
+    while (outerLoopCounter < 8) {
+      outerLoopCounter += 1;
+
+      // Step 1 paragraph 1
+      var formattingElement =
+          tree.elementInActiveFormattingElements(token.name);
+      if (formattingElement == null ||
+          (tree.openElements.contains(formattingElement) &&
+              !tree.elementInScope(formattingElement.localName))) {
+        parser.parseError(
+            token.span, "adoption-agency-1.1", {"name": token.name});
+        return;
+        // Step 1 paragraph 2
+      } else if (!tree.openElements.contains(formattingElement)) {
+        parser.parseError(
+            token.span, "adoption-agency-1.2", {"name": token.name});
+        tree.activeFormattingElements.remove(formattingElement);
+        return;
+      }
+
+      // Step 1 paragraph 3
+      if (formattingElement != tree.openElements.last) {
+        parser.parseError(
+            token.span, "adoption-agency-1.3", {"name": token.name});
+      }
+
+      // Step 2
+      // Start of the adoption agency algorithm proper
+      var afeIndex = tree.openElements.indexOf(formattingElement);
+      Node furthestBlock = null;
+      for (Node element in slice(tree.openElements, afeIndex)) {
+        if (specialElements.contains(getElementNameTuple(element))) {
+          furthestBlock = element;
+          break;
+        }
+      }
+      // Step 3
+      if (furthestBlock == null) {
+        var element = tree.openElements.removeLast();
+        while (element != formattingElement) {
+          element = tree.openElements.removeLast();
+        }
+        tree.activeFormattingElements.remove(element);
+        return;
+      }
+
+      var commonAncestor = tree.openElements[afeIndex - 1];
+
+      // Step 5
+      // The bookmark is supposed to help us identify where to reinsert
+      // nodes in step 12. We have to ensure that we reinsert nodes after
+      // the node before the active formatting element. Note the bookmark
+      // can move in step 7.4
+      var bookmark = tree.activeFormattingElements.indexOf(formattingElement);
+
+      // Step 6
+      Node lastNode = furthestBlock;
+      var node = furthestBlock;
+      int innerLoopCounter = 0;
+
+      var index = tree.openElements.indexOf(node);
+      while (innerLoopCounter < 3) {
+        innerLoopCounter += 1;
+
+        // Node is element before node in open elements
+        index -= 1;
+        node = tree.openElements[index];
+        if (!tree.activeFormattingElements.contains(node)) {
+          tree.openElements.remove(node);
+          continue;
+        }
+        // Step 6.3
+        if (node == formattingElement) {
+          break;
+        }
+        // Step 6.4
+        if (lastNode == furthestBlock) {
+          bookmark = (tree.activeFormattingElements.indexOf(node) + 1);
+        }
+        // Step 6.5
+        //cite = node.parent
+        var clone = node.clone(false);
+        // Replace node with clone
+        tree.activeFormattingElements[
+            tree.activeFormattingElements.indexOf(node)] = clone;
+        tree.openElements[tree.openElements.indexOf(node)] = clone;
+        node = clone;
+
+        // Step 6.6
+        // Remove lastNode from its parents, if any
+        if (lastNode.parentNode != null) {
+          lastNode.parentNode.nodes.remove(lastNode);
+        }
+        node.nodes.add(lastNode);
+        // Step 7.7
+        lastNode = node;
+        // End of inner loop
+      }
+
+      // Step 7
+      // Foster parent lastNode if commonAncestor is a
+      // table, tbody, tfoot, thead, or tr we need to foster parent the
+      // lastNode
+      if (lastNode.parentNode != null) {
+        lastNode.parentNode.nodes.remove(lastNode);
+      }
+
+      if (const [
+        "table",
+        "tbody",
+        "tfoot",
+        "thead",
+        "tr"
+      ].contains(commonAncestor.localName)) {
+        var nodePos = tree.getTableMisnestedNodePosition();
+        nodePos[0].insertBefore(lastNode, nodePos[1]);
+      } else {
+        commonAncestor.nodes.add(lastNode);
+      }
+
+      // Step 8
+      var clone = formattingElement.clone(false);
+
+      // Step 9
+      furthestBlock.reparentChildren(clone);
+
+      // Step 10
+      furthestBlock.nodes.add(clone);
+
+      // Step 11
+      tree.activeFormattingElements.remove(formattingElement);
+      tree.activeFormattingElements.insert(
+          min(bookmark, tree.activeFormattingElements.length), clone);
+
+      // Step 12
+      tree.openElements.remove(formattingElement);
+      tree.openElements.insert(
+          tree.openElements.indexOf(furthestBlock) + 1, clone);
+    }
+  }
+
+  void endTagAppletMarqueeObject(EndTagToken token) {
+    if (tree.elementInScope(token.name)) {
+      tree.generateImpliedEndTags();
+    }
+    if (tree.openElements.last.localName != token.name) {
+      parser.parseError(token.span, "end-tag-too-early", {"name": token.name});
+    }
+    if (tree.elementInScope(token.name)) {
+      popOpenElementsUntil(token.name);
+      tree.clearActiveFormattingElements();
+    }
+  }
+
+  void endTagBr(EndTagToken token) {
+    parser.parseError(token.span, "unexpected-end-tag-treated-as", {
+      "originalName": "br",
+      "newName": "br element"
+    });
+    tree.reconstructActiveFormattingElements();
+    tree.insertElement(new StartTagToken("br", data: {}));
+    tree.openElements.removeLast();
+  }
+
+  void endTagOther(EndTagToken token) {
+    for (var node in tree.openElements.reversed) {
+      if (node.localName == token.name) {
+        tree.generateImpliedEndTags(token.name);
+        if (tree.openElements.last.localName != token.name) {
+          parser.parseError(
+              token.span, "unexpected-end-tag", {"name": token.name});
+        }
+        while (tree.openElements.removeLast() != node);
+        break;
+      } else {
+        if (specialElements.contains(getElementNameTuple(node))) {
+          parser.parseError(
+              token.span, "unexpected-end-tag", {"name": token.name});
+          break;
+        }
+      }
+    }
+  }
+}
+
+class TextPhase extends Phase {
+  TextPhase(parser) : super(parser);
+
+  // "Tried to process start tag %s in RCDATA/RAWTEXT mode"%token.name
+  processStartTag(StartTagToken token) {
+    assert(false);
+  }
+
+  processEndTag(EndTagToken token) {
+    if (token.name == 'script') return endTagScript(token);
+    return endTagOther(token);
+  }
+
+  Token processCharacters(CharactersToken token) {
+    tree.insertText(token.data, token.span);
+    return null;
+  }
+
+  bool processEOF() {
+    var last = tree.openElements.last;
+    parser.parseError(last.sourceSpan, "expected-named-closing-tag-but-got-eof",
+        {'name': last.localName});
+    tree.openElements.removeLast();
+    parser.phase = parser.originalPhase;
+    return true;
+  }
+
+  void endTagScript(EndTagToken token) {
+    var node = tree.openElements.removeLast();
+    assert(node.localName == "script");
+    parser.phase = parser.originalPhase;
+    //The rest of this method is all stuff that only happens if
+    //document.write works
+  }
+
+  void endTagOther(EndTagToken token) {
+    tree.openElements.removeLast();
+    parser.phase = parser.originalPhase;
+  }
+}
+
+class InTablePhase extends Phase {
+  // http://www.whatwg.org/specs/web-apps/current-work///in-table
+  InTablePhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case "html":
+        return startTagHtml(token);
+      case "caption":
+        return startTagCaption(token);
+      case "colgroup":
+        return startTagColgroup(token);
+      case "col":
+        return startTagCol(token);
+      case "tbody":
+      case "tfoot":
+      case "thead":
+        return startTagRowGroup(token);
+      case "td":
+      case "th":
+      case "tr":
+        return startTagImplyTbody(token);
+      case "table":
+        return startTagTable(token);
+      case "style":
+      case "script":
+        return startTagStyleScript(token);
+      case "input":
+        return startTagInput(token);
+      case "form":
+        return startTagForm(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "table":
+        return endTagTable(token);
+      case "body":
+      case "caption":
+      case "col":
+      case "colgroup":
+      case "html":
+      case "tbody":
+      case "td":
+      case "tfoot":
+      case "th":
+      case "thead":
+      case "tr":
+        return endTagIgnore(token);
+      default:
+        return endTagOther(token);
+    }
+  }
+
+  // helper methods
+  void clearStackToTableContext() {
+    // "clear the stack back to a table context"
+    while (tree.openElements.last.localName != "table" &&
+        tree.openElements.last.localName != "html") {
+      //parser.parseError(token.span, "unexpected-implied-end-tag-in-table",
+      //  {"name":  tree.openElements.last.name})
+      tree.openElements.removeLast();
+    }
+    // When the current node is <html> it's an innerHTML case
+  }
+
+  // processing methods
+  bool processEOF() {
+    var last = tree.openElements.last;
+    if (last.localName != "html") {
+      parser.parseError(last.sourceSpan, "eof-in-table");
+    } else {
+      assert(parser.innerHTMLMode);
+    }
+    //Stop parsing
+    return false;
+  }
+
+  Token processSpaceCharacters(SpaceCharactersToken token) {
+    var originalPhase = parser.phase;
+    parser.phase = parser._inTableTextPhase;
+    parser._inTableTextPhase.originalPhase = originalPhase;
+    parser.phase.processSpaceCharacters(token);
+    return null;
+  }
+
+  Token processCharacters(CharactersToken token) {
+    var originalPhase = parser.phase;
+    parser.phase = parser._inTableTextPhase;
+    parser._inTableTextPhase.originalPhase = originalPhase;
+    parser.phase.processCharacters(token);
+    return null;
+  }
+
+  void insertText(CharactersToken token) {
+    // If we get here there must be at least one non-whitespace character
+    // Do the table magic!
+    tree.insertFromTable = true;
+    parser._inBodyPhase.processCharacters(token);
+    tree.insertFromTable = false;
+  }
+
+  void startTagCaption(StartTagToken token) {
+    clearStackToTableContext();
+    tree.activeFormattingElements.add(Marker);
+    tree.insertElement(token);
+    parser.phase = parser._inCaptionPhase;
+  }
+
+  void startTagColgroup(StartTagToken token) {
+    clearStackToTableContext();
+    tree.insertElement(token);
+    parser.phase = parser._inColumnGroupPhase;
+  }
+
+  Token startTagCol(StartTagToken token) {
+    startTagColgroup(new StartTagToken("colgroup", data: {}));
+    return token;
+  }
+
+  void startTagRowGroup(StartTagToken token) {
+    clearStackToTableContext();
+    tree.insertElement(token);
+    parser.phase = parser._inTableBodyPhase;
+  }
+
+  Token startTagImplyTbody(StartTagToken token) {
+    startTagRowGroup(new StartTagToken("tbody", data: {}));
+    return token;
+  }
+
+  Token startTagTable(StartTagToken token) {
+    parser.parseError(token.span, "unexpected-start-tag-implies-end-tag", {
+      "startName": "table",
+      "endName": "table"
+    });
+    parser.phase.processEndTag(new EndTagToken("table"));
+    if (!parser.innerHTMLMode) {
+      return token;
+    }
+    return null;
+  }
+
+  Token startTagStyleScript(StartTagToken token) {
+    return parser._inHeadPhase.processStartTag(token);
+  }
+
+  void startTagInput(StartTagToken token) {
+    if (asciiUpper2Lower(token.data["type"]) == "hidden") {
+      parser.parseError(token.span, "unexpected-hidden-input-in-table");
+      tree.insertElement(token);
+      // XXX associate with form
+      tree.openElements.removeLast();
+    } else {
+      startTagOther(token);
+    }
+  }
+
+  void startTagForm(StartTagToken token) {
+    parser.parseError(token.span, "unexpected-form-in-table");
+    if (tree.formPointer == null) {
+      tree.insertElement(token);
+      tree.formPointer = tree.openElements.last;
+      tree.openElements.removeLast();
+    }
+  }
+
+  void startTagOther(StartTagToken token) {
+    parser.parseError(token.span, "unexpected-start-tag-implies-table-voodoo", {
+      "name": token.name
+    });
+    // Do the table magic!
+    tree.insertFromTable = true;
+    parser._inBodyPhase.processStartTag(token);
+    tree.insertFromTable = false;
+  }
+
+  void endTagTable(EndTagToken token) {
+    if (tree.elementInScope("table", variant: "table")) {
+      tree.generateImpliedEndTags();
+      var last = tree.openElements.last;
+      if (last.localName != "table") {
+        parser.parseError(token.span, "end-tag-too-early-named", {
+          "gotName": "table",
+          "expectedName": last.localName
+        });
+      }
+      while (tree.openElements.last.localName != "table") {
+        tree.openElements.removeLast();
+      }
+      tree.openElements.removeLast();
+      parser.resetInsertionMode();
+    } else {
+      // innerHTML case
+      assert(parser.innerHTMLMode);
+      parser.parseError(token.span, "undefined-error");
+    }
+  }
+
+  void endTagIgnore(EndTagToken token) {
+    parser.parseError(token.span, "unexpected-end-tag", {"name": token.name});
+  }
+
+  void endTagOther(EndTagToken token) {
+    parser.parseError(token.span, "unexpected-end-tag-implies-table-voodoo", {
+      "name": token.name
+    });
+    // Do the table magic!
+    tree.insertFromTable = true;
+    parser._inBodyPhase.processEndTag(token);
+    tree.insertFromTable = false;
+  }
+}
+
+class InTableTextPhase extends Phase {
+  Phase originalPhase;
+  List<StringToken> characterTokens;
+
+  InTableTextPhase(parser)
+      : characterTokens = <StringToken>[],
+        super(parser);
+
+  void flushCharacters() {
+    if (characterTokens.length == 0) return;
+
+    // TODO(sigmund,jmesserly): remove '' (dartbug.com/8480)
+    var data = characterTokens.map((t) => t.data).join('');
+    var span = null;
+
+    if (parser.generateSpans) {
+      span = characterTokens[0].span.expand(characterTokens.last.span);
+    }
+
+    if (!allWhitespace(data)) {
+      parser._inTablePhase.insertText(new CharactersToken(data)..span = span);
+    } else if (data.length > 0) {
+      tree.insertText(data, span);
+    }
+    characterTokens = <StringToken>[];
+  }
+
+  Token processComment(CommentToken token) {
+    flushCharacters();
+    parser.phase = originalPhase;
+    return token;
+  }
+
+  bool processEOF() {
+    flushCharacters();
+    parser.phase = originalPhase;
+    return true;
+  }
+
+  Token processCharacters(CharactersToken token) {
+    if (token.data == "\u0000") {
+      return null;
+    }
+    characterTokens.add(token);
+    return null;
+  }
+
+  Token processSpaceCharacters(SpaceCharactersToken token) {
+    //pretty sure we should never reach here
+    characterTokens.add(token);
+    // XXX assert(false);
+    return null;
+  }
+
+  Token processStartTag(StartTagToken token) {
+    flushCharacters();
+    parser.phase = originalPhase;
+    return token;
+  }
+
+  Token processEndTag(EndTagToken token) {
+    flushCharacters();
+    parser.phase = originalPhase;
+    return token;
+  }
+}
+
+class InCaptionPhase extends Phase {
+  // http://www.whatwg.org/specs/web-apps/current-work///in-caption
+  InCaptionPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case "html":
+        return startTagHtml(token);
+      case "caption":
+      case "col":
+      case "colgroup":
+      case "tbody":
+      case "td":
+      case "tfoot":
+      case "th":
+      case "thead":
+      case "tr":
+        return startTagTableElement(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "caption":
+        return endTagCaption(token);
+      case "table":
+        return endTagTable(token);
+      case "body":
+      case "col":
+      case "colgroup":
+      case "html":
+      case "tbody":
+      case "td":
+      case "tfoot":
+      case "th":
+      case "thead":
+      case "tr":
+        return endTagIgnore(token);
+      default:
+        return endTagOther(token);
+    }
+  }
+
+  bool ignoreEndTagCaption() {
+    return !tree.elementInScope("caption", variant: "table");
+  }
+
+  bool processEOF() {
+    parser._inBodyPhase.processEOF();
+    return false;
+  }
+
+  Token processCharacters(CharactersToken token) {
+    return parser._inBodyPhase.processCharacters(token);
+  }
+
+  Token startTagTableElement(StartTagToken token) {
+    parser.parseError(token.span, "undefined-error");
+    //XXX Have to duplicate logic here to find out if the tag is ignored
+    var ignoreEndTag = ignoreEndTagCaption();
+    parser.phase.processEndTag(new EndTagToken("caption"));
+    if (!ignoreEndTag) {
+      return token;
+    }
+    return null;
+  }
+
+  Token startTagOther(StartTagToken token) {
+    return parser._inBodyPhase.processStartTag(token);
+  }
+
+  void endTagCaption(EndTagToken token) {
+    if (!ignoreEndTagCaption()) {
+      // AT this code is quite similar to endTagTable in "InTable"
+      tree.generateImpliedEndTags();
+      if (tree.openElements.last.localName != "caption") {
+        parser.parseError(token.span, "expected-one-end-tag-but-got-another", {
+          "gotName": "caption",
+          "expectedName": tree.openElements.last.localName
+        });
+      }
+      while (tree.openElements.last.localName != "caption") {
+        tree.openElements.removeLast();
+      }
+      tree.openElements.removeLast();
+      tree.clearActiveFormattingElements();
+      parser.phase = parser._inTablePhase;
+    } else {
+      // innerHTML case
+      assert(parser.innerHTMLMode);
+      parser.parseError(token.span, "undefined-error");
+    }
+  }
+
+  Token endTagTable(EndTagToken token) {
+    parser.parseError(token.span, "undefined-error");
+    var ignoreEndTag = ignoreEndTagCaption();
+    parser.phase.processEndTag(new EndTagToken("caption"));
+    if (!ignoreEndTag) {
+      return token;
+    }
+    return null;
+  }
+
+  void endTagIgnore(EndTagToken token) {
+    parser.parseError(token.span, "unexpected-end-tag", {"name": token.name});
+  }
+
+  Token endTagOther(EndTagToken token) {
+    return parser._inBodyPhase.processEndTag(token);
+  }
+}
+
+class InColumnGroupPhase extends Phase {
+  // http://www.whatwg.org/specs/web-apps/current-work///in-column
+  InColumnGroupPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case "html":
+        return startTagHtml(token);
+      case "col":
+        return startTagCol(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "colgroup":
+        return endTagColgroup(token);
+      case "col":
+        return endTagCol(token);
+      default:
+        return endTagOther(token);
+    }
+  }
+
+  bool ignoreEndTagColgroup() {
+    return tree.openElements.last.localName == "html";
+  }
+
+  bool processEOF() {
+    var ignoreEndTag = ignoreEndTagColgroup();
+    if (ignoreEndTag) {
+      assert(parser.innerHTMLMode);
+      return false;
+    } else {
+      endTagColgroup(new EndTagToken("colgroup"));
+      return true;
+    }
+  }
+
+  Token processCharacters(CharactersToken token) {
+    var ignoreEndTag = ignoreEndTagColgroup();
+    endTagColgroup(new EndTagToken("colgroup"));
+    return ignoreEndTag ? null : token;
+  }
+
+  void startTagCol(StartTagToken token) {
+    tree.insertElement(token);
+    tree.openElements.removeLast();
+  }
+
+  Token startTagOther(StartTagToken token) {
+    var ignoreEndTag = ignoreEndTagColgroup();
+    endTagColgroup(new EndTagToken("colgroup"));
+    return ignoreEndTag ? null : token;
+  }
+
+  void endTagColgroup(EndTagToken token) {
+    if (ignoreEndTagColgroup()) {
+      // innerHTML case
+      assert(parser.innerHTMLMode);
+      parser.parseError(token.span, "undefined-error");
+    } else {
+      tree.openElements.removeLast();
+      parser.phase = parser._inTablePhase;
+    }
+  }
+
+  void endTagCol(EndTagToken token) {
+    parser.parseError(token.span, "no-end-tag", {"name": "col"});
+  }
+
+  Token endTagOther(EndTagToken token) {
+    var ignoreEndTag = ignoreEndTagColgroup();
+    endTagColgroup(new EndTagToken("colgroup"));
+    return ignoreEndTag ? null : token;
+  }
+}
+
+class InTableBodyPhase extends Phase {
+  // http://www.whatwg.org/specs/web-apps/current-work///in-table0
+  InTableBodyPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case "html":
+        return startTagHtml(token);
+      case "tr":
+        return startTagTr(token);
+      case "td":
+      case "th":
+        return startTagTableCell(token);
+      case "caption":
+      case "col":
+      case "colgroup":
+      case "tbody":
+      case "tfoot":
+      case "thead":
+        return startTagTableOther(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "tbody":
+      case "tfoot":
+      case "thead":
+        return endTagTableRowGroup(token);
+      case "table":
+        return endTagTable(token);
+      case "body":
+      case "caption":
+      case "col":
+      case "colgroup":
+      case "html":
+      case "td":
+      case "th":
+      case "tr":
+        return endTagIgnore(token);
+      default:
+        return endTagOther(token);
+    }
+  }
+
+  // helper methods
+  void clearStackToTableBodyContext() {
+    var tableTags = const ["tbody", "tfoot", "thead", "html"];
+    while (!tableTags.contains(tree.openElements.last.localName)) {
+      //XXX parser.parseError(token.span, "unexpected-implied-end-tag-in-table",
+      //  {"name": tree.openElements.last.name})
+      tree.openElements.removeLast();
+    }
+    if (tree.openElements.last.localName == "html") {
+      assert(parser.innerHTMLMode);
+    }
+  }
+
+  // the rest
+  bool processEOF() {
+    parser._inTablePhase.processEOF();
+    return false;
+  }
+
+  Token processSpaceCharacters(SpaceCharactersToken token) {
+    return parser._inTablePhase.processSpaceCharacters(token);
+  }
+
+  Token processCharacters(CharactersToken token) {
+    return parser._inTablePhase.processCharacters(token);
+  }
+
+  void startTagTr(StartTagToken token) {
+    clearStackToTableBodyContext();
+    tree.insertElement(token);
+    parser.phase = parser._inRowPhase;
+  }
+
+  Token startTagTableCell(StartTagToken token) {
+    parser.parseError(
+        token.span, "unexpected-cell-in-table-body", {"name": token.name});
+    startTagTr(new StartTagToken("tr", data: {}));
+    return token;
+  }
+
+  Token startTagTableOther(token) => endTagTable(token);
+
+  Token startTagOther(StartTagToken token) {
+    return parser._inTablePhase.processStartTag(token);
+  }
+
+  void endTagTableRowGroup(EndTagToken token) {
+    if (tree.elementInScope(token.name, variant: "table")) {
+      clearStackToTableBodyContext();
+      tree.openElements.removeLast();
+      parser.phase = parser._inTablePhase;
+    } else {
+      parser.parseError(
+          token.span, "unexpected-end-tag-in-table-body", {"name": token.name});
+    }
+  }
+
+  Token endTagTable(TagToken token) {
+    // XXX AT Any ideas on how to share this with endTagTable?
+    if (tree.elementInScope("tbody", variant: "table") ||
+        tree.elementInScope("thead", variant: "table") ||
+        tree.elementInScope("tfoot", variant: "table")) {
+      clearStackToTableBodyContext();
+      endTagTableRowGroup(new EndTagToken(tree.openElements.last.localName));
+      return token;
+    } else {
+      // innerHTML case
+      assert(parser.innerHTMLMode);
+      parser.parseError(token.span, "undefined-error");
+    }
+    return null;
+  }
+
+  void endTagIgnore(EndTagToken token) {
+    parser.parseError(
+        token.span, "unexpected-end-tag-in-table-body", {"name": token.name});
+  }
+
+  Token endTagOther(EndTagToken token) {
+    return parser._inTablePhase.processEndTag(token);
+  }
+}
+
+class InRowPhase extends Phase {
+  // http://www.whatwg.org/specs/web-apps/current-work///in-row
+  InRowPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case "html":
+        return startTagHtml(token);
+      case "td":
+      case "th":
+        return startTagTableCell(token);
+      case "caption":
+      case "col":
+      case "colgroup":
+      case "tbody":
+      case "tfoot":
+      case "thead":
+      case "tr":
+        return startTagTableOther(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "tr":
+        return endTagTr(token);
+      case "table":
+        return endTagTable(token);
+      case "tbody":
+      case "tfoot":
+      case "thead":
+        return endTagTableRowGroup(token);
+      case "body":
+      case "caption":
+      case "col":
+      case "colgroup":
+      case "html":
+      case "td":
+      case "th":
+        return endTagIgnore(token);
+      default:
+        return endTagOther(token);
+    }
+  }
+
+  // helper methods (XXX unify this with other table helper methods)
+  void clearStackToTableRowContext() {
+    while (true) {
+      var last = tree.openElements.last;
+      if (last.localName == "tr" || last.localName == "html") break;
+
+      parser.parseError(last.sourceSpan,
+          "unexpected-implied-end-tag-in-table-row", {
+        "name": tree.openElements.last.localName
+      });
+      tree.openElements.removeLast();
+    }
+  }
+
+  bool ignoreEndTagTr() {
+    return !tree.elementInScope("tr", variant: "table");
+  }
+
+  // the rest
+  bool processEOF() {
+    parser._inTablePhase.processEOF();
+    return false;
+  }
+
+  Token processSpaceCharacters(SpaceCharactersToken token) {
+    return parser._inTablePhase.processSpaceCharacters(token);
+  }
+
+  Token processCharacters(CharactersToken token) {
+    return parser._inTablePhase.processCharacters(token);
+  }
+
+  void startTagTableCell(StartTagToken token) {
+    clearStackToTableRowContext();
+    tree.insertElement(token);
+    parser.phase = parser._inCellPhase;
+    tree.activeFormattingElements.add(Marker);
+  }
+
+  Token startTagTableOther(StartTagToken token) {
+    bool ignoreEndTag = ignoreEndTagTr();
+    endTagTr(new EndTagToken("tr"));
+    // XXX how are we sure it's always ignored in the innerHTML case?
+    return ignoreEndTag ? null : token;
+  }
+
+  Token startTagOther(StartTagToken token) {
+    return parser._inTablePhase.processStartTag(token);
+  }
+
+  void endTagTr(EndTagToken token) {
+    if (!ignoreEndTagTr()) {
+      clearStackToTableRowContext();
+      tree.openElements.removeLast();
+      parser.phase = parser._inTableBodyPhase;
+    } else {
+      // innerHTML case
+      assert(parser.innerHTMLMode);
+      parser.parseError(token.span, "undefined-error");
+    }
+  }
+
+  Token endTagTable(EndTagToken token) {
+    var ignoreEndTag = ignoreEndTagTr();
+    endTagTr(new EndTagToken("tr"));
+    // Reprocess the current tag if the tr end tag was not ignored
+    // XXX how are we sure it's always ignored in the innerHTML case?
+    return ignoreEndTag ? null : token;
+  }
+
+  Token endTagTableRowGroup(EndTagToken token) {
+    if (tree.elementInScope(token.name, variant: "table")) {
+      endTagTr(new EndTagToken("tr"));
+      return token;
+    } else {
+      parser.parseError(token.span, "undefined-error");
+      return null;
+    }
+  }
+
+  void endTagIgnore(EndTagToken token) {
+    parser.parseError(
+        token.span, "unexpected-end-tag-in-table-row", {"name": token.name});
+  }
+
+  Token endTagOther(EndTagToken token) {
+    return parser._inTablePhase.processEndTag(token);
+  }
+}
+
+class InCellPhase extends Phase {
+  // http://www.whatwg.org/specs/web-apps/current-work///in-cell
+  InCellPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case "html":
+        return startTagHtml(token);
+      case "caption":
+      case "col":
+      case "colgroup":
+      case "tbody":
+      case "td":
+      case "tfoot":
+      case "th":
+      case "thead":
+      case "tr":
+        return startTagTableOther(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "td":
+      case "th":
+        return endTagTableCell(token);
+      case "body":
+      case "caption":
+      case "col":
+      case "colgroup":
+      case "html":
+        return endTagIgnore(token);
+      case "table":
+      case "tbody":
+      case "tfoot":
+      case "thead":
+      case "tr":
+        return endTagImply(token);
+      default:
+        return endTagOther(token);
+    }
+  }
+
+  // helper
+  void closeCell() {
+    if (tree.elementInScope("td", variant: "table")) {
+      endTagTableCell(new EndTagToken("td"));
+    } else if (tree.elementInScope("th", variant: "table")) {
+      endTagTableCell(new EndTagToken("th"));
+    }
+  }
+
+  // the rest
+  bool processEOF() {
+    parser._inBodyPhase.processEOF();
+    return false;
+  }
+
+  Token processCharacters(CharactersToken token) {
+    return parser._inBodyPhase.processCharacters(token);
+  }
+
+  Token startTagTableOther(StartTagToken token) {
+    if (tree.elementInScope("td", variant: "table") ||
+        tree.elementInScope("th", variant: "table")) {
+      closeCell();
+      return token;
+    } else {
+      // innerHTML case
+      assert(parser.innerHTMLMode);
+      parser.parseError(token.span, "undefined-error");
+      return null;
+    }
+  }
+
+  Token startTagOther(StartTagToken token) {
+    return parser._inBodyPhase.processStartTag(token);
+  }
+
+  void endTagTableCell(EndTagToken token) {
+    if (tree.elementInScope(token.name, variant: "table")) {
+      tree.generateImpliedEndTags(token.name);
+      if (tree.openElements.last.localName != token.name) {
+        parser.parseError(
+            token.span, "unexpected-cell-end-tag", {"name": token.name});
+        popOpenElementsUntil(token.name);
+      } else {
+        tree.openElements.removeLast();
+      }
+      tree.clearActiveFormattingElements();
+      parser.phase = parser._inRowPhase;
+    } else {
+      parser.parseError(token.span, "unexpected-end-tag", {"name": token.name});
+    }
+  }
+
+  void endTagIgnore(EndTagToken token) {
+    parser.parseError(token.span, "unexpected-end-tag", {"name": token.name});
+  }
+
+  Token endTagImply(EndTagToken token) {
+    if (tree.elementInScope(token.name, variant: "table")) {
+      closeCell();
+      return token;
+    } else {
+      // sometimes innerHTML case
+      parser.parseError(token.span, "undefined-error");
+    }
+    return null;
+  }
+
+  Token endTagOther(EndTagToken token) {
+    return parser._inBodyPhase.processEndTag(token);
+  }
+}
+
+class InSelectPhase extends Phase {
+  InSelectPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case "html":
+        return startTagHtml(token);
+      case "option":
+        return startTagOption(token);
+      case "optgroup":
+        return startTagOptgroup(token);
+      case "select":
+        return startTagSelect(token);
+      case "input":
+      case "keygen":
+      case "textarea":
+        return startTagInput(token);
+      case "script":
+        return startTagScript(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "option":
+        return endTagOption(token);
+      case "optgroup":
+        return endTagOptgroup(token);
+      case "select":
+        return endTagSelect(token);
+      default:
+        return endTagOther(token);
+    }
+  }
+
+  // http://www.whatwg.org/specs/web-apps/current-work///in-select
+  bool processEOF() {
+    var last = tree.openElements.last;
+    if (last.localName != "html") {
+      parser.parseError(last.sourceSpan, "eof-in-select");
+    } else {
+      assert(parser.innerHTMLMode);
+    }
+    return false;
+  }
+
+  Token processCharacters(CharactersToken token) {
+    if (token.data == "\u0000") {
+      return null;
+    }
+    tree.insertText(token.data, token.span);
+    return null;
+  }
+
+  void startTagOption(StartTagToken token) {
+    // We need to imply </option> if <option> is the current node.
+    if (tree.openElements.last.localName == "option") {
+      tree.openElements.removeLast();
+    }
+    tree.insertElement(token);
+  }
+
+  void startTagOptgroup(StartTagToken token) {
+    if (tree.openElements.last.localName == "option") {
+      tree.openElements.removeLast();
+    }
+    if (tree.openElements.last.localName == "optgroup") {
+      tree.openElements.removeLast();
+    }
+    tree.insertElement(token);
+  }
+
+  void startTagSelect(StartTagToken token) {
+    parser.parseError(token.span, "unexpected-select-in-select");
+    endTagSelect(new EndTagToken("select"));
+  }
+
+  Token startTagInput(StartTagToken token) {
+    parser.parseError(token.span, "unexpected-input-in-select");
+    if (tree.elementInScope("select", variant: "select")) {
+      endTagSelect(new EndTagToken("select"));
+      return token;
+    } else {
+      assert(parser.innerHTMLMode);
+    }
+    return null;
+  }
+
+  Token startTagScript(StartTagToken token) {
+    return parser._inHeadPhase.processStartTag(token);
+  }
+
+  Token startTagOther(StartTagToken token) {
+    parser.parseError(
+        token.span, "unexpected-start-tag-in-select", {"name": token.name});
+    return null;
+  }
+
+  void endTagOption(EndTagToken token) {
+    if (tree.openElements.last.localName == "option") {
+      tree.openElements.removeLast();
+    } else {
+      parser.parseError(
+          token.span, "unexpected-end-tag-in-select", {"name": "option"});
+    }
+  }
+
+  void endTagOptgroup(EndTagToken token) {
+    // </optgroup> implicitly closes <option>
+    if (tree.openElements.last.localName == "option" &&
+        tree.openElements[tree.openElements.length - 2].localName ==
+            "optgroup") {
+      tree.openElements.removeLast();
+    }
+    // It also closes </optgroup>
+    if (tree.openElements.last.localName == "optgroup") {
+      tree.openElements.removeLast();
+      // But nothing else
+    } else {
+      parser.parseError(
+          token.span, "unexpected-end-tag-in-select", {"name": "optgroup"});
+    }
+  }
+
+  void endTagSelect(EndTagToken token) {
+    if (tree.elementInScope("select", variant: "select")) {
+      popOpenElementsUntil("select");
+      parser.resetInsertionMode();
+    } else {
+      // innerHTML case
+      assert(parser.innerHTMLMode);
+      parser.parseError(token.span, "undefined-error");
+    }
+  }
+
+  void endTagOther(EndTagToken token) {
+    parser.parseError(
+        token.span, "unexpected-end-tag-in-select", {"name": token.name});
+  }
+}
+
+class InSelectInTablePhase extends Phase {
+  InSelectInTablePhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case "caption":
+      case "table":
+      case "tbody":
+      case "tfoot":
+      case "thead":
+      case "tr":
+      case "td":
+      case "th":
+        return startTagTable(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "caption":
+      case "table":
+      case "tbody":
+      case "tfoot":
+      case "thead":
+      case "tr":
+      case "td":
+      case "th":
+        return endTagTable(token);
+      default:
+        return endTagOther(token);
+    }
+  }
+
+  bool processEOF() {
+    parser._inSelectPhase.processEOF();
+    return false;
+  }
+
+  Token processCharacters(CharactersToken token) {
+    return parser._inSelectPhase.processCharacters(token);
+  }
+
+  Token startTagTable(StartTagToken token) {
+    parser.parseError(token.span,
+        "unexpected-table-element-start-tag-in-select-in-table", {
+      "name": token.name
+    });
+    endTagOther(new EndTagToken("select"));
+    return token;
+  }
+
+  Token startTagOther(StartTagToken token) {
+    return parser._inSelectPhase.processStartTag(token);
+  }
+
+  Token endTagTable(EndTagToken token) {
+    parser.parseError(token.span,
+        "unexpected-table-element-end-tag-in-select-in-table", {
+      "name": token.name
+    });
+    if (tree.elementInScope(token.name, variant: "table")) {
+      endTagOther(new EndTagToken("select"));
+      return token;
+    }
+    return null;
+  }
+
+  Token endTagOther(EndTagToken token) {
+    return parser._inSelectPhase.processEndTag(token);
+  }
+}
+
+class InForeignContentPhase extends Phase {
+  // TODO(jmesserly): this is sorted so we could binary search.
+  static const breakoutElements = const [
+    'b',
+    'big',
+    'blockquote',
+    'body',
+    'br',
+    'center',
+    'code',
+    'dd',
+    'div',
+    'dl',
+    'dt',
+    'em',
+    'embed',
+    'h1',
+    'h2',
+    'h3',
+    'h4',
+    'h5',
+    'h6',
+    'head',
+    'hr',
+    'i',
+    'img',
+    'li',
+    'listing',
+    'menu',
+    'meta',
+    'nobr',
+    'ol',
+    'p',
+    'pre',
+    'ruby',
+    's',
+    'small',
+    'span',
+    'strike',
+    'strong',
+    'sub',
+    'sup',
+    'table',
+    'tt',
+    'u',
+    'ul',
+    'var'
+  ];
+
+  InForeignContentPhase(parser) : super(parser);
+
+  void adjustSVGTagNames(token) {
+    final replacements = const {
+      "altglyph": "altGlyph",
+      "altglyphdef": "altGlyphDef",
+      "altglyphitem": "altGlyphItem",
+      "animatecolor": "animateColor",
+      "animatemotion": "animateMotion",
+      "animatetransform": "animateTransform",
+      "clippath": "clipPath",
+      "feblend": "feBlend",
+      "fecolormatrix": "feColorMatrix",
+      "fecomponenttransfer": "feComponentTransfer",
+      "fecomposite": "feComposite",
+      "feconvolvematrix": "feConvolveMatrix",
+      "fediffuselighting": "feDiffuseLighting",
+      "fedisplacementmap": "feDisplacementMap",
+      "fedistantlight": "feDistantLight",
+      "feflood": "feFlood",
+      "fefunca": "feFuncA",
+      "fefuncb": "feFuncB",
+      "fefuncg": "feFuncG",
+      "fefuncr": "feFuncR",
+      "fegaussianblur": "feGaussianBlur",
+      "feimage": "feImage",
+      "femerge": "feMerge",
+      "femergenode": "feMergeNode",
+      "femorphology": "feMorphology",
+      "feoffset": "feOffset",
+      "fepointlight": "fePointLight",
+      "fespecularlighting": "feSpecularLighting",
+      "fespotlight": "feSpotLight",
+      "fetile": "feTile",
+      "feturbulence": "feTurbulence",
+      "foreignobject": "foreignObject",
+      "glyphref": "glyphRef",
+      "lineargradient": "linearGradient",
+      "radialgradient": "radialGradient",
+      "textpath": "textPath"
+    };
+
+    var replace = replacements[token.name];
+    if (replace != null) {
+      token.name = replace;
+    }
+  }
+
+  Token processCharacters(CharactersToken token) {
+    if (token.data == "\u0000") {
+      token.replaceData("\uFFFD");
+    } else if (parser.framesetOK && !allWhitespace(token.data)) {
+      parser.framesetOK = false;
+    }
+    return super.processCharacters(token);
+  }
+
+  Token processStartTag(StartTagToken token) {
+    var currentNode = tree.openElements.last;
+    if (breakoutElements.contains(token.name) ||
+        (token.name == "font" &&
+            (token.data.containsKey("color") ||
+                token.data.containsKey("face") ||
+                token.data.containsKey("size")))) {
+      parser.parseError(token.span,
+          "unexpected-html-element-in-foreign-content", {'name': token.name});
+      while (tree.openElements.last.namespaceUri != tree.defaultNamespace &&
+          !parser.isHTMLIntegrationPoint(tree.openElements.last) &&
+          !parser.isMathMLTextIntegrationPoint(tree.openElements.last)) {
+        tree.openElements.removeLast();
+      }
+      return token;
+    } else {
+      if (currentNode.namespaceUri == Namespaces.mathml) {
+        parser.adjustMathMLAttributes(token);
+      } else if (currentNode.namespaceUri == Namespaces.svg) {
+        adjustSVGTagNames(token);
+        parser.adjustSVGAttributes(token);
+      }
+      parser.adjustForeignAttributes(token);
+      token.namespace = currentNode.namespaceUri;
+      tree.insertElement(token);
+      if (token.selfClosing) {
+        tree.openElements.removeLast();
+        token.selfClosingAcknowledged = true;
+      }
+      return null;
+    }
+  }
+
+  Token processEndTag(EndTagToken token) {
+    var nodeIndex = tree.openElements.length - 1;
+    var node = tree.openElements.last;
+    if (node.localName != token.name) {
+      parser.parseError(token.span, "unexpected-end-tag", {"name": token.name});
+    }
+
+    var newToken = null;
+    while (true) {
+      if (asciiUpper2Lower(node.localName) == token.name) {
+        //XXX this isn't in the spec but it seems necessary
+        if (parser.phase == parser._inTableTextPhase) {
+          InTableTextPhase inTableText = parser.phase;
+          inTableText.flushCharacters();
+          parser.phase = inTableText.originalPhase;
+        }
+        while (tree.openElements.removeLast() != node) {
+          assert(tree.openElements.length > 0);
+        }
+        newToken = null;
+        break;
+      }
+      nodeIndex -= 1;
+
+      node = tree.openElements[nodeIndex];
+      if (node.namespaceUri != tree.defaultNamespace) {
+        continue;
+      } else {
+        newToken = parser.phase.processEndTag(token);
+        break;
+      }
+    }
+    return newToken;
+  }
+}
+
+class AfterBodyPhase extends Phase {
+  AfterBodyPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    if (token.name == "html") return startTagHtml(token);
+    return startTagOther(token);
+  }
+
+  processEndTag(EndTagToken token) {
+    if (token.name == "html") return endTagHtml(token);
+    return endTagOther(token);
+  }
+
+  //Stop parsing
+  bool processEOF() => false;
+
+  Token processComment(CommentToken token) {
+    // This is needed because data is to be appended to the <html> element
+    // here and not to whatever is currently open.
+    tree.insertComment(token, tree.openElements[0]);
+    return null;
+  }
+
+  Token processCharacters(CharactersToken token) {
+    parser.parseError(token.span, "unexpected-char-after-body");
+    parser.phase = parser._inBodyPhase;
+    return token;
+  }
+
+  Token startTagHtml(StartTagToken token) {
+    return parser._inBodyPhase.processStartTag(token);
+  }
+
+  Token startTagOther(StartTagToken token) {
+    parser.parseError(
+        token.span, "unexpected-start-tag-after-body", {"name": token.name});
+    parser.phase = parser._inBodyPhase;
+    return token;
+  }
+
+  void endTagHtml(Token token) {
+    if (parser.innerHTMLMode) {
+      parser.parseError(token.span, "unexpected-end-tag-after-body-innerhtml");
+    } else {
+      parser.phase = parser._afterAfterBodyPhase;
+    }
+  }
+
+  Token endTagOther(EndTagToken token) {
+    parser.parseError(
+        token.span, "unexpected-end-tag-after-body", {"name": token.name});
+    parser.phase = parser._inBodyPhase;
+    return token;
+  }
+}
+
+class InFramesetPhase extends Phase {
+  // http://www.whatwg.org/specs/web-apps/current-work///in-frameset
+  InFramesetPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case "html":
+        return startTagHtml(token);
+      case "frameset":
+        return startTagFrameset(token);
+      case "frame":
+        return startTagFrame(token);
+      case "noframes":
+        return startTagNoframes(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "frameset":
+        return endTagFrameset(token);
+      default:
+        return endTagOther(token);
+    }
+  }
+
+  bool processEOF() {
+    var last = tree.openElements.last;
+    if (last.localName != "html") {
+      parser.parseError(last.sourceSpan, "eof-in-frameset");
+    } else {
+      assert(parser.innerHTMLMode);
+    }
+    return false;
+  }
+
+  Token processCharacters(CharactersToken token) {
+    parser.parseError(token.span, "unexpected-char-in-frameset");
+    return null;
+  }
+
+  void startTagFrameset(StartTagToken token) {
+    tree.insertElement(token);
+  }
+
+  void startTagFrame(StartTagToken token) {
+    tree.insertElement(token);
+    tree.openElements.removeLast();
+  }
+
+  Token startTagNoframes(StartTagToken token) {
+    return parser._inBodyPhase.processStartTag(token);
+  }
+
+  Token startTagOther(StartTagToken token) {
+    parser.parseError(
+        token.span, "unexpected-start-tag-in-frameset", {"name": token.name});
+    return null;
+  }
+
+  void endTagFrameset(EndTagToken token) {
+    if (tree.openElements.last.localName == "html") {
+      // innerHTML case
+      parser.parseError(
+          token.span, "unexpected-frameset-in-frameset-innerhtml");
+    } else {
+      tree.openElements.removeLast();
+    }
+    if (!parser.innerHTMLMode &&
+        tree.openElements.last.localName != "frameset") {
+      // If we're not in innerHTML mode and the the current node is not a
+      // "frameset" element (anymore) then switch.
+      parser.phase = parser._afterFramesetPhase;
+    }
+  }
+
+  void endTagOther(EndTagToken token) {
+    parser.parseError(
+        token.span, "unexpected-end-tag-in-frameset", {"name": token.name});
+  }
+}
+
+class AfterFramesetPhase extends Phase {
+  // http://www.whatwg.org/specs/web-apps/current-work///after3
+  AfterFramesetPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case "html":
+        return startTagHtml(token);
+      case "noframes":
+        return startTagNoframes(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  processEndTag(EndTagToken token) {
+    switch (token.name) {
+      case "html":
+        return endTagHtml(token);
+      default:
+        return endTagOther(token);
+    }
+  }
+
+  // Stop parsing
+  bool processEOF() => false;
+
+  Token processCharacters(CharactersToken token) {
+    parser.parseError(token.span, "unexpected-char-after-frameset");
+    return null;
+  }
+
+  Token startTagNoframes(StartTagToken token) {
+    return parser._inHeadPhase.processStartTag(token);
+  }
+
+  void startTagOther(StartTagToken token) {
+    parser.parseError(token.span, "unexpected-start-tag-after-frameset", {
+      "name": token.name
+    });
+  }
+
+  void endTagHtml(EndTagToken token) {
+    parser.phase = parser._afterAfterFramesetPhase;
+  }
+
+  void endTagOther(EndTagToken token) {
+    parser.parseError(
+        token.span, "unexpected-end-tag-after-frameset", {"name": token.name});
+  }
+}
+
+class AfterAfterBodyPhase extends Phase {
+  AfterAfterBodyPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    if (token.name == 'html') return startTagHtml(token);
+    return startTagOther(token);
+  }
+
+  bool processEOF() => false;
+
+  Token processComment(CommentToken token) {
+    tree.insertComment(token, tree.document);
+    return null;
+  }
+
+  Token processSpaceCharacters(SpaceCharactersToken token) {
+    return parser._inBodyPhase.processSpaceCharacters(token);
+  }
+
+  Token processCharacters(CharactersToken token) {
+    parser.parseError(token.span, "expected-eof-but-got-char");
+    parser.phase = parser._inBodyPhase;
+    return token;
+  }
+
+  Token startTagHtml(StartTagToken token) {
+    return parser._inBodyPhase.processStartTag(token);
+  }
+
+  Token startTagOther(StartTagToken token) {
+    parser.parseError(
+        token.span, "expected-eof-but-got-start-tag", {"name": token.name});
+    parser.phase = parser._inBodyPhase;
+    return token;
+  }
+
+  Token processEndTag(EndTagToken token) {
+    parser.parseError(
+        token.span, "expected-eof-but-got-end-tag", {"name": token.name});
+    parser.phase = parser._inBodyPhase;
+    return token;
+  }
+}
+
+class AfterAfterFramesetPhase extends Phase {
+  AfterAfterFramesetPhase(parser) : super(parser);
+
+  processStartTag(StartTagToken token) {
+    switch (token.name) {
+      case "html":
+        return startTagHtml(token);
+      case "noframes":
+        return startTagNoFrames(token);
+      default:
+        return startTagOther(token);
+    }
+  }
+
+  bool processEOF() => false;
+
+  Token processComment(CommentToken token) {
+    tree.insertComment(token, tree.document);
+    return null;
+  }
+
+  Token processSpaceCharacters(SpaceCharactersToken token) {
+    return parser._inBodyPhase.processSpaceCharacters(token);
+  }
+
+  Token processCharacters(CharactersToken token) {
+    parser.parseError(token.span, "expected-eof-but-got-char");
+    return null;
+  }
+
+  Token startTagHtml(StartTagToken token) {
+    return parser._inBodyPhase.processStartTag(token);
+  }
+
+  Token startTagNoFrames(StartTagToken token) {
+    return parser._inHeadPhase.processStartTag(token);
+  }
+
+  void startTagOther(StartTagToken token) {
+    parser.parseError(
+        token.span, "expected-eof-but-got-start-tag", {"name": token.name});
+  }
+
+  Token processEndTag(EndTagToken token) {
+    parser.parseError(
+        token.span, "expected-eof-but-got-end-tag", {"name": token.name});
+    return null;
+  }
+}
+
+/// Error in parsed document.
+class ParseError implements SourceSpanException {
+  final String errorCode;
+  final SourceSpan span;
+  final Map data;
+
+  ParseError(this.errorCode, this.span, this.data);
+
+  int get line => span.start.line;
+
+  int get column => span.start.column;
+
+  /// Gets the human readable error message for this error. Use
+  /// [span.getLocationMessage] or [toString] to get a message including span
+  /// information. If there is a file associated with the span, both
+  /// [span.getLocationMessage] and [toString] are equivalent. Otherwise,
+  /// [span.getLocationMessage] will not show any source url information, but
+  /// [toString] will include 'ParserError:' as a prefix.
+  String get message => formatStr(errorMessages[errorCode], data);
+
+  String toString({color}) {
+    var res = span.message(message, color: color);
+    return span.sourceUrl == null ? 'ParserError on $res' : 'On $res';
+  }
+}
+
+/// Convenience function to get the pair of namespace and localName.
+Pair<String, String> getElementNameTuple(Element e) {
+  var ns = e.namespaceUri;
+  if (ns == null) ns = Namespaces.html;
+  return new Pair(ns, e.localName);
+}
diff --git a/html/lib/parser_console.dart b/html/lib/parser_console.dart
new file mode 100644
index 0000000..515f891
--- /dev/null
+++ b/html/lib/parser_console.dart
@@ -0,0 +1,42 @@
+/// This library adds `dart:io` support to the HTML5 parser. Call
+/// [initDartIOSupport] before calling the [parse] methods and they will accept
+/// a [RandomAccessFile] as input, in addition to the other input types.
+library parser_console;
+
+import 'dart:io';
+import 'parser.dart';
+import 'src/inputstream.dart' as inputstream;
+
+/// Adds support to the [HtmlParser] for running on a console VM. In particular
+/// this means it will be able to handle `dart:io` and [RandomAccessFile]s as
+/// input to the various [parse] methods.
+void useConsole() {
+  inputstream.consoleSupport = new _ConsoleSupport();
+}
+
+class _ConsoleSupport extends inputstream.ConsoleSupport {
+  List<int> bytesFromFile(source) {
+    if (source is! RandomAccessFile) return null;
+    return readAllBytesFromFile(source);
+  }
+}
+
+// TODO(jmesserly): this should be `RandomAccessFile.readAllBytes`.
+/// Synchronously reads all bytes from the [file].
+List<int> readAllBytesFromFile(RandomAccessFile file) {
+  int length = file.lengthSync();
+  var bytes = new List<int>(length);
+
+  int bytesRead = 0;
+  while (bytesRead < length) {
+    int read = file.readIntoSync(bytes, bytesRead, length - bytesRead);
+    if (read <= 0) {
+      // This could happen if, for example, the file was resized while
+      // we're reading. Just shrink the bytes array and move on.
+      bytes = bytes.sublist(0, bytesRead);
+      break;
+    }
+    bytesRead += read;
+  }
+  return bytes;
+}
diff --git a/html/lib/src/char_encodings.dart b/html/lib/src/char_encodings.dart
new file mode 100644
index 0000000..5ddf727
--- /dev/null
+++ b/html/lib/src/char_encodings.dart
@@ -0,0 +1,223 @@
+/// Decodes bytes using the correct name. See [decodeBytes].
+library char_encodings;
+
+import 'dart:collection';
+import 'package:utf/utf.dart';
+
+// TODO(jmesserly): this function is conspicuously absent from dart:utf.
+/// Returns true if the [bytes] starts with a UTF-8 byte order mark.
+/// Since UTF-8 doesn't have byte order, it's somewhat of a misnomer, but it is
+/// used in HTML to detect the UTF-
+bool hasUtf8Bom(List<int> bytes, [int offset = 0, int length]) {
+  int end = length != null ? offset + length : bytes.length;
+  return (offset + 3) <= end &&
+      bytes[offset] == 0xEF &&
+      bytes[offset + 1] == 0xBB &&
+      bytes[offset + 2] == 0xBF;
+}
+
+// TODO(jmesserly): it's unfortunate that this has to be one-shot on the entire
+// file, but dart:utf does not expose stream-based decoders yet.
+/// Decodes the [bytes] with the provided [encoding] and returns an iterable for
+/// the codepoints. Supports the major unicode encodings as well as ascii and
+/// and windows-1252 encodings.
+Iterable<int> decodeBytes(String encoding, List<int> bytes, [int offset = 0,
+    int length,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  if (length == null) length = bytes.length;
+  final replace = replacementCodepoint;
+  switch (encoding) {
+    case 'ascii':
+      bytes = bytes.sublist(offset, offset + length);
+      // TODO(jmesserly): this was taken from runtime/bin/string_stream.dart
+      for (int byte in bytes) {
+        if (byte > 127) {
+          // TODO(jmesserly): ideally this would be DecoderException, like the
+          // one thrown in runtime/bin/string_stream.dart, but we don't want to
+          // depend on dart:io.
+          throw new FormatException("Illegal ASCII character $byte");
+        }
+      }
+      return bytes;
+
+    case 'windows-1252':
+    case 'cp1252':
+      return decodeWindows1252AsIterable(bytes, offset, length, replace);
+
+    case 'utf-8':
+      // NOTE: to match the behavior of the other decode functions, we eat the
+      // utf-8 BOM here.
+      if (hasUtf8Bom(bytes, offset, length)) {
+        offset += 3;
+        length -= 3;
+      }
+      return decodeUtf8AsIterable(bytes, offset, length, replace);
+
+    case 'utf-16':
+      return decodeUtf16AsIterable(bytes, offset, length, replace);
+    case 'utf-16-be':
+      return decodeUtf16beAsIterable(bytes, offset, length, true, replace);
+    case 'utf-16-le':
+      return decodeUtf16leAsIterable(bytes, offset, length, true, replace);
+
+    case 'utf-32':
+      return decodeUtf32AsIterable(bytes, offset, length, replace);
+    case 'utf-32-be':
+      return decodeUtf32beAsIterable(bytes, offset, length, true, replace);
+    case 'utf-32-le':
+      return decodeUtf32leAsIterable(bytes, offset, length, true, replace);
+
+    default:
+      throw new ArgumentError('Encoding $encoding not supported');
+  }
+}
+
+// TODO(jmesserly): use dart:utf once http://dartbug.com/6476 is fixed.
+/// Returns the code points for the [input]. This works like [String.charCodes]
+/// but it decodes UTF-16 surrogate pairs.
+List<int> toCodepoints(String input) {
+  var newCodes = <int>[];
+  for (int i = 0; i < input.length; i++) {
+    var c = input.codeUnitAt(i);
+    if (0xD800 <= c && c <= 0xDBFF) {
+      int next = i + 1;
+      if (next < input.length) {
+        var d = input.codeUnitAt(next);
+        if (0xDC00 <= d && d <= 0xDFFF) {
+          c = 0x10000 + ((c - 0xD800) << 10) + (d - 0xDC00);
+          i = next;
+        }
+      }
+    }
+    newCodes.add(c);
+  }
+  return newCodes;
+}
+
+/// Decodes [windows-1252](http://en.wikipedia.org/wiki/Windows-1252) bytes as
+/// an iterable. Thus, the consumer can only convert as much of the input as
+/// needed. Set the [replacementCharacter] to null to throw an [ArgumentError]
+/// rather than replace the bad value.
+IterableWindows1252Decoder decodeWindows1252AsIterable(List<int> bytes,
+    [int offset = 0, int length,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  return new IterableWindows1252Decoder(
+      bytes, offset, length, replacementCodepoint);
+}
+
+/// Return type of [decodeWindows1252AsIterable] and variants. The Iterable type
+/// provides an iterator on demand and the iterator will only translate bytes
+/// as requested by the user of the iterator. (Note: results are not cached.)
+class IterableWindows1252Decoder extends IterableBase<int> {
+  final List<int> bytes;
+  final int offset;
+  final int length;
+  final int replacementCodepoint;
+
+  IterableWindows1252Decoder(List<int> this.bytes, [int this.offset = 0,
+      int this.length = null,
+      int this.replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]);
+
+  Windows1252Decoder get iterator =>
+      new Windows1252Decoder(bytes, offset, length, replacementCodepoint);
+}
+
+/// Provides an iterator of Unicode codepoints from windows-1252 encoded bytes.
+/// The parameters can set an offset into a list of bytes (as int), limit the
+/// length of the values to be decoded, and override the default Unicode
+/// replacement character. Set the replacementCharacter to null to throw an
+/// ArgumentError rather than replace the bad value. The return value
+/// from this method can be used as an Iterable (e.g. in a for-loop).
+class Windows1252Decoder implements Iterator<int> {
+  final int replacementCodepoint;
+  final List<int> _bytes;
+  int _offset;
+  final int _length;
+
+  Windows1252Decoder(List<int> bytes, [int offset = 0, int length,
+      this.replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT])
+      : _bytes = bytes,
+        _offset = offset - 1,
+        _length = length == null ? bytes.length : length;
+
+  bool get _inRange => _offset >= 0 && _offset < _length;
+  int get current => _inRange ? _mapChar(_bytes[_offset]) : null;
+
+  bool moveNext() {
+    _offset++;
+    return _inRange;
+  }
+
+  int _mapChar(int char) {
+    // TODO(jmesserly): this is duplicating entitiesWindows1252 and
+    // replacementCharacters from constants.dart
+    switch (char) {
+      case 0x80:
+        return 0x20AC; // EURO SIGN
+      case 0x82:
+        return 0x201A; // SINGLE LOW-9 QUOTATION MARK
+      case 0x83:
+        return 0x0192; // LATIN SMALL LETTER F WITH HOOK
+      case 0x84:
+        return 0x201E; // DOUBLE LOW-9 QUOTATION MARK
+      case 0x85:
+        return 0x2026; // HORIZONTAL ELLIPSIS
+      case 0x86:
+        return 0x2020; // DAGGER
+      case 0x87:
+        return 0x2021; // DOUBLE DAGGER
+      case 0x88:
+        return 0x02C6; // MODIFIER LETTER CIRCUMFLEX ACCENT
+      case 0x89:
+        return 0x2030; // PER MILLE SIGN
+      case 0x8A:
+        return 0x0160; // LATIN CAPITAL LETTER S WITH CARON
+      case 0x8B:
+        return 0x2039; // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+      case 0x8C:
+        return 0x0152; // LATIN CAPITAL LIGATURE OE
+      case 0x8E:
+        return 0x017D; // LATIN CAPITAL LETTER Z WITH CARON
+      case 0x91:
+        return 0x2018; // LEFT SINGLE QUOTATION MARK
+      case 0x92:
+        return 0x2019; // RIGHT SINGLE QUOTATION MARK
+      case 0x93:
+        return 0x201C; // LEFT DOUBLE QUOTATION MARK
+      case 0x94:
+        return 0x201D; // RIGHT DOUBLE QUOTATION MARK
+      case 0x95:
+        return 0x2022; // BULLET
+      case 0x96:
+        return 0x2013; // EN DASH
+      case 0x97:
+        return 0x2014; // EM DASH
+      case 0x98:
+        return 0x02DC; // SMALL TILDE
+      case 0x99:
+        return 0x2122; // TRADE MARK SIGN
+      case 0x9A:
+        return 0x0161; // LATIN SMALL LETTER S WITH CARON
+      case 0x9B:
+        return 0x203A; // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+      case 0x9C:
+        return 0x0153; // LATIN SMALL LIGATURE OE
+      case 0x9E:
+        return 0x017E; // LATIN SMALL LETTER Z WITH CARON
+      case 0x9F:
+        return 0x0178; // LATIN CAPITAL LETTER Y WITH DIAERESIS
+
+      case 0x81:
+      case 0x8D:
+      case 0x8F:
+      case 0x90:
+      case 0x9D:
+        if (replacementCodepoint == null) {
+          throw new ArgumentError(
+              "Invalid windows-1252 code point $char at $_offset");
+        }
+        return replacementCodepoint;
+    }
+    return char;
+  }
+}
diff --git a/html/lib/src/constants.dart b/html/lib/src/constants.dart
new file mode 100644
index 0000000..e85e43a
--- /dev/null
+++ b/html/lib/src/constants.dart
@@ -0,0 +1,3082 @@
+library constants;
+
+import 'utils.dart';
+
+// TODO(jmesserly): fix up the const lists. For the bigger ones, we need faster
+// lookup than linear search "contains". In the Python code they were
+// frozensets.
+
+final String EOF = null;
+
+class ReparseException implements Exception {
+  final String message;
+  ReparseException(this.message);
+  String toString() => "ReparseException: $message";
+}
+
+// TODO(jmesserly): assuming the programmatic name is not important, it would be
+// good to make these "static const" fields on an ErrorMessage class.
+/// These are error messages emitted by [HtmlParser]. The values use Python
+/// style string formatting, as implemented by [formatStr]. That function only
+/// supports the subset of format functionality used here.
+const Map<String, String> errorMessages = const {
+  "null-character": "Null character in input stream, replaced with U+FFFD.",
+  "invalid-codepoint": "Invalid codepoint in stream.",
+  "incorrectly-placed-solidus": "Solidus (/) incorrectly placed in tag.",
+  "incorrect-cr-newline-entity":
+      "Incorrect CR newline entity, replaced with LF.",
+  "illegal-windows-1252-entity":
+      "Entity used with illegal number (windows-1252 reference).",
+  "cant-convert-numeric-entity":
+      "Numeric entity couldn't be converted to character "
+      "(codepoint U+%(charAsInt)08x).",
+  "illegal-codepoint-for-numeric-entity":
+      "Numeric entity represents an illegal codepoint: "
+      "U+%(charAsInt)08x.",
+  "numeric-entity-without-semicolon": "Numeric entity didn't end with ';'.",
+  "expected-numeric-entity-but-got-eof":
+      "Numeric entity expected. Got end of file instead.",
+  "expected-numeric-entity": "Numeric entity expected but none found.",
+  "named-entity-without-semicolon": "Named entity didn't end with ';'.",
+  "expected-named-entity": "Named entity expected. Got none.",
+  "attributes-in-end-tag": "End tag contains unexpected attributes.",
+  'self-closing-flag-on-end-tag':
+      "End tag contains unexpected self-closing flag.",
+  "expected-tag-name-but-got-right-bracket":
+      "Expected tag name. Got '>' instead.",
+  "expected-tag-name-but-got-question-mark":
+      "Expected tag name. Got '?' instead. (HTML doesn't "
+      "support processing instructions.)",
+  "expected-tag-name": "Expected tag name. Got something else instead",
+  "expected-closing-tag-but-got-right-bracket":
+      "Expected closing tag. Got '>' instead. Ignoring '</>'.",
+  "expected-closing-tag-but-got-eof":
+      "Expected closing tag. Unexpected end of file.",
+  "expected-closing-tag-but-got-char":
+      "Expected closing tag. Unexpected character '%(data)s' found.",
+  "eof-in-tag-name": "Unexpected end of file in the tag name.",
+  "expected-attribute-name-but-got-eof":
+      "Unexpected end of file. Expected attribute name instead.",
+  "eof-in-attribute-name": "Unexpected end of file in attribute name.",
+  "invalid-character-in-attribute-name": "Invalid character in attribute name",
+  "duplicate-attribute": "Dropped duplicate attribute on tag.",
+  "expected-end-of-tag-name-but-got-eof":
+      "Unexpected end of file. Expected = or end of tag.",
+  "expected-attribute-value-but-got-eof":
+      "Unexpected end of file. Expected attribute value.",
+  "expected-attribute-value-but-got-right-bracket":
+      "Expected attribute value. Got '>' instead.",
+  'equals-in-unquoted-attribute-value': "Unexpected = in unquoted attribute",
+  'unexpected-character-in-unquoted-attribute-value':
+      "Unexpected character in unquoted attribute",
+  "invalid-character-after-attribute-name":
+      "Unexpected character after attribute name.",
+  "unexpected-character-after-attribute-value":
+      "Unexpected character after attribute value.",
+  "eof-in-attribute-value-double-quote":
+      "Unexpected end of file in attribute value (\".",
+  "eof-in-attribute-value-single-quote":
+      "Unexpected end of file in attribute value (').",
+  "eof-in-attribute-value-no-quotes":
+      "Unexpected end of file in attribute value.",
+  "unexpected-EOF-after-solidus-in-tag":
+      "Unexpected end of file in tag. Expected >",
+  "unexpected-character-after-soldius-in-tag":
+      "Unexpected character after / in tag. Expected >",
+  "expected-dashes-or-doctype": "Expected '--' or 'DOCTYPE'. Not found.",
+  "unexpected-bang-after-double-dash-in-comment":
+      "Unexpected ! after -- in comment",
+  "unexpected-space-after-double-dash-in-comment":
+      "Unexpected space after -- in comment",
+  "incorrect-comment": "Incorrect comment.",
+  "eof-in-comment": "Unexpected end of file in comment.",
+  "eof-in-comment-end-dash": "Unexpected end of file in comment (-)",
+  "unexpected-dash-after-double-dash-in-comment":
+      "Unexpected '-' after '--' found in comment.",
+  "eof-in-comment-double-dash": "Unexpected end of file in comment (--).",
+  "eof-in-comment-end-space-state": "Unexpected end of file in comment.",
+  "eof-in-comment-end-bang-state": "Unexpected end of file in comment.",
+  "unexpected-char-in-comment": "Unexpected character in comment found.",
+  "need-space-after-doctype": "No space after literal string 'DOCTYPE'.",
+  "expected-doctype-name-but-got-right-bracket":
+      "Unexpected > character. Expected DOCTYPE name.",
+  "expected-doctype-name-but-got-eof":
+      "Unexpected end of file. Expected DOCTYPE name.",
+  "eof-in-doctype-name": "Unexpected end of file in DOCTYPE name.",
+  "eof-in-doctype": "Unexpected end of file in DOCTYPE.",
+  "expected-space-or-right-bracket-in-doctype":
+      "Expected space or '>'. Got '%(data)s'",
+  "unexpected-end-of-doctype": "Unexpected end of DOCTYPE.",
+  "unexpected-char-in-doctype": "Unexpected character in DOCTYPE.",
+  "eof-in-innerhtml": "XXX innerHTML EOF",
+  "unexpected-doctype": "Unexpected DOCTYPE. Ignored.",
+  "non-html-root": "html needs to be the first start tag.",
+  "expected-doctype-but-got-eof": "Unexpected End of file. Expected DOCTYPE.",
+  "unknown-doctype": "Erroneous DOCTYPE.",
+  "expected-doctype-but-got-chars":
+      "Unexpected non-space characters. Expected DOCTYPE.",
+  "expected-doctype-but-got-start-tag":
+      "Unexpected start tag (%(name)s). Expected DOCTYPE.",
+  "expected-doctype-but-got-end-tag":
+      "Unexpected end tag (%(name)s). Expected DOCTYPE.",
+  "end-tag-after-implied-root":
+      "Unexpected end tag (%(name)s) after the (implied) root element.",
+  "expected-named-closing-tag-but-got-eof":
+      "Unexpected end of file. Expected end tag (%(name)s).",
+  "two-heads-are-not-better-than-one":
+      "Unexpected start tag head in existing head. Ignored.",
+  "unexpected-end-tag": "Unexpected end tag (%(name)s). Ignored.",
+  "unexpected-start-tag-out-of-my-head":
+      "Unexpected start tag (%(name)s) that can be in head. Moved.",
+  "unexpected-start-tag": "Unexpected start tag (%(name)s).",
+  "missing-end-tag": "Missing end tag (%(name)s).",
+  "missing-end-tags": "Missing end tags (%(name)s).",
+  "unexpected-start-tag-implies-end-tag":
+      "Unexpected start tag (%(startName)s) "
+      "implies end tag (%(endName)s).",
+  "unexpected-start-tag-treated-as":
+      "Unexpected start tag (%(originalName)s). Treated as %(newName)s.",
+  "deprecated-tag": "Unexpected start tag %(name)s. Don't use it!",
+  "unexpected-start-tag-ignored": "Unexpected start tag %(name)s. Ignored.",
+  "expected-one-end-tag-but-got-another": "Unexpected end tag (%(gotName)s). "
+      "Missing end tag (%(expectedName)s).",
+  "end-tag-too-early":
+      "End tag (%(name)s) seen too early. Expected other end tag.",
+  "end-tag-too-early-named":
+      "Unexpected end tag (%(gotName)s). Expected end tag (%(expectedName)s).",
+  "end-tag-too-early-ignored": "End tag (%(name)s) seen too early. Ignored.",
+  "adoption-agency-1.1": "End tag (%(name)s) violates step 1, "
+      "paragraph 1 of the adoption agency algorithm.",
+  "adoption-agency-1.2": "End tag (%(name)s) violates step 1, "
+      "paragraph 2 of the adoption agency algorithm.",
+  "adoption-agency-1.3": "End tag (%(name)s) violates step 1, "
+      "paragraph 3 of the adoption agency algorithm.",
+  "unexpected-end-tag-treated-as":
+      "Unexpected end tag (%(originalName)s). Treated as %(newName)s.",
+  "no-end-tag": "This element (%(name)s) has no end tag.",
+  "unexpected-implied-end-tag-in-table":
+      "Unexpected implied end tag (%(name)s) in the table phase.",
+  "unexpected-implied-end-tag-in-table-body":
+      "Unexpected implied end tag (%(name)s) in the table body phase.",
+  "unexpected-char-implies-table-voodoo": "Unexpected non-space characters in "
+      "table context caused voodoo mode.",
+  "unexpected-hidden-input-in-table":
+      "Unexpected input with type hidden in table context.",
+  "unexpected-form-in-table": "Unexpected form in table context.",
+  "unexpected-start-tag-implies-table-voodoo":
+      "Unexpected start tag (%(name)s) in "
+      "table context caused voodoo mode.",
+  "unexpected-end-tag-implies-table-voodoo": "Unexpected end tag (%(name)s) in "
+      "table context caused voodoo mode.",
+  "unexpected-cell-in-table-body": "Unexpected table cell start tag (%(name)s) "
+      "in the table body phase.",
+  "unexpected-cell-end-tag": "Got table cell end tag (%(name)s) "
+      "while required end tags are missing.",
+  "unexpected-end-tag-in-table-body":
+      "Unexpected end tag (%(name)s) in the table body phase. Ignored.",
+  "unexpected-implied-end-tag-in-table-row":
+      "Unexpected implied end tag (%(name)s) in the table row phase.",
+  "unexpected-end-tag-in-table-row":
+      "Unexpected end tag (%(name)s) in the table row phase. Ignored.",
+  "unexpected-select-in-select":
+      "Unexpected select start tag in the select phase "
+      "treated as select end tag.",
+  "unexpected-input-in-select":
+      "Unexpected input start tag in the select phase.",
+  "unexpected-start-tag-in-select":
+      "Unexpected start tag token (%(name)s in the select phase. "
+      "Ignored.",
+  "unexpected-end-tag-in-select":
+      "Unexpected end tag (%(name)s) in the select phase. Ignored.",
+  "unexpected-table-element-start-tag-in-select-in-table":
+      "Unexpected table element start tag (%(name)s) in the select in table phase.",
+  "unexpected-table-element-end-tag-in-select-in-table":
+      "Unexpected table element end tag (%(name)s) in the select in table phase.",
+  "unexpected-char-after-body":
+      "Unexpected non-space characters in the after body phase.",
+  "unexpected-start-tag-after-body": "Unexpected start tag token (%(name)s)"
+      " in the after body phase.",
+  "unexpected-end-tag-after-body": "Unexpected end tag token (%(name)s)"
+      " in the after body phase.",
+  "unexpected-char-in-frameset":
+      "Unepxected characters in the frameset phase. Characters ignored.",
+  "unexpected-start-tag-in-frameset": "Unexpected start tag token (%(name)s)"
+      " in the frameset phase. Ignored.",
+  "unexpected-frameset-in-frameset-innerhtml":
+      "Unexpected end tag token (frameset) "
+      "in the frameset phase (innerHTML).",
+  "unexpected-end-tag-in-frameset": "Unexpected end tag token (%(name)s)"
+      " in the frameset phase. Ignored.",
+  "unexpected-char-after-frameset": "Unexpected non-space characters in the "
+      "after frameset phase. Ignored.",
+  "unexpected-start-tag-after-frameset": "Unexpected start tag (%(name)s)"
+      " in the after frameset phase. Ignored.",
+  "unexpected-end-tag-after-frameset": "Unexpected end tag (%(name)s)"
+      " in the after frameset phase. Ignored.",
+  "unexpected-end-tag-after-body-innerhtml":
+      "Unexpected end tag after body(innerHtml)",
+  "expected-eof-but-got-char":
+      "Unexpected non-space characters. Expected end of file.",
+  "expected-eof-but-got-start-tag": "Unexpected start tag (%(name)s)"
+      ". Expected end of file.",
+  "expected-eof-but-got-end-tag": "Unexpected end tag (%(name)s)"
+      ". Expected end of file.",
+  "eof-in-table": "Unexpected end of file. Expected table content.",
+  "eof-in-select": "Unexpected end of file. Expected select content.",
+  "eof-in-frameset": "Unexpected end of file. Expected frameset content.",
+  "eof-in-script-in-script": "Unexpected end of file. Expected script content.",
+  "eof-in-foreign-lands": "Unexpected end of file. Expected foreign content",
+  "non-void-element-with-trailing-solidus":
+      "Trailing solidus not allowed on element %(name)s",
+  "unexpected-html-element-in-foreign-content":
+      "Element %(name)s not allowed in a non-html context",
+  "unexpected-end-tag-before-html":
+      "Unexpected end tag (%(name)s) before html.",
+  "undefined-error": "Undefined error (this sucks and should be fixed)",
+};
+
+class Namespaces {
+  static const html = "http://www.w3.org/1999/xhtml";
+  static const mathml = "http://www.w3.org/1998/Math/MathML";
+  static const svg = "http://www.w3.org/2000/svg";
+  static const xlink = "http://www.w3.org/1999/xlink";
+  static const xml = "http://www.w3.org/XML/1998/namespace";
+  static const xmlns = "http://www.w3.org/2000/xmlns/";
+  Namespaces._();
+
+  static String getPrefix(String url) {
+    switch (url) {
+      case html:
+        return 'html';
+      case mathml:
+        return 'math';
+      case svg:
+        return 'svg';
+      case xlink:
+        return 'xlink';
+      case xml:
+        return 'xml';
+      case xmlns:
+        return 'xmlns';
+      default:
+        return null;
+    }
+  }
+}
+
+const List scopingElements = const [
+  const Pair(Namespaces.html, "applet"),
+  const Pair(Namespaces.html, "caption"),
+  const Pair(Namespaces.html, "html"),
+  const Pair(Namespaces.html, "marquee"),
+  const Pair(Namespaces.html, "object"),
+  const Pair(Namespaces.html, "table"),
+  const Pair(Namespaces.html, "td"),
+  const Pair(Namespaces.html, "th"),
+  const Pair(Namespaces.mathml, "mi"),
+  const Pair(Namespaces.mathml, "mo"),
+  const Pair(Namespaces.mathml, "mn"),
+  const Pair(Namespaces.mathml, "ms"),
+  const Pair(Namespaces.mathml, "mtext"),
+  const Pair(Namespaces.mathml, "annotation-xml"),
+  const Pair(Namespaces.svg, "foreignObject"),
+  const Pair(Namespaces.svg, "desc"),
+  const Pair(Namespaces.svg, "title")
+];
+
+const formattingElements = const [
+  const Pair(Namespaces.html, "a"),
+  const Pair(Namespaces.html, "b"),
+  const Pair(Namespaces.html, "big"),
+  const Pair(Namespaces.html, "code"),
+  const Pair(Namespaces.html, "em"),
+  const Pair(Namespaces.html, "font"),
+  const Pair(Namespaces.html, "i"),
+  const Pair(Namespaces.html, "nobr"),
+  const Pair(Namespaces.html, "s"),
+  const Pair(Namespaces.html, "small"),
+  const Pair(Namespaces.html, "strike"),
+  const Pair(Namespaces.html, "strong"),
+  const Pair(Namespaces.html, "tt"),
+  const Pair(Namespaces.html, "")
+];
+
+const specialElements = const [
+  const Pair(Namespaces.html, "address"),
+  const Pair(Namespaces.html, "applet"),
+  const Pair(Namespaces.html, "area"),
+  const Pair(Namespaces.html, "article"),
+  const Pair(Namespaces.html, "aside"),
+  const Pair(Namespaces.html, "base"),
+  const Pair(Namespaces.html, "basefont"),
+  const Pair(Namespaces.html, "bgsound"),
+  const Pair(Namespaces.html, "blockquote"),
+  const Pair(Namespaces.html, "body"),
+  const Pair(Namespaces.html, "br"),
+  const Pair(Namespaces.html, "button"),
+  const Pair(Namespaces.html, "caption"),
+  const Pair(Namespaces.html, "center"),
+  const Pair(Namespaces.html, "col"),
+  const Pair(Namespaces.html, "colgroup"),
+  const Pair(Namespaces.html, "command"),
+  const Pair(Namespaces.html, "dd"),
+  const Pair(Namespaces.html, "details"),
+  const Pair(Namespaces.html, "dir"),
+  const Pair(Namespaces.html, "div"),
+  const Pair(Namespaces.html, "dl"),
+  const Pair(Namespaces.html, "dt"),
+  const Pair(Namespaces.html, "embed"),
+  const Pair(Namespaces.html, "fieldset"),
+  const Pair(Namespaces.html, "figure"),
+  const Pair(Namespaces.html, "footer"),
+  const Pair(Namespaces.html, "form"),
+  const Pair(Namespaces.html, "frame"),
+  const Pair(Namespaces.html, "frameset"),
+  const Pair(Namespaces.html, "h1"),
+  const Pair(Namespaces.html, "h2"),
+  const Pair(Namespaces.html, "h3"),
+  const Pair(Namespaces.html, "h4"),
+  const Pair(Namespaces.html, "h5"),
+  const Pair(Namespaces.html, "h6"),
+  const Pair(Namespaces.html, "head"),
+  const Pair(Namespaces.html, "header"),
+  const Pair(Namespaces.html, "hr"),
+  const Pair(Namespaces.html, "html"),
+  const Pair(Namespaces.html, "iframe"),
+  // Note that image is commented out in the spec as "this isn't an
+  // element that can end up on the stack, so it doesn't matter,"
+  const Pair(Namespaces.html, "image"),
+  const Pair(Namespaces.html, "img"),
+  const Pair(Namespaces.html, "input"),
+  const Pair(Namespaces.html, "isindex"),
+  const Pair(Namespaces.html, "li"),
+  const Pair(Namespaces.html, "link"),
+  const Pair(Namespaces.html, "listing"),
+  const Pair(Namespaces.html, "marquee"),
+  const Pair(Namespaces.html, "men"),
+  const Pair(Namespaces.html, "meta"),
+  const Pair(Namespaces.html, "nav"),
+  const Pair(Namespaces.html, "noembed"),
+  const Pair(Namespaces.html, "noframes"),
+  const Pair(Namespaces.html, "noscript"),
+  const Pair(Namespaces.html, "object"),
+  const Pair(Namespaces.html, "ol"),
+  const Pair(Namespaces.html, "p"),
+  const Pair(Namespaces.html, "param"),
+  const Pair(Namespaces.html, "plaintext"),
+  const Pair(Namespaces.html, "pre"),
+  const Pair(Namespaces.html, "script"),
+  const Pair(Namespaces.html, "section"),
+  const Pair(Namespaces.html, "select"),
+  const Pair(Namespaces.html, "style"),
+  const Pair(Namespaces.html, "table"),
+  const Pair(Namespaces.html, "tbody"),
+  const Pair(Namespaces.html, "td"),
+  const Pair(Namespaces.html, "textarea"),
+  const Pair(Namespaces.html, "tfoot"),
+  const Pair(Namespaces.html, "th"),
+  const Pair(Namespaces.html, "thead"),
+  const Pair(Namespaces.html, "title"),
+  const Pair(Namespaces.html, "tr"),
+  const Pair(Namespaces.html, "ul"),
+  const Pair(Namespaces.html, "wbr"),
+  const Pair(Namespaces.html, "xmp"),
+  const Pair(Namespaces.svg, "foreignObject")
+];
+
+const htmlIntegrationPointElements = const [
+  const Pair(Namespaces.mathml, "annotaion-xml"),
+  const Pair(Namespaces.svg, "foreignObject"),
+  const Pair(Namespaces.svg, "desc"),
+  const Pair(Namespaces.svg, "title")
+];
+
+const mathmlTextIntegrationPointElements = const [
+  const Pair(Namespaces.mathml, "mi"),
+  const Pair(Namespaces.mathml, "mo"),
+  const Pair(Namespaces.mathml, "mn"),
+  const Pair(Namespaces.mathml, "ms"),
+  const Pair(Namespaces.mathml, "mtext")
+];
+
+const spaceCharacters = " \n\r\t\u000C";
+
+const int NEWLINE = 10;
+const int RETURN = 13;
+
+bool isWhitespace(String char) {
+  if (char == null) return false;
+  return isWhitespaceCC(char.codeUnitAt(0));
+}
+
+bool isWhitespaceCC(int charCode) {
+  switch (charCode) {
+    case 9: // '\t'
+    case NEWLINE: // '\n'
+    case 12: // '\f'
+    case RETURN: // '\r'
+    case 32: // ' '
+      return true;
+  }
+  return false;
+}
+
+const List<String> tableInsertModeElements = const [
+  "table",
+  "tbody",
+  "tfoot",
+  "thead",
+  "tr"
+];
+
+// TODO(jmesserly): remove these in favor of the test functions
+const asciiLetters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
+const ZERO = 48;
+const LOWER_A = 97;
+const LOWER_Z = 122;
+const UPPER_A = 65;
+const UPPER_Z = 90;
+
+bool isLetterOrDigit(String char) => isLetter(char) || isDigit(char);
+
+// Note: this is intentially ASCII only
+bool isLetter(String char) {
+  if (char == null) return false;
+  int cc = char.codeUnitAt(0);
+  return cc >= LOWER_A && cc <= LOWER_Z || cc >= UPPER_A && cc <= UPPER_Z;
+}
+
+bool isDigit(String char) {
+  if (char == null) return false;
+  int cc = char.codeUnitAt(0);
+  return cc >= ZERO && cc < ZERO + 10;
+}
+
+bool isHexDigit(String char) {
+  if (char == null) return false;
+  switch (char.codeUnitAt(0)) {
+    case 48:
+    case 49:
+    case 50:
+    case 51:
+    case 52: // '0' - '4'
+    case 53:
+    case 54:
+    case 55:
+    case 56:
+    case 57: // '5' - '9'
+    case 65:
+    case 66:
+    case 67:
+    case 68:
+    case 69:
+    case 70: // 'A' - 'F'
+    case 97:
+    case 98:
+    case 99:
+    case 100:
+    case 101:
+    case 102: // 'a' - 'f'
+      return true;
+  }
+  return false;
+}
+
+// Note: based on the original Python code, I assume we only want to convert
+// ASCII chars to.toLowerCase() case, unlike Dart's toLowerCase function.
+String asciiUpper2Lower(String text) {
+  if (text == null) return null;
+  var result = new List<int>(text.length);
+  for (int i = 0; i < text.length; i++) {
+    var c = text.codeUnitAt(i);
+    if (c >= UPPER_A && c <= UPPER_Z) {
+      c += LOWER_A - UPPER_A;
+    }
+    result[i] = c;
+  }
+  return new String.fromCharCodes(result);
+}
+
+// Heading elements need to be ordered
+const headingElements = const ["h1", "h2", "h3", "h4", "h5", "h6"];
+
+const cdataElements = const ['title', 'textarea'];
+
+const rcdataElements = const [
+  'style',
+  'script',
+  'xmp',
+  'iframe',
+  'noembed',
+  'noframes',
+  'noscript'
+];
+
+const Map<String, List<String>> booleanAttributes = const {
+  "": const ["irrelevant",],
+  "style": const ["scoped",],
+  "img": const ["ismap",],
+  "audio": const ["autoplay", "controls"],
+  "video": const ["autoplay", "controls"],
+  "script": const ["defer", "async"],
+  "details": const ["open",],
+  "datagrid": const ["multiple", "disabled"],
+  "command": const ["hidden", "disabled", "checked", "default"],
+  "hr": const ["noshade"],
+  "men": const ["autosubmit",],
+  "fieldset": const ["disabled", "readonly"],
+  "option": const ["disabled", "readonly", "selected"],
+  "optgroup": const ["disabled", "readonly"],
+  "button": const ["disabled", "autofocus"],
+  "input": const [
+    "disabled",
+    "readonly",
+    "required",
+    "autofocus",
+    "checked",
+    "ismap"
+  ],
+  "select": const ["disabled", "readonly", "autofocus", "multiple"],
+  "output": const ["disabled", "readonly"],
+};
+
+// entitiesWindows1252 has to be _ordered_ and needs to have an index. It
+// therefore can't be a frozenset.
+const List<int> entitiesWindows1252 = const [
+  8364, // 0x80  0x20AC  EURO SIGN
+  65533, // 0x81          UNDEFINED
+  8218, // 0x82  0x201A  SINGLE LOW-9 QUOTATION MARK
+  402, // 0x83  0x0192  LATIN SMALL LETTER F WITH HOOK
+  8222, // 0x84  0x201E  DOUBLE LOW-9 QUOTATION MARK
+  8230, // 0x85  0x2026  HORIZONTAL ELLIPSIS
+  8224, // 0x86  0x2020  DAGGER
+  8225, // 0x87  0x2021  DOUBLE DAGGER
+  710, // 0x88  0x02C6  MODIFIER LETTER CIRCUMFLEX ACCENT
+  8240, // 0x89  0x2030  PER MILLE SIGN
+  352, // 0x8A  0x0160  LATIN CAPITAL LETTER S WITH CARON
+  8249, // 0x8B  0x2039  SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+  338, // 0x8C  0x0152  LATIN CAPITAL LIGATURE OE
+  65533, // 0x8D          UNDEFINED
+  381, // 0x8E  0x017D  LATIN CAPITAL LETTER Z WITH CARON
+  65533, // 0x8F          UNDEFINED
+  65533, // 0x90          UNDEFINED
+  8216, // 0x91  0x2018  LEFT SINGLE QUOTATION MARK
+  8217, // 0x92  0x2019  RIGHT SINGLE QUOTATION MARK
+  8220, // 0x93  0x201C  LEFT DOUBLE QUOTATION MARK
+  8221, // 0x94  0x201D  RIGHT DOUBLE QUOTATION MARK
+  8226, // 0x95  0x2022  BULLET
+  8211, // 0x96  0x2013  EN DASH
+  8212, // 0x97  0x2014  EM DASH
+  732, // 0x98  0x02DC  SMALL TILDE
+  8482, // 0x99  0x2122  TRADE MARK SIGN
+  353, // 0x9A  0x0161  LATIN SMALL LETTER S WITH CARON
+  8250, // 0x9B  0x203A  SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+  339, // 0x9C  0x0153  LATIN SMALL LIGATURE OE
+  65533, // 0x9D          UNDEFINED
+  382, // 0x9E  0x017E  LATIN SMALL LETTER Z WITH CARON
+  376 // 0x9F  0x0178  LATIN CAPITAL LETTER Y WITH DIAERESIS
+];
+
+const xmlEntities = const ['lt;', 'gt;', 'amp;', 'apos;', 'quot;'];
+
+const Map<String, String> entities = const {
+  "AElig": "\xc6",
+  "AElig;": "\xc6",
+  "AMP": "&",
+  "AMP;": "&",
+  "Aacute": "\xc1",
+  "Aacute;": "\xc1",
+  "Abreve;": "\u0102",
+  "Acirc": "\xc2",
+  "Acirc;": "\xc2",
+  "Acy;": "\u0410",
+  "Afr;": "\u{01d504}",
+  "Agrave": "\xc0",
+  "Agrave;": "\xc0",
+  "Alpha;": "\u0391",
+  "Amacr;": "\u0100",
+  "And;": "\u2a53",
+  "Aogon;": "\u0104",
+  "Aopf;": "\u{01d538}",
+  "ApplyFunction;": "\u2061",
+  "Aring": "\xc5",
+  "Aring;": "\xc5",
+  "Ascr;": "\u{01d49c}",
+  "Assign;": "\u2254",
+  "Atilde": "\xc3",
+  "Atilde;": "\xc3",
+  "Auml": "\xc4",
+  "Auml;": "\xc4",
+  "Backslash;": "\u2216",
+  "Barv;": "\u2ae7",
+  "Barwed;": "\u2306",
+  "Bcy;": "\u0411",
+  "Because;": "\u2235",
+  "Bernoullis;": "\u212c",
+  "Beta;": "\u0392",
+  "Bfr;": "\u{01d505}",
+  "Bopf;": "\u{01d539}",
+  "Breve;": "\u02d8",
+  "Bscr;": "\u212c",
+  "Bumpeq;": "\u224e",
+  "CHcy;": "\u0427",
+  "COPY": "\xa9",
+  "COPY;": "\xa9",
+  "Cacute;": "\u0106",
+  "Cap;": "\u22d2",
+  "CapitalDifferentialD;": "\u2145",
+  "Cayleys;": "\u212d",
+  "Ccaron;": "\u010c",
+  "Ccedil": "\xc7",
+  "Ccedil;": "\xc7",
+  "Ccirc;": "\u0108",
+  "Cconint;": "\u2230",
+  "Cdot;": "\u010a",
+  "Cedilla;": "\xb8",
+  "CenterDot;": "\xb7",
+  "Cfr;": "\u212d",
+  "Chi;": "\u03a7",
+  "CircleDot;": "\u2299",
+  "CircleMinus;": "\u2296",
+  "CirclePlus;": "\u2295",
+  "CircleTimes;": "\u2297",
+  "ClockwiseContourIntegral;": "\u2232",
+  "CloseCurlyDoubleQuote;": "\u201d",
+  "CloseCurlyQuote;": "\u2019",
+  "Colon;": "\u2237",
+  "Colone;": "\u2a74",
+  "Congruent;": "\u2261",
+  "Conint;": "\u222f",
+  "ContourIntegral;": "\u222e",
+  "Copf;": "\u2102",
+  "Coproduct;": "\u2210",
+  "CounterClockwiseContourIntegral;": "\u2233",
+  "Cross;": "\u2a2f",
+  "Cscr;": "\u{01d49e}",
+  "Cup;": "\u22d3",
+  "CupCap;": "\u224d",
+  "DD;": "\u2145",
+  "DDotrahd;": "\u2911",
+  "DJcy;": "\u0402",
+  "DScy;": "\u0405",
+  "DZcy;": "\u040f",
+  "Dagger;": "\u2021",
+  "Darr;": "\u21a1",
+  "Dashv;": "\u2ae4",
+  "Dcaron;": "\u010e",
+  "Dcy;": "\u0414",
+  "Del;": "\u2207",
+  "Delta;": "\u0394",
+  "Dfr;": "\u{01d507}",
+  "DiacriticalAcute;": "\xb4",
+  "DiacriticalDot;": "\u02d9",
+  "DiacriticalDoubleAcute;": "\u02dd",
+  "DiacriticalGrave;": "`",
+  "DiacriticalTilde;": "\u02dc",
+  "Diamond;": "\u22c4",
+  "DifferentialD;": "\u2146",
+  "Dopf;": "\u{01d53b}",
+  "Dot;": "\xa8",
+  "DotDot;": "\u20dc",
+  "DotEqual;": "\u2250",
+  "DoubleContourIntegral;": "\u222f",
+  "DoubleDot;": "\xa8",
+  "DoubleDownArrow;": "\u21d3",
+  "DoubleLeftArrow;": "\u21d0",
+  "DoubleLeftRightArrow;": "\u21d4",
+  "DoubleLeftTee;": "\u2ae4",
+  "DoubleLongLeftArrow;": "\u27f8",
+  "DoubleLongLeftRightArrow;": "\u27fa",
+  "DoubleLongRightArrow;": "\u27f9",
+  "DoubleRightArrow;": "\u21d2",
+  "DoubleRightTee;": "\u22a8",
+  "DoubleUpArrow;": "\u21d1",
+  "DoubleUpDownArrow;": "\u21d5",
+  "DoubleVerticalBar;": "\u2225",
+  "DownArrow;": "\u2193",
+  "DownArrowBar;": "\u2913",
+  "DownArrowUpArrow;": "\u21f5",
+  "DownBreve;": "\u0311",
+  "DownLeftRightVector;": "\u2950",
+  "DownLeftTeeVector;": "\u295e",
+  "DownLeftVector;": "\u21bd",
+  "DownLeftVectorBar;": "\u2956",
+  "DownRightTeeVector;": "\u295f",
+  "DownRightVector;": "\u21c1",
+  "DownRightVectorBar;": "\u2957",
+  "DownTee;": "\u22a4",
+  "DownTeeArrow;": "\u21a7",
+  "Downarrow;": "\u21d3",
+  "Dscr;": "\u{01d49f}",
+  "Dstrok;": "\u0110",
+  "ENG;": "\u014a",
+  "ETH": "\xd0",
+  "ETH;": "\xd0",
+  "Eacute": "\xc9",
+  "Eacute;": "\xc9",
+  "Ecaron;": "\u011a",
+  "Ecirc": "\xca",
+  "Ecirc;": "\xca",
+  "Ecy;": "\u042d",
+  "Edot;": "\u0116",
+  "Efr;": "\u{01d508}",
+  "Egrave": "\xc8",
+  "Egrave;": "\xc8",
+  "Element;": "\u2208",
+  "Emacr;": "\u0112",
+  "EmptySmallSquare;": "\u25fb",
+  "EmptyVerySmallSquare;": "\u25ab",
+  "Eogon;": "\u0118",
+  "Eopf;": "\u{01d53c}",
+  "Epsilon;": "\u0395",
+  "Equal;": "\u2a75",
+  "EqualTilde;": "\u2242",
+  "Equilibrium;": "\u21cc",
+  "Escr;": "\u2130",
+  "Esim;": "\u2a73",
+  "Eta;": "\u0397",
+  "Euml": "\xcb",
+  "Euml;": "\xcb",
+  "Exists;": "\u2203",
+  "ExponentialE;": "\u2147",
+  "Fcy;": "\u0424",
+  "Ffr;": "\u{01d509}",
+  "FilledSmallSquare;": "\u25fc",
+  "FilledVerySmallSquare;": "\u25aa",
+  "Fopf;": "\u{01d53d}",
+  "ForAll;": "\u2200",
+  "Fouriertrf;": "\u2131",
+  "Fscr;": "\u2131",
+  "GJcy;": "\u0403",
+  "GT": ">",
+  "GT;": ">",
+  "Gamma;": "\u0393",
+  "Gammad;": "\u03dc",
+  "Gbreve;": "\u011e",
+  "Gcedil;": "\u0122",
+  "Gcirc;": "\u011c",
+  "Gcy;": "\u0413",
+  "Gdot;": "\u0120",
+  "Gfr;": "\u{01d50a}",
+  "Gg;": "\u22d9",
+  "Gopf;": "\u{01d53e}",
+  "GreaterEqual;": "\u2265",
+  "GreaterEqualLess;": "\u22db",
+  "GreaterFullEqual;": "\u2267",
+  "GreaterGreater;": "\u2aa2",
+  "GreaterLess;": "\u2277",
+  "GreaterSlantEqual;": "\u2a7e",
+  "GreaterTilde;": "\u2273",
+  "Gscr;": "\u{01d4a2}",
+  "Gt;": "\u226b",
+  "HARDcy;": "\u042a",
+  "Hacek;": "\u02c7",
+  "Hat;": "^",
+  "Hcirc;": "\u0124",
+  "Hfr;": "\u210c",
+  "HilbertSpace;": "\u210b",
+  "Hopf;": "\u210d",
+  "HorizontalLine;": "\u2500",
+  "Hscr;": "\u210b",
+  "Hstrok;": "\u0126",
+  "HumpDownHump;": "\u224e",
+  "HumpEqual;": "\u224f",
+  "IEcy;": "\u0415",
+  "IJlig;": "\u0132",
+  "IOcy;": "\u0401",
+  "Iacute": "\xcd",
+  "Iacute;": "\xcd",
+  "Icirc": "\xce",
+  "Icirc;": "\xce",
+  "Icy;": "\u0418",
+  "Idot;": "\u0130",
+  "Ifr;": "\u2111",
+  "Igrave": "\xcc",
+  "Igrave;": "\xcc",
+  "Im;": "\u2111",
+  "Imacr;": "\u012a",
+  "ImaginaryI;": "\u2148",
+  "Implies;": "\u21d2",
+  "Int;": "\u222c",
+  "Integral;": "\u222b",
+  "Intersection;": "\u22c2",
+  "InvisibleComma;": "\u2063",
+  "InvisibleTimes;": "\u2062",
+  "Iogon;": "\u012e",
+  "Iopf;": "\u{01d540}",
+  "Iota;": "\u0399",
+  "Iscr;": "\u2110",
+  "Itilde;": "\u0128",
+  "Iukcy;": "\u0406",
+  "Iuml": "\xcf",
+  "Iuml;": "\xcf",
+  "Jcirc;": "\u0134",
+  "Jcy;": "\u0419",
+  "Jfr;": "\u{01d50d}",
+  "Jopf;": "\u{01d541}",
+  "Jscr;": "\u{01d4a5}",
+  "Jsercy;": "\u0408",
+  "Jukcy;": "\u0404",
+  "KHcy;": "\u0425",
+  "KJcy;": "\u040c",
+  "Kappa;": "\u039a",
+  "Kcedil;": "\u0136",
+  "Kcy;": "\u041a",
+  "Kfr;": "\u{01d50e}",
+  "Kopf;": "\u{01d542}",
+  "Kscr;": "\u{01d4a6}",
+  "LJcy;": "\u0409",
+  "LT": "<",
+  "LT;": "<",
+  "Lacute;": "\u0139",
+  "Lambda;": "\u039b",
+  "Lang;": "\u27ea",
+  "Laplacetrf;": "\u2112",
+  "Larr;": "\u219e",
+  "Lcaron;": "\u013d",
+  "Lcedil;": "\u013b",
+  "Lcy;": "\u041b",
+  "LeftAngleBracket;": "\u27e8",
+  "LeftArrow;": "\u2190",
+  "LeftArrowBar;": "\u21e4",
+  "LeftArrowRightArrow;": "\u21c6",
+  "LeftCeiling;": "\u2308",
+  "LeftDoubleBracket;": "\u27e6",
+  "LeftDownTeeVector;": "\u2961",
+  "LeftDownVector;": "\u21c3",
+  "LeftDownVectorBar;": "\u2959",
+  "LeftFloor;": "\u230a",
+  "LeftRightArrow;": "\u2194",
+  "LeftRightVector;": "\u294e",
+  "LeftTee;": "\u22a3",
+  "LeftTeeArrow;": "\u21a4",
+  "LeftTeeVector;": "\u295a",
+  "LeftTriangle;": "\u22b2",
+  "LeftTriangleBar;": "\u29cf",
+  "LeftTriangleEqual;": "\u22b4",
+  "LeftUpDownVector;": "\u2951",
+  "LeftUpTeeVector;": "\u2960",
+  "LeftUpVector;": "\u21bf",
+  "LeftUpVectorBar;": "\u2958",
+  "LeftVector;": "\u21bc",
+  "LeftVectorBar;": "\u2952",
+  "Leftarrow;": "\u21d0",
+  "Leftrightarrow;": "\u21d4",
+  "LessEqualGreater;": "\u22da",
+  "LessFullEqual;": "\u2266",
+  "LessGreater;": "\u2276",
+  "LessLess;": "\u2aa1",
+  "LessSlantEqual;": "\u2a7d",
+  "LessTilde;": "\u2272",
+  "Lfr;": "\u{01d50f}",
+  "Ll;": "\u22d8",
+  "Lleftarrow;": "\u21da",
+  "Lmidot;": "\u013f",
+  "LongLeftArrow;": "\u27f5",
+  "LongLeftRightArrow;": "\u27f7",
+  "LongRightArrow;": "\u27f6",
+  "Longleftarrow;": "\u27f8",
+  "Longleftrightarrow;": "\u27fa",
+  "Longrightarrow;": "\u27f9",
+  "Lopf;": "\u{01d543}",
+  "LowerLeftArrow;": "\u2199",
+  "LowerRightArrow;": "\u2198",
+  "Lscr;": "\u2112",
+  "Lsh;": "\u21b0",
+  "Lstrok;": "\u0141",
+  "Lt;": "\u226a",
+  "Map;": "\u2905",
+  "Mcy;": "\u041c",
+  "MediumSpace;": "\u205f",
+  "Mellintrf;": "\u2133",
+  "Mfr;": "\u{01d510}",
+  "MinusPlus;": "\u2213",
+  "Mopf;": "\u{01d544}",
+  "Mscr;": "\u2133",
+  "Mu;": "\u039c",
+  "NJcy;": "\u040a",
+  "Nacute;": "\u0143",
+  "Ncaron;": "\u0147",
+  "Ncedil;": "\u0145",
+  "Ncy;": "\u041d",
+  "NegativeMediumSpace;": "\u200b",
+  "NegativeThickSpace;": "\u200b",
+  "NegativeThinSpace;": "\u200b",
+  "NegativeVeryThinSpace;": "\u200b",
+  "NestedGreaterGreater;": "\u226b",
+  "NestedLessLess;": "\u226a",
+  "NewLine;": "\n",
+  "Nfr;": "\u{01d511}",
+  "NoBreak;": "\u2060",
+  "NonBreakingSpace;": "\xa0",
+  "Nopf;": "\u2115",
+  "Not;": "\u2aec",
+  "NotCongruent;": "\u2262",
+  "NotCupCap;": "\u226d",
+  "NotDoubleVerticalBar;": "\u2226",
+  "NotElement;": "\u2209",
+  "NotEqual;": "\u2260",
+  "NotEqualTilde;": "\u2242\u0338",
+  "NotExists;": "\u2204",
+  "NotGreater;": "\u226f",
+  "NotGreaterEqual;": "\u2271",
+  "NotGreaterFullEqual;": "\u2267\u0338",
+  "NotGreaterGreater;": "\u226b\u0338",
+  "NotGreaterLess;": "\u2279",
+  "NotGreaterSlantEqual;": "\u2a7e\u0338",
+  "NotGreaterTilde;": "\u2275",
+  "NotHumpDownHump;": "\u224e\u0338",
+  "NotHumpEqual;": "\u224f\u0338",
+  "NotLeftTriangle;": "\u22ea",
+  "NotLeftTriangleBar;": "\u29cf\u0338",
+  "NotLeftTriangleEqual;": "\u22ec",
+  "NotLess;": "\u226e",
+  "NotLessEqual;": "\u2270",
+  "NotLessGreater;": "\u2278",
+  "NotLessLess;": "\u226a\u0338",
+  "NotLessSlantEqual;": "\u2a7d\u0338",
+  "NotLessTilde;": "\u2274",
+  "NotNestedGreaterGreater;": "\u2aa2\u0338",
+  "NotNestedLessLess;": "\u2aa1\u0338",
+  "NotPrecedes;": "\u2280",
+  "NotPrecedesEqual;": "\u2aaf\u0338",
+  "NotPrecedesSlantEqual;": "\u22e0",
+  "NotReverseElement;": "\u220c",
+  "NotRightTriangle;": "\u22eb",
+  "NotRightTriangleBar;": "\u29d0\u0338",
+  "NotRightTriangleEqual;": "\u22ed",
+  "NotSquareSubset;": "\u228f\u0338",
+  "NotSquareSubsetEqual;": "\u22e2",
+  "NotSquareSuperset;": "\u2290\u0338",
+  "NotSquareSupersetEqual;": "\u22e3",
+  "NotSubset;": "\u2282\u20d2",
+  "NotSubsetEqual;": "\u2288",
+  "NotSucceeds;": "\u2281",
+  "NotSucceedsEqual;": "\u2ab0\u0338",
+  "NotSucceedsSlantEqual;": "\u22e1",
+  "NotSucceedsTilde;": "\u227f\u0338",
+  "NotSuperset;": "\u2283\u20d2",
+  "NotSupersetEqual;": "\u2289",
+  "NotTilde;": "\u2241",
+  "NotTildeEqual;": "\u2244",
+  "NotTildeFullEqual;": "\u2247",
+  "NotTildeTilde;": "\u2249",
+  "NotVerticalBar;": "\u2224",
+  "Nscr;": "\u{01d4a9}",
+  "Ntilde": "\xd1",
+  "Ntilde;": "\xd1",
+  "Nu;": "\u039d",
+  "OElig;": "\u0152",
+  "Oacute": "\xd3",
+  "Oacute;": "\xd3",
+  "Ocirc": "\xd4",
+  "Ocirc;": "\xd4",
+  "Ocy;": "\u041e",
+  "Odblac;": "\u0150",
+  "Ofr;": "\u{01d512}",
+  "Ograve": "\xd2",
+  "Ograve;": "\xd2",
+  "Omacr;": "\u014c",
+  "Omega;": "\u03a9",
+  "Omicron;": "\u039f",
+  "Oopf;": "\u{01d546}",
+  "OpenCurlyDoubleQuote;": "\u201c",
+  "OpenCurlyQuote;": "\u2018",
+  "Or;": "\u2a54",
+  "Oscr;": "\u{01d4aa}",
+  "Oslash": "\xd8",
+  "Oslash;": "\xd8",
+  "Otilde": "\xd5",
+  "Otilde;": "\xd5",
+  "Otimes;": "\u2a37",
+  "Ouml": "\xd6",
+  "Ouml;": "\xd6",
+  "OverBar;": "\u203e",
+  "OverBrace;": "\u23de",
+  "OverBracket;": "\u23b4",
+  "OverParenthesis;": "\u23dc",
+  "PartialD;": "\u2202",
+  "Pcy;": "\u041f",
+  "Pfr;": "\u{01d513}",
+  "Phi;": "\u03a6",
+  "Pi;": "\u03a0",
+  "PlusMinus;": "\xb1",
+  "Poincareplane;": "\u210c",
+  "Popf;": "\u2119",
+  "Pr;": "\u2abb",
+  "Precedes;": "\u227a",
+  "PrecedesEqual;": "\u2aaf",
+  "PrecedesSlantEqual;": "\u227c",
+  "PrecedesTilde;": "\u227e",
+  "Prime;": "\u2033",
+  "Product;": "\u220f",
+  "Proportion;": "\u2237",
+  "Proportional;": "\u221d",
+  "Pscr;": "\u{01d4ab}",
+  "Psi;": "\u03a8",
+  "QUOT": "\"",
+  "QUOT;": "\"",
+  "Qfr;": "\u{01d514}",
+  "Qopf;": "\u211a",
+  "Qscr;": "\u{01d4ac}",
+  "RBarr;": "\u2910",
+  "REG": "\xae",
+  "REG;": "\xae",
+  "Racute;": "\u0154",
+  "Rang;": "\u27eb",
+  "Rarr;": "\u21a0",
+  "Rarrtl;": "\u2916",
+  "Rcaron;": "\u0158",
+  "Rcedil;": "\u0156",
+  "Rcy;": "\u0420",
+  "Re;": "\u211c",
+  "ReverseElement;": "\u220b",
+  "ReverseEquilibrium;": "\u21cb",
+  "ReverseUpEquilibrium;": "\u296f",
+  "Rfr;": "\u211c",
+  "Rho;": "\u03a1",
+  "RightAngleBracket;": "\u27e9",
+  "RightArrow;": "\u2192",
+  "RightArrowBar;": "\u21e5",
+  "RightArrowLeftArrow;": "\u21c4",
+  "RightCeiling;": "\u2309",
+  "RightDoubleBracket;": "\u27e7",
+  "RightDownTeeVector;": "\u295d",
+  "RightDownVector;": "\u21c2",
+  "RightDownVectorBar;": "\u2955",
+  "RightFloor;": "\u230b",
+  "RightTee;": "\u22a2",
+  "RightTeeArrow;": "\u21a6",
+  "RightTeeVector;": "\u295b",
+  "RightTriangle;": "\u22b3",
+  "RightTriangleBar;": "\u29d0",
+  "RightTriangleEqual;": "\u22b5",
+  "RightUpDownVector;": "\u294f",
+  "RightUpTeeVector;": "\u295c",
+  "RightUpVector;": "\u21be",
+  "RightUpVectorBar;": "\u2954",
+  "RightVector;": "\u21c0",
+  "RightVectorBar;": "\u2953",
+  "Rightarrow;": "\u21d2",
+  "Ropf;": "\u211d",
+  "RoundImplies;": "\u2970",
+  "Rrightarrow;": "\u21db",
+  "Rscr;": "\u211b",
+  "Rsh;": "\u21b1",
+  "RuleDelayed;": "\u29f4",
+  "SHCHcy;": "\u0429",
+  "SHcy;": "\u0428",
+  "SOFTcy;": "\u042c",
+  "Sacute;": "\u015a",
+  "Sc;": "\u2abc",
+  "Scaron;": "\u0160",
+  "Scedil;": "\u015e",
+  "Scirc;": "\u015c",
+  "Scy;": "\u0421",
+  "Sfr;": "\u{01d516}",
+  "ShortDownArrow;": "\u2193",
+  "ShortLeftArrow;": "\u2190",
+  "ShortRightArrow;": "\u2192",
+  "ShortUpArrow;": "\u2191",
+  "Sigma;": "\u03a3",
+  "SmallCircle;": "\u2218",
+  "Sopf;": "\u{01d54a}",
+  "Sqrt;": "\u221a",
+  "Square;": "\u25a1",
+  "SquareIntersection;": "\u2293",
+  "SquareSubset;": "\u228f",
+  "SquareSubsetEqual;": "\u2291",
+  "SquareSuperset;": "\u2290",
+  "SquareSupersetEqual;": "\u2292",
+  "SquareUnion;": "\u2294",
+  "Sscr;": "\u{01d4ae}",
+  "Star;": "\u22c6",
+  "Sub;": "\u22d0",
+  "Subset;": "\u22d0",
+  "SubsetEqual;": "\u2286",
+  "Succeeds;": "\u227b",
+  "SucceedsEqual;": "\u2ab0",
+  "SucceedsSlantEqual;": "\u227d",
+  "SucceedsTilde;": "\u227f",
+  "SuchThat;": "\u220b",
+  "Sum;": "\u2211",
+  "Sup;": "\u22d1",
+  "Superset;": "\u2283",
+  "SupersetEqual;": "\u2287",
+  "Supset;": "\u22d1",
+  "THORN": "\xde",
+  "THORN;": "\xde",
+  "TRADE;": "\u2122",
+  "TSHcy;": "\u040b",
+  "TScy;": "\u0426",
+  "Tab;": "\t",
+  "Tau;": "\u03a4",
+  "Tcaron;": "\u0164",
+  "Tcedil;": "\u0162",
+  "Tcy;": "\u0422",
+  "Tfr;": "\u{01d517}",
+  "Therefore;": "\u2234",
+  "Theta;": "\u0398",
+  "ThickSpace;": "\u205f\u200a",
+  "ThinSpace;": "\u2009",
+  "Tilde;": "\u223c",
+  "TildeEqual;": "\u2243",
+  "TildeFullEqual;": "\u2245",
+  "TildeTilde;": "\u2248",
+  "Topf;": "\u{01d54b}",
+  "TripleDot;": "\u20db",
+  "Tscr;": "\u{01d4af}",
+  "Tstrok;": "\u0166",
+  "Uacute": "\xda",
+  "Uacute;": "\xda",
+  "Uarr;": "\u219f",
+  "Uarrocir;": "\u2949",
+  "Ubrcy;": "\u040e",
+  "Ubreve;": "\u016c",
+  "Ucirc": "\xdb",
+  "Ucirc;": "\xdb",
+  "Ucy;": "\u0423",
+  "Udblac;": "\u0170",
+  "Ufr;": "\u{01d518}",
+  "Ugrave": "\xd9",
+  "Ugrave;": "\xd9",
+  "Umacr;": "\u016a",
+  "UnderBar;": "_",
+  "UnderBrace;": "\u23df",
+  "UnderBracket;": "\u23b5",
+  "UnderParenthesis;": "\u23dd",
+  "Union;": "\u22c3",
+  "UnionPlus;": "\u228e",
+  "Uogon;": "\u0172",
+  "Uopf;": "\u{01d54c}",
+  "UpArrow;": "\u2191",
+  "UpArrowBar;": "\u2912",
+  "UpArrowDownArrow;": "\u21c5",
+  "UpDownArrow;": "\u2195",
+  "UpEquilibrium;": "\u296e",
+  "UpTee;": "\u22a5",
+  "UpTeeArrow;": "\u21a5",
+  "Uparrow;": "\u21d1",
+  "Updownarrow;": "\u21d5",
+  "UpperLeftArrow;": "\u2196",
+  "UpperRightArrow;": "\u2197",
+  "Upsi;": "\u03d2",
+  "Upsilon;": "\u03a5",
+  "Uring;": "\u016e",
+  "Uscr;": "\u{01d4b0}",
+  "Utilde;": "\u0168",
+  "Uuml": "\xdc",
+  "Uuml;": "\xdc",
+  "VDash;": "\u22ab",
+  "Vbar;": "\u2aeb",
+  "Vcy;": "\u0412",
+  "Vdash;": "\u22a9",
+  "Vdashl;": "\u2ae6",
+  "Vee;": "\u22c1",
+  "Verbar;": "\u2016",
+  "Vert;": "\u2016",
+  "VerticalBar;": "\u2223",
+  "VerticalLine;": "|",
+  "VerticalSeparator;": "\u2758",
+  "VerticalTilde;": "\u2240",
+  "VeryThinSpace;": "\u200a",
+  "Vfr;": "\u{01d519}",
+  "Vopf;": "\u{01d54d}",
+  "Vscr;": "\u{01d4b1}",
+  "Vvdash;": "\u22aa",
+  "Wcirc;": "\u0174",
+  "Wedge;": "\u22c0",
+  "Wfr;": "\u{01d51a}",
+  "Wopf;": "\u{01d54e}",
+  "Wscr;": "\u{01d4b2}",
+  "Xfr;": "\u{01d51b}",
+  "Xi;": "\u039e",
+  "Xopf;": "\u{01d54f}",
+  "Xscr;": "\u{01d4b3}",
+  "YAcy;": "\u042f",
+  "YIcy;": "\u0407",
+  "YUcy;": "\u042e",
+  "Yacute": "\xdd",
+  "Yacute;": "\xdd",
+  "Ycirc;": "\u0176",
+  "Ycy;": "\u042b",
+  "Yfr;": "\u{01d51c}",
+  "Yopf;": "\u{01d550}",
+  "Yscr;": "\u{01d4b4}",
+  "Yuml;": "\u0178",
+  "ZHcy;": "\u0416",
+  "Zacute;": "\u0179",
+  "Zcaron;": "\u017d",
+  "Zcy;": "\u0417",
+  "Zdot;": "\u017b",
+  "ZeroWidthSpace;": "\u200b",
+  "Zeta;": "\u0396",
+  "Zfr;": "\u2128",
+  "Zopf;": "\u2124",
+  "Zscr;": "\u{01d4b5}",
+  "aacute": "\xe1",
+  "aacute;": "\xe1",
+  "abreve;": "\u0103",
+  "ac;": "\u223e",
+  "acE;": "\u223e\u0333",
+  "acd;": "\u223f",
+  "acirc": "\xe2",
+  "acirc;": "\xe2",
+  "acute": "\xb4",
+  "acute;": "\xb4",
+  "acy;": "\u0430",
+  "aelig": "\xe6",
+  "aelig;": "\xe6",
+  "af;": "\u2061",
+  "afr;": "\u{01d51e}",
+  "agrave": "\xe0",
+  "agrave;": "\xe0",
+  "alefsym;": "\u2135",
+  "aleph;": "\u2135",
+  "alpha;": "\u03b1",
+  "amacr;": "\u0101",
+  "amalg;": "\u2a3f",
+  "amp": "&",
+  "amp;": "&",
+  "and;": "\u2227",
+  "andand;": "\u2a55",
+  "andd;": "\u2a5c",
+  "andslope;": "\u2a58",
+  "andv;": "\u2a5a",
+  "ang;": "\u2220",
+  "ange;": "\u29a4",
+  "angle;": "\u2220",
+  "angmsd;": "\u2221",
+  "angmsdaa;": "\u29a8",
+  "angmsdab;": "\u29a9",
+  "angmsdac;": "\u29aa",
+  "angmsdad;": "\u29ab",
+  "angmsdae;": "\u29ac",
+  "angmsdaf;": "\u29ad",
+  "angmsdag;": "\u29ae",
+  "angmsdah;": "\u29af",
+  "angrt;": "\u221f",
+  "angrtvb;": "\u22be",
+  "angrtvbd;": "\u299d",
+  "angsph;": "\u2222",
+  "angst;": "\xc5",
+  "angzarr;": "\u237c",
+  "aogon;": "\u0105",
+  "aopf;": "\u{01d552}",
+  "ap;": "\u2248",
+  "apE;": "\u2a70",
+  "apacir;": "\u2a6f",
+  "ape;": "\u224a",
+  "apid;": "\u224b",
+  "apos;": "'",
+  "approx;": "\u2248",
+  "approxeq;": "\u224a",
+  "aring": "\xe5",
+  "aring;": "\xe5",
+  "ascr;": "\u{01d4b6}",
+  "ast;": "*",
+  "asymp;": "\u2248",
+  "asympeq;": "\u224d",
+  "atilde": "\xe3",
+  "atilde;": "\xe3",
+  "auml": "\xe4",
+  "auml;": "\xe4",
+  "awconint;": "\u2233",
+  "awint;": "\u2a11",
+  "bNot;": "\u2aed",
+  "backcong;": "\u224c",
+  "backepsilon;": "\u03f6",
+  "backprime;": "\u2035",
+  "backsim;": "\u223d",
+  "backsimeq;": "\u22cd",
+  "barvee;": "\u22bd",
+  "barwed;": "\u2305",
+  "barwedge;": "\u2305",
+  "bbrk;": "\u23b5",
+  "bbrktbrk;": "\u23b6",
+  "bcong;": "\u224c",
+  "bcy;": "\u0431",
+  "bdquo;": "\u201e",
+  "becaus;": "\u2235",
+  "because;": "\u2235",
+  "bemptyv;": "\u29b0",
+  "bepsi;": "\u03f6",
+  "bernou;": "\u212c",
+  "beta;": "\u03b2",
+  "beth;": "\u2136",
+  "between;": "\u226c",
+  "bfr;": "\u{01d51f}",
+  "bigcap;": "\u22c2",
+  "bigcirc;": "\u25ef",
+  "bigcup;": "\u22c3",
+  "bigodot;": "\u2a00",
+  "bigoplus;": "\u2a01",
+  "bigotimes;": "\u2a02",
+  "bigsqcup;": "\u2a06",
+  "bigstar;": "\u2605",
+  "bigtriangledown;": "\u25bd",
+  "bigtriangleup;": "\u25b3",
+  "biguplus;": "\u2a04",
+  "bigvee;": "\u22c1",
+  "bigwedge;": "\u22c0",
+  "bkarow;": "\u290d",
+  "blacklozenge;": "\u29eb",
+  "blacksquare;": "\u25aa",
+  "blacktriangle;": "\u25b4",
+  "blacktriangledown;": "\u25be",
+  "blacktriangleleft;": "\u25c2",
+  "blacktriangleright;": "\u25b8",
+  "blank;": "\u2423",
+  "blk12;": "\u2592",
+  "blk14;": "\u2591",
+  "blk34;": "\u2593",
+  "block;": "\u2588",
+  "bne;": "=\u20e5",
+  "bnequiv;": "\u2261\u20e5",
+  "bnot;": "\u2310",
+  "bopf;": "\u{01d553}",
+  "bot;": "\u22a5",
+  "bottom;": "\u22a5",
+  "bowtie;": "\u22c8",
+  "boxDL;": "\u2557",
+  "boxDR;": "\u2554",
+  "boxDl;": "\u2556",
+  "boxDr;": "\u2553",
+  "boxH;": "\u2550",
+  "boxHD;": "\u2566",
+  "boxHU;": "\u2569",
+  "boxHd;": "\u2564",
+  "boxHu;": "\u2567",
+  "boxUL;": "\u255d",
+  "boxUR;": "\u255a",
+  "boxUl;": "\u255c",
+  "boxUr;": "\u2559",
+  "boxV;": "\u2551",
+  "boxVH;": "\u256c",
+  "boxVL;": "\u2563",
+  "boxVR;": "\u2560",
+  "boxVh;": "\u256b",
+  "boxVl;": "\u2562",
+  "boxVr;": "\u255f",
+  "boxbox;": "\u29c9",
+  "boxdL;": "\u2555",
+  "boxdR;": "\u2552",
+  "boxdl;": "\u2510",
+  "boxdr;": "\u250c",
+  "boxh;": "\u2500",
+  "boxhD;": "\u2565",
+  "boxhU;": "\u2568",
+  "boxhd;": "\u252c",
+  "boxhu;": "\u2534",
+  "boxminus;": "\u229f",
+  "boxplus;": "\u229e",
+  "boxtimes;": "\u22a0",
+  "boxuL;": "\u255b",
+  "boxuR;": "\u2558",
+  "boxul;": "\u2518",
+  "boxur;": "\u2514",
+  "boxv;": "\u2502",
+  "boxvH;": "\u256a",
+  "boxvL;": "\u2561",
+  "boxvR;": "\u255e",
+  "boxvh;": "\u253c",
+  "boxvl;": "\u2524",
+  "boxvr;": "\u251c",
+  "bprime;": "\u2035",
+  "breve;": "\u02d8",
+  "brvbar": "\xa6",
+  "brvbar;": "\xa6",
+  "bscr;": "\u{01d4b7}",
+  "bsemi;": "\u204f",
+  "bsim;": "\u223d",
+  "bsime;": "\u22cd",
+  "bsol;": "\\",
+  "bsolb;": "\u29c5",
+  "bsolhsub;": "\u27c8",
+  "bull;": "\u2022",
+  "bullet;": "\u2022",
+  "bump;": "\u224e",
+  "bumpE;": "\u2aae",
+  "bumpe;": "\u224f",
+  "bumpeq;": "\u224f",
+  "cacute;": "\u0107",
+  "cap;": "\u2229",
+  "capand;": "\u2a44",
+  "capbrcup;": "\u2a49",
+  "capcap;": "\u2a4b",
+  "capcup;": "\u2a47",
+  "capdot;": "\u2a40",
+  "caps;": "\u2229\ufe00",
+  "caret;": "\u2041",
+  "caron;": "\u02c7",
+  "ccaps;": "\u2a4d",
+  "ccaron;": "\u010d",
+  "ccedil": "\xe7",
+  "ccedil;": "\xe7",
+  "ccirc;": "\u0109",
+  "ccups;": "\u2a4c",
+  "ccupssm;": "\u2a50",
+  "cdot;": "\u010b",
+  "cedil": "\xb8",
+  "cedil;": "\xb8",
+  "cemptyv;": "\u29b2",
+  "cent": "\xa2",
+  "cent;": "\xa2",
+  "centerdot;": "\xb7",
+  "cfr;": "\u{01d520}",
+  "chcy;": "\u0447",
+  "check;": "\u2713",
+  "checkmark;": "\u2713",
+  "chi;": "\u03c7",
+  "cir;": "\u25cb",
+  "cirE;": "\u29c3",
+  "circ;": "\u02c6",
+  "circeq;": "\u2257",
+  "circlearrowleft;": "\u21ba",
+  "circlearrowright;": "\u21bb",
+  "circledR;": "\xae",
+  "circledS;": "\u24c8",
+  "circledast;": "\u229b",
+  "circledcirc;": "\u229a",
+  "circleddash;": "\u229d",
+  "cire;": "\u2257",
+  "cirfnint;": "\u2a10",
+  "cirmid;": "\u2aef",
+  "cirscir;": "\u29c2",
+  "clubs;": "\u2663",
+  "clubsuit;": "\u2663",
+  "colon;": ":",
+  "colone;": "\u2254",
+  "coloneq;": "\u2254",
+  "comma;": ",",
+  "commat;": "@",
+  "comp;": "\u2201",
+  "compfn;": "\u2218",
+  "complement;": "\u2201",
+  "complexes;": "\u2102",
+  "cong;": "\u2245",
+  "congdot;": "\u2a6d",
+  "conint;": "\u222e",
+  "copf;": "\u{01d554}",
+  "coprod;": "\u2210",
+  "copy": "\xa9",
+  "copy;": "\xa9",
+  "copysr;": "\u2117",
+  "crarr;": "\u21b5",
+  "cross;": "\u2717",
+  "cscr;": "\u{01d4b8}",
+  "csub;": "\u2acf",
+  "csube;": "\u2ad1",
+  "csup;": "\u2ad0",
+  "csupe;": "\u2ad2",
+  "ctdot;": "\u22ef",
+  "cudarrl;": "\u2938",
+  "cudarrr;": "\u2935",
+  "cuepr;": "\u22de",
+  "cuesc;": "\u22df",
+  "cularr;": "\u21b6",
+  "cularrp;": "\u293d",
+  "cup;": "\u222a",
+  "cupbrcap;": "\u2a48",
+  "cupcap;": "\u2a46",
+  "cupcup;": "\u2a4a",
+  "cupdot;": "\u228d",
+  "cupor;": "\u2a45",
+  "cups;": "\u222a\ufe00",
+  "curarr;": "\u21b7",
+  "curarrm;": "\u293c",
+  "curlyeqprec;": "\u22de",
+  "curlyeqsucc;": "\u22df",
+  "curlyvee;": "\u22ce",
+  "curlywedge;": "\u22cf",
+  "curren": "\xa4",
+  "curren;": "\xa4",
+  "curvearrowleft;": "\u21b6",
+  "curvearrowright;": "\u21b7",
+  "cuvee;": "\u22ce",
+  "cuwed;": "\u22cf",
+  "cwconint;": "\u2232",
+  "cwint;": "\u2231",
+  "cylcty;": "\u232d",
+  "dArr;": "\u21d3",
+  "dHar;": "\u2965",
+  "dagger;": "\u2020",
+  "daleth;": "\u2138",
+  "darr;": "\u2193",
+  "dash;": "\u2010",
+  "dashv;": "\u22a3",
+  "dbkarow;": "\u290f",
+  "dblac;": "\u02dd",
+  "dcaron;": "\u010f",
+  "dcy;": "\u0434",
+  "dd;": "\u2146",
+  "ddagger;": "\u2021",
+  "ddarr;": "\u21ca",
+  "ddotseq;": "\u2a77",
+  "deg": "\xb0",
+  "deg;": "\xb0",
+  "delta;": "\u03b4",
+  "demptyv;": "\u29b1",
+  "dfisht;": "\u297f",
+  "dfr;": "\u{01d521}",
+  "dharl;": "\u21c3",
+  "dharr;": "\u21c2",
+  "diam;": "\u22c4",
+  "diamond;": "\u22c4",
+  "diamondsuit;": "\u2666",
+  "diams;": "\u2666",
+  "die;": "\xa8",
+  "digamma;": "\u03dd",
+  "disin;": "\u22f2",
+  "div;": "\xf7",
+  "divide": "\xf7",
+  "divide;": "\xf7",
+  "divideontimes;": "\u22c7",
+  "divonx;": "\u22c7",
+  "djcy;": "\u0452",
+  "dlcorn;": "\u231e",
+  "dlcrop;": "\u230d",
+  "dollar;": "\$",
+  "dopf;": "\u{01d555}",
+  "dot;": "\u02d9",
+  "doteq;": "\u2250",
+  "doteqdot;": "\u2251",
+  "dotminus;": "\u2238",
+  "dotplus;": "\u2214",
+  "dotsquare;": "\u22a1",
+  "doublebarwedge;": "\u2306",
+  "downarrow;": "\u2193",
+  "downdownarrows;": "\u21ca",
+  "downharpoonleft;": "\u21c3",
+  "downharpoonright;": "\u21c2",
+  "drbkarow;": "\u2910",
+  "drcorn;": "\u231f",
+  "drcrop;": "\u230c",
+  "dscr;": "\u{01d4b9}",
+  "dscy;": "\u0455",
+  "dsol;": "\u29f6",
+  "dstrok;": "\u0111",
+  "dtdot;": "\u22f1",
+  "dtri;": "\u25bf",
+  "dtrif;": "\u25be",
+  "duarr;": "\u21f5",
+  "duhar;": "\u296f",
+  "dwangle;": "\u29a6",
+  "dzcy;": "\u045f",
+  "dzigrarr;": "\u27ff",
+  "eDDot;": "\u2a77",
+  "eDot;": "\u2251",
+  "eacute": "\xe9",
+  "eacute;": "\xe9",
+  "easter;": "\u2a6e",
+  "ecaron;": "\u011b",
+  "ecir;": "\u2256",
+  "ecirc": "\xea",
+  "ecirc;": "\xea",
+  "ecolon;": "\u2255",
+  "ecy;": "\u044d",
+  "edot;": "\u0117",
+  "ee;": "\u2147",
+  "efDot;": "\u2252",
+  "efr;": "\u{01d522}",
+  "eg;": "\u2a9a",
+  "egrave": "\xe8",
+  "egrave;": "\xe8",
+  "egs;": "\u2a96",
+  "egsdot;": "\u2a98",
+  "el;": "\u2a99",
+  "elinters;": "\u23e7",
+  "ell;": "\u2113",
+  "els;": "\u2a95",
+  "elsdot;": "\u2a97",
+  "emacr;": "\u0113",
+  "empty;": "\u2205",
+  "emptyset;": "\u2205",
+  "emptyv;": "\u2205",
+  "emsp13;": "\u2004",
+  "emsp14;": "\u2005",
+  "emsp;": "\u2003",
+  "eng;": "\u014b",
+  "ensp;": "\u2002",
+  "eogon;": "\u0119",
+  "eopf;": "\u{01d556}",
+  "epar;": "\u22d5",
+  "eparsl;": "\u29e3",
+  "eplus;": "\u2a71",
+  "epsi;": "\u03b5",
+  "epsilon;": "\u03b5",
+  "epsiv;": "\u03f5",
+  "eqcirc;": "\u2256",
+  "eqcolon;": "\u2255",
+  "eqsim;": "\u2242",
+  "eqslantgtr;": "\u2a96",
+  "eqslantless;": "\u2a95",
+  "equals;": "=",
+  "equest;": "\u225f",
+  "equiv;": "\u2261",
+  "equivDD;": "\u2a78",
+  "eqvparsl;": "\u29e5",
+  "erDot;": "\u2253",
+  "erarr;": "\u2971",
+  "escr;": "\u212f",
+  "esdot;": "\u2250",
+  "esim;": "\u2242",
+  "eta;": "\u03b7",
+  "eth": "\xf0",
+  "eth;": "\xf0",
+  "euml": "\xeb",
+  "euml;": "\xeb",
+  "euro;": "\u20ac",
+  "excl;": "!",
+  "exist;": "\u2203",
+  "expectation;": "\u2130",
+  "exponentiale;": "\u2147",
+  "fallingdotseq;": "\u2252",
+  "fcy;": "\u0444",
+  "female;": "\u2640",
+  "ffilig;": "\ufb03",
+  "fflig;": "\ufb00",
+  "ffllig;": "\ufb04",
+  "ffr;": "\u{01d523}",
+  "filig;": "\ufb01",
+  "fjlig;": "fj",
+  "flat;": "\u266d",
+  "fllig;": "\ufb02",
+  "fltns;": "\u25b1",
+  "fnof;": "\u0192",
+  "fopf;": "\u{01d557}",
+  "forall;": "\u2200",
+  "fork;": "\u22d4",
+  "forkv;": "\u2ad9",
+  "fpartint;": "\u2a0d",
+  "frac12": "\xbd",
+  "frac12;": "\xbd",
+  "frac13;": "\u2153",
+  "frac14": "\xbc",
+  "frac14;": "\xbc",
+  "frac15;": "\u2155",
+  "frac16;": "\u2159",
+  "frac18;": "\u215b",
+  "frac23;": "\u2154",
+  "frac25;": "\u2156",
+  "frac34": "\xbe",
+  "frac34;": "\xbe",
+  "frac35;": "\u2157",
+  "frac38;": "\u215c",
+  "frac45;": "\u2158",
+  "frac56;": "\u215a",
+  "frac58;": "\u215d",
+  "frac78;": "\u215e",
+  "frasl;": "\u2044",
+  "frown;": "\u2322",
+  "fscr;": "\u{01d4bb}",
+  "gE;": "\u2267",
+  "gEl;": "\u2a8c",
+  "gacute;": "\u01f5",
+  "gamma;": "\u03b3",
+  "gammad;": "\u03dd",
+  "gap;": "\u2a86",
+  "gbreve;": "\u011f",
+  "gcirc;": "\u011d",
+  "gcy;": "\u0433",
+  "gdot;": "\u0121",
+  "ge;": "\u2265",
+  "gel;": "\u22db",
+  "geq;": "\u2265",
+  "geqq;": "\u2267",
+  "geqslant;": "\u2a7e",
+  "ges;": "\u2a7e",
+  "gescc;": "\u2aa9",
+  "gesdot;": "\u2a80",
+  "gesdoto;": "\u2a82",
+  "gesdotol;": "\u2a84",
+  "gesl;": "\u22db\ufe00",
+  "gesles;": "\u2a94",
+  "gfr;": "\u{01d524}",
+  "gg;": "\u226b",
+  "ggg;": "\u22d9",
+  "gimel;": "\u2137",
+  "gjcy;": "\u0453",
+  "gl;": "\u2277",
+  "glE;": "\u2a92",
+  "gla;": "\u2aa5",
+  "glj;": "\u2aa4",
+  "gnE;": "\u2269",
+  "gnap;": "\u2a8a",
+  "gnapprox;": "\u2a8a",
+  "gne;": "\u2a88",
+  "gneq;": "\u2a88",
+  "gneqq;": "\u2269",
+  "gnsim;": "\u22e7",
+  "gopf;": "\u{01d558}",
+  "grave;": "`",
+  "gscr;": "\u210a",
+  "gsim;": "\u2273",
+  "gsime;": "\u2a8e",
+  "gsiml;": "\u2a90",
+  "gt": ">",
+  "gt;": ">",
+  "gtcc;": "\u2aa7",
+  "gtcir;": "\u2a7a",
+  "gtdot;": "\u22d7",
+  "gtlPar;": "\u2995",
+  "gtquest;": "\u2a7c",
+  "gtrapprox;": "\u2a86",
+  "gtrarr;": "\u2978",
+  "gtrdot;": "\u22d7",
+  "gtreqless;": "\u22db",
+  "gtreqqless;": "\u2a8c",
+  "gtrless;": "\u2277",
+  "gtrsim;": "\u2273",
+  "gvertneqq;": "\u2269\ufe00",
+  "gvnE;": "\u2269\ufe00",
+  "hArr;": "\u21d4",
+  "hairsp;": "\u200a",
+  "half;": "\xbd",
+  "hamilt;": "\u210b",
+  "hardcy;": "\u044a",
+  "harr;": "\u2194",
+  "harrcir;": "\u2948",
+  "harrw;": "\u21ad",
+  "hbar;": "\u210f",
+  "hcirc;": "\u0125",
+  "hearts;": "\u2665",
+  "heartsuit;": "\u2665",
+  "hellip;": "\u2026",
+  "hercon;": "\u22b9",
+  "hfr;": "\u{01d525}",
+  "hksearow;": "\u2925",
+  "hkswarow;": "\u2926",
+  "hoarr;": "\u21ff",
+  "homtht;": "\u223b",
+  "hookleftarrow;": "\u21a9",
+  "hookrightarrow;": "\u21aa",
+  "hopf;": "\u{01d559}",
+  "horbar;": "\u2015",
+  "hscr;": "\u{01d4bd}",
+  "hslash;": "\u210f",
+  "hstrok;": "\u0127",
+  "hybull;": "\u2043",
+  "hyphen;": "\u2010",
+  "iacute": "\xed",
+  "iacute;": "\xed",
+  "ic;": "\u2063",
+  "icirc": "\xee",
+  "icirc;": "\xee",
+  "icy;": "\u0438",
+  "iecy;": "\u0435",
+  "iexcl": "\xa1",
+  "iexcl;": "\xa1",
+  "iff;": "\u21d4",
+  "ifr;": "\u{01d526}",
+  "igrave": "\xec",
+  "igrave;": "\xec",
+  "ii;": "\u2148",
+  "iiiint;": "\u2a0c",
+  "iiint;": "\u222d",
+  "iinfin;": "\u29dc",
+  "iiota;": "\u2129",
+  "ijlig;": "\u0133",
+  "imacr;": "\u012b",
+  "image;": "\u2111",
+  "imagline;": "\u2110",
+  "imagpart;": "\u2111",
+  "imath;": "\u0131",
+  "imof;": "\u22b7",
+  "imped;": "\u01b5",
+  "in;": "\u2208",
+  "incare;": "\u2105",
+  "infin;": "\u221e",
+  "infintie;": "\u29dd",
+  "inodot;": "\u0131",
+  "int;": "\u222b",
+  "intcal;": "\u22ba",
+  "integers;": "\u2124",
+  "intercal;": "\u22ba",
+  "intlarhk;": "\u2a17",
+  "intprod;": "\u2a3c",
+  "iocy;": "\u0451",
+  "iogon;": "\u012f",
+  "iopf;": "\u{01d55a}",
+  "iota;": "\u03b9",
+  "iprod;": "\u2a3c",
+  "iquest": "\xbf",
+  "iquest;": "\xbf",
+  "iscr;": "\u{01d4be}",
+  "isin;": "\u2208",
+  "isinE;": "\u22f9",
+  "isindot;": "\u22f5",
+  "isins;": "\u22f4",
+  "isinsv;": "\u22f3",
+  "isinv;": "\u2208",
+  "it;": "\u2062",
+  "itilde;": "\u0129",
+  "iukcy;": "\u0456",
+  "iuml": "\xef",
+  "iuml;": "\xef",
+  "jcirc;": "\u0135",
+  "jcy;": "\u0439",
+  "jfr;": "\u{01d527}",
+  "jmath;": "\u0237",
+  "jopf;": "\u{01d55b}",
+  "jscr;": "\u{01d4bf}",
+  "jsercy;": "\u0458",
+  "jukcy;": "\u0454",
+  "kappa;": "\u03ba",
+  "kappav;": "\u03f0",
+  "kcedil;": "\u0137",
+  "kcy;": "\u043a",
+  "kfr;": "\u{01d528}",
+  "kgreen;": "\u0138",
+  "khcy;": "\u0445",
+  "kjcy;": "\u045c",
+  "kopf;": "\u{01d55c}",
+  "kscr;": "\u{01d4c0}",
+  "lAarr;": "\u21da",
+  "lArr;": "\u21d0",
+  "lAtail;": "\u291b",
+  "lBarr;": "\u290e",
+  "lE;": "\u2266",
+  "lEg;": "\u2a8b",
+  "lHar;": "\u2962",
+  "lacute;": "\u013a",
+  "laemptyv;": "\u29b4",
+  "lagran;": "\u2112",
+  "lambda;": "\u03bb",
+  "lang;": "\u27e8",
+  "langd;": "\u2991",
+  "langle;": "\u27e8",
+  "lap;": "\u2a85",
+  "laquo": "\xab",
+  "laquo;": "\xab",
+  "larr;": "\u2190",
+  "larrb;": "\u21e4",
+  "larrbfs;": "\u291f",
+  "larrfs;": "\u291d",
+  "larrhk;": "\u21a9",
+  "larrlp;": "\u21ab",
+  "larrpl;": "\u2939",
+  "larrsim;": "\u2973",
+  "larrtl;": "\u21a2",
+  "lat;": "\u2aab",
+  "latail;": "\u2919",
+  "late;": "\u2aad",
+  "lates;": "\u2aad\ufe00",
+  "lbarr;": "\u290c",
+  "lbbrk;": "\u2772",
+  "lbrace;": "{",
+  "lbrack;": "[",
+  "lbrke;": "\u298b",
+  "lbrksld;": "\u298f",
+  "lbrkslu;": "\u298d",
+  "lcaron;": "\u013e",
+  "lcedil;": "\u013c",
+  "lceil;": "\u2308",
+  "lcub;": "{",
+  "lcy;": "\u043b",
+  "ldca;": "\u2936",
+  "ldquo;": "\u201c",
+  "ldquor;": "\u201e",
+  "ldrdhar;": "\u2967",
+  "ldrushar;": "\u294b",
+  "ldsh;": "\u21b2",
+  "le;": "\u2264",
+  "leftarrow;": "\u2190",
+  "leftarrowtail;": "\u21a2",
+  "leftharpoondown;": "\u21bd",
+  "leftharpoonup;": "\u21bc",
+  "leftleftarrows;": "\u21c7",
+  "leftrightarrow;": "\u2194",
+  "leftrightarrows;": "\u21c6",
+  "leftrightharpoons;": "\u21cb",
+  "leftrightsquigarrow;": "\u21ad",
+  "leftthreetimes;": "\u22cb",
+  "leg;": "\u22da",
+  "leq;": "\u2264",
+  "leqq;": "\u2266",
+  "leqslant;": "\u2a7d",
+  "les;": "\u2a7d",
+  "lescc;": "\u2aa8",
+  "lesdot;": "\u2a7f",
+  "lesdoto;": "\u2a81",
+  "lesdotor;": "\u2a83",
+  "lesg;": "\u22da\ufe00",
+  "lesges;": "\u2a93",
+  "lessapprox;": "\u2a85",
+  "lessdot;": "\u22d6",
+  "lesseqgtr;": "\u22da",
+  "lesseqqgtr;": "\u2a8b",
+  "lessgtr;": "\u2276",
+  "lesssim;": "\u2272",
+  "lfisht;": "\u297c",
+  "lfloor;": "\u230a",
+  "lfr;": "\u{01d529}",
+  "lg;": "\u2276",
+  "lgE;": "\u2a91",
+  "lhard;": "\u21bd",
+  "lharu;": "\u21bc",
+  "lharul;": "\u296a",
+  "lhblk;": "\u2584",
+  "ljcy;": "\u0459",
+  "ll;": "\u226a",
+  "llarr;": "\u21c7",
+  "llcorner;": "\u231e",
+  "llhard;": "\u296b",
+  "lltri;": "\u25fa",
+  "lmidot;": "\u0140",
+  "lmoust;": "\u23b0",
+  "lmoustache;": "\u23b0",
+  "lnE;": "\u2268",
+  "lnap;": "\u2a89",
+  "lnapprox;": "\u2a89",
+  "lne;": "\u2a87",
+  "lneq;": "\u2a87",
+  "lneqq;": "\u2268",
+  "lnsim;": "\u22e6",
+  "loang;": "\u27ec",
+  "loarr;": "\u21fd",
+  "lobrk;": "\u27e6",
+  "longleftarrow;": "\u27f5",
+  "longleftrightarrow;": "\u27f7",
+  "longmapsto;": "\u27fc",
+  "longrightarrow;": "\u27f6",
+  "looparrowleft;": "\u21ab",
+  "looparrowright;": "\u21ac",
+  "lopar;": "\u2985",
+  "lopf;": "\u{01d55d}",
+  "loplus;": "\u2a2d",
+  "lotimes;": "\u2a34",
+  "lowast;": "\u2217",
+  "lowbar;": "_",
+  "loz;": "\u25ca",
+  "lozenge;": "\u25ca",
+  "lozf;": "\u29eb",
+  "lpar;": "(",
+  "lparlt;": "\u2993",
+  "lrarr;": "\u21c6",
+  "lrcorner;": "\u231f",
+  "lrhar;": "\u21cb",
+  "lrhard;": "\u296d",
+  "lrm;": "\u200e",
+  "lrtri;": "\u22bf",
+  "lsaquo;": "\u2039",
+  "lscr;": "\u{01d4c1}",
+  "lsh;": "\u21b0",
+  "lsim;": "\u2272",
+  "lsime;": "\u2a8d",
+  "lsimg;": "\u2a8f",
+  "lsqb;": "[",
+  "lsquo;": "\u2018",
+  "lsquor;": "\u201a",
+  "lstrok;": "\u0142",
+  "lt": "<",
+  "lt;": "<",
+  "ltcc;": "\u2aa6",
+  "ltcir;": "\u2a79",
+  "ltdot;": "\u22d6",
+  "lthree;": "\u22cb",
+  "ltimes;": "\u22c9",
+  "ltlarr;": "\u2976",
+  "ltquest;": "\u2a7b",
+  "ltrPar;": "\u2996",
+  "ltri;": "\u25c3",
+  "ltrie;": "\u22b4",
+  "ltrif;": "\u25c2",
+  "lurdshar;": "\u294a",
+  "luruhar;": "\u2966",
+  "lvertneqq;": "\u2268\ufe00",
+  "lvnE;": "\u2268\ufe00",
+  "mDDot;": "\u223a",
+  "macr": "\xaf",
+  "macr;": "\xaf",
+  "male;": "\u2642",
+  "malt;": "\u2720",
+  "maltese;": "\u2720",
+  "map;": "\u21a6",
+  "mapsto;": "\u21a6",
+  "mapstodown;": "\u21a7",
+  "mapstoleft;": "\u21a4",
+  "mapstoup;": "\u21a5",
+  "marker;": "\u25ae",
+  "mcomma;": "\u2a29",
+  "mcy;": "\u043c",
+  "mdash;": "\u2014",
+  "measuredangle;": "\u2221",
+  "mfr;": "\u{01d52a}",
+  "mho;": "\u2127",
+  "micro": "\xb5",
+  "micro;": "\xb5",
+  "mid;": "\u2223",
+  "midast;": "*",
+  "midcir;": "\u2af0",
+  "middot": "\xb7",
+  "middot;": "\xb7",
+  "minus;": "\u2212",
+  "minusb;": "\u229f",
+  "minusd;": "\u2238",
+  "minusdu;": "\u2a2a",
+  "mlcp;": "\u2adb",
+  "mldr;": "\u2026",
+  "mnplus;": "\u2213",
+  "models;": "\u22a7",
+  "mopf;": "\u{01d55e}",
+  "mp;": "\u2213",
+  "mscr;": "\u{01d4c2}",
+  "mstpos;": "\u223e",
+  "mu;": "\u03bc",
+  "multimap;": "\u22b8",
+  "mumap;": "\u22b8",
+  "nGg;": "\u22d9\u0338",
+  "nGt;": "\u226b\u20d2",
+  "nGtv;": "\u226b\u0338",
+  "nLeftarrow;": "\u21cd",
+  "nLeftrightarrow;": "\u21ce",
+  "nLl;": "\u22d8\u0338",
+  "nLt;": "\u226a\u20d2",
+  "nLtv;": "\u226a\u0338",
+  "nRightarrow;": "\u21cf",
+  "nVDash;": "\u22af",
+  "nVdash;": "\u22ae",
+  "nabla;": "\u2207",
+  "nacute;": "\u0144",
+  "nang;": "\u2220\u20d2",
+  "nap;": "\u2249",
+  "napE;": "\u2a70\u0338",
+  "napid;": "\u224b\u0338",
+  "napos;": "\u0149",
+  "napprox;": "\u2249",
+  "natur;": "\u266e",
+  "natural;": "\u266e",
+  "naturals;": "\u2115",
+  "nbsp": "\xa0",
+  "nbsp;": "\xa0",
+  "nbump;": "\u224e\u0338",
+  "nbumpe;": "\u224f\u0338",
+  "ncap;": "\u2a43",
+  "ncaron;": "\u0148",
+  "ncedil;": "\u0146",
+  "ncong;": "\u2247",
+  "ncongdot;": "\u2a6d\u0338",
+  "ncup;": "\u2a42",
+  "ncy;": "\u043d",
+  "ndash;": "\u2013",
+  "ne;": "\u2260",
+  "neArr;": "\u21d7",
+  "nearhk;": "\u2924",
+  "nearr;": "\u2197",
+  "nearrow;": "\u2197",
+  "nedot;": "\u2250\u0338",
+  "nequiv;": "\u2262",
+  "nesear;": "\u2928",
+  "nesim;": "\u2242\u0338",
+  "nexist;": "\u2204",
+  "nexists;": "\u2204",
+  "nfr;": "\u{01d52b}",
+  "ngE;": "\u2267\u0338",
+  "nge;": "\u2271",
+  "ngeq;": "\u2271",
+  "ngeqq;": "\u2267\u0338",
+  "ngeqslant;": "\u2a7e\u0338",
+  "nges;": "\u2a7e\u0338",
+  "ngsim;": "\u2275",
+  "ngt;": "\u226f",
+  "ngtr;": "\u226f",
+  "nhArr;": "\u21ce",
+  "nharr;": "\u21ae",
+  "nhpar;": "\u2af2",
+  "ni;": "\u220b",
+  "nis;": "\u22fc",
+  "nisd;": "\u22fa",
+  "niv;": "\u220b",
+  "njcy;": "\u045a",
+  "nlArr;": "\u21cd",
+  "nlE;": "\u2266\u0338",
+  "nlarr;": "\u219a",
+  "nldr;": "\u2025",
+  "nle;": "\u2270",
+  "nleftarrow;": "\u219a",
+  "nleftrightarrow;": "\u21ae",
+  "nleq;": "\u2270",
+  "nleqq;": "\u2266\u0338",
+  "nleqslant;": "\u2a7d\u0338",
+  "nles;": "\u2a7d\u0338",
+  "nless;": "\u226e",
+  "nlsim;": "\u2274",
+  "nlt;": "\u226e",
+  "nltri;": "\u22ea",
+  "nltrie;": "\u22ec",
+  "nmid;": "\u2224",
+  "nopf;": "\u{01d55f}",
+  "not": "\xac",
+  "not;": "\xac",
+  "notin;": "\u2209",
+  "notinE;": "\u22f9\u0338",
+  "notindot;": "\u22f5\u0338",
+  "notinva;": "\u2209",
+  "notinvb;": "\u22f7",
+  "notinvc;": "\u22f6",
+  "notni;": "\u220c",
+  "notniva;": "\u220c",
+  "notnivb;": "\u22fe",
+  "notnivc;": "\u22fd",
+  "npar;": "\u2226",
+  "nparallel;": "\u2226",
+  "nparsl;": "\u2afd\u20e5",
+  "npart;": "\u2202\u0338",
+  "npolint;": "\u2a14",
+  "npr;": "\u2280",
+  "nprcue;": "\u22e0",
+  "npre;": "\u2aaf\u0338",
+  "nprec;": "\u2280",
+  "npreceq;": "\u2aaf\u0338",
+  "nrArr;": "\u21cf",
+  "nrarr;": "\u219b",
+  "nrarrc;": "\u2933\u0338",
+  "nrarrw;": "\u219d\u0338",
+  "nrightarrow;": "\u219b",
+  "nrtri;": "\u22eb",
+  "nrtrie;": "\u22ed",
+  "nsc;": "\u2281",
+  "nsccue;": "\u22e1",
+  "nsce;": "\u2ab0\u0338",
+  "nscr;": "\u{01d4c3}",
+  "nshortmid;": "\u2224",
+  "nshortparallel;": "\u2226",
+  "nsim;": "\u2241",
+  "nsime;": "\u2244",
+  "nsimeq;": "\u2244",
+  "nsmid;": "\u2224",
+  "nspar;": "\u2226",
+  "nsqsube;": "\u22e2",
+  "nsqsupe;": "\u22e3",
+  "nsub;": "\u2284",
+  "nsubE;": "\u2ac5\u0338",
+  "nsube;": "\u2288",
+  "nsubset;": "\u2282\u20d2",
+  "nsubseteq;": "\u2288",
+  "nsubseteqq;": "\u2ac5\u0338",
+  "nsucc;": "\u2281",
+  "nsucceq;": "\u2ab0\u0338",
+  "nsup;": "\u2285",
+  "nsupE;": "\u2ac6\u0338",
+  "nsupe;": "\u2289",
+  "nsupset;": "\u2283\u20d2",
+  "nsupseteq;": "\u2289",
+  "nsupseteqq;": "\u2ac6\u0338",
+  "ntgl;": "\u2279",
+  "ntilde": "\xf1",
+  "ntilde;": "\xf1",
+  "ntlg;": "\u2278",
+  "ntriangleleft;": "\u22ea",
+  "ntrianglelefteq;": "\u22ec",
+  "ntriangleright;": "\u22eb",
+  "ntrianglerighteq;": "\u22ed",
+  "nu;": "\u03bd",
+  "num;": "#",
+  "numero;": "\u2116",
+  "numsp;": "\u2007",
+  "nvDash;": "\u22ad",
+  "nvHarr;": "\u2904",
+  "nvap;": "\u224d\u20d2",
+  "nvdash;": "\u22ac",
+  "nvge;": "\u2265\u20d2",
+  "nvgt;": ">\u20d2",
+  "nvinfin;": "\u29de",
+  "nvlArr;": "\u2902",
+  "nvle;": "\u2264\u20d2",
+  "nvlt;": "<\u20d2",
+  "nvltrie;": "\u22b4\u20d2",
+  "nvrArr;": "\u2903",
+  "nvrtrie;": "\u22b5\u20d2",
+  "nvsim;": "\u223c\u20d2",
+  "nwArr;": "\u21d6",
+  "nwarhk;": "\u2923",
+  "nwarr;": "\u2196",
+  "nwarrow;": "\u2196",
+  "nwnear;": "\u2927",
+  "oS;": "\u24c8",
+  "oacute": "\xf3",
+  "oacute;": "\xf3",
+  "oast;": "\u229b",
+  "ocir;": "\u229a",
+  "ocirc": "\xf4",
+  "ocirc;": "\xf4",
+  "ocy;": "\u043e",
+  "odash;": "\u229d",
+  "odblac;": "\u0151",
+  "odiv;": "\u2a38",
+  "odot;": "\u2299",
+  "odsold;": "\u29bc",
+  "oelig;": "\u0153",
+  "ofcir;": "\u29bf",
+  "ofr;": "\u{01d52c}",
+  "ogon;": "\u02db",
+  "ograve": "\xf2",
+  "ograve;": "\xf2",
+  "ogt;": "\u29c1",
+  "ohbar;": "\u29b5",
+  "ohm;": "\u03a9",
+  "oint;": "\u222e",
+  "olarr;": "\u21ba",
+  "olcir;": "\u29be",
+  "olcross;": "\u29bb",
+  "oline;": "\u203e",
+  "olt;": "\u29c0",
+  "omacr;": "\u014d",
+  "omega;": "\u03c9",
+  "omicron;": "\u03bf",
+  "omid;": "\u29b6",
+  "ominus;": "\u2296",
+  "oopf;": "\u{01d560}",
+  "opar;": "\u29b7",
+  "operp;": "\u29b9",
+  "oplus;": "\u2295",
+  "or;": "\u2228",
+  "orarr;": "\u21bb",
+  "ord;": "\u2a5d",
+  "order;": "\u2134",
+  "orderof;": "\u2134",
+  "ordf": "\xaa",
+  "ordf;": "\xaa",
+  "ordm": "\xba",
+  "ordm;": "\xba",
+  "origof;": "\u22b6",
+  "oror;": "\u2a56",
+  "orslope;": "\u2a57",
+  "orv;": "\u2a5b",
+  "oscr;": "\u2134",
+  "oslash": "\xf8",
+  "oslash;": "\xf8",
+  "osol;": "\u2298",
+  "otilde": "\xf5",
+  "otilde;": "\xf5",
+  "otimes;": "\u2297",
+  "otimesas;": "\u2a36",
+  "ouml": "\xf6",
+  "ouml;": "\xf6",
+  "ovbar;": "\u233d",
+  "par;": "\u2225",
+  "para": "\xb6",
+  "para;": "\xb6",
+  "parallel;": "\u2225",
+  "parsim;": "\u2af3",
+  "parsl;": "\u2afd",
+  "part;": "\u2202",
+  "pcy;": "\u043f",
+  "percnt;": "%",
+  "period;": ".",
+  "permil;": "\u2030",
+  "perp;": "\u22a5",
+  "pertenk;": "\u2031",
+  "pfr;": "\u{01d52d}",
+  "phi;": "\u03c6",
+  "phiv;": "\u03d5",
+  "phmmat;": "\u2133",
+  "phone;": "\u260e",
+  "pi;": "\u03c0",
+  "pitchfork;": "\u22d4",
+  "piv;": "\u03d6",
+  "planck;": "\u210f",
+  "planckh;": "\u210e",
+  "plankv;": "\u210f",
+  "plus;": "+",
+  "plusacir;": "\u2a23",
+  "plusb;": "\u229e",
+  "pluscir;": "\u2a22",
+  "plusdo;": "\u2214",
+  "plusdu;": "\u2a25",
+  "pluse;": "\u2a72",
+  "plusmn": "\xb1",
+  "plusmn;": "\xb1",
+  "plussim;": "\u2a26",
+  "plustwo;": "\u2a27",
+  "pm;": "\xb1",
+  "pointint;": "\u2a15",
+  "popf;": "\u{01d561}",
+  "pound": "\xa3",
+  "pound;": "\xa3",
+  "pr;": "\u227a",
+  "prE;": "\u2ab3",
+  "prap;": "\u2ab7",
+  "prcue;": "\u227c",
+  "pre;": "\u2aaf",
+  "prec;": "\u227a",
+  "precapprox;": "\u2ab7",
+  "preccurlyeq;": "\u227c",
+  "preceq;": "\u2aaf",
+  "precnapprox;": "\u2ab9",
+  "precneqq;": "\u2ab5",
+  "precnsim;": "\u22e8",
+  "precsim;": "\u227e",
+  "prime;": "\u2032",
+  "primes;": "\u2119",
+  "prnE;": "\u2ab5",
+  "prnap;": "\u2ab9",
+  "prnsim;": "\u22e8",
+  "prod;": "\u220f",
+  "profalar;": "\u232e",
+  "profline;": "\u2312",
+  "profsurf;": "\u2313",
+  "prop;": "\u221d",
+  "propto;": "\u221d",
+  "prsim;": "\u227e",
+  "prurel;": "\u22b0",
+  "pscr;": "\u{01d4c5}",
+  "psi;": "\u03c8",
+  "puncsp;": "\u2008",
+  "qfr;": "\u{01d52e}",
+  "qint;": "\u2a0c",
+  "qopf;": "\u{01d562}",
+  "qprime;": "\u2057",
+  "qscr;": "\u{01d4c6}",
+  "quaternions;": "\u210d",
+  "quatint;": "\u2a16",
+  "quest;": "?",
+  "questeq;": "\u225f",
+  "quot": "\"",
+  "quot;": "\"",
+  "rAarr;": "\u21db",
+  "rArr;": "\u21d2",
+  "rAtail;": "\u291c",
+  "rBarr;": "\u290f",
+  "rHar;": "\u2964",
+  "race;": "\u223d\u0331",
+  "racute;": "\u0155",
+  "radic;": "\u221a",
+  "raemptyv;": "\u29b3",
+  "rang;": "\u27e9",
+  "rangd;": "\u2992",
+  "range;": "\u29a5",
+  "rangle;": "\u27e9",
+  "raquo": "\xbb",
+  "raquo;": "\xbb",
+  "rarr;": "\u2192",
+  "rarrap;": "\u2975",
+  "rarrb;": "\u21e5",
+  "rarrbfs;": "\u2920",
+  "rarrc;": "\u2933",
+  "rarrfs;": "\u291e",
+  "rarrhk;": "\u21aa",
+  "rarrlp;": "\u21ac",
+  "rarrpl;": "\u2945",
+  "rarrsim;": "\u2974",
+  "rarrtl;": "\u21a3",
+  "rarrw;": "\u219d",
+  "ratail;": "\u291a",
+  "ratio;": "\u2236",
+  "rationals;": "\u211a",
+  "rbarr;": "\u290d",
+  "rbbrk;": "\u2773",
+  "rbrace;": "}",
+  "rbrack;": "]",
+  "rbrke;": "\u298c",
+  "rbrksld;": "\u298e",
+  "rbrkslu;": "\u2990",
+  "rcaron;": "\u0159",
+  "rcedil;": "\u0157",
+  "rceil;": "\u2309",
+  "rcub;": "}",
+  "rcy;": "\u0440",
+  "rdca;": "\u2937",
+  "rdldhar;": "\u2969",
+  "rdquo;": "\u201d",
+  "rdquor;": "\u201d",
+  "rdsh;": "\u21b3",
+  "real;": "\u211c",
+  "realine;": "\u211b",
+  "realpart;": "\u211c",
+  "reals;": "\u211d",
+  "rect;": "\u25ad",
+  "reg": "\xae",
+  "reg;": "\xae",
+  "rfisht;": "\u297d",
+  "rfloor;": "\u230b",
+  "rfr;": "\u{01d52f}",
+  "rhard;": "\u21c1",
+  "rharu;": "\u21c0",
+  "rharul;": "\u296c",
+  "rho;": "\u03c1",
+  "rhov;": "\u03f1",
+  "rightarrow;": "\u2192",
+  "rightarrowtail;": "\u21a3",
+  "rightharpoondown;": "\u21c1",
+  "rightharpoonup;": "\u21c0",
+  "rightleftarrows;": "\u21c4",
+  "rightleftharpoons;": "\u21cc",
+  "rightrightarrows;": "\u21c9",
+  "rightsquigarrow;": "\u219d",
+  "rightthreetimes;": "\u22cc",
+  "ring;": "\u02da",
+  "risingdotseq;": "\u2253",
+  "rlarr;": "\u21c4",
+  "rlhar;": "\u21cc",
+  "rlm;": "\u200f",
+  "rmoust;": "\u23b1",
+  "rmoustache;": "\u23b1",
+  "rnmid;": "\u2aee",
+  "roang;": "\u27ed",
+  "roarr;": "\u21fe",
+  "robrk;": "\u27e7",
+  "ropar;": "\u2986",
+  "ropf;": "\u{01d563}",
+  "roplus;": "\u2a2e",
+  "rotimes;": "\u2a35",
+  "rpar;": ")",
+  "rpargt;": "\u2994",
+  "rppolint;": "\u2a12",
+  "rrarr;": "\u21c9",
+  "rsaquo;": "\u203a",
+  "rscr;": "\u{01d4c7}",
+  "rsh;": "\u21b1",
+  "rsqb;": "]",
+  "rsquo;": "\u2019",
+  "rsquor;": "\u2019",
+  "rthree;": "\u22cc",
+  "rtimes;": "\u22ca",
+  "rtri;": "\u25b9",
+  "rtrie;": "\u22b5",
+  "rtrif;": "\u25b8",
+  "rtriltri;": "\u29ce",
+  "ruluhar;": "\u2968",
+  "rx;": "\u211e",
+  "sacute;": "\u015b",
+  "sbquo;": "\u201a",
+  "sc;": "\u227b",
+  "scE;": "\u2ab4",
+  "scap;": "\u2ab8",
+  "scaron;": "\u0161",
+  "sccue;": "\u227d",
+  "sce;": "\u2ab0",
+  "scedil;": "\u015f",
+  "scirc;": "\u015d",
+  "scnE;": "\u2ab6",
+  "scnap;": "\u2aba",
+  "scnsim;": "\u22e9",
+  "scpolint;": "\u2a13",
+  "scsim;": "\u227f",
+  "scy;": "\u0441",
+  "sdot;": "\u22c5",
+  "sdotb;": "\u22a1",
+  "sdote;": "\u2a66",
+  "seArr;": "\u21d8",
+  "searhk;": "\u2925",
+  "searr;": "\u2198",
+  "searrow;": "\u2198",
+  "sect": "\xa7",
+  "sect;": "\xa7",
+  "semi;": ";",
+  "seswar;": "\u2929",
+  "setminus;": "\u2216",
+  "setmn;": "\u2216",
+  "sext;": "\u2736",
+  "sfr;": "\u{01d530}",
+  "sfrown;": "\u2322",
+  "sharp;": "\u266f",
+  "shchcy;": "\u0449",
+  "shcy;": "\u0448",
+  "shortmid;": "\u2223",
+  "shortparallel;": "\u2225",
+  "shy": "\xad",
+  "shy;": "\xad",
+  "sigma;": "\u03c3",
+  "sigmaf;": "\u03c2",
+  "sigmav;": "\u03c2",
+  "sim;": "\u223c",
+  "simdot;": "\u2a6a",
+  "sime;": "\u2243",
+  "simeq;": "\u2243",
+  "simg;": "\u2a9e",
+  "simgE;": "\u2aa0",
+  "siml;": "\u2a9d",
+  "simlE;": "\u2a9f",
+  "simne;": "\u2246",
+  "simplus;": "\u2a24",
+  "simrarr;": "\u2972",
+  "slarr;": "\u2190",
+  "smallsetminus;": "\u2216",
+  "smashp;": "\u2a33",
+  "smeparsl;": "\u29e4",
+  "smid;": "\u2223",
+  "smile;": "\u2323",
+  "smt;": "\u2aaa",
+  "smte;": "\u2aac",
+  "smtes;": "\u2aac\ufe00",
+  "softcy;": "\u044c",
+  "sol;": "/",
+  "solb;": "\u29c4",
+  "solbar;": "\u233f",
+  "sopf;": "\u{01d564}",
+  "spades;": "\u2660",
+  "spadesuit;": "\u2660",
+  "spar;": "\u2225",
+  "sqcap;": "\u2293",
+  "sqcaps;": "\u2293\ufe00",
+  "sqcup;": "\u2294",
+  "sqcups;": "\u2294\ufe00",
+  "sqsub;": "\u228f",
+  "sqsube;": "\u2291",
+  "sqsubset;": "\u228f",
+  "sqsubseteq;": "\u2291",
+  "sqsup;": "\u2290",
+  "sqsupe;": "\u2292",
+  "sqsupset;": "\u2290",
+  "sqsupseteq;": "\u2292",
+  "squ;": "\u25a1",
+  "square;": "\u25a1",
+  "squarf;": "\u25aa",
+  "squf;": "\u25aa",
+  "srarr;": "\u2192",
+  "sscr;": "\u{01d4c8}",
+  "ssetmn;": "\u2216",
+  "ssmile;": "\u2323",
+  "sstarf;": "\u22c6",
+  "star;": "\u2606",
+  "starf;": "\u2605",
+  "straightepsilon;": "\u03f5",
+  "straightphi;": "\u03d5",
+  "strns;": "\xaf",
+  "sub;": "\u2282",
+  "subE;": "\u2ac5",
+  "subdot;": "\u2abd",
+  "sube;": "\u2286",
+  "subedot;": "\u2ac3",
+  "submult;": "\u2ac1",
+  "subnE;": "\u2acb",
+  "subne;": "\u228a",
+  "subplus;": "\u2abf",
+  "subrarr;": "\u2979",
+  "subset;": "\u2282",
+  "subseteq;": "\u2286",
+  "subseteqq;": "\u2ac5",
+  "subsetneq;": "\u228a",
+  "subsetneqq;": "\u2acb",
+  "subsim;": "\u2ac7",
+  "subsub;": "\u2ad5",
+  "subsup;": "\u2ad3",
+  "succ;": "\u227b",
+  "succapprox;": "\u2ab8",
+  "succcurlyeq;": "\u227d",
+  "succeq;": "\u2ab0",
+  "succnapprox;": "\u2aba",
+  "succneqq;": "\u2ab6",
+  "succnsim;": "\u22e9",
+  "succsim;": "\u227f",
+  "sum;": "\u2211",
+  "sung;": "\u266a",
+  "sup1": "\xb9",
+  "sup1;": "\xb9",
+  "sup2": "\xb2",
+  "sup2;": "\xb2",
+  "sup3": "\xb3",
+  "sup3;": "\xb3",
+  "sup;": "\u2283",
+  "supE;": "\u2ac6",
+  "supdot;": "\u2abe",
+  "supdsub;": "\u2ad8",
+  "supe;": "\u2287",
+  "supedot;": "\u2ac4",
+  "suphsol;": "\u27c9",
+  "suphsub;": "\u2ad7",
+  "suplarr;": "\u297b",
+  "supmult;": "\u2ac2",
+  "supnE;": "\u2acc",
+  "supne;": "\u228b",
+  "supplus;": "\u2ac0",
+  "supset;": "\u2283",
+  "supseteq;": "\u2287",
+  "supseteqq;": "\u2ac6",
+  "supsetneq;": "\u228b",
+  "supsetneqq;": "\u2acc",
+  "supsim;": "\u2ac8",
+  "supsub;": "\u2ad4",
+  "supsup;": "\u2ad6",
+  "swArr;": "\u21d9",
+  "swarhk;": "\u2926",
+  "swarr;": "\u2199",
+  "swarrow;": "\u2199",
+  "swnwar;": "\u292a",
+  "szlig": "\xdf",
+  "szlig;": "\xdf",
+  "target;": "\u2316",
+  "tau;": "\u03c4",
+  "tbrk;": "\u23b4",
+  "tcaron;": "\u0165",
+  "tcedil;": "\u0163",
+  "tcy;": "\u0442",
+  "tdot;": "\u20db",
+  "telrec;": "\u2315",
+  "tfr;": "\u{01d531}",
+  "there4;": "\u2234",
+  "therefore;": "\u2234",
+  "theta;": "\u03b8",
+  "thetasym;": "\u03d1",
+  "thetav;": "\u03d1",
+  "thickapprox;": "\u2248",
+  "thicksim;": "\u223c",
+  "thinsp;": "\u2009",
+  "thkap;": "\u2248",
+  "thksim;": "\u223c",
+  "thorn": "\xfe",
+  "thorn;": "\xfe",
+  "tilde;": "\u02dc",
+  "times": "\xd7",
+  "times;": "\xd7",
+  "timesb;": "\u22a0",
+  "timesbar;": "\u2a31",
+  "timesd;": "\u2a30",
+  "tint;": "\u222d",
+  "toea;": "\u2928",
+  "top;": "\u22a4",
+  "topbot;": "\u2336",
+  "topcir;": "\u2af1",
+  "topf;": "\u{01d565}",
+  "topfork;": "\u2ada",
+  "tosa;": "\u2929",
+  "tprime;": "\u2034",
+  "trade;": "\u2122",
+  "triangle;": "\u25b5",
+  "triangledown;": "\u25bf",
+  "triangleleft;": "\u25c3",
+  "trianglelefteq;": "\u22b4",
+  "triangleq;": "\u225c",
+  "triangleright;": "\u25b9",
+  "trianglerighteq;": "\u22b5",
+  "tridot;": "\u25ec",
+  "trie;": "\u225c",
+  "triminus;": "\u2a3a",
+  "triplus;": "\u2a39",
+  "trisb;": "\u29cd",
+  "tritime;": "\u2a3b",
+  "trpezium;": "\u23e2",
+  "tscr;": "\u{01d4c9}",
+  "tscy;": "\u0446",
+  "tshcy;": "\u045b",
+  "tstrok;": "\u0167",
+  "twixt;": "\u226c",
+  "twoheadleftarrow;": "\u219e",
+  "twoheadrightarrow;": "\u21a0",
+  "uArr;": "\u21d1",
+  "uHar;": "\u2963",
+  "uacute": "\xfa",
+  "uacute;": "\xfa",
+  "uarr;": "\u2191",
+  "ubrcy;": "\u045e",
+  "ubreve;": "\u016d",
+  "ucirc": "\xfb",
+  "ucirc;": "\xfb",
+  "ucy;": "\u0443",
+  "udarr;": "\u21c5",
+  "udblac;": "\u0171",
+  "udhar;": "\u296e",
+  "ufisht;": "\u297e",
+  "ufr;": "\u{01d532}",
+  "ugrave": "\xf9",
+  "ugrave;": "\xf9",
+  "uharl;": "\u21bf",
+  "uharr;": "\u21be",
+  "uhblk;": "\u2580",
+  "ulcorn;": "\u231c",
+  "ulcorner;": "\u231c",
+  "ulcrop;": "\u230f",
+  "ultri;": "\u25f8",
+  "umacr;": "\u016b",
+  "uml": "\xa8",
+  "uml;": "\xa8",
+  "uogon;": "\u0173",
+  "uopf;": "\u{01d566}",
+  "uparrow;": "\u2191",
+  "updownarrow;": "\u2195",
+  "upharpoonleft;": "\u21bf",
+  "upharpoonright;": "\u21be",
+  "uplus;": "\u228e",
+  "upsi;": "\u03c5",
+  "upsih;": "\u03d2",
+  "upsilon;": "\u03c5",
+  "upuparrows;": "\u21c8",
+  "urcorn;": "\u231d",
+  "urcorner;": "\u231d",
+  "urcrop;": "\u230e",
+  "uring;": "\u016f",
+  "urtri;": "\u25f9",
+  "uscr;": "\u{01d4ca}",
+  "utdot;": "\u22f0",
+  "utilde;": "\u0169",
+  "utri;": "\u25b5",
+  "utrif;": "\u25b4",
+  "uuarr;": "\u21c8",
+  "uuml": "\xfc",
+  "uuml;": "\xfc",
+  "uwangle;": "\u29a7",
+  "vArr;": "\u21d5",
+  "vBar;": "\u2ae8",
+  "vBarv;": "\u2ae9",
+  "vDash;": "\u22a8",
+  "vangrt;": "\u299c",
+  "varepsilon;": "\u03f5",
+  "varkappa;": "\u03f0",
+  "varnothing;": "\u2205",
+  "varphi;": "\u03d5",
+  "varpi;": "\u03d6",
+  "varpropto;": "\u221d",
+  "varr;": "\u2195",
+  "varrho;": "\u03f1",
+  "varsigma;": "\u03c2",
+  "varsubsetneq;": "\u228a\ufe00",
+  "varsubsetneqq;": "\u2acb\ufe00",
+  "varsupsetneq;": "\u228b\ufe00",
+  "varsupsetneqq;": "\u2acc\ufe00",
+  "vartheta;": "\u03d1",
+  "vartriangleleft;": "\u22b2",
+  "vartriangleright;": "\u22b3",
+  "vcy;": "\u0432",
+  "vdash;": "\u22a2",
+  "vee;": "\u2228",
+  "veebar;": "\u22bb",
+  "veeeq;": "\u225a",
+  "vellip;": "\u22ee",
+  "verbar;": "|",
+  "vert;": "|",
+  "vfr;": "\u{01d533}",
+  "vltri;": "\u22b2",
+  "vnsub;": "\u2282\u20d2",
+  "vnsup;": "\u2283\u20d2",
+  "vopf;": "\u{01d567}",
+  "vprop;": "\u221d",
+  "vrtri;": "\u22b3",
+  "vscr;": "\u{01d4cb}",
+  "vsubnE;": "\u2acb\ufe00",
+  "vsubne;": "\u228a\ufe00",
+  "vsupnE;": "\u2acc\ufe00",
+  "vsupne;": "\u228b\ufe00",
+  "vzigzag;": "\u299a",
+  "wcirc;": "\u0175",
+  "wedbar;": "\u2a5f",
+  "wedge;": "\u2227",
+  "wedgeq;": "\u2259",
+  "weierp;": "\u2118",
+  "wfr;": "\u{01d534}",
+  "wopf;": "\u{01d568}",
+  "wp;": "\u2118",
+  "wr;": "\u2240",
+  "wreath;": "\u2240",
+  "wscr;": "\u{01d4cc}",
+  "xcap;": "\u22c2",
+  "xcirc;": "\u25ef",
+  "xcup;": "\u22c3",
+  "xdtri;": "\u25bd",
+  "xfr;": "\u{01d535}",
+  "xhArr;": "\u27fa",
+  "xharr;": "\u27f7",
+  "xi;": "\u03be",
+  "xlArr;": "\u27f8",
+  "xlarr;": "\u27f5",
+  "xmap;": "\u27fc",
+  "xnis;": "\u22fb",
+  "xodot;": "\u2a00",
+  "xopf;": "\u{01d569}",
+  "xoplus;": "\u2a01",
+  "xotime;": "\u2a02",
+  "xrArr;": "\u27f9",
+  "xrarr;": "\u27f6",
+  "xscr;": "\u{01d4cd}",
+  "xsqcup;": "\u2a06",
+  "xuplus;": "\u2a04",
+  "xutri;": "\u25b3",
+  "xvee;": "\u22c1",
+  "xwedge;": "\u22c0",
+  "yacute": "\xfd",
+  "yacute;": "\xfd",
+  "yacy;": "\u044f",
+  "ycirc;": "\u0177",
+  "ycy;": "\u044b",
+  "yen": "\xa5",
+  "yen;": "\xa5",
+  "yfr;": "\u{01d536}",
+  "yicy;": "\u0457",
+  "yopf;": "\u{01d56a}",
+  "yscr;": "\u{01d4ce}",
+  "yucy;": "\u044e",
+  "yuml": "\xff",
+  "yuml;": "\xff",
+  "zacute;": "\u017a",
+  "zcaron;": "\u017e",
+  "zcy;": "\u0437",
+  "zdot;": "\u017c",
+  "zeetrf;": "\u2128",
+  "zeta;": "\u03b6",
+  "zfr;": "\u{01d537}",
+  "zhcy;": "\u0436",
+  "zigrarr;": "\u21dd",
+  "zopf;": "\u{01d56b}",
+  "zscr;": "\u{01d4cf}",
+  "zwj;": "\u200d",
+  "zwnj;": "\u200c",
+};
+
+const Map<int, String> replacementCharacters = const {
+  0x00: "\uFFFD",
+  0x0d: "\u000D",
+  0x80: "\u20AC",
+  0x81: "\u0081",
+  0x82: "\u201A",
+  0x83: "\u0192",
+  0x84: "\u201E",
+  0x85: "\u2026",
+  0x86: "\u2020",
+  0x87: "\u2021",
+  0x88: "\u02C6",
+  0x89: "\u2030",
+  0x8A: "\u0160",
+  0x8B: "\u2039",
+  0x8C: "\u0152",
+  0x8D: "\u008D",
+  0x8E: "\u017D",
+  0x8F: "\u008F",
+  0x90: "\u0090",
+  0x91: "\u2018",
+  0x92: "\u2019",
+  0x93: "\u201C",
+  0x94: "\u201D",
+  0x95: "\u2022",
+  0x96: "\u2013",
+  0x97: "\u2014",
+  0x98: "\u02DC",
+  0x99: "\u2122",
+  0x9A: "\u0161",
+  0x9B: "\u203A",
+  0x9C: "\u0153",
+  0x9D: "\u009D",
+  0x9E: "\u017E",
+  0x9F: "\u0178"
+};
+
+const Map<String, String> encodings = const {
+  '437': 'cp437',
+  '850': 'cp850',
+  '852': 'cp852',
+  '855': 'cp855',
+  '857': 'cp857',
+  '860': 'cp860',
+  '861': 'cp861',
+  '862': 'cp862',
+  '863': 'cp863',
+  '865': 'cp865',
+  '866': 'cp866',
+  '869': 'cp869',
+  'ansix341968': 'ascii',
+  'ansix341986': 'ascii',
+  'arabic': 'iso8859-6',
+  'ascii': 'ascii',
+  'asmo708': 'iso8859-6',
+  'big5': 'big5',
+  'big5hkscs': 'big5hkscs',
+  'chinese': 'gbk',
+  'cp037': 'cp037',
+  'cp1026': 'cp1026',
+  'cp154': 'ptcp154',
+  'cp367': 'ascii',
+  'cp424': 'cp424',
+  'cp437': 'cp437',
+  'cp500': 'cp500',
+  'cp775': 'cp775',
+  'cp819': 'windows-1252',
+  'cp850': 'cp850',
+  'cp852': 'cp852',
+  'cp855': 'cp855',
+  'cp857': 'cp857',
+  'cp860': 'cp860',
+  'cp861': 'cp861',
+  'cp862': 'cp862',
+  'cp863': 'cp863',
+  'cp864': 'cp864',
+  'cp865': 'cp865',
+  'cp866': 'cp866',
+  'cp869': 'cp869',
+  'cp936': 'gbk',
+  'cpgr': 'cp869',
+  'cpis': 'cp861',
+  'csascii': 'ascii',
+  'csbig5': 'big5',
+  'cseuckr': 'cp949',
+  'cseucpkdfmtjapanese': 'euc_jp',
+  'csgb2312': 'gbk',
+  'cshproman8': 'hp-roman8',
+  'csibm037': 'cp037',
+  'csibm1026': 'cp1026',
+  'csibm424': 'cp424',
+  'csibm500': 'cp500',
+  'csibm855': 'cp855',
+  'csibm857': 'cp857',
+  'csibm860': 'cp860',
+  'csibm861': 'cp861',
+  'csibm863': 'cp863',
+  'csibm864': 'cp864',
+  'csibm865': 'cp865',
+  'csibm866': 'cp866',
+  'csibm869': 'cp869',
+  'csiso2022jp': 'iso2022_jp',
+  'csiso2022jp2': 'iso2022_jp_2',
+  'csiso2022kr': 'iso2022_kr',
+  'csiso58gb231280': 'gbk',
+  'csisolatin1': 'windows-1252',
+  'csisolatin2': 'iso8859-2',
+  'csisolatin3': 'iso8859-3',
+  'csisolatin4': 'iso8859-4',
+  'csisolatin5': 'windows-1254',
+  'csisolatin6': 'iso8859-10',
+  'csisolatinarabic': 'iso8859-6',
+  'csisolatincyrillic': 'iso8859-5',
+  'csisolatingreek': 'iso8859-7',
+  'csisolatinhebrew': 'iso8859-8',
+  'cskoi8r': 'koi8-r',
+  'csksc56011987': 'cp949',
+  'cspc775baltic': 'cp775',
+  'cspc850multilingual': 'cp850',
+  'cspc862latinhebrew': 'cp862',
+  'cspc8codepage437': 'cp437',
+  'cspcp852': 'cp852',
+  'csptcp154': 'ptcp154',
+  'csshiftjis': 'shift_jis',
+  'csunicode11utf7': 'utf-7',
+  'cyrillic': 'iso8859-5',
+  'cyrillicasian': 'ptcp154',
+  'ebcdiccpbe': 'cp500',
+  'ebcdiccpca': 'cp037',
+  'ebcdiccpch': 'cp500',
+  'ebcdiccphe': 'cp424',
+  'ebcdiccpnl': 'cp037',
+  'ebcdiccpus': 'cp037',
+  'ebcdiccpwt': 'cp037',
+  'ecma114': 'iso8859-6',
+  'ecma118': 'iso8859-7',
+  'elot928': 'iso8859-7',
+  'eucjp': 'euc_jp',
+  'euckr': 'cp949',
+  'extendedunixcodepackedformatforjapanese': 'euc_jp',
+  'gb18030': 'gb18030',
+  'gb2312': 'gbk',
+  'gb231280': 'gbk',
+  'gbk': 'gbk',
+  'greek': 'iso8859-7',
+  'greek8': 'iso8859-7',
+  'hebrew': 'iso8859-8',
+  'hproman8': 'hp-roman8',
+  'hzgb2312': 'hz',
+  'ibm037': 'cp037',
+  'ibm1026': 'cp1026',
+  'ibm367': 'ascii',
+  'ibm424': 'cp424',
+  'ibm437': 'cp437',
+  'ibm500': 'cp500',
+  'ibm775': 'cp775',
+  'ibm819': 'windows-1252',
+  'ibm850': 'cp850',
+  'ibm852': 'cp852',
+  'ibm855': 'cp855',
+  'ibm857': 'cp857',
+  'ibm860': 'cp860',
+  'ibm861': 'cp861',
+  'ibm862': 'cp862',
+  'ibm863': 'cp863',
+  'ibm864': 'cp864',
+  'ibm865': 'cp865',
+  'ibm866': 'cp866',
+  'ibm869': 'cp869',
+  'iso2022jp': 'iso2022_jp',
+  'iso2022jp2': 'iso2022_jp_2',
+  'iso2022kr': 'iso2022_kr',
+  'iso646irv1991': 'ascii',
+  'iso646us': 'ascii',
+  'iso88591': 'windows-1252',
+  'iso885910': 'iso8859-10',
+  'iso8859101992': 'iso8859-10',
+  'iso885911987': 'windows-1252',
+  'iso885913': 'iso8859-13',
+  'iso885914': 'iso8859-14',
+  'iso8859141998': 'iso8859-14',
+  'iso885915': 'iso8859-15',
+  'iso885916': 'iso8859-16',
+  'iso8859162001': 'iso8859-16',
+  'iso88592': 'iso8859-2',
+  'iso885921987': 'iso8859-2',
+  'iso88593': 'iso8859-3',
+  'iso885931988': 'iso8859-3',
+  'iso88594': 'iso8859-4',
+  'iso885941988': 'iso8859-4',
+  'iso88595': 'iso8859-5',
+  'iso885951988': 'iso8859-5',
+  'iso88596': 'iso8859-6',
+  'iso885961987': 'iso8859-6',
+  'iso88597': 'iso8859-7',
+  'iso885971987': 'iso8859-7',
+  'iso88598': 'iso8859-8',
+  'iso885981988': 'iso8859-8',
+  'iso88599': 'windows-1254',
+  'iso885991989': 'windows-1254',
+  'isoceltic': 'iso8859-14',
+  'isoir100': 'windows-1252',
+  'isoir101': 'iso8859-2',
+  'isoir109': 'iso8859-3',
+  'isoir110': 'iso8859-4',
+  'isoir126': 'iso8859-7',
+  'isoir127': 'iso8859-6',
+  'isoir138': 'iso8859-8',
+  'isoir144': 'iso8859-5',
+  'isoir148': 'windows-1254',
+  'isoir149': 'cp949',
+  'isoir157': 'iso8859-10',
+  'isoir199': 'iso8859-14',
+  'isoir226': 'iso8859-16',
+  'isoir58': 'gbk',
+  'isoir6': 'ascii',
+  'koi8r': 'koi8-r',
+  'koi8u': 'koi8-u',
+  'korean': 'cp949',
+  'ksc5601': 'cp949',
+  'ksc56011987': 'cp949',
+  'ksc56011989': 'cp949',
+  'l1': 'windows-1252',
+  'l10': 'iso8859-16',
+  'l2': 'iso8859-2',
+  'l3': 'iso8859-3',
+  'l4': 'iso8859-4',
+  'l5': 'windows-1254',
+  'l6': 'iso8859-10',
+  'l8': 'iso8859-14',
+  'latin1': 'windows-1252',
+  'latin10': 'iso8859-16',
+  'latin2': 'iso8859-2',
+  'latin3': 'iso8859-3',
+  'latin4': 'iso8859-4',
+  'latin5': 'windows-1254',
+  'latin6': 'iso8859-10',
+  'latin8': 'iso8859-14',
+  'latin9': 'iso8859-15',
+  'ms936': 'gbk',
+  'mskanji': 'shift_jis',
+  'pt154': 'ptcp154',
+  'ptcp154': 'ptcp154',
+  'r8': 'hp-roman8',
+  'roman8': 'hp-roman8',
+  'shiftjis': 'shift_jis',
+  'tis620': 'cp874',
+  'unicode11utf7': 'utf-7',
+  'us': 'ascii',
+  'usascii': 'ascii',
+  'utf16': 'utf-16',
+  'utf16be': 'utf-16-be',
+  'utf16le': 'utf-16-le',
+  'utf8': 'utf-8',
+  'windows1250': 'cp1250',
+  'windows1251': 'cp1251',
+  'windows1252': 'cp1252',
+  'windows1253': 'cp1253',
+  'windows1254': 'cp1254',
+  'windows1255': 'cp1255',
+  'windows1256': 'cp1256',
+  'windows1257': 'cp1257',
+  'windows1258': 'cp1258',
+  'windows936': 'gbk',
+  'x-x-big5': 'big5'
+};
diff --git a/html/lib/src/css_class_set.dart b/html/lib/src/css_class_set.dart
new file mode 100644
index 0000000..2e131a5
--- /dev/null
+++ b/html/lib/src/css_class_set.dart
@@ -0,0 +1,330 @@
+// 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.
+
+// TODO(jmesserly): everything in this file is copied straight from "dart:html".
+library html.dom.src;
+
+import 'dart:collection';
+import 'package:html/dom.dart';
+
+class ElementCssClassSet extends CssClassSetImpl {
+  final Element _element;
+
+  ElementCssClassSet(this._element);
+
+  Set<String> readClasses() {
+    var s = new LinkedHashSet<String>();
+    var classname = _element.className;
+
+    for (String name in classname.split(' ')) {
+      String trimmed = name.trim();
+      if (!trimmed.isEmpty) {
+        s.add(trimmed);
+      }
+    }
+    return s;
+  }
+
+  void writeClasses(Set<String> s) {
+    _element.className = s.join(' ');
+  }
+}
+
+/** A Set that stores the CSS class names for an element. */
+abstract class CssClassSet implements Set<String> {
+
+  /**
+   * Adds the class [value] to the element if it is not on it, removes it if it
+   * is.
+   *
+   * If [shouldAdd] is true, then we always add that [value] to the element. If
+   * [shouldAdd] is false then we always remove [value] from the element.
+   */
+  bool toggle(String value, [bool shouldAdd]);
+
+  /**
+   * Returns [:true:] if classes cannot be added or removed from this
+   * [:CssClassSet:].
+   */
+  bool get frozen;
+
+  /**
+   * Determine if this element contains the class [value].
+   *
+   * This is the Dart equivalent of jQuery's
+   * [hasClass](http://api.jquery.com/hasClass/).
+   */
+  bool contains(String value);
+
+  /**
+   * Add the class [value] to element.
+   *
+   * This is the Dart equivalent of jQuery's
+   * [addClass](http://api.jquery.com/addClass/).
+   *
+   * If this corresponds to one element. Returns true if [value] was added to
+   * the set, otherwise false.
+   *
+   * If this corresponds to many elements, null is always returned.
+   */
+  bool add(String value);
+
+  /**
+   * Remove the class [value] from element, and return true on successful
+   * removal.
+   *
+   * This is the Dart equivalent of jQuery's
+   * [removeClass](http://api.jquery.com/removeClass/).
+   */
+  bool remove(Object value);
+
+  /**
+   * Add all classes specified in [iterable] to element.
+   *
+   * This is the Dart equivalent of jQuery's
+   * [addClass](http://api.jquery.com/addClass/).
+   */
+  void addAll(Iterable<String> iterable);
+
+  /**
+   * Remove all classes specified in [iterable] from element.
+   *
+   * This is the Dart equivalent of jQuery's
+   * [removeClass](http://api.jquery.com/removeClass/).
+   */
+  void removeAll(Iterable<String> iterable);
+
+  /**
+   * Toggles all classes specified in [iterable] on element.
+   *
+   * Iterate through [iterable]'s items, and add it if it is not on it, or
+   * remove it if it is. This is the Dart equivalent of jQuery's
+   * [toggleClass](http://api.jquery.com/toggleClass/).
+   * If [shouldAdd] is true, then we always add all the classes in [iterable]
+   * element. If [shouldAdd] is false then we always remove all the classes in
+   * [iterable] from the element.
+   */
+  void toggleAll(Iterable<String> iterable, [bool shouldAdd]);
+}
+
+abstract class CssClassSetImpl implements CssClassSet {
+  String toString() {
+    return readClasses().join(' ');
+  }
+
+  /**
+   * Adds the class [value] to the element if it is not on it, removes it if it
+   * is.
+   *
+   * If [shouldAdd] is true, then we always add that [value] to the element. If
+   * [shouldAdd] is false then we always remove [value] from the element.
+   */
+  bool toggle(String value, [bool shouldAdd]) {
+    Set<String> s = readClasses();
+    bool result = false;
+    if (shouldAdd == null) shouldAdd = !s.contains(value);
+    if (shouldAdd) {
+      s.add(value);
+      result = true;
+    } else {
+      s.remove(value);
+    }
+    writeClasses(s);
+    return result;
+  }
+
+  /**
+   * Returns [:true:] if classes cannot be added or removed from this
+   * [:CssClassSet:].
+   */
+  bool get frozen => false;
+
+  // interface Iterable - BEGIN
+  Iterator<String> get iterator => readClasses().iterator;
+  // interface Iterable - END
+
+  // interface Collection - BEGIN
+  void forEach(void f(String element)) {
+    readClasses().forEach(f);
+  }
+
+  String join([String separator = ""]) => readClasses().join(separator);
+
+  Iterable map(f(String element)) => readClasses().map(f);
+
+  Iterable<String> where(bool f(String element)) => readClasses().where(f);
+
+  Iterable expand(Iterable f(String element)) => readClasses().expand(f);
+
+  bool every(bool f(String element)) => readClasses().every(f);
+
+  bool any(bool f(String element)) => readClasses().any(f);
+
+  bool get isEmpty => readClasses().isEmpty;
+
+  bool get isNotEmpty => readClasses().isNotEmpty;
+
+  int get length => readClasses().length;
+
+  String reduce(String combine(String value, String element)) {
+    return readClasses().reduce(combine);
+  }
+
+  dynamic fold(dynamic initialValue,
+      dynamic combine(dynamic previousValue, String element)) {
+    return readClasses().fold(initialValue, combine);
+  }
+  // interface Collection - END
+
+  // interface Set - BEGIN
+  /**
+   * Determine if this element contains the class [value].
+   *
+   * This is the Dart equivalent of jQuery's
+   * [hasClass](http://api.jquery.com/hasClass/).
+   */
+  bool contains(String value) => readClasses().contains(value);
+
+  /** Lookup from the Set interface. Not interesting for a String set. */
+  String lookup(String value) => contains(value) ? value : null;
+
+  /**
+   * Add the class [value] to element.
+   *
+   * This is the Dart equivalent of jQuery's
+   * [addClass](http://api.jquery.com/addClass/).
+   */
+  bool add(String value) {
+    // TODO - figure out if we need to do any validation here
+    // or if the browser natively does enough.
+    return modify((s) => s.add(value));
+  }
+
+  /**
+   * Remove the class [value] from element, and return true on successful
+   * removal.
+   *
+   * This is the Dart equivalent of jQuery's
+   * [removeClass](http://api.jquery.com/removeClass/).
+   */
+  bool remove(Object value) {
+    if (value is! String) return false;
+    Set<String> s = readClasses();
+    bool result = s.remove(value);
+    writeClasses(s);
+    return result;
+  }
+
+  /**
+   * Add all classes specified in [iterable] to element.
+   *
+   * This is the Dart equivalent of jQuery's
+   * [addClass](http://api.jquery.com/addClass/).
+   */
+  void addAll(Iterable<String> iterable) {
+    // TODO - see comment above about validation.
+    modify((s) => s.addAll(iterable));
+  }
+
+  /**
+   * Remove all classes specified in [iterable] from element.
+   *
+   * This is the Dart equivalent of jQuery's
+   * [removeClass](http://api.jquery.com/removeClass/).
+   */
+  void removeAll(Iterable<String> iterable) {
+    modify((s) => s.removeAll(iterable));
+  }
+
+  /**
+   * Toggles all classes specified in [iterable] on element.
+   *
+   * Iterate through [iterable]'s items, and add it if it is not on it, or
+   * remove it if it is. This is the Dart equivalent of jQuery's
+   * [toggleClass](http://api.jquery.com/toggleClass/).
+   * If [shouldAdd] is true, then we always add all the classes in [iterable]
+   * element. If [shouldAdd] is false then we always remove all the classes in
+   * [iterable] from the element.
+   */
+  void toggleAll(Iterable<String> iterable, [bool shouldAdd]) {
+    iterable.forEach((e) => toggle(e, shouldAdd));
+  }
+
+  void retainAll(Iterable<String> iterable) {
+    modify((s) => s.retainAll(iterable));
+  }
+
+  void removeWhere(bool test(String name)) {
+    modify((s) => s.removeWhere(test));
+  }
+
+  void retainWhere(bool test(String name)) {
+    modify((s) => s.retainWhere(test));
+  }
+
+  bool containsAll(Iterable<String> collection) =>
+      readClasses().containsAll(collection);
+
+  Set<String> intersection(Set<String> other) =>
+      readClasses().intersection(other);
+
+  Set<String> union(Set<String> other) => readClasses().union(other);
+
+  Set<String> difference(Set<String> other) => readClasses().difference(other);
+
+  String get first => readClasses().first;
+  String get last => readClasses().last;
+  String get single => readClasses().single;
+  List<String> toList({bool growable: true}) =>
+      readClasses().toList(growable: growable);
+  Set<String> toSet() => readClasses().toSet();
+  Iterable<String> take(int n) => readClasses().take(n);
+  Iterable<String> takeWhile(bool test(String value)) =>
+      readClasses().takeWhile(test);
+  Iterable<String> skip(int n) => readClasses().skip(n);
+  Iterable<String> skipWhile(bool test(String value)) =>
+      readClasses().skipWhile(test);
+  dynamic firstWhere(bool test(String value), {Object orElse()}) =>
+      readClasses().firstWhere(test, orElse: orElse);
+  dynamic lastWhere(bool test(String value), {Object orElse()}) =>
+      readClasses().lastWhere(test, orElse: orElse);
+  String singleWhere(bool test(String value)) =>
+      readClasses().singleWhere(test);
+  String elementAt(int index) => readClasses().elementAt(index);
+
+  void clear() {
+    modify((s) => s.clear());
+  }
+  // interface Set - END
+
+  /**
+   * Helper method used to modify the set of css classes on this element.
+   *
+   *   f - callback with:
+   *   s - a Set of all the css class name currently on this element.
+   *
+   *   After f returns, the modified set is written to the
+   *       className property of this element.
+   */
+  modify(f(Set<String> s)) {
+    Set<String> s = readClasses();
+    var ret = f(s);
+    writeClasses(s);
+    return ret;
+  }
+
+  /**
+   * Read the class names from the Element class property,
+   * and put them into a set (duplicates are discarded).
+   * This is intended to be overridden by specific implementations.
+   */
+  Set<String> readClasses();
+
+  /**
+   * Join all the elements of a set into one string and write
+   * back to the element.
+   * This is intended to be overridden by specific implementations.
+   */
+  void writeClasses(Set<String> s);
+}
diff --git a/html/lib/src/encoding_parser.dart b/html/lib/src/encoding_parser.dart
new file mode 100644
index 0000000..fd64ff9
--- /dev/null
+++ b/html/lib/src/encoding_parser.dart
@@ -0,0 +1,370 @@
+library encoding_parser;
+
+import 'constants.dart';
+import 'inputstream.dart';
+
+// TODO(jmesserly): I converted StopIteration to StateError("No more elements").
+// Seems strange to throw this from outside of an iterator though.
+/// String-like object with an associated position and various extra methods
+/// If the position is ever greater than the string length then an exception is
+/// raised.
+class EncodingBytes {
+  final String _bytes;
+  int _position = -1;
+
+  EncodingBytes(this._bytes);
+
+  int get length => _bytes.length;
+
+  String next() {
+    var p = _position = _position + 1;
+    if (p >= length) {
+      throw new StateError("No more elements");
+    } else if (p < 0) {
+      throw new RangeError(p);
+    }
+    return _bytes[p];
+  }
+
+  String previous() {
+    var p = _position;
+    if (p >= length) {
+      throw new StateError("No more elements");
+    } else if (p < 0) {
+      throw new RangeError(p);
+    }
+    _position = p = p - 1;
+    return _bytes[p];
+  }
+
+  set position(int value) {
+    if (_position >= length) {
+      throw new StateError("No more elements");
+    }
+    _position = value;
+  }
+
+  int get position {
+    if (_position >= length) {
+      throw new StateError("No more elements");
+    }
+    if (_position >= 0) {
+      return _position;
+    } else {
+      return 0;
+    }
+  }
+
+  String get currentByte => _bytes[position];
+
+  /// Skip past a list of characters. Defaults to skipping [isWhitespace].
+  String skipChars([CharPreciate skipChars]) {
+    if (skipChars == null) skipChars = isWhitespace;
+    var p = position; // use property for the error-checking
+    while (p < length) {
+      var c = _bytes[p];
+      if (!skipChars(c)) {
+        _position = p;
+        return c;
+      }
+      p += 1;
+    }
+    _position = p;
+    return null;
+  }
+
+  String skipUntil(CharPreciate untilChars) {
+    var p = position;
+    while (p < length) {
+      var c = _bytes[p];
+      if (untilChars(c)) {
+        _position = p;
+        return c;
+      }
+      p += 1;
+    }
+    return null;
+  }
+
+  /// Look for a sequence of bytes at the start of a string. If the bytes
+  /// are found return true and advance the position to the byte after the
+  /// match. Otherwise return false and leave the position alone.
+  bool matchBytes(String bytes) {
+    var p = position;
+    if (_bytes.length < p + bytes.length) {
+      return false;
+    }
+    var data = _bytes.substring(p, p + bytes.length);
+    if (data == bytes) {
+      position += bytes.length;
+      return true;
+    }
+    return false;
+  }
+
+  /// Look for the next sequence of bytes matching a given sequence. If
+  /// a match is found advance the position to the last byte of the match
+  bool jumpTo(String bytes) {
+    var newPosition = _bytes.indexOf(bytes, position);
+    if (newPosition >= 0) {
+      _position = newPosition + bytes.length - 1;
+      return true;
+    } else {
+      throw new StateError("No more elements");
+    }
+  }
+
+  String slice(int start, [int end]) {
+    if (end == null) end = length;
+    if (end < 0) end += length;
+    return _bytes.substring(start, end - start);
+  }
+}
+
+/// Mini parser for detecting character encoding from meta elements.
+class EncodingParser {
+  final EncodingBytes data;
+  String encoding;
+
+  /// [bytes] - the data to work on for encoding detection.
+  EncodingParser(List<int> bytes)
+      // Note: this is intentionally interpreting bytes as codepoints.
+      : data = new EncodingBytes(new String.fromCharCodes(bytes).toLowerCase());
+
+  String getEncoding() {
+    final methodDispatch = [
+      ["<!--", handleComment],
+      ["<meta", handleMeta],
+      ["</", handlePossibleEndTag],
+      ["<!", handleOther],
+      ["<?", handleOther],
+      ["<", handlePossibleStartTag]
+    ];
+
+    try {
+      for (;;) {
+        for (var dispatch in methodDispatch) {
+          if (data.matchBytes(dispatch[0])) {
+            var keepParsing = dispatch[1]();
+            if (keepParsing) break;
+
+            // We found an encoding. Stop.
+            return encoding;
+          }
+        }
+        data.position += 1;
+      }
+    } on StateError catch (e) {
+      // Catch this here to match behavior of Python's StopIteration
+      // TODO(jmesserly): refactor to not use exceptions
+    }
+    return encoding;
+  }
+
+  /// Skip over comments.
+  bool handleComment() => data.jumpTo("-->");
+
+  bool handleMeta() {
+    if (!isWhitespace(data.currentByte)) {
+      // if we have <meta not followed by a space so just keep going
+      return true;
+    }
+    // We have a valid meta element we want to search for attributes
+    while (true) {
+      // Try to find the next attribute after the current position
+      var attr = getAttribute();
+      if (attr == null) return true;
+
+      if (attr[0] == "charset") {
+        var tentativeEncoding = attr[1];
+        var codec = codecName(tentativeEncoding);
+        if (codec != null) {
+          encoding = codec;
+          return false;
+        }
+      } else if (attr[0] == "content") {
+        var contentParser = new ContentAttrParser(new EncodingBytes(attr[1]));
+        var tentativeEncoding = contentParser.parse();
+        var codec = codecName(tentativeEncoding);
+        if (codec != null) {
+          encoding = codec;
+          return false;
+        }
+      }
+    }
+    return true; // unreachable
+  }
+
+  bool handlePossibleStartTag() => handlePossibleTag(false);
+
+  bool handlePossibleEndTag() {
+    data.next();
+    return handlePossibleTag(true);
+  }
+
+  bool handlePossibleTag(bool endTag) {
+    if (!isLetter(data.currentByte)) {
+      //If the next byte is not an ascii letter either ignore this
+      //fragment (possible start tag case) or treat it according to
+      //handleOther
+      if (endTag) {
+        data.previous();
+        handleOther();
+      }
+      return true;
+    }
+
+    var c = data.skipUntil(isSpaceOrAngleBracket);
+    if (c == "<") {
+      // return to the first step in the overall "two step" algorithm
+      // reprocessing the < byte
+      data.previous();
+    } else {
+      //Read all attributes
+      var attr = getAttribute();
+      while (attr != null) {
+        attr = getAttribute();
+      }
+    }
+    return true;
+  }
+
+  bool handleOther() => data.jumpTo(">");
+
+  /// Return a name,value pair for the next attribute in the stream,
+  /// if one is found, or null
+  List<String> getAttribute() {
+    // Step 1 (skip chars)
+    var c = data.skipChars((x) => x == "/" || isWhitespace(x));
+    // Step 2
+    if (c == ">" || c == null) {
+      return null;
+    }
+    // Step 3
+    var attrName = [];
+    var attrValue = [];
+    // Step 4 attribute name
+    while (true) {
+      if (c == null) {
+        return null;
+      } else if (c == "=" && attrName.length > 0) {
+        break;
+      } else if (isWhitespace(c)) {
+        // Step 6!
+        c = data.skipChars();
+        c = data.next();
+        break;
+      } else if (c == "/" || c == ">") {
+        return [attrName.join(), ""];
+      } else if (isLetter(c)) {
+        attrName.add(c.toLowerCase());
+      } else {
+        attrName.add(c);
+      }
+      // Step 5
+      c = data.next();
+    }
+    // Step 7
+    if (c != "=") {
+      data.previous();
+      return [attrName.join(), ""];
+    }
+    // Step 8
+    data.next();
+    // Step 9
+    c = data.skipChars();
+    // Step 10
+    if (c == "'" || c == '"') {
+      // 10.1
+      var quoteChar = c;
+      while (true) {
+        // 10.2
+        c = data.next();
+        if (c == quoteChar) {
+          // 10.3
+          data.next();
+          return [attrName.join(), attrValue.join()];
+        } else if (isLetter(c)) {
+          // 10.4
+          attrValue.add(c.toLowerCase());
+        } else {
+          // 10.5
+          attrValue.add(c);
+        }
+      }
+    } else if (c == ">") {
+      return [attrName.join(), ""];
+    } else if (c == null) {
+      return null;
+    } else if (isLetter(c)) {
+      attrValue.add(c.toLowerCase());
+    } else {
+      attrValue.add(c);
+    }
+    // Step 11
+    while (true) {
+      c = data.next();
+      if (isSpaceOrAngleBracket(c)) {
+        return [attrName.join(), attrValue.join()];
+      } else if (c == null) {
+        return null;
+      } else if (isLetter(c)) {
+        attrValue.add(c.toLowerCase());
+      } else {
+        attrValue.add(c);
+      }
+    }
+    return null; // unreachable
+  }
+}
+
+class ContentAttrParser {
+  final EncodingBytes data;
+
+  ContentAttrParser(this.data);
+
+  String parse() {
+    try {
+      // Check if the attr name is charset
+      // otherwise return
+      data.jumpTo("charset");
+      data.position += 1;
+      data.skipChars();
+      if (data.currentByte != "=") {
+        // If there is no = sign keep looking for attrs
+        return null;
+      }
+      data.position += 1;
+      data.skipChars();
+      // Look for an encoding between matching quote marks
+      if (data.currentByte == '"' || data.currentByte == "'") {
+        var quoteMark = data.currentByte;
+        data.position += 1;
+        var oldPosition = data.position;
+        if (data.jumpTo(quoteMark)) {
+          return data.slice(oldPosition, data.position);
+        } else {
+          return null;
+        }
+      } else {
+        // Unquoted value
+        var oldPosition = data.position;
+        try {
+          data.skipUntil(isWhitespace);
+          return data.slice(oldPosition, data.position);
+        } on StateError catch (e) {
+          //Return the whole remaining value
+          return data.slice(oldPosition);
+        }
+      }
+    } on StateError catch (e) {
+      return null;
+    }
+  }
+}
+
+bool isSpaceOrAngleBracket(String char) {
+  return char == ">" || char == "<" || isWhitespace(char);
+}
+
+typedef bool CharPreciate(String char);
diff --git a/html/lib/src/inputstream.dart b/html/lib/src/inputstream.dart
new file mode 100644
index 0000000..cb40bc9
--- /dev/null
+++ b/html/lib/src/inputstream.dart
@@ -0,0 +1,318 @@
+library inputstream;
+
+import 'dart:collection';
+import 'package:utf/utf.dart';
+import 'package:source_span/source_span.dart';
+import 'char_encodings.dart';
+import 'constants.dart';
+import 'utils.dart';
+import 'encoding_parser.dart';
+
+/// Hooks to call into dart:io without directly referencing it.
+class ConsoleSupport {
+  List<int> bytesFromFile(source) => null;
+}
+
+// TODO(jmesserly): use lazy init here when supported.
+ConsoleSupport consoleSupport = new ConsoleSupport();
+
+/// Provides a unicode stream of characters to the HtmlTokenizer.
+///
+/// This class takes care of character encoding and removing or replacing
+/// incorrect byte-sequences and also provides column and line tracking.
+class HtmlInputStream {
+  /// Number of bytes to use when looking for a meta element with
+  /// encoding information.
+  static const int numBytesMeta = 512;
+
+  /// Encoding to use if no other information can be found.
+  static const String defaultEncoding = 'windows-1252';
+
+  /// The name of the character encoding.
+  String charEncodingName;
+
+  /// True if we are certain about [charEncodingName], false for tenative.
+  bool charEncodingCertain = true;
+
+  final bool generateSpans;
+
+  /// Location where the contents of the stream were found.
+  final String sourceUrl;
+
+  List<int> _rawBytes;
+
+  /// Raw UTF-16 codes, used if a Dart String is passed in.
+  Iterable<int> _rawChars;
+
+  Queue<String> errors;
+
+  SourceFile fileInfo;
+
+  List<int> _lineStarts;
+
+  List<int> _chars;
+
+  int _offset;
+
+  /// Initialises the HtmlInputStream.
+  ///
+  /// HtmlInputStream(source, [encoding]) -> Normalized stream from source
+  /// for use by html5lib.
+  ///
+  /// [source] can be either a [String] or a [List<int>] containing the raw
+  /// bytes, or a file if [consoleSupport] is initialized.
+  ///
+  /// The optional encoding parameter must be a string that indicates
+  /// the encoding.  If specified, that encoding will be used,
+  /// regardless of any BOM or later declaration (such as in a meta
+  /// element)
+  ///
+  /// [parseMeta] - Look for a <meta> element containing encoding information
+  HtmlInputStream(source, [String encoding, bool parseMeta = true,
+      this.generateSpans = false, this.sourceUrl])
+      : charEncodingName = codecName(encoding) {
+    if (source is String) {
+      _rawChars = toCodepoints(source);
+      charEncodingName = 'utf-8';
+      charEncodingCertain = true;
+    } else if (source is List<int>) {
+      _rawBytes = source;
+    } else {
+      // TODO(jmesserly): it's unfortunate we need to read all bytes in advance,
+      // but it's necessary because of how the UTF decoders work.
+      _rawBytes = consoleSupport.bytesFromFile(source);
+
+      if (_rawBytes == null) {
+        // TODO(jmesserly): we should accept some kind of stream API too.
+        // Unfortunately dart:io InputStream is async only, which won't work.
+        throw new ArgumentError("'source' must be a String or "
+            "List<int> (of bytes). You can also pass a RandomAccessFile if you"
+            "`import 'package:html/parser_console.dart'` and call "
+            "`useConsole()`.");
+      }
+    }
+
+    // Detect encoding iff no explicit "transport level" encoding is supplied
+    if (charEncodingName == null) {
+      detectEncoding(parseMeta);
+    }
+
+    reset();
+  }
+
+  void reset() {
+    errors = new Queue<String>();
+
+    _offset = 0;
+    _lineStarts = <int>[0];
+    _chars = <int>[];
+
+    if (_rawChars == null) {
+      _rawChars = decodeBytes(charEncodingName, _rawBytes);
+    }
+
+    bool skipNewline = false;
+    for (var c in _rawChars) {
+      if (skipNewline) {
+        skipNewline = false;
+        if (c == NEWLINE) continue;
+      }
+
+      if (invalidUnicode(c)) errors.add('invalid-codepoint');
+
+      if (0xD800 <= c && c <= 0xDFFF) {
+        c = 0xFFFD;
+      } else if (c == RETURN) {
+        skipNewline = true;
+        c = NEWLINE;
+      }
+
+      _chars.add(c);
+      if (c == NEWLINE) _lineStarts.add(_chars.length);
+    }
+
+    // Free decoded characters if they aren't needed anymore.
+    if (_rawBytes != null) _rawChars = null;
+
+    // TODO(sigmund): Don't parse the file at all if spans aren't being
+    // generated.
+    fileInfo = new SourceFile.decoded(_chars, url: sourceUrl);
+  }
+
+  void detectEncoding([bool parseMeta = true]) {
+    // First look for a BOM
+    // This will also read past the BOM if present
+    charEncodingName = detectBOM();
+    charEncodingCertain = true;
+
+    // If there is no BOM need to look for meta elements with encoding
+    // information
+    if (charEncodingName == null && parseMeta) {
+      charEncodingName = detectEncodingMeta();
+      charEncodingCertain = false;
+    }
+    // If all else fails use the default encoding
+    if (charEncodingName == null) {
+      charEncodingCertain = false;
+      charEncodingName = defaultEncoding;
+    }
+
+    // Substitute for equivalent encodings:
+    if (charEncodingName.toLowerCase() == 'iso-8859-1') {
+      charEncodingName = 'windows-1252';
+    }
+  }
+
+  void changeEncoding(String newEncoding) {
+    if (_rawBytes == null) {
+      // We should never get here -- if encoding is certain we won't try to
+      // change it.
+      throw new StateError('cannot change encoding when parsing a String.');
+    }
+
+    newEncoding = codecName(newEncoding);
+    if (const ['utf-16', 'utf-16-be', 'utf-16-le'].contains(newEncoding)) {
+      newEncoding = 'utf-8';
+    }
+    if (newEncoding == null) {
+      return;
+    } else if (newEncoding == charEncodingName) {
+      charEncodingCertain = true;
+    } else {
+      charEncodingName = newEncoding;
+      charEncodingCertain = true;
+      _rawChars = null;
+      reset();
+      throw new ReparseException(
+          'Encoding changed from $charEncodingName to $newEncoding');
+    }
+  }
+
+  /// Attempts to detect at BOM at the start of the stream. If
+  /// an encoding can be determined from the BOM return the name of the
+  /// encoding otherwise return null.
+  String detectBOM() {
+    // Try detecting the BOM using bytes from the string
+    if (hasUtf8Bom(_rawBytes)) {
+      return 'utf-8';
+    }
+    // Note: we don't need to remember whether it was big or little endian
+    // because the decoder will do that later. It will also eat the BOM for us.
+    if (hasUtf16Bom(_rawBytes)) {
+      return 'utf-16';
+    }
+    if (hasUtf32Bom(_rawBytes)) {
+      return 'utf-32';
+    }
+    return null;
+  }
+
+  /// Report the encoding declared by the meta element.
+  String detectEncodingMeta() {
+    var parser = new EncodingParser(slice(_rawBytes, 0, numBytesMeta));
+    var encoding = parser.getEncoding();
+
+    if (const ['utf-16', 'utf-16-be', 'utf-16-le'].contains(encoding)) {
+      encoding = 'utf-8';
+    }
+
+    return encoding;
+  }
+
+  /// Returns the current offset in the stream, i.e. the number of codepoints
+  /// since the start of the file.
+  int get position => _offset;
+
+  /// Read one character from the stream or queue if available. Return
+  /// EOF when EOF is reached.
+  String char() {
+    if (_offset >= _chars.length) return EOF;
+    return new String.fromCharCodes([_chars[_offset++]]);
+  }
+
+  String peekChar() {
+    if (_offset >= _chars.length) return EOF;
+    return new String.fromCharCodes([_chars[_offset]]);
+  }
+
+  /// Returns a string of characters from the stream up to but not
+  /// including any character in 'characters' or EOF.
+  String charsUntil(String characters, [bool opposite = false]) {
+    int start = _offset;
+    String c;
+    while ((c = peekChar()) != null && characters.contains(c) == opposite) {
+      _offset++;
+    }
+
+    return new String.fromCharCodes(_chars.sublist(start, _offset));
+  }
+
+  void unget(String ch) {
+    // Only one character is allowed to be ungotten at once - it must
+    // be consumed again before any further call to unget
+    if (ch != null) {
+      _offset--;
+      assert(peekChar() == ch);
+    }
+  }
+}
+
+// TODO(jmesserly): the Python code used a regex to check for this. But
+// Dart doesn't let you create a regexp with invalid characters.
+bool invalidUnicode(int c) {
+  if (0x0001 <= c && c <= 0x0008) return true;
+  if (0x000E <= c && c <= 0x001F) return true;
+  if (0x007F <= c && c <= 0x009F) return true;
+  if (0xD800 <= c && c <= 0xDFFF) return true;
+  if (0xFDD0 <= c && c <= 0xFDEF) return true;
+  switch (c) {
+    case 0x000B:
+    case 0xFFFE:
+    case 0xFFFF:
+    case 0x01FFFE:
+    case 0x01FFFF:
+    case 0x02FFFE:
+    case 0x02FFFF:
+    case 0x03FFFE:
+    case 0x03FFFF:
+    case 0x04FFFE:
+    case 0x04FFFF:
+    case 0x05FFFE:
+    case 0x05FFFF:
+    case 0x06FFFE:
+    case 0x06FFFF:
+    case 0x07FFFE:
+    case 0x07FFFF:
+    case 0x08FFFE:
+    case 0x08FFFF:
+    case 0x09FFFE:
+    case 0x09FFFF:
+    case 0x0AFFFE:
+    case 0x0AFFFF:
+    case 0x0BFFFE:
+    case 0x0BFFFF:
+    case 0x0CFFFE:
+    case 0x0CFFFF:
+    case 0x0DFFFE:
+    case 0x0DFFFF:
+    case 0x0EFFFE:
+    case 0x0EFFFF:
+    case 0x0FFFFE:
+    case 0x0FFFFF:
+    case 0x10FFFE:
+    case 0x10FFFF:
+      return true;
+  }
+  return false;
+}
+
+/// Return the python codec name corresponding to an encoding or null if the
+/// string doesn't correspond to a valid encoding.
+String codecName(String encoding) {
+  final asciiPunctuation = new RegExp(
+      "[\u0009-\u000D\u0020-\u002F\u003A-\u0040\u005B-\u0060\u007B-\u007E]");
+
+  if (encoding == null) return null;
+  var canonicalName = encoding.replaceAll(asciiPunctuation, '').toLowerCase();
+  return encodings[canonicalName];
+}
diff --git a/html/lib/src/list_proxy.dart b/html/lib/src/list_proxy.dart
new file mode 100644
index 0000000..99acdb6
--- /dev/null
+++ b/html/lib/src/list_proxy.dart
@@ -0,0 +1,105 @@
+// TODO(jmesserly): remove this once we have a subclassable growable list
+// in our libraries.
+
+/// A [List] proxy that you can subclass.
+library list_proxy;
+
+import 'dart:collection';
+import 'dart:math' show Random;
+
+// TOOD(jmesserly): this needs to be removed, but fixing NodeList is tricky.
+class ListProxy<E> extends IterableBase<E> implements List<E> {
+
+  /// The inner [List<T>] with the actual storage.
+  final List<E> _list;
+
+  /// Creates a list proxy.
+  /// You can optionally specify the list to use for [storage] of the items,
+  /// otherwise this will create a [List<E>].
+  ListProxy([List<E> storage]) : _list = storage != null ? storage : <E>[];
+
+  // TODO(jmesserly): This should be on List.
+  // See http://code.google.com/p/dart/issues/detail?id=947
+  bool remove(E item) {
+    int i = indexOf(item);
+    if (i == -1) return false;
+    removeAt(i);
+    return true;
+  }
+
+  void insert(int index, E item) => _list.insert(index, item);
+
+  // Override from Iterable to fix performance
+  // Length and last become O(1) instead of O(N)
+  // The others are just different constant factor.
+  int get length => _list.length;
+  E get last => _list.last;
+  E get first => _list.first;
+  E get single => _list.single;
+
+  // From Iterable
+  Iterator<E> get iterator => _list.iterator;
+
+  // From List
+  E operator [](int index) => _list[index];
+  operator []=(int index, E value) {
+    _list[index] = value;
+  }
+  set length(int value) {
+    _list.length = value;
+  }
+  void add(E value) {
+    _list.add(value);
+  }
+
+  void addLast(E value) {
+    add(value);
+  }
+  void addAll(Iterable<E> collection) {
+    _list.addAll(collection);
+  }
+  void sort([int compare(E a, E b)]) {
+    _list.sort(compare);
+  }
+  void shuffle([Random random]) {
+    _list.shuffle(random);
+  }
+
+  int indexOf(E element, [int start = 0]) => _list.indexOf(element, start);
+  int lastIndexOf(E element, [int start]) => _list.lastIndexOf(element, start);
+  void clear() {
+    _list.clear();
+  }
+
+  E removeAt(int index) => _list.removeAt(index);
+  E removeLast() => _list.removeLast();
+
+  void removeWhere(bool test(E element)) => _list.removeWhere(test);
+  void retainWhere(bool test(E element)) => _list.retainWhere(test);
+
+  List<E> sublist(int start, [int end]) => _list.sublist(start, end);
+
+  List<E> getRange(int start, int end) => _list.getRange(start, end);
+
+  void setRange(int start, int length, List<E> from, [int startFrom = 0]) {
+    _list.setRange(start, length, from, startFrom);
+  }
+  void removeRange(int start, int length) {
+    _list.removeRange(start, length);
+  }
+  void insertAll(int index, Iterable<E> iterable) {
+    _list.insertAll(index, iterable);
+  }
+
+  Iterable<E> get reversed => _list.reversed;
+
+  Map<int, E> asMap() => _list.asMap();
+
+  void replaceRange(int start, int end, Iterable<E> newContents) =>
+      _list.replaceRange(start, end, newContents);
+
+  void setAll(int index, Iterable<E> iterable) => _list.setAll(index, iterable);
+
+  void fillRange(int start, int end, [E fillValue]) =>
+      _list.fillRange(start, end, fillValue);
+}
diff --git a/html/lib/src/query_selector.dart b/html/lib/src/query_selector.dart
new file mode 100644
index 0000000..e586d1a
--- /dev/null
+++ b/html/lib/src/query_selector.dart
@@ -0,0 +1,293 @@
+/// Query selector implementation for our DOM.
+library html.src.query;
+
+import 'package:csslib/parser.dart' as css;
+import 'package:csslib/parser.dart' show TokenKind;
+import 'package:csslib/visitor.dart'; // the CSSOM
+import 'package:html/dom.dart';
+import 'package:html/src/constants.dart' show isWhitespaceCC;
+
+bool matches(Node node, String selector) =>
+    new SelectorEvaluator().matches(node, _parseSelectorList(selector));
+
+Element querySelector(Node node, String selector) =>
+    new SelectorEvaluator().querySelector(node, _parseSelectorList(selector));
+
+List<Element> querySelectorAll(Node node, String selector) {
+  var results = [];
+  new SelectorEvaluator().querySelectorAll(
+      node, _parseSelectorList(selector), results);
+  return results;
+}
+
+// http://dev.w3.org/csswg/selectors-4/#grouping
+SelectorGroup _parseSelectorList(String selector) {
+  var errors = [];
+  var group = css.parseSelectorGroup(selector, errors: errors);
+  if (group == null || errors.isNotEmpty) {
+    throw new FormatException("'$selector' is not a valid selector: $errors");
+  }
+  return group;
+}
+
+class SelectorEvaluator extends Visitor {
+  /// The current HTML element to match against.
+  Element _element;
+
+  bool matches(Element element, SelectorGroup selector) {
+    _element = element;
+    return visitSelectorGroup(selector);
+  }
+
+  Element querySelector(Node root, SelectorGroup selector) {
+    for (var node in root.nodes) {
+      if (node is! Element) continue;
+      if (matches(node, selector)) return node;
+      var result = querySelector(node, selector);
+      if (result != null) return result;
+    }
+    return null;
+  }
+
+  void querySelectorAll(
+      Node root, SelectorGroup selector, List<Element> results) {
+    for (var node in root.nodes) {
+      if (node is! Element) continue;
+      if (matches(node, selector)) results.add(node);
+      querySelectorAll(node, selector, results);
+    }
+  }
+
+  bool visitSelectorGroup(SelectorGroup group) =>
+      group.selectors.any(visitSelector);
+
+  bool visitSelector(Selector selector) {
+    var old = _element;
+    var result = true;
+
+    // Note: evaluate selectors right-to-left as it's more efficient.
+    int combinator = null;
+    for (var s in selector.simpleSelectorSequences.reversed) {
+      if (combinator == null) {
+        result = s.simpleSelector.visit(this);
+      } else if (combinator == TokenKind.COMBINATOR_DESCENDANT) {
+        // descendant combinator
+        // http://dev.w3.org/csswg/selectors-4/#descendant-combinators
+        do {
+          _element = _element.parent;
+        } while (_element != null && !s.simpleSelector.visit(this));
+
+        if (_element == null) result = false;
+      } else if (combinator == TokenKind.COMBINATOR_TILDE) {
+        // Following-sibling combinator
+        // http://dev.w3.org/csswg/selectors-4/#general-sibling-combinators
+        do {
+          _element = _element.previousElementSibling;
+        } while (_element != null && !s.simpleSelector.visit(this));
+
+        if (_element == null) result = false;
+      }
+
+      if (!result) break;
+
+      switch (s.combinator) {
+        case TokenKind.COMBINATOR_PLUS:
+          // Next-sibling combinator
+          // http://dev.w3.org/csswg/selectors-4/#adjacent-sibling-combinators
+          _element = _element.previousElementSibling;
+          break;
+        case TokenKind.COMBINATOR_GREATER:
+          // Child combinator
+          // http://dev.w3.org/csswg/selectors-4/#child-combinators
+          _element = _element.parent;
+          break;
+        case TokenKind.COMBINATOR_DESCENDANT:
+        case TokenKind.COMBINATOR_TILDE:
+          // We need to iterate through all siblings or parents.
+          // For now, just remember what the combinator was.
+          combinator = s.combinator;
+          break;
+        case TokenKind.COMBINATOR_NONE:
+          break;
+        default:
+          throw _unsupported(selector);
+      }
+
+      if (_element == null) {
+        result = false;
+        break;
+      }
+    }
+
+    _element = old;
+    return result;
+  }
+
+  _unimplemented(SimpleSelector selector) => new UnimplementedError(
+      "'$selector' selector of type "
+      "${selector.runtimeType} is not implemented");
+
+  _unsupported(selector) =>
+      new FormatException("'$selector' is not a valid selector");
+
+  bool visitPseudoClassSelector(PseudoClassSelector selector) {
+    switch (selector.name) {
+      // http://dev.w3.org/csswg/selectors-4/#structural-pseudos
+
+      // http://dev.w3.org/csswg/selectors-4/#the-root-pseudo
+      case 'root':
+        // TODO(jmesserly): fix when we have a .ownerDocument pointer
+        // return _element == _element.ownerDocument.rootElement;
+        return _element.localName == 'html' && _element.parentNode == null;
+
+      // http://dev.w3.org/csswg/selectors-4/#the-empty-pseudo
+      case 'empty':
+        return _element.nodes
+            .any((n) => !(n is Element || n is Text && n.text.isNotEmpty));
+
+      // http://dev.w3.org/csswg/selectors-4/#the-blank-pseudo
+      case 'blank':
+        return _element.nodes.any((n) => !(n is Element ||
+            n is Text && n.text.runes.any((r) => !isWhitespaceCC(r))));
+
+      // http://dev.w3.org/csswg/selectors-4/#the-first-child-pseudo
+      case 'first-child':
+        return _element.previousElementSibling == null;
+
+      // http://dev.w3.org/csswg/selectors-4/#the-last-child-pseudo
+      case 'last-child':
+        return _element.nextElementSibling == null;
+
+      // http://dev.w3.org/csswg/selectors-4/#the-only-child-pseudo
+      case 'only-child':
+        return _element.previousElementSibling == null &&
+            _element.nextElementSibling == null;
+
+      // http://dev.w3.org/csswg/selectors-4/#link
+      case 'link':
+        return _element.attributes['href'] != null;
+
+      case 'visited':
+        // Always return false since we aren't a browser. This is allowed per:
+        // http://dev.w3.org/csswg/selectors-4/#visited-pseudo
+        return false;
+    }
+
+    // :before, :after, :first-letter/line can't match DOM elements.
+    if (_isLegacyPsuedoClass(selector.name)) return false;
+
+    throw _unimplemented(selector);
+  }
+
+  bool visitPseudoElementSelector(PseudoElementSelector selector) {
+    // :before, :after, :first-letter/line can't match DOM elements.
+    if (_isLegacyPsuedoClass(selector.name)) return false;
+
+    throw _unimplemented(selector);
+  }
+
+  static bool _isLegacyPsuedoClass(String name) {
+    switch (name) {
+      case 'before':
+      case 'after':
+      case 'first-line':
+      case 'first-letter':
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  bool visitPseudoElementFunctionSelector(PseudoElementFunctionSelector s) =>
+      throw _unimplemented(s);
+
+  bool visitPseudoClassFunctionSelector(PseudoClassFunctionSelector selector) {
+    switch (selector.name) {
+      // http://dev.w3.org/csswg/selectors-4/#child-index
+
+      // http://dev.w3.org/csswg/selectors-4/#the-nth-child-pseudo
+      case 'nth-child':
+        // TODO(jmesserly): support An+B syntax too.
+        var exprs = selector.expression.expressions;
+        if (exprs.length == 1 && exprs[0] is LiteralTerm) {
+          LiteralTerm literal = exprs[0];
+          var parent = _element.parentNode;
+          return parent != null &&
+              literal.value > 0 &&
+              parent.nodes.indexOf(_element) == literal.value;
+        }
+        break;
+
+      // http://dev.w3.org/csswg/selectors-4/#the-lang-pseudo
+      case 'lang':
+        // TODO(jmesserly): shouldn't need to get the raw text here, but csslib
+        // gets confused by the "-" in the expression, such as in "es-AR".
+        var toMatch = selector.expression.span.text;
+        var lang = _getInheritedLanguage(_element);
+        // TODO(jmesserly): implement wildcards in level 4
+        return lang != null && lang.startsWith(toMatch);
+    }
+    throw _unimplemented(selector);
+  }
+
+  static String _getInheritedLanguage(Node node) {
+    while (node != null) {
+      var lang = node.attributes['lang'];
+      if (lang != null) return lang;
+      node = node.parent;
+    }
+    return null;
+  }
+
+  bool visitNamespaceSelector(NamespaceSelector selector) {
+    // Match element tag name
+    if (!selector.nameAsSimpleSelector.visit(this)) return false;
+
+    if (selector.isNamespaceWildcard) return true;
+
+    if (selector.namespace == '') return _element.namespaceUri == null;
+
+    throw _unimplemented(selector);
+  }
+
+  bool visitElementSelector(ElementSelector selector) =>
+      selector.isWildcard || _element.localName == selector.name.toLowerCase();
+
+  bool visitIdSelector(IdSelector selector) => _element.id == selector.name;
+
+  bool visitClassSelector(ClassSelector selector) =>
+      _element.classes.contains(selector.name);
+
+  // TODO(jmesserly): negation should support any selectors in level 4,
+  // not just simple selectors.
+  // http://dev.w3.org/csswg/selectors-4/#negation
+  bool visitNegationSelector(NegationSelector selector) =>
+      !selector.negationArg.visit(this);
+
+  bool visitAttributeSelector(AttributeSelector selector) {
+    // Match name first
+    var value = _element.attributes[selector.name.toLowerCase()];
+    if (value == null) return false;
+
+    if (selector.operatorKind == TokenKind.NO_MATCH) return true;
+
+    var select = '${selector.value}';
+    switch (selector.operatorKind) {
+      case TokenKind.EQUALS:
+        return value == select;
+      case TokenKind.INCLUDES:
+        return value.split(' ').any((v) => v.isNotEmpty && v == select);
+      case TokenKind.DASH_MATCH:
+        return value.startsWith(select) &&
+            (value.length == select.length || value[select.length] == '-');
+      case TokenKind.PREFIX_MATCH:
+        return value.startsWith(select);
+      case TokenKind.SUFFIX_MATCH:
+        return value.endsWith(select);
+      case TokenKind.SUBSTRING_MATCH:
+        return value.contains(select);
+      default:
+        throw _unsupported(selector);
+    }
+  }
+}
diff --git a/html/lib/src/token.dart b/html/lib/src/token.dart
new file mode 100644
index 0000000..9a2843c
--- /dev/null
+++ b/html/lib/src/token.dart
@@ -0,0 +1,141 @@
+/// This library contains token types used by the html5 tokenizer.
+library token;
+
+import 'dart:collection';
+import 'package:source_span/source_span.dart';
+
+/// An html5 token.
+abstract class Token {
+  FileSpan span;
+
+  int get kind;
+}
+
+abstract class TagToken extends Token {
+  String name;
+
+  bool selfClosing;
+
+  TagToken(this.name, this.selfClosing);
+}
+
+class StartTagToken extends TagToken {
+  /// The tag's attributes. A map from the name to the value, where the name
+  /// can be a [String] or [AttributeName].
+  LinkedHashMap<dynamic, String> data;
+
+  /// The attribute spans if requested. Otherwise null.
+  List<TagAttribute> attributeSpans;
+
+  bool selfClosingAcknowledged;
+
+  /// The namespace. This is filled in later during tree building.
+  String namespace;
+
+  StartTagToken(String name, {this.data, bool selfClosing: false,
+      this.selfClosingAcknowledged: false, this.namespace})
+      : super(name, selfClosing);
+
+  int get kind => TokenKind.startTag;
+}
+
+class EndTagToken extends TagToken {
+  EndTagToken(String name, {bool selfClosing: false})
+      : super(name, selfClosing);
+
+  int get kind => TokenKind.endTag;
+}
+
+abstract class StringToken extends Token {
+  StringBuffer _buffer;
+
+  String _string;
+  String get data {
+    if (_string == null) {
+      _string = _buffer.toString();
+      _buffer = null;
+    }
+    return _string;
+  }
+
+  StringToken(string)
+      : _string = string,
+        _buffer = string == null ? new StringBuffer() : null;
+
+  StringToken add(String data) {
+    _buffer.write(data);
+    return this;
+  }
+}
+
+class ParseErrorToken extends StringToken {
+  /// Extra information that goes along with the error message.
+  Map messageParams;
+
+  ParseErrorToken(String data, {this.messageParams}) : super(data);
+
+  int get kind => TokenKind.parseError;
+}
+
+class CharactersToken extends StringToken {
+  CharactersToken([String data]) : super(data);
+
+  int get kind => TokenKind.characters;
+
+  /// Replaces the token's [data]. This should only be used to wholly replace
+  /// data, not to append data.
+  void replaceData(String newData) {
+    _string = newData;
+    _buffer = null;
+  }
+}
+
+class SpaceCharactersToken extends StringToken {
+  SpaceCharactersToken([String data]) : super(data);
+
+  int get kind => TokenKind.spaceCharacters;
+}
+
+class CommentToken extends StringToken {
+  CommentToken([String data]) : super(data);
+
+  int get kind => TokenKind.comment;
+}
+
+class DoctypeToken extends Token {
+  String publicId;
+  String systemId;
+  String name = "";
+  bool correct;
+
+  DoctypeToken({this.publicId, this.systemId, this.correct: false});
+
+  int get kind => TokenKind.doctype;
+}
+
+/// These are used by the tokenizer to build up the attribute map.
+/// They're also used by [StartTagToken.attributeSpans] if attribute spans are
+/// requested.
+class TagAttribute {
+  String name;
+  String value;
+
+  // The spans of the attribute. This is not used unless we are computing an
+  // attribute span on demand.
+  int start;
+  int end;
+  int startValue;
+  int endValue;
+
+  TagAttribute();
+}
+
+class TokenKind {
+  static const int spaceCharacters = 0;
+  static const int characters = 1;
+  static const int startTag = 2;
+  static const int endTag = 3;
+  static const int comment = 4;
+  static const int doctype = 5;
+  static const int parseError = 6;
+}
diff --git a/html/lib/src/tokenizer.dart b/html/lib/src/tokenizer.dart
new file mode 100644
index 0000000..3273858
--- /dev/null
+++ b/html/lib/src/tokenizer.dart
@@ -0,0 +1,1908 @@
+library tokenizer;
+
+import 'dart:collection';
+import 'package:html/parser.dart' show HtmlParser;
+import 'constants.dart';
+import 'inputstream.dart';
+import 'token.dart';
+import 'utils.dart';
+
+// Group entities by their first character, for faster lookups
+
+// TODO(jmesserly): we could use a better data structure here like a trie, if
+// we had it implemented in Dart.
+Map<String, List<String>> entitiesByFirstChar = (() {
+  var result = {};
+  for (var k in entities.keys) {
+    result.putIfAbsent(k[0], () => []).add(k);
+  }
+  return result;
+})();
+
+// TODO(jmesserly): lots of ways to make this faster:
+// - use char codes everywhere instead of 1-char strings
+// - use switch instead of contains, indexOf
+// - use switch instead of the sequential if tests
+// - avoid string concat
+
+/// This class takes care of tokenizing HTML.
+class HtmlTokenizer implements Iterator<Token> {
+  // TODO(jmesserly): a lot of these could be made private
+
+  final HtmlInputStream stream;
+
+  final bool lowercaseElementName;
+
+  final bool lowercaseAttrName;
+
+  /// True to generate spans in for [Token.span].
+  final bool generateSpans;
+
+  /// True to generate spans for attributes.
+  final bool attributeSpans;
+
+  /// This reference to the parser is used for correct CDATA handling.
+  /// The [HtmlParser] will set this at construction time.
+  HtmlParser parser;
+
+  final Queue<Token> tokenQueue;
+
+  /// Holds the token that is currently being processed.
+  Token currentToken;
+
+  /// Holds a reference to the method to be invoked for the next parser state.
+  // TODO(jmesserly): the type should be "Predicate" but a dart2js checked mode
+  // bug prevents us from doing that. See http://dartbug.com/12465
+  Function state;
+
+  final StringBuffer _buffer = new StringBuffer();
+
+  int _lastOffset;
+
+  // TODO(jmesserly): ideally this would be a LinkedHashMap and we wouldn't add
+  // an item until it's ready. But the code doesn't have a clear notion of when
+  // it's "done" with the attribute.
+  List<TagAttribute> _attributes;
+  Set<String> _attributeNames;
+
+  HtmlTokenizer(doc, {String encoding, bool parseMeta: true,
+      this.lowercaseElementName: true, this.lowercaseAttrName: true,
+      bool generateSpans: false, String sourceUrl, this.attributeSpans: false})
+      : stream = new HtmlInputStream(
+          doc, encoding, parseMeta, generateSpans, sourceUrl),
+        tokenQueue = new Queue(),
+        generateSpans = generateSpans {
+    reset();
+  }
+
+  TagToken get currentTagToken => currentToken;
+  DoctypeToken get currentDoctypeToken => currentToken;
+  StringToken get currentStringToken => currentToken;
+
+  Token _current;
+  Token get current => _current;
+
+  final StringBuffer _attributeName = new StringBuffer();
+  final StringBuffer _attributeValue = new StringBuffer();
+
+  void _markAttributeEnd(int offset) {
+    _attributes.last.value = '$_attributeValue';
+    if (attributeSpans) _attributes.last.end = stream.position + offset;
+  }
+
+  void _markAttributeValueStart(int offset) {
+    if (attributeSpans) _attributes.last.startValue = stream.position + offset;
+  }
+
+  void _markAttributeValueEnd(int offset) {
+    if (attributeSpans) _attributes.last.endValue = stream.position + offset;
+    _markAttributeEnd(offset);
+  }
+
+  // Note: we could track the name span here, if we need it.
+  void _markAttributeNameEnd(int offset) => _markAttributeEnd(offset);
+
+  void _addAttribute(String name) {
+    if (_attributes == null) _attributes = [];
+    _attributeName.clear();
+    _attributeName.write(name);
+    _attributeValue.clear();
+    var attr = new TagAttribute();
+    _attributes.add(attr);
+    if (attributeSpans) attr.start = stream.position - name.length;
+  }
+
+  /// This is where the magic happens.
+  ///
+  /// We do our usually processing through the states and when we have a token
+  /// to return we yield the token which pauses processing until the next token
+  /// is requested.
+  bool moveNext() {
+    // Start processing. When EOF is reached state will return false;
+    // instead of true and the loop will terminate.
+    while (stream.errors.length == 0 && tokenQueue.length == 0) {
+      if (!state()) {
+        _current = null;
+        return false;
+      }
+    }
+    if (stream.errors.length > 0) {
+      _current = new ParseErrorToken(stream.errors.removeFirst());
+    } else {
+      assert(tokenQueue.length > 0);
+      _current = tokenQueue.removeFirst();
+    }
+    return true;
+  }
+
+  /// Resets the tokenizer state. Calling this does not reset the [stream] or
+  /// the [parser].
+  void reset() {
+    _lastOffset = 0;
+    tokenQueue.clear();
+    currentToken = null;
+    _buffer.clear();
+    _attributes = null;
+    _attributeNames = null;
+    state = dataState;
+  }
+
+  /// Adds a token to the queue. Sets the span if needed.
+  void _addToken(Token token) {
+    if (generateSpans && token.span == null) {
+      int offset = stream.position;
+      token.span = stream.fileInfo.span(_lastOffset, offset);
+      if (token is! ParseErrorToken) {
+        _lastOffset = offset;
+      }
+    }
+    tokenQueue.add(token);
+  }
+
+  /// This function returns either U+FFFD or the character based on the
+  /// decimal or hexadecimal representation. It also discards ";" if present.
+  /// If not present it will add a [ParseErrorToken].
+  String consumeNumberEntity(bool isHex) {
+    var allowed = isDigit;
+    var radix = 10;
+    if (isHex) {
+      allowed = isHexDigit;
+      radix = 16;
+    }
+
+    var charStack = [];
+
+    // Consume all the characters that are in range while making sure we
+    // don't hit an EOF.
+    var c = stream.char();
+    while (allowed(c) && c != EOF) {
+      charStack.add(c);
+      c = stream.char();
+    }
+
+    // Convert the set of characters consumed to an int.
+    var charAsInt = parseIntRadix(charStack.join(), radix);
+
+    // Certain characters get replaced with others
+    var char = replacementCharacters[charAsInt];
+    if (char != null) {
+      _addToken(new ParseErrorToken("illegal-codepoint-for-numeric-entity",
+          messageParams: {"charAsInt": charAsInt}));
+    } else if ((0xD800 <= charAsInt && charAsInt <= 0xDFFF) ||
+        (charAsInt > 0x10FFFF)) {
+      char = "\uFFFD";
+      _addToken(new ParseErrorToken("illegal-codepoint-for-numeric-entity",
+          messageParams: {"charAsInt": charAsInt}));
+    } else {
+      // Should speed up this check somehow (e.g. move the set to a constant)
+      if ((0x0001 <= charAsInt && charAsInt <= 0x0008) ||
+          (0x000E <= charAsInt && charAsInt <= 0x001F) ||
+          (0x007F <= charAsInt && charAsInt <= 0x009F) ||
+          (0xFDD0 <= charAsInt && charAsInt <= 0xFDEF) ||
+          const [
+        0x000B,
+        0xFFFE,
+        0xFFFF,
+        0x1FFFE,
+        0x1FFFF,
+        0x2FFFE,
+        0x2FFFF,
+        0x3FFFE,
+        0x3FFFF,
+        0x4FFFE,
+        0x4FFFF,
+        0x5FFFE,
+        0x5FFFF,
+        0x6FFFE,
+        0x6FFFF,
+        0x7FFFE,
+        0x7FFFF,
+        0x8FFFE,
+        0x8FFFF,
+        0x9FFFE,
+        0x9FFFF,
+        0xAFFFE,
+        0xAFFFF,
+        0xBFFFE,
+        0xBFFFF,
+        0xCFFFE,
+        0xCFFFF,
+        0xDFFFE,
+        0xDFFFF,
+        0xEFFFE,
+        0xEFFFF,
+        0xFFFFE,
+        0xFFFFF,
+        0x10FFFE,
+        0x10FFFF
+      ].contains(charAsInt)) {
+        _addToken(new ParseErrorToken("illegal-codepoint-for-numeric-entity",
+            messageParams: {"charAsInt": charAsInt}));
+      }
+      char = new String.fromCharCodes([charAsInt]);
+    }
+
+    // Discard the ; if present. Otherwise, put it back on the queue and
+    // invoke parseError on parser.
+    if (c != ";") {
+      _addToken(new ParseErrorToken("numeric-entity-without-semicolon"));
+      stream.unget(c);
+    }
+    return char;
+  }
+
+  void consumeEntity({String allowedChar, bool fromAttribute: false}) {
+    // Initialise to the default output for when no entity is matched
+    var output = "&";
+
+    var charStack = [stream.char()];
+    if (isWhitespace(charStack[0]) ||
+        charStack[0] == '<' ||
+        charStack[0] == '&' ||
+        charStack[0] == EOF ||
+        allowedChar == charStack[0]) {
+      stream.unget(charStack[0]);
+    } else if (charStack[0] == "#") {
+      // Read the next character to see if it's hex or decimal
+      bool hex = false;
+      charStack.add(stream.char());
+      if (charStack.last == 'x' || charStack.last == 'X') {
+        hex = true;
+        charStack.add(stream.char());
+      }
+
+      // charStack.last should be the first digit
+      if (hex && isHexDigit(charStack.last) ||
+          (!hex && isDigit(charStack.last))) {
+        // At least one digit found, so consume the whole number
+        stream.unget(charStack.last);
+        output = consumeNumberEntity(hex);
+      } else {
+        // No digits found
+        _addToken(new ParseErrorToken("expected-numeric-entity"));
+        stream.unget(charStack.removeLast());
+        output = "&${charStack.join()}";
+      }
+    } else {
+      // At this point in the process might have named entity. Entities
+      // are stored in the global variable "entities".
+      //
+      // Consume characters and compare to these to a substring of the
+      // entity names in the list until the substring no longer matches.
+      var filteredEntityList = entitiesByFirstChar[charStack[0]];
+      if (filteredEntityList == null) filteredEntityList = const [];
+
+      while (charStack.last != EOF) {
+        var name = charStack.join();
+        filteredEntityList =
+            filteredEntityList.where((e) => e.startsWith(name)).toList();
+
+        if (filteredEntityList.length == 0) {
+          break;
+        }
+        charStack.add(stream.char());
+      }
+
+      // At this point we have a string that starts with some characters
+      // that may match an entity
+      String entityName = null;
+
+      // Try to find the longest entity the string will match to take care
+      // of &noti for instance.
+
+      int entityLen;
+      for (entityLen = charStack.length - 1; entityLen > 1; entityLen--) {
+        var possibleEntityName = charStack.sublist(0, entityLen).join();
+        if (entities.containsKey(possibleEntityName)) {
+          entityName = possibleEntityName;
+          break;
+        }
+      }
+
+      if (entityName != null) {
+        var lastChar = entityName[entityName.length - 1];
+        if (lastChar != ";") {
+          _addToken(new ParseErrorToken("named-entity-without-semicolon"));
+        }
+        if (lastChar != ";" &&
+            fromAttribute &&
+            (isLetterOrDigit(charStack[entityLen]) ||
+                charStack[entityLen] == '=')) {
+          stream.unget(charStack.removeLast());
+          output = "&${charStack.join()}";
+        } else {
+          output = entities[entityName];
+          stream.unget(charStack.removeLast());
+          output = '${output}${slice(charStack, entityLen).join()}';
+        }
+      } else {
+        _addToken(new ParseErrorToken("expected-named-entity"));
+        stream.unget(charStack.removeLast());
+        output = "&${charStack.join()}";
+      }
+    }
+    if (fromAttribute) {
+      _attributeValue.write(output);
+    } else {
+      var token;
+      if (isWhitespace(output)) {
+        token = new SpaceCharactersToken(output);
+      } else {
+        token = new CharactersToken(output);
+      }
+      _addToken(token);
+    }
+  }
+
+  /// This method replaces the need for "entityInAttributeValueState".
+  void processEntityInAttribute(String allowedChar) {
+    consumeEntity(allowedChar: allowedChar, fromAttribute: true);
+  }
+
+  /// This method is a generic handler for emitting the tags. It also sets
+  /// the state to "data" because that's what's needed after a token has been
+  /// emitted.
+  void emitCurrentToken() {
+    var token = currentToken;
+    // Add token to the queue to be yielded
+    if (token is TagToken) {
+      if (lowercaseElementName) {
+        token.name = asciiUpper2Lower(token.name);
+      }
+      if (token is EndTagToken) {
+        if (_attributes != null) {
+          _addToken(new ParseErrorToken("attributes-in-end-tag"));
+        }
+        if (token.selfClosing) {
+          _addToken(new ParseErrorToken("this-closing-flag-on-end-tag"));
+        }
+      } else if (token is StartTagToken) {
+        // HTML5 specific normalizations to the token stream.
+        // Convert the list into a map where first key wins.
+        token.data = new LinkedHashMap<Object, String>();
+        if (_attributes != null) {
+          for (var attr in _attributes) {
+            token.data.putIfAbsent(attr.name, () => attr.value);
+          }
+          if (attributeSpans) token.attributeSpans = _attributes;
+        }
+      }
+      _attributes = null;
+      _attributeNames = null;
+    }
+    _addToken(token);
+    state = dataState;
+  }
+
+  // Below are the various tokenizer states worked out.
+
+  bool dataState() {
+    var data = stream.char();
+    if (data == "&") {
+      state = entityDataState;
+    } else if (data == "<") {
+      state = tagOpenState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _addToken(new CharactersToken("\u0000"));
+    } else if (data == EOF) {
+      // Tokenization ends.
+      return false;
+    } else if (isWhitespace(data)) {
+      // Directly after emitting a token you switch back to the "data
+      // state". At that point spaceCharacters are important so they are
+      // emitted separately.
+      _addToken(new SpaceCharactersToken(
+          '${data}${stream.charsUntil(spaceCharacters, true)}'));
+      // No need to update lastFourChars here, since the first space will
+      // have already been appended to lastFourChars and will have broken
+      // any <!-- or --> sequences
+    } else {
+      var chars = stream.charsUntil("&<\u0000");
+      _addToken(new CharactersToken('${data}${chars}'));
+    }
+    return true;
+  }
+
+  bool entityDataState() {
+    consumeEntity();
+    state = dataState;
+    return true;
+  }
+
+  bool rcdataState() {
+    var data = stream.char();
+    if (data == "&") {
+      state = characterReferenceInRcdata;
+    } else if (data == "<") {
+      state = rcdataLessThanSignState;
+    } else if (data == EOF) {
+      // Tokenization ends.
+      return false;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _addToken(new CharactersToken("\uFFFD"));
+    } else if (isWhitespace(data)) {
+      // Directly after emitting a token you switch back to the "data
+      // state". At that point spaceCharacters are important so they are
+      // emitted separately.
+      _addToken(new SpaceCharactersToken(
+          '${data}${stream.charsUntil(spaceCharacters, true)}'));
+    } else {
+      var chars = stream.charsUntil("&<");
+      _addToken(new CharactersToken('${data}${chars}'));
+    }
+    return true;
+  }
+
+  bool characterReferenceInRcdata() {
+    consumeEntity();
+    state = rcdataState;
+    return true;
+  }
+
+  bool rawtextState() {
+    var data = stream.char();
+    if (data == "<") {
+      state = rawtextLessThanSignState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _addToken(new CharactersToken("\uFFFD"));
+    } else if (data == EOF) {
+      // Tokenization ends.
+      return false;
+    } else {
+      var chars = stream.charsUntil("<\u0000");
+      _addToken(new CharactersToken("${data}${chars}"));
+    }
+    return true;
+  }
+
+  bool scriptDataState() {
+    var data = stream.char();
+    if (data == "<") {
+      state = scriptDataLessThanSignState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _addToken(new CharactersToken("\uFFFD"));
+    } else if (data == EOF) {
+      // Tokenization ends.
+      return false;
+    } else {
+      var chars = stream.charsUntil("<\u0000");
+      _addToken(new CharactersToken("${data}${chars}"));
+    }
+    return true;
+  }
+
+  bool plaintextState() {
+    var data = stream.char();
+    if (data == EOF) {
+      // Tokenization ends.
+      return false;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _addToken(new CharactersToken("\uFFFD"));
+    } else {
+      _addToken(new CharactersToken('${data}${stream.charsUntil("\u0000")}'));
+    }
+    return true;
+  }
+
+  bool tagOpenState() {
+    var data = stream.char();
+    if (data == "!") {
+      state = markupDeclarationOpenState;
+    } else if (data == "/") {
+      state = closeTagOpenState;
+    } else if (isLetter(data)) {
+      currentToken = new StartTagToken(data);
+      state = tagNameState;
+    } else if (data == ">") {
+      // XXX In theory it could be something besides a tag name. But
+      // do we really care?
+      _addToken(new ParseErrorToken("expected-tag-name-but-got-right-bracket"));
+      _addToken(new CharactersToken("<>"));
+      state = dataState;
+    } else if (data == "?") {
+      // XXX In theory it could be something besides a tag name. But
+      // do we really care?
+      _addToken(new ParseErrorToken("expected-tag-name-but-got-question-mark"));
+      stream.unget(data);
+      state = bogusCommentState;
+    } else {
+      // XXX
+      _addToken(new ParseErrorToken("expected-tag-name"));
+      _addToken(new CharactersToken("<"));
+      stream.unget(data);
+      state = dataState;
+    }
+    return true;
+  }
+
+  bool closeTagOpenState() {
+    var data = stream.char();
+    if (isLetter(data)) {
+      currentToken = new EndTagToken(data);
+      state = tagNameState;
+    } else if (data == ">") {
+      _addToken(
+          new ParseErrorToken("expected-closing-tag-but-got-right-bracket"));
+      state = dataState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("expected-closing-tag-but-got-eof"));
+      _addToken(new CharactersToken("</"));
+      state = dataState;
+    } else {
+      // XXX data can be _'_...
+      _addToken(new ParseErrorToken("expected-closing-tag-but-got-char",
+          messageParams: {"data": data}));
+      stream.unget(data);
+      state = bogusCommentState;
+    }
+    return true;
+  }
+
+  bool tagNameState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      state = beforeAttributeNameState;
+    } else if (data == ">") {
+      emitCurrentToken();
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-tag-name"));
+      state = dataState;
+    } else if (data == "/") {
+      state = selfClosingStartTagState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      currentTagToken.name = '${currentTagToken.name}\uFFFD';
+    } else {
+      currentTagToken.name = '${currentTagToken.name}$data';
+      // (Don't use charsUntil here, because tag names are
+      // very short and it's faster to not do anything fancy)
+    }
+    return true;
+  }
+
+  bool rcdataLessThanSignState() {
+    var data = stream.char();
+    if (data == "/") {
+      _buffer.clear();
+      state = rcdataEndTagOpenState;
+    } else {
+      _addToken(new CharactersToken("<"));
+      stream.unget(data);
+      state = rcdataState;
+    }
+    return true;
+  }
+
+  bool rcdataEndTagOpenState() {
+    var data = stream.char();
+    if (isLetter(data)) {
+      _buffer.write(data);
+      state = rcdataEndTagNameState;
+    } else {
+      _addToken(new CharactersToken("</"));
+      stream.unget(data);
+      state = rcdataState;
+    }
+    return true;
+  }
+
+  bool _tokenIsAppropriate() {
+    // TODO(jmesserly): this should use case insensitive compare instead.
+    return currentToken is TagToken &&
+        currentTagToken.name.toLowerCase() == '$_buffer'.toLowerCase();
+  }
+
+  bool rcdataEndTagNameState() {
+    var appropriate = _tokenIsAppropriate();
+    var data = stream.char();
+    if (isWhitespace(data) && appropriate) {
+      currentToken = new EndTagToken('$_buffer');
+      state = beforeAttributeNameState;
+    } else if (data == "/" && appropriate) {
+      currentToken = new EndTagToken('$_buffer');
+      state = selfClosingStartTagState;
+    } else if (data == ">" && appropriate) {
+      currentToken = new EndTagToken('$_buffer');
+      emitCurrentToken();
+      state = dataState;
+    } else if (isLetter(data)) {
+      _buffer.write(data);
+    } else {
+      _addToken(new CharactersToken("</$_buffer"));
+      stream.unget(data);
+      state = rcdataState;
+    }
+    return true;
+  }
+
+  bool rawtextLessThanSignState() {
+    var data = stream.char();
+    if (data == "/") {
+      _buffer.clear();
+      state = rawtextEndTagOpenState;
+    } else {
+      _addToken(new CharactersToken("<"));
+      stream.unget(data);
+      state = rawtextState;
+    }
+    return true;
+  }
+
+  bool rawtextEndTagOpenState() {
+    var data = stream.char();
+    if (isLetter(data)) {
+      _buffer.write(data);
+      state = rawtextEndTagNameState;
+    } else {
+      _addToken(new CharactersToken("</"));
+      stream.unget(data);
+      state = rawtextState;
+    }
+    return true;
+  }
+
+  bool rawtextEndTagNameState() {
+    var appropriate = _tokenIsAppropriate();
+    var data = stream.char();
+    if (isWhitespace(data) && appropriate) {
+      currentToken = new EndTagToken('$_buffer');
+      state = beforeAttributeNameState;
+    } else if (data == "/" && appropriate) {
+      currentToken = new EndTagToken('$_buffer');
+      state = selfClosingStartTagState;
+    } else if (data == ">" && appropriate) {
+      currentToken = new EndTagToken('$_buffer');
+      emitCurrentToken();
+      state = dataState;
+    } else if (isLetter(data)) {
+      _buffer.write(data);
+    } else {
+      _addToken(new CharactersToken("</$_buffer"));
+      stream.unget(data);
+      state = rawtextState;
+    }
+    return true;
+  }
+
+  bool scriptDataLessThanSignState() {
+    var data = stream.char();
+    if (data == "/") {
+      _buffer.clear();
+      state = scriptDataEndTagOpenState;
+    } else if (data == "!") {
+      _addToken(new CharactersToken("<!"));
+      state = scriptDataEscapeStartState;
+    } else {
+      _addToken(new CharactersToken("<"));
+      stream.unget(data);
+      state = scriptDataState;
+    }
+    return true;
+  }
+
+  bool scriptDataEndTagOpenState() {
+    var data = stream.char();
+    if (isLetter(data)) {
+      _buffer.write(data);
+      state = scriptDataEndTagNameState;
+    } else {
+      _addToken(new CharactersToken("</"));
+      stream.unget(data);
+      state = scriptDataState;
+    }
+    return true;
+  }
+
+  bool scriptDataEndTagNameState() {
+    var appropriate = _tokenIsAppropriate();
+    var data = stream.char();
+    if (isWhitespace(data) && appropriate) {
+      currentToken = new EndTagToken('$_buffer');
+      state = beforeAttributeNameState;
+    } else if (data == "/" && appropriate) {
+      currentToken = new EndTagToken('$_buffer');
+      state = selfClosingStartTagState;
+    } else if (data == ">" && appropriate) {
+      currentToken = new EndTagToken('$_buffer');
+      emitCurrentToken();
+      state = dataState;
+    } else if (isLetter(data)) {
+      _buffer.write(data);
+    } else {
+      _addToken(new CharactersToken("</$_buffer"));
+      stream.unget(data);
+      state = scriptDataState;
+    }
+    return true;
+  }
+
+  bool scriptDataEscapeStartState() {
+    var data = stream.char();
+    if (data == "-") {
+      _addToken(new CharactersToken("-"));
+      state = scriptDataEscapeStartDashState;
+    } else {
+      stream.unget(data);
+      state = scriptDataState;
+    }
+    return true;
+  }
+
+  bool scriptDataEscapeStartDashState() {
+    var data = stream.char();
+    if (data == "-") {
+      _addToken(new CharactersToken("-"));
+      state = scriptDataEscapedDashDashState;
+    } else {
+      stream.unget(data);
+      state = scriptDataState;
+    }
+    return true;
+  }
+
+  bool scriptDataEscapedState() {
+    var data = stream.char();
+    if (data == "-") {
+      _addToken(new CharactersToken("-"));
+      state = scriptDataEscapedDashState;
+    } else if (data == "<") {
+      state = scriptDataEscapedLessThanSignState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _addToken(new CharactersToken("\uFFFD"));
+    } else if (data == EOF) {
+      state = dataState;
+    } else {
+      var chars = stream.charsUntil("<-\u0000");
+      _addToken(new CharactersToken("${data}${chars}"));
+    }
+    return true;
+  }
+
+  bool scriptDataEscapedDashState() {
+    var data = stream.char();
+    if (data == "-") {
+      _addToken(new CharactersToken("-"));
+      state = scriptDataEscapedDashDashState;
+    } else if (data == "<") {
+      state = scriptDataEscapedLessThanSignState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _addToken(new CharactersToken("\uFFFD"));
+      state = scriptDataEscapedState;
+    } else if (data == EOF) {
+      state = dataState;
+    } else {
+      _addToken(new CharactersToken(data));
+      state = scriptDataEscapedState;
+    }
+    return true;
+  }
+
+  bool scriptDataEscapedDashDashState() {
+    var data = stream.char();
+    if (data == "-") {
+      _addToken(new CharactersToken("-"));
+    } else if (data == "<") {
+      state = scriptDataEscapedLessThanSignState;
+    } else if (data == ">") {
+      _addToken(new CharactersToken(">"));
+      state = scriptDataState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _addToken(new CharactersToken("\uFFFD"));
+      state = scriptDataEscapedState;
+    } else if (data == EOF) {
+      state = dataState;
+    } else {
+      _addToken(new CharactersToken(data));
+      state = scriptDataEscapedState;
+    }
+    return true;
+  }
+
+  bool scriptDataEscapedLessThanSignState() {
+    var data = stream.char();
+    if (data == "/") {
+      _buffer.clear();
+      state = scriptDataEscapedEndTagOpenState;
+    } else if (isLetter(data)) {
+      _addToken(new CharactersToken("<$data"));
+      _buffer.clear();
+      _buffer.write(data);
+      state = scriptDataDoubleEscapeStartState;
+    } else {
+      _addToken(new CharactersToken("<"));
+      stream.unget(data);
+      state = scriptDataEscapedState;
+    }
+    return true;
+  }
+
+  bool scriptDataEscapedEndTagOpenState() {
+    var data = stream.char();
+    if (isLetter(data)) {
+      _buffer.clear();
+      _buffer.write(data);
+      state = scriptDataEscapedEndTagNameState;
+    } else {
+      _addToken(new CharactersToken("</"));
+      stream.unget(data);
+      state = scriptDataEscapedState;
+    }
+    return true;
+  }
+
+  bool scriptDataEscapedEndTagNameState() {
+    var appropriate = _tokenIsAppropriate();
+    var data = stream.char();
+    if (isWhitespace(data) && appropriate) {
+      currentToken = new EndTagToken('$_buffer');
+      state = beforeAttributeNameState;
+    } else if (data == "/" && appropriate) {
+      currentToken = new EndTagToken('$_buffer');
+      state = selfClosingStartTagState;
+    } else if (data == ">" && appropriate) {
+      currentToken = new EndTagToken('$_buffer');
+      emitCurrentToken();
+      state = dataState;
+    } else if (isLetter(data)) {
+      _buffer.write(data);
+    } else {
+      _addToken(new CharactersToken("</$_buffer"));
+      stream.unget(data);
+      state = scriptDataEscapedState;
+    }
+    return true;
+  }
+
+  bool scriptDataDoubleEscapeStartState() {
+    var data = stream.char();
+    if (isWhitespace(data) || data == "/" || data == ">") {
+      _addToken(new CharactersToken(data));
+      if ('$_buffer'.toLowerCase() == "script") {
+        state = scriptDataDoubleEscapedState;
+      } else {
+        state = scriptDataEscapedState;
+      }
+    } else if (isLetter(data)) {
+      _addToken(new CharactersToken(data));
+      _buffer.write(data);
+    } else {
+      stream.unget(data);
+      state = scriptDataEscapedState;
+    }
+    return true;
+  }
+
+  bool scriptDataDoubleEscapedState() {
+    var data = stream.char();
+    if (data == "-") {
+      _addToken(new CharactersToken("-"));
+      state = scriptDataDoubleEscapedDashState;
+    } else if (data == "<") {
+      _addToken(new CharactersToken("<"));
+      state = scriptDataDoubleEscapedLessThanSignState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _addToken(new CharactersToken("\uFFFD"));
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-script-in-script"));
+      state = dataState;
+    } else {
+      _addToken(new CharactersToken(data));
+    }
+    return true;
+  }
+
+  bool scriptDataDoubleEscapedDashState() {
+    var data = stream.char();
+    if (data == "-") {
+      _addToken(new CharactersToken("-"));
+      state = scriptDataDoubleEscapedDashDashState;
+    } else if (data == "<") {
+      _addToken(new CharactersToken("<"));
+      state = scriptDataDoubleEscapedLessThanSignState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _addToken(new CharactersToken("\uFFFD"));
+      state = scriptDataDoubleEscapedState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-script-in-script"));
+      state = dataState;
+    } else {
+      _addToken(new CharactersToken(data));
+      state = scriptDataDoubleEscapedState;
+    }
+    return true;
+  }
+
+  // TODO(jmesserly): report bug in original code
+  // (was "Dash" instead of "DashDash")
+  bool scriptDataDoubleEscapedDashDashState() {
+    var data = stream.char();
+    if (data == "-") {
+      _addToken(new CharactersToken("-"));
+    } else if (data == "<") {
+      _addToken(new CharactersToken("<"));
+      state = scriptDataDoubleEscapedLessThanSignState;
+    } else if (data == ">") {
+      _addToken(new CharactersToken(">"));
+      state = scriptDataState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _addToken(new CharactersToken("\uFFFD"));
+      state = scriptDataDoubleEscapedState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-script-in-script"));
+      state = dataState;
+    } else {
+      _addToken(new CharactersToken(data));
+      state = scriptDataDoubleEscapedState;
+    }
+    return true;
+  }
+
+  bool scriptDataDoubleEscapedLessThanSignState() {
+    var data = stream.char();
+    if (data == "/") {
+      _addToken(new CharactersToken("/"));
+      _buffer.clear();
+      state = scriptDataDoubleEscapeEndState;
+    } else {
+      stream.unget(data);
+      state = scriptDataDoubleEscapedState;
+    }
+    return true;
+  }
+
+  bool scriptDataDoubleEscapeEndState() {
+    var data = stream.char();
+    if (isWhitespace(data) || data == "/" || data == ">") {
+      _addToken(new CharactersToken(data));
+      if ('$_buffer'.toLowerCase() == "script") {
+        state = scriptDataEscapedState;
+      } else {
+        state = scriptDataDoubleEscapedState;
+      }
+    } else if (isLetter(data)) {
+      _addToken(new CharactersToken(data));
+      _buffer.write(data);
+    } else {
+      stream.unget(data);
+      state = scriptDataDoubleEscapedState;
+    }
+    return true;
+  }
+
+  bool beforeAttributeNameState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      stream.charsUntil(spaceCharacters, true);
+    } else if (isLetter(data)) {
+      _addAttribute(data);
+      state = attributeNameState;
+    } else if (data == ">") {
+      emitCurrentToken();
+    } else if (data == "/") {
+      state = selfClosingStartTagState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("expected-attribute-name-but-got-eof"));
+      state = dataState;
+    } else if ("'\"=<".contains(data)) {
+      _addToken(new ParseErrorToken("invalid-character-in-attribute-name"));
+      _addAttribute(data);
+      state = attributeNameState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _addAttribute("\uFFFD");
+      state = attributeNameState;
+    } else {
+      _addAttribute(data);
+      state = attributeNameState;
+    }
+    return true;
+  }
+
+  bool attributeNameState() {
+    var data = stream.char();
+    bool leavingThisState = true;
+    bool emitToken = false;
+    if (data == "=") {
+      state = beforeAttributeValueState;
+    } else if (isLetter(data)) {
+      _attributeName.write(data);
+      _attributeName.write(stream.charsUntil(asciiLetters, true));
+      leavingThisState = false;
+    } else if (data == ">") {
+      // XXX If we emit here the attributes are converted to a dict
+      // without being checked and when the code below runs we error
+      // because data is a dict not a list
+      emitToken = true;
+    } else if (isWhitespace(data)) {
+      state = afterAttributeNameState;
+    } else if (data == "/") {
+      state = selfClosingStartTagState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _attributeName.write('\uFFFD');
+      leavingThisState = false;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-attribute-name"));
+      state = dataState;
+    } else if ("'\"<".contains(data)) {
+      _addToken(new ParseErrorToken("invalid-character-in-attribute-name"));
+      _attributeName.write(data);
+      leavingThisState = false;
+    } else {
+      _attributeName.write(data);
+      leavingThisState = false;
+    }
+
+    if (leavingThisState) {
+      _markAttributeNameEnd(-1);
+
+      // Attributes are not dropped at this stage. That happens when the
+      // start tag token is emitted so values can still be safely appended
+      // to attributes, but we do want to report the parse error in time.
+      var attrName = _attributeName.toString();
+      if (lowercaseAttrName) {
+        attrName = asciiUpper2Lower(attrName);
+      }
+      _attributes.last.name = attrName;
+      if (_attributeNames == null) _attributeNames = new Set();
+      if (_attributeNames.contains(attrName)) {
+        _addToken(new ParseErrorToken("duplicate-attribute"));
+      }
+      _attributeNames.add(attrName);
+
+      // XXX Fix for above XXX
+      if (emitToken) {
+        emitCurrentToken();
+      }
+    }
+    return true;
+  }
+
+  bool afterAttributeNameState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      stream.charsUntil(spaceCharacters, true);
+    } else if (data == "=") {
+      state = beforeAttributeValueState;
+    } else if (data == ">") {
+      emitCurrentToken();
+    } else if (isLetter(data)) {
+      _addAttribute(data);
+      state = attributeNameState;
+    } else if (data == "/") {
+      state = selfClosingStartTagState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _addAttribute("\uFFFD");
+      state = attributeNameState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("expected-end-of-tag-but-got-eof"));
+      state = dataState;
+    } else if ("'\"<".contains(data)) {
+      _addToken(new ParseErrorToken("invalid-character-after-attribute-name"));
+      _addAttribute(data);
+      state = attributeNameState;
+    } else {
+      _addAttribute(data);
+      state = attributeNameState;
+    }
+    return true;
+  }
+
+  bool beforeAttributeValueState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      stream.charsUntil(spaceCharacters, true);
+    } else if (data == "\"") {
+      _markAttributeValueStart(0);
+      state = attributeValueDoubleQuotedState;
+    } else if (data == "&") {
+      state = attributeValueUnQuotedState;
+      stream.unget(data);
+      _markAttributeValueStart(0);
+    } else if (data == "'") {
+      _markAttributeValueStart(0);
+      state = attributeValueSingleQuotedState;
+    } else if (data == ">") {
+      _addToken(new ParseErrorToken(
+          "expected-attribute-value-but-got-right-bracket"));
+      emitCurrentToken();
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _markAttributeValueStart(-1);
+      _attributeValue.write('\uFFFD');
+      state = attributeValueUnQuotedState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("expected-attribute-value-but-got-eof"));
+      state = dataState;
+    } else if ("=<`".contains(data)) {
+      _addToken(new ParseErrorToken("equals-in-unquoted-attribute-value"));
+      _markAttributeValueStart(-1);
+      _attributeValue.write(data);
+      state = attributeValueUnQuotedState;
+    } else {
+      _markAttributeValueStart(-1);
+      _attributeValue.write(data);
+      state = attributeValueUnQuotedState;
+    }
+    return true;
+  }
+
+  bool attributeValueDoubleQuotedState() {
+    var data = stream.char();
+    if (data == "\"") {
+      _markAttributeValueEnd(-1);
+      _markAttributeEnd(0);
+      state = afterAttributeValueState;
+    } else if (data == "&") {
+      processEntityInAttribute('"');
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _attributeValue.write('\uFFFD');
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-attribute-value-double-quote"));
+      _markAttributeValueEnd(-1);
+      state = dataState;
+    } else {
+      _attributeValue.write(data);
+      _attributeValue.write(stream.charsUntil("\"&"));
+    }
+    return true;
+  }
+
+  bool attributeValueSingleQuotedState() {
+    var data = stream.char();
+    if (data == "'") {
+      _markAttributeValueEnd(-1);
+      _markAttributeEnd(0);
+      state = afterAttributeValueState;
+    } else if (data == "&") {
+      processEntityInAttribute("'");
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _attributeValue.write('\uFFFD');
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-attribute-value-single-quote"));
+      _markAttributeValueEnd(-1);
+      state = dataState;
+    } else {
+      _attributeValue.write(data);
+      _attributeValue.write(stream.charsUntil("\'&"));
+    }
+    return true;
+  }
+
+  bool attributeValueUnQuotedState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      _markAttributeValueEnd(-1);
+      state = beforeAttributeNameState;
+    } else if (data == "&") {
+      processEntityInAttribute(">");
+    } else if (data == ">") {
+      _markAttributeValueEnd(-1);
+      emitCurrentToken();
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-attribute-value-no-quotes"));
+      _markAttributeValueEnd(-1);
+      state = dataState;
+    } else if ('"\'=<`'.contains(data)) {
+      _addToken(new ParseErrorToken(
+          "unexpected-character-in-unquoted-attribute-value"));
+      _attributeValue.write(data);
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      _attributeValue.write('\uFFFD');
+    } else {
+      _attributeValue.write(data);
+      _attributeValue.write(stream.charsUntil("&>\"\'=<`$spaceCharacters"));
+    }
+    return true;
+  }
+
+  bool afterAttributeValueState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      state = beforeAttributeNameState;
+    } else if (data == ">") {
+      emitCurrentToken();
+    } else if (data == "/") {
+      state = selfClosingStartTagState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("unexpected-EOF-after-attribute-value"));
+      stream.unget(data);
+      state = dataState;
+    } else {
+      _addToken(
+          new ParseErrorToken("unexpected-character-after-attribute-value"));
+      stream.unget(data);
+      state = beforeAttributeNameState;
+    }
+    return true;
+  }
+
+  bool selfClosingStartTagState() {
+    var data = stream.char();
+    if (data == ">") {
+      currentTagToken.selfClosing = true;
+      emitCurrentToken();
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("unexpected-EOF-after-solidus-in-tag"));
+      stream.unget(data);
+      state = dataState;
+    } else {
+      _addToken(
+          new ParseErrorToken("unexpected-character-after-soldius-in-tag"));
+      stream.unget(data);
+      state = beforeAttributeNameState;
+    }
+    return true;
+  }
+
+  bool bogusCommentState() {
+    // Make a new comment token and give it as value all the characters
+    // until the first > or EOF (charsUntil checks for EOF automatically)
+    // and emit it.
+    var data = stream.charsUntil(">");
+    data = data.replaceAll("\u0000", "\uFFFD");
+    _addToken(new CommentToken(data));
+
+    // Eat the character directly after the bogus comment which is either a
+    // ">" or an EOF.
+    stream.char();
+    state = dataState;
+    return true;
+  }
+
+  bool markupDeclarationOpenState() {
+    var charStack = [stream.char()];
+    if (charStack.last == "-") {
+      charStack.add(stream.char());
+      if (charStack.last == "-") {
+        currentToken = new CommentToken();
+        state = commentStartState;
+        return true;
+      }
+    } else if (charStack.last == 'd' || charStack.last == 'D') {
+      var matched = true;
+      for (var expected in const ['oO', 'cC', 'tT', 'yY', 'pP', 'eE']) {
+        var char = stream.char();
+        charStack.add(char);
+        if (char == EOF || !expected.contains(char)) {
+          matched = false;
+          break;
+        }
+      }
+      if (matched) {
+        currentToken = new DoctypeToken(correct: true);
+        state = doctypeState;
+        return true;
+      }
+    } else if (charStack.last == "[" &&
+        parser != null &&
+        parser.tree.openElements.length > 0 &&
+        parser.tree.openElements.last.namespaceUri !=
+            parser.tree.defaultNamespace) {
+      var matched = true;
+      for (var expected in const ["C", "D", "A", "T", "A", "["]) {
+        charStack.add(stream.char());
+        if (charStack.last != expected) {
+          matched = false;
+          break;
+        }
+      }
+      if (matched) {
+        state = cdataSectionState;
+        return true;
+      }
+    }
+
+    _addToken(new ParseErrorToken("expected-dashes-or-doctype"));
+
+    while (charStack.length > 0) {
+      stream.unget(charStack.removeLast());
+    }
+    state = bogusCommentState;
+    return true;
+  }
+
+  bool commentStartState() {
+    var data = stream.char();
+    if (data == "-") {
+      state = commentStartDashState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      currentStringToken.add('\uFFFD');
+    } else if (data == ">") {
+      _addToken(new ParseErrorToken("incorrect-comment"));
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-comment"));
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      currentStringToken.add(data);
+      state = commentState;
+    }
+    return true;
+  }
+
+  bool commentStartDashState() {
+    var data = stream.char();
+    if (data == "-") {
+      state = commentEndState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      currentStringToken.add('-\uFFFD');
+    } else if (data == ">") {
+      _addToken(new ParseErrorToken("incorrect-comment"));
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-comment"));
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      currentStringToken.add('-').add(data);
+      state = commentState;
+    }
+    return true;
+  }
+
+  bool commentState() {
+    var data = stream.char();
+    if (data == "-") {
+      state = commentEndDashState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      currentStringToken.add('\uFFFD');
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-comment"));
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      currentStringToken.add(data).add(stream.charsUntil("-\u0000"));
+    }
+    return true;
+  }
+
+  bool commentEndDashState() {
+    var data = stream.char();
+    if (data == "-") {
+      state = commentEndState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      currentStringToken.add('-\uFFFD');
+      state = commentState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-comment-end-dash"));
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      currentStringToken.add('-').add(data);
+      state = commentState;
+    }
+    return true;
+  }
+
+  bool commentEndState() {
+    var data = stream.char();
+    if (data == ">") {
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      currentStringToken.add('--\uFFFD');
+      state = commentState;
+    } else if (data == "!") {
+      _addToken(
+          new ParseErrorToken("unexpected-bang-after-double-dash-in-comment"));
+      state = commentEndBangState;
+    } else if (data == "-") {
+      _addToken(
+          new ParseErrorToken("unexpected-dash-after-double-dash-in-comment"));
+      currentStringToken.add(data);
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-comment-double-dash"));
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      // XXX
+      _addToken(new ParseErrorToken("unexpected-char-in-comment"));
+      currentStringToken.add('--').add(data);
+      state = commentState;
+    }
+    return true;
+  }
+
+  bool commentEndBangState() {
+    var data = stream.char();
+    if (data == ">") {
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == "-") {
+      currentStringToken.add('--!');
+      state = commentEndDashState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      currentStringToken.add('--!\uFFFD');
+      state = commentState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-comment-end-bang-state"));
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      currentStringToken.add('--!').add(data);
+      state = commentState;
+    }
+    return true;
+  }
+
+  bool doctypeState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      state = beforeDoctypeNameState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("expected-doctype-name-but-got-eof"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      _addToken(new ParseErrorToken("need-space-after-doctype"));
+      stream.unget(data);
+      state = beforeDoctypeNameState;
+    }
+    return true;
+  }
+
+  bool beforeDoctypeNameState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      return true;
+    } else if (data == ">") {
+      _addToken(
+          new ParseErrorToken("expected-doctype-name-but-got-right-bracket"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      currentDoctypeToken.name = "\uFFFD";
+      state = doctypeNameState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("expected-doctype-name-but-got-eof"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      currentDoctypeToken.name = data;
+      state = doctypeNameState;
+    }
+    return true;
+  }
+
+  bool doctypeNameState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      currentDoctypeToken.name = asciiUpper2Lower(currentDoctypeToken.name);
+      state = afterDoctypeNameState;
+    } else if (data == ">") {
+      currentDoctypeToken.name = asciiUpper2Lower(currentDoctypeToken.name);
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      currentDoctypeToken.name = "${currentDoctypeToken.name}\uFFFD";
+      state = doctypeNameState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-doctype-name"));
+      currentDoctypeToken.correct = false;
+      currentDoctypeToken.name = asciiUpper2Lower(currentDoctypeToken.name);
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      currentDoctypeToken.name = '${currentDoctypeToken.name}$data';
+    }
+    return true;
+  }
+
+  bool afterDoctypeNameState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      return true;
+    } else if (data == ">") {
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == EOF) {
+      currentDoctypeToken.correct = false;
+      stream.unget(data);
+      _addToken(new ParseErrorToken("eof-in-doctype"));
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      if (data == "p" || data == "P") {
+        // TODO(jmesserly): would be nice to have a helper for this.
+        var matched = true;
+        for (var expected in const ["uU", "bB", "lL", "iI", "cC"]) {
+          data = stream.char();
+          if (data == EOF || !expected.contains(data)) {
+            matched = false;
+            break;
+          }
+        }
+        if (matched) {
+          state = afterDoctypePublicKeywordState;
+          return true;
+        }
+      } else if (data == "s" || data == "S") {
+        var matched = true;
+        for (var expected in const ["yY", "sS", "tT", "eE", "mM"]) {
+          data = stream.char();
+          if (data == EOF || !expected.contains(data)) {
+            matched = false;
+            break;
+          }
+        }
+        if (matched) {
+          state = afterDoctypeSystemKeywordState;
+          return true;
+        }
+      }
+
+      // All the characters read before the current 'data' will be
+      // [a-zA-Z], so they're garbage in the bogus doctype and can be
+      // discarded; only the latest character might be '>' or EOF
+      // and needs to be ungetted
+      stream.unget(data);
+      _addToken(new ParseErrorToken(
+          "expected-space-or-right-bracket-in-doctype",
+          messageParams: {"data": data}));
+      currentDoctypeToken.correct = false;
+      state = bogusDoctypeState;
+    }
+    return true;
+  }
+
+  bool afterDoctypePublicKeywordState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      state = beforeDoctypePublicIdentifierState;
+    } else if (data == "'" || data == '"') {
+      _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
+      stream.unget(data);
+      state = beforeDoctypePublicIdentifierState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      stream.unget(data);
+      state = beforeDoctypePublicIdentifierState;
+    }
+    return true;
+  }
+
+  bool beforeDoctypePublicIdentifierState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      return true;
+    } else if (data == "\"") {
+      currentDoctypeToken.publicId = "";
+      state = doctypePublicIdentifierDoubleQuotedState;
+    } else if (data == "'") {
+      currentDoctypeToken.publicId = "";
+      state = doctypePublicIdentifierSingleQuotedState;
+    } else if (data == ">") {
+      _addToken(new ParseErrorToken("unexpected-end-of-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
+      currentDoctypeToken.correct = false;
+      state = bogusDoctypeState;
+    }
+    return true;
+  }
+
+  bool doctypePublicIdentifierDoubleQuotedState() {
+    var data = stream.char();
+    if (data == '"') {
+      state = afterDoctypePublicIdentifierState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      currentDoctypeToken.publicId = "${currentDoctypeToken.publicId}\uFFFD";
+    } else if (data == ">") {
+      _addToken(new ParseErrorToken("unexpected-end-of-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      currentDoctypeToken.publicId = '${currentDoctypeToken.publicId}$data';
+    }
+    return true;
+  }
+
+  bool doctypePublicIdentifierSingleQuotedState() {
+    var data = stream.char();
+    if (data == "'") {
+      state = afterDoctypePublicIdentifierState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      currentDoctypeToken.publicId = "${currentDoctypeToken.publicId}\uFFFD";
+    } else if (data == ">") {
+      _addToken(new ParseErrorToken("unexpected-end-of-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      currentDoctypeToken.publicId = '${currentDoctypeToken.publicId}$data';
+    }
+    return true;
+  }
+
+  bool afterDoctypePublicIdentifierState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      state = betweenDoctypePublicAndSystemIdentifiersState;
+    } else if (data == ">") {
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == '"') {
+      _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
+      currentDoctypeToken.systemId = "";
+      state = doctypeSystemIdentifierDoubleQuotedState;
+    } else if (data == "'") {
+      _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
+      currentDoctypeToken.systemId = "";
+      state = doctypeSystemIdentifierSingleQuotedState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
+      currentDoctypeToken.correct = false;
+      state = bogusDoctypeState;
+    }
+    return true;
+  }
+
+  bool betweenDoctypePublicAndSystemIdentifiersState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      return true;
+    } else if (data == ">") {
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == '"') {
+      currentDoctypeToken.systemId = "";
+      state = doctypeSystemIdentifierDoubleQuotedState;
+    } else if (data == "'") {
+      currentDoctypeToken.systemId = "";
+      state = doctypeSystemIdentifierSingleQuotedState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
+      currentDoctypeToken.correct = false;
+      state = bogusDoctypeState;
+    }
+    return true;
+  }
+
+  bool afterDoctypeSystemKeywordState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      state = beforeDoctypeSystemIdentifierState;
+    } else if (data == "'" || data == '"') {
+      _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
+      stream.unget(data);
+      state = beforeDoctypeSystemIdentifierState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      stream.unget(data);
+      state = beforeDoctypeSystemIdentifierState;
+    }
+    return true;
+  }
+
+  bool beforeDoctypeSystemIdentifierState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      return true;
+    } else if (data == "\"") {
+      currentDoctypeToken.systemId = "";
+      state = doctypeSystemIdentifierDoubleQuotedState;
+    } else if (data == "'") {
+      currentDoctypeToken.systemId = "";
+      state = doctypeSystemIdentifierSingleQuotedState;
+    } else if (data == ">") {
+      _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
+      currentDoctypeToken.correct = false;
+      state = bogusDoctypeState;
+    }
+    return true;
+  }
+
+  bool doctypeSystemIdentifierDoubleQuotedState() {
+    var data = stream.char();
+    if (data == "\"") {
+      state = afterDoctypeSystemIdentifierState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      currentDoctypeToken.systemId = "${currentDoctypeToken.systemId}\uFFFD";
+    } else if (data == ">") {
+      _addToken(new ParseErrorToken("unexpected-end-of-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      currentDoctypeToken.systemId = '${currentDoctypeToken.systemId}$data';
+    }
+    return true;
+  }
+
+  bool doctypeSystemIdentifierSingleQuotedState() {
+    var data = stream.char();
+    if (data == "'") {
+      state = afterDoctypeSystemIdentifierState;
+    } else if (data == "\u0000") {
+      _addToken(new ParseErrorToken("invalid-codepoint"));
+      currentDoctypeToken.systemId = "${currentDoctypeToken.systemId}\uFFFD";
+    } else if (data == ">") {
+      _addToken(new ParseErrorToken("unexpected-end-of-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      currentDoctypeToken.systemId = '${currentDoctypeToken.systemId}$data';
+    }
+    return true;
+  }
+
+  bool afterDoctypeSystemIdentifierState() {
+    var data = stream.char();
+    if (isWhitespace(data)) {
+      return true;
+    } else if (data == ">") {
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == EOF) {
+      _addToken(new ParseErrorToken("eof-in-doctype"));
+      currentDoctypeToken.correct = false;
+      _addToken(currentToken);
+      state = dataState;
+    } else {
+      _addToken(new ParseErrorToken("unexpected-char-in-doctype"));
+      state = bogusDoctypeState;
+    }
+    return true;
+  }
+
+  bool bogusDoctypeState() {
+    var data = stream.char();
+    if (data == ">") {
+      _addToken(currentToken);
+      state = dataState;
+    } else if (data == EOF) {
+      // XXX EMIT
+      stream.unget(data);
+      _addToken(currentToken);
+      state = dataState;
+    }
+    return true;
+  }
+
+  bool cdataSectionState() {
+    var data = [];
+    int matchedEnd = 0;
+    while (true) {
+      var ch = stream.char();
+      if (ch == EOF) {
+        break;
+      }
+      // Deal with null here rather than in the parser
+      if (ch == "\u0000") {
+        _addToken(new ParseErrorToken("invalid-codepoint"));
+        ch = "\uFFFD";
+      }
+      data.add(ch);
+      // TODO(jmesserly): it'd be nice if we had an easier way to match the end,
+      // perhaps with a "peek" API.
+      if (ch == "]" && matchedEnd < 2) {
+        matchedEnd++;
+      } else if (ch == ">" && matchedEnd == 2) {
+        // Remove "]]>" from the end.
+        data.removeLast();
+        data.removeLast();
+        data.removeLast();
+        break;
+      } else {
+        matchedEnd = 0;
+      }
+    }
+
+    if (data.length > 0) {
+      _addToken(new CharactersToken(data.join()));
+    }
+    state = dataState;
+    return true;
+  }
+}
diff --git a/html/lib/src/treebuilder.dart b/html/lib/src/treebuilder.dart
new file mode 100644
index 0000000..409cba4
--- /dev/null
+++ b/html/lib/src/treebuilder.dart
@@ -0,0 +1,408 @@
+/// Internals to the tree builders.
+library treebuilder;
+
+import 'dart:collection';
+import 'package:html/dom.dart';
+import 'package:html/parser.dart' show getElementNameTuple;
+import 'package:source_span/source_span.dart';
+import 'constants.dart';
+import 'list_proxy.dart';
+import 'token.dart';
+import 'utils.dart';
+
+// The scope markers are inserted when entering object elements,
+// marquees, table cells, and table captions, and are used to prevent formatting
+// from "leaking" into tables, object elements, and marquees.
+const Node Marker = null;
+
+// TODO(jmesserly): this should extend ListBase<Element>, but my simple attempt
+// didn't work.
+class ActiveFormattingElements extends ListProxy<Element> {
+  ActiveFormattingElements() : super();
+
+  // Override the "add" method.
+  // TODO(jmesserly): I'd rather not override this; can we do this in the
+  // calling code instead?
+  void add(Element node) {
+    int equalCount = 0;
+    if (node != Marker) {
+      for (var element in reversed) {
+        if (element == Marker) {
+          break;
+        }
+        if (_nodesEqual(element, node)) {
+          equalCount += 1;
+        }
+        if (equalCount == 3) {
+          remove(element);
+          break;
+        }
+      }
+    }
+    super.add(node);
+  }
+}
+
+// TODO(jmesserly): this should exist in corelib...
+bool _mapEquals(Map a, Map b) {
+  if (a.length != b.length) return false;
+  if (a.length == 0) return true;
+
+  for (var keyA in a.keys) {
+    var valB = b[keyA];
+    if (valB == null && !b.containsKey(keyA)) {
+      return false;
+    }
+
+    if (a[keyA] != valB) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool _nodesEqual(Element node1, Element node2) {
+  return getElementNameTuple(node1) == getElementNameTuple(node2) &&
+      _mapEquals(node1.attributes, node2.attributes);
+}
+
+/// Basic treebuilder implementation.
+class TreeBuilder {
+  final String defaultNamespace;
+
+  Document document;
+
+  final List<Element> openElements = <Element>[];
+
+  final activeFormattingElements = new ActiveFormattingElements();
+
+  Node headPointer;
+
+  Node formPointer;
+
+  /// Switch the function used to insert an element from the
+  /// normal one to the misnested table one and back again
+  bool insertFromTable;
+
+  TreeBuilder(bool namespaceHTMLElements)
+      : defaultNamespace = namespaceHTMLElements ? Namespaces.html : null {
+    reset();
+  }
+
+  void reset() {
+    openElements.clear();
+    activeFormattingElements.clear();
+
+    //XXX - rename these to headElement, formElement
+    headPointer = null;
+    formPointer = null;
+
+    insertFromTable = false;
+
+    document = new Document();
+  }
+
+  bool elementInScope(target, {String variant}) {
+    //If we pass a node in we match that. if we pass a string
+    //match any node with that name
+    bool exactNode = target is Node;
+
+    List listElements1 = scopingElements;
+    List listElements2 = const [];
+    bool invert = false;
+    if (variant != null) {
+      switch (variant) {
+        case "button":
+          listElements2 = const [const Pair(Namespaces.html, "button")];
+          break;
+        case "list":
+          listElements2 = const [
+            const Pair(Namespaces.html, "ol"),
+            const Pair(Namespaces.html, "ul")
+          ];
+          break;
+        case "table":
+          listElements1 = const [
+            const Pair(Namespaces.html, "html"),
+            const Pair(Namespaces.html, "table")
+          ];
+          break;
+        case "select":
+          listElements1 = const [
+            const Pair(Namespaces.html, "optgroup"),
+            const Pair(Namespaces.html, "option")
+          ];
+          invert = true;
+          break;
+        default:
+          throw new StateError('We should never reach this point');
+      }
+    }
+
+    for (var node in openElements.reversed) {
+      if (!exactNode && node.localName == target ||
+          exactNode && node == target) {
+        return true;
+      } else if (invert !=
+          (listElements1.contains(getElementNameTuple(node)) ||
+              listElements2.contains(getElementNameTuple(node)))) {
+        return false;
+      }
+    }
+
+    throw new StateError('We should never reach this point');
+  }
+
+  void reconstructActiveFormattingElements() {
+    // Within this algorithm the order of steps described in the
+    // specification is not quite the same as the order of steps in the
+    // code. It should still do the same though.
+
+    // Step 1: stop the algorithm when there's nothing to do.
+    if (activeFormattingElements.length == 0) {
+      return;
+    }
+
+    // Step 2 and step 3: we start with the last element. So i is -1.
+    int i = activeFormattingElements.length - 1;
+    var entry = activeFormattingElements[i];
+    if (entry == Marker || openElements.contains(entry)) {
+      return;
+    }
+
+    // Step 6
+    while (entry != Marker && !openElements.contains(entry)) {
+      if (i == 0) {
+        //This will be reset to 0 below
+        i = -1;
+        break;
+      }
+      i -= 1;
+      // Step 5: let entry be one earlier in the list.
+      entry = activeFormattingElements[i];
+    }
+
+    while (true) {
+      // Step 7
+      i += 1;
+
+      // Step 8
+      entry = activeFormattingElements[i];
+
+      // TODO(jmesserly): optimize this. No need to create a token.
+      var cloneToken = new StartTagToken(entry.localName,
+          namespace: entry.namespaceUri,
+          data: new LinkedHashMap.from(entry.attributes))
+        ..span = entry.sourceSpan;
+
+      // Step 9
+      var element = insertElement(cloneToken);
+
+      // Step 10
+      activeFormattingElements[i] = element;
+
+      // Step 11
+      if (element == activeFormattingElements.last) {
+        break;
+      }
+    }
+  }
+
+  void clearActiveFormattingElements() {
+    var entry = activeFormattingElements.removeLast();
+    while (activeFormattingElements.length > 0 && entry != Marker) {
+      entry = activeFormattingElements.removeLast();
+    }
+  }
+
+  /// Check if an element exists between the end of the active
+  /// formatting elements and the last marker. If it does, return it, else
+  /// return null.
+  Element elementInActiveFormattingElements(String name) {
+    for (var item in activeFormattingElements.reversed) {
+      // Check for Marker first because if it's a Marker it doesn't have a
+      // name attribute.
+      if (item == Marker) {
+        break;
+      } else if (item.localName == name) {
+        return item;
+      }
+    }
+    return null;
+  }
+
+  void insertRoot(Token token) {
+    var element = createElement(token);
+    openElements.add(element);
+    document.nodes.add(element);
+  }
+
+  void insertDoctype(DoctypeToken token) {
+    var doctype = new DocumentType(token.name, token.publicId, token.systemId)
+      ..sourceSpan = token.span;
+    document.nodes.add(doctype);
+  }
+
+  void insertComment(StringToken token, [Node parent]) {
+    if (parent == null) {
+      parent = openElements.last;
+    }
+    parent.nodes.add(new Comment(token.data)..sourceSpan = token.span);
+  }
+
+  /// Create an element but don't insert it anywhere
+  Element createElement(StartTagToken token) {
+    var name = token.name;
+    var namespace = token.namespace;
+    if (namespace == null) namespace = defaultNamespace;
+    var element = document.createElementNS(namespace, name)
+      ..attributes = token.data
+      ..sourceSpan = token.span;
+    return element;
+  }
+
+  Element insertElement(StartTagToken token) {
+    if (insertFromTable) return insertElementTable(token);
+    return insertElementNormal(token);
+  }
+
+  Element insertElementNormal(StartTagToken token) {
+    var name = token.name;
+    var namespace = token.namespace;
+    if (namespace == null) namespace = defaultNamespace;
+    var element = document.createElementNS(namespace, name)
+      ..attributes = token.data
+      ..sourceSpan = token.span;
+    openElements.last.nodes.add(element);
+    openElements.add(element);
+    return element;
+  }
+
+  Element insertElementTable(token) {
+    /// Create an element and insert it into the tree
+    var element = createElement(token);
+    if (!tableInsertModeElements.contains(openElements.last.localName)) {
+      return insertElementNormal(token);
+    } else {
+      // We should be in the InTable mode. This means we want to do
+      // special magic element rearranging
+      var nodePos = getTableMisnestedNodePosition();
+      if (nodePos[1] == null) {
+        // TODO(jmesserly): I don't think this is reachable. If insertFromTable
+        // is true, there will be a <table> element open, and it always has a
+        // parent pointer.
+        nodePos[0].nodes.add(element);
+      } else {
+        nodePos[0].insertBefore(element, nodePos[1]);
+      }
+      openElements.add(element);
+    }
+    return element;
+  }
+
+  /// Insert text data.
+  void insertText(String data, FileSpan span) {
+    var parent = openElements.last;
+
+    if (!insertFromTable ||
+        insertFromTable &&
+            !tableInsertModeElements.contains(openElements.last.localName)) {
+      _insertText(parent, data, span);
+    } else {
+      // We should be in the InTable mode. This means we want to do
+      // special magic element rearranging
+      var nodePos = getTableMisnestedNodePosition();
+      _insertText(nodePos[0], data, span, nodePos[1]);
+    }
+  }
+
+  /// Insert [data] as text in the current node, positioned before the
+  /// start of node [refNode] or to the end of the node's text.
+  static void _insertText(Node parent, String data, FileSpan span,
+      [Element refNode]) {
+    var nodes = parent.nodes;
+    if (refNode == null) {
+      if (nodes.length > 0 && nodes.last is Text) {
+        Text last = nodes.last;
+        last.appendData(data);
+
+        if (span != null) {
+          last.sourceSpan =
+              span.file.span(last.sourceSpan.start.offset, span.end.offset);
+        }
+      } else {
+        nodes.add(new Text(data)..sourceSpan = span);
+      }
+    } else {
+      int index = nodes.indexOf(refNode);
+      if (index > 0 && nodes[index - 1] is Text) {
+        Text last = nodes[index - 1];
+        last.appendData(data);
+      } else {
+        nodes.insert(index, new Text(data)..sourceSpan = span);
+      }
+    }
+  }
+
+  /// Get the foster parent element, and sibling to insert before
+  /// (or null) when inserting a misnested table node
+  List<Node> getTableMisnestedNodePosition() {
+    // The foster parent element is the one which comes before the most
+    // recently opened table element
+    // XXX - this is really inelegant
+    Node lastTable = null;
+    Node fosterParent = null;
+    var insertBefore = null;
+    for (var elm in openElements.reversed) {
+      if (elm.localName == "table") {
+        lastTable = elm;
+        break;
+      }
+    }
+    if (lastTable != null) {
+      // XXX - we should really check that this parent is actually a
+      // node here
+      if (lastTable.parentNode != null) {
+        fosterParent = lastTable.parentNode;
+        insertBefore = lastTable;
+      } else {
+        fosterParent = openElements[openElements.indexOf(lastTable) - 1];
+      }
+    } else {
+      fosterParent = openElements[0];
+    }
+    return [fosterParent, insertBefore];
+  }
+
+  void generateImpliedEndTags([String exclude]) {
+    var name = openElements.last.localName;
+    // XXX td, th and tr are not actually needed
+    if (name != exclude &&
+        const [
+      "dd",
+      "dt",
+      "li",
+      "option",
+      "optgroup",
+      "p",
+      "rp",
+      "rt"
+    ].contains(name)) {
+      openElements.removeLast();
+      // XXX This is not entirely what the specification says. We should
+      // investigate it more closely.
+      generateImpliedEndTags(exclude);
+    }
+  }
+
+  /// Return the final tree.
+  Document getDocument() => document;
+
+  /// Return the final fragment.
+  DocumentFragment getFragment() {
+    //XXX assert innerHTML
+    var fragment = new DocumentFragment();
+    openElements[0].reparentChildren(fragment);
+    return fragment;
+  }
+}
diff --git a/html/lib/src/utils.dart b/html/lib/src/utils.dart
new file mode 100644
index 0000000..fb58cf1
--- /dev/null
+++ b/html/lib/src/utils.dart
@@ -0,0 +1,123 @@
+/// Misc things that were useful when porting the code from Python.
+library utils;
+
+import 'constants.dart';
+
+typedef bool Predicate();
+
+class Pair<F, S> {
+  final F first;
+  final S second;
+
+  const Pair(this.first, this.second);
+
+  int get hashCode => 37 * first.hashCode + second.hashCode;
+  bool operator ==(other) => other.first == first && other.second == second;
+}
+
+int parseIntRadix(String str, [int radix = 10]) {
+  int val = 0;
+  for (int i = 0; i < str.length; i++) {
+    var digit = str.codeUnitAt(i);
+    if (digit >= LOWER_A) {
+      digit += 10 - LOWER_A;
+    } else if (digit >= UPPER_A) {
+      digit += 10 - UPPER_A;
+    } else {
+      digit -= ZERO;
+    }
+    val = val * radix + digit;
+  }
+  return val;
+}
+
+bool any(List<bool> iterable) => iterable.any((f) => f);
+
+bool startsWithAny(String str, List<String> prefixes) {
+  for (var prefix in prefixes) {
+    if (str.startsWith(prefix)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// Like the python [:] operator.
+List slice(List list, int start, [int end]) {
+  if (end == null) end = list.length;
+  if (end < 0) end += list.length;
+
+  // Ensure the indexes are in bounds.
+  if (end < start) end = start;
+  if (end > list.length) end = list.length;
+  return list.sublist(start, end);
+}
+
+bool allWhitespace(String str) {
+  for (int i = 0; i < str.length; i++) {
+    if (!isWhitespaceCC(str.codeUnitAt(i))) return false;
+  }
+  return true;
+}
+
+String padWithZeros(String str, int size) {
+  if (str.length == size) return str;
+  var result = new StringBuffer();
+  size -= str.length;
+  for (int i = 0; i < size; i++) result.write('0');
+  result.write(str);
+  return result.toString();
+}
+
+// TODO(jmesserly): this implementation is pretty wrong, but I need something
+// quick until dartbug.com/1694 is fixed.
+/// Format a string like Python's % string format operator. Right now this only
+/// supports a [data] dictionary used with %s or %08x. Those were the only
+/// things needed for [errorMessages].
+String formatStr(String format, Map data) {
+  if (data == null) return format;
+  data.forEach((key, value) {
+    var result = new StringBuffer();
+    var search = '%($key)';
+    int last = 0,
+        match;
+    while ((match = format.indexOf(search, last)) >= 0) {
+      result.write(format.substring(last, match));
+      match += search.length;
+
+      int digits = match;
+      while (isDigit(format[digits])) {
+        digits++;
+      }
+      int numberSize;
+      if (digits > match) {
+        numberSize = int.parse(format.substring(match, digits));
+        match = digits;
+      }
+
+      switch (format[match]) {
+        case 's':
+          result.write(value);
+          break;
+        case 'd':
+          var number = value.toString();
+          result.write(padWithZeros(number, numberSize));
+          break;
+        case 'x':
+          var number = value.toRadixString(16);
+          result.write(padWithZeros(number, numberSize));
+          break;
+        default:
+          throw "not implemented: formatStr does not support format "
+              "character ${format[match]}";
+      }
+
+      last = match + 1;
+    }
+
+    result.write(format.substring(last, format.length));
+    format = result.toString();
+  });
+
+  return format;
+}
diff --git a/html/pubspec.yaml b/html/pubspec.yaml
new file mode 100644
index 0000000..0af845a
--- /dev/null
+++ b/html/pubspec.yaml
@@ -0,0 +1,14 @@
+name: html
+version: 0.12.1+1
+author: Dart Team <misc@dartlang.org>
+description: A library for working with HTML documents. Previously known as html5lib.
+homepage: https://github.com/dart-lang/html
+environment:
+  sdk: '>=1.2.0 <2.0.0'
+dependencies:
+  csslib: '>=0.10.0 <0.13.0'
+  source_span: '>=1.0.0 <2.0.0'
+  utf: '>=0.9.0 <0.10.0'
+dev_dependencies:
+  path: '>=0.9.0 <2.0.0'
+  unittest: '>=0.10.0 <0.12.0'
diff --git a/initialize/lib/build/initializer_plugin.dart b/initialize/lib/build/initializer_plugin.dart
new file mode 100644
index 0000000..8822dc4
--- /dev/null
+++ b/initialize/lib/build/initializer_plugin.dart
@@ -0,0 +1,294 @@
+// Copyright (c) 2015, 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 initialize.build.initializer_plugin;
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:barback/barback.dart';
+import 'package:code_transformers/resolver.dart';
+import 'package:initialize/transformer.dart';
+import 'package:path/path.dart' as path;
+
+/// A plug which allows an initializer to write out an [InitEntry] given some
+/// [InitializerData] from an annotation that was found.
+abstract class InitializerPlugin {
+  /// Whether or not this plugin should be applied to an [Initializer] given
+  /// some [InitializerData]. If [true] is returned then this plugin will take
+  /// ownership of this [InitializerData] and no subsequent plugins will have
+  /// an opportunity to access it.
+  bool shouldApply(InitializerPluginData data);
+
+  /// Returns a [String] or [null]. The [String] should represent dart code
+  /// which creates a new [InitEntry] and that entry is added to the static
+  /// initializers list. If [null] is returned then no entry is added at all for
+  /// this [InitializerData].
+  String apply(InitializerPluginData data);
+}
+
+/// A class which wraps all the default data passed to an [InitializerPlugin]
+/// for each annotation.
+class InitializerPluginData {
+  final InitializerData initializer;
+  final AssetId bootstrapId;
+  final Map<LibraryElement, String> libraryPrefixes;
+  final TransformLogger logger;
+  final Resolver resolver;
+  InitializerPluginData(this.initializer, this.bootstrapId,
+      this.libraryPrefixes, this.resolver, this.logger);
+}
+
+/// The basic [InitializerPlugin]. This generates a new [InitEntry] to be added
+/// to the static initializers list, and applies to every item it sees.
+class DefaultInitializerPlugin implements InitializerPlugin {
+  const DefaultInitializerPlugin();
+
+  /// Applies to everything. Put other plugins before this one to override this
+  /// behaviour.
+  bool shouldApply(InitializerPluginData data) => true;
+
+  /// Creates a normal [InitEntry] string.
+  String apply(InitializerPluginData pluginData) {
+    var target = buildTarget(pluginData);
+    var meta = buildMeta(pluginData);
+    return 'new InitEntry($meta, $target)';
+  }
+
+  /// Builds a [String] representing the meta of an [InitEntry] given an
+  /// [ElementAnnotation] that was found.
+  String buildMeta(InitializerPluginData pluginData) {
+    var logger = pluginData.logger;
+    var element = pluginData.initializer.targetElement;
+    var elementAnnotation = pluginData.initializer.annotationElement;
+    var elementAnnotationElement = elementAnnotation.element;
+    var libraryPrefixes = pluginData.libraryPrefixes;
+    if (elementAnnotationElement is ConstructorElement) {
+      return buildConstructorMeta(elementAnnotation, pluginData);
+    } else if (elementAnnotationElement is PropertyAccessorElement) {
+      return buildPropertyMeta(elementAnnotation, pluginData);
+    } else {
+      logger.error('Unsupported annotation type. Only constructors and '
+          'properties are supported as initializers.');
+    }
+    return null;
+  }
+
+  /// Builds a [String] representing the meta of an [InitEntry] given an
+  /// [ElementAnnotation] whose element was a [ConstructorElement].
+  String buildConstructorMeta(
+      ElementAnnotation elementAnnotation, InitializerPluginData pluginData) {
+    var logger = pluginData.logger;
+    var node = pluginData.initializer.targetNode;
+    var metaPrefix =
+        pluginData.libraryPrefixes[elementAnnotation.element.library];
+
+    var annotation = pluginData.initializer.annotationNode;
+    if (annotation == null) {
+      logger.error(
+          'Initializer annotations are only supported on libraries, classes, '
+          'and top level methods. Found $node.');
+    }
+    var clazz = annotation.name;
+    var constructor = annotation.constructorName == null
+        ? ''
+        : '.${annotation.constructorName}';
+    var args = buildArgumentList(annotation.arguments, pluginData);
+    return 'const $metaPrefix.${clazz}$constructor$args';
+  }
+
+  /// Builds a [String] representing the meta of an [InitEntry] given an
+  /// [ElementAnnotation] whose element was a [PropertyAccessorElement].
+  String buildPropertyMeta(
+      ElementAnnotation annotation, InitializerPluginData pluginData) {
+    var metaPrefix = pluginData.libraryPrefixes[annotation.element.library];
+    return '$metaPrefix.${annotation.element.name}';
+  }
+
+  /// Builds a [String] for the target of an [InitEntry] given an [Element] that
+  /// was annotated.
+  String buildTarget(InitializerPluginData pluginData) {
+    var element = pluginData.initializer.targetElement;
+    var logger = pluginData.logger;
+    if (element is LibraryElement) {
+      return buildLibraryTarget(element, pluginData);
+    } else if (element is ClassElement) {
+      return buildClassTarget(element, pluginData);
+    } else if (element is FunctionElement) {
+      return buildFunctionTarget(element, pluginData);
+    } else {
+      logger.error('Initializers can only be applied to top level functions, '
+          'libraries, and classes.');
+    }
+    return null;
+  }
+
+  /// Builds a [String] for the target of an [InitEntry] given [element] which
+  /// is an annotated class.
+  String buildClassTarget(
+          ClassElement element, InitializerPluginData pluginData) =>
+      buildSimpleTarget(element, pluginData);
+
+  /// Builds a [String] for the target of an [InitEntry] given [element] which
+  /// is an annotated function.
+  String buildFunctionTarget(
+          FunctionElement element, InitializerPluginData pluginData) =>
+      buildSimpleTarget(element, pluginData);
+
+  /// Builds a [String] for the target of an [InitEntry] for a simple [Element].
+  /// This is just the library prefix followed by the element name.
+  String buildSimpleTarget(Element element, InitializerPluginData pluginData) =>
+      '${pluginData.libraryPrefixes[element.library]}.${element.name}';
+
+  /// Builds a [String] for the target of an [InitEntry] given [element] which
+  /// is an annotated library.
+  String buildLibraryTarget(
+      LibraryElement element, InitializerPluginData pluginData) {
+    var bootstrapId = pluginData.bootstrapId;
+    var logger = pluginData.logger;
+    var segments = element.source.uri.pathSegments;
+    var package = segments[0];
+    var libraryPath;
+    var packageString;
+    if (bootstrapId.package == package &&
+        bootstrapId.path.startsWith('${segments[1]}/')) {
+      // reset `package` to null, we will do a relative path in this case.
+      packageString = 'null';
+      libraryPath = path.url.relative(
+          path.url.joinAll(segments.getRange(1, segments.length)),
+          from: path.url.dirname(path.url.join(bootstrapId.path)));
+    } else if (segments[1] == 'lib') {
+      packageString = "'$package'";
+      libraryPath = path.url.joinAll(segments.getRange(2, segments.length));
+    } else {
+      logger.error('Unable to import `${element.source.uri.path}` from '
+          '${bootstrapId.path}.');
+    }
+
+    return "const LibraryIdentifier"
+        "(#${element.name}, $packageString, '$libraryPath')";
+  }
+
+  /// Builds a [String] representing an [ArgumentList] taking into account the
+  /// [libraryPrefixes] from [pluginData].
+  String buildArgumentList(
+      ArgumentList args, InitializerPluginData pluginData) {
+    var buffer = new StringBuffer();
+    buffer.write('(');
+    var first = true;
+    for (var arg in args.arguments) {
+      if (!first) buffer.write(', ');
+      first = false;
+
+      Expression expression;
+      if (arg is NamedExpression) {
+        buffer.write('${arg.name.label.name}: ');
+        expression = arg.expression;
+      } else {
+        expression = arg;
+      }
+
+      buffer.write(buildExpression(expression, pluginData));
+    }
+    buffer.write(')');
+    return buffer.toString();
+  }
+
+  /// Builds a [String] representing [expression] taking into account the
+  /// [libraryPrefixes] from [pluginData].
+  String buildExpression(
+      Expression expression, InitializerPluginData pluginData) {
+    var logger = pluginData.logger;
+    var libraryPrefixes = pluginData.libraryPrefixes;
+    var buffer = new StringBuffer();
+    if (expression is StringLiteral && expression.stringValue != null) {
+      buffer.write(_stringValue(expression.stringValue));
+    } else if (expression is BooleanLiteral ||
+        expression is DoubleLiteral ||
+        expression is IntegerLiteral ||
+        expression is NullLiteral) {
+      buffer.write('${expression}');
+    } else if (expression is ListLiteral) {
+      buffer.write('const [');
+      var first = true;
+      for (Expression listExpression in expression.elements) {
+        if (!first) buffer.write(', ');
+        first = false;
+        buffer.write(buildExpression(listExpression, pluginData));
+      }
+      buffer.write(']');
+    } else if (expression is MapLiteral) {
+      buffer.write('const {');
+      var first = true;
+      for (MapLiteralEntry entry in expression.entries) {
+        if (!first) buffer.write(', ');
+        first = false;
+        buffer.write(buildExpression(entry.key, pluginData));
+        buffer.write(': ');
+        buffer.write(buildExpression(entry.value, pluginData));
+      }
+      buffer.write('}');
+    } else if (expression is Identifier) {
+      var element = expression.bestElement;
+      if (element == null) {
+        logger.error('Unable to get `bestElement` for expression: $expression');
+      } else if (!element.isPublic) {
+        // Inline the evaluated value of private identifiers.
+        buffer.write(_evaluateExpression(expression, pluginData));
+      } else {
+        libraryPrefixes.putIfAbsent(
+          element.library, () => 'i${libraryPrefixes.length}');
+
+        buffer.write('${libraryPrefixes[element.library]}.');
+        if (element is ClassElement) {
+          buffer.write(element.name);
+        } else if (element is PropertyAccessorElement) {
+          var variable = element.variable;
+          if (variable is FieldElement) {
+            buffer.write('${variable.enclosingElement.name}.');
+          }
+          buffer.write('${variable.name}');
+        } else {
+          logger.error('Unsupported argument to initializer constructor.');
+        }
+      }
+    } else if (expression is PropertyAccess) {
+      buffer.write(buildExpression(expression.target, pluginData));
+      buffer.write('.${expression.propertyName}');
+    } else if (expression is InstanceCreationExpression) {
+      logger.error('Unsupported expression in initializer, found $expression. '
+          'Instance creation expressions are not supported (yet). Instead, '
+          'please assign it to a const variable and use that instead.');
+    } else {
+      buffer.write(_evaluateExpression(expression, pluginData));
+    }
+    return buffer.toString();
+  }
+
+  _evaluateExpression(Expression expression, InitializerPluginData pluginData) {
+    var logger = pluginData.logger;
+    var result = pluginData.resolver.evaluateConstant(
+        pluginData.initializer.targetElement.library, expression);
+    if (!result.isValid) {
+      logger.error('Invalid expression in initializer, found $expression. '
+          'And got the following errors: ${result.errors}.');
+      return null;
+    }
+    var value = result.value.value;
+    if (value == null) {
+      logger.error('Unsupported expression in initializer, found '
+          '$expression. Please file a bug at '
+          'https://github.com/dart-lang/initialize/issues');
+      return null;
+    }
+
+    if (value is String) value = _stringValue(value);
+    return value;
+  }
+
+  // Returns an expression for a string value. Wraps it in single quotes and
+  // escapes existing single quotes and escapes.
+  _stringValue(String value) {
+    value = value.replaceAll(r'\', r'\\').replaceAll(r"'", r"\'");
+    return "'$value'";
+  }
+}
diff --git a/initialize/lib/build/loader_replacer.dart b/initialize/lib/build/loader_replacer.dart
new file mode 100644
index 0000000..5888036
--- /dev/null
+++ b/initialize/lib/build/loader_replacer.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2015, 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 initialize.build.loader_replacer;
+
+import 'dart:async';
+import 'package:barback/barback.dart';
+
+/// Removes `mirror_loader.dart` and replaces it with `static_loader.dart`.
+class LoaderReplacer extends Transformer {
+  LoaderReplacer.asPlugin();
+
+  bool isPrimary(AssetId id) =>
+      id.package == 'initialize' && id.path == 'lib/initialize.dart';
+
+  Future apply(Transform transform) {
+    var id = transform.primaryInput.id;
+    return transform.primaryInput.readAsString().then((code) {
+      transform.addOutput(new Asset.fromString(
+          id, code.replaceFirst('mirror_loader.dart', 'static_loader.dart')));
+    });
+  }
+}
diff --git a/initialize/lib/initialize.dart b/initialize/lib/initialize.dart
new file mode 100644
index 0000000..0df98d9
--- /dev/null
+++ b/initialize/lib/initialize.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2015, 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 initialize;
+
+// The `loader_replacer` transformer will replace this with a static_loader.
+import 'src/mirror_loader.dart' as loader;
+import 'dart:async';
+import 'dart:collection';
+
+part 'src/init_method.dart';
+part 'src/initializer.dart';
+
+/// Top level function which crawls the dependency graph and runs initializers.
+/// If [typeFilter] and/or [customFilter] are supplied then only those types of
+/// annotations will be parsed. If both filters are supplied they are treated
+/// as an AND.
+///
+/// If [from] is supplied then initializers will be found starting from the
+/// library at the supplied uri.
+///
+/// **Warning**: Do not use [from] directly in your code unless you are building
+/// a framework that will use a transformer to remove this argument later. This
+/// parameter is supported in Dartium, but [run] will throw if you use the
+/// argument after building an application with `pub build` or `pub serve`.
+Future run({List<Type> typeFilter, InitializerFilter customFilter, Uri from}) {
+  return _runInitQueue(loader.loadInitializers(
+      typeFilter: typeFilter, customFilter: customFilter, from: from));
+}
+
+Future _runInitQueue(Queue<Function> initializers) {
+  if (initializers.isEmpty) return new Future.value(null);
+
+  var initializer = initializers.removeFirst();
+  var val = initializer();
+  if (val is! Future) val = new Future.value(val);
+
+  return val.then((_) => _runInitQueue(initializers));
+}
diff --git a/initialize/lib/src/init_method.dart b/initialize/lib/src/init_method.dart
new file mode 100644
index 0000000..ebc8f93
--- /dev/null
+++ b/initialize/lib/src/init_method.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2015, 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.
+part of initialize;
+
+/// Metadata used to label static or top-level methods that are called
+/// automatically when calling static_init.run(). This class is private because
+/// it shouldn't be used directly in annotations, instead use the `initMethod`
+/// singleton below.
+typedef dynamic _ZeroArg();
+class _InitMethod implements Initializer<_ZeroArg> {
+  const _InitMethod();
+
+  @override
+  initialize(_ZeroArg method) => method();
+}
+
+/// We only ever need one instance of the `_InitMethod` class, this is it.
+const initMethod = const _InitMethod();
diff --git a/initialize/lib/src/initialize_tracker.dart b/initialize/lib/src/initialize_tracker.dart
new file mode 100644
index 0000000..6bd5982
--- /dev/null
+++ b/initialize/lib/src/initialize_tracker.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2015, 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 initialize.test.initialize_tracker;
+
+import 'package:initialize/initialize.dart';
+
+// Initializer that just saves everything it sees.
+class InitializeTracker implements Initializer<dynamic> {
+  static final List seen = [];
+
+  const InitializeTracker();
+
+  @override
+  void initialize(value) => seen.add(value);
+}
+
+const initializeTracker = const InitializeTracker();
diff --git a/initialize/lib/src/initializer.dart b/initialize/lib/src/initializer.dart
new file mode 100644
index 0000000..60a04d5
--- /dev/null
+++ b/initialize/lib/src/initializer.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2015, 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.
+part of initialize;
+
+/// Implement this class to create your own initializer.
+///
+/// Hello world example:
+///
+///   class Print implements Initializer<Type> {
+///     final String message;
+///     const Print(this.message);
+///
+///     @override
+///     initialize(Type t) => print('$t says `$message`');
+///   }
+///
+///   @Print('hello world!')
+///   class Foo {}
+///
+/// Call [run] from your main and this will print 'Foo says `hello world!`'
+///
+abstract class Initializer<T> {
+  dynamic initialize(T target);
+}
+
+/// Typedef for a custom filter function.
+typedef bool InitializerFilter(Initializer initializer);
+
+/// When annotating libraries, this is passed to the initializer.
+class LibraryIdentifier {
+  // The qualified name of the library.
+  final Symbol name;
+
+  // The package this library lives in. May be null if its the same as the root
+  // package.
+  final String package;
+
+  // The path to the library.
+  final String path;
+
+  const LibraryIdentifier(this.name, this.package, this.path);
+
+  bool operator ==(LibraryIdentifier other) =>
+      name == other.name && package == other.package && path == other.path;
+
+  String toString() => '$name: $package:$path';
+}
diff --git a/initialize/lib/src/mirror_loader.dart b/initialize/lib/src/mirror_loader.dart
new file mode 100644
index 0000000..3865e72
--- /dev/null
+++ b/initialize/lib/src/mirror_loader.dart
@@ -0,0 +1,260 @@
+// Copyright (c) 2015, 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 initialize.mirror_loader;
+
+import 'dart:collection' show Queue;
+import 'dart:mirrors';
+import 'package:path/path.dart' as path;
+import 'package:initialize/initialize.dart';
+
+final _root = currentMirrorSystem().isolate.rootLibrary;
+final _libs = currentMirrorSystem().libraries;
+
+Queue<Function> loadInitializers(
+    {List<Type> typeFilter, InitializerFilter customFilter, Uri from}) {
+  return new InitializationCrawler(typeFilter, customFilter, from: from).run();
+}
+
+// Crawls a library and all its dependencies for `Initializer` annotations using
+// mirrors
+class InitializationCrawler {
+  // Set of all visited annotations, keys are the declarations that were
+  // annotated, values are the annotations that have been processed.
+  static final _annotationsFound =
+      new Map<DeclarationMirror, Set<InstanceMirror>>();
+
+  // If non-null, then only these annotations should be processed.
+  final List<Type> typeFilter;
+
+  // If non-null, then only annotations which return true when passed to this
+  // function will be processed.
+  final InitializerFilter customFilter;
+
+  /// The library to start crawling from.
+  final LibraryMirror _rootLibrary;
+
+  /// Note: The [from] argument is only supported in the mirror_loader.dart. It
+  /// is not supported statically.
+  InitializationCrawler(this.typeFilter, this.customFilter, {Uri from})
+      : _rootLibrary = from == null
+          ? _root
+          : _libs[from] {
+    if (_rootLibrary == null) throw 'Unable to find library at $from.';
+  }
+
+  // The primary function in this class, invoke it to crawl and collect all the
+  // annotations into a queue of init functions.
+  Queue<Function> run() {
+    var librariesSeen = new Set<LibraryMirror>();
+    var queue = new Queue<Function>();
+    var libraries = currentMirrorSystem().libraries;
+
+    _readLibraryDeclarations(_rootLibrary, librariesSeen, queue);
+    return queue;
+  }
+
+  /// Returns the canonical [LibraryMirror] for a given [LibraryMirror]. This
+  /// is defined as the one loaded from a `package:` url if available, otherwise
+  /// it is just [lib].
+  LibraryMirror _canonicalLib(LibraryMirror lib) {
+    var uri = lib.uri;
+    if (_isHttpStylePackageUrl(uri)) {
+      var packageUri = _packageUriFor(uri);
+      if (_libs.containsKey(packageUri)) return _libs[packageUri];
+    }
+    return lib;
+  }
+
+  /// Returns the canonical [ClassMirror] for a given [ClassMirror]. This is
+  /// defined as the one that appears in the canonical owner [LibararyMirror].
+  ClassMirror _canonicalClassDeclaration(ClassMirror declaration) =>
+      _canonicalLib(declaration.owner).declarations[declaration.simpleName];
+
+  /// Whether [uri] is an http URI that contains a 'packages' segment, and
+  /// therefore could be converted into a 'package:' URI.
+  bool _isHttpStylePackageUrl(Uri uri) {
+    var uriPath = uri.path;
+    return uri.scheme == _root.uri.scheme &&
+        // Don't process cross-domain uris.
+        uri.authority == _root.uri.authority &&
+        uriPath.endsWith('.dart') &&
+        (uriPath.contains('/packages/') || uriPath.startsWith('packages/'));
+  }
+
+  /// Returns a `package:` version of [uri].
+  Uri _packageUriFor(Uri uri) {
+    var packagePath = uri.path
+        .substring(uri.path.lastIndexOf('packages/') + 'packages/'.length);
+    return Uri.parse('package:$packagePath');
+  }
+
+  // Reads Initializer annotations on this library and all its dependencies in
+  // post-order.
+  Queue<Function> _readLibraryDeclarations(LibraryMirror lib,
+      Set<LibraryMirror> librariesSeen, Queue<Function> queue) {
+    lib = _canonicalLib(lib);
+    if (librariesSeen.contains(lib)) return queue;
+    librariesSeen.add(lib);
+
+    // First visit all our dependencies.
+    for (var dependency in lib.libraryDependencies) {
+      // Skip dart: imports, they never use this package.
+      if (dependency.targetLibrary.uri.toString().startsWith('dart:')) continue;
+      _readLibraryDeclarations(dependency.targetLibrary, librariesSeen, queue);
+    }
+
+    // Second parse the library directive annotations.
+    _readAnnotations(lib, queue);
+
+    // Last, parse all class and method annotations.
+    for (var declaration in _sortedDeclarationsWithMetadata(lib)) {
+      _readAnnotations(declaration, queue);
+      // Check classes for static annotations which are not supported
+      if (declaration is ClassMirror) {
+        for (var classDeclaration in declaration.declarations.values) {
+          _readAnnotations(classDeclaration, queue);
+        }
+      }
+    }
+
+    return queue;
+  }
+
+  Iterable<DeclarationMirror> _sortedDeclarationsWithMetadata(
+      LibraryMirror lib) {
+    return new List()
+      ..addAll(_sortDeclarations(lib, lib.declarations.values
+          .where((d) => d is MethodMirror && d.metadata.isNotEmpty)))
+      ..addAll(_sortDeclarations(lib, lib.declarations.values
+          .where((d) => d is ClassMirror && d.metadata.isNotEmpty)));
+  }
+
+  List<DeclarationMirror> _sortDeclarations(
+      LibraryMirror sourceLib, Iterable<DeclarationMirror> declarations) {
+    var declarationList = declarations.toList();
+    declarationList.sort((DeclarationMirror a, DeclarationMirror b) {
+      // If in the same file, compare by line.
+      var aSourceUri = a.location.sourceUri;
+      var bSourceUri = b.location.sourceUri;
+      if (aSourceUri == bSourceUri) {
+        return a.location.line.compareTo(b.location.line);
+      }
+
+      // Run parts first if one is from the original library.
+      if (aSourceUri == sourceLib.uri) return 1;
+      if (bSourceUri == sourceLib.uri) return -1;
+
+      // Sort parts alphabetically.
+      return aSourceUri.path.compareTo(bSourceUri.path);
+    });
+    return declarationList;
+  }
+
+  String _declarationName(DeclarationMirror declaration) =>
+      MirrorSystem.getName(declaration.qualifiedName);
+
+  /// Reads annotations on a [DeclarationMirror] and adds them to [_initQueue]
+  /// if they are [Initializer]s.
+  void _readAnnotations(DeclarationMirror declaration, Queue<Function> queue) {
+    var annotations =
+        declaration.metadata.where((m) => _filterMetadata(declaration, m));
+    for (var meta in annotations) {
+      _annotationsFound.putIfAbsent(
+          declaration, () => new Set<InstanceMirror>());
+      _annotationsFound[declaration].add(meta);
+
+      // Initialize super classes first, if they are in the same library,
+      // otherwise we throw an error. This can only be the case if there are
+      // cycles in the imports.
+      if (declaration is ClassMirror && declaration.superclass != null) {
+        if (declaration.superclass.owner == declaration.owner) {
+          _readAnnotations(declaration.superclass, queue);
+        } else {
+          // Make sure to check the canonical superclass declaration, the one
+          // we get here is not always that. Specifically, this occurs if all of
+          // the following conditions are met:
+          //
+          //   1. The current library is never loaded via a `package:` dart
+          //      import anywhere in the program.
+          //   2. The current library loads the superclass via a relative file
+          //      import.
+          //   3. The super class is imported via a `package:` import somewhere
+          //      else in the program.
+          var canonicalSuperDeclaration =
+              _canonicalClassDeclaration(declaration.superclass);
+          var superMetas = canonicalSuperDeclaration.metadata
+              .where((m) => _filterMetadata(canonicalSuperDeclaration, m))
+              .toList();
+          if (superMetas.isNotEmpty) {
+            throw new UnsupportedError(
+                'We have detected a cycle in your import graph when running '
+                'initializers on ${declaration.qualifiedName}. This means the '
+                'super class ${canonicalSuperDeclaration.qualifiedName} has a '
+                'dependency on this library (possibly transitive).');
+          }
+        }
+      }
+
+      var annotatedValue;
+      if (declaration is ClassMirror) {
+        annotatedValue = declaration.reflectedType;
+      } else if (declaration is MethodMirror) {
+        if (declaration.owner is! LibraryMirror) {
+          // TODO(jakemac): Support static class methods.
+          throw _TOP_LEVEL_FUNCTIONS_ONLY;
+        }
+        annotatedValue = (declaration.owner as ObjectMirror)
+            .getField(declaration.simpleName).reflectee;
+      } else if (declaration is LibraryMirror) {
+        var package;
+        var filePath;
+        Uri uri = declaration.uri;
+        // Convert to a package style uri if possible.
+        if (_isHttpStylePackageUrl(uri)) {
+          uri = _packageUriFor(uri);
+        }
+        if (uri.scheme == 'file' || uri.scheme.startsWith('http')) {
+          filePath = path.url.relative(uri.path,
+              from: _root.uri.path.endsWith('/')
+                  ? _root.uri.path
+                  : path.url.dirname(_root.uri.path));
+        } else if (uri.scheme == 'package') {
+          var segments = uri.pathSegments;
+          package = segments[0];
+          filePath = path.url.joinAll(segments.getRange(1, segments.length));
+        } else {
+          throw new UnsupportedError('Unsupported uri scheme ${uri.scheme} for '
+              'library ${declaration}.');
+        }
+        annotatedValue =
+            new LibraryIdentifier(declaration.qualifiedName, package, filePath);
+      } else {
+        throw _UNSUPPORTED_DECLARATION;
+      }
+      queue.addLast(() => meta.reflectee.initialize(annotatedValue));
+    }
+  }
+
+  // Filter function that returns true only if `meta` is an `Initializer`,
+  // it passes the `typeFilter` and `customFilter` if they exist, and it has not
+  // yet been seen.
+  bool _filterMetadata(DeclarationMirror declaration, InstanceMirror meta) {
+    if (meta.reflectee is! Initializer) return false;
+    if (typeFilter != null &&
+        !typeFilter.any((t) => meta.reflectee.runtimeType == t)) {
+      return false;
+    }
+    if (customFilter != null && !customFilter(meta.reflectee)) return false;
+    if (!_annotationsFound.containsKey(declaration)) return true;
+    if (_annotationsFound[declaration].contains(meta)) return false;
+    return true;
+  }
+}
+
+final _TOP_LEVEL_FUNCTIONS_ONLY = new UnsupportedError(
+    'Only top level methods are supported for initializers');
+
+final _UNSUPPORTED_DECLARATION = new UnsupportedError(
+    'Initializers are only supported on libraries, classes, and top level '
+    'methods');
diff --git a/initialize/lib/src/static_loader.dart b/initialize/lib/src/static_loader.dart
new file mode 100644
index 0000000..edc679b
--- /dev/null
+++ b/initialize/lib/src/static_loader.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2015, 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 initialize.static_loader;
+
+import 'dart:collection' show Queue;
+import 'package:initialize/initialize.dart';
+
+/// This represents an annotation/declaration pair.
+class InitEntry<T> {
+  /// The annotation instance.
+  final Initializer<T> meta;
+
+  /// The target of the annotation to pass to initialize.
+  final T target;
+
+  InitEntry(this.meta, this.target);
+}
+
+/// Set of initializers that are invoked by `run`.  This is initialized with
+/// code automatically generated by the transformer.
+Queue<InitEntry> initializers = new Queue<InitEntry>();
+
+/// Returns initializer functions matching the supplied filters and removes them
+/// from `initializers` so they won't be ran again.
+Queue<Function> loadInitializers(
+    {List<Type> typeFilter, InitializerFilter customFilter, Uri from}) {
+  if (from != null) {
+    throw 'The `from` option is not supported in deploy mode.';
+  }
+  Queue<Function> result = new Queue<Function>();
+
+  var matchesFilters = (initializer) {
+    if (typeFilter != null &&
+        !typeFilter.any((t) => initializer.meta.runtimeType == t)) {
+      return false;
+    }
+    if (customFilter != null && !customFilter(initializer.meta)) {
+      return false;
+    }
+    return true;
+  };
+
+  result.addAll(initializers
+      .where(matchesFilters)
+      .map((i) => () => i.meta.initialize(i.target)));
+
+  initializers.removeWhere(matchesFilters);
+
+  return result;
+}
diff --git a/initialize/lib/transformer.dart b/initialize/lib/transformer.dart
new file mode 100644
index 0000000..a1f01fa
--- /dev/null
+++ b/initialize/lib/transformer.dart
@@ -0,0 +1,464 @@
+// Copyright (c) 2015, 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 initialize.transformer;
+
+import 'dart:async';
+import 'dart:collection' show Queue;
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:barback/barback.dart';
+import 'package:code_transformers/assets.dart';
+import 'package:code_transformers/resolver.dart';
+import 'package:code_transformers/src/dart_sdk.dart' as dart_sdk;
+import 'package:dart_style/dart_style.dart';
+import 'package:glob/glob.dart';
+import 'package:html/dom.dart' as dom;
+import 'package:html/parser.dart' show parse;
+import 'package:path/path.dart' as path;
+
+import 'build/initializer_plugin.dart';
+export 'build/initializer_plugin.dart';
+
+/// Create a new [Asset] which inlines your [Initializer] annotations into
+/// a new file that bootstraps your application.
+Asset generateBootstrapFile(Resolver resolver, Transform transform,
+    AssetId primaryAssetId, AssetId newEntryPointId,
+    {bool errorIfNotFound: true, List<InitializerPlugin> plugins,
+    bool appendDefaultPlugin: true}) {
+  if (appendDefaultPlugin) {
+    if (plugins == null) plugins = [];
+    plugins.add(const DefaultInitializerPlugin());
+  }
+  return new _BootstrapFileBuilder(
+      resolver, transform, primaryAssetId, newEntryPointId, errorIfNotFound,
+      plugins: plugins).run();
+}
+
+/// Transformer which removes the mirror-based initialization logic and replaces
+/// it with static logic.
+class InitializeTransformer extends Transformer {
+  final Resolvers _resolvers;
+  final Iterable<Glob> _entryPointGlobs;
+  final bool _errorIfNotFound;
+  final List<InitializerPlugin> plugins;
+
+  InitializeTransformer(List<String> entryPoints,
+      {bool errorIfNotFound: true, this.plugins})
+      : _entryPointGlobs = entryPoints.map((e) => new Glob(e)),
+        _errorIfNotFound = errorIfNotFound,
+        _resolvers = new Resolvers.fromMock(dart_sdk.mockSdkSources);
+
+  factory InitializeTransformer.asPlugin(BarbackSettings settings) =>
+      new InitializeTransformer(_readFileList(settings, 'entry_points'));
+
+  bool isPrimary(AssetId id) => _entryPointGlobs.any((g) => g.matches(id.path));
+
+  Future apply(Transform transform) {
+    if (transform.primaryInput.id.path.endsWith('.dart')) {
+      return _buildBootstrapFile(transform);
+    } else if (transform.primaryInput.id.path.endsWith('.html')) {
+      return transform.primaryInput.readAsString().then((html) {
+        var document = parse(html);
+        var originalDartFile =
+            _findMainScript(document, transform.primaryInput.id, transform);
+        return _buildBootstrapFile(transform, primaryId: originalDartFile)
+            .then((AssetId newDartFile) {
+          return _replaceEntryWithBootstrap(transform, document,
+              transform.primaryInput.id, originalDartFile, newDartFile);
+        });
+      });
+    } else {
+      transform.logger.warning(
+          'Invalid entry point ${transform.primaryInput.id}. Must be either a '
+          '.dart or .html file.');
+    }
+    return new Future.value();
+  }
+
+  // Returns the AssetId of the newly created bootstrap file.
+  Future<AssetId> _buildBootstrapFile(Transform transform,
+      {AssetId primaryId}) {
+    if (primaryId == null) primaryId = transform.primaryInput.id;
+    var newEntryPointId = new AssetId(primaryId.package,
+        '${path.url.withoutExtension(primaryId.path)}.initialize.dart');
+    return transform.hasInput(newEntryPointId).then((exists) {
+      if (exists) {
+        transform.logger
+            .error('New entry point file $newEntryPointId already exists.');
+        return null;
+      }
+
+      return _resolvers.get(transform, [primaryId]).then((resolver) {
+        transform.addOutput(generateBootstrapFile(
+            resolver, transform, primaryId, newEntryPointId,
+            errorIfNotFound: _errorIfNotFound, plugins: plugins));
+        resolver.release();
+        return newEntryPointId;
+      });
+    });
+  }
+  // Finds the first (and only) dart script on an html page and returns the
+  // [AssetId] that points to it
+  AssetId _findMainScript(
+      dom.Document document, AssetId entryPoint, Transform transform) {
+    var scripts = document.querySelectorAll('script[type="application/dart"]');
+    if (scripts.length != 1) {
+      transform.logger.error('Expected exactly one dart script in $entryPoint '
+          'but found ${scripts.length}.');
+      return null;
+    }
+
+    var src = scripts[0].attributes['src'];
+    if (src == null) {
+      // TODO(jakemac): Support inline scripts,
+      transform.logger.error('Inline scripts are not supported at this time, '
+          'see https://github.com/dart-lang/initialize/issues/20.');
+      return null;
+    }
+
+    return uriToAssetId(
+        entryPoint, src, transform.logger, scripts[0].sourceSpan);
+  }
+
+  // Replaces script tags pointing to [originalDartFile] with [newDartFile] in
+  // [entryPoint].
+  void _replaceEntryWithBootstrap(Transform transform, dom.Document document,
+      AssetId entryPoint, AssetId originalDartFile, AssetId newDartFile) {
+    var found = false;
+
+    var scripts = document
+        .querySelectorAll('script[type="application/dart"]')
+        .where((script) {
+      var assetId = uriToAssetId(entryPoint, script.attributes['src'],
+          transform.logger, script.sourceSpan);
+      return assetId == originalDartFile;
+    }).toList();
+
+    if (scripts.length != 1) {
+      transform.logger.error(
+          'Expected exactly one script pointing to $originalDartFile in '
+          '$entryPoint, but found ${scripts.length}.');
+      return;
+    }
+    scripts[0].attributes['src'] = path.url.relative(newDartFile.path,
+        from: path.dirname(entryPoint.path));
+    transform.addOutput(new Asset.fromString(entryPoint, document.outerHtml));
+  }
+}
+
+// Class which builds a bootstrap file.
+class _BootstrapFileBuilder {
+  final Resolver _resolver;
+  final Transform _transform;
+  final bool _errorIfNotFound;
+  AssetId _entryPoint;
+  AssetId _newEntryPoint;
+
+  /// The resolved initialize library.
+  LibraryElement _initializeLibrary;
+  /// The resolved Initializer class from the initialize library.
+  ClassElement _initializer;
+
+  /// Queue for intialization annotations.
+  final _initQueue = new Queue<InitializerData>();
+  /// All the annotations we have seen for each element
+  final _seenAnnotations = new Map<Element, Set<ElementAnnotation>>();
+
+  /// The list of [InitializerPlugin]s to apply. The first plugin which asks to
+  /// be applied to a given initializer is the only one that will apply.
+  List<InitializerPlugin> _plugins;
+
+  TransformLogger _logger;
+
+  _BootstrapFileBuilder(this._resolver, this._transform, this._entryPoint,
+      this._newEntryPoint, this._errorIfNotFound,
+      {List<InitializerPlugin> plugins}) {
+    _logger = _transform.logger;
+    _initializeLibrary =
+        _resolver.getLibrary(new AssetId('initialize', 'lib/initialize.dart'));
+    if (_initializeLibrary != null) {
+      _initializer = _initializeLibrary.getType('Initializer');
+    } else if (_errorIfNotFound) {
+      _logger.warning('Unable to read "package:initialize/initialize.dart". '
+          'This file must be imported via $_entryPoint or a transitive '
+          'dependency.');
+    }
+    _plugins = plugins != null ? plugins : [const DefaultInitializerPlugin()];
+  }
+
+  /// Creates and returns the new bootstrap file.
+  Asset run() {
+    var entryLib = _resolver.getLibrary(_entryPoint);
+    _readLibraries(entryLib);
+
+    return new Asset.fromString(_newEntryPoint, _buildNewEntryPoint(entryLib));
+  }
+
+  /// Reads Initializer annotations on this library and all its dependencies in
+  /// post-order.
+  void _readLibraries(LibraryElement library, [Set<LibraryElement> seen]) {
+    if (seen == null) seen = new Set<LibraryElement>();
+    seen.add(library);
+
+    // Visit all our dependencies.
+    for (var library in _sortedLibraryDependencies(library)) {
+      // Don't include anything from the sdk.
+      if (library.isInSdk) continue;
+      if (seen.contains(library)) continue;
+      _readLibraries(library, seen);
+    }
+
+    // Read annotations in this order: library, top level methods, classes.
+    _readAnnotations(library);
+    for (var method in _topLevelMethodsOfLibrary(library, seen)) {
+      _readAnnotations(method);
+    }
+    for (var clazz in _classesOfLibrary(library, seen)) {
+      readSuperClassAnnotations(InterfaceType superClass) {
+        if (superClass == null) return;
+        readSuperClassAnnotations(superClass.superclass);
+        if (_readAnnotations(superClass.element) &&
+            superClass.element.library != clazz.library) {
+          _logger.warning(
+              'We have detected a cycle in your import graph when running '
+              'initializers on ${clazz.name}. This means the super class '
+              '${superClass.name} has a dependency on this library '
+              '(possibly transitive).');
+        }
+      }
+      readSuperClassAnnotations(clazz.supertype);
+      _readAnnotations(clazz);
+    }
+  }
+
+  bool _readAnnotations(Element element) {
+    var found = false;
+    if (element.metadata.isEmpty) return found;
+
+    var metaNodes;
+    var node = element.node;
+    if (node is SimpleIdentifier && node.parent is LibraryIdentifier) {
+      metaNodes = node.parent.parent.metadata;
+    } else if (node is ClassDeclaration || node is FunctionDeclaration) {
+      metaNodes = node.metadata;
+    } else {
+      return found;
+    }
+
+    metaNodes.where((Annotation metaNode) {
+      // First filter out anything that is not a Initializer.
+      var meta = metaNode.elementAnnotation;
+      var e = meta.element;
+      if (e is PropertyAccessorElement) {
+        return _isInitializer(e.variable.evaluationResult.value.type);
+      } else if (e is ConstructorElement) {
+        return _isInitializer(e.returnType);
+      }
+      return false;
+    }).where((Annotation metaNode) {
+      var meta = metaNode.elementAnnotation;
+      _seenAnnotations.putIfAbsent(element, () => new Set<ElementAnnotation>());
+      return !_seenAnnotations[element].contains(meta);
+    }).forEach((Annotation metaNode) {
+      var meta = metaNode.elementAnnotation;
+      _seenAnnotations[element].add(meta);
+      _initQueue.addLast(new InitializerData._(node, metaNode));
+      found = true;
+    });
+    return found;
+  }
+
+  String _buildNewEntryPoint(LibraryElement entryLib) {
+    var importsBuffer = new StringBuffer();
+    var initializersBuffer = new StringBuffer();
+    var libraryPrefixes = new Map<LibraryElement, String>();
+
+    // Import the static_loader, initializer, and original entry point.
+    importsBuffer
+        .writeln("import 'package:initialize/src/static_loader.dart';");
+    importsBuffer.writeln("import 'package:initialize/initialize.dart';");
+    libraryPrefixes[entryLib] = 'i0';
+
+    initializersBuffer.writeln('initializers.addAll([');
+    while (_initQueue.isNotEmpty) {
+      var next = _initQueue.removeFirst();
+
+      libraryPrefixes.putIfAbsent(
+          next.targetElement.library, () => 'i${libraryPrefixes.length}');
+      libraryPrefixes.putIfAbsent(next.annotationElement.element.library,
+          () => 'i${libraryPrefixes.length}');
+
+      // Run the first plugin which asks to be ran and then stop.
+      var data = new InitializerPluginData(
+          next, _newEntryPoint, libraryPrefixes, _resolver, _logger);
+      var plugin = _plugins.firstWhere((p) => p.shouldApply(data), orElse: () {
+        _logger.error('No InitializerPlugin handled the annotation: '
+            '${next.annotationElement} on: ${next.targetElement}.');
+      });
+      if (plugin == null) continue;
+
+      var text = plugin.apply(data);
+      if (text != null) initializersBuffer.writeln('$text,');
+    }
+    initializersBuffer.writeln(']);');
+
+    libraryPrefixes
+        .forEach((lib, prefix) => _writeImport(lib, prefix, importsBuffer));
+
+    // TODO(jakemac): copyright and library declaration
+    return new DartFormatter().format('''
+$importsBuffer
+main() {
+$initializersBuffer
+  return i0.main();
+}
+''');
+  }
+
+  _writeImport(LibraryElement lib, String prefix, StringBuffer buffer) {
+    AssetId id = (lib.source as dynamic).assetId;
+
+    if (id.path.startsWith('lib/')) {
+      var packagePath = id.path.replaceFirst('lib/', '');
+      buffer.write("import 'package:${id.package}/${packagePath}'");
+    } else if (id.package != _newEntryPoint.package) {
+      _logger.error("Can't import `${id}` from `${_newEntryPoint}`");
+    } else if (path.url.split(id.path)[0] ==
+        path.url.split(_newEntryPoint.path)[0]) {
+      var relativePath = path.url.relative(id.path,
+          from: path.url.dirname(_newEntryPoint.path));
+      buffer.write("import '${relativePath}'");
+    } else {
+      _logger.error("Can't import `${id}` from `${_newEntryPoint}`");
+    }
+    buffer.writeln(' as $prefix;');
+  }
+
+  bool _isInitializer(InterfaceType type) {
+    // If `_initializer` wasn't found then it was never loaded (even
+    // transitively), and so no annotations can be initializers.
+    if (_initializer == null) return false;
+    if (type == null) return false;
+    if (type.element.type == _initializer.type) return true;
+    if (_isInitializer(type.superclass)) return true;
+    for (var interface in type.interfaces) {
+      if (_isInitializer(interface)) return true;
+    }
+    return false;
+  }
+
+  /// Retrieves all top-level methods that are visible if you were to import
+  /// [lib]. This includes exported methods from other libraries too.
+  List<FunctionElement> _topLevelMethodsOfLibrary(
+      LibraryElement library, Set<LibraryElement> seen) {
+    var methods = [];
+
+    var orderedExports = new List.from(library.exports)
+      ..sort((a, b) => a.uriOffset.compareTo(b.uriOffset));
+    for (var export in orderedExports) {
+      if (seen.contains(export.exportedLibrary)) continue;
+      methods.addAll(_topLevelMethodsOfLibrary(export.exportedLibrary, seen));
+    }
+
+    for (CompilationUnitElement unit in _orderedUnits(library)) {
+      methods.addAll(new List.from(unit.functions)
+        ..sort((a, b) => a.nameOffset.compareTo(b.nameOffset)));
+    }
+
+    return methods;
+  }
+
+  /// Retrieves all classes that are visible if you were to import [lib]. This
+  /// includes exported classes from other libraries.
+  List<ClassElement> _classesOfLibrary(
+      LibraryElement library, Set<LibraryElement> seen) {
+    var classes = [];
+
+    var orderedExports = new List.from(library.exports)
+      ..sort((a, b) => a.uriOffset.compareTo(b.uriOffset));
+    for (var export in orderedExports) {
+      if (seen.contains(export.exportedLibrary)) continue;
+      classes.addAll(_classesOfLibrary(export.exportedLibrary, seen));
+    }
+
+    for (var unit in _orderedUnits(library)) {
+      classes.addAll(new List.from(unit.types)
+        ..sort((a, b) => a.nameOffset.compareTo(b.nameOffset)));
+    }
+
+    return classes;
+  }
+
+  List<CompilationUnitElement> _orderedUnits(LibraryElement library) {
+    var definingUnit = library.definingCompilationUnit;
+    // The first item is the source library, remove it for now.
+    return new List.from(library.units)
+      ..sort((a, b) {
+        if (a == definingUnit) return 1;
+        if (b == definingUnit) return -1;
+        return a.uri.compareTo(b.uri);
+      });
+  }
+
+  Iterable<LibraryElement> _sortedLibraryDependencies(LibraryElement library) {
+    // TODO(jakemac): Investigate supporting annotations on part-of directives.
+    getLibrary(UriReferencedElement element) {
+      if (element is ImportElement) return element.importedLibrary;
+      if (element is ExportElement) return element.exportedLibrary;
+    }
+
+    return (new List.from(library.imports)
+      ..addAll(library.exports)
+      ..sort((a, b) => a.nameOffset.compareTo(b.nameOffset))).map(getLibrary);
+  }
+}
+
+/// An [Initializer] annotation and the target of that annotation.
+class InitializerData {
+  /// The target [AstNode] of the annotation.
+  final AstNode targetNode;
+
+  /// The [Annotation] representing the annotation itself.
+  final Annotation annotationNode;
+
+  /// The [ElementAnnotation] representing the annotation itself.
+  ElementAnnotation get annotationElement => annotationNode.elementAnnotation;
+
+  /// The target [Element] of the annotation.
+  Element get targetElement {
+    if (targetNode is SimpleIdentifier &&
+        targetNode.parent is LibraryIdentifier) {
+      return (targetNode.parent.parent as LibraryDirective).element;
+    } else if (targetNode is ClassDeclaration ||
+        targetNode is FunctionDeclaration) {
+      return (targetNode as dynamic).element;
+    } else {
+      return null;
+    }
+  }
+
+  InitializerData._(this.targetNode, this.annotationNode);
+}
+
+// Reads a file list from a barback settings configuration field.
+_readFileList(BarbackSettings settings, String field) {
+  var value = settings.configuration[field];
+  if (value == null) return null;
+  var files = [];
+  bool error;
+  if (value is List) {
+    files = value;
+    error = value.any((e) => e is! String);
+  } else if (value is String) {
+    files = [value];
+    error = false;
+  } else {
+    error = true;
+  }
+  if (error) {
+    print('Bad value for "$field" in the initialize transformer. '
+        'Expected either one String or a list of Strings.');
+  }
+  return files;
+}
diff --git a/initialize/pubspec.yaml b/initialize/pubspec.yaml
new file mode 100644
index 0000000..4454681
--- /dev/null
+++ b/initialize/pubspec.yaml
@@ -0,0 +1,36 @@
+name: initialize
+version: 0.6.1
+author: Polymer.dart Authors <web@dartlang.org>
+description: Generic building blocks for doing static initialization.
+homepage: https://github.com/dart-lang/initialize
+dependencies:
+  analyzer: '>=0.15.6 <0.26.0'
+  barback: '>=0.14.2 <0.16.0'
+  code_transformers: '>=0.2.7 <0.3.0'
+  dart_style: '>=0.1.3 <0.2.0'
+  glob: ">=1.0.4 <2.0.0"
+  html: '>=0.12.0 <0.13.0'
+  path: '>=1.3.0 <2.0.0'
+dev_dependencies:
+  test_package:
+    path: test_package
+  unittest: '>=0.10.0 <0.12.0'
+environment:
+  sdk: ">=1.9.0-dev.7.1 <2.0.0"
+transformers:
+- initialize/build/loader_replacer:
+    $include: lib/initialize.dart
+- initialize:
+    $include: '**/*_test.dart'
+    entry_points:
+      - test/deferred_library_test.dart
+      - test/initializer_test.dart
+      - test/initializer_from_test.dart
+      - test/initializer_parts_test.dart
+      - test/initializer_super_test.dart
+      - test/initializer_cycle_error_test.dart
+      - test/initializer_custom_filter_test.dart
+      - test/initializer_type_filter_test.dart
+      - test/init_method_test.dart
+- $dart2js:
+    $exclude: '**/*.dart'
diff --git a/intl/lib/date_symbol_data_file.dart b/intl/lib/date_symbol_data_file.dart
new file mode 100644
index 0000000..c1b5ea4
--- /dev/null
+++ b/intl/lib/date_symbol_data_file.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * This file should be imported, along with date_format.dart in order to read
+ * locale data from files in the file system.
+ */
+
+library date_symbol_data_file;
+
+import 'dart:async';
+
+import 'package:path/path.dart' as path;
+
+import 'date_symbols.dart';
+import 'src/data/dates/locale_list.dart';
+import 'src/date_format_internal.dart';
+import 'src/file_data_reader.dart';
+import 'src/lazy_locale_data.dart';
+
+export 'src/data/dates/locale_list.dart';
+
+/**
+ * This should be called for at least one [locale] before any date formatting
+ * methods are called. It sets up the lookup for date symbols using [path].
+ * The [path] parameter should end with a directory separator appropriate
+ * for the platform.
+ */
+Future initializeDateFormatting(String locale, String filePath) {
+  var reader = new FileDataReader(path.join(filePath, 'symbols'));
+  initializeDateSymbols(() => new LazyLocaleData(
+      reader, _createDateSymbol, availableLocalesForDateFormatting));
+  var reader2 = new FileDataReader(path.join(filePath, 'patterns'));
+  initializeDatePatterns(() =>
+      new LazyLocaleData(reader2, (x) => x, availableLocalesForDateFormatting));
+  return initializeIndividualLocaleDateFormatting((symbols, patterns) {
+    return Future
+        .wait([symbols.initLocale(locale), patterns.initLocale(locale)]);
+  });
+}
+
+/** Defines how new date symbol entries are created. */
+DateSymbols _createDateSymbol(Map map) =>
+    new DateSymbols.deserializeFromMap(map);
diff --git a/intl/lib/date_symbol_data_http_request.dart b/intl/lib/date_symbol_data_http_request.dart
new file mode 100644
index 0000000..5e206f6
--- /dev/null
+++ b/intl/lib/date_symbol_data_http_request.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * This file should be imported, along with date_format.dart in order to read
+ * locale data via http requests to a web server..
+ */
+library date_symbol_data_http_request;
+
+import 'dart:async';
+
+import 'date_symbols.dart';
+import 'intl.dart';
+import 'src/data/dates/locale_list.dart';
+import 'src/date_format_internal.dart';
+import 'src/http_request_data_reader.dart';
+import 'src/lazy_locale_data.dart';
+
+export 'src/data/dates/locale_list.dart';
+
+/**
+ * This should be called for at least one [locale] before any date formatting
+ * methods are called. It sets up the lookup for date symbols using [url].
+ * The [url] parameter should end with a "/". For example,
+ *   "http://localhost:8000/dates/"
+ */
+Future initializeDateFormatting(String locale, String url) {
+  var reader = new HttpRequestDataReader('${url}symbols/');
+  initializeDateSymbols(() => new LazyLocaleData(
+      reader, _createDateSymbol, availableLocalesForDateFormatting));
+  var reader2 = new HttpRequestDataReader('${url}patterns/');
+  initializeDatePatterns(() =>
+      new LazyLocaleData(reader2, (x) => x, availableLocalesForDateFormatting));
+  var actualLocale = Intl.verifiedLocale(
+      locale, (l) => availableLocalesForDateFormatting.contains(l));
+  return initializeIndividualLocaleDateFormatting((symbols, patterns) {
+    return Future.wait(
+        [symbols.initLocale(actualLocale), patterns.initLocale(actualLocale)]);
+  });
+}
+
+/** Defines how new date symbol entries are created. */
+DateSymbols _createDateSymbol(Map map) =>
+    new DateSymbols.deserializeFromMap(map);
diff --git a/intl/lib/date_symbol_data_local.dart b/intl/lib/date_symbol_data_local.dart
new file mode 100644
index 0000000..6e3a1e7
--- /dev/null
+++ b/intl/lib/date_symbol_data_local.dart
@@ -0,0 +1,14156 @@
+// 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.
+
+/**
+ * Date/time formatting symbols for all locales.
+ *
+ * DO NOT EDIT. This file is autogenerated by script.  See
+ * 'http://go/generate_datetime_constants.py' using the --for_dart
+ * flag.
+ * File generated from CLDR ver. 25
+ *
+ * Before checkin, this file could have been manually edited. This is
+ * to incorporate changes before we could correct CLDR. All manual
+ * modification must be documented in this section, and should be
+ * removed after those changes land to CLDR.
+ */
+library date_symbol_data_local;
+
+import "date_symbols.dart";
+import "src/date_format_internal.dart";
+import "date_time_patterns.dart";
+import "dart:async";
+
+/**
+ * This should be called for at least one [locale] before any date
+ * formatting methods are called. It sets up the lookup for date
+ * symbols. Both the [locale] and [ignored] parameter are ignored, as
+ * the data for all locales is directly available.
+ */
+Future initializeDateFormatting(String locale, String ignored) {
+  initializeDateSymbols(dateTimeSymbolMap);
+  initializeDatePatterns(dateTimePatternMap);
+  return new Future.value(null);
+}
+
+/**
+ * Returns a Map from locale names to the DateSymbols instance for
+ * that locale. Internal use only. Call initializeDateFormatting
+ * instead.
+ */
+Map dateTimeSymbolMap() => {
+  /**
+   * Date/time formatting symbols for locale en_ISO.
+   */
+  "en_ISO": new DateSymbols(
+      NAME: 'en_ISO',
+      ERAS: const ['BC', 'AD'],
+      ERANAMES: const ['Before Christ', 'Anno Domini'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      STANDALONEMONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      WEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      SHORTWEEKDAYS: const ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Sun',
+    'Mon',
+    'Tue',
+    'Wed',
+    'Thu',
+    'Fri',
+    'Sat'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    '1st quarter',
+    '2nd quarter',
+    '3rd quarter',
+    '4th quarter'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const [
+    'EEEE, y MMMM dd',
+    'y MMMM d',
+    'y MMM d',
+    'yyyy-MM-dd'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss v', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const [
+    '{1} \'at\' {0}',
+    '{1} \'at\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      AVAILABLEFORMATS: const {'Md': 'M/d', 'MMMMd': 'MMMM d', 'MMMd': 'MMM d'},
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+
+  /**
+   * Date/time formatting symbols for locale af.
+   */
+  "af": new DateSymbols(
+      NAME: "af",
+      ERAS: const ['v.C.', 'n.C.'],
+      ERANAMES: const ['voor Christus', 'na Christus'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Januarie',
+    'Februarie',
+    'Maart',
+    'April',
+    'Mei',
+    'Junie',
+    'Julie',
+    'Augustus',
+    'September',
+    'Oktober',
+    'November',
+    'Desember'
+  ],
+      STANDALONEMONTHS: const [
+    'Januarie',
+    'Februarie',
+    'Maart',
+    'April',
+    'Mei',
+    'Junie',
+    'Julie',
+    'Augustus',
+    'September',
+    'Oktober',
+    'November',
+    'Desember'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'Mei',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Des'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'Mei',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Des'
+  ],
+      WEEKDAYS: const [
+    'Sondag',
+    'Maandag',
+    'Dinsdag',
+    'Woensdag',
+    'Donderdag',
+    'Vrydag',
+    'Saterdag'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sondag',
+    'Maandag',
+    'Dinsdag',
+    'Woensdag',
+    'Donderdag',
+    'Vrydag',
+    'Saterdag'
+  ],
+      SHORTWEEKDAYS: const ['So', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Sa'],
+      STANDALONESHORTWEEKDAYS: const ['So', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Sa'],
+      NARROWWEEKDAYS: const ['S', 'M', 'D', 'W', 'D', 'V', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'D', 'W', 'D', 'V', 'S'],
+      SHORTQUARTERS: const ['K1', 'K2', 'K3', 'K4'],
+      QUARTERS: const [
+    '1ste kwartaal',
+    '2de kwartaal',
+    '3de kwartaal',
+    '4de kwartaal'
+  ],
+      AMPMS: const ['vm.', 'nm.'],
+      DATEFORMATS: const ['EEEE dd MMMM y', 'dd MMMM y', 'dd MMM y', 'y-MM-dd'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale am.
+   */
+  "am": new DateSymbols(
+      NAME: "am",
+      ERAS: const ['ዓ/ዓ', 'ዓ/ም'],
+      ERANAMES: const ['ዓመተ ዓለም', 'ዓመተ ምሕረት'],
+      NARROWMONTHS: const [
+    'ጃ',
+    'ፌ',
+    'ማ',
+    'ኤ',
+    'ሜ',
+    'ጁ',
+    'ጁ',
+    'ኦ',
+    'ሴ',
+    'ኦ',
+    'ኖ',
+    'ዲ'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ጃ',
+    'ፌ',
+    'ማ',
+    'ኤ',
+    'ሜ',
+    'ጁ',
+    'ጁ',
+    'ኦ',
+    'ሴ',
+    'ኦ',
+    'ኖ',
+    'ዲ'
+  ],
+      MONTHS: const [
+    'ጃንዩወሪ',
+    'ፌብሩወሪ',
+    'ማርች',
+    'ኤፕሪል',
+    'ሜይ',
+    'ጁን',
+    'ጁላይ',
+    'ኦገስት',
+    'ሴፕቴምበር',
+    'ኦክተውበር',
+    'ኖቬምበር',
+    'ዲሴምበር'
+  ],
+      STANDALONEMONTHS: const [
+    'ጃንዩወሪ',
+    'ፌብሩወሪ',
+    'ማርች',
+    'ኤፕሪል',
+    'ሜይ',
+    'ጁን',
+    'ጁላይ',
+    'ኦገስት',
+    'ሴፕቴምበር',
+    'ኦክቶበር',
+    'ኖቬምበር',
+    'ዲሴምበር'
+  ],
+      SHORTMONTHS: const [
+    'ጃንዩ',
+    'ፌብሩ',
+    'ማርች',
+    'ኤፕሪ',
+    'ሜይ',
+    'ጁን',
+    'ጁላይ',
+    'ኦገስ',
+    'ሴፕቴ',
+    'ኦክተ',
+    'ኖቬም',
+    'ዲሴም'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ጃንዩ',
+    'ፌብሩ',
+    'ማርች',
+    'ኤፕሪ',
+    'ሜይ',
+    'ጁን',
+    'ጁላይ',
+    'ኦገስ',
+    'ሴፕቴ',
+    'ኦክቶ',
+    'ኖቬም',
+    'ዲሴም'
+  ],
+      WEEKDAYS: const ['እሑድ', 'ሰኞ', 'ማክሰኞ', 'ረቡዕ', 'ሐሙስ', 'ዓርብ', 'ቅዳሜ'],
+      STANDALONEWEEKDAYS: const [
+    'እሑድ',
+    'ሰኞ',
+    'ማክሰኞ',
+    'ረቡዕ',
+    'ሐሙስ',
+    'ዓርብ',
+    'ቅዳሜ'
+  ],
+      SHORTWEEKDAYS: const ['እሑድ', 'ሰኞ', 'ማክሰ', 'ረቡዕ', 'ሐሙስ', 'ዓርብ', 'ቅዳሜ'],
+      STANDALONESHORTWEEKDAYS: const [
+    'እሑድ',
+    'ሰኞ',
+    'ማክሰ',
+    'ረቡዕ',
+    'ሐሙስ',
+    'ዓርብ',
+    'ቅዳሜ'
+  ],
+      NARROWWEEKDAYS: const ['እ', 'ሰ', 'ማ', 'ረ', 'ሐ', 'ዓ', 'ቅ'],
+      STANDALONENARROWWEEKDAYS: const ['እ', 'ሰ', 'ማ', 'ረ', 'ሐ', 'ዓ', 'ቅ'],
+      SHORTQUARTERS: const ['ሩብ1', 'ሩብ2', 'ሩብ3', 'ሩብ4'],
+      QUARTERS: const ['1ኛው ሩብ', 'ሁለተኛው ሩብ', '3ኛው ሩብ', '4ኛው ሩብ'],
+      AMPMS: const ['ጥዋት', 'ከሰዓት'],
+      DATEFORMATS: const ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale ar.
+   */
+  "ar": new DateSymbols(
+      NAME: "ar",
+      ERAS: const ['ق.م', 'م'],
+      ERANAMES: const ['قبل الميلاد', 'ميلادي'],
+      NARROWMONTHS: const [
+    'ي',
+    'ف',
+    'م',
+    'أ',
+    'و',
+    'ن',
+    'ل',
+    'غ',
+    'س',
+    'ك',
+    'ب',
+    'د'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ي',
+    'ف',
+    'م',
+    'أ',
+    'و',
+    'ن',
+    'ل',
+    'غ',
+    'س',
+    'ك',
+    'ب',
+    'د'
+  ],
+      MONTHS: const [
+    'يناير',
+    'فبراير',
+    'مارس',
+    'أبريل',
+    'مايو',
+    'يونيو',
+    'يوليو',
+    'أغسطس',
+    'سبتمبر',
+    'أكتوبر',
+    'نوفمبر',
+    'ديسمبر'
+  ],
+      STANDALONEMONTHS: const [
+    'يناير',
+    'فبراير',
+    'مارس',
+    'أبريل',
+    'مايو',
+    'يونيو',
+    'يوليو',
+    'أغسطس',
+    'سبتمبر',
+    'أكتوبر',
+    'نوفمبر',
+    'ديسمبر'
+  ],
+      SHORTMONTHS: const [
+    'يناير',
+    'فبراير',
+    'مارس',
+    'أبريل',
+    'مايو',
+    'يونيو',
+    'يوليو',
+    'أغسطس',
+    'سبتمبر',
+    'أكتوبر',
+    'نوفمبر',
+    'ديسمبر'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'يناير',
+    'فبراير',
+    'مارس',
+    'أبريل',
+    'مايو',
+    'يونيو',
+    'يوليو',
+    'أغسطس',
+    'سبتمبر',
+    'أكتوبر',
+    'نوفمبر',
+    'ديسمبر'
+  ],
+      WEEKDAYS: const [
+    'الأحد',
+    'الاثنين',
+    'الثلاثاء',
+    'الأربعاء',
+    'الخميس',
+    'الجمعة',
+    'السبت'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'الأحد',
+    'الاثنين',
+    'الثلاثاء',
+    'الأربعاء',
+    'الخميس',
+    'الجمعة',
+    'السبت'
+  ],
+      SHORTWEEKDAYS: const [
+    'الأحد',
+    'الاثنين',
+    'الثلاثاء',
+    'الأربعاء',
+    'الخميس',
+    'الجمعة',
+    'السبت'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'الأحد',
+    'الاثنين',
+    'الثلاثاء',
+    'الأربعاء',
+    'الخميس',
+    'الجمعة',
+    'السبت'
+  ],
+      NARROWWEEKDAYS: const ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+      STANDALONENARROWWEEKDAYS: const ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
+      SHORTQUARTERS: const [
+    'الربع الأول',
+    'الربع الثاني',
+    'الربع الثالث',
+    'الربع الرابع'
+  ],
+      QUARTERS: const [
+    'الربع الأول',
+    'الربع الثاني',
+    'الربع الثالث',
+    'الربع الرابع'
+  ],
+      AMPMS: const ['ص', 'م'],
+      DATEFORMATS: const [
+    'EEEE، d MMMM، y',
+    'd MMMM، y',
+    'dd‏/MM‏/y',
+    'd‏/M‏/y'
+  ],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 5,
+      WEEKENDRANGE: const [4, 5],
+      FIRSTWEEKCUTOFFDAY: 4),
+  /**
+   * Date/time formatting symbols for locale az.
+   */
+  "az": new DateSymbols(
+      NAME: "az",
+      ERAS: const ['e.ə.', 'b.e.'],
+      ERANAMES: const ['eramızdan əvvəl', 'bizim eramızın'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    'yanvar',
+    'fevral',
+    'mart',
+    'aprel',
+    'may',
+    'iyun',
+    'iyul',
+    'avqust',
+    'sentyabr',
+    'oktyabr',
+    'noyabr',
+    'dekabr'
+  ],
+      STANDALONEMONTHS: const [
+    'Yanvar',
+    'Fevral',
+    'Mart',
+    'Aprel',
+    'May',
+    'İyun',
+    'İyul',
+    'Avqust',
+    'Sentyabr',
+    'Oktyabr',
+    'Noyabr',
+    'Dekabr'
+  ],
+      SHORTMONTHS: const [
+    'yan',
+    'fev',
+    'mar',
+    'apr',
+    'may',
+    'iyn',
+    'iyl',
+    'avq',
+    'sen',
+    'okt',
+    'noy',
+    'dek'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'yan',
+    'fev',
+    'mar',
+    'apr',
+    'may',
+    'iyn',
+    'iyl',
+    'avq',
+    'sen',
+    'okt',
+    'noy',
+    'dek'
+  ],
+      WEEKDAYS: const [
+    'bazar',
+    'bazar ertəsi',
+    'çərşənbə axşamı',
+    'çərşənbə',
+    'cümə axşamı',
+    'cümə',
+    'şənbə'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'bazar',
+    'bazar ertəsi',
+    'çərşənbə axşamı',
+    'çərşənbə',
+    'cümə axşamı',
+    'cümə',
+    'şənbə'
+  ],
+      SHORTWEEKDAYS: const ['B.', 'B.E.', 'Ç.A.', 'Ç.', 'C.A.', 'C', 'Ş.'],
+      STANDALONESHORTWEEKDAYS: const [
+    'B.',
+    'B.E.',
+    'Ç.A.',
+    'Ç.',
+    'C.A.',
+    'C',
+    'Ş.'
+  ],
+      NARROWWEEKDAYS: const ['7', '1', '2', '3', '4', '5', '6'],
+      STANDALONENARROWWEEKDAYS: const ['7', '1', '2', '3', '4', '5', '6'],
+      SHORTQUARTERS: const ['1-ci kv.', '2-ci kv.', '3-cü kv.', '4-cü kv.'],
+      QUARTERS: const [
+    '1-ci kvartal',
+    '2-ci kvartal',
+    '3-cü kvartal',
+    '4-cü kvartal'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['d MMMM y, EEEE', 'd MMMM y', 'd MMM y', 'dd.MM.yy'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale bg.
+   */
+  "bg": new DateSymbols(
+      NAME: "bg",
+      ERAS: const ['пр.Хр.', 'сл.Хр.'],
+      ERANAMES: const ['пр.Хр.', 'сл.Хр.'],
+      NARROWMONTHS: const [
+    'я',
+    'ф',
+    'м',
+    'а',
+    'м',
+    'ю',
+    'ю',
+    'а',
+    'с',
+    'о',
+    'н',
+    'д'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'я',
+    'ф',
+    'м',
+    'а',
+    'м',
+    'ю',
+    'ю',
+    'а',
+    'с',
+    'о',
+    'н',
+    'д'
+  ],
+      MONTHS: const [
+    'януари',
+    'февруари',
+    'март',
+    'април',
+    'май',
+    'юни',
+    'юли',
+    'август',
+    'септември',
+    'октомври',
+    'ноември',
+    'декември'
+  ],
+      STANDALONEMONTHS: const [
+    'януари',
+    'февруари',
+    'март',
+    'април',
+    'май',
+    'юни',
+    'юли',
+    'август',
+    'септември',
+    'октомври',
+    'ноември',
+    'декември'
+  ],
+      SHORTMONTHS: const [
+    'ян.',
+    'февр.',
+    'март',
+    'апр.',
+    'май',
+    'юни',
+    'юли',
+    'авг.',
+    'септ.',
+    'окт.',
+    'ноем.',
+    'дек.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ян.',
+    'февр.',
+    'март',
+    'апр.',
+    'май',
+    'юни',
+    'юли',
+    'авг.',
+    'септ.',
+    'окт.',
+    'ноем.',
+    'дек.'
+  ],
+      WEEKDAYS: const [
+    'неделя',
+    'понеделник',
+    'вторник',
+    'сряда',
+    'четвъртък',
+    'петък',
+    'събота'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'неделя',
+    'понеделник',
+    'вторник',
+    'сряда',
+    'четвъртък',
+    'петък',
+    'събота'
+  ],
+      SHORTWEEKDAYS: const ['нд', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
+      STANDALONESHORTWEEKDAYS: const ['нд', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
+      NARROWWEEKDAYS: const ['н', 'п', 'в', 'с', 'ч', 'п', 'с'],
+      STANDALONENARROWWEEKDAYS: const ['н', 'п', 'в', 'с', 'ч', 'п', 'с'],
+      SHORTQUARTERS: const ['1 трим.', '2 трим.', '3 трим.', '4 трим.'],
+      QUARTERS: const [
+    '1-во тримесечие',
+    '2-ро тримесечие',
+    '3-то тримесечие',
+    '4-то тримесечие'
+  ],
+      AMPMS: const ['пр.об.', 'сл.об.'],
+      DATEFORMATS: const [
+    'EEEE, d MMMM y \'г\'.',
+    'd MMMM y \'г\'.',
+    'd.MM.y \'г\'.',
+    'd.MM.yy'
+  ],
+      TIMEFORMATS: const ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
+      DATETIMEFORMATS: const ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale bn.
+   */
+  "bn": new DateSymbols(
+      NAME: "bn",
+      ERAS: const ['খ্রিস্টপূর্ব', 'খৃষ্টাব্দ'],
+      ERANAMES: const ['খ্রিস্টপূর্ব', 'খৃষ্টাব্দ'],
+      NARROWMONTHS: const [
+    'জা',
+    'ফে',
+    'মা',
+    'এ',
+    'মে',
+    'জুন',
+    'জু',
+    'আ',
+    'সে',
+    'অ',
+    'ন',
+    'ডি'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'জা',
+    'ফে',
+    'মা',
+    'এ',
+    'মে',
+    'জুন',
+    'জু',
+    'আ',
+    'সে',
+    'অ',
+    'ন',
+    'ডি'
+  ],
+      MONTHS: const [
+    'জানুয়ারী',
+    'ফেব্রুয়ারী',
+    'মার্চ',
+    'এপ্রিল',
+    'মে',
+    'জুন',
+    'জুলাই',
+    'আগস্ট',
+    'সেপ্টেম্বর',
+    'অক্টোবর',
+    'নভেম্বর',
+    'ডিসেম্বর'
+  ],
+      STANDALONEMONTHS: const [
+    'জানুয়ারী',
+    'ফেব্রুয়ারী',
+    'মার্চ',
+    'এপ্রিল',
+    'মে',
+    'জুন',
+    'জুলাই',
+    'আগস্ট',
+    'সেপ্টেম্বর',
+    'অক্টোবর',
+    'নভেম্বর',
+    'ডিসেম্বর'
+  ],
+      SHORTMONTHS: const [
+    'জানুয়ারী',
+    'ফেব্রুয়ারী',
+    'মার্চ',
+    'এপ্রিল',
+    'মে',
+    'জুন',
+    'জুলাই',
+    'আগস্ট',
+    'সেপ্টেম্বর',
+    'অক্টোবর',
+    'নভেম্বর',
+    'ডিসেম্বর'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'জানুয়ারী',
+    'ফেব্রুয়ারী',
+    'মার্চ',
+    'এপ্রিল',
+    'মে',
+    'জুন',
+    'জুলাই',
+    'আগস্ট',
+    'সেপ্টেম্বর',
+    'অক্টোবর',
+    'নভেম্বর',
+    'ডিসেম্বর'
+  ],
+      WEEKDAYS: const [
+    'রবিবার',
+    'সোমবার',
+    'মঙ্গলবার',
+    'বুধবার',
+    'বৃহষ্পতিবার',
+    'শুক্রবার',
+    'শনিবার'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'রবিবার',
+    'সোমবার',
+    'মঙ্গলবার',
+    'বুধবার',
+    'বৃহষ্পতিবার',
+    'শুক্রবার',
+    'শনিবার'
+  ],
+      SHORTWEEKDAYS: const [
+    'রবি',
+    'সোম',
+    'মঙ্গল',
+    'বুধ',
+    'বৃহস্পতি',
+    'শুক্র',
+    'শনি'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'রবি',
+    'সোম',
+    'মঙ্গল',
+    'বুধ',
+    'বৃহস্পতি',
+    'শুক্র',
+    'শনি'
+  ],
+      NARROWWEEKDAYS: const ['র', 'সো', 'ম', 'বু', 'বৃ', 'শু', 'শ'],
+      STANDALONENARROWWEEKDAYS: const ['র', 'সো', 'ম', 'বু', 'বৃ', 'শু', 'শ'],
+      SHORTQUARTERS: const [
+    'চতুর্থাংশ ১',
+    'চতুর্থাংশ ২',
+    'চতুর্থাংশ ৩',
+    'চতুর্থাংশ ৪'
+  ],
+      QUARTERS: const [
+    'প্রথম চতুর্থাংশ',
+    'দ্বিতীয় চতুর্থাংশ',
+    'তৃতীয় চতুর্থাংশ',
+    'চতুর্থ চতুর্থাংশ'
+  ],
+      AMPMS: const ['am', 'pm'],
+      DATEFORMATS: const ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd/M/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 4,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale br.
+   */
+  "br": new DateSymbols(
+      NAME: "br",
+      ERAS: const ['BCE', 'CE'],
+      ERANAMES: const ['BCE', 'CE'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    'Genver',
+    'Cʼhwevrer',
+    'Meurzh',
+    'Ebrel',
+    'Mae',
+    'Mezheven',
+    'Gouere',
+    'Eost',
+    'Gwengolo',
+    'Here',
+    'Du',
+    'Kerzu'
+  ],
+      STANDALONEMONTHS: const [
+    'Genver',
+    'Cʼhwevrer',
+    'Meurzh',
+    'Ebrel',
+    'Mae',
+    'Mezheven',
+    'Gouere',
+    'Eost',
+    'Gwengolo',
+    'Here',
+    'Du',
+    'Kerzu'
+  ],
+      SHORTMONTHS: const [
+    'Gen',
+    'Cʼhwe',
+    'Meur',
+    'Ebr',
+    'Mae',
+    'Mezh',
+    'Goue',
+    'Eost',
+    'Gwen',
+    'Here',
+    'Du',
+    'Ker'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Gen',
+    'Cʼhwe',
+    'Meur',
+    'Ebr',
+    'Mae',
+    'Mezh',
+    'Goue',
+    'Eost',
+    'Gwen',
+    'Here',
+    'Du',
+    'Ker'
+  ],
+      WEEKDAYS: const [
+    'Sul',
+    'Lun',
+    'Meurzh',
+    'Mercʼher',
+    'Yaou',
+    'Gwener',
+    'Sadorn'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sul',
+    'Lun',
+    'Meurzh',
+    'Mercʼher',
+    'Yaou',
+    'Gwener',
+    'Sadorn'
+  ],
+      SHORTWEEKDAYS: const [
+    'sul',
+    'lun',
+    'meu.',
+    'mer.',
+    'yaou',
+    'gwe.',
+    'sad.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'sul',
+    'lun',
+    'meu.',
+    'mer.',
+    'yaou',
+    'gwe.',
+    'sad.'
+  ],
+      NARROWWEEKDAYS: const ['su', 'lu', 'mz', 'mc', 'ya', 'gw', 'sa'],
+      STANDALONENARROWWEEKDAYS: const [
+    'su',
+    'lu',
+    'mz',
+    'mc',
+    'ya',
+    'gw',
+    'sa'
+  ],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale ca.
+   */
+  "ca": new DateSymbols(
+      NAME: "ca",
+      ERAS: const ['aC', 'dC'],
+      ERANAMES: const ['abans de Crist', 'després de Crist'],
+      NARROWMONTHS: const [
+    'GN',
+    'FB',
+    'MÇ',
+    'AB',
+    'MG',
+    'JN',
+    'JL',
+    'AG',
+    'ST',
+    'OC',
+    'NV',
+    'DS'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'GN',
+    'FB',
+    'MÇ',
+    'AB',
+    'MG',
+    'JN',
+    'JL',
+    'AG',
+    'ST',
+    'OC',
+    'NV',
+    'DS'
+  ],
+      MONTHS: const [
+    'gener',
+    'febrer',
+    'març',
+    'abril',
+    'maig',
+    'juny',
+    'juliol',
+    'agost',
+    'setembre',
+    'octubre',
+    'novembre',
+    'desembre'
+  ],
+      STANDALONEMONTHS: const [
+    'gener',
+    'febrer',
+    'març',
+    'abril',
+    'maig',
+    'juny',
+    'juliol',
+    'agost',
+    'setembre',
+    'octubre',
+    'novembre',
+    'desembre'
+  ],
+      SHORTMONTHS: const [
+    'gen.',
+    'feb.',
+    'març',
+    'abr.',
+    'maig',
+    'juny',
+    'jul.',
+    'ag.',
+    'set.',
+    'oct.',
+    'nov.',
+    'des.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'gen.',
+    'feb.',
+    'març',
+    'abr.',
+    'maig',
+    'juny',
+    'jul.',
+    'ag.',
+    'set.',
+    'oct.',
+    'nov.',
+    'des.'
+  ],
+      WEEKDAYS: const [
+    'diumenge',
+    'dilluns',
+    'dimarts',
+    'dimecres',
+    'dijous',
+    'divendres',
+    'dissabte'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'diumenge',
+    'dilluns',
+    'dimarts',
+    'dimecres',
+    'dijous',
+    'divendres',
+    'dissabte'
+  ],
+      SHORTWEEKDAYS: const ['dg.', 'dl.', 'dt.', 'dc.', 'dj.', 'dv.', 'ds.'],
+      STANDALONESHORTWEEKDAYS: const [
+    'dg.',
+    'dl.',
+    'dt.',
+    'dc.',
+    'dj.',
+    'dv.',
+    'ds.'
+  ],
+      NARROWWEEKDAYS: const ['dg', 'dl', 'dt', 'dc', 'dj', 'dv', 'ds'],
+      STANDALONENARROWWEEKDAYS: const [
+    'dg',
+    'dl',
+    'dt',
+    'dc',
+    'dj',
+    'dv',
+    'ds'
+  ],
+      SHORTQUARTERS: const ['1T', '2T', '3T', '4T'],
+      QUARTERS: const [
+    '1r trimestre',
+    '2n trimestre',
+    '3r trimestre',
+    '4t trimestre'
+  ],
+      AMPMS: const ['a. m.', 'p. m.'],
+      DATEFORMATS: const [
+    'EEEE, d MMMM \'de\' y',
+    'd MMMM \'de\' y',
+    'dd/MM/y',
+    'd/M/yy'
+  ],
+      TIMEFORMATS: const ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale chr.
+   */
+  "chr": new DateSymbols(
+      NAME: "chr",
+      ERAS: const ['ᎤᏓᎷᎸ', 'ᎤᎶᏐᏅ'],
+      ERANAMES: const ['Ꮟ ᏥᏌ ᎾᏕᎲᏍᎬᎾ', 'ᎠᎩᏃᎮᎵᏓᏍᏗᏱ ᎠᏕᏘᏱᏍᎬ ᏱᎰᏩ ᏧᏓᏂᎸᎢᏍᏗ'],
+      NARROWMONTHS: const [
+    'Ꭴ',
+    'Ꭷ',
+    'Ꭰ',
+    'Ꭷ',
+    'Ꭰ',
+    'Ꮥ',
+    'Ꭻ',
+    'Ꭶ',
+    'Ꮪ',
+    'Ꮪ',
+    'Ꮕ',
+    'Ꭵ'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'Ꭴ',
+    'Ꭷ',
+    'Ꭰ',
+    'Ꭷ',
+    'Ꭰ',
+    'Ꮥ',
+    'Ꭻ',
+    'Ꭶ',
+    'Ꮪ',
+    'Ꮪ',
+    'Ꮕ',
+    'Ꭵ'
+  ],
+      MONTHS: const [
+    'ᎤᏃᎸᏔᏅ',
+    'ᎧᎦᎵ',
+    'ᎠᏅᏱ',
+    'ᎧᏬᏂ',
+    'ᎠᏂᏍᎬᏘ',
+    'ᏕᎭᎷᏱ',
+    'ᎫᏰᏉᏂ',
+    'ᎦᎶᏂ',
+    'ᏚᎵᏍᏗ',
+    'ᏚᏂᏅᏗ',
+    'ᏅᏓᏕᏆ',
+    'ᎥᏍᎩᏱ'
+  ],
+      STANDALONEMONTHS: const [
+    'ᎤᏃᎸᏔᏅ',
+    'ᎧᎦᎵ',
+    'ᎠᏅᏱ',
+    'ᎧᏬᏂ',
+    'ᎠᏂᏍᎬᏘ',
+    'ᏕᎭᎷᏱ',
+    'ᎫᏰᏉᏂ',
+    'ᎦᎶᏂ',
+    'ᏚᎵᏍᏗ',
+    'ᏚᏂᏅᏗ',
+    'ᏅᏓᏕᏆ',
+    'ᎥᏍᎩᏱ'
+  ],
+      SHORTMONTHS: const [
+    'ᎤᏃ',
+    'ᎧᎦ',
+    'ᎠᏅ',
+    'ᎧᏬ',
+    'ᎠᏂ',
+    'ᏕᎭ',
+    'ᎫᏰ',
+    'ᎦᎶ',
+    'ᏚᎵ',
+    'ᏚᏂ',
+    'ᏅᏓ',
+    'ᎥᏍ'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ᎤᏃ',
+    'ᎧᎦ',
+    'ᎠᏅ',
+    'ᎧᏬ',
+    'ᎠᏂ',
+    'ᏕᎭ',
+    'ᎫᏰ',
+    'ᎦᎶ',
+    'ᏚᎵ',
+    'ᏚᏂ',
+    'ᏅᏓ',
+    'ᎥᏍ'
+  ],
+      WEEKDAYS: const [
+    'ᎤᎾᏙᏓᏆᏍᎬ',
+    'ᎤᎾᏙᏓᏉᏅᎯ',
+    'ᏔᎵᏁᎢᎦ',
+    'ᏦᎢᏁᎢᎦ',
+    'ᏅᎩᏁᎢᎦ',
+    'ᏧᎾᎩᎶᏍᏗ',
+    'ᎤᎾᏙᏓᏈᏕᎾ'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'ᎤᎾᏙᏓᏆᏍᎬ',
+    'ᎤᎾᏙᏓᏉᏅᎯ',
+    'ᏔᎵᏁᎢᎦ',
+    'ᏦᎢᏁᎢᎦ',
+    'ᏅᎩᏁᎢᎦ',
+    'ᏧᎾᎩᎶᏍᏗ',
+    'ᎤᎾᏙᏓᏈᏕᎾ'
+  ],
+      SHORTWEEKDAYS: const ['ᏆᏍᎬ', 'ᏉᏅᎯ', 'ᏔᎵᏁ', 'ᏦᎢᏁ', 'ᏅᎩᏁ', 'ᏧᎾᎩ', 'ᏈᏕᎾ'],
+      STANDALONESHORTWEEKDAYS: const [
+    'ᏆᏍᎬ',
+    'ᏉᏅᎯ',
+    'ᏔᎵᏁ',
+    'ᏦᎢᏁ',
+    'ᏅᎩᏁ',
+    'ᏧᎾᎩ',
+    'ᏈᏕᎾ'
+  ],
+      NARROWWEEKDAYS: const ['Ꮖ', 'Ꮙ', 'Ꮤ', 'Ꮶ', 'Ꮕ', 'Ꮷ', 'Ꭴ'],
+      STANDALONENARROWWEEKDAYS: const ['Ꮖ', 'Ꮙ', 'Ꮤ', 'Ꮶ', 'Ꮕ', 'Ꮷ', 'Ꭴ'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      AMPMS: const ['ᏌᎾᎴ', 'ᏒᎯᏱᎢᏗᏢ'],
+      DATEFORMATS: const ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale cs.
+   */
+  "cs": new DateSymbols(
+      NAME: "cs",
+      ERAS: const ['př. n. l.', 'n. l.'],
+      ERANAMES: const ['př. n. l.', 'n. l.'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'l',
+    'ú',
+    'b',
+    'd',
+    'k',
+    'č',
+    'č',
+    's',
+    'z',
+    'ř',
+    'l',
+    'p'
+  ],
+      MONTHS: const [
+    'ledna',
+    'února',
+    'března',
+    'dubna',
+    'května',
+    'června',
+    'července',
+    'srpna',
+    'září',
+    'října',
+    'listopadu',
+    'prosince'
+  ],
+      STANDALONEMONTHS: const [
+    'leden',
+    'únor',
+    'březen',
+    'duben',
+    'květen',
+    'červen',
+    'červenec',
+    'srpen',
+    'září',
+    'říjen',
+    'listopad',
+    'prosinec'
+  ],
+      SHORTMONTHS: const [
+    'led',
+    'úno',
+    'bře',
+    'dub',
+    'kvě',
+    'čvn',
+    'čvc',
+    'srp',
+    'zář',
+    'říj',
+    'lis',
+    'pro'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'led',
+    'úno',
+    'bře',
+    'dub',
+    'kvě',
+    'čvn',
+    'čvc',
+    'srp',
+    'zář',
+    'říj',
+    'lis',
+    'pro'
+  ],
+      WEEKDAYS: const [
+    'neděle',
+    'pondělí',
+    'úterý',
+    'středa',
+    'čtvrtek',
+    'pátek',
+    'sobota'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'neděle',
+    'pondělí',
+    'úterý',
+    'středa',
+    'čtvrtek',
+    'pátek',
+    'sobota'
+  ],
+      SHORTWEEKDAYS: const ['ne', 'po', 'út', 'st', 'čt', 'pá', 'so'],
+      STANDALONESHORTWEEKDAYS: const ['ne', 'po', 'út', 'st', 'čt', 'pá', 'so'],
+      NARROWWEEKDAYS: const ['N', 'P', 'Ú', 'S', 'Č', 'P', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['N', 'P', 'Ú', 'S', 'Č', 'P', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    '1. čtvrtletí',
+    '2. čtvrtletí',
+    '3. čtvrtletí',
+    '4. čtvrtletí'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE d. MMMM y', 'd. MMMM y', 'd. M. y', 'dd.MM.yy'],
+      TIMEFORMATS: const ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale cy.
+   */
+  "cy": new DateSymbols(
+      NAME: "cy",
+      ERAS: const ['CC', 'OC'],
+      ERANAMES: const ['Cyn Crist', 'Oed Crist'],
+      NARROWMONTHS: const [
+    'I',
+    'Ch',
+    'M',
+    'E',
+    'M',
+    'M',
+    'G',
+    'A',
+    'M',
+    'H',
+    'T',
+    'Rh'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'I',
+    'Ch',
+    'M',
+    'E',
+    'M',
+    'M',
+    'G',
+    'A',
+    'M',
+    'H',
+    'T',
+    'Rh'
+  ],
+      MONTHS: const [
+    'Ionawr',
+    'Chwefror',
+    'Mawrth',
+    'Ebrill',
+    'Mai',
+    'Mehefin',
+    'Gorffennaf',
+    'Awst',
+    'Medi',
+    'Hydref',
+    'Tachwedd',
+    'Rhagfyr'
+  ],
+      STANDALONEMONTHS: const [
+    'Ionawr',
+    'Chwefror',
+    'Mawrth',
+    'Ebrill',
+    'Mai',
+    'Mehefin',
+    'Gorffennaf',
+    'Awst',
+    'Medi',
+    'Hydref',
+    'Tachwedd',
+    'Rhagfyr'
+  ],
+      SHORTMONTHS: const [
+    'Ion',
+    'Chwef',
+    'Mawrth',
+    'Ebrill',
+    'Mai',
+    'Meh',
+    'Gorff',
+    'Awst',
+    'Medi',
+    'Hyd',
+    'Tach',
+    'Rhag'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Ion',
+    'Chw',
+    'Maw',
+    'Ebr',
+    'Mai',
+    'Meh',
+    'Gor',
+    'Awst',
+    'Medi',
+    'Hyd',
+    'Tach',
+    'Rhag'
+  ],
+      WEEKDAYS: const [
+    'Dydd Sul',
+    'Dydd Llun',
+    'Dydd Mawrth',
+    'Dydd Mercher',
+    'Dydd Iau',
+    'Dydd Gwener',
+    'Dydd Sadwrn'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Dydd Sul',
+    'Dydd Llun',
+    'Dydd Mawrth',
+    'Dydd Mercher',
+    'Dydd Iau',
+    'Dydd Gwener',
+    'Dydd Sadwrn'
+  ],
+      SHORTWEEKDAYS: const ['Sul', 'Llun', 'Maw', 'Mer', 'Iau', 'Gwen', 'Sad'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Sul',
+    'Llun',
+    'Maw',
+    'Mer',
+    'Iau',
+    'Gwe',
+    'Sad'
+  ],
+      NARROWWEEKDAYS: const ['S', 'Ll', 'M', 'M', 'I', 'G', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'Ll', 'M', 'M', 'I', 'G', 'S'],
+      SHORTQUARTERS: const ['Ch1', 'Ch2', 'Ch3', 'Ch4'],
+      QUARTERS: const [
+    'Chwarter 1af',
+    '2il chwarter',
+    '3ydd chwarter',
+    '4ydd chwarter'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const [
+    '{1} \'am\' {0}',
+    '{1} \'am\' {0}',
+    '{1} {0}',
+    '{1} {0}'
+  ],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale da.
+   */
+  "da": new DateSymbols(
+      NAME: "da",
+      ERAS: const ['f.Kr.', 'e.Kr.'],
+      ERANAMES: const ['f.Kr.', 'e.Kr.'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'januar',
+    'februar',
+    'marts',
+    'april',
+    'maj',
+    'juni',
+    'juli',
+    'august',
+    'september',
+    'oktober',
+    'november',
+    'december'
+  ],
+      STANDALONEMONTHS: const [
+    'januar',
+    'februar',
+    'marts',
+    'april',
+    'maj',
+    'juni',
+    'juli',
+    'august',
+    'september',
+    'oktober',
+    'november',
+    'december'
+  ],
+      SHORTMONTHS: const [
+    'jan.',
+    'feb.',
+    'mar.',
+    'apr.',
+    'maj',
+    'jun.',
+    'jul.',
+    'aug.',
+    'sep.',
+    'okt.',
+    'nov.',
+    'dec.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'jan',
+    'feb',
+    'mar',
+    'apr',
+    'maj',
+    'jun',
+    'jul',
+    'aug',
+    'sep',
+    'okt',
+    'nov',
+    'dec'
+  ],
+      WEEKDAYS: const [
+    'søndag',
+    'mandag',
+    'tirsdag',
+    'onsdag',
+    'torsdag',
+    'fredag',
+    'lørdag'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'søndag',
+    'mandag',
+    'tirsdag',
+    'onsdag',
+    'torsdag',
+    'fredag',
+    'lørdag'
+  ],
+      SHORTWEEKDAYS: const [
+    'søn.',
+    'man.',
+    'tir.',
+    'ons.',
+    'tor.',
+    'fre.',
+    'lør.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'søn',
+    'man',
+    'tir',
+    'ons',
+    'tor',
+    'fre',
+    'lør'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
+      SHORTQUARTERS: const ['K1', 'K2', 'K3', 'K4'],
+      QUARTERS: const ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const [
+    'EEEE \'den\' d. MMMM y',
+    'd. MMM y',
+    'dd/MM/y',
+    'dd/MM/yy'
+  ],
+      TIMEFORMATS: const ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
+      DATETIMEFORMATS: const [
+    '{1} \'kl.\' {0}',
+    '{1} \'kl.\' {0}',
+    '{1} {0}',
+    '{1} {0}'
+  ],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale de.
+   */
+  "de": new DateSymbols(
+      NAME: "de",
+      ERAS: const ['v. Chr.', 'n. Chr.'],
+      ERANAMES: const ['v. Chr.', 'n. Chr.'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Januar',
+    'Februar',
+    'März',
+    'April',
+    'Mai',
+    'Juni',
+    'Juli',
+    'August',
+    'September',
+    'Oktober',
+    'November',
+    'Dezember'
+  ],
+      STANDALONEMONTHS: const [
+    'Januar',
+    'Februar',
+    'März',
+    'April',
+    'Mai',
+    'Juni',
+    'Juli',
+    'August',
+    'September',
+    'Oktober',
+    'November',
+    'Dezember'
+  ],
+      SHORTMONTHS: const [
+    'Jan.',
+    'Feb.',
+    'März',
+    'Apr.',
+    'Mai',
+    'Juni',
+    'Juli',
+    'Aug.',
+    'Sep.',
+    'Okt.',
+    'Nov.',
+    'Dez.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mär',
+    'Apr',
+    'Mai',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Dez'
+  ],
+      WEEKDAYS: const [
+    'Sonntag',
+    'Montag',
+    'Dienstag',
+    'Mittwoch',
+    'Donnerstag',
+    'Freitag',
+    'Samstag'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sonntag',
+    'Montag',
+    'Dienstag',
+    'Mittwoch',
+    'Donnerstag',
+    'Freitag',
+    'Samstag'
+  ],
+      SHORTWEEKDAYS: const ['So.', 'Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.'],
+      STANDALONESHORTWEEKDAYS: const ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
+      NARROWWEEKDAYS: const ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
+      AMPMS: const ['vorm.', 'nachm.'],
+      DATEFORMATS: const [
+    'EEEE, d. MMMM y',
+    'd. MMMM y',
+    'dd.MM.y',
+    'dd.MM.yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale de_AT.
+   */
+  "de_AT": new DateSymbols(
+      NAME: "de_AT",
+      ERAS: const ['v. Chr.', 'n. Chr.'],
+      ERANAMES: const ['v. Chr.', 'n. Chr.'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Jänner',
+    'Februar',
+    'März',
+    'April',
+    'Mai',
+    'Juni',
+    'Juli',
+    'August',
+    'September',
+    'Oktober',
+    'November',
+    'Dezember'
+  ],
+      STANDALONEMONTHS: const [
+    'Jänner',
+    'Februar',
+    'März',
+    'April',
+    'Mai',
+    'Juni',
+    'Juli',
+    'August',
+    'September',
+    'Oktober',
+    'November',
+    'Dezember'
+  ],
+      SHORTMONTHS: const [
+    'Jän.',
+    'Feb.',
+    'März',
+    'Apr.',
+    'Mai',
+    'Juni',
+    'Juli',
+    'Aug.',
+    'Sep.',
+    'Okt.',
+    'Nov.',
+    'Dez.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jän',
+    'Feb',
+    'Mär',
+    'Apr',
+    'Mai',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Dez'
+  ],
+      WEEKDAYS: const [
+    'Sonntag',
+    'Montag',
+    'Dienstag',
+    'Mittwoch',
+    'Donnerstag',
+    'Freitag',
+    'Samstag'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sonntag',
+    'Montag',
+    'Dienstag',
+    'Mittwoch',
+    'Donnerstag',
+    'Freitag',
+    'Samstag'
+  ],
+      SHORTWEEKDAYS: const ['So.', 'Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.'],
+      STANDALONESHORTWEEKDAYS: const ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
+      NARROWWEEKDAYS: const ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
+      AMPMS: const ['vorm.', 'nachm.'],
+      DATEFORMATS: const [
+    'EEEE, dd. MMMM y',
+    'dd. MMMM y',
+    'dd.MM.y',
+    'dd.MM.yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale de_CH.
+   */
+  /**
+   * Date/time formatting symbols for locale de_CH.
+   */
+  "de_CH": new DateSymbols(
+      NAME: "de_CH",
+      ERAS: const ['v. Chr.', 'n. Chr.'],
+      ERANAMES: const ['v. Chr.', 'n. Chr.'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Januar',
+    'Februar',
+    'März',
+    'April',
+    'Mai',
+    'Juni',
+    'Juli',
+    'August',
+    'September',
+    'Oktober',
+    'November',
+    'Dezember'
+  ],
+      STANDALONEMONTHS: const [
+    'Januar',
+    'Februar',
+    'März',
+    'April',
+    'Mai',
+    'Juni',
+    'Juli',
+    'August',
+    'September',
+    'Oktober',
+    'November',
+    'Dezember'
+  ],
+      SHORTMONTHS: const [
+    'Jan.',
+    'Feb.',
+    'März',
+    'Apr.',
+    'Mai',
+    'Juni',
+    'Juli',
+    'Aug.',
+    'Sep.',
+    'Okt.',
+    'Nov.',
+    'Dez.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mär',
+    'Apr',
+    'Mai',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Dez'
+  ],
+      WEEKDAYS: const [
+    'Sonntag',
+    'Montag',
+    'Dienstag',
+    'Mittwoch',
+    'Donnerstag',
+    'Freitag',
+    'Samstag'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sonntag',
+    'Montag',
+    'Dienstag',
+    'Mittwoch',
+    'Donnerstag',
+    'Freitag',
+    'Samstag'
+  ],
+      SHORTWEEKDAYS: const ['So.', 'Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.'],
+      STANDALONESHORTWEEKDAYS: const ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
+      NARROWWEEKDAYS: const ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
+      AMPMS: const ['vorm.', 'nachm.'],
+      DATEFORMATS: const [
+    'EEEE, d. MMMM y',
+    'd. MMMM y',
+    'dd.MM.y',
+    'dd.MM.yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale el.
+   */
+  "el": new DateSymbols(
+      NAME: "el",
+      ERAS: const ['π.Χ.', 'μ.Χ.'],
+      ERANAMES: const ['π.Χ.', 'μ.Χ.'],
+      NARROWMONTHS: const [
+    'Ι',
+    'Φ',
+    'Μ',
+    'Α',
+    'Μ',
+    'Ι',
+    'Ι',
+    'Α',
+    'Σ',
+    'Ο',
+    'Ν',
+    'Δ'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'Ι',
+    'Φ',
+    'Μ',
+    'Α',
+    'Μ',
+    'Ι',
+    'Ι',
+    'Α',
+    'Σ',
+    'Ο',
+    'Ν',
+    'Δ'
+  ],
+      MONTHS: const [
+    'Ιανουαρίου',
+    'Φεβρουαρίου',
+    'Μαρτίου',
+    'Απριλίου',
+    'Μαΐου',
+    'Ιουνίου',
+    'Ιουλίου',
+    'Αυγούστου',
+    'Σεπτεμβρίου',
+    'Οκτωβρίου',
+    'Νοεμβρίου',
+    'Δεκεμβρίου'
+  ],
+      STANDALONEMONTHS: const [
+    'Ιανουάριος',
+    'Φεβρουάριος',
+    'Μάρτιος',
+    'Απρίλιος',
+    'Μάιος',
+    'Ιούνιος',
+    'Ιούλιος',
+    'Αύγουστος',
+    'Σεπτέμβριος',
+    'Οκτώβριος',
+    'Νοέμβριος',
+    'Δεκέμβριος'
+  ],
+      SHORTMONTHS: const [
+    'Ιαν',
+    'Φεβ',
+    'Μαρ',
+    'Απρ',
+    'Μαΐ',
+    'Ιουν',
+    'Ιουλ',
+    'Αυγ',
+    'Σεπ',
+    'Οκτ',
+    'Νοε',
+    'Δεκ'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Ιαν',
+    'Φεβ',
+    'Μάρ',
+    'Απρ',
+    'Μάι',
+    'Ιούν',
+    'Ιούλ',
+    'Αύγ',
+    'Σεπ',
+    'Οκτ',
+    'Νοέ',
+    'Δεκ'
+  ],
+      WEEKDAYS: const [
+    'Κυριακή',
+    'Δευτέρα',
+    'Τρίτη',
+    'Τετάρτη',
+    'Πέμπτη',
+    'Παρασκευή',
+    'Σάββατο'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Κυριακή',
+    'Δευτέρα',
+    'Τρίτη',
+    'Τετάρτη',
+    'Πέμπτη',
+    'Παρασκευή',
+    'Σάββατο'
+  ],
+      SHORTWEEKDAYS: const ['Κυρ', 'Δευ', 'Τρί', 'Τετ', 'Πέμ', 'Παρ', 'Σάβ'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Κυρ',
+    'Δευ',
+    'Τρί',
+    'Τετ',
+    'Πέμ',
+    'Παρ',
+    'Σάβ'
+  ],
+      NARROWWEEKDAYS: const ['Κ', 'Δ', 'Τ', 'Τ', 'Π', 'Π', 'Σ'],
+      STANDALONENARROWWEEKDAYS: const ['Κ', 'Δ', 'Τ', 'Τ', 'Π', 'Π', 'Σ'],
+      SHORTQUARTERS: const ['Τ1', 'Τ2', 'Τ3', 'Τ4'],
+      QUARTERS: const ['1ο τρίμηνο', '2ο τρίμηνο', '3ο τρίμηνο', '4ο τρίμηνο'],
+      AMPMS: const ['π.μ.', 'μ.μ.'],
+      DATEFORMATS: const ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const [
+    '{1} - {0}',
+    '{1} - {0}',
+    '{1} - {0}',
+    '{1} - {0}'
+  ],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale en.
+   */
+  "en": new DateSymbols(
+      NAME: "en",
+      ERAS: const ['BC', 'AD'],
+      ERANAMES: const ['Before Christ', 'Anno Domini'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      STANDALONEMONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      WEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      SHORTWEEKDAYS: const ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Sun',
+    'Mon',
+    'Tue',
+    'Wed',
+    'Thu',
+    'Fri',
+    'Sat'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    '1st quarter',
+    '2nd quarter',
+    '3rd quarter',
+    '4th quarter'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const [
+    '{1} \'at\' {0}',
+    '{1} \'at\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale en_AU.
+   */
+  "en_AU": new DateSymbols(
+      NAME: "en_AU",
+      ERAS: const ['BC', 'AD'],
+      ERANAMES: const ['Before Christ', 'Anno Domini'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      STANDALONEMONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      WEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      SHORTWEEKDAYS: const ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Sun',
+    'Mon',
+    'Tue',
+    'Wed',
+    'Thu',
+    'Fri',
+    'Sat'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    '1st quarter',
+    '2nd quarter',
+    '3rd quarter',
+    '4th quarter'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/MM/y'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const [
+    '{1} \'at\' {0}',
+    '{1} \'at\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale en_GB.
+   */
+  "en_GB": new DateSymbols(
+      NAME: "en_GB",
+      ERAS: const ['BC', 'AD'],
+      ERANAMES: const ['Before Christ', 'Anno Domini'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      STANDALONEMONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      WEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      SHORTWEEKDAYS: const ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Sun',
+    'Mon',
+    'Tue',
+    'Wed',
+    'Thu',
+    'Fri',
+    'Sat'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    '1st quarter',
+    '2nd quarter',
+    '3rd quarter',
+    '4th quarter'
+  ],
+      AMPMS: const ['am', 'pm'],
+      DATEFORMATS: const ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale en_IE.
+   */
+  "en_IE": new DateSymbols(
+      NAME: "en_IE",
+      ERAS: const ['BC', 'AD'],
+      ERANAMES: const ['Before Christ', 'Anno Domini'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      STANDALONEMONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      WEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      SHORTWEEKDAYS: const ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Sun',
+    'Mon',
+    'Tue',
+    'Wed',
+    'Thu',
+    'Fri',
+    'Sat'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    '1st quarter',
+    '2nd quarter',
+    '3rd quarter',
+    '4th quarter'
+  ],
+      AMPMS: const ['a.m.', 'p.m.'],
+      DATEFORMATS: const ['EEEE d MMMM y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const [
+    '{1} \'at\' {0}',
+    '{1} \'at\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 2),
+  /**
+   * Date/time formatting symbols for locale en_IN.
+   */
+  "en_IN": new DateSymbols(
+      NAME: "en_IN",
+      ERAS: const ['BC', 'AD'],
+      ERANAMES: const ['Before Christ', 'Anno Domini'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      STANDALONEMONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      WEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      SHORTWEEKDAYS: const ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Sun',
+    'Mon',
+    'Tue',
+    'Wed',
+    'Thu',
+    'Fri',
+    'Sat'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    '1st quarter',
+    '2nd quarter',
+    '3rd quarter',
+    '4th quarter'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE d MMMM y', 'd MMMM y', 'dd-MMM-y', 'dd/MM/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const [
+    '{1} \'at\' {0}',
+    '{1} \'at\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [6, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale en_SG.
+   */
+  "en_SG": new DateSymbols(
+      NAME: "en_SG",
+      ERAS: const ['BC', 'AD'],
+      ERANAMES: const ['Before Christ', 'Anno Domini'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      STANDALONEMONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      WEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      SHORTWEEKDAYS: const ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Sun',
+    'Mon',
+    'Tue',
+    'Wed',
+    'Thu',
+    'Fri',
+    'Sat'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    '1st quarter',
+    '2nd quarter',
+    '3rd quarter',
+    '4th quarter'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const [
+    '{1} \'at\' {0}',
+    '{1} \'at\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale en_US.
+   */
+  /**
+   * Date/time formatting symbols for locale en_US.
+   */
+  "en_US": new DateSymbols(
+      NAME: "en_US",
+      ERAS: const ['BC', 'AD'],
+      ERANAMES: const ['Before Christ', 'Anno Domini'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      STANDALONEMONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      WEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      SHORTWEEKDAYS: const ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Sun',
+    'Mon',
+    'Tue',
+    'Wed',
+    'Thu',
+    'Fri',
+    'Sat'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    '1st quarter',
+    '2nd quarter',
+    '3rd quarter',
+    '4th quarter'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const [
+    '{1} \'at\' {0}',
+    '{1} \'at\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale en_ZA.
+   */
+  "en_ZA": new DateSymbols(
+      NAME: "en_ZA",
+      ERAS: const ['BC', 'AD'],
+      ERANAMES: const ['Before Christ', 'Anno Domini'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      STANDALONEMONTHS: const [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'May',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Oct',
+    'Nov',
+    'Dec'
+  ],
+      WEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sunday',
+    'Monday',
+    'Tuesday',
+    'Wednesday',
+    'Thursday',
+    'Friday',
+    'Saturday'
+  ],
+      SHORTWEEKDAYS: const ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Sun',
+    'Mon',
+    'Tue',
+    'Wed',
+    'Thu',
+    'Fri',
+    'Sat'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    '1st quarter',
+    '2nd quarter',
+    '3rd quarter',
+    '4th quarter'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE dd MMMM y', 'dd MMMM y', 'dd MMM y', 'y/MM/dd'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const [
+    '{1} \'at\' {0}',
+    '{1} \'at\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale es.
+   */
+  "es": new DateSymbols(
+      NAME: "es",
+      ERAS: const ['a. C.', 'd. C.'],
+      ERANAMES: const ['antes de Cristo', 'anno Dómini'],
+      NARROWMONTHS: const [
+    'E',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'E',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'enero',
+    'febrero',
+    'marzo',
+    'abril',
+    'mayo',
+    'junio',
+    'julio',
+    'agosto',
+    'septiembre',
+    'octubre',
+    'noviembre',
+    'diciembre'
+  ],
+      STANDALONEMONTHS: const [
+    'Enero',
+    'Febrero',
+    'Marzo',
+    'Abril',
+    'Mayo',
+    'Junio',
+    'Julio',
+    'Agosto',
+    'Septiembre',
+    'Octubre',
+    'Noviembre',
+    'Diciembre'
+  ],
+      SHORTMONTHS: const [
+    'ene.',
+    'feb.',
+    'mar.',
+    'abr.',
+    'may.',
+    'jun.',
+    'jul.',
+    'ago.',
+    'sept.',
+    'oct.',
+    'nov.',
+    'dic.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Ene.',
+    'Feb.',
+    'Mar.',
+    'Abr.',
+    'May.',
+    'Jun.',
+    'Jul.',
+    'Ago.',
+    'Sept.',
+    'Oct.',
+    'Nov.',
+    'Dic.'
+  ],
+      WEEKDAYS: const [
+    'domingo',
+    'lunes',
+    'martes',
+    'miércoles',
+    'jueves',
+    'viernes',
+    'sábado'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Domingo',
+    'Lunes',
+    'Martes',
+    'Miércoles',
+    'Jueves',
+    'Viernes',
+    'Sábado'
+  ],
+      SHORTWEEKDAYS: const [
+    'dom.',
+    'lun.',
+    'mar.',
+    'mié.',
+    'jue.',
+    'vie.',
+    'sáb.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'Dom.',
+    'Lun.',
+    'Mar.',
+    'Mié.',
+    'Jue.',
+    'Vie.',
+    'Sáb.'
+  ],
+      NARROWWEEKDAYS: const ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
+      SHORTQUARTERS: const ['T1', 'T2', 'T3', 'T4'],
+      QUARTERS: const [
+    '1.er trimestre',
+    '2.º trimestre',
+    '3.er trimestre',
+    '4.º trimestre'
+  ],
+      AMPMS: const ['a. m.', 'p. m.'],
+      DATEFORMATS: const [
+    'EEEE, d \'de\' MMMM \'de\' y',
+    'd \'de\' MMMM \'de\' y',
+    'd/M/y',
+    'd/M/yy'
+  ],
+      TIMEFORMATS: const ['H:mm:ss (zzzz)', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
+      DATETIMEFORMATS: const ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale es_419.
+   */
+  /**
+   * Date/time formatting symbols for locale es_419.
+   */
+  "es_419": new DateSymbols(
+      NAME: "es_419",
+      ERAS: const ['a. C.', 'd. C.'],
+      ERANAMES: const ['antes de Cristo', 'anno Dómini'],
+      NARROWMONTHS: const [
+    'E',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'E',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'enero',
+    'febrero',
+    'marzo',
+    'abril',
+    'mayo',
+    'junio',
+    'julio',
+    'agosto',
+    'septiembre',
+    'octubre',
+    'noviembre',
+    'diciembre'
+  ],
+      STANDALONEMONTHS: const [
+    'Enero',
+    'Febrero',
+    'Marzo',
+    'Abril',
+    'Mayo',
+    'Junio',
+    'Julio',
+    'Agosto',
+    'Septiembre',
+    'Octubre',
+    'Noviembre',
+    'Diciembre'
+  ],
+      SHORTMONTHS: const [
+    'ene.',
+    'feb.',
+    'mar.',
+    'abr.',
+    'may.',
+    'jun.',
+    'jul.',
+    'ago.',
+    'sept.',
+    'oct.',
+    'nov.',
+    'dic.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Ene.',
+    'Feb.',
+    'Mar.',
+    'Abr.',
+    'May.',
+    'Jun.',
+    'Jul.',
+    'Ago.',
+    'Sept.',
+    'Oct.',
+    'Nov.',
+    'Dic.'
+  ],
+      WEEKDAYS: const [
+    'domingo',
+    'lunes',
+    'martes',
+    'miércoles',
+    'jueves',
+    'viernes',
+    'sábado'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Domingo',
+    'Lunes',
+    'Martes',
+    'Miércoles',
+    'Jueves',
+    'Viernes',
+    'Sábado'
+  ],
+      SHORTWEEKDAYS: const [
+    'dom.',
+    'lun.',
+    'mar.',
+    'mié.',
+    'jue.',
+    'vie.',
+    'sáb.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'Dom.',
+    'Lun.',
+    'Mar.',
+    'Mié.',
+    'Jue.',
+    'Vie.',
+    'Sáb.'
+  ],
+      NARROWWEEKDAYS: const ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
+      SHORTQUARTERS: const ['T1', 'T2', 'T3', 'T4'],
+      QUARTERS: const [
+    '1.er trimestre',
+    '2.º trimestre',
+    '3.er trimestre',
+    '4.º trimestre'
+  ],
+      AMPMS: const ['a. m.', 'p. m.'],
+      DATEFORMATS: const [
+    'EEEE, d \'de\' MMMM \'de\' y',
+    'd \'de\' MMMM \'de\' y',
+    'd/M/y',
+    'd/M/yy'
+  ],
+      TIMEFORMATS: const ['H:mm:ss (zzzz)', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
+      DATETIMEFORMATS: const ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale es_ES.
+   */
+  /**
+   * Date/time formatting symbols for locale es_ES.
+   */
+  "es_ES": new DateSymbols(
+      NAME: "es_ES",
+      ERAS: const ['a. C.', 'd. C.'],
+      ERANAMES: const ['antes de Cristo', 'anno Dómini'],
+      NARROWMONTHS: const [
+    'E',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'E',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'enero',
+    'febrero',
+    'marzo',
+    'abril',
+    'mayo',
+    'junio',
+    'julio',
+    'agosto',
+    'septiembre',
+    'octubre',
+    'noviembre',
+    'diciembre'
+  ],
+      STANDALONEMONTHS: const [
+    'Enero',
+    'Febrero',
+    'Marzo',
+    'Abril',
+    'Mayo',
+    'Junio',
+    'Julio',
+    'Agosto',
+    'Septiembre',
+    'Octubre',
+    'Noviembre',
+    'Diciembre'
+  ],
+      SHORTMONTHS: const [
+    'ene.',
+    'feb.',
+    'mar.',
+    'abr.',
+    'may.',
+    'jun.',
+    'jul.',
+    'ago.',
+    'sept.',
+    'oct.',
+    'nov.',
+    'dic.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Ene.',
+    'Feb.',
+    'Mar.',
+    'Abr.',
+    'May.',
+    'Jun.',
+    'Jul.',
+    'Ago.',
+    'Sept.',
+    'Oct.',
+    'Nov.',
+    'Dic.'
+  ],
+      WEEKDAYS: const [
+    'domingo',
+    'lunes',
+    'martes',
+    'miércoles',
+    'jueves',
+    'viernes',
+    'sábado'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Domingo',
+    'Lunes',
+    'Martes',
+    'Miércoles',
+    'Jueves',
+    'Viernes',
+    'Sábado'
+  ],
+      SHORTWEEKDAYS: const [
+    'dom.',
+    'lun.',
+    'mar.',
+    'mié.',
+    'jue.',
+    'vie.',
+    'sáb.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'Dom.',
+    'Lun.',
+    'Mar.',
+    'Mié.',
+    'Jue.',
+    'Vie.',
+    'Sáb.'
+  ],
+      NARROWWEEKDAYS: const ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['D', 'L', 'M', 'X', 'J', 'V', 'S'],
+      SHORTQUARTERS: const ['T1', 'T2', 'T3', 'T4'],
+      QUARTERS: const [
+    '1.er trimestre',
+    '2.º trimestre',
+    '3.er trimestre',
+    '4.º trimestre'
+  ],
+      AMPMS: const ['a. m.', 'p. m.'],
+      DATEFORMATS: const [
+    'EEEE, d \'de\' MMMM \'de\' y',
+    'd \'de\' MMMM \'de\' y',
+    'd/M/y',
+    'd/M/yy'
+  ],
+      TIMEFORMATS: const ['H:mm:ss (zzzz)', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
+      DATETIMEFORMATS: const ['{1}, {0}', '{1}, {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale et.
+   */
+  "et": new DateSymbols(
+      NAME: "et",
+      ERAS: const ['e.m.a.', 'm.a.j.'],
+      ERANAMES: const ['enne meie aega', 'meie aja järgi'],
+      NARROWMONTHS: const [
+    'J',
+    'V',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'V',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'jaanuar',
+    'veebruar',
+    'märts',
+    'aprill',
+    'mai',
+    'juuni',
+    'juuli',
+    'august',
+    'september',
+    'oktoober',
+    'november',
+    'detsember'
+  ],
+      STANDALONEMONTHS: const [
+    'jaanuar',
+    'veebruar',
+    'märts',
+    'aprill',
+    'mai',
+    'juuni',
+    'juuli',
+    'august',
+    'september',
+    'oktoober',
+    'november',
+    'detsember'
+  ],
+      SHORTMONTHS: const [
+    'jaan',
+    'veebr',
+    'märts',
+    'apr',
+    'mai',
+    'juuni',
+    'juuli',
+    'aug',
+    'sept',
+    'okt',
+    'nov',
+    'dets'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'jaan',
+    'veebr',
+    'märts',
+    'apr',
+    'mai',
+    'juuni',
+    'juuli',
+    'aug',
+    'sept',
+    'okt',
+    'nov',
+    'dets'
+  ],
+      WEEKDAYS: const [
+    'pühapäev',
+    'esmaspäev',
+    'teisipäev',
+    'kolmapäev',
+    'neljapäev',
+    'reede',
+    'laupäev'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'pühapäev',
+    'esmaspäev',
+    'teisipäev',
+    'kolmapäev',
+    'neljapäev',
+    'reede',
+    'laupäev'
+  ],
+      SHORTWEEKDAYS: const ['P', 'E', 'T', 'K', 'N', 'R', 'L'],
+      STANDALONESHORTWEEKDAYS: const ['P', 'E', 'T', 'K', 'N', 'R', 'L'],
+      NARROWWEEKDAYS: const ['P', 'E', 'T', 'K', 'N', 'R', 'L'],
+      STANDALONENARROWWEEKDAYS: const ['P', 'E', 'T', 'K', 'N', 'R', 'L'],
+      SHORTQUARTERS: const ['K1', 'K2', 'K3', 'K4'],
+      QUARTERS: const ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const [
+    'EEEE, d. MMMM y',
+    'd. MMMM y',
+    'dd.MM.y',
+    'dd.MM.yy'
+  ],
+      TIMEFORMATS: const ['H:mm.ss zzzz', 'H:mm.ss z', 'H:mm.ss', 'H:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale eu.
+   */
+  "eu": new DateSymbols(
+      NAME: "eu",
+      ERAS: const ['K.a.', 'K.o.'],
+      ERANAMES: const ['K.a.', 'K.o.'],
+      NARROWMONTHS: const [
+    'U',
+    'O',
+    'M',
+    'A',
+    'M',
+    'E',
+    'U',
+    'A',
+    'I',
+    'U',
+    'A',
+    'A'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'U',
+    'O',
+    'M',
+    'A',
+    'M',
+    'E',
+    'U',
+    'A',
+    'I',
+    'U',
+    'A',
+    'A'
+  ],
+      MONTHS: const [
+    'urtarrilak',
+    'otsailak',
+    'martxoak',
+    'apirilak',
+    'maiatzak',
+    'ekainak',
+    'uztailak',
+    'abuztuak',
+    'irailak',
+    'urriak',
+    'azaroak',
+    'abenduak'
+  ],
+      STANDALONEMONTHS: const [
+    'urtarrila',
+    'otsaila',
+    'martxoa',
+    'apirila',
+    'maiatza',
+    'ekaina',
+    'uztaila',
+    'abuztua',
+    'iraila',
+    'urria',
+    'azaroa',
+    'abendua'
+  ],
+      SHORTMONTHS: const [
+    'urt.',
+    'ots.',
+    'mar.',
+    'api.',
+    'mai.',
+    'eka.',
+    'uzt.',
+    'abu.',
+    'ira.',
+    'urr.',
+    'aza.',
+    'abe.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'urt.',
+    'ots.',
+    'mar.',
+    'api.',
+    'mai.',
+    'eka.',
+    'uzt.',
+    'abu.',
+    'ira.',
+    'urr.',
+    'aza.',
+    'abe.'
+  ],
+      WEEKDAYS: const [
+    'igandea',
+    'astelehena',
+    'asteartea',
+    'asteazkena',
+    'osteguna',
+    'ostirala',
+    'larunbata'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'igandea',
+    'astelehena',
+    'asteartea',
+    'asteazkena',
+    'osteguna',
+    'ostirala',
+    'larunbata'
+  ],
+      SHORTWEEKDAYS: const ['ig.', 'al.', 'ar.', 'az.', 'og.', 'or.', 'lr.'],
+      STANDALONESHORTWEEKDAYS: const [
+    'ig.',
+    'al.',
+    'ar.',
+    'az.',
+    'og.',
+    'or.',
+    'lr.'
+  ],
+      NARROWWEEKDAYS: const ['I', 'A', 'A', 'A', 'O', 'O', 'L'],
+      STANDALONENARROWWEEKDAYS: const ['I', 'A', 'A', 'A', 'O', 'O', 'L'],
+      SHORTQUARTERS: const ['1Hh', '2Hh', '3Hh', '4Hh'],
+      QUARTERS: const [
+    '1. hiruhilekoa',
+    '2. hiruhilekoa',
+    '3. hiruhilekoa',
+    '4. hiruhilekoa'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const [
+    'y(\'e\')\'ko\' MMMM d, EEEE',
+    'y(\'e\')\'ko\' MMMM d',
+    'y MMM d',
+    'y-MM-dd'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale fa.
+   */
+  "fa": new DateSymbols(
+      NAME: "fa",
+      ERAS: const ['ق.م.', 'م.'],
+      ERANAMES: const ['قبل از میلاد', 'میلادی'],
+      NARROWMONTHS: const [
+    'ژ',
+    'ف',
+    'م',
+    'آ',
+    'م',
+    'ژ',
+    'ژ',
+    'ا',
+    'س',
+    'ا',
+    'ن',
+    'د'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ژ',
+    'ف',
+    'م',
+    'آ',
+    'م',
+    'ژ',
+    'ژ',
+    'ا',
+    'س',
+    'ا',
+    'ن',
+    'د'
+  ],
+      MONTHS: const [
+    'ژانویهٔ',
+    'فوریهٔ',
+    'مارس',
+    'آوریل',
+    'مهٔ',
+    'ژوئن',
+    'ژوئیهٔ',
+    'اوت',
+    'سپتامبر',
+    'اکتبر',
+    'نوامبر',
+    'دسامبر'
+  ],
+      STANDALONEMONTHS: const [
+    'ژانویه',
+    'فوریه',
+    'مارس',
+    'آوریل',
+    'مه',
+    'ژوئن',
+    'ژوئیه',
+    'اوت',
+    'سپتامبر',
+    'اکتبر',
+    'نوامبر',
+    'دسامبر'
+  ],
+      SHORTMONTHS: const [
+    'ژانویهٔ',
+    'فوریهٔ',
+    'مارس',
+    'آوریل',
+    'مهٔ',
+    'ژوئن',
+    'ژوئیهٔ',
+    'اوت',
+    'سپتامبر',
+    'اکتبر',
+    'نوامبر',
+    'دسامبر'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ژانویه',
+    'فوریه',
+    'مارس',
+    'آوریل',
+    'مه',
+    'ژوئن',
+    'ژوئیه',
+    'اوت',
+    'سپتامبر',
+    'اکتبر',
+    'نوامبر',
+    'دسامبر'
+  ],
+      WEEKDAYS: const [
+    'یکشنبه',
+    'دوشنبه',
+    'سه‌شنبه',
+    'چهارشنبه',
+    'پنجشنبه',
+    'جمعه',
+    'شنبه'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'یکشنبه',
+    'دوشنبه',
+    'سه‌شنبه',
+    'چهارشنبه',
+    'پنجشنبه',
+    'جمعه',
+    'شنبه'
+  ],
+      SHORTWEEKDAYS: const [
+    'یکشنبه',
+    'دوشنبه',
+    'سه‌شنبه',
+    'چهارشنبه',
+    'پنجشنبه',
+    'جمعه',
+    'شنبه'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'یکشنبه',
+    'دوشنبه',
+    'سه‌شنبه',
+    'چهارشنبه',
+    'پنجشنبه',
+    'جمعه',
+    'شنبه'
+  ],
+      NARROWWEEKDAYS: const ['ی', 'د', 'س', 'چ', 'پ', 'ج', 'ش'],
+      STANDALONENARROWWEEKDAYS: const ['ی', 'د', 'س', 'چ', 'پ', 'ج', 'ش'],
+      SHORTQUARTERS: const ['س‌م۱', 'س‌م۲', 'س‌م۳', 'س‌م۴'],
+      QUARTERS: const [
+    'سه‌ماههٔ اول',
+    'سه‌ماههٔ دوم',
+    'سه‌ماههٔ سوم',
+    'سه‌ماههٔ چهارم'
+  ],
+      AMPMS: const ['قبل‌ازظهر', 'بعدازظهر'],
+      DATEFORMATS: const ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'y/M/d'],
+      TIMEFORMATS: const ['H:mm:ss (zzzz)', 'H:mm:ss (z)', 'H:mm:ss', 'H:mm'],
+      DATETIMEFORMATS: const [
+    '{1}، ساعت {0}',
+    '{1}، ساعت {0}',
+    '{1}،‏ {0}',
+    '{1}،‏ {0}'
+  ],
+      FIRSTDAYOFWEEK: 5,
+      WEEKENDRANGE: const [3, 4],
+      FIRSTWEEKCUTOFFDAY: 4),
+  /**
+   * Date/time formatting symbols for locale fi.
+   */
+  "fi": new DateSymbols(
+      NAME: "fi",
+      ERAS: const ['eKr.', 'jKr.'],
+      ERANAMES: const [
+    'ennen Kristuksen syntymää',
+    'jälkeen Kristuksen syntymän'
+  ],
+      NARROWMONTHS: const [
+    'T',
+    'H',
+    'M',
+    'H',
+    'T',
+    'K',
+    'H',
+    'E',
+    'S',
+    'L',
+    'M',
+    'J'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'T',
+    'H',
+    'M',
+    'H',
+    'T',
+    'K',
+    'H',
+    'E',
+    'S',
+    'L',
+    'M',
+    'J'
+  ],
+      MONTHS: const [
+    'tammikuuta',
+    'helmikuuta',
+    'maaliskuuta',
+    'huhtikuuta',
+    'toukokuuta',
+    'kesäkuuta',
+    'heinäkuuta',
+    'elokuuta',
+    'syyskuuta',
+    'lokakuuta',
+    'marraskuuta',
+    'joulukuuta'
+  ],
+      STANDALONEMONTHS: const [
+    'tammikuu',
+    'helmikuu',
+    'maaliskuu',
+    'huhtikuu',
+    'toukokuu',
+    'kesäkuu',
+    'heinäkuu',
+    'elokuu',
+    'syyskuu',
+    'lokakuu',
+    'marraskuu',
+    'joulukuu'
+  ],
+      SHORTMONTHS: const [
+    'tammikuuta',
+    'helmikuuta',
+    'maaliskuuta',
+    'huhtikuuta',
+    'toukokuuta',
+    'kesäkuuta',
+    'heinäkuuta',
+    'elokuuta',
+    'syyskuuta',
+    'lokakuuta',
+    'marraskuuta',
+    'joulukuuta'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'tammi',
+    'helmi',
+    'maalis',
+    'huhti',
+    'touko',
+    'kesä',
+    'heinä',
+    'elo',
+    'syys',
+    'loka',
+    'marras',
+    'joulu'
+  ],
+      WEEKDAYS: const [
+    'sunnuntaina',
+    'maanantaina',
+    'tiistaina',
+    'keskiviikkona',
+    'torstaina',
+    'perjantaina',
+    'lauantaina'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'sunnuntai',
+    'maanantai',
+    'tiistai',
+    'keskiviikko',
+    'torstai',
+    'perjantai',
+    'lauantai'
+  ],
+      SHORTWEEKDAYS: const ['su', 'ma', 'ti', 'ke', 'to', 'pe', 'la'],
+      STANDALONESHORTWEEKDAYS: const ['su', 'ma', 'ti', 'ke', 'to', 'pe', 'la'],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'K', 'T', 'P', 'L'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'K', 'T', 'P', 'L'],
+      SHORTQUARTERS: const ['1. nelj.', '2. nelj.', '3. nelj.', '4. nelj.'],
+      QUARTERS: const [
+    '1. neljännes',
+    '2. neljännes',
+    '3. neljännes',
+    '4. neljännes'
+  ],
+      AMPMS: const ['ap.', 'ip.'],
+      DATEFORMATS: const ['cccc d. MMMM y', 'd. MMMM y', 'd.M.y', 'd.M.y'],
+      TIMEFORMATS: const ['H.mm.ss zzzz', 'H.mm.ss z', 'H.mm.ss', 'H.mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale fil.
+   */
+  "fil": new DateSymbols(
+      NAME: "fil",
+      ERAS: const ['BC', 'AD'],
+      ERANAMES: const ['BC', 'AD'],
+      NARROWMONTHS: const [
+    'E',
+    'P',
+    'M',
+    'A',
+    'M',
+    'H',
+    'H',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'E',
+    'P',
+    'M',
+    'A',
+    'M',
+    'H',
+    'H',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Enero',
+    'Pebrero',
+    'Marso',
+    'Abril',
+    'Mayo',
+    'Hunyo',
+    'Hulyo',
+    'Agosto',
+    'Setyembre',
+    'Oktubre',
+    'Nobyembre',
+    'Disyembre'
+  ],
+      STANDALONEMONTHS: const [
+    'Enero',
+    'Pebrero',
+    'Marso',
+    'Abril',
+    'Mayo',
+    'Hunyo',
+    'Hulyo',
+    'Agosto',
+    'Setyembre',
+    'Oktubre',
+    'Nobyembre',
+    'Disyembre'
+  ],
+      SHORTMONTHS: const [
+    'Ene',
+    'Peb',
+    'Mar',
+    'Abr',
+    'May',
+    'Hun',
+    'Hul',
+    'Ago',
+    'Set',
+    'Okt',
+    'Nob',
+    'Dis'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Ene',
+    'Peb',
+    'Mar',
+    'Abr',
+    'May',
+    'Hun',
+    'Hul',
+    'Ago',
+    'Set',
+    'Okt',
+    'Nob',
+    'Dis'
+  ],
+      WEEKDAYS: const [
+    'Linggo',
+    'Lunes',
+    'Martes',
+    'Miyerkules',
+    'Huwebes',
+    'Biyernes',
+    'Sabado'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Linggo',
+    'Lunes',
+    'Martes',
+    'Miyerkules',
+    'Huwebes',
+    'Biyernes',
+    'Sabado'
+  ],
+      SHORTWEEKDAYS: const ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Lin',
+    'Lun',
+    'Mar',
+    'Miy',
+    'Huw',
+    'Biy',
+    'Sab'
+  ],
+      NARROWWEEKDAYS: const ['L', 'L', 'M', 'M', 'H', 'B', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['L', 'L', 'M', 'M', 'H', 'B', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    'ika-1 quarter',
+    'ika-2 quarter',
+    'ika-3 quarter',
+    'ika-4 na quarter'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const [
+    '{1} \'ng\' {0}',
+    '{1} \'ng\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale fr.
+   */
+  "fr": new DateSymbols(
+      NAME: "fr",
+      ERAS: const ['av. J.-C.', 'ap. J.-C.'],
+      ERANAMES: const ['avant Jésus-Christ', 'après Jésus-Christ'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'janvier',
+    'février',
+    'mars',
+    'avril',
+    'mai',
+    'juin',
+    'juillet',
+    'août',
+    'septembre',
+    'octobre',
+    'novembre',
+    'décembre'
+  ],
+      STANDALONEMONTHS: const [
+    'janvier',
+    'février',
+    'mars',
+    'avril',
+    'mai',
+    'juin',
+    'juillet',
+    'août',
+    'septembre',
+    'octobre',
+    'novembre',
+    'décembre'
+  ],
+      SHORTMONTHS: const [
+    'janv.',
+    'févr.',
+    'mars',
+    'avr.',
+    'mai',
+    'juin',
+    'juil.',
+    'août',
+    'sept.',
+    'oct.',
+    'nov.',
+    'déc.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'janv.',
+    'févr.',
+    'mars',
+    'avr.',
+    'mai',
+    'juin',
+    'juil.',
+    'août',
+    'sept.',
+    'oct.',
+    'nov.',
+    'déc.'
+  ],
+      WEEKDAYS: const [
+    'dimanche',
+    'lundi',
+    'mardi',
+    'mercredi',
+    'jeudi',
+    'vendredi',
+    'samedi'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'dimanche',
+    'lundi',
+    'mardi',
+    'mercredi',
+    'jeudi',
+    'vendredi',
+    'samedi'
+  ],
+      SHORTWEEKDAYS: const [
+    'dim.',
+    'lun.',
+    'mar.',
+    'mer.',
+    'jeu.',
+    'ven.',
+    'sam.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'dim.',
+    'lun.',
+    'mar.',
+    'mer.',
+    'jeu.',
+    'ven.',
+    'sam.'
+  ],
+      NARROWWEEKDAYS: const ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
+      SHORTQUARTERS: const ['T1', 'T2', 'T3', 'T4'],
+      QUARTERS: const [
+    '1er trimestre',
+    '2e trimestre',
+    '3e trimestre',
+    '4e trimestre'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale fr_CA.
+   */
+  "fr_CA": new DateSymbols(
+      NAME: "fr_CA",
+      ERAS: const ['av. J.-C.', 'ap. J.-C.'],
+      ERANAMES: const ['avant Jésus-Christ', 'après Jésus-Christ'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'janvier',
+    'février',
+    'mars',
+    'avril',
+    'mai',
+    'juin',
+    'juillet',
+    'août',
+    'septembre',
+    'octobre',
+    'novembre',
+    'décembre'
+  ],
+      STANDALONEMONTHS: const [
+    'janvier',
+    'février',
+    'mars',
+    'avril',
+    'mai',
+    'juin',
+    'juillet',
+    'août',
+    'septembre',
+    'octobre',
+    'novembre',
+    'décembre'
+  ],
+      SHORTMONTHS: const [
+    'janv.',
+    'févr.',
+    'mars',
+    'avr.',
+    'mai',
+    'juin',
+    'juil.',
+    'août',
+    'sept.',
+    'oct.',
+    'nov.',
+    'déc.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'janv.',
+    'févr.',
+    'mars',
+    'avr.',
+    'mai',
+    'juin',
+    'juil.',
+    'août',
+    'sept.',
+    'oct.',
+    'nov.',
+    'déc.'
+  ],
+      WEEKDAYS: const [
+    'dimanche',
+    'lundi',
+    'mardi',
+    'mercredi',
+    'jeudi',
+    'vendredi',
+    'samedi'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'dimanche',
+    'lundi',
+    'mardi',
+    'mercredi',
+    'jeudi',
+    'vendredi',
+    'samedi'
+  ],
+      SHORTWEEKDAYS: const [
+    'dim.',
+    'lun.',
+    'mar.',
+    'mer.',
+    'jeu.',
+    'ven.',
+    'sam.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'dim.',
+    'lun.',
+    'mar.',
+    'mer.',
+    'jeu.',
+    'ven.',
+    'sam.'
+  ],
+      NARROWWEEKDAYS: const ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
+      SHORTQUARTERS: const ['T1', 'T2', 'T3', 'T4'],
+      QUARTERS: const [
+    '1er trimestre',
+    '2e trimestre',
+    '3e trimestre',
+    '4e trimestre'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE d MMMM y', 'd MMMM y', 'y-MM-dd', 'yy-MM-dd'],
+      TIMEFORMATS: const [
+    'HH \'h\' mm \'min\' ss \'s\' zzzz',
+    'HH:mm:ss z',
+    'HH:mm:ss',
+    'HH:mm'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale gl.
+   */
+  "gl": new DateSymbols(
+      NAME: "gl",
+      ERAS: const ['a.C.', 'd.C.'],
+      ERANAMES: const ['antes de Cristo', 'despois de Cristo'],
+      NARROWMONTHS: const [
+    'X',
+    'F',
+    'M',
+    'A',
+    'M',
+    'X',
+    'X',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'X',
+    'F',
+    'M',
+    'A',
+    'M',
+    'X',
+    'X',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'xaneiro',
+    'febreiro',
+    'marzo',
+    'abril',
+    'maio',
+    'xuño',
+    'xullo',
+    'agosto',
+    'setembro',
+    'outubro',
+    'novembro',
+    'decembro'
+  ],
+      STANDALONEMONTHS: const [
+    'Xaneiro',
+    'Febreiro',
+    'Marzo',
+    'Abril',
+    'Maio',
+    'Xuño',
+    'Xullo',
+    'Agosto',
+    'Setembro',
+    'Outubro',
+    'Novembro',
+    'Decembro'
+  ],
+      SHORTMONTHS: const [
+    'xan',
+    'feb',
+    'mar',
+    'abr',
+    'mai',
+    'xuñ',
+    'xul',
+    'ago',
+    'set',
+    'out',
+    'nov',
+    'dec'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Xan',
+    'Feb',
+    'Mar',
+    'Abr',
+    'Mai',
+    'Xuñ',
+    'Xul',
+    'Ago',
+    'Set',
+    'Out',
+    'Nov',
+    'Dec'
+  ],
+      WEEKDAYS: const [
+    'domingo',
+    'luns',
+    'martes',
+    'mércores',
+    'xoves',
+    'venres',
+    'sábado'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Domingo',
+    'Luns',
+    'Martes',
+    'Mércores',
+    'Xoves',
+    'Venres',
+    'Sábado'
+  ],
+      SHORTWEEKDAYS: const ['dom', 'lun', 'mar', 'mér', 'xov', 'ven', 'sáb'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Dom',
+    'Lun',
+    'Mar',
+    'Mér',
+    'Xov',
+    'Ven',
+    'Sáb'
+  ],
+      NARROWWEEKDAYS: const ['D', 'L', 'M', 'M', 'X', 'V', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['D', 'L', 'M', 'M', 'X', 'V', 'S'],
+      SHORTQUARTERS: const ['T1', 'T2', 'T3', 'T4'],
+      QUARTERS: const [
+    '1o trimestre',
+    '2o trimestre',
+    '3o trimestre',
+    '4o trimestre'
+  ],
+      AMPMS: const ['a.m.', 'p.m.'],
+      DATEFORMATS: const [
+    'EEEE dd MMMM y',
+    'dd MMMM y',
+    'd MMM, y',
+    'dd/MM/yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale gsw.
+   */
+  "gsw": new DateSymbols(
+      NAME: "gsw",
+      ERAS: const ['v. Chr.', 'n. Chr.'],
+      ERANAMES: const ['v. Chr.', 'n. Chr.'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Januar',
+    'Februar',
+    'März',
+    'April',
+    'Mai',
+    'Juni',
+    'Juli',
+    'Auguscht',
+    'Septämber',
+    'Oktoober',
+    'Novämber',
+    'Dezämber'
+  ],
+      STANDALONEMONTHS: const [
+    'Januar',
+    'Februar',
+    'März',
+    'April',
+    'Mai',
+    'Juni',
+    'Juli',
+    'Auguscht',
+    'Septämber',
+    'Oktoober',
+    'Novämber',
+    'Dezämber'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mär',
+    'Apr',
+    'Mai',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Dez'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mär',
+    'Apr',
+    'Mai',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Dez'
+  ],
+      WEEKDAYS: const [
+    'Sunntig',
+    'Määntig',
+    'Ziischtig',
+    'Mittwuch',
+    'Dunschtig',
+    'Friitig',
+    'Samschtig'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sunntig',
+    'Määntig',
+    'Ziischtig',
+    'Mittwuch',
+    'Dunschtig',
+    'Friitig',
+    'Samschtig'
+  ],
+      SHORTWEEKDAYS: const ['Su.', 'Mä.', 'Zi.', 'Mi.', 'Du.', 'Fr.', 'Sa.'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Su.',
+    'Mä.',
+    'Zi.',
+    'Mi.',
+    'Du.',
+    'Fr.',
+    'Sa.'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'D', 'M', 'D', 'F', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const ['1. Quartal', '2. Quartal', '3. Quartal', '4. Quartal'],
+      AMPMS: const ['vorm.', 'nam.'],
+      DATEFORMATS: const [
+    'EEEE, d. MMMM y',
+    'd. MMMM y',
+    'dd.MM.y',
+    'dd.MM.yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale gu.
+   */
+  "gu": new DateSymbols(
+      NAME: "gu",
+      ERAS: const ['ઈસુના જન્મ પહેલા', 'ઇસવીસન'],
+      ERANAMES: const ['ઈસવીસન પૂર્વે', 'ઇસવીસન'],
+      NARROWMONTHS: const [
+    'જા',
+    'ફે',
+    'મા',
+    'એ',
+    'મે',
+    'જૂ',
+    'જુ',
+    'ઑ',
+    'સ',
+    'ઑ',
+    'ન',
+    'ડિ'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'જા',
+    'ફે',
+    'મા',
+    'એ',
+    'મે',
+    'જૂ',
+    'જુ',
+    'ઑ',
+    'સ',
+    'ઑ',
+    'ન',
+    'ડિ'
+  ],
+      MONTHS: const [
+    'જાન્યુઆરી',
+    'ફેબ્રુઆરી',
+    'માર્ચ',
+    'એપ્રિલ',
+    'મે',
+    'જૂન',
+    'જુલાઈ',
+    'ઑગસ્ટ',
+    'સપ્ટેમ્બર',
+    'ઑક્ટોબર',
+    'નવેમ્બર',
+    'ડિસેમ્બર'
+  ],
+      STANDALONEMONTHS: const [
+    'જાન્યુઆરી',
+    'ફેબ્રુઆરી',
+    'માર્ચ',
+    'એપ્રિલ',
+    'મે',
+    'જૂન',
+    'જુલાઈ',
+    'ઑગસ્ટ',
+    'સપ્ટેમ્બર',
+    'ઑક્ટોબર',
+    'નવેમ્બર',
+    'ડિસેમ્બર'
+  ],
+      SHORTMONTHS: const [
+    'જાન્યુ',
+    'ફેબ્રુ',
+    'માર્ચ',
+    'એપ્રિલ',
+    'મે',
+    'જૂન',
+    'જુલાઈ',
+    'ઑગસ્ટ',
+    'સપ્ટે',
+    'ઑક્ટો',
+    'નવે',
+    'ડિસે'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'જાન્યુ',
+    'ફેબ્રુ',
+    'માર્ચ',
+    'એપ્રિલ',
+    'મે',
+    'જૂન',
+    'જુલાઈ',
+    'ઑગ',
+    'સપ્ટે',
+    'ઑક્ટો',
+    'નવે',
+    'ડિસે'
+  ],
+      WEEKDAYS: const [
+    'રવિવાર',
+    'સોમવાર',
+    'મંગળવાર',
+    'બુધવાર',
+    'ગુરુવાર',
+    'શુક્રવાર',
+    'શનિવાર'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'રવિવાર',
+    'સોમવાર',
+    'મંગળવાર',
+    'બુધવાર',
+    'ગુરુવાર',
+    'શુક્રવાર',
+    'શનિવાર'
+  ],
+      SHORTWEEKDAYS: const [
+    'રવિ',
+    'સોમ',
+    'મંગળ',
+    'બુધ',
+    'ગુરુ',
+    'શુક્ર',
+    'શનિ'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'રવિ',
+    'સોમ',
+    'મંગળ',
+    'બુધ',
+    'ગુરુ',
+    'શુક્ર',
+    'શનિ'
+  ],
+      NARROWWEEKDAYS: const ['ર', 'સો', 'મં', 'બુ', 'ગુ', 'શુ', 'શ'],
+      STANDALONENARROWWEEKDAYS: const ['ર', 'સો', 'મં', 'બુ', 'ગુ', 'શુ', 'શ'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    'પહેલો ત્રિમાસ',
+    'બીજો ત્રિમાસ',
+    'ત્રીજો ત્રિમાસ',
+    'ચોથો ત્રિમાસ'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const [
+    'EEEE, d MMMM, y',
+    'd MMMM, y',
+    'd MMM, y',
+    'd-MM-yy'
+  ],
+      TIMEFORMATS: const [
+    'hh:mm:ss a zzzz',
+    'hh:mm:ss a z',
+    'hh:mm:ss a',
+    'hh:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [6, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale haw.
+   */
+  "haw": new DateSymbols(
+      NAME: "haw",
+      ERAS: const ['BCE', 'CE'],
+      ERANAMES: const ['BCE', 'CE'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    'Ianuali',
+    'Pepeluali',
+    'Malaki',
+    'ʻApelila',
+    'Mei',
+    'Iune',
+    'Iulai',
+    'ʻAukake',
+    'Kepakemapa',
+    'ʻOkakopa',
+    'Nowemapa',
+    'Kekemapa'
+  ],
+      STANDALONEMONTHS: const [
+    'Ianuali',
+    'Pepeluali',
+    'Malaki',
+    'ʻApelila',
+    'Mei',
+    'Iune',
+    'Iulai',
+    'ʻAukake',
+    'Kepakemapa',
+    'ʻOkakopa',
+    'Nowemapa',
+    'Kekemapa'
+  ],
+      SHORTMONTHS: const [
+    'Ian.',
+    'Pep.',
+    'Mal.',
+    'ʻAp.',
+    'Mei',
+    'Iun.',
+    'Iul.',
+    'ʻAu.',
+    'Kep.',
+    'ʻOk.',
+    'Now.',
+    'Kek.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Ian.',
+    'Pep.',
+    'Mal.',
+    'ʻAp.',
+    'Mei',
+    'Iun.',
+    'Iul.',
+    'ʻAu.',
+    'Kep.',
+    'ʻOk.',
+    'Now.',
+    'Kek.'
+  ],
+      WEEKDAYS: const [
+    'Lāpule',
+    'Poʻakahi',
+    'Poʻalua',
+    'Poʻakolu',
+    'Poʻahā',
+    'Poʻalima',
+    'Poʻaono'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Lāpule',
+    'Poʻakahi',
+    'Poʻalua',
+    'Poʻakolu',
+    'Poʻahā',
+    'Poʻalima',
+    'Poʻaono'
+  ],
+      SHORTWEEKDAYS: const ['LP', 'P1', 'P2', 'P3', 'P4', 'P5', 'P6'],
+      STANDALONESHORTWEEKDAYS: const ['LP', 'P1', 'P2', 'P3', 'P4', 'P5', 'P6'],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale he.
+   */
+  "he": new DateSymbols(
+      NAME: "he",
+      ERAS: const ['לפנה״ס', 'לסה״נ'],
+      ERANAMES: const ['לפני הספירה', 'לספירה'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    'ינואר',
+    'פברואר',
+    'מרץ',
+    'אפריל',
+    'מאי',
+    'יוני',
+    'יולי',
+    'אוגוסט',
+    'ספטמבר',
+    'אוקטובר',
+    'נובמבר',
+    'דצמבר'
+  ],
+      STANDALONEMONTHS: const [
+    'ינואר',
+    'פברואר',
+    'מרץ',
+    'אפריל',
+    'מאי',
+    'יוני',
+    'יולי',
+    'אוגוסט',
+    'ספטמבר',
+    'אוקטובר',
+    'נובמבר',
+    'דצמבר'
+  ],
+      SHORTMONTHS: const [
+    'ינו׳',
+    'פבר׳',
+    'מרץ',
+    'אפר׳',
+    'מאי',
+    'יוני',
+    'יולי',
+    'אוג׳',
+    'ספט׳',
+    'אוק׳',
+    'נוב׳',
+    'דצמ׳'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ינו׳',
+    'פבר׳',
+    'מרץ',
+    'אפר׳',
+    'מאי',
+    'יוני',
+    'יולי',
+    'אוג׳',
+    'ספט׳',
+    'אוק׳',
+    'נוב׳',
+    'דצמ׳'
+  ],
+      WEEKDAYS: const [
+    'יום ראשון',
+    'יום שני',
+    'יום שלישי',
+    'יום רביעי',
+    'יום חמישי',
+    'יום שישי',
+    'יום שבת'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'יום ראשון',
+    'יום שני',
+    'יום שלישי',
+    'יום רביעי',
+    'יום חמישי',
+    'יום שישי',
+    'יום שבת'
+  ],
+      SHORTWEEKDAYS: const [
+    'יום א׳',
+    'יום ב׳',
+    'יום ג׳',
+    'יום ד׳',
+    'יום ה׳',
+    'יום ו׳',
+    'שבת'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'יום א׳',
+    'יום ב׳',
+    'יום ג׳',
+    'יום ד׳',
+    'יום ה׳',
+    'יום ו׳',
+    'שבת'
+  ],
+      NARROWWEEKDAYS: const ['א׳', 'ב׳', 'ג׳', 'ד׳', 'ה׳', 'ו׳', 'ש׳'],
+      STANDALONENARROWWEEKDAYS: const [
+    'א׳',
+    'ב׳',
+    'ג׳',
+    'ד׳',
+    'ה׳',
+    'ו׳',
+    'ש׳'
+  ],
+      SHORTQUARTERS: const ['רבעון 1', 'רבעון 2', 'רבעון 3', 'רבעון 4'],
+      QUARTERS: const ['רבעון 1', 'רבעון 2', 'רבעון 3', 'רבעון 4'],
+      AMPMS: const ['לפנה״צ', 'אחה״צ'],
+      DATEFORMATS: const [
+    'EEEE, d בMMMM y',
+    'd בMMMM y',
+    'd בMMM y',
+    'dd/MM/yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const [
+    '{1} בשעה {0}',
+    '{1} בשעה {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [4, 5],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale hi.
+   */
+  "hi": new DateSymbols(
+      NAME: "hi",
+      ERAS: const ['ईसा-पूर्व', 'ईस्वी'],
+      ERANAMES: const ['ईसा-पूर्व', 'ईस्वी'],
+      NARROWMONTHS: const [
+    'ज',
+    'फ़',
+    'मा',
+    'अ',
+    'म',
+    'जू',
+    'जु',
+    'अ',
+    'सि',
+    'अ',
+    'न',
+    'दि'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ज',
+    'फ़',
+    'मा',
+    'अ',
+    'म',
+    'जू',
+    'जु',
+    'अ',
+    'सि',
+    'अ',
+    'न',
+    'दि'
+  ],
+      MONTHS: const [
+    'जनवरी',
+    'फ़रवरी',
+    'मार्च',
+    'अप्रैल',
+    'मई',
+    'जून',
+    'जुलाई',
+    'अगस्त',
+    'सितंबर',
+    'अक्टूबर',
+    'नवंबर',
+    'दिसंबर'
+  ],
+      STANDALONEMONTHS: const [
+    'जनवरी',
+    'फ़रवरी',
+    'मार्च',
+    'अप्रैल',
+    'मई',
+    'जून',
+    'जुलाई',
+    'अगस्त',
+    'सितंबर',
+    'अक्टूबर',
+    'नवंबर',
+    'दिसंबर'
+  ],
+      SHORTMONTHS: const [
+    'जन',
+    'फ़र',
+    'मार्च',
+    'अप्रै',
+    'मई',
+    'जून',
+    'जुला',
+    'अग',
+    'सितं',
+    'अक्टू',
+    'नवं',
+    'दिसं'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'जन',
+    'फ़र',
+    'मार्च',
+    'अप्रै',
+    'मई',
+    'जून',
+    'जुला',
+    'अग',
+    'सितं',
+    'अक्टू',
+    'नवं',
+    'दिसं'
+  ],
+      WEEKDAYS: const [
+    'रविवार',
+    'सोमवार',
+    'मंगलवार',
+    'बुधवार',
+    'गुरुवार',
+    'शुक्रवार',
+    'शनिवार'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'रविवार',
+    'सोमवार',
+    'मंगलवार',
+    'बुधवार',
+    'गुरुवार',
+    'शुक्रवार',
+    'शनिवार'
+  ],
+      SHORTWEEKDAYS: const [
+    'रवि',
+    'सोम',
+    'मंगल',
+    'बुध',
+    'गुरु',
+    'शुक्र',
+    'शनि'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'रवि',
+    'सोम',
+    'मंगल',
+    'बुध',
+    'गुरु',
+    'शुक्र',
+    'शनि'
+  ],
+      NARROWWEEKDAYS: const ['र', 'सो', 'मं', 'बु', 'गु', 'शु', 'श'],
+      STANDALONENARROWWEEKDAYS: const ['र', 'सो', 'मं', 'बु', 'गु', 'शु', 'श'],
+      SHORTQUARTERS: const ['ति1', 'ति2', 'ति3', 'ति4'],
+      QUARTERS: const [
+    'पहली तिमाही',
+    'दूसरी तिमाही',
+    'तीसरी तिमाही',
+    'चौथी तिमाही'
+  ],
+      AMPMS: const ['am', 'pm'],
+      DATEFORMATS: const ['EEEE, d MMMM y', 'd MMMM y', 'dd-MM-y', 'd-M-yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const [
+    '{1} को {0}',
+    '{1} को {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [6, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale hr.
+   */
+  "hr": new DateSymbols(
+      NAME: "hr",
+      ERAS: const ['pr. Kr.', 'p. Kr.'],
+      ERANAMES: const ['Prije Krista', 'Poslije Krista'],
+      NARROWMONTHS: const [
+    '1.',
+    '2.',
+    '3.',
+    '4.',
+    '5.',
+    '6.',
+    '7.',
+    '8.',
+    '9.',
+    '10.',
+    '11.',
+    '12.'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1.',
+    '2.',
+    '3.',
+    '4.',
+    '5.',
+    '6.',
+    '7.',
+    '8.',
+    '9.',
+    '10.',
+    '11.',
+    '12.'
+  ],
+      MONTHS: const [
+    'siječnja',
+    'veljače',
+    'ožujka',
+    'travnja',
+    'svibnja',
+    'lipnja',
+    'srpnja',
+    'kolovoza',
+    'rujna',
+    'listopada',
+    'studenoga',
+    'prosinca'
+  ],
+      STANDALONEMONTHS: const [
+    'siječanj',
+    'veljača',
+    'ožujak',
+    'travanj',
+    'svibanj',
+    'lipanj',
+    'srpanj',
+    'kolovoz',
+    'rujan',
+    'listopad',
+    'studeni',
+    'prosinac'
+  ],
+      SHORTMONTHS: const [
+    'sij',
+    'velj',
+    'ožu',
+    'tra',
+    'svi',
+    'lip',
+    'srp',
+    'kol',
+    'ruj',
+    'lis',
+    'stu',
+    'pro'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'sij',
+    'velj',
+    'ožu',
+    'tra',
+    'svi',
+    'lip',
+    'srp',
+    'kol',
+    'ruj',
+    'lis',
+    'stu',
+    'pro'
+  ],
+      WEEKDAYS: const [
+    'nedjelja',
+    'ponedjeljak',
+    'utorak',
+    'srijeda',
+    'četvrtak',
+    'petak',
+    'subota'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'nedjelja',
+    'ponedjeljak',
+    'utorak',
+    'srijeda',
+    'četvrtak',
+    'petak',
+    'subota'
+  ],
+      SHORTWEEKDAYS: const ['ned', 'pon', 'uto', 'sri', 'čet', 'pet', 'sub'],
+      STANDALONESHORTWEEKDAYS: const [
+    'ned',
+    'pon',
+    'uto',
+    'sri',
+    'čet',
+    'pet',
+    'sub'
+  ],
+      NARROWWEEKDAYS: const ['N', 'P', 'U', 'S', 'Č', 'P', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['n', 'p', 'u', 's', 'č', 'p', 's'],
+      SHORTQUARTERS: const ['1kv', '2kv', '3kv', '4kv'],
+      QUARTERS: const ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const [
+    'EEEE, d. MMMM y.',
+    'd. MMMM y.',
+    'd. MMM y.',
+    'd.M.yy.'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const [
+    '{1} \'u\' {0}',
+    '{1} \'u\' {0}',
+    '{1} {0}',
+    '{1} {0}'
+  ],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale hu.
+   */
+  "hu": new DateSymbols(
+      NAME: "hu",
+      ERAS: const ['i. e.', 'i. sz.'],
+      ERANAMES: const ['időszámításunk előtt', 'időszámításunk szerint'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'Á',
+    'M',
+    'J',
+    'J',
+    'A',
+    'Sz',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'Á',
+    'M',
+    'J',
+    'J',
+    'A',
+    'Sz',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'január',
+    'február',
+    'március',
+    'április',
+    'május',
+    'június',
+    'július',
+    'augusztus',
+    'szeptember',
+    'október',
+    'november',
+    'december'
+  ],
+      STANDALONEMONTHS: const [
+    'január',
+    'február',
+    'március',
+    'április',
+    'május',
+    'június',
+    'július',
+    'augusztus',
+    'szeptember',
+    'október',
+    'november',
+    'december'
+  ],
+      SHORTMONTHS: const [
+    'jan.',
+    'febr.',
+    'márc.',
+    'ápr.',
+    'máj.',
+    'jún.',
+    'júl.',
+    'aug.',
+    'szept.',
+    'okt.',
+    'nov.',
+    'dec.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'jan.',
+    'febr.',
+    'márc.',
+    'ápr.',
+    'máj.',
+    'jún.',
+    'júl.',
+    'aug.',
+    'szept.',
+    'okt.',
+    'nov.',
+    'dec.'
+  ],
+      WEEKDAYS: const [
+    'vasárnap',
+    'hétfő',
+    'kedd',
+    'szerda',
+    'csütörtök',
+    'péntek',
+    'szombat'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'vasárnap',
+    'hétfő',
+    'kedd',
+    'szerda',
+    'csütörtök',
+    'péntek',
+    'szombat'
+  ],
+      SHORTWEEKDAYS: const ['V', 'H', 'K', 'Sze', 'Cs', 'P', 'Szo'],
+      STANDALONESHORTWEEKDAYS: const ['V', 'H', 'K', 'Sze', 'Cs', 'P', 'Szo'],
+      NARROWWEEKDAYS: const ['V', 'H', 'K', 'Sz', 'Cs', 'P', 'Sz'],
+      STANDALONENARROWWEEKDAYS: const ['V', 'H', 'K', 'Sz', 'Cs', 'P', 'Sz'],
+      SHORTQUARTERS: const ['N1', 'N2', 'N3', 'N4'],
+      QUARTERS: const [
+    'I. negyedév',
+    'II. negyedév',
+    'III. negyedév',
+    'IV. negyedév'
+  ],
+      AMPMS: const ['de.', 'du.'],
+      DATEFORMATS: const [
+    'y. MMMM d., EEEE',
+    'y. MMMM d.',
+    'y. MMM d.',
+    'y. MM. dd.'
+  ],
+      TIMEFORMATS: const ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale hy.
+   */
+  "hy": new DateSymbols(
+      NAME: "hy",
+      ERAS: const ['մ.թ.ա.', 'մ.թ.'],
+      ERANAMES: const ['մ.թ.ա.', 'մ.թ.'],
+      NARROWMONTHS: const [
+    'Հ',
+    'Փ',
+    'Մ',
+    'Ա',
+    'Մ',
+    'Հ',
+    'Հ',
+    'Օ',
+    'Ս',
+    'Հ',
+    'Ն',
+    'Դ'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'Հ',
+    'Փ',
+    'Մ',
+    'Ա',
+    'Մ',
+    'Հ',
+    'Հ',
+    'Օ',
+    'Ս',
+    'Հ',
+    'Ն',
+    'Դ'
+  ],
+      MONTHS: const [
+    'հունվարի',
+    'փետրվարի',
+    'մարտի',
+    'ապրիլի',
+    'մայիսի',
+    'հունիսի',
+    'հուլիսի',
+    'օգոստոսի',
+    'սեպտեմբերի',
+    'հոկտեմբերի',
+    'նոյեմբերի',
+    'դեկտեմբերի'
+  ],
+      STANDALONEMONTHS: const [
+    'հունվար',
+    'փետրվար',
+    'մարտ',
+    'ապրիլ',
+    'մայիս',
+    'հունիս',
+    'հուլիս',
+    'օգոստոս',
+    'սեպտեմբեր',
+    'հոկտեմբեր',
+    'նոյեմբեր',
+    'դեկտեմբեր'
+  ],
+      SHORTMONTHS: const [
+    'հնվ',
+    'փտվ',
+    'մրտ',
+    'ապր',
+    'մյս',
+    'հնս',
+    'հլս',
+    'օգս',
+    'սպտ',
+    'հկտ',
+    'նյմ',
+    'դկտ'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'հնվ',
+    'փտվ',
+    'մրտ',
+    'ապր',
+    'մյս',
+    'հնս',
+    'հլս',
+    'օգս',
+    'սպտ',
+    'հկտ',
+    'նյմ',
+    'դկտ'
+  ],
+      WEEKDAYS: const [
+    'կիրակի',
+    'երկուշաբթի',
+    'երեքշաբթի',
+    'չորեքշաբթի',
+    'հինգշաբթի',
+    'ուրբաթ',
+    'շաբաթ'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'կիրակի',
+    'երկուշաբթի',
+    'երեքշաբթի',
+    'չորեքշաբթի',
+    'հինգշաբթի',
+    'ուրբաթ',
+    'շաբաթ'
+  ],
+      SHORTWEEKDAYS: const ['կիր', 'երկ', 'երք', 'չրք', 'հնգ', 'ուր', 'շբթ'],
+      STANDALONESHORTWEEKDAYS: const [
+    'կիր',
+    'երկ',
+    'երք',
+    'չրք',
+    'հնգ',
+    'ուր',
+    'շբթ'
+  ],
+      NARROWWEEKDAYS: const ['Կ', 'Ե', 'Ե', 'Չ', 'Հ', 'Ու', 'Շ'],
+      STANDALONENARROWWEEKDAYS: const ['Կ', 'Ե', 'Ե', 'Չ', 'Հ', 'Ու', 'Շ'],
+      SHORTQUARTERS: const [
+    '1-ին եռմս.',
+    '2-րդ եռմս.',
+    '3-րդ եռմս.',
+    '4-րդ եռմս.'
+  ],
+      QUARTERS: const [
+    '1-ին եռամսյակ',
+    '2-րդ եռամսյակ',
+    '3-րդ եռամսյակ',
+    '4-րդ եռամսյակ'
+  ],
+      AMPMS: const ['կեսօրից առաջ', 'կեսօրից հետո'],
+      DATEFORMATS: const [
+    'yթ. MMMM d, EEEE',
+    'dd MMMM, yթ.',
+    'dd MMM, y թ.',
+    'dd.MM.yy'
+  ],
+      TIMEFORMATS: const ['H:mm:ss, zzzz', 'H:mm:ss, z', 'H:mm:ss', 'H:mm'],
+      DATETIMEFORMATS: const ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale id.
+   */
+  "id": new DateSymbols(
+      NAME: "id",
+      ERAS: const ['SM', 'M'],
+      ERANAMES: const ['SM', 'M'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Januari',
+    'Februari',
+    'Maret',
+    'April',
+    'Mei',
+    'Juni',
+    'Juli',
+    'Agustus',
+    'September',
+    'Oktober',
+    'November',
+    'Desember'
+  ],
+      STANDALONEMONTHS: const [
+    'Januari',
+    'Februari',
+    'Maret',
+    'April',
+    'Mei',
+    'Juni',
+    'Juli',
+    'Agustus',
+    'September',
+    'Oktober',
+    'November',
+    'Desember'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'Mei',
+    'Jun',
+    'Jul',
+    'Agt',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Des'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'Mei',
+    'Jun',
+    'Jul',
+    'Agt',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Des'
+  ],
+      WEEKDAYS: const [
+    'Minggu',
+    'Senin',
+    'Selasa',
+    'Rabu',
+    'Kamis',
+    'Jumat',
+    'Sabtu'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Minggu',
+    'Senin',
+    'Selasa',
+    'Rabu',
+    'Kamis',
+    'Jumat',
+    'Sabtu'
+  ],
+      SHORTWEEKDAYS: const ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Min',
+    'Sen',
+    'Sel',
+    'Rab',
+    'Kam',
+    'Jum',
+    'Sab'
+  ],
+      NARROWWEEKDAYS: const ['M', 'S', 'S', 'R', 'K', 'J', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['M', 'S', 'S', 'R', 'K', 'J', 'S'],
+      SHORTQUARTERS: const ['K1', 'K2', 'K3', 'K4'],
+      QUARTERS: const [
+    'Kuartal ke-1',
+    'Kuartal ke-2',
+    'Kuartal ke-3',
+    'Kuartal ke-4'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, dd MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/yy'],
+      TIMEFORMATS: const ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale in.
+   */
+  "in": new DateSymbols(
+      NAME: "in",
+      ERAS: const ['SM', 'M'],
+      ERANAMES: const ['SM', 'M'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Januari',
+    'Februari',
+    'Maret',
+    'April',
+    'Mei',
+    'Juni',
+    'Juli',
+    'Agustus',
+    'September',
+    'Oktober',
+    'November',
+    'Desember'
+  ],
+      STANDALONEMONTHS: const [
+    'Januari',
+    'Februari',
+    'Maret',
+    'April',
+    'Mei',
+    'Juni',
+    'Juli',
+    'Agustus',
+    'September',
+    'Oktober',
+    'November',
+    'Desember'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'Mei',
+    'Jun',
+    'Jul',
+    'Agt',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Des'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'Mei',
+    'Jun',
+    'Jul',
+    'Agt',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Des'
+  ],
+      WEEKDAYS: const [
+    'Minggu',
+    'Senin',
+    'Selasa',
+    'Rabu',
+    'Kamis',
+    'Jumat',
+    'Sabtu'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Minggu',
+    'Senin',
+    'Selasa',
+    'Rabu',
+    'Kamis',
+    'Jumat',
+    'Sabtu'
+  ],
+      SHORTWEEKDAYS: const ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Min',
+    'Sen',
+    'Sel',
+    'Rab',
+    'Kam',
+    'Jum',
+    'Sab'
+  ],
+      NARROWWEEKDAYS: const ['M', 'S', 'S', 'R', 'K', 'J', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['M', 'S', 'S', 'R', 'K', 'J', 'S'],
+      SHORTQUARTERS: const ['K1', 'K2', 'K3', 'K4'],
+      QUARTERS: const [
+    'Kuartal ke-1',
+    'Kuartal ke-2',
+    'Kuartal ke-3',
+    'Kuartal ke-4'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, dd MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/yy'],
+      TIMEFORMATS: const ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale is.
+   */
+  "is": new DateSymbols(
+      NAME: "is",
+      ERAS: const ['f.Kr.', 'e.Kr.'],
+      ERANAMES: const ['fyrir Krist', 'eftir Krist'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'Á',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'Á',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'janúar',
+    'febrúar',
+    'mars',
+    'apríl',
+    'maí',
+    'júní',
+    'júlí',
+    'ágúst',
+    'september',
+    'október',
+    'nóvember',
+    'desember'
+  ],
+      STANDALONEMONTHS: const [
+    'janúar',
+    'febrúar',
+    'mars',
+    'apríl',
+    'maí',
+    'júní',
+    'júlí',
+    'ágúst',
+    'september',
+    'október',
+    'nóvember',
+    'desember'
+  ],
+      SHORTMONTHS: const [
+    'jan.',
+    'feb.',
+    'mar.',
+    'apr.',
+    'maí',
+    'jún.',
+    'júl.',
+    'ágú.',
+    'sep.',
+    'okt.',
+    'nóv.',
+    'des.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'jan.',
+    'feb.',
+    'mar.',
+    'apr.',
+    'maí',
+    'jún.',
+    'júl.',
+    'ágú.',
+    'sep.',
+    'okt.',
+    'nóv.',
+    'des.'
+  ],
+      WEEKDAYS: const [
+    'sunnudagur',
+    'mánudagur',
+    'þriðjudagur',
+    'miðvikudagur',
+    'fimmtudagur',
+    'föstudagur',
+    'laugardagur'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'sunnudagur',
+    'mánudagur',
+    'þriðjudagur',
+    'miðvikudagur',
+    'fimmtudagur',
+    'föstudagur',
+    'laugardagur'
+  ],
+      SHORTWEEKDAYS: const [
+    'sun.',
+    'mán.',
+    'þri.',
+    'mið.',
+    'fim.',
+    'fös.',
+    'lau.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'sun.',
+    'mán.',
+    'þri.',
+    'mið.',
+    'fim.',
+    'fös.',
+    'lau.'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'Þ', 'M', 'F', 'F', 'L'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'Þ', 'M', 'F', 'F', 'L'],
+      SHORTQUARTERS: const ['F1', 'F2', 'F3', 'F4'],
+      QUARTERS: const [
+    '1. fjórðungur',
+    '2. fjórðungur',
+    '3. fjórðungur',
+    '4. fjórðungur'
+  ],
+      AMPMS: const ['f.h.', 'e.h.'],
+      DATEFORMATS: const ['EEEE, d. MMMM y', 'd. MMMM y', 'd. MMM y', 'd.M.y'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const [
+    '{1} \'kl.\' {0}',
+    '{1} \'kl.\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale it.
+   */
+  "it": new DateSymbols(
+      NAME: "it",
+      ERAS: const ['aC', 'dC'],
+      ERANAMES: const ['a.C.', 'd.C.'],
+      NARROWMONTHS: const [
+    'G',
+    'F',
+    'M',
+    'A',
+    'M',
+    'G',
+    'L',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'G',
+    'F',
+    'M',
+    'A',
+    'M',
+    'G',
+    'L',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'gennaio',
+    'febbraio',
+    'marzo',
+    'aprile',
+    'maggio',
+    'giugno',
+    'luglio',
+    'agosto',
+    'settembre',
+    'ottobre',
+    'novembre',
+    'dicembre'
+  ],
+      STANDALONEMONTHS: const [
+    'Gennaio',
+    'Febbraio',
+    'Marzo',
+    'Aprile',
+    'Maggio',
+    'Giugno',
+    'Luglio',
+    'Agosto',
+    'Settembre',
+    'Ottobre',
+    'Novembre',
+    'Dicembre'
+  ],
+      SHORTMONTHS: const [
+    'gen',
+    'feb',
+    'mar',
+    'apr',
+    'mag',
+    'giu',
+    'lug',
+    'ago',
+    'set',
+    'ott',
+    'nov',
+    'dic'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'gen',
+    'feb',
+    'mar',
+    'apr',
+    'mag',
+    'giu',
+    'lug',
+    'ago',
+    'set',
+    'ott',
+    'nov',
+    'dic'
+  ],
+      WEEKDAYS: const [
+    'domenica',
+    'lunedì',
+    'martedì',
+    'mercoledì',
+    'giovedì',
+    'venerdì',
+    'sabato'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Domenica',
+    'Lunedì',
+    'Martedì',
+    'Mercoledì',
+    'Giovedì',
+    'Venerdì',
+    'Sabato'
+  ],
+      SHORTWEEKDAYS: const ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
+      STANDALONESHORTWEEKDAYS: const [
+    'dom',
+    'lun',
+    'mar',
+    'mer',
+    'gio',
+    'ven',
+    'sab'
+  ],
+      NARROWWEEKDAYS: const ['D', 'L', 'M', 'M', 'G', 'V', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['D', 'L', 'M', 'M', 'G', 'V', 'S'],
+      SHORTQUARTERS: const ['T1', 'T2', 'T3', 'T4'],
+      QUARTERS: const [
+    '1º trimestre',
+    '2º trimestre',
+    '3º trimestre',
+    '4º trimestre'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE d MMMM y', 'dd MMMM y', 'dd/MMM/y', 'dd/MM/yy'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale iw.
+   */
+  "iw": new DateSymbols(
+      NAME: "iw",
+      ERAS: const ['לפנה״ס', 'לסה״נ'],
+      ERANAMES: const ['לפני הספירה', 'לספירה'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    'ינואר',
+    'פברואר',
+    'מרץ',
+    'אפריל',
+    'מאי',
+    'יוני',
+    'יולי',
+    'אוגוסט',
+    'ספטמבר',
+    'אוקטובר',
+    'נובמבר',
+    'דצמבר'
+  ],
+      STANDALONEMONTHS: const [
+    'ינואר',
+    'פברואר',
+    'מרץ',
+    'אפריל',
+    'מאי',
+    'יוני',
+    'יולי',
+    'אוגוסט',
+    'ספטמבר',
+    'אוקטובר',
+    'נובמבר',
+    'דצמבר'
+  ],
+      SHORTMONTHS: const [
+    'ינו׳',
+    'פבר׳',
+    'מרץ',
+    'אפר׳',
+    'מאי',
+    'יוני',
+    'יולי',
+    'אוג׳',
+    'ספט׳',
+    'אוק׳',
+    'נוב׳',
+    'דצמ׳'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ינו׳',
+    'פבר׳',
+    'מרץ',
+    'אפר׳',
+    'מאי',
+    'יוני',
+    'יולי',
+    'אוג׳',
+    'ספט׳',
+    'אוק׳',
+    'נוב׳',
+    'דצמ׳'
+  ],
+      WEEKDAYS: const [
+    'יום ראשון',
+    'יום שני',
+    'יום שלישי',
+    'יום רביעי',
+    'יום חמישי',
+    'יום שישי',
+    'יום שבת'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'יום ראשון',
+    'יום שני',
+    'יום שלישי',
+    'יום רביעי',
+    'יום חמישי',
+    'יום שישי',
+    'יום שבת'
+  ],
+      SHORTWEEKDAYS: const [
+    'יום א׳',
+    'יום ב׳',
+    'יום ג׳',
+    'יום ד׳',
+    'יום ה׳',
+    'יום ו׳',
+    'שבת'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'יום א׳',
+    'יום ב׳',
+    'יום ג׳',
+    'יום ד׳',
+    'יום ה׳',
+    'יום ו׳',
+    'שבת'
+  ],
+      NARROWWEEKDAYS: const ['א׳', 'ב׳', 'ג׳', 'ד׳', 'ה׳', 'ו׳', 'ש׳'],
+      STANDALONENARROWWEEKDAYS: const [
+    'א׳',
+    'ב׳',
+    'ג׳',
+    'ד׳',
+    'ה׳',
+    'ו׳',
+    'ש׳'
+  ],
+      SHORTQUARTERS: const ['רבעון 1', 'רבעון 2', 'רבעון 3', 'רבעון 4'],
+      QUARTERS: const ['רבעון 1', 'רבעון 2', 'רבעון 3', 'רבעון 4'],
+      AMPMS: const ['לפנה״צ', 'אחה״צ'],
+      DATEFORMATS: const [
+    'EEEE, d בMMMM y',
+    'd בMMMM y',
+    'd בMMM y',
+    'dd/MM/yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const [
+    '{1} בשעה {0}',
+    '{1} בשעה {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [4, 5],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale ja.
+   */
+  "ja": new DateSymbols(
+      NAME: "ja",
+      ERAS: const ['紀元前', '西暦'],
+      ERANAMES: const ['紀元前', '西暦'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      STANDALONEMONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      SHORTMONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      STANDALONESHORTMONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      WEEKDAYS: const ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'],
+      STANDALONEWEEKDAYS: const [
+    '日曜日',
+    '月曜日',
+    '火曜日',
+    '水曜日',
+    '木曜日',
+    '金曜日',
+    '土曜日'
+  ],
+      SHORTWEEKDAYS: const ['日', '月', '火', '水', '木', '金', '土'],
+      STANDALONESHORTWEEKDAYS: const ['日', '月', '火', '水', '木', '金', '土'],
+      NARROWWEEKDAYS: const ['日', '月', '火', '水', '木', '金', '土'],
+      STANDALONENARROWWEEKDAYS: const ['日', '月', '火', '水', '木', '金', '土'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const ['第1四半期', '第2四半期', '第3四半期', '第4四半期'],
+      AMPMS: const ['午前', '午後'],
+      DATEFORMATS: const ['y年M月d日EEEE', 'y年M月d日', 'y/MM/dd', 'y/MM/dd'],
+      TIMEFORMATS: const ['H時mm分ss秒 zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale ka.
+   */
+  "ka": new DateSymbols(
+      NAME: "ka",
+      ERAS: const ['ძვ. წ.', 'ახ. წ.'],
+      ERANAMES: const ['ძველი წელთაღრიცხვით', 'ახალი წელთაღრიცხვით'],
+      NARROWMONTHS: const [
+    'ი',
+    'თ',
+    'მ',
+    'ა',
+    'მ',
+    'ი',
+    'ი',
+    'ა',
+    'ს',
+    'ო',
+    'ნ',
+    'დ'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ი',
+    'თ',
+    'მ',
+    'ა',
+    'მ',
+    'ი',
+    'ი',
+    'ა',
+    'ს',
+    'ო',
+    'ნ',
+    'დ'
+  ],
+      MONTHS: const [
+    'იანვარი',
+    'თებერვალი',
+    'მარტი',
+    'აპრილი',
+    'მაისი',
+    'ივნისი',
+    'ივლისი',
+    'აგვისტო',
+    'სექტემბერი',
+    'ოქტომბერი',
+    'ნოემბერი',
+    'დეკემბერი'
+  ],
+      STANDALONEMONTHS: const [
+    'იანვარი',
+    'თებერვალი',
+    'მარტი',
+    'აპრილი',
+    'მაისი',
+    'ივნისი',
+    'ივლისი',
+    'აგვისტო',
+    'სექტემბერი',
+    'ოქტომბერი',
+    'ნოემბერი',
+    'დეკემბერი'
+  ],
+      SHORTMONTHS: const [
+    'იან',
+    'თებ',
+    'მარ',
+    'აპრ',
+    'მაი',
+    'ივნ',
+    'ივლ',
+    'აგვ',
+    'სექ',
+    'ოქტ',
+    'ნოე',
+    'დეკ'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'იან',
+    'თებ',
+    'მარ',
+    'აპრ',
+    'მაი',
+    'ივნ',
+    'ივლ',
+    'აგვ',
+    'სექ',
+    'ოქტ',
+    'ნოე',
+    'დეკ'
+  ],
+      WEEKDAYS: const [
+    'კვირა',
+    'ორშაბათი',
+    'სამშაბათი',
+    'ოთხშაბათი',
+    'ხუთშაბათი',
+    'პარასკევი',
+    'შაბათი'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'კვირა',
+    'ორშაბათი',
+    'სამშაბათი',
+    'ოთხშაბათი',
+    'ხუთშაბათი',
+    'პარასკევი',
+    'შაბათი'
+  ],
+      SHORTWEEKDAYS: const ['კვი', 'ორშ', 'სამ', 'ოთხ', 'ხუთ', 'პარ', 'შაბ'],
+      STANDALONESHORTWEEKDAYS: const [
+    'კვი',
+    'ორშ',
+    'სამ',
+    'ოთხ',
+    'ხუთ',
+    'პარ',
+    'შაბ'
+  ],
+      NARROWWEEKDAYS: const ['კ', 'ო', 'ს', 'ო', 'ხ', 'პ', 'შ'],
+      STANDALONENARROWWEEKDAYS: const ['კ', 'ო', 'ს', 'ო', 'ხ', 'პ', 'შ'],
+      SHORTQUARTERS: const ['I კვ.', 'II კვ.', 'III კვ.', 'IV კვ.'],
+      QUARTERS: const [
+    'I კვარტალი',
+    'II კვარტალი',
+    'III კვარტალი',
+    'IV კვარტალი'
+  ],
+      AMPMS: const ['დილის', 'საღამოს'],
+      DATEFORMATS: const [
+    'EEEE, dd MMMM, y',
+    'd MMMM, y',
+    'd MMM, y',
+    'dd.MM.yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1}, {0}', '{1} {0}', '{1}, {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale kk.
+   */
+  "kk": new DateSymbols(
+      NAME: "kk",
+      ERAS: const ['б.з.д.', 'б.з.'],
+      ERANAMES: const ['б.з.д.', 'б.з.'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    'қаңтар',
+    'ақпан',
+    'наурыз',
+    'сәуір',
+    'мамыр',
+    'маусым',
+    'шілде',
+    'тамыз',
+    'қыркүйек',
+    'қазан',
+    'қараша',
+    'желтоқсан'
+  ],
+      STANDALONEMONTHS: const [
+    'қаңтар',
+    'ақпан',
+    'наурыз',
+    'сәуір',
+    'мамыр',
+    'маусым',
+    'шілде',
+    'тамыз',
+    'қыркүйек',
+    'қазан',
+    'қараша',
+    'желтоқсан'
+  ],
+      SHORTMONTHS: const [
+    'қаң.',
+    'ақп.',
+    'нау.',
+    'сәу.',
+    'мам.',
+    'мау.',
+    'шіл.',
+    'там.',
+    'қыр.',
+    'қаз.',
+    'қар.',
+    'желт.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'қаң.',
+    'ақп.',
+    'нау.',
+    'сәу.',
+    'мам.',
+    'мау.',
+    'шіл.',
+    'там.',
+    'қыр.',
+    'қаз.',
+    'қар.',
+    'желт.'
+  ],
+      WEEKDAYS: const [
+    'жексенбі',
+    'дүйсенбі',
+    'сейсенбі',
+    'сәрсенбі',
+    'бейсенбі',
+    'жұма',
+    'сенбі'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'жексенбі',
+    'дүйсенбі',
+    'сейсенбі',
+    'сәрсенбі',
+    'бейсенбі',
+    'жұма',
+    'сенбі'
+  ],
+      SHORTWEEKDAYS: const ['жс.', 'дс.', 'сс.', 'ср.', 'бс.', 'жм.', 'сб.'],
+      STANDALONESHORTWEEKDAYS: const [
+    'жс.',
+    'дс.',
+    'сс.',
+    'ср.',
+    'бс.',
+    'жм.',
+    'сб.'
+  ],
+      NARROWWEEKDAYS: const ['Ж', 'Д', 'С', 'С', 'Б', 'Ж', 'С'],
+      STANDALONENARROWWEEKDAYS: const ['Ж', 'Д', 'С', 'С', 'Б', 'Ж', 'С'],
+      SHORTQUARTERS: const ['1-тоқсан', '2-тоқсан', '3-тоқсан', '4-тоқсан'],
+      QUARTERS: const [
+    '1-інші тоқсан',
+    '2-інші тоқсан',
+    '3-інші тоқсан',
+    '4-інші тоқсан'
+  ],
+      AMPMS: const ['түске дейін', 'түстен кейін'],
+      DATEFORMATS: const [
+    'EEEE, d MMMM y \'ж\'.',
+    'd MMMM y \'ж\'.',
+    'dd.MM.y',
+    'dd/MM/yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale km.
+   */
+  "km": new DateSymbols(
+      NAME: "km",
+      ERAS: const ['មុន គ.ស.', 'គ.ស.'],
+      ERANAMES: const ['មុន​គ្រិស្តសករាជ', 'គ្រិស្តសករាជ'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    'មករា',
+    'កុម្ភៈ',
+    'មីនា',
+    'មេសា',
+    'ឧសភា',
+    'មិថុនា',
+    'កក្កដា',
+    'សីហា',
+    'កញ្ញា',
+    'តុលា',
+    'វិច្ឆិកា',
+    'ធ្នូ'
+  ],
+      STANDALONEMONTHS: const [
+    'មករា',
+    'កុម្ភៈ',
+    'មីនា',
+    'មេសា',
+    'ឧសភា',
+    'មិថុនា',
+    'កក្កដា',
+    'សីហា',
+    'កញ្ញា',
+    'តុលា',
+    'វិច្ឆិកា',
+    'ធ្នូ'
+  ],
+      SHORTMONTHS: const [
+    'មករា',
+    'កុម្ភៈ',
+    'មីនា',
+    'មេសា',
+    'ឧសភា',
+    'មិថុនា',
+    'កក្កដា',
+    'សីហា',
+    'កញ្ញា',
+    'តុលា',
+    'វិច្ឆិកា',
+    'ធ្នូ'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'មករា',
+    'កុម្ភៈ',
+    'មីនា',
+    'មេសា',
+    'ឧសភា',
+    'មិថុនា',
+    'កក្កដា',
+    'សីហា',
+    'កញ្ញា',
+    'តុលា',
+    'វិច្ឆិកា',
+    'ធ្នូ'
+  ],
+      WEEKDAYS: const [
+    'អាទិត្យ',
+    'ចន្ទ',
+    'អង្គារ',
+    'ពុធ',
+    'ព្រហស្បតិ៍',
+    'សុក្រ',
+    'សៅរ៍'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'អាទិត្យ',
+    'ចន្ទ',
+    'អង្គារ',
+    'ពុធ',
+    'ព្រហស្បតិ៍',
+    'សុក្រ',
+    'សៅរ៍'
+  ],
+      SHORTWEEKDAYS: const [
+    'អាទិត្យ',
+    'ចន្ទ',
+    'អង្គារ',
+    'ពុធ',
+    'ព្រហស្បតិ៍',
+    'សុក្រ',
+    'សៅរ៍'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'អាទិត្យ',
+    'ចន្ទ',
+    'អង្គារ',
+    'ពុធ',
+    'ព្រហស្បតិ៍',
+    'សុក្រ',
+    'សៅរ៍'
+  ],
+      NARROWWEEKDAYS: const ['1', '2', '3', '4', '5', '6', '7'],
+      STANDALONENARROWWEEKDAYS: const ['1', '2', '3', '4', '5', '6', '7'],
+      SHORTQUARTERS: const ['ត្រីមាស ១', 'ត្រីមាស ២', 'ត្រីមាស ៣', 'ត្រីមាស ៤'],
+      QUARTERS: const [
+    'ត្រីមាសទី ១',
+    'ត្រីមាសទី ២',
+    'ត្រីមាសទី ៣',
+    'ត្រីមាសទី ៤'
+  ],
+      AMPMS: const ['ព្រឹក', 'ល្ងាច'],
+      DATEFORMATS: const ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale kn.
+   */
+  "kn": new DateSymbols(
+      NAME: "kn",
+      ERAS: const ['ಕ್ರಿ.ಪೂ', 'ಜಾಹೀ'],
+      ERANAMES: const ['ಈಸಪೂವ೯.', 'ಕ್ರಿಸ್ತ ಶಕ'],
+      NARROWMONTHS: const [
+    'ಜ',
+    'ಫೆ',
+    'ಮಾ',
+    'ಏ',
+    'ಮೇ',
+    'ಜೂ',
+    'ಜು',
+    'ಆ',
+    'ಸೆ',
+    'ಅ',
+    'ನ',
+    'ಡಿ'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ಜ',
+    'ಫೆ',
+    'ಮಾ',
+    'ಏ',
+    'ಮೇ',
+    'ಜೂ',
+    'ಜು',
+    'ಆ',
+    'ಸೆ',
+    'ಅ',
+    'ನ',
+    'ಡಿ'
+  ],
+      MONTHS: const [
+    'ಜನವರಿ',
+    'ಫೆಬ್ರವರಿ',
+    'ಮಾರ್ಚ್',
+    'ಏಪ್ರಿಲ್',
+    'ಮೇ',
+    'ಜೂನ್',
+    'ಜುಲೈ',
+    'ಆಗಸ್ಟ್',
+    'ಸಪ್ಟೆಂಬರ್',
+    'ಅಕ್ಟೋಬರ್',
+    'ನವೆಂಬರ್',
+    'ಡಿಸೆಂಬರ್'
+  ],
+      STANDALONEMONTHS: const [
+    'ಜನವರಿ',
+    'ಫೆಬ್ರವರಿ',
+    'ಮಾರ್ಚ್',
+    'ಏಪ್ರಿಲ್',
+    'ಮೇ',
+    'ಜೂನ್',
+    'ಜುಲೈ',
+    'ಆಗಸ್ಟ್',
+    'ಸಪ್ಟೆಂಬರ್',
+    'ಅಕ್ಟೋಬರ್',
+    'ನವೆಂಬರ್',
+    'ಡಿಸೆಂಬರ್'
+  ],
+      SHORTMONTHS: const [
+    'ಜನ.',
+    'ಫೆಬ್ರು.',
+    'ಮಾ',
+    'ಏಪ್ರಿ.',
+    'ಮೇ',
+    'ಜೂ',
+    'ಜು.',
+    'ಆಗ.',
+    'ಸೆಪ್ಟೆಂ.',
+    'ಅಕ್ಟೋ.',
+    'ನವೆಂ.',
+    'ಡಿಸೆಂ.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ಜನ.',
+    'ಫೆಬ್ರು.',
+    'ಮಾ',
+    'ಏಪ್ರಿ.',
+    'ಮೇ',
+    'ಜೂ',
+    'ಜು.',
+    'ಆಗ.',
+    'ಸೆಪ್ಟೆಂ.',
+    'ಅಕ್ಟೋ.',
+    'ನವೆಂ.',
+    'ಡಿಸೆಂ.'
+  ],
+      WEEKDAYS: const [
+    'ರವಿವಾರ',
+    'ಸೋಮವಾರ',
+    'ಮಂಗಳವಾರ',
+    'ಬುಧವಾರ',
+    'ಗುರುವಾರ',
+    'ಶುಕ್ರವಾರ',
+    'ಶನಿವಾರ'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'ರವಿವಾರ',
+    'ಸೋಮವಾರ',
+    'ಮಂಗಳವಾರ',
+    'ಬುಧವಾರ',
+    'ಗುರುವಾರ',
+    'ಶುಕ್ರವಾರ',
+    'ಶನಿವಾರ'
+  ],
+      SHORTWEEKDAYS: const ['ರ.', 'ಸೋ.', 'ಮಂ.', 'ಬು.', 'ಗು.', 'ಶು.', 'ಶನಿ.'],
+      STANDALONESHORTWEEKDAYS: const [
+    'ರವಿ',
+    'ಸೋಮ',
+    'ಮಂಗಳ',
+    'ಬುಧ',
+    'ಗುರು',
+    'ಶುಕ್ರ',
+    'ಶನಿ'
+  ],
+      NARROWWEEKDAYS: const ['ರ', 'ಸೋ', 'ಮಂ', 'ಬು', 'ಗು', 'ಶು', 'ಶ'],
+      STANDALONENARROWWEEKDAYS: const ['ರ', 'ಸೋ', 'ಮಂ', 'ಬು', 'ಗು', 'ಶು', 'ಶ'],
+      SHORTQUARTERS: const ['ತ್ರೈ 1', 'ತ್ರೈ 2', 'ತ್ರೈ 3', 'ತ್ರೈ 4'],
+      QUARTERS: const [
+    '1 ನೇ ತ್ರೈಮಾಸಿಕ',
+    '2ನೇ ತ್ರೈಮಾಸಿಕ',
+    '3 ನೇ ತ್ರೈಮಾಸಿಕ',
+    '4 ನೇ ತ್ರೈಮಾಸಿಕ'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['d MMMM y, EEEE', 'd MMMM y', 'd MMM y', 'd-M-yy'],
+      TIMEFORMATS: const [
+    'hh:mm:ss a zzzz',
+    'hh:mm:ss a z',
+    'hh:mm:ss a',
+    'hh:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [6, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale ko.
+   */
+  "ko": new DateSymbols(
+      NAME: "ko",
+      ERAS: const ['기원전', '서기'],
+      ERANAMES: const ['서력기원전', '서력기원'],
+      NARROWMONTHS: const [
+    '1월',
+    '2월',
+    '3월',
+    '4월',
+    '5월',
+    '6월',
+    '7월',
+    '8월',
+    '9월',
+    '10월',
+    '11월',
+    '12월'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1월',
+    '2월',
+    '3월',
+    '4월',
+    '5월',
+    '6월',
+    '7월',
+    '8월',
+    '9월',
+    '10월',
+    '11월',
+    '12월'
+  ],
+      MONTHS: const [
+    '1월',
+    '2월',
+    '3월',
+    '4월',
+    '5월',
+    '6월',
+    '7월',
+    '8월',
+    '9월',
+    '10월',
+    '11월',
+    '12월'
+  ],
+      STANDALONEMONTHS: const [
+    '1월',
+    '2월',
+    '3월',
+    '4월',
+    '5월',
+    '6월',
+    '7월',
+    '8월',
+    '9월',
+    '10월',
+    '11월',
+    '12월'
+  ],
+      SHORTMONTHS: const [
+    '1월',
+    '2월',
+    '3월',
+    '4월',
+    '5월',
+    '6월',
+    '7월',
+    '8월',
+    '9월',
+    '10월',
+    '11월',
+    '12월'
+  ],
+      STANDALONESHORTMONTHS: const [
+    '1월',
+    '2월',
+    '3월',
+    '4월',
+    '5월',
+    '6월',
+    '7월',
+    '8월',
+    '9월',
+    '10월',
+    '11월',
+    '12월'
+  ],
+      WEEKDAYS: const ['일요일', '월요일', '화요일', '수요일', '목요일', '금요일', '토요일'],
+      STANDALONEWEEKDAYS: const [
+    '일요일',
+    '월요일',
+    '화요일',
+    '수요일',
+    '목요일',
+    '금요일',
+    '토요일'
+  ],
+      SHORTWEEKDAYS: const ['일', '월', '화', '수', '목', '금', '토'],
+      STANDALONESHORTWEEKDAYS: const ['일', '월', '화', '수', '목', '금', '토'],
+      NARROWWEEKDAYS: const ['일', '월', '화', '수', '목', '금', '토'],
+      STANDALONENARROWWEEKDAYS: const ['일', '월', '화', '수', '목', '금', '토'],
+      SHORTQUARTERS: const ['1분기', '2분기', '3분기', '4분기'],
+      QUARTERS: const ['제 1/4분기', '제 2/4분기', '제 3/4분기', '제 4/4분기'],
+      AMPMS: const ['오전', '오후'],
+      DATEFORMATS: const ['y년 M월 d일 EEEE', 'y년 M월 d일', 'y. M. d.', 'yy. M. d.'],
+      TIMEFORMATS: const [
+    'a h시 m분 s초 zzzz',
+    'a h시 m분 s초 z',
+    'a h:mm:ss',
+    'a h:mm'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale ky.
+   */
+  "ky": new DateSymbols(
+      NAME: "ky",
+      ERAS: const ['б.з. ч.', 'б.з.'],
+      ERANAMES: const ['б.з. чейин', 'б.з.'],
+      NARROWMONTHS: const [
+    'Я',
+    'Ф',
+    'М',
+    'А',
+    'М',
+    'И',
+    'И',
+    'А',
+    'С',
+    'О',
+    'Н',
+    'Д'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'Я',
+    'Ф',
+    'М',
+    'А',
+    'М',
+    'И',
+    'И',
+    'А',
+    'С',
+    'О',
+    'Н',
+    'Д'
+  ],
+      MONTHS: const [
+    'январь',
+    'февраль',
+    'март',
+    'апрель',
+    'май',
+    'июнь',
+    'июль',
+    'август',
+    'сентябрь',
+    'октябрь',
+    'ноябрь',
+    'декабрь'
+  ],
+      STANDALONEMONTHS: const [
+    'январь',
+    'февраль',
+    'март',
+    'апрель',
+    'май',
+    'июнь',
+    'июль',
+    'август',
+    'сентябрь',
+    'октябрь',
+    'ноябрь',
+    'декабрь'
+  ],
+      SHORTMONTHS: const [
+    'янв.',
+    'фев.',
+    'мар.',
+    'апр.',
+    'май',
+    'июн.',
+    'июл.',
+    'авг.',
+    'сен.',
+    'окт.',
+    'ноя.',
+    'дек.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'янв.',
+    'фев.',
+    'мар.',
+    'апр.',
+    'май',
+    'июн.',
+    'июл.',
+    'авг.',
+    'сен.',
+    'окт.',
+    'ноя.',
+    'дек.'
+  ],
+      WEEKDAYS: const ['Жек', 'Дүй', 'Шей', 'Шар', 'Бей', 'Жум', 'Ишм'],
+      STANDALONEWEEKDAYS: const [
+    'Жекшемби',
+    'Дүйшөмбү',
+    'Шейшемби',
+    'Шаршемби',
+    'Бейшемби',
+    'Жума',
+    'Ишемби'
+  ],
+      SHORTWEEKDAYS: const ['Жк', 'Дш', 'Ше', 'Ша', 'Бш', 'Жм', 'Иш'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Жек',
+    'Дүй',
+    'Шей',
+    'Шар',
+    'Бей',
+    'Жум',
+    'Ишм'
+  ],
+      NARROWWEEKDAYS: const ['Ж', 'Д', 'Ш', 'Ш', 'Б', 'Ж', 'И'],
+      STANDALONENARROWWEEKDAYS: const ['Ж', 'Д', 'Ш', 'Ш', 'Б', 'Ж', 'И'],
+      SHORTQUARTERS: const ['1-чей.', '2-чей.', '3-чей.', '4-чей.'],
+      QUARTERS: const ['1-чейрек', '2-чейрек', '3-чейрек', '4-чейрек'],
+      AMPMS: const ['түшкө чейинки', 'түштөн кийинки'],
+      DATEFORMATS: const [
+    'EEEE, d-MMMM, y-\'ж\'.',
+    'd-MMMM, y-\'ж\'.',
+    'dd.MM.y',
+    'dd.MM.yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale ln.
+   */
+  "ln": new DateSymbols(
+      NAME: "ln",
+      ERAS: const ['libóso ya', 'nsima ya Y'],
+      ERANAMES: const ['Yambo ya Yézu Krís', 'Nsima ya Yézu Krís'],
+      NARROWMONTHS: const [
+    'y',
+    'f',
+    'm',
+    'a',
+    'm',
+    'y',
+    'y',
+    'a',
+    's',
+    'ɔ',
+    'n',
+    'd'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'y',
+    'f',
+    'm',
+    'a',
+    'm',
+    'y',
+    'y',
+    'a',
+    's',
+    'ɔ',
+    'n',
+    'd'
+  ],
+      MONTHS: const [
+    'sánzá ya yambo',
+    'sánzá ya míbalé',
+    'sánzá ya mísáto',
+    'sánzá ya mínei',
+    'sánzá ya mítáno',
+    'sánzá ya motóbá',
+    'sánzá ya nsambo',
+    'sánzá ya mwambe',
+    'sánzá ya libwa',
+    'sánzá ya zómi',
+    'sánzá ya zómi na mɔ̌kɔ́',
+    'sánzá ya zómi na míbalé'
+  ],
+      STANDALONEMONTHS: const [
+    'sánzá ya yambo',
+    'sánzá ya míbalé',
+    'sánzá ya mísáto',
+    'sánzá ya mínei',
+    'sánzá ya mítáno',
+    'sánzá ya motóbá',
+    'sánzá ya nsambo',
+    'sánzá ya mwambe',
+    'sánzá ya libwa',
+    'sánzá ya zómi',
+    'sánzá ya zómi na mɔ̌kɔ́',
+    'sánzá ya zómi na míbalé'
+  ],
+      SHORTMONTHS: const [
+    'yan',
+    'fbl',
+    'msi',
+    'apl',
+    'mai',
+    'yun',
+    'yul',
+    'agt',
+    'stb',
+    'ɔtb',
+    'nvb',
+    'dsb'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'yan',
+    'fbl',
+    'msi',
+    'apl',
+    'mai',
+    'yun',
+    'yul',
+    'agt',
+    'stb',
+    'ɔtb',
+    'nvb',
+    'dsb'
+  ],
+      WEEKDAYS: const [
+    'eyenga',
+    'mokɔlɔ mwa yambo',
+    'mokɔlɔ mwa míbalé',
+    'mokɔlɔ mwa mísáto',
+    'mokɔlɔ ya mínéi',
+    'mokɔlɔ ya mítáno',
+    'mpɔ́sɔ'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'eyenga',
+    'mokɔlɔ mwa yambo',
+    'mokɔlɔ mwa míbalé',
+    'mokɔlɔ mwa mísáto',
+    'mokɔlɔ ya mínéi',
+    'mokɔlɔ ya mítáno',
+    'mpɔ́sɔ'
+  ],
+      SHORTWEEKDAYS: const ['eye', 'ybo', 'mbl', 'mst', 'min', 'mtn', 'mps'],
+      STANDALONESHORTWEEKDAYS: const [
+    'eye',
+    'ybo',
+    'mbl',
+    'mst',
+    'min',
+    'mtn',
+    'mps'
+  ],
+      NARROWWEEKDAYS: const ['e', 'y', 'm', 'm', 'm', 'm', 'p'],
+      STANDALONENARROWWEEKDAYS: const ['e', 'y', 'm', 'm', 'm', 'm', 'p'],
+      SHORTQUARTERS: const ['SM1', 'SM2', 'SM3', 'SM4'],
+      QUARTERS: const [
+    'sánzá mísáto ya yambo',
+    'sánzá mísáto ya míbalé',
+    'sánzá mísáto ya mísáto',
+    'sánzá mísáto ya mínei'
+  ],
+      AMPMS: const ['ntɔ́ngɔ́', 'mpókwa'],
+      DATEFORMATS: const ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/y'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale lo.
+   */
+  "lo": new DateSymbols(
+      NAME: "lo",
+      ERAS: const ['ກ່ອນ ຄ.ສ.', 'ຄ.ສ.'],
+      ERANAMES: const ['ກ່ອນ ຄ.ສ.', 'ຄ.ສ.'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    'ມັງກອນ',
+    'ກຸມພາ',
+    'ມີນາ',
+    'ເມສາ',
+    'ພຶດສະພາ',
+    'ມິຖຸນາ',
+    'ກໍລະກົດ',
+    'ສິງຫາ',
+    'ກັນຍາ',
+    'ຕຸລາ',
+    'ພະຈິກ',
+    'ທັນວາ'
+  ],
+      STANDALONEMONTHS: const [
+    'ມັງກອນ',
+    'ກຸມພາ',
+    'ມີນາ',
+    'ເມສາ',
+    'ພຶດສະພາ',
+    'ມິຖຸນາ',
+    'ກໍລະກົດ',
+    'ສິງຫາ',
+    'ກັນຍາ',
+    'ຕຸລາ',
+    'ພະຈິກ',
+    'ທັນວາ'
+  ],
+      SHORTMONTHS: const [
+    'ມ.ກ.',
+    'ກ.ພ.',
+    'ມ.ນ.',
+    'ມ.ສ.',
+    'ພ.ພ.',
+    'ມິ.ຖ.',
+    'ກ.ລ.',
+    'ສ.ຫ.',
+    'ກ.ຍ.',
+    'ຕ.ລ.',
+    'ພ.ຈ.',
+    'ທ.ວ.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ມ.ກ.',
+    'ກ.ພ.',
+    'ມ.ນ.',
+    'ມ.ສ.',
+    'ພ.ພ.',
+    'ມິ.ຖ.',
+    'ກ.ລ.',
+    'ສ.ຫ.',
+    'ກ.ຍ.',
+    'ຕ.ລ.',
+    'ພ.ຈ.',
+    'ທ.ວ.'
+  ],
+      WEEKDAYS: const [
+    'ວັນອາທິດ',
+    'ວັນຈັນ',
+    'ວັນອັງຄານ',
+    'ວັນພຸດ',
+    'ວັນພະຫັດ',
+    'ວັນສຸກ',
+    'ວັນເສົາ'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'ວັນອາທິດ',
+    'ວັນຈັນ',
+    'ວັນອັງຄານ',
+    'ວັນພຸດ',
+    'ວັນພະຫັດ',
+    'ວັນສຸກ',
+    'ວັນເສົາ'
+  ],
+      SHORTWEEKDAYS: const [
+    'ວັນອາທິດ',
+    'ວັນຈັນ',
+    'ວັນອັງຄານ',
+    'ວັນພຸດ',
+    'ວັນພະຫັດ',
+    'ວັນສຸກ',
+    'ວັນເສົາ'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'ວັນອາທິດ',
+    'ວັນຈັນ',
+    'ວັນອັງຄານ',
+    'ວັນພຸດ',
+    'ວັນພະຫັດ',
+    'ວັນສຸກ',
+    'ວັນເສົາ'
+  ],
+      NARROWWEEKDAYS: const ['1', '2', '3', '4', '5', '6', '7'],
+      STANDALONENARROWWEEKDAYS: const ['ທ', 'ຈ', 'ຄ', '​ພຸ', 'ພ', '​ສຸ', 'ສ'],
+      SHORTQUARTERS: const ['ຕມ1', 'ຕມ2', 'ຕມ3', 'ຕມ4'],
+      QUARTERS: const ['ໄຕຣມາດ 1', 'ໄຕຣມາດ 2', 'ໄຕຣມາດ 3', 'ໄຕຣມາດ 4'],
+      AMPMS: const ['ກ່ອນທ່ຽງ', 'ຫຼັງທ່ຽງ'],
+      DATEFORMATS: const ['EEEE ທີ d MMMM G y', 'd MMMM y', 'd MMM y', 'd/M/y'],
+      TIMEFORMATS: const [
+    'H ໂມງ m ນາທີ ss ວິນາທີ zzzz',
+    'H ໂມງ m ນາທີ ss ວິນາທີ z',
+    'H:mm:ss',
+    'H:mm'
+  ],
+      DATETIMEFORMATS: const ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale lt.
+   */
+  "lt": new DateSymbols(
+      NAME: "lt",
+      ERAS: const ['pr. Kr.', 'po Kr.'],
+      ERANAMES: const ['prieš Kristų', 'po Kristaus'],
+      NARROWMONTHS: const [
+    'S',
+    'V',
+    'K',
+    'B',
+    'G',
+    'B',
+    'L',
+    'R',
+    'R',
+    'S',
+    'L',
+    'G'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'S',
+    'V',
+    'K',
+    'B',
+    'G',
+    'B',
+    'L',
+    'R',
+    'R',
+    'S',
+    'L',
+    'G'
+  ],
+      MONTHS: const [
+    'sausis',
+    'vasaris',
+    'kovas',
+    'balandis',
+    'gegužė',
+    'birželis',
+    'liepa',
+    'rugpjūtis',
+    'rugsėjis',
+    'spalis',
+    'lapkritis',
+    'gruodis'
+  ],
+      STANDALONEMONTHS: const [
+    'sausis',
+    'vasaris',
+    'kovas',
+    'balandis',
+    'gegužė',
+    'birželis',
+    'liepa',
+    'rugpjūtis',
+    'rugsėjis',
+    'spalis',
+    'lapkritis',
+    'gruodis'
+  ],
+      SHORTMONTHS: const [
+    'saus.',
+    'vas.',
+    'kov.',
+    'bal.',
+    'geg.',
+    'birž.',
+    'liep.',
+    'rugp.',
+    'rugs.',
+    'spal.',
+    'lapkr.',
+    'gruod.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'saus.',
+    'vas.',
+    'kov.',
+    'bal.',
+    'geg.',
+    'birž.',
+    'liep.',
+    'rugp.',
+    'rugs.',
+    'spal.',
+    'lapkr.',
+    'gruod.'
+  ],
+      WEEKDAYS: const [
+    'sekmadienis',
+    'pirmadienis',
+    'antradienis',
+    'trečiadienis',
+    'ketvirtadienis',
+    'penktadienis',
+    'šeštadienis'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'sekmadienis',
+    'pirmadienis',
+    'antradienis',
+    'trečiadienis',
+    'ketvirtadienis',
+    'penktadienis',
+    'šeštadienis'
+  ],
+      SHORTWEEKDAYS: const ['sk', 'pr', 'an', 'tr', 'kt', 'pn', 'št'],
+      STANDALONESHORTWEEKDAYS: const ['sk', 'pr', 'an', 'tr', 'kt', 'pn', 'št'],
+      NARROWWEEKDAYS: const ['S', 'P', 'A', 'T', 'K', 'P', 'Š'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'P', 'A', 'T', 'K', 'P', 'Š'],
+      SHORTQUARTERS: const ['I k.', 'II k.', 'III k.', 'IV k.'],
+      QUARTERS: const [
+    'I ketvirtis',
+    'II ketvirtis',
+    'III ketvirtis',
+    'IV ketvirtis'
+  ],
+      AMPMS: const ['priešpiet', 'popiet'],
+      DATEFORMATS: const [
+    'y \'m\'. MMMM d \'d\'., EEEE',
+    'y \'m\'. MMMM d \'d\'.',
+    'y MMM d',
+    'y-MM-dd'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale lv.
+   */
+  "lv": new DateSymbols(
+      NAME: "lv",
+      ERAS: const ['p.m.ē.', 'm.ē.'],
+      ERANAMES: const ['pirms mūsu ēras', 'mūsu ērā'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'janvāris',
+    'februāris',
+    'marts',
+    'aprīlis',
+    'maijs',
+    'jūnijs',
+    'jūlijs',
+    'augusts',
+    'septembris',
+    'oktobris',
+    'novembris',
+    'decembris'
+  ],
+      STANDALONEMONTHS: const [
+    'Janvāris',
+    'Februāris',
+    'Marts',
+    'Aprīlis',
+    'Maijs',
+    'Jūnijs',
+    'Jūlijs',
+    'Augusts',
+    'Septembris',
+    'Oktobris',
+    'Novembris',
+    'Decembris'
+  ],
+      SHORTMONTHS: const [
+    'janv.',
+    'febr.',
+    'marts',
+    'apr.',
+    'maijs',
+    'jūn.',
+    'jūl.',
+    'aug.',
+    'sept.',
+    'okt.',
+    'nov.',
+    'dec.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Janv.',
+    'Febr.',
+    'Marts',
+    'Apr.',
+    'Maijs',
+    'Jūn.',
+    'Jūl.',
+    'Aug.',
+    'Sept.',
+    'Okt.',
+    'Nov.',
+    'Dec.'
+  ],
+      WEEKDAYS: const [
+    'svētdiena',
+    'pirmdiena',
+    'otrdiena',
+    'trešdiena',
+    'ceturtdiena',
+    'piektdiena',
+    'sestdiena'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Svētdiena',
+    'Pirmdiena',
+    'Otrdiena',
+    'Trešdiena',
+    'Ceturtdiena',
+    'Piektdiena',
+    'Sestdiena'
+  ],
+      SHORTWEEKDAYS: const ['Sv', 'Pr', 'Ot', 'Tr', 'Ce', 'Pk', 'Se'],
+      STANDALONESHORTWEEKDAYS: const ['Sv', 'Pr', 'Ot', 'Tr', 'Ce', 'Pk', 'Se'],
+      NARROWWEEKDAYS: const ['S', 'P', 'O', 'T', 'C', 'P', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'P', 'O', 'T', 'C', 'P', 'S'],
+      SHORTQUARTERS: const ['C1', 'C2', 'C3', 'C4'],
+      QUARTERS: const [
+    '1. ceturksnis',
+    '2. ceturksnis',
+    '3. ceturksnis',
+    '4. ceturksnis'
+  ],
+      AMPMS: const ['priekšpusdienā', 'pēcpusdienā'],
+      DATEFORMATS: const [
+    'EEEE, y. \'gada\' d. MMMM',
+    'y. \'gada\' d. MMMM',
+    'y. \'gada\' d. MMM',
+    'dd.MM.yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale mk.
+   */
+  "mk": new DateSymbols(
+      NAME: "mk",
+      ERAS: const ['пр.н.е.', 'н.е.'],
+      ERANAMES: const ['пр.н.е.', 'н.е.'],
+      NARROWMONTHS: const [
+    'ј',
+    'ф',
+    'м',
+    'а',
+    'м',
+    'ј',
+    'ј',
+    'а',
+    'с',
+    'о',
+    'н',
+    'д'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ј',
+    'ф',
+    'м',
+    'а',
+    'м',
+    'ј',
+    'ј',
+    'а',
+    'с',
+    'о',
+    'н',
+    'д'
+  ],
+      MONTHS: const [
+    'јануари',
+    'февруари',
+    'март',
+    'април',
+    'мај',
+    'јуни',
+    'јули',
+    'август',
+    'септември',
+    'октомври',
+    'ноември',
+    'декември'
+  ],
+      STANDALONEMONTHS: const [
+    'јануари',
+    'февруари',
+    'март',
+    'април',
+    'мај',
+    'јуни',
+    'јули',
+    'август',
+    'септември',
+    'октомври',
+    'ноември',
+    'декември'
+  ],
+      SHORTMONTHS: const [
+    'јан.',
+    'фев.',
+    'мар.',
+    'апр.',
+    'мај',
+    'јун.',
+    'јул.',
+    'авг.',
+    'септ.',
+    'окт.',
+    'ноем.',
+    'дек.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'јан.',
+    'фев.',
+    'мар.',
+    'апр.',
+    'мај',
+    'јун.',
+    'јул.',
+    'авг.',
+    'септ.',
+    'окт.',
+    'ноем.',
+    'дек.'
+  ],
+      WEEKDAYS: const [
+    'недела',
+    'понеделник',
+    'вторник',
+    'среда',
+    'четврток',
+    'петок',
+    'сабота'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'недела',
+    'понеделник',
+    'вторник',
+    'среда',
+    'четврток',
+    'петок',
+    'сабота'
+  ],
+      SHORTWEEKDAYS: const [
+    'нед.',
+    'пон.',
+    'вт.',
+    'сре.',
+    'чет.',
+    'пет.',
+    'саб.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'нед.',
+    'пон.',
+    'вт.',
+    'сре.',
+    'чет.',
+    'пет.',
+    'саб.'
+  ],
+      NARROWWEEKDAYS: const ['н', 'п', 'в', 'с', 'ч', 'п', 'с'],
+      STANDALONENARROWWEEKDAYS: const ['н', 'п', 'в', 'с', 'ч', 'п', 'с'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    'прво тромесечје',
+    'второ тромесечје',
+    'трето тромесечје',
+    'четврто тромесечје'
+  ],
+      AMPMS: const ['претпладне', 'попладне'],
+      DATEFORMATS: const [
+    'EEEE, dd MMMM y \'г\'.',
+    'dd MMMM y \'г\'.',
+    'dd.M.y',
+    'dd.M.yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale ml.
+   */
+  "ml": new DateSymbols(
+      NAME: "ml",
+      ERAS: const ['ക്രി.മൂ', 'എഡി'],
+      ERANAMES: const ['ക്രിസ്തുവിനു് മുമ്പ്‌', 'ക്രിസ്തുവിന് പിൻപ്'],
+      NARROWMONTHS: const [
+    'ജ',
+    'ഫെ',
+    'മാ',
+    'ഏ',
+    'മേ',
+    'ജൂ',
+    'ജൂ',
+    'ഓ',
+    'സെ',
+    'ഒ',
+    'ന',
+    'ഡി'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ജ',
+    'ഫെ',
+    'മാ',
+    'ഏ',
+    'മേ',
+    'ജൂ',
+    'ജൂ',
+    'ഓ',
+    'സെ',
+    'ഒ',
+    'ന',
+    'ഡി'
+  ],
+      MONTHS: const [
+    'ജനുവരി',
+    'ഫെബ്രുവരി',
+    'മാർച്ച്',
+    'ഏപ്രിൽ',
+    'മേയ്',
+    'ജൂൺ',
+    'ജൂലൈ',
+    'ആഗസ്റ്റ്',
+    'സെപ്റ്റംബർ',
+    'ഒക്‌ടോബർ',
+    'നവംബർ',
+    'ഡിസംബർ'
+  ],
+      STANDALONEMONTHS: const [
+    'ജനുവരി',
+    'ഫെബ്രുവരി',
+    'മാർച്ച്',
+    'ഏപ്രിൽ',
+    'മേയ്',
+    'ജൂൺ',
+    'ജൂലൈ',
+    'ആഗസ്റ്റ്',
+    'സെപ്റ്റംബർ',
+    'ഒക്‌ടോബർ',
+    'നവംബർ',
+    'ഡിസംബർ'
+  ],
+      SHORTMONTHS: const [
+    'ജനു',
+    'ഫെബ്രു',
+    'മാർ',
+    'ഏപ്രി',
+    'മേയ്',
+    'ജൂൺ',
+    'ജൂലൈ',
+    'ഓഗ',
+    'സെപ്റ്റം',
+    'ഒക്ടോ',
+    'നവം',
+    'ഡിസം'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ജനു',
+    'ഫെബ്രു',
+    'മാർ',
+    'ഏപ്രി',
+    'മേയ്',
+    'ജൂൺ',
+    'ജൂലൈ',
+    'ഓഗ',
+    'സെപ്റ്റം',
+    'ഒക്ടോ',
+    'നവം',
+    'ഡിസം'
+  ],
+      WEEKDAYS: const [
+    'ഞായറാഴ്‌ച',
+    'തിങ്കളാഴ്‌ച',
+    'ചൊവ്വാഴ്ച',
+    'ബുധനാഴ്‌ച',
+    'വ്യാഴാഴ്‌ച',
+    'വെള്ളിയാഴ്‌ച',
+    'ശനിയാഴ്‌ച'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'ഞായറാഴ്‌ച',
+    'തിങ്കളാഴ്‌ച',
+    'ചൊവ്വാഴ്‌ച',
+    'ബുധനാഴ്‌ച',
+    'വ്യാഴാഴ്‌ച',
+    'വെള്ളിയാഴ്‌ച',
+    'ശനിയാഴ്‌ച'
+  ],
+      SHORTWEEKDAYS: const [
+    'ഞായർ',
+    'തിങ്കൾ',
+    'ചൊവ്വ',
+    'ബുധൻ',
+    'വ്യാഴം',
+    'വെള്ളി',
+    'ശനി'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'ഞായർ',
+    'തിങ്കൾ',
+    'ചൊവ്വ',
+    'ബുധൻ',
+    'വ്യാഴം',
+    'വെള്ളി',
+    'ശനി'
+  ],
+      NARROWWEEKDAYS: const ['ഞാ', 'തി', 'ചൊ', 'ബു', 'വ്യാ', 'വെ', 'ശ'],
+      STANDALONENARROWWEEKDAYS: const [
+    'ഞാ',
+    'തി',
+    'ചൊ',
+    'ബു',
+    'വ്യാ',
+    'വെ',
+    'ശ'
+  ],
+      SHORTQUARTERS: const [
+    'ഒന്നാം പാദം',
+    'രണ്ടാം പാദം',
+    'മൂന്നാം പാദം',
+    'നാലാം പാദം'
+  ],
+      QUARTERS: const [
+    'ഒന്നാം പാദം',
+    'രണ്ടാം പാദം',
+    'മൂന്നാം പാദം',
+    'നാലാം പാദം'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const [
+    'y, MMMM d, EEEE',
+    'y, MMMM d',
+    'y, MMM d',
+    'dd/MM/yy'
+  ],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [6, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale mn.
+   */
+  "mn": new DateSymbols(
+      NAME: "mn",
+      ERAS: const ['МЭӨ', 'МЭ'],
+      ERANAMES: const ['манай эриний өмнөх', 'манай эриний'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    'Нэгдүгээр сар',
+    'Хоёрдугаар сар',
+    'Гуравдугаар сар',
+    'Дөрөвдүгээр сар',
+    'Тавдугаар сар',
+    'Зургадугаар сар',
+    'Долдугаар сар',
+    'Наймдугаар сар',
+    'Есдүгээр сар',
+    'Аравдугаар сар',
+    'Арван нэгдүгээр сар',
+    'Арван хоёрдугаар сар'
+  ],
+      STANDALONEMONTHS: const [
+    'Нэгдүгээр сар',
+    'Хоёрдугаар сар',
+    'Гуравдугаар сар',
+    'Дөрөвдүгээр сар',
+    'Тавдугаар сар',
+    'Зургадугаар сар',
+    'Долдугаар сар',
+    'Наймдугаар сар',
+    'Есдүгээр сар',
+    'Аравдугаар сар',
+    'Арван нэгдүгээр сар',
+    'Арван хоёрдугаар сар'
+  ],
+      SHORTMONTHS: const [
+    '1-р сар',
+    '2-р сар',
+    '3-р сар',
+    '4-р сар',
+    '5-р сар',
+    '6-р сар',
+    '7-р сар',
+    '8-р сар',
+    '9-р сар',
+    '10-р сар',
+    '11-р сар',
+    '12-р сар'
+  ],
+      STANDALONESHORTMONTHS: const [
+    '1-р сар',
+    '2-р сар',
+    '3-р сар',
+    '4-р сар',
+    '5-р сар',
+    '6-р сар',
+    '7-р сар',
+    '8-р сар',
+    '9-р сар',
+    '10-р сар',
+    '11-р сар',
+    '12-р сар'
+  ],
+      WEEKDAYS: const [
+    'ням',
+    'даваа',
+    'мягмар',
+    'лхагва',
+    'пүрэв',
+    'баасан',
+    'бямба'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'ням',
+    'даваа',
+    'мягмар',
+    'лхагва',
+    'пүрэв',
+    'баасан',
+    'бямба'
+  ],
+      SHORTWEEKDAYS: const ['Ня', 'Да', 'Мя', 'Лх', 'Пү', 'Ба', 'Бя'],
+      STANDALONESHORTWEEKDAYS: const ['Ня', 'Да', 'Мя', 'Лх', 'Пү', 'Ба', 'Бя'],
+      NARROWWEEKDAYS: const ['1', '2', '3', '4', '5', '6', '7'],
+      STANDALONENARROWWEEKDAYS: const ['1', '2', '3', '4', '5', '6', '7'],
+      SHORTQUARTERS: const ['У1', 'У2', 'У3', 'У4'],
+      QUARTERS: const ['1-р улирал', '2-р улирал', '3-р улирал', '4-р улирал'],
+      AMPMS: const ['ҮӨ', 'ҮХ'],
+      DATEFORMATS: const [
+    'EEEE, y \'оны\' MMMM \'сарын\' dd',
+    'y \'оны\' MMMM \'сарын\' d',
+    'y MMM d',
+    'y-MM-dd'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1}, {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale mr.
+   */
+  "mr": new DateSymbols(
+      NAME: "mr",
+      ERAS: const ['ईसापूर्व', 'सन'],
+      ERANAMES: const ['ईसवीसनपूर्व', 'ईसवीसन'],
+      NARROWMONTHS: const [
+    'जा',
+    'फे',
+    'मा',
+    'ए',
+    'मे',
+    'जू',
+    'जु',
+    'ऑ',
+    'स',
+    'ऑ',
+    'नो',
+    'डि'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'जा',
+    'फे',
+    'मा',
+    'ए',
+    'मे',
+    'जू',
+    'जु',
+    'ऑ',
+    'स',
+    'ऑ',
+    'नो',
+    'डि'
+  ],
+      MONTHS: const [
+    'जानेवारी',
+    'फेब्रुवारी',
+    'मार्च',
+    'एप्रिल',
+    'मे',
+    'जून',
+    'जुलै',
+    'ऑगस्ट',
+    'सप्टेंबर',
+    'ऑक्टोबर',
+    'नोव्हेंबर',
+    'डिसेंबर'
+  ],
+      STANDALONEMONTHS: const [
+    'जानेवारी',
+    'फेब्रुवारी',
+    'मार्च',
+    'एप्रिल',
+    'मे',
+    'जून',
+    'जुलै',
+    'ऑगस्ट',
+    'सप्टेंबर',
+    'ऑक्टोबर',
+    'नोव्हेंबर',
+    'डिसेंबर'
+  ],
+      SHORTMONTHS: const [
+    'जाने',
+    'फेब्रु',
+    'मार्च',
+    'एप्रि',
+    'मे',
+    'जून',
+    'जुलै',
+    'ऑग',
+    'सप्टें',
+    'ऑक्टो',
+    'नोव्हें',
+    'डिसें'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'जाने',
+    'फेब्रु',
+    'मार्च',
+    'एप्रि',
+    'मे',
+    'जून',
+    'जुलै',
+    'ऑग',
+    'सप्टें',
+    'ऑक्टो',
+    'नोव्हें',
+    'डिसें'
+  ],
+      WEEKDAYS: const [
+    'रविवार',
+    'सोमवार',
+    'मंगळवार',
+    'बुधवार',
+    'गुरुवार',
+    'शुक्रवार',
+    'शनिवार'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'रविवार',
+    'सोमवार',
+    'मंगळवार',
+    'बुधवार',
+    'गुरुवार',
+    'शुक्रवार',
+    'शनिवार'
+  ],
+      SHORTWEEKDAYS: const [
+    'रवि',
+    'सोम',
+    'मंगळ',
+    'बुध',
+    'गुरु',
+    'शुक्र',
+    'शनि'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'रवि',
+    'सोम',
+    'मंगळ',
+    'बुध',
+    'गुरु',
+    'शुक्र',
+    'शनि'
+  ],
+      NARROWWEEKDAYS: const ['र', 'सो', 'मं', 'बु', 'गु', 'शु', 'श'],
+      STANDALONENARROWWEEKDAYS: const ['र', 'सो', 'मं', 'बु', 'गु', 'शु', 'श'],
+      SHORTQUARTERS: const ['ति1', 'ति2', 'ति3', 'ति4'],
+      QUARTERS: const [
+    'प्रथम तिमाही',
+    'द्वितीय तिमाही',
+    'तृतीय तिमाही',
+    'चतुर्थ तिमाही'
+  ],
+      AMPMS: const ['[AM]', '[PM]'],
+      DATEFORMATS: const ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd/M/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const [
+    '{1} \'रोजी\' {0}',
+    '{1} \'रोजी\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [6, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale ms.
+   */
+  "ms": new DateSymbols(
+      NAME: "ms",
+      ERAS: const ['S.M.', 'TM'],
+      ERANAMES: const ['S.M.', 'TM'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'O',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'O',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Januari',
+    'Februari',
+    'Mac',
+    'April',
+    'Mei',
+    'Jun',
+    'Julai',
+    'Ogos',
+    'September',
+    'Oktober',
+    'November',
+    'Disember'
+  ],
+      STANDALONEMONTHS: const [
+    'Januari',
+    'Februari',
+    'Mac',
+    'April',
+    'Mei',
+    'Jun',
+    'Julai',
+    'Ogos',
+    'September',
+    'Oktober',
+    'November',
+    'Disember'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mac',
+    'Apr',
+    'Mei',
+    'Jun',
+    'Jul',
+    'Ogo',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Dis'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mac',
+    'Apr',
+    'Mei',
+    'Jun',
+    'Jul',
+    'Ogo',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Dis'
+  ],
+      WEEKDAYS: const [
+    'Ahad',
+    'Isnin',
+    'Selasa',
+    'Rabu',
+    'Khamis',
+    'Jumaat',
+    'Sabtu'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Ahad',
+    'Isnin',
+    'Selasa',
+    'Rabu',
+    'Khamis',
+    'Jumaat',
+    'Sabtu'
+  ],
+      SHORTWEEKDAYS: const ['Ahd', 'Isn', 'Sel', 'Rab', 'Kha', 'Jum', 'Sab'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Ahd',
+    'Isn',
+    'Sel',
+    'Rab',
+    'Kha',
+    'Jum',
+    'Sab'
+  ],
+      NARROWWEEKDAYS: const ['A', 'I', 'S', 'R', 'K', 'J', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['A', 'I', 'S', 'R', 'K', 'J', 'S'],
+      SHORTQUARTERS: const ['S1', 'S2', 'S3', 'S4'],
+      QUARTERS: const ['Suku pertama', 'Suku Ke-2', 'Suku Ke-3', 'Suku Ke-4'],
+      AMPMS: const ['PG', 'PTG'],
+      DATEFORMATS: const ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/MM/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale mt.
+   */
+  "mt": new DateSymbols(
+      NAME: "mt",
+      ERAS: const ['QK', 'WK'],
+      ERANAMES: const ['Qabel Kristu', 'Wara Kristu'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'Ġ',
+    'L',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'Ġ',
+    'L',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Jannar',
+    'Frar',
+    'Marzu',
+    'April',
+    'Mejju',
+    'Ġunju',
+    'Lulju',
+    'Awwissu',
+    'Settembru',
+    'Ottubru',
+    'Novembru',
+    'Diċembru'
+  ],
+      STANDALONEMONTHS: const [
+    'Jannar',
+    'Frar',
+    'Marzu',
+    'April',
+    'Mejju',
+    'Ġunju',
+    'Lulju',
+    'Awwissu',
+    'Settembru',
+    'Ottubru',
+    'Novembru',
+    'Diċembru'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Fra',
+    'Mar',
+    'Apr',
+    'Mej',
+    'Ġun',
+    'Lul',
+    'Aww',
+    'Set',
+    'Ott',
+    'Nov',
+    'Diċ'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Fra',
+    'Mar',
+    'Apr',
+    'Mej',
+    'Ġun',
+    'Lul',
+    'Aww',
+    'Set',
+    'Ott',
+    'Nov',
+    'Diċ'
+  ],
+      WEEKDAYS: const [
+    'Il-Ħadd',
+    'It-Tnejn',
+    'It-Tlieta',
+    'L-Erbgħa',
+    'Il-Ħamis',
+    'Il-Ġimgħa',
+    'Is-Sibt'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Il-Ħadd',
+    'It-Tnejn',
+    'It-Tlieta',
+    'L-Erbgħa',
+    'Il-Ħamis',
+    'Il-Ġimgħa',
+    'Is-Sibt'
+  ],
+      SHORTWEEKDAYS: const ['Ħad', 'Tne', 'Tli', 'Erb', 'Ħam', 'Ġim', 'Sib'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Ħad',
+    'Tne',
+    'Tli',
+    'Erb',
+    'Ħam',
+    'Ġim',
+    'Sib'
+  ],
+      NARROWWEEKDAYS: const ['Ħ', 'T', 'T', 'E', 'Ħ', 'Ġ', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['Ħ', 'T', 'T', 'E', 'Ħ', 'Ġ', 'S'],
+      SHORTQUARTERS: const ['K1', 'K2', 'K3', 'K4'],
+      QUARTERS: const ['K1', 'K2', 'K3', 'K4'],
+      AMPMS: const ['QN', 'WN'],
+      DATEFORMATS: const [
+    'EEEE, d \'ta\'’ MMMM y',
+    'd \'ta\'’ MMMM y',
+    'dd MMM y',
+    'dd/MM/y'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale my.
+   */
+  "my": new DateSymbols(
+      NAME: "my",
+      ERAS: const ['ဘီစီ', 'အေဒီ'],
+      ERANAMES: const ['ခရစ်တော် မပေါ်မီကာလ', 'ခရစ်တော် ပေါ်ထွန်းပြီးကာလ'],
+      NARROWMONTHS: const [
+    'ဇ',
+    'ဖ',
+    'မ',
+    'ဧ',
+    'မ',
+    'ဇ',
+    'ဇ',
+    'ဩ',
+    'စ',
+    'အ',
+    'န',
+    'ဒ'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ဇ',
+    'ဖ',
+    'မ',
+    'ဧ',
+    'မ',
+    'ဇ',
+    'ဇ',
+    'ဩ',
+    'စ',
+    'အ',
+    'န',
+    'ဒ'
+  ],
+      MONTHS: const [
+    'ဇန်နဝါရီ',
+    'ဖေဖော်ဝါရီ',
+    'မတ်',
+    'ဧပြီ',
+    'မေ',
+    'ဇွန်',
+    'ဇူလိုင်',
+    'ဩဂုတ်',
+    'စက်တင်ဘာ',
+    'အောက်တိုဘာ',
+    'နိုဝင်ဘာ',
+    'ဒီဇင်ဘာ'
+  ],
+      STANDALONEMONTHS: const [
+    'ဇန်နဝါရီ',
+    'ဖေဖော်ဝါရီ',
+    'မတ်',
+    'ဧပြီ',
+    'မေ',
+    'ဇွန်',
+    'ဇူလိုင်',
+    'ဩဂုတ်',
+    'စက်တင်ဘာ',
+    'အောက်တိုဘာ',
+    'နိုဝင်ဘာ',
+    'ဒီဇင်ဘာ'
+  ],
+      SHORTMONTHS: const [
+    'ဇန်နဝါရီ',
+    'ဖေဖော်ဝါရီ',
+    'မတ်',
+    'ဧပြီ',
+    'မေ',
+    'ဇွန်',
+    'ဇူလိုင်',
+    'ဩဂုတ်',
+    'စက်တင်ဘာ',
+    'အောက်တိုဘာ',
+    'နိုဝင်ဘာ',
+    'ဒီဇင်ဘာ'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ဇန်နဝါရီ',
+    'ဖေဖော်ဝါရီ',
+    'မတ်',
+    'ဧပြီ',
+    'မေ',
+    'ဇွန်',
+    'ဇူလိုင်',
+    'ဩဂုတ်',
+    'စက်တင်ဘာ',
+    'အောက်တိုဘာ',
+    'နိုဝင်ဘာ',
+    'ဒီဇင်ဘာ'
+  ],
+      WEEKDAYS: const [
+    'တနင်္ဂနွေ',
+    'တနင်္လာ',
+    'အင်္ဂါ',
+    'ဗုဒ္ဓဟူး',
+    'ကြာသပတေး',
+    'သောကြာ',
+    'စနေ'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'တနင်္ဂနွေ',
+    'တနင်္လာ',
+    'အင်္ဂါ',
+    'ဗုဒ္ဓဟူး',
+    'ကြာသပတေး',
+    'သောကြာ',
+    'စနေ'
+  ],
+      SHORTWEEKDAYS: const [
+    'တနင်္ဂနွေ',
+    'တနင်္လာ',
+    'အင်္ဂါ',
+    'ဗုဒ္ဓဟူး',
+    'ကြာသပတေး',
+    'သောကြာ',
+    'စနေ'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'တနင်္ဂနွေ',
+    'တနင်္လာ',
+    'အင်္ဂါ',
+    'ဗုဒ္ဓဟူး',
+    'ကြာသပတေး',
+    'သောကြာ',
+    'စနေ'
+  ],
+      NARROWWEEKDAYS: const ['တ', 'တ', 'အ', 'ဗ', 'က', 'သ', 'စ'],
+      STANDALONENARROWWEEKDAYS: const ['တ', 'တ', 'အ', 'ဗ', 'က', 'သ', 'စ'],
+      SHORTQUARTERS: const [
+    'ပထမ သုံးလပတ်',
+    'ဒုတိယ သုံးလပတ်',
+    'တတိယ သုံးလပတ်',
+    'စတုတ္ထ သုံးလပတ်'
+  ],
+      QUARTERS: const [
+    'ပထမ သုံးလပတ်',
+    'ဒုတိယ သုံးလပတ်',
+    'တတိယ သုံးလပတ်',
+    'စတုတ္ထ သုံးလပတ်'
+  ],
+      AMPMS: const ['နံနက်', 'ညနေ'],
+      DATEFORMATS: const ['EEEE, y MMMM dd', 'y MMMM d', 'y MMM d', 'yy/MM/dd'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1}မှာ {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale nb.
+   */
+  "nb": new DateSymbols(
+      NAME: "nb",
+      ERAS: const ['f.Kr.', 'e.Kr.'],
+      ERANAMES: const ['f.Kr.', 'e.Kr.'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'januar',
+    'februar',
+    'mars',
+    'april',
+    'mai',
+    'juni',
+    'juli',
+    'august',
+    'september',
+    'oktober',
+    'november',
+    'desember'
+  ],
+      STANDALONEMONTHS: const [
+    'januar',
+    'februar',
+    'mars',
+    'april',
+    'mai',
+    'juni',
+    'juli',
+    'august',
+    'september',
+    'oktober',
+    'november',
+    'desember'
+  ],
+      SHORTMONTHS: const [
+    'jan.',
+    'feb.',
+    'mar.',
+    'apr.',
+    'mai',
+    'jun.',
+    'jul.',
+    'aug.',
+    'sep.',
+    'okt.',
+    'nov.',
+    'des.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'jan',
+    'feb',
+    'mar',
+    'apr',
+    'mai',
+    'jun',
+    'jul',
+    'aug',
+    'sep',
+    'okt',
+    'nov',
+    'des'
+  ],
+      WEEKDAYS: const [
+    'søndag',
+    'mandag',
+    'tirsdag',
+    'onsdag',
+    'torsdag',
+    'fredag',
+    'lørdag'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'søndag',
+    'mandag',
+    'tirsdag',
+    'onsdag',
+    'torsdag',
+    'fredag',
+    'lørdag'
+  ],
+      SHORTWEEKDAYS: const [
+    'søn.',
+    'man.',
+    'tir.',
+    'ons.',
+    'tor.',
+    'fre.',
+    'lør.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'sø.',
+    'ma.',
+    'ti.',
+    'on.',
+    'to.',
+    'fr.',
+    'lø.'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
+      SHORTQUARTERS: const ['K1', 'K2', 'K3', 'K4'],
+      QUARTERS: const ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
+      AMPMS: const ['a.m.', 'p.m.'],
+      DATEFORMATS: const [
+    'EEEE d. MMMM y',
+    'd. MMMM y',
+    'd. MMM y',
+    'dd.MM.yy'
+  ],
+      TIMEFORMATS: const ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
+      DATETIMEFORMATS: const [
+    '{1} {0}',
+    '{1} \'kl.\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale ne.
+   */
+  "ne": new DateSymbols(
+      NAME: "ne",
+      ERAS: const ['ईसा पूर्व', 'सन्'],
+      ERANAMES: const ['ईसा पूर्व', 'सन्'],
+      NARROWMONTHS: const [
+    '१',
+    '२',
+    '३',
+    '४',
+    '५',
+    '६',
+    '७',
+    '८',
+    '९',
+    '१०',
+    '११',
+    '१२'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '१',
+    '२',
+    '३',
+    '४',
+    '५',
+    '६',
+    '७',
+    '८',
+    '९',
+    '१०',
+    '११',
+    '१२'
+  ],
+      MONTHS: const [
+    'जनवरी',
+    'फेब्रुअरी',
+    'मार्च',
+    'अप्रिल',
+    'मे',
+    'जुन',
+    'जुलाई',
+    'अगस्ट',
+    'सेप्टेम्बर',
+    'अक्टोबर',
+    'नोभेम्बर',
+    'डिसेम्बर'
+  ],
+      STANDALONEMONTHS: const [
+    'जनवरी',
+    'फेब्रुअरी',
+    'मार्च',
+    'अप्रिल',
+    'मे',
+    'जुन',
+    'जुलाई',
+    'अगस्ट',
+    'सेप्टेम्बर',
+    'अक्टोबर',
+    'नोभेम्बर',
+    'डिसेम्बर'
+  ],
+      SHORTMONTHS: const [
+    'जनवरी',
+    'फेब्रुअरी',
+    'मार्च',
+    'अप्रिल',
+    'मे',
+    'जुन',
+    'जुलाई',
+    'अगस्ट',
+    'सेप्टेम्बर',
+    'अक्टोबर',
+    'नोभेम्बर',
+    'डिसेम्बर'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'जनवरी',
+    'फेब्रुअरी',
+    'मार्च',
+    'अप्रिल',
+    'मे',
+    'जुन',
+    'जुलाई',
+    'अगस्ट',
+    'सेप्टेम्बर',
+    'अक्टोबर',
+    'नोभेम्बर',
+    'डिसेम्बर'
+  ],
+      WEEKDAYS: const [
+    'आइतबार',
+    'सोमबार',
+    'मङ्गलबार',
+    'बुधबार',
+    'बिहीबार',
+    'शुक्रबार',
+    'शनिबार'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'आइतबार',
+    'सोमबार',
+    'मङ्गलबार',
+    'बुधबार',
+    'बिहीबार',
+    'शुक्रबार',
+    'शनिबार'
+  ],
+      SHORTWEEKDAYS: const [
+    'आइत',
+    'सोम',
+    'मङ्गल',
+    'बुध',
+    'बिही',
+    'शुक्र',
+    'शनि'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'आइत',
+    'सोम',
+    'मङ्गल',
+    'बुध',
+    'बिही',
+    'शुक्र',
+    'शनि'
+  ],
+      NARROWWEEKDAYS: const ['आ', 'सो', 'म', 'बु', 'बि', 'शु', 'श'],
+      STANDALONENARROWWEEKDAYS: const ['आ', 'सो', 'म', 'बु', 'बि', 'शु', 'श'],
+      SHORTQUARTERS: const [
+    'पहिलो सत्र',
+    'दोस्रो सत्र',
+    'तेस्रो सत्र',
+    'चौथो सत्र'
+  ],
+      QUARTERS: const ['पहिलो सत्र', 'दोस्रो सत्र', 'तेस्रो सत्र', 'चौथो सत्र'],
+      AMPMS: const ['पूर्व मध्यान्ह', 'उत्तर मध्यान्ह'],
+      DATEFORMATS: const ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1}, {0}', '{1}, {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale nl.
+   */
+  "nl": new DateSymbols(
+      NAME: "nl",
+      ERAS: const ['v.Chr.', 'n.Chr.'],
+      ERANAMES: const ['Voor Christus', 'na Christus'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'januari',
+    'februari',
+    'maart',
+    'april',
+    'mei',
+    'juni',
+    'juli',
+    'augustus',
+    'september',
+    'oktober',
+    'november',
+    'december'
+  ],
+      STANDALONEMONTHS: const [
+    'januari',
+    'februari',
+    'maart',
+    'april',
+    'mei',
+    'juni',
+    'juli',
+    'augustus',
+    'september',
+    'oktober',
+    'november',
+    'december'
+  ],
+      SHORTMONTHS: const [
+    'jan.',
+    'feb.',
+    'mrt.',
+    'apr.',
+    'mei',
+    'jun.',
+    'jul.',
+    'aug.',
+    'sep.',
+    'okt.',
+    'nov.',
+    'dec.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'jan',
+    'feb',
+    'mrt',
+    'apr',
+    'mei',
+    'jun',
+    'jul',
+    'aug',
+    'sep',
+    'okt',
+    'nov',
+    'dec'
+  ],
+      WEEKDAYS: const [
+    'zondag',
+    'maandag',
+    'dinsdag',
+    'woensdag',
+    'donderdag',
+    'vrijdag',
+    'zaterdag'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'zondag',
+    'maandag',
+    'dinsdag',
+    'woensdag',
+    'donderdag',
+    'vrijdag',
+    'zaterdag'
+  ],
+      SHORTWEEKDAYS: const ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
+      STANDALONESHORTWEEKDAYS: const ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
+      NARROWWEEKDAYS: const ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
+      STANDALONENARROWWEEKDAYS: const ['Z', 'M', 'D', 'W', 'D', 'V', 'Z'],
+      SHORTQUARTERS: const ['K1', 'K2', 'K3', 'K4'],
+      QUARTERS: const [
+    '1e kwartaal',
+    '2e kwartaal',
+    '3e kwartaal',
+    '4e kwartaal'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd-MM-yy'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale no.
+   */
+  "no": new DateSymbols(
+      NAME: "no",
+      ERAS: const ['f.Kr.', 'e.Kr.'],
+      ERANAMES: const ['f.Kr.', 'e.Kr.'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'januar',
+    'februar',
+    'mars',
+    'april',
+    'mai',
+    'juni',
+    'juli',
+    'august',
+    'september',
+    'oktober',
+    'november',
+    'desember'
+  ],
+      STANDALONEMONTHS: const [
+    'januar',
+    'februar',
+    'mars',
+    'april',
+    'mai',
+    'juni',
+    'juli',
+    'august',
+    'september',
+    'oktober',
+    'november',
+    'desember'
+  ],
+      SHORTMONTHS: const [
+    'jan.',
+    'feb.',
+    'mar.',
+    'apr.',
+    'mai',
+    'jun.',
+    'jul.',
+    'aug.',
+    'sep.',
+    'okt.',
+    'nov.',
+    'des.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'jan',
+    'feb',
+    'mar',
+    'apr',
+    'mai',
+    'jun',
+    'jul',
+    'aug',
+    'sep',
+    'okt',
+    'nov',
+    'des'
+  ],
+      WEEKDAYS: const [
+    'søndag',
+    'mandag',
+    'tirsdag',
+    'onsdag',
+    'torsdag',
+    'fredag',
+    'lørdag'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'søndag',
+    'mandag',
+    'tirsdag',
+    'onsdag',
+    'torsdag',
+    'fredag',
+    'lørdag'
+  ],
+      SHORTWEEKDAYS: const [
+    'søn.',
+    'man.',
+    'tir.',
+    'ons.',
+    'tor.',
+    'fre.',
+    'lør.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'sø.',
+    'ma.',
+    'ti.',
+    'on.',
+    'to.',
+    'fr.',
+    'lø.'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
+      SHORTQUARTERS: const ['K1', 'K2', 'K3', 'K4'],
+      QUARTERS: const ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
+      AMPMS: const ['a.m.', 'p.m.'],
+      DATEFORMATS: const [
+    'EEEE d. MMMM y',
+    'd. MMMM y',
+    'd. MMM y',
+    'dd.MM.yy'
+  ],
+      TIMEFORMATS: const ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
+      DATETIMEFORMATS: const [
+    '{1} {0}',
+    '{1} \'kl.\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale no_NO.
+   */
+  /**
+   * Date/time formatting symbols for locale no_NO.
+   */
+  "no_NO": new DateSymbols(
+      NAME: "no_NO",
+      ERAS: const ['f.Kr.', 'e.Kr.'],
+      ERANAMES: const ['f.Kr.', 'e.Kr.'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'januar',
+    'februar',
+    'mars',
+    'april',
+    'mai',
+    'juni',
+    'juli',
+    'august',
+    'september',
+    'oktober',
+    'november',
+    'desember'
+  ],
+      STANDALONEMONTHS: const [
+    'januar',
+    'februar',
+    'mars',
+    'april',
+    'mai',
+    'juni',
+    'juli',
+    'august',
+    'september',
+    'oktober',
+    'november',
+    'desember'
+  ],
+      SHORTMONTHS: const [
+    'jan.',
+    'feb.',
+    'mar.',
+    'apr.',
+    'mai',
+    'jun.',
+    'jul.',
+    'aug.',
+    'sep.',
+    'okt.',
+    'nov.',
+    'des.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'jan',
+    'feb',
+    'mar',
+    'apr',
+    'mai',
+    'jun',
+    'jul',
+    'aug',
+    'sep',
+    'okt',
+    'nov',
+    'des'
+  ],
+      WEEKDAYS: const [
+    'søndag',
+    'mandag',
+    'tirsdag',
+    'onsdag',
+    'torsdag',
+    'fredag',
+    'lørdag'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'søndag',
+    'mandag',
+    'tirsdag',
+    'onsdag',
+    'torsdag',
+    'fredag',
+    'lørdag'
+  ],
+      SHORTWEEKDAYS: const [
+    'søn.',
+    'man.',
+    'tir.',
+    'ons.',
+    'tor.',
+    'fre.',
+    'lør.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'sø.',
+    'ma.',
+    'ti.',
+    'on.',
+    'to.',
+    'fr.',
+    'lø.'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
+      SHORTQUARTERS: const ['K1', 'K2', 'K3', 'K4'],
+      QUARTERS: const ['1. kvartal', '2. kvartal', '3. kvartal', '4. kvartal'],
+      AMPMS: const ['a.m.', 'p.m.'],
+      DATEFORMATS: const [
+    'EEEE d. MMMM y',
+    'd. MMMM y',
+    'd. MMM y',
+    'dd.MM.yy'
+  ],
+      TIMEFORMATS: const ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
+      DATETIMEFORMATS: const [
+    '{1} {0}',
+    '{1} \'kl.\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale or.
+   */
+  "or": new DateSymbols(
+      NAME: "or",
+      ERAS: const ['BCE', 'CE'],
+      ERANAMES: const ['BCE', 'CE'],
+      NARROWMONTHS: const [
+    'ଜା',
+    'ଫେ',
+    'ମା',
+    'ଅ',
+    'ମେ',
+    'ଜୁ',
+    'ଜୁ',
+    'ଅ',
+    'ସେ',
+    'ଅ',
+    'ନ',
+    'ଡି'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ଜା',
+    'ଫେ',
+    'ମା',
+    'ଅ',
+    'ମେ',
+    'ଜୁ',
+    'ଜୁ',
+    'ଅ',
+    'ସେ',
+    'ଅ',
+    'ନ',
+    'ଡି'
+  ],
+      MONTHS: const [
+    'ଜାନୁଆରୀ',
+    'ଫେବ୍ରୁୟାରୀ',
+    'ମାର୍ଚ୍ଚ',
+    'ଅପ୍ରେଲ',
+    'ମେ',
+    'ଜୁନ',
+    'ଜୁଲାଇ',
+    'ଅଗଷ୍ଟ',
+    'ସେପ୍ଟେମ୍ବର',
+    'ଅକ୍ଟୋବର',
+    'ନଭେମ୍ବର',
+    'ଡିସେମ୍ବର'
+  ],
+      STANDALONEMONTHS: const [
+    'ଜାନୁଆରୀ',
+    'ଫେବ୍ରୁୟାରୀ',
+    'ମାର୍ଚ୍ଚ',
+    'ଅପ୍ରେଲ',
+    'ମେ',
+    'ଜୁନ',
+    'ଜୁଲାଇ',
+    'ଅଗଷ୍ଟ',
+    'ସେପ୍ଟେମ୍ବର',
+    'ଅକ୍ଟୋବର',
+    'ନଭେମ୍ବର',
+    'ଡିସେମ୍ବର'
+  ],
+      SHORTMONTHS: const [
+    'ଜାନୁଆରୀ',
+    'ଫେବ୍ରୁୟାରୀ',
+    'ମାର୍ଚ୍ଚ',
+    'ଅପ୍ରେଲ',
+    'ମେ',
+    'ଜୁନ',
+    'ଜୁଲାଇ',
+    'ଅଗଷ୍ଟ',
+    'ସେପ୍ଟେମ୍ବର',
+    'ଅକ୍ଟୋବର',
+    'ନଭେମ୍ବର',
+    'ଡିସେମ୍ବର'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ଜାନୁଆରୀ',
+    'ଫେବ୍ରୁୟାରୀ',
+    'ମାର୍ଚ୍ଚ',
+    'ଅପ୍ରେଲ',
+    'ମେ',
+    'ଜୁନ',
+    'ଜୁଲାଇ',
+    'ଅଗଷ୍ଟ',
+    'ସେପ୍ଟେମ୍ବର',
+    'ଅକ୍ଟୋବର',
+    'ନଭେମ୍ବର',
+    'ଡିସେମ୍ବର'
+  ],
+      WEEKDAYS: const [
+    'ରବିବାର',
+    'ସୋମବାର',
+    'ମଙ୍ଗଳବାର',
+    'ବୁଧବାର',
+    'ଗୁରୁବାର',
+    'ଶୁକ୍ରବାର',
+    'ଶନିବାର'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'ରବିବାର',
+    'ସୋମବାର',
+    'ମଙ୍ଗଳବାର',
+    'ବୁଧବାର',
+    'ଗୁରୁବାର',
+    'ଶୁକ୍ରବାର',
+    'ଶନିବାର'
+  ],
+      SHORTWEEKDAYS: const [
+    'ରବି',
+    'ସୋମ',
+    'ମଙ୍ଗଳ',
+    'ବୁଧ',
+    'ଗୁରୁ',
+    'ଶୁକ୍ର',
+    'ଶନି'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'ରବି',
+    'ସୋମ',
+    'ମଙ୍ଗଳ',
+    'ବୁଧ',
+    'ଗୁରୁ',
+    'ଶୁକ୍ର',
+    'ଶନି'
+  ],
+      NARROWWEEKDAYS: const ['ର', 'ସୋ', 'ମ', 'ବୁ', 'ଗୁ', 'ଶୁ', 'ଶ'],
+      STANDALONENARROWWEEKDAYS: const ['ର', 'ସୋ', 'ମ', 'ବୁ', 'ଗୁ', 'ଶୁ', 'ଶ'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      AMPMS: const ['am', 'pm'],
+      DATEFORMATS: const ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd-M-yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [6, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale pa.
+   */
+  "pa": new DateSymbols(
+      NAME: "pa",
+      ERAS: const ['ਈ. ਪੂ.', 'ਸੰਨ'],
+      ERANAMES: const ['ਈ. ਪੂ.', 'ਸੰਨ'],
+      NARROWMONTHS: const [
+    'ਜ',
+    'ਫ਼',
+    'ਮਾ',
+    'ਅ',
+    'ਮ',
+    'ਜੂ',
+    'ਜੁ',
+    'ਅ',
+    'ਸ',
+    'ਅ',
+    'ਨ',
+    'ਦ'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ਜ',
+    'ਫ਼',
+    'ਮਾ',
+    'ਅ',
+    'ਮ',
+    'ਜੂ',
+    'ਜੁ',
+    'ਅ',
+    'ਸ',
+    'ਅ',
+    'ਨ',
+    'ਦ'
+  ],
+      MONTHS: const [
+    'ਜਨਵਰੀ',
+    'ਫ਼ਰਵਰੀ',
+    'ਮਾਰਚ',
+    'ਅਪ੍ਰੈਲ',
+    'ਮਈ',
+    'ਜੂਨ',
+    'ਜੁਲਾਈ',
+    'ਅਗਸਤ',
+    'ਸਤੰਬਰ',
+    'ਅਕਤੂਬਰ',
+    'ਨਵੰਬਰ',
+    'ਦਸੰਬਰ'
+  ],
+      STANDALONEMONTHS: const [
+    'ਜਨਵਰੀ',
+    'ਫ਼ਰਵਰੀ',
+    'ਮਾਰਚ',
+    'ਅਪ੍ਰੈਲ',
+    'ਮਈ',
+    'ਜੂਨ',
+    'ਜੁਲਾਈ',
+    'ਅਗਸਤ',
+    'ਸਤੰਬਰ',
+    'ਅਕਤੂਬਰ',
+    'ਨਵੰਬਰ',
+    'ਦਸੰਬਰ'
+  ],
+      SHORTMONTHS: const [
+    'ਜਨਵਰੀ',
+    'ਫ਼ਰਵਰੀ',
+    'ਮਾਰਚ',
+    'ਅਪ੍ਰੈਲ',
+    'ਮਈ',
+    'ਜੂਨ',
+    'ਜੁਲਾਈ',
+    'ਅਗਸਤ',
+    'ਸਤੰਬਰ',
+    'ਅਕਤੂਬਰ',
+    'ਨਵੰਬਰ',
+    'ਦਸੰਬਰ'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ਜਨਵਰੀ',
+    'ਫ਼ਰਵਰੀ',
+    'ਮਾਰਚ',
+    'ਅਪ੍ਰੈਲ',
+    'ਮਈ',
+    'ਜੂਨ',
+    'ਜੁਲਾਈ',
+    'ਅਗਸਤ',
+    'ਸਤੰਬਰ',
+    'ਅਕਤੂਬਰ',
+    'ਨਵੰਬਰ',
+    'ਦਸੰਬਰ'
+  ],
+      WEEKDAYS: const [
+    'ਐਤਵਾਰ',
+    'ਸੋਮਵਾਰ',
+    'ਮੰਗਲਵਾਰ',
+    'ਬੁਧਵਾਰ',
+    'ਵੀਰਵਾਰ',
+    'ਸ਼ੁੱਕਰਵਾਰ',
+    'ਸ਼ਨੀਵਾਰ'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'ਐਤਵਾਰ',
+    'ਸੋਮਵਾਰ',
+    'ਮੰਗਲਵਾਰ',
+    'ਬੁਧਵਾਰ',
+    'ਵੀਰਵਾਰ',
+    'ਸ਼ੁੱਕਰਵਾਰ',
+    'ਸ਼ਨੀਵਾਰ'
+  ],
+      SHORTWEEKDAYS: const [
+    'ਐਤ.',
+    'ਸੋਮ.',
+    'ਮੰਗਲ.',
+    'ਬੁਧ.',
+    'ਵੀਰ.',
+    'ਸ਼ੁੱਕਰ.',
+    'ਸ਼ਨੀ.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'ਐਤ.',
+    'ਸੋਮ.',
+    'ਮੰਗਲ.',
+    'ਬੁਧ.',
+    'ਵੀਰ.',
+    'ਸ਼ੁੱਕਰ.',
+    'ਸ਼ਨੀ.'
+  ],
+      NARROWWEEKDAYS: const ['ਐ', 'ਸੋ', 'ਮੰ', 'ਬੁੱ', 'ਵੀ', 'ਸ਼ੁੱ', 'ਸ਼'],
+      STANDALONENARROWWEEKDAYS: const [
+    'ਐ',
+    'ਸੋ',
+    'ਮੰ',
+    'ਬੁੱ',
+    'ਵੀ',
+    'ਸ਼ੁੱ',
+    'ਸ਼'
+  ],
+      SHORTQUARTERS: const ['ਪਊਆ', 'ਅੱਧਾ', 'ਪੌਣਾ', 'ਪੂਰਾ'],
+      QUARTERS: const ['ਪਊਆ', 'ਅੱਧਾ', 'ਪੌਣਾ', 'ਪੂਰਾ'],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'd/M/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1}, {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [6, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale pl.
+   */
+  "pl": new DateSymbols(
+      NAME: "pl",
+      ERAS: const ['p.n.e.', 'n.e.'],
+      ERANAMES: const ['p.n.e.', 'n.e.'],
+      NARROWMONTHS: const [
+    's',
+    'l',
+    'm',
+    'k',
+    'm',
+    'c',
+    'l',
+    's',
+    'w',
+    'p',
+    'l',
+    'g'
+  ],
+      STANDALONENARROWMONTHS: const [
+    's',
+    'l',
+    'm',
+    'k',
+    'm',
+    'c',
+    'l',
+    's',
+    'w',
+    'p',
+    'l',
+    'g'
+  ],
+      MONTHS: const [
+    'stycznia',
+    'lutego',
+    'marca',
+    'kwietnia',
+    'maja',
+    'czerwca',
+    'lipca',
+    'sierpnia',
+    'września',
+    'października',
+    'listopada',
+    'grudnia'
+  ],
+      STANDALONEMONTHS: const [
+    'styczeń',
+    'luty',
+    'marzec',
+    'kwiecień',
+    'maj',
+    'czerwiec',
+    'lipiec',
+    'sierpień',
+    'wrzesień',
+    'październik',
+    'listopad',
+    'grudzień'
+  ],
+      SHORTMONTHS: const [
+    'sty',
+    'lut',
+    'mar',
+    'kwi',
+    'maj',
+    'cze',
+    'lip',
+    'sie',
+    'wrz',
+    'paź',
+    'lis',
+    'gru'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'sty',
+    'lut',
+    'mar',
+    'kwi',
+    'maj',
+    'cze',
+    'lip',
+    'sie',
+    'wrz',
+    'paź',
+    'lis',
+    'gru'
+  ],
+      WEEKDAYS: const [
+    'niedziela',
+    'poniedziałek',
+    'wtorek',
+    'środa',
+    'czwartek',
+    'piątek',
+    'sobota'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'niedziela',
+    'poniedziałek',
+    'wtorek',
+    'środa',
+    'czwartek',
+    'piątek',
+    'sobota'
+  ],
+      SHORTWEEKDAYS: const [
+    'niedz.',
+    'pon.',
+    'wt.',
+    'śr.',
+    'czw.',
+    'pt.',
+    'sob.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'niedz.',
+    'pon.',
+    'wt.',
+    'śr.',
+    'czw.',
+    'pt.',
+    'sob.'
+  ],
+      NARROWWEEKDAYS: const ['N', 'P', 'W', 'Ś', 'C', 'P', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['N', 'P', 'W', 'Ś', 'C', 'P', 'S'],
+      SHORTQUARTERS: const ['K1', 'K2', 'K3', 'K4'],
+      QUARTERS: const ['I kwartał', 'II kwartał', 'III kwartał', 'IV kwartał'],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd.MM.y'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1}, {0}', '{1}, {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale pt.
+   */
+  "pt": new DateSymbols(
+      NAME: "pt",
+      ERAS: const ['a.C.', 'd.C.'],
+      ERANAMES: const ['Antes de Cristo', 'Ano do Senhor'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'janeiro',
+    'fevereiro',
+    'março',
+    'abril',
+    'maio',
+    'junho',
+    'julho',
+    'agosto',
+    'setembro',
+    'outubro',
+    'novembro',
+    'dezembro'
+  ],
+      STANDALONEMONTHS: const [
+    'janeiro',
+    'fevereiro',
+    'março',
+    'abril',
+    'maio',
+    'junho',
+    'julho',
+    'agosto',
+    'setembro',
+    'outubro',
+    'novembro',
+    'dezembro'
+  ],
+      SHORTMONTHS: const [
+    'jan',
+    'fev',
+    'mar',
+    'abr',
+    'mai',
+    'jun',
+    'jul',
+    'ago',
+    'set',
+    'out',
+    'nov',
+    'dez'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'jan',
+    'fev',
+    'mar',
+    'abr',
+    'mai',
+    'jun',
+    'jul',
+    'ago',
+    'set',
+    'out',
+    'nov',
+    'dez'
+  ],
+      WEEKDAYS: const [
+    'domingo',
+    'segunda-feira',
+    'terça-feira',
+    'quarta-feira',
+    'quinta-feira',
+    'sexta-feira',
+    'sábado'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'domingo',
+    'segunda-feira',
+    'terça-feira',
+    'quarta-feira',
+    'quinta-feira',
+    'sexta-feira',
+    'sábado'
+  ],
+      SHORTWEEKDAYS: const ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
+      STANDALONESHORTWEEKDAYS: const [
+    'dom',
+    'seg',
+    'ter',
+    'qua',
+    'qui',
+    'sex',
+    'sáb'
+  ],
+      NARROWWEEKDAYS: const ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
+      SHORTQUARTERS: const ['T1', 'T2', 'T3', 'T4'],
+      QUARTERS: const [
+    '1º trimestre',
+    '2º trimestre',
+    '3º trimestre',
+    '4º trimestre'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const [
+    'EEEE, d \'de\' MMMM \'de\' y',
+    'd \'de\' MMMM \'de\' y',
+    'dd/MM/y',
+    'dd/MM/yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale pt_BR.
+   */
+  /**
+   * Date/time formatting symbols for locale pt_BR.
+   */
+  "pt_BR": new DateSymbols(
+      NAME: "pt_BR",
+      ERAS: const ['a.C.', 'd.C.'],
+      ERANAMES: const ['Antes de Cristo', 'Ano do Senhor'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'janeiro',
+    'fevereiro',
+    'março',
+    'abril',
+    'maio',
+    'junho',
+    'julho',
+    'agosto',
+    'setembro',
+    'outubro',
+    'novembro',
+    'dezembro'
+  ],
+      STANDALONEMONTHS: const [
+    'janeiro',
+    'fevereiro',
+    'março',
+    'abril',
+    'maio',
+    'junho',
+    'julho',
+    'agosto',
+    'setembro',
+    'outubro',
+    'novembro',
+    'dezembro'
+  ],
+      SHORTMONTHS: const [
+    'jan',
+    'fev',
+    'mar',
+    'abr',
+    'mai',
+    'jun',
+    'jul',
+    'ago',
+    'set',
+    'out',
+    'nov',
+    'dez'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'jan',
+    'fev',
+    'mar',
+    'abr',
+    'mai',
+    'jun',
+    'jul',
+    'ago',
+    'set',
+    'out',
+    'nov',
+    'dez'
+  ],
+      WEEKDAYS: const [
+    'domingo',
+    'segunda-feira',
+    'terça-feira',
+    'quarta-feira',
+    'quinta-feira',
+    'sexta-feira',
+    'sábado'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'domingo',
+    'segunda-feira',
+    'terça-feira',
+    'quarta-feira',
+    'quinta-feira',
+    'sexta-feira',
+    'sábado'
+  ],
+      SHORTWEEKDAYS: const ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
+      STANDALONESHORTWEEKDAYS: const [
+    'dom',
+    'seg',
+    'ter',
+    'qua',
+    'qui',
+    'sex',
+    'sáb'
+  ],
+      NARROWWEEKDAYS: const ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
+      SHORTQUARTERS: const ['T1', 'T2', 'T3', 'T4'],
+      QUARTERS: const [
+    '1º trimestre',
+    '2º trimestre',
+    '3º trimestre',
+    '4º trimestre'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const [
+    'EEEE, d \'de\' MMMM \'de\' y',
+    'd \'de\' MMMM \'de\' y',
+    'dd/MM/y',
+    'dd/MM/yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale pt_PT.
+   */
+  "pt_PT": new DateSymbols(
+      NAME: "pt_PT",
+      ERAS: const ['a.C.', 'd.C.'],
+      ERANAMES: const ['Antes de Cristo', 'Ano do Senhor'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Janeiro',
+    'Fevereiro',
+    'Março',
+    'Abril',
+    'Maio',
+    'Junho',
+    'Julho',
+    'Agosto',
+    'Setembro',
+    'Outubro',
+    'Novembro',
+    'Dezembro'
+  ],
+      STANDALONEMONTHS: const [
+    'Janeiro',
+    'Fevereiro',
+    'Março',
+    'Abril',
+    'Maio',
+    'Junho',
+    'Julho',
+    'Agosto',
+    'Setembro',
+    'Outubro',
+    'Novembro',
+    'Dezembro'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Fev',
+    'Mar',
+    'Abr',
+    'Mai',
+    'Jun',
+    'Jul',
+    'Ago',
+    'Set',
+    'Out',
+    'Nov',
+    'Dez'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Fev',
+    'Mar',
+    'Abr',
+    'Mai',
+    'Jun',
+    'Jul',
+    'Ago',
+    'Set',
+    'Out',
+    'Nov',
+    'Dez'
+  ],
+      WEEKDAYS: const [
+    'domingo',
+    'segunda-feira',
+    'terça-feira',
+    'quarta-feira',
+    'quinta-feira',
+    'sexta-feira',
+    'sábado'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'domingo',
+    'segunda-feira',
+    'terça-feira',
+    'quarta-feira',
+    'quinta-feira',
+    'sexta-feira',
+    'sábado'
+  ],
+      SHORTWEEKDAYS: const ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
+      STANDALONESHORTWEEKDAYS: const [
+    'dom',
+    'seg',
+    'ter',
+    'qua',
+    'qui',
+    'sex',
+    'sáb'
+  ],
+      NARROWWEEKDAYS: const ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
+      SHORTQUARTERS: const ['T1', 'T2', 'T3', 'T4'],
+      QUARTERS: const [
+    '1.º trimestre',
+    '2.º trimestre',
+    '3.º trimestre',
+    '4.º trimestre'
+  ],
+      AMPMS: const ['da manhã', 'da tarde'],
+      DATEFORMATS: const [
+    'EEEE, d \'de\' MMMM \'de\' y',
+    'd \'de\' MMMM \'de\' y',
+    'dd/MM/y',
+    'dd/MM/yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const [
+    '{1} \'às\' {0}',
+    '{1} \'às\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale ro.
+   */
+  "ro": new DateSymbols(
+      NAME: "ro",
+      ERAS: const ['î.Hr.', 'd.Hr.'],
+      ERANAMES: const ['înainte de Hristos', 'după Hristos'],
+      NARROWMONTHS: const [
+    'I',
+    'F',
+    'M',
+    'A',
+    'M',
+    'I',
+    'I',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'I',
+    'F',
+    'M',
+    'A',
+    'M',
+    'I',
+    'I',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'ianuarie',
+    'februarie',
+    'martie',
+    'aprilie',
+    'mai',
+    'iunie',
+    'iulie',
+    'august',
+    'septembrie',
+    'octombrie',
+    'noiembrie',
+    'decembrie'
+  ],
+      STANDALONEMONTHS: const [
+    'ianuarie',
+    'februarie',
+    'martie',
+    'aprilie',
+    'mai',
+    'iunie',
+    'iulie',
+    'august',
+    'septembrie',
+    'octombrie',
+    'noiembrie',
+    'decembrie'
+  ],
+      SHORTMONTHS: const [
+    'ian.',
+    'feb.',
+    'mar.',
+    'apr.',
+    'mai',
+    'iun.',
+    'iul.',
+    'aug.',
+    'sept.',
+    'oct.',
+    'nov.',
+    'dec.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ian.',
+    'feb.',
+    'mar.',
+    'apr.',
+    'mai',
+    'iun.',
+    'iul.',
+    'aug.',
+    'sept.',
+    'oct.',
+    'nov.',
+    'dec.'
+  ],
+      WEEKDAYS: const [
+    'duminică',
+    'luni',
+    'marți',
+    'miercuri',
+    'joi',
+    'vineri',
+    'sâmbătă'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'duminică',
+    'luni',
+    'marți',
+    'miercuri',
+    'joi',
+    'vineri',
+    'sâmbătă'
+  ],
+      SHORTWEEKDAYS: const ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Dum',
+    'Lun',
+    'Mar',
+    'Mie',
+    'Joi',
+    'Vin',
+    'Sâm'
+  ],
+      NARROWWEEKDAYS: const ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
+      SHORTQUARTERS: const ['trim. I', 'trim. II', 'trim. III', 'trim. IV'],
+      QUARTERS: const [
+    'trimestrul I',
+    'trimestrul al II-lea',
+    'trimestrul al III-lea',
+    'trimestrul al IV-lea'
+  ],
+      AMPMS: const ['a.m.', 'p.m.'],
+      DATEFORMATS: const ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd.MM.y'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale ru.
+   */
+  "ru": new DateSymbols(
+      NAME: "ru",
+      ERAS: const ['до н. э.', 'н. э.'],
+      ERANAMES: const ['до н.э.', 'н.э.'],
+      NARROWMONTHS: const [
+    'Я',
+    'Ф',
+    'М',
+    'А',
+    'М',
+    'И',
+    'И',
+    'А',
+    'С',
+    'О',
+    'Н',
+    'Д'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'Я',
+    'Ф',
+    'М',
+    'А',
+    'М',
+    'И',
+    'И',
+    'А',
+    'С',
+    'О',
+    'Н',
+    'Д'
+  ],
+      MONTHS: const [
+    'января',
+    'февраля',
+    'марта',
+    'апреля',
+    'мая',
+    'июня',
+    'июля',
+    'августа',
+    'сентября',
+    'октября',
+    'ноября',
+    'декабря'
+  ],
+      STANDALONEMONTHS: const [
+    'Январь',
+    'Февраль',
+    'Март',
+    'Апрель',
+    'Май',
+    'Июнь',
+    'Июль',
+    'Август',
+    'Сентябрь',
+    'Октябрь',
+    'Ноябрь',
+    'Декабрь'
+  ],
+      SHORTMONTHS: const [
+    'янв.',
+    'февр.',
+    'марта',
+    'апр.',
+    'мая',
+    'июня',
+    'июля',
+    'авг.',
+    'сент.',
+    'окт.',
+    'нояб.',
+    'дек.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Янв.',
+    'Февр.',
+    'Март',
+    'Апр.',
+    'Май',
+    'Июнь',
+    'Июль',
+    'Авг.',
+    'Сент.',
+    'Окт.',
+    'Нояб.',
+    'Дек.'
+  ],
+      WEEKDAYS: const [
+    'воскресенье',
+    'понедельник',
+    'вторник',
+    'среда',
+    'четверг',
+    'пятница',
+    'суббота'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Воскресенье',
+    'Понедельник',
+    'Вторник',
+    'Среда',
+    'Четверг',
+    'Пятница',
+    'Суббота'
+  ],
+      SHORTWEEKDAYS: const ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
+      STANDALONESHORTWEEKDAYS: const ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
+      NARROWWEEKDAYS: const ['вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб'],
+      STANDALONENARROWWEEKDAYS: const ['В', 'П', 'В', 'С', 'Ч', 'П', 'С'],
+      SHORTQUARTERS: const ['1-й кв.', '2-й кв.', '3-й кв.', '4-й кв.'],
+      QUARTERS: const [
+    '1-й квартал',
+    '2-й квартал',
+    '3-й квартал',
+    '4-й квартал'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const [
+    'EEEE, d MMMM y \'г\'.',
+    'd MMMM y \'г\'.',
+    'd MMM y \'г\'.',
+    'dd.MM.yy'
+  ],
+      TIMEFORMATS: const ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
+      DATETIMEFORMATS: const ['{1}, {0}', '{1}, {0}', '{1}, {0}', '{1}, {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale si.
+   */
+  "si": new DateSymbols(
+      NAME: "si",
+      ERAS: const ['ක්‍රි.පූ.', 'ක්‍රි.ව.'],
+      ERANAMES: const ['ක්‍රිස්තු පූර්‍ව', 'ක්‍රිස්තු වර්‍ෂ'],
+      NARROWMONTHS: const [
+    'ජ',
+    'පෙ',
+    'මා',
+    'අ',
+    'මැ',
+    'ජූ',
+    'ජූ',
+    'අ',
+    'සැ',
+    'ඔ',
+    'නෙ',
+    'දෙ'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ජ',
+    'පෙ',
+    'මා',
+    'අ',
+    'මැ',
+    'ජූ',
+    'ජූ',
+    'අ',
+    'සැ',
+    'ඔ',
+    'නෙ',
+    'දෙ'
+  ],
+      MONTHS: const [
+    'ජනවාරි',
+    'පෙබරවාරි',
+    'මාර්තු',
+    'අප්‍රේල්',
+    'මැයි',
+    'ජූනි',
+    'ජූලි',
+    'අගෝස්තු',
+    'සැප්තැම්බර්',
+    'ඔක්තෝබර්',
+    'නොවැම්බර්',
+    'දෙසැම්බර්'
+  ],
+      STANDALONEMONTHS: const [
+    'ජනවාරි',
+    'පෙබරවාරි',
+    'මාර්තු',
+    'අප්‍රේල්',
+    'මැයි',
+    'ජූනි',
+    'ජූලි',
+    'අගෝස්තු',
+    'සැප්තැම්බර්',
+    'ඔක්තෝබර්',
+    'නොවැම්බර්',
+    'දෙසැම්බර්'
+  ],
+      SHORTMONTHS: const [
+    'ජන',
+    'පෙබ',
+    'මාර්තු',
+    'අප්‍රේල්',
+    'මැයි',
+    'ජූනි',
+    'ජූලි',
+    'අගෝ',
+    'සැප්',
+    'ඔක්',
+    'නොවැ',
+    'දෙසැ'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ජන',
+    'පෙබ',
+    'මාර්',
+    'අප්‍රේල්',
+    'මැයි',
+    'ජූනි',
+    'ජූලි',
+    'අගෝ',
+    'සැප්',
+    'ඔක්',
+    'නොවැ',
+    'දෙසැ'
+  ],
+      WEEKDAYS: const [
+    'ඉරිදා',
+    'සඳුදා',
+    'අඟහරුවාදා',
+    'බදාදා',
+    'බ්‍රහස්පතින්දා',
+    'සිකුරාදා',
+    'සෙනසුරාදා'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'ඉරිදා',
+    'සඳුදා',
+    'අඟහරුවාදා',
+    'බදාදා',
+    'බ්‍රහස්පතින්දා',
+    'සිකුරාදා',
+    'සෙනසුරාදා'
+  ],
+      SHORTWEEKDAYS: const [
+    'ඉරිදා',
+    'සඳුදා',
+    'අඟහ',
+    'බදාදා',
+    'බ්‍රහස්',
+    'සිකු',
+    'සෙන'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'ඉරිදා',
+    'සඳුදා',
+    'අඟහ',
+    'බදාදා',
+    'බ්‍රහස්',
+    'සිකු',
+    'සෙන'
+  ],
+      NARROWWEEKDAYS: const ['ඉ', 'ස', 'අ', 'බ', 'බ්‍ර', 'සි', 'සෙ'],
+      STANDALONENARROWWEEKDAYS: const ['ඉ', 'ස', 'අ', 'බ', 'බ්‍ර', 'සි', 'සෙ'],
+      SHORTQUARTERS: const ['කාර්:1', 'කාර්:2', 'කාර්:3', 'කාර්:4'],
+      QUARTERS: const [
+    '1 වන කාර්තුව',
+    '2 වන කාර්තුව',
+    '3 වන කාර්තුව',
+    '4 වන කාර්තුව'
+  ],
+      AMPMS: const ['පෙ.ව.', 'ප.ව.'],
+      DATEFORMATS: const ['y MMMM d, EEEE', 'y MMMM d', 'y MMM d', 'y-MM-dd'],
+      TIMEFORMATS: const [
+    'a h.mm.ss zzzz',
+    'a h.mm.ss z',
+    'a h.mm.ss',
+    'a h.mm'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale sk.
+   */
+  "sk": new DateSymbols(
+      NAME: "sk",
+      ERAS: const ['pred n.l.', 'n.l.'],
+      ERANAMES: const ['pred n.l.', 'n.l.'],
+      NARROWMONTHS: const [
+    'j',
+    'f',
+    'm',
+    'a',
+    'm',
+    'j',
+    'j',
+    'a',
+    's',
+    'o',
+    'n',
+    'd'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'j',
+    'f',
+    'm',
+    'a',
+    'm',
+    'j',
+    'j',
+    'a',
+    's',
+    'o',
+    'n',
+    'd'
+  ],
+      MONTHS: const [
+    'januára',
+    'februára',
+    'marca',
+    'apríla',
+    'mája',
+    'júna',
+    'júla',
+    'augusta',
+    'septembra',
+    'októbra',
+    'novembra',
+    'decembra'
+  ],
+      STANDALONEMONTHS: const [
+    'január',
+    'február',
+    'marec',
+    'apríl',
+    'máj',
+    'jún',
+    'júl',
+    'august',
+    'september',
+    'október',
+    'november',
+    'december'
+  ],
+      SHORTMONTHS: const [
+    'jan',
+    'feb',
+    'mar',
+    'apr',
+    'máj',
+    'jún',
+    'júl',
+    'aug',
+    'sep',
+    'okt',
+    'nov',
+    'dec'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'jan',
+    'feb',
+    'mar',
+    'apr',
+    'máj',
+    'jún',
+    'júl',
+    'aug',
+    'sep',
+    'okt',
+    'nov',
+    'dec'
+  ],
+      WEEKDAYS: const [
+    'nedeľa',
+    'pondelok',
+    'utorok',
+    'streda',
+    'štvrtok',
+    'piatok',
+    'sobota'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'nedeľa',
+    'pondelok',
+    'utorok',
+    'streda',
+    'štvrtok',
+    'piatok',
+    'sobota'
+  ],
+      SHORTWEEKDAYS: const ['ne', 'po', 'ut', 'st', 'št', 'pi', 'so'],
+      STANDALONESHORTWEEKDAYS: const ['ne', 'po', 'ut', 'st', 'št', 'pi', 'so'],
+      NARROWWEEKDAYS: const ['N', 'P', 'U', 'S', 'Š', 'P', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['N', 'P', 'U', 'S', 'Š', 'P', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    '1. štvrťrok',
+    '2. štvrťrok',
+    '3. štvrťrok',
+    '4. štvrťrok'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, d. MMMM y', 'd. MMMM y', 'd.M.y', 'd.M.y'],
+      TIMEFORMATS: const ['H:mm:ss zzzz', 'H:mm:ss z', 'H:mm:ss', 'H:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale sl.
+   */
+  "sl": new DateSymbols(
+      NAME: "sl",
+      ERAS: const ['pr. n. št.', 'po Kr.'],
+      ERANAMES: const ['pred našim štetjem', 'naše štetje'],
+      NARROWMONTHS: const [
+    'j',
+    'f',
+    'm',
+    'a',
+    'm',
+    'j',
+    'j',
+    'a',
+    's',
+    'o',
+    'n',
+    'd'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'j',
+    'f',
+    'm',
+    'a',
+    'm',
+    'j',
+    'j',
+    'a',
+    's',
+    'o',
+    'n',
+    'd'
+  ],
+      MONTHS: const [
+    'januar',
+    'februar',
+    'marec',
+    'april',
+    'maj',
+    'junij',
+    'julij',
+    'avgust',
+    'september',
+    'oktober',
+    'november',
+    'december'
+  ],
+      STANDALONEMONTHS: const [
+    'januar',
+    'februar',
+    'marec',
+    'april',
+    'maj',
+    'junij',
+    'julij',
+    'avgust',
+    'september',
+    'oktober',
+    'november',
+    'december'
+  ],
+      SHORTMONTHS: const [
+    'jan.',
+    'feb.',
+    'mar.',
+    'apr.',
+    'maj',
+    'jun.',
+    'jul.',
+    'avg.',
+    'sep.',
+    'okt.',
+    'nov.',
+    'dec.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'jan',
+    'feb',
+    'mar',
+    'apr',
+    'maj',
+    'jun',
+    'jul',
+    'avg',
+    'sep',
+    'okt',
+    'nov',
+    'dec'
+  ],
+      WEEKDAYS: const [
+    'nedelja',
+    'ponedeljek',
+    'torek',
+    'sreda',
+    'četrtek',
+    'petek',
+    'sobota'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'nedelja',
+    'ponedeljek',
+    'torek',
+    'sreda',
+    'četrtek',
+    'petek',
+    'sobota'
+  ],
+      SHORTWEEKDAYS: const [
+    'ned.',
+    'pon.',
+    'tor.',
+    'sre.',
+    'čet.',
+    'pet.',
+    'sob.'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'ned',
+    'pon',
+    'tor',
+    'sre',
+    'čet',
+    'pet',
+    'sob'
+  ],
+      NARROWWEEKDAYS: const ['n', 'p', 't', 's', 'č', 'p', 's'],
+      STANDALONENARROWWEEKDAYS: const ['n', 'p', 't', 's', 'č', 'p', 's'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    '1. četrtletje',
+    '2. četrtletje',
+    '3. četrtletje',
+    '4. četrtletje'
+  ],
+      AMPMS: const ['dop.', 'pop.'],
+      DATEFORMATS: const [
+    'EEEE, dd. MMMM y',
+    'dd. MMMM y',
+    'd. MMM y',
+    'd. MM. yy'
+  ],
+      TIMEFORMATS: const ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale sq.
+   */
+  "sq": new DateSymbols(
+      NAME: "sq",
+      ERAS: const ['p.e.r.', 'e.r.'],
+      ERANAMES: const ['para erës së re', 'erës së re'],
+      NARROWMONTHS: const [
+    'J',
+    'S',
+    'M',
+    'P',
+    'M',
+    'Q',
+    'K',
+    'G',
+    'S',
+    'T',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'S',
+    'M',
+    'P',
+    'M',
+    'Q',
+    'K',
+    'G',
+    'S',
+    'T',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'janar',
+    'shkurt',
+    'mars',
+    'prill',
+    'maj',
+    'qershor',
+    'korrik',
+    'gusht',
+    'shtator',
+    'tetor',
+    'nëntor',
+    'dhjetor'
+  ],
+      STANDALONEMONTHS: const [
+    'janar',
+    'shkurt',
+    'mars',
+    'prill',
+    'maj',
+    'qershor',
+    'korrik',
+    'gusht',
+    'shtator',
+    'tetor',
+    'nëntor',
+    'dhjetor'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Shk',
+    'Mar',
+    'Pri',
+    'Maj',
+    'Qer',
+    'Kor',
+    'Gsh',
+    'Sht',
+    'Tet',
+    'Nën',
+    'Dhj'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Shk',
+    'Mar',
+    'Pri',
+    'Maj',
+    'Qer',
+    'Kor',
+    'Gsh',
+    'Sht',
+    'Tet',
+    'Nën',
+    'Dhj'
+  ],
+      WEEKDAYS: const [
+    'e diel',
+    'e hënë',
+    'e martë',
+    'e mërkurë',
+    'e enjte',
+    'e premte',
+    'e shtunë'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'e diel',
+    'e hënë',
+    'e martë',
+    'e mërkurë',
+    'e enjte',
+    'e premte',
+    'e shtunë'
+  ],
+      SHORTWEEKDAYS: const ['Die', 'Hën', 'Mar', 'Mër', 'Enj', 'Pre', 'Sht'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Die',
+    'Hën',
+    'Mar',
+    'Mër',
+    'Enj',
+    'Pre',
+    'Sht'
+  ],
+      NARROWWEEKDAYS: const ['D', 'H', 'M', 'M', 'E', 'P', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['D', 'H', 'M', 'M', 'E', 'P', 'S'],
+      SHORTQUARTERS: const ['T1', 'T2', 'T3', 'T4'],
+      QUARTERS: const [
+    'tremujori i parë',
+    'tremujori i dytë',
+    'tremujori i tretë',
+    'tremujori i katërt'
+  ],
+      AMPMS: const ['paradite', 'pasdite'],
+      DATEFORMATS: const [
+    'EEEE, dd MMMM y',
+    'dd MMMM y',
+    'dd/MM/y',
+    'dd/MM/yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const [
+    '{1} \'në\' {0}',
+    '{1} \'në\' {0}',
+    '{1} {0}',
+    '{1} {0}'
+  ],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale sr.
+   */
+  "sr": new DateSymbols(
+      NAME: "sr",
+      ERAS: const ['п. н. е.', 'н. е.'],
+      ERANAMES: const ['Пре нове ере', 'Нове ере'],
+      NARROWMONTHS: const [
+    'ј',
+    'ф',
+    'м',
+    'а',
+    'м',
+    'ј',
+    'ј',
+    'а',
+    'с',
+    'о',
+    'н',
+    'д'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ј',
+    'ф',
+    'м',
+    'а',
+    'м',
+    'ј',
+    'ј',
+    'а',
+    'с',
+    'о',
+    'н',
+    'д'
+  ],
+      MONTHS: const [
+    'јануар',
+    'фебруар',
+    'март',
+    'април',
+    'мај',
+    'јун',
+    'јул',
+    'август',
+    'септембар',
+    'октобар',
+    'новембар',
+    'децембар'
+  ],
+      STANDALONEMONTHS: const [
+    'јануар',
+    'фебруар',
+    'март',
+    'април',
+    'мај',
+    'јун',
+    'јул',
+    'август',
+    'септембар',
+    'октобар',
+    'новембар',
+    'децембар'
+  ],
+      SHORTMONTHS: const [
+    'јан',
+    'феб',
+    'мар',
+    'апр',
+    'мај',
+    'јун',
+    'јул',
+    'авг',
+    'сеп',
+    'окт',
+    'нов',
+    'дец'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'јан',
+    'феб',
+    'мар',
+    'апр',
+    'мај',
+    'јун',
+    'јул',
+    'авг',
+    'сеп',
+    'окт',
+    'нов',
+    'дец'
+  ],
+      WEEKDAYS: const [
+    'недеља',
+    'понедељак',
+    'уторак',
+    'среда',
+    'четвртак',
+    'петак',
+    'субота'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'недеља',
+    'понедељак',
+    'уторак',
+    'среда',
+    'четвртак',
+    'петак',
+    'субота'
+  ],
+      SHORTWEEKDAYS: const ['нед', 'пон', 'уто', 'сре', 'чет', 'пет', 'суб'],
+      STANDALONESHORTWEEKDAYS: const [
+    'нед',
+    'пон',
+    'уто',
+    'сре',
+    'чет',
+    'пет',
+    'суб'
+  ],
+      NARROWWEEKDAYS: const ['н', 'п', 'у', 'с', 'ч', 'п', 'с'],
+      STANDALONENARROWWEEKDAYS: const ['н', 'п', 'у', 'с', 'ч', 'п', 'с'],
+      SHORTQUARTERS: const ['К1', 'К2', 'К3', 'К4'],
+      QUARTERS: const [
+    'Прво тромесечје',
+    'Друго тромесечје',
+    'Треће тромесечје',
+    'Четврто тромесечје'
+  ],
+      AMPMS: const ['пре подне', 'поподне'],
+      DATEFORMATS: const [
+    'EEEE, dd. MMMM y.',
+    'dd. MMMM y.',
+    'dd.MM.y.',
+    'd.M.yy.'
+  ],
+      TIMEFORMATS: const ['HH.mm.ss zzzz', 'HH.mm.ss z', 'HH.mm.ss', 'HH.mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale sv.
+   */
+  "sv": new DateSymbols(
+      NAME: "sv",
+      ERAS: const ['f.Kr.', 'e.Kr.'],
+      ERANAMES: const ['före Kristus', 'efter Kristus'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'januari',
+    'februari',
+    'mars',
+    'april',
+    'maj',
+    'juni',
+    'juli',
+    'augusti',
+    'september',
+    'oktober',
+    'november',
+    'december'
+  ],
+      STANDALONEMONTHS: const [
+    'Januari',
+    'Februari',
+    'Mars',
+    'April',
+    'Maj',
+    'Juni',
+    'Juli',
+    'Augusti',
+    'September',
+    'Oktober',
+    'November',
+    'December'
+  ],
+      SHORTMONTHS: const [
+    'jan',
+    'feb',
+    'mar',
+    'apr',
+    'maj',
+    'jun',
+    'jul',
+    'aug',
+    'sep',
+    'okt',
+    'nov',
+    'dec'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mar',
+    'Apr',
+    'Maj',
+    'Jun',
+    'Jul',
+    'Aug',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Dec'
+  ],
+      WEEKDAYS: const [
+    'söndag',
+    'måndag',
+    'tisdag',
+    'onsdag',
+    'torsdag',
+    'fredag',
+    'lördag'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Söndag',
+    'Måndag',
+    'Tisdag',
+    'Onsdag',
+    'Torsdag',
+    'Fredag',
+    'Lördag'
+  ],
+      SHORTWEEKDAYS: const ['sön', 'mån', 'tis', 'ons', 'tors', 'fre', 'lör'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Sön',
+    'Mån',
+    'Tis',
+    'Ons',
+    'Tor',
+    'Fre',
+    'Lör'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'O', 'T', 'F', 'L'],
+      SHORTQUARTERS: const ['K1', 'K2', 'K3', 'K4'],
+      QUARTERS: const [
+    '1:a kvartalet',
+    '2:a kvartalet',
+    '3:e kvartalet',
+    '4:e kvartalet'
+  ],
+      AMPMS: const ['fm', 'em'],
+      DATEFORMATS: const ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'y-MM-dd'],
+      TIMEFORMATS: const [
+    '\'kl\'. HH:mm:ss zzzz',
+    'HH:mm:ss z',
+    'HH:mm:ss',
+    'HH:mm'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 3),
+  /**
+   * Date/time formatting symbols for locale sw.
+   */
+  "sw": new DateSymbols(
+      NAME: "sw",
+      ERAS: const ['KK', 'BK'],
+      ERANAMES: const ['Kabla ya Kristo', 'Baada ya Kristo'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Januari',
+    'Februari',
+    'Machi',
+    'Aprili',
+    'Mei',
+    'Juni',
+    'Julai',
+    'Agosti',
+    'Septemba',
+    'Oktoba',
+    'Novemba',
+    'Desemba'
+  ],
+      STANDALONEMONTHS: const [
+    'Januari',
+    'Februari',
+    'Machi',
+    'Aprili',
+    'Mei',
+    'Juni',
+    'Julai',
+    'Agosti',
+    'Septemba',
+    'Oktoba',
+    'Novemba',
+    'Desemba'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mac',
+    'Apr',
+    'Mei',
+    'Jun',
+    'Jul',
+    'Ago',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Des'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mac',
+    'Apr',
+    'Mei',
+    'Jun',
+    'Jul',
+    'Ago',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Des'
+  ],
+      WEEKDAYS: const [
+    'Jumapili',
+    'Jumatatu',
+    'Jumanne',
+    'Jumatano',
+    'Alhamisi',
+    'Ijumaa',
+    'Jumamosi'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Jumapili',
+    'Jumatatu',
+    'Jumanne',
+    'Jumatano',
+    'Alhamisi',
+    'Ijumaa',
+    'Jumamosi'
+  ],
+      SHORTWEEKDAYS: const [
+    'Jumapili',
+    'Jumatatu',
+    'Jumanne',
+    'Jumatano',
+    'Alhamisi',
+    'Ijumaa',
+    'Jumamosi'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'Jumapili',
+    'Jumatatu',
+    'Jumanne',
+    'Jumatano',
+    'Alhamisi',
+    'Ijumaa',
+    'Jumamosi'
+  ],
+      NARROWWEEKDAYS: const ['2', '3', '4', '5', 'A', 'I', '1'],
+      STANDALONENARROWWEEKDAYS: const ['2', '3', '4', '5', 'A', 'I', '1'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const ['Robo 1', 'Robo 2', 'Robo 3', 'Robo 4'],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale ta.
+   */
+  "ta": new DateSymbols(
+      NAME: "ta",
+      ERAS: const ['கி.மு.', 'கி.பி.'],
+      ERANAMES: const ['கிறிஸ்துவுக்கு முன்', 'அனோ டோமினி'],
+      NARROWMONTHS: const [
+    'ஜ',
+    'பி',
+    'மா',
+    'ஏ',
+    'மே',
+    'ஜூ',
+    'ஜூ',
+    'ஆ',
+    'செ',
+    'அ',
+    'ந',
+    'டி'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ஜ',
+    'பி',
+    'மா',
+    'ஏ',
+    'மே',
+    'ஜூ',
+    'ஜூ',
+    'ஆ',
+    'செ',
+    'அ',
+    'ந',
+    'டி'
+  ],
+      MONTHS: const [
+    'ஜனவரி',
+    'பிப்ரவரி',
+    'மார்ச்',
+    'ஏப்ரல்',
+    'மே',
+    'ஜூன்',
+    'ஜூலை',
+    'ஆகஸ்ட்',
+    'செப்டம்பர்',
+    'அக்டோபர்',
+    'நவம்பர்',
+    'டிசம்பர்'
+  ],
+      STANDALONEMONTHS: const [
+    'ஜனவரி',
+    'பிப்ரவரி',
+    'மார்ச்',
+    'ஏப்ரல்',
+    'மே',
+    'ஜூன்',
+    'ஜூலை',
+    'ஆகஸ்டு',
+    'செப்டம்பர்',
+    'அக்டோபர்',
+    'நவம்பர்',
+    'டிசம்பர்'
+  ],
+      SHORTMONTHS: const [
+    'ஜன.',
+    'பிப்.',
+    'மார்.',
+    'ஏப்.',
+    'மே',
+    'ஜூன்',
+    'ஜூலை',
+    'ஆக.',
+    'செப்.',
+    'அக்.',
+    'நவ.',
+    'டிச.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ஜன.',
+    'பிப்.',
+    'மார்.',
+    'ஏப்.',
+    'மே',
+    'ஜூன்',
+    'ஜூலை',
+    'ஆக.',
+    'செப்.',
+    'அக்.',
+    'நவ.',
+    'டிச.'
+  ],
+      WEEKDAYS: const [
+    'ஞாயிறு',
+    'திங்கள்',
+    'செவ்வாய்',
+    'புதன்',
+    'வியாழன்',
+    'வெள்ளி',
+    'சனி'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'ஞாயிறு',
+    'திங்கள்',
+    'செவ்வாய்',
+    'புதன்',
+    'வியாழன்',
+    'வெள்ளி',
+    'சனி'
+  ],
+      SHORTWEEKDAYS: const ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'],
+      STANDALONESHORTWEEKDAYS: const ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'],
+      NARROWWEEKDAYS: const ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'],
+      STANDALONENARROWWEEKDAYS: const ['ஞா', 'தி', 'செ', 'பு', 'வி', 'வெ', 'ச'],
+      SHORTQUARTERS: const ['காலாண்டு1', 'காலாண்டு2', 'காலாண்டு3', 'காலாண்டு4'],
+      QUARTERS: const [
+    'முதல் காலாண்டு',
+    'இரண்டாம் காலாண்டு',
+    'மூன்றாம் காலாண்டு',
+    'நான்காம் காலாண்டு'
+  ],
+      AMPMS: const ['முற்பகல்', 'பிற்பகல்'],
+      DATEFORMATS: const ['EEEE, d MMMM, y', 'd MMMM, y', 'd MMM, y', 'd-M-yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [6, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale te.
+   */
+  "te": new DateSymbols(
+      NAME: "te",
+      ERAS: const ['క్రీపూ', 'క్రీశ'],
+      ERANAMES: const ['ఈసాపూర్వ.', 'సన్.'],
+      NARROWMONTHS: const [
+    'జ',
+    'ఫి',
+    'మా',
+    'ఏ',
+    'మే',
+    'జూ',
+    'జు',
+    'ఆ',
+    'సె',
+    'అ',
+    'న',
+    'డి'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'జ',
+    'ఫి',
+    'మా',
+    'ఏ',
+    'మే',
+    'జూ',
+    'జు',
+    'ఆ',
+    'సె',
+    'అ',
+    'న',
+    'డి'
+  ],
+      MONTHS: const [
+    'జనవరి',
+    'ఫిబ్రవరి',
+    'మార్చి',
+    'ఎప్రిల్',
+    'మే',
+    'జూన్',
+    'జులై',
+    'ఆగస్టు',
+    'సెప్టెంబర్',
+    'అక్టోబర్',
+    'నవంబర్',
+    'డిసెంబర్'
+  ],
+      STANDALONEMONTHS: const [
+    'జనవరి',
+    'ఫిబ్రవరి',
+    'మార్చి',
+    'ఎప్రిల్',
+    'మే',
+    'జూన్',
+    'జూలై',
+    'ఆగస్టు',
+    'సెప్టెంబర్',
+    'అక్టోబర్',
+    'నవంబర్',
+    'డిసెంబర్'
+  ],
+      SHORTMONTHS: const [
+    'జన',
+    'ఫిబ్ర',
+    'మార్చి',
+    'ఏప్రి',
+    'మే',
+    'జూన్',
+    'జులై',
+    'ఆగ',
+    'సెప్టెం',
+    'అక్టో',
+    'నవం',
+    'డిసెం'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'జన',
+    'ఫిబ్ర',
+    'మార్చి',
+    'ఏప్రి',
+    'మే',
+    'జూన్',
+    'జులై',
+    'ఆగస్టు',
+    'సెప్టెం',
+    'అక్టో',
+    'నవం',
+    'డిసెం'
+  ],
+      WEEKDAYS: const [
+    'ఆదివారం',
+    'సోమవారం',
+    'మంగళవారం',
+    'బుధవారం',
+    'గురువారం',
+    'శుక్రవారం',
+    'శనివారం'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'ఆదివారం',
+    'సోమవారం',
+    'మంగళవారం',
+    'బుధవారం',
+    'గురువారం',
+    'శుక్రవారం',
+    'శనివారం'
+  ],
+      SHORTWEEKDAYS: const [
+    'ఆది',
+    'సోమ',
+    'మంగళ',
+    'బుధ',
+    'గురు',
+    'శుక్ర',
+    'శని'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'ఆది',
+    'సోమ',
+    'మంగళ',
+    'బుధ',
+    'గురు',
+    'శుక్ర',
+    'శని'
+  ],
+      NARROWWEEKDAYS: const ['ఆ', 'సో', 'మ', 'బు', 'గు', 'శు', 'శ'],
+      STANDALONENARROWWEEKDAYS: const ['ఆ', 'సో', 'మ', 'బు', 'గు', 'శు', 'శ'],
+      SHORTQUARTERS: const ['త్రై1', 'త్రై2', 'త్రై3', 'త్రై4'],
+      QUARTERS: const [
+    '1వ త్రైమాసం',
+    '2వ త్రైమాసం',
+    '3వ త్రైమాసం',
+    '4వ త్రైమాసం'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['d MMMM y EEEE', 'd MMMM y', 'd MMM y', 'dd-MM-yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [6, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale th.
+   */
+  "th": new DateSymbols(
+      NAME: "th",
+      ERAS: const ['ปีก่อน ค.ศ.', 'ค.ศ.'],
+      ERANAMES: const ['ปีก่อนคริสต์ศักราช', 'คริสต์ศักราช'],
+      NARROWMONTHS: const [
+    'ม.ค.',
+    'ก.พ.',
+    'มี.ค.',
+    'เม.ย.',
+    'พ.ค.',
+    'มิ.ย.',
+    'ก.ค.',
+    'ส.ค.',
+    'ก.ย.',
+    'ต.ค.',
+    'พ.ย.',
+    'ธ.ค.'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'ม.ค.',
+    'ก.พ.',
+    'มี.ค.',
+    'เม.ย.',
+    'พ.ค.',
+    'มิ.ย.',
+    'ก.ค.',
+    'ส.ค.',
+    'ก.ย.',
+    'ต.ค.',
+    'พ.ย.',
+    'ธ.ค.'
+  ],
+      MONTHS: const [
+    'มกราคม',
+    'กุมภาพันธ์',
+    'มีนาคม',
+    'เมษายน',
+    'พฤษภาคม',
+    'มิถุนายน',
+    'กรกฎาคม',
+    'สิงหาคม',
+    'กันยายน',
+    'ตุลาคม',
+    'พฤศจิกายน',
+    'ธันวาคม'
+  ],
+      STANDALONEMONTHS: const [
+    'มกราคม',
+    'กุมภาพันธ์',
+    'มีนาคม',
+    'เมษายน',
+    'พฤษภาคม',
+    'มิถุนายน',
+    'กรกฎาคม',
+    'สิงหาคม',
+    'กันยายน',
+    'ตุลาคม',
+    'พฤศจิกายน',
+    'ธันวาคม'
+  ],
+      SHORTMONTHS: const [
+    'ม.ค.',
+    'ก.พ.',
+    'มี.ค.',
+    'เม.ย.',
+    'พ.ค.',
+    'มิ.ย.',
+    'ก.ค.',
+    'ส.ค.',
+    'ก.ย.',
+    'ต.ค.',
+    'พ.ย.',
+    'ธ.ค.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'ม.ค.',
+    'ก.พ.',
+    'มี.ค.',
+    'เม.ย.',
+    'พ.ค.',
+    'มิ.ย.',
+    'ก.ค.',
+    'ส.ค.',
+    'ก.ย.',
+    'ต.ค.',
+    'พ.ย.',
+    'ธ.ค.'
+  ],
+      WEEKDAYS: const [
+    'วันอาทิตย์',
+    'วันจันทร์',
+    'วันอังคาร',
+    'วันพุธ',
+    'วันพฤหัสบดี',
+    'วันศุกร์',
+    'วันเสาร์'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'วันอาทิตย์',
+    'วันจันทร์',
+    'วันอังคาร',
+    'วันพุธ',
+    'วันพฤหัสบดี',
+    'วันศุกร์',
+    'วันเสาร์'
+  ],
+      SHORTWEEKDAYS: const ['อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'],
+      STANDALONESHORTWEEKDAYS: const [
+    'อา.',
+    'จ.',
+    'อ.',
+    'พ.',
+    'พฤ.',
+    'ศ.',
+    'ส.'
+  ],
+      NARROWWEEKDAYS: const ['อา', 'จ', 'อ', 'พ', 'พฤ', 'ศ', 'ส'],
+      STANDALONENARROWWEEKDAYS: const ['อา', 'จ', 'อ', 'พ', 'พฤ', 'ศ', 'ส'],
+      SHORTQUARTERS: const ['ไตรมาส 1', 'ไตรมาส 2', 'ไตรมาส 3', 'ไตรมาส 4'],
+      QUARTERS: const ['ไตรมาส 1', 'ไตรมาส 2', 'ไตรมาส 3', 'ไตรมาส 4'],
+      AMPMS: const ['ก่อนเที่ยง', 'หลังเที่ยง'],
+      DATEFORMATS: const [
+    'EEEEที่ d MMMM G y',
+    'd MMMM y',
+    'd MMM y',
+    'd/M/yy'
+  ],
+      TIMEFORMATS: const [
+    'H นาฬิกา mm นาที ss วินาที zzzz',
+    'H นาฬิกา mm นาที ss วินาที z',
+    'HH:mm:ss',
+    'HH:mm'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale tl.
+   */
+  "tl": new DateSymbols(
+      NAME: "tl",
+      ERAS: const ['BC', 'AD'],
+      ERANAMES: const ['BC', 'AD'],
+      NARROWMONTHS: const [
+    'E',
+    'P',
+    'M',
+    'A',
+    'M',
+    'H',
+    'H',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'E',
+    'P',
+    'M',
+    'A',
+    'M',
+    'H',
+    'H',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Enero',
+    'Pebrero',
+    'Marso',
+    'Abril',
+    'Mayo',
+    'Hunyo',
+    'Hulyo',
+    'Agosto',
+    'Setyembre',
+    'Oktubre',
+    'Nobyembre',
+    'Disyembre'
+  ],
+      STANDALONEMONTHS: const [
+    'Enero',
+    'Pebrero',
+    'Marso',
+    'Abril',
+    'Mayo',
+    'Hunyo',
+    'Hulyo',
+    'Agosto',
+    'Setyembre',
+    'Oktubre',
+    'Nobyembre',
+    'Disyembre'
+  ],
+      SHORTMONTHS: const [
+    'Ene',
+    'Peb',
+    'Mar',
+    'Abr',
+    'May',
+    'Hun',
+    'Hul',
+    'Ago',
+    'Set',
+    'Okt',
+    'Nob',
+    'Dis'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Ene',
+    'Peb',
+    'Mar',
+    'Abr',
+    'May',
+    'Hun',
+    'Hul',
+    'Ago',
+    'Set',
+    'Okt',
+    'Nob',
+    'Dis'
+  ],
+      WEEKDAYS: const [
+    'Linggo',
+    'Lunes',
+    'Martes',
+    'Miyerkules',
+    'Huwebes',
+    'Biyernes',
+    'Sabado'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Linggo',
+    'Lunes',
+    'Martes',
+    'Miyerkules',
+    'Huwebes',
+    'Biyernes',
+    'Sabado'
+  ],
+      SHORTWEEKDAYS: const ['Lin', 'Lun', 'Mar', 'Miy', 'Huw', 'Biy', 'Sab'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Lin',
+    'Lun',
+    'Mar',
+    'Miy',
+    'Huw',
+    'Biy',
+    'Sab'
+  ],
+      NARROWWEEKDAYS: const ['L', 'L', 'M', 'M', 'H', 'B', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['L', 'L', 'M', 'M', 'H', 'B', 'S'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    'ika-1 quarter',
+    'ika-2 quarter',
+    'ika-3 quarter',
+    'ika-4 na quarter'
+  ],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const [
+    '{1} \'ng\' {0}',
+    '{1} \'ng\' {0}',
+    '{1}, {0}',
+    '{1}, {0}'
+  ],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale tr.
+   */
+  "tr": new DateSymbols(
+      NAME: "tr",
+      ERAS: const ['MÖ', 'MS'],
+      ERANAMES: const ['Milattan Önce', 'Milattan Sonra'],
+      NARROWMONTHS: const [
+    'O',
+    'Ş',
+    'M',
+    'N',
+    'M',
+    'H',
+    'T',
+    'A',
+    'E',
+    'E',
+    'K',
+    'A'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'O',
+    'Ş',
+    'M',
+    'N',
+    'M',
+    'H',
+    'T',
+    'A',
+    'E',
+    'E',
+    'K',
+    'A'
+  ],
+      MONTHS: const [
+    'Ocak',
+    'Şubat',
+    'Mart',
+    'Nisan',
+    'Mayıs',
+    'Haziran',
+    'Temmuz',
+    'Ağustos',
+    'Eylül',
+    'Ekim',
+    'Kasım',
+    'Aralık'
+  ],
+      STANDALONEMONTHS: const [
+    'Ocak',
+    'Şubat',
+    'Mart',
+    'Nisan',
+    'Mayıs',
+    'Haziran',
+    'Temmuz',
+    'Ağustos',
+    'Eylül',
+    'Ekim',
+    'Kasım',
+    'Aralık'
+  ],
+      SHORTMONTHS: const [
+    'Oca',
+    'Şub',
+    'Mar',
+    'Nis',
+    'May',
+    'Haz',
+    'Tem',
+    'Ağu',
+    'Eyl',
+    'Eki',
+    'Kas',
+    'Ara'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Oca',
+    'Şub',
+    'Mar',
+    'Nis',
+    'May',
+    'Haz',
+    'Tem',
+    'Ağu',
+    'Eyl',
+    'Eki',
+    'Kas',
+    'Ara'
+  ],
+      WEEKDAYS: const [
+    'Pazar',
+    'Pazartesi',
+    'Salı',
+    'Çarşamba',
+    'Perşembe',
+    'Cuma',
+    'Cumartesi'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Pazar',
+    'Pazartesi',
+    'Salı',
+    'Çarşamba',
+    'Perşembe',
+    'Cuma',
+    'Cumartesi'
+  ],
+      SHORTWEEKDAYS: const ['Paz', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Paz',
+    'Pzt',
+    'Sal',
+    'Çar',
+    'Per',
+    'Cum',
+    'Cmt'
+  ],
+      NARROWWEEKDAYS: const ['P', 'P', 'S', 'Ç', 'P', 'C', 'C'],
+      STANDALONENARROWWEEKDAYS: const ['P', 'P', 'S', 'Ç', 'P', 'C', 'C'],
+      SHORTQUARTERS: const ['Ç1', 'Ç2', 'Ç3', 'Ç4'],
+      QUARTERS: const ['1. çeyrek', '2. çeyrek', '3. çeyrek', '4. çeyrek'],
+      AMPMS: const ['ÖÖ', 'ÖS'],
+      DATEFORMATS: const ['d MMMM y EEEE', 'd MMMM y', 'd MMM y', 'd MM y'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale uk.
+   */
+  "uk": new DateSymbols(
+      NAME: "uk",
+      ERAS: const ['до н.е.', 'н.е.'],
+      ERANAMES: const ['до нашої ери', 'нашої ери'],
+      NARROWMONTHS: const [
+    'С',
+    'Л',
+    'Б',
+    'К',
+    'Т',
+    'Ч',
+    'Л',
+    'С',
+    'В',
+    'Ж',
+    'Л',
+    'Г'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'С',
+    'Л',
+    'Б',
+    'К',
+    'Т',
+    'Ч',
+    'Л',
+    'С',
+    'В',
+    'Ж',
+    'Л',
+    'Г'
+  ],
+      MONTHS: const [
+    'січня',
+    'лютого',
+    'березня',
+    'квітня',
+    'травня',
+    'червня',
+    'липня',
+    'серпня',
+    'вересня',
+    'жовтня',
+    'листопада',
+    'грудня'
+  ],
+      STANDALONEMONTHS: const [
+    'Січень',
+    'Лютий',
+    'Березень',
+    'Квітень',
+    'Травень',
+    'Червень',
+    'Липень',
+    'Серпень',
+    'Вересень',
+    'Жовтень',
+    'Листопад',
+    'Грудень'
+  ],
+      SHORTMONTHS: const [
+    'січ.',
+    'лют.',
+    'бер.',
+    'квіт.',
+    'трав.',
+    'черв.',
+    'лип.',
+    'серп.',
+    'вер.',
+    'жовт.',
+    'лист.',
+    'груд.'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Січ',
+    'Лют',
+    'Бер',
+    'Кві',
+    'Тра',
+    'Чер',
+    'Лип',
+    'Сер',
+    'Вер',
+    'Жов',
+    'Лис',
+    'Гру'
+  ],
+      WEEKDAYS: const [
+    'неділя',
+    'понеділок',
+    'вівторок',
+    'середа',
+    'четвер',
+    'пʼятниця',
+    'субота'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Неділя',
+    'Понеділок',
+    'Вівторок',
+    'Середа',
+    'Четвер',
+    'Пʼятниця',
+    'Субота'
+  ],
+      SHORTWEEKDAYS: const ['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
+      STANDALONESHORTWEEKDAYS: const ['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
+      NARROWWEEKDAYS: const ['Н', 'П', 'В', 'С', 'Ч', 'П', 'С'],
+      STANDALONENARROWWEEKDAYS: const ['Н', 'П', 'В', 'С', 'Ч', 'П', 'С'],
+      SHORTQUARTERS: const ['I кв.', 'II кв.', 'III кв.', 'IV кв.'],
+      QUARTERS: const ['I квартал', 'II квартал', 'III квартал', 'IV квартал'],
+      AMPMS: const ['дп', 'пп'],
+      DATEFORMATS: const [
+    'EEEE, d MMMM y \'р\'.',
+    'd MMMM y \'р\'.',
+    'd MMM y',
+    'dd.MM.yy'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale ur.
+   */
+  "ur": new DateSymbols(
+      NAME: "ur",
+      ERAS: const ['ق م', 'عیسوی سن'],
+      ERANAMES: const ['قبل مسیح', 'عیسوی سن'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'جنوری',
+    'فروری',
+    'مارچ',
+    'اپریل',
+    'مئی',
+    'جون',
+    'جولائی',
+    'اگست',
+    'ستمبر',
+    'اکتوبر',
+    'نومبر',
+    'دسمبر'
+  ],
+      STANDALONEMONTHS: const [
+    'جنوری',
+    'فروری',
+    'مارچ',
+    'اپریل',
+    'مئی',
+    'جون',
+    'جولائی',
+    'اگست',
+    'ستمبر',
+    'اکتوبر',
+    'نومبر',
+    'دسمبر'
+  ],
+      SHORTMONTHS: const [
+    'جنوری',
+    'فروری',
+    'مارچ',
+    'اپریل',
+    'مئی',
+    'جون',
+    'جولائی',
+    'اگست',
+    'ستمبر',
+    'اکتوبر',
+    'نومبر',
+    'دسمبر'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'جنوری',
+    'فروری',
+    'مارچ',
+    'اپریل',
+    'مئی',
+    'جون',
+    'جولائی',
+    'اگست',
+    'ستمبر',
+    'اکتوبر',
+    'نومبر',
+    'دسمبر'
+  ],
+      WEEKDAYS: const [
+    'اتوار',
+    'سوموار',
+    'منگل',
+    'بدھ',
+    'جمعرات',
+    'جمعہ',
+    'ہفتہ'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'اتوار',
+    'سوموار',
+    'منگل',
+    'بدھ',
+    'جمعرات',
+    'جمعہ',
+    'ہفتہ'
+  ],
+      SHORTWEEKDAYS: const [
+    'اتوار',
+    'سوموار',
+    'منگل',
+    'بدھ',
+    'جمعرات',
+    'جمعہ',
+    'ہفتہ'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'اتوار',
+    'سوموار',
+    'منگل',
+    'بدھ',
+    'جمعرات',
+    'جمعہ',
+    'ہفتہ'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+      SHORTQUARTERS: const [
+    'پہلی سہ ماہی',
+    'دوسری سہ ماہی',
+    'تیسری سہ ماہی',
+    'چوتهی سہ ماہی'
+  ],
+      QUARTERS: const [
+    'پہلی سہ ماہی',
+    'دوسری سہ ماہی',
+    'تیسری سہ ماہی',
+    'چوتهی سہ ماہی'
+  ],
+      AMPMS: const ['قبل دوپہر', 'بعد دوپہر'],
+      DATEFORMATS: const ['EEEE، d MMMM، y', 'd MMMM، y', 'd MMM، y', 'd/M/yy'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale uz.
+   */
+  "uz": new DateSymbols(
+      NAME: "uz",
+      ERAS: const ['M.A.', 'E'],
+      ERANAMES: const ['M.A.', 'E'],
+      NARROWMONTHS: const [
+    'Y',
+    'F',
+    'M',
+    'A',
+    'M',
+    'I',
+    'I',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'Y',
+    'F',
+    'M',
+    'A',
+    'M',
+    'I',
+    'I',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Yanvar',
+    'Fevral',
+    'Mart',
+    'Aprel',
+    'May',
+    'Iyun',
+    'Iyul',
+    'Avgust',
+    'Sentyabr',
+    'Oktyabr',
+    'Noyabr',
+    'Dekabr'
+  ],
+      STANDALONEMONTHS: const [
+    'Yanvar',
+    'Fevral',
+    'Mart',
+    'Aprel',
+    'May',
+    'Iyun',
+    'Iyul',
+    'Avgust',
+    'Sentyabr',
+    'Oktyabr',
+    'Noyabr',
+    'Dekabr'
+  ],
+      SHORTMONTHS: const [
+    'Yanv',
+    'Fev',
+    'Mar',
+    'Apr',
+    'May',
+    'Iyun',
+    'Iyul',
+    'Avg',
+    'Sen',
+    'Okt',
+    'Noya',
+    'Dek'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Yanv',
+    'Fev',
+    'Mar',
+    'Apr',
+    'May',
+    'Iyun',
+    'Iyul',
+    'Avg',
+    'Sen',
+    'Okt',
+    'Noya',
+    'Dek'
+  ],
+      WEEKDAYS: const [
+    'yakshanba',
+    'dushanba',
+    'seshanba',
+    'chorshanba',
+    'payshanba',
+    'juma',
+    'shanba'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'yakshanba',
+    'dushanba',
+    'seshanba',
+    'chorshanba',
+    'payshanba',
+    'juma',
+    'shanba'
+  ],
+      SHORTWEEKDAYS: const [
+    'Yaksh',
+    'Dush',
+    'Sesh',
+    'Chor',
+    'Pay',
+    'Jum',
+    'Shan'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'Yaksh',
+    'Dush',
+    'Sesh',
+    'Chor',
+    'Pay',
+    'Jum',
+    'Shan'
+  ],
+      NARROWWEEKDAYS: const ['Y', 'D', 'S', 'C', 'P', 'J', 'S'],
+      STANDALONENARROWWEEKDAYS: const ['Y', 'D', 'S', 'C', 'P', 'J', 'S'],
+      SHORTQUARTERS: const ['1-ch', '2-ch', '3-ch', '4-ch'],
+      QUARTERS: const ['1-chorak', '2-chorak', '3-chorak', '4-chorak'],
+      AMPMS: const ['AM', 'PM'],
+      DATEFORMATS: const ['EEEE, y MMMM dd', 'y MMMM d', 'y MMM d', 'yy/MM/dd'],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale vi.
+   */
+  "vi": new DateSymbols(
+      NAME: "vi",
+      ERAS: const ['tr. CN', 'sau CN'],
+      ERANAMES: const ['tr. CN', 'sau CN'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    'tháng 1',
+    'tháng 2',
+    'tháng 3',
+    'tháng 4',
+    'tháng 5',
+    'tháng 6',
+    'tháng 7',
+    'tháng 8',
+    'tháng 9',
+    'tháng 10',
+    'tháng 11',
+    'tháng 12'
+  ],
+      STANDALONEMONTHS: const [
+    'Tháng 1',
+    'Tháng 2',
+    'Tháng 3',
+    'Tháng 4',
+    'Tháng 5',
+    'Tháng 6',
+    'Tháng 7',
+    'Tháng 8',
+    'Tháng 9',
+    'Tháng 10',
+    'Tháng 11',
+    'Tháng 12'
+  ],
+      SHORTMONTHS: const [
+    'thg 1',
+    'thg 2',
+    'thg 3',
+    'thg 4',
+    'thg 5',
+    'thg 6',
+    'thg 7',
+    'thg 8',
+    'thg 9',
+    'thg 10',
+    'thg 11',
+    'thg 12'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Thg 1',
+    'Thg 2',
+    'Thg 3',
+    'Thg 4',
+    'Thg 5',
+    'Thg 6',
+    'Thg 7',
+    'Thg 8',
+    'Thg 9',
+    'Thg 10',
+    'Thg 11',
+    'Thg 12'
+  ],
+      WEEKDAYS: const [
+    'Chủ Nhật',
+    'Thứ Hai',
+    'Thứ Ba',
+    'Thứ Tư',
+    'Thứ Năm',
+    'Thứ Sáu',
+    'Thứ Bảy'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Chủ Nhật',
+    'Thứ Hai',
+    'Thứ Ba',
+    'Thứ Tư',
+    'Thứ Năm',
+    'Thứ Sáu',
+    'Thứ Bảy'
+  ],
+      SHORTWEEKDAYS: const [
+    'CN',
+    'Th 2',
+    'Th 3',
+    'Th 4',
+    'Th 5',
+    'Th 6',
+    'Th 7'
+  ],
+      STANDALONESHORTWEEKDAYS: const [
+    'CN',
+    'Th 2',
+    'Th 3',
+    'Th 4',
+    'Th 5',
+    'Th 6',
+    'Th 7'
+  ],
+      NARROWWEEKDAYS: const ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'],
+      STANDALONENARROWWEEKDAYS: const [
+    'CN',
+    'T2',
+    'T3',
+    'T4',
+    'T5',
+    'T6',
+    'T7'
+  ],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const ['Quý 1', 'Quý 2', 'Quý 3', 'Quý 4'],
+      AMPMS: const ['SA', 'CH'],
+      DATEFORMATS: const [
+    'EEEE, \'ngày\' dd MMMM \'năm\' y',
+    '\'Ngày\' dd \'tháng\' MM \'năm\' y',
+    'dd-MM-y',
+    'dd/MM/y'
+  ],
+      TIMEFORMATS: const ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
+      DATETIMEFORMATS: const ['{0} {1}', '{0} {1}', '{0} {1}', '{0} {1}'],
+      FIRSTDAYOFWEEK: 0,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 6),
+  /**
+   * Date/time formatting symbols for locale zh.
+   */
+  "zh": new DateSymbols(
+      NAME: "zh",
+      ERAS: const ['公元前', '公元'],
+      ERANAMES: const ['公元前', '公元'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    '一月',
+    '二月',
+    '三月',
+    '四月',
+    '五月',
+    '六月',
+    '七月',
+    '八月',
+    '九月',
+    '十月',
+    '十一月',
+    '十二月'
+  ],
+      STANDALONEMONTHS: const [
+    '一月',
+    '二月',
+    '三月',
+    '四月',
+    '五月',
+    '六月',
+    '七月',
+    '八月',
+    '九月',
+    '十月',
+    '十一月',
+    '十二月'
+  ],
+      SHORTMONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      STANDALONESHORTMONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      WEEKDAYS: const ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
+      STANDALONEWEEKDAYS: const [
+    '星期日',
+    '星期一',
+    '星期二',
+    '星期三',
+    '星期四',
+    '星期五',
+    '星期六'
+  ],
+      SHORTWEEKDAYS: const ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
+      STANDALONESHORTWEEKDAYS: const ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
+      NARROWWEEKDAYS: const ['日', '一', '二', '三', '四', '五', '六'],
+      STANDALONENARROWWEEKDAYS: const ['日', '一', '二', '三', '四', '五', '六'],
+      SHORTQUARTERS: const ['1季度', '2季度', '3季度', '4季度'],
+      QUARTERS: const ['第一季度', '第二季度', '第三季度', '第四季度'],
+      AMPMS: const ['上午', '下午'],
+      DATEFORMATS: const ['y年M月d日EEEE', 'y年M月d日', 'y年M月d日', 'yy/M/d'],
+      TIMEFORMATS: const ['zzzzah:mm:ss', 'zah:mm:ss', 'ah:mm:ss', 'ah:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale zh_CN.
+   */
+  /**
+   * Date/time formatting symbols for locale zh_CN.
+   */
+  "zh_CN": new DateSymbols(
+      NAME: "zh_CN",
+      ERAS: const ['公元前', '公元'],
+      ERANAMES: const ['公元前', '公元'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    '一月',
+    '二月',
+    '三月',
+    '四月',
+    '五月',
+    '六月',
+    '七月',
+    '八月',
+    '九月',
+    '十月',
+    '十一月',
+    '十二月'
+  ],
+      STANDALONEMONTHS: const [
+    '一月',
+    '二月',
+    '三月',
+    '四月',
+    '五月',
+    '六月',
+    '七月',
+    '八月',
+    '九月',
+    '十月',
+    '十一月',
+    '十二月'
+  ],
+      SHORTMONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      STANDALONESHORTMONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      WEEKDAYS: const ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
+      STANDALONEWEEKDAYS: const [
+    '星期日',
+    '星期一',
+    '星期二',
+    '星期三',
+    '星期四',
+    '星期五',
+    '星期六'
+  ],
+      SHORTWEEKDAYS: const ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
+      STANDALONESHORTWEEKDAYS: const ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
+      NARROWWEEKDAYS: const ['日', '一', '二', '三', '四', '五', '六'],
+      STANDALONENARROWWEEKDAYS: const ['日', '一', '二', '三', '四', '五', '六'],
+      SHORTQUARTERS: const ['1季度', '2季度', '3季度', '4季度'],
+      QUARTERS: const ['第一季度', '第二季度', '第三季度', '第四季度'],
+      AMPMS: const ['上午', '下午'],
+      DATEFORMATS: const ['y年M月d日EEEE', 'y年M月d日', 'y年M月d日', 'yy/M/d'],
+      TIMEFORMATS: const ['zzzzah:mm:ss', 'zah:mm:ss', 'ah:mm:ss', 'ah:mm'],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale zh_HK.
+   */
+  "zh_HK": new DateSymbols(
+      NAME: "zh_HK",
+      ERAS: const ['西元前', '西元'],
+      ERANAMES: const ['西元前', '西元'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      STANDALONEMONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      SHORTMONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      STANDALONESHORTMONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      WEEKDAYS: const ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
+      STANDALONEWEEKDAYS: const [
+    '星期日',
+    '星期一',
+    '星期二',
+    '星期三',
+    '星期四',
+    '星期五',
+    '星期六'
+  ],
+      SHORTWEEKDAYS: const ['週日', '週一', '週二', '週三', '週四', '週五', '週六'],
+      STANDALONESHORTWEEKDAYS: const ['週日', '週一', '週二', '週三', '週四', '週五', '週六'],
+      NARROWWEEKDAYS: const ['日', '一', '二', '三', '四', '五', '六'],
+      STANDALONENARROWWEEKDAYS: const ['日', '一', '二', '三', '四', '五', '六'],
+      SHORTQUARTERS: const ['1季', '2季', '3季', '4季'],
+      QUARTERS: const ['第1季', '第2季', '第3季', '第4季'],
+      AMPMS: const ['上午', '下午'],
+      DATEFORMATS: const ['y年M月d日EEEE', 'y年M月d日', 'y年M月d日', 'd/M/yy'],
+      TIMEFORMATS: const [
+    'ah:mm:ss [zzzz]',
+    'ah:mm:ss [z]',
+    'ah:mm:ss',
+    'ah:mm'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1}{0}', '{1}{0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale zh_TW.
+   */
+  "zh_TW": new DateSymbols(
+      NAME: "zh_TW",
+      ERAS: const ['西元前', '西元'],
+      ERANAMES: const ['西元前', '西元'],
+      NARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      STANDALONENARROWMONTHS: const [
+    '1',
+    '2',
+    '3',
+    '4',
+    '5',
+    '6',
+    '7',
+    '8',
+    '9',
+    '10',
+    '11',
+    '12'
+  ],
+      MONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      STANDALONEMONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      SHORTMONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      STANDALONESHORTMONTHS: const [
+    '1月',
+    '2月',
+    '3月',
+    '4月',
+    '5月',
+    '6月',
+    '7月',
+    '8月',
+    '9月',
+    '10月',
+    '11月',
+    '12月'
+  ],
+      WEEKDAYS: const ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
+      STANDALONEWEEKDAYS: const [
+    '星期日',
+    '星期一',
+    '星期二',
+    '星期三',
+    '星期四',
+    '星期五',
+    '星期六'
+  ],
+      SHORTWEEKDAYS: const ['週日', '週一', '週二', '週三', '週四', '週五', '週六'],
+      STANDALONESHORTWEEKDAYS: const ['週日', '週一', '週二', '週三', '週四', '週五', '週六'],
+      NARROWWEEKDAYS: const ['日', '一', '二', '三', '四', '五', '六'],
+      STANDALONENARROWWEEKDAYS: const ['日', '一', '二', '三', '四', '五', '六'],
+      SHORTQUARTERS: const ['1季', '2季', '3季', '4季'],
+      QUARTERS: const ['第1季', '第2季', '第3季', '第4季'],
+      AMPMS: const ['上午', '下午'],
+      DATEFORMATS: const ['y年M月d日EEEE', 'y年M月d日', 'y年M月d日', 'y/M/d'],
+      TIMEFORMATS: const ['zzzzah時mm分ss秒', 'zah時mm分ss秒', 'ah:mm:ss', 'ah:mm'],
+      DATETIMEFORMATS: const ['{1}{0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5),
+  /**
+   * Date/time formatting symbols for locale zu.
+   */
+  "zu": new DateSymbols(
+      NAME: "zu",
+      ERAS: const ['BC', 'AD'],
+      ERANAMES: const ['BC', 'AD'],
+      NARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      STANDALONENARROWMONTHS: const [
+    'J',
+    'F',
+    'M',
+    'A',
+    'M',
+    'J',
+    'J',
+    'A',
+    'S',
+    'O',
+    'N',
+    'D'
+  ],
+      MONTHS: const [
+    'Januwari',
+    'Februwari',
+    'Mashi',
+    'Apreli',
+    'Meyi',
+    'Juni',
+    'Julayi',
+    'Agasti',
+    'Septhemba',
+    'Okthoba',
+    'Novemba',
+    'Disemba'
+  ],
+      STANDALONEMONTHS: const [
+    'uJanuwari',
+    'uFebruwari',
+    'uMashi',
+    'u-Apreli',
+    'uMeyi',
+    'uJuni',
+    'uJulayi',
+    'uAgasti',
+    'uSepthemba',
+    'u-Okthoba',
+    'uNovemba',
+    'uDisemba'
+  ],
+      SHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mas',
+    'Apr',
+    'Mey',
+    'Jun',
+    'Jul',
+    'Aga',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Dis'
+  ],
+      STANDALONESHORTMONTHS: const [
+    'Jan',
+    'Feb',
+    'Mas',
+    'Apr',
+    'Mey',
+    'Jun',
+    'Jul',
+    'Aga',
+    'Sep',
+    'Okt',
+    'Nov',
+    'Dis'
+  ],
+      WEEKDAYS: const [
+    'Sonto',
+    'Msombuluko',
+    'Lwesibili',
+    'Lwesithathu',
+    'Lwesine',
+    'Lwesihlanu',
+    'Mgqibelo'
+  ],
+      STANDALONEWEEKDAYS: const [
+    'Sonto',
+    'Msombuluko',
+    'Lwesibili',
+    'Lwesithathu',
+    'Lwesine',
+    'Lwesihlanu',
+    'Mgqibelo'
+  ],
+      SHORTWEEKDAYS: const ['Son', 'Mso', 'Bil', 'Tha', 'Sin', 'Hla', 'Mgq'],
+      STANDALONESHORTWEEKDAYS: const [
+    'Son',
+    'Mso',
+    'Bil',
+    'Tha',
+    'Sin',
+    'Hla',
+    'Mgq'
+  ],
+      NARROWWEEKDAYS: const ['S', 'M', 'T', 'T', 'S', 'H', 'M'],
+      STANDALONENARROWWEEKDAYS: const ['S', 'M', 'B', 'T', 'S', 'H', 'M'],
+      SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+      QUARTERS: const [
+    'ikota engu-1',
+    'ikota engu-2',
+    'ikota engu-3',
+    'ikota engu-4'
+  ],
+      AMPMS: const ['Ekuseni', 'Ntambama'],
+      DATEFORMATS: const ['EEEE dd MMMM y', 'd MMMM y', 'd MMM y', 'y-MM-dd'],
+      TIMEFORMATS: const [
+    'h:mm:ss a zzzz',
+    'h:mm:ss a z',
+    'h:mm:ss a',
+    'h:mm a'
+  ],
+      DATETIMEFORMATS: const ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
+      FIRSTDAYOFWEEK: 6,
+      WEEKENDRANGE: const [5, 6],
+      FIRSTWEEKCUTOFFDAY: 5)
+};
diff --git a/intl/lib/date_symbols.dart b/intl/lib/date_symbols.dart
new file mode 100644
index 0000000..bbded50
--- /dev/null
+++ b/intl/lib/date_symbols.dart
@@ -0,0 +1,304 @@
+// Copyright (c) 2012, 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 date_symbols;
+
+/**
+ * This holds onto information about how a particular locale formats dates. It
+ * contains mostly strings, e.g. what the names of months or weekdays are,
+ * but also indicates things like the first day of the week. We expect the data
+ * for instances of these to be generated out of ICU or a similar reference
+ * source. This is used in conjunction with the date_time_patterns, which
+ * defines for a particular locale the different named formats that will
+ * make use of this data.
+ */
+class DateSymbols {
+  String NAME;
+  List<String> ERAS,
+      ERANAMES,
+      NARROWMONTHS,
+      STANDALONENARROWMONTHS,
+      MONTHS,
+      STANDALONEMONTHS,
+      SHORTMONTHS,
+      STANDALONESHORTMONTHS,
+      WEEKDAYS,
+      STANDALONEWEEKDAYS,
+      SHORTWEEKDAYS,
+      STANDALONESHORTWEEKDAYS,
+      NARROWWEEKDAYS,
+      STANDALONENARROWWEEKDAYS,
+      SHORTQUARTERS,
+      QUARTERS,
+      AMPMS,
+      DATEFORMATS,
+      TIMEFORMATS,
+      DATETIMEFORMATS;
+  Map<String, String> AVAILABLEFORMATS;
+  int FIRSTDAYOFWEEK;
+  List<int> WEEKENDRANGE;
+  int FIRSTWEEKCUTOFFDAY;
+
+  DateSymbols({this.NAME, this.ERAS, this.ERANAMES, this.NARROWMONTHS,
+      this.STANDALONENARROWMONTHS, this.MONTHS, this.STANDALONEMONTHS,
+      this.SHORTMONTHS, this.STANDALONESHORTMONTHS, this.WEEKDAYS,
+      this.STANDALONEWEEKDAYS, this.SHORTWEEKDAYS, this.STANDALONESHORTWEEKDAYS,
+      this.NARROWWEEKDAYS, this.STANDALONENARROWWEEKDAYS, this.SHORTQUARTERS,
+      this.QUARTERS, this.AMPMS,
+      // TODO(alanknight): These formats are taken from Closure,
+      // where there's only a fixed set of available formats.
+      // Here we have the patterns separately. These should
+      // either be used, or removed.
+      this.DATEFORMATS, this.TIMEFORMATS, this.AVAILABLEFORMATS,
+      this.FIRSTDAYOFWEEK, this.WEEKENDRANGE, this.FIRSTWEEKCUTOFFDAY,
+      this.DATETIMEFORMATS});
+
+  // TODO(alanknight): Replace this with use of a more general serialization
+  // facility once one is available. Issue 4926.
+  DateSymbols.deserializeFromMap(Map map) {
+    NAME = map["NAME"];
+    ERAS = map["ERAS"];
+    ERANAMES = map["ERANAMES"];
+    NARROWMONTHS = map["NARROWMONTHS"];
+    STANDALONENARROWMONTHS = map["STANDALONENARROWMONTHS"];
+    MONTHS = map["MONTHS"];
+    STANDALONEMONTHS = map["STANDALONEMONTHS"];
+    SHORTMONTHS = map["SHORTMONTHS"];
+    STANDALONESHORTMONTHS = map["STANDALONESHORTMONTHS"];
+    WEEKDAYS = map["WEEKDAYS"];
+    STANDALONEWEEKDAYS = map["STANDALONEWEEKDAYS"];
+    SHORTWEEKDAYS = map["SHORTWEEKDAYS"];
+    STANDALONESHORTWEEKDAYS = map["STANDALONESHORTWEEKDAYS"];
+    NARROWWEEKDAYS = map["NARROWWEEKDAYS"];
+    STANDALONENARROWWEEKDAYS = map["STANDALONENARROWWEEKDAYS"];
+    SHORTQUARTERS = map["SHORTQUARTERS"];
+    QUARTERS = map["QUARTERS"];
+    AMPMS = map["AMPMS"];
+    DATEFORMATS = map["DATEFORMATS"];
+    TIMEFORMATS = map["TIMEFORMATS"];
+    AVAILABLEFORMATS = map["AVAILABLEFORMATS"];
+    FIRSTDAYOFWEEK = map["FIRSTDAYOFWEEK"];
+    WEEKENDRANGE = map["WEEKENDRANGE"];
+    FIRSTWEEKCUTOFFDAY = map["FIRSTWEEKCUTOFFDAY"];
+    DATETIMEFORMATS = map["DATETIMEFORAMTS"];
+  }
+
+  Map serializeToMap() => {
+    "NAME": NAME,
+    "ERAS": ERAS,
+    "ERANAMES": ERANAMES,
+    "NARROWMONTHS": NARROWMONTHS,
+    "STANDALONENARROWMONTHS": STANDALONENARROWMONTHS,
+    "MONTHS": MONTHS,
+    "STANDALONEMONTHS": STANDALONEMONTHS,
+    "SHORTMONTHS": SHORTMONTHS,
+    "STANDALONESHORTMONTHS": STANDALONESHORTMONTHS,
+    "WEEKDAYS": WEEKDAYS,
+    "STANDALONEWEEKDAYS": STANDALONEWEEKDAYS,
+    "SHORTWEEKDAYS": SHORTWEEKDAYS,
+    "STANDALONESHORTWEEKDAYS": STANDALONESHORTWEEKDAYS,
+    "NARROWWEEKDAYS": NARROWWEEKDAYS,
+    "STANDALONENARROWWEEKDAYS": STANDALONENARROWWEEKDAYS,
+    "SHORTQUARTERS": SHORTQUARTERS,
+    "QUARTERS": QUARTERS,
+    "AMPMS": AMPMS,
+    "DATEFORMATS": DATEFORMATS,
+    "TIMEFORMATS": TIMEFORMATS,
+    "AVAILABLEFORMATS": AVAILABLEFORMATS,
+    "FIRSTDAYOFWEEK": FIRSTDAYOFWEEK,
+    "WEEKENDRANGE": WEEKENDRANGE,
+    "FIRSTWEEKCUTOFFDAY": FIRSTWEEKCUTOFFDAY,
+    "DATETIMEFORMATS": DATETIMEFORMATS,
+  };
+
+  toString() => NAME;
+}
+
+/**
+ * We hard-code the locale data for en_US here so that there's at least one
+ * locale always available.
+ */
+var en_USSymbols = new DateSymbols(
+    NAME: "en_US",
+    ERAS: const ['BC', 'AD'],
+    ERANAMES: const ['Before Christ', 'Anno Domini'],
+    NARROWMONTHS: const [
+  'J',
+  'F',
+  'M',
+  'A',
+  'M',
+  'J',
+  'J',
+  'A',
+  'S',
+  'O',
+  'N',
+  'D'
+],
+    STANDALONENARROWMONTHS: const [
+  'J',
+  'F',
+  'M',
+  'A',
+  'M',
+  'J',
+  'J',
+  'A',
+  'S',
+  'O',
+  'N',
+  'D'
+],
+    MONTHS: const [
+  'January',
+  'February',
+  'March',
+  'April',
+  'May',
+  'June',
+  'July',
+  'August',
+  'September',
+  'October',
+  'November',
+  'December'
+],
+    STANDALONEMONTHS: const [
+  'January',
+  'February',
+  'March',
+  'April',
+  'May',
+  'June',
+  'July',
+  'August',
+  'September',
+  'October',
+  'November',
+  'December'
+],
+    SHORTMONTHS: const [
+  'Jan',
+  'Feb',
+  'Mar',
+  'Apr',
+  'May',
+  'Jun',
+  'Jul',
+  'Aug',
+  'Sep',
+  'Oct',
+  'Nov',
+  'Dec'
+],
+    STANDALONESHORTMONTHS: const [
+  'Jan',
+  'Feb',
+  'Mar',
+  'Apr',
+  'May',
+  'Jun',
+  'Jul',
+  'Aug',
+  'Sep',
+  'Oct',
+  'Nov',
+  'Dec'
+],
+    WEEKDAYS: const [
+  'Sunday',
+  'Monday',
+  'Tuesday',
+  'Wednesday',
+  'Thursday',
+  'Friday',
+  'Saturday'
+],
+    STANDALONEWEEKDAYS: const [
+  'Sunday',
+  'Monday',
+  'Tuesday',
+  'Wednesday',
+  'Thursday',
+  'Friday',
+  'Saturday'
+],
+    SHORTWEEKDAYS: const ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+    STANDALONESHORTWEEKDAYS: const [
+  'Sun',
+  'Mon',
+  'Tue',
+  'Wed',
+  'Thu',
+  'Fri',
+  'Sat'
+],
+    NARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+    STANDALONENARROWWEEKDAYS: const ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+    SHORTQUARTERS: const ['Q1', 'Q2', 'Q3', 'Q4'],
+    QUARTERS: const [
+  '1st quarter',
+  '2nd quarter',
+  '3rd quarter',
+  '4th quarter'
+],
+    AMPMS: const ['AM', 'PM'],
+    DATEFORMATS: const ['EEEE, MMMM d, y', 'MMMM d, y', 'MMM d, y', 'M/d/yy'],
+    TIMEFORMATS: const ['h:mm:ss a zzzz', 'h:mm:ss a z', 'h:mm:ss a', 'h:mm a'],
+    FIRSTDAYOFWEEK: 6,
+    WEEKENDRANGE: const [5, 6],
+    FIRSTWEEKCUTOFFDAY: 5,
+    DATETIMEFORMATS: const [
+  '{1} \'at\' {0}',
+  '{1} \'at\' {0}',
+  '{1}, {0}',
+  '{1}, {0}'
+]);
+
+var en_USPatterns = const {
+  'd': 'd', // DAY
+  'E': 'EEE', // ABBR_WEEKDAY
+  'EEEE': 'EEEE', // WEEKDAY
+  'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+  'LLLL': 'LLLL', // STANDALONE_MONTH
+  'M': 'L', // NUM_MONTH
+  'Md': 'M/d', // NUM_MONTH_DAY
+  'MEd': 'EEE, M/d', // NUM_MONTH_WEEKDAY_DAY
+  'MMM': 'LLL', // ABBR_MONTH
+  'MMMd': 'MMM d', // ABBR_MONTH_DAY
+  'MMMEd': 'EEE, MMM d', // ABBR_MONTH_WEEKDAY_DAY
+  'MMMM': 'LLLL', // MONTH
+  'MMMMd': 'MMMM d', // MONTH_DAY
+  'MMMMEEEEd': 'EEEE, MMMM d', // MONTH_WEEKDAY_DAY
+  'QQQ': 'QQQ', // ABBR_QUARTER
+  'QQQQ': 'QQQQ', // QUARTER
+  'y': 'y', // YEAR
+  'yM': 'M/y', // YEAR_NUM_MONTH
+  'yMd': 'M/d/y', // YEAR_NUM_MONTH_DAY
+  'yMEd': 'EEE, M/d/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+  'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+  'yMMMd': 'MMM d, y', // YEAR_ABBR_MONTH_DAY
+  'yMMMEd': 'EEE, MMM d, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+  'yMMMM': 'MMMM y', // YEAR_MONTH
+  'yMMMMd': 'MMMM d, y', // YEAR_MONTH_DAY
+  'yMMMMEEEEd': 'EEEE, MMMM d, y', // YEAR_MONTH_WEEKDAY_DAY
+  'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+  'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+  'H': 'HH', // HOUR24
+  'Hm': 'HH:mm', // HOUR24_MINUTE
+  'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+  'j': 'h a', // HOUR
+  'jm': 'h:mm a', // HOUR_MINUTE
+  'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+  'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+  'jmz': 'h:mm a z', // HOUR_MINUTETZ
+  'jz': 'h a z', // HOURGENERIC_TZ
+  'm': 'm', // MINUTE
+  'ms': 'mm:ss', // MINUTE_SECOND
+  's': 's', // SECOND
+  'v': 'v', // ABBR_GENERIC_TZ
+  'z': 'z', // ABBR_SPECIFIC_TZ
+  'zzzz': 'zzzz', // SPECIFIC_TZ
+  'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+};
diff --git a/intl/lib/date_time_patterns.dart b/intl/lib/date_time_patterns.dart
new file mode 100644
index 0000000..e43c477
--- /dev/null
+++ b/intl/lib/date_time_patterns.dart
@@ -0,0 +1,5171 @@
+// 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.
+
+/**
+ * Date/time formatting symbols for a large subset of locales.
+ *
+ * DO NOT EDIT. This file is autogenerated from ICU data. See
+ * 'http://go/generate_datetime_pattern_dart.cc' (Google internal)
+ * File generated from CLDR ver. 25.0
+ */
+
+library date_time_patterns;
+
+/**
+ * Returns a Map from locale names to another Map that goes from skeletons
+ * to the locale-specific formatting patterns.
+ * Internal use only. Call initializeDateFormatting instead.
+ **/
+Map dateTimePatternMap() => const {
+  /**
+   * Extended set of localized date/time patterns for locale af.
+   */
+  'af': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'EEE, M/d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, MMM d', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, MMMM d', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'M/d/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale am.
+   */
+  'am': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'EEE, M/d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, MMM d', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, MMMM d', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE፣ d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE፣ MMM d y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale ar.
+   */
+  'ar': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/‏M', // NUM_MONTH_DAY
+    'MEd': 'EEE، d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE، d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE، d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M‏/y', // YEAR_NUM_MONTH
+    'yMd': 'd‏/M‏/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE، d/‏M/‏y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM، y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE، d MMM، y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM، y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE، d MMMM، y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale az.
+   */
+  'az': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'dd.MM', // NUM_MONTH_DAY
+    'MEd': 'dd.MM, EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'd MMM, EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'd MMMM, EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM.y', // YEAR_NUM_MONTH
+    'yMd': 'dd.MM.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'dd.MM.y, EEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'd MMM y, EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'd MMMM y, EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale bg.
+   */
+  'bg': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd.MM', // NUM_MONTH_DAY
+    'MEd': 'EEE, d.MM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'MM', // ABBR_MONTH
+    'MMMd': 'd.MM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d.MM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y \'г\'.', // YEAR
+    'yM': 'M.y \'г\'.', // YEAR_NUM_MONTH
+    'yMd': 'd.MM.y \'г\'.', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d.MM.y \'г\'.', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MM.y \'г\'.', // YEAR_ABBR_MONTH
+    'yMMMd': 'd.MM.y \'г\'.', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d.MM.y \'г\'.', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y \'г\'.', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y \'г\'.', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y \'г\'.', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y \'г\'.', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y \'г\'.', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'H', // HOUR
+    'jm': 'H:mm', // HOUR_MINUTE
+    'jms': 'H:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'H:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'H:mm z', // HOUR_MINUTETZ
+    'jz': 'H z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale bn.
+   */
+  'bn': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d-M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM, y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM, y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale br.
+   */
+  'br': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'MM-dd', // NUM_MONTH_DAY
+    'MEd': 'MM-dd, EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'MMM d, EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'MMMM d, EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'y-MM', // YEAR_NUM_MONTH
+    'yMd': 'y-MM-dd', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y-MM-dd, EEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'y MMM d', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y MMM d, EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y MMMM', // YEAR_MONTH
+    'yMMMMd': 'y MMMM d', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'y MMMM d, EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale ca.
+   */
+  'ca': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'LLL y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'LLLL \'de\' y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM \'de\' y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM \'de\' y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'H', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'H z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale chr.
+   */
+  'chr': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'M', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'EEE, M/d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'MMM d, EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'MMMM d, EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'M/d/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, M/d/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'MMM d, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, MMM d, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'MMMM d, y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, MMMM d, y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale cs.
+   */
+  'cs': const {
+    'd': 'd.', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd. M.', // NUM_MONTH_DAY
+    'MEd': 'EEE d. M.', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. M.', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d. M.', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd. M. y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d. M. y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'LLLL y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. M. y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d. M. y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'LLLL y', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d. MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'H', // HOUR
+    'jm': 'H:mm', // HOUR_MINUTE
+    'jms': 'H:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'H:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'H:mm z', // HOUR_MINUTETZ
+    'jz': 'H z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale cy.
+   */
+  'cy': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale da.
+   */
+  'da': const {
+    'd': 'd.', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'MMM', // ABBR_STANDALONE_MONTH
+    'LLLL': 'MMMM', // STANDALONE_MONTH
+    'M': 'M', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'MMM', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'MMMM', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d. MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE \'den\' d. MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH.mm', // HOUR24_MINUTE
+    'Hms': 'HH.mm.ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH.mm', // HOUR_MINUTE
+    'jms': 'HH.mm.ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH.mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH.mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm.ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale de.
+   */
+  'de': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd.M.', // NUM_MONTH_DAY
+    'MEd': 'EEE, d.M.', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M.y', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d.M.y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d. MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d. MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH \'Uhr\'', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH \'Uhr\'', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH \'Uhr\' z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale de_AT.
+   */
+  'de_AT': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd.M.', // NUM_MONTH_DAY
+    'MEd': 'EEE, d.M.', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M.y', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d.M.y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d. MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d. MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH \'Uhr\'', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH \'Uhr\'', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH \'Uhr\' z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale de_CH.
+   */
+  'de_CH': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd.M.', // NUM_MONTH_DAY
+    'MEd': 'EEE, d.M.', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M.y', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d.M.y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d. MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d. MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH \'Uhr\'', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH \'Uhr\'', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH \'Uhr\' z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale el.
+   */
+  'el': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'LLL y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'LLLL y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale en.
+   */
+  'en': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'EEE, M/d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, MMM d', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, MMMM d', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'M/d/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, M/d/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'MMM d, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, MMM d, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'MMMM d, y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, MMMM d, y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale en_AU.
+   */
+  'en_AU': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'LL', // NUM_MONTH
+    'Md': 'dd/MM', // NUM_MONTH_DAY
+    'MEd': 'EEE dd/MM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale en_GB.
+   */
+  'en_GB': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'LL', // NUM_MONTH
+    'Md': 'dd/MM', // NUM_MONTH_DAY
+    'MEd': 'EEE dd/MM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM/y', // YEAR_NUM_MONTH
+    'yMd': 'dd/MM/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, dd/MM/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale en_IE.
+   */
+  'en_IE': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'LL', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale en_IN.
+   */
+  'en_IN': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'LL', // NUM_MONTH
+    'Md': 'dd/MM', // NUM_MONTH_DAY
+    'MEd': 'EEE dd/MM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d MMM, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale en_SG.
+   */
+  'en_SG': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'LL', // NUM_MONTH
+    'Md': 'dd/MM', // NUM_MONTH_DAY
+    'MEd': 'EEE dd/MM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM/y', // YEAR_NUM_MONTH
+    'yMd': 'dd/MM/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, dd/MM/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale en_US.
+   */
+  'en_US': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'EEE, M/d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, MMM d', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, MMMM d', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'M/d/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, M/d/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'MMM d, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, MMM d, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'MMMM d, y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, MMMM d, y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale en_ZA.
+   */
+  'en_ZA': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'MM/dd', // NUM_MONTH_DAY
+    'MEd': 'EEE MM/dd', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'dd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE dd MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'dd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE dd MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'y/MM/dd', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, y/MM/dd', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'dd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, dd MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale es.
+   */
+  'es': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd \'de\' MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d \'de\' MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd \'de\' MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d \'de\' MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM \'de\' y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd \'de\' MMM \'de\' y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d \'de\' MMMM \'de\' y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM \'de\' y', // YEAR_MONTH
+    'yMMMMd': 'd \'de\' MMMM \'de\' y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d \'de\' MMMM \'de\' y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ \'de\' y', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'H', // HOUR
+    'jm': 'H:mm', // HOUR_MINUTE
+    'jms': 'H:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'H:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'H:mm z', // HOUR_MINUTETZ
+    'jz': 'H z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale es_419.
+   */
+  'es_419': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd \'de\' MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d \'de\' MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd \'de\' MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d \'de\' MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM \'de\' y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd \'de\' MMM \'de\' y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d \'de\' MMMM \'de\' y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM \'de\' y', // YEAR_MONTH
+    'yMMMMd': 'd \'de\' MMMM \'de\' y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d \'de\' MMMM \'de\' y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ \'de\' y', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'H', // HOUR
+    'jm': 'H:mm', // HOUR_MINUTE
+    'jms': 'H:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'H:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'H:mm z', // HOUR_MINUTETZ
+    'jz': 'H z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale es_ES.
+   */
+  'es_ES': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd \'de\' MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d \'de\' MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd \'de\' MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d \'de\' MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM \'de\' y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd \'de\' MMM \'de\' y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d \'de\' MMMM \'de\' y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM \'de\' y', // YEAR_MONTH
+    'yMMMMd': 'd \'de\' MMMM \'de\' y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d \'de\' MMMM \'de\' y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ \'de\' y', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'H', // HOUR
+    'jm': 'H:mm', // HOUR_MINUTE
+    'jms': 'H:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'H:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'H:mm z', // HOUR_MINUTETZ
+    'jz': 'H z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale et.
+   */
+  'et': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'MMMM', // ABBR_STANDALONE_MONTH
+    'LLLL': 'MMMM', // STANDALONE_MONTH
+    'M': 'M', // NUM_MONTH
+    'Md': 'd.M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d.M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'MMMM', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'MMMM', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M.y', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d.M y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d. MMMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d. MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm.ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'H:mm.ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm.ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale eu.
+   */
+  'eu': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'M/d, EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'MMM d, EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'MMMM d, EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'y/M', // YEAR_NUM_MONTH
+    'yMd': 'y/M/d', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y/M/d, EEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'y MMM d', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y MMM d, EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y(\'e\')\'ko\' MMMM', // YEAR_MONTH
+    'yMMMMd': 'y(\'e\')\'ko\' MMMM d', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'y(\'e\')\'ko\' MMMM d, EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y(\'e\')\'ko\' QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y(\'e\')\'ko\' QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm (v)', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm (z)', // HOUR_MINUTETZ
+    'jz': 'HH (z)', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale fa.
+   */
+  'fa': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'EEE M/d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd LLL', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d LLL', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd LLLL', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d LLLL', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'y/M', // YEAR_NUM_MONTH
+    'yMd': 'y/M/d', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE y/M/d', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'H', // HOUR
+    'jm': 'H:mm', // HOUR_MINUTE
+    'jms': 'H:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm (v)', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm (z)', // HOUR_MINUTETZ
+    'jz': 'H (z)', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale fi.
+   */
+  'fi': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd.M.', // NUM_MONTH_DAY
+    'MEd': 'EEE d.M.', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'ccc d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'cccc d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'L.y', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d.M.y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'LLL y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d. MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'LLLL y', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d. MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'H.mm', // HOUR24_MINUTE
+    'Hms': 'H.mm.ss', // HOUR24_MINUTE_SECOND
+    'j': 'H', // HOUR
+    'jm': 'H.mm', // HOUR_MINUTE
+    'jms': 'H.mm.ss', // HOUR_MINUTE_SECOND
+    'jmv': 'H.mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'H.mm z', // HOUR_MINUTETZ
+    'jz': 'H z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'm.ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale fil.
+   */
+  'fil': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'EEE, M/d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, MMM d', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, MMMM d', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'M/d/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, M/d/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'MMM d, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, MMM d, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'MMMM d, y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, MMMM d, y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale fr.
+   */
+  'fr': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH \'h\'', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH \'h\'', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH \'h\' z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale fr_CA.
+   */
+  'fr_CA': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M-d', // NUM_MONTH_DAY
+    'MEd': 'EEE M-d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'y-MM', // YEAR_NUM_MONTH
+    'yMd': 'y-MM-dd', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE y-MM-dd', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH \'h\'', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH \'h\'', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH \'h\' z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale gl.
+   */
+  'gl': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd-M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d-M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M-y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale gsw.
+   */
+  'gsw': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd.M.', // NUM_MONTH_DAY
+    'MEd': 'EEE, d.M.', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'y-M', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, y-M-d', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'y MMM d', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d. MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d. MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'H', // HOUR
+    'jm': 'H:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'H:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'H:mm z', // HOUR_MINUTETZ
+    'jz': 'H z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale gu.
+   */
+  'gu': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM, y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM, y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale haw.
+   */
+  'haw': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'M', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale he.
+   */
+  'he': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd בMMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d בMMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd בMMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d בMMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M.y', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd בMMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d בMMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd בMMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d בMMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale hi.
+   */
+  'hi': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale hr.
+   */
+  'hr': const {
+    'd': 'd.', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L.', // NUM_MONTH
+    'Md': 'd. M.', // NUM_MONTH_DAY
+    'MEd': 'EEE, d. M.', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y.', // YEAR
+    'yM': 'M. y.', // YEAR_NUM_MONTH
+    'yMd': 'd. M. y.', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d. M. y.', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'LLL y.', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. MMM y.', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d. MMM y.', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'LLLL y.', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y.', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d. MMMM y.', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y.', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y.', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale hu.
+   */
+  'hu': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M. d.', // NUM_MONTH_DAY
+    'MEd': 'M. d., EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d.', // ABBR_MONTH_DAY
+    'MMMEd': 'MMM d., EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d.', // MONTH_DAY
+    'MMMMEEEEd': 'MMMM d., EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y.', // YEAR
+    'yM': 'y. M.', // YEAR_NUM_MONTH
+    'yMd': 'y. MM. dd.', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y. MM. dd., EEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y. MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'y. MMM d.', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y. MMM d., EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y. MMMM', // YEAR_MONTH
+    'yMMMMd': 'y. MMMM d.', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'y. MMMM d., EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y. QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y. QQQQ', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'H', // HOUR
+    'jm': 'H:mm', // HOUR_MINUTE
+    'jms': 'H:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'H:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'H:mm z', // HOUR_MINUTETZ
+    'jz': 'H z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale hy.
+   */
+  'hy': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'dd.MM', // NUM_MONTH_DAY
+    'MEd': 'dd.MM, EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'd MMM, EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'd MMMM, EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM.y', // YEAR_NUM_MONTH
+    'yMd': 'dd.MM.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'd.MM.yթ., EEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'yթ. LLL', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM, yթ.', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'yթ. MMM d, EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'yթ. LLLL', // YEAR_MONTH
+    'yMMMMd': 'd MMMM, yթ.', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'yթ. MMMM d, EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y թ, QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y թ, QQQQ', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'H', // HOUR
+    'jm': 'H:mm', // HOUR_MINUTE
+    'jms': 'H:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'H:mm, v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'H:mm, z', // HOUR_MINUTETZ
+    'jz': 'H, z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale id.
+   */
+  'id': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH.mm', // HOUR24_MINUTE
+    'Hms': 'HH.mm.ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH.mm', // HOUR_MINUTE
+    'jms': 'HH.mm.ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH.mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH.mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm.ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale in.
+   */
+  'in': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH.mm', // HOUR24_MINUTE
+    'Hms': 'HH.mm.ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH.mm', // HOUR_MINUTE
+    'jms': 'HH.mm.ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH.mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH.mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm.ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale is.
+   */
+  'is': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd.M.', // NUM_MONTH_DAY
+    'MEd': 'EEE, d.M.', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M. y', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d.M.y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d. MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d. MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale it.
+   */
+  'it': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale iw.
+   */
+  'iw': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd בMMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d בMMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd בMMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d בMMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M.y', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd בMMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d בMMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd בMMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d בMMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale ja.
+   */
+  'ja': const {
+    'd': 'd日', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'M月', // ABBR_STANDALONE_MONTH
+    'LLLL': 'M月', // STANDALONE_MONTH
+    'M': 'M月', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'M/d(EEE)', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'M月', // ABBR_MONTH
+    'MMMd': 'M月d日', // ABBR_MONTH_DAY
+    'MMMEd': 'M月d日(EEE)', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'M月', // MONTH
+    'MMMMd': 'M月d日', // MONTH_DAY
+    'MMMMEEEEd': 'M月d日EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y年', // YEAR
+    'yM': 'y/M', // YEAR_NUM_MONTH
+    'yMd': 'y/M/d', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y/M/d(EEE)', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y年M月', // YEAR_ABBR_MONTH
+    'yMMMd': 'y年M月d日', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y年M月d日(EEE)', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y年M月', // YEAR_MONTH
+    'yMMMMd': 'y年M月d日', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'y年M月d日EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y/QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'yQQQQ', // YEAR_QUARTER
+    'H': 'H時', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'H時', // HOUR
+    'jm': 'H:mm', // HOUR_MINUTE
+    'jms': 'H:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'H:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'H:mm z', // HOUR_MINUTETZ
+    'jz': 'H時 z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale ka.
+   */
+  'ka': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd.M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d.M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M.y', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d.M.y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM, y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM, y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM, y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM, y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ, y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ, y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale kk.
+   */
+  'kk': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'dd-MM', // NUM_MONTH_DAY
+    'MEd': 'EEE, dd-MM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM-y', // YEAR_NUM_MONTH
+    'yMd': 'dd-MM-y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, dd-MM-y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y \'ж\'.', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y \'ж\'.', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale km.
+   */
+  'km': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd-M', // NUM_MONTH_DAY
+    'MEd': 'EEE d MMM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M-y', // YEAR_NUM_MONTH
+    'yMd': 'd-M-y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d-M-y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale kn.
+   */
+  'kn': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'd/M, EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd, MMM, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'd MMM, y EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'd MMMM y, EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale ko.
+   */
+  'ko': const {
+    'd': 'd일', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'M월', // NUM_MONTH
+    'Md': 'M. d.', // NUM_MONTH_DAY
+    'MEd': 'M. d. (EEE)', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d일', // ABBR_MONTH_DAY
+    'MMMEd': 'MMM d일 (EEE)', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d일', // MONTH_DAY
+    'MMMMEEEEd': 'MMMM d일 EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y년', // YEAR
+    'yM': 'y. M.', // YEAR_NUM_MONTH
+    'yMd': 'y. M. d.', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y. M. d. (EEE)', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y년 MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'y년 MMM d일', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y년 MMM d일 (EEE)', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y년 MMMM', // YEAR_MONTH
+    'yMMMMd': 'y년 MMMM d일', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'y년 MMMM d일 EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y년 QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y년 QQQQ', // YEAR_QUARTER
+    'H': 'H시', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'H시 m분 s초', // HOUR24_MINUTE_SECOND
+    'j': 'a h시', // HOUR
+    'jm': 'a h:mm', // HOUR_MINUTE
+    'jms': 'a h:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'a h:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'a h:mm z', // HOUR_MINUTETZ
+    'jz': 'a h시 z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale ky.
+   */
+  'ky': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'dd-MM', // NUM_MONTH_DAY
+    'MEd': 'dd-MM, EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd-MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'd-MMM, EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd-MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'd-MMMM, EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'y-MM', // YEAR_NUM_MONTH
+    'yMd': 'y-MM-dd', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y-MM-dd, EEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y-\'ж\'. MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'y-\'ж\'. d-MMM', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y-\'ж\'. d-MMM, EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y-\'ж\'. MMMM', // YEAR_MONTH
+    'yMMMMd': 'd-MMMM, y-\'ж\'.', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d-MMMM, y-\'ж\'.', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y-\'ж\'., QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y-\'ж\'., QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale ln.
+   */
+  'ln': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'H', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'H z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale lo.
+   */
+  'lo': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale lt.
+   */
+  'lt': const {
+    'd': 'dd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'LL', // NUM_MONTH
+    'Md': 'MM-d', // NUM_MONTH_DAY
+    'MEd': 'MM-dd, EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'MMM d, EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'MMMM d, EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'y-MM', // YEAR_NUM_MONTH
+    'yMd': 'y-MM-dd', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y-MM-dd, EEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'y MMM d', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y MMM d, EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y MMMM', // YEAR_MONTH
+    'yMMMMd': 'y \'m\'. MMMM d \'d\'.', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'y \'m\'. MMMM d \'d\'., EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale lv.
+   */
+  'lv': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'dd.MM.', // NUM_MONTH_DAY
+    'MEd': 'EEE, dd.MM.', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y. \'g\'.', // YEAR
+    'yM': 'MM.y.', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y.', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d.M.y.', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y. \'g\'. MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'y. \'g\'. d. MMM', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, y. \'g\'. d. MMM', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y. \'g\'. MMMM', // YEAR_MONTH
+    'yMMMMd': 'y. \'gada\' d. MMMM', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, y. \'gada\' d. MMMM', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y. \'g\'. QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale mk.
+   */
+  'mk': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd.M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d.M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y \'г\'.', // YEAR
+    'yM': 'M.y', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d.M.y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y \'г\'.', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y \'г\'.', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y \'г\'.', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y \'г\'.', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y \'г\'.', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y \'г\'.', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y \'г\'.', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y \'г\'.', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale ml.
+   */
+  'ml': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'M/d, EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'MMM d, EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'MMMM d, EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M-y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'd-M-y, EEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'y MMM d', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y MMM d, EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y MMMM', // YEAR_MONTH
+    'yMMMMd': 'y, MMMM d', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'y, MMMM d, EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale mn.
+   */
+  'mn': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M-d', // NUM_MONTH_DAY
+    'MEd': 'EEE, M-d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE MMM d', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE MMMM d', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'y-M', // YEAR_NUM_MONTH
+    'yMd': 'y-M-d', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, y-M-d', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'y MMM d', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, y MMM d', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y MMMM', // YEAR_MONTH
+    'yMMMMd': 'y \'оны\' MMMM \'сарын\' d', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, y \'оны\' MMMM \'сарын\' d',
+    // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y \'оны\' QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale mo.
+   */
+  'mo': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'dd.MM', // NUM_MONTH_DAY
+    'MEd': 'EEE, dd.MM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM.y', // YEAR_NUM_MONTH
+    'yMd': 'dd.MM.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, dd.MM.y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale mr.
+   */
+  'mr': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d, MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM, y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM, y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale ms.
+   */
+  'ms': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd-M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d-M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M-y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale mt.
+   */
+  'mt': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'MM-dd', // NUM_MONTH_DAY
+    'MEd': 'MM-dd, EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'MMM d, EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'MMMM d, EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'y-MM', // YEAR_NUM_MONTH
+    'yMd': 'y-MM-dd', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y-MM-dd, EEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'y MMM d', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y MMM d, EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y MMMM', // YEAR_MONTH
+    'yMMMMd': 'd \'ta\'’ MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d \'ta\'’ MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale my.
+   */
+  'my': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'EEE, M/d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, MMM d', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, MMMM d', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'y/M', // YEAR_NUM_MONTH
+    'yMd': 'y-MM-dd', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, y/M/d', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'y MMM d', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, y MMM d', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y MMMM', // YEAR_MONTH
+    'yMMMMd': 'y MMMM d', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, y MMMM d', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale nb.
+   */
+  'nb': const {
+    'd': 'd.', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L.', // NUM_MONTH
+    'Md': 'd.M.', // NUM_MONTH_DAY
+    'MEd': 'EEE d.M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M.y', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d.MM.y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d. MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d. MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH.mm', // HOUR24_MINUTE
+    'Hms': 'HH.mm.ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH.mm', // HOUR_MINUTE
+    'jms': 'HH.mm.ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH.mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH.mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm.ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale ne.
+   */
+  'ne': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'MM-dd', // NUM_MONTH_DAY
+    'MEd': 'MM-dd, EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'MMM d, EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'MMMM d, EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'y-MM', // YEAR_NUM_MONTH
+    'yMd': 'y-MM-dd', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y-MM-dd, EEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'y MMM d', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y MMM d, EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y MMMM', // YEAR_MONTH
+    'yMMMMd': 'y MMMM d', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'y MMMM d, EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale nl.
+   */
+  'nl': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd-M', // NUM_MONTH_DAY
+    'MEd': 'EEE d-M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M-y', // YEAR_NUM_MONTH
+    'yMd': 'd-M-y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d-M-y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale no.
+   */
+  'no': const {
+    'd': 'd.', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L.', // NUM_MONTH
+    'Md': 'd.M.', // NUM_MONTH_DAY
+    'MEd': 'EEE d.M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M.y', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d.MM.y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d. MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d. MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH.mm', // HOUR24_MINUTE
+    'Hms': 'HH.mm.ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH.mm', // HOUR_MINUTE
+    'jms': 'HH.mm.ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH.mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH.mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm.ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale no_NO.
+   */
+  'no_NO': const {
+    'd': 'd.', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L.', // NUM_MONTH
+    'Md': 'd.M.', // NUM_MONTH_DAY
+    'MEd': 'EEE d.M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M.y', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d.MM.y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d. MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d. MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH.mm', // HOUR24_MINUTE
+    'Hms': 'HH.mm.ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH.mm', // HOUR_MINUTE
+    'jms': 'HH.mm.ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH.mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH.mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm.ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale or.
+   */
+  'or': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'M', // NUM_MONTH
+    'Md': 'd-M', // NUM_MONTH_DAY
+    'MEd': 'MM-dd, EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'MMM d, EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'MMMM d, EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M-y', // YEAR_NUM_MONTH
+    'yMd': 'd-M-y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y-MM-dd, EEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y MMM d, EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale pa.
+   */
+  'pa': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, dd-MM.', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M-y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale pl.
+   */
+  'pl': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd.MM', // NUM_MONTH_DAY
+    'MEd': 'EEE, d.MM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM.y', // YEAR_NUM_MONTH
+    'yMd': 'd.MM.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d.MM.y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'LLL y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'LLLL y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale pt.
+   */
+  'pt': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, dd/MM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd \'de\' MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d \'de\' MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd \'de\' MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d \'de\' MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM/y', // YEAR_NUM_MONTH
+    'yMd': 'dd/MM/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, dd/MM/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM \'de\' y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd \'de\' MMM \'de\' y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d \'de\' MMM \'de\' y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM \'de\' y', // YEAR_MONTH
+    'yMMMMd': 'd \'de\' MMMM \'de\' y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d \'de\' MMMM \'de\' y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale pt_BR.
+   */
+  'pt_BR': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, dd/MM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd \'de\' MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d \'de\' MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd \'de\' MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d \'de\' MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM/y', // YEAR_NUM_MONTH
+    'yMd': 'dd/MM/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, dd/MM/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM \'de\' y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd \'de\' MMM \'de\' y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d \'de\' MMM \'de\' y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM \'de\' y', // YEAR_MONTH
+    'yMMMMd': 'd \'de\' MMMM \'de\' y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d \'de\' MMMM \'de\' y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale pt_PT.
+   */
+  'pt_PT': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, dd/MM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd/MM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d/MM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd \'de\' MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d \'de\' MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM/y', // YEAR_NUM_MONTH
+    'yMd': 'dd/MM/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, dd/MM/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MM/y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd/MM/y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d/MM/y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM \'de\' y', // YEAR_MONTH
+    'yMMMMd': 'd \'de\' MMMM \'de\' y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d \'de\' MMMM \'de\' y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQQ \'de\' y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ \'de\' y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale ro.
+   */
+  'ro': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'dd.MM', // NUM_MONTH_DAY
+    'MEd': 'EEE, dd.MM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM.y', // YEAR_NUM_MONTH
+    'yMd': 'dd.MM.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, dd.MM.y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale ru.
+   */
+  'ru': const {
+    'd': 'd', // DAY
+    'E': 'ccc', // ABBR_WEEKDAY
+    'EEEE': 'cccc', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'dd.MM', // NUM_MONTH_DAY
+    'MEd': 'EEE, dd.MM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'ccc, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'cccc, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM.y', // YEAR_NUM_MONTH
+    'yMd': 'dd.MM.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'ccc, d.MM.y \'г\'.', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'LLL y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y \'г\'.', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'LLLL y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y \'г\'.', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y \'г\'.', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y \'г\'.', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y \'г\'.', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'H', // HOUR
+    'jm': 'H:mm', // HOUR_MINUTE
+    'jms': 'H:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'H:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'H:mm z', // HOUR_MINUTETZ
+    'jz': 'H z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale sh.
+   */
+  'sh': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, M-d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y.', // YEAR
+    'yM': 'M.y.', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y.', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d.M.y.', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y.', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. MMM y.', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d. MMM y.', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y.', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y.', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d. MMMM y.', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ. y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ. y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH.mm', // HOUR24_MINUTE
+    'Hms': 'HH.mm.ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH.mm', // HOUR_MINUTE
+    'jms': 'HH.mm.ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH.mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH.mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm.ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale si.
+   */
+  'si': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M-d', // NUM_MONTH_DAY
+    'MEd': 'M-d, EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'MMM d EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'MMMM d EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'y-M', // YEAR_NUM_MONTH
+    'yMd': 'y-M-d', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y-M-d, EEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'y MMM d', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y MMM d, EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y MMMM', // YEAR_MONTH
+    'yMMMMd': 'y MMMM d', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'y MMMM d, EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'a h', // HOUR
+    'jm': 'a h.mm', // HOUR_MINUTE
+    'jms': 'a h.mm.ss', // HOUR_MINUTE_SECOND
+    'jmv': 'a h.mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'a h.mm z', // HOUR_MINUTETZ
+    'jz': 'a h z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm.ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale sk.
+   */
+  'sk': const {
+    'd': 'd.', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L.', // NUM_MONTH
+    'Md': 'd.M.', // NUM_MONTH_DAY
+    'MEd': 'EEE, d.M.', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM.', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d. MMM.', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M.y', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d. M. y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'LLL y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd.M.y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d. MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'LLLL y', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d. MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'H', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'H', // HOUR
+    'jm': 'H:mm', // HOUR_MINUTE
+    'jms': 'H:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'H:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'H:mm z', // HOUR_MINUTETZ
+    'jz': 'H z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale sl.
+   */
+  'sl': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd. M.', // NUM_MONTH_DAY
+    'MEd': 'EEE, d. MM.', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd. M. y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d. M. y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d. MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d. MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH.mm', // HOUR24_MINUTE
+    'Hms': 'HH.mm.ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH.mm', // HOUR_MINUTE
+    'jms': 'HH.mm.ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH.mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH.mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm.ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale sq.
+   */
+  'sq': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'dd/MM/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale sr.
+   */
+  'sr': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, M-d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd. MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d. MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd. MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d. MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y.', // YEAR
+    'yM': 'M.y.', // YEAR_NUM_MONTH
+    'yMd': 'd.M.y.', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d.M.y.', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y.', // YEAR_ABBR_MONTH
+    'yMMMd': 'd. MMM y.', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d. MMM y.', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y.', // YEAR_MONTH
+    'yMMMMd': 'd. MMMM y.', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d. MMMM y.', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ. y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ. y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH.mm', // HOUR24_MINUTE
+    'Hms': 'HH.mm.ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH.mm', // HOUR_MINUTE
+    'jms': 'HH.mm.ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH.mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH.mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm.ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale sv.
+   */
+  'sv': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'y-MM', // YEAR_NUM_MONTH
+    'yMd': 'y-MM-dd', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, y-MM-dd', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale sw.
+   */
+  'sw': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd-M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, MMM d, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale ta.
+   */
+  'ta': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'MM-dd, EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'MMM d, EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'MMMM d, EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM, y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM, y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale te.
+   */
+  'te': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd, MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d, MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'd MMMM y EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale th.
+   */
+  'th': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale tl.
+   */
+  'tl': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'EEE, M/d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, MMM d', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, MMMM d', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'M/d/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, M/d/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'MMM d, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, MMM d, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'MMMM d, y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, MMMM d, y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale tr.
+   */
+  'tr': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'dd/MM', // NUM_MONTH_DAY
+    'MEd': 'dd/MM EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'd MMMM EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'dd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'dd MMMM EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM/y', // YEAR_NUM_MONTH
+    'yMd': 'dd.MM.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'dd.MM.y EEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'dd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'd MMM y EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'd MMMM y EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y/QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y/QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale uk.
+   */
+  'uk': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'dd.MM', // NUM_MONTH_DAY
+    'MEd': 'EEE, dd.MM', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'MM.y', // YEAR_NUM_MONTH
+    'yMd': 'dd.MM.y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, dd.MM.y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'LLL y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, d MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'LLLL y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y \'р\'.', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, d MMMM y \'р\'.', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y \'р\'.', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale ur.
+   */
+  'ur': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE، d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE، d MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE، d MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE، d/M/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'd MMM، y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE، d MMM، y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM، y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE، d MMMM، y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale uz.
+   */
+  'uz': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'MM-dd', // NUM_MONTH_DAY
+    'MEd': 'MM-dd, EEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'MMM d, EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'MMMM d, EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'y-MM', // YEAR_NUM_MONTH
+    'yMd': 'y-MM-dd', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y-MM-dd, EEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y MMM', // YEAR_ABBR_MONTH
+    'yMMMd': 'y MMM d', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y MMM d, EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y MMMM', // YEAR_MONTH
+    'yMMMMd': 'y MMMM d', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, y MMMM d', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y QQQQ', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'HH:mm', // HOUR_MINUTE
+    'jms': 'HH:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'HH:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'HH:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale vi.
+   */
+  'vi': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'dd-M', // NUM_MONTH_DAY
+    'MEd': 'EEE, dd-M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'dd MMM', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, dd MMM', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'dd MMMM', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, dd MMMM', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': '\'Năm\' y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, dd-M-y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'dd MMM, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, dd MMM y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'dd MMMM, y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, \'ngày\' d MMMM \'năm\' y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'H:mm', // HOUR24_MINUTE
+    'Hms': 'H:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'HH', // HOUR
+    'jm': 'H:mm', // HOUR_MINUTE
+    'jms': 'H:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'H:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'H:mm z', // HOUR_MINUTETZ
+    'jz': 'HH z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale zh.
+   */
+  'zh': const {
+    'd': 'd日', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'M月', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'M/dEEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'M月d日', // ABBR_MONTH_DAY
+    'MMMEd': 'M月d日EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'M月d日', // MONTH_DAY
+    'MMMMEEEEd': 'M月d日EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y年', // YEAR
+    'yM': 'y/M', // YEAR_NUM_MONTH
+    'yMd': 'y/M/d', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y/M/dEEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y年M月', // YEAR_ABBR_MONTH
+    'yMMMd': 'y年M月d日', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y年M月d日EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y年M月', // YEAR_MONTH
+    'yMMMMd': 'y年M月d日', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'y年M月d日EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y年第Q季度', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y年第Q季度', // YEAR_QUARTER
+    'H': 'H时', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'ah时', // HOUR
+    'jm': 'ah:mm', // HOUR_MINUTE
+    'jms': 'ah:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'vah:mm', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'zah:mm', // HOUR_MINUTETZ
+    'jz': 'zah时', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale zh_CN.
+   */
+  'zh_CN': const {
+    'd': 'd日', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'M月', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'M/dEEE', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'M月d日', // ABBR_MONTH_DAY
+    'MMMEd': 'M月d日EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'M月d日', // MONTH_DAY
+    'MMMMEEEEd': 'M月d日EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y年', // YEAR
+    'yM': 'y/M', // YEAR_NUM_MONTH
+    'yMd': 'y/M/d', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y/M/dEEE', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y年M月', // YEAR_ABBR_MONTH
+    'yMMMd': 'y年M月d日', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y年M月d日EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y年M月', // YEAR_MONTH
+    'yMMMMd': 'y年M月d日', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'y年M月d日EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y年第Q季度', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y年第Q季度', // YEAR_QUARTER
+    'H': 'H时', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'ah时', // HOUR
+    'jm': 'ah:mm', // HOUR_MINUTE
+    'jms': 'ah:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'vah:mm', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'zah:mm', // HOUR_MINUTETZ
+    'jz': 'zah时', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale zh_HK.
+   */
+  'zh_HK': const {
+    'd': 'd日', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'M月', // NUM_MONTH
+    'Md': 'd/M', // NUM_MONTH_DAY
+    'MEd': 'EEE, d/M', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'M月d日', // ABBR_MONTH_DAY
+    'MMMEd': 'M月d日 (EEE)', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'M月d日', // MONTH_DAY
+    'MMMMEEEEd': 'M月d日 (EEEE)', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y年', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'd/M/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'd/M/y(EEE)', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y 年 M 月', // YEAR_ABBR_MONTH
+    'yMMMd': 'y 年 M 月 d 日', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y 年 M 月 d 日 (EEE)', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y 年 M 月', // YEAR_MONTH
+    'yMMMMd': 'y 年 M 月 d 日', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'y 年 M 月 d 日 (EEEE)', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y年QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y年QQQQ', // YEAR_QUARTER
+    'H': 'H時', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'ah時', // HOUR
+    'jm': 'ah:mm', // HOUR_MINUTE
+    'jms': 'ah:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'ah:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'ah:mm z', // HOUR_MINUTETZ
+    'jz': 'ah時 z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale zh_TW.
+   */
+  'zh_TW': const {
+    'd': 'd日', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'M月', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'M/d(EEE)', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'M月d日', // ABBR_MONTH_DAY
+    'MMMEd': 'M月d日EEE', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'M月d日', // MONTH_DAY
+    'MMMMEEEEd': 'M月d日EEEE', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y年', // YEAR
+    'yM': 'y/M', // YEAR_NUM_MONTH
+    'yMd': 'y/M/d', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'y/M/d(EEE)', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'y年M月', // YEAR_ABBR_MONTH
+    'yMMMd': 'y年M月d日', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'y年M月d日EEE', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'y年M月', // YEAR_MONTH
+    'yMMMMd': 'y年M月d日', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'y年M月d日EEEE', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'y年QQQ', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'y年QQQQ', // YEAR_QUARTER
+    'H': 'H時', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'ah時', // HOUR
+    'jm': 'ah:mm', // HOUR_MINUTE
+    'jms': 'ah:mm:ss', // HOUR_MINUTE_SECOND
+    'jmv': 'ah:mm v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'ah:mm z', // HOUR_MINUTETZ
+    'jz': 'ah時 z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale zu.
+   */
+  'zu': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'EEE, M/d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, MMM d', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, MMMM d', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'M/d/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, M/d/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'MMM d, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, MMM d, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'd MMMM y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE d MMMM y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  },
+
+  /**
+   * Extended set of localized date/time patterns for locale en_ISO.
+   */
+  'en_ISO': const {
+    'd': 'd', // DAY
+    'E': 'EEE', // ABBR_WEEKDAY
+    'EEEE': 'EEEE', // WEEKDAY
+    'LLL': 'LLL', // ABBR_STANDALONE_MONTH
+    'LLLL': 'LLLL', // STANDALONE_MONTH
+    'M': 'L', // NUM_MONTH
+    'Md': 'M/d', // NUM_MONTH_DAY
+    'MEd': 'EEE, M/d', // NUM_MONTH_WEEKDAY_DAY
+    'MMM': 'LLL', // ABBR_MONTH
+    'MMMd': 'MMM d', // ABBR_MONTH_DAY
+    'MMMEd': 'EEE, MMM d', // ABBR_MONTH_WEEKDAY_DAY
+    'MMMM': 'LLLL', // MONTH
+    'MMMMd': 'MMMM d', // MONTH_DAY
+    'MMMMEEEEd': 'EEEE, MMMM d', // MONTH_WEEKDAY_DAY
+    'QQQ': 'QQQ', // ABBR_QUARTER
+    'QQQQ': 'QQQQ', // QUARTER
+    'y': 'y', // YEAR
+    'yM': 'M/y', // YEAR_NUM_MONTH
+    'yMd': 'M/d/y', // YEAR_NUM_MONTH_DAY
+    'yMEd': 'EEE, M/d/y', // YEAR_NUM_MONTH_WEEKDAY_DAY
+    'yMMM': 'MMM y', // YEAR_ABBR_MONTH
+    'yMMMd': 'MMM d, y', // YEAR_ABBR_MONTH_DAY
+    'yMMMEd': 'EEE, MMM d, y', // YEAR_ABBR_MONTH_WEEKDAY_DAY
+    'yMMMM': 'MMMM y', // YEAR_MONTH
+    'yMMMMd': 'MMMM d, y', // YEAR_MONTH_DAY
+    'yMMMMEEEEd': 'EEEE, MMMM d, y', // YEAR_MONTH_WEEKDAY_DAY
+    'yQQQ': 'QQQ y', // YEAR_ABBR_QUARTER
+    'yQQQQ': 'QQQQ y', // YEAR_QUARTER
+    'H': 'HH', // HOUR24
+    'Hm': 'HH:mm', // HOUR24_MINUTE
+    'Hms': 'HH:mm:ss', // HOUR24_MINUTE_SECOND
+    'j': 'h a', // HOUR
+    'jm': 'h:mm a', // HOUR_MINUTE
+    'jms': 'h:mm:ss a', // HOUR_MINUTE_SECOND
+    'jmv': 'h:mm a v', // HOUR_MINUTE_GENERIC_TZ
+    'jmz': 'h:mm a z', // HOUR_MINUTETZ
+    'jz': 'h a z', // HOURGENERIC_TZ
+    'm': 'm', // MINUTE
+    'ms': 'mm:ss', // MINUTE_SECOND
+    's': 's', // SECOND
+    'v': 'v', // ABBR_GENERIC_TZ
+    'z': 'z', // ABBR_SPECIFIC_TZ
+    'zzzz': 'zzzz', // SPECIFIC_TZ
+    'ZZZZ': 'ZZZZ' // ABBR_UTC_TZ
+  }
+};
diff --git a/intl/lib/extract_messages.dart b/intl/lib/extract_messages.dart
new file mode 100644
index 0000000..6bc7032
--- /dev/null
+++ b/intl/lib/extract_messages.dart
@@ -0,0 +1,512 @@
+// Copyright (c) 2013, 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.
+
+/**
+ * This is for use in extracting messages from a Dart program
+ * using the Intl.message() mechanism and writing them to a file for
+ * translation. This provides only the stub of a mechanism, because it
+ * doesn't define how the file should be written. It provides an
+ * [IntlMessage] class that holds the extracted data and [parseString]
+ * and [parseFile] methods which
+ * can extract messages that conform to the expected pattern:
+ *       (parameters) => Intl.message("Message $parameters", desc: ...);
+ * It uses the analyzer package to do the parsing, so may
+ * break if there are changes to the API that it provides.
+ * An example can be found in test/message_extraction/extract_to_json.dart
+ *
+ * Note that this does not understand how to follow part directives, so it
+ * has to explicitly be given all the files that it needs. A typical use case
+ * is to run it on all .dart files in a directory.
+ */
+library extract_messages;
+
+import 'dart:io';
+
+import 'package:analyzer/analyzer.dart';
+import 'package:intl/src/intl_message.dart';
+
+/**
+ * If this is true, print warnings for skipped messages. Otherwise, warnings
+ * are suppressed.
+ */
+bool suppressWarnings = false;
+
+/**
+ * If this is true, then treat all warnings as errors.
+ */
+bool warningsAreErrors = false;
+
+/**
+ * This accumulates a list of all warnings/errors we have found. These are
+ * saved as strings right now, so all that can really be done is print and
+ * count them.
+ */
+List<String> warnings = [];
+
+/** Were there any warnings or errors in extracting messages. */
+bool get hasWarnings => warnings.isNotEmpty;
+
+/** Are plural and gender expressions required to be at the top level
+ * of an expression, or are they allowed to be embedded in string literals.
+ *
+ * For example, the following expression
+ *     'There are ${Intl.plural(...)} items'.
+ * is legal if [allowEmbeddedPluralsAndGenders] is true, but illegal
+ * if [allowEmbeddedPluralsAndGenders] is false.
+ */
+bool allowEmbeddedPluralsAndGenders = true;
+
+/**
+ * Parse the source of the Dart program file [file] and return a Map from
+ * message names to [IntlMessage] instances.
+ */
+Map<String, MainMessage> parseFile(File file) {
+  try {
+    _root = parseDartFile(file.path);
+  } on AnalyzerErrorGroup catch (e) {
+    print("Error in parsing ${file.path}, no messages extracted.");
+    print("  $e");
+    return {};
+  }
+  _origin = file.path;
+  var visitor = new MessageFindingVisitor();
+  _root.accept(visitor);
+  return visitor.messages;
+}
+
+/**
+ * The root of the compilation unit, and the first node we visit. We hold
+ * on to this for error reporting, as it can give us line numbers of other
+ * nodes.
+ */
+CompilationUnit _root;
+
+/**
+ * An arbitrary string describing where the source code came from. Most
+ * obviously, this could be a file path. We use this when reporting
+ * invalid messages.
+ */
+String _origin;
+
+String _reportErrorLocation(AstNode node) {
+  var result = new StringBuffer();
+  if (_origin != null) result.write("    from $_origin");
+  var info = _root.lineInfo;
+  if (info != null) {
+    var line = info.getLocation(node.offset);
+    result.write("    line: ${line.lineNumber}, column: ${line.columnNumber}");
+  }
+  return result.toString();
+}
+
+/**
+ * This visits the program source nodes looking for Intl.message uses
+ * that conform to its pattern and then creating the corresponding
+ * IntlMessage objects. We have to find both the enclosing function, and
+ * the Intl.message invocation.
+ */
+class MessageFindingVisitor extends GeneralizingAstVisitor {
+  MessageFindingVisitor();
+
+  /**
+   * Accumulates the messages we have found, keyed by name.
+   */
+  final Map<String, MainMessage> messages = new Map<String, MainMessage>();
+
+  /**
+   * We keep track of the data from the last MethodDeclaration,
+   * FunctionDeclaration or FunctionExpression that we saw on the way down,
+   * as that will be the nearest parent of the Intl.message invocation.
+   */
+  FormalParameterList parameters;
+  String name;
+
+  /** Return true if [node] matches the pattern we expect for Intl.message() */
+  bool looksLikeIntlMessage(MethodInvocation node) {
+    const validNames = const ["message", "plural", "gender", "select"];
+    if (!validNames.contains(node.methodName.name)) return false;
+    if (!(node.target is SimpleIdentifier)) return false;
+    SimpleIdentifier target = node.target;
+    return target.token.toString() == "Intl";
+  }
+
+  Message _expectedInstance(String type) {
+    switch (type) {
+      case 'message':
+        return new MainMessage();
+      case 'plural':
+        return new Plural();
+      case 'gender':
+        return new Gender();
+      case 'select':
+        return new Select();
+      default:
+        return null;
+    }
+  }
+
+  /**
+   * Returns a String describing why the node is invalid, or null if no
+   * reason is found, so it's presumed valid.
+   */
+  String checkValidity(MethodInvocation node) {
+    // The containing function cannot have named parameters.
+    if (parameters.parameters.any((each) => each.kind == ParameterKind.NAMED)) {
+      return "Named parameters on message functions are not supported.";
+    }
+    var arguments = node.argumentList.arguments;
+    var instance = _expectedInstance(node.methodName.name);
+    return instance.checkValidity(node, arguments, name, parameters);
+  }
+
+  /**
+   * Record the parameters of the function or method declaration we last
+   * encountered before seeing the Intl.message call.
+   */
+  void visitMethodDeclaration(MethodDeclaration node) {
+    parameters = node.parameters;
+    if (parameters == null) {
+      parameters = new FormalParameterList(null, [], null, null, null);
+    }
+    name = node.name.name;
+    super.visitMethodDeclaration(node);
+  }
+
+  /**
+   * Record the parameters of the function or method declaration we last
+   * encountered before seeing the Intl.message call.
+   */
+  void visitFunctionDeclaration(FunctionDeclaration node) {
+    parameters = node.functionExpression.parameters;
+    if (parameters == null) {
+      parameters = new FormalParameterList(null, [], null, null, null);
+    }
+    name = node.name.name;
+    super.visitFunctionDeclaration(node);
+  }
+
+  /**
+   * Examine method invocations to see if they look like calls to Intl.message.
+   * If we've found one, stop recursing. This is important because we can have
+   * Intl.message(...Intl.plural...) and we don't want to treat the inner
+   * plural as if it was an outermost message.
+   */
+  void visitMethodInvocation(MethodInvocation node) {
+    if (!addIntlMessage(node)) {
+      super.visitMethodInvocation(node);
+    }
+  }
+
+  /**
+   * Check that the node looks like an Intl.message invocation, and create
+   * the [IntlMessage] object from it and store it in [messages]. Return true
+   * if we successfully extracted a message and should stop looking. Return
+   * false if we didn't, so should continue recursing.
+   */
+  bool addIntlMessage(MethodInvocation node) {
+    if (!looksLikeIntlMessage(node)) return false;
+    var reason = checkValidity(node);
+    if (reason != null) {
+      if (!suppressWarnings) {
+        var err = new StringBuffer()
+          ..write("Skipping invalid Intl.message invocation\n    <$node>\n")
+          ..writeAll(["    reason: $reason\n", _reportErrorLocation(node)]);
+        warnings.add(err.toString());
+        print(err);
+      }
+      // We found one, but it's not valid. Stop recursing.
+      return true;
+    }
+    var message;
+    if (node.methodName.name == "message") {
+      message = messageFromIntlMessageCall(node);
+    } else {
+      message = messageFromDirectPluralOrGenderCall(node);
+    }
+    if (message != null) messages[message.name] = message;
+    return true;
+  }
+
+  /**
+   * Create a MainMessage from [node] using the name and
+   * parameters of the last function/method declaration we encountered,
+   * and the values we get by calling [extract]. We set those values
+   * by calling [setAttribute]. This is the common parts between
+   * [messageFromIntlMessageCall] and [messageFromDirectPluralOrGenderCall].
+   */
+  MainMessage _messageFromNode(
+      MethodInvocation node, Function extract, Function setAttribute) {
+    var message = new MainMessage();
+    message.name = name;
+    message.arguments =
+        parameters.parameters.map((x) => x.identifier.name).toList();
+    var arguments = node.argumentList.arguments;
+    var extractionResult = extract(message, arguments);
+    if (extractionResult == null) return null;
+
+    for (var namedArgument in arguments.where((x) => x is NamedExpression)) {
+      var name = namedArgument.name.label.name;
+      var exp = namedArgument.expression;
+      var evaluator = new ConstantEvaluator();
+      var basicValue = exp.accept(evaluator);
+      var value = basicValue == ConstantEvaluator.NOT_A_CONSTANT
+          ? exp.toString()
+          : basicValue;
+      setAttribute(message, name, value);
+    }
+    return message;
+  }
+
+  /**
+   * Create a MainMessage from [node] using the name and
+   * parameters of the last function/method declaration we encountered
+   * and the parameters to the Intl.message call.
+   */
+  MainMessage messageFromIntlMessageCall(MethodInvocation node) {
+    MainMessage extractFromIntlCall(MainMessage message, List arguments) {
+      try {
+        var interpolation = new InterpolationVisitor(message);
+        arguments.first.accept(interpolation);
+        if (interpolation.pieces.any((x) => x is Plural || x is Gender) &&
+            !allowEmbeddedPluralsAndGenders) {
+          if (interpolation.pieces.any((x) => x is String && x.isNotEmpty)) {
+            throw new IntlMessageExtractionException(
+                "Plural and gender expressions must be at the top level, "
+                "they cannot be embedded in larger string literals.\n"
+                "Error at $node");
+          }
+        }
+        message.messagePieces.addAll(interpolation.pieces);
+      } on IntlMessageExtractionException catch (e) {
+        message = null;
+        var err = new StringBuffer()
+          ..writeAll(["Error ", e, "\nProcessing <", node, ">\n"])
+          ..write(_reportErrorLocation(node));
+        print(err);
+        warnings.add(err.toString());
+      }
+      return message; // Because we may have set it to null on an error.
+    }
+
+    void setValue(MainMessage message, String fieldName, Object fieldValue) {
+      message[fieldName] = fieldValue;
+    }
+
+    return _messageFromNode(node, extractFromIntlCall, setValue);
+  }
+
+  /**
+   * Create a MainMessage from [node] using the name and
+   * parameters of the last function/method declaration we encountered
+   * and the parameters to the Intl.plural or Intl.gender call.
+   */
+  MainMessage messageFromDirectPluralOrGenderCall(MethodInvocation node) {
+    MainMessage extractFromPluralOrGender(MainMessage message, _) {
+      var visitor = new PluralAndGenderVisitor(message.messagePieces, message);
+      node.accept(visitor);
+      return message;
+    }
+
+    void setAttribute(MainMessage msg, String fieldName, String fieldValue) {
+      if (msg.attributeNames.contains(fieldName)) {
+        msg[fieldName] = fieldValue;
+      }
+    }
+    return _messageFromNode(node, extractFromPluralOrGender, setAttribute);
+  }
+}
+
+/**
+ * Given an interpolation, find all of its chunks, validate that they are only
+ * simple variable substitutions or else Intl.plural/gender calls,
+ * and keep track of the pieces of text so that other parts
+ * of the program can deal with the simple string sections and the generated
+ * parts separately. Note that this is a SimpleAstVisitor, so it only
+ * traverses one level of children rather than automatically recursing. If we
+ * find a plural or gender, which requires recursion, we do it with a separate
+ * special-purpose visitor.
+ */
+class InterpolationVisitor extends SimpleAstVisitor {
+  final Message message;
+
+  InterpolationVisitor(this.message);
+
+  List pieces = [];
+  String get extractedMessage => pieces.join();
+
+  void visitAdjacentStrings(AdjacentStrings node) {
+    node.visitChildren(this);
+    super.visitAdjacentStrings(node);
+  }
+
+  void visitStringInterpolation(StringInterpolation node) {
+    node.visitChildren(this);
+    super.visitStringInterpolation(node);
+  }
+
+  void visitSimpleStringLiteral(SimpleStringLiteral node) {
+    pieces.add(node.value);
+    super.visitSimpleStringLiteral(node);
+  }
+
+  void visitInterpolationString(InterpolationString node) {
+    pieces.add(node.value);
+    super.visitInterpolationString(node);
+  }
+
+  void visitInterpolationExpression(InterpolationExpression node) {
+    if (node.expression is SimpleIdentifier) {
+      return handleSimpleInterpolation(node);
+    } else {
+      return lookForPluralOrGender(node);
+    }
+    // Note that we never end up calling super.
+  }
+
+  lookForPluralOrGender(InterpolationExpression node) {
+    var visitor = new PluralAndGenderVisitor(pieces, message);
+    node.accept(visitor);
+    if (!visitor.foundPluralOrGender) {
+      throw new IntlMessageExtractionException(
+          "Only simple identifiers and Intl.plural/gender/select expressions "
+          "are allowed in message "
+          "interpolation expressions.\nError at $node");
+    }
+  }
+
+  void handleSimpleInterpolation(InterpolationExpression node) {
+    var index = arguments.indexOf(node.expression.toString());
+    if (index == -1) {
+      throw new IntlMessageExtractionException(
+          "Cannot find argument ${node.expression}");
+    }
+    pieces.add(index);
+  }
+
+  List get arguments => message.arguments;
+}
+
+/**
+ * A visitor to extract information from Intl.plural/gender sends. Note that
+ * this is a SimpleAstVisitor, so it doesn't automatically recurse. So this
+ * needs to be called where we expect a plural or gender immediately below.
+ */
+class PluralAndGenderVisitor extends SimpleAstVisitor {
+  /**
+   * A plural or gender always exists in the context of a parent message,
+   * which could in turn also be a plural or gender.
+   */
+  final ComplexMessage parent;
+
+  /**
+   * The pieces of the message. We are given an initial version of this
+   * from our parent and we add to it as we find additional information.
+   */
+  List pieces;
+
+  /** This will be set to true if we find a plural or gender. */
+  bool foundPluralOrGender = false;
+
+  PluralAndGenderVisitor(this.pieces, this.parent) : super();
+
+  visitInterpolationExpression(InterpolationExpression node) {
+    // TODO(alanknight): Provide better errors for malformed expressions.
+    if (!looksLikePluralOrGender(node.expression)) return;
+    var reason = checkValidity(node.expression);
+    if (reason != null) throw reason;
+    var message = messageFromMethodInvocation(node.expression);
+    foundPluralOrGender = true;
+    pieces.add(message);
+    super.visitInterpolationExpression(node);
+  }
+
+  visitMethodInvocation(MethodInvocation node) {
+    pieces.add(messageFromMethodInvocation(node));
+    super.visitMethodInvocation(node);
+  }
+
+  /** Return true if [node] matches the pattern for plural or gender message.*/
+  bool looksLikePluralOrGender(MethodInvocation node) {
+    if (!["plural", "gender", "select"].contains(node.methodName.name)) {
+      return false;
+    }
+    if (!(node.target is SimpleIdentifier)) return false;
+    SimpleIdentifier target = node.target;
+    return target.token.toString() == "Intl";
+  }
+
+  /**
+   * Returns a String describing why the node is invalid, or null if no
+   * reason is found, so it's presumed valid.
+   */
+  String checkValidity(MethodInvocation node) {
+    // TODO(alanknight): Add reasonable validity checks.
+    return null;
+  }
+
+  /**
+   * Create a MainMessage from [node] using the name and
+   * parameters of the last function/method declaration we encountered
+   * and the parameters to the Intl.message call.
+   */
+  Message messageFromMethodInvocation(MethodInvocation node) {
+    var message;
+    switch (node.methodName.name) {
+      case "gender":
+        message = new Gender();
+        break;
+      case "plural":
+        message = new Plural();
+        break;
+      case "select":
+        message = new Select();
+        break;
+      default:
+        throw new IntlMessageExtractionException(
+            "Invalid plural/gender/select message");
+    }
+    message.parent = parent;
+
+    var arguments = message.argumentsOfInterestFor(node);
+    arguments.forEach((key, value) {
+      try {
+        var interpolation = new InterpolationVisitor(message);
+        value.accept(interpolation);
+        message[key] = interpolation.pieces;
+      } on IntlMessageExtractionException catch (e) {
+        message = null;
+        var err = new StringBuffer()
+          ..writeAll(["Error ", e, "\nProcessing <", node, ">"])
+          ..write(_reportErrorLocation(node));
+        print(err);
+        warnings.add(err.toString());
+      }
+    });
+    var mainArg = node.argumentList.arguments
+        .firstWhere((each) => each is! NamedExpression);
+    if (mainArg is SimpleStringLiteral) {
+      message.mainArgument = mainArg.toString();
+    } else {
+      message.mainArgument = mainArg.name;
+    }
+    return message;
+  }
+}
+
+/**
+ * Exception thrown when we cannot process a message properly.
+ */
+class IntlMessageExtractionException implements Exception {
+  /**
+   * A message describing the error.
+   */
+  final String message;
+
+  /**
+   * Creates a new exception with an optional error [message].
+   */
+  const IntlMessageExtractionException([this.message = ""]);
+
+  String toString() => "IntlMessageExtractionException: $message";
+}
diff --git a/intl/lib/generate_localized.dart b/intl/lib/generate_localized.dart
new file mode 100644
index 0000000..80ab958
--- /dev/null
+++ b/intl/lib/generate_localized.dart
@@ -0,0 +1,253 @@
+// Copyright (c) 2013, 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.
+
+/**
+ * This provides utilities for generating localized versions of
+ * messages. It does not stand alone, but expects to be given
+ * TranslatedMessage objects and generate code for a particular locale
+ * based on them.
+ *
+ * An example of usage can be found
+ * in test/message_extract/generate_from_json.dart
+ */
+library generate_localized;
+
+import 'intl.dart';
+import 'src/intl_message.dart';
+import 'dart:io';
+import 'package:path/path.dart' as path;
+
+/**
+ * If the import path following package: is something else, modify the
+ * [intlImportPath] variable to change the import directives in the generated
+ * code.
+ */
+var intlImportPath = 'intl';
+
+/**
+ * If the path to the generated files is something other than the current
+ * directory, update the [generatedImportPath] variable to change the import
+ * directives in the generated code.
+ */
+var generatedImportPath = '';
+
+/**
+ * Given a base file, return the file prefixed with the path to import it.
+ * By default, that is in the current directory, but if [generatedImportPath]
+ * has been set, then use that as a prefix.
+ */
+String importForGeneratedFile(String file) =>
+    generatedImportPath.isEmpty ? file : "$generatedImportPath/$file";
+
+/**
+ * A list of all the locales for which we have translations. Code that does
+ * the reading of translations should add to this.
+ */
+List<String> allLocales = [];
+
+/**
+ * If we have more than one set of messages to generate in a particular
+ * directory we may want to prefix some to distinguish them.
+ */
+String generatedFilePrefix = '';
+
+/**
+ * Should we use deferred loading for the generated libraries.
+ */
+bool useDeferredLoading = true;
+
+/**
+ * This represents a message and its translation. We assume that the translation
+ * has some identifier that allows us to figure out the original message it
+ * corresponds to, and that it may want to transform the translated text in
+ * some way, e.g. to turn whatever format the translation uses for variables
+ * into a Dart string interpolation. Specific translation
+ * mechanisms are expected to subclass this.
+ */
+abstract class TranslatedMessage {
+  /**
+   * The identifier for this message. In the simplest case, this is the name
+   * parameter from the Intl.message call,
+   * but it can be any identifier that this program and the output of the
+   * translation can agree on as identifying a message.
+   */
+  final String id;
+
+  /** Our translated version of all the [originalMessages]. */
+  final Message translated;
+
+  /**
+   * The original messages that we are a translation of. There can
+   *  be more than one original message for the same translation.
+   */
+  List<MainMessage> originalMessages;
+
+  /**
+   * For backward compatibility, we still have the originalMessage API.
+   */
+  MainMessage get originalMessage => originalMessages.first;
+  set originalMessage(MainMessage m) => originalMessages = [m];
+
+  TranslatedMessage(this.id, this.translated);
+
+  Message get message => translated;
+
+  toString() => id.toString();
+}
+
+/**
+ * We can't use a hyphen in a Dart library name, so convert the locale
+ * separator to an underscore.
+ */
+String _libraryName(String x) => 'messages_' + x.replaceAll('-', '_');
+
+/**
+ * Generate a file <[generated_file_prefix]>_messages_<[locale]>.dart
+ * for the [translations] in [locale] and put it in [targetDir].
+ */
+void generateIndividualMessageFile(String basicLocale,
+    Iterable<TranslatedMessage> translations, String targetDir) {
+  var result = new StringBuffer();
+  var locale = new MainMessage()
+      .escapeAndValidateString(Intl.canonicalizedLocale(basicLocale));
+  result.write(prologue(locale));
+  // Exclude messages with no translation and translations with no matching
+  // original message (e.g. if we're using some messages from a larger catalog)
+  var usableTranslations = translations
+      .where((each) => each.originalMessages != null && each.message != null)
+      .toList();
+  for (var each in usableTranslations) {
+    for (var original in each.originalMessages) {
+      original.addTranslation(locale, each.message);
+    }
+  }
+  usableTranslations.sort((a, b) =>
+      a.originalMessages.first.name.compareTo(b.originalMessages.first.name));
+  for (var translation in usableTranslations) {
+    for (var original in translation.originalMessages) {
+      result
+        ..write("  ")
+        ..write(original.toCodeForLocale(locale))
+        ..write("\n\n");
+    }
+  }
+  result.write("\n  final messages = const {\n");
+  var entries = usableTranslations
+      .expand((translation) => translation.originalMessages)
+      .map((original) => original.name)
+      .map((name) => "    \"$name\" : $name");
+  result
+    ..write(entries.join(",\n"))
+    ..write("\n  };\n}");
+
+  // To preserve compatibility, we don't use the canonical version of the locale
+  // in the file name.
+  var filename =
+      path.join(targetDir, "${generatedFilePrefix}messages_$basicLocale.dart");
+  new File(filename).writeAsStringSync(result.toString());
+}
+
+/**
+ * This returns the mostly constant string used in
+ * [generateIndividualMessageFile] for the beginning of the file,
+ * parameterized by [locale].
+ */
+String prologue(String locale) => """
+/**
+ * DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
+ * This is a library that provides messages for a $locale locale. All the
+ * messages from the main program should be duplicated here with the same
+ * function name.
+ */
+
+library ${_libraryName(locale)};
+import 'package:$intlImportPath/intl.dart';
+import 'package:$intlImportPath/message_lookup_by_library.dart';
+
+final messages = new MessageLookup();
+
+class MessageLookup extends MessageLookupByLibrary {
+
+  get localeName => '$locale';
+""";
+
+/**
+ * This section generates the messages_all.dart file based on the list of
+ * [allLocales].
+ */
+String generateMainImportFile() {
+  var output = new StringBuffer();
+  output.write(mainPrologue);
+  for (var locale in allLocales) {
+    var baseFile = '${generatedFilePrefix}messages_$locale.dart';
+    var file = importForGeneratedFile(baseFile);
+    output.write("import '$file' ");
+    if (useDeferredLoading) output.write("deferred ");
+    output.write("as ${_libraryName(locale)};\n");
+  }
+  output.write("\n");
+  output.write("\nMap<String, Function> _deferredLibraries = {\n");
+  for (var rawLocale in allLocales) {
+    var locale = Intl.canonicalizedLocale(rawLocale);
+    var loadOperation = (useDeferredLoading)
+        ? "  '$locale' : () => ${_libraryName(locale)}.loadLibrary(),\n"
+        : "  '$locale' : () => new Future.value(null),\n";
+    output.write(loadOperation);
+  }
+  output.write("};\n");
+  output.write("\nMessageLookupByLibrary _findExact(localeName) {\n"
+      "  switch (localeName) {\n");
+  for (var rawLocale in allLocales) {
+    var locale = Intl.canonicalizedLocale(rawLocale);
+    output.write(
+        "    case '$locale' : return ${_libraryName(locale)}.messages;\n");
+  }
+  output.write(closing);
+  return output.toString();
+}
+
+/**
+ * Constant string used in [generateMainImportFile] for the beginning of the
+ * file.
+ */
+var mainPrologue = """
+/**
+ * DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
+ * This is a library that looks up messages for specific locales by
+ * delegating to the appropriate library.
+ */
+
+library messages_all;
+
+import 'dart:async';
+import 'package:$intlImportPath/message_lookup_by_library.dart';
+import 'package:$intlImportPath/src/intl_helpers.dart';
+import 'package:$intlImportPath/intl.dart';
+
+""";
+
+/**
+ * Constant string used in [generateMainImportFile] as the end of the file.
+ */
+const closing = """
+    default: return null;
+  }
+}
+
+/** User programs should call this before using [localeName] for messages.*/
+Future initializeMessages(String localeName) {
+  initializeInternalMessageLookup(() => new CompositeMessageLookup());
+  var lib = _deferredLibraries[Intl.canonicalizedLocale(localeName)];
+  var load = lib == null ? new Future.value(false) : lib();
+  return load.then((_) =>
+      messageLookup.addLocale(localeName, _findGeneratedMessagesFor));
+}
+
+MessageLookupByLibrary _findGeneratedMessagesFor(locale) {
+  var actualLocale = Intl.verifiedLocale(locale, (x) => _findExact(x) != null,
+      onFailure: (_) => null);
+  if (actualLocale == null) return null;
+  return _findExact(actualLocale);
+}
+""";
diff --git a/intl/lib/intl.dart b/intl/lib/intl.dart
new file mode 100644
index 0000000..bb9298e
--- /dev/null
+++ b/intl/lib/intl.dart
@@ -0,0 +1,401 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * This library provides internationalization and localization. This includes
+ * message formatting and replacement, date and number formatting and parsing,
+ * and utilities for working with Bidirectional text.
+ *
+ * This is part of the [intl package]
+ * (https://pub.dartlang.org/packages/intl).
+ *
+ * For things that require locale or other data, there are multiple different
+ * ways of making that data available, which may require importing different
+ * libraries. See the class comments for more details.
+ *
+ * There is also a simple example application that can be found in the
+ * [example/basic](https://github.com/dart-lang/intl/tree/master/example/basic)
+ * directory.
+ */
+library intl;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:convert';
+import 'dart:math';
+
+import 'date_symbols.dart';
+import 'number_symbols.dart';
+import 'number_symbols_data.dart';
+import 'src/date_format_internal.dart';
+import 'src/intl_helpers.dart';
+
+part 'src/intl/bidi_formatter.dart';
+part 'src/intl/bidi_utils.dart';
+part 'src/intl/date_format.dart';
+part 'src/intl/date_format_field.dart';
+part 'src/intl/date_format_helpers.dart';
+part 'src/intl/number_format.dart';
+
+/**
+ * The Intl class provides a common entry point for internationalization
+ * related tasks. An Intl instance can be created for a particular locale
+ * and used to create a date format via `anIntl.date()`. Static methods
+ * on this class are also used in message formatting.
+ *
+ * Examples:
+ *      today(date) => Intl.message(
+ *          "Today's date is $date",
+ *          name: 'today',
+ *          args: [date],
+ *          desc: 'Indicate the current date',
+ *          examples: {'date' : 'June 8, 2012'});
+ *      print(today(new DateTime.now().toString());
+ *
+ *      howManyPeople(numberOfPeople, place) => Intl.plural(
+ *            zero: 'I see no one at all',
+ *            one: 'I see one other person',
+ *            other: 'I see $numberOfPeople other people')} in $place.''',
+ *          name: 'msg',
+ *          args: [numberOfPeople, place],
+ *          desc: 'Description of how many people are seen in a place.',
+ *          examples: {'numberOfPeople': 3, 'place': 'London'});
+ *
+ * Calling `howManyPeople(2, 'Athens');` would
+ * produce "I see 2 other people in Athens." as output in the default locale.
+ * If run in a different locale it would produce appropriately translated
+ * output.
+ *
+ * For more detailed information on messages and localizing them see
+ * the main [package documentation](https://pub.dartlang.org/packages/intl)
+ *
+ * You can set the default locale.
+ *       Intl.defaultLocale = "pt_BR";
+ *
+ * To temporarily use a locale other than the default, use the `withLocale`
+ * function.
+ *       var todayString = new DateFormat("pt_BR").format(new DateTime.now());
+ *       print(withLocale("pt_BR", () => today(todayString));
+ *
+ * See `tests/message_format_test.dart` for more examples.
+ */
+//TODO(efortuna): documentation example involving the offset parameter?
+
+class Intl {
+  /**
+   * String indicating the locale code with which the message is to be
+   * formatted (such as en-CA).
+   */
+  String _locale;
+
+  /**
+   * The default locale. This defaults to being set from systemLocale, but
+   * can also be set explicitly, and will then apply to any new instances where
+   * the locale isn't specified. Note that a locale parameter to
+   * [Intl.withLocale]
+   * will supercede this value while that operation is active. Using
+   * [Intl.withLocale] may be preferable if you are using different locales
+   * in the same application.
+   */
+  static String get defaultLocale {
+    var zoneLocale = Zone.current[#Intl.locale];
+    return zoneLocale == null ? _defaultLocale : zoneLocale;
+  }
+  static set defaultLocale(String newLocale) => _defaultLocale = newLocale;
+  static String _defaultLocale;
+
+  /**
+   * The system's locale, as obtained from the window.navigator.language
+   * or other operating system mechanism. Note that due to system limitations
+   * this is not automatically set, and must be set by importing one of
+   * intl_browser.dart or intl_standalone.dart and calling findSystemLocale().
+   */
+  static String systemLocale = 'en_US';
+
+  /**
+   * Return a new date format using the specified [pattern].
+   * If [desiredLocale] is not specified, then we default to [locale].
+   */
+  DateFormat date([String pattern, String desiredLocale]) {
+    var actualLocale = (desiredLocale == null) ? locale : desiredLocale;
+    return new DateFormat(pattern, actualLocale);
+  }
+
+  /**
+   * Constructor optionally [aLocale] for specifics of the language
+   * locale to be used, otherwise, we will attempt to infer it (acceptable if
+   * Dart is running on the client, we can infer from the browser/client
+   * preferences).
+   */
+  Intl([String aLocale]) {
+    _locale = aLocale != null ? aLocale : getCurrentLocale();
+  }
+
+  /**
+   * Use this for a message that will be translated for different locales. The
+   * expected usage is that this is inside an enclosing function that only
+   * returns the value of this call and provides a scope for the variables that
+   * will be substituted in the message.
+   *
+   * The parameters are a
+   * [message_str] to be translated, which may be interpolated
+   * based on one or more variables, the [name] of the message, which should
+   * match the enclosing function name, the [args] of the enclosing
+   * function, a [desc] providing a description of usage
+   * and a map of [examples] for each interpolated variable. For example
+   *       hello(yourName) => Intl.message(
+   *         "Hello, $yourName",
+   *         name: "hello",
+   *         args: [name],
+   *         desc: "Say hello",
+   *         examples = {"yourName": "Sparky"}.
+   * The source code will be processed via the analyzer to extract out the
+   * message data, so only a subset of valid Dart code is accepted. In
+   * particular, everything must be literal and cannot refer to variables
+   * outside the scope of the enclosing function. The [examples] map must
+   * be a valid const literal map. Similarly, the [desc] argument must
+   * be a single, simple string. These two arguments will not be used at runtime
+   * but will be extracted from
+   * the source code and used as additional data for translators. For more
+   * information see the "Messages" section of the main [package documentation]
+   * (https://pub.dartlang.org/packages/intl).
+   *
+   * The [name] and [args] arguments are required, and are used at runtime
+   * to look up the localized version and pass the appropriate arguments to it.
+   * We may in the future modify the code during compilation to make manually
+   * passing those arguments unnecessary.
+   */
+  static String message(String message_str, {String desc: '',
+      Map<String, String> examples: const {}, String locale, String name,
+      List<String> args, String meaning}) {
+    return messageLookup.lookupMessage(
+        message_str, desc, examples, locale, name, args, meaning);
+  }
+
+  /**
+   * Return the locale for this instance. If none was set, the locale will
+   * be the default.
+   */
+  String get locale => _locale;
+
+  /**
+   * Return true if the locale exists, or if it is null. The null case
+   * is interpreted to mean that we use the default locale.
+   */
+  static bool _localeExists(localeName) => DateFormat.localeExists(localeName);
+
+  /**
+   * Given [newLocale] return a locale that we have data for that is similar
+   * to it, if possible.
+   *
+   * If [newLocale] is found directly, return it. If it can't be found, look up
+   * based on just the language (e.g. 'en_CA' -> 'en'). Also accepts '-'
+   * as a separator and changes it into '_' for lookup, and changes the
+   * country to uppercase.
+   *
+   * There is a special case that if a locale named "fallback" is present
+   * and has been initialized, this will return that name. This can be useful
+   * for messages where you don't want to just use the text from the original
+   * source code, but wish to have a universal fallback translation.
+   *
+   * Note that null is interpreted as meaning the default locale, so if
+   * [newLocale] is null it will be returned.
+   */
+  static String verifiedLocale(String newLocale, Function localeExists,
+      {Function onFailure: _throwLocaleError}) {
+    // TODO(alanknight): Previously we kept a single verified locale on the Intl
+    // object, but with different verification for different uses, that's more
+    // difficult. As a result, we call this more often. Consider keeping
+    // verified locales for each purpose if it turns out to be a performance
+    // issue.
+    if (newLocale == null) {
+      return verifiedLocale(getCurrentLocale(), localeExists,
+          onFailure: onFailure);
+    }
+    if (localeExists(newLocale)) {
+      return newLocale;
+    }
+    for (var each in
+        [canonicalizedLocale(newLocale), shortLocale(newLocale), "fallback"]) {
+      if (localeExists(each)) {
+        return each;
+      }
+    }
+    return onFailure(newLocale);
+  }
+
+  /**
+   * The default action if a locale isn't found in verifiedLocale. Throw
+   * an exception indicating the locale isn't correct.
+   */
+  static String _throwLocaleError(String localeName) {
+    throw new ArgumentError("Invalid locale '$localeName'");
+  }
+
+  /** Return the short version of a locale name, e.g. 'en_US' => 'en' */
+  static String shortLocale(String aLocale) {
+    if (aLocale.length < 2) return aLocale;
+    return aLocale.substring(0, 2).toLowerCase();
+  }
+
+  /**
+   * Return the name [aLocale] turned into xx_YY where it might possibly be
+   * in the wrong case or with a hyphen instead of an underscore. If
+   * [aLocale] is null, for example, if you tried to get it from IE,
+   * return the current system locale.
+   */
+  static String canonicalizedLocale(String aLocale) {
+    // Locales of length < 5 are presumably two-letter forms, or else malformed.
+    // We return them unmodified and if correct they will be found.
+    // Locales longer than 6 might be malformed, but also do occur. Do as
+    // little as possible to them, but make the '-' be an '_' if it's there.
+    // We treat C as a special case, and assume it wants en_ISO for formatting.
+    // TODO(alanknight): en_ISO is probably not quite right for the C/Posix
+    // locale for formatting. Consider adding C to the formats database.
+    if (aLocale == null) return getCurrentLocale();
+    if (aLocale == "C") return "en_ISO";
+    if (aLocale.length < 5) return aLocale;
+    if (aLocale[2] != '-' && (aLocale[2] != '_')) return aLocale;
+    var region = aLocale.substring(3);
+    // If it's longer than three it's something odd, so don't touch it.
+    if (region.length <= 3) region = region.toUpperCase();
+    return '${aLocale[0]}${aLocale[1]}_$region';
+  }
+
+  /**
+   * Format a message differently depending on [howMany]. Normally used
+   * as part of an `Intl.message` text that is to be translated.
+   * Selects the correct plural form from
+   * the provided alternatives. The [other] named argument is mandatory.
+   */
+  static String plural(int howMany, {zero, one, two, few, many, other,
+      String desc, Map<String, String> examples, String locale, String name,
+      List<String> args, String meaning}) {
+    // If we are passed a name and arguments, then we are operating as a
+    // top-level message, so look up our translation by calling Intl.message
+    // with ourselves as an argument.
+    if (name != null) {
+      return message(plural(howMany,
+              zero: zero,
+              one: one,
+              two: two,
+              few: few,
+              many: many,
+              other: other),
+          name: name, args: args, locale: locale, meaning: meaning);
+    }
+    if (other == null) {
+      throw new ArgumentError("The 'other' named argument must be provided");
+    }
+    // TODO(alanknight): This algorithm needs to be locale-dependent.
+    switch (howMany) {
+      case 0:
+        return (zero == null) ? other : zero;
+      case 1:
+        return (one == null) ? other : one;
+      case 2:
+        return (two == null) ? ((few == null) ? other : few) : two;
+      default:
+        if ((howMany == 3 || howMany == 4) && few != null) return few;
+        if (howMany > 10 && howMany < 100 && many != null) return many;
+        return other;
+    }
+    throw new ArgumentError("Invalid plural usage for $howMany");
+  }
+
+  /**
+   * Format a message differently depending on [targetGender]. Normally used as
+   * part of an Intl.message message that is to be translated.
+   */
+  static String gender(String targetGender, {String male, String female,
+      String other, String desc, Map<String, String> examples, String locale,
+      String name, List<String> args, String meaning}) {
+    // If we are passed a name and arguments, then we are operating as a
+    // top-level message, so look up our translation by calling Intl.message
+    // with ourselves as an argument.
+    if (name != null) {
+      return message(
+          gender(targetGender, male: male, female: female, other: other),
+          name: name, args: args, locale: locale, meaning: meaning);
+    }
+
+    if (other == null) {
+      throw new ArgumentError("The 'other' named argument must be specified");
+    }
+    switch (targetGender) {
+      case "female":
+        return female == null ? other : female;
+      case "male":
+        return male == null ? other : male;
+      default:
+        return other;
+    }
+  }
+
+  /**
+   * Format a message differently depending on [choice]. We look up the value
+   * of [choice] in [cases] and return the result, or an empty string if
+   * it is not found. Normally used as part
+   * of an Intl.message message that is to be translated.
+   */
+  static String select(String choice, Map<String, String> cases, {String desc,
+      Map<String, String> examples, String locale, String name,
+      List<String> args, String meaning}) {
+    // If we are passed a name and arguments, then we are operating as a
+    // top-level message, so look up our translation by calling Intl.message
+    // with ourselves as an argument.
+    if (name != null) {
+      return message(select(choice, cases),
+          name: name, args: args, locale: locale);
+    }
+    var exact = cases[choice];
+    if (exact != null) return exact;
+    var other = cases["other"];
+    if (other ==
+        null) throw new ArgumentError("The 'other' case must be specified");
+    return other;
+  }
+
+  /**
+   * Run [function] with the default locale set to [locale] and
+   * return the result.
+   *
+   * This is run in a zone, so async operations invoked
+   * from within [function] will still have the locale set.
+   *
+   * In simple usage [function] might be a single
+   * `Intl.message()` call or number/date formatting operation. But it can
+   * also be an arbitrary function that calls multiple Intl operations.
+   *
+   * For example
+   *
+   *       Intl.withLocale("fr", () => new NumberFormat.format(123456));
+   *
+   * or
+   *
+   *       hello(name) => Intl.message(
+   *           "Hello $name.",
+   *           name: 'hello',
+   *           args: [name],
+   *           desc: 'Say Hello');
+   *       Intl.withLocale("zh", new Timer(new Duration(milliseconds:10),
+   *           () => print(hello("World")));
+   */
+  static withLocale(String locale, function()) {
+    var canonical = Intl.canonicalizedLocale(locale);
+    return runZoned(function, zoneValues: {#Intl.locale: canonical});
+  }
+
+  /**
+   * Accessor for the current locale. This should always == the default locale,
+   * unless for some reason this gets called inside a message that resets the
+   * locale.
+   */
+  static String getCurrentLocale() {
+    if (defaultLocale == null) defaultLocale = systemLocale;
+    return defaultLocale;
+  }
+
+  toString() => "Intl($locale)";
+}
diff --git a/intl/lib/intl_browser.dart b/intl/lib/intl_browser.dart
new file mode 100644
index 0000000..995b2c1
--- /dev/null
+++ b/intl/lib/intl_browser.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * This provides facilities for Internationalization that are only available
+ * when running in the web browser. You should import only one of this or
+ * intl_standalone.dart. Right now the only thing provided here is the
+ * ability to find the default locale from the browser.
+ */
+
+library intl_browser;
+
+import "dart:async";
+import "dart:html";
+import "intl.dart";
+
+// TODO(alanknight): The need to do this by forcing the user to specially
+// import a particular library is a horrible hack, only done because there
+// seems to be no graceful way to do this at all. Either mirror access on
+// dart2js or the ability to do spawnUri in the browser would be promising
+// as ways to get rid of this requirement.
+/**
+ * Find the system locale, accessed as window.navigator.language, and
+ * set it as the default for internationalization operations in the
+ * [Intl.systemLocale] variable.
+ */
+Future<String> findSystemLocale() {
+  Intl.systemLocale = Intl.canonicalizedLocale(window.navigator.language);
+  return new Future.value(Intl.systemLocale);
+}
diff --git a/intl/lib/intl_standalone.dart b/intl/lib/intl_standalone.dart
new file mode 100644
index 0000000..c69378d
--- /dev/null
+++ b/intl/lib/intl_standalone.dart
@@ -0,0 +1,100 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * This provides facilities for Internationalization that are only available
+ * when running standalone. You should import only one of this or
+ * intl_browser.dart. Right now the only thing provided here is finding
+ * the operating system locale.
+ */
+
+library intl_standalone;
+
+import "dart:async";
+import "dart:io";
+import "intl.dart";
+
+// TODO(alanknight): The need to do this by forcing the user to specially
+// import a particular library is a horrible hack, only done because there
+// seems to be no graceful way to do this at all. Either mirror access on
+// dart2js or the ability to do spawnUri in the browser would be promising
+// as ways to get rid of this requirement.
+/**
+ * Find the system locale, accessed via the appropriate system APIs, and
+ * set it as the default for internationalization operations in
+ * the [Intl.systemLocale] variable. To find it, we
+ * check the "LANG" environment variable on *nix, use the "systeminfo"
+ * command on Windows, and on the Mac check the environment variable "LANG",
+ * and if it's not found, use "defaults read -g AppleLocale". This
+ * is not an ideal way of getting a single system locale, even if that
+ * concept really made sense, but it's a reasonable first approximation that's
+ * not too difficult to get. If it can't find the locale information, it will
+ * not modify [Intl.systemLocale] and the Future will complete with null.
+ */
+Future<String> findSystemLocale() {
+  // On *nix systems we expect this is an environment variable, which is the
+  // easiest thing to check. On a Mac the environment variable may be present
+  // so always check it first. We have no mechanism for this right now on
+  // Windows, so it will just fail.
+  String baseLocale = _checkEnvironmentVariable();
+  if (baseLocale != null) return _setLocale(baseLocale);
+  if (Platform.operatingSystem == 'macos') {
+    return _getAppleDefaults();
+  }
+  // We can't find anything, don't set the system locale and return null.
+  return new Future.value();
+}
+
+/**
+ * Regular expression to match the expected output of reading the defaults
+ * database for AppleLanguages on Mac systems.
+ * e.g. {
+ *     en,
+ *     "pt-PT",
+ *     ...
+ */
+RegExp _appleDefaultsRegex = new RegExp(r'((\w\w)_\w+)');
+
+/**
+ * Check to see if we have a "LANG" environment variable we can use and return
+ * it if found. Otherwise return null;
+ */
+String _checkEnvironmentVariable() {
+  try {
+    return Platform.environment['LANG'];
+  } catch (e) {}
+  return null;
+}
+
+/**
+ * Run the "defaults read -g AppleLocale" command and return the output in
+ * a future.
+ */
+Future _getAppleDefaults() {
+  var p = Process.run('defaults', ['read', '-g', 'AppleLocale']);
+  var myResult = p.then((result) => _checkResult(result, _appleDefaultsRegex));
+  return myResult;
+}
+
+/**
+ * Given [result], find its text and extract the locale from it using
+ * [regex], and set it as the system locale. If the process didn't run correctly
+ * then don't set the variable and return a future that completes with null.
+ */
+Future<String> _checkResult(ProcessResult result, RegExp regex) {
+  if (result.exitCode != 0) return new Future.value();
+  var match = regex.firstMatch(result.stdout);
+  if (match == null) return new Future.value();
+  var locale = match.group(1);
+  _setLocale(locale);
+  return new Future.value(locale);
+}
+
+/**
+ * Set [Intl.systemLocale] to be the canonicalizedLocale of [aLocale].
+ */
+Future<String> _setLocale(aLocale) {
+  Intl.systemLocale = Intl.canonicalizedLocale(aLocale);
+  return new Future.value(Intl.systemLocale);
+}
diff --git a/intl/lib/message_lookup_by_library.dart b/intl/lib/message_lookup_by_library.dart
new file mode 100644
index 0000000..3b668d4
--- /dev/null
+++ b/intl/lib/message_lookup_by_library.dart
@@ -0,0 +1,114 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * Message/plural format library with locale support. This can have different
+ * implementations based on the mechanism for finding the localized versions
+ * of messages. This version expects them to be in a library named e.g.
+ * 'messages_en_US'. The prefix is set in the "initializeMessages" call, which
+ * must be made for a locale before any lookups can be done.
+ *
+ * See Intl class comment or `tests/message_format_test.dart` for more examples.
+ */
+library message_lookup_by_library;
+
+import 'intl.dart';
+
+/**
+ * This is a message lookup mechanism that delegates to one of a collection
+ * of individual [MessageLookupByLibrary] instances.
+ */
+class CompositeMessageLookup {
+  /** A map from locale names to the corresponding lookups. */
+  Map<String, MessageLookupByLibrary> availableMessages = new Map();
+
+  /** Return true if we have a message lookup for [localeName]. */
+  bool localeExists(localeName) => availableMessages.containsKey(localeName);
+
+  /**
+   * Look up the message with the given [name] and [locale] and return
+   * the translated version with the values in [args] interpolated.
+   * If nothing is found, return [message_str]. The [desc] and [examples]
+   * parameters are ignored
+   */
+  String lookupMessage(String message_str, [final String desc = '',
+      final Map examples = const {}, String locale, String name,
+      List<String> args, String meaning]) {
+    var actualLocale = (locale == null) ? Intl.getCurrentLocale() : locale;
+    // For this usage, if the locale doesn't exist for messages, just return
+    // it and we'll fall back to the original version.
+    var verifiedLocale = Intl.verifiedLocale(actualLocale, localeExists,
+        onFailure: (locale) => locale);
+    var messages = availableMessages[verifiedLocale];
+    if (messages == null) return message_str;
+    return messages.lookupMessage(
+        message_str, desc, examples, locale, name, args, meaning);
+  }
+
+  /**
+   * If we do not already have a locale for [localeName] then
+   * [findLocale] will be called and the result stored as the lookup
+   * mechanism for that locale.
+   */
+  addLocale(String localeName, Function findLocale) {
+    if (localeExists(localeName)) return;
+    var canonical = Intl.canonicalizedLocale(localeName);
+    var newLocale = findLocale(canonical);
+    if (newLocale != null) {
+      availableMessages[localeName] = newLocale;
+      availableMessages[canonical] = newLocale;
+    }
+  }
+}
+
+/**
+ * This provides an abstract class for messages looked up in generated code.
+ * Each locale will have a separate subclass of this class with its set of
+ * messages. See generate_localized.dart.
+ */
+abstract class MessageLookupByLibrary {
+  /**
+   * Return the localized version of a message. We are passed the original
+   * version of the message, which consists of a
+   * [message_str] that will be translated, and which may be interpolated
+   * based on one or more variables, a [desc] providing a description of usage
+   * for the [message_str], and a map of [examples] for each data element to be
+   * substituted into the message.
+   *
+   * For example, if message="Hello, $name", then
+   * examples = {'name': 'Sparky'}. If not using the user's default locale, or
+   * if the locale is not easily detectable, explicitly pass [locale].
+   *
+   * The values of [desc] and [examples] are not used at run-time but are only
+   * made available to the translators, so they MUST be simple Strings available
+   * at compile time: no String interpolation or concatenation.
+   * The expected usage of this is inside a function that takes as parameters
+   * the variables used in the interpolated string.
+   *
+   * Ultimately, the information about the enclosing function and its arguments
+   * will be extracted automatically but for the time being it must be passed
+   * explicitly in the [name] and [args] arguments.
+   */
+  String lookupMessage(String message_str, [final String desc = '',
+      final Map examples = const {}, String locale, String name,
+      List<String> args, String meaning]) {
+    if (name == null) return message_str;
+    var function = this[name];
+    return function == null ? message_str : Function.apply(function, args);
+  }
+
+  /** Return our message with the given name */
+  operator [](String messageName) => messages[messageName];
+
+  /**
+   * Subclasses should override this to return a list of their message
+   * functions.
+   */
+  Map<String, Function> get messages;
+
+  /** Subclasses should override this to return their locale, e.g. 'en_US' */
+  String get localeName;
+
+  toString() => localeName;
+}
diff --git a/intl/lib/number_symbols.dart b/intl/lib/number_symbols.dart
new file mode 100644
index 0000000..3eece39
--- /dev/null
+++ b/intl/lib/number_symbols.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2012, 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 number_symbols;
+
+/**
+ * This holds onto information about how a particular locale formats numbers. It
+ * contains strings for things like the decimal separator, digit to use for "0"
+ * and infinity. We expect the data for instances to be generated out of ICU
+ * or a similar reference source.
+ */
+class NumberSymbols {
+  final String NAME;
+  final String DECIMAL_SEP,
+      GROUP_SEP,
+      PERCENT,
+      ZERO_DIGIT,
+      PLUS_SIGN,
+      MINUS_SIGN,
+      EXP_SYMBOL,
+      PERMILL,
+      INFINITY,
+      NAN,
+      DECIMAL_PATTERN,
+      SCIENTIFIC_PATTERN,
+      PERCENT_PATTERN,
+      CURRENCY_PATTERN,
+      DEF_CURRENCY_CODE;
+
+  const NumberSymbols({this.NAME, this.DECIMAL_SEP, this.GROUP_SEP,
+      this.PERCENT, this.ZERO_DIGIT, this.PLUS_SIGN, this.MINUS_SIGN,
+      this.EXP_SYMBOL, this.PERMILL, this.INFINITY, this.NAN,
+      this.DECIMAL_PATTERN, this.SCIENTIFIC_PATTERN, this.PERCENT_PATTERN,
+      this.CURRENCY_PATTERN, this.DEF_CURRENCY_CODE});
+
+  toString() => NAME;
+}
diff --git a/intl/lib/number_symbols_data.dart b/intl/lib/number_symbols_data.dart
new file mode 100644
index 0000000..a1f6821
--- /dev/null
+++ b/intl/lib/number_symbols_data.dart
@@ -0,0 +1,2044 @@
+// 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.
+
+/**
+ * Date/time formatting symbols for all locales.
+ *
+ * DO NOT EDIT. This file is autogenerated by script.  See
+ * http://go/generate_number_constants.py using the --for_dart flag.
+ *
+ * Before checkin, this file could have been manually edited. This is
+ * to incorporate changes before we could correct CLDR. All manual
+ * modification must be documented in this section, and should be
+ * removed after those changes land to CLDR.
+ */
+
+library number_symbol_data;
+
+import "number_symbols.dart";
+
+Map numberFormatSymbols = const {
+  /**
+   * Number formatting symbols for locale af.
+   */
+  "af": const NumberSymbols(
+      NAME: "af",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'ZAR'),
+  /**
+   * Number formatting symbols for locale am.
+   */
+  "am": const NumberSymbols(
+      NAME: "am",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'ETB'),
+  /**
+   * Number formatting symbols for locale ar.
+   */
+  "ar": const NumberSymbols(
+      NAME: "ar",
+      DECIMAL_SEP: '\u066B',
+      GROUP_SEP: '\u066C',
+      PERCENT: '\u066A',
+      ZERO_DIGIT: '\u0660',
+      PLUS_SIGN: '\u200F+',
+      MINUS_SIGN: '\u200F-',
+      EXP_SYMBOL: '\u0627\u0633',
+      PERMILL: '\u0609',
+      INFINITY: '\u221E',
+      NAN: '\u0644\u064A\u0633\u00A0\u0631\u0642\u0645',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
+      DEF_CURRENCY_CODE: 'EGP'),
+  /**
+   * Number formatting symbols for locale az.
+   */
+  "az": const NumberSymbols(
+      NAME: "az",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
+      DEF_CURRENCY_CODE: 'AZN'),
+  /**
+   * Number formatting symbols for locale bg.
+   */
+  "bg": const NumberSymbols(
+      NAME: "bg",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'BGN'),
+  /**
+   * Number formatting symbols for locale bn.
+   */
+  "bn": const NumberSymbols(
+      NAME: "bn",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '\u09E6',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: '\u09B8\u0982\u0996\u09CD\u09AF\u09BE\u00A0\u09A8\u09BE',
+      DECIMAL_PATTERN: '#,##,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##,##0%',
+      CURRENCY_PATTERN: '#,##,##0.00\u00A4',
+      DEF_CURRENCY_CODE: 'BDT'),
+  /**
+   * Number formatting symbols for locale br.
+   */
+  "br": const NumberSymbols(
+      NAME: "br",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale ca.
+   */
+  "ca": const NumberSymbols(
+      NAME: "ca",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale chr.
+   */
+  "chr": const NumberSymbols(
+      NAME: "chr",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'USD'),
+  /**
+   * Number formatting symbols for locale cs.
+   */
+  "cs": const NumberSymbols(
+      NAME: "cs",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'CZK'),
+  /**
+   * Number formatting symbols for locale cy.
+   */
+  "cy": const NumberSymbols(
+      NAME: "cy",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'GBP'),
+  /**
+   * Number formatting symbols for locale da.
+   */
+  "da": const NumberSymbols(
+      NAME: "da",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'DKK'),
+  /**
+   * Number formatting symbols for locale de.
+   */
+  "de": const NumberSymbols(
+      NAME: "de",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale de_AT.
+   */
+  "de_AT": const NumberSymbols(
+      NAME: "de_AT",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale de_CH.
+   */
+  "de_CH": const NumberSymbols(
+      NAME: "de_CH",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: '\'',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4-#,##0.00',
+      DEF_CURRENCY_CODE: 'CHF'),
+  /**
+   * Number formatting symbols for locale el.
+   */
+  "el": const NumberSymbols(
+      NAME: "el",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'e',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale en.
+   */
+  "en": const NumberSymbols(
+      NAME: "en",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'USD'),
+  /**
+   * Number formatting symbols for locale en_AU.
+   */
+  "en_AU": const NumberSymbols(
+      NAME: "en_AU",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'AUD'),
+  /**
+   * Number formatting symbols for locale en_GB.
+   */
+  "en_GB": const NumberSymbols(
+      NAME: "en_GB",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'GBP'),
+  /**
+   * Number formatting symbols for locale en_IE.
+   */
+  "en_IE": const NumberSymbols(
+      NAME: "en_IE",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale en_IN.
+   */
+  "en_IN": const NumberSymbols(
+      NAME: "en_IN",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##,##0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
+      DEF_CURRENCY_CODE: 'INR'),
+  /**
+   * Number formatting symbols for locale en_SG.
+   */
+  "en_SG": const NumberSymbols(
+      NAME: "en_SG",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'SGD'),
+  /**
+   * Number formatting symbols for locale en_US.
+   */
+  "en_US": const NumberSymbols(
+      NAME: "en_US",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'USD'),
+  /**
+   * Number formatting symbols for locale en_ZA.
+   */
+  "en_ZA": const NumberSymbols(
+      NAME: "en_ZA",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'ZAR'),
+  /**
+   * Number formatting symbols for locale es.
+   */
+  "es": const NumberSymbols(
+      NAME: "es",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale es_419.
+   */
+  "es_419": const NumberSymbols(
+      NAME: "es_419",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'MXN'),
+  /**
+   * Number formatting symbols for locale es_ES.
+   */
+  "es_ES": const NumberSymbols(
+      NAME: "es_ES",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale et.
+   */
+  "et": const NumberSymbols(
+      NAME: "et",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: '\u00D710^',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale eu.
+   */
+  "eu": const NumberSymbols(
+      NAME: "eu",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '%\u00A0#,##0',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale fa.
+   */
+  "fa": const NumberSymbols(
+      NAME: "fa",
+      DECIMAL_SEP: '\u066B',
+      GROUP_SEP: '\u066C',
+      PERCENT: '\u066A',
+      ZERO_DIGIT: '\u06F0',
+      PLUS_SIGN: '\u200E+\u200E',
+      MINUS_SIGN: '\u200E\u2212',
+      EXP_SYMBOL: '\u00D7\u06F1\u06F0^',
+      PERMILL: '\u0609',
+      INFINITY: '\u221E',
+      NAN: '\u0646\u0627\u0639\u062F\u062F',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u200E\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'IRR'),
+  /**
+   * Number formatting symbols for locale fi.
+   */
+  "fi": const NumberSymbols(
+      NAME: "fi",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '\u2212',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'ep\u00E4luku',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale fil.
+   */
+  "fil": const NumberSymbols(
+      NAME: "fil",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'PHP'),
+  /**
+   * Number formatting symbols for locale fr.
+   */
+  "fr": const NumberSymbols(
+      NAME: "fr",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale fr_CA.
+   */
+  "fr_CA": const NumberSymbols(
+      NAME: "fr_CA",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'CAD'),
+  /**
+   * Number formatting symbols for locale ga.
+   */
+  "ga": const NumberSymbols(
+      NAME: "ga",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale gl.
+   */
+  "gl": const NumberSymbols(
+      NAME: "gl",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale gsw.
+   */
+  "gsw": const NumberSymbols(
+      NAME: "gsw",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: '\u2019',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '\u2212',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'CHF'),
+  /**
+   * Number formatting symbols for locale gu.
+   */
+  "gu": const NumberSymbols(
+      NAME: "gu",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##,##0.00',
+      DEF_CURRENCY_CODE: 'INR'),
+  /**
+   * Number formatting symbols for locale haw.
+   */
+  "haw": const NumberSymbols(
+      NAME: "haw",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'USD'),
+  /**
+   * Number formatting symbols for locale he.
+   */
+  "he": const NumberSymbols(
+      NAME: "he",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '\u200E+',
+      MINUS_SIGN: '\u200E-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'ILS'),
+  /**
+   * Number formatting symbols for locale hi.
+   */
+  "hi": const NumberSymbols(
+      NAME: "hi",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##,##0.00',
+      DEF_CURRENCY_CODE: 'INR'),
+  /**
+   * Number formatting symbols for locale hr.
+   */
+  "hr": const NumberSymbols(
+      NAME: "hr",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'HRK'),
+  /**
+   * Number formatting symbols for locale hu.
+   */
+  "hu": const NumberSymbols(
+      NAME: "hu",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'HUF'),
+  /**
+   * Number formatting symbols for locale hy.
+   */
+  "hy": const NumberSymbols(
+      NAME: "hy",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#0%',
+      CURRENCY_PATTERN: '#0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'AMD'),
+  /**
+   * Number formatting symbols for locale id.
+   */
+  "id": const NumberSymbols(
+      NAME: "id",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'IDR'),
+  /**
+   * Number formatting symbols for locale in.
+   */
+  "in": const NumberSymbols(
+      NAME: "in",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'IDR'),
+  /**
+   * Number formatting symbols for locale is.
+   */
+  "is": const NumberSymbols(
+      NAME: "is",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'ISK'),
+  /**
+   * Number formatting symbols for locale it.
+   */
+  "it": const NumberSymbols(
+      NAME: "it",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale iw.
+   */
+  "iw": const NumberSymbols(
+      NAME: "iw",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '\u200E+',
+      MINUS_SIGN: '\u200E-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'ILS'),
+  /**
+   * Number formatting symbols for locale ja.
+   */
+  "ja": const NumberSymbols(
+      NAME: "ja",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'JPY'),
+  /**
+   * Number formatting symbols for locale ka.
+   */
+  "ka": const NumberSymbols(
+      NAME: "ka",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: '\u10D0\u10E0\u00A0\u10D0\u10E0\u10D8\u10E1\u00A0\u10E0\u10D8\u10EA\u10EE\u10D5\u10D8',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'GEL'),
+  /**
+   * Number formatting symbols for locale kk.
+   */
+  "kk": const NumberSymbols(
+      NAME: "kk",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'KZT'),
+  /**
+   * Number formatting symbols for locale km.
+   */
+  "km": const NumberSymbols(
+      NAME: "km",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'KHR'),
+  /**
+   * Number formatting symbols for locale kn.
+   */
+  "kn": const NumberSymbols(
+      NAME: "kn",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: '\u0C88',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'INR'),
+  /**
+   * Number formatting symbols for locale ko.
+   */
+  "ko": const NumberSymbols(
+      NAME: "ko",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'KRW'),
+  /**
+   * Number formatting symbols for locale ky.
+   */
+  "ky": const NumberSymbols(
+      NAME: "ky",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: '\u0441\u0430\u043D\u00A0\u044D\u043C\u0435\u0441',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'KGS'),
+  /**
+   * Number formatting symbols for locale ln.
+   */
+  "ln": const NumberSymbols(
+      NAME: "ln",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'CDF'),
+  /**
+   * Number formatting symbols for locale lo.
+   */
+  "lo": const NumberSymbols(
+      NAME: "lo",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: '\u0E9A\u0ECD\u0EC8\u0EC1\u0EA1\u0EC8\u0E99\u0EC2\u0E95\u0EC0\u0EA5\u0E81',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00;\u00A4-#,##0.00',
+      DEF_CURRENCY_CODE: 'LAK'),
+  /**
+   * Number formatting symbols for locale lt.
+   */
+  "lt": const NumberSymbols(
+      NAME: "lt",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '\u2212',
+      EXP_SYMBOL: '\u00D710^',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'LTL'),
+  /**
+   * Number formatting symbols for locale lv.
+   */
+  "lv": const NumberSymbols(
+      NAME: "lv",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'nav\u00A0skaitlis',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale mk.
+   */
+  "mk": const NumberSymbols(
+      NAME: "mk",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
+      DEF_CURRENCY_CODE: 'MKD'),
+  /**
+   * Number formatting symbols for locale ml.
+   */
+  "ml": const NumberSymbols(
+      NAME: "ml",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##,##0%',
+      CURRENCY_PATTERN: '#,##,##0.00\u00A4',
+      DEF_CURRENCY_CODE: 'INR'),
+  /**
+   * Number formatting symbols for locale mn.
+   */
+  "mn": const NumberSymbols(
+      NAME: "mn",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
+      DEF_CURRENCY_CODE: 'MNT'),
+  /**
+   * Number formatting symbols for locale mr.
+   */
+  "mr": const NumberSymbols(
+      NAME: "mr",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '\u0966',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '[#E0]',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'INR'),
+  /**
+   * Number formatting symbols for locale ms.
+   */
+  "ms": const NumberSymbols(
+      NAME: "ms",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'MYR'),
+  /**
+   * Number formatting symbols for locale mt.
+   */
+  "mt": const NumberSymbols(
+      NAME: "mt",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale my.
+   */
+  "my": const NumberSymbols(
+      NAME: "my",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '\u1040',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: '\u1002\u100F\u1014\u103A\u1038\u1019\u101F\u102F\u1010\u103A\u101E\u1031\u102C',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
+      DEF_CURRENCY_CODE: 'MMK'),
+  /**
+   * Number formatting symbols for locale nb.
+   */
+  "nb": const NumberSymbols(
+      NAME: "nb",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '\u2212',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
+      DEF_CURRENCY_CODE: 'NOK'),
+  /**
+   * Number formatting symbols for locale ne.
+   */
+  "ne": const NumberSymbols(
+      NAME: "ne",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '\u0966',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'NPR'),
+  /**
+   * Number formatting symbols for locale nl.
+   */
+  "nl": const NumberSymbols(
+      NAME: "nl",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00;\u00A4\u00A0#,##0.00-',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale no.
+   */
+  "no": const NumberSymbols(
+      NAME: "no",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '\u2212',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
+      DEF_CURRENCY_CODE: 'NOK'),
+  /**
+   * Number formatting symbols for locale no_NO.
+   */
+  "no_NO": const NumberSymbols(
+      NAME: "no_NO",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '\u2212',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
+      DEF_CURRENCY_CODE: 'NOK'),
+  /**
+   * Number formatting symbols for locale or.
+   */
+  "or": const NumberSymbols(
+      NAME: "or",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##,##0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
+      DEF_CURRENCY_CODE: 'INR'),
+  /**
+   * Number formatting symbols for locale pa.
+   */
+  "pa": const NumberSymbols(
+      NAME: "pa",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##,##0.00',
+      DEF_CURRENCY_CODE: 'INR'),
+  /**
+   * Number formatting symbols for locale pl.
+   */
+  "pl": const NumberSymbols(
+      NAME: "pl",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'PLN'),
+  /**
+   * Number formatting symbols for locale pt.
+   */
+  "pt": const NumberSymbols(
+      NAME: "pt",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'BRL'),
+  /**
+   * Number formatting symbols for locale pt_BR.
+   */
+  "pt_BR": const NumberSymbols(
+      NAME: "pt_BR",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'BRL'),
+  /**
+   * Number formatting symbols for locale pt_PT.
+   */
+  "pt_PT": const NumberSymbols(
+      NAME: "pt_PT",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale ro.
+   */
+  "ro": const NumberSymbols(
+      NAME: "ro",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'RON'),
+  /**
+   * Number formatting symbols for locale ru.
+   */
+  "ru": const NumberSymbols(
+      NAME: "ru",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: '\u043D\u0435\u00A0\u0447\u0438\u0441\u043B\u043E',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'RUB'),
+  /**
+   * Number formatting symbols for locale si.
+   */
+  "si": const NumberSymbols(
+      NAME: "si",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'LKR'),
+  /**
+   * Number formatting symbols for locale sk.
+   */
+  "sk": const NumberSymbols(
+      NAME: "sk",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale sl.
+   */
+  "sl": const NumberSymbols(
+      NAME: "sl",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'e',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'EUR'),
+  /**
+   * Number formatting symbols for locale sq.
+   */
+  "sq": const NumberSymbols(
+      NAME: "sq",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'ALL'),
+  /**
+   * Number formatting symbols for locale sr.
+   */
+  "sr": const NumberSymbols(
+      NAME: "sr",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'RSD'),
+  /**
+   * Number formatting symbols for locale sv.
+   */
+  "sv": const NumberSymbols(
+      NAME: "sv",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '\u2212',
+      EXP_SYMBOL: '\u00D710^',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: '\u00A4\u00A4\u00A4',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0\u00A0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'SEK'),
+  /**
+   * Number formatting symbols for locale sw.
+   */
+  "sw": const NumberSymbols(
+      NAME: "sw",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'TZS'),
+  /**
+   * Number formatting symbols for locale ta.
+   */
+  "ta": const NumberSymbols(
+      NAME: "ta",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##,##0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
+      DEF_CURRENCY_CODE: 'INR'),
+  /**
+   * Number formatting symbols for locale te.
+   */
+  "te": const NumberSymbols(
+      NAME: "te",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'INR'),
+  /**
+   * Number formatting symbols for locale th.
+   */
+  "th": const NumberSymbols(
+      NAME: "th",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'THB'),
+  /**
+   * Number formatting symbols for locale tl.
+   */
+  "tl": const NumberSymbols(
+      NAME: "tl",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'PHP'),
+  /**
+   * Number formatting symbols for locale tr.
+   */
+  "tr": const NumberSymbols(
+      NAME: "tr",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '%#,##0',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'TRY'),
+  /**
+   * Number formatting symbols for locale uk.
+   */
+  "uk": const NumberSymbols(
+      NAME: "uk",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: '\u0415',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: '\u041D\u0435\u00A0\u0447\u0438\u0441\u043B\u043E',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'UAH'),
+  /**
+   * Number formatting symbols for locale ur.
+   */
+  "ur": const NumberSymbols(
+      NAME: "ur",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '\u200E+',
+      MINUS_SIGN: '\u200E-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00\u200E',
+      DEF_CURRENCY_CODE: 'PKR'),
+  /**
+   * Number formatting symbols for locale uz.
+   */
+  "uz": const NumberSymbols(
+      NAME: "uz",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '\u00A0',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
+      DEF_CURRENCY_CODE: 'UZS'),
+  /**
+   * Number formatting symbols for locale vi.
+   */
+  "vi": const NumberSymbols(
+      NAME: "vi",
+      DECIMAL_SEP: ',',
+      GROUP_SEP: '.',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
+      DEF_CURRENCY_CODE: 'VND'),
+  /**
+   * Number formatting symbols for locale zh.
+   */
+  "zh": const NumberSymbols(
+      NAME: "zh",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
+      DEF_CURRENCY_CODE: 'CNY'),
+  /**
+   * Number formatting symbols for locale zh_CN.
+   */
+  "zh_CN": const NumberSymbols(
+      NAME: "zh_CN",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
+      DEF_CURRENCY_CODE: 'CNY'),
+  /**
+   * Number formatting symbols for locale zh_HK.
+   */
+  "zh_HK": const NumberSymbols(
+      NAME: "zh_HK",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: '\u975E\u6578\u503C',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'HKD'),
+  /**
+   * Number formatting symbols for locale zh_TW.
+   */
+  "zh_TW": const NumberSymbols(
+      NAME: "zh_TW",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: '\u975E\u6578\u503C',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'TWD'),
+  /**
+   * Number formatting symbols for locale zu.
+   */
+  "zu": const NumberSymbols(
+      NAME: "zu",
+      DECIMAL_SEP: '.',
+      GROUP_SEP: ',',
+      PERCENT: '%',
+      ZERO_DIGIT: '0',
+      PLUS_SIGN: '+',
+      MINUS_SIGN: '-',
+      EXP_SYMBOL: 'E',
+      PERMILL: '\u2030',
+      INFINITY: '\u221E',
+      NAN: 'I-NaN',
+      DECIMAL_PATTERN: '#,##0.###',
+      SCIENTIFIC_PATTERN: '#E0',
+      PERCENT_PATTERN: '#,##0%',
+      CURRENCY_PATTERN: '\u00A4#,##0.00',
+      DEF_CURRENCY_CODE: 'ZAR')
+};
diff --git a/intl/lib/src/data/dates/README.txt b/intl/lib/src/data/dates/README.txt
new file mode 100644
index 0000000..813b97c
--- /dev/null
+++ b/intl/lib/src/data/dates/README.txt
@@ -0,0 +1,16 @@
+These directories hold data used for date formatting when you read the data
+from JSON files on disk or over the network. There are two directories
+
+  patterns - Holds the mapping form skeletons to a locale-specific format. The
+data is exactly equivalent to that in date_time_patterns.dart and is generated
+from it using the tools/generate_locale_data_files.dart program.
+ 
+  symbols - Holds the symbols used for formatting in a particular locale. This
+includes things like the names of weekdays and months. The data is the same as
+that in date_symbol_data_locale.dart and is generated from it using the 
+tools/generate_locale_data_files.dart program.
+
+There is also a localeList.dart file that lists all of the 
+locales in these directories and which is sourced by other files in order to 
+have a list of the available locales.
+ 
diff --git a/intl/lib/src/data/dates/locale_list.dart b/intl/lib/src/data/dates/locale_list.dart
new file mode 100644
index 0000000..2c55f28
--- /dev/null
+++ b/intl/lib/src/data/dates/locale_list.dart
@@ -0,0 +1,108 @@
+// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this sourcecode is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// Hard-coded list of all available locales for dates.
+final availableLocalesForDateFormatting = const [
+  "en_ISO",
+  "af",
+  "am",
+  "ar",
+  "az",
+  "bg",
+  "bn",
+  "br",
+  "ca",
+  "chr",
+  "cs",
+  "cy",
+  "da",
+  "de",
+  "de_AT",
+  "de_CH",
+  "el",
+  "en",
+  "en_AU",
+  "en_GB",
+  "en_IE",
+  "en_IN",
+  "en_SG",
+  "en_US",
+  "en_ZA",
+  "es",
+  "es_419",
+  "es_ES",
+  "et",
+  "eu",
+  "fa",
+  "fi",
+  "fil",
+  "fr",
+  "fr_CA",
+  "gl",
+  "gsw",
+  "gu",
+  "haw",
+  "he",
+  "hi",
+  "hr",
+  "hu",
+  "hy",
+  "id",
+  "in",
+  "is",
+  "it",
+  "iw",
+  "ja",
+  "ka",
+  "kk",
+  "km",
+  "kn",
+  "ko",
+  "ky",
+  "ln",
+  "lo",
+  "lt",
+  "lv",
+  "mk",
+  "ml",
+  "mn",
+  "mr",
+  "ms",
+  "mt",
+  "my",
+  "nb",
+  "ne",
+  "nl",
+  "no",
+  "no_NO",
+  "or",
+  "pa",
+  "pl",
+  "pt",
+  "pt_BR",
+  "pt_PT",
+  "ro",
+  "ru",
+  "si",
+  "sk",
+  "sl",
+  "sq",
+  "sr",
+  "sv",
+  "sw",
+  "ta",
+  "te",
+  "th",
+  "tl",
+  "tr",
+  "uk",
+  "ur",
+  "uz",
+  "vi",
+  "zh",
+  "zh_CN",
+  "zh_HK",
+  "zh_TW",
+  "zu"
+];
diff --git a/intl/lib/src/data/dates/patterns/af.json b/intl/lib/src/data/dates/patterns/af.json
new file mode 100644
index 0000000..d34eeab
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/af.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M/d","MEd":"EEE, M/d","MMM":"LLL","MMMd":"MMM d","MMMEd":"EEE, MMM d","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"EEEE, MMMM d","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"M/d/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/am.json b/intl/lib/src/data/dates/patterns/am.json
new file mode 100644
index 0000000..08e24d6
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/am.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M/d","MEd":"EEE, M/d","MMM":"LLL","MMMd":"MMM d","MMMEd":"EEE, MMM d","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"EEEE, MMMM d","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE፣ d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE፣ MMM d y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"H","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/ar.json b/intl/lib/src/data/dates/patterns/ar.json
new file mode 100644
index 0000000..0f787ff
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/ar.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/‏M","MEd":"EEE، d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE، d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE، d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M‏/y","yMd":"d‏/M‏/y","yMEd":"EEE، d/‏M/‏y","yMMM":"MMM y","yMMMd":"d MMM، y","yMMMEd":"EEE، d MMM، y","yMMMM":"MMMM y","yMMMMd":"d MMMM، y","yMMMMEEEEd":"EEEE، d MMMM، y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/az.json b/intl/lib/src/data/dates/patterns/az.json
new file mode 100644
index 0000000..4d2176e
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/az.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"dd.MM","MEd":"dd.MM, EEE","MMM":"LLL","MMMd":"d MMM","MMMEd":"d MMM, EEE","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"d MMMM, EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM.y","yMd":"dd.MM.y","yMEd":"dd.MM.y, EEE","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"d MMM y, EEE","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"d MMMM y, EEEE","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/bg.json b/intl/lib/src/data/dates/patterns/bg.json
new file mode 100644
index 0000000..5c50e33
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/bg.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d.MM","MEd":"EEE, d.MM","MMM":"MM","MMMd":"d.MM","MMMEd":"EEE, d.MM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y 'г'.","yM":"M.y 'г'.","yMd":"d.MM.y 'г'.","yMEd":"EEE, d.MM.y 'г'.","yMMM":"MM.y 'г'.","yMMMd":"d.MM.y 'г'.","yMMMEd":"EEE, d.MM.y 'г'.","yMMMM":"MMMM y 'г'.","yMMMMd":"d MMMM y 'г'.","yMMMMEEEEd":"EEEE, d MMMM y 'г'.","yQQQ":"QQQ y 'г'.","yQQQQ":"QQQQ y 'г'.","H":"H","Hm":"H:mm","Hms":"H:mm:ss","j":"H","jm":"H:mm","jms":"H:mm:ss","jmv":"H:mm v","jmz":"H:mm z","jz":"H z","m":"m","ms":"m:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/bn.json b/intl/lib/src/data/dates/patterns/bn.json
new file mode 100644
index 0000000..e58647b
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/bn.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d-M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM, y","yMMMEd":"EEE, d MMM, y","yMMMM":"MMMM y","yMMMMd":"d MMMM, y","yMMMMEEEEd":"EEEE, d MMMM, y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/br.json b/intl/lib/src/data/dates/patterns/br.json
new file mode 100644
index 0000000..45a919d
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/br.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"MM-dd","MEd":"MM-dd, EEE","MMM":"LLL","MMMd":"MMM d","MMMEd":"MMM d, EEE","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"MMMM d, EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"y-MM","yMd":"y-MM-dd","yMEd":"y-MM-dd, EEE","yMMM":"y MMM","yMMMd":"y MMM d","yMMMEd":"y MMM d, EEE","yMMMM":"y MMMM","yMMMMd":"y MMMM d","yMMMMEEEEd":"y MMMM d, EEEE","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/ca.json b/intl/lib/src/data/dates/patterns/ca.json
new file mode 100644
index 0000000..322ccbe
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/ca.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"LLL y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM, y","yMMMM":"LLLL 'de' y","yMMMMd":"d MMMM 'de' y","yMMMMEEEEd":"EEEE, d MMMM 'de' y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"H","Hm":"HH:mm","Hms":"HH:mm:ss","j":"H","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"H z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/chr.json b/intl/lib/src/data/dates/patterns/chr.json
new file mode 100644
index 0000000..afe8598
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/chr.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"M","Md":"M/d","MEd":"EEE, M/d","MMM":"LLL","MMMd":"MMM d","MMMEd":"MMM d, EEE","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"MMMM d, EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"M/d/y","yMEd":"EEE, M/d/y","yMMM":"MMM y","yMMMd":"MMM d, y","yMMMEd":"EEE, MMM d, y","yMMMM":"MMMM y","yMMMMd":"MMMM d, y","yMMMMEEEEd":"EEEE, MMMM d, y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"H","Hm":"H:mm","Hms":"H:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/cs.json b/intl/lib/src/data/dates/patterns/cs.json
new file mode 100644
index 0000000..1a09695
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/cs.json
@@ -0,0 +1 @@
+{"d":"d.","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d. M.","MEd":"EEE d. M.","MMM":"LLL","MMMd":"d. M.","MMMEd":"EEE d. M.","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d. M. y","yMEd":"EEE d. M. y","yMMM":"LLLL y","yMMMd":"d. M. y","yMMMEd":"EEE d. M. y","yMMMM":"LLLL y","yMMMMd":"d. MMMM y","yMMMMEEEEd":"EEEE d. MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"H","Hm":"H:mm","Hms":"H:mm:ss","j":"H","jm":"H:mm","jms":"H:mm:ss","jmv":"H:mm v","jmz":"H:mm z","jz":"H z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/cy.json b/intl/lib/src/data/dates/patterns/cy.json
new file mode 100644
index 0000000..021ce1d
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/cy.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/da.json b/intl/lib/src/data/dates/patterns/da.json
new file mode 100644
index 0000000..ca2bf7d
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/da.json
@@ -0,0 +1 @@
+{"d":"d.","E":"EEE","EEEE":"EEEE","LLL":"MMM","LLLL":"MMMM","M":"M","Md":"d/M","MEd":"EEE d/M","MMM":"MMM","MMMd":"d. MMM","MMMEd":"EEE d. MMM","MMMM":"MMMM","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE d/M/y","yMMM":"MMM y","yMMMd":"d. MMM y","yMMMEd":"EEE d. MMM y","yMMMM":"MMMM y","yMMMMd":"d. MMMM y","yMMMMEEEEd":"EEEE 'den' d. MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH.mm","Hms":"HH.mm.ss","j":"HH","jm":"HH.mm","jms":"HH.mm.ss","jmv":"HH.mm v","jmz":"HH.mm z","jz":"HH z","m":"m","ms":"mm.ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/de.json b/intl/lib/src/data/dates/patterns/de.json
new file mode 100644
index 0000000..df26113
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/de.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d.M.","MEd":"EEE, d.M.","MMM":"LLL","MMMd":"d. MMM","MMMEd":"EEE, d. MMM","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE, d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M.y","yMd":"d.M.y","yMEd":"EEE, d.M.y","yMMM":"MMM y","yMMMd":"d. MMM y","yMMMEd":"EEE, d. MMM y","yMMMM":"MMMM y","yMMMMd":"d. MMMM y","yMMMMEEEEd":"EEEE, d. MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH 'Uhr'","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH 'Uhr'","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH 'Uhr' z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/de_AT.json b/intl/lib/src/data/dates/patterns/de_AT.json
new file mode 100644
index 0000000..df26113
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/de_AT.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d.M.","MEd":"EEE, d.M.","MMM":"LLL","MMMd":"d. MMM","MMMEd":"EEE, d. MMM","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE, d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M.y","yMd":"d.M.y","yMEd":"EEE, d.M.y","yMMM":"MMM y","yMMMd":"d. MMM y","yMMMEd":"EEE, d. MMM y","yMMMM":"MMMM y","yMMMMd":"d. MMMM y","yMMMMEEEEd":"EEEE, d. MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH 'Uhr'","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH 'Uhr'","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH 'Uhr' z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/de_CH.json b/intl/lib/src/data/dates/patterns/de_CH.json
new file mode 100644
index 0000000..df26113
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/de_CH.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d.M.","MEd":"EEE, d.M.","MMM":"LLL","MMMd":"d. MMM","MMMEd":"EEE, d. MMM","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE, d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M.y","yMd":"d.M.y","yMEd":"EEE, d.M.y","yMMM":"MMM y","yMMMd":"d. MMM y","yMMMEd":"EEE, d. MMM y","yMMMM":"MMMM y","yMMMMd":"d. MMMM y","yMMMMEEEEd":"EEEE, d. MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH 'Uhr'","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH 'Uhr'","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH 'Uhr' z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/el.json b/intl/lib/src/data/dates/patterns/el.json
new file mode 100644
index 0000000..0dc654c
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/el.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"LLL y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"LLLL y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/en.json b/intl/lib/src/data/dates/patterns/en.json
new file mode 100644
index 0000000..9141e1d
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/en.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M/d","MEd":"EEE, M/d","MMM":"LLL","MMMd":"MMM d","MMMEd":"EEE, MMM d","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"EEEE, MMMM d","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"M/d/y","yMEd":"EEE, M/d/y","yMMM":"MMM y","yMMMd":"MMM d, y","yMMMEd":"EEE, MMM d, y","yMMMM":"MMMM y","yMMMMd":"MMMM d, y","yMMMMEEEEd":"EEEE, MMMM d, y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/en_AU.json b/intl/lib/src/data/dates/patterns/en_AU.json
new file mode 100644
index 0000000..fcbe5fa
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/en_AU.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"LL","Md":"dd/MM","MEd":"EEE dd/MM","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/en_GB.json b/intl/lib/src/data/dates/patterns/en_GB.json
new file mode 100644
index 0000000..6809dbf
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/en_GB.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"LL","Md":"dd/MM","MEd":"EEE dd/MM","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM/y","yMd":"dd/MM/y","yMEd":"EEE, dd/MM/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/en_IE.json b/intl/lib/src/data/dates/patterns/en_IE.json
new file mode 100644
index 0000000..8b8b9bb
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/en_IE.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"LL","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/en_IN.json b/intl/lib/src/data/dates/patterns/en_IN.json
new file mode 100644
index 0000000..bea43e2
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/en_IN.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"LL","Md":"dd/MM","MEd":"EEE dd/MM","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM/y","yMd":"d/M/y","yMEd":"EEE d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE d MMM, y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/en_ISO.json b/intl/lib/src/data/dates/patterns/en_ISO.json
new file mode 100644
index 0000000..9141e1d
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/en_ISO.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M/d","MEd":"EEE, M/d","MMM":"LLL","MMMd":"MMM d","MMMEd":"EEE, MMM d","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"EEEE, MMMM d","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"M/d/y","yMEd":"EEE, M/d/y","yMMM":"MMM y","yMMMd":"MMM d, y","yMMMEd":"EEE, MMM d, y","yMMMM":"MMMM y","yMMMMd":"MMMM d, y","yMMMMEEEEd":"EEEE, MMMM d, y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/en_SG.json b/intl/lib/src/data/dates/patterns/en_SG.json
new file mode 100644
index 0000000..8ce600e
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/en_SG.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"LL","Md":"dd/MM","MEd":"EEE dd/MM","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM/y","yMd":"dd/MM/y","yMEd":"EEE, dd/MM/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/en_US.json b/intl/lib/src/data/dates/patterns/en_US.json
new file mode 100644
index 0000000..9141e1d
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/en_US.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M/d","MEd":"EEE, M/d","MMM":"LLL","MMMd":"MMM d","MMMEd":"EEE, MMM d","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"EEEE, MMMM d","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"M/d/y","yMEd":"EEE, M/d/y","yMMM":"MMM y","yMMMd":"MMM d, y","yMMMEd":"EEE, MMM d, y","yMMMM":"MMMM y","yMMMMd":"MMMM d, y","yMMMMEEEEd":"EEEE, MMMM d, y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/en_ZA.json b/intl/lib/src/data/dates/patterns/en_ZA.json
new file mode 100644
index 0000000..0af5646
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/en_ZA.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"MM/dd","MEd":"EEE MM/dd","MMM":"LLL","MMMd":"dd MMM","MMMEd":"EEE dd MMM","MMMM":"LLLL","MMMMd":"dd MMMM","MMMMEEEEd":"EEEE dd MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"y/MM/dd","yMEd":"EEE, y/MM/dd","yMMM":"MMM y","yMMMd":"dd MMM y","yMMMEd":"EEE, dd MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/es.json b/intl/lib/src/data/dates/patterns/es.json
new file mode 100644
index 0000000..f05c9f9
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/es.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d 'de' MMM","MMMEd":"EEE d 'de' MMM","MMMM":"LLLL","MMMMd":"d 'de' MMMM","MMMMEEEEd":"EEEE d 'de' MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM 'de' y","yMMMd":"d 'de' MMM 'de' y","yMMMEd":"EEE, d 'de' MMMM 'de' y","yMMMM":"MMMM 'de' y","yMMMMd":"d 'de' MMMM 'de' y","yMMMMEEEEd":"EEEE, d 'de' MMMM 'de' y","yQQQ":"QQQ y","yQQQQ":"QQQQ 'de' y","H":"H","Hm":"H:mm","Hms":"H:mm:ss","j":"H","jm":"H:mm","jms":"H:mm:ss","jmv":"H:mm v","jmz":"H:mm z","jz":"H z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/es_419.json b/intl/lib/src/data/dates/patterns/es_419.json
new file mode 100644
index 0000000..e77fae4
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/es_419.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d 'de' MMM","MMMEd":"EEE d 'de' MMM","MMMM":"LLLL","MMMMd":"d 'de' MMMM","MMMMEEEEd":"EEEE d 'de' MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE d/M/y","yMMM":"MMM 'de' y","yMMMd":"d 'de' MMM 'de' y","yMMMEd":"EEE, d 'de' MMMM 'de' y","yMMMM":"MMMM 'de' y","yMMMMd":"d 'de' MMMM 'de' y","yMMMMEEEEd":"EEEE, d 'de' MMMM 'de' y","yQQQ":"QQQ y","yQQQQ":"QQQQ 'de' y","H":"H","Hm":"H:mm","Hms":"H:mm:ss","j":"H","jm":"H:mm","jms":"H:mm:ss","jmv":"H:mm v","jmz":"H:mm z","jz":"H z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/es_ES.json b/intl/lib/src/data/dates/patterns/es_ES.json
new file mode 100644
index 0000000..f05c9f9
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/es_ES.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d 'de' MMM","MMMEd":"EEE d 'de' MMM","MMMM":"LLLL","MMMMd":"d 'de' MMMM","MMMMEEEEd":"EEEE d 'de' MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM 'de' y","yMMMd":"d 'de' MMM 'de' y","yMMMEd":"EEE, d 'de' MMMM 'de' y","yMMMM":"MMMM 'de' y","yMMMMd":"d 'de' MMMM 'de' y","yMMMMEEEEd":"EEEE, d 'de' MMMM 'de' y","yQQQ":"QQQ y","yQQQQ":"QQQQ 'de' y","H":"H","Hm":"H:mm","Hms":"H:mm:ss","j":"H","jm":"H:mm","jms":"H:mm:ss","jmv":"H:mm v","jmz":"H:mm z","jz":"H z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/et.json b/intl/lib/src/data/dates/patterns/et.json
new file mode 100644
index 0000000..5f5c6e7
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/et.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"MMMM","LLLL":"MMMM","M":"M","Md":"d.M","MEd":"EEE, d.M","MMM":"MMMM","MMMd":"d. MMM","MMMEd":"EEE, d. MMM","MMMM":"MMMM","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE, d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M.y","yMd":"d.M.y","yMEd":"EEE, d.M y","yMMM":"MMM y","yMMMd":"d. MMM y","yMMMEd":"EEE, d. MMMM y","yMMMM":"MMMM y","yMMMMd":"d. MMMM y","yMMMMEEEEd":"EEEE, d. MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"H:mm.ss","j":"HH","jm":"HH:mm","jms":"H:mm.ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm.ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/eu.json b/intl/lib/src/data/dates/patterns/eu.json
new file mode 100644
index 0000000..50d6a77
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/eu.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M/d","MEd":"M/d, EEE","MMM":"LLL","MMMd":"MMM d","MMMEd":"MMM d, EEE","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"MMMM d, EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"y/M","yMd":"y/M/d","yMEd":"y/M/d, EEE","yMMM":"y MMM","yMMMd":"y MMM d","yMMMEd":"y MMM d, EEE","yMMMM":"y('e')'ko' MMMM","yMMMMd":"y('e')'ko' MMMM d","yMMMMEEEEd":"y('e')'ko' MMMM d, EEEE","yQQQ":"y('e')'ko' QQQ","yQQQQ":"y('e')'ko' QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm (v)","jmz":"HH:mm (z)","jz":"HH (z)","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/fa.json b/intl/lib/src/data/dates/patterns/fa.json
new file mode 100644
index 0000000..031e71c
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/fa.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M/d","MEd":"EEE M/d","MMM":"LLL","MMMd":"d LLL","MMMEd":"EEE d LLL","MMMM":"LLLL","MMMMd":"d LLLL","MMMMEEEEd":"EEEE d LLLL","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"y/M","yMd":"y/M/d","yMEd":"EEE y/M/d","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"QQQQ y","yQQQQ":"QQQQ y","H":"H","Hm":"H:mm","Hms":"H:mm:ss","j":"H","jm":"H:mm","jms":"H:mm:ss","jmv":"HH:mm (v)","jmz":"HH:mm (z)","jz":"H (z)","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/fi.json b/intl/lib/src/data/dates/patterns/fi.json
new file mode 100644
index 0000000..ce1a345
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/fi.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d.M.","MEd":"EEE d.M.","MMM":"LLL","MMMd":"d. MMM","MMMEd":"ccc d. MMM","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"cccc d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"L.y","yMd":"d.M.y","yMEd":"EEE d.M.y","yMMM":"LLL y","yMMMd":"d. MMM y","yMMMEd":"EEE d. MMM y","yMMMM":"LLLL y","yMMMMd":"d. MMMM y","yMMMMEEEEd":"EEEE d. MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"H","Hm":"H.mm","Hms":"H.mm.ss","j":"H","jm":"H.mm","jms":"H.mm.ss","jmv":"H.mm v","jmz":"H.mm z","jz":"H z","m":"m","ms":"m.ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/fil.json b/intl/lib/src/data/dates/patterns/fil.json
new file mode 100644
index 0000000..9141e1d
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/fil.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M/d","MEd":"EEE, M/d","MMM":"LLL","MMMd":"MMM d","MMMEd":"EEE, MMM d","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"EEEE, MMMM d","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"M/d/y","yMEd":"EEE, M/d/y","yMMM":"MMM y","yMMMd":"MMM d, y","yMMMEd":"EEE, MMM d, y","yMMMM":"MMMM y","yMMMMd":"MMMM d, y","yMMMMEEEEd":"EEEE, MMMM d, y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/fr.json b/intl/lib/src/data/dates/patterns/fr.json
new file mode 100644
index 0000000..212f9b1
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/fr.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH 'h'","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH 'h'","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH 'h' z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/fr_CA.json b/intl/lib/src/data/dates/patterns/fr_CA.json
new file mode 100644
index 0000000..1bc6b18
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/fr_CA.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M-d","MEd":"EEE M-d","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"y-MM","yMd":"y-MM-dd","yMEd":"EEE y-MM-dd","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH 'h'","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH 'h'","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH 'h' z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/gl.json b/intl/lib/src/data/dates/patterns/gl.json
new file mode 100644
index 0000000..b3ee8b2
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/gl.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d-M","MEd":"EEE, d-M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M-y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM, y","yMMMEd":"EEE, d MMM, y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/gsw.json b/intl/lib/src/data/dates/patterns/gsw.json
new file mode 100644
index 0000000..7c20110
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/gsw.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d.M.","MEd":"EEE, d.M.","MMM":"LLL","MMMd":"d. MMM","MMMEd":"EEE d. MMM","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"y-M","yMd":"d.M.y","yMEd":"EEE, y-M-d","yMMM":"MMM y","yMMMd":"y MMM d","yMMMEd":"EEE, d. MMM y","yMMMM":"MMMM y","yMMMMd":"d. MMMM y","yMMMMEEEEd":"EEEE, d. MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"H","Hm":"H:mm","Hms":"HH:mm:ss","j":"H","jm":"H:mm","jms":"HH:mm:ss","jmv":"H:mm v","jmz":"H:mm z","jz":"H z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/gu.json b/intl/lib/src/data/dates/patterns/gu.json
new file mode 100644
index 0000000..4e5c8dd
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/gu.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM, y","yMMMEd":"EEE, d MMM, y","yMMMM":"MMMM y","yMMMMd":"d MMMM, y","yMMMMEEEEd":"EEEE, d MMMM, y","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/haw.json b/intl/lib/src/data/dates/patterns/haw.json
new file mode 100644
index 0000000..12bddd1
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/haw.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"M","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"H","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/he.json b/intl/lib/src/data/dates/patterns/he.json
new file mode 100644
index 0000000..a710048
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/he.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d בMMM","MMMEd":"EEE, d בMMM","MMMM":"LLLL","MMMMd":"d בMMMM","MMMMEEEEd":"EEEE, d בMMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M.y","yMd":"d.M.y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d בMMM y","yMMMEd":"EEE, d בMMM y","yMMMM":"MMMM y","yMMMMd":"d בMMMM y","yMMMMEEEEd":"EEEE, d בMMMM y","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/hi.json b/intl/lib/src/data/dates/patterns/hi.json
new file mode 100644
index 0000000..7f5d858
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/hi.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM, y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/hr.json b/intl/lib/src/data/dates/patterns/hr.json
new file mode 100644
index 0000000..39918c7
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/hr.json
@@ -0,0 +1 @@
+{"d":"d.","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L.","Md":"d. M.","MEd":"EEE, d. M.","MMM":"LLL","MMMd":"d. MMM","MMMEd":"EEE, d. MMM","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE, d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y.","yM":"M. y.","yMd":"d. M. y.","yMEd":"EEE, d. M. y.","yMMM":"LLL y.","yMMMd":"d. MMM y.","yMMMEd":"EEE, d. MMM y.","yMMMM":"LLLL y.","yMMMMd":"d. MMMM y.","yMMMMEEEEd":"EEEE, d. MMMM y.","yQQQ":"QQQ y.","yQQQQ":"QQQQ y.","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/hu.json b/intl/lib/src/data/dates/patterns/hu.json
new file mode 100644
index 0000000..0dadb65
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/hu.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M. d.","MEd":"M. d., EEE","MMM":"LLL","MMMd":"MMM d.","MMMEd":"MMM d., EEE","MMMM":"LLLL","MMMMd":"MMMM d.","MMMMEEEEd":"MMMM d., EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y.","yM":"y. M.","yMd":"y. MM. dd.","yMEd":"y. MM. dd., EEE","yMMM":"y. MMM","yMMMd":"y. MMM d.","yMMMEd":"y. MMM d., EEE","yMMMM":"y. MMMM","yMMMMd":"y. MMMM d.","yMMMMEEEEd":"y. MMMM d., EEEE","yQQQ":"y. QQQ","yQQQQ":"y. QQQQ","H":"H","Hm":"H:mm","Hms":"H:mm:ss","j":"H","jm":"H:mm","jms":"H:mm:ss","jmv":"H:mm v","jmz":"H:mm z","jz":"H z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/hy.json b/intl/lib/src/data/dates/patterns/hy.json
new file mode 100644
index 0000000..5964bf3
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/hy.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"dd.MM","MEd":"dd.MM, EEE","MMM":"LLL","MMMd":"d MMM","MMMEd":"d MMM, EEE","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"d MMMM, EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM.y","yMd":"dd.MM.y","yMEd":"d.MM.yթ., EEE","yMMM":"yթ. LLL","yMMMd":"d MMM, yթ.","yMMMEd":"yթ. MMM d, EEE","yMMMM":"yթ. LLLL","yMMMMd":"d MMMM, yթ.","yMMMMEEEEd":"yթ. MMMM d, EEEE","yQQQ":"y թ, QQQ","yQQQQ":"y թ, QQQQ","H":"H","Hm":"H:mm","Hms":"H:mm:ss","j":"H","jm":"H:mm","jms":"H:mm:ss","jmv":"H:mm, v","jmz":"H:mm, z","jz":"H, z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/id.json b/intl/lib/src/data/dates/patterns/id.json
new file mode 100644
index 0000000..1efa71f
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/id.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH.mm","Hms":"HH.mm.ss","j":"HH","jm":"HH.mm","jms":"HH.mm.ss","jmv":"HH.mm v","jmz":"HH.mm z","jz":"HH z","m":"m","ms":"mm.ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/in.json b/intl/lib/src/data/dates/patterns/in.json
new file mode 100644
index 0000000..1efa71f
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/in.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH.mm","Hms":"HH.mm.ss","j":"HH","jm":"HH.mm","jms":"HH.mm.ss","jmv":"HH.mm v","jmz":"HH.mm z","jz":"HH z","m":"m","ms":"mm.ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/is.json b/intl/lib/src/data/dates/patterns/is.json
new file mode 100644
index 0000000..1349efd
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/is.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d.M.","MEd":"EEE, d.M.","MMM":"LLL","MMMd":"d. MMM","MMMEd":"EEE, d. MMM","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE, d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M. y","yMd":"d.M.y","yMEd":"EEE, d.M.y","yMMM":"MMM y","yMMMd":"d. MMM y","yMMMEd":"EEE, d. MMM y","yMMMM":"MMMM y","yMMMMd":"d. MMMM y","yMMMMEEEEd":"EEEE, d. MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/it.json b/intl/lib/src/data/dates/patterns/it.json
new file mode 100644
index 0000000..79bd37f
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/it.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/iw.json b/intl/lib/src/data/dates/patterns/iw.json
new file mode 100644
index 0000000..a710048
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/iw.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d בMMM","MMMEd":"EEE, d בMMM","MMMM":"LLLL","MMMMd":"d בMMMM","MMMMEEEEd":"EEEE, d בMMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M.y","yMd":"d.M.y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d בMMM y","yMMMEd":"EEE, d בMMM y","yMMMM":"MMMM y","yMMMMd":"d בMMMM y","yMMMMEEEEd":"EEEE, d בMMMM y","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/ja.json b/intl/lib/src/data/dates/patterns/ja.json
new file mode 100644
index 0000000..6521d81
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/ja.json
@@ -0,0 +1 @@
+{"d":"d日","E":"EEE","EEEE":"EEEE","LLL":"M月","LLLL":"M月","M":"M月","Md":"M/d","MEd":"M/d(EEE)","MMM":"M月","MMMd":"M月d日","MMMEd":"M月d日(EEE)","MMMM":"M月","MMMMd":"M月d日","MMMMEEEEd":"M月d日EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y年","yM":"y/M","yMd":"y/M/d","yMEd":"y/M/d(EEE)","yMMM":"y年M月","yMMMd":"y年M月d日","yMMMEd":"y年M月d日(EEE)","yMMMM":"y年M月","yMMMMd":"y年M月d日","yMMMMEEEEd":"y年M月d日EEEE","yQQQ":"y/QQQ","yQQQQ":"yQQQQ","H":"H時","Hm":"H:mm","Hms":"H:mm:ss","j":"H時","jm":"H:mm","jms":"H:mm:ss","jmv":"H:mm v","jmz":"H:mm z","jz":"H時 z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/ka.json b/intl/lib/src/data/dates/patterns/ka.json
new file mode 100644
index 0000000..7228325
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/ka.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d.M","MEd":"EEE, d.M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M.y","yMd":"d.M.y","yMEd":"EEE, d.M.y","yMMM":"MMM, y","yMMMd":"d MMM, y","yMMMEd":"EEE, d MMM, y","yMMMM":"MMMM, y","yMMMMd":"d MMMM, y","yMMMMEEEEd":"EEEE, d MMMM, y","yQQQ":"QQQ, y","yQQQQ":"QQQQ, y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/kk.json b/intl/lib/src/data/dates/patterns/kk.json
new file mode 100644
index 0000000..fcc0684
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/kk.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"dd-MM","MEd":"EEE, dd-MM","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM-y","yMd":"dd-MM-y","yMEd":"EEE, dd-MM-y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y 'ж'.","yMMMMEEEEd":"EEEE, d MMMM y 'ж'.","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/km.json b/intl/lib/src/data/dates/patterns/km.json
new file mode 100644
index 0000000..d3e062f
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/km.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d-M","MEd":"EEE d MMM","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M-y","yMd":"d-M-y","yMEd":"EEE d-M-y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/kn.json b/intl/lib/src/data/dates/patterns/kn.json
new file mode 100644
index 0000000..a5b34ac
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/kn.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"d/M, EEE","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d, MMM, y","yMMMEd":"d MMM, y EEE","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"d MMMM y, EEEE","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/ko.json b/intl/lib/src/data/dates/patterns/ko.json
new file mode 100644
index 0000000..2602182
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/ko.json
@@ -0,0 +1 @@
+{"d":"d일","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"M월","Md":"M. d.","MEd":"M. d. (EEE)","MMM":"LLL","MMMd":"MMM d일","MMMEd":"MMM d일 (EEE)","MMMM":"LLLL","MMMMd":"MMMM d일","MMMMEEEEd":"MMMM d일 EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y년","yM":"y. M.","yMd":"y. M. d.","yMEd":"y. M. d. (EEE)","yMMM":"y년 MMM","yMMMd":"y년 MMM d일","yMMMEd":"y년 MMM d일 (EEE)","yMMMM":"y년 MMMM","yMMMMd":"y년 MMMM d일","yMMMMEEEEd":"y년 MMMM d일 EEEE","yQQQ":"y년 QQQ","yQQQQ":"y년 QQQQ","H":"H시","Hm":"HH:mm","Hms":"H시 m분 s초","j":"a h시","jm":"a h:mm","jms":"a h:mm:ss","jmv":"a h:mm v","jmz":"a h:mm z","jz":"a h시 z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/ky.json b/intl/lib/src/data/dates/patterns/ky.json
new file mode 100644
index 0000000..07f6b5b
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/ky.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"dd-MM","MEd":"dd-MM, EEE","MMM":"LLL","MMMd":"d-MMM","MMMEd":"d-MMM, EEE","MMMM":"LLLL","MMMMd":"d-MMMM","MMMMEEEEd":"d-MMMM, EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"y-MM","yMd":"y-MM-dd","yMEd":"y-MM-dd, EEE","yMMM":"y-'ж'. MMM","yMMMd":"y-'ж'. d-MMM","yMMMEd":"y-'ж'. d-MMM, EEE","yMMMM":"y-'ж'. MMMM","yMMMMd":"d-MMMM, y-'ж'.","yMMMMEEEEd":"EEEE, d-MMMM, y-'ж'.","yQQQ":"y-'ж'., QQQ","yQQQQ":"y-'ж'., QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/ln.json b/intl/lib/src/data/dates/patterns/ln.json
new file mode 100644
index 0000000..971825c
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/ln.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"H","Hm":"HH:mm","Hms":"HH:mm:ss","j":"H","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"H z","m":"m","ms":"m:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/lo.json b/intl/lib/src/data/dates/patterns/lo.json
new file mode 100644
index 0000000..6847469
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/lo.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/lt.json b/intl/lib/src/data/dates/patterns/lt.json
new file mode 100644
index 0000000..cce6313
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/lt.json
@@ -0,0 +1 @@
+{"d":"dd","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"LL","Md":"MM-d","MEd":"MM-dd, EEE","MMM":"LLL","MMMd":"MMM d","MMMEd":"MMM d, EEE","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"MMMM d, EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"y-MM","yMd":"y-MM-dd","yMEd":"y-MM-dd, EEE","yMMM":"y MMM","yMMMd":"y MMM d","yMMMEd":"y MMM d, EEE","yMMMM":"y MMMM","yMMMMd":"y 'm'. MMMM d 'd'.","yMMMMEEEEd":"y 'm'. MMMM d 'd'., EEEE","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/lv.json b/intl/lib/src/data/dates/patterns/lv.json
new file mode 100644
index 0000000..df4fea4
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/lv.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"dd.MM.","MEd":"EEE, dd.MM.","MMM":"LLL","MMMd":"d. MMM","MMMEd":"EEE, d. MMM","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE, d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y. 'g'.","yM":"MM.y.","yMd":"d.M.y.","yMEd":"EEE, d.M.y.","yMMM":"y. 'g'. MMM","yMMMd":"y. 'g'. d. MMM","yMMMEd":"EEE, y. 'g'. d. MMM","yMMMM":"y. 'g'. MMMM","yMMMMd":"y. 'gada' d. MMMM","yMMMMEEEEd":"EEEE, y. 'gada' d. MMMM","yQQQ":"QQQ y","yQQQQ":"y. 'g'. QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/mk.json b/intl/lib/src/data/dates/patterns/mk.json
new file mode 100644
index 0000000..5bbfa3e
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/mk.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d.M","MEd":"EEE, d.M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y 'г'.","yM":"M.y","yMd":"d.M.y","yMEd":"EEE, d.M.y","yMMM":"MMM y 'г'.","yMMMd":"d MMM y 'г'.","yMMMEd":"EEE, d MMM y 'г'.","yMMMM":"MMMM y 'г'.","yMMMMd":"d MMMM y 'г'.","yMMMMEEEEd":"EEEE, d MMMM y 'г'.","yQQQ":"QQQ y 'г'.","yQQQQ":"QQQQ y 'г'.","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/ml.json b/intl/lib/src/data/dates/patterns/ml.json
new file mode 100644
index 0000000..1b10e22
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/ml.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"M/d, EEE","MMM":"LLL","MMMd":"MMM d","MMMEd":"MMM d, EEE","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"MMMM d, EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M-y","yMd":"d/M/y","yMEd":"d-M-y, EEE","yMMM":"y MMM","yMMMd":"y MMM d","yMMMEd":"y MMM d, EEE","yMMMM":"y MMMM","yMMMMd":"y, MMMM d","yMMMMEEEEd":"y, MMMM d, EEEE","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/mn.json b/intl/lib/src/data/dates/patterns/mn.json
new file mode 100644
index 0000000..767d1b1
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/mn.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M-d","MEd":"EEE, M-d","MMM":"LLL","MMMd":"MMM d","MMMEd":"EEE MMM d","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"EEEE MMMM d","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"y-M","yMd":"y-M-d","yMEd":"EEE, y-M-d","yMMM":"y MMM","yMMMd":"y MMM d","yMMMEd":"EEE, y MMM d","yMMMM":"y MMMM","yMMMMd":"y 'оны' MMMM 'сарын' d","yMMMMEEEEd":"EEEE, y 'оны' MMMM 'сарын' d","yQQQ":"y QQQ","yQQQQ":"y 'оны' QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/mo.json b/intl/lib/src/data/dates/patterns/mo.json
new file mode 100644
index 0000000..a8694ff
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/mo.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"dd.MM","MEd":"EEE, dd.MM","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM.y","yMd":"dd.MM.y","yMEd":"EEE, dd.MM.y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/mr.json b/intl/lib/src/data/dates/patterns/mr.json
new file mode 100644
index 0000000..215525d
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/mr.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM, y","yMMMEd":"EEE, d, MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM, y","yMMMMEEEEd":"EEEE, d MMMM, y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"H:mm","Hms":"H:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/ms.json b/intl/lib/src/data/dates/patterns/ms.json
new file mode 100644
index 0000000..d88b217
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/ms.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d-M","MEd":"EEE, d-M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M-y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/mt.json b/intl/lib/src/data/dates/patterns/mt.json
new file mode 100644
index 0000000..9bd9164
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/mt.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"MM-dd","MEd":"MM-dd, EEE","MMM":"LLL","MMMd":"MMM d","MMMEd":"MMM d, EEE","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"MMMM d, EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"y-MM","yMd":"y-MM-dd","yMEd":"y-MM-dd, EEE","yMMM":"y MMM","yMMMd":"y MMM d","yMMMEd":"y MMM d, EEE","yMMMM":"y MMMM","yMMMMd":"d 'ta'’ MMMM y","yMMMMEEEEd":"EEEE, d 'ta'’ MMMM y","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/my.json b/intl/lib/src/data/dates/patterns/my.json
new file mode 100644
index 0000000..29c62e8
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/my.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M/d","MEd":"EEE, M/d","MMM":"LLL","MMMd":"MMM d","MMMEd":"EEE, MMM d","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"EEEE, MMMM d","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"y/M","yMd":"y-MM-dd","yMEd":"EEE, y/M/d","yMMM":"y MMM","yMMMd":"y MMM d","yMMMEd":"EEE, y MMM d","yMMMM":"y MMMM","yMMMMd":"y MMMM d","yMMMMEEEEd":"EEEE, y MMMM d","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/nb.json b/intl/lib/src/data/dates/patterns/nb.json
new file mode 100644
index 0000000..a103c4f
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/nb.json
@@ -0,0 +1 @@
+{"d":"d.","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L.","Md":"d.M.","MEd":"EEE d.M","MMM":"LLL","MMMd":"d. MMM","MMMEd":"EEE d. MMM","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M.y","yMd":"d.M.y","yMEd":"EEE d.MM.y","yMMM":"MMM y","yMMMd":"d. MMM y","yMMMEd":"EEE d. MMM y","yMMMM":"MMMM y","yMMMMd":"d. MMMM y","yMMMMEEEEd":"EEEE d. MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH.mm","Hms":"HH.mm.ss","j":"HH","jm":"HH.mm","jms":"HH.mm.ss","jmv":"HH.mm v","jmz":"HH.mm z","jz":"HH z","m":"m","ms":"mm.ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/ne.json b/intl/lib/src/data/dates/patterns/ne.json
new file mode 100644
index 0000000..45a919d
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/ne.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"MM-dd","MEd":"MM-dd, EEE","MMM":"LLL","MMMd":"MMM d","MMMEd":"MMM d, EEE","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"MMMM d, EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"y-MM","yMd":"y-MM-dd","yMEd":"y-MM-dd, EEE","yMMM":"y MMM","yMMMd":"y MMM d","yMMMEd":"y MMM d, EEE","yMMMM":"y MMMM","yMMMMd":"y MMMM d","yMMMMEEEEd":"y MMMM d, EEEE","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/nl.json b/intl/lib/src/data/dates/patterns/nl.json
new file mode 100644
index 0000000..aea70dd
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/nl.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d-M","MEd":"EEE d-M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M-y","yMd":"d-M-y","yMEd":"EEE d-M-y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/no.json b/intl/lib/src/data/dates/patterns/no.json
new file mode 100644
index 0000000..a103c4f
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/no.json
@@ -0,0 +1 @@
+{"d":"d.","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L.","Md":"d.M.","MEd":"EEE d.M","MMM":"LLL","MMMd":"d. MMM","MMMEd":"EEE d. MMM","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M.y","yMd":"d.M.y","yMEd":"EEE d.MM.y","yMMM":"MMM y","yMMMd":"d. MMM y","yMMMEd":"EEE d. MMM y","yMMMM":"MMMM y","yMMMMd":"d. MMMM y","yMMMMEEEEd":"EEEE d. MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH.mm","Hms":"HH.mm.ss","j":"HH","jm":"HH.mm","jms":"HH.mm.ss","jmv":"HH.mm v","jmz":"HH.mm z","jz":"HH z","m":"m","ms":"mm.ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/no_NO.json b/intl/lib/src/data/dates/patterns/no_NO.json
new file mode 100644
index 0000000..a103c4f
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/no_NO.json
@@ -0,0 +1 @@
+{"d":"d.","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L.","Md":"d.M.","MEd":"EEE d.M","MMM":"LLL","MMMd":"d. MMM","MMMEd":"EEE d. MMM","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M.y","yMd":"d.M.y","yMEd":"EEE d.MM.y","yMMM":"MMM y","yMMMd":"d. MMM y","yMMMEd":"EEE d. MMM y","yMMMM":"MMMM y","yMMMMd":"d. MMMM y","yMMMMEEEEd":"EEEE d. MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH.mm","Hms":"HH.mm.ss","j":"HH","jm":"HH.mm","jms":"HH.mm.ss","jmv":"HH.mm v","jmz":"HH.mm z","jz":"HH z","m":"m","ms":"mm.ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/or.json b/intl/lib/src/data/dates/patterns/or.json
new file mode 100644
index 0000000..a2dcff3
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/or.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"M","Md":"d-M","MEd":"MM-dd, EEE","MMM":"LLL","MMMd":"MMM d","MMMEd":"MMM d, EEE","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"MMMM d, EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M-y","yMd":"d-M-y","yMEd":"y-MM-dd, EEE","yMMM":"y MMM","yMMMd":"d MMM y","yMMMEd":"y MMM d, EEE","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"y QQQQ","H":"H","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/pa.json b/intl/lib/src/data/dates/patterns/pa.json
new file mode 100644
index 0000000..2a82066
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/pa.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, dd-MM.","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M-y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/pl.json b/intl/lib/src/data/dates/patterns/pl.json
new file mode 100644
index 0000000..cc17f6c
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/pl.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d.MM","MEd":"EEE, d.MM","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM.y","yMd":"d.MM.y","yMEd":"EEE, d.MM.y","yMMM":"LLL y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"LLLL y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/pt.json b/intl/lib/src/data/dates/patterns/pt.json
new file mode 100644
index 0000000..e9af06f
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/pt.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, dd/MM","MMM":"LLL","MMMd":"d 'de' MMM","MMMEd":"EEE, d 'de' MMM","MMMM":"LLLL","MMMMd":"d 'de' MMMM","MMMMEEEEd":"EEEE, d 'de' MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM/y","yMd":"dd/MM/y","yMEd":"EEE, dd/MM/y","yMMM":"MMM 'de' y","yMMMd":"d 'de' MMM 'de' y","yMMMEd":"EEE, d 'de' MMM 'de' y","yMMMM":"MMMM 'de' y","yMMMMd":"d 'de' MMMM 'de' y","yMMMMEEEEd":"EEEE, d 'de' MMMM 'de' y","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/pt_BR.json b/intl/lib/src/data/dates/patterns/pt_BR.json
new file mode 100644
index 0000000..e9af06f
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/pt_BR.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, dd/MM","MMM":"LLL","MMMd":"d 'de' MMM","MMMEd":"EEE, d 'de' MMM","MMMM":"LLLL","MMMMd":"d 'de' MMMM","MMMMEEEEd":"EEEE, d 'de' MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM/y","yMd":"dd/MM/y","yMEd":"EEE, dd/MM/y","yMMM":"MMM 'de' y","yMMMd":"d 'de' MMM 'de' y","yMMMEd":"EEE, d 'de' MMM 'de' y","yMMMM":"MMMM 'de' y","yMMMMd":"d 'de' MMMM 'de' y","yMMMMEEEEd":"EEEE, d 'de' MMMM 'de' y","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/pt_PT.json b/intl/lib/src/data/dates/patterns/pt_PT.json
new file mode 100644
index 0000000..086a1db
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/pt_PT.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, dd/MM","MMM":"LLL","MMMd":"d/MM","MMMEd":"EEE, d/MM","MMMM":"LLLL","MMMMd":"d 'de' MMMM","MMMMEEEEd":"EEEE, d 'de' MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM/y","yMd":"dd/MM/y","yMEd":"EEE, dd/MM/y","yMMM":"MM/y","yMMMd":"d/MM/y","yMMMEd":"EEE, d/MM/y","yMMMM":"MMMM 'de' y","yMMMMd":"d 'de' MMMM 'de' y","yMMMMEEEEd":"EEEE, d 'de' MMMM 'de' y","yQQQ":"QQQQ 'de' y","yQQQQ":"QQQQ 'de' y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/ro.json b/intl/lib/src/data/dates/patterns/ro.json
new file mode 100644
index 0000000..a8694ff
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/ro.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"dd.MM","MEd":"EEE, dd.MM","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM.y","yMd":"dd.MM.y","yMEd":"EEE, dd.MM.y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/ru.json b/intl/lib/src/data/dates/patterns/ru.json
new file mode 100644
index 0000000..7b8c17d
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/ru.json
@@ -0,0 +1 @@
+{"d":"d","E":"ccc","EEEE":"cccc","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"dd.MM","MEd":"EEE, dd.MM","MMM":"LLL","MMMd":"d MMM","MMMEd":"ccc, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"cccc, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM.y","yMd":"dd.MM.y","yMEd":"ccc, d.MM.y 'г'.","yMMM":"LLL y","yMMMd":"d MMM y 'г'.","yMMMEd":"EEE, d MMM y","yMMMM":"LLLL y","yMMMMd":"d MMMM y 'г'.","yMMMMEEEEd":"EEEE, d MMMM y 'г'.","yQQQ":"QQQ y 'г'.","yQQQQ":"QQQQ y 'г'.","H":"H","Hm":"H:mm","Hms":"H:mm:ss","j":"H","jm":"H:mm","jms":"H:mm:ss","jmv":"H:mm v","jmz":"H:mm z","jz":"H z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/sh.json b/intl/lib/src/data/dates/patterns/sh.json
new file mode 100644
index 0000000..3bfb526
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/sh.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, M-d","MMM":"LLL","MMMd":"d. MMM","MMMEd":"EEE d. MMM","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y.","yM":"M.y.","yMd":"d.M.y.","yMEd":"EEE, d.M.y.","yMMM":"MMM y.","yMMMd":"d. MMM y.","yMMMEd":"EEE, d. MMM y.","yMMMM":"MMMM y.","yMMMMd":"d. MMMM y.","yMMMMEEEEd":"EEEE, d. MMMM y.","yQQQ":"QQQ. y","yQQQQ":"QQQQ. y","H":"HH","Hm":"HH.mm","Hms":"HH.mm.ss","j":"HH","jm":"HH.mm","jms":"HH.mm.ss","jmv":"HH.mm v","jmz":"HH.mm z","jz":"HH z","m":"m","ms":"mm.ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/si.json b/intl/lib/src/data/dates/patterns/si.json
new file mode 100644
index 0000000..0a2ea70
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/si.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M-d","MEd":"M-d, EEE","MMM":"LLL","MMMd":"MMM d","MMMEd":"MMM d EEE","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"MMMM d EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"y-M","yMd":"y-M-d","yMEd":"y-M-d, EEE","yMMM":"y MMM","yMMMd":"y MMM d","yMMMEd":"y MMM d, EEE","yMMMM":"y MMMM","yMMMMd":"y MMMM d","yMMMMEEEEd":"y MMMM d, EEEE","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"a h","jm":"a h.mm","jms":"a h.mm.ss","jmv":"a h.mm v","jmz":"a h.mm z","jz":"a h z","m":"m","ms":"mm.ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/sk.json b/intl/lib/src/data/dates/patterns/sk.json
new file mode 100644
index 0000000..f3ecaeb
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/sk.json
@@ -0,0 +1 @@
+{"d":"d.","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L.","Md":"d.M.","MEd":"EEE, d.M.","MMM":"LLL","MMMd":"d. MMM.","MMMEd":"EEE, d. MMM.","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE, d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M.y","yMd":"d.M.y","yMEd":"EEE d. M. y","yMMM":"LLL y","yMMMd":"d.M.y","yMMMEd":"EEE, d. MMM y","yMMMM":"LLLL y","yMMMMd":"d. MMMM y","yMMMMEEEEd":"EEEE, d. MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"H","Hm":"H:mm","Hms":"H:mm:ss","j":"H","jm":"H:mm","jms":"H:mm:ss","jmv":"H:mm v","jmz":"H:mm z","jz":"H z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/sl.json b/intl/lib/src/data/dates/patterns/sl.json
new file mode 100644
index 0000000..a0f902a
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/sl.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d. M.","MEd":"EEE, d. MM.","MMM":"LLL","MMMd":"d. MMM","MMMEd":"EEE, d. MMM","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE, d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d. M. y","yMEd":"EEE, d. M. y","yMMM":"MMM y","yMMMd":"d. MMM y","yMMMEd":"EEE, d. MMM y","yMMMM":"MMMM y","yMMMMd":"d. MMMM y","yMMMMEEEEd":"EEEE, d. MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH.mm","Hms":"HH.mm.ss","j":"HH","jm":"HH.mm","jms":"HH.mm.ss","jmv":"HH.mm v","jmz":"HH.mm z","jz":"HH z","m":"m","ms":"mm.ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/sq.json b/intl/lib/src/data/dates/patterns/sq.json
new file mode 100644
index 0000000..0597df8
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/sq.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"dd/MM/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/sr.json b/intl/lib/src/data/dates/patterns/sr.json
new file mode 100644
index 0000000..3bfb526
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/sr.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, M-d","MMM":"LLL","MMMd":"d. MMM","MMMEd":"EEE d. MMM","MMMM":"LLLL","MMMMd":"d. MMMM","MMMMEEEEd":"EEEE d. MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y.","yM":"M.y.","yMd":"d.M.y.","yMEd":"EEE, d.M.y.","yMMM":"MMM y.","yMMMd":"d. MMM y.","yMMMEd":"EEE, d. MMM y.","yMMMM":"MMMM y.","yMMMMd":"d. MMMM y.","yMMMMEEEEd":"EEEE, d. MMMM y.","yQQQ":"QQQ. y","yQQQQ":"QQQQ. y","H":"HH","Hm":"HH.mm","Hms":"HH.mm.ss","j":"HH","jm":"HH.mm","jms":"HH.mm.ss","jmv":"HH.mm v","jmz":"HH.mm z","jz":"HH z","m":"m","ms":"mm.ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/sv.json b/intl/lib/src/data/dates/patterns/sv.json
new file mode 100644
index 0000000..4367651
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/sv.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"y-MM","yMd":"y-MM-dd","yMEd":"EEE, y-MM-dd","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/sw.json b/intl/lib/src/data/dates/patterns/sw.json
new file mode 100644
index 0000000..2c73553
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/sw.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d-M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE, MMM d, y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE, d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/ta.json b/intl/lib/src/data/dates/patterns/ta.json
new file mode 100644
index 0000000..2ebbe91
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/ta.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"MM-dd, EEE","MMM":"LLL","MMMd":"MMM d","MMMEd":"MMM d, EEE","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"MMMM d, EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d MMM, y","yMMMEd":"EEE, d MMM, y","yMMMM":"MMMM y","yMMMMd":"d MMMM, y","yMMMMEEEEd":"EEEE, d MMMM, y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/te.json b/intl/lib/src/data/dates/patterns/te.json
new file mode 100644
index 0000000..9a7eec6
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/te.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, d/M/y","yMMM":"MMM y","yMMMd":"d, MMM y","yMMMEd":"EEE, d, MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"d MMMM y EEEE","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/th.json b/intl/lib/src/data/dates/patterns/th.json
new file mode 100644
index 0000000..79bd37f
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/th.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE d/M/y","yMMM":"MMM y","yMMMd":"d MMM y","yMMMEd":"EEE d MMM y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/tl.json b/intl/lib/src/data/dates/patterns/tl.json
new file mode 100644
index 0000000..9141e1d
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/tl.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M/d","MEd":"EEE, M/d","MMM":"LLL","MMMd":"MMM d","MMMEd":"EEE, MMM d","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"EEEE, MMMM d","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"M/d/y","yMEd":"EEE, M/d/y","yMMM":"MMM y","yMMMd":"MMM d, y","yMMMEd":"EEE, MMM d, y","yMMMM":"MMMM y","yMMMMd":"MMMM d, y","yMMMMEEEEd":"EEEE, MMMM d, y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/tr.json b/intl/lib/src/data/dates/patterns/tr.json
new file mode 100644
index 0000000..b2b3630
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/tr.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"dd/MM","MEd":"dd/MM EEE","MMM":"LLL","MMMd":"d MMM","MMMEd":"d MMMM EEE","MMMM":"LLLL","MMMMd":"dd MMMM","MMMMEEEEd":"dd MMMM EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM/y","yMd":"dd.MM.y","yMEd":"dd.MM.y EEE","yMMM":"MMM y","yMMMd":"dd MMM y","yMMMEd":"d MMM y EEE","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"d MMMM y EEEE","yQQQ":"y/QQQ","yQQQQ":"y/QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/uk.json b/intl/lib/src/data/dates/patterns/uk.json
new file mode 100644
index 0000000..fc8ec35
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/uk.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"dd.MM","MEd":"EEE, dd.MM","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE, d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE, d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"MM.y","yMd":"dd.MM.y","yMEd":"EEE, dd.MM.y","yMMM":"LLL y","yMMMd":"d MMM y","yMMMEd":"EEE, d MMM y","yMMMM":"LLLL y","yMMMMd":"d MMMM y 'р'.","yMMMMEEEEd":"EEEE, d MMMM y 'р'.","yQQQ":"QQQ y","yQQQQ":"QQQQ y 'р'.","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/ur.json b/intl/lib/src/data/dates/patterns/ur.json
new file mode 100644
index 0000000..4ca6759
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/ur.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"d/M","MEd":"EEE، d/M","MMM":"LLL","MMMd":"d MMM","MMMEd":"EEE، d MMM","MMMM":"LLLL","MMMMd":"d MMMM","MMMMEEEEd":"EEEE، d MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE، d/M/y","yMMM":"MMM y","yMMMd":"d MMM، y","yMMMEd":"EEE، d MMM، y","yMMMM":"MMMM y","yMMMMd":"d MMMM، y","yMMMMEEEEd":"EEEE، d MMMM، y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/uz.json b/intl/lib/src/data/dates/patterns/uz.json
new file mode 100644
index 0000000..245e54e
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/uz.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"MM-dd","MEd":"MM-dd, EEE","MMM":"LLL","MMMd":"MMM d","MMMEd":"MMM d, EEE","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"MMMM d, EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"y-MM","yMd":"y-MM-dd","yMEd":"y-MM-dd, EEE","yMMM":"y MMM","yMMMd":"y MMM d","yMMMEd":"y MMM d, EEE","yMMMM":"y MMMM","yMMMMd":"y MMMM d","yMMMMEEEEd":"EEEE, y MMMM d","yQQQ":"y QQQ","yQQQQ":"y QQQQ","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"HH","jm":"HH:mm","jms":"HH:mm:ss","jmv":"HH:mm v","jmz":"HH:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/vi.json b/intl/lib/src/data/dates/patterns/vi.json
new file mode 100644
index 0000000..3b29ea6
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/vi.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"dd-M","MEd":"EEE, dd-M","MMM":"LLL","MMMd":"dd MMM","MMMEd":"EEE, dd MMM","MMMM":"LLLL","MMMMd":"dd MMMM","MMMMEEEEd":"EEEE, dd MMMM","QQQ":"QQQ","QQQQ":"QQQQ","y":"'Năm' y","yM":"M/y","yMd":"d/M/y","yMEd":"EEE, dd-M-y","yMMM":"MMM y","yMMMd":"dd MMM, y","yMMMEd":"EEE, dd MMM y","yMMMM":"MMMM y","yMMMMd":"dd MMMM, y","yMMMMEEEEd":"EEEE, 'ngày' d MMMM 'năm' y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"H:mm","Hms":"H:mm:ss","j":"HH","jm":"H:mm","jms":"H:mm:ss","jmv":"H:mm v","jmz":"H:mm z","jz":"HH z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/zh.json b/intl/lib/src/data/dates/patterns/zh.json
new file mode 100644
index 0000000..8235fb6
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/zh.json
@@ -0,0 +1 @@
+{"d":"d日","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"M月","Md":"M/d","MEd":"M/dEEE","MMM":"LLL","MMMd":"M月d日","MMMEd":"M月d日EEE","MMMM":"LLLL","MMMMd":"M月d日","MMMMEEEEd":"M月d日EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y年","yM":"y/M","yMd":"y/M/d","yMEd":"y/M/dEEE","yMMM":"y年M月","yMMMd":"y年M月d日","yMMMEd":"y年M月d日EEE","yMMMM":"y年M月","yMMMMd":"y年M月d日","yMMMMEEEEd":"y年M月d日EEEE","yQQQ":"y年第Q季度","yQQQQ":"y年第Q季度","H":"H时","Hm":"HH:mm","Hms":"HH:mm:ss","j":"ah时","jm":"ah:mm","jms":"ah:mm:ss","jmv":"vah:mm","jmz":"zah:mm","jz":"zah时","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/zh_CN.json b/intl/lib/src/data/dates/patterns/zh_CN.json
new file mode 100644
index 0000000..8235fb6
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/zh_CN.json
@@ -0,0 +1 @@
+{"d":"d日","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"M月","Md":"M/d","MEd":"M/dEEE","MMM":"LLL","MMMd":"M月d日","MMMEd":"M月d日EEE","MMMM":"LLLL","MMMMd":"M月d日","MMMMEEEEd":"M月d日EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y年","yM":"y/M","yMd":"y/M/d","yMEd":"y/M/dEEE","yMMM":"y年M月","yMMMd":"y年M月d日","yMMMEd":"y年M月d日EEE","yMMMM":"y年M月","yMMMMd":"y年M月d日","yMMMMEEEEd":"y年M月d日EEEE","yQQQ":"y年第Q季度","yQQQQ":"y年第Q季度","H":"H时","Hm":"HH:mm","Hms":"HH:mm:ss","j":"ah时","jm":"ah:mm","jms":"ah:mm:ss","jmv":"vah:mm","jmz":"zah:mm","jz":"zah时","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/zh_HK.json b/intl/lib/src/data/dates/patterns/zh_HK.json
new file mode 100644
index 0000000..54c66f7
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/zh_HK.json
@@ -0,0 +1 @@
+{"d":"d日","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"M月","Md":"d/M","MEd":"EEE, d/M","MMM":"LLL","MMMd":"M月d日","MMMEd":"M月d日 (EEE)","MMMM":"LLLL","MMMMd":"M月d日","MMMMEEEEd":"M月d日 (EEEE)","QQQ":"QQQ","QQQQ":"QQQQ","y":"y年","yM":"M/y","yMd":"d/M/y","yMEd":"d/M/y(EEE)","yMMM":"y 年 M 月","yMMMd":"y 年 M 月 d 日","yMMMEd":"y 年 M 月 d 日 (EEE)","yMMMM":"y 年 M 月","yMMMMd":"y 年 M 月 d 日","yMMMMEEEEd":"y 年 M 月 d 日 (EEEE)","yQQQ":"y年QQQ","yQQQQ":"y年QQQQ","H":"H時","Hm":"HH:mm","Hms":"HH:mm:ss","j":"ah時","jm":"ah:mm","jms":"ah:mm:ss","jmv":"ah:mm v","jmz":"ah:mm z","jz":"ah時 z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/zh_TW.json b/intl/lib/src/data/dates/patterns/zh_TW.json
new file mode 100644
index 0000000..1d0d9c4
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/zh_TW.json
@@ -0,0 +1 @@
+{"d":"d日","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"M月","Md":"M/d","MEd":"M/d(EEE)","MMM":"LLL","MMMd":"M月d日","MMMEd":"M月d日EEE","MMMM":"LLLL","MMMMd":"M月d日","MMMMEEEEd":"M月d日EEEE","QQQ":"QQQ","QQQQ":"QQQQ","y":"y年","yM":"y/M","yMd":"y/M/d","yMEd":"y/M/d(EEE)","yMMM":"y年M月","yMMMd":"y年M月d日","yMMMEd":"y年M月d日EEE","yMMMM":"y年M月","yMMMMd":"y年M月d日","yMMMMEEEEd":"y年M月d日EEEE","yQQQ":"y年QQQ","yQQQQ":"y年QQQQ","H":"H時","Hm":"HH:mm","Hms":"HH:mm:ss","j":"ah時","jm":"ah:mm","jms":"ah:mm:ss","jmv":"ah:mm v","jmz":"ah:mm z","jz":"ah時 z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/patterns/zu.json b/intl/lib/src/data/dates/patterns/zu.json
new file mode 100644
index 0000000..3129b04
--- /dev/null
+++ b/intl/lib/src/data/dates/patterns/zu.json
@@ -0,0 +1 @@
+{"d":"d","E":"EEE","EEEE":"EEEE","LLL":"LLL","LLLL":"LLLL","M":"L","Md":"M/d","MEd":"EEE, M/d","MMM":"LLL","MMMd":"MMM d","MMMEd":"EEE, MMM d","MMMM":"LLLL","MMMMd":"MMMM d","MMMMEEEEd":"EEEE, MMMM d","QQQ":"QQQ","QQQQ":"QQQQ","y":"y","yM":"M/y","yMd":"M/d/y","yMEd":"EEE, M/d/y","yMMM":"MMM y","yMMMd":"MMM d, y","yMMMEd":"EEE, MMM d, y","yMMMM":"MMMM y","yMMMMd":"d MMMM y","yMMMMEEEEd":"EEEE d MMMM y","yQQQ":"QQQ y","yQQQQ":"QQQQ y","H":"HH","Hm":"HH:mm","Hms":"HH:mm:ss","j":"h a","jm":"h:mm a","jms":"h:mm:ss a","jmv":"h:mm a v","jmz":"h:mm a z","jz":"h a z","m":"m","ms":"mm:ss","s":"s","v":"v","z":"z","zzzz":"zzzz","ZZZZ":"ZZZZ"}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/af.json b/intl/lib/src/data/dates/symbols/af.json
new file mode 100644
index 0000000..f0a0a77
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/af.json
@@ -0,0 +1 @@
+{"NAME":"af","ERAS":["v.C.","n.C."],"ERANAMES":["voor Christus","na Christus"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["Januarie","Februarie","Maart","April","Mei","Junie","Julie","Augustus","September","Oktober","November","Desember"],"STANDALONEMONTHS":["Januarie","Februarie","Maart","April","Mei","Junie","Julie","Augustus","September","Oktober","November","Desember"],"SHORTMONTHS":["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Aug","Sep","Okt","Nov","Des"],"STANDALONESHORTMONTHS":["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Aug","Sep","Okt","Nov","Des"],"WEEKDAYS":["Sondag","Maandag","Dinsdag","Woensdag","Donderdag","Vrydag","Saterdag"],"STANDALONEWEEKDAYS":["Sondag","Maandag","Dinsdag","Woensdag","Donderdag","Vrydag","Saterdag"],"SHORTWEEKDAYS":["So","Ma","Di","Wo","Do","Vr","Sa"],"STANDALONESHORTWEEKDAYS":["So","Ma","Di","Wo","Do","Vr","Sa"],"NARROWWEEKDAYS":["S","M","D","W","D","V","S"],"STANDALONENARROWWEEKDAYS":["S","M","D","W","D","V","S"],"SHORTQUARTERS":["K1","K2","K3","K4"],"QUARTERS":["1ste kwartaal","2de kwartaal","3de kwartaal","4de kwartaal"],"AMPMS":["vm.","nm."],"DATEFORMATS":["EEEE dd MMMM y","dd MMMM y","dd MMM y","y-MM-dd"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/am.json b/intl/lib/src/data/dates/symbols/am.json
new file mode 100644
index 0000000..c1c18ed
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/am.json
@@ -0,0 +1 @@
+{"NAME":"am","ERAS":["ዓ/ዓ","ዓ/ም"],"ERANAMES":["ዓመተ ዓለም","ዓመተ ምሕረት"],"NARROWMONTHS":["ጃ","ፌ","ማ","ኤ","ሜ","ጁ","ጁ","ኦ","ሴ","ኦ","ኖ","ዲ"],"STANDALONENARROWMONTHS":["ጃ","ፌ","ማ","ኤ","ሜ","ጁ","ጁ","ኦ","ሴ","ኦ","ኖ","ዲ"],"MONTHS":["ጃንዩወሪ","ፌብሩወሪ","ማርች","ኤፕሪል","ሜይ","ጁን","ጁላይ","ኦገስት","ሴፕቴምበር","ኦክተውበር","ኖቬምበር","ዲሴምበር"],"STANDALONEMONTHS":["ጃንዩወሪ","ፌብሩወሪ","ማርች","ኤፕሪል","ሜይ","ጁን","ጁላይ","ኦገስት","ሴፕቴምበር","ኦክቶበር","ኖቬምበር","ዲሴምበር"],"SHORTMONTHS":["ጃንዩ","ፌብሩ","ማርች","ኤፕሪ","ሜይ","ጁን","ጁላይ","ኦገስ","ሴፕቴ","ኦክተ","ኖቬም","ዲሴም"],"STANDALONESHORTMONTHS":["ጃንዩ","ፌብሩ","ማርች","ኤፕሪ","ሜይ","ጁን","ጁላይ","ኦገስ","ሴፕቴ","ኦክቶ","ኖቬም","ዲሴም"],"WEEKDAYS":["እሑድ","ሰኞ","ማክሰኞ","ረቡዕ","ሐሙስ","ዓርብ","ቅዳሜ"],"STANDALONEWEEKDAYS":["እሑድ","ሰኞ","ማክሰኞ","ረቡዕ","ሐሙስ","ዓርብ","ቅዳሜ"],"SHORTWEEKDAYS":["እሑድ","ሰኞ","ማክሰ","ረቡዕ","ሐሙስ","ዓርብ","ቅዳሜ"],"STANDALONESHORTWEEKDAYS":["እሑድ","ሰኞ","ማክሰ","ረቡዕ","ሐሙስ","ዓርብ","ቅዳሜ"],"NARROWWEEKDAYS":["እ","ሰ","ማ","ረ","ሐ","ዓ","ቅ"],"STANDALONENARROWWEEKDAYS":["እ","ሰ","ማ","ረ","ሐ","ዓ","ቅ"],"SHORTQUARTERS":["ሩብ1","ሩብ2","ሩብ3","ሩብ4"],"QUARTERS":["1ኛው ሩብ","ሁለተኛው ሩብ","3ኛው ሩብ","4ኛው ሩብ"],"AMPMS":["ጥዋት","ከሰዓት"],"DATEFORMATS":["EEEE, d MMMM y","d MMMM y","d MMM y","dd/MM/y"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/ar.json b/intl/lib/src/data/dates/symbols/ar.json
new file mode 100644
index 0000000..7fd827f
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/ar.json
@@ -0,0 +1 @@
+{"NAME":"ar","ERAS":["ق.م","م"],"ERANAMES":["قبل الميلاد","ميلادي"],"NARROWMONTHS":["ي","ف","م","أ","و","ن","ل","غ","س","ك","ب","د"],"STANDALONENARROWMONTHS":["ي","ف","م","أ","و","ن","ل","غ","س","ك","ب","د"],"MONTHS":["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],"STANDALONEMONTHS":["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],"SHORTMONTHS":["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],"STANDALONESHORTMONTHS":["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],"WEEKDAYS":["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],"STANDALONEWEEKDAYS":["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],"SHORTWEEKDAYS":["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],"STANDALONESHORTWEEKDAYS":["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],"NARROWWEEKDAYS":["ح","ن","ث","ر","خ","ج","س"],"STANDALONENARROWWEEKDAYS":["ح","ن","ث","ر","خ","ج","س"],"SHORTQUARTERS":["الربع الأول","الربع الثاني","الربع الثالث","الربع الرابع"],"QUARTERS":["الربع الأول","الربع الثاني","الربع الثالث","الربع الرابع"],"AMPMS":["ص","م"],"DATEFORMATS":["EEEE، d MMMM، y","d MMMM، y","dd‏/MM‏/y","d‏/M‏/y"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":5,"WEEKENDRANGE":[4,5],"FIRSTWEEKCUTOFFDAY":4,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/az.json b/intl/lib/src/data/dates/symbols/az.json
new file mode 100644
index 0000000..dbba148
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/az.json
@@ -0,0 +1 @@
+{"NAME":"az","ERAS":["e.ə.","b.e."],"ERANAMES":["eramızdan əvvəl","bizim eramızın"],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["yanvar","fevral","mart","aprel","may","iyun","iyul","avqust","sentyabr","oktyabr","noyabr","dekabr"],"STANDALONEMONTHS":["Yanvar","Fevral","Mart","Aprel","May","İyun","İyul","Avqust","Sentyabr","Oktyabr","Noyabr","Dekabr"],"SHORTMONTHS":["yan","fev","mar","apr","may","iyn","iyl","avq","sen","okt","noy","dek"],"STANDALONESHORTMONTHS":["yan","fev","mar","apr","may","iyn","iyl","avq","sen","okt","noy","dek"],"WEEKDAYS":["bazar","bazar ertəsi","çərşənbə axşamı","çərşənbə","cümə axşamı","cümə","şənbə"],"STANDALONEWEEKDAYS":["bazar","bazar ertəsi","çərşənbə axşamı","çərşənbə","cümə axşamı","cümə","şənbə"],"SHORTWEEKDAYS":["B.","B.E.","Ç.A.","Ç.","C.A.","C","Ş."],"STANDALONESHORTWEEKDAYS":["B.","B.E.","Ç.A.","Ç.","C.A.","C","Ş."],"NARROWWEEKDAYS":["7","1","2","3","4","5","6"],"STANDALONENARROWWEEKDAYS":["7","1","2","3","4","5","6"],"SHORTQUARTERS":["1-ci kv.","2-ci kv.","3-cü kv.","4-cü kv."],"QUARTERS":["1-ci kvartal","2-ci kvartal","3-cü kvartal","4-cü kvartal"],"AMPMS":["AM","PM"],"DATEFORMATS":["d MMMM y, EEEE","d MMMM y","d MMM y","dd.MM.yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/bg.json b/intl/lib/src/data/dates/symbols/bg.json
new file mode 100644
index 0000000..647a5e5
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/bg.json
@@ -0,0 +1 @@
+{"NAME":"bg","ERAS":["пр.Хр.","сл.Хр."],"ERANAMES":["пр.Хр.","сл.Хр."],"NARROWMONTHS":["я","ф","м","а","м","ю","ю","а","с","о","н","д"],"STANDALONENARROWMONTHS":["я","ф","м","а","м","ю","ю","а","с","о","н","д"],"MONTHS":["януари","февруари","март","април","май","юни","юли","август","септември","октомври","ноември","декември"],"STANDALONEMONTHS":["януари","февруари","март","април","май","юни","юли","август","септември","октомври","ноември","декември"],"SHORTMONTHS":["ян.","февр.","март","апр.","май","юни","юли","авг.","септ.","окт.","ноем.","дек."],"STANDALONESHORTMONTHS":["ян.","февр.","март","апр.","май","юни","юли","авг.","септ.","окт.","ноем.","дек."],"WEEKDAYS":["неделя","понеделник","вторник","сряда","четвъртък","петък","събота"],"STANDALONEWEEKDAYS":["неделя","понеделник","вторник","сряда","четвъртък","петък","събота"],"SHORTWEEKDAYS":["нд","пн","вт","ср","чт","пт","сб"],"STANDALONESHORTWEEKDAYS":["нд","пн","вт","ср","чт","пт","сб"],"NARROWWEEKDAYS":["н","п","в","с","ч","п","с"],"STANDALONENARROWWEEKDAYS":["н","п","в","с","ч","п","с"],"SHORTQUARTERS":["1 трим.","2 трим.","3 трим.","4 трим."],"QUARTERS":["1-во тримесечие","2-ро тримесечие","3-то тримесечие","4-то тримесечие"],"AMPMS":["пр.об.","сл.об."],"DATEFORMATS":["EEEE, d MMMM y 'г'.","d MMMM y 'г'.","d.MM.y 'г'.","d.MM.yy"],"TIMEFORMATS":["H:mm:ss zzzz","H:mm:ss z","H:mm:ss","H:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1}, {0}","{1}, {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/bn.json b/intl/lib/src/data/dates/symbols/bn.json
new file mode 100644
index 0000000..97ac316
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/bn.json
@@ -0,0 +1 @@
+{"NAME":"bn","ERAS":["খ্রিস্টপূর্ব","খৃষ্টাব্দ"],"ERANAMES":["খ্রিস্টপূর্ব","খৃষ্টাব্দ"],"NARROWMONTHS":["জা","ফে","মা","এ","মে","জুন","জু","আ","সে","অ","ন","ডি"],"STANDALONENARROWMONTHS":["জা","ফে","মা","এ","মে","জুন","জু","আ","সে","অ","ন","ডি"],"MONTHS":["জানুয়ারী","ফেব্রুয়ারী","মার্চ","এপ্রিল","মে","জুন","জুলাই","আগস্ট","সেপ্টেম্বর","অক্টোবর","নভেম্বর","ডিসেম্বর"],"STANDALONEMONTHS":["জানুয়ারী","ফেব্রুয়ারী","মার্চ","এপ্রিল","মে","জুন","জুলাই","আগস্ট","সেপ্টেম্বর","অক্টোবর","নভেম্বর","ডিসেম্বর"],"SHORTMONTHS":["জানুয়ারী","ফেব্রুয়ারী","মার্চ","এপ্রিল","মে","জুন","জুলাই","আগস্ট","সেপ্টেম্বর","অক্টোবর","নভেম্বর","ডিসেম্বর"],"STANDALONESHORTMONTHS":["জানুয়ারী","ফেব্রুয়ারী","মার্চ","এপ্রিল","মে","জুন","জুলাই","আগস্ট","সেপ্টেম্বর","অক্টোবর","নভেম্বর","ডিসেম্বর"],"WEEKDAYS":["রবিবার","সোমবার","মঙ্গলবার","বুধবার","বৃহষ্পতিবার","শুক্রবার","শনিবার"],"STANDALONEWEEKDAYS":["রবিবার","সোমবার","মঙ্গলবার","বুধবার","বৃহষ্পতিবার","শুক্রবার","শনিবার"],"SHORTWEEKDAYS":["রবি","সোম","মঙ্গল","বুধ","বৃহস্পতি","শুক্র","শনি"],"STANDALONESHORTWEEKDAYS":["রবি","সোম","মঙ্গল","বুধ","বৃহস্পতি","শুক্র","শনি"],"NARROWWEEKDAYS":["র","সো","ম","বু","বৃ","শু","শ"],"STANDALONENARROWWEEKDAYS":["র","সো","ম","বু","বৃ","শু","শ"],"SHORTQUARTERS":["চতুর্থাংশ ১","চতুর্থাংশ ২","চতুর্থাংশ ৩","চতুর্থাংশ ৪"],"QUARTERS":["প্রথম চতুর্থাংশ","দ্বিতীয় চতুর্থাংশ","তৃতীয় চতুর্থাংশ","চতুর্থ চতুর্থাংশ"],"AMPMS":["am","pm"],"DATEFORMATS":["EEEE, d MMMM, y","d MMMM, y","d MMM, y","d/M/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":4,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/br.json b/intl/lib/src/data/dates/symbols/br.json
new file mode 100644
index 0000000..ff3bd11
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/br.json
@@ -0,0 +1 @@
+{"NAME":"br","ERAS":["BCE","CE"],"ERANAMES":["BCE","CE"],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["Genver","Cʼhwevrer","Meurzh","Ebrel","Mae","Mezheven","Gouere","Eost","Gwengolo","Here","Du","Kerzu"],"STANDALONEMONTHS":["Genver","Cʼhwevrer","Meurzh","Ebrel","Mae","Mezheven","Gouere","Eost","Gwengolo","Here","Du","Kerzu"],"SHORTMONTHS":["Gen","Cʼhwe","Meur","Ebr","Mae","Mezh","Goue","Eost","Gwen","Here","Du","Ker"],"STANDALONESHORTMONTHS":["Gen","Cʼhwe","Meur","Ebr","Mae","Mezh","Goue","Eost","Gwen","Here","Du","Ker"],"WEEKDAYS":["Sul","Lun","Meurzh","Mercʼher","Yaou","Gwener","Sadorn"],"STANDALONEWEEKDAYS":["Sul","Lun","Meurzh","Mercʼher","Yaou","Gwener","Sadorn"],"SHORTWEEKDAYS":["sul","lun","meu.","mer.","yaou","gwe.","sad."],"STANDALONESHORTWEEKDAYS":["sul","lun","meu.","mer.","yaou","gwe.","sad."],"NARROWWEEKDAYS":["su","lu","mz","mc","ya","gw","sa"],"STANDALONENARROWWEEKDAYS":["su","lu","mz","mc","ya","gw","sa"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["Q1","Q2","Q3","Q4"],"AMPMS":["AM","PM"],"DATEFORMATS":["y MMMM d, EEEE","y MMMM d","y MMM d","y-MM-dd"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/ca.json b/intl/lib/src/data/dates/symbols/ca.json
new file mode 100644
index 0000000..245c8c3
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/ca.json
@@ -0,0 +1 @@
+{"NAME":"ca","ERAS":["aC","dC"],"ERANAMES":["abans de Crist","després de Crist"],"NARROWMONTHS":["GN","FB","MÇ","AB","MG","JN","JL","AG","ST","OC","NV","DS"],"STANDALONENARROWMONTHS":["GN","FB","MÇ","AB","MG","JN","JL","AG","ST","OC","NV","DS"],"MONTHS":["gener","febrer","març","abril","maig","juny","juliol","agost","setembre","octubre","novembre","desembre"],"STANDALONEMONTHS":["gener","febrer","març","abril","maig","juny","juliol","agost","setembre","octubre","novembre","desembre"],"SHORTMONTHS":["gen.","feb.","març","abr.","maig","juny","jul.","ag.","set.","oct.","nov.","des."],"STANDALONESHORTMONTHS":["gen.","feb.","març","abr.","maig","juny","jul.","ag.","set.","oct.","nov.","des."],"WEEKDAYS":["diumenge","dilluns","dimarts","dimecres","dijous","divendres","dissabte"],"STANDALONEWEEKDAYS":["diumenge","dilluns","dimarts","dimecres","dijous","divendres","dissabte"],"SHORTWEEKDAYS":["dg.","dl.","dt.","dc.","dj.","dv.","ds."],"STANDALONESHORTWEEKDAYS":["dg.","dl.","dt.","dc.","dj.","dv.","ds."],"NARROWWEEKDAYS":["dg","dl","dt","dc","dj","dv","ds"],"STANDALONENARROWWEEKDAYS":["dg","dl","dt","dc","dj","dv","ds"],"SHORTQUARTERS":["1T","2T","3T","4T"],"QUARTERS":["1r trimestre","2n trimestre","3r trimestre","4t trimestre"],"AMPMS":["a. m.","p. m."],"DATEFORMATS":["EEEE, d MMMM 'de' y","d MMMM 'de' y","dd/MM/y","d/M/yy"],"TIMEFORMATS":["H:mm:ss zzzz","H:mm:ss z","H:mm:ss","H:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/chr.json b/intl/lib/src/data/dates/symbols/chr.json
new file mode 100644
index 0000000..2bd27ac
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/chr.json
@@ -0,0 +1 @@
+{"NAME":"chr","ERAS":["ᎤᏓᎷᎸ","ᎤᎶᏐᏅ"],"ERANAMES":["Ꮟ ᏥᏌ ᎾᏕᎲᏍᎬᎾ","ᎠᎩᏃᎮᎵᏓᏍᏗᏱ ᎠᏕᏘᏱᏍᎬ ᏱᎰᏩ ᏧᏓᏂᎸᎢᏍᏗ"],"NARROWMONTHS":["Ꭴ","Ꭷ","Ꭰ","Ꭷ","Ꭰ","Ꮥ","Ꭻ","Ꭶ","Ꮪ","Ꮪ","Ꮕ","Ꭵ"],"STANDALONENARROWMONTHS":["Ꭴ","Ꭷ","Ꭰ","Ꭷ","Ꭰ","Ꮥ","Ꭻ","Ꭶ","Ꮪ","Ꮪ","Ꮕ","Ꭵ"],"MONTHS":["ᎤᏃᎸᏔᏅ","ᎧᎦᎵ","ᎠᏅᏱ","ᎧᏬᏂ","ᎠᏂᏍᎬᏘ","ᏕᎭᎷᏱ","ᎫᏰᏉᏂ","ᎦᎶᏂ","ᏚᎵᏍᏗ","ᏚᏂᏅᏗ","ᏅᏓᏕᏆ","ᎥᏍᎩᏱ"],"STANDALONEMONTHS":["ᎤᏃᎸᏔᏅ","ᎧᎦᎵ","ᎠᏅᏱ","ᎧᏬᏂ","ᎠᏂᏍᎬᏘ","ᏕᎭᎷᏱ","ᎫᏰᏉᏂ","ᎦᎶᏂ","ᏚᎵᏍᏗ","ᏚᏂᏅᏗ","ᏅᏓᏕᏆ","ᎥᏍᎩᏱ"],"SHORTMONTHS":["ᎤᏃ","ᎧᎦ","ᎠᏅ","ᎧᏬ","ᎠᏂ","ᏕᎭ","ᎫᏰ","ᎦᎶ","ᏚᎵ","ᏚᏂ","ᏅᏓ","ᎥᏍ"],"STANDALONESHORTMONTHS":["ᎤᏃ","ᎧᎦ","ᎠᏅ","ᎧᏬ","ᎠᏂ","ᏕᎭ","ᎫᏰ","ᎦᎶ","ᏚᎵ","ᏚᏂ","ᏅᏓ","ᎥᏍ"],"WEEKDAYS":["ᎤᎾᏙᏓᏆᏍᎬ","ᎤᎾᏙᏓᏉᏅᎯ","ᏔᎵᏁᎢᎦ","ᏦᎢᏁᎢᎦ","ᏅᎩᏁᎢᎦ","ᏧᎾᎩᎶᏍᏗ","ᎤᎾᏙᏓᏈᏕᎾ"],"STANDALONEWEEKDAYS":["ᎤᎾᏙᏓᏆᏍᎬ","ᎤᎾᏙᏓᏉᏅᎯ","ᏔᎵᏁᎢᎦ","ᏦᎢᏁᎢᎦ","ᏅᎩᏁᎢᎦ","ᏧᎾᎩᎶᏍᏗ","ᎤᎾᏙᏓᏈᏕᎾ"],"SHORTWEEKDAYS":["ᏆᏍᎬ","ᏉᏅᎯ","ᏔᎵᏁ","ᏦᎢᏁ","ᏅᎩᏁ","ᏧᎾᎩ","ᏈᏕᎾ"],"STANDALONESHORTWEEKDAYS":["ᏆᏍᎬ","ᏉᏅᎯ","ᏔᎵᏁ","ᏦᎢᏁ","ᏅᎩᏁ","ᏧᎾᎩ","ᏈᏕᎾ"],"NARROWWEEKDAYS":["Ꮖ","Ꮙ","Ꮤ","Ꮶ","Ꮕ","Ꮷ","Ꭴ"],"STANDALONENARROWWEEKDAYS":["Ꮖ","Ꮙ","Ꮤ","Ꮶ","Ꮕ","Ꮷ","Ꭴ"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["Q1","Q2","Q3","Q4"],"AMPMS":["ᏌᎾᎴ","ᏒᎯᏱᎢᏗᏢ"],"DATEFORMATS":["EEEE, MMMM d, y","MMMM d, y","MMM d, y","M/d/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/cs.json b/intl/lib/src/data/dates/symbols/cs.json
new file mode 100644
index 0000000..ce7279d
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/cs.json
@@ -0,0 +1 @@
+{"NAME":"cs","ERAS":["př. n. l.","n. l."],"ERANAMES":["př. n. l.","n. l."],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["l","ú","b","d","k","č","č","s","z","ř","l","p"],"MONTHS":["ledna","února","března","dubna","května","června","července","srpna","září","října","listopadu","prosince"],"STANDALONEMONTHS":["leden","únor","březen","duben","květen","červen","červenec","srpen","září","říjen","listopad","prosinec"],"SHORTMONTHS":["led","úno","bře","dub","kvě","čvn","čvc","srp","zář","říj","lis","pro"],"STANDALONESHORTMONTHS":["led","úno","bře","dub","kvě","čvn","čvc","srp","zář","říj","lis","pro"],"WEEKDAYS":["neděle","pondělí","úterý","středa","čtvrtek","pátek","sobota"],"STANDALONEWEEKDAYS":["neděle","pondělí","úterý","středa","čtvrtek","pátek","sobota"],"SHORTWEEKDAYS":["ne","po","út","st","čt","pá","so"],"STANDALONESHORTWEEKDAYS":["ne","po","út","st","čt","pá","so"],"NARROWWEEKDAYS":["N","P","Ú","S","Č","P","S"],"STANDALONENARROWWEEKDAYS":["N","P","Ú","S","Č","P","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1. čtvrtletí","2. čtvrtletí","3. čtvrtletí","4. čtvrtletí"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE d. MMMM y","d. MMMM y","d. M. y","dd.MM.yy"],"TIMEFORMATS":["H:mm:ss zzzz","H:mm:ss z","H:mm:ss","H:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/cy.json b/intl/lib/src/data/dates/symbols/cy.json
new file mode 100644
index 0000000..c7e25f9
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/cy.json
@@ -0,0 +1 @@
+{"NAME":"cy","ERAS":["CC","OC"],"ERANAMES":["Cyn Crist","Oed Crist"],"NARROWMONTHS":["I","Ch","M","E","M","M","G","A","M","H","T","Rh"],"STANDALONENARROWMONTHS":["I","Ch","M","E","M","M","G","A","M","H","T","Rh"],"MONTHS":["Ionawr","Chwefror","Mawrth","Ebrill","Mai","Mehefin","Gorffennaf","Awst","Medi","Hydref","Tachwedd","Rhagfyr"],"STANDALONEMONTHS":["Ionawr","Chwefror","Mawrth","Ebrill","Mai","Mehefin","Gorffennaf","Awst","Medi","Hydref","Tachwedd","Rhagfyr"],"SHORTMONTHS":["Ion","Chwef","Mawrth","Ebrill","Mai","Meh","Gorff","Awst","Medi","Hyd","Tach","Rhag"],"STANDALONESHORTMONTHS":["Ion","Chw","Maw","Ebr","Mai","Meh","Gor","Awst","Medi","Hyd","Tach","Rhag"],"WEEKDAYS":["Dydd Sul","Dydd Llun","Dydd Mawrth","Dydd Mercher","Dydd Iau","Dydd Gwener","Dydd Sadwrn"],"STANDALONEWEEKDAYS":["Dydd Sul","Dydd Llun","Dydd Mawrth","Dydd Mercher","Dydd Iau","Dydd Gwener","Dydd Sadwrn"],"SHORTWEEKDAYS":["Sul","Llun","Maw","Mer","Iau","Gwen","Sad"],"STANDALONESHORTWEEKDAYS":["Sul","Llun","Maw","Mer","Iau","Gwe","Sad"],"NARROWWEEKDAYS":["S","Ll","M","M","I","G","S"],"STANDALONENARROWWEEKDAYS":["S","Ll","M","M","I","G","S"],"SHORTQUARTERS":["Ch1","Ch2","Ch3","Ch4"],"QUARTERS":["Chwarter 1af","2il chwarter","3ydd chwarter","4ydd chwarter"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, d MMMM y","d MMMM y","d MMM y","dd/MM/y"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} 'am' {0}","{1} 'am' {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/da.json b/intl/lib/src/data/dates/symbols/da.json
new file mode 100644
index 0000000..80111c3
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/da.json
@@ -0,0 +1 @@
+{"NAME":"da","ERAS":["f.Kr.","e.Kr."],"ERANAMES":["f.Kr.","e.Kr."],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["januar","februar","marts","april","maj","juni","juli","august","september","oktober","november","december"],"STANDALONEMONTHS":["januar","februar","marts","april","maj","juni","juli","august","september","oktober","november","december"],"SHORTMONTHS":["jan.","feb.","mar.","apr.","maj","jun.","jul.","aug.","sep.","okt.","nov.","dec."],"STANDALONESHORTMONTHS":["jan","feb","mar","apr","maj","jun","jul","aug","sep","okt","nov","dec"],"WEEKDAYS":["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],"STANDALONEWEEKDAYS":["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],"SHORTWEEKDAYS":["søn.","man.","tir.","ons.","tor.","fre.","lør."],"STANDALONESHORTWEEKDAYS":["søn","man","tir","ons","tor","fre","lør"],"NARROWWEEKDAYS":["S","M","T","O","T","F","L"],"STANDALONENARROWWEEKDAYS":["S","M","T","O","T","F","L"],"SHORTQUARTERS":["K1","K2","K3","K4"],"QUARTERS":["1. kvartal","2. kvartal","3. kvartal","4. kvartal"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE 'den' d. MMMM y","d. MMM y","dd/MM/y","dd/MM/yy"],"TIMEFORMATS":["HH.mm.ss zzzz","HH.mm.ss z","HH.mm.ss","HH.mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} 'kl.' {0}","{1} 'kl.' {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/de.json b/intl/lib/src/data/dates/symbols/de.json
new file mode 100644
index 0000000..e1f8c13
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/de.json
@@ -0,0 +1 @@
+{"NAME":"de","ERAS":["v. Chr.","n. Chr."],"ERANAMES":["v. Chr.","n. Chr."],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],"STANDALONEMONTHS":["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],"SHORTMONTHS":["Jan.","Feb.","März","Apr.","Mai","Juni","Juli","Aug.","Sep.","Okt.","Nov.","Dez."],"STANDALONESHORTMONTHS":["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],"WEEKDAYS":["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],"STANDALONEWEEKDAYS":["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],"SHORTWEEKDAYS":["So.","Mo.","Di.","Mi.","Do.","Fr.","Sa."],"STANDALONESHORTWEEKDAYS":["So","Mo","Di","Mi","Do","Fr","Sa"],"NARROWWEEKDAYS":["S","M","D","M","D","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","D","M","D","F","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1. Quartal","2. Quartal","3. Quartal","4. Quartal"],"AMPMS":["vorm.","nachm."],"DATEFORMATS":["EEEE, d. MMMM y","d. MMMM y","dd.MM.y","dd.MM.yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/de_AT.json b/intl/lib/src/data/dates/symbols/de_AT.json
new file mode 100644
index 0000000..c4bdd25
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/de_AT.json
@@ -0,0 +1 @@
+{"NAME":"de_AT","ERAS":["v. Chr.","n. Chr."],"ERANAMES":["v. Chr.","n. Chr."],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["Jänner","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],"STANDALONEMONTHS":["Jänner","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],"SHORTMONTHS":["Jän.","Feb.","März","Apr.","Mai","Juni","Juli","Aug.","Sep.","Okt.","Nov.","Dez."],"STANDALONESHORTMONTHS":["Jän","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],"WEEKDAYS":["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],"STANDALONEWEEKDAYS":["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],"SHORTWEEKDAYS":["So.","Mo.","Di.","Mi.","Do.","Fr.","Sa."],"STANDALONESHORTWEEKDAYS":["So","Mo","Di","Mi","Do","Fr","Sa"],"NARROWWEEKDAYS":["S","M","D","M","D","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","D","M","D","F","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1. Quartal","2. Quartal","3. Quartal","4. Quartal"],"AMPMS":["vorm.","nachm."],"DATEFORMATS":["EEEE, dd. MMMM y","dd. MMMM y","dd.MM.y","dd.MM.yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/de_CH.json b/intl/lib/src/data/dates/symbols/de_CH.json
new file mode 100644
index 0000000..ed58d46
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/de_CH.json
@@ -0,0 +1 @@
+{"NAME":"de_CH","ERAS":["v. Chr.","n. Chr."],"ERANAMES":["v. Chr.","n. Chr."],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],"STANDALONEMONTHS":["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],"SHORTMONTHS":["Jan.","Feb.","März","Apr.","Mai","Juni","Juli","Aug.","Sep.","Okt.","Nov.","Dez."],"STANDALONESHORTMONTHS":["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],"WEEKDAYS":["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],"STANDALONEWEEKDAYS":["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],"SHORTWEEKDAYS":["So.","Mo.","Di.","Mi.","Do.","Fr.","Sa."],"STANDALONESHORTWEEKDAYS":["So","Mo","Di","Mi","Do","Fr","Sa"],"NARROWWEEKDAYS":["S","M","D","M","D","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","D","M","D","F","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1. Quartal","2. Quartal","3. Quartal","4. Quartal"],"AMPMS":["vorm.","nachm."],"DATEFORMATS":["EEEE, d. MMMM y","d. MMMM y","dd.MM.y","dd.MM.yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/el.json b/intl/lib/src/data/dates/symbols/el.json
new file mode 100644
index 0000000..cf8c73e
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/el.json
@@ -0,0 +1 @@
+{"NAME":"el","ERAS":["π.Χ.","μ.Χ."],"ERANAMES":["π.Χ.","μ.Χ."],"NARROWMONTHS":["Ι","Φ","Μ","Α","Μ","Ι","Ι","Α","Σ","Ο","Ν","Δ"],"STANDALONENARROWMONTHS":["Ι","Φ","Μ","Α","Μ","Ι","Ι","Α","Σ","Ο","Ν","Δ"],"MONTHS":["Ιανουαρίου","Φεβρουαρίου","Μαρτίου","Απριλίου","Μαΐου","Ιουνίου","Ιουλίου","Αυγούστου","Σεπτεμβρίου","Οκτωβρίου","Νοεμβρίου","Δεκεμβρίου"],"STANDALONEMONTHS":["Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος","Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος"],"SHORTMONTHS":["Ιαν","Φεβ","Μαρ","Απρ","Μαΐ","Ιουν","Ιουλ","Αυγ","Σεπ","Οκτ","Νοε","Δεκ"],"STANDALONESHORTMONTHS":["Ιαν","Φεβ","Μάρ","Απρ","Μάι","Ιούν","Ιούλ","Αύγ","Σεπ","Οκτ","Νοέ","Δεκ"],"WEEKDAYS":["Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο"],"STANDALONEWEEKDAYS":["Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο"],"SHORTWEEKDAYS":["Κυρ","Δευ","Τρί","Τετ","Πέμ","Παρ","Σάβ"],"STANDALONESHORTWEEKDAYS":["Κυρ","Δευ","Τρί","Τετ","Πέμ","Παρ","Σάβ"],"NARROWWEEKDAYS":["Κ","Δ","Τ","Τ","Π","Π","Σ"],"STANDALONENARROWWEEKDAYS":["Κ","Δ","Τ","Τ","Π","Π","Σ"],"SHORTQUARTERS":["Τ1","Τ2","Τ3","Τ4"],"QUARTERS":["1ο τρίμηνο","2ο τρίμηνο","3ο τρίμηνο","4ο τρίμηνο"],"AMPMS":["π.μ.","μ.μ."],"DATEFORMATS":["EEEE, d MMMM y","d MMMM y","d MMM y","d/M/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} - {0}","{1} - {0}","{1} - {0}","{1} - {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/en.json b/intl/lib/src/data/dates/symbols/en.json
new file mode 100644
index 0000000..aef2282
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/en.json
@@ -0,0 +1 @@
+{"NAME":"en","ERAS":["BC","AD"],"ERANAMES":["Before Christ","Anno Domini"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"STANDALONEMONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"SHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"STANDALONESHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"WEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"STANDALONEWEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"SHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"STANDALONESHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"NARROWWEEKDAYS":["S","M","T","W","T","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","T","W","T","F","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1st quarter","2nd quarter","3rd quarter","4th quarter"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, MMMM d, y","MMMM d, y","MMM d, y","M/d/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} 'at' {0}","{1} 'at' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/en_AU.json b/intl/lib/src/data/dates/symbols/en_AU.json
new file mode 100644
index 0000000..027594a
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/en_AU.json
@@ -0,0 +1 @@
+{"NAME":"en_AU","ERAS":["BC","AD"],"ERANAMES":["Before Christ","Anno Domini"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"STANDALONEMONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"SHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"STANDALONESHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"WEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"STANDALONEWEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"SHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"STANDALONESHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"NARROWWEEKDAYS":["S","M","T","W","T","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","T","W","T","F","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1st quarter","2nd quarter","3rd quarter","4th quarter"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, d MMMM y","d MMMM y","d MMM y","d/MM/y"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} 'at' {0}","{1} 'at' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/en_GB.json b/intl/lib/src/data/dates/symbols/en_GB.json
new file mode 100644
index 0000000..df07512
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/en_GB.json
@@ -0,0 +1 @@
+{"NAME":"en_GB","ERAS":["BC","AD"],"ERANAMES":["Before Christ","Anno Domini"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"STANDALONEMONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"SHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"STANDALONESHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"WEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"STANDALONEWEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"SHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"STANDALONESHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"NARROWWEEKDAYS":["S","M","T","W","T","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","T","W","T","F","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1st quarter","2nd quarter","3rd quarter","4th quarter"],"AMPMS":["am","pm"],"DATEFORMATS":["EEEE, d MMMM y","d MMMM y","d MMM y","dd/MM/y"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/en_IE.json b/intl/lib/src/data/dates/symbols/en_IE.json
new file mode 100644
index 0000000..12bf798
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/en_IE.json
@@ -0,0 +1 @@
+{"NAME":"en_IE","ERAS":["BC","AD"],"ERANAMES":["Before Christ","Anno Domini"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"STANDALONEMONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"SHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"STANDALONESHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"WEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"STANDALONEWEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"SHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"STANDALONESHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"NARROWWEEKDAYS":["S","M","T","W","T","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","T","W","T","F","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1st quarter","2nd quarter","3rd quarter","4th quarter"],"AMPMS":["a.m.","p.m."],"DATEFORMATS":["EEEE d MMMM y","MMMM d, y","MMM d, y","M/d/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":2,"DATETIMEFORMATS":["{1} 'at' {0}","{1} 'at' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/en_IN.json b/intl/lib/src/data/dates/symbols/en_IN.json
new file mode 100644
index 0000000..164637f
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/en_IN.json
@@ -0,0 +1 @@
+{"NAME":"en_IN","ERAS":["BC","AD"],"ERANAMES":["Before Christ","Anno Domini"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"STANDALONEMONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"SHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"STANDALONESHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"WEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"STANDALONEWEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"SHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"STANDALONESHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"NARROWWEEKDAYS":["S","M","T","W","T","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","T","W","T","F","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1st quarter","2nd quarter","3rd quarter","4th quarter"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE d MMMM y","d MMMM y","dd-MMM-y","dd/MM/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[6,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} 'at' {0}","{1} 'at' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/en_ISO.json b/intl/lib/src/data/dates/symbols/en_ISO.json
new file mode 100644
index 0000000..cbfb6d8
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/en_ISO.json
@@ -0,0 +1 @@
+{"NAME":"en_ISO","ERAS":["BC","AD"],"ERANAMES":["Before Christ","Anno Domini"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"STANDALONEMONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"SHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"STANDALONESHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"WEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"STANDALONEWEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"SHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"STANDALONESHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"NARROWWEEKDAYS":["S","M","T","W","T","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","T","W","T","F","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1st quarter","2nd quarter","3rd quarter","4th quarter"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, y MMMM dd","y MMMM d","y MMM d","yyyy-MM-dd"],"TIMEFORMATS":["HH:mm:ss v","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":{"Md":"M/d","MMMMd":"MMMM d","MMMd":"MMM d"},"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} 'at' {0}","{1} 'at' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/en_SG.json b/intl/lib/src/data/dates/symbols/en_SG.json
new file mode 100644
index 0000000..2a9e502
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/en_SG.json
@@ -0,0 +1 @@
+{"NAME":"en_SG","ERAS":["BC","AD"],"ERANAMES":["Before Christ","Anno Domini"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"STANDALONEMONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"SHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"STANDALONESHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"WEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"STANDALONEWEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"SHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"STANDALONESHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"NARROWWEEKDAYS":["S","M","T","W","T","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","T","W","T","F","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1st quarter","2nd quarter","3rd quarter","4th quarter"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, d MMMM y","d MMMM y","d MMM y","d/M/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} 'at' {0}","{1} 'at' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/en_US.json b/intl/lib/src/data/dates/symbols/en_US.json
new file mode 100644
index 0000000..a549088
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/en_US.json
@@ -0,0 +1 @@
+{"NAME":"en_US","ERAS":["BC","AD"],"ERANAMES":["Before Christ","Anno Domini"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"STANDALONEMONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"SHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"STANDALONESHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"WEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"STANDALONEWEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"SHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"STANDALONESHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"NARROWWEEKDAYS":["S","M","T","W","T","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","T","W","T","F","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1st quarter","2nd quarter","3rd quarter","4th quarter"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, MMMM d, y","MMMM d, y","MMM d, y","M/d/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} 'at' {0}","{1} 'at' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/en_ZA.json b/intl/lib/src/data/dates/symbols/en_ZA.json
new file mode 100644
index 0000000..e0f9627
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/en_ZA.json
@@ -0,0 +1 @@
+{"NAME":"en_ZA","ERAS":["BC","AD"],"ERANAMES":["Before Christ","Anno Domini"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"STANDALONEMONTHS":["January","February","March","April","May","June","July","August","September","October","November","December"],"SHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"STANDALONESHORTMONTHS":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"WEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"STANDALONEWEEKDAYS":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"SHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"STANDALONESHORTWEEKDAYS":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"NARROWWEEKDAYS":["S","M","T","W","T","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","T","W","T","F","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1st quarter","2nd quarter","3rd quarter","4th quarter"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE dd MMMM y","dd MMMM y","dd MMM y","y/MM/dd"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} 'at' {0}","{1} 'at' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/es.json b/intl/lib/src/data/dates/symbols/es.json
new file mode 100644
index 0000000..094a145
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/es.json
@@ -0,0 +1 @@
+{"NAME":"es","ERAS":["a. C.","d. C."],"ERANAMES":["antes de Cristo","anno Dómini"],"NARROWMONTHS":["E","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["E","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],"STANDALONEMONTHS":["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],"SHORTMONTHS":["ene.","feb.","mar.","abr.","may.","jun.","jul.","ago.","sept.","oct.","nov.","dic."],"STANDALONESHORTMONTHS":["Ene.","Feb.","Mar.","Abr.","May.","Jun.","Jul.","Ago.","Sept.","Oct.","Nov.","Dic."],"WEEKDAYS":["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],"STANDALONEWEEKDAYS":["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"],"SHORTWEEKDAYS":["dom.","lun.","mar.","mié.","jue.","vie.","sáb."],"STANDALONESHORTWEEKDAYS":["Dom.","Lun.","Mar.","Mié.","Jue.","Vie.","Sáb."],"NARROWWEEKDAYS":["D","L","M","X","J","V","S"],"STANDALONENARROWWEEKDAYS":["D","L","M","X","J","V","S"],"SHORTQUARTERS":["T1","T2","T3","T4"],"QUARTERS":["1.er trimestre","2.º trimestre","3.er trimestre","4.º trimestre"],"AMPMS":["a. m.","p. m."],"DATEFORMATS":["EEEE, d 'de' MMMM 'de' y","d 'de' MMMM 'de' y","d/M/y","d/M/yy"],"TIMEFORMATS":["H:mm:ss (zzzz)","H:mm:ss z","H:mm:ss","H:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1}, {0}","{1}, {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/es_419.json b/intl/lib/src/data/dates/symbols/es_419.json
new file mode 100644
index 0000000..ffa62d0
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/es_419.json
@@ -0,0 +1 @@
+{"NAME":"es_419","ERAS":["a. C.","d. C."],"ERANAMES":["antes de Cristo","anno Dómini"],"NARROWMONTHS":["E","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["E","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],"STANDALONEMONTHS":["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],"SHORTMONTHS":["ene.","feb.","mar.","abr.","may.","jun.","jul.","ago.","sept.","oct.","nov.","dic."],"STANDALONESHORTMONTHS":["Ene.","Feb.","Mar.","Abr.","May.","Jun.","Jul.","Ago.","Sept.","Oct.","Nov.","Dic."],"WEEKDAYS":["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],"STANDALONEWEEKDAYS":["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"],"SHORTWEEKDAYS":["dom.","lun.","mar.","mié.","jue.","vie.","sáb."],"STANDALONESHORTWEEKDAYS":["Dom.","Lun.","Mar.","Mié.","Jue.","Vie.","Sáb."],"NARROWWEEKDAYS":["D","L","M","X","J","V","S"],"STANDALONENARROWWEEKDAYS":["D","L","M","X","J","V","S"],"SHORTQUARTERS":["T1","T2","T3","T4"],"QUARTERS":["1.er trimestre","2.º trimestre","3.er trimestre","4.º trimestre"],"AMPMS":["a. m.","p. m."],"DATEFORMATS":["EEEE, d 'de' MMMM 'de' y","d 'de' MMMM 'de' y","d/M/y","d/M/yy"],"TIMEFORMATS":["H:mm:ss (zzzz)","H:mm:ss z","H:mm:ss","H:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1}, {0}","{1}, {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/es_ES.json b/intl/lib/src/data/dates/symbols/es_ES.json
new file mode 100644
index 0000000..12a5bd1
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/es_ES.json
@@ -0,0 +1 @@
+{"NAME":"es_ES","ERAS":["a. C.","d. C."],"ERANAMES":["antes de Cristo","anno Dómini"],"NARROWMONTHS":["E","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["E","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],"STANDALONEMONTHS":["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],"SHORTMONTHS":["ene.","feb.","mar.","abr.","may.","jun.","jul.","ago.","sept.","oct.","nov.","dic."],"STANDALONESHORTMONTHS":["Ene.","Feb.","Mar.","Abr.","May.","Jun.","Jul.","Ago.","Sept.","Oct.","Nov.","Dic."],"WEEKDAYS":["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],"STANDALONEWEEKDAYS":["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"],"SHORTWEEKDAYS":["dom.","lun.","mar.","mié.","jue.","vie.","sáb."],"STANDALONESHORTWEEKDAYS":["Dom.","Lun.","Mar.","Mié.","Jue.","Vie.","Sáb."],"NARROWWEEKDAYS":["D","L","M","X","J","V","S"],"STANDALONENARROWWEEKDAYS":["D","L","M","X","J","V","S"],"SHORTQUARTERS":["T1","T2","T3","T4"],"QUARTERS":["1.er trimestre","2.º trimestre","3.er trimestre","4.º trimestre"],"AMPMS":["a. m.","p. m."],"DATEFORMATS":["EEEE, d 'de' MMMM 'de' y","d 'de' MMMM 'de' y","d/M/y","d/M/yy"],"TIMEFORMATS":["H:mm:ss (zzzz)","H:mm:ss z","H:mm:ss","H:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1}, {0}","{1}, {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/et.json b/intl/lib/src/data/dates/symbols/et.json
new file mode 100644
index 0000000..fa848ae
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/et.json
@@ -0,0 +1 @@
+{"NAME":"et","ERAS":["e.m.a.","m.a.j."],"ERANAMES":["enne meie aega","meie aja järgi"],"NARROWMONTHS":["J","V","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","V","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["jaanuar","veebruar","märts","aprill","mai","juuni","juuli","august","september","oktoober","november","detsember"],"STANDALONEMONTHS":["jaanuar","veebruar","märts","aprill","mai","juuni","juuli","august","september","oktoober","november","detsember"],"SHORTMONTHS":["jaan","veebr","märts","apr","mai","juuni","juuli","aug","sept","okt","nov","dets"],"STANDALONESHORTMONTHS":["jaan","veebr","märts","apr","mai","juuni","juuli","aug","sept","okt","nov","dets"],"WEEKDAYS":["pühapäev","esmaspäev","teisipäev","kolmapäev","neljapäev","reede","laupäev"],"STANDALONEWEEKDAYS":["pühapäev","esmaspäev","teisipäev","kolmapäev","neljapäev","reede","laupäev"],"SHORTWEEKDAYS":["P","E","T","K","N","R","L"],"STANDALONESHORTWEEKDAYS":["P","E","T","K","N","R","L"],"NARROWWEEKDAYS":["P","E","T","K","N","R","L"],"STANDALONENARROWWEEKDAYS":["P","E","T","K","N","R","L"],"SHORTQUARTERS":["K1","K2","K3","K4"],"QUARTERS":["1. kvartal","2. kvartal","3. kvartal","4. kvartal"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, d. MMMM y","d. MMMM y","dd.MM.y","dd.MM.yy"],"TIMEFORMATS":["H:mm.ss zzzz","H:mm.ss z","H:mm.ss","H:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/eu.json b/intl/lib/src/data/dates/symbols/eu.json
new file mode 100644
index 0000000..6713e77
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/eu.json
@@ -0,0 +1 @@
+{"NAME":"eu","ERAS":["K.a.","K.o."],"ERANAMES":["K.a.","K.o."],"NARROWMONTHS":["U","O","M","A","M","E","U","A","I","U","A","A"],"STANDALONENARROWMONTHS":["U","O","M","A","M","E","U","A","I","U","A","A"],"MONTHS":["urtarrilak","otsailak","martxoak","apirilak","maiatzak","ekainak","uztailak","abuztuak","irailak","urriak","azaroak","abenduak"],"STANDALONEMONTHS":["urtarrila","otsaila","martxoa","apirila","maiatza","ekaina","uztaila","abuztua","iraila","urria","azaroa","abendua"],"SHORTMONTHS":["urt.","ots.","mar.","api.","mai.","eka.","uzt.","abu.","ira.","urr.","aza.","abe."],"STANDALONESHORTMONTHS":["urt.","ots.","mar.","api.","mai.","eka.","uzt.","abu.","ira.","urr.","aza.","abe."],"WEEKDAYS":["igandea","astelehena","asteartea","asteazkena","osteguna","ostirala","larunbata"],"STANDALONEWEEKDAYS":["igandea","astelehena","asteartea","asteazkena","osteguna","ostirala","larunbata"],"SHORTWEEKDAYS":["ig.","al.","ar.","az.","og.","or.","lr."],"STANDALONESHORTWEEKDAYS":["ig.","al.","ar.","az.","og.","or.","lr."],"NARROWWEEKDAYS":["I","A","A","A","O","O","L"],"STANDALONENARROWWEEKDAYS":["I","A","A","A","O","O","L"],"SHORTQUARTERS":["1Hh","2Hh","3Hh","4Hh"],"QUARTERS":["1. hiruhilekoa","2. hiruhilekoa","3. hiruhilekoa","4. hiruhilekoa"],"AMPMS":["AM","PM"],"DATEFORMATS":["y('e')'ko' MMMM d, EEEE","y('e')'ko' MMMM d","y MMM d","y-MM-dd"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/fa.json b/intl/lib/src/data/dates/symbols/fa.json
new file mode 100644
index 0000000..963f68b
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/fa.json
@@ -0,0 +1 @@
+{"NAME":"fa","ERAS":["ق.م.","م."],"ERANAMES":["قبل از میلاد","میلادی"],"NARROWMONTHS":["ژ","ف","م","آ","م","ژ","ژ","ا","س","ا","ن","د"],"STANDALONENARROWMONTHS":["ژ","ف","م","آ","م","ژ","ژ","ا","س","ا","ن","د"],"MONTHS":["ژانویهٔ","فوریهٔ","مارس","آوریل","مهٔ","ژوئن","ژوئیهٔ","اوت","سپتامبر","اکتبر","نوامبر","دسامبر"],"STANDALONEMONTHS":["ژانویه","فوریه","مارس","آوریل","مه","ژوئن","ژوئیه","اوت","سپتامبر","اکتبر","نوامبر","دسامبر"],"SHORTMONTHS":["ژانویهٔ","فوریهٔ","مارس","آوریل","مهٔ","ژوئن","ژوئیهٔ","اوت","سپتامبر","اکتبر","نوامبر","دسامبر"],"STANDALONESHORTMONTHS":["ژانویه","فوریه","مارس","آوریل","مه","ژوئن","ژوئیه","اوت","سپتامبر","اکتبر","نوامبر","دسامبر"],"WEEKDAYS":["یکشنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"],"STANDALONEWEEKDAYS":["یکشنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"],"SHORTWEEKDAYS":["یکشنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"],"STANDALONESHORTWEEKDAYS":["یکشنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"],"NARROWWEEKDAYS":["ی","د","س","چ","پ","ج","ش"],"STANDALONENARROWWEEKDAYS":["ی","د","س","چ","پ","ج","ش"],"SHORTQUARTERS":["س‌م۱","س‌م۲","س‌م۳","س‌م۴"],"QUARTERS":["سه‌ماههٔ اول","سه‌ماههٔ دوم","سه‌ماههٔ سوم","سه‌ماههٔ چهارم"],"AMPMS":["قبل‌ازظهر","بعدازظهر"],"DATEFORMATS":["EEEE d MMMM y","d MMMM y","d MMM y","y/M/d"],"TIMEFORMATS":["H:mm:ss (zzzz)","H:mm:ss (z)","H:mm:ss","H:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":5,"WEEKENDRANGE":[3,4],"FIRSTWEEKCUTOFFDAY":4,"DATETIMEFORMATS":["{1}، ساعت {0}","{1}، ساعت {0}","{1}،‏ {0}","{1}،‏ {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/fi.json b/intl/lib/src/data/dates/symbols/fi.json
new file mode 100644
index 0000000..610dfa5
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/fi.json
@@ -0,0 +1 @@
+{"NAME":"fi","ERAS":["eKr.","jKr."],"ERANAMES":["ennen Kristuksen syntymää","jälkeen Kristuksen syntymän"],"NARROWMONTHS":["T","H","M","H","T","K","H","E","S","L","M","J"],"STANDALONENARROWMONTHS":["T","H","M","H","T","K","H","E","S","L","M","J"],"MONTHS":["tammikuuta","helmikuuta","maaliskuuta","huhtikuuta","toukokuuta","kesäkuuta","heinäkuuta","elokuuta","syyskuuta","lokakuuta","marraskuuta","joulukuuta"],"STANDALONEMONTHS":["tammikuu","helmikuu","maaliskuu","huhtikuu","toukokuu","kesäkuu","heinäkuu","elokuu","syyskuu","lokakuu","marraskuu","joulukuu"],"SHORTMONTHS":["tammikuuta","helmikuuta","maaliskuuta","huhtikuuta","toukokuuta","kesäkuuta","heinäkuuta","elokuuta","syyskuuta","lokakuuta","marraskuuta","joulukuuta"],"STANDALONESHORTMONTHS":["tammi","helmi","maalis","huhti","touko","kesä","heinä","elo","syys","loka","marras","joulu"],"WEEKDAYS":["sunnuntaina","maanantaina","tiistaina","keskiviikkona","torstaina","perjantaina","lauantaina"],"STANDALONEWEEKDAYS":["sunnuntai","maanantai","tiistai","keskiviikko","torstai","perjantai","lauantai"],"SHORTWEEKDAYS":["su","ma","ti","ke","to","pe","la"],"STANDALONESHORTWEEKDAYS":["su","ma","ti","ke","to","pe","la"],"NARROWWEEKDAYS":["S","M","T","K","T","P","L"],"STANDALONENARROWWEEKDAYS":["S","M","T","K","T","P","L"],"SHORTQUARTERS":["1. nelj.","2. nelj.","3. nelj.","4. nelj."],"QUARTERS":["1. neljännes","2. neljännes","3. neljännes","4. neljännes"],"AMPMS":["ap.","ip."],"DATEFORMATS":["cccc d. MMMM y","d. MMMM y","d.M.y","d.M.y"],"TIMEFORMATS":["H.mm.ss zzzz","H.mm.ss z","H.mm.ss","H.mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/fil.json b/intl/lib/src/data/dates/symbols/fil.json
new file mode 100644
index 0000000..255f632
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/fil.json
@@ -0,0 +1 @@
+{"NAME":"fil","ERAS":["BC","AD"],"ERANAMES":["BC","AD"],"NARROWMONTHS":["E","P","M","A","M","H","H","A","S","O","N","D"],"STANDALONENARROWMONTHS":["E","P","M","A","M","H","H","A","S","O","N","D"],"MONTHS":["Enero","Pebrero","Marso","Abril","Mayo","Hunyo","Hulyo","Agosto","Setyembre","Oktubre","Nobyembre","Disyembre"],"STANDALONEMONTHS":["Enero","Pebrero","Marso","Abril","Mayo","Hunyo","Hulyo","Agosto","Setyembre","Oktubre","Nobyembre","Disyembre"],"SHORTMONTHS":["Ene","Peb","Mar","Abr","May","Hun","Hul","Ago","Set","Okt","Nob","Dis"],"STANDALONESHORTMONTHS":["Ene","Peb","Mar","Abr","May","Hun","Hul","Ago","Set","Okt","Nob","Dis"],"WEEKDAYS":["Linggo","Lunes","Martes","Miyerkules","Huwebes","Biyernes","Sabado"],"STANDALONEWEEKDAYS":["Linggo","Lunes","Martes","Miyerkules","Huwebes","Biyernes","Sabado"],"SHORTWEEKDAYS":["Lin","Lun","Mar","Miy","Huw","Biy","Sab"],"STANDALONESHORTWEEKDAYS":["Lin","Lun","Mar","Miy","Huw","Biy","Sab"],"NARROWWEEKDAYS":["L","L","M","M","H","B","S"],"STANDALONENARROWWEEKDAYS":["L","L","M","M","H","B","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["ika-1 quarter","ika-2 quarter","ika-3 quarter","ika-4 na quarter"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, MMMM d, y","MMMM d, y","MMM d, y","M/d/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} 'ng' {0}","{1} 'ng' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/fr.json b/intl/lib/src/data/dates/symbols/fr.json
new file mode 100644
index 0000000..68a4cc9
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/fr.json
@@ -0,0 +1 @@
+{"NAME":"fr","ERAS":["av. J.-C.","ap. J.-C."],"ERANAMES":["avant Jésus-Christ","après Jésus-Christ"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],"STANDALONEMONTHS":["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],"SHORTMONTHS":["janv.","févr.","mars","avr.","mai","juin","juil.","août","sept.","oct.","nov.","déc."],"STANDALONESHORTMONTHS":["janv.","févr.","mars","avr.","mai","juin","juil.","août","sept.","oct.","nov.","déc."],"WEEKDAYS":["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],"STANDALONEWEEKDAYS":["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],"SHORTWEEKDAYS":["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],"STANDALONESHORTWEEKDAYS":["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],"NARROWWEEKDAYS":["D","L","M","M","J","V","S"],"STANDALONENARROWWEEKDAYS":["D","L","M","M","J","V","S"],"SHORTQUARTERS":["T1","T2","T3","T4"],"QUARTERS":["1er trimestre","2e trimestre","3e trimestre","4e trimestre"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE d MMMM y","d MMMM y","d MMM y","dd/MM/y"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/fr_CA.json b/intl/lib/src/data/dates/symbols/fr_CA.json
new file mode 100644
index 0000000..05233a9
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/fr_CA.json
@@ -0,0 +1 @@
+{"NAME":"fr_CA","ERAS":["av. J.-C.","ap. J.-C."],"ERANAMES":["avant Jésus-Christ","après Jésus-Christ"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],"STANDALONEMONTHS":["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],"SHORTMONTHS":["janv.","févr.","mars","avr.","mai","juin","juil.","août","sept.","oct.","nov.","déc."],"STANDALONESHORTMONTHS":["janv.","févr.","mars","avr.","mai","juin","juil.","août","sept.","oct.","nov.","déc."],"WEEKDAYS":["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],"STANDALONEWEEKDAYS":["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],"SHORTWEEKDAYS":["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],"STANDALONESHORTWEEKDAYS":["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],"NARROWWEEKDAYS":["D","L","M","M","J","V","S"],"STANDALONENARROWWEEKDAYS":["D","L","M","M","J","V","S"],"SHORTQUARTERS":["T1","T2","T3","T4"],"QUARTERS":["1er trimestre","2e trimestre","3e trimestre","4e trimestre"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE d MMMM y","d MMMM y","y-MM-dd","yy-MM-dd"],"TIMEFORMATS":["HH 'h' mm 'min' ss 's' zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/gl.json b/intl/lib/src/data/dates/symbols/gl.json
new file mode 100644
index 0000000..a7cf33c
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/gl.json
@@ -0,0 +1 @@
+{"NAME":"gl","ERAS":["a.C.","d.C."],"ERANAMES":["antes de Cristo","despois de Cristo"],"NARROWMONTHS":["X","F","M","A","M","X","X","A","S","O","N","D"],"STANDALONENARROWMONTHS":["X","F","M","A","M","X","X","A","S","O","N","D"],"MONTHS":["xaneiro","febreiro","marzo","abril","maio","xuño","xullo","agosto","setembro","outubro","novembro","decembro"],"STANDALONEMONTHS":["Xaneiro","Febreiro","Marzo","Abril","Maio","Xuño","Xullo","Agosto","Setembro","Outubro","Novembro","Decembro"],"SHORTMONTHS":["xan","feb","mar","abr","mai","xuñ","xul","ago","set","out","nov","dec"],"STANDALONESHORTMONTHS":["Xan","Feb","Mar","Abr","Mai","Xuñ","Xul","Ago","Set","Out","Nov","Dec"],"WEEKDAYS":["domingo","luns","martes","mércores","xoves","venres","sábado"],"STANDALONEWEEKDAYS":["Domingo","Luns","Martes","Mércores","Xoves","Venres","Sábado"],"SHORTWEEKDAYS":["dom","lun","mar","mér","xov","ven","sáb"],"STANDALONESHORTWEEKDAYS":["Dom","Lun","Mar","Mér","Xov","Ven","Sáb"],"NARROWWEEKDAYS":["D","L","M","M","X","V","S"],"STANDALONENARROWWEEKDAYS":["D","L","M","M","X","V","S"],"SHORTQUARTERS":["T1","T2","T3","T4"],"QUARTERS":["1o trimestre","2o trimestre","3o trimestre","4o trimestre"],"AMPMS":["a.m.","p.m."],"DATEFORMATS":["EEEE dd MMMM y","dd MMMM y","d MMM, y","dd/MM/yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/gsw.json b/intl/lib/src/data/dates/symbols/gsw.json
new file mode 100644
index 0000000..53c9b20
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/gsw.json
@@ -0,0 +1 @@
+{"NAME":"gsw","ERAS":["v. Chr.","n. Chr."],"ERANAMES":["v. Chr.","n. Chr."],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["Januar","Februar","März","April","Mai","Juni","Juli","Auguscht","Septämber","Oktoober","Novämber","Dezämber"],"STANDALONEMONTHS":["Januar","Februar","März","April","Mai","Juni","Juli","Auguscht","Septämber","Oktoober","Novämber","Dezämber"],"SHORTMONTHS":["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],"STANDALONESHORTMONTHS":["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],"WEEKDAYS":["Sunntig","Määntig","Ziischtig","Mittwuch","Dunschtig","Friitig","Samschtig"],"STANDALONEWEEKDAYS":["Sunntig","Määntig","Ziischtig","Mittwuch","Dunschtig","Friitig","Samschtig"],"SHORTWEEKDAYS":["Su.","Mä.","Zi.","Mi.","Du.","Fr.","Sa."],"STANDALONESHORTWEEKDAYS":["Su.","Mä.","Zi.","Mi.","Du.","Fr.","Sa."],"NARROWWEEKDAYS":["S","M","D","M","D","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","D","M","D","F","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1. Quartal","2. Quartal","3. Quartal","4. Quartal"],"AMPMS":["vorm.","nam."],"DATEFORMATS":["EEEE, d. MMMM y","d. MMMM y","dd.MM.y","dd.MM.yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/gu.json b/intl/lib/src/data/dates/symbols/gu.json
new file mode 100644
index 0000000..412b6af
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/gu.json
@@ -0,0 +1 @@
+{"NAME":"gu","ERAS":["ઈસુના જન્મ પહેલા","ઇસવીસન"],"ERANAMES":["ઈસવીસન પૂર્વે","ઇસવીસન"],"NARROWMONTHS":["જા","ફે","મા","એ","મે","જૂ","જુ","ઑ","સ","ઑ","ન","ડિ"],"STANDALONENARROWMONTHS":["જા","ફે","મા","એ","મે","જૂ","જુ","ઑ","સ","ઑ","ન","ડિ"],"MONTHS":["જાન્યુઆરી","ફેબ્રુઆરી","માર્ચ","એપ્રિલ","મે","જૂન","જુલાઈ","ઑગસ્ટ","સપ્ટેમ્બર","ઑક્ટોબર","નવેમ્બર","ડિસેમ્બર"],"STANDALONEMONTHS":["જાન્યુઆરી","ફેબ્રુઆરી","માર્ચ","એપ્રિલ","મે","જૂન","જુલાઈ","ઑગસ્ટ","સપ્ટેમ્બર","ઑક્ટોબર","નવેમ્બર","ડિસેમ્બર"],"SHORTMONTHS":["જાન્યુ","ફેબ્રુ","માર્ચ","એપ્રિલ","મે","જૂન","જુલાઈ","ઑગસ્ટ","સપ્ટે","ઑક્ટો","નવે","ડિસે"],"STANDALONESHORTMONTHS":["જાન્યુ","ફેબ્રુ","માર્ચ","એપ્રિલ","મે","જૂન","જુલાઈ","ઑગ","સપ્ટે","ઑક્ટો","નવે","ડિસે"],"WEEKDAYS":["રવિવાર","સોમવાર","મંગળવાર","બુધવાર","ગુરુવાર","શુક્રવાર","શનિવાર"],"STANDALONEWEEKDAYS":["રવિવાર","સોમવાર","મંગળવાર","બુધવાર","ગુરુવાર","શુક્રવાર","શનિવાર"],"SHORTWEEKDAYS":["રવિ","સોમ","મંગળ","બુધ","ગુરુ","શુક્ર","શનિ"],"STANDALONESHORTWEEKDAYS":["રવિ","સોમ","મંગળ","બુધ","ગુરુ","શુક્ર","શનિ"],"NARROWWEEKDAYS":["ર","સો","મં","બુ","ગુ","શુ","શ"],"STANDALONENARROWWEEKDAYS":["ર","સો","મં","બુ","ગુ","શુ","શ"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["પહેલો ત્રિમાસ","બીજો ત્રિમાસ","ત્રીજો ત્રિમાસ","ચોથો ત્રિમાસ"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, d MMMM, y","d MMMM, y","d MMM, y","d-MM-yy"],"TIMEFORMATS":["hh:mm:ss a zzzz","hh:mm:ss a z","hh:mm:ss a","hh:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[6,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/haw.json b/intl/lib/src/data/dates/symbols/haw.json
new file mode 100644
index 0000000..ba7241d
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/haw.json
@@ -0,0 +1 @@
+{"NAME":"haw","ERAS":["BCE","CE"],"ERANAMES":["BCE","CE"],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["Ianuali","Pepeluali","Malaki","ʻApelila","Mei","Iune","Iulai","ʻAukake","Kepakemapa","ʻOkakopa","Nowemapa","Kekemapa"],"STANDALONEMONTHS":["Ianuali","Pepeluali","Malaki","ʻApelila","Mei","Iune","Iulai","ʻAukake","Kepakemapa","ʻOkakopa","Nowemapa","Kekemapa"],"SHORTMONTHS":["Ian.","Pep.","Mal.","ʻAp.","Mei","Iun.","Iul.","ʻAu.","Kep.","ʻOk.","Now.","Kek."],"STANDALONESHORTMONTHS":["Ian.","Pep.","Mal.","ʻAp.","Mei","Iun.","Iul.","ʻAu.","Kep.","ʻOk.","Now.","Kek."],"WEEKDAYS":["Lāpule","Poʻakahi","Poʻalua","Poʻakolu","Poʻahā","Poʻalima","Poʻaono"],"STANDALONEWEEKDAYS":["Lāpule","Poʻakahi","Poʻalua","Poʻakolu","Poʻahā","Poʻalima","Poʻaono"],"SHORTWEEKDAYS":["LP","P1","P2","P3","P4","P5","P6"],"STANDALONESHORTWEEKDAYS":["LP","P1","P2","P3","P4","P5","P6"],"NARROWWEEKDAYS":["S","M","T","W","T","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","T","W","T","F","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["Q1","Q2","Q3","Q4"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, d MMMM y","d MMMM y","d MMM y","d/M/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/he.json b/intl/lib/src/data/dates/symbols/he.json
new file mode 100644
index 0000000..d8404b8
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/he.json
@@ -0,0 +1 @@
+{"NAME":"he","ERAS":["לפנה״ס","לסה״נ"],"ERANAMES":["לפני הספירה","לספירה"],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["ינואר","פברואר","מרץ","אפריל","מאי","יוני","יולי","אוגוסט","ספטמבר","אוקטובר","נובמבר","דצמבר"],"STANDALONEMONTHS":["ינואר","פברואר","מרץ","אפריל","מאי","יוני","יולי","אוגוסט","ספטמבר","אוקטובר","נובמבר","דצמבר"],"SHORTMONTHS":["ינו׳","פבר׳","מרץ","אפר׳","מאי","יוני","יולי","אוג׳","ספט׳","אוק׳","נוב׳","דצמ׳"],"STANDALONESHORTMONTHS":["ינו׳","פבר׳","מרץ","אפר׳","מאי","יוני","יולי","אוג׳","ספט׳","אוק׳","נוב׳","דצמ׳"],"WEEKDAYS":["יום ראשון","יום שני","יום שלישי","יום רביעי","יום חמישי","יום שישי","יום שבת"],"STANDALONEWEEKDAYS":["יום ראשון","יום שני","יום שלישי","יום רביעי","יום חמישי","יום שישי","יום שבת"],"SHORTWEEKDAYS":["יום א׳","יום ב׳","יום ג׳","יום ד׳","יום ה׳","יום ו׳","שבת"],"STANDALONESHORTWEEKDAYS":["יום א׳","יום ב׳","יום ג׳","יום ד׳","יום ה׳","יום ו׳","שבת"],"NARROWWEEKDAYS":["א׳","ב׳","ג׳","ד׳","ה׳","ו׳","ש׳"],"STANDALONENARROWWEEKDAYS":["א׳","ב׳","ג׳","ד׳","ה׳","ו׳","ש׳"],"SHORTQUARTERS":["רבעון 1","רבעון 2","רבעון 3","רבעון 4"],"QUARTERS":["רבעון 1","רבעון 2","רבעון 3","רבעון 4"],"AMPMS":["לפנה״צ","אחה״צ"],"DATEFORMATS":["EEEE, d בMMMM y","d בMMMM y","d בMMM y","dd/MM/yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[4,5],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} בשעה {0}","{1} בשעה {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/hi.json b/intl/lib/src/data/dates/symbols/hi.json
new file mode 100644
index 0000000..e7346a5
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/hi.json
@@ -0,0 +1 @@
+{"NAME":"hi","ERAS":["ईसा-पूर्व","ईस्वी"],"ERANAMES":["ईसा-पूर्व","ईस्वी"],"NARROWMONTHS":["ज","फ़","मा","अ","म","जू","जु","अ","सि","अ","न","दि"],"STANDALONENARROWMONTHS":["ज","फ़","मा","अ","म","जू","जु","अ","सि","अ","न","दि"],"MONTHS":["जनवरी","फ़रवरी","मार्च","अप्रैल","मई","जून","जुलाई","अगस्त","सितंबर","अक्टूबर","नवंबर","दिसंबर"],"STANDALONEMONTHS":["जनवरी","फ़रवरी","मार्च","अप्रैल","मई","जून","जुलाई","अगस्त","सितंबर","अक्टूबर","नवंबर","दिसंबर"],"SHORTMONTHS":["जन","फ़र","मार्च","अप्रै","मई","जून","जुला","अग","सितं","अक्टू","नवं","दिसं"],"STANDALONESHORTMONTHS":["जन","फ़र","मार्च","अप्रै","मई","जून","जुला","अग","सितं","अक्टू","नवं","दिसं"],"WEEKDAYS":["रविवार","सोमवार","मंगलवार","बुधवार","गुरुवार","शुक्रवार","शनिवार"],"STANDALONEWEEKDAYS":["रविवार","सोमवार","मंगलवार","बुधवार","गुरुवार","शुक्रवार","शनिवार"],"SHORTWEEKDAYS":["रवि","सोम","मंगल","बुध","गुरु","शुक्र","शनि"],"STANDALONESHORTWEEKDAYS":["रवि","सोम","मंगल","बुध","गुरु","शुक्र","शनि"],"NARROWWEEKDAYS":["र","सो","मं","बु","गु","शु","श"],"STANDALONENARROWWEEKDAYS":["र","सो","मं","बु","गु","शु","श"],"SHORTQUARTERS":["ति1","ति2","ति3","ति4"],"QUARTERS":["पहली तिमाही","दूसरी तिमाही","तीसरी तिमाही","चौथी तिमाही"],"AMPMS":["am","pm"],"DATEFORMATS":["EEEE, d MMMM y","d MMMM y","dd-MM-y","d-M-yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[6,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} को {0}","{1} को {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/hr.json b/intl/lib/src/data/dates/symbols/hr.json
new file mode 100644
index 0000000..ae1258e
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/hr.json
@@ -0,0 +1 @@
+{"NAME":"hr","ERAS":["pr. Kr.","p. Kr."],"ERANAMES":["Prije Krista","Poslije Krista"],"NARROWMONTHS":["1.","2.","3.","4.","5.","6.","7.","8.","9.","10.","11.","12."],"STANDALONENARROWMONTHS":["1.","2.","3.","4.","5.","6.","7.","8.","9.","10.","11.","12."],"MONTHS":["siječnja","veljače","ožujka","travnja","svibnja","lipnja","srpnja","kolovoza","rujna","listopada","studenoga","prosinca"],"STANDALONEMONTHS":["siječanj","veljača","ožujak","travanj","svibanj","lipanj","srpanj","kolovoz","rujan","listopad","studeni","prosinac"],"SHORTMONTHS":["sij","velj","ožu","tra","svi","lip","srp","kol","ruj","lis","stu","pro"],"STANDALONESHORTMONTHS":["sij","velj","ožu","tra","svi","lip","srp","kol","ruj","lis","stu","pro"],"WEEKDAYS":["nedjelja","ponedjeljak","utorak","srijeda","četvrtak","petak","subota"],"STANDALONEWEEKDAYS":["nedjelja","ponedjeljak","utorak","srijeda","četvrtak","petak","subota"],"SHORTWEEKDAYS":["ned","pon","uto","sri","čet","pet","sub"],"STANDALONESHORTWEEKDAYS":["ned","pon","uto","sri","čet","pet","sub"],"NARROWWEEKDAYS":["N","P","U","S","Č","P","S"],"STANDALONENARROWWEEKDAYS":["n","p","u","s","č","p","s"],"SHORTQUARTERS":["1kv","2kv","3kv","4kv"],"QUARTERS":["1. kvartal","2. kvartal","3. kvartal","4. kvartal"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, d. MMMM y.","d. MMMM y.","d. MMM y.","d.M.yy."],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} 'u' {0}","{1} 'u' {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/hu.json b/intl/lib/src/data/dates/symbols/hu.json
new file mode 100644
index 0000000..85229fb
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/hu.json
@@ -0,0 +1 @@
+{"NAME":"hu","ERAS":["i. e.","i. sz."],"ERANAMES":["időszámításunk előtt","időszámításunk szerint"],"NARROWMONTHS":["J","F","M","Á","M","J","J","A","Sz","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","Á","M","J","J","A","Sz","O","N","D"],"MONTHS":["január","február","március","április","május","június","július","augusztus","szeptember","október","november","december"],"STANDALONEMONTHS":["január","február","március","április","május","június","július","augusztus","szeptember","október","november","december"],"SHORTMONTHS":["jan.","febr.","márc.","ápr.","máj.","jún.","júl.","aug.","szept.","okt.","nov.","dec."],"STANDALONESHORTMONTHS":["jan.","febr.","márc.","ápr.","máj.","jún.","júl.","aug.","szept.","okt.","nov.","dec."],"WEEKDAYS":["vasárnap","hétfő","kedd","szerda","csütörtök","péntek","szombat"],"STANDALONEWEEKDAYS":["vasárnap","hétfő","kedd","szerda","csütörtök","péntek","szombat"],"SHORTWEEKDAYS":["V","H","K","Sze","Cs","P","Szo"],"STANDALONESHORTWEEKDAYS":["V","H","K","Sze","Cs","P","Szo"],"NARROWWEEKDAYS":["V","H","K","Sz","Cs","P","Sz"],"STANDALONENARROWWEEKDAYS":["V","H","K","Sz","Cs","P","Sz"],"SHORTQUARTERS":["N1","N2","N3","N4"],"QUARTERS":["I. negyedév","II. negyedév","III. negyedév","IV. negyedév"],"AMPMS":["de.","du."],"DATEFORMATS":["y. MMMM d., EEEE","y. MMMM d.","y. MMM d.","y. MM. dd."],"TIMEFORMATS":["H:mm:ss zzzz","H:mm:ss z","H:mm:ss","H:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/hy.json b/intl/lib/src/data/dates/symbols/hy.json
new file mode 100644
index 0000000..e6e9a06
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/hy.json
@@ -0,0 +1 @@
+{"NAME":"hy","ERAS":["մ.թ.ա.","մ.թ."],"ERANAMES":["մ.թ.ա.","մ.թ."],"NARROWMONTHS":["Հ","Փ","Մ","Ա","Մ","Հ","Հ","Օ","Ս","Հ","Ն","Դ"],"STANDALONENARROWMONTHS":["Հ","Փ","Մ","Ա","Մ","Հ","Հ","Օ","Ս","Հ","Ն","Դ"],"MONTHS":["հունվարի","փետրվարի","մարտի","ապրիլի","մայիսի","հունիսի","հուլիսի","օգոստոսի","սեպտեմբերի","հոկտեմբերի","նոյեմբերի","դեկտեմբերի"],"STANDALONEMONTHS":["հունվար","փետրվար","մարտ","ապրիլ","մայիս","հունիս","հուլիս","օգոստոս","սեպտեմբեր","հոկտեմբեր","նոյեմբեր","դեկտեմբեր"],"SHORTMONTHS":["հնվ","փտվ","մրտ","ապր","մյս","հնս","հլս","օգս","սպտ","հկտ","նյմ","դկտ"],"STANDALONESHORTMONTHS":["հնվ","փտվ","մրտ","ապր","մյս","հնս","հլս","օգս","սպտ","հկտ","նյմ","դկտ"],"WEEKDAYS":["կիրակի","երկուշաբթի","երեքշաբթի","չորեքշաբթի","հինգշաբթի","ուրբաթ","շաբաթ"],"STANDALONEWEEKDAYS":["կիրակի","երկուշաբթի","երեքշաբթի","չորեքշաբթի","հինգշաբթի","ուրբաթ","շաբաթ"],"SHORTWEEKDAYS":["կիր","երկ","երք","չրք","հնգ","ուր","շբթ"],"STANDALONESHORTWEEKDAYS":["կիր","երկ","երք","չրք","հնգ","ուր","շբթ"],"NARROWWEEKDAYS":["Կ","Ե","Ե","Չ","Հ","Ու","Շ"],"STANDALONENARROWWEEKDAYS":["Կ","Ե","Ե","Չ","Հ","Ու","Շ"],"SHORTQUARTERS":["1-ին եռմս.","2-րդ եռմս.","3-րդ եռմս.","4-րդ եռմս."],"QUARTERS":["1-ին եռամսյակ","2-րդ եռամսյակ","3-րդ եռամսյակ","4-րդ եռամսյակ"],"AMPMS":["կեսօրից առաջ","կեսօրից հետո"],"DATEFORMATS":["yթ. MMMM d, EEEE","dd MMMM, yթ.","dd MMM, y թ.","dd.MM.yy"],"TIMEFORMATS":["H:mm:ss, zzzz","H:mm:ss, z","H:mm:ss","H:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1}, {0}","{1}, {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/id.json b/intl/lib/src/data/dates/symbols/id.json
new file mode 100644
index 0000000..449175e
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/id.json
@@ -0,0 +1 @@
+{"NAME":"id","ERAS":["SM","M"],"ERANAMES":["SM","M"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","November","Desember"],"STANDALONEMONTHS":["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","November","Desember"],"SHORTMONTHS":["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Agt","Sep","Okt","Nov","Des"],"STANDALONESHORTMONTHS":["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Agt","Sep","Okt","Nov","Des"],"WEEKDAYS":["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"],"STANDALONEWEEKDAYS":["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"],"SHORTWEEKDAYS":["Min","Sen","Sel","Rab","Kam","Jum","Sab"],"STANDALONESHORTWEEKDAYS":["Min","Sen","Sel","Rab","Kam","Jum","Sab"],"NARROWWEEKDAYS":["M","S","S","R","K","J","S"],"STANDALONENARROWWEEKDAYS":["M","S","S","R","K","J","S"],"SHORTQUARTERS":["K1","K2","K3","K4"],"QUARTERS":["Kuartal ke-1","Kuartal ke-2","Kuartal ke-3","Kuartal ke-4"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, dd MMMM y","d MMMM y","d MMM y","dd/MM/yy"],"TIMEFORMATS":["HH.mm.ss zzzz","HH.mm.ss z","HH.mm.ss","HH.mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/in.json b/intl/lib/src/data/dates/symbols/in.json
new file mode 100644
index 0000000..b6e21f5
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/in.json
@@ -0,0 +1 @@
+{"NAME":"in","ERAS":["SM","M"],"ERANAMES":["SM","M"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","November","Desember"],"STANDALONEMONTHS":["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","November","Desember"],"SHORTMONTHS":["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Agt","Sep","Okt","Nov","Des"],"STANDALONESHORTMONTHS":["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Agt","Sep","Okt","Nov","Des"],"WEEKDAYS":["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"],"STANDALONEWEEKDAYS":["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"],"SHORTWEEKDAYS":["Min","Sen","Sel","Rab","Kam","Jum","Sab"],"STANDALONESHORTWEEKDAYS":["Min","Sen","Sel","Rab","Kam","Jum","Sab"],"NARROWWEEKDAYS":["M","S","S","R","K","J","S"],"STANDALONENARROWWEEKDAYS":["M","S","S","R","K","J","S"],"SHORTQUARTERS":["K1","K2","K3","K4"],"QUARTERS":["Kuartal ke-1","Kuartal ke-2","Kuartal ke-3","Kuartal ke-4"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, dd MMMM y","d MMMM y","d MMM y","dd/MM/yy"],"TIMEFORMATS":["HH.mm.ss zzzz","HH.mm.ss z","HH.mm.ss","HH.mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/is.json b/intl/lib/src/data/dates/symbols/is.json
new file mode 100644
index 0000000..5598364
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/is.json
@@ -0,0 +1 @@
+{"NAME":"is","ERAS":["f.Kr.","e.Kr."],"ERANAMES":["fyrir Krist","eftir Krist"],"NARROWMONTHS":["J","F","M","A","M","J","J","Á","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","Á","S","O","N","D"],"MONTHS":["janúar","febrúar","mars","apríl","maí","júní","júlí","ágúst","september","október","nóvember","desember"],"STANDALONEMONTHS":["janúar","febrúar","mars","apríl","maí","júní","júlí","ágúst","september","október","nóvember","desember"],"SHORTMONTHS":["jan.","feb.","mar.","apr.","maí","jún.","júl.","ágú.","sep.","okt.","nóv.","des."],"STANDALONESHORTMONTHS":["jan.","feb.","mar.","apr.","maí","jún.","júl.","ágú.","sep.","okt.","nóv.","des."],"WEEKDAYS":["sunnudagur","mánudagur","þriðjudagur","miðvikudagur","fimmtudagur","föstudagur","laugardagur"],"STANDALONEWEEKDAYS":["sunnudagur","mánudagur","þriðjudagur","miðvikudagur","fimmtudagur","föstudagur","laugardagur"],"SHORTWEEKDAYS":["sun.","mán.","þri.","mið.","fim.","fös.","lau."],"STANDALONESHORTWEEKDAYS":["sun.","mán.","þri.","mið.","fim.","fös.","lau."],"NARROWWEEKDAYS":["S","M","Þ","M","F","F","L"],"STANDALONENARROWWEEKDAYS":["S","M","Þ","M","F","F","L"],"SHORTQUARTERS":["F1","F2","F3","F4"],"QUARTERS":["1. fjórðungur","2. fjórðungur","3. fjórðungur","4. fjórðungur"],"AMPMS":["f.h.","e.h."],"DATEFORMATS":["EEEE, d. MMMM y","d. MMMM y","d. MMM y","d.M.y"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} 'kl.' {0}","{1} 'kl.' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/it.json b/intl/lib/src/data/dates/symbols/it.json
new file mode 100644
index 0000000..935bf96
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/it.json
@@ -0,0 +1 @@
+{"NAME":"it","ERAS":["aC","dC"],"ERANAMES":["a.C.","d.C."],"NARROWMONTHS":["G","F","M","A","M","G","L","A","S","O","N","D"],"STANDALONENARROWMONTHS":["G","F","M","A","M","G","L","A","S","O","N","D"],"MONTHS":["gennaio","febbraio","marzo","aprile","maggio","giugno","luglio","agosto","settembre","ottobre","novembre","dicembre"],"STANDALONEMONTHS":["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],"SHORTMONTHS":["gen","feb","mar","apr","mag","giu","lug","ago","set","ott","nov","dic"],"STANDALONESHORTMONTHS":["gen","feb","mar","apr","mag","giu","lug","ago","set","ott","nov","dic"],"WEEKDAYS":["domenica","lunedì","martedì","mercoledì","giovedì","venerdì","sabato"],"STANDALONEWEEKDAYS":["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],"SHORTWEEKDAYS":["dom","lun","mar","mer","gio","ven","sab"],"STANDALONESHORTWEEKDAYS":["dom","lun","mar","mer","gio","ven","sab"],"NARROWWEEKDAYS":["D","L","M","M","G","V","S"],"STANDALONENARROWWEEKDAYS":["D","L","M","M","G","V","S"],"SHORTQUARTERS":["T1","T2","T3","T4"],"QUARTERS":["1º trimestre","2º trimestre","3º trimestre","4º trimestre"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE d MMMM y","dd MMMM y","dd/MMM/y","dd/MM/yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/iw.json b/intl/lib/src/data/dates/symbols/iw.json
new file mode 100644
index 0000000..d17ce90
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/iw.json
@@ -0,0 +1 @@
+{"NAME":"iw","ERAS":["לפנה״ס","לסה״נ"],"ERANAMES":["לפני הספירה","לספירה"],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["ינואר","פברואר","מרץ","אפריל","מאי","יוני","יולי","אוגוסט","ספטמבר","אוקטובר","נובמבר","דצמבר"],"STANDALONEMONTHS":["ינואר","פברואר","מרץ","אפריל","מאי","יוני","יולי","אוגוסט","ספטמבר","אוקטובר","נובמבר","דצמבר"],"SHORTMONTHS":["ינו׳","פבר׳","מרץ","אפר׳","מאי","יוני","יולי","אוג׳","ספט׳","אוק׳","נוב׳","דצמ׳"],"STANDALONESHORTMONTHS":["ינו׳","פבר׳","מרץ","אפר׳","מאי","יוני","יולי","אוג׳","ספט׳","אוק׳","נוב׳","דצמ׳"],"WEEKDAYS":["יום ראשון","יום שני","יום שלישי","יום רביעי","יום חמישי","יום שישי","יום שבת"],"STANDALONEWEEKDAYS":["יום ראשון","יום שני","יום שלישי","יום רביעי","יום חמישי","יום שישי","יום שבת"],"SHORTWEEKDAYS":["יום א׳","יום ב׳","יום ג׳","יום ד׳","יום ה׳","יום ו׳","שבת"],"STANDALONESHORTWEEKDAYS":["יום א׳","יום ב׳","יום ג׳","יום ד׳","יום ה׳","יום ו׳","שבת"],"NARROWWEEKDAYS":["א׳","ב׳","ג׳","ד׳","ה׳","ו׳","ש׳"],"STANDALONENARROWWEEKDAYS":["א׳","ב׳","ג׳","ד׳","ה׳","ו׳","ש׳"],"SHORTQUARTERS":["רבעון 1","רבעון 2","רבעון 3","רבעון 4"],"QUARTERS":["רבעון 1","רבעון 2","רבעון 3","רבעון 4"],"AMPMS":["לפנה״צ","אחה״צ"],"DATEFORMATS":["EEEE, d בMMMM y","d בMMMM y","d בMMM y","dd/MM/yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[4,5],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} בשעה {0}","{1} בשעה {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/ja.json b/intl/lib/src/data/dates/symbols/ja.json
new file mode 100644
index 0000000..cc7f4a2
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/ja.json
@@ -0,0 +1 @@
+{"NAME":"ja","ERAS":["紀元前","西暦"],"ERANAMES":["紀元前","西暦"],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"STANDALONEMONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"SHORTMONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"STANDALONESHORTMONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"WEEKDAYS":["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"],"STANDALONEWEEKDAYS":["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"],"SHORTWEEKDAYS":["日","月","火","水","木","金","土"],"STANDALONESHORTWEEKDAYS":["日","月","火","水","木","金","土"],"NARROWWEEKDAYS":["日","月","火","水","木","金","土"],"STANDALONENARROWWEEKDAYS":["日","月","火","水","木","金","土"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["第1四半期","第2四半期","第3四半期","第4四半期"],"AMPMS":["午前","午後"],"DATEFORMATS":["y年M月d日EEEE","y年M月d日","y/MM/dd","y/MM/dd"],"TIMEFORMATS":["H時mm分ss秒 zzzz","H:mm:ss z","H:mm:ss","H:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/ka.json b/intl/lib/src/data/dates/symbols/ka.json
new file mode 100644
index 0000000..87d01d3
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/ka.json
@@ -0,0 +1 @@
+{"NAME":"ka","ERAS":["ძვ. წ.","ახ. წ."],"ERANAMES":["ძველი წელთაღრიცხვით","ახალი წელთაღრიცხვით"],"NARROWMONTHS":["ი","თ","მ","ა","მ","ი","ი","ა","ს","ო","ნ","დ"],"STANDALONENARROWMONTHS":["ი","თ","მ","ა","მ","ი","ი","ა","ს","ო","ნ","დ"],"MONTHS":["იანვარი","თებერვალი","მარტი","აპრილი","მაისი","ივნისი","ივლისი","აგვისტო","სექტემბერი","ოქტომბერი","ნოემბერი","დეკემბერი"],"STANDALONEMONTHS":["იანვარი","თებერვალი","მარტი","აპრილი","მაისი","ივნისი","ივლისი","აგვისტო","სექტემბერი","ოქტომბერი","ნოემბერი","დეკემბერი"],"SHORTMONTHS":["იან","თებ","მარ","აპრ","მაი","ივნ","ივლ","აგვ","სექ","ოქტ","ნოე","დეკ"],"STANDALONESHORTMONTHS":["იან","თებ","მარ","აპრ","მაი","ივნ","ივლ","აგვ","სექ","ოქტ","ნოე","დეკ"],"WEEKDAYS":["კვირა","ორშაბათი","სამშაბათი","ოთხშაბათი","ხუთშაბათი","პარასკევი","შაბათი"],"STANDALONEWEEKDAYS":["კვირა","ორშაბათი","სამშაბათი","ოთხშაბათი","ხუთშაბათი","პარასკევი","შაბათი"],"SHORTWEEKDAYS":["კვი","ორშ","სამ","ოთხ","ხუთ","პარ","შაბ"],"STANDALONESHORTWEEKDAYS":["კვი","ორშ","სამ","ოთხ","ხუთ","პარ","შაბ"],"NARROWWEEKDAYS":["კ","ო","ს","ო","ხ","პ","შ"],"STANDALONENARROWWEEKDAYS":["კ","ო","ს","ო","ხ","პ","შ"],"SHORTQUARTERS":["I კვ.","II კვ.","III კვ.","IV კვ."],"QUARTERS":["I კვარტალი","II კვარტალი","III კვარტალი","IV კვარტალი"],"AMPMS":["დილის","საღამოს"],"DATEFORMATS":["EEEE, dd MMMM, y","d MMMM, y","d MMM, y","dd.MM.yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1}, {0}","{1} {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/kk.json b/intl/lib/src/data/dates/symbols/kk.json
new file mode 100644
index 0000000..c72f03f
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/kk.json
@@ -0,0 +1 @@
+{"NAME":"kk","ERAS":["б.з.д.","б.з."],"ERANAMES":["б.з.д.","б.з."],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["қаңтар","ақпан","наурыз","сәуір","мамыр","маусым","шілде","тамыз","қыркүйек","қазан","қараша","желтоқсан"],"STANDALONEMONTHS":["қаңтар","ақпан","наурыз","сәуір","мамыр","маусым","шілде","тамыз","қыркүйек","қазан","қараша","желтоқсан"],"SHORTMONTHS":["қаң.","ақп.","нау.","сәу.","мам.","мау.","шіл.","там.","қыр.","қаз.","қар.","желт."],"STANDALONESHORTMONTHS":["қаң.","ақп.","нау.","сәу.","мам.","мау.","шіл.","там.","қыр.","қаз.","қар.","желт."],"WEEKDAYS":["жексенбі","дүйсенбі","сейсенбі","сәрсенбі","бейсенбі","жұма","сенбі"],"STANDALONEWEEKDAYS":["жексенбі","дүйсенбі","сейсенбі","сәрсенбі","бейсенбі","жұма","сенбі"],"SHORTWEEKDAYS":["жс.","дс.","сс.","ср.","бс.","жм.","сб."],"STANDALONESHORTWEEKDAYS":["жс.","дс.","сс.","ср.","бс.","жм.","сб."],"NARROWWEEKDAYS":["Ж","Д","С","С","Б","Ж","С"],"STANDALONENARROWWEEKDAYS":["Ж","Д","С","С","Б","Ж","С"],"SHORTQUARTERS":["1-тоқсан","2-тоқсан","3-тоқсан","4-тоқсан"],"QUARTERS":["1-інші тоқсан","2-інші тоқсан","3-інші тоқсан","4-інші тоқсан"],"AMPMS":["түске дейін","түстен кейін"],"DATEFORMATS":["EEEE, d MMMM y 'ж'.","d MMMM y 'ж'.","dd.MM.y","dd/MM/yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/km.json b/intl/lib/src/data/dates/symbols/km.json
new file mode 100644
index 0000000..9a1f243
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/km.json
@@ -0,0 +1 @@
+{"NAME":"km","ERAS":["មុន គ.ស.","គ.ស."],"ERANAMES":["មុន​គ្រិស្តសករាជ","គ្រិស្តសករាជ"],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["មករា","កុម្ភៈ","មីនា","មេសា","ឧសភា","មិថុនា","កក្កដា","សីហា","កញ្ញា","តុលា","វិច្ឆិកា","ធ្នូ"],"STANDALONEMONTHS":["មករា","កុម្ភៈ","មីនា","មេសា","ឧសភា","មិថុនា","កក្កដា","សីហា","កញ្ញា","តុលា","វិច្ឆិកា","ធ្នូ"],"SHORTMONTHS":["មករា","កុម្ភៈ","មីនា","មេសា","ឧសភា","មិថុនា","កក្កដា","សីហា","កញ្ញា","តុលា","វិច្ឆិកា","ធ្នូ"],"STANDALONESHORTMONTHS":["មករា","កុម្ភៈ","មីនា","មេសា","ឧសភា","មិថុនា","កក្កដា","សីហា","កញ្ញា","តុលា","វិច្ឆិកា","ធ្នូ"],"WEEKDAYS":["អាទិត្យ","ចន្ទ","អង្គារ","ពុធ","ព្រហស្បតិ៍","សុក្រ","សៅរ៍"],"STANDALONEWEEKDAYS":["អាទិត្យ","ចន្ទ","អង្គារ","ពុធ","ព្រហស្បតិ៍","សុក្រ","សៅរ៍"],"SHORTWEEKDAYS":["អាទិត្យ","ចន្ទ","អង្គារ","ពុធ","ព្រហស្បតិ៍","សុក្រ","សៅរ៍"],"STANDALONESHORTWEEKDAYS":["អាទិត្យ","ចន្ទ","អង្គារ","ពុធ","ព្រហស្បតិ៍","សុក្រ","សៅរ៍"],"NARROWWEEKDAYS":["1","2","3","4","5","6","7"],"STANDALONENARROWWEEKDAYS":["1","2","3","4","5","6","7"],"SHORTQUARTERS":["ត្រីមាស ១","ត្រីមាស ២","ត្រីមាស ៣","ត្រីមាស ៤"],"QUARTERS":["ត្រីមាសទី ១","ត្រីមាសទី ២","ត្រីមាសទី ៣","ត្រីមាសទី ៤"],"AMPMS":["ព្រឹក","ល្ងាច"],"DATEFORMATS":["EEEE d MMMM y","d MMMM y","d MMM y","d/M/y"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/kn.json b/intl/lib/src/data/dates/symbols/kn.json
new file mode 100644
index 0000000..58efc3f
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/kn.json
@@ -0,0 +1 @@
+{"NAME":"kn","ERAS":["ಕ್ರಿ.ಪೂ","ಜಾಹೀ"],"ERANAMES":["ಈಸಪೂವ೯.","ಕ್ರಿಸ್ತ ಶಕ"],"NARROWMONTHS":["ಜ","ಫೆ","ಮಾ","ಏ","ಮೇ","ಜೂ","ಜು","ಆ","ಸೆ","ಅ","ನ","ಡಿ"],"STANDALONENARROWMONTHS":["ಜ","ಫೆ","ಮಾ","ಏ","ಮೇ","ಜೂ","ಜು","ಆ","ಸೆ","ಅ","ನ","ಡಿ"],"MONTHS":["ಜನವರಿ","ಫೆಬ್ರವರಿ","ಮಾರ್ಚ್","ಏಪ್ರಿಲ್","ಮೇ","ಜೂನ್","ಜುಲೈ","ಆಗಸ್ಟ್","ಸಪ್ಟೆಂಬರ್","ಅಕ್ಟೋಬರ್","ನವೆಂಬರ್","ಡಿಸೆಂಬರ್"],"STANDALONEMONTHS":["ಜನವರಿ","ಫೆಬ್ರವರಿ","ಮಾರ್ಚ್","ಏಪ್ರಿಲ್","ಮೇ","ಜೂನ್","ಜುಲೈ","ಆಗಸ್ಟ್","ಸಪ್ಟೆಂಬರ್","ಅಕ್ಟೋಬರ್","ನವೆಂಬರ್","ಡಿಸೆಂಬರ್"],"SHORTMONTHS":["ಜನ.","ಫೆಬ್ರು.","ಮಾ","ಏಪ್ರಿ.","ಮೇ","ಜೂ","ಜು.","ಆಗ.","ಸೆಪ್ಟೆಂ.","ಅಕ್ಟೋ.","ನವೆಂ.","ಡಿಸೆಂ."],"STANDALONESHORTMONTHS":["ಜನ.","ಫೆಬ್ರು.","ಮಾ","ಏಪ್ರಿ.","ಮೇ","ಜೂ","ಜು.","ಆಗ.","ಸೆಪ್ಟೆಂ.","ಅಕ್ಟೋ.","ನವೆಂ.","ಡಿಸೆಂ."],"WEEKDAYS":["ರವಿವಾರ","ಸೋಮವಾರ","ಮಂಗಳವಾರ","ಬುಧವಾರ","ಗುರುವಾರ","ಶುಕ್ರವಾರ","ಶನಿವಾರ"],"STANDALONEWEEKDAYS":["ರವಿವಾರ","ಸೋಮವಾರ","ಮಂಗಳವಾರ","ಬುಧವಾರ","ಗುರುವಾರ","ಶುಕ್ರವಾರ","ಶನಿವಾರ"],"SHORTWEEKDAYS":["ರ.","ಸೋ.","ಮಂ.","ಬು.","ಗು.","ಶು.","ಶನಿ."],"STANDALONESHORTWEEKDAYS":["ರವಿ","ಸೋಮ","ಮಂಗಳ","ಬುಧ","ಗುರು","ಶುಕ್ರ","ಶನಿ"],"NARROWWEEKDAYS":["ರ","ಸೋ","ಮಂ","ಬು","ಗು","ಶು","ಶ"],"STANDALONENARROWWEEKDAYS":["ರ","ಸೋ","ಮಂ","ಬು","ಗು","ಶು","ಶ"],"SHORTQUARTERS":["ತ್ರೈ 1","ತ್ರೈ 2","ತ್ರೈ 3","ತ್ರೈ 4"],"QUARTERS":["1 ನೇ ತ್ರೈಮಾಸಿಕ","2ನೇ ತ್ರೈಮಾಸಿಕ","3 ನೇ ತ್ರೈಮಾಸಿಕ","4 ನೇ ತ್ರೈಮಾಸಿಕ"],"AMPMS":["AM","PM"],"DATEFORMATS":["d MMMM y, EEEE","d MMMM y","d MMM y","d-M-yy"],"TIMEFORMATS":["hh:mm:ss a zzzz","hh:mm:ss a z","hh:mm:ss a","hh:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[6,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/ko.json b/intl/lib/src/data/dates/symbols/ko.json
new file mode 100644
index 0000000..4f2217e
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/ko.json
@@ -0,0 +1 @@
+{"NAME":"ko","ERAS":["기원전","서기"],"ERANAMES":["서력기원전","서력기원"],"NARROWMONTHS":["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],"STANDALONENARROWMONTHS":["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],"MONTHS":["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],"STANDALONEMONTHS":["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],"SHORTMONTHS":["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],"STANDALONESHORTMONTHS":["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],"WEEKDAYS":["일요일","월요일","화요일","수요일","목요일","금요일","토요일"],"STANDALONEWEEKDAYS":["일요일","월요일","화요일","수요일","목요일","금요일","토요일"],"SHORTWEEKDAYS":["일","월","화","수","목","금","토"],"STANDALONESHORTWEEKDAYS":["일","월","화","수","목","금","토"],"NARROWWEEKDAYS":["일","월","화","수","목","금","토"],"STANDALONENARROWWEEKDAYS":["일","월","화","수","목","금","토"],"SHORTQUARTERS":["1분기","2분기","3분기","4분기"],"QUARTERS":["제 1/4분기","제 2/4분기","제 3/4분기","제 4/4분기"],"AMPMS":["오전","오후"],"DATEFORMATS":["y년 M월 d일 EEEE","y년 M월 d일","y. M. d.","yy. M. d."],"TIMEFORMATS":["a h시 m분 s초 zzzz","a h시 m분 s초 z","a h:mm:ss","a h:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/ky.json b/intl/lib/src/data/dates/symbols/ky.json
new file mode 100644
index 0000000..b673aa7
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/ky.json
@@ -0,0 +1 @@
+{"NAME":"ky","ERAS":["б.з. ч.","б.з."],"ERANAMES":["б.з. чейин","б.з."],"NARROWMONTHS":["Я","Ф","М","А","М","И","И","А","С","О","Н","Д"],"STANDALONENARROWMONTHS":["Я","Ф","М","А","М","И","И","А","С","О","Н","Д"],"MONTHS":["январь","февраль","март","апрель","май","июнь","июль","август","сентябрь","октябрь","ноябрь","декабрь"],"STANDALONEMONTHS":["январь","февраль","март","апрель","май","июнь","июль","август","сентябрь","октябрь","ноябрь","декабрь"],"SHORTMONTHS":["янв.","фев.","мар.","апр.","май","июн.","июл.","авг.","сен.","окт.","ноя.","дек."],"STANDALONESHORTMONTHS":["янв.","фев.","мар.","апр.","май","июн.","июл.","авг.","сен.","окт.","ноя.","дек."],"WEEKDAYS":["Жек","Дүй","Шей","Шар","Бей","Жум","Ишм"],"STANDALONEWEEKDAYS":["Жекшемби","Дүйшөмбү","Шейшемби","Шаршемби","Бейшемби","Жума","Ишемби"],"SHORTWEEKDAYS":["Жк","Дш","Ше","Ша","Бш","Жм","Иш"],"STANDALONESHORTWEEKDAYS":["Жек","Дүй","Шей","Шар","Бей","Жум","Ишм"],"NARROWWEEKDAYS":["Ж","Д","Ш","Ш","Б","Ж","И"],"STANDALONENARROWWEEKDAYS":["Ж","Д","Ш","Ш","Б","Ж","И"],"SHORTQUARTERS":["1-чей.","2-чей.","3-чей.","4-чей."],"QUARTERS":["1-чейрек","2-чейрек","3-чейрек","4-чейрек"],"AMPMS":["түшкө чейинки","түштөн кийинки"],"DATEFORMATS":["EEEE, d-MMMM, y-'ж'.","d-MMMM, y-'ж'.","dd.MM.y","dd.MM.yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/ln.json b/intl/lib/src/data/dates/symbols/ln.json
new file mode 100644
index 0000000..fcf799d
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/ln.json
@@ -0,0 +1 @@
+{"NAME":"ln","ERAS":["libóso ya","nsima ya Y"],"ERANAMES":["Yambo ya Yézu Krís","Nsima ya Yézu Krís"],"NARROWMONTHS":["y","f","m","a","m","y","y","a","s","ɔ","n","d"],"STANDALONENARROWMONTHS":["y","f","m","a","m","y","y","a","s","ɔ","n","d"],"MONTHS":["sánzá ya yambo","sánzá ya míbalé","sánzá ya mísáto","sánzá ya mínei","sánzá ya mítáno","sánzá ya motóbá","sánzá ya nsambo","sánzá ya mwambe","sánzá ya libwa","sánzá ya zómi","sánzá ya zómi na mɔ̌kɔ́","sánzá ya zómi na míbalé"],"STANDALONEMONTHS":["sánzá ya yambo","sánzá ya míbalé","sánzá ya mísáto","sánzá ya mínei","sánzá ya mítáno","sánzá ya motóbá","sánzá ya nsambo","sánzá ya mwambe","sánzá ya libwa","sánzá ya zómi","sánzá ya zómi na mɔ̌kɔ́","sánzá ya zómi na míbalé"],"SHORTMONTHS":["yan","fbl","msi","apl","mai","yun","yul","agt","stb","ɔtb","nvb","dsb"],"STANDALONESHORTMONTHS":["yan","fbl","msi","apl","mai","yun","yul","agt","stb","ɔtb","nvb","dsb"],"WEEKDAYS":["eyenga","mokɔlɔ mwa yambo","mokɔlɔ mwa míbalé","mokɔlɔ mwa mísáto","mokɔlɔ ya mínéi","mokɔlɔ ya mítáno","mpɔ́sɔ"],"STANDALONEWEEKDAYS":["eyenga","mokɔlɔ mwa yambo","mokɔlɔ mwa míbalé","mokɔlɔ mwa mísáto","mokɔlɔ ya mínéi","mokɔlɔ ya mítáno","mpɔ́sɔ"],"SHORTWEEKDAYS":["eye","ybo","mbl","mst","min","mtn","mps"],"STANDALONESHORTWEEKDAYS":["eye","ybo","mbl","mst","min","mtn","mps"],"NARROWWEEKDAYS":["e","y","m","m","m","m","p"],"STANDALONENARROWWEEKDAYS":["e","y","m","m","m","m","p"],"SHORTQUARTERS":["SM1","SM2","SM3","SM4"],"QUARTERS":["sánzá mísáto ya yambo","sánzá mísáto ya míbalé","sánzá mísáto ya mísáto","sánzá mísáto ya mínei"],"AMPMS":["ntɔ́ngɔ́","mpókwa"],"DATEFORMATS":["EEEE d MMMM y","d MMMM y","d MMM y","d/M/y"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/lo.json b/intl/lib/src/data/dates/symbols/lo.json
new file mode 100644
index 0000000..b31968f
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/lo.json
@@ -0,0 +1 @@
+{"NAME":"lo","ERAS":["ກ່ອນ ຄ.ສ.","ຄ.ສ."],"ERANAMES":["ກ່ອນ ຄ.ສ.","ຄ.ສ."],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["ມັງກອນ","ກຸມພາ","ມີນາ","ເມສາ","ພຶດສະພາ","ມິຖຸນາ","ກໍລະກົດ","ສິງຫາ","ກັນຍາ","ຕຸລາ","ພະຈິກ","ທັນວາ"],"STANDALONEMONTHS":["ມັງກອນ","ກຸມພາ","ມີນາ","ເມສາ","ພຶດສະພາ","ມິຖຸນາ","ກໍລະກົດ","ສິງຫາ","ກັນຍາ","ຕຸລາ","ພະຈິກ","ທັນວາ"],"SHORTMONTHS":["ມ.ກ.","ກ.ພ.","ມ.ນ.","ມ.ສ.","ພ.ພ.","ມິ.ຖ.","ກ.ລ.","ສ.ຫ.","ກ.ຍ.","ຕ.ລ.","ພ.ຈ.","ທ.ວ."],"STANDALONESHORTMONTHS":["ມ.ກ.","ກ.ພ.","ມ.ນ.","ມ.ສ.","ພ.ພ.","ມິ.ຖ.","ກ.ລ.","ສ.ຫ.","ກ.ຍ.","ຕ.ລ.","ພ.ຈ.","ທ.ວ."],"WEEKDAYS":["ວັນອາທິດ","ວັນຈັນ","ວັນອັງຄານ","ວັນພຸດ","ວັນພະຫັດ","ວັນສຸກ","ວັນເສົາ"],"STANDALONEWEEKDAYS":["ວັນອາທິດ","ວັນຈັນ","ວັນອັງຄານ","ວັນພຸດ","ວັນພະຫັດ","ວັນສຸກ","ວັນເສົາ"],"SHORTWEEKDAYS":["ວັນອາທິດ","ວັນຈັນ","ວັນອັງຄານ","ວັນພຸດ","ວັນພະຫັດ","ວັນສຸກ","ວັນເສົາ"],"STANDALONESHORTWEEKDAYS":["ວັນອາທິດ","ວັນຈັນ","ວັນອັງຄານ","ວັນພຸດ","ວັນພະຫັດ","ວັນສຸກ","ວັນເສົາ"],"NARROWWEEKDAYS":["1","2","3","4","5","6","7"],"STANDALONENARROWWEEKDAYS":["ທ","ຈ","ຄ","​ພຸ","ພ","​ສຸ","ສ"],"SHORTQUARTERS":["ຕມ1","ຕມ2","ຕມ3","ຕມ4"],"QUARTERS":["ໄຕຣມາດ 1","ໄຕຣມາດ 2","ໄຕຣມາດ 3","ໄຕຣມາດ 4"],"AMPMS":["ກ່ອນທ່ຽງ","ຫຼັງທ່ຽງ"],"DATEFORMATS":["EEEE ທີ d MMMM G y","d MMMM y","d MMM y","d/M/y"],"TIMEFORMATS":["H ໂມງ m ນາທີ ss ວິນາທີ zzzz","H ໂມງ m ນາທີ ss ວິນາທີ z","H:mm:ss","H:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1}, {0}","{1}, {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/lt.json b/intl/lib/src/data/dates/symbols/lt.json
new file mode 100644
index 0000000..6e2b559
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/lt.json
@@ -0,0 +1 @@
+{"NAME":"lt","ERAS":["pr. Kr.","po Kr."],"ERANAMES":["prieš Kristų","po Kristaus"],"NARROWMONTHS":["S","V","K","B","G","B","L","R","R","S","L","G"],"STANDALONENARROWMONTHS":["S","V","K","B","G","B","L","R","R","S","L","G"],"MONTHS":["sausis","vasaris","kovas","balandis","gegužė","birželis","liepa","rugpjūtis","rugsėjis","spalis","lapkritis","gruodis"],"STANDALONEMONTHS":["sausis","vasaris","kovas","balandis","gegužė","birželis","liepa","rugpjūtis","rugsėjis","spalis","lapkritis","gruodis"],"SHORTMONTHS":["saus.","vas.","kov.","bal.","geg.","birž.","liep.","rugp.","rugs.","spal.","lapkr.","gruod."],"STANDALONESHORTMONTHS":["saus.","vas.","kov.","bal.","geg.","birž.","liep.","rugp.","rugs.","spal.","lapkr.","gruod."],"WEEKDAYS":["sekmadienis","pirmadienis","antradienis","trečiadienis","ketvirtadienis","penktadienis","šeštadienis"],"STANDALONEWEEKDAYS":["sekmadienis","pirmadienis","antradienis","trečiadienis","ketvirtadienis","penktadienis","šeštadienis"],"SHORTWEEKDAYS":["sk","pr","an","tr","kt","pn","št"],"STANDALONESHORTWEEKDAYS":["sk","pr","an","tr","kt","pn","št"],"NARROWWEEKDAYS":["S","P","A","T","K","P","Š"],"STANDALONENARROWWEEKDAYS":["S","P","A","T","K","P","Š"],"SHORTQUARTERS":["I k.","II k.","III k.","IV k."],"QUARTERS":["I ketvirtis","II ketvirtis","III ketvirtis","IV ketvirtis"],"AMPMS":["priešpiet","popiet"],"DATEFORMATS":["y 'm'. MMMM d 'd'., EEEE","y 'm'. MMMM d 'd'.","y MMM d","y-MM-dd"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/lv.json b/intl/lib/src/data/dates/symbols/lv.json
new file mode 100644
index 0000000..e407243
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/lv.json
@@ -0,0 +1 @@
+{"NAME":"lv","ERAS":["p.m.ē.","m.ē."],"ERANAMES":["pirms mūsu ēras","mūsu ērā"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["janvāris","februāris","marts","aprīlis","maijs","jūnijs","jūlijs","augusts","septembris","oktobris","novembris","decembris"],"STANDALONEMONTHS":["Janvāris","Februāris","Marts","Aprīlis","Maijs","Jūnijs","Jūlijs","Augusts","Septembris","Oktobris","Novembris","Decembris"],"SHORTMONTHS":["janv.","febr.","marts","apr.","maijs","jūn.","jūl.","aug.","sept.","okt.","nov.","dec."],"STANDALONESHORTMONTHS":["Janv.","Febr.","Marts","Apr.","Maijs","Jūn.","Jūl.","Aug.","Sept.","Okt.","Nov.","Dec."],"WEEKDAYS":["svētdiena","pirmdiena","otrdiena","trešdiena","ceturtdiena","piektdiena","sestdiena"],"STANDALONEWEEKDAYS":["Svētdiena","Pirmdiena","Otrdiena","Trešdiena","Ceturtdiena","Piektdiena","Sestdiena"],"SHORTWEEKDAYS":["Sv","Pr","Ot","Tr","Ce","Pk","Se"],"STANDALONESHORTWEEKDAYS":["Sv","Pr","Ot","Tr","Ce","Pk","Se"],"NARROWWEEKDAYS":["S","P","O","T","C","P","S"],"STANDALONENARROWWEEKDAYS":["S","P","O","T","C","P","S"],"SHORTQUARTERS":["C1","C2","C3","C4"],"QUARTERS":["1. ceturksnis","2. ceturksnis","3. ceturksnis","4. ceturksnis"],"AMPMS":["priekšpusdienā","pēcpusdienā"],"DATEFORMATS":["EEEE, y. 'gada' d. MMMM","y. 'gada' d. MMMM","y. 'gada' d. MMM","dd.MM.yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/mk.json b/intl/lib/src/data/dates/symbols/mk.json
new file mode 100644
index 0000000..86dd0ee
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/mk.json
@@ -0,0 +1 @@
+{"NAME":"mk","ERAS":["пр.н.е.","н.е."],"ERANAMES":["пр.н.е.","н.е."],"NARROWMONTHS":["ј","ф","м","а","м","ј","ј","а","с","о","н","д"],"STANDALONENARROWMONTHS":["ј","ф","м","а","м","ј","ј","а","с","о","н","д"],"MONTHS":["јануари","февруари","март","април","мај","јуни","јули","август","септември","октомври","ноември","декември"],"STANDALONEMONTHS":["јануари","февруари","март","април","мај","јуни","јули","август","септември","октомври","ноември","декември"],"SHORTMONTHS":["јан.","фев.","мар.","апр.","мај","јун.","јул.","авг.","септ.","окт.","ноем.","дек."],"STANDALONESHORTMONTHS":["јан.","фев.","мар.","апр.","мај","јун.","јул.","авг.","септ.","окт.","ноем.","дек."],"WEEKDAYS":["недела","понеделник","вторник","среда","четврток","петок","сабота"],"STANDALONEWEEKDAYS":["недела","понеделник","вторник","среда","четврток","петок","сабота"],"SHORTWEEKDAYS":["нед.","пон.","вт.","сре.","чет.","пет.","саб."],"STANDALONESHORTWEEKDAYS":["нед.","пон.","вт.","сре.","чет.","пет.","саб."],"NARROWWEEKDAYS":["н","п","в","с","ч","п","с"],"STANDALONENARROWWEEKDAYS":["н","п","в","с","ч","п","с"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["прво тромесечје","второ тромесечје","трето тромесечје","четврто тромесечје"],"AMPMS":["претпладне","попладне"],"DATEFORMATS":["EEEE, dd MMMM y 'г'.","dd MMMM y 'г'.","dd.M.y","dd.M.yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/ml.json b/intl/lib/src/data/dates/symbols/ml.json
new file mode 100644
index 0000000..9dba285
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/ml.json
@@ -0,0 +1 @@
+{"NAME":"ml","ERAS":["ക്രി.മൂ","എഡി"],"ERANAMES":["ക്രിസ്തുവിനു് മുമ്പ്‌","ക്രിസ്തുവിന് പിൻപ്"],"NARROWMONTHS":["ജ","ഫെ","മാ","ഏ","മേ","ജൂ","ജൂ","ഓ","സെ","ഒ","ന","ഡി"],"STANDALONENARROWMONTHS":["ജ","ഫെ","മാ","ഏ","മേ","ജൂ","ജൂ","ഓ","സെ","ഒ","ന","ഡി"],"MONTHS":["ജനുവരി","ഫെബ്രുവരി","മാർച്ച്","ഏപ്രിൽ","മേയ്","ജൂൺ","ജൂലൈ","ആഗസ്റ്റ്","സെപ്റ്റംബർ","ഒക്‌ടോബർ","നവംബർ","ഡിസംബർ"],"STANDALONEMONTHS":["ജനുവരി","ഫെബ്രുവരി","മാർച്ച്","ഏപ്രിൽ","മേയ്","ജൂൺ","ജൂലൈ","ആഗസ്റ്റ്","സെപ്റ്റംബർ","ഒക്‌ടോബർ","നവംബർ","ഡിസംബർ"],"SHORTMONTHS":["ജനു","ഫെബ്രു","മാർ","ഏപ്രി","മേയ്","ജൂൺ","ജൂലൈ","ഓഗ","സെപ്റ്റം","ഒക്ടോ","നവം","ഡിസം"],"STANDALONESHORTMONTHS":["ജനു","ഫെബ്രു","മാർ","ഏപ്രി","മേയ്","ജൂൺ","ജൂലൈ","ഓഗ","സെപ്റ്റം","ഒക്ടോ","നവം","ഡിസം"],"WEEKDAYS":["ഞായറാഴ്‌ച","തിങ്കളാഴ്‌ച","ചൊവ്വാഴ്ച","ബുധനാഴ്‌ച","വ്യാഴാഴ്‌ച","വെള്ളിയാഴ്‌ച","ശനിയാഴ്‌ച"],"STANDALONEWEEKDAYS":["ഞായറാഴ്‌ച","തിങ്കളാഴ്‌ച","ചൊവ്വാഴ്‌ച","ബുധനാഴ്‌ച","വ്യാഴാഴ്‌ച","വെള്ളിയാഴ്‌ച","ശനിയാഴ്‌ച"],"SHORTWEEKDAYS":["ഞായർ","തിങ്കൾ","ചൊവ്വ","ബുധൻ","വ്യാഴം","വെള്ളി","ശനി"],"STANDALONESHORTWEEKDAYS":["ഞായർ","തിങ്കൾ","ചൊവ്വ","ബുധൻ","വ്യാഴം","വെള്ളി","ശനി"],"NARROWWEEKDAYS":["ഞാ","തി","ചൊ","ബു","വ്യാ","വെ","ശ"],"STANDALONENARROWWEEKDAYS":["ഞാ","തി","ചൊ","ബു","വ്യാ","വെ","ശ"],"SHORTQUARTERS":["ഒന്നാം പാദം","രണ്ടാം പാദം","മൂന്നാം പാദം","നാലാം പാദം"],"QUARTERS":["ഒന്നാം പാദം","രണ്ടാം പാദം","മൂന്നാം പാദം","നാലാം പാദം"],"AMPMS":["AM","PM"],"DATEFORMATS":["y, MMMM d, EEEE","y, MMMM d","y, MMM d","dd/MM/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[6,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/mn.json b/intl/lib/src/data/dates/symbols/mn.json
new file mode 100644
index 0000000..4989e29
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/mn.json
@@ -0,0 +1 @@
+{"NAME":"mn","ERAS":["МЭӨ","МЭ"],"ERANAMES":["манай эриний өмнөх","манай эриний"],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["Нэгдүгээр сар","Хоёрдугаар сар","Гуравдугаар сар","Дөрөвдүгээр сар","Тавдугаар сар","Зургадугаар сар","Долдугаар сар","Наймдугаар сар","Есдүгээр сар","Аравдугаар сар","Арван нэгдүгээр сар","Арван хоёрдугаар сар"],"STANDALONEMONTHS":["Нэгдүгээр сар","Хоёрдугаар сар","Гуравдугаар сар","Дөрөвдүгээр сар","Тавдугаар сар","Зургадугаар сар","Долдугаар сар","Наймдугаар сар","Есдүгээр сар","Аравдугаар сар","Арван нэгдүгээр сар","Арван хоёрдугаар сар"],"SHORTMONTHS":["1-р сар","2-р сар","3-р сар","4-р сар","5-р сар","6-р сар","7-р сар","8-р сар","9-р сар","10-р сар","11-р сар","12-р сар"],"STANDALONESHORTMONTHS":["1-р сар","2-р сар","3-р сар","4-р сар","5-р сар","6-р сар","7-р сар","8-р сар","9-р сар","10-р сар","11-р сар","12-р сар"],"WEEKDAYS":["ням","даваа","мягмар","лхагва","пүрэв","баасан","бямба"],"STANDALONEWEEKDAYS":["ням","даваа","мягмар","лхагва","пүрэв","баасан","бямба"],"SHORTWEEKDAYS":["Ня","Да","Мя","Лх","Пү","Ба","Бя"],"STANDALONESHORTWEEKDAYS":["Ня","Да","Мя","Лх","Пү","Ба","Бя"],"NARROWWEEKDAYS":["1","2","3","4","5","6","7"],"STANDALONENARROWWEEKDAYS":["1","2","3","4","5","6","7"],"SHORTQUARTERS":["У1","У2","У3","У4"],"QUARTERS":["1-р улирал","2-р улирал","3-р улирал","4-р улирал"],"AMPMS":["ҮӨ","ҮХ"],"DATEFORMATS":["EEEE, y 'оны' MMMM 'сарын' dd","y 'оны' MMMM 'сарын' d","y MMM d","y-MM-dd"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/mr.json b/intl/lib/src/data/dates/symbols/mr.json
new file mode 100644
index 0000000..24d52b0
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/mr.json
@@ -0,0 +1 @@
+{"NAME":"mr","ERAS":["ईसापूर्व","सन"],"ERANAMES":["ईसवीसनपूर्व","ईसवीसन"],"NARROWMONTHS":["जा","फे","मा","ए","मे","जू","जु","ऑ","स","ऑ","नो","डि"],"STANDALONENARROWMONTHS":["जा","फे","मा","ए","मे","जू","जु","ऑ","स","ऑ","नो","डि"],"MONTHS":["जानेवारी","फेब्रुवारी","मार्च","एप्रिल","मे","जून","जुलै","ऑगस्ट","सप्टेंबर","ऑक्टोबर","नोव्हेंबर","डिसेंबर"],"STANDALONEMONTHS":["जानेवारी","फेब्रुवारी","मार्च","एप्रिल","मे","जून","जुलै","ऑगस्ट","सप्टेंबर","ऑक्टोबर","नोव्हेंबर","डिसेंबर"],"SHORTMONTHS":["जाने","फेब्रु","मार्च","एप्रि","मे","जून","जुलै","ऑग","सप्टें","ऑक्टो","नोव्हें","डिसें"],"STANDALONESHORTMONTHS":["जाने","फेब्रु","मार्च","एप्रि","मे","जून","जुलै","ऑग","सप्टें","ऑक्टो","नोव्हें","डिसें"],"WEEKDAYS":["रविवार","सोमवार","मंगळवार","बुधवार","गुरुवार","शुक्रवार","शनिवार"],"STANDALONEWEEKDAYS":["रविवार","सोमवार","मंगळवार","बुधवार","गुरुवार","शुक्रवार","शनिवार"],"SHORTWEEKDAYS":["रवि","सोम","मंगळ","बुध","गुरु","शुक्र","शनि"],"STANDALONESHORTWEEKDAYS":["रवि","सोम","मंगळ","बुध","गुरु","शुक्र","शनि"],"NARROWWEEKDAYS":["र","सो","मं","बु","गु","शु","श"],"STANDALONENARROWWEEKDAYS":["र","सो","मं","बु","गु","शु","श"],"SHORTQUARTERS":["ति1","ति2","ति3","ति4"],"QUARTERS":["प्रथम तिमाही","द्वितीय तिमाही","तृतीय तिमाही","चतुर्थ तिमाही"],"AMPMS":["[AM]","[PM]"],"DATEFORMATS":["EEEE, d MMMM, y","d MMMM, y","d MMM, y","d/M/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[6,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} 'रोजी' {0}","{1} 'रोजी' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/ms.json b/intl/lib/src/data/dates/symbols/ms.json
new file mode 100644
index 0000000..d73f78d
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/ms.json
@@ -0,0 +1 @@
+{"NAME":"ms","ERAS":["S.M.","TM"],"ERANAMES":["S.M.","TM"],"NARROWMONTHS":["J","F","M","A","M","J","J","O","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","O","S","O","N","D"],"MONTHS":["Januari","Februari","Mac","April","Mei","Jun","Julai","Ogos","September","Oktober","November","Disember"],"STANDALONEMONTHS":["Januari","Februari","Mac","April","Mei","Jun","Julai","Ogos","September","Oktober","November","Disember"],"SHORTMONTHS":["Jan","Feb","Mac","Apr","Mei","Jun","Jul","Ogo","Sep","Okt","Nov","Dis"],"STANDALONESHORTMONTHS":["Jan","Feb","Mac","Apr","Mei","Jun","Jul","Ogo","Sep","Okt","Nov","Dis"],"WEEKDAYS":["Ahad","Isnin","Selasa","Rabu","Khamis","Jumaat","Sabtu"],"STANDALONEWEEKDAYS":["Ahad","Isnin","Selasa","Rabu","Khamis","Jumaat","Sabtu"],"SHORTWEEKDAYS":["Ahd","Isn","Sel","Rab","Kha","Jum","Sab"],"STANDALONESHORTWEEKDAYS":["Ahd","Isn","Sel","Rab","Kha","Jum","Sab"],"NARROWWEEKDAYS":["A","I","S","R","K","J","S"],"STANDALONENARROWWEEKDAYS":["A","I","S","R","K","J","S"],"SHORTQUARTERS":["S1","S2","S3","S4"],"QUARTERS":["Suku pertama","Suku Ke-2","Suku Ke-3","Suku Ke-4"],"AMPMS":["PG","PTG"],"DATEFORMATS":["EEEE, d MMMM y","d MMMM y","d MMM y","d/MM/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/mt.json b/intl/lib/src/data/dates/symbols/mt.json
new file mode 100644
index 0000000..fd923ea
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/mt.json
@@ -0,0 +1 @@
+{"NAME":"mt","ERAS":["QK","WK"],"ERANAMES":["Qabel Kristu","Wara Kristu"],"NARROWMONTHS":["J","F","M","A","M","Ġ","L","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","Ġ","L","A","S","O","N","D"],"MONTHS":["Jannar","Frar","Marzu","April","Mejju","Ġunju","Lulju","Awwissu","Settembru","Ottubru","Novembru","Diċembru"],"STANDALONEMONTHS":["Jannar","Frar","Marzu","April","Mejju","Ġunju","Lulju","Awwissu","Settembru","Ottubru","Novembru","Diċembru"],"SHORTMONTHS":["Jan","Fra","Mar","Apr","Mej","Ġun","Lul","Aww","Set","Ott","Nov","Diċ"],"STANDALONESHORTMONTHS":["Jan","Fra","Mar","Apr","Mej","Ġun","Lul","Aww","Set","Ott","Nov","Diċ"],"WEEKDAYS":["Il-Ħadd","It-Tnejn","It-Tlieta","L-Erbgħa","Il-Ħamis","Il-Ġimgħa","Is-Sibt"],"STANDALONEWEEKDAYS":["Il-Ħadd","It-Tnejn","It-Tlieta","L-Erbgħa","Il-Ħamis","Il-Ġimgħa","Is-Sibt"],"SHORTWEEKDAYS":["Ħad","Tne","Tli","Erb","Ħam","Ġim","Sib"],"STANDALONESHORTWEEKDAYS":["Ħad","Tne","Tli","Erb","Ħam","Ġim","Sib"],"NARROWWEEKDAYS":["Ħ","T","T","E","Ħ","Ġ","S"],"STANDALONENARROWWEEKDAYS":["Ħ","T","T","E","Ħ","Ġ","S"],"SHORTQUARTERS":["K1","K2","K3","K4"],"QUARTERS":["K1","K2","K3","K4"],"AMPMS":["QN","WN"],"DATEFORMATS":["EEEE, d 'ta'’ MMMM y","d 'ta'’ MMMM y","dd MMM y","dd/MM/y"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/my.json b/intl/lib/src/data/dates/symbols/my.json
new file mode 100644
index 0000000..5e8d828
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/my.json
@@ -0,0 +1 @@
+{"NAME":"my","ERAS":["ဘီစီ","အေဒီ"],"ERANAMES":["ခရစ်တော် မပေါ်မီကာလ","ခရစ်တော် ပေါ်ထွန်းပြီးကာလ"],"NARROWMONTHS":["ဇ","ဖ","မ","ဧ","မ","ဇ","ဇ","ဩ","စ","အ","န","ဒ"],"STANDALONENARROWMONTHS":["ဇ","ဖ","မ","ဧ","မ","ဇ","ဇ","ဩ","စ","အ","န","ဒ"],"MONTHS":["ဇန်နဝါရီ","ဖေဖော်ဝါရီ","မတ်","ဧပြီ","မေ","ဇွန်","ဇူလိုင်","ဩဂုတ်","စက်တင်ဘာ","အောက်တိုဘာ","နိုဝင်ဘာ","ဒီဇင်ဘာ"],"STANDALONEMONTHS":["ဇန်နဝါရီ","ဖေဖော်ဝါရီ","မတ်","ဧပြီ","မေ","ဇွန်","ဇူလိုင်","ဩဂုတ်","စက်တင်ဘာ","အောက်တိုဘာ","နိုဝင်ဘာ","ဒီဇင်ဘာ"],"SHORTMONTHS":["ဇန်နဝါရီ","ဖေဖော်ဝါရီ","မတ်","ဧပြီ","မေ","ဇွန်","ဇူလိုင်","ဩဂုတ်","စက်တင်ဘာ","အောက်တိုဘာ","နိုဝင်ဘာ","ဒီဇင်ဘာ"],"STANDALONESHORTMONTHS":["ဇန်နဝါရီ","ဖေဖော်ဝါရီ","မတ်","ဧပြီ","မေ","ဇွန်","ဇူလိုင်","ဩဂုတ်","စက်တင်ဘာ","အောက်တိုဘာ","နိုဝင်ဘာ","ဒီဇင်ဘာ"],"WEEKDAYS":["တနင်္ဂနွေ","တနင်္လာ","အင်္ဂါ","ဗုဒ္ဓဟူး","ကြာသပတေး","သောကြာ","စနေ"],"STANDALONEWEEKDAYS":["တနင်္ဂနွေ","တနင်္လာ","အင်္ဂါ","ဗုဒ္ဓဟူး","ကြာသပတေး","သောကြာ","စနေ"],"SHORTWEEKDAYS":["တနင်္ဂနွေ","တနင်္လာ","အင်္ဂါ","ဗုဒ္ဓဟူး","ကြာသပတေး","သောကြာ","စနေ"],"STANDALONESHORTWEEKDAYS":["တနင်္ဂနွေ","တနင်္လာ","အင်္ဂါ","ဗုဒ္ဓဟူး","ကြာသပတေး","သောကြာ","စနေ"],"NARROWWEEKDAYS":["တ","တ","အ","ဗ","က","သ","စ"],"STANDALONENARROWWEEKDAYS":["တ","တ","အ","ဗ","က","သ","စ"],"SHORTQUARTERS":["ပထမ သုံးလပတ်","ဒုတိယ သုံးလပတ်","တတိယ သုံးလပတ်","စတုတ္ထ သုံးလပတ်"],"QUARTERS":["ပထမ သုံးလပတ်","ဒုတိယ သုံးလပတ်","တတိယ သုံးလပတ်","စတုတ္ထ သုံးလပတ်"],"AMPMS":["နံနက်","ညနေ"],"DATEFORMATS":["EEEE, y MMMM dd","y MMMM d","y MMM d","yy/MM/dd"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1}မှာ {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/nb.json b/intl/lib/src/data/dates/symbols/nb.json
new file mode 100644
index 0000000..390939a
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/nb.json
@@ -0,0 +1 @@
+{"NAME":"nb","ERAS":["f.Kr.","e.Kr."],"ERANAMES":["f.Kr.","e.Kr."],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],"STANDALONEMONTHS":["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],"SHORTMONTHS":["jan.","feb.","mar.","apr.","mai","jun.","jul.","aug.","sep.","okt.","nov.","des."],"STANDALONESHORTMONTHS":["jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des"],"WEEKDAYS":["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],"STANDALONEWEEKDAYS":["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],"SHORTWEEKDAYS":["søn.","man.","tir.","ons.","tor.","fre.","lør."],"STANDALONESHORTWEEKDAYS":["sø.","ma.","ti.","on.","to.","fr.","lø."],"NARROWWEEKDAYS":["S","M","T","O","T","F","L"],"STANDALONENARROWWEEKDAYS":["S","M","T","O","T","F","L"],"SHORTQUARTERS":["K1","K2","K3","K4"],"QUARTERS":["1. kvartal","2. kvartal","3. kvartal","4. kvartal"],"AMPMS":["a.m.","p.m."],"DATEFORMATS":["EEEE d. MMMM y","d. MMMM y","d. MMM y","dd.MM.yy"],"TIMEFORMATS":["HH.mm.ss zzzz","HH.mm.ss z","HH.mm.ss","HH.mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} 'kl.' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/ne.json b/intl/lib/src/data/dates/symbols/ne.json
new file mode 100644
index 0000000..f8d374b
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/ne.json
@@ -0,0 +1 @@
+{"NAME":"ne","ERAS":["ईसा पूर्व","सन्"],"ERANAMES":["ईसा पूर्व","सन्"],"NARROWMONTHS":["१","२","३","४","५","६","७","८","९","१०","११","१२"],"STANDALONENARROWMONTHS":["१","२","३","४","५","६","७","८","९","१०","११","१२"],"MONTHS":["जनवरी","फेब्रुअरी","मार्च","अप्रिल","मे","जुन","जुलाई","अगस्ट","सेप्टेम्बर","अक्टोबर","नोभेम्बर","डिसेम्बर"],"STANDALONEMONTHS":["जनवरी","फेब्रुअरी","मार्च","अप्रिल","मे","जुन","जुलाई","अगस्ट","सेप्टेम्बर","अक्टोबर","नोभेम्बर","डिसेम्बर"],"SHORTMONTHS":["जनवरी","फेब्रुअरी","मार्च","अप्रिल","मे","जुन","जुलाई","अगस्ट","सेप्टेम्बर","अक्टोबर","नोभेम्बर","डिसेम्बर"],"STANDALONESHORTMONTHS":["जनवरी","फेब्रुअरी","मार्च","अप्रिल","मे","जुन","जुलाई","अगस्ट","सेप्टेम्बर","अक्टोबर","नोभेम्बर","डिसेम्बर"],"WEEKDAYS":["आइतबार","सोमबार","मङ्गलबार","बुधबार","बिहीबार","शुक्रबार","शनिबार"],"STANDALONEWEEKDAYS":["आइतबार","सोमबार","मङ्गलबार","बुधबार","बिहीबार","शुक्रबार","शनिबार"],"SHORTWEEKDAYS":["आइत","सोम","मङ्गल","बुध","बिही","शुक्र","शनि"],"STANDALONESHORTWEEKDAYS":["आइत","सोम","मङ्गल","बुध","बिही","शुक्र","शनि"],"NARROWWEEKDAYS":["आ","सो","म","बु","बि","शु","श"],"STANDALONENARROWWEEKDAYS":["आ","सो","म","बु","बि","शु","श"],"SHORTQUARTERS":["पहिलो सत्र","दोस्रो सत्र","तेस्रो सत्र","चौथो सत्र"],"QUARTERS":["पहिलो सत्र","दोस्रो सत्र","तेस्रो सत्र","चौथो सत्र"],"AMPMS":["पूर्व मध्यान्ह","उत्तर मध्यान्ह"],"DATEFORMATS":["y MMMM d, EEEE","y MMMM d","y MMM d","y-MM-dd"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/nl.json b/intl/lib/src/data/dates/symbols/nl.json
new file mode 100644
index 0000000..823a2db
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/nl.json
@@ -0,0 +1 @@
+{"NAME":"nl","ERAS":["v.Chr.","n.Chr."],"ERANAMES":["Voor Christus","na Christus"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],"STANDALONEMONTHS":["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],"SHORTMONTHS":["jan.","feb.","mrt.","apr.","mei","jun.","jul.","aug.","sep.","okt.","nov.","dec."],"STANDALONESHORTMONTHS":["jan","feb","mrt","apr","mei","jun","jul","aug","sep","okt","nov","dec"],"WEEKDAYS":["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],"STANDALONEWEEKDAYS":["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],"SHORTWEEKDAYS":["zo","ma","di","wo","do","vr","za"],"STANDALONESHORTWEEKDAYS":["zo","ma","di","wo","do","vr","za"],"NARROWWEEKDAYS":["Z","M","D","W","D","V","Z"],"STANDALONENARROWWEEKDAYS":["Z","M","D","W","D","V","Z"],"SHORTQUARTERS":["K1","K2","K3","K4"],"QUARTERS":["1e kwartaal","2e kwartaal","3e kwartaal","4e kwartaal"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE d MMMM y","d MMMM y","d MMM y","dd-MM-yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/no.json b/intl/lib/src/data/dates/symbols/no.json
new file mode 100644
index 0000000..b219bb3
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/no.json
@@ -0,0 +1 @@
+{"NAME":"no","ERAS":["f.Kr.","e.Kr."],"ERANAMES":["f.Kr.","e.Kr."],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],"STANDALONEMONTHS":["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],"SHORTMONTHS":["jan.","feb.","mar.","apr.","mai","jun.","jul.","aug.","sep.","okt.","nov.","des."],"STANDALONESHORTMONTHS":["jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des"],"WEEKDAYS":["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],"STANDALONEWEEKDAYS":["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],"SHORTWEEKDAYS":["søn.","man.","tir.","ons.","tor.","fre.","lør."],"STANDALONESHORTWEEKDAYS":["sø.","ma.","ti.","on.","to.","fr.","lø."],"NARROWWEEKDAYS":["S","M","T","O","T","F","L"],"STANDALONENARROWWEEKDAYS":["S","M","T","O","T","F","L"],"SHORTQUARTERS":["K1","K2","K3","K4"],"QUARTERS":["1. kvartal","2. kvartal","3. kvartal","4. kvartal"],"AMPMS":["a.m.","p.m."],"DATEFORMATS":["EEEE d. MMMM y","d. MMMM y","d. MMM y","dd.MM.yy"],"TIMEFORMATS":["HH.mm.ss zzzz","HH.mm.ss z","HH.mm.ss","HH.mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} 'kl.' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/no_NO.json b/intl/lib/src/data/dates/symbols/no_NO.json
new file mode 100644
index 0000000..eeeacfa
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/no_NO.json
@@ -0,0 +1 @@
+{"NAME":"no_NO","ERAS":["f.Kr.","e.Kr."],"ERANAMES":["f.Kr.","e.Kr."],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],"STANDALONEMONTHS":["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],"SHORTMONTHS":["jan.","feb.","mar.","apr.","mai","jun.","jul.","aug.","sep.","okt.","nov.","des."],"STANDALONESHORTMONTHS":["jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des"],"WEEKDAYS":["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],"STANDALONEWEEKDAYS":["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],"SHORTWEEKDAYS":["søn.","man.","tir.","ons.","tor.","fre.","lør."],"STANDALONESHORTWEEKDAYS":["sø.","ma.","ti.","on.","to.","fr.","lø."],"NARROWWEEKDAYS":["S","M","T","O","T","F","L"],"STANDALONENARROWWEEKDAYS":["S","M","T","O","T","F","L"],"SHORTQUARTERS":["K1","K2","K3","K4"],"QUARTERS":["1. kvartal","2. kvartal","3. kvartal","4. kvartal"],"AMPMS":["a.m.","p.m."],"DATEFORMATS":["EEEE d. MMMM y","d. MMMM y","d. MMM y","dd.MM.yy"],"TIMEFORMATS":["HH.mm.ss zzzz","HH.mm.ss z","HH.mm.ss","HH.mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} 'kl.' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/or.json b/intl/lib/src/data/dates/symbols/or.json
new file mode 100644
index 0000000..35df032
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/or.json
@@ -0,0 +1 @@
+{"NAME":"or","ERAS":["BCE","CE"],"ERANAMES":["BCE","CE"],"NARROWMONTHS":["ଜା","ଫେ","ମା","ଅ","ମେ","ଜୁ","ଜୁ","ଅ","ସେ","ଅ","ନ","ଡି"],"STANDALONENARROWMONTHS":["ଜା","ଫେ","ମା","ଅ","ମେ","ଜୁ","ଜୁ","ଅ","ସେ","ଅ","ନ","ଡି"],"MONTHS":["ଜାନୁଆରୀ","ଫେବ୍ରୁୟାରୀ","ମାର୍ଚ୍ଚ","ଅପ୍ରେଲ","ମେ","ଜୁନ","ଜୁଲାଇ","ଅଗଷ୍ଟ","ସେପ୍ଟେମ୍ବର","ଅକ୍ଟୋବର","ନଭେମ୍ବର","ଡିସେମ୍ବର"],"STANDALONEMONTHS":["ଜାନୁଆରୀ","ଫେବ୍ରୁୟାରୀ","ମାର୍ଚ୍ଚ","ଅପ୍ରେଲ","ମେ","ଜୁନ","ଜୁଲାଇ","ଅଗଷ୍ଟ","ସେପ୍ଟେମ୍ବର","ଅକ୍ଟୋବର","ନଭେମ୍ବର","ଡିସେମ୍ବର"],"SHORTMONTHS":["ଜାନୁଆରୀ","ଫେବ୍ରୁୟାରୀ","ମାର୍ଚ୍ଚ","ଅପ୍ରେଲ","ମେ","ଜୁନ","ଜୁଲାଇ","ଅଗଷ୍ଟ","ସେପ୍ଟେମ୍ବର","ଅକ୍ଟୋବର","ନଭେମ୍ବର","ଡିସେମ୍ବର"],"STANDALONESHORTMONTHS":["ଜାନୁଆରୀ","ଫେବ୍ରୁୟାରୀ","ମାର୍ଚ୍ଚ","ଅପ୍ରେଲ","ମେ","ଜୁନ","ଜୁଲାଇ","ଅଗଷ୍ଟ","ସେପ୍ଟେମ୍ବର","ଅକ୍ଟୋବର","ନଭେମ୍ବର","ଡିସେମ୍ବର"],"WEEKDAYS":["ରବିବାର","ସୋମବାର","ମଙ୍ଗଳବାର","ବୁଧବାର","ଗୁରୁବାର","ଶୁକ୍ରବାର","ଶନିବାର"],"STANDALONEWEEKDAYS":["ରବିବାର","ସୋମବାର","ମଙ୍ଗଳବାର","ବୁଧବାର","ଗୁରୁବାର","ଶୁକ୍ରବାର","ଶନିବାର"],"SHORTWEEKDAYS":["ରବି","ସୋମ","ମଙ୍ଗଳ","ବୁଧ","ଗୁରୁ","ଶୁକ୍ର","ଶନି"],"STANDALONESHORTWEEKDAYS":["ରବି","ସୋମ","ମଙ୍ଗଳ","ବୁଧ","ଗୁରୁ","ଶୁକ୍ର","ଶନି"],"NARROWWEEKDAYS":["ର","ସୋ","ମ","ବୁ","ଗୁ","ଶୁ","ଶ"],"STANDALONENARROWWEEKDAYS":["ର","ସୋ","ମ","ବୁ","ଗୁ","ଶୁ","ଶ"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["Q1","Q2","Q3","Q4"],"AMPMS":["am","pm"],"DATEFORMATS":["EEEE, d MMMM y","d MMMM y","d MMM y","d-M-yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[6,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/pa.json b/intl/lib/src/data/dates/symbols/pa.json
new file mode 100644
index 0000000..49a3ce1
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/pa.json
@@ -0,0 +1 @@
+{"NAME":"pa","ERAS":["ਈ. ਪੂ.","ਸੰਨ"],"ERANAMES":["ਈ. ਪੂ.","ਸੰਨ"],"NARROWMONTHS":["ਜ","ਫ਼","ਮਾ","ਅ","ਮ","ਜੂ","ਜੁ","ਅ","ਸ","ਅ","ਨ","ਦ"],"STANDALONENARROWMONTHS":["ਜ","ਫ਼","ਮਾ","ਅ","ਮ","ਜੂ","ਜੁ","ਅ","ਸ","ਅ","ਨ","ਦ"],"MONTHS":["ਜਨਵਰੀ","ਫ਼ਰਵਰੀ","ਮਾਰਚ","ਅਪ੍ਰੈਲ","ਮਈ","ਜੂਨ","ਜੁਲਾਈ","ਅਗਸਤ","ਸਤੰਬਰ","ਅਕਤੂਬਰ","ਨਵੰਬਰ","ਦਸੰਬਰ"],"STANDALONEMONTHS":["ਜਨਵਰੀ","ਫ਼ਰਵਰੀ","ਮਾਰਚ","ਅਪ੍ਰੈਲ","ਮਈ","ਜੂਨ","ਜੁਲਾਈ","ਅਗਸਤ","ਸਤੰਬਰ","ਅਕਤੂਬਰ","ਨਵੰਬਰ","ਦਸੰਬਰ"],"SHORTMONTHS":["ਜਨਵਰੀ","ਫ਼ਰਵਰੀ","ਮਾਰਚ","ਅਪ੍ਰੈਲ","ਮਈ","ਜੂਨ","ਜੁਲਾਈ","ਅਗਸਤ","ਸਤੰਬਰ","ਅਕਤੂਬਰ","ਨਵੰਬਰ","ਦਸੰਬਰ"],"STANDALONESHORTMONTHS":["ਜਨਵਰੀ","ਫ਼ਰਵਰੀ","ਮਾਰਚ","ਅਪ੍ਰੈਲ","ਮਈ","ਜੂਨ","ਜੁਲਾਈ","ਅਗਸਤ","ਸਤੰਬਰ","ਅਕਤੂਬਰ","ਨਵੰਬਰ","ਦਸੰਬਰ"],"WEEKDAYS":["ਐਤਵਾਰ","ਸੋਮਵਾਰ","ਮੰਗਲਵਾਰ","ਬੁਧਵਾਰ","ਵੀਰਵਾਰ","ਸ਼ੁੱਕਰਵਾਰ","ਸ਼ਨੀਵਾਰ"],"STANDALONEWEEKDAYS":["ਐਤਵਾਰ","ਸੋਮਵਾਰ","ਮੰਗਲਵਾਰ","ਬੁਧਵਾਰ","ਵੀਰਵਾਰ","ਸ਼ੁੱਕਰਵਾਰ","ਸ਼ਨੀਵਾਰ"],"SHORTWEEKDAYS":["ਐਤ.","ਸੋਮ.","ਮੰਗਲ.","ਬੁਧ.","ਵੀਰ.","ਸ਼ੁੱਕਰ.","ਸ਼ਨੀ."],"STANDALONESHORTWEEKDAYS":["ਐਤ.","ਸੋਮ.","ਮੰਗਲ.","ਬੁਧ.","ਵੀਰ.","ਸ਼ੁੱਕਰ.","ਸ਼ਨੀ."],"NARROWWEEKDAYS":["ਐ","ਸੋ","ਮੰ","ਬੁੱ","ਵੀ","ਸ਼ੁੱ","ਸ਼"],"STANDALONENARROWWEEKDAYS":["ਐ","ਸੋ","ਮੰ","ਬੁੱ","ਵੀ","ਸ਼ੁੱ","ਸ਼"],"SHORTQUARTERS":["ਪਊਆ","ਅੱਧਾ","ਪੌਣਾ","ਪੂਰਾ"],"QUARTERS":["ਪਊਆ","ਅੱਧਾ","ਪੌਣਾ","ਪੂਰਾ"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, d MMMM y","d MMMM y","d MMM y","d/M/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[6,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/pl.json b/intl/lib/src/data/dates/symbols/pl.json
new file mode 100644
index 0000000..de4d1a2
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/pl.json
@@ -0,0 +1 @@
+{"NAME":"pl","ERAS":["p.n.e.","n.e."],"ERANAMES":["p.n.e.","n.e."],"NARROWMONTHS":["s","l","m","k","m","c","l","s","w","p","l","g"],"STANDALONENARROWMONTHS":["s","l","m","k","m","c","l","s","w","p","l","g"],"MONTHS":["stycznia","lutego","marca","kwietnia","maja","czerwca","lipca","sierpnia","września","października","listopada","grudnia"],"STANDALONEMONTHS":["styczeń","luty","marzec","kwiecień","maj","czerwiec","lipiec","sierpień","wrzesień","październik","listopad","grudzień"],"SHORTMONTHS":["sty","lut","mar","kwi","maj","cze","lip","sie","wrz","paź","lis","gru"],"STANDALONESHORTMONTHS":["sty","lut","mar","kwi","maj","cze","lip","sie","wrz","paź","lis","gru"],"WEEKDAYS":["niedziela","poniedziałek","wtorek","środa","czwartek","piątek","sobota"],"STANDALONEWEEKDAYS":["niedziela","poniedziałek","wtorek","środa","czwartek","piątek","sobota"],"SHORTWEEKDAYS":["niedz.","pon.","wt.","śr.","czw.","pt.","sob."],"STANDALONESHORTWEEKDAYS":["niedz.","pon.","wt.","śr.","czw.","pt.","sob."],"NARROWWEEKDAYS":["N","P","W","Ś","C","P","S"],"STANDALONENARROWWEEKDAYS":["N","P","W","Ś","C","P","S"],"SHORTQUARTERS":["K1","K2","K3","K4"],"QUARTERS":["I kwartał","II kwartał","III kwartał","IV kwartał"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, d MMMM y","d MMMM y","d MMM y","dd.MM.y"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/pt.json b/intl/lib/src/data/dates/symbols/pt.json
new file mode 100644
index 0000000..b842c89
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/pt.json
@@ -0,0 +1 @@
+{"NAME":"pt","ERAS":["a.C.","d.C."],"ERANAMES":["Antes de Cristo","Ano do Senhor"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["janeiro","fevereiro","março","abril","maio","junho","julho","agosto","setembro","outubro","novembro","dezembro"],"STANDALONEMONTHS":["janeiro","fevereiro","março","abril","maio","junho","julho","agosto","setembro","outubro","novembro","dezembro"],"SHORTMONTHS":["jan","fev","mar","abr","mai","jun","jul","ago","set","out","nov","dez"],"STANDALONESHORTMONTHS":["jan","fev","mar","abr","mai","jun","jul","ago","set","out","nov","dez"],"WEEKDAYS":["domingo","segunda-feira","terça-feira","quarta-feira","quinta-feira","sexta-feira","sábado"],"STANDALONEWEEKDAYS":["domingo","segunda-feira","terça-feira","quarta-feira","quinta-feira","sexta-feira","sábado"],"SHORTWEEKDAYS":["dom","seg","ter","qua","qui","sex","sáb"],"STANDALONESHORTWEEKDAYS":["dom","seg","ter","qua","qui","sex","sáb"],"NARROWWEEKDAYS":["D","S","T","Q","Q","S","S"],"STANDALONENARROWWEEKDAYS":["D","S","T","Q","Q","S","S"],"SHORTQUARTERS":["T1","T2","T3","T4"],"QUARTERS":["1º trimestre","2º trimestre","3º trimestre","4º trimestre"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, d 'de' MMMM 'de' y","d 'de' MMMM 'de' y","dd/MM/y","dd/MM/yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/pt_BR.json b/intl/lib/src/data/dates/symbols/pt_BR.json
new file mode 100644
index 0000000..90b63ae
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/pt_BR.json
@@ -0,0 +1 @@
+{"NAME":"pt_BR","ERAS":["a.C.","d.C."],"ERANAMES":["Antes de Cristo","Ano do Senhor"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["janeiro","fevereiro","março","abril","maio","junho","julho","agosto","setembro","outubro","novembro","dezembro"],"STANDALONEMONTHS":["janeiro","fevereiro","março","abril","maio","junho","julho","agosto","setembro","outubro","novembro","dezembro"],"SHORTMONTHS":["jan","fev","mar","abr","mai","jun","jul","ago","set","out","nov","dez"],"STANDALONESHORTMONTHS":["jan","fev","mar","abr","mai","jun","jul","ago","set","out","nov","dez"],"WEEKDAYS":["domingo","segunda-feira","terça-feira","quarta-feira","quinta-feira","sexta-feira","sábado"],"STANDALONEWEEKDAYS":["domingo","segunda-feira","terça-feira","quarta-feira","quinta-feira","sexta-feira","sábado"],"SHORTWEEKDAYS":["dom","seg","ter","qua","qui","sex","sáb"],"STANDALONESHORTWEEKDAYS":["dom","seg","ter","qua","qui","sex","sáb"],"NARROWWEEKDAYS":["D","S","T","Q","Q","S","S"],"STANDALONENARROWWEEKDAYS":["D","S","T","Q","Q","S","S"],"SHORTQUARTERS":["T1","T2","T3","T4"],"QUARTERS":["1º trimestre","2º trimestre","3º trimestre","4º trimestre"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, d 'de' MMMM 'de' y","d 'de' MMMM 'de' y","dd/MM/y","dd/MM/yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/pt_PT.json b/intl/lib/src/data/dates/symbols/pt_PT.json
new file mode 100644
index 0000000..94ba1c7
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/pt_PT.json
@@ -0,0 +1 @@
+{"NAME":"pt_PT","ERAS":["a.C.","d.C."],"ERANAMES":["Antes de Cristo","Ano do Senhor"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],"STANDALONEMONTHS":["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],"SHORTMONTHS":["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],"STANDALONESHORTMONTHS":["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],"WEEKDAYS":["domingo","segunda-feira","terça-feira","quarta-feira","quinta-feira","sexta-feira","sábado"],"STANDALONEWEEKDAYS":["domingo","segunda-feira","terça-feira","quarta-feira","quinta-feira","sexta-feira","sábado"],"SHORTWEEKDAYS":["dom","seg","ter","qua","qui","sex","sáb"],"STANDALONESHORTWEEKDAYS":["dom","seg","ter","qua","qui","sex","sáb"],"NARROWWEEKDAYS":["D","S","T","Q","Q","S","S"],"STANDALONENARROWWEEKDAYS":["D","S","T","Q","Q","S","S"],"SHORTQUARTERS":["T1","T2","T3","T4"],"QUARTERS":["1.º trimestre","2.º trimestre","3.º trimestre","4.º trimestre"],"AMPMS":["da manhã","da tarde"],"DATEFORMATS":["EEEE, d 'de' MMMM 'de' y","d 'de' MMMM 'de' y","dd/MM/y","dd/MM/yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} 'às' {0}","{1} 'às' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/ro.json b/intl/lib/src/data/dates/symbols/ro.json
new file mode 100644
index 0000000..42838e3
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/ro.json
@@ -0,0 +1 @@
+{"NAME":"ro","ERAS":["î.Hr.","d.Hr."],"ERANAMES":["înainte de Hristos","după Hristos"],"NARROWMONTHS":["I","F","M","A","M","I","I","A","S","O","N","D"],"STANDALONENARROWMONTHS":["I","F","M","A","M","I","I","A","S","O","N","D"],"MONTHS":["ianuarie","februarie","martie","aprilie","mai","iunie","iulie","august","septembrie","octombrie","noiembrie","decembrie"],"STANDALONEMONTHS":["ianuarie","februarie","martie","aprilie","mai","iunie","iulie","august","septembrie","octombrie","noiembrie","decembrie"],"SHORTMONTHS":["ian.","feb.","mar.","apr.","mai","iun.","iul.","aug.","sept.","oct.","nov.","dec."],"STANDALONESHORTMONTHS":["ian.","feb.","mar.","apr.","mai","iun.","iul.","aug.","sept.","oct.","nov.","dec."],"WEEKDAYS":["duminică","luni","marți","miercuri","joi","vineri","sâmbătă"],"STANDALONEWEEKDAYS":["duminică","luni","marți","miercuri","joi","vineri","sâmbătă"],"SHORTWEEKDAYS":["Dum","Lun","Mar","Mie","Joi","Vin","Sâm"],"STANDALONESHORTWEEKDAYS":["Dum","Lun","Mar","Mie","Joi","Vin","Sâm"],"NARROWWEEKDAYS":["D","L","M","M","J","V","S"],"STANDALONENARROWWEEKDAYS":["D","L","M","M","J","V","S"],"SHORTQUARTERS":["trim. I","trim. II","trim. III","trim. IV"],"QUARTERS":["trimestrul I","trimestrul al II-lea","trimestrul al III-lea","trimestrul al IV-lea"],"AMPMS":["a.m.","p.m."],"DATEFORMATS":["EEEE, d MMMM y","d MMMM y","d MMM y","dd.MM.y"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1}, {0}","{1}, {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/ru.json b/intl/lib/src/data/dates/symbols/ru.json
new file mode 100644
index 0000000..3b9f286
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/ru.json
@@ -0,0 +1 @@
+{"NAME":"ru","ERAS":["до н. э.","н. э."],"ERANAMES":["до н.э.","н.э."],"NARROWMONTHS":["Я","Ф","М","А","М","И","И","А","С","О","Н","Д"],"STANDALONENARROWMONTHS":["Я","Ф","М","А","М","И","И","А","С","О","Н","Д"],"MONTHS":["января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря"],"STANDALONEMONTHS":["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],"SHORTMONTHS":["янв.","февр.","марта","апр.","мая","июня","июля","авг.","сент.","окт.","нояб.","дек."],"STANDALONESHORTMONTHS":["Янв.","Февр.","Март","Апр.","Май","Июнь","Июль","Авг.","Сент.","Окт.","Нояб.","Дек."],"WEEKDAYS":["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"],"STANDALONEWEEKDAYS":["Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница","Суббота"],"SHORTWEEKDAYS":["вс","пн","вт","ср","чт","пт","сб"],"STANDALONESHORTWEEKDAYS":["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],"NARROWWEEKDAYS":["вс","пн","вт","ср","чт","пт","сб"],"STANDALONENARROWWEEKDAYS":["В","П","В","С","Ч","П","С"],"SHORTQUARTERS":["1-й кв.","2-й кв.","3-й кв.","4-й кв."],"QUARTERS":["1-й квартал","2-й квартал","3-й квартал","4-й квартал"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, d MMMM y 'г'.","d MMMM y 'г'.","d MMM y 'г'.","dd.MM.yy"],"TIMEFORMATS":["H:mm:ss zzzz","H:mm:ss z","H:mm:ss","H:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1}, {0}","{1}, {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/si.json b/intl/lib/src/data/dates/symbols/si.json
new file mode 100644
index 0000000..381e408
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/si.json
@@ -0,0 +1 @@
+{"NAME":"si","ERAS":["ක්‍රි.පූ.","ක්‍රි.ව."],"ERANAMES":["ක්‍රිස්තු පූර්‍ව","ක්‍රිස්තු වර්‍ෂ"],"NARROWMONTHS":["ජ","පෙ","මා","අ","මැ","ජූ","ජූ","අ","සැ","ඔ","නෙ","දෙ"],"STANDALONENARROWMONTHS":["ජ","පෙ","මා","අ","මැ","ජූ","ජූ","අ","සැ","ඔ","නෙ","දෙ"],"MONTHS":["ජනවාරි","පෙබරවාරි","මාර්තු","අප්‍රේල්","මැයි","ජූනි","ජූලි","අගෝස්තු","සැප්තැම්බර්","ඔක්තෝබර්","නොවැම්බර්","දෙසැම්බර්"],"STANDALONEMONTHS":["ජනවාරි","පෙබරවාරි","මාර්තු","අප්‍රේල්","මැයි","ජූනි","ජූලි","අගෝස්තු","සැප්තැම්බර්","ඔක්තෝබර්","නොවැම්බර්","දෙසැම්බර්"],"SHORTMONTHS":["ජන","පෙබ","මාර්තු","අප්‍රේල්","මැයි","ජූනි","ජූලි","අගෝ","සැප්","ඔක්","නොවැ","දෙසැ"],"STANDALONESHORTMONTHS":["ජන","පෙබ","මාර්","අප්‍රේල්","මැයි","ජූනි","ජූලි","අගෝ","සැප්","ඔක්","නොවැ","දෙසැ"],"WEEKDAYS":["ඉරිදා","සඳුදා","අඟහරුවාදා","බදාදා","බ්‍රහස්පතින්දා","සිකුරාදා","සෙනසුරාදා"],"STANDALONEWEEKDAYS":["ඉරිදා","සඳුදා","අඟහරුවාදා","බදාදා","බ්‍රහස්පතින්දා","සිකුරාදා","සෙනසුරාදා"],"SHORTWEEKDAYS":["ඉරිදා","සඳුදා","අඟහ","බදාදා","බ්‍රහස්","සිකු","සෙන"],"STANDALONESHORTWEEKDAYS":["ඉරිදා","සඳුදා","අඟහ","බදාදා","බ්‍රහස්","සිකු","සෙන"],"NARROWWEEKDAYS":["ඉ","ස","අ","බ","බ්‍ර","සි","සෙ"],"STANDALONENARROWWEEKDAYS":["ඉ","ස","අ","බ","බ්‍ර","සි","සෙ"],"SHORTQUARTERS":["කාර්:1","කාර්:2","කාර්:3","කාර්:4"],"QUARTERS":["1 වන කාර්තුව","2 වන කාර්තුව","3 වන කාර්තුව","4 වන කාර්තුව"],"AMPMS":["පෙ.ව.","ප.ව."],"DATEFORMATS":["y MMMM d, EEEE","y MMMM d","y MMM d","y-MM-dd"],"TIMEFORMATS":["a h.mm.ss zzzz","a h.mm.ss z","a h.mm.ss","a h.mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/sk.json b/intl/lib/src/data/dates/symbols/sk.json
new file mode 100644
index 0000000..bf5d72d
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/sk.json
@@ -0,0 +1 @@
+{"NAME":"sk","ERAS":["pred n.l.","n.l."],"ERANAMES":["pred n.l.","n.l."],"NARROWMONTHS":["j","f","m","a","m","j","j","a","s","o","n","d"],"STANDALONENARROWMONTHS":["j","f","m","a","m","j","j","a","s","o","n","d"],"MONTHS":["januára","februára","marca","apríla","mája","júna","júla","augusta","septembra","októbra","novembra","decembra"],"STANDALONEMONTHS":["január","február","marec","apríl","máj","jún","júl","august","september","október","november","december"],"SHORTMONTHS":["jan","feb","mar","apr","máj","jún","júl","aug","sep","okt","nov","dec"],"STANDALONESHORTMONTHS":["jan","feb","mar","apr","máj","jún","júl","aug","sep","okt","nov","dec"],"WEEKDAYS":["nedeľa","pondelok","utorok","streda","štvrtok","piatok","sobota"],"STANDALONEWEEKDAYS":["nedeľa","pondelok","utorok","streda","štvrtok","piatok","sobota"],"SHORTWEEKDAYS":["ne","po","ut","st","št","pi","so"],"STANDALONESHORTWEEKDAYS":["ne","po","ut","st","št","pi","so"],"NARROWWEEKDAYS":["N","P","U","S","Š","P","S"],"STANDALONENARROWWEEKDAYS":["N","P","U","S","Š","P","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1. štvrťrok","2. štvrťrok","3. štvrťrok","4. štvrťrok"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, d. MMMM y","d. MMMM y","d.M.y","d.M.y"],"TIMEFORMATS":["H:mm:ss zzzz","H:mm:ss z","H:mm:ss","H:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/sl.json b/intl/lib/src/data/dates/symbols/sl.json
new file mode 100644
index 0000000..ff41ebb
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/sl.json
@@ -0,0 +1 @@
+{"NAME":"sl","ERAS":["pr. n. št.","po Kr."],"ERANAMES":["pred našim štetjem","naše štetje"],"NARROWMONTHS":["j","f","m","a","m","j","j","a","s","o","n","d"],"STANDALONENARROWMONTHS":["j","f","m","a","m","j","j","a","s","o","n","d"],"MONTHS":["januar","februar","marec","april","maj","junij","julij","avgust","september","oktober","november","december"],"STANDALONEMONTHS":["januar","februar","marec","april","maj","junij","julij","avgust","september","oktober","november","december"],"SHORTMONTHS":["jan.","feb.","mar.","apr.","maj","jun.","jul.","avg.","sep.","okt.","nov.","dec."],"STANDALONESHORTMONTHS":["jan","feb","mar","apr","maj","jun","jul","avg","sep","okt","nov","dec"],"WEEKDAYS":["nedelja","ponedeljek","torek","sreda","četrtek","petek","sobota"],"STANDALONEWEEKDAYS":["nedelja","ponedeljek","torek","sreda","četrtek","petek","sobota"],"SHORTWEEKDAYS":["ned.","pon.","tor.","sre.","čet.","pet.","sob."],"STANDALONESHORTWEEKDAYS":["ned","pon","tor","sre","čet","pet","sob"],"NARROWWEEKDAYS":["n","p","t","s","č","p","s"],"STANDALONENARROWWEEKDAYS":["n","p","t","s","č","p","s"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["1. četrtletje","2. četrtletje","3. četrtletje","4. četrtletje"],"AMPMS":["dop.","pop."],"DATEFORMATS":["EEEE, dd. MMMM y","dd. MMMM y","d. MMM y","d. MM. yy"],"TIMEFORMATS":["HH.mm.ss zzzz","HH.mm.ss z","HH.mm.ss","HH.mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/sq.json b/intl/lib/src/data/dates/symbols/sq.json
new file mode 100644
index 0000000..d35b59f
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/sq.json
@@ -0,0 +1 @@
+{"NAME":"sq","ERAS":["p.e.r.","e.r."],"ERANAMES":["para erës së re","erës së re"],"NARROWMONTHS":["J","S","M","P","M","Q","K","G","S","T","N","D"],"STANDALONENARROWMONTHS":["J","S","M","P","M","Q","K","G","S","T","N","D"],"MONTHS":["janar","shkurt","mars","prill","maj","qershor","korrik","gusht","shtator","tetor","nëntor","dhjetor"],"STANDALONEMONTHS":["janar","shkurt","mars","prill","maj","qershor","korrik","gusht","shtator","tetor","nëntor","dhjetor"],"SHORTMONTHS":["Jan","Shk","Mar","Pri","Maj","Qer","Kor","Gsh","Sht","Tet","Nën","Dhj"],"STANDALONESHORTMONTHS":["Jan","Shk","Mar","Pri","Maj","Qer","Kor","Gsh","Sht","Tet","Nën","Dhj"],"WEEKDAYS":["e diel","e hënë","e martë","e mërkurë","e enjte","e premte","e shtunë"],"STANDALONEWEEKDAYS":["e diel","e hënë","e martë","e mërkurë","e enjte","e premte","e shtunë"],"SHORTWEEKDAYS":["Die","Hën","Mar","Mër","Enj","Pre","Sht"],"STANDALONESHORTWEEKDAYS":["Die","Hën","Mar","Mër","Enj","Pre","Sht"],"NARROWWEEKDAYS":["D","H","M","M","E","P","S"],"STANDALONENARROWWEEKDAYS":["D","H","M","M","E","P","S"],"SHORTQUARTERS":["T1","T2","T3","T4"],"QUARTERS":["tremujori i parë","tremujori i dytë","tremujori i tretë","tremujori i katërt"],"AMPMS":["paradite","pasdite"],"DATEFORMATS":["EEEE, dd MMMM y","dd MMMM y","dd/MM/y","dd/MM/yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} 'në' {0}","{1} 'në' {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/sr.json b/intl/lib/src/data/dates/symbols/sr.json
new file mode 100644
index 0000000..52fdb49
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/sr.json
@@ -0,0 +1 @@
+{"NAME":"sr","ERAS":["п. н. е.","н. е."],"ERANAMES":["Пре нове ере","Нове ере"],"NARROWMONTHS":["ј","ф","м","а","м","ј","ј","а","с","о","н","д"],"STANDALONENARROWMONTHS":["ј","ф","м","а","м","ј","ј","а","с","о","н","д"],"MONTHS":["јануар","фебруар","март","април","мај","јун","јул","август","септембар","октобар","новембар","децембар"],"STANDALONEMONTHS":["јануар","фебруар","март","април","мај","јун","јул","август","септембар","октобар","новембар","децембар"],"SHORTMONTHS":["јан","феб","мар","апр","мај","јун","јул","авг","сеп","окт","нов","дец"],"STANDALONESHORTMONTHS":["јан","феб","мар","апр","мај","јун","јул","авг","сеп","окт","нов","дец"],"WEEKDAYS":["недеља","понедељак","уторак","среда","четвртак","петак","субота"],"STANDALONEWEEKDAYS":["недеља","понедељак","уторак","среда","четвртак","петак","субота"],"SHORTWEEKDAYS":["нед","пон","уто","сре","чет","пет","суб"],"STANDALONESHORTWEEKDAYS":["нед","пон","уто","сре","чет","пет","суб"],"NARROWWEEKDAYS":["н","п","у","с","ч","п","с"],"STANDALONENARROWWEEKDAYS":["н","п","у","с","ч","п","с"],"SHORTQUARTERS":["К1","К2","К3","К4"],"QUARTERS":["Прво тромесечје","Друго тромесечје","Треће тромесечје","Четврто тромесечје"],"AMPMS":["пре подне","поподне"],"DATEFORMATS":["EEEE, dd. MMMM y.","dd. MMMM y.","dd.MM.y.","d.M.yy."],"TIMEFORMATS":["HH.mm.ss zzzz","HH.mm.ss z","HH.mm.ss","HH.mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/sv.json b/intl/lib/src/data/dates/symbols/sv.json
new file mode 100644
index 0000000..6d9a7e7
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/sv.json
@@ -0,0 +1 @@
+{"NAME":"sv","ERAS":["f.Kr.","e.Kr."],"ERANAMES":["före Kristus","efter Kristus"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["januari","februari","mars","april","maj","juni","juli","augusti","september","oktober","november","december"],"STANDALONEMONTHS":["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],"SHORTMONTHS":["jan","feb","mar","apr","maj","jun","jul","aug","sep","okt","nov","dec"],"STANDALONESHORTMONTHS":["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],"WEEKDAYS":["söndag","måndag","tisdag","onsdag","torsdag","fredag","lördag"],"STANDALONEWEEKDAYS":["Söndag","Måndag","Tisdag","Onsdag","Torsdag","Fredag","Lördag"],"SHORTWEEKDAYS":["sön","mån","tis","ons","tors","fre","lör"],"STANDALONESHORTWEEKDAYS":["Sön","Mån","Tis","Ons","Tor","Fre","Lör"],"NARROWWEEKDAYS":["S","M","T","O","T","F","L"],"STANDALONENARROWWEEKDAYS":["S","M","T","O","T","F","L"],"SHORTQUARTERS":["K1","K2","K3","K4"],"QUARTERS":["1:a kvartalet","2:a kvartalet","3:e kvartalet","4:e kvartalet"],"AMPMS":["fm","em"],"DATEFORMATS":["EEEE d MMMM y","d MMMM y","d MMM y","y-MM-dd"],"TIMEFORMATS":["'kl'. HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":3,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/sw.json b/intl/lib/src/data/dates/symbols/sw.json
new file mode 100644
index 0000000..0f6dc5a
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/sw.json
@@ -0,0 +1 @@
+{"NAME":"sw","ERAS":["KK","BK"],"ERANAMES":["Kabla ya Kristo","Baada ya Kristo"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["Januari","Februari","Machi","Aprili","Mei","Juni","Julai","Agosti","Septemba","Oktoba","Novemba","Desemba"],"STANDALONEMONTHS":["Januari","Februari","Machi","Aprili","Mei","Juni","Julai","Agosti","Septemba","Oktoba","Novemba","Desemba"],"SHORTMONTHS":["Jan","Feb","Mac","Apr","Mei","Jun","Jul","Ago","Sep","Okt","Nov","Des"],"STANDALONESHORTMONTHS":["Jan","Feb","Mac","Apr","Mei","Jun","Jul","Ago","Sep","Okt","Nov","Des"],"WEEKDAYS":["Jumapili","Jumatatu","Jumanne","Jumatano","Alhamisi","Ijumaa","Jumamosi"],"STANDALONEWEEKDAYS":["Jumapili","Jumatatu","Jumanne","Jumatano","Alhamisi","Ijumaa","Jumamosi"],"SHORTWEEKDAYS":["Jumapili","Jumatatu","Jumanne","Jumatano","Alhamisi","Ijumaa","Jumamosi"],"STANDALONESHORTWEEKDAYS":["Jumapili","Jumatatu","Jumanne","Jumatano","Alhamisi","Ijumaa","Jumamosi"],"NARROWWEEKDAYS":["2","3","4","5","A","I","1"],"STANDALONENARROWWEEKDAYS":["2","3","4","5","A","I","1"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["Robo 1","Robo 2","Robo 3","Robo 4"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, d MMMM y","d MMMM y","d MMM y","dd/MM/y"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/ta.json b/intl/lib/src/data/dates/symbols/ta.json
new file mode 100644
index 0000000..8990827
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/ta.json
@@ -0,0 +1 @@
+{"NAME":"ta","ERAS":["கி.மு.","கி.பி."],"ERANAMES":["கிறிஸ்துவுக்கு முன்","அனோ டோமினி"],"NARROWMONTHS":["ஜ","பி","மா","ஏ","மே","ஜூ","ஜூ","ஆ","செ","அ","ந","டி"],"STANDALONENARROWMONTHS":["ஜ","பி","மா","ஏ","மே","ஜூ","ஜூ","ஆ","செ","அ","ந","டி"],"MONTHS":["ஜனவரி","பிப்ரவரி","மார்ச்","ஏப்ரல்","மே","ஜூன்","ஜூலை","ஆகஸ்ட்","செப்டம்பர்","அக்டோபர்","நவம்பர்","டிசம்பர்"],"STANDALONEMONTHS":["ஜனவரி","பிப்ரவரி","மார்ச்","ஏப்ரல்","மே","ஜூன்","ஜூலை","ஆகஸ்டு","செப்டம்பர்","அக்டோபர்","நவம்பர்","டிசம்பர்"],"SHORTMONTHS":["ஜன.","பிப்.","மார்.","ஏப்.","மே","ஜூன்","ஜூலை","ஆக.","செப்.","அக்.","நவ.","டிச."],"STANDALONESHORTMONTHS":["ஜன.","பிப்.","மார்.","ஏப்.","மே","ஜூன்","ஜூலை","ஆக.","செப்.","அக்.","நவ.","டிச."],"WEEKDAYS":["ஞாயிறு","திங்கள்","செவ்வாய்","புதன்","வியாழன்","வெள்ளி","சனி"],"STANDALONEWEEKDAYS":["ஞாயிறு","திங்கள்","செவ்வாய்","புதன்","வியாழன்","வெள்ளி","சனி"],"SHORTWEEKDAYS":["ஞா","தி","செ","பு","வி","வெ","ச"],"STANDALONESHORTWEEKDAYS":["ஞா","தி","செ","பு","வி","வெ","ச"],"NARROWWEEKDAYS":["ஞா","தி","செ","பு","வி","வெ","ச"],"STANDALONENARROWWEEKDAYS":["ஞா","தி","செ","பு","வி","வெ","ச"],"SHORTQUARTERS":["காலாண்டு1","காலாண்டு2","காலாண்டு3","காலாண்டு4"],"QUARTERS":["முதல் காலாண்டு","இரண்டாம் காலாண்டு","மூன்றாம் காலாண்டு","நான்காம் காலாண்டு"],"AMPMS":["முற்பகல்","பிற்பகல்"],"DATEFORMATS":["EEEE, d MMMM, y","d MMMM, y","d MMM, y","d-M-yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[6,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/te.json b/intl/lib/src/data/dates/symbols/te.json
new file mode 100644
index 0000000..1714cd5
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/te.json
@@ -0,0 +1 @@
+{"NAME":"te","ERAS":["క్రీపూ","క్రీశ"],"ERANAMES":["ఈసాపూర్వ.","సన్."],"NARROWMONTHS":["జ","ఫి","మా","ఏ","మే","జూ","జు","ఆ","సె","అ","న","డి"],"STANDALONENARROWMONTHS":["జ","ఫి","మా","ఏ","మే","జూ","జు","ఆ","సె","అ","న","డి"],"MONTHS":["జనవరి","ఫిబ్రవరి","మార్చి","ఎప్రిల్","మే","జూన్","జులై","ఆగస్టు","సెప్టెంబర్","అక్టోబర్","నవంబర్","డిసెంబర్"],"STANDALONEMONTHS":["జనవరి","ఫిబ్రవరి","మార్చి","ఎప్రిల్","మే","జూన్","జూలై","ఆగస్టు","సెప్టెంబర్","అక్టోబర్","నవంబర్","డిసెంబర్"],"SHORTMONTHS":["జన","ఫిబ్ర","మార్చి","ఏప్రి","మే","జూన్","జులై","ఆగ","సెప్టెం","అక్టో","నవం","డిసెం"],"STANDALONESHORTMONTHS":["జన","ఫిబ్ర","మార్చి","ఏప్రి","మే","జూన్","జులై","ఆగస్టు","సెప్టెం","అక్టో","నవం","డిసెం"],"WEEKDAYS":["ఆదివారం","సోమవారం","మంగళవారం","బుధవారం","గురువారం","శుక్రవారం","శనివారం"],"STANDALONEWEEKDAYS":["ఆదివారం","సోమవారం","మంగళవారం","బుధవారం","గురువారం","శుక్రవారం","శనివారం"],"SHORTWEEKDAYS":["ఆది","సోమ","మంగళ","బుధ","గురు","శుక్ర","శని"],"STANDALONESHORTWEEKDAYS":["ఆది","సోమ","మంగళ","బుధ","గురు","శుక్ర","శని"],"NARROWWEEKDAYS":["ఆ","సో","మ","బు","గు","శు","శ"],"STANDALONENARROWWEEKDAYS":["ఆ","సో","మ","బు","గు","శు","శ"],"SHORTQUARTERS":["త్రై1","త్రై2","త్రై3","త్రై4"],"QUARTERS":["1వ త్రైమాసం","2వ త్రైమాసం","3వ త్రైమాసం","4వ త్రైమాసం"],"AMPMS":["AM","PM"],"DATEFORMATS":["d MMMM y EEEE","d MMMM y","d MMM y","dd-MM-yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[6,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/th.json b/intl/lib/src/data/dates/symbols/th.json
new file mode 100644
index 0000000..e5e8d17
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/th.json
@@ -0,0 +1 @@
+{"NAME":"th","ERAS":["ปีก่อน ค.ศ.","ค.ศ."],"ERANAMES":["ปีก่อนคริสต์ศักราช","คริสต์ศักราช"],"NARROWMONTHS":["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."],"STANDALONENARROWMONTHS":["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."],"MONTHS":["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"],"STANDALONEMONTHS":["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"],"SHORTMONTHS":["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."],"STANDALONESHORTMONTHS":["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."],"WEEKDAYS":["วันอาทิตย์","วันจันทร์","วันอังคาร","วันพุธ","วันพฤหัสบดี","วันศุกร์","วันเสาร์"],"STANDALONEWEEKDAYS":["วันอาทิตย์","วันจันทร์","วันอังคาร","วันพุธ","วันพฤหัสบดี","วันศุกร์","วันเสาร์"],"SHORTWEEKDAYS":["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],"STANDALONESHORTWEEKDAYS":["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],"NARROWWEEKDAYS":["อา","จ","อ","พ","พฤ","ศ","ส"],"STANDALONENARROWWEEKDAYS":["อา","จ","อ","พ","พฤ","ศ","ส"],"SHORTQUARTERS":["ไตรมาส 1","ไตรมาส 2","ไตรมาส 3","ไตรมาส 4"],"QUARTERS":["ไตรมาส 1","ไตรมาส 2","ไตรมาส 3","ไตรมาส 4"],"AMPMS":["ก่อนเที่ยง","หลังเที่ยง"],"DATEFORMATS":["EEEEที่ d MMMM G y","d MMMM y","d MMM y","d/M/yy"],"TIMEFORMATS":["H นาฬิกา mm นาที ss วินาที zzzz","H นาฬิกา mm นาที ss วินาที z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/tl.json b/intl/lib/src/data/dates/symbols/tl.json
new file mode 100644
index 0000000..ecb3357
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/tl.json
@@ -0,0 +1 @@
+{"NAME":"tl","ERAS":["BC","AD"],"ERANAMES":["BC","AD"],"NARROWMONTHS":["E","P","M","A","M","H","H","A","S","O","N","D"],"STANDALONENARROWMONTHS":["E","P","M","A","M","H","H","A","S","O","N","D"],"MONTHS":["Enero","Pebrero","Marso","Abril","Mayo","Hunyo","Hulyo","Agosto","Setyembre","Oktubre","Nobyembre","Disyembre"],"STANDALONEMONTHS":["Enero","Pebrero","Marso","Abril","Mayo","Hunyo","Hulyo","Agosto","Setyembre","Oktubre","Nobyembre","Disyembre"],"SHORTMONTHS":["Ene","Peb","Mar","Abr","May","Hun","Hul","Ago","Set","Okt","Nob","Dis"],"STANDALONESHORTMONTHS":["Ene","Peb","Mar","Abr","May","Hun","Hul","Ago","Set","Okt","Nob","Dis"],"WEEKDAYS":["Linggo","Lunes","Martes","Miyerkules","Huwebes","Biyernes","Sabado"],"STANDALONEWEEKDAYS":["Linggo","Lunes","Martes","Miyerkules","Huwebes","Biyernes","Sabado"],"SHORTWEEKDAYS":["Lin","Lun","Mar","Miy","Huw","Biy","Sab"],"STANDALONESHORTWEEKDAYS":["Lin","Lun","Mar","Miy","Huw","Biy","Sab"],"NARROWWEEKDAYS":["L","L","M","M","H","B","S"],"STANDALONENARROWWEEKDAYS":["L","L","M","M","H","B","S"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["ika-1 quarter","ika-2 quarter","ika-3 quarter","ika-4 na quarter"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, MMMM d, y","MMMM d, y","MMM d, y","M/d/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} 'ng' {0}","{1} 'ng' {0}","{1}, {0}","{1}, {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/tr.json b/intl/lib/src/data/dates/symbols/tr.json
new file mode 100644
index 0000000..b56c491
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/tr.json
@@ -0,0 +1 @@
+{"NAME":"tr","ERAS":["MÖ","MS"],"ERANAMES":["Milattan Önce","Milattan Sonra"],"NARROWMONTHS":["O","Ş","M","N","M","H","T","A","E","E","K","A"],"STANDALONENARROWMONTHS":["O","Ş","M","N","M","H","T","A","E","E","K","A"],"MONTHS":["Ocak","Şubat","Mart","Nisan","Mayıs","Haziran","Temmuz","Ağustos","Eylül","Ekim","Kasım","Aralık"],"STANDALONEMONTHS":["Ocak","Şubat","Mart","Nisan","Mayıs","Haziran","Temmuz","Ağustos","Eylül","Ekim","Kasım","Aralık"],"SHORTMONTHS":["Oca","Şub","Mar","Nis","May","Haz","Tem","Ağu","Eyl","Eki","Kas","Ara"],"STANDALONESHORTMONTHS":["Oca","Şub","Mar","Nis","May","Haz","Tem","Ağu","Eyl","Eki","Kas","Ara"],"WEEKDAYS":["Pazar","Pazartesi","Salı","Çarşamba","Perşembe","Cuma","Cumartesi"],"STANDALONEWEEKDAYS":["Pazar","Pazartesi","Salı","Çarşamba","Perşembe","Cuma","Cumartesi"],"SHORTWEEKDAYS":["Paz","Pzt","Sal","Çar","Per","Cum","Cmt"],"STANDALONESHORTWEEKDAYS":["Paz","Pzt","Sal","Çar","Per","Cum","Cmt"],"NARROWWEEKDAYS":["P","P","S","Ç","P","C","C"],"STANDALONENARROWWEEKDAYS":["P","P","S","Ç","P","C","C"],"SHORTQUARTERS":["Ç1","Ç2","Ç3","Ç4"],"QUARTERS":["1. çeyrek","2. çeyrek","3. çeyrek","4. çeyrek"],"AMPMS":["ÖÖ","ÖS"],"DATEFORMATS":["d MMMM y EEEE","d MMMM y","d MMM y","d MM y"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/uk.json b/intl/lib/src/data/dates/symbols/uk.json
new file mode 100644
index 0000000..b384964
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/uk.json
@@ -0,0 +1 @@
+{"NAME":"uk","ERAS":["до н.е.","н.е."],"ERANAMES":["до нашої ери","нашої ери"],"NARROWMONTHS":["С","Л","Б","К","Т","Ч","Л","С","В","Ж","Л","Г"],"STANDALONENARROWMONTHS":["С","Л","Б","К","Т","Ч","Л","С","В","Ж","Л","Г"],"MONTHS":["січня","лютого","березня","квітня","травня","червня","липня","серпня","вересня","жовтня","листопада","грудня"],"STANDALONEMONTHS":["Січень","Лютий","Березень","Квітень","Травень","Червень","Липень","Серпень","Вересень","Жовтень","Листопад","Грудень"],"SHORTMONTHS":["січ.","лют.","бер.","квіт.","трав.","черв.","лип.","серп.","вер.","жовт.","лист.","груд."],"STANDALONESHORTMONTHS":["Січ","Лют","Бер","Кві","Тра","Чер","Лип","Сер","Вер","Жов","Лис","Гру"],"WEEKDAYS":["неділя","понеділок","вівторок","середа","четвер","пʼятниця","субота"],"STANDALONEWEEKDAYS":["Неділя","Понеділок","Вівторок","Середа","Четвер","Пʼятниця","Субота"],"SHORTWEEKDAYS":["Нд","Пн","Вт","Ср","Чт","Пт","Сб"],"STANDALONESHORTWEEKDAYS":["Нд","Пн","Вт","Ср","Чт","Пт","Сб"],"NARROWWEEKDAYS":["Н","П","В","С","Ч","П","С"],"STANDALONENARROWWEEKDAYS":["Н","П","В","С","Ч","П","С"],"SHORTQUARTERS":["I кв.","II кв.","III кв.","IV кв."],"QUARTERS":["I квартал","II квартал","III квартал","IV квартал"],"AMPMS":["дп","пп"],"DATEFORMATS":["EEEE, d MMMM y 'р'.","d MMMM y 'р'.","d MMM y","dd.MM.yy"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/ur.json b/intl/lib/src/data/dates/symbols/ur.json
new file mode 100644
index 0000000..9940605
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/ur.json
@@ -0,0 +1 @@
+{"NAME":"ur","ERAS":["ق م","عیسوی سن"],"ERANAMES":["قبل مسیح","عیسوی سن"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["جنوری","فروری","مارچ","اپریل","مئی","جون","جولائی","اگست","ستمبر","اکتوبر","نومبر","دسمبر"],"STANDALONEMONTHS":["جنوری","فروری","مارچ","اپریل","مئی","جون","جولائی","اگست","ستمبر","اکتوبر","نومبر","دسمبر"],"SHORTMONTHS":["جنوری","فروری","مارچ","اپریل","مئی","جون","جولائی","اگست","ستمبر","اکتوبر","نومبر","دسمبر"],"STANDALONESHORTMONTHS":["جنوری","فروری","مارچ","اپریل","مئی","جون","جولائی","اگست","ستمبر","اکتوبر","نومبر","دسمبر"],"WEEKDAYS":["اتوار","سوموار","منگل","بدھ","جمعرات","جمعہ","ہفتہ"],"STANDALONEWEEKDAYS":["اتوار","سوموار","منگل","بدھ","جمعرات","جمعہ","ہفتہ"],"SHORTWEEKDAYS":["اتوار","سوموار","منگل","بدھ","جمعرات","جمعہ","ہفتہ"],"STANDALONESHORTWEEKDAYS":["اتوار","سوموار","منگل","بدھ","جمعرات","جمعہ","ہفتہ"],"NARROWWEEKDAYS":["S","M","T","W","T","F","S"],"STANDALONENARROWWEEKDAYS":["S","M","T","W","T","F","S"],"SHORTQUARTERS":["پہلی سہ ماہی","دوسری سہ ماہی","تیسری سہ ماہی","چوتهی سہ ماہی"],"QUARTERS":["پہلی سہ ماہی","دوسری سہ ماہی","تیسری سہ ماہی","چوتهی سہ ماہی"],"AMPMS":["قبل دوپہر","بعد دوپہر"],"DATEFORMATS":["EEEE، d MMMM، y","d MMMM، y","d MMM، y","d/M/yy"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/uz.json b/intl/lib/src/data/dates/symbols/uz.json
new file mode 100644
index 0000000..f256e50
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/uz.json
@@ -0,0 +1 @@
+{"NAME":"uz","ERAS":["M.A.","E"],"ERANAMES":["M.A.","E"],"NARROWMONTHS":["Y","F","M","A","M","I","I","A","S","O","N","D"],"STANDALONENARROWMONTHS":["Y","F","M","A","M","I","I","A","S","O","N","D"],"MONTHS":["Yanvar","Fevral","Mart","Aprel","May","Iyun","Iyul","Avgust","Sentyabr","Oktyabr","Noyabr","Dekabr"],"STANDALONEMONTHS":["Yanvar","Fevral","Mart","Aprel","May","Iyun","Iyul","Avgust","Sentyabr","Oktyabr","Noyabr","Dekabr"],"SHORTMONTHS":["Yanv","Fev","Mar","Apr","May","Iyun","Iyul","Avg","Sen","Okt","Noya","Dek"],"STANDALONESHORTMONTHS":["Yanv","Fev","Mar","Apr","May","Iyun","Iyul","Avg","Sen","Okt","Noya","Dek"],"WEEKDAYS":["yakshanba","dushanba","seshanba","chorshanba","payshanba","juma","shanba"],"STANDALONEWEEKDAYS":["yakshanba","dushanba","seshanba","chorshanba","payshanba","juma","shanba"],"SHORTWEEKDAYS":["Yaksh","Dush","Sesh","Chor","Pay","Jum","Shan"],"STANDALONESHORTWEEKDAYS":["Yaksh","Dush","Sesh","Chor","Pay","Jum","Shan"],"NARROWWEEKDAYS":["Y","D","S","C","P","J","S"],"STANDALONENARROWWEEKDAYS":["Y","D","S","C","P","J","S"],"SHORTQUARTERS":["1-ch","2-ch","3-ch","4-ch"],"QUARTERS":["1-chorak","2-chorak","3-chorak","4-chorak"],"AMPMS":["AM","PM"],"DATEFORMATS":["EEEE, y MMMM dd","y MMMM d","y MMM d","yy/MM/dd"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/vi.json b/intl/lib/src/data/dates/symbols/vi.json
new file mode 100644
index 0000000..fe815a0
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/vi.json
@@ -0,0 +1 @@
+{"NAME":"vi","ERAS":["tr. CN","sau CN"],"ERANAMES":["tr. CN","sau CN"],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["tháng 1","tháng 2","tháng 3","tháng 4","tháng 5","tháng 6","tháng 7","tháng 8","tháng 9","tháng 10","tháng 11","tháng 12"],"STANDALONEMONTHS":["Tháng 1","Tháng 2","Tháng 3","Tháng 4","Tháng 5","Tháng 6","Tháng 7","Tháng 8","Tháng 9","Tháng 10","Tháng 11","Tháng 12"],"SHORTMONTHS":["thg 1","thg 2","thg 3","thg 4","thg 5","thg 6","thg 7","thg 8","thg 9","thg 10","thg 11","thg 12"],"STANDALONESHORTMONTHS":["Thg 1","Thg 2","Thg 3","Thg 4","Thg 5","Thg 6","Thg 7","Thg 8","Thg 9","Thg 10","Thg 11","Thg 12"],"WEEKDAYS":["Chủ Nhật","Thứ Hai","Thứ Ba","Thứ Tư","Thứ Năm","Thứ Sáu","Thứ Bảy"],"STANDALONEWEEKDAYS":["Chủ Nhật","Thứ Hai","Thứ Ba","Thứ Tư","Thứ Năm","Thứ Sáu","Thứ Bảy"],"SHORTWEEKDAYS":["CN","Th 2","Th 3","Th 4","Th 5","Th 6","Th 7"],"STANDALONESHORTWEEKDAYS":["CN","Th 2","Th 3","Th 4","Th 5","Th 6","Th 7"],"NARROWWEEKDAYS":["CN","T2","T3","T4","T5","T6","T7"],"STANDALONENARROWWEEKDAYS":["CN","T2","T3","T4","T5","T6","T7"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["Quý 1","Quý 2","Quý 3","Quý 4"],"AMPMS":["SA","CH"],"DATEFORMATS":["EEEE, 'ngày' dd MMMM 'năm' y","'Ngày' dd 'tháng' MM 'năm' y","dd-MM-y","dd/MM/y"],"TIMEFORMATS":["HH:mm:ss zzzz","HH:mm:ss z","HH:mm:ss","HH:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":0,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":6,"DATETIMEFORMATS":["{0} {1}","{0} {1}","{0} {1}","{0} {1}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/zh.json b/intl/lib/src/data/dates/symbols/zh.json
new file mode 100644
index 0000000..70890c5
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/zh.json
@@ -0,0 +1 @@
+{"NAME":"zh","ERAS":["公元前","公元"],"ERANAMES":["公元前","公元"],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],"STANDALONEMONTHS":["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],"SHORTMONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"STANDALONESHORTMONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"WEEKDAYS":["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],"STANDALONEWEEKDAYS":["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],"SHORTWEEKDAYS":["周日","周一","周二","周三","周四","周五","周六"],"STANDALONESHORTWEEKDAYS":["周日","周一","周二","周三","周四","周五","周六"],"NARROWWEEKDAYS":["日","一","二","三","四","五","六"],"STANDALONENARROWWEEKDAYS":["日","一","二","三","四","五","六"],"SHORTQUARTERS":["1季度","2季度","3季度","4季度"],"QUARTERS":["第一季度","第二季度","第三季度","第四季度"],"AMPMS":["上午","下午"],"DATEFORMATS":["y年M月d日EEEE","y年M月d日","y年M月d日","yy/M/d"],"TIMEFORMATS":["zzzzah:mm:ss","zah:mm:ss","ah:mm:ss","ah:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/zh_CN.json b/intl/lib/src/data/dates/symbols/zh_CN.json
new file mode 100644
index 0000000..b143e62
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/zh_CN.json
@@ -0,0 +1 @@
+{"NAME":"zh_CN","ERAS":["公元前","公元"],"ERANAMES":["公元前","公元"],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],"STANDALONEMONTHS":["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],"SHORTMONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"STANDALONESHORTMONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"WEEKDAYS":["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],"STANDALONEWEEKDAYS":["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],"SHORTWEEKDAYS":["周日","周一","周二","周三","周四","周五","周六"],"STANDALONESHORTWEEKDAYS":["周日","周一","周二","周三","周四","周五","周六"],"NARROWWEEKDAYS":["日","一","二","三","四","五","六"],"STANDALONENARROWWEEKDAYS":["日","一","二","三","四","五","六"],"SHORTQUARTERS":["1季度","2季度","3季度","4季度"],"QUARTERS":["第一季度","第二季度","第三季度","第四季度"],"AMPMS":["上午","下午"],"DATEFORMATS":["y年M月d日EEEE","y年M月d日","y年M月d日","yy/M/d"],"TIMEFORMATS":["zzzzah:mm:ss","zah:mm:ss","ah:mm:ss","ah:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/zh_HK.json b/intl/lib/src/data/dates/symbols/zh_HK.json
new file mode 100644
index 0000000..1a82ccf
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/zh_HK.json
@@ -0,0 +1 @@
+{"NAME":"zh_HK","ERAS":["西元前","西元"],"ERANAMES":["西元前","西元"],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"STANDALONEMONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"SHORTMONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"STANDALONESHORTMONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"WEEKDAYS":["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],"STANDALONEWEEKDAYS":["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],"SHORTWEEKDAYS":["週日","週一","週二","週三","週四","週五","週六"],"STANDALONESHORTWEEKDAYS":["週日","週一","週二","週三","週四","週五","週六"],"NARROWWEEKDAYS":["日","一","二","三","四","五","六"],"STANDALONENARROWWEEKDAYS":["日","一","二","三","四","五","六"],"SHORTQUARTERS":["1季","2季","3季","4季"],"QUARTERS":["第1季","第2季","第3季","第4季"],"AMPMS":["上午","下午"],"DATEFORMATS":["y年M月d日EEEE","y年M月d日","y年M月d日","d/M/yy"],"TIMEFORMATS":["ah:mm:ss [zzzz]","ah:mm:ss [z]","ah:mm:ss","ah:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1}{0}","{1}{0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/zh_TW.json b/intl/lib/src/data/dates/symbols/zh_TW.json
new file mode 100644
index 0000000..61685b9
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/zh_TW.json
@@ -0,0 +1 @@
+{"NAME":"zh_TW","ERAS":["西元前","西元"],"ERANAMES":["西元前","西元"],"NARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"STANDALONENARROWMONTHS":["1","2","3","4","5","6","7","8","9","10","11","12"],"MONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"STANDALONEMONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"SHORTMONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"STANDALONESHORTMONTHS":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],"WEEKDAYS":["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],"STANDALONEWEEKDAYS":["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],"SHORTWEEKDAYS":["週日","週一","週二","週三","週四","週五","週六"],"STANDALONESHORTWEEKDAYS":["週日","週一","週二","週三","週四","週五","週六"],"NARROWWEEKDAYS":["日","一","二","三","四","五","六"],"STANDALONENARROWWEEKDAYS":["日","一","二","三","四","五","六"],"SHORTQUARTERS":["1季","2季","3季","4季"],"QUARTERS":["第1季","第2季","第3季","第4季"],"AMPMS":["上午","下午"],"DATEFORMATS":["y年M月d日EEEE","y年M月d日","y年M月d日","y/M/d"],"TIMEFORMATS":["zzzzah時mm分ss秒","zah時mm分ss秒","ah:mm:ss","ah:mm"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1}{0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/data/dates/symbols/zu.json b/intl/lib/src/data/dates/symbols/zu.json
new file mode 100644
index 0000000..4e41d5e
--- /dev/null
+++ b/intl/lib/src/data/dates/symbols/zu.json
@@ -0,0 +1 @@
+{"NAME":"zu","ERAS":["BC","AD"],"ERANAMES":["BC","AD"],"NARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"STANDALONENARROWMONTHS":["J","F","M","A","M","J","J","A","S","O","N","D"],"MONTHS":["Januwari","Februwari","Mashi","Apreli","Meyi","Juni","Julayi","Agasti","Septhemba","Okthoba","Novemba","Disemba"],"STANDALONEMONTHS":["uJanuwari","uFebruwari","uMashi","u-Apreli","uMeyi","uJuni","uJulayi","uAgasti","uSepthemba","u-Okthoba","uNovemba","uDisemba"],"SHORTMONTHS":["Jan","Feb","Mas","Apr","Mey","Jun","Jul","Aga","Sep","Okt","Nov","Dis"],"STANDALONESHORTMONTHS":["Jan","Feb","Mas","Apr","Mey","Jun","Jul","Aga","Sep","Okt","Nov","Dis"],"WEEKDAYS":["Sonto","Msombuluko","Lwesibili","Lwesithathu","Lwesine","Lwesihlanu","Mgqibelo"],"STANDALONEWEEKDAYS":["Sonto","Msombuluko","Lwesibili","Lwesithathu","Lwesine","Lwesihlanu","Mgqibelo"],"SHORTWEEKDAYS":["Son","Mso","Bil","Tha","Sin","Hla","Mgq"],"STANDALONESHORTWEEKDAYS":["Son","Mso","Bil","Tha","Sin","Hla","Mgq"],"NARROWWEEKDAYS":["S","M","T","T","S","H","M"],"STANDALONENARROWWEEKDAYS":["S","M","B","T","S","H","M"],"SHORTQUARTERS":["Q1","Q2","Q3","Q4"],"QUARTERS":["ikota engu-1","ikota engu-2","ikota engu-3","ikota engu-4"],"AMPMS":["Ekuseni","Ntambama"],"DATEFORMATS":["EEEE dd MMMM y","d MMMM y","d MMM y","y-MM-dd"],"TIMEFORMATS":["h:mm:ss a zzzz","h:mm:ss a z","h:mm:ss a","h:mm a"],"AVAILABLEFORMATS":null,"FIRSTDAYOFWEEK":6,"WEEKENDRANGE":[5,6],"FIRSTWEEKCUTOFFDAY":5,"DATETIMEFORMATS":["{1} {0}","{1} {0}","{1} {0}","{1} {0}"]}
\ No newline at end of file
diff --git a/intl/lib/src/date_format_internal.dart b/intl/lib/src/date_format_internal.dart
new file mode 100644
index 0000000..ddb7aad
--- /dev/null
+++ b/intl/lib/src/date_format_internal.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * This contains internal implementation details of the date formatting code
+ * which are exposed as public functions because they must be called by other
+ * libraries in order to configure the source for the locale data. We don't want
+ * them exposed as public API functions in the date formatting library, so they
+ * are put in a separate library here. These are for internal use only. User
+ * code should import one of the `date_symbol_data...` libraries and call the
+ * `initializeDateFormatting` method exposed there.
+ */
+
+library date_format_internal;
+
+import 'dart:async';
+import 'intl_helpers.dart';
+import '../date_symbols.dart';
+
+/**
+ * This holds the symbols to be used for date/time formatting, indexed
+ * by locale. Note that it will be set differently during initialization,
+ * depending on what implementation we are using. By default, it is initialized
+ * to an instance of UninitializedLocaleData, so any attempt to use it will
+ * result in an informative error message.
+ */
+var dateTimeSymbols = new UninitializedLocaleData(
+    'initializeDateFormatting(<locale>)', en_USSymbols);
+
+/**
+ * This holds the patterns used for date/time formatting, indexed
+ * by locale. Note that it will be set differently during initialization,
+ * depending on what implementation we are using. By default, it is initialized
+ * to an instance of UninitializedLocaleData, so any attempt to use it will
+ * result in an informative error message.
+ */
+var dateTimePatterns = new UninitializedLocaleData(
+    'initializeDateFormatting(<locale>)', en_USPatterns);
+
+/**
+ * Initialize the symbols dictionary. This should be passed a function that
+ * creates and returns the symbol data. We take a function so that if
+ * initializing the data is an expensive operation it need only be done once,
+ * no matter how many times this method is called.
+ */
+void initializeDateSymbols(Function symbols) {
+  if (dateTimeSymbols is UninitializedLocaleData) {
+    dateTimeSymbols = symbols();
+  }
+}
+
+/**
+ * Initialize the patterns dictionary. This should be passed a function that
+ * creates and returns the pattern data. We take a function so that if
+ * initializing the data is an expensive operation it need only be done once,
+ * no matter how many times this method is called.
+ */
+void initializeDatePatterns(Function patterns) {
+  if (dateTimePatterns is UninitializedLocaleData) {
+    dateTimePatterns = patterns();
+  }
+}
+
+Future initializeIndividualLocaleDateFormatting(Function init) {
+  return init(dateTimeSymbols, dateTimePatterns);
+}
diff --git a/intl/lib/src/file_data_reader.dart b/intl/lib/src/file_data_reader.dart
new file mode 100644
index 0000000..61c42e2
--- /dev/null
+++ b/intl/lib/src/file_data_reader.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * This contains a reader that accesses data from local files, so it can't
+ * be run in the browser.
+ */
+
+library file_data_reader;
+
+import 'dart:async';
+import 'package:path/path.dart';
+import 'dart:io';
+import 'intl_helpers.dart';
+
+class FileDataReader implements LocaleDataReader {
+
+  /// The base path from which we will read data.
+  String path;
+
+  FileDataReader(this.path);
+
+  /// Read the locale data found for [locale] on our [path].
+  Future read(String locale) {
+    var file = new File(join(path, '$locale.json'));
+    return file.readAsString();
+  }
+}
diff --git a/intl/lib/src/http_request_data_reader.dart b/intl/lib/src/http_request_data_reader.dart
new file mode 100644
index 0000000..cbe214d
--- /dev/null
+++ b/intl/lib/src/http_request_data_reader.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * This contains a reader that accesses data using the HttpRequest
+ * facility, and thus works only in the web browser.
+ */
+
+library http_request_data_reader;
+
+import 'dart:async';
+import 'dart:html';
+import 'intl_helpers.dart';
+
+class HttpRequestDataReader implements LocaleDataReader {
+
+  /** The base url from which we read the data. */
+  String url;
+  HttpRequestDataReader(this.url);
+
+  Future read(String locale) {
+    // TODO(alanknight): Remove this once it's not necessary for Chrome.
+    // Without it, the tests will be flaky on Chrome. Issue 11834.
+    var someNumber = new DateTime.now().millisecondsSinceEpoch;
+    var request = new HttpRequest();
+    request.timeout = 5000;
+    return _getString('$url$locale.json?cacheBlocker=$someNumber', request)
+        .then((r) => r.responseText);
+  }
+
+  /// Read a string with the given request. This is a stripped down copy
+  /// of HttpRequest getString, but was the simplest way I could find to
+  /// issue a request with a timeout.
+  Future<HttpRequest> _getString(String url, HttpRequest xhr) {
+    var completer = new Completer<HttpRequest>();
+    xhr.open('GET', url, async: true);
+    xhr.onLoad.listen((e) {
+      // Note: file:// URIs have status of 0.
+      if ((xhr.status >= 200 && xhr.status < 300) ||
+          xhr.status == 0 ||
+          xhr.status == 304) {
+        completer.complete(xhr);
+      } else {
+        completer.completeError(e);
+      }
+    });
+
+    xhr.onError.listen(completer.completeError);
+    xhr.send();
+
+    return completer.future;
+  }
+}
diff --git a/intl/lib/src/icu_parser.dart b/intl/lib/src/icu_parser.dart
new file mode 100644
index 0000000..c07934a
--- /dev/null
+++ b/intl/lib/src/icu_parser.dart
@@ -0,0 +1,108 @@
+// 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.
+
+/**
+ * Contains a parser for ICU format plural/gender/select format for localized
+ * messages. See extract_to_arb.dart and make_hardcoded_translation.dart.
+ */
+library icu_parser;
+
+import 'package:intl/src/intl_message.dart';
+import 'package:petitparser/petitparser.dart';
+
+/**
+ * This defines a grammar for ICU MessageFormat syntax. Usage is
+ *       new IcuParser.message.parse(<string>).value;
+ * The "parse" method will return a Success or Failure object which responds
+ * to "value".
+ */
+class IcuParser {
+  get openCurly => char("{");
+
+  get closeCurly => char("}");
+  get quotedCurly => (string("'{'") | string("'}'")).map((x) => x[1]);
+
+  get icuEscapedText => quotedCurly | twoSingleQuotes;
+  get curly => (openCurly | closeCurly);
+  get notAllowedInIcuText => curly | char("<");
+  get icuText => notAllowedInIcuText.neg();
+  get notAllowedInNormalText => char("{");
+  get normalText => notAllowedInNormalText.neg();
+  get messageText => (icuEscapedText | icuText).plus().map((x) => x.join());
+  get nonIcuMessageText => normalText.plus().map((x) => x.join());
+  get twoSingleQuotes => string("''").map((x) => "'");
+  get number => digit().plus().flatten().trim().map(int.parse);
+  get id => (letter() & (word() | char("_")).star()).flatten();
+  get comma => char(",").trim();
+
+  /**
+   * Given a list of possible keywords, return a rule that accepts any of them.
+   * e.g., given ["male", "female", "other"], accept any of them.
+   */
+  asKeywords(list) => list.map(string).reduce((a, b) => a | b).flatten().trim();
+
+  get pluralKeyword => asKeywords(
+      ["=0", "=1", "=2", "zero", "one", "two", "few", "many", "other"]);
+  get genderKeyword => asKeywords(["female", "male", "other"]);
+
+  var interiorText = undefined();
+
+  get preface => (openCurly & id & comma).map((values) => values[1]);
+
+  get pluralLiteral => string("plural");
+  get pluralClause => (pluralKeyword & openCurly & interiorText & closeCurly)
+      .trim()
+      .map((result) => [result[0], result[2]]);
+  get plural =>
+      preface & pluralLiteral & comma & pluralClause.plus() & closeCurly;
+  get intlPlural =>
+      plural.map((values) => new Plural.from(values.first, values[3], null));
+
+  get selectLiteral => string("select");
+  get genderClause => (genderKeyword & openCurly & interiorText & closeCurly)
+      .trim()
+      .map((result) => [result[0], result[2]]);
+  get gender =>
+      preface & selectLiteral & comma & genderClause.plus() & closeCurly;
+  get intlGender =>
+      gender.map((values) => new Gender.from(values.first, values[3], null));
+  get selectClause =>
+      (id & openCurly & interiorText & closeCurly).map((x) => [x.first, x[2]]);
+  get generalSelect =>
+      preface & selectLiteral & comma & selectClause.plus() & closeCurly;
+  get intlSelect => generalSelect
+      .map((values) => new Select.from(values.first, values[3], null));
+
+  get pluralOrGenderOrSelect => intlPlural | intlGender | intlSelect;
+
+  get contents => pluralOrGenderOrSelect | parameter | messageText;
+  get simpleText => (nonIcuMessageText | parameter | openCurly).plus();
+  get empty => epsilon().map((_) => '');
+
+  get parameter => (openCurly & id & closeCurly)
+      .map((param) => new VariableSubstitution.named(param[1], null));
+
+  /**
+   * The primary entry point for parsing. Accepts a string and produces
+   * a parsed representation of it as a Message.
+   */
+  get message => (pluralOrGenderOrSelect | empty)
+      .map((chunk) => Message.from(chunk, null));
+
+  /**
+   * Represents an ordinary message, i.e. not a plural/gender/select, although
+   * it may have parameters.
+   */
+  get nonIcuMessage =>
+      (simpleText | empty).map((chunk) => Message.from(chunk, null));
+
+  get stuff => (pluralOrGenderOrSelect | empty)
+      .map((chunk) => Message.from(chunk, null));
+
+  IcuParser() {
+    // There is a cycle here, so we need the explicit set to avoid
+    // infinite recursion.
+    interiorText.set(contents.plus() | empty);
+  }
+}
diff --git a/intl/lib/src/intl/bidi_formatter.dart b/intl/lib/src/intl/bidi_formatter.dart
new file mode 100644
index 0000000..9f9495c
--- /dev/null
+++ b/intl/lib/src/intl/bidi_formatter.dart
@@ -0,0 +1,182 @@
+// Copyright (c) 2012, 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.
+
+part of intl;
+
+/**
+ * Bidi stands for Bi-directional text.
+ * According to [Wikipedia](http://en.wikipedia.org/wiki/Bi-directional_text):
+ * Bi-directional text is text containing text in both text directionalities,
+ * both right-to-left (RTL) and left-to-right (LTR). It generally involves text
+ * containing different types of alphabets, but may also refer to boustrophedon,
+ * which is changing text directionality in each row.
+ *
+ * Utility class for formatting display text in a potentially
+ * opposite-directionality context without garbling layout issues.
+ * Mostly a very "slimmed-down" and dart-ified port of the Closure Birectional
+ * formatting libary. If there is a utility in the Closure library (or ICU, or
+ * elsewhere) that you would like this formatter to make available, please
+ * contact the Dart team.
+ *
+ * Provides the following functionality:
+ *
+ * 1. *BiDi Wrapping*
+ * When text in one language is mixed into a document in another, opposite-
+ * directionality language, e.g. when an English business name is embedded in a
+ * Hebrew web page, both the inserted string and the text following it may be
+ * displayed incorrectly unless the inserted string is explicitly separated
+ * from the surrounding text in a "wrapper" that declares its directionality at
+ * the start and then resets it back at the end. This wrapping can be done in
+ * HTML mark-up (e.g. a 'span dir=rtl' tag) or - only in contexts where mark-up
+ * can not be used - in Unicode BiDi formatting codes (LRE|RLE and PDF).
+ * Providing such wrapping services is the basic purpose of the BiDi formatter.
+ *
+ * 2. *Directionality estimation*
+ * How does one know whether a string about to be inserted into surrounding
+ * text has the same directionality? Well, in many cases, one knows that this
+ * must be the case when writing the code doing the insertion, e.g. when a
+ * localized message is inserted into a localized page. In such cases there is
+ * no need to involve the BiDi formatter at all. In the remaining cases, e.g.
+ * when the string is user-entered or comes from a database, the language of
+ * the string (and thus its directionality) is not known a priori, and must be
+ * estimated at run-time. The BiDi formatter does this automatically.
+ *
+ * 3. *Escaping*
+ * When wrapping plain text - i.e. text that is not already HTML or HTML-
+ * escaped - in HTML mark-up, the text must first be HTML-escaped to prevent XSS
+ * attacks and other nasty business. This of course is always true, but the
+ * escaping cannot be done after the string has already been wrapped in
+ * mark-up, so the BiDi formatter also serves as a last chance and includes
+ * escaping services.
+ *
+ * Thus, in a single call, the formatter will escape the input string as
+ * specified, determine its directionality, and wrap it as necessary. It is
+ * then up to the caller to insert the return value in the output.
+ */
+
+class BidiFormatter {
+
+  /** The direction of the surrounding text (the context). */
+  TextDirection contextDirection;
+
+  /**
+   * Indicates if we should always wrap the formatted text in a &lt;span&lt;,.
+   */
+  bool _alwaysSpan;
+
+  /**
+   * Create a formatting object with a direction. If [alwaysSpan] is true we
+   * should always use a `span` tag, even when the input directionality is
+   * neutral or matches the context, so that the DOM structure of the output
+   * does not depend on the combination of directionalities.
+   */
+  BidiFormatter.LTR([alwaysSpan = false])
+      : contextDirection = TextDirection.LTR,
+        _alwaysSpan = alwaysSpan;
+  BidiFormatter.RTL([alwaysSpan = false])
+      : contextDirection = TextDirection.RTL,
+        _alwaysSpan = alwaysSpan;
+  BidiFormatter.UNKNOWN([alwaysSpan = false])
+      : contextDirection = TextDirection.UNKNOWN,
+        _alwaysSpan = alwaysSpan;
+
+  /** Is true if the known context direction for this formatter is RTL. */
+  bool get isRTL => contextDirection == TextDirection.RTL;
+
+  /**
+   * Formats a string of a given (or estimated, if not provided)
+   * [direction] for use in HTML output of the context directionality, so
+   * an opposite-directionality string is neither garbled nor garbles what
+   * follows it.
+   * If the input string's directionality doesn't match the context
+   * directionality, we wrap it with a `span` tag and add a `dir` attribute
+   * (either "dir=rtl" or "dir=ltr").
+   * If alwaysSpan was true when constructing the formatter, the input is always
+   * wrapped with `span` tag, skipping the dir attribute when it's not needed.
+   *
+   * If [resetDir] is true and the overall directionality or the exit
+   * directionality of [text] is opposite to the context directionality,
+   * a trailing unicode BiDi mark matching the context directionality is
+   * appended (LRM or RLM). If [isHtml] is false, we HTML-escape the [text].
+   */
+  String wrapWithSpan(String text,
+      {bool isHtml: false, bool resetDir: true, TextDirection direction}) {
+    if (direction == null) direction = estimateDirection(text, isHtml: isHtml);
+    var result;
+    if (!isHtml) text = HTML_ESCAPE.convert(text);
+    var directionChange = contextDirection.isDirectionChange(direction);
+    if (_alwaysSpan || directionChange) {
+      var spanDirection = '';
+      if (directionChange) {
+        spanDirection = ' dir=${direction.spanText}';
+      }
+      result = '<span$spanDirection>$text</span>';
+    } else {
+      result = text;
+    }
+    return result + (resetDir ? _resetDir(text, direction, isHtml) : '');
+  }
+
+  /**
+   * Format [text] of a known (if specified) or estimated [direction] for use
+   * in *plain-text* output of the context directionality, so an
+   * opposite-directionality text is neither garbled nor garbles what follows
+   * it. Unlike wrapWithSpan, this makes use of unicode BiDi formatting
+   * characters instead of spans for wrapping. The returned string would be
+   * RLE+text+PDF for RTL text, or LRE+text+PDF for LTR text.
+   *
+   * If [resetDir] is true, and if the overall directionality or the exit
+   * directionality of text are opposite to the context directionality,
+   * a trailing unicode BiDi mark matching the context directionality is
+   * appended (LRM or RLM).
+   *
+   * In HTML, the *only* valid use of this function is inside of elements that
+   * do not allow markup, e.g. an 'option' tag.
+   * This function does *not* do HTML-escaping regardless of the value of
+   * [isHtml]. [isHtml] is used to designate if the text contains HTML (escaped
+   * or unescaped).
+   */
+  String wrapWithUnicode(String text,
+      {bool isHtml: false, bool resetDir: true, TextDirection direction}) {
+    if (direction == null) direction = estimateDirection(text, isHtml: isHtml);
+    var result = text;
+    if (contextDirection.isDirectionChange(direction)) {
+      var marker = direction == TextDirection.RTL ? Bidi.RLE : Bidi.LRE;
+      result = "${marker}$text${Bidi.PDF}";
+    }
+    return result + (resetDir ? _resetDir(text, direction, isHtml) : '');
+  }
+
+  /**
+   * Estimates the directionality of [text] using the best known
+   * general-purpose method (using relative word counts). A
+   * TextDirection.UNKNOWN return value indicates completely neutral input.
+   * [isHtml] is true if [text] HTML or HTML-escaped.
+   */
+  TextDirection estimateDirection(String text, {bool isHtml: false}) {
+    return Bidi.estimateDirectionOfText(text, isHtml: isHtml); //TODO~!!!
+  }
+
+  /**
+   * Returns a unicode BiDi mark matching the surrounding context's [direction]
+   * (not necessarily the direction of [text]). The function returns an LRM or
+   * RLM if the overall directionality or the exit directionality of [text] is
+   * opposite the context directionality. Otherwise
+   * return the empty string. [isHtml] is true if [text] is HTML or
+   * HTML-escaped.
+   */
+  String _resetDir(String text, TextDirection direction, bool isHtml) {
+    // endsWithRtl and endsWithLtr are called only if needed (short-circuit).
+    if ((contextDirection == TextDirection.LTR &&
+            (direction == TextDirection.RTL ||
+                Bidi.endsWithRtl(text, isHtml))) ||
+        (contextDirection == TextDirection.RTL &&
+            (direction == TextDirection.LTR ||
+                Bidi.endsWithLtr(text, isHtml)))) {
+      return contextDirection == TextDirection.LTR ? Bidi.LRM : Bidi.RLM;
+    } else {
+      return '';
+    }
+  }
+}
diff --git a/intl/lib/src/intl/bidi_utils.dart b/intl/lib/src/intl/bidi_utils.dart
new file mode 100644
index 0000000..378e0cc
--- /dev/null
+++ b/intl/lib/src/intl/bidi_utils.dart
@@ -0,0 +1,381 @@
+// Copyright (c) 2012, 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.
+
+part of intl;
+
+/**
+ * Bidi stands for Bi-directional text.
+ * According to http://en.wikipedia.org/wiki/Bi-directional_text:
+ * Bi-directional text is text containing text in both text directionalities,
+ * both right-to-left (RTL) and left-to-right (LTR). It generally involves text
+ * containing different types of alphabets, but may also refer to boustrophedon,
+ * which is changing text directionality in each row.
+ *
+ * This file provides some utility classes for determining directionality of
+ * text, switching CSS layout from LTR to RTL, and other normalizing utilities
+ * needed when switching between RTL and LTR formatting.
+ *
+ * It defines the TextDirection class which is used to represent directionality
+ * of text,
+ * In most cases, it is preferable to use bidi_formatter.dart, which provides
+ * bidi functionality in the given directional context, instead of using
+ * bidi_utils.dart directly.
+ */
+class TextDirection {
+  static const LTR = const TextDirection._('LTR', 'ltr');
+  static const RTL = const TextDirection._('RTL', 'rtl');
+  // If the directionality of the text cannot be determined and we are not using
+  // the context direction (or if the context direction is unknown), then the
+  // text falls back on the more common ltr direction.
+  static const UNKNOWN = const TextDirection._('UNKNOWN', 'ltr');
+
+  /**
+   * Textual representation of the directionality constant. One of
+   * 'LTR', 'RTL', or 'UNKNOWN'.
+   */
+  final String value;
+
+  /** Textual representation of the directionality when used in span tag. */
+  final String spanText;
+
+  const TextDirection._(this.value, this.spanText);
+
+  /**
+   * Returns true if [otherDirection] is known to be different from this
+   * direction.
+   */
+  bool isDirectionChange(TextDirection otherDirection) =>
+      otherDirection != TextDirection.UNKNOWN && this != otherDirection;
+}
+
+/**
+ * This provides utility methods for working with bidirectional text. All
+ * of the methods are static, and are organized into a class primarily to
+ * group them together for documentation and discoverability.
+ */
+class Bidi {
+
+  /** Unicode "Left-To-Right Embedding" (LRE) character. */
+  static const LRE = '\u202A';
+
+  /** Unicode "Right-To-Left Embedding" (RLE) character. */
+  static const RLE = '\u202B';
+
+  /** Unicode "Pop Directional Formatting" (PDF) character. */
+  static const PDF = '\u202C';
+
+  /** Unicode "Left-To-Right Mark" (LRM) character. */
+  static const LRM = '\u200E';
+
+  /** Unicode "Right-To-Left Mark" (RLM) character. */
+  static const RLM = '\u200F';
+
+  /** Constant to define the threshold of RTL directionality. */
+  static num _RTL_DETECTION_THRESHOLD = 0.40;
+
+  /**
+   * Practical patterns to identify strong LTR and RTL characters, respectively.
+   * These patterns are not completely correct according to the Unicode
+   * standard. They are simplified for performance and small code size.
+   */
+  static const String _LTR_CHARS =
+      r'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590'
+      r'\u0800-\u1FFF\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF';
+  static const String _RTL_CHARS = r'\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC';
+
+  /**
+   * Returns the input [text] with spaces instead of HTML tags or HTML escapes,
+   * which is helpful for text directionality estimation.
+   * Note: This function should not be used in other contexts.
+   * It does not deal well with many things: comments, script,
+   * elements, style elements, dir attribute,`>` in quoted attribute values,
+   * etc. But it does handle well enough the most common use cases.
+   * Since the worst that can happen as a result of these shortcomings is that
+   * the wrong directionality will be estimated, we have not invested in
+   * improving this.
+   */
+  static String stripHtmlIfNeeded(String text) {
+    // The regular expression is simplified for an HTML tag (opening or
+    // closing) or an HTML escape. We might want to skip over such expressions
+    // when estimating the text directionality.
+    return text.replaceAll(new RegExp(r'<[^>]*>|&[^;]+;'), ' ');
+  }
+
+  /**
+   * Determines if the first character in [text] with strong directionality is
+   * LTR. If [isHtml] is true, the text is HTML or HTML-escaped.
+   */
+  static bool startsWithLtr(String text, [isHtml = false]) {
+    return new RegExp('^[^$_RTL_CHARS]*[$_LTR_CHARS]')
+        .hasMatch(isHtml ? stripHtmlIfNeeded(text) : text);
+  }
+
+  /**
+   * Determines if the first character in [text] with strong directionality is
+   * RTL. If [isHtml] is true, the text is HTML or HTML-escaped.
+   */
+  static bool startsWithRtl(String text, [isHtml = false]) {
+    return new RegExp('^[^$_LTR_CHARS]*[$_RTL_CHARS]')
+        .hasMatch(isHtml ? stripHtmlIfNeeded(text) : text);
+  }
+
+  /**
+   * Determines if the exit directionality (ie, the last strongly-directional
+   * character in [text] is LTR. If [isHtml] is true, the text is HTML or
+   * HTML-escaped.
+   */
+  static bool endsWithLtr(String text, [isHtml = false]) {
+    return new RegExp('[$_LTR_CHARS][^$_RTL_CHARS]*\$')
+        .hasMatch(isHtml ? stripHtmlIfNeeded(text) : text);
+  }
+
+  /**
+   * Determines if the exit directionality (ie, the last strongly-directional
+   * character in [text] is RTL. If [isHtml] is true, the text is HTML or
+   * HTML-escaped.
+   */
+  static bool endsWithRtl(String text, [isHtml = false]) {
+    return new RegExp('[$_RTL_CHARS][^$_LTR_CHARS]*\$')
+        .hasMatch(isHtml ? stripHtmlIfNeeded(text) : text);
+  }
+
+  /**
+   * Determines if the given [text] has any LTR characters in it.
+   * If [isHtml] is true, the text is HTML or HTML-escaped.
+   */
+  static bool hasAnyLtr(String text, [isHtml = false]) {
+    return new RegExp(r'[' '$_LTR_CHARS' r']')
+        .hasMatch(isHtml ? stripHtmlIfNeeded(text) : text);
+  }
+
+  /**
+   * Determines if the given [text] has any RTL characters in it.
+   * If [isHtml] is true, the text is HTML or HTML-escaped.
+   */
+  static bool hasAnyRtl(String text, [isHtml = false]) {
+    return new RegExp(r'[' '$_RTL_CHARS' r']')
+        .hasMatch(isHtml ? stripHtmlIfNeeded(text) : text);
+  }
+
+  /**
+   * Check if a BCP 47 / III [languageString] indicates an RTL language.
+   *
+   * i.e. either:
+   * - a language code explicitly specifying one of the right-to-left scripts,
+   *   e.g. "az-Arab", or
+   * - a language code specifying one of the languages normally written in a
+   *   right-to-left script, e.g. "fa" (Farsi), except ones explicitly
+   *   specifying Latin or Cyrillic script (which are the usual LTR
+   *   alternatives).
+   *
+   * The list of right-to-left scripts appears in the 100-199 range in
+   * http://www.unicode.org/iso15924/iso15924-num.html, of which Arabic and
+   * Hebrew are by far the most widely used. We also recognize Thaana, N'Ko, and
+   * Tifinagh, which also have significant modern usage. The rest (Syriac,
+   * Samaritan, Mandaic, etc.) seem to have extremely limited or no modern usage
+   * and are not recognized.
+   * The languages usually written in a right-to-left script are taken as those
+   * with Suppress-Script: Hebr|Arab|Thaa|Nkoo|Tfng  in
+   * http://www.iana.org/assignments/language-subtag-registry,
+   * as well as Sindhi (sd) and Uyghur (ug).
+   * The presence of other subtags of the language code, e.g. regions like EG
+   * (Egypt), is ignored.
+   */
+  static bool isRtlLanguage(String languageString) {
+    return new RegExp(r'^(ar|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|.*[-_]'
+        r'(Arab|Hebr|Thaa|Nkoo|Tfng))(?!.*[-_](Latn|Cyrl)($|-|_))'
+        r'($|-|_)', caseSensitive: false).hasMatch(languageString);
+  }
+
+  /**
+   * Enforce the [html] snippet in RTL directionality regardless of overall
+   * context. If the html piece was enclosed by a tag, the direction will be
+   * applied to existing tag, otherwise a span tag will be added as wrapper.
+   * For this reason, if html snippet start with with tag, this tag must enclose
+   * the whole piece. If the tag already has a direction specified, this new one
+   * will override existing one in behavior (should work on Chrome, FF, and IE
+   * since this was ported directly from the Closure version).
+   */
+  static String enforceRtlInHtml(String html) =>
+      _enforceInHtmlHelper(html, 'rtl');
+
+  /**
+   * Enforce RTL on both end of the given [text] using unicode BiDi formatting
+   * characters RLE and PDF.
+   */
+  static String enforceRtlInText(String text) => '$RLE$text$PDF';
+
+  /**
+   * Enforce the [html] snippet in LTR directionality regardless of overall
+   * context. If the html piece was enclosed by a tag, the direction will be
+   * applied to existing tag, otherwise a span tag will be added as wrapper.
+   * For this reason, if html snippet start with with tag, this tag must enclose
+   * the whole piece. If the tag already has a direction specified, this new one
+   * will override existing one in behavior (tested on FF and IE).
+   */
+  static String enforceLtrInHtml(String html) =>
+      _enforceInHtmlHelper(html, 'ltr');
+
+  /**
+   * Enforce LTR on both end of the given [text] using unicode BiDi formatting
+   * characters LRE and PDF.
+   */
+  static String enforceLtrInText(String text) => '$LRE$text$PDF';
+
+  /**
+   * Enforce the [html] snippet in the desired [direction] regardless of overall
+   * context. If the html piece was enclosed by a tag, the direction will be
+   * applied to existing tag, otherwise a span tag will be added as wrapper.
+   * For this reason, if html snippet start with with tag, this tag must enclose
+   * the whole piece. If the tag already has a direction specified, this new one
+   * will override existing one in behavior (tested on FF and IE).
+   */
+  static String _enforceInHtmlHelper(String html, String direction) {
+    if (html.startsWith('<')) {
+      StringBuffer buffer = new StringBuffer();
+      var startIndex = 0;
+      Match match = new RegExp('<\\w+').firstMatch(html);
+      if (match != null) {
+        buffer
+          ..write(html.substring(startIndex, match.end))
+          ..write(' dir=$direction');
+        startIndex = match.end;
+      }
+      return (buffer..write(html.substring(startIndex))).toString();
+    }
+    // '\n' is important for FF so that it won't incorrectly merge span groups.
+    return '\n<span dir=$direction>$html</span>';
+  }
+
+  /**
+   * Apply bracket guard to [str] using html span tag. This is to address the
+   * problem of messy bracket display that frequently happens in RTL layout.
+   * If [isRtlContext] is true, then we explicitly want to wrap in a span of RTL
+   * directionality, regardless of the estimated directionality.
+   */
+  static String guardBracketInHtml(String str, [bool isRtlContext]) {
+    var useRtl = isRtlContext == null ? hasAnyRtl(str) : isRtlContext;
+    RegExp matchingBrackets =
+        new RegExp(r'(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(&lt;.*?(&gt;)+)');
+    return _guardBracketHelper(str, matchingBrackets,
+        '<span dir=${useRtl? "rtl" : "ltr"}>', '</span>');
+  }
+
+  /**
+   * Apply bracket guard to [str] using LRM and RLM. This is to address the
+   * problem of messy bracket display that frequently happens in RTL layout.
+   * This version works for both plain text and html, but in some cases is not
+   * as good as guardBracketInHtml.
+   * If [isRtlContext] is true, then we explicitly want to wrap in a span of RTL
+   * directionality, regardless of the estimated directionality.
+   */
+  static String guardBracketInText(String str, [bool isRtlContext]) {
+    var useRtl = isRtlContext == null ? hasAnyRtl(str) : isRtlContext;
+    var mark = useRtl ? RLM : LRM;
+    return _guardBracketHelper(str,
+        new RegExp(r'(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(<.*?>+)'), mark, mark);
+  }
+
+  /**
+   * (Mostly) reimplements the $& functionality of "replace" in JavaScript.
+   * Given a [str] and the [regexp] to match with, optionally supply a string to
+   * be inserted [before] the match and/or [after]. For example,
+   * `_guardBracketHelper('firetruck', new RegExp('truck'), 'hydrant', '!')`
+   * would return 'firehydrant!'.
+   */
+  // TODO(efortuna): Get rid of this once this is implemented in Dart.
+  // See Issue 2979.
+  static String _guardBracketHelper(String str, RegExp regexp,
+      [String before, String after]) {
+    var buffer = new StringBuffer();
+    var startIndex = 0;
+    regexp.allMatches(str).forEach((match) {
+      buffer
+        ..write(str.substring(startIndex, match.start))
+        ..write(before)
+        ..write(str.substring(match.start, match.end))
+        ..write(after);
+      startIndex = match.end;
+    });
+    return (buffer..write(str.substring(startIndex))).toString();
+  }
+
+  /**
+   * Estimates the directionality of [text] using the best known
+   * general-purpose method (using relative word counts). A
+   * TextDirection.UNKNOWN return value indicates completely neutral input.
+   * [isHtml] is true if [text] HTML or HTML-escaped.
+   *
+   * If the number of RTL words is above a certain percentage of the total
+   * number of strongly directional words, returns RTL.
+   * Otherwise, if any words are strongly or weakly LTR, returns LTR.
+   * Otherwise, returns UNKNOWN, which is used to mean `neutral`.
+   * Numbers and URLs are counted as weakly LTR.
+   */
+  static TextDirection estimateDirectionOfText(String text,
+      {bool isHtml: false}) {
+    text = isHtml ? stripHtmlIfNeeded(text) : text;
+    var rtlCount = 0;
+    var total = 0;
+    var hasWeaklyLtr = false;
+    // Split a string into 'words' for directionality estimation based on
+    // relative word counts.
+    for (String token in text.split(new RegExp(r'\s+'))) {
+      if (startsWithRtl(token)) {
+        rtlCount++;
+        total++;
+      } else if (new RegExp(r'^http://').hasMatch(token)) {
+        // Checked if token looks like something that must always be LTR even in
+        // RTL text, such as a URL.
+        hasWeaklyLtr = true;
+      } else if (hasAnyLtr(token)) {
+        total++;
+      } else if (new RegExp(r'\d').hasMatch(token)) {
+        // Checked if token contains any numerals.
+        hasWeaklyLtr = true;
+      }
+    }
+
+    if (total == 0) {
+      return hasWeaklyLtr ? TextDirection.LTR : TextDirection.UNKNOWN;
+    } else if (rtlCount > _RTL_DETECTION_THRESHOLD * total) {
+      return TextDirection.RTL;
+    } else {
+      return TextDirection.LTR;
+    }
+  }
+
+  /**
+   * Replace the double and single quote directly after a Hebrew character in
+   * [str] with GERESH and GERSHAYIM. This is most likely the user's intention.
+   */
+  static String normalizeHebrewQuote(String str) {
+    StringBuffer buf = new StringBuffer();
+    if (str.length > 0) {
+      buf.write(str.substring(0, 1));
+    }
+    // Start at 1 because we're looking for the patterns [\u0591-\u05f2])" or
+    // [\u0591-\u05f2]'.
+    for (int i = 1; i < str.length; i++) {
+      if (str.substring(i, i + 1) == '"' &&
+          new RegExp('[\u0591-\u05f2]').hasMatch(str.substring(i - 1, i))) {
+        buf.write('\u05f4');
+      } else if (str.substring(i, i + 1) == "'" &&
+          new RegExp('[\u0591-\u05f2]').hasMatch(str.substring(i - 1, i))) {
+        buf.write('\u05f3');
+      } else {
+        buf.write(str.substring(i, i + 1));
+      }
+    }
+    return buf.toString();
+  }
+
+  /**
+   * Check the estimated directionality of [str], return true if the piece of
+   * text should be laid out in RTL direction. If [isHtml] is true, the string
+   * is HTML or HTML-escaped.
+   */
+  static bool detectRtlDirectionality(String str, {bool isHtml: false}) =>
+      estimateDirectionOfText(str, isHtml: isHtml) == TextDirection.RTL;
+}
diff --git a/intl/lib/src/intl/date_format.dart b/intl/lib/src/intl/date_format.dart
new file mode 100644
index 0000000..8f023fc
--- /dev/null
+++ b/intl/lib/src/intl/date_format.dart
@@ -0,0 +1,682 @@
+// Copyright (c) 2012, 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.
+
+part of intl;
+
+// TODO(efortuna): Customized pattern system -- suggested by i18n needs
+// feedback on appropriateness.
+/**
+ * DateFormat is for formatting and parsing dates in a locale-sensitive
+ * manner.
+ * It allows the user to choose from a set of standard date time formats as well
+ * as specify a customized pattern under certain locales. Date elements that
+ * vary across locales include month name, week name, field order, etc.
+ * We also allow the user to use any customized pattern to parse or format
+ * date-time strings under certain locales. Date elements that vary across
+ * locales include month name, weekname, field, order, etc.
+ *
+ * Formatting dates in the default "en_US" format does not require any
+ * initialization. e.g.
+ *       print(new DateFormat.yMMMd().format(new Date.now()));
+ *
+ * But for other locales, the formatting data for the locale must be
+ * obtained. This can currently be done
+ * in one of three ways, determined by which library you import. In all cases,
+ * the "initializeDateFormatting" method must be called and will return a future
+ * that is complete once the locale data is available. The result of the future
+ * isn't important, but the data for that locale is available to the date
+ * formatting and parsing once it completes.
+ *
+ * The easiest option is that the data may be available locally, imported in a
+ * library that contains data for all the locales.
+ *       import 'package:intl/date_symbol_data_local.dart';
+ *       initializeDateFormatting("fr_FR", null).then((_) => runMyCode());
+ *
+ * If we are running outside of a browser, we may want to read the data
+ * from files in the file system.
+ *       import 'package:intl/date_symbol_data_file.dart';
+ *       initializeDateFormatting("de_DE", null).then((_) => runMyCode());
+ *
+ * If we are running in a browser, we may want to read the data from the
+ * server using the XmlHttpRequest mechanism.
+ *       import 'package:intl/date_symbol_data_http_request.dart';
+ *       initializeDateFormatting("pt_BR", null).then((_) => runMyCode());
+ *
+ * The code in example/basic/basic_example.dart shows a full example of
+ * using this mechanism.
+ *
+ * Once we have the locale data, we need to specify the particular format.
+ * This library uses the ICU/JDK date/time pattern specification both for
+ * complete format specifications and also the abbreviated "skeleton" form
+ * which can also adapt to different locales and is preferred where available.
+ *
+ * Skeletons: These can be specified either as the ICU constant name or as the
+ * skeleton to which it resolves. The supported set of skeletons is as follows
+ *      ICU Name                   Skeleton
+ *      --------                   --------
+ *      DAY                          d
+ *      ABBR_WEEKDAY                 E
+ *      WEEKDAY                      EEEE
+ *      ABBR_STANDALONE_MONTH        LLL
+ *      STANDALONE_MONTH             LLLL
+ *      NUM_MONTH                    M
+ *      NUM_MONTH_DAY                Md
+ *      NUM_MONTH_WEEKDAY_DAY        MEd
+ *      ABBR_MONTH                   MMM
+ *      ABBR_MONTH_DAY               MMMd
+ *      ABBR_MONTH_WEEKDAY_DAY       MMMEd
+ *      MONTH                        MMMM
+ *      MONTH_DAY                    MMMMd
+ *      MONTH_WEEKDAY_DAY            MMMMEEEEd
+ *      ABBR_QUARTER                 QQQ
+ *      QUARTER                      QQQQ
+ *      YEAR                         y
+ *      YEAR_NUM_MONTH               yM
+ *      YEAR_NUM_MONTH_DAY           yMd
+ *      YEAR_NUM_MONTH_WEEKDAY_DAY   yMEd
+ *      YEAR_ABBR_MONTH              yMMM
+ *      YEAR_ABBR_MONTH_DAY          yMMMd
+ *      YEAR_ABBR_MONTH_WEEKDAY_DAY  yMMMEd
+ *      YEAR_MONTH                   yMMMM
+ *      YEAR_MONTH_DAY               yMMMMd
+ *      YEAR_MONTH_WEEKDAY_DAY       yMMMMEEEEd
+ *      YEAR_ABBR_QUARTER            yQQQ
+ *      YEAR_QUARTER                 yQQQQ
+ *      HOUR24                       H
+ *      HOUR24_MINUTE                Hm
+ *      HOUR24_MINUTE_SECOND         Hms
+ *      HOUR                         j
+ *      HOUR_MINUTE                  jm
+ *      HOUR_MINUTE_SECOND           jms
+ *      HOUR_MINUTE_GENERIC_TZ       jmv
+ *      HOUR_MINUTE_TZ               jmz
+ *      HOUR_GENERIC_TZ              jv
+ *      HOUR_TZ                      jz
+ *      MINUTE                       m
+ *      MINUTE_SECOND                ms
+ *      SECOND                       s
+ *
+ * Examples Using the US Locale:
+ *
+ *      Pattern                           Result
+ *      ----------------                  -------
+ *      new DateFormat.yMd()             -> 7/10/1996
+ *      new DateFormat("yMd")            -> 7/10/1996
+ *      new DateFormat.yMMMMd("en_US")   -> July 10, 1996
+ *      new DateFormat("Hm", "en_US")    -> 12:08 PM
+ *      new DateFormat.yMd().add_Hm()    -> 7/10/1996 12:08 PM
+ *
+ * Explicit Pattern Syntax: Formats can also be specified with a pattern string.
+ * The skeleton forms will resolve to explicit patterns of this form, but will
+ * also adapt to different patterns in different locales.
+ * The following characters are reserved:
+ *
+ *     Symbol   Meaning                Presentation        Example
+ *     ------   -------                ------------        -------
+ *     G        era designator         (Text)              AD
+ *     y        year                   (Number)            1996
+ *     M        month in year          (Text & Number)     July & 07
+ *     L        standalone month       (Text & Number)     July & 07
+ *     d        day in month           (Number)            10
+ *     c        standalone day         (Number)            10
+ *     h        hour in am/pm (1~12)   (Number)            12
+ *     H        hour in day (0~23)     (Number)            0
+ *     m        minute in hour         (Number)            30
+ *     s        second in minute       (Number)            55
+ *     S        fractional second      (Number)            978
+ *     E        day of week            (Text)              Tuesday
+ *     D        day in year            (Number)            189
+ *     a        am/pm marker           (Text)              PM
+ *     k        hour in day (1~24)     (Number)            24
+ *     K        hour in am/pm (0~11)   (Number)            0
+ *     z        time zone              (Text)              Pacific Standard Time
+ *     Z        time zone (RFC 822)    (Number)            -0800
+ *     v        time zone (generic)    (Text)              Pacific Time
+ *     Q        quarter                (Text)              Q3
+ *     '        escape for text        (Delimiter)         'Date='
+ *     ''       single quote           (Literal)           'o''clock'
+ *
+ * The count of pattern letters determine the format.
+ *
+ * **Text**:
+ * * 5 pattern letters--use narrow form for standalone. Otherwise does not apply
+ * * 4 or more pattern letters--use full form,
+ * * 3 pattern letters--use short or abbreviated form if one exists
+ * * less than 3--use numeric form if one exists
+ *
+ * **Number**: the minimum number of digits. Shorter numbers are zero-padded to
+ * this amount (e.g. if "m" produces "6", "mm" produces "06"). Year is handled
+ * specially; that is, if the count of 'y' is 2, the Year will be truncated to
+ * 2 digits. (e.g., if "yyyy" produces "1997", "yy" produces "97".) Unlike other
+ * fields, fractional seconds are padded on the right with zero.
+ *
+ * **(Text & Number)**: 3 or over, use text, otherwise use number.
+ *
+ * Any characters not in the pattern will be treated as quoted text. For
+ * instance, characters like ':', '.', ' ', '#' and '@' will appear in the
+ * resulting text even though they are not enclosed in single quotes. In our
+ * current pattern usage, not all letters have meanings. But those unused
+ * letters are strongly discouraged to be used as quoted text without quotes,
+ * because we may use other letters as pattern characters in the future.
+ *
+ * Examples Using the US Locale:
+ *
+ *     Format Pattern                     Result
+ *     --------------                     -------
+ *     "yyyy.MM.dd G 'at' HH:mm:ss vvvv"  1996.07.10 AD at 15:08:56 Pacific Time
+ *     "EEE, MMM d, ''yy"                 Wed, July 10, '96
+ *     "h:mm a"                           12:08 PM
+ *     "hh 'o''clock' a, zzzz"            12 o'clock PM, Pacific Daylight Time
+ *     "K:mm a, vvv"                      0:00 PM, PT
+ *     "yyyyy.MMMMM.dd GGG hh:mm aaa"     01996.July.10 AD 12:08 PM
+ *
+ * When parsing a date string using the abbreviated year pattern ("yy"),
+ * DateFormat must interpret the abbreviated year relative to some
+ * century. It does this by adjusting dates to be within 80 years before and 20
+ * years after the time the parse function is called. For example, using a
+ * pattern of "MM/dd/yy" and a DateParse instance created on Jan 1, 1997,
+ * the string "01/11/12" would be interpreted as Jan 11, 2012 while the string
+ * "05/04/64" would be interpreted as May 4, 1964. During parsing, only
+ * strings consisting of exactly two digits, as defined by {@link
+ * java.lang.Character#isDigit(char)}, will be parsed into the default
+ * century. Any other numeric string, such as a one digit string, a three or
+ * more digit string will be interpreted as its face value.
+ *
+ * If the year pattern does not have exactly two 'y' characters, the year is
+ * interpreted literally, regardless of the number of digits. So using the
+ * pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D.
+ */
+
+class DateFormat {
+
+  /**
+   * Creates a new DateFormat, using the format specified by [newPattern]. For
+   * forms that match one of our predefined skeletons, we look up the
+   * corresponding pattern in [locale] (or in the default locale if none is
+   * specified) and use the resulting full format string. This is the
+   * preferred usage, but if [newPattern] does not match one of the skeletons,
+   * then it is used as a format directly, but will not be adapted to suit
+   * the locale.
+   *
+   * For example, in an en_US locale, specifying the skeleton
+   *     new DateFormat('yMEd');
+   * or the explicit
+   *     new DateFormat('EEE, M/d/y');
+   * would produce the same result, a date of the form
+   *     Wed, 6/27/2012
+   * The first version would produce a different format string if used in
+   * another locale, but the second format would always be the same.
+   *
+   * If [locale] does not exist in our set of supported locales then an
+   * [ArgumentError] is thrown.
+   */
+  DateFormat([String newPattern, String locale]) {
+    // TODO(alanknight): It should be possible to specify multiple skeletons eg
+    // date, time, timezone all separately. Adding many or named parameters to
+    // the constructor seems awkward, especially with the possibility of
+    // confusion with the locale. A "fluent" interface with cascading on an
+    // instance might work better? A list of patterns is also possible.
+    _locale = Intl.verifiedLocale(locale, localeExists);
+    addPattern(newPattern);
+  }
+
+  /**
+   * Return a string representing [date] formatted according to our locale
+   * and internal format.
+   */
+  String format(DateTime date) {
+    // TODO(efortuna): read optional TimeZone argument (or similar)?
+    var result = new StringBuffer();
+    _formatFields.forEach((field) => result.write(field.format(date)));
+    return result.toString();
+  }
+
+  /**
+   * NOT YET IMPLEMENTED.
+   *
+   * Returns a date string indicating how long ago (3 hours, 2 minutes)
+   * something has happened or how long in the future something will happen
+   * given a [reference] DateTime relative to the current time.
+   */
+  String formatDuration(DateTime reference) => '';
+
+  /**
+   * NOT YET IMPLEMENTED.
+   *
+   * Formats a string indicating how long ago (negative [duration]) or how far
+   * in the future (positive [duration]) some time is with respect to a
+   * reference [date].
+   */
+  String formatDurationFrom(Duration duration, DateTime date) => '';
+
+  /**
+   * Given user input, attempt to parse the [inputString] into the anticipated
+   * format, treating it as being in the local timezone. If [inputString] does
+   * not match our format, throws a [FormatException]. This will accept dates
+   * whose values are not strictly valid, or strings with additional characters
+   * (including whitespace) after a valid date. For stricter parsing, use
+   * [parseStrict].
+   */
+  DateTime parse(String inputString, [utc = false]) =>
+      _parse(inputString, utc: utc, strict: false);
+
+  /**
+   * Given user input, attempt to parse the [inputString] "loosely" into the
+   * anticipated format, accepting some variations from the strict format.
+   *
+   * If [inputString]
+   * is accepted by [parseStrict], just return the result. If not, attempt to
+   * parse it, but accepting either upper or
+   * lower case, allowing delimiters to be missing and replaced or
+   * supplemented with whitespace,
+   * and allowing arbitrary amounts of whitespace wherever whitespace is
+   * permitted. Note that this does not allow trailing characters, the way
+   * [parse] does. It also does not allow leading whitespace on delimiters,
+   * and does not allow alternative names for months or weekdays other than
+   * those the format knows about. The restrictions are quite arbitrary and
+   * it's not known how well they'll work for locales that aren't English-like.
+   *
+   * If [inputString] does not parse, this throws a
+   * [FormatException].
+   *
+   * For example, this will accept
+   *
+   *       new DateTimeFormat.yMMMd("en_US").parseLoose("SEp   3 2014");
+   *       new DateTimeFormat.yMd("en_US").parseLoose("09    03/2014");
+   *
+   * It will NOT accept
+   *
+   *      // "Sept" is not a valid month name.
+   *      new DateTimeFormat.yMMMd("en_US").parseLoose("Sept 3, 2014");
+   *      // Delimiters can't have leading whitespace.
+   *      new DateTimeFormat.yMd("en_US").parseLoose("09 / 03 / 2014");
+   */
+  DateTime parseLoose(String inputString, [utc = false]) {
+    try {
+      return _parse(inputString, utc: utc, strict: true);
+    } on FormatException {
+      return _parseLoose(inputString.toLowerCase(), utc);
+    }
+  }
+
+  _parseLoose(String inputString, bool utc) {
+    var dateFields = new _DateBuilder();
+    if (utc) dateFields.utc = true;
+    var stream = new _Stream(inputString);
+    _formatFields.forEach((f) => f.parseLoose(stream, dateFields));
+    if (!stream.atEnd()) {
+      throw new FormatException(
+          "Characters remaining after date parsing in $inputString");
+    }
+    dateFields.verify(inputString);
+    return dateFields.asDate();
+  }
+
+  /**
+   * Given user input, attempt to parse the [inputString] into the anticipated
+   * format, treating it as being in the local timezone. If [inputString] does
+   * not match our format, throws a [FormatException]. This will reject dates
+   * whose values are not strictly valid, even if the
+   * DateTime constructor will accept them. It will also rejct strings with
+   * additional characters (including whitespace) after a valid date. For
+   * looser parsing, use [parse].
+   */
+  DateTime parseStrict(String inputString, [utc = false]) =>
+      _parse(inputString, utc: utc, strict: true);
+
+  DateTime _parse(String inputString, {utc: false, strict: false}) {
+    // TODO(alanknight): The Closure code refers to special parsing of numeric
+    // values with no delimiters, which we currently don't do. Should we?
+    var dateFields = new _DateBuilder();
+    if (utc) dateFields.utc = true;
+    var stream = new _Stream(inputString);
+    _formatFields.forEach((f) => f.parse(stream, dateFields));
+    if (strict && !stream.atEnd()) {
+      throw new FormatException(
+          "Characters remaining after date parsing in $inputString");
+    }
+    if (strict) dateFields.verify(inputString);
+    return dateFields.asDate();
+  }
+
+  /**
+   * Given user input, attempt to parse the [inputString] into the anticipated
+   * format, treating it as being in UTC.
+   *
+   * The canonical Dart style name
+   * is [parseUtc], but [parseUTC] is retained
+   * for backward-compatibility.
+   */
+  DateTime parseUTC(String inputString) => parse(inputString, true);
+
+  /**
+   * Given user input, attempt to parse the [inputString] into the anticipated
+   * format, treating it as being in UTC.
+   *
+   * The canonical Dart style name
+   * is [parseUtc], but [parseUTC] is retained
+   * for backward-compatibility.
+   */
+  DateTime parseUtc(String inputString) => parse(inputString, true);
+
+  /**
+   * Return the locale code in which we operate, e.g. 'en_US' or 'pt'.
+   */
+  String get locale => _locale;
+
+  /**
+   * Returns a list of all locales for which we have date formatting
+   * information.
+   */
+  static List<String> allLocalesWithSymbols() => dateTimeSymbols.keys.toList();
+
+  /**
+   * The named constructors for this class are all conveniences for creating
+   * instances using one of the known "skeleton" formats, and having code
+   * completion support for discovering those formats.
+   * So,
+   *     new DateFormat.yMd("en_US")
+   * is equivalent to
+   *     new DateFormat("yMd", "en_US")
+   * To create a compound format you can use these constructors in combination
+   * with the add_ methods below. e.g.
+   *     new DateFormat.yMd().add_Hms();
+   * If the optional [locale] is omitted, the format will be created using the
+   * default locale in [Intl.systemLocale].
+   */
+  DateFormat.d([locale]) : this("d", locale);
+  DateFormat.E([locale]) : this("E", locale);
+  DateFormat.EEEE([locale]) : this("EEEE", locale);
+  DateFormat.LLL([locale]) : this("LLL", locale);
+  DateFormat.LLLL([locale]) : this("LLLL", locale);
+  DateFormat.M([locale]) : this("M", locale);
+  DateFormat.Md([locale]) : this("Md", locale);
+  DateFormat.MEd([locale]) : this("MEd", locale);
+  DateFormat.MMM([locale]) : this("MMM", locale);
+  DateFormat.MMMd([locale]) : this("MMMd", locale);
+  DateFormat.MMMEd([locale]) : this("MMMEd", locale);
+  DateFormat.MMMM([locale]) : this("MMMM", locale);
+  DateFormat.MMMMd([locale]) : this("MMMMd", locale);
+  DateFormat.MMMMEEEEd([locale]) : this("MMMMEEEEd", locale);
+  DateFormat.QQQ([locale]) : this("QQQ", locale);
+  DateFormat.QQQQ([locale]) : this("QQQQ", locale);
+  DateFormat.y([locale]) : this("y", locale);
+  DateFormat.yM([locale]) : this("yM", locale);
+  DateFormat.yMd([locale]) : this("yMd", locale);
+  DateFormat.yMEd([locale]) : this("yMEd", locale);
+  DateFormat.yMMM([locale]) : this("yMMM", locale);
+  DateFormat.yMMMd([locale]) : this("yMMMd", locale);
+  DateFormat.yMMMEd([locale]) : this("yMMMEd", locale);
+  DateFormat.yMMMM([locale]) : this("yMMMM", locale);
+  DateFormat.yMMMMd([locale]) : this("yMMMMd", locale);
+  DateFormat.yMMMMEEEEd([locale]) : this("yMMMMEEEEd", locale);
+  DateFormat.yQQQ([locale]) : this("yQQQ", locale);
+  DateFormat.yQQQQ([locale]) : this("yQQQQ", locale);
+  DateFormat.H([locale]) : this("H", locale);
+  DateFormat.Hm([locale]) : this("Hm", locale);
+  DateFormat.Hms([locale]) : this("Hms", locale);
+  DateFormat.j([locale]) : this("j", locale);
+  DateFormat.jm([locale]) : this("jm", locale);
+  DateFormat.jms([locale]) : this("jms", locale);
+  DateFormat.jmv([locale]) : this("jmv", locale);
+  DateFormat.jmz([locale]) : this("jmz", locale);
+  DateFormat.jv([locale]) : this("jv", locale);
+  DateFormat.jz([locale]) : this("jz", locale);
+  DateFormat.m([locale]) : this("m", locale);
+  DateFormat.ms([locale]) : this("ms", locale);
+  DateFormat.s([locale]) : this("s", locale);
+
+  /**
+   * The "add_*" methods append a particular skeleton to the format, or set
+   * it as the only format if none was previously set. These are primarily
+   * useful for creating compound formats. For example
+   *       new DateFormat.yMd().add_Hms();
+   * would create a date format that prints both the date and the time.
+   */
+  DateFormat add_d() => addPattern("d");
+  DateFormat add_E() => addPattern("E");
+  DateFormat add_EEEE() => addPattern("EEEE");
+  DateFormat add_LLL() => addPattern("LLL");
+  DateFormat add_LLLL() => addPattern("LLLL");
+  DateFormat add_M() => addPattern("M");
+  DateFormat add_Md() => addPattern("Md");
+  DateFormat add_MEd() => addPattern("MEd");
+  DateFormat add_MMM() => addPattern("MMM");
+  DateFormat add_MMMd() => addPattern("MMMd");
+  DateFormat add_MMMEd() => addPattern("MMMEd");
+  DateFormat add_MMMM() => addPattern("MMMM");
+  DateFormat add_MMMMd() => addPattern("MMMMd");
+  DateFormat add_MMMMEEEEd() => addPattern("MMMMEEEEd");
+  DateFormat add_QQQ() => addPattern("QQQ");
+  DateFormat add_QQQQ() => addPattern("QQQQ");
+  DateFormat add_y() => addPattern("y");
+  DateFormat add_yM() => addPattern("yM");
+  DateFormat add_yMd() => addPattern("yMd");
+  DateFormat add_yMEd() => addPattern("yMEd");
+  DateFormat add_yMMM() => addPattern("yMMM");
+  DateFormat add_yMMMd() => addPattern("yMMMd");
+  DateFormat add_yMMMEd() => addPattern("yMMMEd");
+  DateFormat add_yMMMM() => addPattern("yMMMM");
+  DateFormat add_yMMMMd() => addPattern("yMMMMd");
+  DateFormat add_yMMMMEEEEd() => addPattern("yMMMMEEEEd");
+  DateFormat add_yQQQ() => addPattern("yQQQ");
+  DateFormat add_yQQQQ() => addPattern("yQQQQ");
+  DateFormat add_H() => addPattern("H");
+  DateFormat add_Hm() => addPattern("Hm");
+  DateFormat add_Hms() => addPattern("Hms");
+  DateFormat add_j() => addPattern("j");
+  DateFormat add_jm() => addPattern("jm");
+  DateFormat add_jms() => addPattern("jms");
+  DateFormat add_jmv() => addPattern("jmv");
+  DateFormat add_jmz() => addPattern("jmz");
+  DateFormat add_jv() => addPattern("jv");
+  DateFormat add_jz() => addPattern("jz");
+  DateFormat add_m() => addPattern("m");
+  DateFormat add_ms() => addPattern("ms");
+  DateFormat add_s() => addPattern("s");
+
+  /**
+   * For each of the skeleton formats we also allow the use of the corresponding
+   * ICU constant names.
+   */
+  static const String ABBR_MONTH = 'MMM';
+  static const String DAY = 'd';
+  static const String ABBR_WEEKDAY = 'E';
+  static const String WEEKDAY = 'EEEE';
+  static const String ABBR_STANDALONE_MONTH = 'LLL';
+  static const String STANDALONE_MONTH = 'LLLL';
+  static const String NUM_MONTH = 'M';
+  static const String NUM_MONTH_DAY = 'Md';
+  static const String NUM_MONTH_WEEKDAY_DAY = 'MEd';
+  static const String ABBR_MONTH_DAY = 'MMMd';
+  static const String ABBR_MONTH_WEEKDAY_DAY = 'MMMEd';
+  static const String MONTH = 'MMMM';
+  static const String MONTH_DAY = 'MMMMd';
+  static const String MONTH_WEEKDAY_DAY = 'MMMMEEEEd';
+  static const String ABBR_QUARTER = 'QQQ';
+  static const String QUARTER = 'QQQQ';
+  static const String YEAR = 'y';
+  static const String YEAR_NUM_MONTH = 'yM';
+  static const String YEAR_NUM_MONTH_DAY = 'yMd';
+  static const String YEAR_NUM_MONTH_WEEKDAY_DAY = 'yMEd';
+  static const String YEAR_ABBR_MONTH = 'yMMM';
+  static const String YEAR_ABBR_MONTH_DAY = 'yMMMd';
+  static const String YEAR_ABBR_MONTH_WEEKDAY_DAY = 'yMMMEd';
+  static const String YEAR_MONTH = 'yMMMM';
+  static const String YEAR_MONTH_DAY = 'yMMMMd';
+  static const String YEAR_MONTH_WEEKDAY_DAY = 'yMMMMEEEEd';
+  static const String YEAR_ABBR_QUARTER = 'yQQQ';
+  static const String YEAR_QUARTER = 'yQQQQ';
+  static const String HOUR24 = 'H';
+  static const String HOUR24_MINUTE = 'Hm';
+  static const String HOUR24_MINUTE_SECOND = 'Hms';
+  static const String HOUR = 'j';
+  static const String HOUR_MINUTE = 'jm';
+  static const String HOUR_MINUTE_SECOND = 'jms';
+  static const String HOUR_MINUTE_GENERIC_TZ = 'jmv';
+  static const String HOUR_MINUTE_TZ = 'jmz';
+  static const String HOUR_GENERIC_TZ = 'jv';
+  static const String HOUR_TZ = 'jz';
+  static const String MINUTE = 'm';
+  static const String MINUTE_SECOND = 'ms';
+  static const String SECOND = 's';
+
+  /** The locale in which we operate, e.g. 'en_US', or 'pt'. */
+  String _locale;
+
+  /**
+   * The full template string. This may have been specified directly, or
+   * it may have been derived from a skeleton and the locale information
+   * on how to interpret that skeleton.
+   */
+  String _pattern;
+
+  /**
+   * We parse the format string into individual [_DateFormatField] objects
+   * that are used to do the actual formatting and parsing. Do not use
+   * this variable directly, use the getter [_formatFields].
+   */
+  List<_DateFormatField> _formatFieldsPrivate;
+
+  /**
+   * Getter for [_formatFieldsPrivate] that lazily initializes it.
+   */
+  get _formatFields {
+    if (_formatFieldsPrivate == null) {
+      if (_pattern == null) _useDefaultPattern();
+      _formatFieldsPrivate = parsePattern(_pattern);
+    }
+    return _formatFieldsPrivate;
+  }
+
+  /**
+   * We are being asked to do formatting without having set any pattern.
+   * Use a default.
+   */
+  _useDefaultPattern() {
+    add_yMMMMd();
+    add_jms();
+  }
+
+  /**
+   * A series of regular expressions used to parse a format string into its
+   * component fields.
+   */
+  static List<RegExp> _matchers = [
+    // Quoted String - anything between single quotes, with escaping
+    //   of single quotes by doubling them.
+    // e.g. in the pattern "hh 'o''clock'" will match 'o''clock'
+    new RegExp("^\'(?:[^\']|\'\')*\'"),
+    // Fields - any sequence of 1 or more of the same field characters.
+    // e.g. in "hh:mm:ss" will match hh, mm, and ss. But in "hms" would
+    // match each letter individually.
+    new RegExp(
+        "^(?:G+|y+|M+|k+|S+|E+|a+|h+|K+|H+|c+|L+|Q+|d+|D+|m+|s+|v+|z+|Z+)"),
+    // Everything else - A sequence that is not quotes or field characters.
+    // e.g. in "hh:mm:ss" will match the colons.
+    new RegExp("^[^\'GyMkSEahKHcLQdDmsvzZ]+")
+  ];
+
+  /**
+   * Set our pattern, appending it to any existing patterns. Also adds a single
+   * space to separate the two.
+   */
+  _appendPattern(String inputPattern, [String separator = ' ']) {
+    _pattern =
+        _pattern == null ? inputPattern : "$_pattern$separator$inputPattern";
+  }
+
+  /**
+   * Add [inputPattern] to this instance as a pattern. If there was a previous
+   * pattern, then this appends to it, separating the two by [separator].
+   * [inputPattern] is first looked up in our list of known skeletons.
+   * If it's found there, then use the corresponding pattern for this locale.
+   * If it's not, then treat [inputPattern] as an explicit pattern.
+   */
+  DateFormat addPattern(String inputPattern, [String separator = ' ']) {
+    // TODO(alanknight): This is an expensive operation. Caching recently used
+    // formats, or possibly introducing an entire "locale" object that would
+    // cache patterns for that locale could be a good optimization.
+    // If we have already parsed the format fields, reset them.
+    _formatFieldsPrivate = null;
+    if (inputPattern == null) return this;
+    if (!_availableSkeletons.containsKey(inputPattern)) {
+      _appendPattern(inputPattern, separator);
+    } else {
+      _appendPattern(_availableSkeletons[inputPattern], separator);
+    }
+    return this;
+  }
+
+  /** Return the pattern that we use to format dates.*/
+  get pattern => _pattern;
+
+  /** Return the skeletons for our current locale. */
+  Map get _availableSkeletons => dateTimePatterns[locale];
+
+  /**
+   * Return the [DateSymbol] information for the locale. This can be useful
+   * to find lists like the names of weekdays or months in a locale, but
+   * the structure of this data may change, and it's generally better to go
+   * through the [format] and [parse] APIs. If the locale isn't present, or
+   * is uninitialized, returns null;
+   */
+  DateSymbols get dateSymbols => dateTimeSymbols[_locale];
+
+  /**
+   * Set the locale. If the locale can't be found, we also look up
+   * based on alternative versions, e.g. if we have no 'en_CA' we will
+   * look for 'en' as a fallback. It will also translate en-ca into en_CA.
+   * Null is also considered a valid value for [newLocale], indicating
+   * to use the default.
+   */
+  _setLocale(String newLocale) {
+    _locale = Intl.verifiedLocale(newLocale, localeExists);
+  }
+
+  /**
+   * Return true if the locale exists, or if it is null. The null case
+   * is interpreted to mean that we use the default locale.
+   */
+  static bool localeExists(localeName) {
+    if (localeName == null) return false;
+    return dateTimeSymbols.containsKey(localeName);
+  }
+
+  static List get _fieldConstructors => [
+    (pattern, parent) => new _DateFormatQuotedField(pattern, parent),
+    (pattern, parent) => new _DateFormatPatternField(pattern, parent),
+    (pattern, parent) => new _DateFormatLiteralField(pattern, parent)
+  ];
+
+  /** Parse the template pattern and return a list of field objects.*/
+  List parsePattern(String pattern) {
+    if (pattern == null) return null;
+    return _parsePatternHelper(pattern).reversed.toList();
+  }
+
+  /** Recursive helper for parsing the template pattern. */
+  List _parsePatternHelper(String pattern) {
+    if (pattern.isEmpty) return [];
+
+    var matched = _match(pattern);
+    if (matched == null) return [];
+
+    var parsed =
+        _parsePatternHelper(pattern.substring(matched.fullPattern().length));
+    parsed.add(matched);
+    return parsed;
+  }
+
+  /** Find elements in a string that are patterns for specific fields.*/
+  _DateFormatField _match(String pattern) {
+    for (var i = 0; i < _matchers.length; i++) {
+      var regex = _matchers[i];
+      var match = regex.firstMatch(pattern);
+      if (match != null) {
+        return _fieldConstructors[i](match.group(0), this);
+      }
+    }
+  }
+}
diff --git a/intl/lib/src/intl/date_format_field.dart b/intl/lib/src/intl/date_format_field.dart
new file mode 100644
index 0000000..8df1154
--- /dev/null
+++ b/intl/lib/src/intl/date_format_field.dart
@@ -0,0 +1,660 @@
+// Copyright (c) 2012, 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.
+
+part of intl;
+
+/**
+ * This is a private class internal to DateFormat which is used for formatting
+ * particular fields in a template. e.g. if the format is hh:mm:ss then the
+ * fields would be "hh", ":", "mm", ":", and "ss". Each type of field knows
+ * how to format that portion of a date.
+ */
+abstract class _DateFormatField {
+  /** The format string that defines us, e.g. "hh" */
+  String pattern;
+
+  /** The DateFormat that we are part of.*/
+  DateFormat parent;
+
+  _DateFormatField(this.pattern, this.parent);
+
+  /**
+   * Return the width of [pattern]. Different widths represent different
+   * formatting options. See the comment for DateFormat for details.
+   */
+  int get width => pattern.length;
+
+  String fullPattern() => pattern;
+
+  String toString() => pattern;
+
+  /** Format date according to our specification and return the result. */
+  String format(DateTime date) {
+    // Default implementation in the superclass, works for both types of
+    // literal patterns, and is overridden by _DateFormatPatternField.
+    return pattern;
+  }
+
+  /** Abstract method for subclasses to implementing parsing for their format.*/
+  void parse(_Stream input, _DateBuilder dateFields);
+
+  /**
+   * Abstract method for subclasses to implementing 'loose' parsing for
+   * their format, accepting input case-insensitively, and allowing some
+   * delimiters to be skipped.
+   */
+  void parseLoose(_Stream input, _DateBuilder dateFields);
+
+  /** Parse a literal field. We just look for the exact input. */
+  void parseLiteral(_Stream input) {
+    var found = input.read(width);
+    if (found != pattern) {
+      throwFormatException(input);
+    }
+  }
+
+  /**
+   * Parse a literal field. We accept either an exact match, or an arbitrary
+   * amount of whitespace.
+   */
+  void parseLiteralLoose(_Stream input) {
+    var found = input.peek(width);
+    if (found == pattern) {
+      input.read(width);
+    }
+    while (!input.atEnd() && input.peek().trim().isEmpty) {
+      input.read();
+    }
+  }
+
+  /** Throw a format exception with an error message indicating the position.*/
+  void throwFormatException(_Stream stream) {
+    throw new FormatException("Trying to read $this from ${stream.contents} "
+        "at position ${stream.index}");
+  }
+}
+
+/**
+ * Represents a literal field - a sequence of characters that doesn't
+ * change according to the date's data. As such, the implementation
+ * is extremely simple.
+ */
+class _DateFormatLiteralField extends _DateFormatField {
+  _DateFormatLiteralField(pattern, parent) : super(pattern, parent);
+
+  parse(_Stream input, _DateBuilder dateFields) {
+    parseLiteral(input);
+  }
+
+  parseLoose(_Stream input, _DateBuilder dateFields) =>
+      parseLiteralLoose(input);
+}
+
+/**
+ * Represents a literal field with quoted characters in it. This is
+ * only slightly more complex than a _DateFormatLiteralField.
+ */
+class _DateFormatQuotedField extends _DateFormatField {
+  String _fullPattern;
+
+  String fullPattern() => _fullPattern;
+
+  _DateFormatQuotedField(pattern, parent) : super(pattern, parent) {
+    _fullPattern = pattern;
+    patchQuotes();
+  }
+
+  parse(_Stream input, _DateBuilder dateFields) {
+    parseLiteral(input);
+  }
+
+  parseLoose(_Stream input, _DateBuilder dateFields) =>
+      parseLiteralLoose(input);
+
+  void patchQuotes() {
+    if (pattern == "''") {
+      pattern = "'";
+    } else {
+      pattern = pattern.substring(1, pattern.length - 1);
+      var twoEscapedQuotes = new RegExp(r"''");
+      pattern = pattern.replaceAll(twoEscapedQuotes, "'");
+    }
+  }
+}
+
+/**
+ * A field that parses "loosely", meaning that we'll accept input that is
+ * missing delimiters, has upper/lower case mixed up, and might not strictly
+ * conform to the pattern, e.g. the pattern calls for Sep we might accept
+ * sep, september, sEPTember. Doesn't affect numeric fields.
+ */
+class _LoosePatternField extends _DateFormatPatternField {
+  _LoosePatternField(String pattern, parent) : super(pattern, parent);
+
+  /**
+    * Parse from a list of possibilities, but case-insensitively.
+    * Assumes that input is lower case.
+    */
+  int parseEnumeratedString(_Stream input, List possibilities) {
+    var lowercasePossibilities =
+        possibilities.map((x) => x.toLowerCase()).toList();
+    try {
+      return super.parseEnumeratedString(input, lowercasePossibilities);
+    } on FormatException {
+      return -1;
+    }
+  }
+
+  /**
+   * Parse a month name, case-insensitively, and set it in [dateFields].
+   * Assumes that [input] is lower case.
+   */
+  void parseMonth(input, dateFields) {
+    if (width <= 2) {
+      handleNumericField(input, dateFields.setMonth);
+      return;
+    }
+    var possibilities = [symbols.MONTHS, symbols.SHORTMONTHS];
+    for (var monthNames in possibilities) {
+      var month = parseEnumeratedString(input, monthNames);
+      if (month != -1) {
+        dateFields.month = month + 1;
+        return;
+      }
+    }
+  }
+
+  /**
+   * Parse a standalone day name, case-insensitively.
+   * Assumes that input is lower case. Doesn't do anything
+   */
+  void parseStandaloneDay(input) {
+    // This is ignored, but we still have to skip over it the correct amount.
+    if (width <= 2) {
+      handleNumericField(input, (x) => x);
+      return;
+    }
+    var possibilities = [
+      symbols.STANDALONEWEEKDAYS,
+      symbols.STANDALONESHORTWEEKDAYS
+    ];
+    for (var dayNames in possibilities) {
+      var day = parseEnumeratedString(input, dayNames);
+      if (day != -1) {
+        return;
+      }
+    }
+  }
+
+  /**
+   * Parse a standalone month name, case-insensitively.
+   * Assumes that input is lower case. Doesn't do anything
+   */
+  void parseStandaloneMonth(input, dateFields) {
+    if (width <= 2) {
+      handleNumericField(input, (x) => x);
+      return;
+    }
+    var possibilities = [
+      symbols.STANDALONEMONTHS,
+      symbols.STANDALONESHORTMONTHS
+    ];
+    for (var monthNames in possibilities) {
+      var month = parseEnumeratedString(input, monthNames);
+      if (month != -1) {
+        dateFields.month = month + 1;
+        return;
+      }
+    }
+  }
+
+  /**
+   * Parse a day of the week name, case-insensitively.
+   * Assumes that input is lower case. Doesn't do anything
+   */
+  void parseDayOfWeek(_Stream input) {
+    // This is IGNORED, but we still have to skip over it the correct amount.
+    if (width <= 2) {
+      handleNumericField(input, (x) => x);
+      return;
+    }
+    var possibilities = [symbols.WEEKDAYS, symbols.SHORTWEEKDAYS];
+    for (var dayNames in possibilities) {
+      var day = parseEnumeratedString(input, dayNames);
+      if (day != -1) {
+        return;
+      }
+    }
+  }
+}
+
+/*
+ * Represents a field in the pattern that formats some aspect of the
+ * date. Consists primarily of a switch on the particular pattern characters
+ * to determine what to do.
+ */
+class _DateFormatPatternField extends _DateFormatField {
+  _DateFormatPatternField(pattern, parent) : super(pattern, parent);
+
+  /** Format date according to our specification and return the result. */
+  String format(DateTime date) {
+    return formatField(date);
+  }
+
+  /**
+   * Parse the date according to our specification and put the result
+   * into the correct place in dateFields.
+   */
+  void parse(_Stream input, _DateBuilder dateFields) {
+    parseField(input, dateFields);
+  }
+
+  /**
+   * Parse the date according to our specification and put the result
+   * into the correct place in dateFields. Allow looser parsing, accepting
+   * case-insensitive input and skipped delimiters.
+   */
+  void parseLoose(_Stream input, _DateBuilder dateFields) {
+    new _LoosePatternField(pattern, parent).parse(input, dateFields);
+  }
+
+  /**
+   * Parse a field representing part of a date pattern. Note that we do not
+   * return a value, but rather build up the result in [builder].
+   */
+  void parseField(_Stream input, _DateBuilder builder) {
+    try {
+      switch (pattern[0]) {
+        case 'a':
+          parseAmPm(input, builder);
+          break;
+        case 'c':
+          parseStandaloneDay(input);
+          break;
+        case 'd':
+          handleNumericField(input, builder.setDay);
+          break; // day
+        // Day of year. Setting month=January with any day of the year works
+        case 'D':
+          handleNumericField(input, builder.setDay);
+          break; // dayofyear
+        case 'E':
+          parseDayOfWeek(input);
+          break;
+        case 'G':
+          break; // era
+        case 'h':
+          parse1To12Hours(input, builder);
+          break;
+        case 'H':
+          handleNumericField(input, builder.setHour);
+          break; // hour 0-23
+        case 'K':
+          handleNumericField(input, builder.setHour);
+          break; //hour 0-11
+        case 'k':
+          handleNumericField(input, builder.setHour, -1);
+          break; //hr 1-24
+        case 'L':
+          parseStandaloneMonth(input, builder);
+          break;
+        case 'M':
+          parseMonth(input, builder);
+          break;
+        case 'm':
+          handleNumericField(input, builder.setMinute);
+          break; // minutes
+        case 'Q':
+          break; // quarter
+        case 'S':
+          handleNumericField(input, builder.setFractionalSecond);
+          break;
+        case 's':
+          handleNumericField(input, builder.setSecond);
+          break;
+        case 'v':
+          break; // time zone id
+        case 'y':
+          handleNumericField(input, builder.setYear);
+          break;
+        case 'z':
+          break; // time zone
+        case 'Z':
+          break; // time zone RFC
+        default:
+          return;
+      }
+    } catch (e) {
+      throwFormatException(input);
+    }
+  }
+
+  /** Formatting logic if we are of type FIELD */
+  String formatField(DateTime date) {
+    switch (pattern[0]) {
+      case 'a':
+        return formatAmPm(date);
+      case 'c':
+        return formatStandaloneDay(date);
+      case 'd':
+        return formatDayOfMonth(date);
+      case 'D':
+        return formatDayOfYear(date);
+      case 'E':
+        return formatDayOfWeek(date);
+      case 'G':
+        return formatEra(date);
+      case 'h':
+        return format1To12Hours(date);
+      case 'H':
+        return format0To23Hours(date);
+      case 'K':
+        return format0To11Hours(date);
+      case 'k':
+        return format24Hours(date);
+      case 'L':
+        return formatStandaloneMonth(date);
+      case 'M':
+        return formatMonth(date);
+      case 'm':
+        return formatMinutes(date);
+      case 'Q':
+        return formatQuarter(date);
+      case 'S':
+        return formatFractionalSeconds(date);
+      case 's':
+        return formatSeconds(date);
+      case 'v':
+        return formatTimeZoneId(date);
+      case 'y':
+        return formatYear(date);
+      case 'z':
+        return formatTimeZone(date);
+      case 'Z':
+        return formatTimeZoneRFC(date);
+      default:
+        return '';
+    }
+  }
+
+  /** Return the symbols for our current locale. */
+  DateSymbols get symbols => dateTimeSymbols[parent.locale];
+
+  formatEra(DateTime date) {
+    var era = date.year > 0 ? 1 : 0;
+    return width >= 4 ? symbols.ERANAMES[era] : symbols.ERAS[era];
+  }
+
+  formatYear(DateTime date) {
+    // TODO(alanknight): Proper handling of years <= 0
+    var year = date.year;
+    if (year < 0) {
+      year = -year;
+    }
+    return width == 2 ? padTo(2, year % 100) : padTo(width, year);
+  }
+
+  /**
+   * We are given [input] as a stream from which we want to read a date. We
+   * can't dynamically build up a date, so we are given a list [dateFields] of
+   * the constructor arguments and an [position] at which to set it
+   * (year,month,day,hour,minute,second,fractionalSecond)
+   * then after all parsing is done we construct a date from the arguments.
+   * This method handles reading any of the numeric fields. The [offset]
+   * argument allows us to compensate for zero-based versus one-based values.
+   */
+  void handleNumericField(_Stream input, Function setter, [int offset = 0]) {
+    var result = input.nextInteger();
+    if (result == null) throwFormatException(input);
+    setter(result + offset);
+  }
+
+  /**
+   * We are given [input] as a stream from which we want to read a date. We
+   * can't dynamically build up a date, so we are given a list [dateFields] of
+   * the constructor arguments and an [position] at which to set it
+   * (year,month,day,hour,minute,second,fractionalSecond)
+   * then after all parsing is done we construct a date from the arguments.
+   * This method handles reading any of string fields from an enumerated set.
+   */
+  int parseEnumeratedString(_Stream input, List possibilities) {
+    var results = new _Stream(possibilities)
+        .findIndexes((each) => input.peek(each.length) == each);
+    if (results.isEmpty) throwFormatException(input);
+    results.sort(
+        (a, b) => possibilities[a].length.compareTo(possibilities[b].length));
+    var longestResult = results.last;
+    input.read(possibilities[longestResult].length);
+    return longestResult;
+  }
+
+  String formatMonth(DateTime date) {
+    switch (width) {
+      case 5:
+        return symbols.NARROWMONTHS[date.month - 1];
+      case 4:
+        return symbols.MONTHS[date.month - 1];
+      case 3:
+        return symbols.SHORTMONTHS[date.month - 1];
+      default:
+        return padTo(width, date.month);
+    }
+  }
+
+  void parseMonth(input, dateFields) {
+    var possibilities;
+    switch (width) {
+      case 5:
+        possibilities = symbols.NARROWMONTHS;
+        break;
+      case 4:
+        possibilities = symbols.MONTHS;
+        break;
+      case 3:
+        possibilities = symbols.SHORTMONTHS;
+        break;
+      default:
+        return handleNumericField(input, dateFields.setMonth);
+    }
+    dateFields.month = parseEnumeratedString(input, possibilities) + 1;
+  }
+
+  String format24Hours(DateTime date) {
+    return padTo(width, date.hour);
+  }
+
+  String formatFractionalSeconds(DateTime date) {
+    // Always print at least 3 digits. If the width is greater, append 0s
+    var basic = padTo(3, date.millisecond);
+    if (width - 3 > 0) {
+      var extra = padTo(width - 3, 0);
+      return basic + extra;
+    } else {
+      return basic;
+    }
+  }
+
+  String formatAmPm(DateTime date) {
+    var hours = date.hour;
+    var index = (date.hour >= 12) && (date.hour < 24) ? 1 : 0;
+    var ampm = symbols.AMPMS;
+    return ampm[index];
+  }
+
+  void parseAmPm(input, dateFields) {
+    // If we see a "PM" note it in an extra field.
+    var ampm = parseEnumeratedString(input, symbols.AMPMS);
+    if (ampm == 1) dateFields.pm = true;
+  }
+
+  String format1To12Hours(DateTime date) {
+    var hours = date.hour;
+    if (date.hour > 12) hours = hours - 12;
+    if (hours == 0) hours = 12;
+    return padTo(width, hours);
+  }
+
+  void parse1To12Hours(_Stream input, _DateBuilder dateFields) {
+    handleNumericField(input, dateFields.setHour);
+    if (dateFields.hour == 12) dateFields.hour = 0;
+  }
+
+  String format0To11Hours(DateTime date) {
+    return padTo(width, date.hour % 12);
+  }
+
+  String format0To23Hours(DateTime date) {
+    return padTo(width, date.hour);
+  }
+
+  String formatStandaloneDay(DateTime date) {
+    switch (width) {
+      case 5:
+        return symbols.STANDALONENARROWWEEKDAYS[date.weekday % 7];
+      case 4:
+        return symbols.STANDALONEWEEKDAYS[date.weekday % 7];
+      case 3:
+        return symbols.STANDALONESHORTWEEKDAYS[date.weekday % 7];
+      default:
+        return padTo(1, date.day);
+    }
+  }
+
+  void parseStandaloneDay(_Stream input) {
+    // This is ignored, but we still have to skip over it the correct amount.
+    var possibilities;
+    switch (width) {
+      case 5:
+        possibilities = symbols.STANDALONENARROWWEEKDAYS;
+        break;
+      case 4:
+        possibilities = symbols.STANDALONEWEEKDAYS;
+        break;
+      case 3:
+        possibilities = symbols.STANDALONESHORTWEEKDAYS;
+        break;
+      default:
+        return handleNumericField(input, (x) => x);
+    }
+    parseEnumeratedString(input, possibilities);
+  }
+
+  String formatStandaloneMonth(DateTime date) {
+    switch (width) {
+      case 5:
+        return symbols.STANDALONENARROWMONTHS[date.month - 1];
+      case 4:
+        return symbols.STANDALONEMONTHS[date.month - 1];
+      case 3:
+        return symbols.STANDALONESHORTMONTHS[date.month - 1];
+      default:
+        return padTo(width, date.month);
+    }
+  }
+
+  void parseStandaloneMonth(input, dateFields) {
+    var possibilities;
+    switch (width) {
+      case 5:
+        possibilities = symbols.STANDALONENARROWMONTHS;
+        break;
+      case 4:
+        possibilities = symbols.STANDALONEMONTHS;
+        break;
+      case 3:
+        possibilities = symbols.STANDALONESHORTMONTHS;
+        break;
+      default:
+        return handleNumericField(input, dateFields.setMonth);
+    }
+    dateFields.month = parseEnumeratedString(input, possibilities) + 1;
+  }
+
+  String formatQuarter(DateTime date) {
+    var quarter = ((date.month - 1) / 3).truncate();
+    if (width < 4) {
+      return symbols.SHORTQUARTERS[quarter];
+    } else {
+      return symbols.QUARTERS[quarter];
+    }
+  }
+  String formatDayOfMonth(DateTime date) {
+    return padTo(width, date.day);
+  }
+
+  String formatDayOfYear(DateTime date) => padTo(width, dayNumberInYear(date));
+
+  /** Return the ordinal day, i.e. the day number in the year. */
+  int dayNumberInYear(DateTime date) {
+    if (date.month == 1) return date.day;
+    if (date.month == 2) return date.day + 31;
+    return ordinalDayFromMarchFirst(date) + 59 + (isLeapYear(date) ? 1 : 0);
+  }
+
+  /**
+   * Return the day of the year counting March 1st as 1, after which the
+   * number of days per month is constant, so it's easier to calculate.
+   * Formula from http://en.wikipedia.org/wiki/Ordinal_date
+   */
+  int ordinalDayFromMarchFirst(DateTime date) =>
+      ((30.6 * date.month) - 91.4).floor() + date.day;
+
+  /**
+   * Return true if this is a leap year. Rely on [DateTime] to do the
+   * underlying calculation, even though it doesn't expose the test to us.
+   */
+  bool isLeapYear(DateTime date) {
+    var feb29 = new DateTime(date.year, 2, 29);
+    return feb29.month == 2;
+  }
+
+  String formatDayOfWeek(DateTime date) {
+    // Note that Dart's weekday returns 1 for Monday and 7 for Sunday.
+    return (width >= 4
+        ? symbols.WEEKDAYS
+        : symbols.SHORTWEEKDAYS)[(date.weekday) % 7];
+  }
+
+  void parseDayOfWeek(_Stream input) {
+    // This is IGNORED, but we still have to skip over it the correct amount.
+    var possibilities = width >= 4 ? symbols.WEEKDAYS : symbols.SHORTWEEKDAYS;
+    parseEnumeratedString(input, possibilities);
+  }
+
+  String formatMinutes(DateTime date) {
+    return padTo(width, date.minute);
+  }
+
+  String formatSeconds(DateTime date) {
+    return padTo(width, date.second);
+  }
+
+  String formatTimeZoneId(DateTime date) {
+    // TODO(alanknight): implement time zone support
+    throw new UnimplementedError();
+  }
+
+  String formatTimeZone(DateTime date) {
+    throw new UnimplementedError();
+  }
+
+  String formatTimeZoneRFC(DateTime date) {
+    throw new UnimplementedError();
+  }
+
+  /**
+  * Return a string representation of the object padded to the left with
+  * zeros. Primarily useful for numbers.
+  */
+  String padTo(int width, Object toBePrinted) {
+    var basicString = toBePrinted.toString();
+    if (basicString.length >= width) return basicString;
+    var buffer = new StringBuffer();
+    for (var i = 0; i < width - basicString.length; i++) {
+      buffer.write('0');
+    }
+    buffer.write(basicString);
+    return buffer.toString();
+  }
+}
diff --git a/intl/lib/src/intl/date_format_helpers.dart b/intl/lib/src/intl/date_format_helpers.dart
new file mode 100644
index 0000000..6a6e68c
--- /dev/null
+++ b/intl/lib/src/intl/date_format_helpers.dart
@@ -0,0 +1,195 @@
+// Copyright (c) 2012, 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.
+
+part of intl;
+
+/**
+ * A class for holding onto the data for a date so that it can be built
+ * up incrementally.
+ */
+class _DateBuilder {
+  // Default the date values to the EPOCH so that there's a valid date
+  // in case the format doesn't set them.
+  int year = 1970,
+      month = 1,
+      day = 1,
+      hour = 0,
+      minute = 0,
+      second = 0,
+      fractionalSecond = 0;
+  bool pm = false;
+  bool utc = false;
+
+  // Functions that exist just to be closurized so we can pass them to a general
+  // method.
+  void setYear(x) {
+    year = x;
+  }
+  void setMonth(x) {
+    month = x;
+  }
+  void setDay(x) {
+    day = x;
+  }
+  void setHour(x) {
+    hour = x;
+  }
+  void setMinute(x) {
+    minute = x;
+  }
+  void setSecond(x) {
+    second = x;
+  }
+  void setFractionalSecond(x) {
+    fractionalSecond = x;
+  }
+
+  get hour24 => pm ? hour + 12 : hour;
+
+  /**
+   * Verify that we correspond to a valid date. This will reject out of
+   * range values, even if the DateTime constructor would accept them. An
+   * invalid message will result in throwing a [FormatException].
+   */
+  verify(String s) {
+    _verify(month, 1, 12, "month", s);
+    _verify(hour24, 0, 23, "hour", s);
+    _verify(minute, 0, 59, "minute", s);
+    _verify(second, 0, 59, "second", s);
+    _verify(fractionalSecond, 0, 999, "fractional second", s);
+    // Verifying the day is tricky, because it depends on the month. Create
+    // our resulting date and then verify that our values agree with it
+    // as an additional verification. And since we're doing that, also
+    // check the year, which we otherwise can't verify, and the hours,
+    // which will catch cases like "14:00:00 PM".
+    var date = asDate();
+    _verify(hour24, date.hour, date.hour, "hour", s);
+    _verify(day, date.day, date.day, "day", s);
+    _verify(year, date.year, date.year, "year", s);
+  }
+
+  _verify(int value, int min, int max, String desc, String originalInput) {
+    if (value < min || value > max) {
+      throw new FormatException(
+          "Error parsing $originalInput, invalid $desc value: $value");
+    }
+  }
+
+  /**
+   * Return a date built using our values. If no date portion is set,
+   * use the "Epoch" of January 1, 1970.
+   */
+  DateTime asDate({retry: true}) {
+    // TODO(alanknight): Validate the date, especially for things which
+    // can crash the VM, e.g. large month values.
+    var result;
+    if (utc) {
+      result = new DateTime.utc(
+          year, month, day, hour24, minute, second, fractionalSecond);
+    } else {
+      result = new DateTime(
+          year, month, day, hour24, minute, second, fractionalSecond);
+      // TODO(alanknight): Issue 15560 means non-UTC dates occasionally come
+      // out in UTC. If that happens, retry once. This will always happen if
+      // the local time zone is UTC, but that's ok.
+      if (result.toUtc() == result) {
+        result = asDate(retry: false);
+      }
+    }
+    return result;
+  }
+}
+
+/**
+ * A simple and not particularly general stream class to make parsing
+ * dates from strings simpler. It is general enough to operate on either
+ * lists or strings.
+ */
+// TODO(alanknight): With the improvements to the collection libraries
+// since this was written we might be able to get rid of it entirely
+// in favor of e.g. aString.split('') giving us an iterable of one-character
+// strings, or else make the implementation trivial. And consider renaming,
+// as _Stream is now just confusing with the system Streams.
+class _Stream {
+  var contents;
+  int index = 0;
+
+  _Stream(this.contents);
+
+  bool atEnd() => index >= contents.length;
+
+  next() => contents[index++];
+
+  /**
+   * Return the next [howMany] items, or as many as there are remaining.
+   * Advance the stream by that many positions.
+   */
+  read([int howMany = 1]) {
+    var result = peek(howMany);
+    index += howMany;
+    return result;
+  }
+
+  /**
+   * Does the input start with the given string, if we start from the
+   * current position.
+   */
+  bool startsWith(String pattern) {
+    if (contents is String) return contents.startsWith(pattern, index);
+    return pattern == peek(pattern.length);
+  }
+
+  /**
+   * Return the next [howMany] items, or as many as there are remaining.
+   * Does not modify the stream position.
+   */
+  peek([int howMany = 1]) {
+    var result;
+    if (contents is String) {
+      result = contents.substring(index, min(index + howMany, contents.length));
+    } else {
+      // Assume List
+      result = contents.sublist(index, index + howMany);
+    }
+    return result;
+  }
+
+  /** Return the remaining contents of the stream */
+  rest() => peek(contents.length - index);
+
+  /**
+   * Find the index of the first element for which [f] returns true.
+   * Advances the stream to that position.
+   */
+  int findIndex(Function f) {
+    while (!atEnd()) {
+      if (f(next())) return index - 1;
+    }
+    return null;
+  }
+
+  /**
+   * Find the indexes of all the elements for which [f] returns true.
+   * Leaves the stream positioned at the end.
+   */
+  List findIndexes(Function f) {
+    var results = [];
+    while (!atEnd()) {
+      if (f(next())) results.add(index - 1);
+    }
+    return results;
+  }
+
+  /**
+   * Assuming that the contents are characters, read as many digits as we
+   * can see and then return the corresponding integer. Advance the stream.
+   */
+  var digitMatcher = new RegExp(r'\d+');
+  int nextInteger() {
+    var string = digitMatcher.stringMatch(rest());
+    if (string == null || string.isEmpty) return null;
+    read(string.length);
+    return int.parse(string);
+  }
+}
diff --git a/intl/lib/src/intl/number_format.dart b/intl/lib/src/intl/number_format.dart
new file mode 100644
index 0000000..9e0de51
--- /dev/null
+++ b/intl/lib/src/intl/number_format.dart
@@ -0,0 +1,1081 @@
+// Copyright (c) 2012, 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.
+
+part of intl;
+
+/**
+ * Provides the ability to format a number in a locale-specific way. The
+ * format is specified as a pattern using a subset of the ICU formatting
+ * patterns.
+ *
+ * - `0` A single digit
+ * - `#` A single digit, omitted if the value is zero
+ * - `.` Decimal separator
+ * - `-` Minus sign
+ * - `,` Grouping separator
+ * - `E` Separates mantissa and expontent
+ * - `+` - Before an exponent, indicates it should be prefixed with a plus sign.
+ * - `%` - In prefix or suffix, multiply by 100 and show as percentage
+ * - `‰ (\u2030)` In prefix or suffix, multiply by 1000 and show as per mille
+ * - `¤ (\u00A4)` Currency sign, replaced by currency name
+ * - `'` Used to quote special characters
+ * - `;` Used to separate the positive and negative patterns if both are present
+ *
+ * For example,
+ *       var f = new NumberFormat("###.0#", "en_US");
+ *       print(f.format(12.345));
+ *       ==> 12.34
+ * If the locale is not specified, it will default to the current locale. If
+ * the format is not specified it will print in a basic format with at least
+ * one integer digit and three fraction digits.
+ *
+ * There are also standard patterns available via the special constructors. e.g.
+ *       var percent = new NumberFormat.percentFormat("ar");
+ *       var eurosInUSFormat = new NumberFormat.currencyPattern("en_US", "€");
+ * There are four such constructors: decimalFormat, percentFormat,
+ * scientificFormat and currencyFormat. However, at the moment,
+ * scientificFormat prints only as equivalent to "#E0" and does not take
+ * into account significant digits. The currencyFormat will default to the
+ * three-letter name of the currency if no explicit name/symbol is provided.
+ */
+class NumberFormat {
+  /** Variables to determine how number printing behaves. */
+  // TODO(alanknight): If these remain as variables and are set based on the
+  // pattern, can we make them final?
+  String _negativePrefix = '-';
+  String _positivePrefix = '';
+  String _negativeSuffix = '';
+  String _positiveSuffix = '';
+  /**
+   * How many numbers in a group when using punctuation to group digits in
+   * large numbers. e.g. in en_US: "1,000,000" has a grouping size of 3 digits
+   * between commas.
+   */
+  int _groupingSize = 3;
+  /**
+   * In some formats the last grouping size may be different than previous
+   * ones, e.g. Hindi.
+   */
+  int _finalGroupingSize = 3;
+  /**
+   * Set to true if the format has explicitly set the grouping size.
+   */
+  bool _groupingSizeSetExplicitly = false;
+  bool _decimalSeparatorAlwaysShown = false;
+  bool _useSignForPositiveExponent = false;
+  bool _useExponentialNotation = false;
+
+  int maximumIntegerDigits = 40;
+  int minimumIntegerDigits = 1;
+  int maximumFractionDigits = 3;
+  int minimumFractionDigits = 0;
+  int minimumExponentDigits = 0;
+
+  /**
+   * For percent and permille, what are we multiplying by in order to
+   * get the printed value, e.g. 100 for percent.
+   */
+  int get _multiplier => _internalMultiplier;
+  set _multiplier(int x) {
+    _internalMultiplier = x;
+    _multiplierDigits = (log(_multiplier) / LN10).round();
+  }
+  int _internalMultiplier = 1;
+
+  /** How many digits are there in the [_multiplier]. */
+  int _multiplierDigits = 0;
+
+  /**
+   * Stores the pattern used to create this format. This isn't used, but
+   * is helpful in debugging.
+   */
+  String _pattern;
+
+  /** The locale in which we print numbers. */
+  final String _locale;
+
+  /** Caches the symbols used for our locale. */
+  NumberSymbols _symbols;
+
+  /** The name (or symbol) of the currency to print. */
+  String currencyName;
+
+  /**
+   * Transient internal state in which to build up the result of the format
+   * operation. We can have this be just an instance variable because Dart is
+   * single-threaded and unless we do an asynchronous operation in the process
+   * of formatting then there will only ever be one number being formatted
+   * at a time. In languages with threads we'd need to pass this on the stack.
+   */
+  final StringBuffer _buffer = new StringBuffer();
+
+  /**
+   * Create a number format that prints using [newPattern] as it applies in
+   * [locale].
+   */
+  factory NumberFormat([String newPattern, String locale]) =>
+      new NumberFormat._forPattern(locale, (x) => newPattern);
+
+  /** Create a number format that prints as DECIMAL_PATTERN. */
+  NumberFormat.decimalPattern([String locale])
+      : this._forPattern(locale, (x) => x.DECIMAL_PATTERN);
+
+  /** Create a number format that prints as PERCENT_PATTERN. */
+  NumberFormat.percentPattern([String locale])
+      : this._forPattern(locale, (x) => x.PERCENT_PATTERN);
+
+  /** Create a number format that prints as SCIENTIFIC_PATTERN. */
+  NumberFormat.scientificPattern([String locale])
+      : this._forPattern(locale, (x) => x.SCIENTIFIC_PATTERN);
+
+  /**
+   * Create a number format that prints as CURRENCY_PATTERN. If provided,
+   * use [nameOrSymbol] in place of the default currency name. e.g.
+   *        var eurosInCurrentLocale = new NumberFormat
+   *            .currencyPattern(Intl.defaultLocale, "€");
+   */
+  NumberFormat.currencyPattern([String locale, String nameOrSymbol])
+      : this._forPattern(locale, (x) => x.CURRENCY_PATTERN, nameOrSymbol);
+
+  /**
+   * Create a number format that prints in a pattern we get from
+   * the [getPattern] function using the locale [locale].
+   */
+  NumberFormat._forPattern(String locale, Function getPattern,
+      [this.currencyName])
+      : _locale = Intl.verifiedLocale(locale, localeExists) {
+    _symbols = numberFormatSymbols[_locale];
+    if (currencyName == null) {
+      currencyName = _symbols.DEF_CURRENCY_CODE;
+    }
+    _setPattern(getPattern(_symbols));
+  }
+
+  /**
+   * Return the locale code in which we operate, e.g. 'en_US' or 'pt'.
+   */
+  String get locale => _locale;
+
+  /**
+   * Return true if the locale exists, or if it is null. The null case
+   * is interpreted to mean that we use the default locale.
+   */
+  static bool localeExists(localeName) {
+    if (localeName == null) return false;
+    return numberFormatSymbols.containsKey(localeName);
+  }
+
+  /**
+   * Return the symbols which are used in our locale. Cache them to avoid
+   * repeated lookup.
+   */
+  NumberSymbols get symbols => _symbols;
+
+  /**
+   * Format [number] according to our pattern and return the formatted string.
+   */
+  String format(number) {
+    if (_isNaN(number)) return symbols.NAN;
+    if (_isInfinite(number)) return "${_signPrefix(number)}${symbols.INFINITY}";
+
+    _add(_signPrefix(number));
+    _formatNumber(number.abs());
+    _add(_signSuffix(number));
+
+    var result = _buffer.toString();
+    _buffer.clear();
+    return result;
+  }
+
+  /**
+   * Parse the number represented by the string. If it's not
+   * parseable, throws a [FormatException].
+   */
+  num parse(String text) => new _NumberParser(this, text).value;
+
+  /**
+   * Format the main part of the number in the form dictated by the pattern.
+   */
+  void _formatNumber(number) {
+    if (_useExponentialNotation) {
+      _formatExponential(number);
+    } else {
+      _formatFixed(number);
+    }
+  }
+
+  /** Format the number in exponential notation. */
+  void _formatExponential(num number) {
+    if (number == 0.0) {
+      _formatFixed(number);
+      _formatExponent(0);
+      return;
+    }
+
+    var exponent = (log(number) / log(10)).floor();
+    var mantissa = number / pow(10.0, exponent);
+
+    var minIntDigits = minimumIntegerDigits;
+    if (maximumIntegerDigits > 1 &&
+        maximumIntegerDigits > minimumIntegerDigits) {
+      // A repeating range is defined; adjust to it as follows.
+      // If repeat == 3, we have 6,5,4=>3; 3,2,1=>0; 0,-1,-2=>-3;
+      // -3,-4,-5=>-6, etc. This takes into account that the
+      // exponent we have here is off by one from what we expect;
+      // it is for the format 0.MMMMMx10^n.
+      while ((exponent % maximumIntegerDigits) != 0) {
+        mantissa *= 10;
+        exponent--;
+      }
+      minIntDigits = 1;
+    } else {
+      // No repeating range is defined, use minimum integer digits.
+      if (minimumIntegerDigits < 1) {
+        exponent++;
+        mantissa /= 10;
+      } else {
+        exponent -= minimumIntegerDigits - 1;
+        mantissa *= pow(10, minimumIntegerDigits - 1);
+      }
+    }
+    _formatFixed(mantissa);
+    _formatExponent(exponent);
+  }
+
+  /**
+   * Format the exponent portion, e.g. in "1.3e-5" the "e-5".
+   */
+  void _formatExponent(num exponent) {
+    _add(symbols.EXP_SYMBOL);
+    if (exponent < 0) {
+      exponent = -exponent;
+      _add(symbols.MINUS_SIGN);
+    } else if (_useSignForPositiveExponent) {
+      _add(symbols.PLUS_SIGN);
+    }
+    _pad(minimumExponentDigits, exponent.toString());
+  }
+
+  /** Used to test if we have exceeded Javascript integer limits. */
+  final _maxInt = pow(2, 52);
+
+  /**
+   * Helpers to check numbers that don't conform to the [num] interface,
+   * e.g. Int64
+   */
+  _isInfinite(number) => number is num ? number.isInfinite : false;
+  _isNaN(number) => number is num ? number.isNaN : false;
+  _round(number) => number is num ? number.round() : number;
+  _floor(number) => number is num ? number.floor() : number;
+
+  /**
+   * Format the basic number portion, inluding the fractional digits.
+   */
+  void _formatFixed(number) {
+    var integerPart;
+    int fractionPart;
+    int extraIntegerDigits;
+
+    final power = pow(10, maximumFractionDigits);
+    final digitMultiplier = power * _multiplier;
+
+    if (_isInfinite(number)) {
+      integerPart = number.toInt();
+      extraIntegerDigits = 0;
+      fractionPart = 0;
+    } else {
+      // We have three possible pieces. First, the basic integer part. If this
+      // is a percent or permille, the additional 2 or 3 digits. Finally the
+      // fractional part.
+      // We avoid multiplying the number because it might overflow if we have
+      // a fixed-size integer type, so we extract each of the three as an
+      // integer pieces.
+      integerPart = _floor(number);
+      var fraction = number - integerPart;
+      // Multiply out to the number of decimal places and the percent, then
+      // round. For fixed-size integer types this should always be zero, so
+      // multiplying is OK.
+      var remainingDigits = _round(fraction * digitMultiplier).toInt();
+      // However, in rounding we may overflow into the main digits.
+      if (remainingDigits >= digitMultiplier) {
+        integerPart++;
+        remainingDigits -= digitMultiplier;
+      }
+      // Separate out the extra integer parts from the fraction part.
+      extraIntegerDigits = remainingDigits ~/ power;
+      fractionPart = remainingDigits % power;
+    }
+    var fractionPresent = minimumFractionDigits > 0 || fractionPart > 0;
+
+    var integerDigits = _integerDigits(integerPart, extraIntegerDigits);
+    var digitLength = integerDigits.length;
+
+    if (_hasIntegerDigits(integerDigits)) {
+      _pad(minimumIntegerDigits - digitLength);
+      for (var i = 0; i < digitLength; i++) {
+        _addDigit(integerDigits.codeUnitAt(i));
+        _group(digitLength, i);
+      }
+    } else if (!fractionPresent) {
+      // If neither fraction nor integer part exists, just print zero.
+      _addZero();
+    }
+
+    _decimalSeparator(fractionPresent);
+    _formatFractionPart((fractionPart + power).toString());
+  }
+
+  /**
+   * Compute the raw integer digits which will then be printed with
+   * grouping and translated to localized digits.
+   */
+  String _integerDigits(integerPart, extraIntegerDigits) {
+    // If the int part is larger than 2^52 and we're on Javascript (so it's
+    // really a float) it will lose precision, so pad out the rest of it
+    // with zeros. Check for Javascript by seeing if an integer is double.
+    var paddingDigits = '';
+    if (1 is double && integerPart is num && integerPart > _maxInt) {
+      var howManyDigitsTooBig = (log(integerPart) / LN10).ceil() - 16;
+      var divisor = pow(10, howManyDigitsTooBig).round();
+      paddingDigits = symbols.ZERO_DIGIT * howManyDigitsTooBig.toInt();
+      integerPart = (integerPart / divisor).truncate();
+    }
+
+    var extra = extraIntegerDigits == 0 ? '' : extraIntegerDigits.toString();
+    var intDigits = _mainIntegerDigits(integerPart);
+    var paddedExtra =
+        intDigits.isEmpty ? extra : extra.padLeft(_multiplierDigits, '0');
+    return "${intDigits}${paddedExtra}${paddingDigits}";
+  }
+
+  /**
+   * The digit string of the integer part. This is the empty string if the
+   * integer part is zero and otherwise is the toString() of the integer
+   * part, stripping off any minus sign.
+   */
+  String _mainIntegerDigits(integer) {
+    if (integer == 0) return '';
+    var digits = integer.toString();
+    // If we have a fixed-length int representation, it can have a negative
+    // number whose negation is also negative, e.g. 2^-63 in 64-bit.
+    // Remove the minus sign.
+    return digits.startsWith('-') ? digits.substring(1) : digits;
+  }
+
+  /**
+   * Format the part after the decimal place in a fixed point number.
+   */
+  void _formatFractionPart(String fractionPart) {
+    var fractionCodes = fractionPart.codeUnits;
+    var fractionLength = fractionPart.length;
+    while (fractionCodes[fractionLength - 1] == _zero &&
+        fractionLength > minimumFractionDigits + 1) {
+      fractionLength--;
+    }
+    for (var i = 1; i < fractionLength; i++) {
+      _addDigit(fractionCodes[i]);
+    }
+  }
+
+  /** Print the decimal separator if appropriate. */
+  void _decimalSeparator(bool fractionPresent) {
+    if (_decimalSeparatorAlwaysShown || fractionPresent) {
+      _add(symbols.DECIMAL_SEP);
+    }
+  }
+
+  /**
+   * Return true if we have a main integer part which is printable, either
+   * because we have digits left of the decimal point (this may include digits
+   * which have been moved left because of percent or permille formatting),
+   * or because the minimum number of printable digits is greater than 1.
+   */
+  bool _hasIntegerDigits(String digits) =>
+      digits.isNotEmpty || minimumIntegerDigits > 0;
+
+  /** A group of methods that provide support for writing digits and other
+   * required characters into [_buffer] easily.
+   */
+  void _add(String x) {
+    _buffer.write(x);
+  }
+  void _addCharCode(int x) {
+    _buffer.writeCharCode(x);
+  }
+  void _addZero() {
+    _buffer.write(symbols.ZERO_DIGIT);
+  }
+  void _addDigit(int x) {
+    _buffer.writeCharCode(_localeZero + x - _zero);
+  }
+
+  /** Print padding up to [numberOfDigits] above what's included in [basic]. */
+  void _pad(int numberOfDigits, [String basic = '']) {
+    for (var i = 0; i < numberOfDigits - basic.length; i++) {
+      _add(symbols.ZERO_DIGIT);
+    }
+    for (var x in basic.codeUnits) {
+      _addDigit(x);
+    }
+  }
+
+  /**
+   * We are printing the digits of the number from left to right. We may need
+   * to print a thousands separator or other grouping character as appropriate
+   * to the locale. So we find how many places we are from the end of the number
+   * by subtracting our current [position] from the [totalLength] and printing
+   * the separator character every [_groupingSize] digits, with the final
+   * grouping possibly being of a different size, [_finalGroupingSize].
+   */
+  void _group(int totalLength, int position) {
+    var distanceFromEnd = totalLength - position;
+    if (distanceFromEnd <= 1 || _groupingSize <= 0) return;
+    if (distanceFromEnd == _finalGroupingSize + 1) {
+      _add(symbols.GROUP_SEP);
+    } else if ((distanceFromEnd > _finalGroupingSize) &&
+        (distanceFromEnd - _finalGroupingSize) % _groupingSize == 1) {
+      _add(symbols.GROUP_SEP);
+    }
+  }
+
+  /** Returns the code point for the character '0'. */
+  final _zero = '0'.codeUnits.first;
+
+  /** Returns the code point for the locale's zero digit. */
+  // Note that there is a slight risk of a locale's zero digit not fitting
+  // into a single code unit, but it seems very unlikely, and if it did,
+  // there's a pretty good chance that our assumptions about being able to do
+  // arithmetic on it would also be invalid.
+  get _localeZero => symbols.ZERO_DIGIT.codeUnits.first;
+
+  /**
+   * Returns the prefix for [x] based on whether it's positive or negative.
+   * In en_US this would be '' and '-' respectively.
+   */
+  String _signPrefix(x) => x.isNegative ? _negativePrefix : _positivePrefix;
+
+  /**
+   * Returns the suffix for [x] based on wether it's positive or negative.
+   * In en_US there are no suffixes for positive or negative.
+   */
+  String _signSuffix(x) => x.isNegative ? _negativeSuffix : _positiveSuffix;
+
+  void _setPattern(String newPattern) {
+    if (newPattern == null) return;
+    // Make spaces non-breaking
+    _pattern = newPattern.replaceAll(' ', '\u00a0');
+    var parser = new _NumberFormatParser(this, newPattern, currencyName);
+    parser.parse();
+  }
+
+  String toString() => "NumberFormat($_locale, $_pattern)";
+}
+
+/**
+ *  A one-time object for parsing a particular numeric string. One-time here
+ * means an instance can only parse one string. This is implemented by
+ * transforming from a locale-specific format to one that the system can parse,
+ * then calls the system parsing methods on it.
+ */
+class _NumberParser {
+
+  /** The format for which we are parsing. */
+  final NumberFormat format;
+
+  /** The text we are parsing. */
+  final String text;
+
+  /** What we use to iterate over the input text. */
+  final _Stream input;
+
+  /**
+   * The result of parsing [text] according to [format]. Automatically
+   * populated in the constructor.
+   */
+  num value;
+
+  /** The symbols used by our format. */
+  NumberSymbols get symbols => format.symbols;
+
+  /** Where we accumulate the normalized representation of the number. */
+  final StringBuffer _normalized = new StringBuffer();
+
+  /**
+   * Did we see something that indicates this is, or at least might be,
+   * a positive number.
+   */
+  bool gotPositive = false;
+
+  /**
+   * Did we see something that indicates this is, or at least might be,
+   * a negative number.
+   */
+  bool gotNegative = false;
+  /**
+   * Did we see the required positive suffix at the end. Should
+   * match [gotPositive].
+   */
+  bool gotPositiveSuffix = false;
+  /**
+   * Did we see the required negative suffix at the end. Should
+   * match [gotNegative].
+   */
+  bool gotNegativeSuffix = false;
+
+  /** Should we stop parsing before hitting the end of the string. */
+  bool done = false;
+
+  /** Have we already skipped over any required prefixes. */
+  bool prefixesSkipped = false;
+
+  /** If the number is percent or permill, what do we divide by at the end. */
+  int scale = 1;
+
+  String get _positivePrefix => format._positivePrefix;
+  String get _negativePrefix => format._negativePrefix;
+  String get _positiveSuffix => format._positiveSuffix;
+  String get _negativeSuffix => format._negativeSuffix;
+  int get _zero => format._zero;
+  int get _localeZero => format._localeZero;
+
+  /**
+   *  Create a new [_NumberParser] on which we can call parse().
+   */
+  _NumberParser(this.format, text)
+      : this.text = text,
+        this.input = new _Stream(text) {
+    value = parse();
+  }
+
+  /**
+   *  The strings we might replace with functions that return the replacement
+   * values. They are functions because we might need to check something
+   * in the context. Note that the ordering is important here. For example,
+   * [symbols.PERCENT] might be " %", and we must handle that before we
+   * look at an individual space.
+   */
+  Map<String, Function> get replacements => _replacements == null
+      ? _replacements = _initializeReplacements()
+      : _replacements;
+
+  var _replacements;
+
+  Map _initializeReplacements() => {
+    symbols.DECIMAL_SEP: () => '.',
+    symbols.EXP_SYMBOL: () => 'E',
+    symbols.GROUP_SEP: handleSpace,
+    symbols.PERCENT: () {
+      scale = _NumberFormatParser._PERCENT_SCALE;
+      return '';
+    },
+    symbols.PERMILL: () {
+      scale = _NumberFormatParser._PER_MILLE_SCALE;
+      return '';
+    },
+    ' ': handleSpace,
+    '\u00a0': handleSpace,
+    '+': () => '+',
+    '-': () => '-',
+  };
+
+  invalidFormat() =>
+      throw new FormatException("Invalid number: ${input.contents}");
+
+  /**
+   * Replace a space in the number with the normalized form. If space is not
+   * a significant character (normally grouping) then it's just invalid. If it
+   * is the grouping character, then it's only valid if it's followed by a
+   * digit. e.g. '$12 345.00'
+   */
+  handleSpace() =>
+      groupingIsNotASpaceOrElseItIsSpaceFollowedByADigit ? '' : invalidFormat();
+
+  /**
+   * Determine if a space is a valid character in the number. See [handleSpace].
+   */
+  bool get groupingIsNotASpaceOrElseItIsSpaceFollowedByADigit {
+    if (symbols.GROUP_SEP != '\u00a0' || symbols.GROUP_SEP != ' ') return true;
+    var peeked = input.peek(symbols.GROUP_SEP.length + 1);
+    return asDigit(peeked[peeked.length - 1]) != null;
+  }
+
+  /**
+   * Turn [char] into a number representing a digit, or null if it doesn't
+   * represent a digit in this locale.
+   */
+  int asDigit(String char) {
+    var charCode = char.codeUnitAt(0);
+    var digitValue = charCode - _localeZero;
+    if (digitValue >= 0 && digitValue < 10) {
+      return digitValue;
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * Check to see if the input begins with either the positive or negative
+   * prefixes. Set the [gotPositive] and [gotNegative] variables accordingly.
+   */
+  void checkPrefixes({bool skip: false}) {
+    bool checkPrefix(String prefix, skip) {
+      var matched = prefix.isNotEmpty && input.startsWith(prefix);
+      if (skip && matched) input.read(prefix.length);
+      return matched;
+    }
+
+    // TODO(alanknight): There's a faint possibility of a bug here where
+    // a positive prefix is followed by a negative prefix that's also a valid
+    // part of the number, but that seems very unlikely.
+    if (checkPrefix(_positivePrefix, skip)) gotPositive = true;
+    if (checkPrefix(_negativePrefix, skip)) gotNegative = true;
+
+    // Copied from Closure. It doesn't seem to be necessary to pass the test
+    // suite, so I'm not sure it's really needed.
+    if (gotPositive && gotNegative) {
+      if (_positivePrefix.length > _negativePrefix.length) {
+        gotNegative = false;
+      } else if (_negativePrefix.length > _positivePrefix.length) {
+        gotPositive = false;
+      }
+    }
+  }
+
+  /**
+   * If the rest of our input is either the positive or negative suffix,
+   * set [gotPositiveSuffix] or [gotNegativeSuffix] accordingly.
+   */
+  void checkSuffixes() {
+    var remainder = input.rest();
+    if (remainder == _positiveSuffix) gotPositiveSuffix = true;
+    if (remainder == _negativeSuffix) gotNegativeSuffix = true;
+  }
+
+  /**
+   * We've encountered a character that's not a digit. Go through our
+   * replacement rules looking for how to handle it. If we see something
+   * that's not a digit and doesn't have a replacement, then we're done
+   * and the number is probably invalid.
+   */
+  void processNonDigit() {
+    for (var key in replacements.keys) {
+      if (input.startsWith(key)) {
+        _normalized.write(replacements[key]());
+        input.read(key.length);
+        return;
+      }
+    }
+    // It might just be a prefix that we haven't skipped. We don't want to
+    // skip them initially because they might also be semantically meaningful,
+    // e.g. leading %. So we allow them through the loop, but only once.
+    if (input.index == 0 && !prefixesSkipped) {
+      prefixesSkipped = true;
+      checkPrefixes(skip: true);
+    } else {
+      done = true;
+    }
+  }
+
+  /**
+   * Parse [text] and return the resulting number. Throws [FormatException]
+   * if we can't parse it.
+   */
+  num parse() {
+    if (text == symbols.NAN) return double.NAN;
+    if (text == "$_positivePrefix${symbols.INFINITY}$_positiveSuffix") {
+      return double.INFINITY;
+    }
+    if (text == "$_negativePrefix${symbols.INFINITY}$_negativeSuffix") {
+      return double.NEGATIVE_INFINITY;
+    }
+
+    checkPrefixes();
+    var parsed = parseNumber(input);
+
+    if (gotPositive && !gotPositiveSuffix) invalidNumber();
+    if (gotNegative && !gotNegativeSuffix) invalidNumber();
+    if (!input.atEnd()) invalidNumber();
+
+    return parsed;
+  }
+
+  /** The number is invalid, throw a [FormatException]. */
+  void invalidNumber() =>
+      throw new FormatException("Invalid Number: ${input.contents}");
+
+  /**
+   * Parse the number portion of the input, i.e. not any prefixes or suffixes,
+   * and assuming NaN and Infinity are already handled.
+   */
+  num parseNumber(_Stream input) {
+    while (!done && !input.atEnd()) {
+      int digit = asDigit(input.peek());
+      if (digit != null) {
+        _normalized.writeCharCode(_zero + digit);
+        input.next();
+      } else {
+        processNonDigit();
+      }
+      checkSuffixes();
+    }
+
+    var normalizedText = _normalized.toString();
+    num parsed = int.parse(normalizedText, onError: (message) => null);
+    if (parsed == null) parsed = double.parse(normalizedText);
+    return parsed / scale;
+  }
+}
+
+/**
+ * Private class that parses the numeric formatting pattern and sets the
+ * variables in [format] to appropriate values. Instances of this are
+ * transient and store parsing state in instance variables, so can only be used
+ * to parse a single pattern.
+ */
+class _NumberFormatParser {
+
+  /**
+   * The special characters in the pattern language. All others are treated
+   * as literals.
+   */
+  static const _PATTERN_SEPARATOR = ';';
+  static const _QUOTE = "'";
+  static const _PATTERN_DIGIT = '#';
+  static const _PATTERN_ZERO_DIGIT = '0';
+  static const _PATTERN_GROUPING_SEPARATOR = ',';
+  static const _PATTERN_DECIMAL_SEPARATOR = '.';
+  static const _PATTERN_CURRENCY_SIGN = '\u00A4';
+  static const _PATTERN_PER_MILLE = '\u2030';
+  static const _PER_MILLE_SCALE = 1000;
+  static const _PATTERN_PERCENT = '%';
+  static const _PERCENT_SCALE = 100;
+  static const _PATTERN_EXPONENT = 'E';
+  static const _PATTERN_PLUS = '+';
+
+  /** The format whose state we are setting. */
+  final NumberFormat format;
+
+  /** The pattern we are parsing. */
+  final _StringIterator pattern;
+
+  /** We can be passed a specific currency symbol, regardless of the locale. */
+  String currencyName;
+
+  /**
+   * Create a new [_NumberFormatParser] for a particular [NumberFormat] and
+   * [input] pattern.
+   */
+  _NumberFormatParser(this.format, input, this.currencyName)
+      : pattern = _iterator(input) {
+    pattern.moveNext();
+  }
+
+  /** The [NumberSymbols] for the locale in which our [format] prints. */
+  NumberSymbols get symbols => format.symbols;
+
+  /** Parse the input pattern and set the values. */
+  void parse() {
+    format._positivePrefix = _parseAffix();
+    var trunk = _parseTrunk();
+    format._positiveSuffix = _parseAffix();
+    // If we have separate positive and negative patterns, now parse the
+    // the negative version.
+    if (pattern.current == _NumberFormatParser._PATTERN_SEPARATOR) {
+      pattern.moveNext();
+      format._negativePrefix = _parseAffix();
+      // Skip over the negative trunk, verifying that it's identical to the
+      // positive trunk.
+      for (var each in _iterable(trunk)) {
+        if (pattern.current != each && pattern.current != null) {
+          throw new FormatException(
+              "Positive and negative trunks must be the same");
+        }
+        pattern.moveNext();
+      }
+      format._negativeSuffix = _parseAffix();
+    } else {
+      // If no negative affix is specified, they share the same positive affix.
+      format._negativePrefix = format._negativePrefix + format._positivePrefix;
+      format._negativeSuffix = format._positiveSuffix + format._negativeSuffix;
+    }
+  }
+
+  /**
+   * Variable used in parsing prefixes and suffixes to keep track of
+   * whether or not we are in a quoted region.
+   */
+  bool inQuote = false;
+
+  /**
+   * Parse a prefix or suffix and return the prefix/suffix string. Note that
+   * this also may modify the state of [format].
+   */
+  String _parseAffix() {
+    var affix = new StringBuffer();
+    inQuote = false;
+    while (parseCharacterAffix(affix) && pattern.moveNext());
+    return affix.toString();
+  }
+
+  /**
+   * Parse an individual character as part of a prefix or suffix.  Return true
+   * if we should continue to look for more affix characters, and false if
+   * we have reached the end.
+   */
+  bool parseCharacterAffix(StringBuffer affix) {
+    var ch = pattern.current;
+    if (ch == null) return false;
+    if (ch == _QUOTE) {
+      if (pattern.peek == _QUOTE) {
+        pattern.moveNext();
+        affix.write(_QUOTE); // 'don''t'
+      } else {
+        inQuote = !inQuote;
+      }
+      return true;
+    }
+
+    if (inQuote) {
+      affix.write(ch);
+    } else {
+      switch (ch) {
+        case _PATTERN_DIGIT:
+        case _PATTERN_ZERO_DIGIT:
+        case _PATTERN_GROUPING_SEPARATOR:
+        case _PATTERN_DECIMAL_SEPARATOR:
+        case _PATTERN_SEPARATOR:
+          return false;
+        case _PATTERN_CURRENCY_SIGN:
+          // TODO(alanknight): Handle the local/global/portable currency signs
+          affix.write(currencyName);
+          break;
+        case _PATTERN_PERCENT:
+          if (format._multiplier != 1 && format._multiplier != _PERCENT_SCALE) {
+            throw new FormatException('Too many percent/permill');
+          }
+          format._multiplier = _PERCENT_SCALE;
+          affix.write(symbols.PERCENT);
+          break;
+        case _PATTERN_PER_MILLE:
+          if (format._multiplier != 1 &&
+              format._multiplier != _PER_MILLE_SCALE) {
+            throw new FormatException('Too many percent/permill');
+          }
+          format._multiplier = _PER_MILLE_SCALE;
+          affix.write(symbols.PERMILL);
+          break;
+        default:
+          affix.write(ch);
+      }
+    }
+    return true;
+  }
+
+  /** Variables used in [_parseTrunk] and [parseTrunkCharacter]. */
+  var decimalPos = -1;
+  var digitLeftCount = 0;
+  var zeroDigitCount = 0;
+  var digitRightCount = 0;
+  var groupingCount = -1;
+
+  /**
+   * Parse the "trunk" portion of the pattern, the piece that doesn't include
+   * positive or negative prefixes or suffixes.
+   */
+  String _parseTrunk() {
+    var loop = true;
+    var trunk = new StringBuffer();
+    while (pattern.current != null && loop) {
+      loop = parseTrunkCharacter(trunk);
+    }
+
+    if (zeroDigitCount == 0 && digitLeftCount > 0 && decimalPos >= 0) {
+      // Handle '###.###' and '###.' and '.###'
+      // Handle '.###'
+      var n = decimalPos == 0 ? 1 : decimalPos;
+      digitRightCount = digitLeftCount - n;
+      digitLeftCount = n - 1;
+      zeroDigitCount = 1;
+    }
+
+    // Do syntax checking on the digits.
+    if (decimalPos < 0 && digitRightCount > 0 ||
+        decimalPos >= 0 &&
+            (decimalPos < digitLeftCount ||
+                decimalPos > digitLeftCount + zeroDigitCount) ||
+        groupingCount == 0) {
+      throw new FormatException('Malformed pattern "${pattern.input}"');
+    }
+    var totalDigits = digitLeftCount + zeroDigitCount + digitRightCount;
+
+    format.maximumFractionDigits =
+        decimalPos >= 0 ? totalDigits - decimalPos : 0;
+    if (decimalPos >= 0) {
+      format.minimumFractionDigits =
+          digitLeftCount + zeroDigitCount - decimalPos;
+      if (format.minimumFractionDigits < 0) {
+        format.minimumFractionDigits = 0;
+      }
+    }
+
+    // The effectiveDecimalPos is the position the decimal is at or would be at
+    // if there is no decimal. Note that if decimalPos<0, then digitTotalCount
+    // == digitLeftCount + zeroDigitCount.
+    var effectiveDecimalPos = decimalPos >= 0 ? decimalPos : totalDigits;
+    format.minimumIntegerDigits = effectiveDecimalPos - digitLeftCount;
+    if (format._useExponentialNotation) {
+      format.maximumIntegerDigits =
+          digitLeftCount + format.minimumIntegerDigits;
+
+      // In exponential display, we need to at least show something.
+      if (format.maximumFractionDigits == 0 &&
+          format.minimumIntegerDigits == 0) {
+        format.minimumIntegerDigits = 1;
+      }
+    }
+
+    format._finalGroupingSize = max(0, groupingCount);
+    if (!format._groupingSizeSetExplicitly) {
+      format._groupingSize = format._finalGroupingSize;
+    }
+    format._decimalSeparatorAlwaysShown =
+        decimalPos == 0 || decimalPos == totalDigits;
+
+    return trunk.toString();
+  }
+
+  /**
+   * Parse an individual character of the trunk. Return true if we should
+   * continue to look for additional trunk characters or false if we have
+   * reached the end.
+   */
+  bool parseTrunkCharacter(trunk) {
+    var ch = pattern.current;
+    switch (ch) {
+      case _PATTERN_DIGIT:
+        if (zeroDigitCount > 0) {
+          digitRightCount++;
+        } else {
+          digitLeftCount++;
+        }
+        if (groupingCount >= 0 && decimalPos < 0) {
+          groupingCount++;
+        }
+        break;
+      case _PATTERN_ZERO_DIGIT:
+        if (digitRightCount > 0) {
+          throw new FormatException(
+              'Unexpected "0" in pattern "' + pattern.input + '"');
+        }
+        zeroDigitCount++;
+        if (groupingCount >= 0 && decimalPos < 0) {
+          groupingCount++;
+        }
+        break;
+      case _PATTERN_GROUPING_SEPARATOR:
+        if (groupingCount > 0) {
+          format._groupingSizeSetExplicitly = true;
+          format._groupingSize = groupingCount;
+        }
+        groupingCount = 0;
+        break;
+      case _PATTERN_DECIMAL_SEPARATOR:
+        if (decimalPos >= 0) {
+          throw new FormatException(
+              'Multiple decimal separators in pattern "$pattern"');
+        }
+        decimalPos = digitLeftCount + zeroDigitCount + digitRightCount;
+        break;
+      case _PATTERN_EXPONENT:
+        trunk.write(ch);
+        if (format._useExponentialNotation) {
+          throw new FormatException(
+              'Multiple exponential symbols in pattern "$pattern"');
+        }
+        format._useExponentialNotation = true;
+        format.minimumExponentDigits = 0;
+
+        // exponent pattern can have a optional '+'.
+        pattern.moveNext();
+        var nextChar = pattern.current;
+        if (nextChar == _PATTERN_PLUS) {
+          trunk.write(pattern.current);
+          pattern.moveNext();
+          format._useSignForPositiveExponent = true;
+        }
+
+        // Use lookahead to parse out the exponential part
+        // of the pattern, then jump into phase 2.
+        while (pattern.current == _PATTERN_ZERO_DIGIT) {
+          trunk.write(pattern.current);
+          pattern.moveNext();
+          format.minimumExponentDigits++;
+        }
+
+        if ((digitLeftCount + zeroDigitCount) < 1 ||
+            format.minimumExponentDigits < 1) {
+          throw new FormatException('Malformed exponential pattern "$pattern"');
+        }
+        return false;
+      default:
+        return false;
+    }
+    trunk.write(ch);
+    pattern.moveNext();
+    return true;
+  }
+}
+
+/**
+ * Returns an [Iterable] on the string as a list of substrings.
+ */
+Iterable _iterable(String s) => new _StringIterable(s);
+
+/**
+ * Return an iterator on the string as a list of substrings.
+ */
+Iterator _iterator(String s) => new _StringIterator(s);
+
+// TODO(nweiz): remove this when issue 3780 is fixed.
+/**
+ * Provides an Iterable that wraps [_iterator] so it can be used in a `for`
+ * loop.
+ */
+class _StringIterable extends IterableBase<String> {
+  final Iterator<String> iterator;
+
+  _StringIterable(String s) : iterator = _iterator(s);
+}
+
+/**
+ * Provides an iterator over a string as a list of substrings, and also
+ * gives us a lookahead of one via the [peek] method.
+ */
+class _StringIterator implements Iterator<String> {
+  final String input;
+  int nextIndex = 0;
+  String _current = null;
+
+  _StringIterator(input) : input = _validate(input);
+
+  String get current => _current;
+
+  bool moveNext() {
+    if (nextIndex >= input.length) {
+      _current = null;
+      return false;
+    }
+    _current = input[nextIndex++];
+    return true;
+  }
+
+  String get peek => nextIndex >= input.length ? null : input[nextIndex];
+
+  Iterator<String> get iterator => this;
+
+  static String _validate(input) {
+    if (input is! String) throw new ArgumentError(input);
+    return input;
+  }
+}
diff --git a/intl/lib/src/intl_helpers.dart b/intl/lib/src/intl_helpers.dart
new file mode 100644
index 0000000..f88ff8e
--- /dev/null
+++ b/intl/lib/src/intl_helpers.dart
@@ -0,0 +1,71 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * A library for general helper code associated with the intl library
+ * rather than confined to specific parts of it.
+ */
+
+library intl_helpers;
+
+import 'dart:async';
+
+/**
+ * This is used as a marker for a locale data map that hasn't been initialized,
+ * and will throw an exception on any usage that isn't the fallback
+ * patterns/symbols provided.
+ */
+class UninitializedLocaleData<F> {
+  final String message;
+  final F fallbackData;
+  const UninitializedLocaleData(this.message, this.fallbackData);
+
+  operator [](String key) =>
+      (key == 'en_US') ? fallbackData : _throwException();
+
+  String lookupMessage(String message_str, [final String desc = '',
+      final Map examples = const {}, String locale, String name,
+      List<String> args, String meaning]) => message_str;
+
+  List get keys => _throwException();
+
+  bool containsKey(String key) => (key == 'en_US') ? true : _throwException();
+
+  _throwException() {
+    throw new LocaleDataException("Locale data has not been initialized"
+        ", call $message.");
+  }
+}
+
+class LocaleDataException implements Exception {
+  final String message;
+  LocaleDataException(this.message);
+  toString() => "LocaleDataException: $message";
+}
+
+/**
+ *  An abstract superclass for data readers to keep the type system happy.
+ */
+abstract class LocaleDataReader {
+  Future read(String locale);
+}
+
+/**
+ * The internal mechanism for looking up messages. We expect this to be set
+ * by the implementing package so that we're not dependent on its
+ * implementation.
+ */
+var messageLookup =
+    const UninitializedLocaleData('initializeMessages(<locale>)', null);
+
+/**
+ * Initialize the message lookup mechanism. This is for internal use only.
+ * User applications should import `message_lookup_by_library.dart` and call
+ * `initializeMessages`
+ */
+void initializeInternalMessageLookup(Function lookupFunction) {
+  if (messageLookup is UninitializedLocaleData) {
+    messageLookup = lookupFunction();
+  }
+}
diff --git a/intl/lib/src/intl_message.dart b/intl/lib/src/intl_message.dart
new file mode 100644
index 0000000..28a118a
--- /dev/null
+++ b/intl/lib/src/intl_message.dart
@@ -0,0 +1,754 @@
+// Copyright (c) 2013, 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.
+
+/**
+ * This provides classes to represent the internal structure of the
+ * arguments to `Intl.message`. It is used when parsing sources to extract
+ * messages or to generate code for message substitution. Normal programs
+ * using Intl would not import this library.
+ *
+ * While it's written
+ * in a somewhat abstract way, it has some assumptions about ICU-style
+ * message syntax for parameter substitutions, choices, selects, etc.
+ *
+ * For example, if we have the message
+ *      plurals(num) => Intl.message("""${Intl.plural(num,
+ *          zero : 'Is zero plural?',
+ *          one : 'This is singular.',
+ *          other : 'This is plural ($num).')
+ *         }""",
+ *         name: "plurals", args: [num], desc: "Basic plurals");
+ * That is represented as a MainMessage which has only one message component, a
+ * Plural, but also has a name, list of arguments, and a description.
+ * The Plural has three different clauses. The `zero` clause is
+ * a LiteralString containing 'Is zero plural?'. The `other` clause is a
+ * CompositeMessage containing three pieces, a LiteralString for
+ * 'This is plural (', a VariableSubstitution for `num`. amd a LiteralString
+ * for '.)'.
+ *
+ * This representation isn't used at runtime. Rather, we read some format
+ * from a translation file, parse it into these objects, and they are then
+ * used to generate the code representation above.
+ */
+library intl_message;
+
+import 'package:analyzer/analyzer.dart';
+
+/** A default function for the [Message.expanded] method. */
+_nullTransform(msg, chunk) => chunk;
+
+/**
+ * An abstract superclass for Intl.message/plural/gender calls in the
+ * program's source text. We
+ * assemble these into objects that can be used to write out some translation
+ * format and can also print themselves into code.
+ */
+abstract class Message {
+
+  /**
+   * All [Message]s except a [MainMessage] are contained inside some parent,
+   * terminating at an Intl.message call which supplies the arguments we
+   * use for variable substitutions.
+   */
+  Message parent;
+
+  Message(this.parent);
+
+  /**
+   * We find the arguments from the top-level [MainMessage] and use those to
+   * do variable substitutions. [MainMessage] overrides this to return
+   * the actual arguments.
+   */
+  get arguments => parent == null ? const [] : parent.arguments;
+
+  /**
+   * We find the examples from the top-level [MainMessage] and use those
+   * when writing out variables. [MainMessage] overrides this to return
+   * the actual examples.
+   */
+  get examples => parent == null ? const [] : parent.examples;
+
+  /**
+   * The name of the top-level [MainMessage].
+   */
+  String get name => parent == null ? '<unnamed>' : parent.name;
+
+  String checkValidity(MethodInvocation node, List arguments, String outerName,
+      FormalParameterList outerArgs) {
+    var hasArgs = arguments.any(
+        (each) => each is NamedExpression && each.name.label.name == 'args');
+    var hasParameters = !outerArgs.parameters.isEmpty;
+    if (!hasArgs && hasParameters) {
+      return "The 'args' argument for Intl.message must be specified";
+    }
+
+    var messageName = arguments.firstWhere((eachArg) =>
+            eachArg is NamedExpression && eachArg.name.label.name == 'name',
+        orElse: () => null);
+    if (messageName == null) {
+      return "The 'name' argument for Intl.message must be specified";
+    }
+    if (messageName.expression is! SimpleStringLiteral) {
+      return "The 'name' argument for Intl.message must be a simple string "
+          "literal.";
+    }
+    if (outerName != null && outerName != messageName.expression.value) {
+      return "The 'name' argument for Intl.message must match "
+          "the name of the containing function ("
+          "'${messageName.expression.value}' vs. '$outerName')";
+    }
+    var simpleArguments = arguments.where((each) => each is NamedExpression &&
+        ["desc", "name"].contains(each.name.label.name));
+    var values = simpleArguments.map((each) => each.expression).toList();
+    for (var arg in values) {
+      if (arg is! StringLiteral) {
+        return ("Intl.message arguments must be string literals: $arg");
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Turn a value, typically read from a translation file or created out of an
+   * AST for a source program, into the appropriate
+   * subclass. We expect to get literal Strings, variable substitutions
+   * represented by integers, things that are already MessageChunks and
+   * lists of the same.
+   */
+  static Message from(value, Message parent) {
+    if (value is String) return new LiteralString(value, parent);
+    if (value is int) return new VariableSubstitution(value, parent);
+    if (value is Iterable) {
+      if (value.length == 1) return Message.from(value[0], parent);
+      var result = new CompositeMessage([], parent);
+      var items = value.map((x) => from(x, result)).toList();
+      result.pieces.addAll(items);
+      return result;
+    }
+    // We assume this is already a Message.
+    value.parent = parent;
+    return value;
+  }
+
+  /**
+   * Return a string representation of this message for use in generated Dart
+   * code.
+   */
+  String toCode();
+
+  /**
+   * Escape the string for use in generated Dart code and validate that it
+   * doesn't  doesn't contain any illegal interpolations. We only allow
+   * simple variables ("$foo", but not "${foo}") and Intl.gender/plural
+   * calls.
+   */
+  String escapeAndValidateString(String value) {
+    const escapes = const {
+      r"\": r"\\",
+      '"': r'\"',
+      "\b": r"\b",
+      "\f": r"\f",
+      "\n": r"\n",
+      "\r": r"\r",
+      "\t": r"\t",
+      "\v": r"\v",
+      "'": r"\'",
+    };
+
+    _escape(String s) => (escapes[s] == null) ? s : escapes[s];
+
+    var escaped = value.splitMapJoin("", onNonMatch: _escape);
+
+    // We don't allow any ${} expressions, only $variable to avoid malicious
+    // code. Disallow any usage of "${". If that makes a false positive
+    // on a translation that legitimately contains "\\${" or other variations,
+    // we'll live with that rather than risk a false negative.
+    var validInterpolations = new RegExp(r"(\$\w+)|(\${\w+})");
+    var validMatches = validInterpolations.allMatches(escaped);
+    escapeInvalidMatches(Match m) {
+      var valid = validMatches.any((x) => x.start == m.start);
+      if (valid) {
+        return m.group(0);
+      } else {
+        return "\\${m.group(0)}";
+      }
+    }
+    return escaped.replaceAllMapped("\$", escapeInvalidMatches);
+  }
+
+  /**
+   * Expand this string out into a printed form. The function [f] will be
+   * applied to any sub-messages, allowing this to be used to generate a form
+   * suitable for a wide variety of translation file formats.
+   */
+  String expanded([Function f]);
+}
+
+/**
+ * Abstract class for messages with internal structure, representing the
+ * main Intl.message call, plurals, and genders.
+ */
+abstract class ComplexMessage extends Message {
+  ComplexMessage(parent) : super(parent);
+
+  /**
+   * When we create these from strings or from AST nodes, we want to look up and
+   * set their attributes by string names, so we override the indexing operators
+   * so that they behave like maps with respect to those attribute names.
+   */
+  operator [](x);
+
+  /**
+   * When we create these from strings or from AST nodes, we want to look up and
+   * set their attributes by string names, so we override the indexing operators
+   * so that they behave like maps with respect to those attribute names.
+   */
+  operator []=(x, y);
+
+  List<String> get attributeNames;
+
+  /**
+   * Return the name of the message type, as it will be generated into an
+   * ICU-type format. e.g. choice, select
+   */
+  String get icuMessageName;
+
+  /**
+   * Return the message name we would use for this when doing Dart code
+   * generation, e.g. "Intl.plural".
+   */
+  String get dartMessageName;
+}
+
+/**
+ * This represents a message chunk that is a list of multiple sub-pieces,
+ * each of which is in turn a [Message].
+ */
+class CompositeMessage extends Message {
+  List<Message> pieces;
+
+  CompositeMessage.withParent(parent) : super(parent);
+  CompositeMessage(this.pieces, ComplexMessage parent) : super(parent) {
+    pieces.forEach((x) => x.parent = this);
+  }
+  toCode() => pieces.map((each) => each.toCode()).join('');
+  toString() => "CompositeMessage(" + pieces.toString() + ")";
+  String expanded([Function f = _nullTransform]) =>
+      pieces.map((chunk) => f(this, chunk)).join("");
+}
+
+/** Represents a simple constant string with no dynamic elements. */
+class LiteralString extends Message {
+  String string;
+  LiteralString(this.string, Message parent) : super(parent);
+  toCode() => escapeAndValidateString(string);
+  toString() => "Literal($string)";
+  String expanded([Function f = _nullTransform]) => f(this, string);
+}
+
+/**
+ * Represents an interpolation of a variable value in a message. We expect
+ * this to be specified as an [index] into the list of variables, or else
+ * as the name of a variable that exists in [arguments] and we will
+ * compute the variable name or the index based on the value of the other.
+ */
+class VariableSubstitution extends Message {
+  VariableSubstitution(this._index, Message parent) : super(parent);
+
+  /**
+   * Create a substitution based on the name rather than the index. The name
+   * may have been used as all upper-case in the translation tool, so we
+   * save it separately and look it up case-insensitively once the parent
+   * (and its arguments) are definitely available.
+   */
+  VariableSubstitution.named(String name, Message parent) : super(parent) {
+    _variableNameUpper = name.toUpperCase();
+  }
+
+  /** The index in the list of parameters of the containing function. */
+  int _index;
+  int get index {
+    if (_index != null) return _index;
+    if (arguments.isEmpty) return null;
+    // We may have been given an all-uppercase version of the name, so compare
+    // case-insensitive.
+    _index = arguments
+        .map((x) => x.toUpperCase())
+        .toList()
+        .indexOf(_variableNameUpper);
+    if (_index == -1) {
+      throw new ArgumentError(
+          "Cannot find parameter named '$_variableNameUpper' in "
+          "message named '$name'. Available "
+          "parameters are $arguments");
+    }
+    return _index;
+  }
+
+  /**
+   * The variable name we get from parsing. This may be an all uppercase version
+   * of the Dart argument name.
+   */
+  String _variableNameUpper;
+
+  /**
+   * The name of the variable in the parameter list of the containing function.
+   * Used when generating code for the interpolation.
+   */
+  String get variableName =>
+      _variableName == null ? _variableName = arguments[index] : _variableName;
+  String _variableName;
+  // Although we only allow simple variable references, we always enclose them
+  // in curly braces so that there's no possibility of ambiguity with
+  // surrounding text.
+  toCode() => "\${${variableName}}";
+  toString() => "VariableSubstitution($index)";
+  String expanded([Function f = _nullTransform]) => f(this, index);
+}
+
+class MainMessage extends ComplexMessage {
+  MainMessage() : super(null);
+
+  /**
+   * All the pieces of the message. When we go to print, these will
+   * all be expanded appropriately. The exact form depends on what we're
+   * printing it for See [expanded], [toCode].
+   */
+  List<Message> messagePieces = [];
+
+  /** Verify that this looks like a correct Intl.message invocation. */
+  String checkValidity(MethodInvocation node, List arguments, String outerName,
+      FormalParameterList outerArgs) {
+    if (arguments.first is! StringLiteral) {
+      return "Intl.message messages must be string literals";
+    }
+
+    return super.checkValidity(node, arguments, outerName, outerArgs);
+  }
+
+  void addPieces(List<Message> messages) {
+    for (var each in messages) {
+      messagePieces.add(Message.from(each, this));
+    }
+  }
+
+  /** The description provided in the Intl.message call. */
+  String description;
+
+  /** The examples from the Intl.message call */
+  Map<String, dynamic> examples;
+
+  /**
+   * A field to disambiguate two messages that might have exactly the
+   * same text. The two messages will also need different names, but
+   * this can be used by machine translation tools to distinguish them.
+   */
+  String meaning;
+
+  /**
+   * The name, which may come from the function name, from the arguments
+   * to Intl.message, or we may just re-use the message.
+   */
+  String _name;
+
+  /**
+   * A placeholder for any other identifier that the translation format
+   * may want to use.
+   */
+  String id;
+
+  /** The arguments list from the Intl.message call. */
+  List arguments;
+
+  /**
+   * When generating code, we store translations for each locale
+   * associated with the original message.
+   */
+  Map<String, String> translations = new Map();
+
+  /**
+   * If the message was not given a name, we use the entire message string as
+   * the name.
+   */
+  String get name => _name == null ? computeName() : _name;
+  set name(String newName) {
+    _name = newName;
+  }
+
+  String computeName() => name = expanded((msg, chunk) => "");
+
+  /**
+   * Return the full message, with any interpolation expressions transformed
+   * by [f] and all the results concatenated. The chunk argument to [f] may be
+   * either a String, an int or an object representing a more complex
+   * message entity.
+   * See [messagePieces].
+   */
+  String expanded([Function f = _nullTransform]) =>
+      messagePieces.map((chunk) => f(this, chunk)).join("");
+
+  /**
+   * Record the translation for this message in the given locale, after
+   * suitably escaping it.
+   */
+  void addTranslation(String locale, Message translated) {
+    translated.parent = this;
+    translations[locale] = translated.toCode();
+  }
+
+  toCode() =>
+      throw new UnsupportedError("MainMessage.toCode requires a locale");
+
+  /**
+   * Generate code for this message, expecting it to be part of a map
+   * keyed by name with values the function that calls Intl.message.
+   */
+  String toCodeForLocale(String locale) {
+    var out = new StringBuffer()
+      ..write('static $name(')
+      ..write(arguments.join(", "))
+      ..write(') => "')
+      ..write(translations[locale])
+      ..write('";');
+    return out.toString();
+  }
+
+  /**
+   * The AST node will have the attribute names as strings, so we translate
+   * between those and the fields of the class.
+   */
+  void operator []=(attributeName, value) {
+    switch (attributeName) {
+      case "desc":
+        description = value;
+        return;
+      case "examples":
+        examples = value;
+        return;
+      case "name":
+        name = value;
+        return;
+      // We use the actual args from the parser rather than what's given in the
+      // arguments to Intl.message.
+      case "args":
+        return;
+      case "meaning":
+        meaning = value;
+        return;
+      default:
+        return;
+    }
+  }
+
+  /**
+   * The AST node will have the attribute names as strings, so we translate
+   * between those and the fields of the class.
+   */
+  operator [](attributeName) {
+    switch (attributeName) {
+      case "desc":
+        return description;
+      case "examples":
+        return examples;
+      case "name":
+        return name;
+      // We use the actual args from the parser rather than what's given in the
+      // arguments to Intl.message.
+      case "args":
+        return [];
+      case "meaning":
+        return meaning;
+      default:
+        return null;
+    }
+  }
+
+  // This is the top-level construct, so there's no meaningful ICU name.
+  get icuMessageName => '';
+
+  get dartMessageName => "message";
+
+  /** The parameters that the Intl.message call may provide. */
+  get attributeNames => const ["name", "desc", "examples", "args", "meaning"];
+
+  String toString() =>
+      "Intl.message(${expanded()}, $name, $description, $examples, $arguments)";
+}
+
+/**
+ * An abstract class to represent sub-sections of a message, primarily
+ * plurals and genders.
+ */
+abstract class SubMessage extends ComplexMessage {
+  SubMessage() : super(null);
+
+  /**
+   * Creates the sub-message, given a list of [clauses] in the sort of form
+   * that we're likely to get them from parsing a translation file format,
+   * as a list of [key, value] where value may in turn be a list.
+   */
+  SubMessage.from(this.mainArgument, List clauses, parent) : super(parent) {
+    for (var clause in clauses) {
+      this[clause.first] = (clause.last is List) ? clause.last : [clause.last];
+    }
+  }
+
+  toString() => expanded();
+
+  /**
+   * The name of the main argument, which is expected to have the value
+   * which is one of [attributeNames] and is used to decide which clause to use.
+   */
+  String mainArgument;
+
+  /**
+   * Return the arguments that affect this SubMessage as a map of
+   * argument names and values.
+   */
+  Map argumentsOfInterestFor(MethodInvocation node) {
+    var basicArguments = node.argumentList.arguments;
+    var others = basicArguments.where((each) => each is NamedExpression);
+    return new Map.fromIterable(others,
+        key: (node) => node.name.label.token.value(),
+        value: (node) => node.expression);
+  }
+
+  /**
+   * Return the list of attribute names to use when generating code. This
+   *  may be different from [attributeNames] if there are multiple aliases
+   *  that map to the same clause.
+   */
+  List<String> get codeAttributeNames;
+
+  String expanded([Function transform = _nullTransform]) {
+    fullMessageForClause(key) =>
+        key + '{' + transform(parent, this[key]).toString() + '}';
+    var clauses = attributeNames
+        .where((key) => this[key] != null)
+        .map(fullMessageForClause)
+        .toList();
+    return "{$mainArgument,$icuMessageName, ${clauses.join("")}}";
+  }
+
+  String toCode() {
+    var out = new StringBuffer();
+    out.write('\${');
+    out.write(dartMessageName);
+    out.write('(');
+    out.write(mainArgument);
+    var args = codeAttributeNames.where((attribute) => this[attribute] != null);
+    args.fold(
+        out, (buffer, arg) => buffer..write(", $arg: '${this[arg].toCode()}'"));
+    out.write(")}");
+    return out.toString();
+  }
+}
+
+/**
+ * Represents a message send of [Intl.gender] inside a message that is to
+ * be internationalized. This corresponds to an ICU message syntax "select"
+ * with "male", "female", and "other" as the possible options.
+ */
+class Gender extends SubMessage {
+  Gender();
+  /**
+   * Create a new Gender providing [mainArgument] and the list of possible
+   * clauses. Each clause is expected to be a list whose first element is a
+   * variable name and whose second element is either a [String] or
+   * a list of strings and [Message] or [VariableSubstitution].
+   */
+  Gender.from(String mainArgument, List clauses, Message parent)
+      : super.from(mainArgument, clauses, parent);
+
+  Message female;
+  Message male;
+  Message other;
+
+  String get icuMessageName => "select";
+  String get dartMessageName => 'Intl.gender';
+
+  get attributeNames => ["female", "male", "other"];
+  get codeAttributeNames => attributeNames;
+
+  /**
+   * The node will have the attribute names as strings, so we translate
+   * between those and the fields of the class.
+   */
+  void operator []=(attributeName, rawValue) {
+    var value = Message.from(rawValue, this);
+    switch (attributeName) {
+      case "female":
+        female = value;
+        return;
+      case "male":
+        male = value;
+        return;
+      case "other":
+        other = value;
+        return;
+      default:
+        return;
+    }
+  }
+  Message operator [](String attributeName) {
+    switch (attributeName) {
+      case "female":
+        return female;
+      case "male":
+        return male;
+      case "other":
+        return other;
+      default:
+        return other;
+    }
+  }
+}
+
+class Plural extends SubMessage {
+  Plural();
+  Plural.from(String mainArgument, List clauses, Message parent)
+      : super.from(mainArgument, clauses, parent);
+
+  Message zero;
+  Message one;
+  Message two;
+  Message few;
+  Message many;
+  Message other;
+
+  String get icuMessageName => "plural";
+  String get dartMessageName => "Intl.plural";
+
+  get attributeNames => ["=0", "=1", "=2", "few", "many", "other"];
+  get codeAttributeNames => ["zero", "one", "two", "few", "many", "other"];
+
+  /**
+    * The node will have the attribute names as strings, so we translate
+    * between those and the fields of the class.
+    */
+  void operator []=(String attributeName, rawValue) {
+    var value = Message.from(rawValue, this);
+    switch (attributeName) {
+      case "zero":
+        zero = value;
+        return;
+      case "=0":
+        zero = value;
+        return;
+      case "one":
+        one = value;
+        return;
+      case "=1":
+        one = value;
+        return;
+      case "two":
+        two = value;
+        return;
+      case "=2":
+        two = value;
+        return;
+      case "few":
+        few = value;
+        return;
+      case "many":
+        many = value;
+        return;
+      case "other":
+        other = value;
+        return;
+      default:
+        return;
+    }
+  }
+
+  Message operator [](String attributeName) {
+    switch (attributeName) {
+      case "zero":
+        return zero;
+      case "=0":
+        return zero;
+      case "one":
+        return one;
+      case "=1":
+        return one;
+      case "two":
+        return two;
+      case "=2":
+        return two;
+      case "few":
+        return few;
+      case "many":
+        return many;
+      case "other":
+        return other;
+      default:
+        return other;
+    }
+  }
+}
+
+/**
+ * Represents a message send of [Intl.select] inside a message that is to
+ * be internationalized. This corresponds to an ICU message syntax "select"
+ * with arbitrary options.
+ */
+class Select extends SubMessage {
+  Select();
+  /**
+   * Create a new [Select] providing [mainArgument] and the list of possible
+   * clauses. Each clause is expected to be a list whose first element is a
+   * variable name and whose second element is either a String or
+   * a list of strings and [Message]s or [VariableSubstitution]s.
+   */
+  Select.from(String mainArgument, List clauses, Message parent)
+      : super.from(mainArgument, clauses, parent);
+
+  Map<String, Message> cases = new Map<String, Message>();
+
+  String get icuMessageName => "select";
+  String get dartMessageName => 'Intl.select';
+
+  get attributeNames => cases.keys;
+  get codeAttributeNames => attributeNames;
+
+  void operator []=(attributeName, rawValue) {
+    var value = Message.from(rawValue, this);
+    cases[attributeName] = value;
+  }
+
+  Message operator [](String attributeName) {
+    var exact = cases[attributeName];
+    return exact == null ? cases["other"] : exact;
+  }
+
+  /**
+   * Return the arguments that we care about for the select. In this
+   * case they will all be passed in as a Map rather than as the named
+   * arguments used in Plural/Gender.
+   */
+  Map argumentsOfInterestFor(MethodInvocation node) {
+    var casesArgument = node.argumentList.arguments[1];
+    return new Map.fromIterable(casesArgument.entries,
+        key: (node) => node.key.value, value: (node) => node.value);
+  }
+
+  /**
+   * Write out the generated representation of this message. This differs
+   * from Plural/Gender in that it prints a literal map rather than
+   * named arguments.
+   */
+  String toCode() {
+    var out = new StringBuffer();
+    out.write('\${');
+    out.write(dartMessageName);
+    out.write('(');
+    out.write(mainArgument);
+    var args = codeAttributeNames;
+    out.write(", {");
+    args.fold(out,
+        (buffer, arg) => buffer..write("'$arg': '${this[arg].toCode()}', "));
+    out.write("})}");
+    return out.toString();
+  }
+}
diff --git a/intl/lib/src/lazy_locale_data.dart b/intl/lib/src/lazy_locale_data.dart
new file mode 100644
index 0000000..9be1c0d
--- /dev/null
+++ b/intl/lib/src/lazy_locale_data.dart
@@ -0,0 +1,116 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * This defines a class for loading locale data incrementally from
+ * an external source as JSON. The external sources expected are either
+ * local files or via HTTP request.
+ */
+
+library lazy_locale_data;
+
+import 'dart:async';
+import 'dart:convert';
+import 'intl_helpers.dart';
+
+/**
+ * This implements the very basic map-type operations which are used
+ * in locale lookup, and looks them up based on a URL that defines
+ * the external source.
+ */
+class LazyLocaleData {
+  /// This holds the data we have loaded.
+  Map map;
+
+  /// The object that actually does the data reading.
+  LocaleDataReader _reader;
+
+  /**
+   * In order to avoid a potentially remote call to see if a locale
+   * is available, we hold a complete list of all the available
+   * locales.
+   */
+  List availableLocales;
+
+  /**
+   * Given a piece of remote data, apply [_creationFunction] to it to
+   * convert it into the right form. Typically this means converting it
+   * from a Map into an object form.
+   */
+  Function _creationFunction;
+
+  /**
+   * The set of available locales.
+   */
+  Set availableLocaleSet;
+
+  /**
+   * The constructor. The [_reader] specifies where the data comes
+   * from. The [_creationFunction] creates the appropriate data type
+   * from the remote data (which typically comes in as a Map). The
+   * [keys] lists the set of remotely available locale names so we know which
+   * things can be fetched without having to check remotely.
+   */
+  LazyLocaleData(this._reader, this._creationFunction, List keys) {
+    map = new Map();
+    availableLocales = keys;
+    availableLocaleSet = new Set.from(availableLocales);
+  }
+
+  /**
+   *  Tests if we have data for the locale available. Note that this returns
+   * true even if the data is known to be available remotely but not yet loaded.
+   */
+  bool containsKey(String locale) => availableLocaleSet.contains(locale);
+
+  /** Returns the list of keys/locale names. */
+  List get keys => availableLocales;
+
+  /**
+   * Returns the data stored for [localeName]. If no data has been loaded
+   * for [localeName], throws an exception. If no data is available for
+   * [localeName] then throw an exception with a different message.
+   */
+  operator [](String localeName) {
+    if (containsKey(localeName)) {
+      var data = map[localeName];
+      if (data == null) {
+        throw new LocaleDataException(
+            "Locale $localeName has not been initialized."
+            " Call initializeDateFormatting($localeName, <data url>) first");
+      } else {
+        return data;
+      }
+    } else {
+      unsupportedLocale(localeName);
+    }
+  }
+
+  /**
+   * Throw an exception indicating that the locale has no data available,
+   * either locally or remotely.
+   */
+  unsupportedLocale(localeName) {
+    throw new LocaleDataException('Locale $localeName has no data available');
+  }
+
+  /**
+   * Initialize for locale. Internal use only. As a user, call
+   * initializeDateFormatting instead.
+   */
+  Future initLocale(String localeName) {
+    var data = _reader.read(localeName);
+    return jsonData(data).then((input) {
+      map[localeName] = _creationFunction(input);
+    });
+  }
+
+  /**
+   * Given a Future [input] whose value is expected to be a string in JSON form,
+   * return another future that parses the JSON into a usable format.
+   */
+  Future jsonData(Future input) {
+    return input.then((response) => JSON.decode(response));
+  }
+}
diff --git a/intl/pubspec.yaml b/intl/pubspec.yaml
new file mode 100644
index 0000000..c191723
--- /dev/null
+++ b/intl/pubspec.yaml
@@ -0,0 +1,31 @@
+name: intl
+version: 0.12.3
+author: Dart Team <misc@dartlang.org>
+description: Contains code to deal with internationalized/localized messages, date and number formatting and parsing, bi-directional text, and other internationalization issues.
+homepage: https://github.com/dart-lang/intl
+environment:
+  sdk: '>=1.4.0 <2.0.0'
+documentation: http://www.dartdocs.org/documentation/intl/latest
+dependencies:
+  analyzer: '>=0.13.2 <0.26.0'
+  args: '>=0.12.1 <0.14.0'
+  path: '>=0.9.0 <2.0.0'
+  petitparser: '>=1.1.3 <2.0.0'
+dev_dependencies:
+  fixnum: '>=0.9.0 <0.10.0'
+  unittest: '>=0.10.0 <0.12.0'
+transformers:
+- $dart2js:
+    $exclude:
+    - test/date_time_format_file_even_test.dart
+    - test/date_time_format_file_odd_test.dart
+    - test/find_default_locale_standalone_test.dart
+    - test/message_extraction/embedded_plural_text_after_test.dart
+    - test/message_extraction/embedded_plural_text_before_test.dart
+    - test/message_extraction/examples_parsing_test.dart
+    - test/message_extraction/failed_extraction_test.dart
+    - test/message_extraction/make_hardcoded_translation.dart
+    - test/message_extraction/message_extraction_no_deferred_test.dart
+    - test/message_extraction/message_extraction_test.dart
+    - test/message_extraction/really_fail_extraction_test.dart
+    - test/intl_message_basic_example_test.dart # invalid import under pub's package-layout
diff --git a/logging/lib/logging.dart b/logging/lib/logging.dart
new file mode 100644
index 0000000..42344e8
--- /dev/null
+++ b/logging/lib/logging.dart
@@ -0,0 +1,337 @@
+// Copyright (c) 2012, 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 logging;
+
+import 'dart:async';
+import 'dart:collection';
+
+/**
+ * Whether to allow fine-grain logging and configuration of loggers in a
+ * hierarchy. When false, all logging is merged in the root logger.
+ */
+bool hierarchicalLoggingEnabled = false;
+
+/**
+ * Level for the root-logger. This will be the level of all loggers if
+ * [hierarchicalLoggingEnabled] is false.
+ */
+Level _rootLevel = Level.INFO;
+
+
+/**
+ * Use a [Logger] to log debug messages. [Logger]s are named using a
+ * hierarchical dot-separated name convention.
+ */
+class Logger {
+  /** Simple name of this logger. */
+  final String name;
+
+  /** The full name of this logger, which includes the parent's full name. */
+  String get fullName =>
+      (parent == null || parent.name == '') ? name : '${parent.fullName}.$name';
+
+  /** Parent of this logger in the hierarchy of loggers. */
+  final Logger parent;
+
+  /** Logging [Level] used for entries generated on this logger. */
+  Level _level;
+
+  final Map<String, Logger> _children;
+
+  /** Children in the hierarchy of loggers, indexed by their simple names. */
+  final Map<String, Logger> children;
+
+  /** Controller used to notify when log entries are added to this logger. */
+  StreamController<LogRecord> _controller;
+
+  /**
+   * Singleton constructor. Calling `new Logger(name)` will return the same
+   * actual instance whenever it is called with the same string name.
+   */
+  factory Logger(String name) {
+    return _loggers.putIfAbsent(name, () => new Logger._named(name));
+  }
+
+  factory Logger._named(String name) {
+    if (name.startsWith('.')) {
+      throw new ArgumentError("name shouldn't start with a '.'");
+    }
+    // Split hierarchical names (separated with '.').
+    int dot = name.lastIndexOf('.');
+    Logger parent = null;
+    String thisName;
+    if (dot == -1) {
+      if (name != '') parent = new Logger('');
+      thisName = name;
+    } else {
+      parent = new Logger(name.substring(0, dot));
+      thisName = name.substring(dot + 1);
+    }
+    return new Logger._internal(thisName, parent, new Map<String, Logger>());
+  }
+
+  Logger._internal(this.name, this.parent, Map<String, Logger> children) :
+    this._children = children,
+    this.children = new UnmodifiableMapView(children) {
+    if (parent != null) parent._children[name] = this;
+  }
+
+  /**
+   * Effective level considering the levels established in this logger's parents
+   * (when [hierarchicalLoggingEnabled] is true).
+   */
+  Level get level {
+    if (hierarchicalLoggingEnabled) {
+      if (_level != null) return _level;
+      if (parent != null) return parent.level;
+    }
+    return _rootLevel;
+  }
+
+  /** Override the level for this particular [Logger] and its children. */
+  void set level(Level value) {
+    if (hierarchicalLoggingEnabled && parent != null) {
+      _level = value;
+    } else {
+      if (parent != null) {
+        throw new UnsupportedError(
+            'Please set "hierarchicalLoggingEnabled" to true if you want to '
+            'change the level on a non-root logger.');
+      }
+      _rootLevel = value;
+    }
+  }
+
+  /**
+   * Returns an stream of messages added to this [Logger]. You can listen for
+   * messages using the standard stream APIs, for instance:
+   *    logger.onRecord.listen((record) { ... });
+   */
+  Stream<LogRecord> get onRecord => _getStream();
+
+  void clearListeners() {
+    if (hierarchicalLoggingEnabled || parent == null) {
+      if (_controller != null) {
+        _controller.close();
+        _controller = null;
+      }
+    } else {
+      root.clearListeners();
+    }
+  }
+
+  /** Whether a message for [value]'s level is loggable in this logger. */
+  bool isLoggable(Level value) => (value >= level);
+
+  /**
+   * Adds a log record for a [message] at a particular [logLevel] if
+   * `isLoggable(logLevel)` is true.
+   *
+   * Use this method to create log entries for user-defined levels. To record a
+   * message at a predefined level (e.g. [Level.INFO], [Level.WARNING], etc) you
+   * can use their specialized methods instead (e.g. [info], [warning], etc).
+   *
+   * If [message] is a [Function], it will be lazy evaluated. Additionally, if
+   * [message] or its evaluated value is not a [String], then 'toString()' will
+   * be called on it and the result will be logged.
+   *
+   * The log record will contain a field for the zone in which this call was
+   * made.
+   * This can be advantagous if a log listener wants to handle records of
+   * different zones differently (e.g. group log records by http-request if each
+   * http-request handler runs in it's own zone).
+   */
+  void log(Level logLevel,
+           message,
+           [Object error, StackTrace stackTrace, Zone zone]) {
+    if (isLoggable(logLevel)) {
+      // If message is a Function, evaluate it.
+      if (message is Function) message = message();
+      // If message is still not a String, call toString().
+      if (message is! String) message = message.toString();
+      // Only record the current zone if it was not given.
+      if (zone == null) zone = Zone.current;
+
+      var record = new LogRecord(logLevel, message, fullName, error,
+          stackTrace, zone);
+
+      if (hierarchicalLoggingEnabled) {
+        var target = this;
+        while (target != null) {
+          target._publish(record);
+          target = target.parent;
+        }
+      } else {
+        root._publish(record);
+      }
+    }
+  }
+
+  /** Log message at level [Level.FINEST]. */
+  void finest(message, [Object error, StackTrace stackTrace]) =>
+      log(Level.FINEST, message, error, stackTrace);
+
+  /** Log message at level [Level.FINER]. */
+  void finer(message, [Object error, StackTrace stackTrace]) =>
+      log(Level.FINER, message, error, stackTrace);
+
+  /** Log message at level [Level.FINE]. */
+  void fine(message, [Object error, StackTrace stackTrace]) =>
+      log(Level.FINE, message, error, stackTrace);
+
+  /** Log message at level [Level.CONFIG]. */
+  void config(message, [Object error, StackTrace stackTrace]) =>
+      log(Level.CONFIG, message, error, stackTrace);
+
+  /** Log message at level [Level.INFO]. */
+  void info(message, [Object error, StackTrace stackTrace]) =>
+      log(Level.INFO, message, error, stackTrace);
+
+  /** Log message at level [Level.WARNING]. */
+  void warning(message, [Object error, StackTrace stackTrace]) =>
+      log(Level.WARNING, message, error, stackTrace);
+
+  /** Log message at level [Level.SEVERE]. */
+  void severe(message, [Object error, StackTrace stackTrace]) =>
+      log(Level.SEVERE, message, error, stackTrace);
+
+  /** Log message at level [Level.SHOUT]. */
+  void shout(message, [Object error, StackTrace stackTrace]) =>
+      log(Level.SHOUT, message, error, stackTrace);
+
+  Stream<LogRecord> _getStream() {
+    if (hierarchicalLoggingEnabled || parent == null) {
+      if (_controller == null) {
+        _controller = new StreamController<LogRecord>.broadcast(sync: true);
+      }
+      return _controller.stream;
+    } else {
+      return root._getStream();
+    }
+  }
+
+  void _publish(LogRecord record) {
+    if (_controller != null) {
+      _controller.add(record);
+    }
+  }
+
+  /** Top-level root [Logger]. */
+  static Logger get root => new Logger('');
+
+  /** All [Logger]s in the system. */
+  static final Map<String, Logger> _loggers = <String, Logger>{};
+}
+
+
+/** Handler callback to process log entries as they are added to a [Logger]. */
+typedef void LoggerHandler(LogRecord);
+
+/**
+ * [Level]s to control logging output. Logging can be enabled to include all
+ * levels above certain [Level]. [Level]s are ordered using an integer
+ * value [Level.value]. The predefined [Level] constants below are sorted as
+ * follows (in descending order): [Level.SHOUT], [Level.SEVERE],
+ * [Level.WARNING], [Level.INFO], [Level.CONFIG], [Level.FINE], [Level.FINER],
+ * [Level.FINEST], and [Level.ALL].
+ *
+ * We recommend using one of the predefined logging levels. If you define your
+ * own level, make sure you use a value between those used in [Level.ALL] and
+ * [Level.OFF].
+ */
+class Level implements Comparable<Level> {
+
+  final String name;
+
+  /**
+   * Unique value for this level. Used to order levels, so filtering can exclude
+   * messages whose level is under certain value.
+   */
+  final int value;
+
+  const Level(this.name, this.value);
+
+  /** Special key to turn on logging for all levels ([value] = 0). */
+  static const Level ALL = const Level('ALL', 0);
+
+  /** Special key to turn off all logging ([value] = 2000). */
+  static const Level OFF = const Level('OFF', 2000);
+
+  /** Key for highly detailed tracing ([value] = 300). */
+  static const Level FINEST = const Level('FINEST', 300);
+
+  /** Key for fairly detailed tracing ([value] = 400). */
+  static const Level FINER = const Level('FINER', 400);
+
+  /** Key for tracing information ([value] = 500). */
+  static const Level FINE = const Level('FINE', 500);
+
+  /** Key for static configuration messages ([value] = 700). */
+  static const Level CONFIG = const Level('CONFIG', 700);
+
+  /** Key for informational messages ([value] = 800). */
+  static const Level INFO = const Level('INFO', 800);
+
+  /** Key for potential problems ([value] = 900). */
+  static const Level WARNING = const Level('WARNING', 900);
+
+  /** Key for serious failures ([value] = 1000). */
+  static const Level SEVERE = const Level('SEVERE', 1000);
+
+  /** Key for extra debugging loudness ([value] = 1200). */
+  static const Level SHOUT = const Level('SHOUT', 1200);
+
+  static const List<Level> LEVELS = const
+      [ALL, FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE, SHOUT, OFF];
+
+  bool operator ==(other) => other is Level && value == other.value;
+  bool operator <(Level other) => value < other.value;
+  bool operator <=(Level other) => value <= other.value;
+  bool operator >(Level other) => value > other.value;
+  bool operator >=(Level other) => value >= other.value;
+  int compareTo(Level other) => value - other.value;
+  int get hashCode => value;
+  String toString() => name;
+}
+
+
+/**
+ * A log entry representation used to propagate information from [Logger] to
+ * individual [Handler]s.
+ */
+class LogRecord {
+  final Level level;
+  final String message;
+
+  /** Logger where this record is stored. */
+  final String loggerName;
+
+  /** Time when this record was created. */
+  final DateTime time;
+
+  /** Unique sequence number greater than all log records created before it. */
+  final int sequenceNumber;
+
+  static int _nextNumber = 0;
+
+  /** Associated error (if any) when recording errors messages. */
+  final Object error;
+
+  /** Associated stackTrace (if any) when recording errors messages. */
+  final StackTrace stackTrace;
+
+  /** Zone of the calling code which resulted in this LogRecord. */
+  final Zone zone;
+
+  LogRecord(this.level, this.message, this.loggerName, [this.error,
+                                                        this.stackTrace,
+                                                        this.zone])
+      : time = new DateTime.now(),
+        sequenceNumber = LogRecord._nextNumber++;
+
+  String toString() => '[${level.name}] $loggerName: $message';
+}
diff --git a/logging/pubspec.yaml b/logging/pubspec.yaml
new file mode 100644
index 0000000..3867be5
--- /dev/null
+++ b/logging/pubspec.yaml
@@ -0,0 +1,12 @@
+name: logging
+version: 0.9.3
+author: Dart Team <misc@dartlang.org>
+description: >
+  Provides APIs for debugging and error logging. This library introduces
+  abstractions similar to those used in other languages, such as the Closure
+  JS Logger and java.util.logging.Logger.
+homepage: https://github.com/dart-lang/logging
+environment:
+  sdk: '>=1.5.0 <2.0.0'
+dev_dependencies:
+  unittest: '>=0.9.0 <0.12.0'
diff --git a/matcher/lib/matcher.dart b/matcher/lib/matcher.dart
new file mode 100644
index 0000000..c7790c9
--- /dev/null
+++ b/matcher/lib/matcher.dart
@@ -0,0 +1,22 @@
+// 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.
+
+/// Support for specifying test expectations, such as for unit tests.
+library matcher;
+
+export 'src/core_matchers.dart';
+export 'src/description.dart';
+export 'src/error_matchers.dart';
+export 'src/expect.dart';
+export 'src/future_matchers.dart';
+export 'src/interfaces.dart';
+export 'src/iterable_matchers.dart';
+export 'src/map_matchers.dart';
+export 'src/numeric_matchers.dart';
+export 'src/operator_matchers.dart';
+export 'src/prints_matcher.dart';
+export 'src/string_matchers.dart';
+export 'src/throws_matcher.dart';
+export 'src/throws_matchers.dart';
+export 'src/util.dart';
diff --git a/matcher/lib/mirror_matchers.dart b/matcher/lib/mirror_matchers.dart
new file mode 100644
index 0000000..d244831
--- /dev/null
+++ b/matcher/lib/mirror_matchers.dart
@@ -0,0 +1,79 @@
+// 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.
+
+/// The mirror matchers library provides some additional matchers that
+/// make use of `dart:mirrors`.
+library matcher.mirror_matchers;
+
+import 'dart:mirrors';
+
+import 'matcher.dart';
+
+/// Returns a matcher that checks if a class instance has a property
+/// with name [name], and optionally, if that property in turn satisfies
+/// a [matcher].
+Matcher hasProperty(String name, [matcher]) =>
+    new _HasProperty(name, matcher == null ? null : wrapMatcher(matcher));
+
+class _HasProperty extends Matcher {
+  final String _name;
+  final Matcher _matcher;
+
+  const _HasProperty(this._name, [this._matcher]);
+
+  bool matches(item, Map matchState) {
+    var mirror = reflect(item);
+    var classMirror = mirror.type;
+    var symbol = new Symbol(_name);
+    var candidate = classMirror.declarations[symbol];
+    if (candidate == null) {
+      addStateInfo(matchState, {'reason': 'has no property named "$_name"'});
+      return false;
+    }
+    bool isInstanceField = candidate is VariableMirror && !candidate.isStatic;
+    bool isInstanceGetter =
+        candidate is MethodMirror && candidate.isGetter && !candidate.isStatic;
+    if (!(isInstanceField || isInstanceGetter)) {
+      addStateInfo(matchState, {
+        'reason':
+            'has a member named "$_name", but it is not an instance property'
+      });
+      return false;
+    }
+    if (_matcher == null) return true;
+    var result = mirror.getField(symbol);
+    var resultMatches = _matcher.matches(result.reflectee, matchState);
+    if (!resultMatches) {
+      addStateInfo(matchState, {'value': result.reflectee});
+    }
+    return resultMatches;
+  }
+
+  Description describe(Description description) {
+    description.add('has property "$_name"');
+    if (_matcher != null) {
+      description.add(' which matches ').addDescriptionOf(_matcher);
+    }
+    return description;
+  }
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    var reason = matchState == null ? null : matchState['reason'];
+    if (reason != null) {
+      mismatchDescription.add(reason);
+    } else {
+      mismatchDescription
+          .add('has property "$_name" with value ')
+          .addDescriptionOf(matchState['value']);
+      var innerDescription = new StringDescription();
+      _matcher.describeMismatch(
+          matchState['value'], innerDescription, matchState['state'], verbose);
+      if (innerDescription.length > 0) {
+        mismatchDescription.add(' which ').add(innerDescription.toString());
+      }
+    }
+    return mismatchDescription;
+  }
+}
diff --git a/matcher/lib/src/core_matchers.dart b/matcher/lib/src/core_matchers.dart
new file mode 100644
index 0000000..ba0ac2e
--- /dev/null
+++ b/matcher/lib/src/core_matchers.dart
@@ -0,0 +1,645 @@
+// Copyright (c) 2012, 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 matcher.core_matchers;
+
+import 'description.dart';
+import 'interfaces.dart';
+import 'util.dart';
+
+/// Returns a matcher that matches the isEmpty property.
+const Matcher isEmpty = const _Empty();
+
+class _Empty extends Matcher {
+  const _Empty();
+
+  bool matches(item, Map matchState) => item.isEmpty;
+
+  Description describe(Description description) => description.add('empty');
+}
+
+/// Returns a matcher that matches the isNotEmpty property.
+const Matcher isNotEmpty = const _NotEmpty();
+
+class _NotEmpty extends Matcher {
+  const _NotEmpty();
+
+  bool matches(item, Map matchState) => item.isNotEmpty;
+
+  Description describe(Description description) => description.add('non-empty');
+}
+
+/// A matcher that matches any null value.
+const Matcher isNull = const _IsNull();
+
+/// A matcher that matches any non-null value.
+const Matcher isNotNull = const _IsNotNull();
+
+class _IsNull extends Matcher {
+  const _IsNull();
+  bool matches(item, Map matchState) => item == null;
+  Description describe(Description description) => description.add('null');
+}
+
+class _IsNotNull extends Matcher {
+  const _IsNotNull();
+  bool matches(item, Map matchState) => item != null;
+  Description describe(Description description) => description.add('not null');
+}
+
+/// A matcher that matches the Boolean value true.
+const Matcher isTrue = const _IsTrue();
+
+/// A matcher that matches anything except the Boolean value true.
+const Matcher isFalse = const _IsFalse();
+
+class _IsTrue extends Matcher {
+  const _IsTrue();
+  bool matches(item, Map matchState) => item == true;
+  Description describe(Description description) => description.add('true');
+}
+
+class _IsFalse extends Matcher {
+  const _IsFalse();
+  bool matches(item, Map matchState) => item == false;
+  Description describe(Description description) => description.add('false');
+}
+
+/// A matcher that matches the numeric value NaN.
+const Matcher isNaN = const _IsNaN();
+
+/// A matcher that matches any non-NaN value.
+const Matcher isNotNaN = const _IsNotNaN();
+
+class _IsNaN extends Matcher {
+  const _IsNaN();
+  bool matches(item, Map matchState) => double.NAN.compareTo(item) == 0;
+  Description describe(Description description) => description.add('NaN');
+}
+
+class _IsNotNaN extends Matcher {
+  const _IsNotNaN();
+  bool matches(item, Map matchState) => double.NAN.compareTo(item) != 0;
+  Description describe(Description description) => description.add('not NaN');
+}
+
+/// Returns a matches that matches if the value is the same instance
+/// as [expected], using [identical].
+Matcher same(expected) => new _IsSameAs(expected);
+
+class _IsSameAs extends Matcher {
+  final _expected;
+  const _IsSameAs(this._expected);
+  bool matches(item, Map matchState) => identical(item, _expected);
+  // If all types were hashable we could show a hash here.
+  Description describe(Description description) =>
+      description.add('same instance as ').addDescriptionOf(_expected);
+}
+
+/// Returns a matcher that matches if the value is structurally equal to
+/// [expected].
+///
+/// If [expected] is a [Matcher], then it matches using that. Otherwise it tests
+/// for equality using `==` on the expected value.
+///
+/// For [Iterable]s and [Map]s, this will recursively match the elements. To
+/// handle cyclic structures a recursion depth [limit] can be provided. The
+/// default limit is 100. [Set]s will be compared order-independently.
+Matcher equals(expected, [int limit = 100]) => expected is String
+    ? new _StringEqualsMatcher(expected)
+    : new _DeepMatcher(expected, limit);
+
+class _DeepMatcher extends Matcher {
+  final _expected;
+  final int _limit;
+  var count;
+
+  _DeepMatcher(this._expected, [int limit = 1000]) : this._limit = limit;
+
+  // Returns a pair (reason, location)
+  List _compareIterables(expected, actual, matcher, depth, location) {
+    if (actual is! Iterable) return ['is not Iterable', location];
+
+    var expectedIterator = expected.iterator;
+    var actualIterator = actual.iterator;
+    for (var index = 0; ; index++) {
+      // Advance in lockstep.
+      var expectedNext = expectedIterator.moveNext();
+      var actualNext = actualIterator.moveNext();
+
+      // If we reached the end of both, we succeeded.
+      if (!expectedNext && !actualNext) return null;
+
+      // Fail if their lengths are different.
+      var newLocation = '${location}[${index}]';
+      if (!expectedNext) return ['longer than expected', newLocation];
+      if (!actualNext) return ['shorter than expected', newLocation];
+
+      // Match the elements.
+      var rp = matcher(
+          expectedIterator.current, actualIterator.current, newLocation, depth);
+      if (rp != null) return rp;
+    }
+  }
+
+  List _compareSets(Set expected, actual, matcher, depth, location) {
+    if (actual is! Iterable) return ['is not Iterable', location];
+    actual = actual.toSet();
+
+    for (var expectedElement in expected) {
+      if (actual.every((actualElement) =>
+          matcher(expectedElement, actualElement, location, depth) != null)) {
+        return ['does not contain $expectedElement', location];
+      }
+    }
+
+    if (actual.length > expected.length) {
+      return ['larger than expected', location];
+    } else if (actual.length < expected.length) {
+      return ['smaller than expected', location];
+    } else {
+      return null;
+    }
+  }
+
+  List _recursiveMatch(expected, actual, String location, int depth) {
+    // If the expected value is a matcher, try to match it.
+    if (expected is Matcher) {
+      var matchState = {};
+      if (expected.matches(actual, matchState)) return null;
+
+      var description = new StringDescription();
+      expected.describe(description);
+      return ['does not match $description', location];
+    } else {
+      // Otherwise, test for equality.
+      try {
+        if (expected == actual) return null;
+      } catch (e) {
+        // TODO(gram): Add a test for this case.
+        return ['== threw "$e"', location];
+      }
+    }
+
+    if (depth > _limit) return ['recursion depth limit exceeded', location];
+
+    // If _limit is 1 we can only recurse one level into object.
+    if (depth == 0 || _limit > 1) {
+      if (expected is Set) {
+        return _compareSets(
+            expected, actual, _recursiveMatch, depth + 1, location);
+      } else if (expected is Iterable) {
+        return _compareIterables(
+            expected, actual, _recursiveMatch, depth + 1, location);
+      } else if (expected is Map) {
+        if (actual is! Map) return ['expected a map', location];
+
+        var err = (expected.length == actual.length)
+            ? ''
+            : 'has different length and ';
+        for (var key in expected.keys) {
+          if (!actual.containsKey(key)) {
+            return ["${err}is missing map key '$key'", location];
+          }
+        }
+
+        for (var key in actual.keys) {
+          if (!expected.containsKey(key)) {
+            return ["${err}has extra map key '$key'", location];
+          }
+        }
+
+        for (var key in expected.keys) {
+          var rp = _recursiveMatch(
+              expected[key], actual[key], "${location}['${key}']", depth + 1);
+          if (rp != null) return rp;
+        }
+
+        return null;
+      }
+    }
+
+    var description = new StringDescription();
+
+    // If we have recursed, show the expected value too; if not, expect() will
+    // show it for us.
+    if (depth > 0) {
+      description
+          .add('was ')
+          .addDescriptionOf(actual)
+          .add(' instead of ')
+          .addDescriptionOf(expected);
+      return [description.toString(), location];
+    }
+
+    // We're not adding any value to the actual value.
+    return ["", location];
+  }
+
+  String _match(expected, actual, Map matchState) {
+    var rp = _recursiveMatch(expected, actual, '', 0);
+    if (rp == null) return null;
+    var reason;
+    if (rp[0].length > 0) {
+      if (rp[1].length > 0) {
+        reason = "${rp[0]} at location ${rp[1]}";
+      } else {
+        reason = rp[0];
+      }
+    } else {
+      reason = '';
+    }
+    // Cache the failure reason in the matchState.
+    addStateInfo(matchState, {'reason': reason});
+    return reason;
+  }
+
+  bool matches(item, Map matchState) =>
+      _match(_expected, item, matchState) == null;
+
+  Description describe(Description description) =>
+      description.addDescriptionOf(_expected);
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    var reason = matchState['reason'];
+    // If we didn't get a good reason, that would normally be a
+    // simple 'is <value>' message. We only add that if the mismatch
+    // description is non empty (so we are supplementing the mismatch
+    // description).
+    if (reason.length == 0 && mismatchDescription.length > 0) {
+      mismatchDescription.add('is ').addDescriptionOf(item);
+    } else {
+      mismatchDescription.add(reason);
+    }
+    return mismatchDescription;
+  }
+}
+
+/// A special equality matcher for strings.
+class _StringEqualsMatcher extends Matcher {
+  final String _value;
+
+  _StringEqualsMatcher(this._value);
+
+  bool get showActualValue => true;
+
+  bool matches(item, Map matchState) => _value == item;
+
+  Description describe(Description description) =>
+      description.addDescriptionOf(_value);
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    if (item is! String) {
+      return mismatchDescription.addDescriptionOf(item).add('is not a string');
+    } else {
+      var buff = new StringBuffer();
+      buff.write('is different.');
+      var escapedItem = escape(item);
+      var escapedValue = escape(_value);
+      int minLength = escapedItem.length < escapedValue.length
+          ? escapedItem.length
+          : escapedValue.length;
+      int start;
+      for (start = 0; start < minLength; start++) {
+        if (escapedValue.codeUnitAt(start) != escapedItem.codeUnitAt(start)) {
+          break;
+        }
+      }
+      if (start == minLength) {
+        if (escapedValue.length < escapedItem.length) {
+          buff.write(' Both strings start the same, but the given value also'
+              ' has the following trailing characters: ');
+          _writeTrailing(buff, escapedItem, escapedValue.length);
+        } else {
+          buff.write(' Both strings start the same, but the given value is'
+              ' missing the following trailing characters: ');
+          _writeTrailing(buff, escapedValue, escapedItem.length);
+        }
+      } else {
+        buff.write('\nExpected: ');
+        _writeLeading(buff, escapedValue, start);
+        _writeTrailing(buff, escapedValue, start);
+        buff.write('\n  Actual: ');
+        _writeLeading(buff, escapedItem, start);
+        _writeTrailing(buff, escapedItem, start);
+        buff.write('\n          ');
+        for (int i = (start > 10 ? 14 : start); i > 0; i--) buff.write(' ');
+        buff.write('^\n Differ at offset $start');
+      }
+
+      return mismatchDescription.replace(buff.toString());
+    }
+  }
+
+  static void _writeLeading(StringBuffer buff, String s, int start) {
+    if (start > 10) {
+      buff.write('... ');
+      buff.write(s.substring(start - 10, start));
+    } else {
+      buff.write(s.substring(0, start));
+    }
+  }
+
+  static void _writeTrailing(StringBuffer buff, String s, int start) {
+    if (start + 10 > s.length) {
+      buff.write(s.substring(start));
+    } else {
+      buff.write(s.substring(start, start + 10));
+      buff.write(' ...');
+    }
+  }
+}
+
+/// A matcher that matches any value.
+const Matcher anything = const _IsAnything();
+
+class _IsAnything extends Matcher {
+  const _IsAnything();
+  bool matches(item, Map matchState) => true;
+  Description describe(Description description) => description.add('anything');
+}
+
+/// Returns a matcher that matches if an object is an instance
+/// of [type] (or a subtype).
+///
+/// As types are not first class objects in Dart we can only
+/// approximate this test by using a generic wrapper class.
+///
+/// For example, to test whether 'bar' is an instance of type
+/// 'Foo', we would write:
+///
+///     expect(bar, new isInstanceOf<Foo>());
+class isInstanceOf<T> extends Matcher {
+  /// The [name] parameter does nothing; it's deprecated and will be removed in
+  /// future version of [matcher].
+  const isInstanceOf([@deprecated String name]);
+
+  bool matches(obj, Map matchState) => obj is T;
+
+  Description describe(Description description) =>
+      description.add('an instance of $T');
+}
+
+/// A matcher that matches a function call against no exception.
+///
+/// The function will be called once. Any exceptions will be silently swallowed.
+/// The value passed to expect() should be a reference to the function.
+/// Note that the function cannot take arguments; to handle this
+/// a wrapper will have to be created.
+const Matcher returnsNormally = const _ReturnsNormally();
+
+class _ReturnsNormally extends Matcher {
+  const _ReturnsNormally();
+
+  bool matches(f, Map matchState) {
+    try {
+      f();
+      return true;
+    } catch (e, s) {
+      addStateInfo(matchState, {'exception': e, 'stack': s});
+      return false;
+    }
+  }
+
+  Description describe(Description description) =>
+      description.add("return normally");
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    mismatchDescription.add('threw ').addDescriptionOf(matchState['exception']);
+    if (verbose) {
+      mismatchDescription.add(' at ').add(matchState['stack'].toString());
+    }
+    return mismatchDescription;
+  }
+}
+
+/*
+ * Matchers for different exception types. Ideally we should just be able to
+ * use something like:
+ *
+ * final Matcher throwsException =
+ *     const _Throws(const isInstanceOf<Exception>());
+ *
+ * Unfortunately instanceOf is not working with dart2js.
+ *
+ * Alternatively, if static functions could be used in const expressions,
+ * we could use:
+ *
+ * bool _isException(x) => x is Exception;
+ * final Matcher isException = const _Predicate(_isException, "Exception");
+ * final Matcher throwsException = const _Throws(isException);
+ *
+ * But currently using static functions in const expressions is not supported.
+ * For now the only solution for all platforms seems to be separate classes
+ * for each exception type.
+ */
+
+abstract class TypeMatcher extends Matcher {
+  final String _name;
+  const TypeMatcher(this._name);
+  Description describe(Description description) => description.add(_name);
+}
+
+/// A matcher for Map types.
+const Matcher isMap = const _IsMap();
+
+class _IsMap extends TypeMatcher {
+  const _IsMap() : super("Map");
+  bool matches(item, Map matchState) => item is Map;
+}
+
+/// A matcher for List types.
+const Matcher isList = const _IsList();
+
+class _IsList extends TypeMatcher {
+  const _IsList() : super("List");
+  bool matches(item, Map matchState) => item is List;
+}
+
+/// Returns a matcher that matches if an object has a length property
+/// that matches [matcher].
+Matcher hasLength(matcher) => new _HasLength(wrapMatcher(matcher));
+
+class _HasLength extends Matcher {
+  final Matcher _matcher;
+  const _HasLength([Matcher matcher = null]) : this._matcher = matcher;
+
+  bool matches(item, Map matchState) {
+    try {
+      // This is harmless code that will throw if no length property
+      // but subtle enough that an optimizer shouldn't strip it out.
+      if (item.length * item.length >= 0) {
+        return _matcher.matches(item.length, matchState);
+      }
+    } catch (e) {}
+    return false;
+  }
+
+  Description describe(Description description) =>
+      description.add('an object with length of ').addDescriptionOf(_matcher);
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    try {
+      // We want to generate a different description if there is no length
+      // property; we use the same trick as in matches().
+      if (item.length * item.length >= 0) {
+        return mismatchDescription
+            .add('has length of ')
+            .addDescriptionOf(item.length);
+      }
+    } catch (e) {}
+    return mismatchDescription.add('has no length property');
+  }
+}
+
+/// Returns a matcher that matches if the match argument contains the expected
+/// value.
+///
+/// For [String]s this means substring matching;
+/// for [Map]s it means the map has the key, and for [Iterable]s
+/// it means the iterable has a matching element. In the case of iterables,
+/// [expected] can itself be a matcher.
+Matcher contains(expected) => new _Contains(expected);
+
+class _Contains extends Matcher {
+  final _expected;
+
+  const _Contains(this._expected);
+
+  bool matches(item, Map matchState) {
+    if (item is String) {
+      return item.indexOf(_expected) >= 0;
+    } else if (item is Iterable) {
+      if (_expected is Matcher) {
+        return item.any((e) => _expected.matches(e, matchState));
+      } else {
+        return item.contains(_expected);
+      }
+    } else if (item is Map) {
+      return item.containsKey(_expected);
+    }
+    return false;
+  }
+
+  Description describe(Description description) =>
+      description.add('contains ').addDescriptionOf(_expected);
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    if (item is String || item is Iterable || item is Map) {
+      return super.describeMismatch(
+          item, mismatchDescription, matchState, verbose);
+    } else {
+      return mismatchDescription.add('is not a string, map or iterable');
+    }
+  }
+}
+
+/// Returns a matcher that matches if the match argument is in
+/// the expected value. This is the converse of [contains].
+Matcher isIn(expected) => new _In(expected);
+
+class _In extends Matcher {
+  final _expected;
+
+  const _In(this._expected);
+
+  bool matches(item, Map matchState) {
+    if (_expected is String) {
+      return _expected.indexOf(item) >= 0;
+    } else if (_expected is Iterable) {
+      return _expected.any((e) => e == item);
+    } else if (_expected is Map) {
+      return _expected.containsKey(item);
+    }
+    return false;
+  }
+
+  Description describe(Description description) =>
+      description.add('is in ').addDescriptionOf(_expected);
+}
+
+/// Returns a matcher that uses an arbitrary function that returns
+/// true or false for the actual value.
+///
+/// For example:
+///
+///     expect(v, predicate((x) => ((x % 2) == 0), "is even"))
+Matcher predicate(bool f(value), [String description = 'satisfies function']) =>
+    new _Predicate(f, description);
+
+typedef bool _PredicateFunction(value);
+
+class _Predicate extends Matcher {
+  final _PredicateFunction _matcher;
+  final String _description;
+
+  const _Predicate(this._matcher, this._description);
+
+  bool matches(item, Map matchState) => _matcher(item);
+
+  Description describe(Description description) =>
+      description.add(_description);
+}
+
+/// A useful utility class for implementing other matchers through inheritance.
+/// Derived classes should call the base constructor with a feature name and
+/// description, and an instance matcher, and should implement the
+/// [featureValueOf] abstract method.
+///
+/// The feature description will typically describe the item and the feature,
+/// while the feature name will just name the feature. For example, we may
+/// have a Widget class where each Widget has a price; we could make a
+/// [CustomMatcher] that can make assertions about prices with:
+///
+///     class HasPrice extends CustomMatcher {
+///       const HasPrice(matcher) :
+///           super("Widget with price that is", "price", matcher);
+///       featureValueOf(actual) => actual.price;
+///     }
+///
+/// and then use this for example like:
+///
+///      expect(inventoryItem, new HasPrice(greaterThan(0)));
+class CustomMatcher extends Matcher {
+  final String _featureDescription;
+  final String _featureName;
+  final Matcher _matcher;
+
+  CustomMatcher(this._featureDescription, this._featureName, matcher)
+      : this._matcher = wrapMatcher(matcher);
+
+  /// Override this to extract the interesting feature.
+  featureValueOf(actual) => actual;
+
+  bool matches(item, Map matchState) {
+    var f = featureValueOf(item);
+    if (_matcher.matches(f, matchState)) return true;
+    addStateInfo(matchState, {'feature': f});
+    return false;
+  }
+
+  Description describe(Description description) =>
+      description.add(_featureDescription).add(' ').addDescriptionOf(_matcher);
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    mismatchDescription
+        .add('has ')
+        .add(_featureName)
+        .add(' with value ')
+        .addDescriptionOf(matchState['feature']);
+    var innerDescription = new StringDescription();
+    _matcher.describeMismatch(
+        matchState['feature'], innerDescription, matchState['state'], verbose);
+    if (innerDescription.length > 0) {
+      mismatchDescription.add(' which ').add(innerDescription.toString());
+    }
+    return mismatchDescription;
+  }
+}
diff --git a/matcher/lib/src/description.dart b/matcher/lib/src/description.dart
new file mode 100644
index 0000000..1da5a3a
--- /dev/null
+++ b/matcher/lib/src/description.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2012, 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 matcher.description;
+
+import 'interfaces.dart';
+import 'pretty_print.dart';
+
+/// The default implementation of [Description]. This should rarely need
+/// substitution, although conceivably it is a place where other languages
+/// could be supported.
+class StringDescription implements Description {
+  final StringBuffer _out = new StringBuffer();
+
+  /// Initialize the description with initial contents [init].
+  StringDescription([String init = '']) {
+    _out.write(init);
+  }
+
+  int get length => _out.length;
+
+  /// Get the description as a string.
+  String toString() => _out.toString();
+
+  /// Append [text] to the description.
+  Description add(String text) {
+    _out.write(text);
+    return this;
+  }
+
+  /// Change the value of the description.
+  Description replace(String text) {
+    _out.clear();
+    return add(text);
+  }
+
+  /// Appends a description of [value]. If it is an IMatcher use its
+  /// describe method; if it is a string use its literal value after
+  /// escaping any embedded control characters; otherwise use its
+  /// toString() value and wrap it in angular "quotes".
+  Description addDescriptionOf(value) {
+    if (value is Matcher) {
+      value.describe(this);
+    } else {
+      add(prettyPrint(value, maxLineLength: 80, maxItems: 25));
+    }
+    return this;
+  }
+
+  /// Append an [Iterable] [list] of objects to the description, using the
+  /// specified [separator] and framing the list with [start]
+  /// and [end].
+  Description addAll(
+      String start, String separator, String end, Iterable list) {
+    var separate = false;
+    add(start);
+    for (var item in list) {
+      if (separate) {
+        add(separator);
+      }
+      addDescriptionOf(item);
+      separate = true;
+    }
+    add(end);
+    return this;
+  }
+}
diff --git a/matcher/lib/src/error_matchers.dart b/matcher/lib/src/error_matchers.dart
new file mode 100644
index 0000000..821e731
--- /dev/null
+++ b/matcher/lib/src/error_matchers.dart
@@ -0,0 +1,97 @@
+// Copyright (c) 2012, 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 matcher.error_matchers;
+
+import 'core_matchers.dart';
+import 'interfaces.dart';
+
+/// A matcher for ArgumentErrors.
+const Matcher isArgumentError = const _ArgumentError();
+
+class _ArgumentError extends TypeMatcher {
+  const _ArgumentError() : super("ArgumentError");
+  bool matches(item, Map matchState) => item is ArgumentError;
+}
+
+/// A matcher for ConcurrentModificationError.
+const Matcher isConcurrentModificationError =
+    const _ConcurrentModificationError();
+
+class _ConcurrentModificationError extends TypeMatcher {
+  const _ConcurrentModificationError() : super("ConcurrentModificationError");
+  bool matches(item, Map matchState) => item is ConcurrentModificationError;
+}
+
+/// A matcher for CyclicInitializationError.
+const Matcher isCyclicInitializationError = const _CyclicInitializationError();
+
+class _CyclicInitializationError extends TypeMatcher {
+  const _CyclicInitializationError() : super("CyclicInitializationError");
+  bool matches(item, Map matchState) => item is CyclicInitializationError;
+}
+
+/// A matcher for Exceptions.
+const Matcher isException = const _Exception();
+
+class _Exception extends TypeMatcher {
+  const _Exception() : super("Exception");
+  bool matches(item, Map matchState) => item is Exception;
+}
+
+/// A matcher for FormatExceptions.
+const Matcher isFormatException = const _FormatException();
+
+class _FormatException extends TypeMatcher {
+  const _FormatException() : super("FormatException");
+  bool matches(item, Map matchState) => item is FormatException;
+}
+
+/// A matcher for NoSuchMethodErrors.
+const Matcher isNoSuchMethodError = const _NoSuchMethodError();
+
+class _NoSuchMethodError extends TypeMatcher {
+  const _NoSuchMethodError() : super("NoSuchMethodError");
+  bool matches(item, Map matchState) => item is NoSuchMethodError;
+}
+
+/// A matcher for NullThrownError.
+const Matcher isNullThrownError = const _NullThrownError();
+
+class _NullThrownError extends TypeMatcher {
+  const _NullThrownError() : super("NullThrownError");
+  bool matches(item, Map matchState) => item is NullThrownError;
+}
+
+/// A matcher for RangeErrors.
+const Matcher isRangeError = const _RangeError();
+
+class _RangeError extends TypeMatcher {
+  const _RangeError() : super("RangeError");
+  bool matches(item, Map matchState) => item is RangeError;
+}
+
+/// A matcher for StateErrors.
+const Matcher isStateError = const _StateError();
+
+class _StateError extends TypeMatcher {
+  const _StateError() : super("StateError");
+  bool matches(item, Map matchState) => item is StateError;
+}
+
+/// A matcher for UnimplementedErrors.
+const Matcher isUnimplementedError = const _UnimplementedError();
+
+class _UnimplementedError extends TypeMatcher {
+  const _UnimplementedError() : super("UnimplementedError");
+  bool matches(item, Map matchState) => item is UnimplementedError;
+}
+
+/// A matcher for UnsupportedError.
+const Matcher isUnsupportedError = const _UnsupportedError();
+
+class _UnsupportedError extends TypeMatcher {
+  const _UnsupportedError() : super("UnsupportedError");
+  bool matches(item, Map matchState) => item is UnsupportedError;
+}
diff --git a/matcher/lib/src/expect.dart b/matcher/lib/src/expect.dart
new file mode 100644
index 0000000..08eb68e
--- /dev/null
+++ b/matcher/lib/src/expect.dart
@@ -0,0 +1,177 @@
+// Copyright (c) 2012, 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 matcher.expect;
+
+import 'core_matchers.dart';
+import 'description.dart';
+import 'interfaces.dart';
+import 'util.dart';
+
+/// The objects thrown by the default failure handler.
+class TestFailure extends Error {
+  final String message;
+
+  TestFailure(this.message);
+
+  String toString() => message;
+}
+
+/// Failed matches are reported using a default IFailureHandler.
+/// The default implementation simply throws [TestFailure]s;
+/// this can be replaced by some other implementation of
+/// IFailureHandler by calling configureExpectHandler.
+abstract class FailureHandler {
+  /// This handles failures given a textual decription
+  void fail(String reason);
+
+  /// This handles failures given the actual [value], the [matcher]
+  /// the [reason] (argument from [expect]), some additonal [matchState]
+  /// generated by the [matcher], and a verbose flag which controls in
+  /// some cases how much [matchState] information is used. It will use
+  /// these to create a detailed error message (typically by calling
+  /// an [ErrorFormatter]) and then call [fail] with this message.
+  void failMatch(
+      actual, Matcher matcher, String reason, Map matchState, bool verbose);
+}
+
+/// The ErrorFormatter type is used for functions that
+/// can be used to build up error reports upon [expect] failures.
+/// There is one built-in implementation ([defaultErrorFormatter])
+/// which is used by the default failure handler. If the failure handler
+/// is replaced it may be desirable to replace the [stringDescription]
+/// error formatter with another.
+typedef String ErrorFormatter(
+    actual, Matcher matcher, String reason, Map matchState, bool verbose);
+
+/// This Function is used by certain Matchers to catch certain exceptions.
+///
+/// Some matchers, like those for Futures and exception testing,
+/// can fail in asynchronous sections, and throw exceptions.
+/// A user of this library will typically want to catch and handle
+/// such exceptions. The [wrapAsync] property is a function that
+/// can wrap callbacks used by these Matchers so that they can be
+/// used safely. For example, the unittest library will set this
+/// to be `expectAsync`. By default this is an identity function.
+Function wrapAsync = (Function f, [id]) => f;
+
+/// Assert that [actual] matches [matcher].
+///
+/// This is the main assertion function. [reason] is optional and is typically
+/// not supplied, as a reason is generated from the matcher; if [reason]
+/// is included it is appended to the reason generated by the matcher.
+///
+/// [matcher] can be a value in which case it will be wrapped in an
+/// [equals] matcher.
+///
+/// If the assertion fails, then the default behavior is to throw a
+/// [TestFailure], but this behavior can be changed by calling
+/// [configureExpectFailureHandler] and providing an alternative handler that
+/// implements the [IFailureHandler] interface. It is also possible to
+/// pass a [failureHandler] to [expect] as a final parameter for fine-
+/// grained control.
+///
+/// In some cases extra diagnostic info can be produced on failure (for
+/// example, stack traces on mismatched exceptions). To enable these,
+/// [verbose] should be specified as true;
+void expect(actual, matcher,
+    {String reason, FailureHandler failureHandler, bool verbose: false}) {
+  matcher = wrapMatcher(matcher);
+  bool doesMatch;
+  var matchState = {};
+  try {
+    doesMatch = matcher.matches(actual, matchState);
+  } catch (e, trace) {
+    doesMatch = false;
+    if (reason == null) {
+      reason = '${(e is String) ? e : e.toString()} at $trace';
+    }
+  }
+  if (!doesMatch) {
+    if (failureHandler == null) {
+      failureHandler = getOrCreateExpectFailureHandler();
+    }
+    failureHandler.failMatch(actual, matcher, reason, matchState, verbose);
+  }
+}
+
+void fail(String message, {FailureHandler failureHandler}) {
+  if (failureHandler == null) {
+    failureHandler = getOrCreateExpectFailureHandler();
+  }
+  failureHandler.fail(message);
+}
+
+// The handler for failed asserts.
+FailureHandler _assertFailureHandler = null;
+
+// The default failure handler that throws [TestFailure]s.
+class DefaultFailureHandler implements FailureHandler {
+  DefaultFailureHandler() {
+    if (_assertErrorFormatter == null) {
+      _assertErrorFormatter = _defaultErrorFormatter;
+    }
+  }
+  void fail(String reason) {
+    throw new TestFailure(reason);
+  }
+  void failMatch(
+      actual, Matcher matcher, String reason, Map matchState, bool verbose) {
+    fail(_assertErrorFormatter(actual, matcher, reason, matchState, verbose));
+  }
+}
+
+/// Changes the default failure handler for [expect].
+///
+/// [handler] is a reference to the new handler; if this is omitted
+/// or null then the failure handler is reset to the default, which
+/// throws [TestFailure]s on [expect] assertion failures.
+void configureExpectFailureHandler([FailureHandler handler = null]) {
+  if (handler == null) {
+    handler = new DefaultFailureHandler();
+  }
+  _assertFailureHandler = handler;
+}
+
+FailureHandler getOrCreateExpectFailureHandler() {
+  if (_assertFailureHandler == null) {
+    configureExpectFailureHandler();
+  }
+  return _assertFailureHandler;
+}
+
+// The error message formatter for failed asserts.
+ErrorFormatter _assertErrorFormatter = null;
+
+// The default error formatter implementation.
+String _defaultErrorFormatter(
+    actual, Matcher matcher, String reason, Map matchState, bool verbose) {
+  var description = new StringDescription();
+  description.add('Expected: ').addDescriptionOf(matcher).add('\n');
+  description.add('  Actual: ').addDescriptionOf(actual).add('\n');
+
+  var mismatchDescription = new StringDescription();
+  matcher.describeMismatch(actual, mismatchDescription, matchState, verbose);
+
+  if (mismatchDescription.length > 0) {
+    description.add('   Which: ${mismatchDescription}\n');
+  }
+  if (reason != null) {
+    description.add(reason).add('\n');
+  }
+  return description.toString();
+}
+
+/// Changes the failure message formatter for expect().
+///
+/// [formatter] is a reference to the new formatter; if this is omitted or
+/// null then the failure formatter is reset to the default. The new
+/// formatter is returned; this allows custom expect handlers to easily
+/// get a reference to the default formatter.
+ErrorFormatter configureExpectFormatter([ErrorFormatter formatter = null]) {
+  if (formatter == null) {
+    formatter = _defaultErrorFormatter;
+  }
+  return _assertErrorFormatter = formatter;
+}
diff --git a/matcher/lib/src/future_matchers.dart b/matcher/lib/src/future_matchers.dart
new file mode 100644
index 0000000..bdbd62f
--- /dev/null
+++ b/matcher/lib/src/future_matchers.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2012, 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 matcher.future_matchers;
+
+import 'dart:async';
+
+import 'expect.dart';
+import 'interfaces.dart';
+import 'util.dart';
+
+/// Matches a [Future] that completes successfully with a value.
+///
+/// Note that this creates an asynchronous expectation. The call to `expect()`
+/// that includes this will return immediately and execution will continue.
+/// Later, when the future completes, the actual expectation will run.
+///
+/// To test that a Future completes with an exception, you can use [throws] and
+/// [throwsA].
+final Matcher completes = const _Completes(null, '');
+
+/// Matches a [Future] that completes succesfully with a value that matches
+/// [matcher].
+///
+/// Note that this creates an asynchronous expectation. The call to
+/// `expect()` that includes this will return immediately and execution will
+/// continue. Later, when the future completes, the actual expectation will run.
+///
+/// To test that a Future completes with an exception, you can use [throws] and
+/// [throwsA].
+///
+/// [id] is an optional tag that can be used to identify the completion matcher
+/// in error messages.
+Matcher completion(matcher, [String id = '']) =>
+    new _Completes(wrapMatcher(matcher), id);
+
+class _Completes extends Matcher {
+  final Matcher _matcher;
+  final String _id;
+
+  const _Completes(this._matcher, this._id);
+
+  bool matches(item, Map matchState) {
+    if (item is! Future) return false;
+    var done = wrapAsync((fn) => fn(), _id);
+
+    item.then((value) {
+      done(() {
+        if (_matcher != null) expect(value, _matcher);
+      });
+    }, onError: (error, trace) {
+      var id = _id == '' ? '' : '${_id} ';
+      var reason = 'Expected future ${id}to complete successfully, '
+          'but it failed with ${error}';
+      if (trace != null) {
+        var stackTrace = trace.toString();
+        stackTrace = '  ${stackTrace.replaceAll('\n', '\n  ')}';
+        reason = '$reason\nStack trace:\n$stackTrace';
+      }
+      done(() => fail(reason));
+    });
+
+    return true;
+  }
+
+  Description describe(Description description) {
+    if (_matcher == null) {
+      description.add('completes successfully');
+    } else {
+      description.add('completes to a value that ').addDescriptionOf(_matcher);
+    }
+    return description;
+  }
+}
diff --git a/matcher/lib/src/interfaces.dart b/matcher/lib/src/interfaces.dart
new file mode 100644
index 0000000..984d862
--- /dev/null
+++ b/matcher/lib/src/interfaces.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2012, 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 matcher.interfaces;
+
+// To decouple the reporting of errors, and allow for extensibility of
+// matchers, we make use of some interfaces.
+
+/// Matchers build up their error messages by appending to
+/// Description objects. This interface is implemented by
+/// StringDescription. This interface is unlikely to need
+/// other implementations, but could be useful to replace in
+/// some cases - e.g. language conversion.
+abstract class Description {
+  int get length;
+
+  /// Change the value of the description.
+  Description replace(String text);
+
+  /// This is used to add arbitrary text to the description.
+  Description add(String text);
+
+  /// This is used to add a meaningful description of a value.
+  Description addDescriptionOf(value);
+
+  /// This is used to add a description of an [Iterable] [list],
+  /// with appropriate [start] and [end] markers and inter-element [separator].
+  Description addAll(String start, String separator, String end, Iterable list);
+}
+
+/// [expect] Matchers must implement/extend the Matcher class.
+/// The base Matcher class has a generic implementation of [describeMismatch]
+/// so this does not need to be provided unless a more clear description is
+/// required. The other two methods ([matches] and [describe])
+/// must always be provided as they are highly matcher-specific.
+abstract class Matcher {
+  const Matcher();
+
+  /// This does the matching of the actual vs expected values.
+  /// [item] is the actual value. [matchState] can be supplied
+  /// and may be used to add details about the mismatch that are too
+  /// costly to determine in [describeMismatch].
+  bool matches(item, Map matchState);
+
+  /// This builds a textual description of the matcher.
+  Description describe(Description description);
+
+  /// This builds a textual description of a specific mismatch. [item]
+  /// is the value that was tested by [matches]; [matchState] is
+  /// the [Map] that was passed to and supplemented by [matches]
+  /// with additional information about the mismatch, and [mismatchDescription]
+  /// is the [Description] that is being built to decribe the mismatch.
+  /// A few matchers make use of the [verbose] flag to provide detailed
+  /// information that is not typically included but can be of help in
+  /// diagnosing failures, such as stack traces.
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) =>
+      mismatchDescription;
+}
diff --git a/matcher/lib/src/iterable_matchers.dart b/matcher/lib/src/iterable_matchers.dart
new file mode 100644
index 0000000..118575a
--- /dev/null
+++ b/matcher/lib/src/iterable_matchers.dart
@@ -0,0 +1,267 @@
+// Copyright (c) 2012, 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 matcher.iterable_matchers;
+
+import 'core_matchers.dart';
+import 'description.dart';
+import 'interfaces.dart';
+import 'util.dart';
+
+/// Returns a matcher which matches [Iterable]s in which all elements
+/// match the given [matcher].
+Matcher everyElement(matcher) => new _EveryElement(wrapMatcher(matcher));
+
+class _EveryElement extends _IterableMatcher {
+  final Matcher _matcher;
+
+  _EveryElement(Matcher this._matcher);
+
+  bool matches(item, Map matchState) {
+    if (item is! Iterable) {
+      return false;
+    }
+    var i = 0;
+    for (var element in item) {
+      if (!_matcher.matches(element, matchState)) {
+        addStateInfo(matchState, {'index': i, 'element': element});
+        return false;
+      }
+      ++i;
+    }
+    return true;
+  }
+
+  Description describe(Description description) =>
+      description.add('every element(').addDescriptionOf(_matcher).add(')');
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    if (matchState['index'] != null) {
+      var index = matchState['index'];
+      var element = matchState['element'];
+      mismatchDescription
+          .add('has value ')
+          .addDescriptionOf(element)
+          .add(' which ');
+      var subDescription = new StringDescription();
+      _matcher.describeMismatch(
+          element, subDescription, matchState['state'], verbose);
+      if (subDescription.length > 0) {
+        mismatchDescription.add(subDescription.toString());
+      } else {
+        mismatchDescription.add("doesn't match ");
+        _matcher.describe(mismatchDescription);
+      }
+      mismatchDescription.add(' at index $index');
+      return mismatchDescription;
+    }
+    return super.describeMismatch(
+        item, mismatchDescription, matchState, verbose);
+  }
+}
+
+/// Returns a matcher which matches [Iterable]s in which at least one
+/// element matches the given [matcher].
+Matcher anyElement(matcher) => new _AnyElement(wrapMatcher(matcher));
+
+class _AnyElement extends _IterableMatcher {
+  final Matcher _matcher;
+
+  _AnyElement(this._matcher);
+
+  bool matches(item, Map matchState) {
+    return item.any((e) => _matcher.matches(e, matchState));
+  }
+
+  Description describe(Description description) =>
+      description.add('some element ').addDescriptionOf(_matcher);
+}
+
+/// Returns a matcher which matches [Iterable]s that have the same
+/// length and the same elements as [expected], in the same order.
+///
+/// This is equivalent to [equals] but does not recurse.
+Matcher orderedEquals(Iterable expected) => new _OrderedEquals(expected);
+
+class _OrderedEquals extends Matcher {
+  final Iterable _expected;
+  Matcher _matcher;
+
+  _OrderedEquals(this._expected) {
+    _matcher = equals(_expected, 1);
+  }
+
+  bool matches(item, Map matchState) =>
+      (item is Iterable) && _matcher.matches(item, matchState);
+
+  Description describe(Description description) =>
+      description.add('equals ').addDescriptionOf(_expected).add(' ordered');
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    if (item is! Iterable) {
+      return mismatchDescription.add('is not an Iterable');
+    } else {
+      return _matcher.describeMismatch(
+          item, mismatchDescription, matchState, verbose);
+    }
+  }
+}
+
+/// Returns a matcher which matches [Iterable]s that have the same length and
+/// the same elements as [expected], but not necessarily in the same order.
+///
+/// Note that this is O(n^2) so should only be used on small objects.
+Matcher unorderedEquals(Iterable expected) => new _UnorderedEquals(expected);
+
+class _UnorderedEquals extends _UnorderedMatches {
+  final List _expectedValues;
+
+  _UnorderedEquals(Iterable expected)
+      : super(expected.map(equals)),
+        _expectedValues = expected.toList();
+
+  Description describe(Description description) => description
+      .add('equals ')
+      .addDescriptionOf(_expectedValues)
+      .add(' unordered');
+}
+
+/// Iterable matchers match against [Iterable]s. We add this intermediate
+/// class to give better mismatch error messages than the base Matcher class.
+abstract class _IterableMatcher extends Matcher {
+  const _IterableMatcher();
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    if (item is! Iterable) {
+      return mismatchDescription.addDescriptionOf(item).add(' not an Iterable');
+    } else {
+      return super.describeMismatch(
+          item, mismatchDescription, matchState, verbose);
+    }
+  }
+}
+
+/// Returns a matcher which matches [Iterable]s whose elements match the
+/// matchers in [expected], but not necessarily in the same order.
+///
+///  Note that this is `O(n^2)` and so should only be used on small objects.
+Matcher unorderedMatches(Iterable expected) => new _UnorderedMatches(expected);
+
+class _UnorderedMatches extends Matcher {
+  final List<Matcher> _expected;
+
+  _UnorderedMatches(Iterable expected)
+      : _expected = expected.map(wrapMatcher).toList();
+
+  String _test(item) {
+    if (item is! Iterable) return 'not iterable';
+    item = item.toList();
+
+    // Check the lengths are the same.
+    if (_expected.length > item.length) {
+      return 'has too few elements (${item.length} < ${_expected.length})';
+    } else if (_expected.length < item.length) {
+      return 'has too many elements (${item.length} > ${_expected.length})';
+    }
+
+    var matched = new List<bool>.filled(item.length, false);
+    var expectedPosition = 0;
+    for (var expectedMatcher in _expected) {
+      var actualPosition = 0;
+      var gotMatch = false;
+      for (var actualElement in item) {
+        if (!matched[actualPosition]) {
+          if (expectedMatcher.matches(actualElement, {})) {
+            matched[actualPosition] = gotMatch = true;
+            break;
+          }
+        }
+        ++actualPosition;
+      }
+
+      if (!gotMatch) {
+        return new StringDescription()
+            .add('has no match for ')
+            .addDescriptionOf(expectedMatcher)
+            .add(' at index ${expectedPosition}')
+            .toString();
+      }
+
+      ++expectedPosition;
+    }
+    return null;
+  }
+
+  bool matches(item, Map mismatchState) => _test(item) == null;
+
+  Description describe(Description description) => description
+      .add('matches ')
+      .addAll('[', ', ', ']', _expected)
+      .add(' unordered');
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) =>
+      mismatchDescription.add(_test(item));
+}
+
+/// A pairwise matcher for [Iterable]s.
+///
+/// The [comparator] function, taking an expected and an actual argument, and
+/// returning whether they match, will be applied to each pair in order.
+/// [description] should be a meaningful name for the comparator.
+Matcher pairwiseCompare(
+    Iterable expected, bool comparator(a, b), String description) =>
+        new _PairwiseCompare(expected, comparator, description);
+
+typedef bool _Comparator(a, b);
+
+class _PairwiseCompare extends _IterableMatcher {
+  final Iterable _expected;
+  final _Comparator _comparator;
+  final String _description;
+
+  _PairwiseCompare(this._expected, this._comparator, this._description);
+
+  bool matches(item, Map matchState) {
+    if (item is! Iterable) return false;
+    if (item.length != _expected.length) return false;
+    var iterator = item.iterator;
+    var i = 0;
+    for (var e in _expected) {
+      iterator.moveNext();
+      if (!_comparator(e, iterator.current)) {
+        addStateInfo(matchState, {
+          'index': i,
+          'expected': e,
+          'actual': iterator.current
+        });
+        return false;
+      }
+      i++;
+    }
+    return true;
+  }
+
+  Description describe(Description description) =>
+      description.add('pairwise $_description ').addDescriptionOf(_expected);
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    if (item is! Iterable) {
+      return mismatchDescription.add('is not an Iterable');
+    } else if (item.length != _expected.length) {
+      return mismatchDescription
+          .add('has length ${item.length} instead of ${_expected.length}');
+    } else {
+      return mismatchDescription
+          .add('has ')
+          .addDescriptionOf(matchState["actual"])
+          .add(' which is not $_description ')
+          .addDescriptionOf(matchState["expected"])
+          .add(' at index ${matchState["index"]}');
+    }
+  }
+}
diff --git a/matcher/lib/src/map_matchers.dart b/matcher/lib/src/map_matchers.dart
new file mode 100644
index 0000000..8facbf5
--- /dev/null
+++ b/matcher/lib/src/map_matchers.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2012, 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 matcher.map_matchers;
+
+import 'interfaces.dart';
+import 'util.dart';
+
+/// Returns a matcher which matches maps containing the given [value].
+Matcher containsValue(value) => new _ContainsValue(value);
+
+class _ContainsValue extends Matcher {
+  final _value;
+
+  const _ContainsValue(this._value);
+
+  bool matches(item, Map matchState) => item.containsValue(_value);
+  Description describe(Description description) =>
+      description.add('contains value ').addDescriptionOf(_value);
+}
+
+/// Returns a matcher which matches maps containing the key-value pair
+/// with [key] => [value].
+Matcher containsPair(key, value) =>
+    new _ContainsMapping(key, wrapMatcher(value));
+
+class _ContainsMapping extends Matcher {
+  final _key;
+  final Matcher _valueMatcher;
+
+  const _ContainsMapping(this._key, Matcher this._valueMatcher);
+
+  bool matches(item, Map matchState) =>
+      item.containsKey(_key) && _valueMatcher.matches(item[_key], matchState);
+
+  Description describe(Description description) {
+    return description
+        .add('contains pair ')
+        .addDescriptionOf(_key)
+        .add(' => ')
+        .addDescriptionOf(_valueMatcher);
+  }
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    if (!item.containsKey(_key)) {
+      return mismatchDescription
+          .add(" doesn't contain key ")
+          .addDescriptionOf(_key);
+    } else {
+      mismatchDescription
+          .add(' contains key ')
+          .addDescriptionOf(_key)
+          .add(' but with value ');
+      _valueMatcher.describeMismatch(
+          item[_key], mismatchDescription, matchState, verbose);
+      return mismatchDescription;
+    }
+  }
+}
diff --git a/matcher/lib/src/numeric_matchers.dart b/matcher/lib/src/numeric_matchers.dart
new file mode 100644
index 0000000..770999d
--- /dev/null
+++ b/matcher/lib/src/numeric_matchers.dart
@@ -0,0 +1,202 @@
+// Copyright (c) 2012, 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 matcher.numeric_matchers;
+
+import 'interfaces.dart';
+
+/// Returns a matcher which matches if the match argument is greater
+/// than the given [value].
+Matcher greaterThan(value) =>
+    new _OrderingComparison(value, false, false, true, 'a value greater than');
+
+/// Returns a matcher which matches if the match argument is greater
+/// than or equal to the given [value].
+Matcher greaterThanOrEqualTo(value) => new _OrderingComparison(
+    value, true, false, true, 'a value greater than or equal to');
+
+/// Returns a matcher which matches if the match argument is less
+/// than the given [value].
+Matcher lessThan(value) =>
+    new _OrderingComparison(value, false, true, false, 'a value less than');
+
+/// Returns a matcher which matches if the match argument is less
+/// than or equal to the given [value].
+Matcher lessThanOrEqualTo(value) => new _OrderingComparison(
+    value, true, true, false, 'a value less than or equal to');
+
+/// A matcher which matches if the match argument is zero.
+const Matcher isZero =
+    const _OrderingComparison(0, true, false, false, 'a value equal to');
+
+/// A matcher which matches if the match argument is non-zero.
+const Matcher isNonZero =
+    const _OrderingComparison(0, false, true, true, 'a value not equal to');
+
+/// A matcher which matches if the match argument is positive.
+const Matcher isPositive =
+    const _OrderingComparison(0, false, false, true, 'a positive value', false);
+
+/// A matcher which matches if the match argument is zero or negative.
+const Matcher isNonPositive = const _OrderingComparison(
+    0, true, true, false, 'a non-positive value', false);
+
+/// A matcher which matches if the match argument is negative.
+const Matcher isNegative =
+    const _OrderingComparison(0, false, true, false, 'a negative value', false);
+
+/// A matcher which matches if the match argument is zero or positive.
+const Matcher isNonNegative = const _OrderingComparison(
+    0, true, false, true, 'a non-negative value', false);
+
+bool _isNumeric(value) {
+  return value is num;
+}
+
+// TODO(kevmoo) Note that matchers that use _OrderingComparison only use
+// `==` and `<` operators to evaluate the match. Or change the matcher.
+class _OrderingComparison extends Matcher {
+  /// Expected value.
+  final _value;
+  /// What to return if actual == expected
+  final bool _equalValue;
+  /// What to return if actual < expected
+  final bool _lessThanValue;
+  /// What to return if actual > expected
+  final bool _greaterThanValue;
+  /// Textual name of the inequality
+  final String _comparisonDescription;
+  /// Whether to include the expected value in the description
+  final bool _valueInDescription;
+
+  const _OrderingComparison(this._value, this._equalValue, this._lessThanValue,
+      this._greaterThanValue, this._comparisonDescription,
+      [bool valueInDescription = true])
+      : this._valueInDescription = valueInDescription;
+
+  bool matches(item, Map matchState) {
+    if (item == _value) {
+      return _equalValue;
+    } else if (item < _value) {
+      return _lessThanValue;
+    } else {
+      return _greaterThanValue;
+    }
+  }
+
+  Description describe(Description description) {
+    if (_valueInDescription) {
+      return description
+          .add(_comparisonDescription)
+          .add(' ')
+          .addDescriptionOf(_value);
+    } else {
+      return description.add(_comparisonDescription);
+    }
+  }
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    mismatchDescription.add('is not ');
+    return describe(mismatchDescription);
+  }
+}
+
+/// Returns a matcher which matches if the match argument is within [delta]
+/// of some [value].
+///
+/// In other words, this matches if the match argument is greater than
+/// than or equal [value]-[delta] and less than or equal to [value]+[delta].
+Matcher closeTo(num value, num delta) => new _IsCloseTo(value, delta);
+
+class _IsCloseTo extends Matcher {
+  final num _value, _delta;
+
+  const _IsCloseTo(this._value, this._delta);
+
+  bool matches(item, Map matchState) {
+    if (!_isNumeric(item)) {
+      return false;
+    }
+    var diff = item - _value;
+    if (diff < 0) diff = -diff;
+    return (diff <= _delta);
+  }
+
+  Description describe(Description description) => description
+      .add('a numeric value within ')
+      .addDescriptionOf(_delta)
+      .add(' of ')
+      .addDescriptionOf(_value);
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    if (item is! num) {
+      return mismatchDescription.add(' not numeric');
+    } else {
+      var diff = item - _value;
+      if (diff < 0) diff = -diff;
+      return mismatchDescription.add(' differs by ').addDescriptionOf(diff);
+    }
+  }
+}
+
+/// Returns a matcher which matches if the match argument is greater
+/// than or equal to [low] and less than or equal to [high].
+Matcher inInclusiveRange(num low, num high) =>
+    new _InRange(low, high, true, true);
+
+/// Returns a matcher which matches if the match argument is greater
+/// than [low] and less than [high].
+Matcher inExclusiveRange(num low, num high) =>
+    new _InRange(low, high, false, false);
+
+/// Returns a matcher which matches if the match argument is greater
+/// than [low] and less than or equal to [high].
+Matcher inOpenClosedRange(num low, num high) =>
+    new _InRange(low, high, false, true);
+
+/// Returns a matcher which matches if the match argument is greater
+/// than or equal to a [low] and less than [high].
+Matcher inClosedOpenRange(num low, num high) =>
+    new _InRange(low, high, true, false);
+
+class _InRange extends Matcher {
+  final num _low, _high;
+  final bool _lowMatchValue, _highMatchValue;
+
+  const _InRange(
+      this._low, this._high, this._lowMatchValue, this._highMatchValue);
+
+  bool matches(value, Map matchState) {
+    if (value is! num) {
+      return false;
+    }
+    if (value < _low || value > _high) {
+      return false;
+    }
+    if (value == _low) {
+      return _lowMatchValue;
+    }
+    if (value == _high) {
+      return _highMatchValue;
+    }
+    return true;
+  }
+
+  Description describe(Description description) => description.add(
+      "be in range from "
+      "$_low (${_lowMatchValue ? 'inclusive' : 'exclusive'}) to "
+      "$_high (${_highMatchValue ? 'inclusive' : 'exclusive'})");
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    if (item is! num) {
+      return mismatchDescription.addDescriptionOf(item).add(' not numeric');
+    } else {
+      return super.describeMismatch(
+          item, mismatchDescription, matchState, verbose);
+    }
+  }
+}
diff --git a/matcher/lib/src/operator_matchers.dart b/matcher/lib/src/operator_matchers.dart
new file mode 100644
index 0000000..6824c5b
--- /dev/null
+++ b/matcher/lib/src/operator_matchers.dart
@@ -0,0 +1,113 @@
+// Copyright (c) 2012, 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 matcher.operator_matchers;
+
+import 'interfaces.dart';
+import 'util.dart';
+
+/// This returns a matcher that inverts [matcher] to its logical negation.
+Matcher isNot(matcher) => new _IsNot(wrapMatcher(matcher));
+
+class _IsNot extends Matcher {
+  final Matcher _matcher;
+
+  const _IsNot(this._matcher);
+
+  bool matches(item, Map matchState) => !_matcher.matches(item, matchState);
+
+  Description describe(Description description) =>
+      description.add('not ').addDescriptionOf(_matcher);
+}
+
+/// This returns a matcher that matches if all of the matchers passed as
+/// arguments (up to 7) match.
+///
+/// Instead of passing the matchers separately they can be passed as a single
+/// List argument. Any argument that is not a matcher is implicitly wrapped in a
+/// Matcher to check for equality.
+Matcher allOf(arg0, [arg1, arg2, arg3, arg4, arg5, arg6]) {
+  return new _AllOf(_wrapArgs(arg0, arg1, arg2, arg3, arg4, arg5, arg6));
+}
+
+class _AllOf extends Matcher {
+  final List<Matcher> _matchers;
+
+  const _AllOf(this._matchers);
+
+  bool matches(item, Map matchState) {
+    for (var matcher in _matchers) {
+      if (!matcher.matches(item, matchState)) {
+        addStateInfo(matchState, {'matcher': matcher});
+        return false;
+      }
+    }
+    return true;
+  }
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    var matcher = matchState['matcher'];
+    matcher.describeMismatch(
+        item, mismatchDescription, matchState['state'], verbose);
+    return mismatchDescription;
+  }
+
+  Description describe(Description description) =>
+      description.addAll('(', ' and ', ')', _matchers);
+}
+
+/// Matches if any of the given matchers evaluate to true.
+///
+/// The arguments can be a set of matchers as separate parameters
+/// (up to 7), or a List of matchers.
+///
+/// The matchers are evaluated from left to right using short-circuit
+/// evaluation, so evaluation stops as soon as a matcher returns true.
+///
+/// Any argument that is not a matcher is implicitly wrapped in a
+/// Matcher to check for equality.
+Matcher anyOf(arg0, [arg1, arg2, arg3, arg4, arg5, arg6]) {
+  return new _AnyOf(_wrapArgs(arg0, arg1, arg2, arg3, arg4, arg5, arg6));
+}
+
+class _AnyOf extends Matcher {
+  final List<Matcher> _matchers;
+
+  const _AnyOf(this._matchers);
+
+  bool matches(item, Map matchState) {
+    for (var matcher in _matchers) {
+      if (matcher.matches(item, matchState)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  Description describe(Description description) =>
+      description.addAll('(', ' or ', ')', _matchers);
+}
+
+List<Matcher> _wrapArgs(arg0, arg1, arg2, arg3, arg4, arg5, arg6) {
+  Iterable<Matcher> matchers;
+  if (arg0 is List) {
+    if (arg1 != null ||
+        arg2 != null ||
+        arg3 != null ||
+        arg4 != null ||
+        arg5 != null ||
+        arg6 != null) {
+      throw new ArgumentError('If arg0 is a List, all other arguments must be'
+          ' null.');
+    }
+
+    matchers = arg0;
+  } else {
+    matchers =
+        [arg0, arg1, arg2, arg3, arg4, arg5, arg6].where((e) => e != null);
+  }
+
+  return matchers.map((e) => wrapMatcher(e)).toList();
+}
diff --git a/matcher/lib/src/pretty_print.dart b/matcher/lib/src/pretty_print.dart
new file mode 100644
index 0000000..c20102b
--- /dev/null
+++ b/matcher/lib/src/pretty_print.dart
@@ -0,0 +1,136 @@
+// Copyright (c) 2013, 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 matcher.pretty_print;
+
+import 'description.dart';
+import 'interfaces.dart';
+import 'util.dart';
+
+/// Returns a pretty-printed representation of [object].
+///
+/// If [maxLineLength] is passed, this will attempt to ensure that each line is
+/// no longer than [maxLineLength] characters long. This isn't guaranteed, since
+/// individual objects may have string representations that are too long, but
+/// most lines will be less than [maxLineLength] long.
+///
+/// If [maxItems] is passed, [Iterable]s and [Map]s will only print their first
+/// [maxItems] members or key/value pairs, respectively.
+String prettyPrint(object, {int maxLineLength, int maxItems}) {
+  String _prettyPrint(object, int indent, Set seen, bool top) {
+    // If the object is a matcher, use its description.
+    if (object is Matcher) {
+      var description = new StringDescription();
+      object.describe(description);
+      return "<$description>";
+    }
+
+    // Avoid looping infinitely on recursively-nested data structures.
+    if (seen.contains(object)) return "(recursive)";
+    seen = seen.union(new Set.from([object]));
+    String pp(child) => _prettyPrint(child, indent + 2, seen, false);
+
+    if (object is Iterable) {
+      // Print the type name for non-List iterables.
+      var type = object is List ? "" : _typeName(object) + ":";
+
+      // Truncate the list of strings if it's longer than [maxItems].
+      var strings = object.map(pp).toList();
+      if (maxItems != null && strings.length > maxItems) {
+        strings.replaceRange(maxItems - 1, strings.length, ['...']);
+      }
+
+      // If the printed string is short and doesn't contain a newline, print it
+      // as a single line.
+      var singleLine = "$type[${strings.join(', ')}]";
+      if ((maxLineLength == null ||
+              singleLine.length + indent <= maxLineLength) &&
+          !singleLine.contains("\n")) {
+        return singleLine;
+      }
+
+      // Otherwise, print each member on its own line.
+      return "$type[\n" + strings.map((string) {
+        return _indent(indent + 2) + string;
+      }).join(",\n") + "\n" + _indent(indent) + "]";
+    } else if (object is Map) {
+      // Convert the contents of the map to string representations.
+      var strings = object.keys.map((key) {
+        return '${pp(key)}: ${pp(object[key])}';
+      }).toList();
+
+      // Truncate the list of strings if it's longer than [maxItems].
+      if (maxItems != null && strings.length > maxItems) {
+        strings.replaceRange(maxItems - 1, strings.length, ['...']);
+      }
+
+      // If the printed string is short and doesn't contain a newline, print it
+      // as a single line.
+      var singleLine = "{${strings.join(", ")}}";
+      if ((maxLineLength == null ||
+              singleLine.length + indent <= maxLineLength) &&
+          !singleLine.contains("\n")) {
+        return singleLine;
+      }
+
+      // Otherwise, print each key/value pair on its own line.
+      return "{\n" + strings.map((string) {
+        return _indent(indent + 2) + string;
+      }).join(",\n") + "\n" + _indent(indent) + "}";
+    } else if (object is String) {
+      // Escape strings and print each line on its own line.
+      var lines = object.split("\n");
+      return "'" +
+          lines.map(_escapeString).join("\\n'\n${_indent(indent + 2)}'") +
+          "'";
+    } else {
+      var value = object.toString().replaceAll("\n", _indent(indent) + "\n");
+      var defaultToString = value.startsWith("Instance of ");
+
+      // If this is the top-level call to [prettyPrint], wrap the value on angle
+      // brackets to set it apart visually.
+      if (top) value = "<$value>";
+
+      // Print the type of objects with custom [toString] methods. Primitive
+      // objects and objects that don't implement a custom [toString] don't need
+      // to have their types printed.
+      if (object is num ||
+          object is bool ||
+          object is Function ||
+          object == null ||
+          defaultToString) {
+        return value;
+      } else {
+        return "${_typeName(object)}:$value";
+      }
+    }
+  }
+
+  return _prettyPrint(object, 0, new Set(), true);
+}
+
+String _indent(int length) => new List.filled(length, ' ').join('');
+
+/// Returns the name of the type of [x], or "Unknown" if the type name can't be
+/// determined.
+String _typeName(x) {
+  // dart2js blows up on some objects (e.g. window.navigator).
+  // So we play safe here.
+  try {
+    if (x == null) return "null";
+    var type = x.runtimeType.toString();
+    // TODO(nweiz): if the object's type is private, find a public superclass to
+    // display once there's a portable API to do that.
+    return type.startsWith("_") ? "?" : type;
+  } catch (e) {
+    return "?";
+  }
+}
+
+/// Returns [source] with any control characters replaced by their escape
+/// sequences.
+///
+/// This doesn't add quotes to the string, but it does escape single quote
+/// characters so that single quotes can be applied externally.
+String _escapeString(String source) => escape(source).replaceAll("'", r"\'");
diff --git a/matcher/lib/src/prints_matcher.dart b/matcher/lib/src/prints_matcher.dart
new file mode 100644
index 0000000..be6abe3
--- /dev/null
+++ b/matcher/lib/src/prints_matcher.dart
@@ -0,0 +1,73 @@
+// 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 matcher.prints_matcher;
+
+import 'dart:async';
+
+import 'description.dart';
+import 'expect.dart';
+import 'interfaces.dart';
+import 'future_matchers.dart';
+import 'util.dart';
+
+/// Matches a [Function] that prints text that matches [matcher].
+///
+/// [matcher] may be a String or a [Matcher].
+///
+/// If the function this runs against returns a [Future], all text printed by
+/// the function (using [Zone] scoping) until that Future completes is matched.
+///
+/// This only tracks text printed using the [print] function.
+Matcher prints(matcher) => new _Prints(wrapMatcher(matcher));
+
+class _Prints extends Matcher {
+  final Matcher _matcher;
+
+  _Prints(this._matcher);
+
+  bool matches(item, Map matchState) {
+    if (item is! Function) return false;
+
+    var buffer = new StringBuffer();
+    var result = runZoned(item,
+        zoneSpecification: new ZoneSpecification(print: (_, __, ____, line) {
+      buffer.writeln(line);
+    }));
+
+    if (result is! Future) {
+      var actual = buffer.toString();
+      matchState['prints.actual'] = actual;
+      return _matcher.matches(actual, matchState);
+    }
+
+    return completes.matches(result.then(wrapAsync((_) {
+      expect(buffer.toString(), _matcher);
+    }, 'prints')), matchState);
+  }
+
+  Description describe(Description description) =>
+      description.add('prints ').addDescriptionOf(_matcher);
+
+  Description describeMismatch(
+      item, Description description, Map matchState, bool verbose) {
+    var actual = matchState.remove('prints.actual');
+    if (actual == null) return description;
+    if (actual.isEmpty) return description.add("printed nothing.");
+
+    description.add('printed ').addDescriptionOf(actual);
+
+    // Create a new description for the matcher because at least
+    // [_StringEqualsMatcher] replaces the previous contents of the description.
+    var innerMismatch = _matcher
+        .describeMismatch(actual, new StringDescription(), matchState, verbose)
+        .toString();
+
+    if (innerMismatch.isNotEmpty) {
+      description.add('\n   Which: ').add(innerMismatch.toString());
+    }
+
+    return description;
+  }
+}
diff --git a/matcher/lib/src/string_matchers.dart b/matcher/lib/src/string_matchers.dart
new file mode 100644
index 0000000..4643434
--- /dev/null
+++ b/matcher/lib/src/string_matchers.dart
@@ -0,0 +1,201 @@
+// Copyright (c) 2012, 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 matcher.string_matchers;
+
+import 'interfaces.dart';
+
+/// Returns a matcher which matches if the match argument is a string and
+/// is equal to [value] when compared case-insensitively.
+Matcher equalsIgnoringCase(String value) => new _IsEqualIgnoringCase(value);
+
+class _IsEqualIgnoringCase extends _StringMatcher {
+  final String _value;
+  final String _matchValue;
+
+  _IsEqualIgnoringCase(String value)
+      : _value = value,
+        _matchValue = value.toLowerCase();
+
+  bool matches(item, Map matchState) =>
+      item is String && _matchValue == item.toLowerCase();
+
+  Description describe(Description description) =>
+      description.addDescriptionOf(_value).add(' ignoring case');
+}
+
+/// Returns a matcher which matches if the match argument is a string and
+/// is equal to [value], ignoring whitespace.
+///
+/// In this matcher, "ignoring whitespace" means comparing with all runs of
+/// whitespace collapsed to single space characters and leading and trailing
+/// whitespace removed.
+///
+/// For example, the following will all match successfully:
+///
+///     expect("hello   world", equalsIgnoringWhitespace("hello world"));
+///     expect("  hello world", equalsIgnoringWhitespace("hello world"));
+///     expect("hello world  ", equalsIgnoringWhitespace("hello world"));
+///
+/// The following will not match:
+///
+///     expect("helloworld", equalsIgnoringWhitespace("hello world"));
+///     expect("he llo world", equalsIgnoringWhitespace("hello world"));
+Matcher equalsIgnoringWhitespace(String value) =>
+    new _IsEqualIgnoringWhitespace(value);
+
+class _IsEqualIgnoringWhitespace extends _StringMatcher {
+  final String _value;
+  final String _matchValue;
+
+  _IsEqualIgnoringWhitespace(String value)
+      : _value = value,
+        _matchValue = collapseWhitespace(value);
+
+  bool matches(item, Map matchState) =>
+      item is String && _matchValue == collapseWhitespace(item);
+
+  Description describe(Description description) =>
+      description.addDescriptionOf(_matchValue).add(' ignoring whitespace');
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    if (item is String) {
+      return mismatchDescription
+          .add('is ')
+          .addDescriptionOf(collapseWhitespace(item))
+          .add(' with whitespace compressed');
+    } else {
+      return super.describeMismatch(
+          item, mismatchDescription, matchState, verbose);
+    }
+  }
+}
+
+/// Returns a matcher that matches if the match argument is a string and
+/// starts with [prefixString].
+Matcher startsWith(String prefixString) => new _StringStartsWith(prefixString);
+
+class _StringStartsWith extends _StringMatcher {
+  final String _prefix;
+
+  const _StringStartsWith(this._prefix);
+
+  bool matches(item, Map matchState) =>
+      item is String && item.startsWith(_prefix);
+
+  Description describe(Description description) =>
+      description.add('a string starting with ').addDescriptionOf(_prefix);
+}
+
+/// Returns a matcher that matches if the match argument is a string and
+/// ends with [suffixString].
+Matcher endsWith(String suffixString) => new _StringEndsWith(suffixString);
+
+class _StringEndsWith extends _StringMatcher {
+  final String _suffix;
+
+  const _StringEndsWith(this._suffix);
+
+  bool matches(item, Map matchState) =>
+      item is String && item.endsWith(_suffix);
+
+  Description describe(Description description) =>
+      description.add('a string ending with ').addDescriptionOf(_suffix);
+}
+
+/// Returns a matcher that matches if the match argument is a string and
+/// contains a given list of [substrings] in relative order.
+///
+/// For example, `stringContainsInOrder(["a", "e", "i", "o", "u"])` will match
+/// "abcdefghijklmnopqrstuvwxyz".
+
+Matcher stringContainsInOrder(List<String> substrings) =>
+    new _StringContainsInOrder(substrings);
+
+class _StringContainsInOrder extends _StringMatcher {
+  final List<String> _substrings;
+
+  const _StringContainsInOrder(this._substrings);
+
+  bool matches(item, Map matchState) {
+    if (!(item is String)) {
+      return false;
+    }
+    var from_index = 0;
+    for (var s in _substrings) {
+      from_index = item.indexOf(s, from_index);
+      if (from_index < 0) return false;
+    }
+    return true;
+  }
+
+  Description describe(Description description) => description.addAll(
+      'a string containing ', ', ', ' in order', _substrings);
+}
+
+/// Returns a matcher that matches if the match argument is a string and
+/// matches the regular expression given by [re].
+///
+/// [re] can be a [RegExp] instance or a [String]; in the latter case it will be
+/// used to create a RegExp instance.
+Matcher matches(re) => new _MatchesRegExp(re);
+
+class _MatchesRegExp extends _StringMatcher {
+  RegExp _regexp;
+
+  _MatchesRegExp(re) {
+    if (re is String) {
+      _regexp = new RegExp(re);
+    } else if (re is RegExp) {
+      _regexp = re;
+    } else {
+      throw new ArgumentError('matches requires a regexp or string');
+    }
+  }
+
+  bool matches(item, Map matchState) =>
+      item is String ? _regexp.hasMatch(item) : false;
+
+  Description describe(Description description) =>
+      description.add("match '${_regexp.pattern}'");
+}
+
+// String matchers match against a string. We add this intermediate
+// class to give better mismatch error messages than the base Matcher class.
+abstract class _StringMatcher extends Matcher {
+  const _StringMatcher();
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    if (!(item is String)) {
+      return mismatchDescription.addDescriptionOf(item).add(' not a string');
+    } else {
+      return super.describeMismatch(
+          item, mismatchDescription, matchState, verbose);
+    }
+  }
+}
+
+/// Utility function to collapse whitespace runs to single spaces
+/// and strip leading/trailing whitespace.
+String collapseWhitespace(String string) {
+  var result = new StringBuffer();
+  var skipSpace = true;
+  for (var i = 0; i < string.length; i++) {
+    var character = string[i];
+    if (_isWhitespace(character)) {
+      if (!skipSpace) {
+        result.write(' ');
+        skipSpace = true;
+      }
+    } else {
+      result.write(character);
+      skipSpace = false;
+    }
+  }
+  return result.toString().trim();
+}
+
+bool _isWhitespace(String ch) =>
+    ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
diff --git a/matcher/lib/src/throws_matcher.dart b/matcher/lib/src/throws_matcher.dart
new file mode 100644
index 0000000..461c585
--- /dev/null
+++ b/matcher/lib/src/throws_matcher.dart
@@ -0,0 +1,112 @@
+// 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 matcher.throws_matcher;
+
+import 'dart:async';
+
+import 'expect.dart';
+import 'interfaces.dart';
+import 'util.dart';
+
+/// This can be used to match two kinds of objects:
+///
+///   * A [Function] that throws an exception when called. The function cannot
+///     take any arguments. If you want to test that a function expecting
+///     arguments throws, wrap it in another zero-argument function that calls
+///     the one you want to test.
+///
+///   * A [Future] that completes with an exception. Note that this creates an
+///     asynchronous expectation. The call to `expect()` that includes this will
+///     return immediately and execution will continue. Later, when the future
+///     completes, the actual expectation will run.
+const Matcher throws = const Throws();
+
+/// This can be used to match two kinds of objects:
+///
+///   * A [Function] that throws an exception when called. The function cannot
+///     take any arguments. If you want to test that a function expecting
+///     arguments throws, wrap it in another zero-argument function that calls
+///     the one you want to test.
+///
+///   * A [Future] that completes with an exception. Note that this creates an
+///     asynchronous expectation. The call to `expect()` that includes this will
+///     return immediately and execution will continue. Later, when the future
+///     completes, the actual expectation will run.
+///
+/// In both cases, when an exception is thrown, this will test that the exception
+/// object matches [matcher]. If [matcher] is not an instance of [Matcher], it
+/// will implicitly be treated as `equals(matcher)`.
+Matcher throwsA(matcher) => new Throws(wrapMatcher(matcher));
+
+class Throws extends Matcher {
+  final Matcher _matcher;
+
+  const Throws([Matcher matcher]) : this._matcher = matcher;
+
+  bool matches(item, Map matchState) {
+    if (item is! Function && item is! Future) return false;
+    if (item is Future) {
+      var done = wrapAsync((fn) => fn());
+
+      // Queue up an asynchronous expectation that validates when the future
+      // completes.
+      item.then((value) {
+        done(() {
+          fail("Expected future to fail, but succeeded with '$value'.");
+        });
+      }, onError: (error, trace) {
+        done(() {
+          if (_matcher == null) return;
+          var reason;
+          if (trace != null) {
+            var stackTrace = trace.toString();
+            stackTrace = "  ${stackTrace.replaceAll("\n", "\n  ")}";
+            reason = "Actual exception trace:\n$stackTrace";
+          }
+          expect(error, _matcher, reason: reason);
+        });
+      });
+      // It hasn't failed yet.
+      return true;
+    }
+
+    try {
+      item();
+      return false;
+    } catch (e, s) {
+      if (_matcher == null || _matcher.matches(e, matchState)) {
+        return true;
+      } else {
+        addStateInfo(matchState, {'exception': e, 'stack': s});
+        return false;
+      }
+    }
+  }
+
+  Description describe(Description description) {
+    if (_matcher == null) {
+      return description.add("throws");
+    } else {
+      return description.add('throws ').addDescriptionOf(_matcher);
+    }
+  }
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    if (item is! Function && item is! Future) {
+      return mismatchDescription.add('is not a Function or Future');
+    } else if (_matcher == null || matchState['exception'] == null) {
+      return mismatchDescription.add('did not throw');
+    } else {
+      mismatchDescription
+          .add('threw ')
+          .addDescriptionOf(matchState['exception']);
+      if (verbose) {
+        mismatchDescription.add(' at ').add(matchState['stack'].toString());
+      }
+      return mismatchDescription;
+    }
+  }
+}
diff --git a/matcher/lib/src/throws_matchers.dart b/matcher/lib/src/throws_matchers.dart
new file mode 100644
index 0000000..35dfb39
--- /dev/null
+++ b/matcher/lib/src/throws_matchers.dart
@@ -0,0 +1,44 @@
+// 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 matcher.throws_matchers;
+
+import 'error_matchers.dart';
+import 'interfaces.dart';
+import 'throws_matcher.dart';
+
+/// A matcher for functions that throw ArgumentError.
+const Matcher throwsArgumentError = const Throws(isArgumentError);
+
+/// A matcher for functions that throw ConcurrentModificationError.
+const Matcher throwsConcurrentModificationError =
+    const Throws(isConcurrentModificationError);
+
+/// A matcher for functions that throw CyclicInitializationError.
+const Matcher throwsCyclicInitializationError =
+    const Throws(isCyclicInitializationError);
+
+/// A matcher for functions that throw Exception.
+const Matcher throwsException = const Throws(isException);
+
+/// A matcher for functions that throw FormatException.
+const Matcher throwsFormatException = const Throws(isFormatException);
+
+/// A matcher for functions that throw NoSuchMethodError.
+const Matcher throwsNoSuchMethodError = const Throws(isNoSuchMethodError);
+
+/// A matcher for functions that throw NullThrownError.
+const Matcher throwsNullThrownError = const Throws(isNullThrownError);
+
+/// A matcher for functions that throw RangeError.
+const Matcher throwsRangeError = const Throws(isRangeError);
+
+/// A matcher for functions that throw StateError.
+const Matcher throwsStateError = const Throws(isStateError);
+
+/// A matcher for functions that throw Exception.
+const Matcher throwsUnimplementedError = const Throws(isUnimplementedError);
+
+/// A matcher for functions that throw UnsupportedError.
+const Matcher throwsUnsupportedError = const Throws(isUnsupportedError);
diff --git a/matcher/lib/src/util.dart b/matcher/lib/src/util.dart
new file mode 100644
index 0000000..8706009
--- /dev/null
+++ b/matcher/lib/src/util.dart
@@ -0,0 +1,65 @@
+// 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 matcher.util;
+
+import 'core_matchers.dart';
+import 'interfaces.dart';
+
+/// A [Map] between whitespace characters and their escape sequences.
+const _escapeMap = const {
+  '\n': r'\n',
+  '\r': r'\r',
+  '\f': r'\f',
+  '\b': r'\b',
+  '\t': r'\t',
+  '\v': r'\v',
+  '\x7F': r'\x7F', // delete
+};
+
+/// A [RegExp] that matches whitespace characters that should be escaped.
+final _escapeRegExp = new RegExp(
+    "[\\x00-\\x07\\x0E-\\x1F${_escapeMap.keys.map(_getHexLiteral).join()}]");
+
+/// Useful utility for nesting match states.
+void addStateInfo(Map matchState, Map values) {
+  var innerState = new Map.from(matchState);
+  matchState.clear();
+  matchState['state'] = innerState;
+  matchState.addAll(values);
+}
+
+/// Takes an argument and returns an equivalent [Matcher].
+///
+/// If the argument is already a matcher this does nothing,
+/// else if the argument is a function, it generates a predicate
+/// function matcher, else it generates an equals matcher.
+Matcher wrapMatcher(x) {
+  if (x is Matcher) {
+    return x;
+  } else if (x is Function) {
+    return predicate(x);
+  } else {
+    return equals(x);
+  }
+}
+
+/// Returns [str] with all whitespace characters represented as their escape
+/// sequences.
+///
+/// Backslash characters are escaped as `\\`
+String escape(String str) {
+  str = str.replaceAll('\\', r'\\');
+  return str.replaceAllMapped(_escapeRegExp, (match) {
+    var mapped = _escapeMap[match[0]];
+    if (mapped != null) return mapped;
+    return _getHexLiteral(match[0]);
+  });
+}
+
+/// Given single-character string, return the hex-escaped equivalent.
+String _getHexLiteral(String input) {
+  int rune = input.runes.single;
+  return r'\x' + rune.toRadixString(16).toUpperCase().padLeft(2, '0');
+}
diff --git a/matcher/pubspec.yaml b/matcher/pubspec.yaml
new file mode 100644
index 0000000..47e8e84
--- /dev/null
+++ b/matcher/pubspec.yaml
@@ -0,0 +1,9 @@
+name: matcher
+version: 0.11.4+4
+author: Dart Team <misc@dartlang.org>
+description: Support for specifying test expectations
+homepage: https://github.com/dart-lang/matcher
+environment:
+  sdk: '>=1.0.0 <2.0.0'
+dev_dependencies:
+  unittest: '>=0.10.0 <0.12.0'
diff --git a/observe/lib/html.dart b/observe/lib/html.dart
new file mode 100644
index 0000000..07b4aff
--- /dev/null
+++ b/observe/lib/html.dart
@@ -0,0 +1,79 @@
+// Copyright (c) 2013, 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.
+
+// TODO(jmesserly): can we handle this more elegantly?
+// In general, it seems like we want a convenient way to take a Stream plus a
+// getter and convert this into an Observable.
+
+/// Helpers for exposing dart:html as observable data.
+library observe.html;
+
+import 'dart:html';
+
+import 'observe.dart';
+
+/// An observable version of [window.location.hash].
+final ObservableLocationHash windowLocation = new ObservableLocationHash._();
+
+class ObservableLocationHash extends ChangeNotifier {
+  Object _currentHash;
+
+  ObservableLocationHash._() {
+    // listen on changes to #hash in the URL
+    // Note: listen on both popState and hashChange, because IE9 doesn't support
+    // history API. See http://dartbug.com/5483
+    // TODO(jmesserly): only listen to these if someone is listening to our
+    // changes.
+    window.onHashChange.listen(_notifyHashChange);
+    window.onPopState.listen(_notifyHashChange);
+
+    _currentHash = hash;
+  }
+
+  @reflectable String get hash => window.location.hash;
+
+  /// Pushes a new URL state, similar to the affect of clicking a link.
+  /// Has no effect if the [value] already equals [window.location.hash].
+  @reflectable void set hash(String value) {
+    if (value == hash) return;
+
+    window.history.pushState(null, '', value);
+    _notifyHashChange(null);
+  }
+
+  void _notifyHashChange(_) {
+    var oldValue = _currentHash;
+    _currentHash = hash;
+    notifyPropertyChange(#hash, oldValue, _currentHash);
+  }
+}
+
+/// *Deprecated* use [CssClassSet.toggle] instead.
+///
+/// Add or remove CSS class [className] based on the [value].
+@deprecated
+void updateCssClass(Element element, String className, bool value) {
+  if (value == true) {
+    element.classes.add(className);
+  } else {
+    element.classes.remove(className);
+  }
+}
+
+/// *Deprecated* use `class="{{ binding }}"` in your HTML instead. It will also
+/// work on a `<polymer-element>`.
+///
+/// Bind a CSS class to the observable [object] and property [path].
+@deprecated
+PathObserver bindCssClass(Element element, String className,
+    Observable object, String path) {
+
+  callback(value) {
+    updateCssClass(element, className, value);
+  }
+
+  var obs = new PathObserver(object, path);
+  callback(obs.open(callback));
+  return obs;
+}
diff --git a/observe/lib/mirrors_used.dart b/observe/lib/mirrors_used.dart
new file mode 100644
index 0000000..b3d3f43
--- /dev/null
+++ b/observe/lib/mirrors_used.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2013, 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.
+
+/// An empty library that declares what needs to be retained with [MirrorsUsed]
+/// if you were to use observables together with mirrors. By default this is not
+/// included because frameworks using this package also use code generation to
+/// avoid using mirrors at deploy time.
+library observe.mirrors_used;
+
+// Note: ObservableProperty is in this list only for the unusual use case of
+// invoking dart2js without running this package's transformers. The
+// transformer in `lib/transformer.dart` will replace @observable with the
+// @reflectable annotation.
+@MirrorsUsed(metaTargets: const [Reflectable, ObservableProperty],
+    override: 'smoke.mirrors')
+import 'dart:mirrors' show MirrorsUsed;
+import 'package:observe/observe.dart' show Reflectable, ObservableProperty;
diff --git a/observe/lib/observe.dart b/observe/lib/observe.dart
new file mode 100644
index 0000000..20912f9
--- /dev/null
+++ b/observe/lib/observe.dart
@@ -0,0 +1,117 @@
+// Copyright (c) 2013, 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.
+
+/// Support for observing changes in model-view architectures.
+///
+/// **Warning:** This library is experimental, and APIs are subject to change.
+///
+/// This library is used to observe changes to [Observable] types. It also
+/// has helpers to make implementing and using [Observable] objects easy.
+///
+/// You can provide an observable object in two ways. The simplest way is to
+/// use dirty checking to discover changes automatically:
+///
+///     import 'package:observe/observe.dart';
+///     import 'package:observe/mirrors_used.dart'; // for smaller code
+///
+///     class Monster extends Unit with Observable {
+///       @observable int health = 100;
+///
+///       void damage(int amount) {
+///         print('$this takes $amount damage!');
+///         health -= amount;
+///       }
+///
+///       toString() => 'Monster with $health hit points';
+///     }
+///
+///     main() {
+///       var obj = new Monster();
+///       obj.changes.listen((records) {
+///         print('Changes to $obj were: $records');
+///       });
+///       // No changes are delivered until we check for them
+///       obj.damage(10);
+///       obj.damage(20);
+///       print('dirty checking!');
+///       Observable.dirtyCheck();
+///       print('done!');
+///     }
+///
+/// A more sophisticated approach is to implement the change notification
+/// manually. This avoids the potentially expensive [Observable.dirtyCheck]
+/// operation, but requires more work in the object:
+///
+///     import 'package:observe/observe.dart';
+///     import 'package:observe/mirrors_used.dart'; // for smaller code
+///
+///     class Monster extends Unit with ChangeNotifier {
+///       int _health = 100;
+///       @reflectable get health => _health;
+///       @reflectable set health(val) {
+///         _health = notifyPropertyChange(#health, _health, val);
+///       }
+///
+///       void damage(int amount) {
+///         print('$this takes $amount damage!');
+///         health -= amount;
+///       }
+///
+///       toString() => 'Monster with $health hit points';
+///     }
+///
+///     main() {
+///       var obj = new Monster();
+///       obj.changes.listen((records) {
+///         print('Changes to $obj were: $records');
+///       });
+///       // Schedules asynchronous delivery of these changes
+///       obj.damage(10);
+///       obj.damage(20);
+///       print('done!');
+///     }
+///
+/// **Note**: by default this package uses mirrors to access getters and setters
+/// marked with `@reflectable`. Dart2js disables tree-shaking if there are any
+/// uses of mirrors, unless you declare how mirrors are used (via the
+/// [MirrorsUsed](https://api.dartlang.org/apidocs/channels/stable/#dart-mirrors.MirrorsUsed)
+/// annotation).
+///
+/// As of version 0.10.0, this package doesn't declare `@MirrorsUsed`. This is
+/// because we intend to use mirrors for development time, but assume that
+/// frameworks and apps that use this pacakge will either generate code that
+/// replaces the use of mirrors, or add the `@MirrorsUsed` declaration
+/// themselves.  For convenience, you can import
+/// `package:observe/mirrors_used.dart` as shown on the first example above.
+/// That will add a `@MirrorsUsed` annotation that preserves properties and
+/// classes labeled with `@reflectable` and properties labeled with
+/// `@observable`.
+///
+/// If you are using the `package:observe/mirrors_used.dart` import, you can
+/// also make use of `@reflectable` on your own classes and dart2js will
+/// preserve all of its members for reflection.
+///
+/// [Tools](https://www.dartlang.org/polymer-dart/) exist to convert the first
+/// form into the second form automatically, to get the best of both worlds.
+library observe;
+
+// This library contains code ported from observe-js:
+// https://github.com/Polymer/observe-js/blob/0152d542350239563d0f2cad39d22d3254bd6c2a/src/observe.js
+// We port what is needed for data bindings. Most of the functionality is
+// ported, except where differences are needed for Dart's Observable type.
+
+export 'src/bindable.dart';
+export 'src/bind_property.dart';
+export 'src/change_notifier.dart';
+export 'src/change_record.dart';
+export 'src/list_path_observer.dart';
+export 'src/list_diff.dart' show ListChangeRecord;
+export 'src/metadata.dart';
+export 'src/observable.dart' hide notifyPropertyChangeHelper;
+export 'src/observable_box.dart';
+export 'src/observable_list.dart';
+export 'src/observable_map.dart';
+export 'src/observer_transform.dart';
+export 'src/path_observer.dart' hide getSegmentsOfPropertyPathForTesting;
+export 'src/to_observable.dart';
diff --git a/observe/lib/src/bind_property.dart b/observe/lib/src/bind_property.dart
new file mode 100644
index 0000000..21e14f9
--- /dev/null
+++ b/observe/lib/src/bind_property.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2013, 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 observe.src.bind_property;
+
+import 'dart:async';
+import 'package:observe/observe.dart';
+
+/// Forwards an observable property from one object to another. For example:
+///
+///     class MyModel extends Observable {
+///       StreamSubscription _sub;
+///       MyOtherModel _otherModel;
+///
+///       MyModel() {
+///         ...
+///         _sub = onPropertyChange(_otherModel, #value,
+///             () => notifyPropertyChange(#prop, oldValue, newValue);
+///       }
+///
+///       String get prop => _otherModel.value;
+///       set prop(String value) { _otherModel.value = value; }
+///     }
+///
+/// See also [notifyPropertyChange].
+// TODO(jmesserly): make this an instance method?
+StreamSubscription onPropertyChange(Observable source, Symbol sourceName,
+    void callback()) {
+  return source.changes.listen((records) {
+    for (var record in records) {
+      if (record is PropertyChangeRecord &&
+          (record as PropertyChangeRecord).name == sourceName) {
+        callback();
+        break;
+      }
+    }
+  });
+}
diff --git a/observe/lib/src/bindable.dart b/observe/lib/src/bindable.dart
new file mode 100644
index 0000000..c3314a2
--- /dev/null
+++ b/observe/lib/src/bindable.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2013, 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 observe.src.bindable;
+
+/// An object that can be data bound.
+// Normally this is used with 'package:template_binding'.
+// TODO(jmesserly): Node.bind polyfill calls this "observable"
+abstract class Bindable {
+  // Dart note: changed setValue to be "set value" and discardChanges() to
+  // be "get value".
+
+  /// Initiates observation and returns the initial value.
+  /// The callback will be called with the updated [value].
+  ///
+  /// Some subtypes may chose to provide additional arguments, such as
+  /// [PathObserver] providing the old value as the second argument.
+  /// However, they must support callbacks with as few as 0 or 1 argument.
+  /// This can be implemented by performing an "is" type test on the callback.
+  open(callback);
+
+  /// Stops future notifications and frees the reference to the callback passed
+  /// to [open], so its memory can be collected even if this Bindable is alive.
+  void close();
+
+  /// Gets the current value of the bindings.
+  /// Note: once the value of a [Bindable] is fetched, the callback passed to
+  /// [open] should not be called again with this new value.
+  /// In other words, any pending change notifications must be discarded.
+  // TODO(jmesserly): I don't like a getter with side effects. Should we just
+  // rename the getter/setter pair to discardChanges/setValue like they are in
+  // JavaScript?
+  get value;
+
+  /// This can be implemented for two-way bindings. By default does nothing.
+  set value(newValue) {}
+
+  /// Deliver changes. Typically this will perform dirty-checking, if any is
+  /// needed.
+  void deliver() {}
+}
diff --git a/observe/lib/src/change_notifier.dart b/observe/lib/src/change_notifier.dart
new file mode 100644
index 0000000..7febee5
--- /dev/null
+++ b/observe/lib/src/change_notifier.dart
@@ -0,0 +1,81 @@
+// Copyright (c) 2013, 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 observe.src.change_notifier;
+
+import 'dart:async';
+import 'dart:collection' show UnmodifiableListView;
+import 'package:observe/observe.dart';
+import 'package:observe/src/observable.dart' show notifyPropertyChangeHelper;
+
+/// Mixin and base class for implementing an [Observable] object that performs
+/// its own change notifications, and does not need to be considered by
+/// [Observable.dirtyCheck].
+///
+/// When a field, property, or indexable item is changed, a derived class should
+/// call [notifyPropertyChange]. See that method for an example.
+abstract class ChangeNotifier implements Observable {
+  StreamController _changes;
+  List<ChangeRecord> _records;
+
+  Stream<List<ChangeRecord>> get changes {
+    if (_changes == null) {
+      _changes = new StreamController.broadcast(sync: true,
+          onListen: observed, onCancel: unobserved);
+    }
+    return _changes.stream;
+  }
+
+  // TODO(jmesserly): should these be public? They're useful lifecycle methods
+  // for subclasses. Ideally they'd be protected.
+  /// Override this method to be called when the [changes] are first observed.
+  void observed() {}
+
+  /// Override this method to be called when the [changes] are no longer being
+  /// observed.
+  void unobserved() {
+    // Free some memory
+    _changes = null;
+  }
+
+  bool deliverChanges() {
+    var records = _records;
+    _records = null;
+    if (hasObservers && records != null) {
+      _changes.add(new UnmodifiableListView<ChangeRecord>(records));
+      return true;
+    }
+    return false;
+  }
+
+  /// True if this object has any observers, and should call
+  /// [notifyPropertyChange] for changes.
+  bool get hasObservers => _changes != null && _changes.hasListener;
+
+  /// Notify that the field [name] of this object has been changed.
+  ///
+  /// The [oldValue] and [newValue] are also recorded. If the two values are
+  /// equal, no change will be recorded.
+  ///
+  /// For convenience this returns [newValue]. This makes it easy to use in a
+  /// setter:
+  ///
+  ///     var _myField;
+  ///     @reflectable get myField => _myField;
+  ///     @reflectable set myField(value) {
+  ///       _myField = notifyPropertyChange(#myField, _myField, value);
+  ///     }
+  notifyPropertyChange(Symbol field, Object oldValue, Object newValue)
+      => notifyPropertyChangeHelper(this, field, oldValue, newValue);
+
+  void notifyChange(ChangeRecord record) {
+    if (!hasObservers) return;
+
+    if (_records == null) {
+      _records = [];
+      scheduleMicrotask(deliverChanges);
+    }
+    _records.add(record);
+  }
+}
diff --git a/observe/lib/src/change_record.dart b/observe/lib/src/change_record.dart
new file mode 100644
index 0000000..576a173
--- /dev/null
+++ b/observe/lib/src/change_record.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2013, 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 observe.src.change_record;
+
+import 'package:observe/observe.dart';
+
+
+/// Records a change to an [Observable].
+// TODO(jmesserly): remove this type
+abstract class ChangeRecord {}
+
+/// A change record to a field of an observable object.
+class PropertyChangeRecord<T> extends ChangeRecord {
+  /// The object that changed.
+  final object;
+
+  /// The name of the property that changed.
+  final Symbol name;
+
+  /// The previous value of the property.
+  final T oldValue;
+
+  /// The new value of the property.
+  final T newValue;
+
+  PropertyChangeRecord(this.object, this.name, this.oldValue, this.newValue);
+
+  String toString() =>
+      '#<PropertyChangeRecord $name from: $oldValue to: $newValue>';
+}
diff --git a/observe/lib/src/dirty_check.dart b/observe/lib/src/dirty_check.dart
new file mode 100644
index 0000000..2084f22
--- /dev/null
+++ b/observe/lib/src/dirty_check.dart
@@ -0,0 +1,133 @@
+// Copyright (c) 2013, 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.
+
+/// *Warning*: this library is **internal**, and APIs are subject to change.
+///
+/// Tracks observable objects for dirty checking and testing purposes.
+///
+/// It can collect all observed objects, which can be used to trigger
+/// predictable delivery of all pending changes in a test, including objects
+/// allocated internally to another library, such as those in
+/// `package:template_binding`.
+library observe.src.dirty_check;
+
+import 'dart:async';
+import 'package:logging/logging.dart';
+import 'package:observe/observe.dart' show Observable;
+
+/// The number of active observables in the system.
+int get allObservablesCount => _allObservablesCount;
+
+int _allObservablesCount = 0;
+
+List<Observable> _allObservables = null;
+
+bool _delivering = false;
+
+void registerObservable(Observable obj) {
+  if (_allObservables == null) _allObservables = <Observable>[];
+  _allObservables.add(obj);
+  _allObservablesCount++;
+}
+
+/// Synchronously deliver all change records for known observables.
+///
+/// This will execute [Observable.deliverChanges] on objects that inherit from
+/// [Observable].
+// Note: this is called performMicrotaskCheckpoint in change_summary.js.
+void dirtyCheckObservables() {
+  if (_delivering) return;
+  if (_allObservables == null) return;
+
+  _delivering = true;
+
+  int cycles = 0;
+  bool anyChanged = false;
+  List debugLoop = null;
+  do {
+    cycles++;
+    if (cycles == MAX_DIRTY_CHECK_CYCLES) {
+      debugLoop = [];
+    }
+
+    var toCheck = _allObservables;
+    _allObservables = <Observable>[];
+    anyChanged = false;
+
+    for (int i = 0; i < toCheck.length; i++) {
+      final observer = toCheck[i];
+      if (observer.hasObservers) {
+        if (observer.deliverChanges()) {
+          anyChanged = true;
+          if (debugLoop != null) debugLoop.add([i, observer]);
+        }
+        _allObservables.add(observer);
+      }
+    }
+  } while (cycles < MAX_DIRTY_CHECK_CYCLES && anyChanged);
+
+  if (debugLoop != null && anyChanged) {
+    _logger.warning('Possible loop in Observable.dirtyCheck, stopped '
+        'checking.');
+    for (final info in debugLoop) {
+      _logger.warning('In last iteration Observable changed at index '
+          '${info[0]}, object: ${info[1]}.');
+    }
+  }
+
+  _allObservablesCount = _allObservables.length;
+  _delivering = false;
+}
+
+const MAX_DIRTY_CHECK_CYCLES = 1000;
+
+/// Log for messages produced at runtime by this library. Logging can be
+/// configured by accessing Logger.root from the logging library.
+final Logger _logger = new Logger('Observable.dirtyCheck');
+
+/// Creates a [ZoneSpecification] to set up automatic dirty checking after each
+/// batch of async operations. This ensures that change notifications are always
+/// delivered. Typically used via [dirtyCheckZone].
+ZoneSpecification dirtyCheckZoneSpec() {
+  bool pending = false;
+
+  enqueueDirtyCheck(ZoneDelegate parent, Zone zone) {
+    // Only schedule one dirty check per microtask.
+    if (pending) return;
+
+    pending = true;
+    parent.scheduleMicrotask(zone, () {
+      pending = false;
+      Observable.dirtyCheck();
+    });
+  }
+
+  wrapCallback(Zone self, ZoneDelegate parent, Zone zone, f()) {
+    // TODO(jmesserly): why does this happen?
+    if (f == null) return f;
+    return () {
+      enqueueDirtyCheck(parent, zone);
+      return f();
+    };
+  }
+
+  wrapUnaryCallback(Zone self, ZoneDelegate parent, Zone zone, f(x)) {
+    // TODO(jmesserly): why does this happen?
+    if (f == null) return f;
+    return (x) {
+      enqueueDirtyCheck(parent, zone);
+      return f(x);
+    };
+  }
+
+  return new ZoneSpecification(
+      registerCallback: wrapCallback,
+      registerUnaryCallback: wrapUnaryCallback);
+}
+
+/// Forks a [Zone] off the current one that does dirty-checking automatically
+/// after each batch of async operations. Equivalent to:
+///
+///     Zone.current.fork(specification: dirtyCheckZoneSpec());
+Zone dirtyCheckZone() => Zone.current.fork(specification: dirtyCheckZoneSpec());
diff --git a/observe/lib/src/list_diff.dart b/observe/lib/src/list_diff.dart
new file mode 100644
index 0000000..cbc3f2f
--- /dev/null
+++ b/observe/lib/src/list_diff.dart
@@ -0,0 +1,392 @@
+// Copyright (c) 2013, 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 observe.src.list_diff;
+
+import 'dart:math' as math;
+import 'dart:collection' show UnmodifiableListView;
+import 'change_record.dart' show ChangeRecord;
+
+/// A summary of an individual change to a [List].
+///
+/// Each delta represents that at the [index], [removed] sequence of items were
+/// removed, and counting forward from [index], [addedCount] items were added.
+class ListChangeRecord extends ChangeRecord {
+  /// The list that changed.
+  final List object;
+
+  /// The index of the change.
+  int get index => _index;
+
+  /// The items removed, if any. Otherwise this will be an empty list.
+  List get removed => _unmodifiableRemoved;
+  UnmodifiableListView _unmodifiableRemoved;
+
+  /// Mutable version of [removed], used during the algorithms as they are
+  /// constructing the object.
+  List _removed;
+
+  /// The number of items added.
+  int get addedCount => _addedCount;
+
+  // Note: conceptually these are final, but for convenience we increment it as
+  // we build the object. It will be "frozen" by the time it is returned the the
+  // user.
+  int _index, _addedCount;
+
+  ListChangeRecord._(this.object, this._index, removed, this._addedCount)
+      : _removed = removed,
+        _unmodifiableRemoved = new UnmodifiableListView(removed);
+
+  factory ListChangeRecord(List object, int index,
+      {List removed, int addedCount}) {
+
+    if (removed == null) removed = [];
+    if (addedCount == null) addedCount = 0;
+    return new ListChangeRecord._(object, index, removed, addedCount);
+  }
+
+  /// Returns true if the provided index was changed by this operation.
+  bool indexChanged(key) {
+    // If key isn't an int, or before the index, then it wasn't changed.
+    if (key is! int || key < index) return false;
+
+    // If this was a shift operation, anything after index is changed.
+    if (addedCount != removed.length) return true;
+
+    // Otherwise, anything in the update range was changed.
+    return key < index + addedCount;
+  }
+
+  String toString() => '#<ListChangeRecord index: $index, '
+      'removed: $removed, addedCount: $addedCount>';
+}
+
+// Note: This function is *based* on the computation of the Levenshtein
+// "edit" distance. The one change is that "updates" are treated as two
+// edits - not one. With List splices, an update is really a delete
+// followed by an add. By retaining this, we optimize for "keeping" the
+// maximum array items in the original array. For example:
+//
+//   'xxxx123' -> '123yyyy'
+//
+// With 1-edit updates, the shortest path would be just to update all seven
+// characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
+// leaves the substring '123' intact.
+List<List<int>> _calcEditDistances(List current, int currentStart,
+    int currentEnd, List old, int oldStart, int oldEnd) {
+  // "Deletion" columns
+  var rowCount = oldEnd - oldStart + 1;
+  var columnCount = currentEnd - currentStart + 1;
+  var distances = new List(rowCount);
+
+  // "Addition" rows. Initialize null column.
+  for (var i = 0; i < rowCount; i++) {
+    distances[i] = new List(columnCount);
+    distances[i][0] = i;
+  }
+
+  // Initialize null row
+  for (var j = 0; j < columnCount; j++) {
+    distances[0][j] = j;
+  }
+
+  for (var i = 1; i < rowCount; i++) {
+    for (var j = 1; j < columnCount; j++) {
+      if (old[oldStart + i - 1] == current[currentStart + j - 1]) {
+        distances[i][j] = distances[i - 1][j - 1];
+      } else {
+        var north = distances[i - 1][j] + 1;
+        var west = distances[i][j - 1] + 1;
+        distances[i][j] = math.min(north, west);
+      }
+    }
+  }
+
+  return distances;
+}
+
+const _EDIT_LEAVE = 0;
+const _EDIT_UPDATE = 1;
+const _EDIT_ADD = 2;
+const _EDIT_DELETE = 3;
+
+// This starts at the final weight, and walks "backward" by finding
+// the minimum previous weight recursively until the origin of the weight
+// matrix.
+List<int> _spliceOperationsFromEditDistances(List<List<int>> distances) {
+  var i = distances.length - 1;
+  var j = distances[0].length - 1;
+  var current = distances[i][j];
+  var edits = [];
+  while (i > 0 || j > 0) {
+    if (i == 0) {
+      edits.add(_EDIT_ADD);
+      j--;
+      continue;
+    }
+    if (j == 0) {
+      edits.add(_EDIT_DELETE);
+      i--;
+      continue;
+    }
+    var northWest = distances[i - 1][j - 1];
+    var west = distances[i - 1][j];
+    var north = distances[i][j - 1];
+
+    var min = math.min(math.min(west, north), northWest);
+
+    if (min == northWest) {
+      if (northWest == current) {
+        edits.add(_EDIT_LEAVE);
+      } else {
+        edits.add(_EDIT_UPDATE);
+        current = northWest;
+      }
+      i--;
+      j--;
+    } else if (min == west) {
+      edits.add(_EDIT_DELETE);
+      i--;
+      current = west;
+    } else {
+      edits.add(_EDIT_ADD);
+      j--;
+      current = north;
+    }
+  }
+
+  return edits.reversed.toList();
+}
+
+int _sharedPrefix(List arr1, List arr2, int searchLength) {
+  for (var i = 0; i < searchLength; i++) {
+    if (arr1[i] != arr2[i]) {
+      return i;
+    }
+  }
+  return searchLength;
+}
+
+int _sharedSuffix(List arr1, List arr2, int searchLength) {
+  var index1 = arr1.length;
+  var index2 = arr2.length;
+  var count = 0;
+  while (count < searchLength && arr1[--index1] == arr2[--index2]) {
+    count++;
+  }
+  return count;
+}
+
+/// Lacking individual splice mutation information, the minimal set of
+/// splices can be synthesized given the previous state and final state of an
+/// array. The basic approach is to calculate the edit distance matrix and
+/// choose the shortest path through it.
+///
+/// Complexity: O(l * p)
+///   l: The length of the current array
+///   p: The length of the old array
+List<ListChangeRecord> calcSplices(List current, int currentStart,
+    int currentEnd, List old, int oldStart, int oldEnd) {
+
+  var prefixCount = 0;
+  var suffixCount = 0;
+
+  var minLength = math.min(currentEnd - currentStart, oldEnd - oldStart);
+  if (currentStart == 0 && oldStart == 0) {
+    prefixCount = _sharedPrefix(current, old, minLength);
+  }
+
+  if (currentEnd == current.length && oldEnd == old.length) {
+    suffixCount = _sharedSuffix(current, old, minLength - prefixCount);
+  }
+
+  currentStart += prefixCount;
+  oldStart += prefixCount;
+  currentEnd -= suffixCount;
+  oldEnd -= suffixCount;
+
+  if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0) {
+    return const [];
+  }
+
+  if (currentStart == currentEnd) {
+    var splice = new ListChangeRecord(current, currentStart);
+    while (oldStart < oldEnd) {
+      splice._removed.add(old[oldStart++]);
+    }
+
+    return [splice ];
+  } else if (oldStart == oldEnd)
+    return [new ListChangeRecord(current, currentStart,
+        addedCount: currentEnd - currentStart)];
+
+  var ops = _spliceOperationsFromEditDistances(
+      _calcEditDistances(current, currentStart, currentEnd, old, oldStart,
+          oldEnd));
+
+  ListChangeRecord splice = null;
+  var splices = <ListChangeRecord>[];
+  var index = currentStart;
+  var oldIndex = oldStart;
+  for (var i = 0; i < ops.length; i++) {
+    switch(ops[i]) {
+      case _EDIT_LEAVE:
+        if (splice != null) {
+          splices.add(splice);
+          splice = null;
+        }
+
+        index++;
+        oldIndex++;
+        break;
+      case _EDIT_UPDATE:
+        if (splice == null) splice = new ListChangeRecord(current, index);
+
+        splice._addedCount++;
+        index++;
+
+        splice._removed.add(old[oldIndex]);
+        oldIndex++;
+        break;
+      case _EDIT_ADD:
+        if (splice == null) splice = new ListChangeRecord(current, index);
+
+        splice._addedCount++;
+        index++;
+        break;
+      case _EDIT_DELETE:
+        if (splice == null) splice = new ListChangeRecord(current, index);
+
+        splice._removed.add(old[oldIndex]);
+        oldIndex++;
+        break;
+    }
+  }
+
+  if (splice != null) splices.add(splice);
+  return splices;
+}
+
+int _intersect(int start1, int end1, int start2, int end2) =>
+    math.min(end1, end2) - math.max(start1, start2);
+
+void _mergeSplice(List<ListChangeRecord> splices, ListChangeRecord record) {
+  var splice = new ListChangeRecord(record.object, record.index,
+      removed: record._removed.toList(), addedCount: record.addedCount);
+
+  var inserted = false;
+  var insertionOffset = 0;
+
+  // I think the way this works is:
+  // - the loop finds where the merge should happen
+  // - it applies the merge in a particular splice
+  // - then continues and updates the subsequent splices with any offset diff.
+  for (var i = 0; i < splices.length; i++) {
+    final current = splices[i];
+    current._index += insertionOffset;
+
+    if (inserted) continue;
+
+    var intersectCount = _intersect(
+        splice.index, splice.index + splice.removed.length,
+        current.index, current.index + current.addedCount);
+
+    if (intersectCount >= 0) {
+      // Merge the two splices
+
+      splices.removeAt(i);
+      i--;
+
+      insertionOffset -= current.addedCount - current.removed.length;
+
+      splice._addedCount += current.addedCount - intersectCount;
+      var deleteCount = splice.removed.length +
+                        current.removed.length - intersectCount;
+
+      if (splice.addedCount == 0 && deleteCount == 0) {
+        // merged splice is a noop. discard.
+        inserted = true;
+      } else {
+        var removed = current._removed;
+
+        if (splice.index < current.index) {
+          // some prefix of splice.removed is prepended to current.removed.
+          removed.insertAll(0,
+              splice.removed.getRange(0, current.index - splice.index));
+        }
+
+        if (splice.index + splice.removed.length >
+            current.index + current.addedCount) {
+          // some suffix of splice.removed is appended to current.removed.
+          removed.addAll(splice.removed.getRange(
+              current.index + current.addedCount - splice.index,
+              splice.removed.length));
+        }
+
+        splice._removed = removed;
+        splice._unmodifiableRemoved = current._unmodifiableRemoved;
+        if (current.index < splice.index) {
+          splice._index = current.index;
+        }
+      }
+    } else if (splice.index < current.index) {
+      // Insert splice here.
+
+      inserted = true;
+
+      splices.insert(i, splice);
+      i++;
+
+      var offset = splice.addedCount - splice.removed.length;
+      current._index += offset;
+      insertionOffset += offset;
+    }
+  }
+
+  if (!inserted) splices.add(splice);
+}
+
+List<ListChangeRecord> _createInitialSplices(List<Object> list,
+    List<ListChangeRecord> records) {
+
+  var splices = <ListChangeRecord>[];
+  for (var record in records) {
+    _mergeSplice(splices, record);
+  }
+  return splices;
+}
+
+/// We need to summarize change records. Consumers of these records want to
+/// apply the batch sequentially, and ensure that they can find inserted
+/// items by looking at that position in the list. This property does not
+/// hold in our record-as-you-go records. Consider:
+///
+///     var model = toObservable(['a', 'b']);
+///     model.removeAt(1);
+///     model.insertAll(0, ['c', 'd', 'e']);
+///     model.removeRange(1, 3);
+///     model.insert(1, 'f');
+///
+/// Here, we inserted some records and then removed some of them.
+/// If someone processed these records naively, they would "play back" the
+/// insert incorrectly, because those items will be shifted.
+List<ListChangeRecord> projectListSplices(List list,
+    List<ListChangeRecord> records) {
+  if (records.length <= 1) return records;
+
+  var splices = [];
+  for (var splice in _createInitialSplices(list, records)) {
+    if (splice.addedCount == 1 && splice.removed.length == 1) {
+      if (splice.removed[0] != list[splice.index]) splices.add(splice);
+      continue;
+    }
+
+    splices.addAll(calcSplices(list, splice.index,
+        splice.index + splice.addedCount, splice._removed, 0,
+        splice.removed.length));
+  }
+
+  return splices;
+}
diff --git a/observe/lib/src/list_path_observer.dart b/observe/lib/src/list_path_observer.dart
new file mode 100644
index 0000000..22a8199
--- /dev/null
+++ b/observe/lib/src/list_path_observer.dart
@@ -0,0 +1,73 @@
+// Copyright (c) 2013, 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 observe.src.list_path_observer;
+
+import 'dart:async';
+import 'package:observe/observe.dart';
+
+// Inspired by ArrayReduction at:
+// https://raw.github.com/rafaelw/ChangeSummary/master/util/array_reduction.js
+// The main difference is we support anything on the rich Dart Iterable API.
+
+/// Observes a path starting from each item in the list.
+@deprecated
+class ListPathObserver<E, P> extends ChangeNotifier {
+  final ObservableList<E> list;
+  final String _itemPath;
+  final List<PathObserver> _observers = <PathObserver>[];
+  StreamSubscription _sub;
+  bool _scheduled = false;
+  Iterable<P> _value;
+
+  ListPathObserver(this.list, String path)
+      : _itemPath = path {
+
+    // TODO(jmesserly): delay observation until we are observed.
+    _sub = list.listChanges.listen((records) {
+      for (var record in records) {
+        _observeItems(record.addedCount - record.removed.length);
+      }
+      _scheduleReduce(null);
+    });
+
+    _observeItems(list.length);
+    _reduce();
+  }
+
+  @reflectable Iterable<P> get value => _value;
+
+  void dispose() {
+    if (_sub != null) _sub.cancel();
+    _observers.forEach((o) => o.close());
+    _observers.clear();
+  }
+
+  void _reduce() {
+    _scheduled = false;
+    var newValue = _observers.map((o) => o.value);
+    _value = notifyPropertyChange(#value, _value, newValue);
+  }
+
+  void _scheduleReduce(_) {
+    if (_scheduled) return;
+    _scheduled = true;
+    scheduleMicrotask(_reduce);
+  }
+
+  void _observeItems(int lengthAdjust) {
+    if (lengthAdjust > 0) {
+      for (int i = 0; i < lengthAdjust; i++) {
+        int len = _observers.length;
+        var pathObs = new PathObserver(list, '[$len].$_itemPath');
+        pathObs.open(_scheduleReduce);
+        _observers.add(pathObs);
+      }
+    } else if (lengthAdjust < 0) {
+      for (int i = 0; i < -lengthAdjust; i++) {
+        _observers.removeLast().close();
+      }
+    }
+  }
+}
diff --git a/observe/lib/src/messages.dart b/observe/lib/src/messages.dart
new file mode 100644
index 0000000..ed12162
--- /dev/null
+++ b/observe/lib/src/messages.dart
@@ -0,0 +1,51 @@
+// 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.
+
+/// Contains all warning messages produced by the observe transformer.
+library observe.src.messages;
+
+import 'package:code_transformers/messages/messages.dart';
+
+const NO_OBSERVABLE_ON_LIBRARY = const MessageTemplate(
+    const MessageId('observe', 1),
+    '@observable on a library no longer has any effect. '
+    'Instead, annotate individual fields as @observable.',
+    '`@observable` not supported on libraries',
+    _COMMON_MESSAGE_WHERE_TO_USE_OBSERVABLE);
+
+const NO_OBSERVABLE_ON_TOP_LEVEL = const MessageTemplate(
+    const MessageId('observe', 2),
+    'Top-level fields can no longer be observable. '
+    'Observable fields must be in observable objects.',
+    '`@observable` not supported on top-level fields',
+    _COMMON_MESSAGE_WHERE_TO_USE_OBSERVABLE);
+
+const NO_OBSERVABLE_ON_CLASS = const MessageTemplate(
+    const MessageId('observe', 3),
+    '@observable on a class no longer has any effect. '
+    'Instead, annotate individual fields as @observable.',
+    '`@observable` not supported on classes',
+    _COMMON_MESSAGE_WHERE_TO_USE_OBSERVABLE);
+
+const NO_OBSERVABLE_ON_STATIC_FIELD = const MessageTemplate(
+    const MessageId('observe', 4),
+    'Static fields can no longer be observable. '
+    'Observable fields must be in observable objects.',
+    '`@observable` not supported on static fields',
+    _COMMON_MESSAGE_WHERE_TO_USE_OBSERVABLE);
+
+const REQUIRE_OBSERVABLE_INTERFACE = const MessageTemplate(
+    const MessageId('observe', 5),
+    'Observable fields must be in observable objects. '
+    'Change this class to extend, mix in, or implement Observable.',
+    '`@observable` field not in an `Observable` class',
+    _COMMON_MESSAGE_WHERE_TO_USE_OBSERVABLE);
+
+const String _COMMON_MESSAGE_WHERE_TO_USE_OBSERVABLE = '''
+Only instance fields on `Observable` classes can be observable,
+and you must explicitly annotate each observable field as `@observable`.
+
+Support for using the `@observable` annotation in libraries, classes, and
+elsewhere is deprecated.
+''';
diff --git a/observe/lib/src/metadata.dart b/observe/lib/src/metadata.dart
new file mode 100644
index 0000000..f128102
--- /dev/null
+++ b/observe/lib/src/metadata.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2013, 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 observe.src.metadata;
+
+/// Use `@observable` to make a field automatically observable, or to indicate
+/// that a property is observable. This only works on classes that extend or
+/// mix in `Observable`.
+const ObservableProperty observable = const ObservableProperty();
+
+/// An annotation that is used to make a property observable.
+/// Normally this is used via the [observable] constant, for example:
+///
+///     class Monster extends Observable {
+///       @observable int health;
+///     }
+///
+// TODO(sigmund): re-add this to the documentation when it's really true:
+//     If needed, you can subclass this to create another annotation that will
+//     also be treated as observable.
+// Note: observable properties imply reflectable.
+class ObservableProperty {
+  const ObservableProperty();
+}
+
+
+/// This can be used to retain any properties that you wish to access with
+/// Dart's mirror system. If you import `package:observe/mirrors_used.dart`, all
+/// classes or members annotated with `@reflectable` wil be preserved by dart2js
+/// during compilation.  This is necessary to make the member visible to
+/// `PathObserver`, or similar systems, once the code is deployed, if you are
+/// not doing a different kind of code-generation for your app. If you are using
+/// polymer, you most likely don't need to use this annotation anymore.
+const Reflectable reflectable = const Reflectable();
+
+/// An annotation that is used to make a type or member reflectable. This makes
+/// it available to `PathObserver` at runtime. For example:
+///
+///     @reflectable
+///     class Monster extends ChangeNotifier {
+///       int _health;
+///       int get health => _health;
+///       ...
+///     }
+///     ...
+///       // This will work even if the code has been tree-shaken/minified:
+///       final monster = new Monster();
+///       new PathObserver(monster, 'health').changes.listen(...);
+class Reflectable {
+  const Reflectable();
+}
diff --git a/observe/lib/src/observable.dart b/observe/lib/src/observable.dart
new file mode 100644
index 0000000..eee62f2
--- /dev/null
+++ b/observe/lib/src/observable.dart
@@ -0,0 +1,169 @@
+// Copyright (c) 2013, 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 observe.src.observable;
+
+import 'dart:async';
+import 'dart:collection';
+
+import 'package:smoke/smoke.dart' as smoke;
+import 'package:observe/observe.dart';
+
+// Note: this is an internal library so we can import it from tests.
+// TODO(jmesserly): ideally we could import this with a prefix, but it caused
+// strange problems on the VM when I tested out the dirty-checking example
+// above.
+import 'dirty_check.dart';
+
+/// Represents an object with observable properties. This is used by data in
+/// model-view architectures to notify interested parties of [changes] to the
+/// object's properties (fields or getter/setter pairs).
+///
+/// The interface does not require any specific technique to implement
+/// observability. You can implement it in the following ways:
+///
+/// - extend or mixin this class, and let the application call [dirtyCheck]
+///   periodically to check for changes to your object.
+/// - extend or mixin [ChangeNotifier], and implement change notifications
+///   manually by calling [notifyPropertyChange] from your setters.
+/// - implement this interface and provide your own implementation.
+abstract class Observable {
+  /// Performs dirty checking of objects that inherit from [Observable].
+  /// This scans all observed objects using mirrors and determines if any fields
+  /// have changed. If they have, it delivers the changes for the object.
+  static void dirtyCheck() => dirtyCheckObservables();
+
+  StreamController _changes;
+
+  Map<Symbol, Object> _values;
+  List<ChangeRecord> _records;
+
+  /// The stream of change records to this object. Records will be delivered
+  /// asynchronously.
+  ///
+  /// [deliverChanges] can be called to force synchronous delivery.
+  Stream<List<ChangeRecord>> get changes {
+    if (_changes == null) {
+      _changes = new StreamController.broadcast(sync: true,
+          onListen: _observed, onCancel: _unobserved);
+    }
+    return _changes.stream;
+  }
+
+  /// True if this object has any observers, and should call
+  /// [notifyChange] for changes.
+  bool get hasObservers => _changes != null && _changes.hasListener;
+
+  void _observed() {
+    // Register this object for dirty checking purposes.
+    registerObservable(this);
+
+    var values = new Map<Symbol, Object>();
+
+    // Note: we scan for @observable regardless of whether the base type
+    // actually includes this mixin. While perhaps too inclusive, it lets us
+    // avoid complex logic that walks "with" and "implements" clauses.
+    var queryOptions = new smoke.QueryOptions(includeInherited: true,
+        includeProperties: false, withAnnotations: const [ObservableProperty]);
+    for (var decl in smoke.query(this.runtimeType, queryOptions)) {
+      var name = decl.name;
+      // Note: since this is a field, getting the value shouldn't execute
+      // user code, so we don't need to worry about errors.
+      values[name] = smoke.read(this, name);
+    }
+
+    _values = values;
+  }
+
+  /// Release data associated with observation.
+  void _unobserved() {
+    // Note: we don't need to explicitly unregister from the dirty check list.
+    // This will happen automatically at the next call to dirtyCheck.
+    if (_values != null) {
+      _values = null;
+    }
+  }
+
+  /// Synchronously deliver pending [changes]. Returns true if any records were
+  /// delivered, otherwise false.
+  // TODO(jmesserly): this is a bit different from the ES Harmony version, which
+  // allows delivery of changes to a particular observer:
+  // http://wiki.ecmascript.org/doku.php?id=harmony:observe#object.deliverchangerecords
+  //
+  // The rationale for that, and for async delivery in general, is the principal
+  // that you shouldn't run code (observers) when it doesn't expect to be run.
+  // If you do that, you risk violating invariants that the code assumes.
+  //
+  // For this reason, we need to match the ES Harmony version. The way we can do
+  // this in Dart is to add a method on StreamSubscription (possibly by
+  // subclassing Stream* types) that immediately delivers records for only
+  // that subscription. Alternatively, we could consider using something other
+  // than Stream to deliver the multicast change records, and provide an
+  // Observable->Stream adapter.
+  //
+  // Also: we should be delivering changes to the observer (subscription) based
+  // on the birth order of the observer. This is for compatibility with ES
+  // Harmony as well as predictability for app developers.
+  bool deliverChanges() {
+    if (_values == null || !hasObservers) return false;
+
+    // Start with manually notified records (computed properties, etc),
+    // then scan all fields for additional changes.
+    List records = _records;
+    _records = null;
+
+    _values.forEach((name, oldValue) {
+      var newValue = smoke.read(this, name);
+      if (oldValue != newValue) {
+        if (records == null) records = [];
+        records.add(new PropertyChangeRecord(this, name, oldValue, newValue));
+        _values[name] = newValue;
+      }
+    });
+
+    if (records == null) return false;
+
+    _changes.add(new UnmodifiableListView<ChangeRecord>(records));
+    return true;
+  }
+
+  /// Notify that the field [name] of this object has been changed.
+  ///
+  /// The [oldValue] and [newValue] are also recorded. If the two values are
+  /// equal, no change will be recorded.
+  ///
+  /// For convenience this returns [newValue].
+  notifyPropertyChange(Symbol field, Object oldValue, Object newValue)
+      => notifyPropertyChangeHelper(this, field, oldValue, newValue);
+
+  /// Notify observers of a change.
+  ///
+  /// For most objects [Observable.notifyPropertyChange] is more convenient, but
+  /// collections sometimes deliver other types of changes such as a
+  /// [ListChangeRecord].
+  ///
+  /// Notes:
+  /// - This is *not* required for fields if you mixin or extend [Observable],
+  ///   but you can use it for computed properties.
+  /// - Unlike [ChangeNotifier] this will not schedule [deliverChanges]; use
+  ///   [Observable.dirtyCheck] instead.
+  void notifyChange(ChangeRecord record) {
+    if (!hasObservers) return;
+
+    if (_records == null) _records = [];
+    _records.add(record);
+  }
+}
+
+// TODO(jmesserly): remove the instance method and make this top-level method
+// public instead?
+// NOTE: this is not exported publically.
+notifyPropertyChangeHelper(Observable obj, Symbol field, Object oldValue,
+    Object newValue) {
+
+  if (obj.hasObservers && oldValue != newValue) {
+    obj.notifyChange(new PropertyChangeRecord(obj, field, oldValue, newValue));
+  }
+  return newValue;
+}
diff --git a/observe/lib/src/observable_box.dart b/observe/lib/src/observable_box.dart
new file mode 100644
index 0000000..dfaf7ff
--- /dev/null
+++ b/observe/lib/src/observable_box.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2013, 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 observe.src.observable_box;
+
+import 'package:observe/observe.dart';
+
+// TODO(jmesserly): should the property name be configurable?
+// That would be more convenient.
+/// An observable box that holds a value. Use this if you want to store a single
+/// value. For other cases, it is better to use [ObservableList],
+/// [ObservableMap], or a custom [Observable] implementation based on
+/// [Observable]. The property name for changes is "value".
+class ObservableBox<T> extends ChangeNotifier {
+  T _value;
+
+  ObservableBox([T initialValue]) : _value = initialValue;
+
+  @reflectable T get value => _value;
+
+  @reflectable void set value(T newValue) {
+    _value = notifyPropertyChange(#value, _value, newValue);
+  }
+
+  String toString() => '#<$runtimeType value: $value>';
+}
diff --git a/observe/lib/src/observable_list.dart b/observe/lib/src/observable_list.dart
new file mode 100644
index 0000000..912c4fc
--- /dev/null
+++ b/observe/lib/src/observable_list.dart
@@ -0,0 +1,304 @@
+// Copyright (c) 2013, 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 observe.src.observable_list;
+
+import 'dart:async';
+import 'dart:collection' show ListBase, UnmodifiableListView;
+import 'package:observe/observe.dart';
+import 'list_diff.dart' show projectListSplices, calcSplices;
+
+/// Represents an observable list of model values. If any items are added,
+/// removed, or replaced, then observers that are listening to [changes]
+/// will be notified.
+class ObservableList<E> extends ListBase<E> with ChangeNotifier {
+  List<ListChangeRecord> _listRecords;
+
+  StreamController _listChanges;
+
+  /// The inner [List<E>] with the actual storage.
+  final List<E> _list;
+
+  /// Creates an observable list of the given [length].
+  ///
+  /// If no [length] argument is supplied an extendable list of
+  /// length 0 is created.
+  ///
+  /// If a [length] argument is supplied, a fixed size list of that
+  /// length is created.
+  ObservableList([int length])
+      : _list = length != null ? new List<E>(length) : <E>[];
+
+  /// Creates an observable list with the elements of [other]. The order in
+  /// the list will be the order provided by the iterator of [other].
+  factory ObservableList.from(Iterable<E> other) =>
+      new ObservableList<E>()..addAll(other);
+
+  /// The stream of summarized list changes, delivered asynchronously.
+  ///
+  /// Each list change record contains information about an individual mutation.
+  /// The records are projected so they can be applied sequentially. For
+  /// example, this set of mutations:
+  ///
+  ///     var model = new ObservableList.from(['a', 'b']);
+  ///     model.listChanges.listen((records) => records.forEach(print));
+  ///     model.removeAt(1);
+  ///     model.insertAll(0, ['c', 'd', 'e']);
+  ///     model.removeRange(1, 3);
+  ///     model.insert(1, 'f');
+  ///
+  /// The change records will be summarized so they can be "played back", using
+  /// the final list positions to figure out which item was added:
+  ///
+  ///     #<ListChangeRecord index: 0, removed: [], addedCount: 2>
+  ///     #<ListChangeRecord index: 3, removed: [b], addedCount: 0>
+  ///
+  /// [deliverChanges] can be called to force synchronous delivery.
+  Stream<List<ListChangeRecord>> get listChanges {
+    if (_listChanges == null) {
+      // TODO(jmesserly): split observed/unobserved notions?
+      _listChanges = new StreamController.broadcast(sync: true,
+          onCancel: () { _listChanges = null; });
+    }
+    return _listChanges.stream;
+  }
+
+  bool get hasListObservers =>
+      _listChanges != null && _listChanges.hasListener;
+
+  @reflectable int get length => _list.length;
+
+  @reflectable set length(int value) {
+    int len = _list.length;
+    if (len == value) return;
+
+    // Produce notifications if needed
+    _notifyChangeLength(len, value);
+    if (hasListObservers) {
+      if (value < len) {
+        _recordChange(new ListChangeRecord(this, value,
+            removed: _list.getRange(value, len).toList()));
+      } else {
+        _recordChange(new ListChangeRecord(this, len, addedCount: value - len));
+      }
+    }
+
+    _list.length = value;
+  }
+
+  @reflectable E operator [](int index) => _list[index];
+
+  @reflectable void operator []=(int index, E value) {
+    var oldValue = _list[index];
+    if (hasListObservers) {
+      _recordChange(new ListChangeRecord(this, index, addedCount: 1,
+          removed: [oldValue]));
+    }
+    _list[index] = value;
+  }
+
+  // Forwarders so we can reflect on the properties.
+  @reflectable bool get isEmpty => super.isEmpty;
+  @reflectable bool get isNotEmpty => super.isNotEmpty;
+
+  // TODO(jmesserly): should we support first/last/single? They're kind of
+  // dangerous to use in a path because they throw exceptions. Also we'd need
+  // to produce property change notifications which seems to conflict with our
+  // existing list notifications.
+
+  // The following methods are here so that we can provide nice change events.
+
+  void setAll(int index, Iterable<E> iterable) {
+    if (iterable is! List && iterable is! Set) {
+      iterable = iterable.toList();
+    }
+    var len = iterable.length;
+    if (hasListObservers && len > 0) {
+      _recordChange(new ListChangeRecord(this, index, addedCount: len,
+          removed: _list.getRange(index, len).toList()));
+    }
+    _list.setAll(index, iterable);
+  }
+
+  void add(E value) {
+    int len = _list.length;
+    _notifyChangeLength(len, len + 1);
+    if (hasListObservers) {
+      _recordChange(new ListChangeRecord(this, len, addedCount: 1));
+    }
+
+    _list.add(value);
+  }
+
+  void addAll(Iterable<E> iterable) {
+    int len = _list.length;
+    _list.addAll(iterable);
+
+    _notifyChangeLength(len, _list.length);
+
+    int added = _list.length - len;
+    if (hasListObservers && added > 0) {
+      _recordChange(new ListChangeRecord(this, len, addedCount: added));
+    }
+  }
+
+  bool remove(Object element) {
+    for (int i = 0; i < this.length; i++) {
+      if (this[i] == element) {
+        removeRange(i, i + 1);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  void removeRange(int start, int end) {
+    _rangeCheck(start, end);
+    int rangeLength = end - start;
+    int len = _list.length;
+
+    _notifyChangeLength(len, len - rangeLength);
+    if (hasListObservers && rangeLength > 0) {
+      _recordChange(new ListChangeRecord(this, start,
+          removed: _list.getRange(start, end).toList()));
+    }
+
+    _list.removeRange(start, end);
+  }
+
+  void insertAll(int index, Iterable<E> iterable) {
+    if (index < 0 || index > length) {
+      throw new RangeError.range(index, 0, length);
+    }
+    // TODO(floitsch): we can probably detect more cases.
+    if (iterable is! List && iterable is! Set) {
+      iterable = iterable.toList();
+    }
+    int insertionLength = iterable.length;
+    // There might be errors after the length change, in which case the list
+    // will end up being modified but the operation not complete. Unless we
+    // always go through a "toList" we can't really avoid that.
+    int len = _list.length;
+    _list.length += insertionLength;
+
+    _list.setRange(index + insertionLength, this.length, this, index);
+    _list.setAll(index, iterable);
+
+    _notifyChangeLength(len, _list.length);
+
+    if (hasListObservers && insertionLength > 0) {
+      _recordChange(new ListChangeRecord(this, index,
+          addedCount: insertionLength));
+    }
+  }
+
+  void insert(int index, E element) {
+    if (index < 0 || index > length) {
+      throw new RangeError.range(index, 0, length);
+    }
+    if (index == length) {
+      add(element);
+      return;
+    }
+    // We are modifying the length just below the is-check. Without the check
+    // Array.copy could throw an exception, leaving the list in a bad state
+    // (with a length that has been increased, but without a new element).
+    if (index is! int) throw new ArgumentError(index);
+    _list.length++;
+    _list.setRange(index + 1, length, this, index);
+
+    _notifyChangeLength(_list.length - 1, _list.length);
+    if (hasListObservers) {
+      _recordChange(new ListChangeRecord(this, index, addedCount: 1));
+    }
+    _list[index] = element;
+  }
+
+
+  E removeAt(int index) {
+    E result = this[index];
+    removeRange(index, index + 1);
+    return result;
+  }
+
+  void _rangeCheck(int start, int end) {
+    if (start < 0 || start > this.length) {
+      throw new RangeError.range(start, 0, this.length);
+    }
+    if (end < start || end > this.length) {
+      throw new RangeError.range(end, start, this.length);
+    }
+  }
+
+  void _recordChange(ListChangeRecord record) {
+    if (!hasListObservers) return;
+
+    if (_listRecords == null) {
+      _listRecords = [];
+      scheduleMicrotask(deliverListChanges);
+    }
+    _listRecords.add(record);
+  }
+
+  void _notifyChangeLength(int oldValue, int newValue) {
+    notifyPropertyChange(#length, oldValue, newValue);
+    notifyPropertyChange(#isEmpty, oldValue == 0, newValue == 0);
+    notifyPropertyChange(#isNotEmpty, oldValue != 0, newValue != 0);
+  }
+
+  /// Deprecated. Name had a typo, use [discardListChanges] instead.
+  @deprecated
+  void discardListChages() => discardListChanges();
+
+  void discardListChanges() {
+    // Leave _listRecords set so we don't schedule another delivery.
+    if (_listRecords != null) _listRecords = [];
+  }
+
+  bool deliverListChanges() {
+    if (_listRecords == null) return false;
+    var records = projectListSplices(this, _listRecords);
+    _listRecords = null;
+
+    if (hasListObservers && !records.isEmpty) {
+      _listChanges.add(new UnmodifiableListView<ListChangeRecord>(records));
+      return true;
+    }
+    return false;
+  }
+
+  /// Calculates the changes to the list, if lacking individual splice mutation
+  /// information.
+  ///
+  /// This is not needed for change records produced by [ObservableList] itself,
+  /// but it can be used if the list instance was replaced by another list.
+  ///
+  /// The minimal set of splices can be synthesized given the previous state and
+  /// final state of a list. The basic approach is to calculate the edit
+  /// distance matrix and choose the shortest path through it.
+  ///
+  /// Complexity is `O(l * p)` where `l` is the length of the current list and
+  /// `p` is the length of the old list.
+  static List<ListChangeRecord> calculateChangeRecords(
+      List<Object> oldValue, List<Object> newValue) =>
+      calcSplices(newValue, 0, newValue.length, oldValue, 0, oldValue.length);
+
+  /// Updates the [previous] list using the change [records]. For added items,
+  /// the [current] list is used to find the current value.
+  static void applyChangeRecords(List<Object> previous, List<Object> current,
+      List<ListChangeRecord> changeRecords) {
+
+    if (identical(previous, current)) {
+      throw new ArgumentError("can't use same list for previous and current");
+    }
+
+    for (var change in changeRecords) {
+      int addEnd = change.index + change.addedCount;
+      int removeEnd = change.index + change.removed.length;
+
+      var addedItems = current.getRange(change.index, addEnd);
+      previous.replaceRange(change.index, removeEnd, addedItems);
+    }
+  }
+}
diff --git a/observe/lib/src/observable_map.dart b/observe/lib/src/observable_map.dart
new file mode 100644
index 0000000..b09c468
--- /dev/null
+++ b/observe/lib/src/observable_map.dart
@@ -0,0 +1,180 @@
+// Copyright (c) 2013, 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 observe.src.observable_map;
+
+import 'dart:collection';
+import 'package:observe/observe.dart';
+
+
+// TODO(jmesserly): this needs to be faster. We currently require multiple
+// lookups per key to get the old value.
+// TODO(jmesserly): this doesn't implement the precise interfaces like
+// LinkedHashMap, SplayTreeMap or HashMap. However it can use them for the
+// backing store.
+
+// TODO(jmesserly): should we summarize map changes like we do for list changes?
+class MapChangeRecord<K, V> extends ChangeRecord {
+  // TODO(jmesserly): we could store this more compactly if it matters, with
+  // subtypes for inserted and removed.
+
+  /// The map key that changed.
+  final K key;
+
+  /// The previous value associated with this key.
+  final V oldValue;
+
+  /// The new value associated with this key.
+  final V newValue;
+
+  /// True if this key was inserted.
+  final bool isInsert;
+
+  /// True if this key was removed.
+  final bool isRemove;
+
+  MapChangeRecord(this.key, this.oldValue, this.newValue)
+      : isInsert = false, isRemove = false;
+
+  MapChangeRecord.insert(this.key, this.newValue)
+      : isInsert = true, isRemove = false, oldValue = null;
+
+  MapChangeRecord.remove(this.key, this.oldValue)
+      : isInsert = false, isRemove = true, newValue = null;
+
+  String toString() {
+    var kind = isInsert ? 'insert' : isRemove ? 'remove' : 'set';
+    return '#<MapChangeRecord $kind $key from: $oldValue to: $newValue>';
+  }
+}
+
+/// Represents an observable map of model values. If any items are added,
+/// removed, or replaced, then observers that are listening to [changes]
+/// will be notified.
+class ObservableMap<K, V> extends ChangeNotifier implements Map<K, V> {
+  final Map<K, V> _map;
+
+  /// Creates an observable map.
+  ObservableMap() : _map = new HashMap<K, V>();
+
+  /// Creates a new observable map using a [LinkedHashMap].
+  ObservableMap.linked() : _map = new LinkedHashMap<K, V>();
+
+  /// Creates a new observable map using a [SplayTreeMap].
+  ObservableMap.sorted() : _map = new SplayTreeMap<K, V>();
+
+  /// Creates an observable map that contains all key value pairs of [other].
+  /// It will attempt to use the same backing map type if the other map is a
+  /// [LinkedHashMap], [SplayTreeMap], or [HashMap]. Otherwise it defaults to
+  /// [HashMap].
+  ///
+  /// Note this will perform a shallow conversion. If you want a deep conversion
+  /// you should use [toObservable].
+  factory ObservableMap.from(Map<K, V> other) {
+    return new ObservableMap<K, V>.createFromType(other)..addAll(other);
+  }
+
+  /// Like [ObservableMap.from], but creates an empty map.
+  factory ObservableMap.createFromType(Map<K, V> other) {
+    ObservableMap result;
+    if (other is SplayTreeMap) {
+      result = new ObservableMap<K, V>.sorted();
+    } else if (other is LinkedHashMap) {
+      result = new ObservableMap<K, V>.linked();
+    } else {
+      result = new ObservableMap<K, V>();
+    }
+    return result;
+  }
+
+  @reflectable Iterable<K> get keys => _map.keys;
+
+  @reflectable Iterable<V> get values => _map.values;
+
+  @reflectable int get length =>_map.length;
+
+  @reflectable bool get isEmpty => length == 0;
+
+  @reflectable bool get isNotEmpty => !isEmpty;
+
+  @reflectable bool containsValue(Object value) => _map.containsValue(value);
+
+  @reflectable bool containsKey(Object key) => _map.containsKey(key);
+
+  @reflectable V operator [](Object key) => _map[key];
+
+  @reflectable void operator []=(K key, V value) {
+    if (!hasObservers) {
+      _map[key] = value;
+      return;
+    }
+
+    int len = _map.length;
+    V oldValue = _map[key];
+
+    _map[key] = value;
+
+    if (len != _map.length) {
+      notifyPropertyChange(#length, len, _map.length);
+      notifyChange(new MapChangeRecord.insert(key, value));
+      _notifyKeysValuesChanged();
+    } else if (oldValue != value) {
+      notifyChange(new MapChangeRecord(key, oldValue, value));
+      _notifyValuesChanged();
+    }
+  }
+
+  void addAll(Map<K, V> other) {
+    other.forEach((K key, V value) { this[key] = value; });
+  }
+
+  V putIfAbsent(K key, V ifAbsent()) {
+    int len = _map.length;
+    V result = _map.putIfAbsent(key, ifAbsent);
+    if (hasObservers && len != _map.length) {
+      notifyPropertyChange(#length, len, _map.length);
+      notifyChange(new MapChangeRecord.insert(key, result));
+      _notifyKeysValuesChanged();
+    }
+    return result;
+  }
+
+  V remove(Object key) {
+    int len = _map.length;
+    V result =  _map.remove(key);
+    if (hasObservers && len != _map.length) {
+      notifyChange(new MapChangeRecord.remove(key, result));
+      notifyPropertyChange(#length, len, _map.length);
+      _notifyKeysValuesChanged();
+    }
+    return result;
+  }
+
+  void clear() {
+    int len = _map.length;
+    if (hasObservers && len > 0) {
+      _map.forEach((key, value) {
+        notifyChange(new MapChangeRecord.remove(key, value));
+      });
+      notifyPropertyChange(#length, len, 0);
+      _notifyKeysValuesChanged();
+    }
+    _map.clear();
+  }
+
+  void forEach(void f(K key, V value)) => _map.forEach(f);
+
+  String toString() => Maps.mapToString(this);
+
+  // Note: we don't really have a reasonable old/new value to use here.
+  // But this should fix "keys" and "values" in templates with minimal overhead.
+  void _notifyKeysValuesChanged() {
+    notifyChange(new PropertyChangeRecord(this, #keys, null, null));
+    _notifyValuesChanged();
+  }
+
+  void _notifyValuesChanged() {
+    notifyChange(new PropertyChangeRecord(this, #values, null, null));
+  }
+}
diff --git a/observe/lib/src/observer_transform.dart b/observe/lib/src/observer_transform.dart
new file mode 100644
index 0000000..7fab555
--- /dev/null
+++ b/observe/lib/src/observer_transform.dart
@@ -0,0 +1,85 @@
+// Copyright (c) 2013, 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 observe.src.observer_transform;
+
+import 'package:observe/observe.dart';
+
+/// ObserverTransform is used to dynamically transform observed value(s).
+///
+///    var obj = new ObservableBox(10);
+///    var observer = new PathObserver(obj, 'value');
+///    var transform = new ObserverTransform(observer,
+///        (x) => x * 2, setValue: (x) => x ~/ 2);
+///
+///    // Open returns the current value of 20.
+///    transform.open((newValue) => print('new: $newValue'));
+///
+///    obj.value = 20; // prints 'new: 40' async
+///    new Future(() {
+///      transform.value = 4; // obj.value will be 2
+///    });
+///
+/// ObserverTransform can also be used to reduce a set of observed values to a
+/// single value:
+///
+///    var obj = new ObservableMap.from({'a': 1, 'b': 2, 'c': 3});
+///    var observer = new CompoundObserver()
+///      ..addPath(obj, 'a')
+///      ..addPath(obj, 'b')
+///      ..addPath(obj, 'c');
+///
+///    var transform = new ObserverTransform(observer,
+///        (values) => values.fold(0, (x, y) => x + y));
+///
+///    // Open returns the current value of 6.
+///    transform.open((newValue) => print('new: $newValue'));
+///
+///    obj['a'] = 2;
+///    obj['c'] = 10; // will print 'new 14' asynchronously
+///
+class ObserverTransform extends Bindable {
+  Bindable _bindable;
+  Function _getTransformer, _setTransformer;
+  Function _notifyCallback;
+  var _value;
+
+  ObserverTransform(Bindable bindable, computeValue(value), {setValue(value)})
+      : _bindable = bindable,
+        _getTransformer = computeValue,
+        _setTransformer = setValue;
+
+  open(callback) {
+    _notifyCallback = callback;
+    _value = _getTransformer(_bindable.open(_observedCallback));
+    return _value;
+  }
+
+  _observedCallback(newValue) {
+    final value = _getTransformer(newValue);
+    if (value == _value) return null;
+    _value = value;
+    return _notifyCallback(value);
+  }
+
+  void close() {
+    if (_bindable != null) _bindable.close();
+    _bindable = null;
+    _getTransformer = null;
+    _setTransformer = null;
+    _notifyCallback = null;
+    _value = null;
+  }
+
+  get value => _value = _getTransformer(_bindable.value);
+
+  set value(newValue) {
+    if (_setTransformer != null) {
+      newValue = _setTransformer(newValue);
+    }
+    _bindable.value = newValue;
+  }
+
+  deliver() => _bindable.deliver();
+}
diff --git a/observe/lib/src/path_observer.dart b/observe/lib/src/path_observer.dart
new file mode 100644
index 0000000..76eb9ef
--- /dev/null
+++ b/observe/lib/src/path_observer.dart
@@ -0,0 +1,929 @@
+// Copyright (c) 2013, 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 observe.src.path_observer;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:math' show min;
+
+import 'package:logging/logging.dart' show Logger, Level;
+import 'package:observe/observe.dart';
+import 'package:smoke/smoke.dart' as smoke;
+
+import 'package:utf/utf.dart' show stringToCodepoints;
+
+/// A data-bound path starting from a view-model or model object, for example
+/// `foo.bar.baz`.
+///
+/// When [open] is called, this will observe changes to the object and any
+/// intermediate object along the path, and send updated values accordingly.
+/// When [close] is called it will stop observing the objects.
+///
+/// This class is used to implement `Node.bind` and similar functionality in
+/// the [template_binding](pub.dartlang.org/packages/template_binding) package.
+class PathObserver extends _Observer implements Bindable {
+  PropertyPath _path;
+  Object _object;
+  _ObservedSet _directObserver;
+
+  /// Observes [path] on [object] for changes. This returns an object
+  /// that can be used to get the changes and get/set the value at this path.
+  ///
+  /// The path can be a [PropertyPath], or a [String] used to construct it.
+  ///
+  /// See [open] and [value].
+  PathObserver(Object object, [path])
+      : _object = object,
+        _path = new PropertyPath(path);
+
+  PropertyPath get path => _path;
+
+  /// Sets the value at this path.
+  void set value(newValue) {
+    if (_path != null) _path.setValueFrom(_object, newValue);
+  }
+
+  int get _reportArgumentCount => 2;
+
+  /// Initiates observation and returns the initial value.
+  /// The callback will be passed the updated [value], and may optionally be
+  /// declared to take a second argument, which will contain the previous value.
+  open(callback) => super.open(callback);
+
+  void _connect() {
+    _directObserver = new _ObservedSet(this, _object);
+    _check(skipChanges: true);
+  }
+
+  void _disconnect() {
+    _value = null;
+    if (_directObserver != null) {
+      _directObserver.close(this);
+      _directObserver = null;
+    }
+    // Dart note: the JS impl does not do this, but it seems consistent with
+    // CompoundObserver. After closing the PathObserver can't be reopened.
+    _path = null;
+    _object = null;
+  }
+
+  void _iterateObjects(void observe(obj, prop)) {
+    _path._iterateObjects(_object, observe);
+  }
+
+  bool _check({bool skipChanges: false}) {
+    var oldValue = _value;
+    _value = _path.getValueFrom(_object);
+    if (skipChanges || _value == oldValue) return false;
+
+    _report(_value, oldValue, this);
+    return true;
+  }
+}
+
+/// A dot-delimieted property path such as "foo.bar" or "foo.10.bar".
+///
+/// The path specifies how to get a particular value from an object graph, where
+/// the graph can include arrays and maps. Each segment of the path describes
+/// how to take a single step in the object graph. Properties like 'foo' or
+/// 'bar' are read as properties on objects, or as keys if the object is a [Map]
+/// or a [Indexable], while integer values are read as indexes in a [List].
+// TODO(jmesserly): consider specialized subclasses for:
+// * empty path
+// * "value"
+// * single token in path, e.g. "foo"
+class PropertyPath {
+  /// The segments of the path.
+  final List<Object> _segments;
+
+  /// Creates a new [PropertyPath]. These can be stored to avoid excessive
+  /// parsing of path strings.
+  ///
+  /// The provided [path] should be a String or a List. If it is a list it
+  /// should contain only Symbols and integers. This can be used to avoid
+  /// parsing.
+  ///
+  /// Note that this constructor will canonicalize identical paths in some cases
+  /// to save memory, but this is not guaranteed. Use [==] for comparions
+  /// purposes instead of [identical].
+  // Dart note: this is ported from `function getPath`.
+  factory PropertyPath([path]) {
+    if (path is PropertyPath) return path;
+    if (path == null || (path is List && path.isEmpty)) path = '';
+
+    if (path is List) {
+      var copy = new List.from(path, growable: false);
+      for (var segment in copy) {
+        // Dart note: unlike Javascript, we don't support arbitraty objects that
+        // can be converted to a String.
+        // TODO(sigmund): consider whether we should support that here. It might
+        // be easier to add support for that if we switch first to use strings
+        // for everything instead of symbols.
+        if (segment is! int && segment is! String && segment is! Symbol) {
+          throw new ArgumentError(
+              'List must contain only ints, Strings, and Symbols');
+        }
+      }
+      return new PropertyPath._(copy);
+    }
+
+    var pathObj = _pathCache[path];
+    if (pathObj != null) return pathObj;
+
+
+    final segments = new _PathParser().parse(path);
+    if (segments == null) return _InvalidPropertyPath._instance;
+
+    // TODO(jmesserly): we could use an UnmodifiableListView here, but that adds
+    // memory overhead.
+    pathObj = new PropertyPath._(segments.toList(growable: false));
+    if (_pathCache.length >= _pathCacheLimit) {
+      _pathCache.remove(_pathCache.keys.first);
+    }
+    _pathCache[path] = pathObj;
+    return pathObj;
+  }
+
+  PropertyPath._(this._segments);
+
+  int get length => _segments.length;
+  bool get isEmpty => _segments.isEmpty;
+  bool get isValid => true;
+
+  String toString() {
+    if (!isValid) return '<invalid path>';
+    var sb = new StringBuffer();
+    bool first = true;
+    for (var key in _segments) {
+      if (key is Symbol) {
+        if (!first) sb.write('.');
+        sb.write(smoke.symbolToName(key));
+      } else {
+        _formatAccessor(sb, key);
+      }
+      first = false;
+    }
+    return sb.toString();
+  }
+
+  _formatAccessor(StringBuffer sb, Object key) {
+    if (key is int) {
+      sb.write('[$key]');
+    } else {
+      sb.write('["${key.toString().replaceAll('"', '\\"')}"]');
+    }
+  }
+
+  bool operator ==(other) {
+    if (identical(this, other)) return true;
+    if (other is! PropertyPath) return false;
+    if (isValid != other.isValid) return false;
+
+    int len = _segments.length;
+    if (len != other._segments.length) return false;
+    for (int i = 0; i < len; i++) {
+      if (_segments[i] != other._segments[i]) return false;
+    }
+    return true;
+  }
+
+  /// This is the [Jenkins hash function][1] but using masking to keep
+  /// values in SMI range.
+  /// [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function
+  // TODO(jmesserly): should reuse this instead, see
+  // https://code.google.com/p/dart/issues/detail?id=11617
+  int get hashCode {
+    int hash = 0;
+    for (int i = 0, len = _segments.length; i < len; i++) {
+      hash = 0x1fffffff & (hash + _segments[i].hashCode);
+      hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
+      hash = hash ^ (hash >> 6);
+    }
+    hash = 0x1fffffff & (hash + ((0x03ffffff & hash) <<  3));
+    hash = hash ^ (hash >> 11);
+    return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
+  }
+
+  /// Returns the current value of the path from the provided [obj]ect.
+  getValueFrom(Object obj) {
+    if (!isValid) return null;
+    for (var segment in _segments) {
+      if (obj == null) return null;
+      obj = _getObjectProperty(obj, segment);
+    }
+    return obj;
+  }
+
+  /// Attempts to set the [value] of the path from the provided [obj]ect.
+  /// Returns true if and only if the path was reachable and set.
+  bool setValueFrom(Object obj, Object value) {
+    var end = _segments.length - 1;
+    if (end < 0) return false;
+    for (int i = 0; i < end; i++) {
+      if (obj == null) return false;
+      obj = _getObjectProperty(obj, _segments[i]);
+    }
+    return _setObjectProperty(obj, _segments[end], value);
+  }
+
+  void _iterateObjects(Object obj, void observe(obj, prop)) {
+    if (!isValid || isEmpty) return;
+
+    int i = 0, last = _segments.length - 1;
+    while (obj != null) {
+      // _segments[i] is passed to indicate that we are only observing that
+      // property of obj. See observe declaration in _ObservedSet.
+      observe(obj, _segments[i]);
+
+      if (i >= last) break;
+      obj = _getObjectProperty(obj, _segments[i++]);
+    }
+  }
+
+  // Dart note: it doesn't make sense to have compiledGetValueFromFn in Dart.
+}
+
+
+/// Visible only for testing:
+getSegmentsOfPropertyPathForTesting(p) => p._segments;
+
+class _InvalidPropertyPath extends PropertyPath {
+  static final _instance = new _InvalidPropertyPath();
+
+  bool get isValid => false;
+  _InvalidPropertyPath() : super._([]);
+}
+
+bool _changeRecordMatches(record, key) {
+  if (record is PropertyChangeRecord) {
+    return (record as PropertyChangeRecord).name == key;
+  }
+  if (record is MapChangeRecord) {
+    if (key is Symbol) key = smoke.symbolToName(key);
+    return (record as MapChangeRecord).key == key;
+  }
+  return false;
+}
+
+/// Properties in [Map] that need to be read as properties and not as keys in
+/// the map. We exclude methods ('containsValue', 'containsKey', 'putIfAbsent',
+/// 'addAll', 'remove', 'clear', 'forEach') because there is no use in reading
+/// them as part of path-observer segments.
+const _MAP_PROPERTIES = const [#keys, #values, #length, #isEmpty, #isNotEmpty];
+
+_getObjectProperty(object, property) {
+  if (object == null) return null;
+
+  if (property is int) {
+    if (object is List && property >= 0 && property < object.length) {
+      return object[property];
+    }
+  } else if (property is String) {
+    return object[property];
+  } else if (property is Symbol) {
+    // Support indexer if available, e.g. Maps or polymer_expressions Scope.
+    // This is the default syntax used by polymer/nodebind and
+    // polymer/observe-js PathObserver.
+    // TODO(sigmund): should we also support using checking dynamically for
+    // whether the type practically implements the indexer API
+    // (smoke.hasInstanceMethod(type, const Symbol('[]')))?
+    if (object is Indexable || object is Map && !_MAP_PROPERTIES.contains(property)) {
+      return object[smoke.symbolToName(property)];
+    }
+    try {
+      return smoke.read(object, property);
+    } on NoSuchMethodError catch (e) {
+      // Rethrow, unless the type implements noSuchMethod, in which case we
+      // interpret the exception as a signal that the method was not found.
+      // Dart note: getting invalid properties is an error, unlike in JS where
+      // it returns undefined.
+      if (!smoke.hasNoSuchMethod(object.runtimeType)) rethrow;
+    }
+  }
+
+  if (_logger.isLoggable(Level.FINER)) {
+    _logger.finer("can't get $property in $object");
+  }
+  return null;
+}
+
+bool _setObjectProperty(object, property, value) {
+  if (object == null) return false;
+
+  if (property is int) {
+    if (object is List && property >= 0 && property < object.length) {
+      object[property] = value;
+      return true;
+    }
+  } else if (property is Symbol) {
+    // Support indexer if available, e.g. Maps or polymer_expressions Scope.
+    if (object is Indexable || object is Map && !_MAP_PROPERTIES.contains(property)) {
+      object[smoke.symbolToName(property)] = value;
+      return true;
+    }
+    try {
+      smoke.write(object, property, value);
+      return true;
+    } on NoSuchMethodError catch (e, s) {
+      if (!smoke.hasNoSuchMethod(object.runtimeType)) rethrow;
+    }
+  }
+
+  if (_logger.isLoggable(Level.FINER)) {
+    _logger.finer("can't set $property in $object");
+  }
+  return false;
+}
+
+// From: https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
+
+final _identRegExp = () {
+  const identStart = '[\$_a-zA-Z]';
+  const identPart = '[\$_a-zA-Z0-9]';
+  return new RegExp('^$identStart+$identPart*\$');
+}();
+
+_isIdent(s) => _identRegExp.hasMatch(s);
+
+// Dart note: refactored to convert to codepoints once and operate on codepoints
+// rather than characters.
+class _PathParser {
+  List keys = [];
+  int index = -1;
+  String key;
+
+  final Map<String, List<String>> _pathStateMachine = {
+    'beforePath': {
+      'ws': ['beforePath'],
+      'ident': ['inIdent', 'append'],
+      '[': ['beforeElement'],
+      'eof': ['afterPath']
+    },
+
+    'inPath': {
+      'ws': ['inPath'],
+      '.': ['beforeIdent'],
+      '[': ['beforeElement'],
+      'eof': ['afterPath']
+    },
+
+    'beforeIdent': {
+      'ws': ['beforeIdent'],
+      'ident': ['inIdent', 'append']
+    },
+
+    'inIdent': {
+      'ident': ['inIdent', 'append'],
+      '0': ['inIdent', 'append'],
+      'number': ['inIdent', 'append'],
+      'ws': ['inPath', 'push'],
+      '.': ['beforeIdent', 'push'],
+      '[': ['beforeElement', 'push'],
+      'eof': ['afterPath', 'push']
+    },
+
+    'beforeElement': {
+      'ws': ['beforeElement'],
+      '0': ['afterZero', 'append'],
+      'number': ['inIndex', 'append'],
+      "'": ['inSingleQuote', 'append', ''],
+      '"': ['inDoubleQuote', 'append', '']
+    },
+
+    'afterZero': {
+      'ws': ['afterElement', 'push'],
+      ']': ['inPath', 'push']
+    },
+
+    'inIndex': {
+      '0': ['inIndex', 'append'],
+      'number': ['inIndex', 'append'],
+      'ws': ['afterElement'],
+      ']': ['inPath', 'push']
+    },
+
+    'inSingleQuote': {
+      "'": ['afterElement'],
+      'eof': ['error'],
+      'else': ['inSingleQuote', 'append']
+    },
+
+    'inDoubleQuote': {
+      '"': ['afterElement'],
+      'eof': ['error'],
+      'else': ['inDoubleQuote', 'append']
+    },
+
+    'afterElement': {
+      'ws': ['afterElement'],
+      ']': ['inPath', 'push']
+    }
+  };
+
+  /// From getPathCharType: determines the type of a given [code]point.
+  String _getPathCharType(code) {
+    if (code == null) return 'eof';
+    switch(code) {
+      case 0x5B: // [
+      case 0x5D: // ]
+      case 0x2E: // .
+      case 0x22: // "
+      case 0x27: // '
+      case 0x30: // 0
+        return _char(code);
+
+      case 0x5F: // _
+      case 0x24: // $
+        return 'ident';
+
+      case 0x20: // Space
+      case 0x09: // Tab
+      case 0x0A: // Newline
+      case 0x0D: // Return
+      case 0xA0:  // No-break space
+      case 0xFEFF:  // Byte Order Mark
+      case 0x2028:  // Line Separator
+      case 0x2029:  // Paragraph Separator
+        return 'ws';
+    }
+
+    // a-z, A-Z
+    if ((0x61 <= code && code <= 0x7A) || (0x41 <= code && code <= 0x5A))
+      return 'ident';
+
+    // 1-9
+    if (0x31 <= code && code <= 0x39)
+      return 'number';
+
+    return 'else';
+  }
+
+  static String _char(int codepoint) => new String.fromCharCodes([codepoint]);
+
+  void push() {
+    if (key == null) return;
+
+    // Dart note: we store the keys with different types, rather than
+    // parsing/converting things later in toString.
+    if (_isIdent(key)) {
+      keys.add(smoke.nameToSymbol(key));
+    } else {
+      var index = int.parse(key, radix: 10, onError: (_) => null);
+      keys.add(index != null ? index : key);
+    }
+    key = null;
+  }
+
+  void append(newChar) {
+    key = (key == null) ? newChar : '$key$newChar';
+  }
+
+  bool _maybeUnescapeQuote(String mode, codePoints) {
+    if (index >= codePoints.length) return false;
+    var nextChar = _char(codePoints[index + 1]);
+    if ((mode == 'inSingleQuote' && nextChar == "'") ||
+        (mode == 'inDoubleQuote' && nextChar == '"')) {
+      index++;
+      append(nextChar);
+      return true;
+    }
+    return false;
+  }
+
+  /// Returns the parsed keys, or null if there was a parse error.
+  List<String> parse(String path) {
+    var codePoints = stringToCodepoints(path);
+    var mode = 'beforePath';
+
+    while (mode != null) {
+      index++;
+      var c = index >= codePoints.length ? null : codePoints[index];
+
+      if (c != null &&
+          _char(c) == '\\' && _maybeUnescapeQuote(mode, codePoints)) continue;
+
+      var type = _getPathCharType(c);
+      if (mode == 'error') return null;
+
+      var typeMap = _pathStateMachine[mode];
+      var transition = typeMap[type];
+      if (transition == null) transition = typeMap['else'];
+      if (transition == null) return null; // parse error;
+
+      mode = transition[0];
+      var actionName = transition.length > 1 ? transition[1] : null;
+      if (actionName == 'push' && key != null) push();
+      if (actionName == 'append') {
+        var newChar = transition.length > 2 && transition[2] != null
+            ? transition[2] : _char(c);
+        append(newChar);
+      }
+
+      if (mode == 'afterPath') return keys;
+    }
+    return null; // parse error
+  }
+}
+
+final Logger _logger = new Logger('observe.PathObserver');
+
+
+/// This is a simple cache. It's like LRU but we don't update an item on a
+/// cache hit, because that would require allocation. Better to let it expire
+/// and reallocate the PropertyPath.
+// TODO(jmesserly): this optimization is from observe-js, how valuable is it in
+// practice?
+final _pathCache = new LinkedHashMap<String, PropertyPath>();
+
+/// The size of a path like "foo.bar" is approximately 160 bytes, so this
+/// reserves ~16Kb of memory for recently used paths. Since paths are frequently
+/// reused, the theory is that this ends up being a good tradeoff in practice.
+// (Note: the 160 byte estimate is from Dart VM 1.0.0.10_r30798 on x64 without
+// using UnmodifiableListView in PropertyPath)
+const int _pathCacheLimit = 100;
+
+/// [CompoundObserver] is a [Bindable] object which knows how to listen to
+/// multiple values (registered via [addPath] or [addObserver]) and invoke a
+/// callback when one or more of the values have changed.
+///
+///    var obj = new ObservableMap.from({'a': 1, 'b': 2});
+///    var otherObj = new ObservableMap.from({'c': 3});
+///
+///    var observer = new CompoundObserver()
+///      ..addPath(obj, 'a');
+///      ..addObserver(new PathObserver(obj, 'b'));
+///      ..addPath(otherObj, 'c');
+///      ..open((values) {
+///        for (int i = 0; i < values.length; i++) {
+///          print('The value at index $i is now ${values[i]}');
+///        }
+///      });
+///
+///   obj['a'] = 10; // print will be triggered async
+///
+class CompoundObserver extends _Observer implements Bindable {
+  _ObservedSet _directObserver;
+  bool _reportChangesOnOpen;
+  List _observed = [];
+
+  CompoundObserver([this._reportChangesOnOpen = false]) {
+    _value = [];
+  }
+
+  int get _reportArgumentCount => 3;
+
+  /// Initiates observation and returns the initial value.
+  /// The callback will be passed the updated [value], and may optionally be
+  /// declared to take a second argument, which will contain the previous value.
+  ///
+  /// Implementation note: a third argument can also be declared, which will
+  /// receive a list of objects and paths, such that `list[2 * i]` will access
+  /// the object and `list[2 * i + 1]` will access the path, where `i` is the
+  /// order of the [addPath] call. This parameter is only used by
+  /// `package:polymer` as a performance optimization, and should not be relied
+  /// on in new code.
+  open(callback) => super.open(callback);
+
+  void _connect() {
+    for (var i = 0; i < _observed.length; i += 2) {
+      var object = _observed[i];
+      if (!identical(object, _observerSentinel)) {
+        _directObserver = new _ObservedSet(this, object);
+        break;
+      }
+    }
+
+    _check(skipChanges: !_reportChangesOnOpen);
+  }
+
+  void _disconnect() {
+    for (var i = 0; i < _observed.length; i += 2) {
+      if (identical(_observed[i], _observerSentinel)) {
+        _observed[i + 1].close();
+      }
+    }
+
+    _observed = null;
+    _value = null;
+
+    if (_directObserver != null) {
+      _directObserver.close(this);
+      _directObserver = null;
+    }
+  }
+
+  /// Adds a dependency on the property [path] accessed from [object].
+  /// [path] can be a [PropertyPath] or a [String]. If it is omitted an empty
+  /// path will be used.
+  void addPath(Object object, [path]) {
+    if (_isOpen || _isClosed) {
+      throw new StateError('Cannot add paths once started.');
+    }
+
+    path = new PropertyPath(path);
+    _observed..add(object)..add(path);
+    if (!_reportChangesOnOpen) return;
+    _value.add(path.getValueFrom(object));
+  }
+
+  void addObserver(Bindable observer) {
+    if (_isOpen || _isClosed) {
+      throw new StateError('Cannot add observers once started.');
+    }
+
+    _observed..add(_observerSentinel)..add(observer);
+    if (!_reportChangesOnOpen) return;
+    _value.add(observer.open((_) => deliver()));
+  }
+
+  void _iterateObjects(void observe(obj, prop)) {
+    for (var i = 0; i < _observed.length; i += 2) {
+      var object = _observed[i];
+      if (!identical(object, _observerSentinel)) {
+        (_observed[i + 1] as PropertyPath)._iterateObjects(object, observe);
+      }
+    }
+  }
+
+  bool _check({bool skipChanges: false}) {
+    bool changed = false;
+    _value.length = _observed.length ~/ 2;
+    var oldValues = null;
+    for (var i = 0; i < _observed.length; i += 2) {
+      var object = _observed[i];
+      var path = _observed[i + 1];
+      var value;
+      if (identical(object, _observerSentinel)) {
+        var observable = path as Bindable;
+        value = _state == _Observer._UNOPENED ?
+            observable.open((_) => this.deliver()) :
+            observable.value;
+      } else {
+        value = (path as PropertyPath).getValueFrom(object);
+      }
+
+      if (skipChanges) {
+        _value[i ~/ 2] = value;
+        continue;
+      }
+
+      if (value == _value[i ~/ 2]) continue;
+
+      // don't allocate this unless necessary.
+      if (_notifyArgumentCount >= 2) {
+        if (oldValues == null) oldValues = new Map();
+        oldValues[i ~/ 2] = _value[i ~/ 2];
+      }
+
+      changed = true;
+      _value[i ~/ 2] = value;
+    }
+
+    if (!changed) return false;
+
+    // TODO(rafaelw): Having _observed as the third callback arg here is
+    // pretty lame API. Fix.
+    _report(_value, oldValues, _observed);
+    return true;
+  }
+}
+
+/// An object accepted by [PropertyPath] where properties are read and written
+/// as indexing operations, just like a [Map].
+abstract class Indexable<K, V> {
+  V operator [](K key);
+  operator []=(K key, V value);
+}
+
+const _observerSentinel = const _ObserverSentinel();
+class _ObserverSentinel { const _ObserverSentinel(); }
+
+// Visible for testing
+get observerSentinelForTesting => _observerSentinel;
+
+// A base class for the shared API implemented by PathObserver and
+// CompoundObserver and used in _ObservedSet.
+abstract class _Observer extends Bindable {
+  Function _notifyCallback;
+  int _notifyArgumentCount;
+  var _value;
+
+  // abstract members
+  void _iterateObjects(void observe(obj, prop));
+  void _connect();
+  void _disconnect();
+  bool _check({bool skipChanges: false});
+
+  static int _UNOPENED = 0;
+  static int _OPENED = 1;
+  static int _CLOSED = 2;
+  int _state = _UNOPENED;
+  bool get _isOpen => _state == _OPENED;
+  bool get _isClosed => _state == _CLOSED;
+
+  /// The number of arguments the subclass will pass to [_report].
+  int get _reportArgumentCount;
+
+  open(callback) {
+    if (_isOpen || _isClosed) {
+      throw new StateError('Observer has already been opened.');
+    }
+
+    if (smoke.minArgs(callback) > _reportArgumentCount) {
+      throw new ArgumentError('callback should take $_reportArgumentCount or '
+          'fewer arguments');
+    }
+
+    _notifyCallback = callback;
+    _notifyArgumentCount = min(_reportArgumentCount, smoke.maxArgs(callback));
+
+    _connect();
+    _state = _OPENED;
+    return _value;
+  }
+
+  get value => _discardChanges();
+
+  void close() {
+    if (!_isOpen) return;
+
+    _disconnect();
+    _value = null;
+    _notifyCallback = null;
+    _state = _CLOSED;
+  }
+
+  _discardChanges() {
+    _check(skipChanges: true);
+    return _value;
+  }
+
+  void deliver() {
+    if (_isOpen) _dirtyCheck();
+  }
+
+  bool _dirtyCheck() {
+    var cycles = 0;
+    while (cycles < _MAX_DIRTY_CHECK_CYCLES && _check()) {
+      cycles++;
+    }
+    return cycles > 0;
+  }
+
+  void _report(newValue, oldValue, [extraArg]) {
+    try {
+      switch (_notifyArgumentCount) {
+        case 0: _notifyCallback(); break;
+        case 1: _notifyCallback(newValue); break;
+        case 2: _notifyCallback(newValue, oldValue); break;
+        case 3: _notifyCallback(newValue, oldValue, extraArg); break;
+      }
+    } catch (e, s) {
+      // Deliver errors async, so if a single callback fails it doesn't prevent
+      // other things from working.
+      new Completer().completeError(e, s);
+    }
+  }
+}
+
+/// The observedSet abstraction is a perf optimization which reduces the total
+/// number of Object.observe observations of a set of objects. The idea is that
+/// groups of Observers will have some object dependencies in common and this
+/// observed set ensures that each object in the transitive closure of
+/// dependencies is only observed once. The observedSet acts as a write barrier
+/// such that whenever any change comes through, all Observers are checked for
+/// changed values.
+///
+/// Note that this optimization is explicitly moving work from setup-time to
+/// change-time.
+///
+/// TODO(rafaelw): Implement "garbage collection". In order to move work off
+/// the critical path, when Observers are closed, their observed objects are
+/// not Object.unobserve(d). As a result, it's possible that if the observedSet
+/// is kept open, but some Observers have been closed, it could cause "leaks"
+/// (prevent otherwise collectable objects from being collected). At some
+/// point, we should implement incremental "gc" which keeps a list of
+/// observedSets which may need clean-up and does small amounts of cleanup on a
+/// timeout until all is clean.
+class _ObservedSet {
+  /// To prevent sequential [PathObserver]s and [CompoundObserver]s from
+  /// observing the same object, we check if they are observing the same root
+  /// as the most recently created observer, and if so merge it into the
+  /// existing _ObservedSet.
+  ///
+  /// See <https://github.com/Polymer/observe-js/commit/f0990b1> and
+  /// <https://codereview.appspot.com/46780044/>.
+  static _ObservedSet _lastSet;
+
+  /// The root object for a [PathObserver]. For a [CompoundObserver], the root
+  /// object of the first path observed. This is used by the constructor to
+  /// reuse an [_ObservedSet] that starts from the same object.
+  Object _rootObject;
+
+  /// Subset of properties in [_rootObject] that we care about.
+  Set _rootObjectProperties;
+
+  /// Observers associated with this root object, in birth order.
+  final List<_Observer> _observers = [];
+
+  // Dart note: the JS implementation is O(N^2) because Array.indexOf is used
+  // for lookup in this array. We use HashMap to avoid this problem. It
+  // also gives us a nice way of tracking the StreamSubscription.
+  Map<Object, StreamSubscription> _objects;
+
+  factory _ObservedSet(_Observer observer, Object rootObject) {
+    if (_lastSet == null || !identical(_lastSet._rootObject, rootObject)) {
+      _lastSet = new _ObservedSet._(rootObject);
+    }
+    _lastSet.open(observer, rootObject);
+    return _lastSet;
+  }
+
+  _ObservedSet._(rootObject)
+      : _rootObject = rootObject,
+        _rootObjectProperties = rootObject == null ? null : new Set();
+
+  void open(_Observer obs, Object rootObject) {
+    if (_rootObject == null) {
+      _rootObject = rootObject;
+      _rootObjectProperties = new Set();
+    }
+
+    _observers.add(obs);
+    obs._iterateObjects(observe);
+  }
+
+  void close(_Observer obs) {
+    _observers.remove(obs);
+    if (_observers.isNotEmpty) return;
+
+    if (_objects != null) {
+      for (var sub in _objects.values) sub.cancel();
+      _objects = null;
+    }
+    _rootObject = null;
+    _rootObjectProperties = null;
+    if (identical(_lastSet, this)) _lastSet = null;
+  }
+
+  /// Observe now takes a second argument to indicate which property of an
+  /// object is being observed, so we don't trigger change notifications on
+  /// changes to unrelated properties.
+  void observe(Object obj, Object prop) {
+    if (identical(obj, _rootObject)) _rootObjectProperties.add(prop);
+    if (obj is ObservableList) _observeStream(obj.listChanges);
+    if (obj is Observable) _observeStream(obj.changes);
+  }
+
+  void _observeStream(Stream stream) {
+    // TODO(jmesserly): we hash on streams as we have two separate change
+    // streams for ObservableList. Not sure if that is the design we will use
+    // going forward.
+
+    if (_objects == null) _objects = new HashMap();
+    if (!_objects.containsKey(stream)) {
+      _objects[stream] = stream.listen(_callback);
+    }
+  }
+
+  /// Whether we can ignore all change events in [records]. This is true if all
+  /// records are for properties in the [_rootObject] and we are not observing
+  /// any of those properties. Changes on objects other than [_rootObject], or
+  /// changes for properties in [_rootObjectProperties] can't be ignored.
+  // Dart note: renamed from `allRootObjNonObservedProps` in the JS code.
+  bool _canIgnoreRecords(List<ChangeRecord> records) {
+    for (var rec in records) {
+      if (rec is PropertyChangeRecord) {
+        if (!identical(rec.object, _rootObject) ||
+            _rootObjectProperties.contains(rec.name)) {
+          return false;
+        }
+      } else if (rec is ListChangeRecord) {
+        if (!identical(rec.object, _rootObject) ||
+            _rootObjectProperties.contains(rec.index)) {
+          return false;
+        }
+      } else {
+        // TODO(sigmund): consider adding object to MapChangeRecord, and make
+        // this more precise.
+        return false;
+      }
+    }
+    return true;
+  }
+
+  void _callback(records) {
+    if (_canIgnoreRecords(records)) return;
+    for (var observer in _observers.toList(growable: false)) {
+      if (observer._isOpen) observer._iterateObjects(observe);
+    }
+
+    for (var observer in _observers.toList(growable: false)) {
+      if (observer._isOpen) observer._check();
+    }
+  }
+}
+
+const int _MAX_DIRTY_CHECK_CYCLES = 1000;
diff --git a/observe/lib/src/to_observable.dart b/observe/lib/src/to_observable.dart
new file mode 100644
index 0000000..931fb04
--- /dev/null
+++ b/observe/lib/src/to_observable.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2013, 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 observe.src.to_observable;
+
+import 'package:observe/observe.dart';
+
+/// Converts the [Iterable] or [Map] to an [ObservableList] or [ObservableMap],
+/// respectively. This is a convenience function to make it easier to convert
+/// literals into the corresponding observable collection type.
+///
+/// If [value] is not one of those collection types, or is already [Observable],
+/// it will be returned unmodified.
+///
+/// If [value] is a [Map], the resulting value will use the appropriate kind of
+/// backing map: either [HashMap], [LinkedHashMap], or [SplayTreeMap].
+///
+/// By default this performs a deep conversion, but you can set [deep] to false
+/// for a shallow conversion. This does not handle circular data structures.
+/// If a conversion is peformed, mutations are only observed to the result of
+/// this function. Changing the original collection will not affect it.
+// TODO(jmesserly): ObservableSet?
+toObservable(value, {bool deep: true}) =>
+    deep ? _toObservableDeep(value) : _toObservableShallow(value);
+
+_toObservableShallow(value) {
+  if (value is Observable) return value;
+  if (value is Map) return new ObservableMap.from(value);
+  if (value is Iterable) return new ObservableList.from(value);
+  return value;
+}
+
+_toObservableDeep(value) {
+  if (value is Observable) return value;
+  if (value is Map) {
+    var result = new ObservableMap.createFromType(value);
+    value.forEach((k, v) {
+      result[_toObservableDeep(k)] = _toObservableDeep(v);
+    });
+    return result;
+  }
+  if (value is Iterable) {
+    return new ObservableList.from(value.map(_toObservableDeep));
+  }
+  return value;
+}
diff --git a/observe/lib/transformer.dart b/observe/lib/transformer.dart
new file mode 100644
index 0000000..ce43956
--- /dev/null
+++ b/observe/lib/transformer.dart
@@ -0,0 +1,416 @@
+// Copyright (c) 2013, 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.
+
+/// Code transform for @observable. The core transformation is relatively
+/// straightforward, and essentially like an editor refactoring.
+library observe.transformer;
+
+import 'dart:async';
+
+import 'package:analyzer/analyzer.dart';
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/error.dart';
+import 'package:analyzer/src/generated/parser.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+import 'package:barback/barback.dart';
+import 'package:code_transformers/messages/build_logger.dart';
+import 'package:source_maps/refactor.dart';
+import 'package:source_span/source_span.dart';
+
+import 'src/messages.dart';
+
+/// A [Transformer] that replaces observables based on dirty-checking with an
+/// implementation based on change notifications.
+///
+/// The transformation adds hooks for field setters and notifies the observation
+/// system of the change.
+class ObservableTransformer extends Transformer {
+  final bool releaseMode;
+  final bool injectBuildLogsInOutput;
+  final List<String> _files;
+  ObservableTransformer(
+      {List<String> files, bool releaseMode, bool injectBuildLogsInOutput})
+      : _files = files,
+        releaseMode = releaseMode == true,
+        injectBuildLogsInOutput = injectBuildLogsInOutput == null
+            ? releaseMode != true
+            : injectBuildLogsInOutput;
+  ObservableTransformer.asPlugin(BarbackSettings settings)
+      : _files = _readFiles(settings.configuration['files']),
+        releaseMode = settings.mode == BarbackMode.RELEASE,
+        injectBuildLogsInOutput = settings.mode != BarbackMode.RELEASE;
+
+  static List<String> _readFiles(value) {
+    if (value == null) return null;
+    var files = [];
+    bool error;
+    if (value is List) {
+      files = value;
+      error = value.any((e) => e is! String);
+    } else if (value is String) {
+      files = [value];
+      error = false;
+    } else {
+      error = true;
+    }
+    if (error) print('Invalid value for "files" in the observe transformer.');
+    return files;
+  }
+
+  // TODO(nweiz): This should just take an AssetId when barback <0.13.0 support
+  // is dropped.
+  Future<bool> isPrimary(idOrAsset) {
+    var id = idOrAsset is AssetId ? idOrAsset : idOrAsset.id;
+    return new Future.value(id.extension == '.dart' &&
+        (_files == null || _files.contains(id.path)));
+  }
+
+  Future apply(Transform transform) {
+    return transform.primaryInput.readAsString().then((content) {
+      // Do a quick string check to determine if this is this file even
+      // plausibly might need to be transformed. If not, we can avoid an
+      // expensive parse.
+      if (!observableMatcher.hasMatch(content)) return null;
+
+      var id = transform.primaryInput.id;
+      // TODO(sigmund): improve how we compute this url
+      var url = id.path.startsWith('lib/')
+          ? 'package:${id.package}/${id.path.substring(4)}'
+          : id.path;
+      var sourceFile = new SourceFile(content, url: url);
+      var logger = new BuildLogger(transform,
+          convertErrorsToWarnings: !releaseMode,
+          detailsUri: 'http://goo.gl/5HPeuP');
+      var transaction = _transformCompilationUnit(content, sourceFile, logger);
+      if (!transaction.hasEdits) {
+        transform.addOutput(transform.primaryInput);
+      } else {
+        var printer = transaction.commit();
+        // TODO(sigmund): emit source maps when barback supports it (see
+        // dartbug.com/12340)
+        printer.build(url);
+        transform.addOutput(new Asset.fromString(id, printer.text));
+      }
+
+      if (injectBuildLogsInOutput) return logger.writeOutput();
+    });
+  }
+}
+
+TextEditTransaction _transformCompilationUnit(
+    String inputCode, SourceFile sourceFile, BuildLogger logger) {
+  var unit = parseCompilationUnit(inputCode, suppressErrors: true);
+  var code = new TextEditTransaction(inputCode, sourceFile);
+  for (var directive in unit.directives) {
+    if (directive is LibraryDirective && _hasObservable(directive)) {
+      logger.warning(NO_OBSERVABLE_ON_LIBRARY,
+          span: _getSpan(sourceFile, directive));
+      break;
+    }
+  }
+
+  for (var declaration in unit.declarations) {
+    if (declaration is ClassDeclaration) {
+      _transformClass(declaration, code, sourceFile, logger);
+    } else if (declaration is TopLevelVariableDeclaration) {
+      if (_hasObservable(declaration)) {
+        logger.warning(NO_OBSERVABLE_ON_TOP_LEVEL,
+            span: _getSpan(sourceFile, declaration));
+      }
+    }
+  }
+  return code;
+}
+
+_getSpan(SourceFile file, AstNode node) => file.span(node.offset, node.end);
+
+/// True if the node has the `@observable` or `@published` annotation.
+// TODO(jmesserly): it is not good to be hard coding Polymer support here.
+bool _hasObservable(AnnotatedNode node) =>
+    node.metadata.any(_isObservableAnnotation);
+
+// TODO(jmesserly): this isn't correct if the annotation has been imported
+// with a prefix, or cases like that. We should technically be resolving, but
+// that is expensive in analyzer, so it isn't feasible yet.
+bool _isObservableAnnotation(Annotation node) =>
+    _isAnnotationContant(node, 'observable') ||
+        _isAnnotationContant(node, 'published') ||
+        _isAnnotationType(node, 'ObservableProperty') ||
+        _isAnnotationType(node, 'PublishedProperty');
+
+bool _isAnnotationContant(Annotation m, String name) =>
+    m.name.name == name && m.constructorName == null && m.arguments == null;
+
+bool _isAnnotationType(Annotation m, String name) => m.name.name == name;
+
+void _transformClass(ClassDeclaration cls, TextEditTransaction code,
+    SourceFile file, BuildLogger logger) {
+  if (_hasObservable(cls)) {
+    logger.warning(NO_OBSERVABLE_ON_CLASS, span: _getSpan(file, cls));
+  }
+
+  // We'd like to track whether observable was declared explicitly, otherwise
+  // report a warning later below. Because we don't have type analysis (only
+  // syntactic understanding of the code), we only report warnings that are
+  // known to be true.
+  var explicitObservable = false;
+  var implicitObservable = false;
+  if (cls.extendsClause != null) {
+    var id = _getSimpleIdentifier(cls.extendsClause.superclass.name);
+    if (id.name == 'Observable') {
+      code.edit(id.offset, id.end, 'ChangeNotifier');
+      explicitObservable = true;
+    } else if (id.name == 'ChangeNotifier') {
+      explicitObservable = true;
+    } else if (id.name != 'HtmlElement' &&
+        id.name != 'CustomElement' &&
+        id.name != 'Object') {
+      // TODO(sigmund): this is conservative, consider using type-resolution to
+      // improve this check.
+      implicitObservable = true;
+    }
+  }
+
+  if (cls.withClause != null) {
+    for (var type in cls.withClause.mixinTypes) {
+      var id = _getSimpleIdentifier(type.name);
+      if (id.name == 'Observable') {
+        code.edit(id.offset, id.end, 'ChangeNotifier');
+        explicitObservable = true;
+        break;
+      } else if (id.name == 'ChangeNotifier') {
+        explicitObservable = true;
+        break;
+      } else {
+        // TODO(sigmund): this is conservative, consider using type-resolution
+        // to improve this check.
+        implicitObservable = true;
+      }
+    }
+  }
+
+  if (cls.implementsClause != null) {
+    // TODO(sigmund): consider adding type-resolution to give a more precise
+    // answer.
+    implicitObservable = true;
+  }
+
+  var declaresObservable = explicitObservable || implicitObservable;
+
+  // Track fields that were transformed.
+  var instanceFields = new Set<String>();
+
+  for (var member in cls.members) {
+    if (member is FieldDeclaration) {
+      if (member.isStatic) {
+        if (_hasObservable(member)) {
+          logger.warning(NO_OBSERVABLE_ON_STATIC_FIELD,
+              span: _getSpan(file, member));
+        }
+        continue;
+      }
+      if (_hasObservable(member)) {
+        if (!declaresObservable) {
+          logger.warning(REQUIRE_OBSERVABLE_INTERFACE,
+              span: _getSpan(file, member));
+        }
+        _transformFields(file, member, code, logger);
+
+        var names = member.fields.variables.map((v) => v.name.name);
+
+        if (!_isReadOnly(member.fields)) instanceFields.addAll(names);
+      }
+    }
+  }
+
+  // If nothing was @observable, bail.
+  if (instanceFields.length == 0) return;
+
+  if (!explicitObservable) _mixinObservable(cls, code);
+
+  // Fix initializers, because they aren't allowed to call the setter.
+  for (var member in cls.members) {
+    if (member is ConstructorDeclaration) {
+      _fixConstructor(member, code, instanceFields);
+    }
+  }
+}
+
+/// Adds "with ChangeNotifier" and associated implementation.
+void _mixinObservable(ClassDeclaration cls, TextEditTransaction code) {
+  // Note: we need to be careful to put the with clause after extends, but
+  // before implements clause.
+  if (cls.withClause != null) {
+    var pos = cls.withClause.end;
+    code.edit(pos, pos, ', ChangeNotifier');
+  } else if (cls.extendsClause != null) {
+    var pos = cls.extendsClause.end;
+    code.edit(pos, pos, ' with ChangeNotifier ');
+  } else {
+    var params = cls.typeParameters;
+    var pos = params != null ? params.end : cls.name.end;
+    code.edit(pos, pos, ' extends ChangeNotifier ');
+  }
+}
+
+SimpleIdentifier _getSimpleIdentifier(Identifier id) =>
+    id is PrefixedIdentifier ? id.identifier : id;
+
+bool _hasKeyword(Token token, Keyword keyword) =>
+    token is KeywordToken && token.keyword == keyword;
+
+String _getOriginalCode(TextEditTransaction code, AstNode node) =>
+    code.original.substring(node.offset, node.end);
+
+void _fixConstructor(ConstructorDeclaration ctor, TextEditTransaction code,
+    Set<String> changedFields) {
+
+  // Fix normal initializers
+  for (var initializer in ctor.initializers) {
+    if (initializer is ConstructorFieldInitializer) {
+      var field = initializer.fieldName;
+      if (changedFields.contains(field.name)) {
+        code.edit(field.offset, field.end, '__\$${field.name}');
+      }
+    }
+  }
+
+  // Fix "this." initializer in parameter list. These are tricky:
+  // we need to preserve the name and add an initializer.
+  // Preserving the name is important for named args, and for dartdoc.
+  // BEFORE: Foo(this.bar, this.baz) { ... }
+  // AFTER:  Foo(bar, baz) : __$bar = bar, __$baz = baz { ... }
+
+  var thisInit = [];
+  for (var param in ctor.parameters.parameters) {
+    if (param is DefaultFormalParameter) {
+      param = param.parameter;
+    }
+    if (param is FieldFormalParameter) {
+      var name = param.identifier.name;
+      if (changedFields.contains(name)) {
+        thisInit.add(name);
+        // Remove "this." but keep everything else.
+        code.edit(param.thisToken.offset, param.period.end, '');
+      }
+    }
+  }
+
+  if (thisInit.length == 0) return;
+
+  // TODO(jmesserly): smarter formatting with indent, etc.
+  var inserted = thisInit.map((i) => '__\$$i = $i').join(', ');
+
+  int offset;
+  if (ctor.separator != null) {
+    offset = ctor.separator.end;
+    inserted = ' $inserted,';
+  } else {
+    offset = ctor.parameters.end;
+    inserted = ' : $inserted';
+  }
+
+  code.edit(offset, offset, inserted);
+}
+
+bool _isReadOnly(VariableDeclarationList fields) {
+  return _hasKeyword(fields.keyword, Keyword.CONST) ||
+      _hasKeyword(fields.keyword, Keyword.FINAL);
+}
+
+void _transformFields(SourceFile file, FieldDeclaration member,
+    TextEditTransaction code, BuildLogger logger) {
+  final fields = member.fields;
+  if (_isReadOnly(fields)) return;
+
+  // Private fields aren't supported:
+  for (var field in fields.variables) {
+    final name = field.name.name;
+    if (Identifier.isPrivateName(name)) {
+      logger.warning('Cannot make private field $name observable.',
+          span: _getSpan(file, field));
+      return;
+    }
+  }
+
+  // Unfortunately "var" doesn't work in all positions where type annotations
+  // are allowed, such as "var get name". So we use "dynamic" instead.
+  var type = 'dynamic';
+  if (fields.type != null) {
+    type = _getOriginalCode(code, fields.type);
+  } else if (_hasKeyword(fields.keyword, Keyword.VAR)) {
+    // Replace 'var' with 'dynamic'
+    code.edit(fields.keyword.offset, fields.keyword.end, type);
+  }
+
+  // Note: the replacements here are a bit subtle. It needs to support multiple
+  // fields declared via the same @observable, as well as preserving newlines.
+  // (Preserving newlines is important because it allows the generated code to
+  // be debugged without needing a source map.)
+  //
+  // For example:
+  //
+  //     @observable
+  //     @otherMetaData
+  //         Foo
+  //             foo = 1, bar = 2,
+  //             baz;
+  //
+  // Will be transformed into something like:
+  //
+  //     @reflectable @observable
+  //     @OtherMetaData()
+  //         Foo
+  //             get foo => __foo; Foo __foo = 1; @reflectable set foo ...; ...
+  //             @observable @OtherMetaData() Foo get baz => __baz; Foo baz; ...
+  //
+  // Metadata is moved to the getter.
+
+  String metadata = '';
+  if (fields.variables.length > 1) {
+    metadata = member.metadata.map((m) => _getOriginalCode(code, m)).join(' ');
+    metadata = '@reflectable $metadata';
+  }
+
+  for (int i = 0; i < fields.variables.length; i++) {
+    final field = fields.variables[i];
+    final name = field.name.name;
+
+    var beforeInit = 'get $name => __\$$name; $type __\$$name';
+
+    // The first field is expanded differently from subsequent fields, because
+    // we can reuse the metadata and type annotation.
+    if (i == 0) {
+      final begin = member.metadata.first.offset;
+      code.edit(begin, begin, '@reflectable ');
+    } else {
+      beforeInit = '$metadata $type $beforeInit';
+    }
+
+    code.edit(field.name.offset, field.name.end, beforeInit);
+
+    // Replace comma with semicolon
+    final end = _findFieldSeperator(field.endToken.next);
+    if (end.type == TokenType.COMMA) code.edit(end.offset, end.end, ';');
+
+    code.edit(end.end, end.end, ' @reflectable set $name($type value) { '
+        '__\$$name = notifyPropertyChange(#$name, __\$$name, value); }');
+  }
+}
+
+Token _findFieldSeperator(Token token) {
+  while (token != null) {
+    if (token.type == TokenType.COMMA || token.type == TokenType.SEMICOLON) {
+      break;
+    }
+    token = token.next;
+  }
+  return token;
+}
+
+// TODO(sigmund): remove hard coded Polymer support (@published). The proper way
+// to do this would be to switch to use the analyzer to resolve whether
+// annotations are subtypes of ObservableProperty.
+final observableMatcher =
+    new RegExp("@(published|observable|PublishedProperty|ObservableProperty)");
diff --git a/observe/pubspec.yaml b/observe/pubspec.yaml
new file mode 100644
index 0000000..e1c9d0a
--- /dev/null
+++ b/observe/pubspec.yaml
@@ -0,0 +1,33 @@
+name: observe
+version: 0.13.1
+author: Polymer.dart Authors <web-ui-dev@dartlang.org>
+description: >
+  Observable properties and objects for use in template_binding.
+  Template Binding extends HTML and the DOM APIs to support a sensible
+  separation between the UI (DOM) of a document or application and its
+  underlying data (model). Updates to the model are reflected in the DOM and
+  user input into the DOM is immediately assigned to the model.
+homepage: https://www.dartlang.org/polymer-dart/
+dependencies:
+  analyzer: '>=0.15.6 <0.26.0'
+  barback: '>=0.14.2 <0.16.0'
+  logging: '>=0.9.0 <0.10.0'
+  path: '>=0.9.0 <2.0.0'
+  smoke: '>=0.1.0 <0.4.0'
+  source_maps: '>=0.9.4 <0.11.0'
+  source_span: '>=1.0.0 <2.0.0'
+  utf: '>=0.9.0 <0.10.0'
+  code_transformers: '>=0.2.3 <0.3.0'
+dev_dependencies:
+  benchmark_harness: '>=1.0.0 <2.0.0'
+  browser: any
+  chart: '>=1.0.8 <2.0.0'
+  unittest: '>=0.10.0 <0.12.0'
+  stack_trace: '>=0.9.1 <2.0.0'
+environment:
+  sdk: '>=1.2.0 <2.0.0'
+transformers:
+- observe:
+    files:
+      - benchmark/test_observable.dart
+      - benchmark/test_path_observable.dart
diff --git a/path/lib/path.dart b/path/lib/path.dart
new file mode 100644
index 0000000..af9efe5
--- /dev/null
+++ b/path/lib/path.dart
@@ -0,0 +1,381 @@
+// Copyright (c) 2012, 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.
+
+/// A comprehensive, cross-platform path manipulation library.
+///
+/// ## Installing ##
+///
+/// Use [pub][] to install this package. Add the following to your
+/// `pubspec.yaml` file.
+///
+///     dependencies:
+///       path: any
+///
+/// Then run `pub install`.
+///
+/// For more information, see the [path package on pub.dartlang.org][pkg].
+///
+/// [pub]: http://pub.dartlang.org
+/// [pkg]: http://pub.dartlang.org/packages/path
+///
+/// ## Usage ##
+///
+/// The path library was designed to be imported with a prefix, though you don't
+/// have to if you don't want to:
+///
+///     import 'package:path/path.dart' as path;
+///
+/// The most common way to use the library is through the top-level functions.
+/// These manipulate path strings based on your current working directory and
+/// the path style (POSIX, Windows, or URLs) of the host platform. For example:
+///
+///     path.join("directory", "file.txt");
+///
+/// This calls the top-level [join] function to join "directory" and "file.txt"
+/// using the current platform's directory separator.
+///
+/// If you want to work with paths for a specific platform regardless of the
+/// underlying platform that the program is running on, you can create a
+/// [Context] and give it an explicit [Style]:
+///
+///     var context = new path.Context(style: Style.windows);
+///     context.join("directory", "file.txt");
+///
+/// This will join "directory" and "file.txt" using the Windows path separator,
+/// even when the program is run on a POSIX machine.
+library path;
+
+import 'src/context.dart';
+import 'src/style.dart';
+
+export 'src/context.dart' hide createInternal;
+export 'src/path_exception.dart';
+export 'src/style.dart';
+
+/// A default context for manipulating POSIX paths.
+final Context posix = new Context(style: Style.posix);
+
+/// A default context for manipulating Windows paths.
+final Context windows = new Context(style: Style.windows);
+
+/// A default context for manipulating URLs.
+final Context url = new Context(style: Style.url);
+
+/// The system path context.
+///
+/// This differs from a context created with [new Context] in that its
+/// [Context.current] is always the current working directory, rather than being
+/// set once when the context is created.
+final Context context = createInternal();
+
+/// Returns the [Style] of the current context.
+///
+/// This is the style that all top-level path functions will use.
+Style get style => context.style;
+
+/// Gets the path to the current working directory.
+///
+/// In the browser, this means the current URL, without the last file segment.
+String get current {
+  var uri = Uri.base;
+  if (Style.platform == Style.url) {
+    return uri.resolve('.').toString();
+  } else {
+    var path = uri.toFilePath();
+    // Remove trailing '/' or '\'.
+    int lastIndex = path.length - 1;
+    assert(path[lastIndex] == '/' || path[lastIndex] == '\\');
+    return path.substring(0, lastIndex);
+  }
+}
+
+/// Gets the path separator for the current platform. This is `\` on Windows
+/// and `/` on other platforms (including the browser).
+String get separator => context.separator;
+
+/// Creates a new path by appending the given path parts to [current].
+/// Equivalent to [join()] with [current] as the first argument. Example:
+///
+///     path.absolute('path', 'to/foo'); // -> '/your/current/dir/path/to/foo'
+String absolute(String part1, [String part2, String part3, String part4,
+    String part5, String part6, String part7]) =>
+        context.absolute(part1, part2, part3, part4, part5, part6, part7);
+
+/// Gets the part of [path] after the last separator.
+///
+///     path.basename('path/to/foo.dart'); // -> 'foo.dart'
+///     path.basename('path/to');          // -> 'to'
+///
+/// Trailing separators are ignored.
+///
+///     path.basename('path/to/'); // -> 'to'
+String basename(String path) => context.basename(path);
+
+/// Gets the part of [path] after the last separator, and without any trailing
+/// file extension.
+///
+///     path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo'
+///
+/// Trailing separators are ignored.
+///
+///     path.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo'
+String basenameWithoutExtension(String path) =>
+    context.basenameWithoutExtension(path);
+
+/// Gets the part of [path] before the last separator.
+///
+///     path.dirname('path/to/foo.dart'); // -> 'path/to'
+///     path.dirname('path/to');          // -> 'path'
+///
+/// Trailing separators are ignored.
+///
+///     path.dirname('path/to/'); // -> 'path'
+///
+/// If an absolute path contains no directories, only a root, then the root
+/// is returned.
+///
+///     path.dirname('/');  // -> '/' (posix)
+///     path.dirname('c:\');  // -> 'c:\' (windows)
+///
+/// If a relative path has no directories, then '.' is returned.
+///
+///     path.dirname('foo');  // -> '.'
+///     path.dirname('');  // -> '.'
+String dirname(String path) => context.dirname(path);
+
+/// Gets the file extension of [path]: the portion of [basename] from the last
+/// `.` to the end (including the `.` itself).
+///
+///     path.extension('path/to/foo.dart');    // -> '.dart'
+///     path.extension('path/to/foo');         // -> ''
+///     path.extension('path.to/foo');         // -> ''
+///     path.extension('path/to/foo.dart.js'); // -> '.js'
+///
+/// If the file name starts with a `.`, then that is not considered the
+/// extension:
+///
+///     path.extension('~/.bashrc');    // -> ''
+///     path.extension('~/.notes.txt'); // -> '.txt'
+String extension(String path) => context.extension(path);
+
+// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
+/// Returns the root of [path], if it's absolute, or the empty string if it's
+/// relative.
+///
+///     // Unix
+///     path.rootPrefix('path/to/foo'); // -> ''
+///     path.rootPrefix('/path/to/foo'); // -> '/'
+///
+///     // Windows
+///     path.rootPrefix(r'path\to\foo'); // -> ''
+///     path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\'
+///
+///     // URL
+///     path.rootPrefix('path/to/foo'); // -> ''
+///     path.rootPrefix('http://dartlang.org/path/to/foo');
+///       // -> 'http://dartlang.org'
+String rootPrefix(String path) => context.rootPrefix(path);
+
+/// Returns `true` if [path] is an absolute path and `false` if it is a
+/// relative path.
+///
+/// On POSIX systems, absolute paths start with a `/` (forward slash). On
+/// Windows, an absolute path starts with `\\`, or a drive letter followed by
+/// `:/` or `:\`. For URLs, absolute paths either start with a protocol and
+/// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`.
+///
+/// URLs that start with `/` are known as "root-relative", since they're
+/// relative to the root of the current URL. Since root-relative paths are still
+/// absolute in every other sense, [isAbsolute] will return true for them. They
+/// can be detected using [isRootRelative].
+bool isAbsolute(String path) => context.isAbsolute(path);
+
+/// Returns `true` if [path] is a relative path and `false` if it is absolute.
+/// On POSIX systems, absolute paths start with a `/` (forward slash). On
+/// Windows, an absolute path starts with `\\`, or a drive letter followed by
+/// `:/` or `:\`.
+bool isRelative(String path) => context.isRelative(path);
+
+/// Returns `true` if [path] is a root-relative path and `false` if it's not.
+///
+/// URLs that start with `/` are known as "root-relative", since they're
+/// relative to the root of the current URL. Since root-relative paths are still
+/// absolute in every other sense, [isAbsolute] will return true for them. They
+/// can be detected using [isRootRelative].
+///
+/// No POSIX and Windows paths are root-relative.
+bool isRootRelative(String path) => context.isRootRelative(path);
+
+/// Joins the given path parts into a single path using the current platform's
+/// [separator]. Example:
+///
+///     path.join('path', 'to', 'foo'); // -> 'path/to/foo'
+///
+/// If any part ends in a path separator, then a redundant separator will not
+/// be added:
+///
+///     path.join('path/', 'to', 'foo'); // -> 'path/to/foo
+///
+/// If a part is an absolute path, then anything before that will be ignored:
+///
+///     path.join('path', '/to', 'foo'); // -> '/to/foo'
+String join(String part1, [String part2, String part3, String part4,
+    String part5, String part6, String part7, String part8]) =>
+        context.join(part1, part2, part3, part4, part5, part6, part7, part8);
+
+/// Joins the given path parts into a single path using the current platform's
+/// [separator]. Example:
+///
+///     path.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo'
+///
+/// If any part ends in a path separator, then a redundant separator will not
+/// be added:
+///
+///     path.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo
+///
+/// If a part is an absolute path, then anything before that will be ignored:
+///
+///     path.joinAll(['path', '/to', 'foo']); // -> '/to/foo'
+///
+/// For a fixed number of parts, [join] is usually terser.
+String joinAll(Iterable<String> parts) => context.joinAll(parts);
+
+// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
+/// Splits [path] into its components using the current platform's [separator].
+///
+///     path.split('path/to/foo'); // -> ['path', 'to', 'foo']
+///
+/// The path will *not* be normalized before splitting.
+///
+///     path.split('path/../foo'); // -> ['path', '..', 'foo']
+///
+/// If [path] is absolute, the root directory will be the first element in the
+/// array. Example:
+///
+///     // Unix
+///     path.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo']
+///
+///     // Windows
+///     path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo']
+///
+///     // Browser
+///     path.split('http://dartlang.org/path/to/foo');
+///       // -> ['http://dartlang.org', 'path', 'to', 'foo']
+List<String> split(String path) => context.split(path);
+
+/// Normalizes [path], simplifying it by handling `..`, and `.`, and
+/// removing redundant path separators whenever possible.
+///
+///     path.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
+String normalize(String path) => context.normalize(path);
+
+/// Attempts to convert [path] to an equivalent relative path from the current
+/// directory.
+///
+///     // Given current directory is /root/path:
+///     path.relative('/root/path/a/b.dart'); // -> 'a/b.dart'
+///     path.relative('/root/other.dart'); // -> '../other.dart'
+///
+/// If the [from] argument is passed, [path] is made relative to that instead.
+///
+///     path.relative('/root/path/a/b.dart',
+///         from: '/root/path'); // -> 'a/b.dart'
+///     path.relative('/root/other.dart',
+///         from: '/root/path'); // -> '../other.dart'
+///
+/// If [path] and/or [from] are relative paths, they are assumed to be relative
+/// to the current directory.
+///
+/// Since there is no relative path from one drive letter to another on Windows,
+/// or from one hostname to another for URLs, this will return an absolute path
+/// in those cases.
+///
+///     // Windows
+///     path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other'
+///
+///     // URL
+///     path.relative('http://dartlang.org', from: 'http://pub.dartlang.org');
+///       // -> 'http://dartlang.org'
+String relative(String path, {String from}) =>
+    context.relative(path, from: from);
+
+/// Returns `true` if [child] is a path beneath `parent`, and `false` otherwise.
+///
+///     path.isWithin('/root/path', '/root/path/a'); // -> true
+///     path.isWithin('/root/path', '/root/other'); // -> false
+///     path.isWithin('/root/path', '/root/path') // -> false
+bool isWithin(String parent, String child) => context.isWithin(parent, child);
+
+/// Removes a trailing extension from the last part of [path].
+///
+///     withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
+String withoutExtension(String path) => context.withoutExtension(path);
+
+/// Returns the path represented by [uri], which may be a [String] or a [Uri].
+///
+/// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL
+/// style, this will just convert [uri] to a string.
+///
+///     // POSIX
+///     context.fromUri('file:///path/to/foo')
+///       // -> '/path/to/foo'
+///
+///     // Windows
+///     context.fromUri('file:///C:/path/to/foo')
+///       // -> r'C:\path\to\foo'
+///
+///     // URL
+///     context.fromUri('http://dartlang.org/path/to/foo')
+///       // -> 'http://dartlang.org/path/to/foo'
+///
+/// If [uri] is relative, a relative path will be returned.
+///
+///     path.fromUri('path/to/foo'); // -> 'path/to/foo'
+String fromUri(uri) => context.fromUri(uri);
+
+/// Returns the URI that represents [path].
+///
+/// For POSIX and Windows styles, this will return a `file:` URI. For the URL
+/// style, this will just convert [path] to a [Uri].
+///
+///     // POSIX
+///     path.toUri('/path/to/foo')
+///       // -> Uri.parse('file:///path/to/foo')
+///
+///     // Windows
+///     path.toUri(r'C:\path\to\foo')
+///       // -> Uri.parse('file:///C:/path/to/foo')
+///
+///     // URL
+///     path.toUri('http://dartlang.org/path/to/foo')
+///       // -> Uri.parse('http://dartlang.org/path/to/foo')
+///
+/// If [path] is relative, a relative URI will be returned.
+///
+///     path.toUri('path/to/foo')
+///       // -> Uri.parse('path/to/foo')
+Uri toUri(String path) => context.toUri(path);
+
+/// Returns a terse, human-readable representation of [uri].
+///
+/// [uri] can be a [String] or a [Uri]. If it can be made relative to the
+/// current working directory, that's done. Otherwise, it's returned as-is. This
+/// gracefully handles non-`file:` URIs for [Style.posix] and [Style.windows].
+///
+/// The returned value is meant for human consumption, and may be either URI-
+/// or path-formatted.
+///
+///     // POSIX at "/root/path"
+///     path.prettyUri('file:///root/path/a/b.dart'); // -> 'a/b.dart'
+///     path.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org'
+///
+///     // Windows at "C:\root\path"
+///     path.prettyUri('file:///C:/root/path/a/b.dart'); // -> r'a\b.dart'
+///     path.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org'
+///
+///     // URL at "http://dartlang.org/root/path"
+///     path.prettyUri('http://dartlang.org/root/path/a/b.dart');
+///         // -> r'a/b.dart'
+///     path.prettyUri('file:///root/path'); // -> 'file:///root/path'
+String prettyUri(uri) => context.prettyUri(uri);
diff --git a/path/lib/src/characters.dart b/path/lib/src/characters.dart
new file mode 100644
index 0000000..ff196a6
--- /dev/null
+++ b/path/lib/src/characters.dart
@@ -0,0 +1,19 @@
+// 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.
+
+/// This library contains character-code definitions.
+library path.characters;
+
+const PLUS = 0x2b;
+const MINUS = 0x2d;
+const PERIOD = 0x2e;
+const SLASH = 0x2f;
+const ZERO = 0x30;
+const NINE = 0x39;
+const COLON = 0x3a;
+const UPPER_A = 0x41;
+const UPPER_Z = 0x5a;
+const LOWER_A = 0x61;
+const LOWER_Z = 0x7a;
+const BACKSLASH = 0x5c;
diff --git a/path/lib/src/context.dart b/path/lib/src/context.dart
new file mode 100644
index 0000000..db055a1
--- /dev/null
+++ b/path/lib/src/context.dart
@@ -0,0 +1,574 @@
+// Copyright (c) 2013, 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 path.context;
+
+import 'internal_style.dart';
+import 'style.dart';
+import 'parsed_path.dart';
+import 'path_exception.dart';
+import '../path.dart' as p;
+
+Context createInternal() => new Context._internal();
+
+/// An instantiable class for manipulating paths. Unlike the top-level
+/// functions, this lets you explicitly select what platform the paths will use.
+class Context {
+  /// Creates a new path context for the given style and current directory.
+  ///
+  /// If [style] is omitted, it uses the host operating system's path style. If
+  /// only [current] is omitted, it defaults ".". If *both* [style] and
+  /// [current] are omitted, [current] defaults to the real current working
+  /// directory.
+  ///
+  /// On the browser, [style] defaults to [Style.url] and [current] defaults to
+  /// the current URL.
+  factory Context({Style style, String current}) {
+    if (current == null) {
+      if (style == null) {
+        current = p.current;
+      } else {
+        current = ".";
+      }
+    }
+
+    if (style == null) {
+      style = Style.platform;
+    } else if (style is! InternalStyle) {
+      throw new ArgumentError("Only styles defined by the path package are "
+          "allowed.");
+    }
+
+    return new Context._(style as InternalStyle, current);
+  }
+
+  /// Create a [Context] to be used internally within path.
+  Context._internal()
+      : style = Style.platform as InternalStyle,
+        _current = null;
+
+  Context._(this.style, this._current);
+
+  /// The style of path that this context works with.
+  final InternalStyle style;
+
+  /// The current directory given when Context was created. If null, current
+  /// directory is evaluated from 'p.current'.
+  final String _current;
+
+  /// The current directory that relative paths are relative to.
+  String get current => _current != null ? _current : p.current;
+
+  /// Gets the path separator for the context's [style]. On Mac and Linux,
+  /// this is `/`. On Windows, it's `\`.
+  String get separator => style.separator;
+
+  /// Creates a new path by appending the given path parts to [current].
+  /// Equivalent to [join()] with [current] as the first argument. Example:
+  ///
+  ///     var context = new Context(current: '/root');
+  ///     context.absolute('path', 'to', 'foo'); // -> '/root/path/to/foo'
+  ///
+  /// If [current] isn't absolute, this won't return an absolute path.
+  String absolute(String part1, [String part2, String part3, String part4,
+      String part5, String part6, String part7]) {
+    return join(current, part1, part2, part3, part4, part5, part6, part7);
+  }
+
+  /// Gets the part of [path] after the last separator on the context's
+  /// platform.
+  ///
+  ///     context.basename('path/to/foo.dart'); // -> 'foo.dart'
+  ///     context.basename('path/to');          // -> 'to'
+  ///
+  /// Trailing separators are ignored.
+  ///
+  ///     context.basename('path/to/'); // -> 'to'
+  String basename(String path) => _parse(path).basename;
+
+  /// Gets the part of [path] after the last separator on the context's
+  /// platform, and without any trailing file extension.
+  ///
+  ///     context.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo'
+  ///
+  /// Trailing separators are ignored.
+  ///
+  ///     context.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo'
+  String basenameWithoutExtension(String path) =>
+      _parse(path).basenameWithoutExtension;
+
+  /// Gets the part of [path] before the last separator.
+  ///
+  ///     context.dirname('path/to/foo.dart'); // -> 'path/to'
+  ///     context.dirname('path/to');          // -> 'path'
+  ///
+  /// Trailing separators are ignored.
+  ///
+  ///     context.dirname('path/to/'); // -> 'path'
+  String dirname(String path) {
+    var parsed = _parse(path);
+    parsed.removeTrailingSeparators();
+    if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root;
+    if (parsed.parts.length == 1) {
+      return parsed.root == null ? '.' : parsed.root;
+    }
+    parsed.parts.removeLast();
+    parsed.separators.removeLast();
+    parsed.removeTrailingSeparators();
+    return parsed.toString();
+  }
+
+  /// Gets the file extension of [path]: the portion of [basename] from the last
+  /// `.` to the end (including the `.` itself).
+  ///
+  ///     context.extension('path/to/foo.dart'); // -> '.dart'
+  ///     context.extension('path/to/foo'); // -> ''
+  ///     context.extension('path.to/foo'); // -> ''
+  ///     context.extension('path/to/foo.dart.js'); // -> '.js'
+  ///
+  /// If the file name starts with a `.`, then it is not considered an
+  /// extension:
+  ///
+  ///     context.extension('~/.bashrc');    // -> ''
+  ///     context.extension('~/.notes.txt'); // -> '.txt'
+  String extension(String path) => _parse(path).extension;
+
+  // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
+  /// Returns the root of [path] if it's absolute, or an empty string if it's
+  /// relative.
+  ///
+  ///     // Unix
+  ///     context.rootPrefix('path/to/foo'); // -> ''
+  ///     context.rootPrefix('/path/to/foo'); // -> '/'
+  ///
+  ///     // Windows
+  ///     context.rootPrefix(r'path\to\foo'); // -> ''
+  ///     context.rootPrefix(r'C:\path\to\foo'); // -> r'C:\'
+  ///
+  ///     // URL
+  ///     context.rootPrefix('path/to/foo'); // -> ''
+  ///     context.rootPrefix('http://dartlang.org/path/to/foo');
+  ///       // -> 'http://dartlang.org'
+  String rootPrefix(String path) => path.substring(0, style.rootLength(path));
+
+  /// Returns `true` if [path] is an absolute path and `false` if it is a
+  /// relative path.
+  ///
+  /// On POSIX systems, absolute paths start with a `/` (forward slash). On
+  /// Windows, an absolute path starts with `\\`, or a drive letter followed by
+  /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and
+  /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`.
+  ///
+  /// URLs that start with `/` are known as "root-relative", since they're
+  /// relative to the root of the current URL. Since root-relative paths are
+  /// still absolute in every other sense, [isAbsolute] will return true for
+  /// them. They can be detected using [isRootRelative].
+  bool isAbsolute(String path) => style.rootLength(path) > 0;
+
+  /// Returns `true` if [path] is a relative path and `false` if it is absolute.
+  /// On POSIX systems, absolute paths start with a `/` (forward slash). On
+  /// Windows, an absolute path starts with `\\`, or a drive letter followed by
+  /// `:/` or `:\`.
+  bool isRelative(String path) => !this.isAbsolute(path);
+
+  /// Returns `true` if [path] is a root-relative path and `false` if it's not.
+  ///
+  /// URLs that start with `/` are known as "root-relative", since they're
+  /// relative to the root of the current URL. Since root-relative paths are
+  /// still absolute in every other sense, [isAbsolute] will return true for
+  /// them. They can be detected using [isRootRelative].
+  ///
+  /// No POSIX and Windows paths are root-relative.
+  bool isRootRelative(String path) => style.isRootRelative(path);
+
+  /// Joins the given path parts into a single path. Example:
+  ///
+  ///     context.join('path', 'to', 'foo'); // -> 'path/to/foo'
+  ///
+  /// If any part ends in a path separator, then a redundant separator will not
+  /// be added:
+  ///
+  ///     context.join('path/', 'to', 'foo'); // -> 'path/to/foo
+  ///
+  /// If a part is an absolute path, then anything before that will be ignored:
+  ///
+  ///     context.join('path', '/to', 'foo'); // -> '/to/foo'
+  ///
+  String join(String part1, [String part2, String part3, String part4,
+      String part5, String part6, String part7, String part8]) {
+    var parts = <String>[
+      part1,
+      part2,
+      part3,
+      part4,
+      part5,
+      part6,
+      part7,
+      part8
+    ];
+    _validateArgList("join", parts);
+    return joinAll(parts.where((part) => part != null));
+  }
+
+  /// Joins the given path parts into a single path. Example:
+  ///
+  ///     context.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo'
+  ///
+  /// If any part ends in a path separator, then a redundant separator will not
+  /// be added:
+  ///
+  ///     context.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo
+  ///
+  /// If a part is an absolute path, then anything before that will be ignored:
+  ///
+  ///     context.joinAll(['path', '/to', 'foo']); // -> '/to/foo'
+  ///
+  /// For a fixed number of parts, [join] is usually terser.
+  String joinAll(Iterable<String> parts) {
+    var buffer = new StringBuffer();
+    var needsSeparator = false;
+    var isAbsoluteAndNotRootRelative = false;
+
+    for (var part in parts.where((part) => part != '')) {
+      if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) {
+        // If the new part is root-relative, it preserves the previous root but
+        // replaces the path after it.
+        var parsed = _parse(part);
+        parsed.root = this.rootPrefix(buffer.toString());
+        if (style.needsSeparator(parsed.root)) {
+          parsed.separators[0] = style.separator;
+        }
+        buffer.clear();
+        buffer.write(parsed.toString());
+      } else if (this.isAbsolute(part)) {
+        isAbsoluteAndNotRootRelative = !this.isRootRelative(part);
+        // An absolute path discards everything before it.
+        buffer.clear();
+        buffer.write(part);
+      } else {
+        if (part.length > 0 && style.containsSeparator(part[0])) {
+          // The part starts with a separator, so we don't need to add one.
+        } else if (needsSeparator) {
+          buffer.write(separator);
+        }
+
+        buffer.write(part);
+      }
+
+      // Unless this part ends with a separator, we'll need to add one before
+      // the next part.
+      needsSeparator = style.needsSeparator(part);
+    }
+
+    return buffer.toString();
+  }
+
+  // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
+  /// Splits [path] into its components using the current platform's
+  /// [separator]. Example:
+  ///
+  ///     context.split('path/to/foo'); // -> ['path', 'to', 'foo']
+  ///
+  /// The path will *not* be normalized before splitting.
+  ///
+  ///     context.split('path/../foo'); // -> ['path', '..', 'foo']
+  ///
+  /// If [path] is absolute, the root directory will be the first element in the
+  /// array. Example:
+  ///
+  ///     // Unix
+  ///     context.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo']
+  ///
+  ///     // Windows
+  ///     context.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo']
+  List<String> split(String path) {
+    var parsed = _parse(path);
+    // Filter out empty parts that exist due to multiple separators in a row.
+    parsed.parts = parsed.parts.where((part) => !part.isEmpty).toList();
+    if (parsed.root != null) parsed.parts.insert(0, parsed.root);
+    return parsed.parts;
+  }
+
+  /// Normalizes [path], simplifying it by handling `..`, and `.`, and
+  /// removing redundant path separators whenever possible.
+  ///
+  ///     context.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
+  String normalize(String path) {
+    var parsed = _parse(path);
+    parsed.normalize();
+    return parsed.toString();
+  }
+
+  /// Attempts to convert [path] to an equivalent relative path relative to
+  /// [root].
+  ///
+  ///     var context = new Context(current: '/root/path');
+  ///     context.relative('/root/path/a/b.dart'); // -> 'a/b.dart'
+  ///     context.relative('/root/other.dart'); // -> '../other.dart'
+  ///
+  /// If the [from] argument is passed, [path] is made relative to that instead.
+  ///
+  ///     context.relative('/root/path/a/b.dart',
+  ///         from: '/root/path'); // -> 'a/b.dart'
+  ///     context.relative('/root/other.dart',
+  ///         from: '/root/path'); // -> '../other.dart'
+  ///
+  /// If [path] and/or [from] are relative paths, they are assumed to be
+  /// relative to [current].
+  ///
+  /// Since there is no relative path from one drive letter to another on
+  /// Windows, this will return an absolute path in that case.
+  ///
+  ///     context.relative(r'D:\other', from: r'C:\other'); // -> 'D:\other'
+  ///
+  /// This will also return an absolute path if an absolute [path] is passed to
+  /// a context with a relative path for [current].
+  ///
+  ///     var context = new Context(r'some/relative/path');
+  ///     context.relative(r'/absolute/path'); // -> '/absolute/path'
+  ///
+  /// If [root] is relative, it may be impossible to determine a path from
+  /// [from] to [path]. For example, if [root] and [path] are "." and [from] is
+  /// "/", no path can be determined. In this case, a [PathException] will be
+  /// thrown.
+  String relative(String path, {String from}) {
+    // Avoid calling [current] since it is slow and calling join() when
+    // [from] is absolute does nothing.
+    if (from == null) {
+      from = current;
+    } else if (this.isRelative(from) || this.isRootRelative(from)) {
+      from = this.join(current, from);
+    }
+
+    // We can't determine the path from a relative path to an absolute path.
+    if (this.isRelative(from) && this.isAbsolute(path)) {
+      return this.normalize(path);
+    }
+
+    // If the given path is relative, resolve it relative to the context's
+    // current directory.
+    if (this.isRelative(path) || this.isRootRelative(path)) {
+      path = this.absolute(path);
+    }
+
+    // If the path is still relative and `from` is absolute, we're unable to
+    // find a path from `from` to `path`.
+    if (this.isRelative(path) && this.isAbsolute(from)) {
+      throw new PathException('Unable to find a path to "$path" from "$from".');
+    }
+
+    var fromParsed = _parse(from)..normalize();
+    var pathParsed = _parse(path)..normalize();
+
+    if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '.') {
+      return pathParsed.toString();
+    }
+
+    // If the root prefixes don't match (for example, different drive letters
+    // on Windows), then there is no relative path, so just return the absolute
+    // one. In Windows, drive letters are case-insenstive and we allow
+    // calculation of relative paths, even if a path has not been normalized.
+    if (fromParsed.root != pathParsed.root &&
+        ((fromParsed.root == null || pathParsed.root == null) ||
+            fromParsed.root.toLowerCase().replaceAll('/', '\\') !=
+                pathParsed.root.toLowerCase().replaceAll('/', '\\'))) {
+      return pathParsed.toString();
+    }
+
+    // Strip off their common prefix.
+    while (fromParsed.parts.length > 0 &&
+        pathParsed.parts.length > 0 &&
+        fromParsed.parts[0] == pathParsed.parts[0]) {
+      fromParsed.parts.removeAt(0);
+      fromParsed.separators.removeAt(1);
+      pathParsed.parts.removeAt(0);
+      pathParsed.separators.removeAt(1);
+    }
+
+    // If there are any directories left in the from path, we need to walk up
+    // out of them. If a directory left in the from path is '..', it cannot
+    // be cancelled by adding a '..'.
+    if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') {
+      throw new PathException('Unable to find a path to "$path" from "$from".');
+    }
+    pathParsed.parts.insertAll(
+        0, new List.filled(fromParsed.parts.length, '..'));
+    pathParsed.separators[0] = '';
+    pathParsed.separators.insertAll(
+        1, new List.filled(fromParsed.parts.length, style.separator));
+
+    // Corner case: the paths completely collapsed.
+    if (pathParsed.parts.length == 0) return '.';
+
+    // Corner case: path was '.' and some '..' directories were added in front.
+    // Don't add a final '/.' in that case.
+    if (pathParsed.parts.length > 1 && pathParsed.parts.last == '.') {
+      pathParsed.parts.removeLast();
+      pathParsed.separators
+        ..removeLast()
+        ..removeLast()
+        ..add('');
+    }
+
+    // Make it relative.
+    pathParsed.root = '';
+    pathParsed.removeTrailingSeparators();
+
+    return pathParsed.toString();
+  }
+
+  /// Returns `true` if [child] is a path beneath `parent`, and `false`
+  /// otherwise.
+  ///
+  ///     path.isWithin('/root/path', '/root/path/a'); // -> true
+  ///     path.isWithin('/root/path', '/root/other'); // -> false
+  ///     path.isWithin('/root/path', '/root/path'); // -> false
+  bool isWithin(String parent, String child) {
+    var relative;
+    try {
+      relative = this.relative(child, from: parent);
+    } on PathException catch (_) {
+      // If no relative path from [parent] to [child] is found, [child]
+      // definitely isn't a child of [parent].
+      return false;
+    }
+
+    var parts = this.split(relative);
+    return this.isRelative(relative) &&
+        parts.first != '..' &&
+        parts.first != '.';
+  }
+
+  /// Removes a trailing extension from the last part of [path].
+  ///
+  ///     context.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
+  String withoutExtension(String path) {
+    var parsed = _parse(path);
+
+    for (var i = parsed.parts.length - 1; i >= 0; i--) {
+      if (!parsed.parts[i].isEmpty) {
+        parsed.parts[i] = parsed.basenameWithoutExtension;
+        break;
+      }
+    }
+
+    return parsed.toString();
+  }
+
+  /// Returns the path represented by [uri], which may be a [String] or a [Uri].
+  ///
+  /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL
+  /// style, this will just convert [uri] to a string.
+  ///
+  ///     // POSIX
+  ///     context.fromUri('file:///path/to/foo')
+  ///       // -> '/path/to/foo'
+  ///
+  ///     // Windows
+  ///     context.fromUri('file:///C:/path/to/foo')
+  ///       // -> r'C:\path\to\foo'
+  ///
+  ///     // URL
+  ///     context.fromUri('http://dartlang.org/path/to/foo')
+  ///       // -> 'http://dartlang.org/path/to/foo'
+  ///
+  /// If [uri] is relative, a relative path will be returned.
+  ///
+  ///     path.fromUri('path/to/foo'); // -> 'path/to/foo'
+  String fromUri(uri) {
+    if (uri is String) uri = Uri.parse(uri);
+    return style.pathFromUri(uri);
+  }
+
+  /// Returns the URI that represents [path].
+  ///
+  /// For POSIX and Windows styles, this will return a `file:` URI. For the URL
+  /// style, this will just convert [path] to a [Uri].
+  ///
+  ///     // POSIX
+  ///     context.toUri('/path/to/foo')
+  ///       // -> Uri.parse('file:///path/to/foo')
+  ///
+  ///     // Windows
+  ///     context.toUri(r'C:\path\to\foo')
+  ///       // -> Uri.parse('file:///C:/path/to/foo')
+  ///
+  ///     // URL
+  ///     context.toUri('http://dartlang.org/path/to/foo')
+  ///       // -> Uri.parse('http://dartlang.org/path/to/foo')
+  Uri toUri(String path) {
+    if (isRelative(path)) {
+      return style.relativePathToUri(path);
+    } else {
+      return style.absolutePathToUri(join(current, path));
+    }
+  }
+
+  /// Returns a terse, human-readable representation of [uri].
+  ///
+  /// [uri] can be a [String] or a [Uri]. If it can be made relative to the
+  /// current working directory, that's done. Otherwise, it's returned as-is.
+  /// This gracefully handles non-`file:` URIs for [Style.posix] and
+  /// [Style.windows].
+  ///
+  /// The returned value is meant for human consumption, and may be either URI-
+  /// or path-formatted.
+  ///
+  ///     // POSIX
+  ///     var context = new Context(current: '/root/path');
+  ///     context.prettyUri('file:///root/path/a/b.dart'); // -> 'a/b.dart'
+  ///     context.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org'
+  ///
+  ///     // Windows
+  ///     var context = new Context(current: r'C:\root\path');
+  ///     context.prettyUri('file:///C:/root/path/a/b.dart'); // -> r'a\b.dart'
+  ///     context.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org'
+  ///
+  ///     // URL
+  ///     var context = new Context(current: 'http://dartlang.org/root/path');
+  ///     context.prettyUri('http://dartlang.org/root/path/a/b.dart');
+  ///         // -> r'a/b.dart'
+  ///     context.prettyUri('file:///root/path'); // -> 'file:///root/path'
+  String prettyUri(uri) {
+    if (uri is String) uri = Uri.parse(uri);
+    if (uri.scheme == 'file' && style == Style.url) return uri.toString();
+    if (uri.scheme != 'file' && uri.scheme != '' && style != Style.url) {
+      return uri.toString();
+    }
+
+    var path = normalize(fromUri(uri));
+    var rel = relative(path);
+
+    // Only return a relative path if it's actually shorter than the absolute
+    // path. This avoids ugly things like long "../" chains to get to the root
+    // and then go back down.
+    return split(rel).length > split(path).length ? path : rel;
+  }
+
+  ParsedPath _parse(String path) => new ParsedPath.parse(path, style);
+}
+
+/// Validates that there are no non-null arguments following a null one and
+/// throws an appropriate [ArgumentError] on failure.
+_validateArgList(String method, List<String> args) {
+  for (var i = 1; i < args.length; i++) {
+    // Ignore nulls hanging off the end.
+    if (args[i] == null || args[i - 1] != null) continue;
+
+    var numArgs;
+    for (numArgs = args.length; numArgs >= 1; numArgs--) {
+      if (args[numArgs - 1] != null) break;
+    }
+
+    // Show the arguments.
+    var message = new StringBuffer();
+    message.write("$method(");
+    message.write(args
+        .take(numArgs)
+        .map((arg) => arg == null ? "null" : '"$arg"')
+        .join(", "));
+    message.write("): part ${i - 1} was null, but part $i was not.");
+    throw new ArgumentError(message.toString());
+  }
+}
diff --git a/path/lib/src/internal_style.dart b/path/lib/src/internal_style.dart
new file mode 100644
index 0000000..db2d346
--- /dev/null
+++ b/path/lib/src/internal_style.dart
@@ -0,0 +1,65 @@
+// 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 path.internal_style;
+
+import 'context.dart';
+import 'style.dart';
+
+/// The internal interface for the [Style] type.
+///
+/// Users should be able to pass around instances of [Style] like an enum, but
+/// the members that [Context] uses should be hidden from them. Those members
+/// are defined on this class instead.
+abstract class InternalStyle extends Style {
+  /// The default path separator for this style.
+  ///
+  /// On POSIX, this is `/`. On Windows, it's `\`.
+  String get separator;
+
+  /// Returns whether [path] contains a separator.
+  bool containsSeparator(String path);
+
+  /// Returns whether [codeUnit] is the character code of a separator.
+  bool isSeparator(int codeUnit);
+
+  /// Returns whether this path component needs a separator after it.
+  ///
+  /// Windows and POSIX styles just need separators when the previous component
+  /// doesn't already end in a separator, but the URL always needs to place a
+  /// separator between the root and the first component, even if the root
+  /// already ends in a separator character. For example, to join "file://" and
+  /// "usr", an additional "/" is needed (making "file:///usr").
+  bool needsSeparator(String path);
+
+  /// Returns the number of characters of the root part.
+  ///
+  /// Returns 0 if the path is relative.
+  ///
+  /// If the path is root-relative, the root length is 1.
+  int rootLength(String path);
+
+  /// Gets the root prefix of [path] if path is absolute. If [path] is relative,
+  /// returns `null`.
+  String getRoot(String path) {
+    var length = rootLength(path);
+    if (length > 0) return path.substring(0, length);
+    return isRootRelative(path) ? path[0] : null;
+  }
+
+  /// Returns whether [path] is root-relative.
+  ///
+  /// If [path] is relative or absolute and not root-relative, returns `false`.
+  bool isRootRelative(String path);
+
+  /// Returns the path represented by [uri] in this style.
+  String pathFromUri(Uri uri);
+
+  /// Returns the URI that represents the relative path made of [parts].
+  Uri relativePathToUri(String path) =>
+      new Uri(pathSegments: context.split(path));
+
+  /// Returns the URI that represents [path], which is assumed to be absolute.
+  Uri absolutePathToUri(String path);
+}
diff --git a/path/lib/src/parsed_path.dart b/path/lib/src/parsed_path.dart
new file mode 100644
index 0000000..d37e2d3
--- /dev/null
+++ b/path/lib/src/parsed_path.dart
@@ -0,0 +1,183 @@
+// Copyright (c) 2013, 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 path.parsed_path;
+
+import 'internal_style.dart';
+import 'style.dart';
+
+class ParsedPath {
+  /// The [InternalStyle] that was used to parse this path.
+  InternalStyle style;
+
+  /// The absolute root portion of the path, or `null` if the path is relative.
+  /// On POSIX systems, this will be `null` or "/". On Windows, it can be
+  /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive
+  /// letters.
+  String root;
+
+  /// Whether this path is root-relative.
+  ///
+  /// See [Context.isRootRelative].
+  bool isRootRelative;
+
+  /// The path-separated parts of the path. All but the last will be
+  /// directories.
+  List<String> parts;
+
+  /// The path separators preceding each part.
+  ///
+  /// The first one will be an empty string unless the root requires a separator
+  /// between it and the path. The last one will be an empty string unless the
+  /// path ends with a trailing separator.
+  List<String> separators;
+
+  /// The file extension of the last non-empty part, or "" if it doesn't have
+  /// one.
+  String get extension => _splitExtension()[1];
+
+  /// `true` if this is an absolute path.
+  bool get isAbsolute => root != null;
+
+  factory ParsedPath.parse(String path, InternalStyle style) {
+    // Remove the root prefix, if any.
+    var root = style.getRoot(path);
+    var isRootRelative = style.isRootRelative(path);
+    if (root != null) path = path.substring(root.length);
+
+    // Split the parts on path separators.
+    var parts = <String>[];
+    var separators = <String>[];
+
+    var start = 0;
+
+    if (path.isNotEmpty && style.isSeparator(path.codeUnitAt(0))) {
+      separators.add(path[0]);
+      start = 1;
+    } else {
+      separators.add('');
+    }
+
+    for (var i = start; i < path.length; i++) {
+      if (style.isSeparator(path.codeUnitAt(i))) {
+        parts.add(path.substring(start, i));
+        separators.add(path[i]);
+        start = i + 1;
+      }
+    }
+
+    // Add the final part, if any.
+    if (start < path.length) {
+      parts.add(path.substring(start));
+      separators.add('');
+    }
+
+    return new ParsedPath._(style, root, isRootRelative, parts, separators);
+  }
+
+  ParsedPath._(
+      this.style, this.root, this.isRootRelative, this.parts, this.separators);
+
+  String get basename {
+    var copy = this.clone();
+    copy.removeTrailingSeparators();
+    if (copy.parts.isEmpty) return root == null ? '' : root;
+    return copy.parts.last;
+  }
+
+  String get basenameWithoutExtension => _splitExtension()[0];
+
+  bool get hasTrailingSeparator =>
+      !parts.isEmpty && (parts.last == '' || separators.last != '');
+
+  void removeTrailingSeparators() {
+    while (!parts.isEmpty && parts.last == '') {
+      parts.removeLast();
+      separators.removeLast();
+    }
+    if (separators.length > 0) separators[separators.length - 1] = '';
+  }
+
+  void normalize() {
+    // Handle '.', '..', and empty parts.
+    var leadingDoubles = 0;
+    var newParts = <String>[];
+    for (var part in parts) {
+      if (part == '.' || part == '') {
+        // Do nothing. Ignore it.
+      } else if (part == '..') {
+        // Pop the last part off.
+        if (newParts.length > 0) {
+          newParts.removeLast();
+        } else {
+          // Backed out past the beginning, so preserve the "..".
+          leadingDoubles++;
+        }
+      } else {
+        newParts.add(part);
+      }
+    }
+
+    // A relative path can back out from the start directory.
+    if (!isAbsolute) {
+      newParts.insertAll(0, new List.filled(leadingDoubles, '..'));
+    }
+
+    // If we collapsed down to nothing, do ".".
+    if (newParts.length == 0 && !isAbsolute) {
+      newParts.add('.');
+    }
+
+    // Canonicalize separators.
+    var newSeparators = new List<String>.generate(
+        newParts.length, (_) => style.separator, growable: true);
+    newSeparators.insert(0, isAbsolute &&
+        newParts.length > 0 &&
+        style.needsSeparator(root) ? style.separator : '');
+
+    parts = newParts;
+    separators = newSeparators;
+
+    // Normalize the Windows root if needed.
+    if (root != null && style == Style.windows) {
+      root = root.replaceAll('/', '\\');
+    }
+    removeTrailingSeparators();
+  }
+
+  String toString() {
+    var builder = new StringBuffer();
+    if (root != null) builder.write(root);
+    for (var i = 0; i < parts.length; i++) {
+      builder.write(separators[i]);
+      builder.write(parts[i]);
+    }
+    builder.write(separators.last);
+
+    return builder.toString();
+  }
+
+  /// Splits the last non-empty part of the path into a `[basename, extension`]
+  /// pair.
+  ///
+  /// Returns a two-element list. The first is the name of the file without any
+  /// extension. The second is the extension or "" if it has none.
+  List<String> _splitExtension() {
+    var file = parts.lastWhere((p) => p != '', orElse: () => null);
+
+    if (file == null) return ['', ''];
+    if (file == '..') return ['..', ''];
+
+    var lastDot = file.lastIndexOf('.');
+
+    // If there is no dot, or it's the first character, like '.bashrc', it
+    // doesn't count.
+    if (lastDot <= 0) return [file, ''];
+
+    return [file.substring(0, lastDot), file.substring(lastDot)];
+  }
+
+  ParsedPath clone() => new ParsedPath._(style, root, isRootRelative,
+      new List.from(parts), new List.from(separators));
+}
diff --git a/path/lib/src/path_exception.dart b/path/lib/src/path_exception.dart
new file mode 100644
index 0000000..49bd268
--- /dev/null
+++ b/path/lib/src/path_exception.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2013, 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 path.path_exception;
+
+/// An exception class that's thrown when a path operation is unable to be
+/// computed accurately.
+class PathException implements Exception {
+  String message;
+
+  PathException(this.message);
+
+  String toString() => "PathException: $message";
+}
diff --git a/path/lib/src/style.dart b/path/lib/src/style.dart
new file mode 100644
index 0000000..e0e0e01
--- /dev/null
+++ b/path/lib/src/style.dart
@@ -0,0 +1,88 @@
+// Copyright (c) 2013, 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 path.style;
+
+import 'context.dart';
+import 'style/posix.dart';
+import 'style/url.dart';
+import 'style/windows.dart';
+
+/// An enum type describing a "flavor" of path.
+abstract class Style {
+  /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths
+  /// start with "/". Used by UNIX, Linux, Mac OS X, and others.
+  static final Style posix = new PosixStyle();
+
+  /// Windows paths use "\" (backslash) as separators. Absolute paths start with
+  /// a drive letter followed by a colon (example, "C:") or two backslashes
+  /// ("\\") for UNC paths.
+  // TODO(rnystrom): The UNC root prefix should include the drive name too, not
+  // just the "\\".
+  static final Style windows = new WindowsStyle();
+
+  /// URLs aren't filesystem paths, but they're supported to make it easier to
+  /// manipulate URL paths in the browser.
+  ///
+  /// URLs use "/" (forward slash) as separators. Absolute paths either start
+  /// with a protocol and optional hostname (e.g. `http://dartlang.org`,
+  /// `file://`) or with "/".
+  static final Style url = new UrlStyle();
+
+  /// The style of the host platform.
+  ///
+  /// When running on the command line, this will be [windows] or [posix] based
+  /// on the host operating system. On a browser, this will be [url].
+  static final Style platform = _getPlatformStyle();
+
+  /// Gets the type of the host platform.
+  static Style _getPlatformStyle() {
+    // If we're running a Dart file in the browser from a `file:` URI,
+    // [Uri.base] will point to a file. If we're running on the standalone,
+    // it will point to a directory. We can use that fact to determine which
+    // style to use.
+    if (Uri.base.scheme != 'file') return Style.url;
+    if (!Uri.base.path.endsWith('/')) return Style.url;
+    if (new Uri(path: 'a/b').toFilePath() == 'a\\b') return Style.windows;
+    return Style.posix;
+  }
+
+  /// The name of this path style. Will be "posix" or "windows".
+  String get name;
+
+  /// A [Context] that uses this style.
+  Context get context => new Context(style: this);
+
+  @Deprecated("Most Style members will be removed in path 2.0.")
+  String get separator;
+
+  @Deprecated("Most Style members will be removed in path 2.0.")
+  Pattern get separatorPattern;
+
+  @Deprecated("Most Style members will be removed in path 2.0.")
+  Pattern get needsSeparatorPattern;
+
+  @Deprecated("Most Style members will be removed in path 2.0.")
+  Pattern get rootPattern;
+
+  @Deprecated("Most Style members will be removed in path 2.0.")
+  Pattern get relativeRootPattern;
+
+  @Deprecated("Most style members will be removed in path 2.0.")
+  String getRoot(String path);
+
+  @Deprecated("Most style members will be removed in path 2.0.")
+  String getRelativeRoot(String path);
+
+  @Deprecated("Most style members will be removed in path 2.0.")
+  String pathFromUri(Uri uri);
+
+  @Deprecated("Most style members will be removed in path 2.0.")
+  Uri relativePathToUri(String path);
+
+  @Deprecated("Most style members will be removed in path 2.0.")
+  Uri absolutePathToUri(String path);
+
+  String toString() => name;
+}
diff --git a/path/lib/src/style/posix.dart b/path/lib/src/style/posix.dart
new file mode 100644
index 0000000..74aeb4c
--- /dev/null
+++ b/path/lib/src/style/posix.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2013, 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 path.style.posix;
+
+import '../characters.dart' as chars;
+import '../parsed_path.dart';
+import '../internal_style.dart';
+
+/// The style for POSIX paths.
+class PosixStyle extends InternalStyle {
+  PosixStyle();
+
+  final name = 'posix';
+  final separator = '/';
+  final separators = const ['/'];
+
+  // Deprecated properties.
+
+  final separatorPattern = new RegExp(r'/');
+  final needsSeparatorPattern = new RegExp(r'[^/]$');
+  final rootPattern = new RegExp(r'^/');
+  final relativeRootPattern = null;
+
+  bool containsSeparator(String path) => path.contains('/');
+
+  bool isSeparator(int codeUnit) => codeUnit == chars.SLASH;
+
+  bool needsSeparator(String path) =>
+      path.isNotEmpty && !isSeparator(path.codeUnitAt(path.length - 1));
+
+  int rootLength(String path) {
+    if (path.isNotEmpty && isSeparator(path.codeUnitAt(0))) return 1;
+    return 0;
+  }
+
+  bool isRootRelative(String path) => false;
+
+  String getRelativeRoot(String path) => null;
+
+  String pathFromUri(Uri uri) {
+    if (uri.scheme == '' || uri.scheme == 'file') {
+      return Uri.decodeComponent(uri.path);
+    }
+    throw new ArgumentError("Uri $uri must have scheme 'file:'.");
+  }
+
+  Uri absolutePathToUri(String path) {
+    var parsed = new ParsedPath.parse(path, this);
+    if (parsed.parts.isEmpty) {
+      // If the path is a bare root (e.g. "/"), [components] will
+      // currently be empty. We add two empty components so the URL constructor
+      // produces "file:///", with a trailing slash.
+      parsed.parts.addAll(["", ""]);
+    } else if (parsed.hasTrailingSeparator) {
+      // If the path has a trailing slash, add a single empty component so the
+      // URI has a trailing slash as well.
+      parsed.parts.add("");
+    }
+
+    return new Uri(scheme: 'file', pathSegments: parsed.parts);
+  }
+}
diff --git a/path/lib/src/style/url.dart b/path/lib/src/style/url.dart
new file mode 100644
index 0000000..255f22a
--- /dev/null
+++ b/path/lib/src/style/url.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2013, 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 path.style.url;
+
+import '../characters.dart' as chars;
+import '../internal_style.dart';
+
+/// The style for URL paths.
+class UrlStyle extends InternalStyle {
+  UrlStyle();
+
+  final name = 'url';
+  final separator = '/';
+  final separators = const ['/'];
+
+  // Deprecated properties.
+
+  final separatorPattern = new RegExp(r'/');
+  final needsSeparatorPattern =
+      new RegExp(r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$");
+  final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*");
+  final relativeRootPattern = new RegExp(r"^/");
+
+  bool containsSeparator(String path) => path.contains('/');
+
+  bool isSeparator(int codeUnit) => codeUnit == chars.SLASH;
+
+  bool needsSeparator(String path) {
+    if (path.isEmpty) return false;
+
+    // A URL that doesn't end in "/" always needs a separator.
+    if (!isSeparator(path.codeUnitAt(path.length - 1))) return true;
+
+    // A URI that's just "scheme://" needs an extra separator, despite ending
+    // with "/".
+    return path.endsWith("://") && rootLength(path) == path.length;
+  }
+
+  int rootLength(String path) {
+    if (path.isEmpty) return 0;
+    if (isSeparator(path.codeUnitAt(0))) return 1;
+    var index = path.indexOf("/");
+    if (index > 0 && path.startsWith('://', index - 1)) {
+      // The root part is up until the next '/', or the full path. Skip
+      // '://' and search for '/' after that.
+      index = path.indexOf('/', index + 2);
+      if (index > 0) return index;
+      return path.length;
+    }
+    return 0;
+  }
+
+  bool isRootRelative(String path) =>
+      path.isNotEmpty && isSeparator(path.codeUnitAt(0));
+
+  String getRelativeRoot(String path) => isRootRelative(path) ? '/' : null;
+
+  String pathFromUri(Uri uri) => uri.toString();
+
+  Uri relativePathToUri(String path) => Uri.parse(path);
+  Uri absolutePathToUri(String path) => Uri.parse(path);
+}
diff --git a/path/lib/src/style/windows.dart b/path/lib/src/style/windows.dart
new file mode 100644
index 0000000..f38efa5
--- /dev/null
+++ b/path/lib/src/style/windows.dart
@@ -0,0 +1,125 @@
+// Copyright (c) 2013, 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 path.style.windows;
+
+import '../characters.dart' as chars;
+import '../internal_style.dart';
+import '../parsed_path.dart';
+import '../utils.dart';
+
+/// The style for Windows paths.
+class WindowsStyle extends InternalStyle {
+  WindowsStyle();
+
+  final name = 'windows';
+  final separator = '\\';
+  final separators = const ['/', '\\'];
+
+  // Deprecated properties.
+
+  final separatorPattern = new RegExp(r'[/\\]');
+  final needsSeparatorPattern = new RegExp(r'[^/\\]$');
+  final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])');
+  final relativeRootPattern = new RegExp(r"^[/\\](?![/\\])");
+
+  bool containsSeparator(String path) => path.contains('/');
+
+  bool isSeparator(int codeUnit) =>
+      codeUnit == chars.SLASH || codeUnit == chars.BACKSLASH;
+
+  bool needsSeparator(String path) {
+    if (path.isEmpty) return false;
+    return !isSeparator(path.codeUnitAt(path.length - 1));
+  }
+
+  int rootLength(String path) {
+    if (path.isEmpty) return 0;
+    if (path.codeUnitAt(0) == chars.SLASH) return 1;
+    if (path.codeUnitAt(0) == chars.BACKSLASH) {
+      if (path.length < 2 || path.codeUnitAt(1) != chars.BACKSLASH) return 1;
+      // The path is a network share. Search for up to two '\'s, as they are
+      // the server and share - and part of the root part.
+      var index = path.indexOf('\\', 2);
+      if (index > 0) {
+        index = path.indexOf('\\', index + 1);
+        if (index > 0) return index;
+      }
+      return path.length;
+    }
+    // If the path is of the form 'C:/' or 'C:\', with C being any letter, it's
+    // a root part.
+    if (path.length < 3) return 0;
+    // Check for the letter.
+    if (!isAlphabetic(path.codeUnitAt(0))) return 0;
+    // Check for the ':'.
+    if (path.codeUnitAt(1) != chars.COLON) return 0;
+    // Check for either '/' or '\'.
+    if (!isSeparator(path.codeUnitAt(2))) return 0;
+    return 3;
+  }
+
+  bool isRootRelative(String path) => rootLength(path) == 1;
+
+  String getRelativeRoot(String path) {
+    var length = rootLength(path);
+    if (length == 1) return path[0];
+    return null;
+  }
+
+  String pathFromUri(Uri uri) {
+    if (uri.scheme != '' && uri.scheme != 'file') {
+      throw new ArgumentError("Uri $uri must have scheme 'file:'.");
+    }
+
+    var path = uri.path;
+    if (uri.host == '') {
+      // Drive-letter paths look like "file:///C:/path/to/file". The
+      // replaceFirst removes the extra initial slash.
+      if (path.startsWith('/')) path = path.replaceFirst("/", "");
+    } else {
+      // Network paths look like "file://hostname/path/to/file".
+      path = '\\\\${uri.host}$path';
+    }
+    return Uri.decodeComponent(path.replaceAll("/", "\\"));
+  }
+
+  Uri absolutePathToUri(String path) {
+    var parsed = new ParsedPath.parse(path, this);
+    if (parsed.root.startsWith(r'\\')) {
+      // Network paths become "file://server/share/path/to/file".
+
+      // The root is of the form "\\server\share". We want "server" to be the
+      // URI host, and "share" to be the first element of the path.
+      var rootParts = parsed.root.split('\\').where((part) => part != '');
+      parsed.parts.insert(0, rootParts.last);
+
+      if (parsed.hasTrailingSeparator) {
+        // If the path has a trailing slash, add a single empty component so the
+        // URI has a trailing slash as well.
+        parsed.parts.add("");
+      }
+
+      return new Uri(
+          scheme: 'file', host: rootParts.first, pathSegments: parsed.parts);
+    } else {
+      // Drive-letter paths become "file:///C:/path/to/file".
+
+      // If the path is a bare root (e.g. "C:\"), [parsed.parts] will currently
+      // be empty. We add an empty component so the URL constructor produces
+      // "file:///C:/", with a trailing slash. We also add an empty component if
+      // the URL otherwise has a trailing slash.
+      if (parsed.parts.length == 0 || parsed.hasTrailingSeparator) {
+        parsed.parts.add("");
+      }
+
+      // Get rid of the trailing "\" in "C:\" because the URI constructor will
+      // add a separator on its own.
+      parsed.parts.insert(
+          0, parsed.root.replaceAll("/", "").replaceAll("\\", ""));
+
+      return new Uri(scheme: 'file', pathSegments: parsed.parts);
+    }
+  }
+}
diff --git a/path/lib/src/utils.dart b/path/lib/src/utils.dart
new file mode 100644
index 0000000..e320749
--- /dev/null
+++ b/path/lib/src/utils.dart
@@ -0,0 +1,16 @@
+// 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 path.utils;
+
+import 'characters.dart' as chars;
+
+/// Returns whether [char] is the code for an ASCII letter (uppercase or
+/// lowercase).
+bool isAlphabetic(int char) =>
+    (char >= chars.UPPER_A && char <= chars.UPPER_Z) ||
+        (char >= chars.LOWER_A && char <= chars.LOWER_Z);
+
+/// Returns whether [char] is the code for an ASCII digit.
+bool isNumeric(int char) => char >= chars.ZERO && char <= chars.NINE;
diff --git a/path/pubspec.yaml b/path/pubspec.yaml
new file mode 100644
index 0000000..835129a
--- /dev/null
+++ b/path/pubspec.yaml
@@ -0,0 +1,12 @@
+name: path
+version: 1.3.5
+author: Dart Team <misc@dartlang.org>
+description: >
+ A string-based path manipulation library. All of the path operations you know
+ and love, with solid support on both Windows and POSIX (Linux and Mac OS X)
+ machines.
+homepage: http://github.com/dart-lang/path
+dev_dependencies:
+  unittest: ">=0.9.0 <0.12.0"
+environment:
+  sdk: ">=1.0.0 <2.0.0"
diff --git a/petitparser/lib/beta.dart b/petitparser/lib/beta.dart
new file mode 100644
index 0000000..2f2ea1a
--- /dev/null
+++ b/petitparser/lib/beta.dart
@@ -0,0 +1,68 @@
+/**
+ * This package contains a experimental features of PetitParser. The code here
+ * might be removed or changed in incompatible ways without keeping backward
+ * compatibility.
+ */
+library beta;
+
+import 'dart:mirrors';
+import 'package:petitparser/petitparser.dart';
+
+/**
+ * Experimental helper to compose complex grammars from various primitive
+ * parsers using variable references.
+ *
+ * The difference of this implementation to [CompositeParser] is that
+ * subclasses can define and refer to productions using variables. The
+ * variables themselves are not actually implement anywhere, but their
+ * behavior is defined in [noSuchMethod] and mapped to a collection using
+ * the methods defined in the superclass. To avoid excessive warnings in
+ * the editor, consider adding the [proxy] annotation to subclasses.
+ *
+ * Consider the following example to parse a list of numbers:
+ *
+ *     @proxy
+ *     class NumberListGrammar2 extends CompositeParser2 {
+ *       void initialize() {
+ *         start = list.end();
+ *         list = element.separatedBy(char(','), includeSeparators: false));
+ *         element = digit().plus().flatten();
+ *       }
+ *     }
+ *
+ * Production actions can be attached in subclasses by calling the production,
+ * as in the following example:
+ *
+ *     @proxy
+ *     class NumberListParser2 extends NumberListGrammar2 {
+ *       void initialize() {
+ *         element((value) => int.parse(value));
+ *       }
+ *     }
+ *
+ * Cravats: Pay attention with production names that conflict with methods
+ * defined in superclasses. The generated JavaScript code is slightly bigger,
+ * due to the use of [noSuchMethod]. However, the resulting parser is identical.
+ */
+abstract class CompositeParser2 extends CompositeParser {
+  @override
+  dynamic noSuchMethod(Invocation mirror) {
+    String name = MirrorSystem.getName(mirror.memberName);
+    if (!name.startsWith('_')) {
+      if (mirror.isGetter) {
+        return ref(name);
+      } else if (mirror.isSetter) {
+        return def(name.substring(0, name.length - 1),
+            mirror.positionalArguments.first);
+      } else if (mirror.isMethod && mirror.positionalArguments.length == 1) {
+        var argument = mirror.positionalArguments.first;
+        if (argument is Parser) {
+          redef(name, argument);
+        } else {
+          action(name, argument);
+        }
+      }
+    }
+    return super.noSuchMethod(mirror);
+  }
+}
diff --git a/petitparser/lib/dart.dart b/petitparser/lib/dart.dart
new file mode 100644
index 0000000..954d467
--- /dev/null
+++ b/petitparser/lib/dart.dart
@@ -0,0 +1,11 @@
+/**
+ * This package contains a complete grammar of the Dart programming language.
+ *
+ * The grammar specification was kindly provided by [Gilad Bracha]
+ * (https://github.com/gbracha/dart-executable-grammars/blob/master/dart_runnable_grammar.dart).
+ */
+library dart;
+
+import 'package:petitparser/petitparser.dart';
+
+part 'src/dart/grammar.dart';
diff --git a/petitparser/lib/debug.dart b/petitparser/lib/debug.dart
new file mode 100644
index 0000000..29fe557
--- /dev/null
+++ b/petitparser/lib/debug.dart
@@ -0,0 +1,22 @@
+/**
+ * This package contains some simple debugging tools.
+ */
+library debug;
+
+import 'package:petitparser/petitparser.dart';
+import 'package:petitparser/reflection.dart';
+
+part 'src/debug/continuation.dart';
+part 'src/debug/profile.dart';
+part 'src/debug/progress.dart';
+part 'src/debug/trace.dart';
+
+typedef void OutputHandler(Object object);
+
+String _repeat(int count, String value) {
+  var result = new StringBuffer();
+  for (var i = 0; i < count; i++) {
+    result.write(value);
+  }
+  return result.toString();
+}
diff --git a/petitparser/lib/json.dart b/petitparser/lib/json.dart
new file mode 100644
index 0000000..a6ffb17
--- /dev/null
+++ b/petitparser/lib/json.dart
@@ -0,0 +1,20 @@
+/**
+ * This package contains a complete implementation of [JSON](http://json.org/).
+ *
+ * [JsonParser] creates a nested Dart objects from a given JSON string. For
+ * example the following code prints `{a: 1, b: [2, 3.4], c: false}`:
+ *
+ *     var json = new JsonParser();
+ *     var result = json.parse('{"a": 1, "b": [2, 3.4], "c": false}');
+ *     print(result.value);  // {a: 1, b: [2, 3.4], c: false}
+ *
+ * The grammar definition [JsonGrammar] can be subclassed to construct other
+ * objects.
+ */
+library json;
+
+import 'dart:collection';
+import 'package:petitparser/petitparser.dart';
+
+part 'src/json/grammar.dart';
+part 'src/json/parser.dart';
diff --git a/petitparser/lib/lisp.dart b/petitparser/lib/lisp.dart
new file mode 100644
index 0000000..4bcd5a3
--- /dev/null
+++ b/petitparser/lib/lisp.dart
@@ -0,0 +1,60 @@
+/**
+ * This package contains a simple grammar and evaluator for LISP.
+ *
+ * The code is reasonably complete to run and evaluate reasonably complex
+ * programs from the console and from the web browser.
+ */
+library lisp;
+
+import 'dart:collection';
+import 'package:petitparser/petitparser.dart';
+
+part 'src/lisp/cons.dart';
+part 'src/lisp/environment.dart';
+part 'src/lisp/grammar.dart';
+part 'src/lisp/name.dart';
+part 'src/lisp/natives.dart';
+part 'src/lisp/parser.dart';
+part 'src/lisp/standard.dart';
+
+/** The standard lisp parser definition. */
+final lispParser = new LispParser();
+
+/** The evaluation function. */
+eval(Environment env, expr) {
+  if (expr is Cons) {
+    return eval(env, expr.head)(env, expr.tail);
+  } else if (expr is Name) {
+    return env[expr];
+  } else {
+    return expr;
+  }
+}
+
+/** Evaluate a cons of instructions. */
+evalList(Environment env, expr) {
+  var result = null;
+  while (expr is Cons) {
+    result = eval(env, expr.head);
+    expr = expr.tail;
+  }
+  return result;
+}
+
+/** The arguments evaluation function. */
+evalArguments(Environment env, args) {
+  if (args is Cons) {
+    return new Cons(eval(env, args.head), evalArguments(env, args.tail));
+  } else {
+    return null;
+  }
+}
+
+/** Reads and evaluates a [script]. */
+evalString(Parser parser, Environment env, String script) {
+  var result = null;
+  for (var cell in parser.parse(script).value) {
+    result = eval(env, cell);
+  }
+  return result;
+}
diff --git a/petitparser/lib/petitparser.dart b/petitparser/lib/petitparser.dart
new file mode 100644
index 0000000..739918c
--- /dev/null
+++ b/petitparser/lib/petitparser.dart
@@ -0,0 +1,168 @@
+/**
+ * This package contains the core library of PetitParser, a dynamic parser
+ * combinator framework.
+ *
+ * # Writing a Simple Grammar
+ *
+ * Writing grammars with PetitParser is simple as writing Dart code. For
+ * example, to write a grammar that can parse identifiers that start with
+ * a letter followed by zero or more letter or digits is defined as follows:
+ *
+ *     Parser id = letter().seq(letter().or(digit()).star());
+ *
+ * If you look at the object `id` in the debugger, you'll notice that the
+ * code above builds a tree of parser objects:
+ *
+ * - Sequence: This parser accepts a sequence of parsers.
+ * - - Predicate: This parser accepts a single letter.
+ * - - Repeater: This parser accepts zero or more times another parser.
+ * - - - Choice: This parser accepts a single word character.
+ * - - - - Predicate: This parser accepts a single letter.
+ * - - - - Predicate: This parser accepts a single digit.
+ *
+ * # Parsing Some Input
+ *
+ * To actually parse a [String] (or [List]) we can use the method
+ * [Parser.parse]:
+ *
+ *     Result id1 = id.parse('yeah');
+ *     Result id2 = id.parse('f12');
+ *
+ * The method [Parser.parse] returns a parse [Result], which is either an
+ * instance of [Success] or [Failure]. In both examples above we are
+ * successful and can retrieve the parse result using [Success.value]:
+ *
+ *     print(id1.value);                   // ['y', ['e', 'a', 'h']]
+ *     print(id2.value);                   // ['f', ['1', '2']]
+ *
+ * While it seems odd to get these nested arrays with characters as a return
+ * value, this is the default decomposition of the input into a parse tree.
+ * We'll see in a while how that can be customized.
+ *
+ * If we try to parse something invalid we get an instance of [Failure] as
+ * an answer and we can retrieve a descriptive error message using
+ * [Failure.message]:
+ *
+ *     Result id3 = id.parse('123');
+ *     print(id3.message);                 // 'letter expected'
+ *     print(id3.position);                // 0
+ *
+ * Trying to retrieve the parse result by calling [Failure.value] would throw
+ * the exception [UnsupportedError]. [Context.isSuccess] and [Context.isFailure]
+ * can be used to decide if the parse was successful.
+ *
+ * If you are only interested if a given string matches or not you can use the
+ * helper method [Parser.accept]:
+ *
+ *     print(id.accept('foo'));            // true
+ *     print(id.accept('123'));            // false
+ *
+ * # Different Kinds of Parsers
+ *
+ * PetitParser provide a large set of ready-made parser that you can compose
+ * to consume and transform arbitrarily complex languages. The terminal parsers
+ * are the most simple ones. We've already seen a few of those:
+ *
+ *   * `char('a')` parses the character *a*.
+ *   * `string('abc')` parses the string *abc*.
+ *   * `any()` parses any character.
+ *   * `digit()` parses any digit from *0* to *9*.
+ *   * `letter()` parses any letter from *a* to *z* and *A* to *Z*.
+ *   * `word()` parses any letter or digit.
+ *
+ * So instead of using the letter and digit predicate, we could have written
+ * our identifier parser like this:
+ *
+ *     var id = letter().seq(word().star());
+ *
+ * The next set of parsers are used to combine other parsers together:
+ *
+ *   * `p1.seq(p2)` and `p1 & p2` parse *p1* followed by *p2* (sequence).
+ *   * `p1.or(p2)` and `p1 | p2` parse *p1*, if that doesn't work parses *p2* (ordered choice).
+ *   * `p.star()` parses *p* zero or more times.
+ *   * `p.plus()` parses *p* one or more times.
+ *   * `p.optional()` parses *p*, if possible.
+ *   * `p.and()` parses *p*, but does not consume its input.
+ *   * `p.not()` parses *p* and succeed when p fails, but does not consume its input.
+ *   * `p.end()` parses *p* and succeed at the end of the input.
+ *
+ * To attach an action or transformation to a parser we can use the following
+ * methods:
+ *
+ *   * `p.map((value) => ...)` performs the transformation given the function.
+ *   * `p.pick(n)` returns the *n*-th element of the list *p* returns.
+ *   * `p.flatten()` creates a string from the result of *p*.
+ *   * `p.token()` creates a token from the result of *p*.
+ *   * `p.trim()` trims whitespaces before and after *p*.
+ *
+ * To return a string of the parsed identifier, we can modify our parser like
+ * this:
+ *
+ *     var id = letter().seq(word().star()).flatten();
+ *
+ * To conveniently find all matches in a given input string you can use
+ * [Parser.matchesSkipping]:
+ *
+ *     var matches = id.matchesSkipping('foo 123 bar4');
+ *     print(matches);                     // ['foo', 'bar4']
+ *
+ * These are the basic elements to build parsers. There are a few more well
+ * documented and tested factory methods in the [Parser] class. If you want
+ * browse their documentation and tests.
+ *
+ * # Writing a More Complicated Grammar
+ *
+ * Now we are able to write a more complicated grammar for evaluating simple
+ * arithmetic expressions. Within a file we start with the grammar for a
+ * number (actually an integer):
+ *
+ *     var number = digit().plus().flatten().trim().map(int.parse);
+ *
+ * Then we define the productions for addition and multiplication in order of
+ * precedence. Note that we instantiate the productions with undefined parsers
+ * upfront, because they recursively refer to each other. Later on we can
+ * resolve this recursion by setting their reference:
+ *
+ *     var term = undefined();
+ *     var prod = undefined();
+ *     var prim = undefined();
+ *
+ *     term.set(prod.seq(char('+').trim()).seq(term).map((values) {
+ *       return values[0] + values[2];
+ *     }).or(prod));
+ *     prod.set(prim.seq(char('*').trim()).seq(prod).map((values) {
+ *       return values[0] * values[2];
+ *     }).or(prim));
+ *     prim.set(char('(').trim().seq(term).seq(char(')'.trim())).map((values) {
+ *       return values[1];
+ *     }).or(number));
+ *
+ * To make sure that our parser consumes all input we wrap it with the `end()`
+ * parser into the start production:
+ *
+ *     var start = term.end();
+ *
+ * That's it, now we can test our parser and evaluator:
+ *
+ *     print(start.parse('1 + 2 * 3').value);        // 7
+ *     print(start.parse('(1 + 2) * 3').value);      // 9
+ *
+ * As an exercise we could extend the parser to also accept negative numbers
+ * and floating point numbers, not only integers. Furthermore it would be
+ * useful to support subtraction and division as well. All these features
+ * can be added with a few lines of PetitParser code.
+ */
+library petitparser;
+
+part 'src/core/actions.dart';
+part 'src/core/characters.dart';
+part 'src/core/combinators.dart';
+part 'src/core/composite.dart';
+part 'src/core/context.dart';
+part 'src/core/definition.dart';
+part 'src/core/expression.dart';
+part 'src/core/parser.dart';
+part 'src/core/parsers.dart';
+part 'src/core/predicates.dart';
+part 'src/core/repeaters.dart';
+part 'src/core/token.dart';
diff --git a/petitparser/lib/reflection.dart b/petitparser/lib/reflection.dart
new file mode 100644
index 0000000..b864685
--- /dev/null
+++ b/petitparser/lib/reflection.dart
@@ -0,0 +1,12 @@
+/**
+ * This package contains tools to reflect on and transform parsers.
+ */
+library reflection;
+
+import 'dart:collection';
+
+import 'package:petitparser/petitparser.dart';
+
+part 'src/reflection/iterable.dart';
+part 'src/reflection/optimize.dart';
+part 'src/reflection/transform.dart';
diff --git a/petitparser/lib/smalltalk.dart b/petitparser/lib/smalltalk.dart
new file mode 100644
index 0000000..3a8df38
--- /dev/null
+++ b/petitparser/lib/smalltalk.dart
@@ -0,0 +1,10 @@
+/**
+ * This package contains the complete grammar of Smalltalk.
+ *
+ * It was automatically exported from PetitParser for Smalltalk.
+ */
+library smalltalk;
+
+import 'package:petitparser/petitparser.dart';
+
+part 'src/smalltalk/grammar.dart';
diff --git a/petitparser/lib/src/core/actions.dart b/petitparser/lib/src/core/actions.dart
new file mode 100644
index 0000000..16736b5
--- /dev/null
+++ b/petitparser/lib/src/core/actions.dart
@@ -0,0 +1,121 @@
+part of petitparser;
+
+/**
+ * A parser that performs a transformation with a given function on the
+ * successful parse result of the delegate.
+ */
+class ActionParser extends DelegateParser {
+  final Function _function;
+
+  ActionParser(parser, this._function) : super(parser);
+
+  @override
+  Result parseOn(Context context) {
+    var result = _delegate.parseOn(context);
+    if (result.isSuccess) {
+      return result.success(_function(result.value));
+    } else {
+      return result;
+    }
+  }
+
+  @override
+  Parser copy() => new ActionParser(_delegate, _function);
+
+  @override
+  bool hasEqualProperties(Parser other) {
+    return other is ActionParser && super.hasEqualProperties(other)
+        && _function == other._function;
+  }
+}
+
+/**
+ * A parser that silently consumes input of another parser around
+ * its delegate.
+ */
+class TrimmingParser extends DelegateParser {
+  Parser _left;
+  Parser _right;
+
+  TrimmingParser(parser, this._left, this._right) : super(parser);
+
+  @override
+  Result parseOn(Context context) {
+    var current = context;
+    do {
+      current = _left.parseOn(current);
+    } while (current.isSuccess);
+    var result = _delegate.parseOn(current);
+    if (result.isFailure) {
+      return result;
+    }
+    current = result;
+    do {
+      current = _right.parseOn(current);
+    } while (current.isSuccess);
+    return current.success(result.value);
+  }
+
+  @override
+  Parser copy() => new TrimmingParser(_delegate, _left, _right);
+
+  @override
+  List<Parser> get children => [_delegate, _left, _right];
+
+  @override
+  void replace(Parser source, Parser target) {
+    super.replace(source, target);
+    if (_left == source) {
+      _left = target;
+    }
+    if (_right == source) {
+      _right = target;
+    }
+  }
+}
+
+/**
+ * A parser that answers a substring or sub-list of the range its delegate
+ * parses.
+ */
+class FlattenParser extends DelegateParser {
+  FlattenParser(parser) : super(parser);
+
+  @override
+  Result parseOn(Context context) {
+    var result = _delegate.parseOn(context);
+    if (result.isSuccess) {
+      var output = context.buffer is String
+          ? context.buffer.substring(context.position, result.position)
+          : context.buffer.sublist(context.position, result.position);
+      return result.success(output);
+    } else {
+      return result;
+    }
+  }
+
+  @override
+  Parser copy() => new FlattenParser(_delegate);
+}
+
+/**
+ * A parser that answers a token of the result its delegate parses.
+ */
+class TokenParser extends DelegateParser {
+  TokenParser(parser) : super(parser);
+
+  @override
+  Result parseOn(Context context) {
+    var result = _delegate.parseOn(context);
+    if (result.isSuccess) {
+      var token = new Token(
+          result.value, context.buffer, context.position, result.position);
+      return result.success(token);
+    } else {
+      return result;
+    }
+  }
+
+  @override
+  Parser copy() => new TokenParser(_delegate);
+}
diff --git a/petitparser/lib/src/core/characters.dart b/petitparser/lib/src/core/characters.dart
new file mode 100644
index 0000000..9e6172f
--- /dev/null
+++ b/petitparser/lib/src/core/characters.dart
@@ -0,0 +1,352 @@
+part of petitparser;
+
+/**
+ * Parser class for individual character classes.
+ */
+class CharacterParser extends Parser {
+  final CharacterPredicate _predicate;
+
+  final String _message;
+
+  CharacterParser(this._predicate, this._message);
+
+  @override
+  Result parseOn(Context context) {
+    var buffer = context.buffer;
+    var position = context.position;
+    if (position < buffer.length &&
+        _predicate.test(buffer.codeUnitAt(position))) {
+      return context.success(buffer[position], position + 1);
+    }
+    return context.failure(_message);
+  }
+
+  @override
+  String toString() => '${super.toString()}[$_message]';
+
+  @override
+  Parser copy() => new CharacterParser(_predicate, _message);
+
+  @override
+  bool hasEqualProperties(Parser other) {
+    return other is CharacterParser &&
+        super.hasEqualProperties(other) &&
+        _predicate == other._predicate &&
+        _message == other._message;
+  }
+}
+
+/**
+ * Abstract character predicate class.
+ */
+abstract class CharacterPredicate {
+
+  /**
+   * Tests if the character predicate is satisfied.
+   */
+  bool test(int value);
+}
+
+class _NotCharacterPredicate implements CharacterPredicate {
+  final CharacterPredicate predicate;
+
+  _NotCharacterPredicate(this.predicate);
+
+  @override
+  bool test(int value) => !predicate.test(value);
+}
+
+/**
+ * Returns a parser that accepts any of the specified characters.
+ */
+Parser anyOf(String string, [String message]) {
+  return new CharacterParser(_optimizedString(string),
+      message != null ? message : 'any of "$string" expected');
+}
+
+CharacterPredicate _optimizedString(String string) {
+  var ranges =
+      string.codeUnits.map((value) => new _RangeCharPredicate(value, value));
+  return _optimizedRanges(ranges);
+}
+
+CharacterPredicate _optimizedRanges(Iterable<_RangeCharPredicate> ranges) {
+
+  // 1. sort the ranges
+  var sortedRanges = new List.from(ranges, growable: false);
+  sortedRanges.sort((first, second) {
+    return first.start != second.start
+        ? first.start - second.start
+        : first.stop - second.stop;
+  });
+
+  // 2. merge adjacent or overlapping ranges
+  var mergedRanges = new List();
+  for (var thisRange in sortedRanges) {
+    if (mergedRanges.isEmpty) {
+      mergedRanges.add(thisRange);
+    } else {
+      var lastRange = mergedRanges.last;
+      if (lastRange.stop + 1 >= thisRange.start) {
+        var characterRange =
+            new _RangeCharPredicate(lastRange.start, thisRange.stop);
+        mergedRanges[mergedRanges.length - 1] = characterRange;
+      } else {
+        mergedRanges.add(thisRange);
+      }
+    }
+  }
+
+  // 3. build the best resulting predicates
+  if (mergedRanges.length == 1) {
+    return mergedRanges[0].start == mergedRanges[0].stop
+        ? new _SingleCharPredicate(mergedRanges[0].start)
+        : mergedRanges[0];
+  } else {
+    return new _RangesCharPredicate(mergedRanges.length,
+        mergedRanges.map((range) => range.start).toList(growable: false),
+        mergedRanges.map((range) => range.stop).toList(growable: false));
+  }
+}
+
+/**
+ * Returns a parser that accepts none of the specified characters.
+ */
+Parser noneOf(String string, [String message]) {
+  return new CharacterParser(
+      new _NotCharacterPredicate(_optimizedString(string)),
+      message != null ? message : 'none of "$string" expected');
+}
+
+/**
+ * Returns a parser that accepts a specific character only.
+ */
+Parser char(element, [String message]) {
+  return new CharacterParser(new _SingleCharPredicate(_toCharCode(element)),
+      message != null ? message : '"$element" expected');
+}
+
+class _SingleCharPredicate implements CharacterPredicate {
+  final int value;
+
+  const _SingleCharPredicate(this.value);
+
+  @override
+  bool test(int value) => identical(this.value, value);
+}
+
+/**
+ * Returns a parser that accepts any digit character.
+ */
+Parser digit([String message]) {
+  return new CharacterParser(
+      _digitCharPredicate, message != null ? message : 'digit expected');
+}
+
+class _DigitCharPredicate implements CharacterPredicate {
+  const _DigitCharPredicate();
+
+  @override
+  bool test(int value) => 48 <= value && value <= 57;
+}
+
+const _digitCharPredicate = const _DigitCharPredicate();
+
+/**
+ * Returns a parser that accepts any letter character.
+ */
+Parser letter([String message]) {
+  return new CharacterParser(
+      _letterCharPredicate, message != null ? message : 'letter expected');
+}
+
+class _LetterCharPredicate implements CharacterPredicate {
+  const _LetterCharPredicate();
+
+  @override
+  bool test(int value) =>
+      (65 <= value && value <= 90) || (97 <= value && value <= 122);
+}
+
+const _letterCharPredicate = const _LetterCharPredicate();
+
+/**
+ * Returns a parser that accepts any lowercase character.
+ */
+Parser lowercase([String message]) {
+  return new CharacterParser(_lowercaseCharPredicate,
+      message != null ? message : 'lowercase letter expected');
+}
+
+class _LowercaseCharPredicate implements CharacterPredicate {
+  const _LowercaseCharPredicate();
+
+  @override
+  bool test(int value) => 97 <= value && value <= 122;
+}
+
+const _lowercaseCharPredicate = const _LowercaseCharPredicate();
+
+/**
+ * Returns a parser that accepts the given character class pattern.
+ */
+Parser pattern(String element, [String message]) {
+  return new CharacterParser(_patternParser.parse(element).value,
+      message != null ? message : '[$element] expected');
+}
+
+Parser _createPatternParser() {
+  var single = any().map(
+      (each) => new _RangeCharPredicate(_toCharCode(each), _toCharCode(each)));
+  var multiple = any().seq(char('-')).seq(any()).map((each) =>
+      new _RangeCharPredicate(_toCharCode(each[0]), _toCharCode(each[2])));
+  var positive =
+      multiple.or(single).plus().map((each) => _optimizedRanges(each));
+  return char('^').optional().seq(positive).map((each) =>
+      each[0] == null ? each[1] : new _NotCharacterPredicate(each[1]));
+}
+
+final _patternParser = _createPatternParser();
+
+class _RangesCharPredicate implements CharacterPredicate {
+  final int length;
+  final List<int> starts;
+  final List<int> stops;
+
+  _RangesCharPredicate(this.length, this.starts, this.stops);
+
+  @override
+  bool test(int value) {
+    var min = 0;
+    var max = length;
+    while (min < max) {
+      var mid = min + ((max - min) >> 1);
+      var comp = starts[mid] - value;
+      if (comp == 0) {
+        return true;
+      } else if (comp < 0) {
+        min = mid + 1;
+      } else {
+        max = mid;
+      }
+    }
+    return 0 < min && value <= stops[min - 1];
+  }
+}
+
+/**
+ * Returns a parser that accepts any character in the range
+ * between [start] and [stop].
+ */
+Parser range(start, stop, [String message]) {
+  return new CharacterParser(
+      new _RangeCharPredicate(_toCharCode(start), _toCharCode(stop)),
+      message != null ? message : '$start..$stop expected');
+}
+
+class _RangeCharPredicate implements CharacterPredicate {
+  final int start;
+  final int stop;
+
+  _RangeCharPredicate(this.start, this.stop);
+
+  @override
+  bool test(int value) => start <= value && value <= stop;
+}
+
+/**
+ * Returns a parser that accepts any uppercase character.
+ */
+Parser uppercase([String message]) {
+  return new CharacterParser(_uppercaseCharPredicate,
+      message != null ? message : 'uppercase letter expected');
+}
+
+class _UppercaseCharPredicate implements CharacterPredicate {
+  const _UppercaseCharPredicate();
+
+  @override
+  bool test(int value) => 65 <= value && value <= 90;
+}
+
+const _uppercaseCharPredicate = const _UppercaseCharPredicate();
+
+/**
+ * Returns a parser that accepts any whitespace character.
+ */
+Parser whitespace([String message]) {
+  return new CharacterParser(_whitespaceCharPredicate,
+      message != null ? message : 'whitespace expected');
+}
+
+class _WhitespaceCharPredicate implements CharacterPredicate {
+  const _WhitespaceCharPredicate();
+
+  @override
+  bool test(int value) {
+    if (value < 256) {
+      return value == 0x09 ||
+          value == 0x0A ||
+          value == 0x0B ||
+          value == 0x0C ||
+          value == 0x0D ||
+          value == 0x20 ||
+          value == 0x85 ||
+          value == 0xA0;
+    } else {
+      return value == 0x1680 ||
+          value == 0x180E ||
+          value == 0x2000 ||
+          value == 0x2001 ||
+          value == 0x2002 ||
+          value == 0x2003 ||
+          value == 0x2004 ||
+          value == 0x2005 ||
+          value == 0x2006 ||
+          value == 0x2007 ||
+          value == 0x2008 ||
+          value == 0x2009 ||
+          value == 0x200A ||
+          value == 0x2028 ||
+          value == 0x2029 ||
+          value == 0x202F ||
+          value == 0x205F ||
+          value == 0x3000 ||
+          value == 0xFEFF;
+    }
+  }
+}
+
+const _whitespaceCharPredicate = const _WhitespaceCharPredicate();
+
+/**
+ * Returns a parser that accepts any word character.
+ */
+Parser word([String message]) {
+  return new CharacterParser(_wordCharPredicate,
+      message != null ? message : 'letter or digit expected');
+}
+
+class _WordCharPredicate implements CharacterPredicate {
+  const _WordCharPredicate();
+
+  @override
+  bool test(int value) => (65 <= value && value <= 90) ||
+      (97 <= value && value <= 122) ||
+      (48 <= value && value <= 57) ||
+      (value == 95);
+}
+
+const _wordCharPredicate = const _WordCharPredicate();
+
+// internal converter for character codes
+int _toCharCode(element) {
+  if (element is num) {
+    return element.round();
+  }
+  var value = element.toString();
+  if (value.length != 1) {
+    throw new ArgumentError('$value is not a character');
+  }
+  return value.codeUnitAt(0);
+}
diff --git a/petitparser/lib/src/core/combinators.dart b/petitparser/lib/src/core/combinators.dart
new file mode 100644
index 0000000..8e9b502
--- /dev/null
+++ b/petitparser/lib/src/core/combinators.dart
@@ -0,0 +1,232 @@
+part of petitparser;
+
+/**
+ * A parser that delegates to another one. Normally users do not need to
+ * directly use a delegate parser.
+ */
+class DelegateParser extends Parser {
+  Parser _delegate;
+
+  DelegateParser(this._delegate);
+
+  @override
+  Result parseOn(Context context) {
+    return _delegate.parseOn(context);
+  }
+
+  @override
+  List<Parser> get children => [_delegate];
+
+  @override
+  void replace(Parser source, Parser target) {
+    super.replace(source, target);
+    if (_delegate == source) {
+      _delegate = target;
+    }
+  }
+
+  @override
+  Parser copy() => new DelegateParser(_delegate);
+}
+
+/**
+ * A parser that succeeds only at the end of the input.
+ */
+class EndOfInputParser extends DelegateParser {
+  final String _message;
+
+  EndOfInputParser(parser, this._message) : super(parser);
+
+  @override
+  Result parseOn(Context context) {
+    var result = _delegate.parseOn(context);
+    if (result.isFailure || result.position == result.buffer.length) {
+      return result;
+    }
+    return result.failure(_message, result.position);
+  }
+
+  @override
+  String toString() => '${super.toString()}[$_message]';
+
+  @override
+  Parser copy() => new EndOfInputParser(_delegate, _message);
+
+  @override
+  bool hasEqualProperties(Parser other) {
+    return other is EndOfInputParser && super.hasEqualProperties(other)
+        && _message == other._message;
+  }
+}
+
+/**
+ * The and-predicate, a parser that succeeds whenever its delegate does, but
+ * does not consume the input stream [Parr 1994, 1995].
+ */
+class AndParser extends DelegateParser {
+  AndParser(parser) : super(parser);
+
+  @override
+  Result parseOn(Context context) {
+    var result = _delegate.parseOn(context);
+    if (result.isSuccess) {
+      return context.success(result.value);
+    } else {
+      return result;
+    }
+  }
+
+  @override
+  Parser copy() => new AndParser(_delegate);
+}
+
+/**
+ * The not-predicate, a parser that succeeds whenever its delegate does not,
+ * but consumes no input [Parr 1994, 1995].
+ */
+class NotParser extends DelegateParser {
+  final String _message;
+
+  NotParser(parser, this._message) : super(parser);
+
+  @override
+  Result parseOn(Context context) {
+    var result = _delegate.parseOn(context);
+    if (result.isFailure) {
+      return context.success(null);
+    } else {
+      return context.failure(_message);
+    }
+  }
+
+  @override
+  String toString() => '${super.toString()}[$_message]';
+
+  @override
+  Parser copy() => new NotParser(_delegate, _message);
+
+  @override
+  bool hasEqualProperties(Parser other) {
+    return other is NotParser && super.hasEqualProperties(other)
+        && _message == other._message;
+  }
+}
+
+/**
+ * A parser that optionally parsers its delegate, or answers nil.
+ */
+class OptionalParser extends DelegateParser {
+  final _otherwise;
+
+  OptionalParser(parser, this._otherwise) : super(parser);
+
+  @override
+  Result parseOn(Context context) {
+    var result = _delegate.parseOn(context);
+    if (result.isSuccess) {
+      return result;
+    } else {
+      return context.success(_otherwise);
+    }
+  }
+
+  @override
+  Parser copy() => new OptionalParser(_delegate, _otherwise);
+
+  @override
+  bool hasEqualProperties(Parser other) {
+    return other is OptionalParser && super.hasEqualProperties(other)
+        && _otherwise == other._otherwise;
+  }
+}
+
+/**
+ * Abstract parser that parses a list of things in some way.
+ */
+abstract class ListParser extends Parser {
+  final List<Parser> _parsers;
+
+  ListParser(this._parsers);
+
+  @override
+  List<Parser> get children => _parsers;
+
+  @override
+  void replace(Parser source, Parser target) {
+    super.replace(source, target);
+    for (var i = 0; i < _parsers.length; i++) {
+      if (_parsers[i] == source) {
+        _parsers[i] = target;
+      }
+    }
+  }
+}
+
+/**
+ * A parser that uses the first parser that succeeds.
+ */
+class ChoiceParser extends ListParser {
+  factory ChoiceParser(Iterable<Parser> parsers) {
+    return new ChoiceParser._(new List.from(parsers, growable: false));
+  }
+
+  ChoiceParser._(parsers) : super(parsers);
+
+  @override
+  Result parseOn(Context context) {
+    var result;
+    for (var i = 0; i < _parsers.length; i++) {
+      result = _parsers[i].parseOn(context);
+      if (result.isSuccess) {
+        return result;
+      }
+    }
+    return result;
+  }
+
+  @override
+  Parser or(Parser other) {
+    return new ChoiceParser(new List()
+      ..addAll(_parsers)
+      ..add(other));
+  }
+
+  @override
+  Parser copy() => new ChoiceParser(_parsers);
+}
+
+/**
+ * A parser that parses a sequence of parsers.
+ */
+class SequenceParser extends ListParser {
+  factory SequenceParser(Iterable<Parser> parsers) {
+    return new SequenceParser._(new List.from(parsers, growable: false));
+  }
+
+  SequenceParser._(parsers) : super(parsers);
+
+  @override
+  Result parseOn(Context context) {
+    var current = context;
+    var elements = new List(_parsers.length);
+    for (var i = 0; i < _parsers.length; i++) {
+      var result = _parsers[i].parseOn(current);
+      if (result.isFailure) {
+        return result;
+      }
+      elements[i] = result.value;
+      current = result;
+    }
+    return current.success(elements);
+  }
+
+  @override
+  Parser seq(Parser other) {
+    return new SequenceParser(new List()
+      ..addAll(_parsers)
+      ..add(other));
+  }
+
+  @override
+  Parser copy() => new SequenceParser(_parsers);
+}
diff --git a/petitparser/lib/src/core/composite.dart b/petitparser/lib/src/core/composite.dart
new file mode 100644
index 0000000..f28e2fa
--- /dev/null
+++ b/petitparser/lib/src/core/composite.dart
@@ -0,0 +1,186 @@
+part of petitparser;
+
+/**
+ * Helper to compose complex grammars from various primitive parsers.
+ *
+ * To create a new composite grammar subclass [CompositeParser]. Override
+ * the method [initialize] and for every production call [def] giving the
+ * production a name. The start production must be named 'start'. To refer
+ * to other productions (forward and backward) use [ref].
+ *
+ * Consider the following example to parse a list of numbers:
+ *
+ *     class NumberListGrammar extends CompositeParser {
+ *       void initialize() {
+ *         def('start', ref('list').end());
+ *         def('list', ref('element').separatedBy(char(','),
+ *           includeSeparators: false));
+ *         def('element', digit().plus().flatten());
+ *       }
+ *     }
+ *
+ * You might want to create future subclasses of your composite grammar
+ * to redefine the grammar or attach custom actions. In such a subclass
+ * override the method [initialize] again and call super. Then use
+ * [redef] to redefine an existing production, and [action] to attach an
+ * action to an existing production.
+ *
+ * Consider the following example that attaches a production action and
+ * converts the digits to actual numbers:
+ *
+ *     class NumberListParser extends NumberListGrammar {
+ *       void initialize() {
+ *         action('element', (value) => int.parse(value));
+ *       }
+ *     }
+ */
+@deprecated
+abstract class CompositeParser extends DelegateParser {
+  bool _completed = false;
+  final Map<String, Parser> _defined = new Map();
+  final Map<String, SettableParser> _undefined = new Map();
+
+  CompositeParser() : super(failure('Uninitalized production: start')) {
+    initialize();
+    _complete();
+  }
+
+  /**
+   * Initializes the composite grammar.
+   */
+  void initialize();
+
+  /**
+   * Internal method to complete the grammar.
+   */
+  void _complete() {
+    _delegate = ref('start');
+    _undefined.forEach((name, parser) {
+      if (!_defined.containsKey(name)) {
+        throw new UndefinedProductionError(name);
+      }
+      parser.set(_defined[name]);
+    });
+    _undefined.clear();
+    _completed = true;
+    _delegate = ref('start');
+  }
+
+  /**
+   * Returns a reference to a production with a [name].
+   *
+   * This method works during initialization and after completion of the
+   * initialization. During the initialization it returns delegate parsers
+   * that are eventually replaced by the real parsers. Afterwards it
+   * returns the defined parser (mostly useful for testing).
+   */
+  Parser ref(String name) {
+    if (_completed) {
+      if (_defined.containsKey(name)) {
+        return _defined[name];
+      } else {
+        throw new UndefinedProductionError(name);
+      }
+    } else {
+      return _undefined.putIfAbsent(name, () {
+        return failure('Uninitalized production: $name').settable();
+      });
+    }
+  }
+
+  /**
+   * Convenience operator returning a reference to a production with
+   * a [name]. See [CompositeParser.ref] for details.
+   */
+  Parser operator [](String name) => ref(name);
+
+  /**
+   * Defines a production with a [name] and a [parser]. Only call this method
+   * from [initialize].
+   *
+   * The following example defines a list production that consumes
+   * several elements separated by a comma.
+   *
+   *     def('list', ref('element').separatedBy(char(',')));
+   */
+  void def(String name, Parser parser) {
+    if (_completed) {
+      throw new CompletedParserError();
+    } else if (_defined.containsKey(name)) {
+      throw new RedefinedProductionError(name);
+    } else {
+      _defined[name] = parser;
+    }
+  }
+
+  /**
+   * Redefines an existing production with a [name] and a [replacement]
+   * parser or function producing a new parser. The code raises an
+   * [UndefinedProductionError] if [name] is an undefined production. Only call
+   * this method from [initialize].
+   *
+   * The following example redefines the previously defined list production
+   * by making it optional:
+   *
+   *     redef('list', (parser) => parser.optional());
+   */
+  void redef(String name, replacement) {
+    if (_completed) {
+      throw new CompletedParserError();
+    } else if (!_defined.containsKey(name)) {
+      throw new UndefinedProductionError(name);
+    } else {
+      _defined[name] =
+          replacement is Parser ? replacement : replacement(_defined[name]);
+    }
+  }
+
+  /**
+   * Attaches an action [function] to an existing production [name]. The code
+   * raises an [UndefinedProductionError] if [name] is an undefined production.
+   * Only call this method from [initialize].
+   *
+   * The following example attaches an action returning the size of list of
+   * the previously defined list production:
+   *
+   *     action('list', (list) => list.length);
+   */
+  void action(String name, Function function) {
+    redef(name, (parser) => parser.map(function));
+  }
+}
+
+/**
+ * Error raised when somebody tries to modify a [CompositeParser] outside
+ * the [CompositeParser.initialize] method.
+ */
+class CompletedParserError extends Error {
+  CompletedParserError();
+
+  @override
+  String toString() => 'Completed parser';
+}
+
+/**
+ * Error raised when an undefined production is accessed.
+ */
+class UndefinedProductionError extends Error {
+  final String name;
+
+  UndefinedProductionError(this.name);
+
+  @override
+  String toString() => 'Undefined production: $name';
+}
+
+/**
+ * Error raised when a production is accidentally redefined.
+ */
+class RedefinedProductionError extends Error {
+  final String name;
+
+  RedefinedProductionError(this.name);
+
+  @override
+  String toString() => 'Redefined production: $name';
+}
diff --git a/petitparser/lib/src/core/context.dart b/petitparser/lib/src/core/context.dart
new file mode 100644
index 0000000..eb8f342
--- /dev/null
+++ b/petitparser/lib/src/core/context.dart
@@ -0,0 +1,121 @@
+part of petitparser;
+
+/**
+ * An immutable parse context.
+ */
+class Context {
+  const Context(this.buffer, this.position);
+
+  /**
+   * The buffer we are working on.
+   */
+  final buffer;
+
+  /**
+   * The current position in the buffer.
+   */
+  final int position;
+
+  /**
+   * Returns a result indicating a parse success.
+   */
+  Result success(result, [int position]) {
+    return new Success(
+        buffer, position == null ? this.position : position, result);
+  }
+
+  /**
+   * Returns a result indicating a parse failure.
+   */
+  Result failure(String message, [int position]) {
+    return new Failure(
+        buffer, position == null ? this.position : position, message);
+  }
+
+  /**
+   * Returns a human readable string of the current context.
+   */
+  String toString() => 'Context[${toPositionString()}]';
+
+  /**
+   * Returns the line:column if the input is a string, otherwise the position.
+   */
+  String toPositionString() => Token.positionString(buffer, position);
+}
+
+/**
+ * An immutable parse result.
+ */
+abstract class Result extends Context {
+  const Result(buffer, position) : super(buffer, position);
+
+  /**
+   * Returns [true] if this result indicates a parse success.
+   */
+  bool get isSuccess => false;
+
+  /**
+   * Returns [true] if this result indicates a parse failure.
+   */
+  bool get isFailure => false;
+
+  /**
+   * Returns the parse result of the current context.
+   */
+  get value;
+
+  /**
+   * Returns the parse message of the current context.
+   */
+  String get message;
+}
+
+/**
+ * An immutable parse result in case of a successful parse.
+ */
+class Success extends Result {
+  const Success(buffer, position, this.value) : super(buffer, position);
+
+  @override
+  bool get isSuccess => true;
+
+  @override
+  final value;
+
+  @override
+  String get message => null;
+
+  @override
+  String toString() => 'Success[${toPositionString()}]: $value';
+}
+
+/**
+ * An immutable parse result in case of a failed parse.
+ */
+class Failure extends Result {
+  const Failure(buffer, position, this.message) : super(buffer, position);
+
+  @override
+  bool get isFailure => true;
+
+  @override
+  get value => throw new ParserError(this);
+
+  @override
+  final String message;
+
+  @override
+  String toString() => 'Failure[${toPositionString()}]: $message';
+}
+
+/**
+ * An exception raised in case of a parse error.
+ */
+class ParserError extends Error {
+  final Failure failure;
+
+  ParserError(this.failure);
+
+  @override
+  String toString() => '${failure.message} at ${failure.toPositionString()}';
+}
diff --git a/petitparser/lib/src/core/definition.dart b/petitparser/lib/src/core/definition.dart
new file mode 100644
index 0000000..dbb56f8
--- /dev/null
+++ b/petitparser/lib/src/core/definition.dart
@@ -0,0 +1,178 @@
+part of petitparser;
+
+/**
+ * Helper to conveniently define and build complex, recursive grammars using
+ * plain Dart code.
+ *
+ * To create a new grammar definition subclass [GrammarDefinition]. For every
+ * production create a new method returning the primitive parser defining it.
+ * The method called [start] is supposed to return the start production of the
+ * grammar. To refer to a production defined in the same definition use [ref]
+ * with the function reference as the first argument.
+ *
+ * Consider the following example to parse a list of numbers:
+ *
+ *     class ListGrammarDefinition extends GrammarDefinition {
+ *       start()   => ref(list).end();
+ *       list()    => ref(element) & char(',') & ref(list)
+ *                  | ref(element);
+ *       element() => digit().plus().flatten();
+ *     }
+ *
+ * Since this is plain Dart code, common refactorings such as renaming a production
+ * updates all references correctly. Also code navigation and code completion
+ * works as expected.
+ *
+ * To attach custom production actions you might want to further subclass your
+ * grammar definition and override overriding the necessary productions defined
+ * in the superclass:
+ *
+ *     class ListParserDefinition extends ListGrammarDefinition {
+ *       element() => super.element().map((value) => int.parse(value));
+ *     }
+ *
+ * Note that productions can be parametrized. Define such productions with positional
+ * arguments and reference to multiple instances by passing the arguments to [ref].
+ *
+ * Consider extending the above grammar with a parametrized token production:
+ *
+ *     class TokenizedListGrammarDefinition extends GrammarDefinition {
+ *       start()   => ref(list).end();
+ *       list()    => ref(element) & ref(token, char(',')) & ref(list)
+ *                  | ref(element);
+ *       element() => ref(token, digit().plus());
+ *       token(p)  => p.token().trim();
+ *     }
+  */
+abstract class GrammarDefinition {
+
+  /**
+   * The starting production of this definition.
+   */
+  Parser start();
+
+  /**
+   * Returns a parser reference to a production defined by a [function].
+   *
+   * The optional arguments parametrize the called production.
+   */
+  Parser ref(Function function, [arg1, arg2, arg3, arg4, arg5, arg6]) {
+    var arguments = [
+      arg1,
+      arg2,
+      arg3,
+      arg4,
+      arg5,
+      arg6
+    ].takeWhile((each) => each != null).toList(growable: false);
+    return new _Reference(function, arguments);
+  }
+
+  /**
+   * Builds a composite parser from this definition.
+   *
+   * The optional [start] reference specifies a different starting production into
+   * the grammar. The optional [arguments] list parametrizes the called production.
+   */
+  Parser build({Function start: null, List arguments: const []}) {
+    return _resolve(
+        new _Reference(start != null ? start : this.start, arguments));
+  }
+
+  /**
+   * Internal helper to resolve a complete parser graph.
+   */
+  Parser _resolve(_Reference reference) {
+    var mapping = new Map();
+
+    Parser _dereference(_Reference reference) {
+      var parser = mapping[reference];
+      if (parser == null) {
+        var references = [reference];
+        parser = reference.resolve();
+        while (parser is _Reference) {
+          if (references.contains(parser)) {
+            throw new StateError('Recursive references detected: $references');
+          }
+          references.add(parser);
+          parser = parser.resolve();
+        }
+        for (var each in references) {
+          mapping[each] = parser;
+        }
+      }
+      return parser;
+    }
+
+    var todo = [_dereference(reference)];
+    var seen = new Set.from(todo);
+
+    while (todo.isNotEmpty) {
+      var parent = todo.removeLast();
+      for (var child in parent.children) {
+        if (child is _Reference) {
+          var referenced = _dereference(child);
+          parent.replace(child, referenced);
+          child = referenced;
+        }
+        if (!seen.contains(child)) {
+          seen.add(child);
+          todo.add(child);
+        }
+      }
+    }
+
+    return mapping[reference];
+  }
+}
+
+/**
+ * A helper to build a parser from a {@link GrammarDefinition}.
+ */
+class GrammarParser extends DelegateParser {
+  GrammarParser(GrammarDefinition definition) : super(definition.build());
+}
+
+class _Reference extends Parser {
+  final Function function;
+  final List arguments;
+
+  _Reference(this.function, this.arguments);
+
+  Parser resolve() => Function.apply(function, arguments);
+
+  @override
+  bool operator ==(other) {
+    if (other is! _Reference ||
+        other.function != function ||
+        other.arguments.length != arguments.length) {
+      return false;
+    }
+    for (var i = 0; i < arguments.length; i++) {
+      var a = arguments[i],
+          b = other.arguments[i];
+      if (a is Parser && a is! _Reference && b is Parser && b is! _Reference) {
+        // for parsers do a deep equality check
+        if (!a.isEqualTo(b)) {
+          return false;
+        }
+      } else {
+        // for everything else just do standard equality
+        if (a != b) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  @override
+  int get hashCode => function.hashCode;
+
+  @override
+  Parser copy() => throw new UnsupportedError('References cannot be copied.');
+
+  @override
+  Result parseOn(Context context) =>
+      throw new UnsupportedError('References cannot be parsed.');
+}
diff --git a/petitparser/lib/src/core/expression.dart b/petitparser/lib/src/core/expression.dart
new file mode 100644
index 0000000..272fa75
--- /dev/null
+++ b/petitparser/lib/src/core/expression.dart
@@ -0,0 +1,220 @@
+part of petitparser;
+
+/**
+ * A builder that allows the simple definition of expression grammars with
+ * prefix, postfix, and left- and right-associative infix operators.
+ *
+ * The following code creates the empty expression builder:
+ *
+ *     var builder = new ExpressionBuilder();
+ *
+ * Then we define the operator-groups in descending precedence. The highest
+ * precedence have the literal numbers themselves:
+ *
+ *     builder.group()
+ *       ..primitive(digit().plus()
+ *         .seq(char('.').seq(digit().plus()).optional())
+ *         .flatten().trim().map((a) => double.parse(a)));
+ *
+ * Then come the normal arithmetic operators. Note, that the action blocks receive
+ * both, the terms and the parsed operator in the order they appear in the parsed
+ * input.
+ *
+ *     // negation is a prefix operator
+ *     builder.group()
+ *       ..prefix(char('-').trim(), (op, a) => -a);
+ *
+ *     // power is right-associative
+ *     builder.group()
+ *       ..right(char('^').trim(), (a, op, b) => math.pow(a, b));
+ *
+ *     // multiplication and addition is left-associative
+ *     builder.group()
+ *       ..left(char('*').trim(), (a, op, b) => a * b)
+ *       ..left(char('/').trim(), (a, op, b) => a / b);
+ *     builder.group()
+ *       ..left(char('+').trim(), (a, op, b) => a + b)
+ *       ..left(char('-').trim(), (a, op, b) => a - b);
+ *
+ * Finally we can build the parser:
+ *
+ *     var parser = builder.build();
+ *
+ * After executing the above code we get an efficient parser that correctly
+ * evaluates expressions like:
+ *
+ *     parser.parse('-8');      // -8
+ *     parser.parse('1+2*3');   // 7
+ *     parser.parse('1*2+3');   // 5
+ *     parser.parse('8/4/2');   // 2
+ *     parser.parse('2^2^3');   // 256
+ */
+class ExpressionBuilder {
+  final List<ExpressionGroup> _groups = new List();
+
+  /**
+   * Creates a new group of operators that share the same priority.
+   */
+  ExpressionGroup group() {
+    var group = new ExpressionGroup();
+    _groups.add(group);
+    return group;
+  }
+
+  /**
+   * Builds the expression parser.
+   */
+  Parser build() => _groups.fold(
+      failure('Highest priority group should define a primitive parser.'),
+      (a, b) => b._build(a));
+}
+
+/**
+ * Models a group of operators of the same precedence.
+ */
+class ExpressionGroup {
+
+  /**
+   * Defines a new primitive or literal [parser].
+   */
+  void primitive(Parser parser) {
+    _primitives.add(parser);
+  }
+
+  Parser _build_primitive(Parser inner) {
+    return _build_choice(_primitives, inner);
+  }
+
+  final List<Parser> _primitives = new List();
+
+  /**
+   * Adds a prefix operator [parser]. Evaluates the optional [action] with the
+   * parsed `operator` and `value`.
+   */
+  void prefix(Parser parser, [action(operator, value)]) {
+    if (action == null) action = (operator, value) => [operator, value];
+    _prefix
+        .add(parser.map((operator) => new _ExpressionResult(operator, action)));
+  }
+
+  Parser _build_prefix(Parser inner) {
+    if (_prefix.isEmpty) {
+      return inner;
+    } else {
+      return new SequenceParser([_build_choice(_prefix).star(), inner]).map(
+          (tuple) {
+        return tuple.first.reversed.fold(tuple.last, (value, result) {
+          return result.action(result.operator, value);
+        });
+      });
+    }
+  }
+
+  final List<Parser> _prefix = new List();
+
+  /**
+   * Adds a postfix operator [parser]. Evaluates the optional [action] with the
+   * parsed `value` and `operator`.
+   */
+  void postfix(Parser parser, [action(value, operator)]) {
+    if (action == null) action = (value, operator) => [value, operator];
+    _postfix
+        .add(parser.map((operator) => new _ExpressionResult(operator, action)));
+  }
+
+  Parser _build_postfix(Parser inner) {
+    if (_postfix.isEmpty) {
+      return inner;
+    } else {
+      return new SequenceParser([inner, _build_choice(_postfix).star()]).map(
+          (tuple) {
+        return tuple.last.fold(tuple.first, (value, result) {
+          return result.action(value, result.operator);
+        });
+      });
+    }
+  }
+
+  final List<Parser> _postfix = new List();
+
+  /**
+   * Adds a right-associative operator [parser]. Evaluates the optional [action] with
+   * the parsed `left` term, `operator`, and `right` term.
+   */
+  void right(Parser parser, [action(left, operator, right)]) {
+    if (action == null) action =
+        (left, operator, right) => [left, operator, right];
+    _right
+        .add(parser.map((operator) => new _ExpressionResult(operator, action)));
+  }
+
+  Parser _build_right(Parser inner) {
+    if (_right.isEmpty) {
+      return inner;
+    } else {
+      return inner.separatedBy(_build_choice(_right)).map((sequence) {
+        var result = sequence.last;
+        for (var i = sequence.length - 2; i > 0; i -= 2) {
+          result =
+              sequence[i].action(sequence[i - 1], sequence[i].operator, result);
+        }
+        return result;
+      });
+    }
+  }
+
+  final List<Parser> _right = new List();
+
+  /**
+   * Adds a left-associative operator [parser]. Evaluates the optional [action] with
+   * the parsed `left` term, `operator`, and `right` term.
+   */
+  void left(Parser parser, [action(left, operator, right)]) {
+    if (action == null) action =
+        (left, operator, right) => [left, operator, right];
+    _left
+        .add(parser.map((operator) => new _ExpressionResult(operator, action)));
+  }
+
+  Parser _build_left(Parser inner) {
+    if (_left.isEmpty) {
+      return inner;
+    } else {
+      return inner.separatedBy(_build_choice(_left)).map((sequence) {
+        var result = sequence.first;
+        for (var i = 1; i < sequence.length; i += 2) {
+          result =
+              sequence[i].action(result, sequence[i].operator, sequence[i + 1]);
+        }
+        return result;
+      });
+    }
+  }
+
+  final List<Parser> _left = new List();
+
+  // helper to build an optimal choice parser
+  Parser _build_choice(List<Parser> parsers, [Parser otherwise]) {
+    if (parsers.isEmpty) {
+      return otherwise;
+    } else if (parsers.length == 1) {
+      return parsers.first;
+    } else {
+      return new ChoiceParser(parsers);
+    }
+  }
+
+  // helper to build the group of parsers
+  Parser _build(Parser inner) {
+    return _build_left(
+        _build_right(_build_postfix(_build_prefix(_build_primitive(inner)))));
+  }
+}
+
+// helper class to associate operators and actions
+class _ExpressionResult {
+  final operator;
+  final Function action;
+  _ExpressionResult(this.operator, this.action);
+  String toString() => operator.toString();
+}
diff --git a/petitparser/lib/src/core/parser.dart b/petitparser/lib/src/core/parser.dart
new file mode 100644
index 0000000..32b3d55
--- /dev/null
+++ b/petitparser/lib/src/core/parser.dart
@@ -0,0 +1,477 @@
+part of petitparser;
+
+/**
+ * Abstract base class of all parsers.
+ */
+abstract class Parser {
+
+  /**
+   * Primitive method doing the actual parsing.
+   *
+   * The method is overridden in concrete subclasses to implement the
+   * parser specific logic. The methods takes a parse [context] and
+   * returns the resulting context, which is either a [Success] or
+   * [Failure] context.
+   */
+  Result parseOn(Context context);
+
+  /**
+   * Returns the parse result of the [input].
+   *
+   * The implementation creates a default parse context on the input and calls
+   * the internal parsing logic of the receiving parser.
+   *
+   * For example, `letter().plus().parse('abc')` results in an instance of
+   * [Success], where [Result.position] is `3` and [Success.value] is
+   * `[a, b, c]`.
+   *
+   * Similarly, `letter().plus().parse('123')` results in an instance of
+   * [Failure], where [Result.position] is `0` and [Failure.message] is
+   * ['letter expected'].
+   */
+  Result parse(input) {
+    return parseOn(new Context(input, 0));
+  }
+
+  /**
+   * Tests if the [input] can be successfully parsed.
+   *
+   * For example, `letter().plus().accept('abc')` returns `true`, and
+   * `letter().plus().accept('123')` returns `false`.
+   */
+  bool accept(input) {
+    return parse(input).isSuccess;
+  }
+
+  /**
+   * Returns a list of all successful overlapping parses of the [input].
+   *
+   * For example, `letter().plus().matches('abc de')` results in the list
+   * `[['a', 'b', 'c'], ['b', 'c'], ['c'], ['d', 'e'], ['e']]`. See
+   * [Parser.matchesSkipping] to retrieve non-overlapping parse results.
+   */
+  Iterable matches(input) {
+    var list = new List();
+    and()
+        .map((each) => list.add(each))
+        .seq(any())
+        .or(any())
+        .star()
+        .parse(input);
+    return list;
+  }
+
+  /**
+   * Returns a list of all successful non-overlapping parses of the input.
+   *
+   * For example, `letter().plus().matchesSkipping('abc de')` results in the
+   * list `[['a', 'b', 'c'], ['d', 'e']]`. See [Parser.matches] to retrieve
+   * overlapping parse results.
+   */
+  Iterable matchesSkipping(input) {
+    var list = new List();
+    map((each) => list.add(each)).or(any()).star().parse(input);
+    return list;
+  }
+
+  /**
+   * Returns new parser that accepts the receiver, if possible. The resulting
+   * parser returns the result of the receiver, or `null` if not applicable.
+   * The returned value can be provided as an optional argument [otherwise].
+   *
+   * For example, the parser `letter().optional()` accepts a letter as input
+   * and returns that letter. When given something else the parser succeeds as
+   * well, does not consume anything and returns `null`.
+   */
+  Parser optional([otherwise]) => new OptionalParser(this, otherwise);
+
+  /**
+   * Returns a parser that accepts the receiver zero or more times. The
+   * resulting parser returns a list of the parse results of the receiver.
+   *
+   * This is a greedy and blind implementation that tries to consume as much
+   * input as possible and that does not consider what comes afterwards.
+   *
+   * For example, the parser `letter().star()` accepts the empty string or
+   * any sequence of letters and returns a possibly empty list of the parsed
+   * letters.
+   */
+  Parser star() => repeat(0, unbounded);
+
+  /**
+   * Returns a parser that parses the receiver zero or more times until it
+   * reaches a [limit]. This is a greedy non-blind implementation of the
+   * [Parser.star] operator. The [limit] is not consumed.
+   */
+  Parser starGreedy(Parser limit) => repeatGreedy(limit, 0, unbounded);
+
+  /**
+   * Returns a parser that parses the receiver zero or more times until it
+   * reaches a [limit]. This is a lazy non-blind implementation of the
+   * [Parser.star] operator. The [limit] is not consumed.
+   */
+  Parser starLazy(Parser limit) => repeatLazy(limit, 0, unbounded);
+
+  /**
+   * Returns a parser that accepts the receiver one or more times. The
+   * resulting parser returns a list of the parse results of the receiver.
+   *
+   * This is a greedy and blind implementation that tries to consume as much
+   * input as possible and that does not consider what comes afterwards.
+   *
+   * For example, the parser `letter().plus()` accepts any sequence of
+   * letters and returns a list of the parsed letters.
+   */
+  Parser plus() => repeat(1, unbounded);
+
+  /**
+   * Returns a parser that parses the receiver one or more times until it
+   * reaches [limit]. This is a greedy non-blind implementation of the
+   * [Parser.plus] operator. The [limit] is not consumed.
+   */
+  Parser plusGreedy(Parser limit) => repeatGreedy(limit, 1, unbounded);
+
+  /**
+   * Returns a parser that parses the receiver one or more times until it
+   * reaches a [limit]. This is a lazy non-blind implementation of the
+   * [Parser.plus] operator. The [limit] is not consumed.
+   */
+  Parser plusLazy(Parser limit) => repeatLazy(limit, 1, unbounded);
+
+  /**
+   * Returns a parser that accepts the receiver between [min] and [max] times.
+   * The resulting parser returns a list of the parse results of the receiver.
+   *
+   * This is a greedy and blind implementation that tries to consume as much
+   * input as possible and that does not consider what comes afterwards.
+   *
+   * For example, the parser `letter().repeat(2, 4)` accepts a sequence of
+   * two, three, or four letters and returns the accepted letters as a list.
+   */
+  Parser repeat(int min, int max) {
+    return new PossessiveRepeatingParser(this, min, max);
+  }
+
+  /**
+   * Returns a parser that parses the receiver at least [min] and at most [max]
+   * times until it reaches a [limit]. This is a greedy non-blind implementation of
+   * the [Parser.repeat] operator. The [limit] is not consumed.
+   */
+  Parser repeatGreedy(Parser limit, int min, int max) {
+    return new GreedyRepeatingParser(this, limit, min, max);
+  }
+
+  /**
+   * Returns a parser that parses the receiver at least [min] and at most [max]
+   * times until it reaches a [limit]. This is a lazy non-blind implementation of
+   * the [Parser.repeat] operator. The [limit] is not consumed.
+   */
+  Parser repeatLazy(Parser limit, int min, int max) {
+    return new LazyRepeatingParser(this, limit, min, max);
+  }
+
+  /**
+   * Returns a parser that accepts the receiver exactly [count] times. The
+   * resulting parser returns a list of the parse results of the receiver.
+   *
+   * For example, the parser `letter().times(2)` accepts two letters and
+   * returns a list of the two parsed letters.
+   */
+  Parser times(int count) => repeat(count, count);
+
+  /**
+   * Returns a parser that accepts the receiver followed by [other]. The
+   * resulting parser returns a list of the parse result of the receiver
+   * followed by the parse result of [other]. Calling this method on an
+   * existing sequence code not nest this sequence into a new one, but
+   * instead augments the existing sequence with [other].
+   *
+   * For example, the parser `letter().seq(digit()).seq(letter())` accepts a
+   * letter followed by a digit and another letter. The parse result of the
+   * input string `'a1b'` is the list `['a', '1', 'b']`.
+   */
+  Parser seq(Parser other) => new SequenceParser([this, other]);
+
+  /**
+   * Convenience operator returning a parser that accepts the receiver followed
+   * by [other]. See [Parser.seq] for details.
+   */
+  Parser operator &(Parser other) => this.seq(other);
+
+  /**
+   * Returns a parser that accepts the receiver or [other]. The resulting
+   * parser returns the parse result of the receiver, if the receiver fails
+   * it returns the parse result of [other] (exclusive ordered choice).
+   *
+   * For example, the parser `letter().or(digit())` accepts a letter or a
+   * digit. An example where the order matters is the following choice between
+   * overlapping parsers: `letter().or(char('a'))`. In the example the parser
+   * `char('a')` will never be activated, because the input is always consumed
+   * `letter()`. This can be problematic if the author intended to attach a
+   * production action to `char('a')`.
+   */
+  Parser or(Parser other) => new ChoiceParser([this, other]);
+
+  /**
+   * Convenience operator returning a parser that accepts the receiver or
+   * [other]. See [Parser.or] for details.
+   */
+  Parser operator |(Parser other) => this.or(other);
+
+  /**
+   * Returns a parser (logical and-predicate) that succeeds whenever the
+   * receiver does, but never consumes input.
+   *
+   * For example, the parser `char('_').and().seq(identifier)` accepts
+   * identifiers that start with an underscore character. Since the predicate
+   * does not consume accepted input, the parser `identifier` is given the
+   * ability to process the complete identifier.
+   */
+  Parser and() => new AndParser(this);
+
+  /**
+   * Returns a parser (logical not-predicate) that succeeds whenever the
+   * receiver fails, but never consumes input.
+   *
+   * For example, the parser `char('_').not().seq(identifier)` accepts
+   * identifiers that do not start with an underscore character. If the parser
+   * `char('_')` accepts the input, the negation and subsequently the
+   * complete parser fails. Otherwise the parser `identifier` is given the
+   * ability to process the complete identifier.
+   */
+  Parser not([String message]) => new NotParser(this, message);
+
+  /**
+   * Returns a parser that consumes any input token (character), but the
+   * receiver.
+   *
+   * For example, the parser `letter().neg()` accepts any input but a letter.
+   * The parser fails for inputs like `'a'` or `'Z'`, but succeeds for
+   * input like `'1'`, `'_'` or `'$'`.
+   */
+  Parser neg([String message]) => not(message).seq(any()).pick(1);
+
+  /**
+   * Returns a parser that discards the result of the receiver, and returns
+   * a sub-string of the consumed range in the string/list being parsed.
+   *
+   * For example, the parser `letter().plus().flatten()` returns `'abc'`
+   * for the input `'abc'`. In contrast, the parser `letter().plus()` would
+   * return `['a', 'b', 'c']` for the same input instead.
+   */
+  Parser flatten() => new FlattenParser(this);
+
+  /**
+   * Returns a parser that returns a [Token]. The token carries the parsed
+   * value of the receiver [Token.value], as well as the consumed input
+   * [Token.input] from [Token.start] to [Token.stop] of the input being
+   * parsed.
+   *
+   * For example, the parser `letter().plus().token()` returns the token
+   * `Token[start: 0, stop: 3, value: abc]` for the input `'abc'`.
+   */
+  Parser token() => new TokenParser(this);
+
+  /**
+   * Returns a parser that consumes input before and after the receiver. The
+   * optional argument is a parser that consumes the excess input. By default
+   * `whitespace()` is used. To arguments can be provided to have different
+   * parsers on the [left] and [right] side.
+   *
+   * For example, the parser `letter().plus().trim()` returns `['a', 'b']`
+   * for the input `' ab\n'` and consumes the complete input string.
+   */
+  Parser trim([Parser left, Parser right]) {
+    if (left == null) left = whitespace();
+    if (right == null) right = left;
+    return new TrimmingParser(this, left, right);
+  }
+
+  /**
+   * Returns a parser that succeeds only if the receiver consumes the complete
+   * input, otherwise return a failure with the optional [message].
+   *
+   * For example, the parser `letter().end()` succeeds on the input `'a'`
+   * and fails on `'ab'`. In contrast the parser `letter()` alone would
+   * succeed on both inputs, but not consume everything for the second input.
+   */
+  Parser end([String message = 'end of input expected']) {
+    return new EndOfInputParser(this, message);
+  }
+
+  /**
+   * Returns a parser that points to the receiver, but can be changed to point
+   * to something else at a later point in time.
+   *
+   * For example, the parser `letter().settable()` behaves exactly the same
+   * as `letter()`, but it can be replaced with another parser using
+   * [SettableParser.set].
+   */
+  SettableParser settable() => new SettableParser(this);
+
+  /**
+   * Returns a parser that evaluates a [function] as the production action
+   * on success of the receiver.
+   *
+   * For example, the parser `digit().map((char) => int.parse(char))` returns
+   * the number `1` for the input string `'1'`. If the delegate fail, the
+   * production action is not executed and the failure is passed on.
+   */
+  Parser map(Function function) => new ActionParser(this, function);
+
+  /**
+   * Returns a parser that transform a successful parse result by returning
+   * the element at [index] of a list. A negative index can be used to access
+   * the elements from the back of the list.
+   *
+   * For example, the parser `letter().star().pick(-1)` returns the last
+   * letter parsed. For the input `'abc'` it returns `'c'`.
+   */
+  Parser pick(int index) {
+    return this.map((List list) {
+      return list[index < 0 ? list.length + index : index];
+    });
+  }
+
+  /**
+   * Returns a parser that transforms a successful parse result by returning
+   * the permuted elements at [indexes] of a list. Negative indexes can be
+   * used to access the elements from the back of the list.
+   *
+   * For example, the parser `letter().star().permute([0, -1])` returns the
+   * first and last letter parsed. For the input `'abc'` it returns
+   * `['a', 'c']`.
+   */
+  Parser permute(List<int> indexes) {
+    return this.map((List list) {
+      return indexes.map((index) {
+        return list[index < 0 ? list.length + index : index];
+      }).toList();
+    });
+  }
+
+  /**
+   * Returns a parser that consumes the receiver one or more times separated
+   * by the [separator] parser. The resulting parser returns a flat list of
+   * the parse results of the receiver interleaved with the parse result of the
+   * separator parser.
+   *
+   * If the optional argument [includeSeparators] is set to `false`, then the
+   * separators are not included in the parse result. If the optional argument
+   * [optionalSeparatorAtEnd] is set to `true` the parser also accepts an
+   * optional separator at the end.
+   *
+   * For example, the parser `digit().separatedBy(char('-'))` returns a parser
+   * that consumes input like `'1-2-3'` and returns a list of the elements and
+   * separators: `['1', '-', '2', '-', '3']`.
+   */
+  Parser separatedBy(Parser separator,
+      {bool includeSeparators: true, bool optionalSeparatorAtEnd: false}) {
+    var repeater = new SequenceParser([separator, this]).star();
+    var parser = new SequenceParser(optionalSeparatorAtEnd
+        ? [this, repeater, separator.optional(separator)]
+        : [this, repeater]);
+    return parser.map((List list) {
+      var result = new List();
+      result.add(list[0]);
+      for (var tuple in list[1]) {
+        if (includeSeparators) {
+          result.add(tuple[0]);
+        }
+        result.add(tuple[1]);
+      }
+      if (includeSeparators &&
+          optionalSeparatorAtEnd &&
+          !identical(list[2], separator)) {
+        result.add(list[2]);
+      }
+      return result;
+    });
+  }
+
+  /**
+   * Returns a shallow copy of the receiver.
+   *
+   * Override this method in all subclasses.
+   */
+  Parser copy();
+
+  /**
+   * Recursively tests for structural equality of two parsers.
+   *
+   * The code can automatically deals with recursive parsers and parsers that
+   * refer to other parsers. This code is supposed to be overridden by parsers
+   * that add other state.
+   */
+  bool isEqualTo(Parser other, [Set<Parser> seen]) {
+    if (seen == null) {
+      seen = new Set();
+    }
+    if (this == other || seen.contains(this)) {
+      return true;
+    }
+    seen.add(this);
+    return runtimeType == other.runtimeType &&
+        hasEqualProperties(other) &&
+        hasEqualChildren(other, seen);
+  }
+
+  /**
+   * Compare the properties of two parsers. Normally this method should not be
+   * called directly, instead use [Parser#equals].
+   *
+   * Override this method in all subclasses that add new state.
+   */
+  bool hasEqualProperties(Parser other) => true;
+
+  /**
+   * Compare the children of two parsers. Normally this method should not be
+   * called directly, instead use [Parser#equals].
+   *
+   * Normally this method does not need to be overridden, as this method works
+   * generically on the returned [Parser#children].
+   */
+  bool hasEqualChildren(Parser other, Set<Parser> seen) {
+    var thisChildren = children,
+        otherChildren = other.children;
+    if (thisChildren.length != otherChildren.length) {
+      return false;
+    }
+    for (var i = 0; i < thisChildren.length; i++) {
+      if (!thisChildren[i].isEqualTo(otherChildren[i], seen)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Returns a list of directly referenced parsers.
+   *
+   * For example, `letter().children` returns the empty collection `[]`,
+   * because the letter parser is a primitive or leaf parser that does not
+   * depend or call any other parser.
+   *
+   * In contrast, `letter().or(digit()).children` returns a collection
+   * containing both the `letter()` and `digit()` parser.
+   */
+  List<Parser> get children => const [];
+
+  /**
+   * Changes the receiver by replacing [source] with [target]. Does nothing
+   * if [source] does not exist in [Parser.children].
+   *
+   * The following example creates a letter parser and then defines a parser
+   * called `example` that accepts one or more letters. Eventually the parser
+   * `example` is modified by replacing the `letter` parser with a new
+   * parser that accepts a digit. The resulting `example` parser accepts one
+   * or more digits.
+   *
+   *     var letter = letter();
+   *     var example = letter.plus();
+   *     example.replace(letter, digit());
+   */
+  void replace(Parser source, Parser target) {
+    // no children, nothing to do
+  }
+}
diff --git a/petitparser/lib/src/core/parsers.dart b/petitparser/lib/src/core/parsers.dart
new file mode 100644
index 0000000..79b93f4
--- /dev/null
+++ b/petitparser/lib/src/core/parsers.dart
@@ -0,0 +1,94 @@
+part of petitparser;
+
+/**
+ * Returns a parser that consumes nothing and succeeds.
+ *
+ * For example, `char('a').or(epsilon())` is equivalent to
+ * `char('a').optional()`.
+ */
+Parser epsilon([result]) => new EpsilonParser(result);
+
+/**
+ * A parser that consumes nothing and succeeds.
+ */
+class EpsilonParser extends Parser {
+  final _result;
+
+  EpsilonParser(this._result);
+
+  @override
+  Result parseOn(Context context) => context.success(_result);
+
+  @override
+  Parser copy() => new EpsilonParser(_result);
+
+  @override
+  bool hasEqualProperties(Parser other) {
+    return other is EpsilonParser && super.hasEqualProperties(other)
+        && _result == other._result;
+  }
+}
+
+/**
+ * Returns a parser that consumes nothing and fails.
+ *
+ * For example, `failure()` always fails, no matter what input it is given.
+ */
+Parser failure([String message = 'unable to parse']) {
+  return new FailureParser(message);
+}
+
+/**
+ * A parser that consumes nothing and fails.
+ */
+class FailureParser extends Parser {
+  final String _message;
+
+  FailureParser(this._message);
+
+  @override
+  Result parseOn(Context context) => context.failure(_message);
+
+  @override
+  String toString() => '${super.toString()}[$_message]';
+
+  @override
+  Parser copy() => new FailureParser(_message);
+
+  @override
+  bool hasEqualProperties(Parser other) {
+    return other is FailureParser
+        && super.hasEqualProperties(other)
+        && _message == other._message;
+  }
+}
+
+/**
+ * Returns a parser that is not defined, but that can be set at a later
+ * point in time.
+ *
+ * For example, the following code sets up a parser that points to itself
+ * and that accepts a sequence of a's ended with the letter b.
+ *
+ *     var p = undefined();
+ *     p.set(char('a').seq(p).or(char('b')));
+ */
+SettableParser undefined([String message = 'undefined parser']) {
+  return failure(message).settable();
+}
+
+/**
+ * A parser that is not defined, but that can be set at a later
+ * point in time.
+ */
+class SettableParser extends DelegateParser {
+  SettableParser(parser) : super(parser);
+
+  /**
+   * Sets the receiver to delegate to [parser].
+   */
+  void set(Parser parser) => replace(children[0], parser);
+
+  @override
+  Parser copy() => new SettableParser(_delegate);
+}
diff --git a/petitparser/lib/src/core/predicates.dart b/petitparser/lib/src/core/predicates.dart
new file mode 100644
index 0000000..6cf3abd
--- /dev/null
+++ b/petitparser/lib/src/core/predicates.dart
@@ -0,0 +1,129 @@
+part of petitparser;
+
+/**
+ * Returns a parser that accepts any input element.
+ *
+ * For example, `any()` succeeds and consumes any given letter. It only
+ * fails for an empty input.
+ */
+Parser any([String message = 'input expected']) {
+  return new AnyParser(message);
+}
+
+/**
+ * A parser that accepts any input element.
+ */
+class AnyParser extends Parser {
+  final String _message;
+
+  AnyParser(this._message);
+
+  @override
+  Result parseOn(Context context) {
+    var position = context.position;
+    var buffer = context.buffer;
+    return position < buffer.length
+        ? context.success(buffer[position], position + 1)
+        : context.failure(_message);
+  }
+
+  @override
+  Parser copy() => new AnyParser(_message);
+
+  @override
+  bool hasEqualProperties(Parser other) {
+    return other is AnyParser &&
+        super.hasEqualProperties(other) &&
+        _message == other._message;
+  }
+}
+
+/**
+ * Returns a parser that accepts any of the [elements].
+ *
+ * For example, `anyIn('ab')` succeeds and consumes either the letter
+ * `'a'` or the letter `'b'`. For any other input the parser fails.
+ */
+Parser anyIn(elements, [String message]) {
+  return predicate(1, (each) => elements.indexOf(each) >= 0,
+      message != null ? message : 'any of $elements expected');
+}
+
+/**
+ * Returns a parser that accepts the string [element].
+ *
+ * For example, `string('foo')` succeeds and consumes the input string
+ * `'foo'`. Fails for any other input.
+ */
+Parser string(String element, [String message]) {
+  return predicate(element.length, (String each) => element == each,
+      message != null ? message : '$element expected');
+}
+
+/**
+ * Returns a parser that accepts the string [element] ignoring the case.
+ *
+ * For example, `stringIgnoreCase('foo')` succeeds and consumes the input
+ * string `'Foo'` or `'FOO'`. Fails for any other input.
+ */
+Parser stringIgnoreCase(String element, [String message]) {
+  final lowerElement = element.toLowerCase();
+  return predicate(element.length,
+      (String each) => lowerElement == each.toLowerCase(),
+      message != null ? message : '$element expected');
+}
+
+/**
+ * A generic predicate function returning [true] or [false] for a given
+ * [input] argument.
+ */
+typedef bool Predicate(input);
+
+/**
+ * Returns a parser that reads input of the specified [length], accepts
+ * it if the [predicate] matches, or fails with the given [message].
+ */
+Parser predicate(int length, Predicate predicate, String message) {
+  return new PredicateParser(length, predicate, message);
+}
+
+/**
+ * A parser for a literal satisfying a predicate.
+ */
+class PredicateParser extends Parser {
+  final int _length;
+  final Predicate _predicate;
+  final String _message;
+
+  PredicateParser(this._length, this._predicate, this._message);
+
+  @override
+  Result parseOn(Context context) {
+    final start = context.position;
+    final stop = start + _length;
+    if (stop <= context.buffer.length) {
+      var result = context.buffer is String
+          ? context.buffer.substring(start, stop)
+          : context.buffer.sublist(start, stop);
+      if (_predicate(result)) {
+        return context.success(result, stop);
+      }
+    }
+    return context.failure(_message);
+  }
+
+  @override
+  String toString() => '${super.toString()}[$_message]';
+
+  @override
+  Parser copy() => new PredicateParser(_length, _predicate, _message);
+
+  @override
+  bool hasEqualProperties(Parser other) {
+    return other is PredicateParser &&
+        super.hasEqualProperties(other) &&
+        _length == other._length &&
+        _predicate == other._predicate &&
+        _message == other._message;
+  }
+}
diff --git a/petitparser/lib/src/core/repeaters.dart b/petitparser/lib/src/core/repeaters.dart
new file mode 100644
index 0000000..c884031
--- /dev/null
+++ b/petitparser/lib/src/core/repeaters.dart
@@ -0,0 +1,185 @@
+part of petitparser;
+
+/**
+ * An [int] used to mark an unbounded maximum repetition.
+ */
+const int unbounded = -1;
+
+/**
+ * An abstract parser that repeatedly parses between 'min' and 'max' instances of
+ * its delegate.
+ */
+abstract class RepeatingParser extends DelegateParser {
+  final int _min;
+  final int _max;
+
+  RepeatingParser(Parser parser, this._min, this._max) : super(parser) {
+    assert(0 <= _min);
+    assert(_max == unbounded || _min <= _max);
+  }
+
+  @override
+  String toString() {
+    var max = _max == unbounded ? '*' : _max;
+    return '${super.toString()}[$_min..$max]';
+  }
+
+  @override
+  bool hasEqualProperties(Parser other) {
+    return other is RepeatingParser &&
+        super.hasEqualProperties(other) &&
+        _min == other._min &&
+        _max == other._max;
+  }
+}
+
+/**
+ * A greedy parser that repeatedly parses between 'min' and 'max' instances of
+ * its delegate.
+ */
+class PossessiveRepeatingParser extends RepeatingParser {
+  PossessiveRepeatingParser(Parser parser, int min, int max)
+      : super(parser, min, max);
+
+  @override
+  Result parseOn(Context context) {
+    var current = context;
+    var elements = new List();
+    while (elements.length < _min) {
+      var result = _delegate.parseOn(current);
+      if (result.isFailure) {
+        return result;
+      }
+      elements.add(result.value);
+      current = result;
+    }
+    while (_max == unbounded || elements.length < _max) {
+      var result = _delegate.parseOn(current);
+      if (result.isFailure) {
+        return current.success(elements);
+      }
+      elements.add(result.value);
+      current = result;
+    }
+    return current.success(elements);
+  }
+
+  @override
+  Parser copy() => new PossessiveRepeatingParser(_delegate, _min, _max);
+}
+
+/**
+ * An abstract parser that repeatedly parses between 'min' and 'max' instances of
+ * its delegate and that requires the input to be completed with a specified parser
+ * 'limit'. Subclasses provide repeating behavior as typically seen in regular
+ * expression implementations (non-blind).
+ */
+abstract class LimitedRepeatingParser extends RepeatingParser {
+  Parser _limit;
+
+  LimitedRepeatingParser(Parser parser, this._limit, int min, int max)
+      : super(parser, min, max);
+
+  @override
+  List<Parser> get children => [_delegate, _limit];
+
+  @override
+  void replace(Parser source, Parser target) {
+    super.replace(source, target);
+    if (_limit == source) {
+      _limit = target;
+    }
+  }
+}
+
+/**
+ * A greedy repeating parser, commonly seen in regular expression implementations. It
+ * aggressively consumes as much input as possible and then backtracks to meet the
+ * 'limit' condition.
+ */
+class GreedyRepeatingParser extends LimitedRepeatingParser {
+  GreedyRepeatingParser(Parser parser, Parser limit, int min, int max)
+      : super(parser, limit, min, max);
+
+  @override
+  Result parseOn(Context context) {
+    var current = context;
+    var elements = new List();
+    while (elements.length < _min) {
+      var result = _delegate.parseOn(current);
+      if (result.isFailure) {
+        return result;
+      }
+      elements.add(result.value);
+      current = result;
+    }
+    var contexts = new List.from([current]);
+    while (_max == unbounded || elements.length < _max) {
+      var result = _delegate.parseOn(current);
+      if (result.isFailure) {
+        break;
+      }
+      elements.add(result.value);
+      contexts.add(current = result);
+    }
+    while (true) {
+      var limit = _limit.parseOn(contexts.last);
+      if (limit.isSuccess) {
+        return contexts.last.success(elements);
+      }
+      if (elements.isEmpty) {
+        return limit;
+      }
+      contexts.removeLast();
+      elements.removeLast();
+      if (contexts.isEmpty) {
+        return limit;
+      }
+    }
+  }
+
+  @override
+  Parser copy() => new GreedyRepeatingParser(_delegate, _limit, _min, _max);
+}
+
+/**
+ * A lazy repeating parser, commonly seen in regular expression implementations. It
+ * limits its consumption to meet the 'limit' condition as early as possible.
+ */
+class LazyRepeatingParser extends LimitedRepeatingParser {
+  LazyRepeatingParser(Parser parser, Parser limit, int min, int max)
+      : super(parser, limit, min, max);
+
+  @override
+  Result parseOn(Context context) {
+    var current = context;
+    var elements = new List();
+    while (elements.length < _min) {
+      var result = _delegate.parseOn(current);
+      if (result.isFailure) {
+        return result;
+      }
+      elements.add(result.value);
+      current = result;
+    }
+    while (true) {
+      var limit = _limit.parseOn(current);
+      if (limit.isSuccess) {
+        return current.success(elements);
+      } else {
+        if (_max != unbounded && elements.length >= _max) {
+          return limit;
+        }
+        var result = _delegate.parseOn(current);
+        if (result.isFailure) {
+          return limit;
+        }
+        elements.add(result.value);
+        current = result;
+      }
+    }
+  }
+
+  @override
+  Parser copy() => new LazyRepeatingParser(_delegate, _limit, _min, _max);
+}
diff --git a/petitparser/lib/src/core/token.dart b/petitparser/lib/src/core/token.dart
new file mode 100644
index 0000000..ded3353
--- /dev/null
+++ b/petitparser/lib/src/core/token.dart
@@ -0,0 +1,109 @@
+part of petitparser;
+
+/**
+ * A token represents a parsed part of the input stream.
+ *
+ * The token holds the resulting value of the input, the input buffer,
+ * and the start and stop position in the input buffer. It provides many
+ * convenience methods to access the state of the token.
+ */
+class Token {
+
+  /**
+   * The parsed value of the token.
+   */
+  final value;
+
+  /**
+   * The parsed buffer of the token.
+   */
+  final buffer;
+
+  /**
+   * The start position of the token in the buffer.
+   */
+  final int start;
+
+  /**
+   * The stop position of the token in the buffer.
+   */
+  final int stop;
+
+  /**
+   * Constructs a token from the parsed value, the input buffer, and the
+   * start and stop position in the input buffer.
+   */
+  const Token(this.value, this.buffer, this.start, this.stop);
+
+  /**
+   * The consumed input of the token.
+   */
+  get input => buffer is String
+      ? buffer.substring(start, stop)
+      : buffer.sublist(start, stop);
+
+  /**
+   * The length of the token.
+   */
+  int get length => stop - start;
+
+  /**
+   * The line number of the token (only works for [String] buffers).
+   */
+  int get line => Token.lineAndColumnOf(buffer, start)[0];
+
+  /**
+   * The column number of this token (only works for [String] buffers).
+   */
+  int get column => Token.lineAndColumnOf(buffer, start)[1];
+
+  @override
+  String toString() => 'Token[${positionString(buffer, start)}]: $value';
+
+  @override
+  bool operator ==(other) {
+    return other is Token &&
+        value == other.value &&
+        start == other.start &&
+        stop == other.stop;
+  }
+
+  @override
+  int get hashCode => value.hashCode + start.hashCode + stop.hashCode;
+
+  static final Parser _NEWLINE_PARSER =
+      char('\n').or(char('\r').seq(char('\n').optional()));
+
+  /**
+   * Returns a parser for that detects newlines platform independently.
+   */
+  static Parser newlineParser() => _NEWLINE_PARSER;
+
+  /**
+   * Converts the [position] index in a [buffer] to a line and column tuple.
+   */
+  static List<int> lineAndColumnOf(String buffer, int position) {
+    var line = 1,
+        offset = 0;
+    for (var token in newlineParser().token().matchesSkipping(buffer)) {
+      if (position < token.stop) {
+        return [line, position - offset + 1];
+      }
+      line++;
+      offset = token.stop;
+    }
+    return [line, position - offset + 1];
+  }
+
+  /**
+   * Returns a human readable string representing the [position] index in a [buffer].
+   */
+  static String positionString(buffer, int position) {
+    if (buffer is String) {
+      var lineAndColumn = Token.lineAndColumnOf(buffer, position);
+      return '${lineAndColumn[0]}:${lineAndColumn[1]}';
+    } else {
+      return '${position}';
+    }
+  }
+}
diff --git a/petitparser/lib/src/dart/grammar.dart b/petitparser/lib/src/dart/grammar.dart
new file mode 100644
index 0000000..187ecfa
--- /dev/null
+++ b/petitparser/lib/src/dart/grammar.dart
@@ -0,0 +1,788 @@
+part of dart;
+
+/**
+ * Dart grammar.
+ */
+class DartGrammar extends GrammarParser {
+  DartGrammar() : super(new DartGrammarDefinition());
+}
+
+/**
+ * Dart grammar definition.
+ *
+ * Adapted from [https://code.google.com/p/dart/source/browse/branches/bleeding_edge/dart/language/grammar/Dart.g].
+ */
+class DartGrammarDefinition extends GrammarDefinition {
+
+  Parser token(input) {
+    if (input is String) {
+      input = input.length == 1 ? char(input) : string(input);
+    } else if (input is Function) {
+      input = ref(input);
+    }
+    if (input is! Parser && input is TrimmingParser) {
+      throw new StateError('Invalid token parser: $input');
+    }
+    return input.token().trim(ref(HIDDEN));
+  }
+
+
+  // Copyright (c) 2011, 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.
+
+  // -----------------------------------------------------------------
+  // Keyword definitions.
+  // -----------------------------------------------------------------
+  BREAK()      => ref(token, 'break');
+  CASE()       => ref(token, 'case');
+  CATCH()      => ref(token, 'catch');
+  CONST()      => ref(token, 'const');
+  CONTINUE()   => ref(token, 'continue');
+  DEFAULT()    => ref(token, 'default');
+  DO()         => ref(token, 'do');
+  ELSE()       => ref(token, 'else');
+  FALSE()      => ref(token, 'false');
+  FINAL()      => ref(token, 'final');
+  FINALLY()    => ref(token, 'finally');
+  FOR()        => ref(token, 'for');
+  IF()         => ref(token, 'if');
+  IN()         => ref(token, 'in');
+  NEW()        => ref(token, 'new');
+  NULL()       => ref(token, 'null');
+  RETURN()     => ref(token, 'return');
+  SUPER()      => ref(token, 'super');
+  SWITCH()     => ref(token, 'switch');
+  THIS()       => ref(token, 'this');
+  THROW()      => ref(token, 'throw');
+  TRUE()       => ref(token, 'true');
+  TRY()        => ref(token, 'try');
+  VAR()        => ref(token, 'var');
+  VOID()       => ref(token, 'void');
+  WHILE()      => ref(token, 'while');
+
+  // Pseudo-keywords that should also be valid identifiers.
+  ABSTRACT()   => ref(token, 'abstract');
+  ASSERT()     => ref(token, 'assert');
+  CLASS()      => ref(token, 'class');
+  EXTENDS()    => ref(token, 'extends');
+  FACTORY()    => ref(token, 'factory');
+  GET()        => ref(token, 'get');
+  IMPLEMENTS() => ref(token, 'implements');
+  IMPORT()     => ref(token, 'import');
+  INTERFACE()  => ref(token, 'interface');
+  IS()         => ref(token, 'is');
+  LIBRARY()    => ref(token, 'library');
+  NATIVE()     => ref(token, 'native');
+  NEGATE()     => ref(token, 'negate');
+  OPERATOR()   => ref(token, 'operator');
+  SET()        => ref(token, 'set');
+  SOURCE()     => ref(token, 'source');
+  STATIC()     => ref(token, 'static');
+  TYPEDEF()    => ref(token, 'typedef');
+
+  // -----------------------------------------------------------------
+  // Grammar productions.
+  // -----------------------------------------------------------------
+  start() => ref(compilationUnit).end();
+
+  compilationUnit() =>
+        ref(HASHBANG).optional()
+      & ref(directive).star()
+      & ref(topLevelDefinition).star();
+
+  directive() =>
+        ref(token, '#')
+      & ref(identifier)
+      & ref(arguments)
+      & ref(token, ';');
+
+  topLevelDefinition() =>
+        ref(classDefinition)
+      | ref(interfaceDefinition)
+      | ref(functionTypeAlias)
+      | ref(functionDeclaration) & ref(functionBodyOrNative)
+      | ref(returnType).optional() & ref(getOrSet) & ref(identifier) & ref(formalParameterList) & ref(functionBodyOrNative)
+      | ref(FINAL) & ref(type).optional() & ref(staticFinalDeclarationList) & ref(token, ';')
+      | ref(constInitializedVariableDeclaration) & ref(token, ';');
+
+  classDefinition() =>
+        ref(CLASS) & ref(identifier) & ref(typeParameters).optional() & ref(superclass).optional() & ref(interfaces).optional() &
+        ref(token, '{') & ref(classMemberDefinition).star() & ref(token, '}')
+      | ref(CLASS) & ref(identifier) & ref(typeParameters).optional() & ref(interfaces).optional() & ref(NATIVE) & ref(token, STRING) &
+        ref(token, '{') & ref(classMemberDefinition).star() & ref(token, '}');
+
+  typeParameter() => ref(identifier) & (ref(EXTENDS) & ref(type)).optional();
+
+  typeParameters() => ref(token, '<') & ref(typeParameter) & (ref(token, ',') & ref(typeParameter)).star() & ref(token, '>');
+
+  superclass() => ref(EXTENDS) & ref(type);
+
+  interfaces() => ref(IMPLEMENTS) & ref(typeList);
+
+  superinterfaces() => ref(EXTENDS) & ref(typeList);
+
+  // This rule is organized in a way that may not be most readable, but
+  // gives the best error messages.
+  classMemberDefinition() =>
+        ref(declaration) & ref(token, ';')
+      | ref(constructorDeclaration) & ref(token, ';')
+      | ref(methodDeclaration) & ref(functionBodyOrNative)
+      | ref(CONST) & ref(factoryConstructorDeclaration) & ref(functionNative);
+
+  functionBodyOrNative() =>
+        ref(NATIVE) & ref(functionBody)
+      | ref(functionNative)
+      | ref(functionBody);
+
+  functionNative() => ref(NATIVE) & ref(token, STRING).optional() & ref(token, ';');
+
+  // A method, operator, or constructor (which all should be followed by
+  // a block of code).
+  methodDeclaration() =>
+        ref(factoryConstructorDeclaration)
+      | ref(STATIC) & ref(functionDeclaration)
+      | ref(specialSignatureDefinition)
+      | ref(functionDeclaration) & ref(initializers).optional()
+      | ref(namedConstructorDeclaration) & ref(initializers).optional();
+
+// An abstract method/operator, a field, or const constructor (which
+// all should be followed by a semicolon).
+  declaration() =>
+        ref(constantConstructorDeclaration) & (ref(redirection) | ref(initializers)).optional()
+      | ref(functionDeclaration) & ref(redirection)
+      | ref(namedConstructorDeclaration) & ref(redirection)
+      | ref(ABSTRACT) & ref(specialSignatureDefinition)
+      | ref(ABSTRACT) & ref(functionDeclaration)
+      | ref(STATIC) & ref(FINAL) & ref(type).optional() & ref(staticFinalDeclarationList)
+      | ref(STATIC).optional() & ref(constInitializedVariableDeclaration);
+
+  initializers() => ref(token, ':') & ref(superCallOrFieldInitializer) & (ref(token, ',') & ref(superCallOrFieldInitializer)).star();
+
+  redirection() => ref(token, ':') & ref(THIS) & (ref(token, '.') & ref(identifier)).optional() & ref(arguments);
+
+  fieldInitializer() => (ref(THIS) & ref(token, '.')).optional() & ref(identifier) & ref(token, '=') & ref(conditionalExpression);
+
+  superCallOrFieldInitializer() =>
+        ref(SUPER) & ref(arguments)
+      | ref(SUPER) & ref(token, '.') & ref(identifier) & ref(arguments)
+      | ref(fieldInitializer);
+
+  staticFinalDeclarationList() => ref(staticFinalDeclaration) & (ref(token, ',') & ref(staticFinalDeclaration)).star();
+
+  staticFinalDeclaration() => ref(identifier) & ref(token, '=') & ref(constantExpression);
+
+  interfaceDefinition() => ref(INTERFACE) & ref(identifier) & ref(typeParameters).optional() &
+      ref(superinterfaces).optional() & ref(factorySpecification).optional() & ref(token, '{') &
+      ref(interfaceMemberDefinition).star() & ref(token, '}');
+
+  factorySpecification() => ref(FACTORY) & ref(type);
+
+  functionTypeAlias() => ref(TYPEDEF) & ref(functionPrefix) & ref(typeParameters).optional() &
+      ref(formalParameterList) & ref(token, ';');
+
+  interfaceMemberDefinition() =>
+        ref(STATIC) & ref(FINAL) & ref(type).optional() & ref(initializedIdentifierList) & ref(token, ';')
+      | ref(functionDeclaration) & ref(token, ';')
+      | ref(constantConstructorDeclaration) & ref(token, ';')
+      | ref(namedConstructorDeclaration) & ref(token, ';')
+      | ref(specialSignatureDefinition) & ref(token, ';')
+      | ref(variableDeclaration) & ref(token, ';');
+
+  factoryConstructorDeclaration() => ref(FACTORY) & ref(qualified) & ref(typeParameters).optional() &
+      (ref(token, '.') & ref(identifier)).optional() & ref(formalParameterList);
+
+  namedConstructorDeclaration() => ref(identifier) & ref(token, '.') & ref(identifier) &
+      ref(formalParameterList);
+
+  constructorDeclaration() =>
+        ref(identifier) & ref(formalParameterList) & (ref(redirection) | ref(initializers)).optional()
+      | ref(namedConstructorDeclaration) & (ref(redirection) | ref(initializers)).optional();
+
+  constantConstructorDeclaration() => ref(CONST) & ref(qualified) & ref(formalParameterList);
+
+  specialSignatureDefinition() =>
+        ref(STATIC).optional() & ref(returnType).optional() & ref(getOrSet) & ref(identifier) & ref(formalParameterList)
+      | ref(returnType).optional() & ref(OPERATOR) & ref(userDefinableOperator) & ref(formalParameterList);
+
+  getOrSet() => ref(GET) | ref(SET);
+
+  userDefinableOperator() =>
+        ref(multiplicativeOperator)
+      | ref(additiveOperator)
+      | ref(shiftOperator)
+      | ref(relationalOperator)
+      | ref(bitwiseOperator)
+      | ref(token, '==')  // Disallow negative and === equality checks.
+      | ref(token, '~')   // Disallow ! operator.
+      | ref(NEGATE)
+      | ref(token, '[') & ref(token, ']')
+      | ref(token, '[') & ref(token, ']') & ref(token, '=');
+
+  prefixOperator() =>
+        ref(additiveOperator)
+      | ref(negateOperator);
+
+  postfixOperator() =>
+      ref(incrementOperator);
+
+  negateOperator() =>
+        ref(token, '!')
+      | ref(token, '~');
+
+  multiplicativeOperator() =>
+        ref(token, '*')
+      | ref(token, '/')
+      | ref(token, '%')
+      | ref(token, '~/');
+
+  assignmentOperator() =>
+        ref(token, '=')
+      | ref(token, '*=')
+      | ref(token, '/=')
+      | ref(token, '~/=')
+      | ref(token, '%=')
+      | ref(token, '+=')
+      | ref(token, '-=')
+      | ref(token, '<<=')
+      | ref(token, '>') & ref(token, '>') & ref(token, '>') & ref(token, '=')
+      | ref(token, '>') & ref(token, '>') & ref(token, '=')
+      | ref(token, '&=')
+      | ref(token, '^=')
+      | ref(token, '|=');
+
+  additiveOperator() =>
+        ref(token, '+')
+      | ref(token, '-');
+
+  incrementOperator() =>
+        ref(token, '++')
+      | ref(token, '--');
+
+  shiftOperator() =>
+        ref(token, '<<')
+      | ref(token, '>') & ref(token, '>') & ref(token, '>')
+      | ref(token, '>') & ref(token, '>');
+
+  relationalOperator() =>
+        ref(token, '>') & ref(token, '=')
+      | ref(token, '>')
+      | ref(token, '<=')
+      | ref(token, '<');
+
+  equalityOperator() =>
+        ref(token, '==')
+      | ref(token, '!=')
+      | ref(token, '===')
+      | ref(token, '!==');
+
+  bitwiseOperator() =>
+        ref(token, '&')
+      | ref(token, '^')
+      | ref(token, '|');
+
+  formalParameterList() =>
+        ref(token, '(') & ref(namedFormalParameters).optional() & ref(token, ')')
+      | ref(token, '(') & ref(normalFormalParameter) & ref(normalFormalParameterTail).optional() & ref(token, ')');
+
+  normalFormalParameterTail() =>
+        ref(token, ',') & ref(namedFormalParameters)
+      | ref(token, ',') & ref(normalFormalParameter) & ref(normalFormalParameterTail).optional();
+
+  normalFormalParameter() =>
+        ref(functionDeclaration)
+      | ref(fieldFormalParameter)
+      | ref(simpleFormalParameter);
+
+  simpleFormalParameter() =>
+        ref(declaredIdentifier)
+      | ref(identifier);
+
+  fieldFormalParameter() =>
+        ref(finalVarOrType).optional() & ref(THIS) & ref(token, '.') & ref(identifier);
+
+  namedFormalParameters() =>
+        ref(token, '[') & ref(defaultFormalParameter) & (ref(token, ',') & ref(defaultFormalParameter)).star() & ref(token, ']');
+
+  defaultFormalParameter() =>
+        ref(normalFormalParameter) & (ref(token, '=') & ref(constantExpression)).optional();
+
+  returnType() =>
+        ref(VOID)
+      | ref(type);
+
+  finalVarOrType() =>
+        ref(FINAL) & ref(type).optional()
+      | ref(VAR)
+      | ref(type)
+      ;
+
+  // We have to introduce a separate rule for 'declared' identifiers to
+  // allow ANTLR to decide if the first identifier we encounter after
+  // final is a type or an identifier. Before this change, we used the
+  // production 'finalVarOrType identifier' in numerous places.
+  declaredIdentifier() =>
+        ref(FINAL) & ref(type).optional() & ref(identifier)
+      | ref(VAR) & ref(identifier)
+      | ref(type) & ref(identifier)
+      ;
+
+  identifier() => ref(token, ref(IDENTIFIER)
+      | ref(ABSTRACT)
+      | ref(ASSERT)
+      | ref(CLASS)
+      | ref(EXTENDS)
+      | ref(FACTORY)
+      | ref(GET)
+      | ref(IMPLEMENTS)
+      | ref(IMPORT)
+      | ref(INTERFACE)
+      | ref(IS)
+      | ref(LIBRARY)
+      | ref(NATIVE)
+      | ref(NEGATE)
+      | ref(OPERATOR)
+      | ref(SET)
+      | ref(SOURCE)
+      | ref(STATIC)
+      | ref(TYPEDEF)
+      );
+
+  qualified() =>
+        ref(identifier) & (ref(token, '.') & ref(identifier)).optional()
+      ;
+
+  type() =>
+        ref(qualified) & ref(typeArguments).optional()
+      ;
+
+  typeArguments() =>
+        ref(token, '<') & ref(typeList) & ref(token, '>')
+      ;
+
+  typeList() =>
+        ref(type) & (ref(token, ',') & ref(type)).star()
+      ;
+
+  block() =>
+        ref(token, '{') & ref(statements) & ref(token, '}')
+      ;
+
+  statements() =>
+        ref(statement).star()
+      ;
+
+  statement() =>
+        ref(label).star() & ref(nonLabelledStatement)
+      ;
+
+  nonLabelledStatement() =>
+        ref(block)
+      | ref(initializedVariableDeclaration) & ref(token, ';')
+      | ref(iterationStatement)
+      | ref(selectionStatement)
+      | ref(tryStatement)
+      | ref(BREAK) & ref(identifier).optional() & ref(token, ';')
+      | ref(CONTINUE) & ref(identifier).optional() & ref(token, ';')
+      | ref(RETURN) & ref(expression).optional() & ref(token, ';')
+      | ref(THROW) & ref(expression).optional() & ref(token, ';')
+      | ref(expression).optional() & ref(token, ';')
+      | ref(ASSERT) & ref(token, '(') & ref(conditionalExpression) & ref(token, ')') & ref(token, ';')
+      | ref(functionDeclaration) & ref(functionBody)
+      ;
+
+  label() =>
+        ref(identifier) & ref(token, ':')
+      ;
+
+  iterationStatement() =>
+        ref(WHILE) & ref(token, '(') & ref(expression) & ref(token, ')') & ref(statement)
+      | ref(DO) & ref(statement) & ref(WHILE) & ref(token, '(') & ref(expression) & ref(token, ')') & ref(token, ';')
+      | ref(FOR) & ref(token, '(') & ref(forLoopParts) & ref(token, ')') & ref(statement)
+      ;
+
+  forLoopParts() =>
+        ref(forInitializerStatement) & ref(expression).optional() & ref(token, ';') & ref(expressionList).optional()
+      | ref(declaredIdentifier) & ref(IN) & ref(expression)
+      | ref(identifier) & ref(IN) & ref(expression)
+      ;
+
+  forInitializerStatement() =>
+        ref(initializedVariableDeclaration) & ref(token, ';')
+      | ref(expression).optional() & ref(token, ';')
+      ;
+
+  selectionStatement() =>
+        ref(IF) & ref(token, '(') & ref(expression) & ref(token, ')') & ref(statement) & (ref(ELSE) & ref(statement)).optional()
+      | ref(SWITCH) & ref(token, '(') & ref(expression) & ref(token, ')') & ref(token, '{') & ref(switchCase).star() & ref(defaultCase).optional() & ref(token, '}')
+      ;
+
+  switchCase() =>
+        ref(label).optional() & (ref(CASE) & ref(expression) & ref(token, ':')).plus() & ref(statements)
+      ;
+
+  defaultCase() =>
+        ref(label).optional() & (ref(CASE) & ref(expression) & ref(token, ':')).star() & ref(DEFAULT) & ref(token, ':') & ref(statements)
+      ;
+
+  tryStatement() =>
+        ref(TRY) & ref(block) & (ref(catchPart).plus() & ref(finallyPart).optional() | ref(finallyPart))
+      ;
+
+  catchPart() =>
+        ref(CATCH) & ref(token, '(') & ref(declaredIdentifier) & (ref(token, ',') & ref(declaredIdentifier)).optional() & ref(token, ')') & ref(block)
+      ;
+
+  finallyPart() =>
+        ref(FINALLY) & ref(block)
+      ;
+
+  variableDeclaration() =>
+        ref(declaredIdentifier) & (ref(token, ',') & ref(identifier)).star()
+      ;
+
+  initializedVariableDeclaration() =>
+        ref(declaredIdentifier) & (ref(token, '=') & ref(expression)).optional() & (ref(token, ',') & ref(initializedIdentifier)).star()
+      ;
+
+  initializedIdentifierList() =>
+        ref(initializedIdentifier) & (ref(token, ',') & ref(initializedIdentifier)).star()
+      ;
+
+  initializedIdentifier() =>
+        ref(identifier) & (ref(token, '=') & ref(expression)).optional()
+      ;
+
+  constInitializedVariableDeclaration() =>
+        ref(declaredIdentifier) & (ref(token, '=') & ref(constantExpression)).optional() &
+        (ref(token, ',') & ref(constInitializedIdentifier)).star()
+      ;
+
+  constInitializedIdentifier() =>
+        ref(identifier) & (ref(token, '=') & ref(constantExpression)).optional()
+      ;
+
+  // The constant expression production is used to mark certain expressions
+  // as only being allowed to hold a compile-time constant. The grammar cannot
+  // express these restrictions (yet), so this will have to be enforced by a
+  // separate analysis phase.
+  constantExpression() =>
+        ref(expression)
+      ;
+
+  expression() =>
+        ref(assignableExpression) & ref(assignmentOperator) & ref(expression)
+      | ref(conditionalExpression)
+      ;
+
+  expressionList() =>
+        ref(expression) & (ref(token, ',') & ref(expression)).star()
+      ;
+
+  arguments() =>
+        ref(token, '(') & ref(argumentList).optional() & ref(token, ')')
+      ;
+
+  argumentList() =>
+        ref(namedArgument) & (ref(token, ',') & ref(namedArgument)).star()
+      | ref(expressionList) & (ref(token, ',') & ref(namedArgument)).star()
+      ;
+
+  namedArgument() =>
+        ref(label) & ref(expression)
+      ;
+
+  assignableExpression() =>
+        ref(primary) & (ref(arguments).star() & ref(assignableSelector)).plus()
+      | ref(SUPER) & ref(assignableSelector)
+      | ref(identifier)
+      ;
+
+  conditionalExpression() =>
+        ref(logicalOrExpression) & (ref(token, '?') & ref(expression) & ref(token, ':') & ref(expression)).optional()
+      ;
+
+  logicalOrExpression() =>
+        ref(logicalAndExpression) & (ref(token, '||') & ref(logicalAndExpression)).star()
+      ;
+
+  logicalAndExpression() =>
+        ref(bitwiseOrExpression) & (ref(token, '&&') & ref(bitwiseOrExpression)).star()
+      ;
+
+  bitwiseOrExpression() =>
+        ref(bitwiseXorExpression) & (ref(token, '|') & ref(bitwiseXorExpression)).star()
+      | ref(SUPER) & (ref(token, '|') & ref(bitwiseXorExpression)).plus()
+      ;
+
+  bitwiseXorExpression() =>
+        ref(bitwiseAndExpression) & (ref(token, '^') & ref(bitwiseAndExpression)).star()
+      | ref(SUPER) & (ref(token, '^') & ref(bitwiseAndExpression)).plus()
+      ;
+
+  bitwiseAndExpression() =>
+        ref(equalityExpression) & (ref(token, '&') & ref(equalityExpression)).star()
+      | ref(SUPER) & (ref(token, '&') & ref(equalityExpression)).plus()
+      ;
+
+  equalityExpression() =>
+        ref(relationalExpression) & (ref(equalityOperator) & ref(relationalExpression)).optional()
+      | ref(SUPER) & ref(equalityOperator) & ref(relationalExpression)
+      ;
+
+  relationalExpression() =>
+        ref(shiftExpression) & (ref(isOperator) & ref(type) | ref(relationalOperator) & ref(shiftExpression)).optional()
+      | ref(SUPER) & ref(relationalOperator) & ref(shiftExpression)
+      ;
+
+  isOperator() =>
+        ref(IS) & ref(token, '!').optional()
+      ;
+
+  shiftExpression() =>
+        ref(additiveExpression) & (ref(shiftOperator) & ref(additiveExpression)).star()
+      | ref(SUPER) & (ref(shiftOperator) & ref(additiveExpression)).plus()
+      ;
+
+  additiveExpression() =>
+        ref(multiplicativeExpression) & (ref(additiveOperator) & ref(multiplicativeExpression)).star()
+      | ref(SUPER) & (ref(additiveOperator) & ref(multiplicativeExpression)).plus()
+      ;
+
+  multiplicativeExpression() =>
+        ref(unaryExpression) & (ref(multiplicativeOperator) & ref(unaryExpression)).star()
+      | ref(SUPER) & (ref(multiplicativeOperator) & ref(unaryExpression)).plus()
+      ;
+
+  unaryExpression() =>
+        ref(postfixExpression)
+      | ref(prefixOperator) & ref(unaryExpression)
+      | ref(negateOperator) & ref(SUPER)
+      | ref(token, '-') & ref(SUPER)
+      | ref(incrementOperator) & ref(assignableExpression)
+      ;
+
+  postfixExpression() =>
+        ref(assignableExpression) & ref(postfixOperator)
+      | ref(primary) & ref(selector).star()
+      ;
+
+  selector() =>
+        ref(assignableSelector)
+      | ref(arguments)
+      ;
+
+  assignableSelector() =>
+        ref(token, '[') & ref(expression) & ref(token, ']')
+      | ref(token, '.') & ref(identifier)
+      ;
+
+  primary() =>
+        ref(primaryNoFE)
+      | ref(primaryFE)
+      ;
+
+  primaryFE() =>
+        ref(functionExpression)
+      | ref(primaryNoFE)
+      ;
+
+  primaryNoFE() =>
+        ref(THIS)
+      | ref(SUPER) & ref(assignableSelector)
+      | ref(literal)
+      | ref(identifier)
+      | ref(CONST).optional() & ref(typeArguments).optional() & ref(compoundLiteral)
+      | (ref(NEW) | ref(CONST)) & ref(type) & (ref(token, '.') & ref(identifier)).optional() & ref(arguments)
+      | ref(expressionInParentheses)
+      ;
+
+  expressionInParentheses() =>
+        ref(token, '(') & ref(expression) & ref(token, ')')
+      ;
+
+  literal() => ref(token,
+        ref(NULL)
+      | ref(TRUE)
+      | ref(FALSE)
+      | ref(HEX_NUMBER)
+      | ref(NUMBER)
+      | ref(STRING))
+      ;
+
+  compoundLiteral() =>
+        ref(listLiteral)
+      | ref(mapLiteral)
+      ;
+
+  listLiteral() =>
+        ref(token, '[') & (ref(expressionList) & ref(token, ',').optional()).optional() & ref(token, ']')
+      ;
+
+  mapLiteral() =>
+        ref(token, '{') & (ref(mapLiteralEntry) & (ref(token, ',') & ref(mapLiteralEntry)).star() & ref(token, ',').optional()).optional() & ref(token, '}')
+      ;
+
+  mapLiteralEntry() =>
+        ref(token, STRING) & ref(token, ':') & ref(expression)
+      ;
+
+  functionExpression() =>
+        (ref(returnType).optional() & ref(identifier)).optional() & ref(formalParameterList) & ref(functionExpressionBody)
+      ;
+
+  functionDeclaration() =>
+        ref(returnType).optional() & ref(identifier) & ref(formalParameterList)
+      ;
+
+  functionPrefix() =>
+        ref(returnType).optional() & ref(identifier)
+      ;
+
+  functionBody() =>
+        ref(token, '=>') & ref(expression) & ref(token, ';')
+      | ref(block)
+      ;
+
+  functionExpressionBody() =>
+        ref(token, '=>') & ref(expression)
+      | ref(block)
+      ;
+
+  // -----------------------------------------------------------------
+  // Library files.
+  // -----------------------------------------------------------------
+  libraryUnit() =>
+        ref(libraryDefinition).end()
+      ;
+
+  libraryDefinition() =>
+        ref(LIBRARY) & ref(token, '{') & ref(libraryBody) & ref(token, '}')
+      ;
+
+  libraryBody() =>
+        ref(libraryImport).optional() & ref(librarySource).optional()
+      ;
+
+  libraryImport() =>
+        ref(IMPORT) & ref(token, '=') & ref(token, '[') & ref(importReferences).optional() & ref(token, ']')
+      ;
+
+  importReferences() =>
+        ref(importReference) & (ref(token, ',') & ref(importReference)).star() & ref(token, ',').optional()
+      ;
+
+  importReference() =>
+        (ref(token, IDENTIFIER) & ref(token, ':')).optional() & ref(token, STRING)
+      ;
+
+  librarySource() =>
+        ref(SOURCE) & ref(token, '=') & ref(token, '[') & ref(sourceUrls).optional() & ref(token, ']')
+      ;
+
+  sourceUrls() =>
+        ref(token, STRING) & (ref(token, ',') & ref(token, STRING)).star() & ref(token, ',').optional()
+      ;
+
+
+  // -----------------------------------------------------------------
+  // Lexical tokens.
+  // -----------------------------------------------------------------
+  IDENTIFIER_NO_DOLLAR() =>
+        ref(IDENTIFIER_START_NO_DOLLAR) & ref(IDENTIFIER_PART_NO_DOLLAR).star()
+      ;
+
+  IDENTIFIER() =>
+        ref(IDENTIFIER_START) & ref(IDENTIFIER_PART).star()
+      ;
+
+  HEX_NUMBER() =>
+        string('0x') & ref(HEX_DIGIT).plus()
+      | string('0X') & ref(HEX_DIGIT).plus()
+      ;
+
+  NUMBER() =>
+        ref(DIGIT).plus() & ref(NUMBER_OPT_FRACTIONAL_PART) & ref(EXPONENT).optional() & ref(NUMBER_OPT_ILLEGAL_END)
+      | char('.') & ref(DIGIT).plus() & ref(EXPONENT).optional() & ref(NUMBER_OPT_ILLEGAL_END)
+      ;
+
+  NUMBER_OPT_FRACTIONAL_PART() =>
+        char('.') & ref(DIGIT).plus()
+      | epsilon()
+      ;
+
+  NUMBER_OPT_ILLEGAL_END() => epsilon();
+//        ref(IDENTIFIER_START).end()
+//      | epsilon()
+//      ;
+
+  HEX_DIGIT() => pattern('0-9a-fA-F');
+
+  IDENTIFIER_START() => ref(IDENTIFIER_START_NO_DOLLAR) | char('\$');
+
+  IDENTIFIER_START_NO_DOLLAR() => ref(LETTER) | char('_');
+
+  IDENTIFIER_PART_NO_DOLLAR() => ref(IDENTIFIER_START_NO_DOLLAR) | ref(DIGIT);
+
+  IDENTIFIER_PART() => ref(IDENTIFIER_START) | ref(DIGIT);
+
+  LETTER() => letter();
+
+  DIGIT() => digit();
+
+  EXPONENT() => pattern('eE') & pattern('+-').optional() & ref(DIGIT).plus();
+
+  STRING() =>
+        char('@').optional() & ref(MULTI_LINE_STRING)
+      | ref(SINGLE_LINE_STRING)
+      ;
+
+  MULTI_LINE_STRING() =>
+        string('"""') & any().starLazy(string('"""')) & string('"""')
+      | string("'''") & any().starLazy(string("'''")) & string("'''")
+      ;
+
+  SINGLE_LINE_STRING() =>
+        char('"') & ref(STRING_CONTENT_DQ).star() & char('"')
+      | char("'") & ref(STRING_CONTENT_SQ).star() & char("'")
+      | string('@"') & pattern('^"\n\r').star() & char('"')
+      | string("@'") & pattern("^'\n\r").star() & char("'")
+      ;
+
+  STRING_CONTENT_DQ() =>
+        pattern('^\\"\n\r')
+      | char('\\') & pattern('\n\r')
+      ;
+
+  STRING_CONTENT_SQ() =>
+        pattern("^\\'\n\r")
+      | char('\\') & pattern('\n\r')
+      ;
+
+  NEWLINE() => pattern('\n\r');
+
+  HASHBANG() => string('#!') & pattern('^\n\r').star() & ref(NEWLINE).optional();
+
+
+  // -----------------------------------------------------------------
+  // Whitespace and comments.
+  // -----------------------------------------------------------------
+  HIDDEN() => ref(HIDDEN_STUFF).plus();
+
+  HIDDEN_STUFF() => ref(WHITESPACE)
+      | ref(SINGLE_LINE_COMMENT)
+      | ref(MULTI_LINE_COMMENT)
+      ;
+
+  WHITESPACE() => whitespace();
+
+  SINGLE_LINE_COMMENT() => string('//')
+      & ref(NEWLINE).neg().star()
+      & ref(NEWLINE).optional()
+      ;
+
+  MULTI_LINE_COMMENT() => string('/*')
+      & (ref(MULTI_LINE_COMMENT) | string('*/').neg()).star() & string('*/')
+      ;
+
+}
diff --git a/petitparser/lib/src/debug/continuation.dart b/petitparser/lib/src/debug/continuation.dart
new file mode 100644
index 0000000..b969f7e
--- /dev/null
+++ b/petitparser/lib/src/debug/continuation.dart
@@ -0,0 +1,42 @@
+part of debug;
+
+/**
+ * Handler function for the [ContinuationParser].
+ */
+typedef Result ContinuationHandler(
+    Result continuation(Context context), Context context);
+
+/**
+ * Continuation parser that when activated captures a continuation function
+ * and passes it together with the current context into the handler.
+ *
+ * Handlers are not required to call the continuation, but can completely ignore
+ * it, call it multiple times, and/or store it away for later use. Similarly
+ * handlers can modify the current context and/or modify the returned result.
+ *
+ * The following example shows a simple wrapper. Messages are printed before and
+ * after the `digit()` parser is activated:
+ *
+ *     var wrapped = digit();
+ *     var parser = new ContinuationParser(wrapped, (continuation, context) {
+ *       print('Parser will be activated, the context is $context.');
+ *       var result = continuation(context);
+ *       print('Parser was activated, the result is $result.');
+ *       return result;
+ *     });
+ *
+ * See [profile], [progress], and [trace] for more elaborate examples.
+ */
+class ContinuationParser extends DelegateParser {
+  final ContinuationHandler handler;
+
+  ContinuationParser(parser, this.handler) : super(parser);
+
+  @override
+  Result parseOn(Context context) {
+    return handler((result) => super.parseOn(result), context);
+  }
+
+  @override
+  Parser copy() => new ContinuationParser(children[0], handler);
+}
diff --git a/petitparser/lib/src/debug/profile.dart b/petitparser/lib/src/debug/profile.dart
new file mode 100644
index 0000000..7c28556
--- /dev/null
+++ b/petitparser/lib/src/debug/profile.dart
@@ -0,0 +1,49 @@
+part of debug;
+
+/**
+ * Returns a transformed [parser] that when being used measures
+ * the activation count and total time of each parser.
+ *
+ * For example, the snippet
+ *
+ *     var parser = letter() & word().star();
+ *     profile(parser).parse('f1234567890');
+ *
+ * produces the following output:
+ *
+ *      1  2006  Instance of 'SequenceParser'
+ *      1   697  Instance of 'PossessiveRepeatingParser'[0..*]
+ *     11   406  Instance of 'CharacterParser'[letter or digit expected]
+ *      1   947  Instance of 'CharacterParser'[letter expected]
+ *
+ * The first number refers to the number of activations of each parser, and
+ * the second number is the microseconds spent in this parser and all its
+ * children.
+ */
+Parser profile(Parser root, [OutputHandler output = print]) {
+  var count = new Map();
+  var watch = new Map();
+  var parsers = new List();
+  return new ContinuationParser(transformParser(root, (parser) {
+    parsers.add(parser);
+    return new ContinuationParser(parser, (continuation, context) {
+      count[parser]++;
+      watch[parser].start();
+      var result = continuation(context);
+      watch[parser].stop();
+      return result;
+    });
+  }), (continuation, context) {
+    parsers.forEach((parser) {
+      count[parser] = 0;
+      watch[parser] = new Stopwatch();
+    });
+    var result = continuation(context);
+    parsers.forEach((parser) {
+      output('${count[parser]}\t'
+          '${watch[parser].elapsedMicroseconds}\t'
+          '${parser}');
+    });
+    return result;
+  });
+}
diff --git a/petitparser/lib/src/debug/progress.dart b/petitparser/lib/src/debug/progress.dart
new file mode 100644
index 0000000..2cddf4e
--- /dev/null
+++ b/petitparser/lib/src/debug/progress.dart
@@ -0,0 +1,32 @@
+part of debug;
+
+/**
+ * Returns a transformed [parser] that when being used to read input
+ * visually prints its progress while progressing.
+ *
+ * For example, the snippet
+ *
+ *     var parser = letter() & word().star();
+ *     progress(parser).parse('f123');
+ *
+ * produces the following output:
+ *
+ *     * Instance of 'SequenceParser'
+ *     * Instance of 'CharacterParser'[letter expected]
+ *     ** Instance of 'PossessiveRepeatingParser'[0..*]
+ *     ** Instance of 'CharacterParser'[letter or digit expected]
+ *     *** Instance of 'CharacterParser'[letter or digit expected]
+ *     **** Instance of 'CharacterParser'[letter or digit expected]
+ *     ***** Instance of 'CharacterParser'[letter or digit expected]
+ *
+ * Jumps backwards mean that the parser is back-tracking. Often choices can
+ * be reordered to such expensive parses.
+ */
+Parser progress(Parser parser, [OutputHandler output = print]) {
+  return transformParser(parser, (each) {
+    return new ContinuationParser(each, (continuation, context) {
+      output('${_repeat(1 + context.position, '*')} $each');
+      return continuation(context);
+    });
+  });
+}
diff --git a/petitparser/lib/src/debug/trace.dart b/petitparser/lib/src/debug/trace.dart
new file mode 100644
index 0000000..8e8d7c1
--- /dev/null
+++ b/petitparser/lib/src/debug/trace.dart
@@ -0,0 +1,41 @@
+part of debug;
+
+/**
+ * Returns a transformed [parser] that when being used to read input prints a
+ * trace of all activated parsers and their respective parse results.
+ *
+ * For example, the snippet
+ *
+ *     var parser = letter() & word().star();
+ *     trace(parser).parse('f1');
+ *
+ * produces the following output:
+ *
+ *     Instance of 'SequenceParser'
+ *       Instance of 'CharacterParser'[letter expected]
+ *       Success[1:2]: f
+ *       Instance of 'PossessiveRepeatingParser'[0..*]
+ *         Instance of 'CharacterParser'[letter or digit expected]
+ *         Success[1:3]: 1
+ *         Instance of 'CharacterParser'[letter or digit expected]
+ *         Failure[1:3]: letter or digit expected
+ *       Success[1:3]: [1]
+ *     Success[1:3]: [f, [1]]
+ *
+ * Indentation signifies the activation of a parser object. Reverse indentation
+ * signifies the returning of a parse result either with a success or failure
+ * context.
+ */
+Parser trace(Parser parser, [OutputHandler output = print]) {
+  var level = 0;
+  return transformParser(parser, (each) {
+    return new ContinuationParser(each, (continuation, context) {
+      output('${_repeat(level, '  ')}${each}');
+      level++;
+      var result = continuation(context);
+      level--;
+      output('${_repeat(level, '  ')}${result}');
+      return result;
+    });
+  });
+}
diff --git a/petitparser/lib/src/json/grammar.dart b/petitparser/lib/src/json/grammar.dart
new file mode 100644
index 0000000..7777d1f
--- /dev/null
+++ b/petitparser/lib/src/json/grammar.dart
@@ -0,0 +1,69 @@
+part of json;
+
+/**
+ * JSON grammar.
+ */
+class JsonGrammar extends GrammarParser {
+  JsonGrammar() : super(new JsonGrammarDefinition());
+}
+
+/**
+ * JSON grammar definition.
+ */
+class JsonGrammarDefinition extends GrammarDefinition {
+
+  final _escapeTable = const {
+    '\\': '\\',
+    '/': '/',
+    '"': '"',
+    'b': '\b',
+    'f': '\f',
+    'n': '\n',
+    'r': '\r',
+    't': '\t'
+  };
+
+  start() => ref(value).end();
+  token(p) => p.flatten().trim();
+
+  array() => ref(token, char('['))
+      & ref(elements).optional()
+      & ref(token, char(']'));
+  elements() => ref(value).separatedBy(ref(token, char(',')), includeSeparators: false);
+  members() => ref(pair).separatedBy(ref(token, char(',')), includeSeparators: false);
+  object() => ref(token, char('{'))
+      & ref(members).optional()
+      & ref(token, char('}'));
+  pair() => ref(stringToken)
+      & ref(token, char(':'))
+      & ref(value);
+  value() => ref(stringToken)
+      | ref(numberToken)
+      | ref(object)
+      | ref(array)
+      | ref(trueToken)
+      | ref(falseToken)
+      | ref(nullToken);
+
+  trueToken() => ref(token, string('true'));
+  falseToken() => ref(token, string('false'));
+  nullToken() => ref(token, string('null'));
+  stringToken() => ref(token, ref(stringPrimitive));
+  numberToken() => ref(token, ref(numberPrimitive));
+
+  characterPrimitive() => ref(characterNormal)
+      | ref(characterEscape)
+      | ref(characterOctal);
+  characterNormal() => pattern('^"\\');
+  characterEscape() => char('\\')
+      & pattern(new List.from(_escapeTable.keys).join());
+  characterOctal() => string('\\u').seq(pattern("0-9A-Fa-f").times(4).flatten());
+  numberPrimitive() => char('-').optional()
+      & char('0').or(digit().plus())
+      & char('.').seq(digit().plus()).optional()
+      & pattern('eE').seq(pattern('-+').optional()).seq(digit().plus()).optional();
+  stringPrimitive() => char('"')
+      & ref(characterPrimitive).star()
+      & char('"');
+
+}
diff --git a/petitparser/lib/src/json/parser.dart b/petitparser/lib/src/json/parser.dart
new file mode 100644
index 0000000..2ed351e
--- /dev/null
+++ b/petitparser/lib/src/json/parser.dart
@@ -0,0 +1,46 @@
+part of json;
+
+/**
+ * JSON parser.
+ */
+class JsonParser extends GrammarParser {
+  JsonParser() : super(new JsonParserDefinition());
+}
+
+/**
+ * JSON parser definition.
+ */
+class JsonParserDefinition extends JsonGrammarDefinition {
+
+  array() => super.array().map((each) => each[1] != null ? each[1] : new List());
+  object() => super.object().map((each) {
+    var result = new LinkedHashMap();
+    if (each[1] != null) {
+      for (var element in each[1]) {
+        result[element[0]] = element[2];
+      }
+    }
+    return result;
+  });
+
+  trueToken() => super.trueToken().map((each) => true);
+  falseToken() => super.falseToken().map((each) => false);
+  nullToken() => super.nullToken().map((each) => null);
+  stringToken() => ref(stringPrimitive).trim();
+  numberToken() => super.numberToken().map((each) {
+    var floating = double.parse(each);
+    var integral = floating.toInt();
+    if (floating == integral && each.indexOf('.') == -1) {
+      return integral;
+    } else {
+      return floating;
+    }
+  });
+
+  stringPrimitive() => super.stringPrimitive().map((each) => each[1].join());
+  characterEscape() => super.characterEscape().map((each) => _escapeTable[each[1]]);
+  characterOctal() => super.characterOctal().map((each) {
+    throw new UnsupportedError('Octal characters not supported yet');
+  });
+
+}
diff --git a/petitparser/lib/src/lisp/cons.dart b/petitparser/lib/src/lisp/cons.dart
new file mode 100644
index 0000000..bb0463c
--- /dev/null
+++ b/petitparser/lib/src/lisp/cons.dart
@@ -0,0 +1,44 @@
+part of lisp;
+
+/**
+ * The basic data structure of LISP.
+ */
+class Cons {
+
+  /** The head of the cons. */
+  dynamic head;
+
+  /** The tail of the cons. */
+  dynamic tail;
+
+  /** Constructs a cons. */
+  Cons(this.head, this.tail);
+
+  @override
+  bool operator ==(other) {
+    return other is Cons && head == other.head && tail == other.tail;
+  }
+
+  @override
+  int get hashCode => 31 * head.hashCode + tail.hashCode;
+
+  @override
+  String toString() {
+    var buffer = new StringBuffer();
+    buffer.write('(');
+    var current = this;
+    while (current is Cons) {
+      buffer.write(current.head.toString());
+      current = current.tail;
+      if (current != null) {
+        buffer.write(' ');
+      }
+    }
+    if (current != null) {
+      buffer.write('. ');
+      buffer.write(current);
+    }
+    buffer.write(')');
+    return buffer.toString();
+  }
+}
diff --git a/petitparser/lib/src/lisp/environment.dart b/petitparser/lib/src/lisp/environment.dart
new file mode 100644
index 0000000..79d2b08
--- /dev/null
+++ b/petitparser/lib/src/lisp/environment.dart
@@ -0,0 +1,57 @@
+part of lisp;
+
+/**
+ * Environment of bindings.
+ */
+class Environment {
+
+  /** The owning environment. */
+  final Environment _owner;
+
+  /** The internal environment bindings. */
+  final Map<Name, dynamic> _bindings;
+
+  /** Constructor for the nested environment. */
+  Environment([this._owner]) : _bindings = new Map();
+
+  /** Constructor for a nested environment. */
+  Environment create() => new Environment(this);
+
+  /** Return the binding for [key]. */
+  operator [](Name key) {
+    if (_bindings.containsKey(key)) {
+      return _bindings[key];
+    } else if (_owner != null) {
+      return _owner[key];
+    } else {
+      return _invalidBinding(key);
+    }
+  }
+
+  /** Updates the binding for [key] with a [value]. */
+  void operator []=(Name key, value) {
+    if (_bindings.containsKey(key)) {
+      _bindings[key] = value;
+    } else if (_owner != null) {
+      _owner[key] = value;
+    } else {
+      _invalidBinding(key);
+    }
+  }
+
+  /** Defines a new binding from [key] to [value]. */
+  define(Name key, value) {
+    return _bindings[key] = value;
+  }
+
+  /** Returns the keys of the bindings. */
+  Iterable<Name> get keys => _bindings.keys;
+
+  /** Returns the parent of the bindings. */
+  Environment get owner => _owner;
+
+  /** Called when a missing binding is accessed. */
+  _invalidBinding(Name key) {
+    throw new ArgumentError('Unknown binding for $key');
+  }
+}
diff --git a/petitparser/lib/src/lisp/grammar.dart b/petitparser/lib/src/lisp/grammar.dart
new file mode 100644
index 0000000..7fc508d
--- /dev/null
+++ b/petitparser/lib/src/lisp/grammar.dart
@@ -0,0 +1,59 @@
+part of lisp;
+
+/**
+ * LISP grammar.
+ */
+class LispGrammar extends GrammarParser {
+  LispGrammar() : super(new LispGrammarDefinition());
+}
+
+/**
+ * LISP grammar definition.
+ */
+class LispGrammarDefinition extends GrammarDefinition {
+
+  start() => ref(atom).star().end();
+
+  atom() => ref(atom_).trim(ref(space));
+  atom_() => ref(list)
+      | ref(number)
+      | ref(string)
+      | ref(symbol)
+      | ref(quote)
+      | ref(quasiquote)
+      | ref(unquote)
+      | ref(splice);
+
+  list() => ref(bracket, '()', ref(cells))
+      | ref(bracket, '[]', ref(cells))
+      | ref(bracket, '{}', ref(cells));
+  cells() => ref(cell)
+      | ref(empty);
+  cell() => ref(atom) & ref(cells);
+  empty() => ref(space).star();
+
+  number() => ref(number_).flatten();
+  number_() => anyIn('-+').optional()
+      & char('0').or(digit().plus())
+      & char('.').seq(digit().plus()).optional()
+      & anyIn('eE').seq(anyIn('-+').optional()).seq(digit().plus()).optional();
+
+  string() => ref(bracket, '""', ref(character).star());
+  character() => ref(characterEscape) | ref(characterRaw);
+  characterEscape() => char('\\') & any();
+  characterRaw() => pattern('^"');
+
+  symbol() => ref(symbol_).flatten();
+  symbol_() => pattern('a-zA-Z!#\$%&*/:<=>?@\\^_|~+-')
+      & pattern('a-zA-Z0-9!#\$%&*/:<=>?@\\^_|~+-').star();
+
+  quote() => char('\'') & ref(list);
+  quasiquote() => char('`') & ref(list);
+  unquote() => char(',') & ref(list);
+  splice() => char('@') & ref(list);
+
+  space() => whitespace() | ref(comment);
+  comment() => char(';') & Token.newlineParser().neg().star();
+  bracket(String brackets, Parser parser) => char(brackets[0]) & parser & char(brackets[1]);
+
+}
diff --git a/petitparser/lib/src/lisp/name.dart b/petitparser/lib/src/lisp/name.dart
new file mode 100644
index 0000000..c3046ab
--- /dev/null
+++ b/petitparser/lib/src/lisp/name.dart
@@ -0,0 +1,24 @@
+part of lisp;
+
+/**
+ * An unique symbolic name.
+ */
+class Name {
+
+  /** The interned symbols. */
+  static final Map<String, Name> _interned = new HashMap();
+
+  /** Factory for new symbol cells. */
+  factory Name(String name) {
+    return _interned.putIfAbsent(name, () => new Name._internal(name));
+  }
+
+  /** The name of the symbol. */
+  final String _name;
+
+  /** Internal constructor for symbol. */
+  Name._internal(this._name);
+
+  /** Returns the string representation of the symbolic name. */
+  String toString() => _name;
+}
diff --git a/petitparser/lib/src/lisp/natives.dart b/petitparser/lib/src/lisp/natives.dart
new file mode 100644
index 0000000..ef15e05
--- /dev/null
+++ b/petitparser/lib/src/lisp/natives.dart
@@ -0,0 +1,256 @@
+part of lisp;
+
+/**
+ * The native functions.
+ */
+class Natives {
+
+  /** Imports the native functions into the [environment]. */
+  static Environment import(Environment environment) {
+
+    // basic functions
+    environment.define(new Name('define'), _define);
+    environment.define(new Name('lambda'), _lambda);
+    environment.define(new Name('quote'), _quote);
+    environment.define(new Name('eval'), _eval);
+    environment.define(new Name('apply'), _apply);
+    environment.define(new Name('let'), _let);
+    environment.define(new Name('set!'), _set);
+    environment.define(new Name('print'), _print);
+
+    // control structures
+    environment.define(new Name('if'), _if);
+    environment.define(new Name('while'), _while);
+    environment.define(new Name('and'), _and);
+    environment.define(new Name('or'), _or);
+    environment.define(new Name('not'), _not);
+
+    // arithmetic operators
+    environment.define(new Name('+'), _plus);
+    environment.define(new Name('-'), _minus);
+    environment.define(new Name('*'), _multiply);
+    environment.define(new Name('/'), _divide);
+    environment.define(new Name('%'), _modulo);
+
+    // arithmetic comparators
+    environment.define(new Name('<'), _smaller);
+    environment.define(new Name('<='), _smallerOrEqual);
+    environment.define(new Name('='), _equal);
+    environment.define(new Name('!='), _notEqual);
+    environment.define(new Name('>'), _larger);
+    environment.define(new Name('>='), _largerOrEqual);
+
+    // list operators
+    environment.define(new Name('cons'), _cons);
+    environment.define(new Name('car'), _car);
+    environment.define(new Name('car!'), _carSet);
+    environment.define(new Name('cdr'), _cdr);
+    environment.define(new Name('cdr!'), _cdrSet);
+
+    return environment;
+  }
+
+  static _define(Environment env, args) {
+    if (args.head is Name) {
+      return env.define(args.head, evalList(env, args.tail));
+    } else if (args.head.head is Name) {
+      return env.define(
+          args.head.head, _lambda(env, new Cons(args.head.tail, args.tail)));
+    } else {
+      throw new ArgumentError('Invalid define: $args');
+    }
+  }
+
+  static _lambda(Environment lambda_env, lambda_args) {
+    return (Environment env, args) {
+      var inner = lambda_env.create();
+      var names = lambda_args.head;
+      var values = evalArguments(env, args);
+      while (names != null && values != null) {
+        inner.define(names.head, values.head);
+        names = names.tail;
+        values = values.tail;
+      }
+      return evalList(inner, lambda_args.tail);
+    };
+  }
+
+  static _quote(Environment env, args) {
+    return args;
+  }
+
+  static _eval(Environment env, args) {
+    return eval(env.create(), eval(env, args.head));
+  }
+
+  static _apply(Environment env, args) {
+    return eval(env, args.head)(env.create(), args.tail);
+  }
+
+  static _let(Environment env, args) {
+    var inner = env.create();
+    var binding = args.head;
+    while (binding != null) {
+      inner.define(binding.head.head, eval(env, binding.head.tail.head));
+      binding = binding.tail;
+    }
+    return evalList(inner, args.tail);
+  }
+
+  static _set(Environment env, args) {
+    return env[args.head] = eval(env, args.tail.head);
+  }
+
+  static _print(Environment env, args) {
+    var buffer = new StringBuffer();
+    while (args != null) {
+      buffer.write(eval(env, args.head));
+      args = args.tail;
+    }
+    print(buffer);
+    return null;
+  }
+
+  static _if(Environment env, args) {
+    var condition = eval(env, args.head);
+    if (condition) {
+      if (args.tail != null) {
+        return eval(env, args.tail.head);
+      }
+    } else {
+      if (args.tail != null && args.tail.tail != null) {
+        return eval(env, args.tail.tail.head);
+      }
+    }
+    return null;
+  }
+
+  static _while(Environment env, args) {
+    var result = null;
+    while (eval(env, args.head)) {
+      result = evalList(env, args.tail);
+    }
+    return result;
+  }
+
+  static _and(Environment env, args) {
+    while (args != null) {
+      if (!eval(env, args.head)) {
+        return false;
+      }
+      args = args.tail;
+    }
+    return true;
+  }
+
+  static _or(Environment env, args) {
+    while (args != null) {
+      if (eval(env, args.head)) {
+        return true;
+      }
+      args = args.tail;
+    }
+    return false;
+  }
+
+  static _not(Environment env, args) {
+    return !eval(env, args.head);
+  }
+
+  static _plus(Environment env, args) {
+    var value = eval(env, args.head);
+    for (args = args.tail; args != null; args = args.tail) {
+      value += eval(env, args.head);
+    }
+    return value;
+  }
+
+  static _minus(Environment env, args) {
+    var value = eval(env, args.head);
+    if (args.tail == null) {
+      return -value;
+    }
+    for (args = args.tail; args != null; args = args.tail) {
+      value -= eval(env, args.head);
+    }
+    return value;
+  }
+
+  static _multiply(Environment env, args) {
+    var value = eval(env, args.head);
+    for (args = args.tail; args != null; args = args.tail) {
+      value *= eval(env, args.head);
+    }
+    return value;
+  }
+
+  static _divide(Environment env, args) {
+    var value = eval(env, args.head);
+    for (args = args.tail; args != null; args = args.tail) {
+      value /= eval(env, args.head);
+    }
+    return value;
+  }
+
+  static _modulo(Environment env, args) {
+    var value = eval(env, args.head);
+    for (args = args.tail; args != null; args = args.tail) {
+      value %= eval(env, args.head);
+    }
+    return value;
+  }
+
+  static _smaller(Environment env, args) {
+    return eval(env, args.head) < eval(env, args.tail.head);
+  }
+
+  static _smallerOrEqual(Environment env, args) {
+    return eval(env, args.head) <= eval(env, args.tail.head);
+  }
+
+  static _equal(Environment env, args) {
+    return eval(env, args.head) == eval(env, args.tail.head);
+  }
+
+  static _notEqual(Environment env, args) {
+    return eval(env, args.head) != eval(env, args.tail.head);
+  }
+
+  static _larger(Environment env, args) {
+    return eval(env, args.head) > eval(env, args.tail.head);
+  }
+
+  static _largerOrEqual(Environment env, args) {
+    return eval(env, args.head) >= eval(env, args.tail.head);
+  }
+
+  static _cons(Environment env, args) {
+    return new Cons(eval(env, args.head), eval(env, args.tail.head));
+  }
+
+  static _car(Environment env, args) {
+    var cons = eval(env, args.head);
+    return cons is Cons ? cons.head : null;
+  }
+
+  static _carSet(Environment env, args) {
+    var cons = eval(env, args.head);
+    if (cons is Cons) {
+      cons.head = eval(env, args.tail.head);
+    }
+    return cons;
+  }
+
+  static _cdr(Environment env, args) {
+    var cons = eval(env, args.head);
+    return cons is Cons ? cons.tail : null;
+  }
+
+  static _cdrSet(Environment env, args) {
+    var cons = eval(env, args.head);
+    if (cons is Cons) {
+      cons.tail = eval(env, args.tail.head);
+    }
+    return cons;
+  }
+}
diff --git a/petitparser/lib/src/lisp/parser.dart b/petitparser/lib/src/lisp/parser.dart
new file mode 100644
index 0000000..aa00c18
--- /dev/null
+++ b/petitparser/lib/src/lisp/parser.dart
@@ -0,0 +1,37 @@
+part of lisp;
+
+/**
+ * LISP parser.
+ */
+class LispParser extends GrammarParser {
+  LispParser() : super(new LispParserDefinition());
+}
+
+/**
+ * LISP parser definition.
+ */
+class LispParserDefinition extends LispGrammarDefinition {
+
+  list() => super.list().map((each) => each[1]);
+
+  cell() => super.cell().map((each) => new Cons(each[0], each[1]));
+  empty() => super.empty().map((each) => null);
+
+  string() => super.string().map((each) => new String.fromCharCodes(each[1]));
+  characterEscape() => super.characterEscape().map((each) => each[1].codeUnitAt(0));
+  characterRaw() => super.characterRaw().map((each) => each.codeUnitAt(0));
+
+  symbol() => super.symbol().map((each) => new Name(each));
+  number() => super.number().map((each) {
+    var floating = double.parse(each);
+    var integral = floating.toInt();
+    if (floating == integral && each.indexOf('.') == -1) {
+      return integral;
+    } else {
+      return floating;
+    }
+  });
+
+  quote() => super.quote().map((each) => new Cons(Natives._quote, each[1]));
+
+}
diff --git a/petitparser/lib/src/lisp/standard.dart b/petitparser/lib/src/lisp/standard.dart
new file mode 100644
index 0000000..7286c79
--- /dev/null
+++ b/petitparser/lib/src/lisp/standard.dart
@@ -0,0 +1,68 @@
+part of lisp;
+
+/**
+ * The standard library.
+ */
+class Standard {
+
+  /** Imports the standard library into the [environment]. */
+  static Environment import(Environment environment) {
+    evalString(lispParser, environment, _standardLibrary);
+    return environment;
+  }
+
+  /** A simple standard library, should be moved to external file. */
+  static String _standardLibrary = """
+; null functions
+(define null '())
+(define (null? x) (= '() x))
+
+; booleans
+(define true (and))
+(define false (or))
+
+; list functions
+(define (length list)
+  (if (null? list)
+      0
+      (+ 1 (length (cdr list)))))
+
+(define (append list1 list2)
+  (if (null? list1)
+    list2
+    (cons (car list1) (append (cdr list1) list2))))
+
+(define (list-head list index)
+  (if (= index 0)
+    (car list)
+    (list-head
+      (cdr list)
+      (- index 1))))
+
+(define (list-tail list index)
+  (if (= index 0)
+    (cdr list)
+    (list-tail
+      (cdr list)
+      (- index 1))))
+
+(define (for-each list proc)
+  (while (not (null? list))
+    (proc (car list))
+    (set! list (cdr list))))
+
+(define (map list proc)
+  (if (null? list)
+    '()
+    (cons (proc (car list))
+          (map (cdr list) proc))))
+
+(define (inject list value proc)
+  (if (null? list)
+    value
+    (inject
+      (cdr list)
+      (proc value (car list))
+      proc)))
+""";
+}
diff --git a/petitparser/lib/src/reflection/iterable.dart b/petitparser/lib/src/reflection/iterable.dart
new file mode 100644
index 0000000..b6579ff
--- /dev/null
+++ b/petitparser/lib/src/reflection/iterable.dart
@@ -0,0 +1,52 @@
+part of reflection;
+
+/**
+ * Returns a lazy iterable over all parsers reachable from a [root].
+ *
+ * For example, the following code prints the two parsers of the
+ * defined grammar:
+ *
+ *     var parser = range('0', '9').star();
+ *     allParser(parser).forEach((each) {
+ *       print(each);
+ *     });
+ *
+ */
+Iterable<Parser> allParser(Parser root) => new _ParserIterable(root);
+
+class _ParserIterable extends IterableBase<Parser> {
+  final Parser root;
+
+  _ParserIterable(this.root);
+
+  @override
+  Iterator<Parser> get iterator => new _ParserIterator([root]);
+}
+
+class _ParserIterator implements Iterator<Parser> {
+  final List<Parser> todo;
+  final Set<Parser> seen;
+
+  _ParserIterator(Iterable<Parser> roots)
+      : todo = new List.from(roots),
+        seen = new Set.from(roots);
+
+  @override
+  Parser current;
+
+  @override
+  bool moveNext() {
+    if (todo.isEmpty) {
+      current = null;
+      return false;
+    }
+    current = todo.removeLast();
+    for (var parser in current.children) {
+      if (!seen.contains(parser)) {
+        todo.add(parser);
+        seen.add(parser);
+      }
+    }
+    return true;
+  }
+}
diff --git a/petitparser/lib/src/reflection/optimize.dart b/petitparser/lib/src/reflection/optimize.dart
new file mode 100644
index 0000000..1ae77e7
--- /dev/null
+++ b/petitparser/lib/src/reflection/optimize.dart
@@ -0,0 +1,31 @@
+part of reflection;
+
+/**
+ * Returns a copy of [parser] with all settable parsers removed.
+ */
+Parser removeSettables(Parser parser) {
+  return transformParser(parser, (each) {
+    while (each is SettableParser) {
+      each = each.children.first;
+    }
+    return each;
+  });
+}
+
+/**
+ * Returns a copy of [parser] with all duplicates parsers collapsed.
+ */
+Parser removeDuplicates(Parser parser) {
+  var uniques = new Set();
+  return transformParser(parser, (source) {
+    var target = uniques.firstWhere((each) {
+      return source != each && source.isEqualTo(each);
+    }, orElse: () => null);
+    if (target == null) {
+      uniques.add(source);
+      return source;
+    } else {
+      return target;
+    }
+  });
+}
diff --git a/petitparser/lib/src/reflection/transform.dart b/petitparser/lib/src/reflection/transform.dart
new file mode 100644
index 0000000..7206ea4
--- /dev/null
+++ b/petitparser/lib/src/reflection/transform.dart
@@ -0,0 +1,35 @@
+part of reflection;
+
+/**
+ * A function transforming one parser to another one.
+ */
+typedef Parser TransformationHandler(Parser parser);
+
+/**
+ * Transforms all parsers reachable from [parser] with the given [handler].
+ * The identity function returns a copy of the the incoming parser.
+ *
+ * The implementation first creates a copy of each parser reachable in the
+ * input grammar; then the resulting grammar is traversed until all references
+ * to old parsers are replaced with the transformed ones.
+ */
+Parser transformParser(Parser parser, TransformationHandler handler) {
+  var mapping = new Map.identity();
+  for (var each in allParser(parser)) {
+    mapping[each] = handler(each.copy());
+  }
+  var seen = new Set.from(mapping.values);
+  var todo = new List.from(mapping.values);
+  while (todo.isNotEmpty) {
+    var parent = todo.removeLast();
+    for (var child in parent.children) {
+      if (mapping.containsKey(child)) {
+        parent.replace(child, mapping[child]);
+      } else if (!seen.contains(child)) {
+        seen.add(child);
+        todo.add(child);
+      }
+    }
+  }
+  return mapping[parser];
+}
diff --git a/petitparser/lib/src/smalltalk/grammar.dart b/petitparser/lib/src/smalltalk/grammar.dart
new file mode 100644
index 0000000..1f77c1e
--- /dev/null
+++ b/petitparser/lib/src/smalltalk/grammar.dart
@@ -0,0 +1,229 @@
+part of smalltalk;
+
+/**
+ * Smalltalk grammar.
+ */
+class SmalltalkGrammar extends GrammarParser {
+  SmalltalkGrammar() : super(new SmalltalkGrammarDefinition());
+}
+
+/**
+ * Smalltalk grammar definition.
+ */
+class SmalltalkGrammarDefinition extends GrammarDefinition {
+
+  // the original implementation used a handwritten parser to
+  // build special token objects
+  token(input) {
+    if (input is String) {
+      input = input.length == 1 ? char(input) : string(input);
+    }
+    return input.token().trim(ref(spacer));
+  }
+
+  // the original implementation uses a handwritten parser to
+  // efficiently consume whitespace and comments
+  spacer() => whitespace()
+      .or(ref(comment));
+  comment() => char('"')
+      .seq(char('"').neg().star())
+      .seq(char('"'));
+
+  // the original implementation uses the hand written number
+  // parser of the system, this is the spec of the ANSI standard
+  number() => char('-').optional()
+      .seq(ref(positiveNumber));
+  positiveNumber() => ref(scaledDecimal)
+      .or(ref(float))
+      .or(ref(integer));
+
+  integer() => ref(radixInteger)
+      .or(ref(decimalInteger));
+  decimalInteger() => ref(digits);
+  digits() => digit().plus();
+  radixInteger() => ref(radixSpecifier)
+      .seq(char('r'))
+      .seq(ref(radixDigits));
+  radixSpecifier() => ref(digits);
+  radixDigits() => pattern('0-9A-Z').plus();
+
+  float() => ref(mantissa)
+      .seq(ref(exponentLetter)
+          .seq(ref(exponent))
+          .optional());
+  mantissa() => ref(digits)
+      .seq(char('.'))
+      .seq(ref(digits));
+  exponent() => char('-')
+      .seq(ref(decimalInteger));
+  exponentLetter() => pattern('edq');
+
+  scaledDecimal() => ref(scaledMantissa)
+      .seq(char('s'))
+      .seq(ref(fractionalDigits).optional());
+  scaledMantissa() => ref(decimalInteger)
+      .or(ref(mantissa));
+  fractionalDigits() => ref(decimalInteger);
+
+  // the original smalltalk grammar
+  array() => ref(token, '{')
+      .seq(ref(expression).separatedBy(ref(periodToken))
+        .seq(ref(periodToken).optional()).optional())
+      .seq(ref(token, '}'));
+  arrayItem() => ref(literal)
+      .or(ref(symbolLiteralArray))
+      .or(ref(arrayLiteralArray))
+      .or(ref(byteLiteralArray));
+  arrayLiteral() => ref(token, '#(')
+      .seq(ref(arrayItem).star())
+      .seq(ref(token, ')'));
+  arrayLiteralArray() => ref(token, '(')
+      .seq(ref(arrayItem).star())
+      .seq(ref(token, ')'));
+  assignment() => ref(variable)
+      .seq(ref(assignmentToken));
+  assignmentToken() => ref(token, ':=');
+  binary() => pattern('!%&*+,-/<=>?@\\|~').plus();
+  binaryExpression() => ref(unaryExpression)
+      .seq(ref(binaryMessage).star());
+  binaryMessage() => ref(binaryToken)
+      .seq(ref(unaryExpression));
+  binaryMethod() => ref(binaryToken)
+      .seq(ref(variable));
+  binaryPragma() => ref(binaryToken)
+      .seq(ref(arrayItem));
+  binaryToken() => ref(token, ref(binary));
+  block() => ref(token, '[')
+      .seq(ref(blockBody))
+      .seq(ref(token, ']'));
+  blockArgument() => ref(token, ':')
+      .seq(ref(variable));
+  blockArguments() => ref(blockArgumentsWith)
+      .or(ref(blockArgumentsWithout));
+  blockArgumentsWith() => ref(blockArgument).plus()
+      .seq(ref(token, '|').or(ref(token, ']').and()));
+  blockArgumentsWithout() => epsilon();
+  blockBody() => ref(blockArguments)
+      .seq(ref(sequence));
+  byteLiteral() => ref(token, '#[')
+      .seq(ref(numberLiteral).star())
+      .seq(ref(token, ']'));
+  byteLiteralArray() => ref(token, '[')
+      .seq(ref(numberLiteral).star())
+      .seq(ref(token, ']'));
+  cascadeExpression() => ref(keywordExpression)
+      .seq(ref(cascadeMessage).star());
+  cascadeMessage() => ref(token, ';')
+      .seq(ref(message));
+  character() => char('\$').seq(any());
+  characterLiteral() => ref(characterToken);
+  characterToken() => ref(token, ref(character));
+  expression() => ref(assignment).star()
+      .seq(ref(cascadeExpression));
+  falseLiteral() => ref(falseToken);
+  falseToken() => ref(token, 'false')
+      .seq(word().not());
+  identifier() => pattern('a-zA-Z_')
+      .seq(word().star());
+  identifierToken() => ref(token, ref(identifier));
+  keyword() => ref(identifier)
+      .seq(char(':'));
+  keywordExpression() => ref(binaryExpression)
+      .seq(ref(keywordMessage).optional());
+  keywordMessage() => ref(keywordToken)
+      .seq(ref(binaryExpression)).plus();
+  keywordMethod() => ref(keywordToken)
+      .seq(ref(variable)).plus();
+  keywordPragma() => ref(keywordToken)
+      .seq(ref(arrayItem)).plus();
+  keywordToken() => ref(token, ref(keyword));
+  literal() => ref(numberLiteral)
+      .or(ref(stringLiteral))
+      .or(ref(characterLiteral))
+      .or(ref(arrayLiteral))
+      .or(ref(byteLiteral))
+      .or(ref(symbolLiteral))
+      .or(ref(nilLiteral))
+      .or(ref(trueLiteral))
+      .or(ref(falseLiteral));
+  message() => ref(keywordMessage)
+      .or(ref(binaryMessage))
+      .or(ref(unaryMessage));
+  method() => ref(methodDeclaration)
+      .seq(ref(methodSequence));
+  methodDeclaration() => ref(keywordMethod)
+      .or(ref(unaryMethod))
+      .or(ref(binaryMethod));
+  methodSequence() => ref(periodToken).star()
+      .seq(ref(pragmas))
+      .seq(ref(periodToken).star())
+      .seq(ref(temporaries))
+      .seq(ref(periodToken).star())
+      .seq(ref(pragmas))
+      .seq(ref(periodToken).star())
+      .seq(ref(statements));
+  multiword() => ref(keyword).plus();
+  nilLiteral() => ref(nilToken);
+  nilToken() => ref(token, 'nil')
+      .seq(word().not());
+  numberLiteral() => ref(numberToken);
+  numberToken() => ref(token, ref(number));
+  parens() => ref(token, '(')
+      .seq(ref(expression))
+      .seq(ref(token, ')'));
+  period() => char('.');
+  periodToken() => ref(token, ref(period));
+  pragma() => ref(token, '<')
+      .seq(ref(pragmaMessage))
+      .seq(ref(token, '>'));
+  pragmaMessage() => ref(keywordPragma)
+      .or(ref(unaryPragma))
+      .or(ref(binaryPragma));
+  pragmas() => ref(pragma).star();
+  primary() => ref(literal)
+      .or(ref(variable))
+      .or(ref(block))
+      .or(ref(parens))
+      .or(ref(array));
+  answer() => ref(token, '^')
+      .seq(ref(expression));
+  sequence() => ref(temporaries)
+      .seq(ref(periodToken).star())
+      .seq(ref(statements));
+  start() => ref(startMethod);
+  startMethod() => ref(method).end();
+  statements() => ref(expression)
+      .seq(ref(periodToken).plus().seq(ref(statements))
+          .or(ref(periodToken).star()))
+          .or(ref(answer).seq(ref(periodToken).star()))
+          .or(ref(periodToken).star());
+  string_() => char('\'')
+      .seq(string('\'\'').or(pattern('^\'')).star())
+      .seq(char('\''));
+  stringLiteral() => ref(stringToken);
+  stringToken() => ref(token, ref(string_));
+  symbol() => ref(unary)
+      .or(ref(binary))
+      .or(ref(multiword))
+      .or(ref(string_));
+  symbolLiteral() => ref(token, '#').plus()
+      .seq(ref(token, ref(symbol)));
+  symbolLiteralArray() => ref(token, ref(symbol));
+  temporaries() => ref(token, '|')
+      .seq(ref(variable).star())
+      .seq(ref(token, '|'))
+          .optional();
+  trueLiteral() => ref(trueToken);
+  trueToken() => ref(token, 'true')
+      .seq(word().not());
+  unary() => ref(identifier)
+      .seq(char(':').not());
+  unaryExpression() => ref(primary)
+      .seq(ref(unaryMessage).star());
+  unaryMessage() => ref(unaryToken);
+  unaryMethod() => ref(identifierToken);
+  unaryPragma() => ref(identifierToken);
+  unaryToken() => ref(token, ref(unary));
+  variable() => ref(identifierToken);
+
+}
diff --git a/petitparser/lib/test.dart b/petitparser/lib/test.dart
new file mode 100644
index 0000000..a142e82
--- /dev/null
+++ b/petitparser/lib/test.dart
@@ -0,0 +1,98 @@
+/**
+ * This package contains matches to write tests for parsers.
+ *
+ * Examples:
+ *
+ *     var json = new JsonParser();
+ *
+ *     // verifies that the input gets parsed and all input is consumed
+ *     expect('{"a": 1}', accepts(new JsonParser()));
+ *
+ *     // verifies that the input gets parsed to a dictionary and that all input is consumed
+ *     expect('{"a": 1}', parses(new JsonParser(), {'a': 1}));
+ */
+
+library test_util;
+
+import 'package:matcher/matcher.dart';
+import 'package:petitparser/petitparser.dart';
+
+/**
+ * Returns a matcher that succeeds if the [parser] accepts the input.
+ */
+Matcher accept(Parser parser) {
+  return new _Accept(parser);
+}
+
+class _Accept extends Matcher {
+  final Parser parser;
+
+  _Accept(this.parser);
+
+  bool matches(item, Map matchState) => parser.accept(item);
+
+  Description describe(Description description) {
+    return description.add('$parser to accept input');
+  }
+}
+
+/**
+ * Returns a matcher that succeeds if the [parser] results in [matcher].
+ */
+Matcher parse(Parser parser, matcher, [int position = -1]) {
+  return new _Parse(parser, wrapMatcher(matcher), position);
+}
+
+class _Parse extends Matcher {
+  final Parser parser;
+  final Matcher matcher;
+  final int position;
+
+  _Parse(this.parser, this.matcher, this.position);
+
+  bool matches(item, Map matchState) {
+    Result result = parser.parse(item);
+    if (!matcher.matches(result.value, matchState)) {
+      addStateInfo(matchState, {'property': 'value', 'result': result});
+      return false;
+    }
+    if (position >= 0 &&
+        !equals(position).matches(result.position, matchState)) {
+      addStateInfo(matchState, {'property': 'position', 'result': result});
+      return false;
+    }
+    return true;
+  }
+
+  Description describe(Description description) {
+    return description.add('$parser to accept ').addDescriptionOf(matcher);
+  }
+
+  Description describeMismatch(
+      item, Description mismatchDescription, Map matchState, bool verbose) {
+    mismatchDescription
+        .add('has parse result ')
+        .add('"${matchState['result']}"');
+    if (matchState['property'] == 'value') {
+      mismatchDescription.add(' which parse result ');
+      var subDescription = new StringDescription();
+      matcher.describeMismatch(matchState['result'].value, subDescription,
+          matchState['state'], verbose);
+      if (subDescription.length > 0) {
+        mismatchDescription.add(subDescription.toString());
+      } else {
+        mismatchDescription.add('doesn\'t match');
+        matcher.describe(mismatchDescription);
+      }
+      return mismatchDescription;
+    } else if (matchState['property'] == 'position') {
+      mismatchDescription
+          .add(' that consumes input to ')
+          .add(matchState['result'].position.toString())
+          .add(' instead of ')
+          .add(position.toString());
+      return mismatchDescription;
+    }
+    throw new Exception('Internal matcher error');
+  }
+}
diff --git a/petitparser/pubspec.yaml b/petitparser/pubspec.yaml
new file mode 100644
index 0000000..4c3930b
--- /dev/null
+++ b/petitparser/pubspec.yaml
@@ -0,0 +1,12 @@
+name: petitparser
+version: 1.3.7
+author: Lukas Renggli <renggli@gmail.com>
+description: Dynamic parser combinator framework.
+homepage: https://github.com/renggli/dart-petitparser
+environment:
+  sdk: '>=1.0.0 <2.0.0'
+documentation: http://jenkins.lukas-renggli.ch/job/dart-petitparser/javadoc
+dev_dependencies:
+  browser: '>=0.10.0 <0.11.0'
+  junitconfiguration: '>=1.0.0 <2.0.0'
+  unittest: '>=0.11.0 <0.12.0'
diff --git a/polymer/lib/auto_binding.dart b/polymer/lib/auto_binding.dart
new file mode 100644
index 0000000..97965e6
--- /dev/null
+++ b/polymer/lib/auto_binding.dart
@@ -0,0 +1,117 @@
+// 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 polymer.auto_binding;
+
+import 'dart:html';
+import 'package:polymer/polymer.dart';
+import 'package:template_binding/template_binding.dart';
+
+/**
+ * The `auto-binding-dart` element extends the template element. It provides a
+ * quick and easy way to do data binding without the need to setup a binding
+ * delegate or use the [templateBind] call. Both data and event handlers can be
+ * bound using the [model].
+ *
+ * The `auto-binding-dart` element acts just like a template that is bound to
+ * a model. It stamps its content in the dom adjacent to itself. When the
+ * content is stamped, the `template-bound` event is fired.
+ *
+ * **NOTE**: It is a good idea to use an id to select these elements. During
+ * code transformation it is likely that other template elements will be
+ * inserted at the top of the body above yours, so something like
+ * querySelector('template') is likely to break when built.
+ * See http://dartbug.com/20911.
+ *
+ * Example:
+ *
+ *     <template id="my-template" is="auto-binding-dart">
+ *       <div>Say something: <input value="{{value}}"></div>
+ *       <div>You said: {{value}}</div>
+ *       <button on-tap="{{buttonTap}}">Tap me!</button>
+ *     </template>
+ *     <script type="application/dart">
+ *       import 'dart:html';
+ *       import 'package:polymer/polymer.dart';
+ *
+ *       main() {
+ *         var template = document.querySelector('#my-template');
+ *         template.model = new MyModel();
+ *       }
+ *
+ *       class MyModel {
+ *         String value = 'something';
+ *         buttonTap() => console.log('tap!');
+ *       }
+ *     </script>
+ *
+ */
+// Dart note: renamed to avoid conflict with JS auto-binding.
+class AutoBindingElement extends TemplateElement with Polymer, Observable
+    implements TemplateBindExtension {
+
+  /// Make template_binding "extension methods" friendly.
+  /// Note that [NodeBindExtension] is already implemented by [Polymer].
+  TemplateBindExtension _self;
+
+  get model => _self.model;
+  set model(value) {
+    _self.model = value;
+  }
+
+  BindingDelegate get bindingDelegate => _self.bindingDelegate;
+  set bindingDelegate(BindingDelegate value) {
+    _self.bindingDelegate = value;
+  }
+
+  void clear() => _self.clear();
+
+  @override
+  PolymerExpressions get syntax => bindingDelegate;
+
+  AutoBindingElement.created() : super.created() {
+    polymerCreated();
+
+    _self = templateBindFallback(this);
+
+    bindingDelegate = makeSyntax();
+
+    // delay stamping until polymer-ready so that auto-binding is not
+    // required to load last.
+    Polymer.onReady.then((_) {
+      attributes['bind'] = '';
+      // we don't bother with an explicit signal here, we could ust a MO
+      // if necessary
+      async((_) {
+        // note: this will marshall *all* the elements in the parentNode
+        // rather than just stamped ones. We'd need to use createInstance
+        // to fix this or something else fancier.
+        marshalNodeReferences(parentNode);
+        // template stamping is asynchronous so stamping isn't complete
+        // by polymer-ready; fire an event so users can use stamped elements
+        fire('template-bound');
+      });
+    });
+  }
+
+  PolymerExpressions makeSyntax() => new _AutoBindingSyntax(this);
+
+  DocumentFragment createInstance([model, BindingDelegate delegate]) =>
+      _self.createInstance(model, delegate);
+
+  @override
+  dispatchMethod(obj, method, args) {
+    // Dart note: make sure we dispatch to the model, not ourselves.
+    if (identical(obj, this)) obj = model;
+    return super.dispatchMethod(obj, method, args);
+  }
+}
+
+// Dart note: this is implemented a little differently to keep it in classic
+// OOP style. Instead of monkeypatching findController, override it.
+class _AutoBindingSyntax extends PolymerExpressions {
+  final AutoBindingElement _node;
+  _AutoBindingSyntax(this._node) : super();
+  @override findController(_) => _node;
+}
diff --git a/polymer/lib/builder.dart b/polymer/lib/builder.dart
new file mode 100644
index 0000000..9434485
--- /dev/null
+++ b/polymer/lib/builder.dart
@@ -0,0 +1,358 @@
+// Copyright (c) 2012, 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.
+
+/// Common logic to make it easy to run the polymer linter and deploy tool.
+///
+/// The functions in this library are designed to make it easier to create
+/// `build.dart` files. A `build.dart` file is a Dart script that can be invoked
+/// from the command line, but that can also invoked automatically by the Dart
+/// Editor whenever a file in your project changes or when selecting some menu
+/// options, such as 'Reanalyze Sources'.
+///
+/// To work correctly, place the `build.dart` in the root of your project (where
+/// pubspec.yaml lives). The file must be named exactly `build.dart`.
+///
+/// It's quite likely that in the near future `build.dart` will be replaced with
+/// something else.  For example, `pub deploy` will deal with deploying
+/// applications automatically, and the Dart Editor might provide other
+/// mechanisms to hook linters.
+///
+/// There are three important functions exposed by this library [build], [lint],
+/// and [deploy]. The following examples show common uses of these functions
+/// when writing a `build.dart` file.
+///
+/// **Example 1**: Uses build.dart to run the linter tool.
+///
+///     import 'dart:io';
+///     import 'package:polymer/builder.dart';
+///
+///     main() {
+///        lint();
+///     }
+///
+/// **Example 2**: Runs the linter and creates a deployable version of the app
+/// every time.
+///
+///     import 'dart:io';
+///     import 'package:polymer/builder.dart';
+///
+///     main() {
+///        deploy(); // deploy also calls the linter internally.
+///     }
+///
+/// **Example 3**: Always run the linter, but conditionally build a deployable
+/// version. See [parseOptions] for a description of options parsed
+/// automatically by this helper library.
+///
+///     import 'dart:io';
+///     import 'package:polymer/builder.dart';
+///
+///     main(args) {
+///        var options = parseOptions(args);
+///        if (options.forceDeploy) {
+///          deploy();
+///        } else {
+///          lint();
+///        }
+///     }
+///
+/// **Example 4**: Same as above, but uses [build] (which internally calls
+/// either [lint] or [deploy]).
+///
+///     import 'dart:io';
+///     import 'package:polymer/builder.dart';
+///
+///     main(args) {
+///        build(options: parseOptions(args));
+///     }
+///
+/// **Example 5**: Like the previous example, but indicates to the linter and
+/// deploy tool which files are actually used as entry point files. See the
+/// documentation of [build] below for more details.
+///
+///     import 'dart:io';
+///     import 'package:polymer/builder.dart';
+///
+///     main(args) {
+///        build(entryPoints: ['web/index.html'], options: parseOptions(args));
+///     }
+library polymer.builder;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:args/args.dart';
+import 'package:path/path.dart' as path;
+import 'package:yaml/yaml.dart';
+
+import 'src/build/linter.dart';
+import 'src/build/runner.dart';
+import 'src/build/common.dart';
+
+import 'transformer.dart';
+
+/// Runs the polymer linter on any relevant file in your package, such as any
+/// .html file under 'lib/', 'asset/', and 'web/'. And, if requested, creates a
+/// directory suitable for deploying a Polymer application to a server.
+///
+/// The [entryPoints] list contains files under web/ that should be treated as
+/// entry points. Each entry on this list is a relative path from the package
+/// root (for example 'web/index.html'). If null, all files under 'web/' are
+/// treated as possible entry points.
+///
+/// Options must be passed by
+/// passing the [options] argument. The deploy operation is run only when the
+/// command-line argument `--deploy` is present, or equivalently when
+/// `options.forceDeploy` is true.
+///
+/// The linter and deploy steps needs to know the name of the [currentPackage]
+/// and the location where to find the code for any package it depends on
+/// ([packageDirs]). This is inferred automatically, but can be overriden if
+/// those arguments are provided.
+Future build({List<String> entryPoints, CommandLineOptions options,
+    String currentPackage, Map<String, String> packageDirs}) {
+  if (options == null) {
+    print('warning: now that main takes arguments, you need to explicitly pass'
+        ' options to build(). Running as if no options were passed.');
+    options = parseOptions([]);
+  }
+  if (entryPoints == null) entryPoints = _parseEntryPointsFromPubspec();
+
+  return options.forceDeploy
+      ? deploy(
+          entryPoints: entryPoints,
+          options: options,
+          currentPackage: currentPackage,
+          packageDirs: packageDirs)
+      : lint(
+          entryPoints: entryPoints,
+          options: options,
+          currentPackage: currentPackage,
+          packageDirs: packageDirs);
+}
+
+/// Runs the polymer linter on any relevant file in your package,
+/// such as any .html file under 'lib/', 'asset/', and 'web/'.
+///
+/// The [entryPoints] list contains files under web/ that should be treated as
+/// entry points. Each entry on this list is a relative path from the package
+/// root (for example 'web/index.html'). If null, all files under 'web/' are
+/// treated as possible entry points.
+///
+/// Options must be passed by passing the [options] argument.
+///
+/// The linter needs to know the name of the [currentPackage] and the location
+/// where to find the code for any package it depends on ([packageDirs]). This
+/// is inferred automatically, but can be overriden by passing the arguments.
+Future lint({List<String> entryPoints, CommandLineOptions options,
+    String currentPackage, Map<String, String> packageDirs}) {
+  if (options == null) {
+    print('warning: now that main takes arguments, you need to explicitly pass'
+        ' options to lint(). Running as if no options were passed.');
+    options = parseOptions([]);
+  }
+  if (currentPackage == null) currentPackage = readCurrentPackageFromPubspec();
+  if (entryPoints == null) entryPoints = _parseEntryPointsFromPubspec();
+  var linterOptions = new TransformOptions(entryPoints: entryPoints);
+  var linter = new Linter(linterOptions, skipMissingElementWarning: true);
+
+  return runBarback(new BarbackOptions([[linter]], null,
+      currentPackage: currentPackage,
+      packageDirs: packageDirs,
+      machineFormat: options.machineFormat));
+}
+
+/// Creates a directory suitable for deploying a Polymer application to a
+/// server.
+///
+/// **Note**: this function will be replaced in the future by the `pub deploy`
+/// command.
+///
+/// The [entryPoints] list contains files under web/ that should be treated as
+/// entry points. Each entry on this list is a relative path from the package
+/// root (for example 'web/index.html'). If null, all files under 'web/' are
+/// treated as possible entry points.
+///
+/// Options must be passed by passing the [options] list.
+///
+/// The deploy step needs to know the name of the [currentPackage] and the
+/// location where to find the code for any package it depends on
+/// ([packageDirs]). This is inferred automatically, but can be overriden if
+/// those arguments are provided.
+Future deploy({List<String> entryPoints, CommandLineOptions options,
+    String currentPackage, Map<String, String> packageDirs}) {
+  if (options == null) {
+    print('warning: now that main takes arguments, you need to explicitly pass'
+        ' options to deploy(). Running as if no options were passed.');
+    options = parseOptions([]);
+  }
+  if (currentPackage == null) currentPackage = readCurrentPackageFromPubspec();
+  if (entryPoints == null) entryPoints = _parseEntryPointsFromPubspec();
+
+  var transformOptions = new TransformOptions(
+      entryPoints: entryPoints,
+      directlyIncludeJS: options.directlyIncludeJS,
+      contentSecurityPolicy: options.contentSecurityPolicy,
+      releaseMode: options.releaseMode);
+
+  var phases = new PolymerTransformerGroup(transformOptions).phases;
+  var barbackOptions = new BarbackOptions(phases, options.outDir,
+      currentPackage: currentPackage,
+      packageDirs: packageDirs,
+      machineFormat: options.machineFormat,
+      // TODO(sigmund): include here also smoke transformer when it's on by
+      // default.
+      packagePhases: {'polymer': phasesForPolymer});
+  return runBarback(barbackOptions)
+      .then((_) => print('Done! All files written to "${options.outDir}"'));
+}
+
+/// Options that may be used either in build.dart or by the linter and deploy
+/// tools.
+class CommandLineOptions {
+  /// Files marked as changed.
+  final List<String> changedFiles;
+
+  /// Files marked as removed.
+  final List<String> removedFiles;
+
+  /// Whether to clean intermediate artifacts, if any.
+  final bool clean;
+
+  /// Whether to do a full build (as if all files have changed).
+  final bool full;
+
+  /// Whether to print results using a machine parseable format.
+  final bool machineFormat;
+
+  /// Whether the force deploy option was passed in the command line.
+  final bool forceDeploy;
+
+  /// Location where to generate output files.
+  final String outDir;
+
+  /// True to use the CSP-compliant JS file.
+  final bool contentSecurityPolicy;
+
+  /// True to include the JS script tag directly, without the
+  /// "packages/browser/dart.js" trampoline.
+  final bool directlyIncludeJS;
+
+  /// Run transformers in release mode. For instance, uses the minified versions
+  /// of the web_components polyfill.
+  final bool releaseMode;
+
+  CommandLineOptions(this.changedFiles, this.removedFiles, this.clean,
+      this.full, this.machineFormat, this.forceDeploy, this.outDir,
+      this.directlyIncludeJS, this.contentSecurityPolicy, this.releaseMode);
+}
+
+/// Parse command-line arguments and return a [CommandLineOptions] object. The
+/// following flags are parsed by this method.
+///
+///   * `--changed file-path`: notify of a file change.
+///   * `--removed file-path`: notify that a file was removed.
+///   * `--clean`: remove temporary artifacts (if any)
+///   * `--full`: build everything, similar to marking every file as changed
+///   * `--machine`: produce output that can be parsed by tools, such as the
+///     Dart Editor.
+///   * `--deploy`: force deploy.
+///   * `--no-js`: deploy replaces *.dart scripts with *.dart.js. You can turn
+///     this feature off with --no-js, which leaves "packages/browser/dart.js".
+///   * `--csp`: extracts inlined JavaScript code to comply with Content
+///     Security Policy restrictions.
+///   * `--help`: print documentation for each option and exit.
+///
+/// Currently not all the flags are used by [lint] or [deploy] above, but they
+/// are available so they can be used from your `build.dart`. For instance, see
+/// the top-level library documentation for an example that uses the
+/// force-deploy option to conditionally call [deploy].
+///
+/// If this documentation becomes out of date, the best way to discover which
+/// flags are supported is to invoke this function from your build.dart, and run
+/// it with the `--help` command-line flag.
+CommandLineOptions parseOptions([List<String> args]) {
+  if (args == null) {
+    print('warning: the list of arguments from main(List<String> args) now '
+        'needs to be passed explicitly to parseOptions.');
+    args = [];
+  }
+  var parser = new ArgParser()
+    ..addOption('changed',
+        help: 'The file has changed since the last build.', allowMultiple: true)
+    ..addOption('removed',
+        help: 'The file was removed since the last build.', allowMultiple: true)
+    ..addFlag('clean',
+        negatable: false, help: 'Remove any build artifacts (if any).')
+    ..addFlag('full', negatable: false, help: 'perform a full build')
+    ..addFlag('machine',
+        negatable: false,
+        help: 'Produce warnings in a machine parseable format.')
+    ..addFlag('deploy', negatable: false, help: 'Whether to force deploying.')
+    ..addOption('out',
+        abbr: 'o', help: 'Directory to generate files into.', defaultsTo: 'out')
+    ..addFlag('js',
+        help: 'deploy replaces *.dart scripts with *.dart.js. This flag \n'
+        'leaves "packages/browser/dart.js" to do the replacement at runtime.',
+        defaultsTo: true)
+    ..addFlag('csp', help: 'extracts inlined JavaScript code to comply with \n'
+        'Content Security Policy restrictions.')
+    ..addFlag('debug',
+        help: 'run in debug mode. For example, use the debug polyfill \n'
+        'web_components/webcomponents.js instead of the minified one.\n',
+        defaultsTo: false)
+    ..addFlag('help',
+        abbr: 'h', negatable: false, help: 'Displays this help and exit.');
+
+  showUsage() {
+    print('Usage: dart build.dart [options]');
+    print('\nThese are valid options expected by build.dart:');
+    print(parser.getUsage());
+  }
+
+  var res;
+  try {
+    res = parser.parse(args);
+  } on FormatException catch (e) {
+    print(e.message);
+    showUsage();
+    exit(1);
+  }
+  if (res['help']) {
+    print('A build script that invokes the polymer linter and deploy tools.');
+    showUsage();
+    exit(0);
+  }
+  return new CommandLineOptions(res['changed'], res['removed'], res['clean'],
+      res['full'], res['machine'], res['deploy'], res['out'], res['js'],
+      res['csp'], !res['debug']);
+}
+
+List<String> _parseEntryPointsFromPubspec() {
+  var entryPoints = [];
+  var pubspec =
+      new File(path.join(path.dirname(Platform.script.path), 'pubspec.yaml'));
+  if (!pubspec.existsSync()) {
+    print('error: pubspec.yaml file not found.');
+    return null;
+  }
+  var transformers = loadYaml(pubspec.readAsStringSync())['transformers'];
+  if (transformers == null) return null;
+  if (transformers is! List) {
+    print('Unexpected value for transformers, expected a List.');
+    return null;
+  }
+
+  transformers.forEach((t) {
+    if (t is! Map) return;
+    var polymer = t['polymer'];
+    if (polymer == null || polymer is! Map) return;
+
+    var parsedEntryPoints = readFileList(polymer['entry_points']);
+    if (parsedEntryPoints == null) return;
+
+    entryPoints.addAll(parsedEntryPoints);
+  });
+  return entryPoints.isEmpty ? null : entryPoints;
+}
diff --git a/polymer/lib/default_build.dart b/polymer/lib/default_build.dart
new file mode 100644
index 0000000..8e1c246
--- /dev/null
+++ b/polymer/lib/default_build.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2013, 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 that defines a main which calls build() from builder.dart. For
+/// polymer projects the build.dart file can simply export this to make the
+/// linter run on all of the entry points defined in your pubspec.yaml.
+library polymer.default_build;
+
+import 'builder.dart';
+
+main(args) {
+  lint(options: parseOptions(args));
+}
diff --git a/polymer/lib/deploy.dart b/polymer/lib/deploy.dart
new file mode 100644
index 0000000..96fa5ad
--- /dev/null
+++ b/polymer/lib/deploy.dart
@@ -0,0 +1,188 @@
+// Copyright (c) 2013, 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.
+
+/// **Note**: If you already have a `build.dart` in your application, we
+/// recommend to use the `package:polymer/builder.dart` library instead.
+
+/// Temporary deploy command used to create a version of the app that can be
+/// compiled with dart2js and deployed. Following pub layout conventions, this
+/// script will treat any HTML file under a package 'web/' and 'test/'
+/// directories as entry points.
+///
+/// From an application package you can run deploy by creating a small program
+/// as follows:
+///
+///    import "package:polymer/deploy.dart" as deploy;
+///    main() => deploy.main();
+///
+/// This library should go away once `pub deploy` can be configured to run
+/// barback transformers.
+library polymer.deploy;
+
+import 'dart:io';
+
+import 'package:args/args.dart';
+import 'package:code_transformers/tests.dart' show testingDartSdkDirectory;
+import 'package:path/path.dart' as path;
+
+import 'src/build/common.dart'
+    show TransformOptions, LintOptions, phasesForPolymer;
+import 'src/build/runner.dart';
+import 'transformer.dart';
+
+main(List<String> arguments) {
+  var args = _parseArgs(arguments);
+  if (args == null) exit(1);
+
+  var test = args['test'];
+  var outDir = args['out'];
+  var filters = [];
+  if (args['file-filter'] != null) {
+    filters = args['file-filter'].split(',');
+  }
+
+  var options;
+  if (test == null) {
+    var transformOps = new TransformOptions(
+        directlyIncludeJS: args['js'],
+        contentSecurityPolicy: args['csp'],
+        releaseMode: !args['debug']);
+    var phases = createDeployPhases(transformOps);
+    options = new BarbackOptions(phases, outDir,
+        // TODO(sigmund): include here also smoke transformer when it's on by
+        // default.
+        packagePhases: {'polymer': phasesForPolymer});
+  } else {
+    options = _createTestOptions(
+        test, outDir, args['js'], args['csp'], !args['debug'], filters);
+  }
+  if (options == null) exit(1);
+
+  print('polymer/deploy.dart: creating a deploy target for '
+      '"${options.currentPackage}"');
+
+  runBarback(options)
+      .then((_) => print('Done! All files written to "$outDir"'))
+      .catchError(_reportErrorAndExit);
+}
+
+BarbackOptions _createTestOptions(String testFile, String outDir,
+    bool directlyIncludeJS, bool contentSecurityPolicy, bool releaseMode,
+    List<String> filters) {
+  var testDir = path.normalize(path.dirname(testFile));
+
+  // A test must be allowed to import things in the package.
+  // So we must find its package root, given the entry point. We can do this
+  // by walking up to find pubspec.yaml.
+  var pubspecDir = _findDirWithFile(path.absolute(testDir), 'pubspec.yaml');
+  if (pubspecDir == null) {
+    print('error: pubspec.yaml file not found, please run this script from '
+        'your package root directory or a subdirectory.');
+    return null;
+  }
+  var packageName = readCurrentPackageFromPubspec(pubspecDir);
+
+  // Find the dart-root so we can include all package dependencies and
+  // transformers from other packages.
+  var pkgDir = path.join(_findDirWithDir(path.absolute(testDir), 'pkg'), 'pkg');
+
+  var phases = createDeployPhases(new TransformOptions(
+      entryPoints: [path.relative(testFile, from: pubspecDir)],
+      directlyIncludeJS: directlyIncludeJS,
+      contentSecurityPolicy: contentSecurityPolicy,
+      releaseMode: releaseMode,
+      lint: new LintOptions.disabled()), sdkDir: testingDartSdkDirectory);
+  var dirs = {};
+  // Note: we include all packages in pkg/ to keep things simple. Ideally this
+  // should be restricted to the transitive dependencies of this package.
+  _subDirs(pkgDir).forEach((p) {
+    dirs[path.basename(p)] = p;
+  });
+  // Note: packageName may be a duplicate of 'polymer', but that's ok (they
+  // should be the same value).
+  dirs[packageName] = pubspecDir;
+  return new BarbackOptions(phases, outDir,
+      currentPackage: packageName, packageDirs: dirs,
+      // TODO(sigmund): include here also smoke transformer when it's on by
+      // default.
+      packagePhases: {'polymer': phasesForPolymer},
+      transformTests: true,
+      fileFilter: filters);
+}
+
+String _findDirWithFile(String dir, String filename) {
+  while (!new File(path.join(dir, filename)).existsSync()) {
+    var parentDir = path.dirname(dir);
+    // If we reached root and failed to find it, bail.
+    if (parentDir == dir) return null;
+    dir = parentDir;
+  }
+  return dir;
+}
+
+String _findDirWithDir(String dir, String subdir) {
+  while (!new Directory(path.join(dir, subdir)).existsSync()) {
+    var parentDir = path.dirname(dir);
+    // If we reached root and failed to find it, bail.
+    if (parentDir == dir) return null;
+    dir = parentDir;
+  }
+  return dir;
+}
+
+List<String> _subDirs(String dir) => new Directory(dir)
+    .listSync(followLinks: false)
+    .where((d) => d is Directory)
+    .map((d) => d.path)
+    .toList();
+
+void _reportErrorAndExit(e, trace) {
+  print('Uncaught error: $e');
+  if (trace != null) print(trace);
+  exit(1);
+}
+
+ArgResults _parseArgs(arguments) {
+  var parser = new ArgParser()
+    ..addFlag('help',
+        abbr: 'h',
+        help: 'Displays this help message.',
+        defaultsTo: false,
+        negatable: false)
+    ..addOption('out',
+        abbr: 'o', help: 'Directory to generate files into.', defaultsTo: 'out')
+    ..addOption('file-filter', help: 'Do not copy in files that match \n'
+        'these filters to the deployed folder, e.g., ".svn"', defaultsTo: null)
+    ..addOption('test', help: 'Deploy the test at the given path.\n'
+        'Note: currently this will deploy all tests in its directory,\n'
+        'but it will eventually deploy only the specified test.')
+    ..addFlag('js',
+        help: 'deploy replaces *.dart scripts with *.dart.js. This flag \n'
+        'leaves "packages/browser/dart.js" to do the replacement at runtime.',
+        defaultsTo: true)
+    ..addFlag('debug',
+        help: 'run in debug mode. For example, use the debug polyfill \n'
+        'web_components/webcomponents.js instead of the minified one.\n',
+        defaultsTo: false)
+    ..addFlag('csp', help: 'extracts inlined JavaScript code to comply with \n'
+        'Content Security Policy restrictions.');
+  try {
+    var results = parser.parse(arguments);
+    if (results['help']) {
+      _showUsage(parser);
+      return null;
+    }
+    return results;
+  } on FormatException catch (e) {
+    print(e.message);
+    _showUsage(parser);
+    return null;
+  }
+}
+
+_showUsage(parser) {
+  print('Usage: dart --package-root=packages/ '
+      'package:polymer/deploy.dart [options]');
+  print(parser.getUsage());
+}
diff --git a/polymer/lib/deserialize.dart b/polymer/lib/deserialize.dart
new file mode 100644
index 0000000..6d07aca
--- /dev/null
+++ b/polymer/lib/deserialize.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2013, 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 polymer.deserialize;
+
+import 'dart:convert' show JSON;
+
+final _typeHandlers = {
+  String: (x, _) => x,
+  Null: (x, _) => x,
+  DateTime: (x, def) {
+    // TODO(jmesserly): shouldn't need to try-catch here
+    // See: https://code.google.com/p/dart/issues/detail?id=1878
+    try {
+      return DateTime.parse(x);
+    } catch (e) {
+      return def;
+    }
+  },
+  bool: (x, _) => x != 'false',
+  int: (x, def) => int.parse(x, onError: (_) => def),
+  double: (x, def) => double.parse(x, (_) => def),
+};
+
+/// Convert representation of [value] based on [type] and [currentValue].
+Object deserializeValue(String value, Object currentValue, Type type) {
+  var handler = _typeHandlers[type];
+  if (handler != null) return handler(value, currentValue);
+
+  try {
+    // If the string is an object, we can parse is with the JSON library.
+    // include convenience replace for single-quotes. If the author omits
+    // quotes altogether, parse will fail.
+    return JSON.decode(value.replaceAll("'", '"'));
+
+    // TODO(jmesserly): deserialized JSON is not assignable to most objects in
+    // Dart. We should attempt to convert it appropriately.
+  } catch (e) {
+    // The object isn't valid JSON, return the raw value
+    return value;
+  }
+}
diff --git a/polymer/lib/html_element_names.dart b/polymer/lib/html_element_names.dart
new file mode 100644
index 0000000..c61ca36
--- /dev/null
+++ b/polymer/lib/html_element_names.dart
@@ -0,0 +1,128 @@
+// Copyright (c) 2012, 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 polymer.html_element_names;
+
+/**
+ * HTML element to DOM type mapping. Source:
+ * <http://dev.w3.org/html5/spec/section-index.html#element-interfaces>
+ *
+ * The 'HTML' prefix has been removed to match `dart:html`, as per:
+ * <http://code.google.com/p/dart/source/browse/branches/bleeding_edge/dart/lib/html/scripts/htmlrenamer.py>
+ * It does not appear any element types are being renamed other than the prefix.
+ * However there does not appear to be the last subtypes for the following tags:
+ * command, data, td, th, and time.
+ */
+const HTML_ELEMENT_NAMES = const {
+  'a': 'AnchorElement',
+  'abbr': 'Element',
+  'address': 'Element',
+  'area': 'AreaElement',
+  'article': 'Element',
+  'aside': 'Element',
+  'audio': 'AudioElement',
+  'b': 'Element',
+  'base': 'BaseElement',
+  'bdi': 'Element',
+  'bdo': 'Element',
+  'blockquote': 'QuoteElement',
+  'body': 'BodyElement',
+  'br': 'BRElement',
+  'button': 'ButtonElement',
+  'canvas': 'CanvasElement',
+  'caption': 'TableCaptionElement',
+  'cite': 'Element',
+  'code': 'Element',
+  'col': 'TableColElement',
+  'colgroup': 'TableColElement',
+  'command': 'Element', // see doc comment, was: 'CommandElement'
+  'data': 'Element', // see doc comment, was: 'DataElement'
+  'datalist': 'DataListElement',
+  'dd': 'Element',
+  'del': 'ModElement',
+  'details': 'DetailsElement',
+  'dfn': 'Element',
+  'dialog': 'DialogElement',
+  'div': 'DivElement',
+  'dl': 'DListElement',
+  'dt': 'Element',
+  'em': 'Element',
+  'embed': 'EmbedElement',
+  'fieldset': 'FieldSetElement',
+  'figcaption': 'Element',
+  'figure': 'Element',
+  'footer': 'Element',
+  'form': 'FormElement',
+  'h1': 'HeadingElement',
+  'h2': 'HeadingElement',
+  'h3': 'HeadingElement',
+  'h4': 'HeadingElement',
+  'h5': 'HeadingElement',
+  'h6': 'HeadingElement',
+  'head': 'HeadElement',
+  'header': 'Element',
+  'hgroup': 'Element',
+  'hr': 'HRElement',
+  'html': 'HtmlElement',
+  'i': 'Element',
+  'iframe': 'IFrameElement',
+  'img': 'ImageElement',
+  'input': 'InputElement',
+  'ins': 'ModElement',
+  'kbd': 'Element',
+  'keygen': 'KeygenElement',
+  'label': 'LabelElement',
+  'legend': 'LegendElement',
+  'li': 'LIElement',
+  'link': 'LinkElement',
+  'map': 'MapElement',
+  'mark': 'Element',
+  'menu': 'MenuElement',
+  'meta': 'MetaElement',
+  'meter': 'MeterElement',
+  'nav': 'Element',
+  'noscript': 'Element',
+  'object': 'ObjectElement',
+  'ol': 'OListElement',
+  'optgroup': 'OptGroupElement',
+  'option': 'OptionElement',
+  'output': 'OutputElement',
+  'p': 'ParagraphElement',
+  'param': 'ParamElement',
+  'pre': 'PreElement',
+  'progress': 'ProgressElement',
+  'q': 'QuoteElement',
+  'rp': 'Element',
+  'rt': 'Element',
+  'ruby': 'Element',
+  's': 'Element',
+  'samp': 'Element',
+  'script': 'ScriptElement',
+  'section': 'Element',
+  'select': 'SelectElement',
+  'small': 'Element',
+  'source': 'SourceElement',
+  'span': 'SpanElement',
+  'strong': 'Element',
+  'style': 'StyleElement',
+  'sub': 'Element',
+  'summary': 'Element',
+  'sup': 'Element',
+  'table': 'TableElement',
+  'tbody': 'TableSectionElement',
+  'td': 'TableCellElement', // see doc comment, was: 'TableDataCellElement'
+  'template': 'TemplateElement',
+  'textarea': 'TextAreaElement',
+  'tfoot': 'TableSectionElement',
+  'th': 'TableCellElement', // see doc comment, was: 'TableHeaderCellElement'
+  'thead': 'TableSectionElement',
+  'time': 'Element', // see doc comment, was: 'TimeElement'
+  'title': 'TitleElement',
+  'tr': 'TableRowElement',
+  'track': 'TrackElement',
+  'u': 'Element',
+  'ul': 'UListElement',
+  'var': 'Element',
+  'video': 'VideoElement',
+  'wbr': 'Element',
+};
diff --git a/polymer/lib/init.dart b/polymer/lib/init.dart
new file mode 100644
index 0000000..108a0ec
--- /dev/null
+++ b/polymer/lib/init.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2013, 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 that automatically initializes polymer elements without having to
+/// write a main for your application.
+///
+/// If a polymer application is written entirely with `<polymer-element>` tags
+/// and there is no initialization code that needs to happen before these
+/// elements are created, then, instead of creating your own `main`, you can
+/// simply include a script tag loading this library:
+///
+///    <script type="application/dart">export "package:polymer/init.dart";
+///    </script>
+///
+/// This script tag should be placed after all HTML imports on your page.
+library polymer.init;
+
+import 'dart:async';
+import 'package:polymer/polymer.dart';
+
+/// Returns a [Future<Zone>] that code should be executed in for dirty checking.
+/// The returned future will complete once polymer is ready and all @initMethod
+/// and @whenPolymerReady functions have been executed.
+Future<Zone> main() =>
+    initPolymer().then((zone) => Polymer.onReady.then((_) => zone));
diff --git a/polymer/lib/polymer.dart b/polymer/lib/polymer.dart
new file mode 100644
index 0000000..95f1af7
--- /dev/null
+++ b/polymer/lib/polymer.dart
@@ -0,0 +1,76 @@
+// 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.
+
+/// Custom HTML tags, data binding, and templates for building
+/// structured, encapsulated, client-side web apps.
+///
+/// Polymer.dart, the next evolution of Web UI,
+/// is an in-progress Dart port of the
+/// [Polymer project](http://www.polymer-project.org/).
+/// Polymer.dart compiles to JavaScript and runs across the modern web.
+///
+/// To use polymer.dart in your application,
+/// first add a
+/// [dependency](http://pub.dartlang.org/doc/dependencies.html)
+/// to the app's pubspec.yaml file.
+/// Instead of using the open-ended `any` version specifier,
+/// we recommend using a range of version numbers, as in this example:
+///
+///     dependencies:
+///       polymer: '>=0.7.1 <0.8'
+///
+/// Then import the library into your application:
+///
+///     import 'package:polymer/polymer.dart';
+///
+/// ## Other resources
+///
+/// * [Polymer.dart homepage](http://www.dartlang.org/polymer-dart/):
+/// Example code, project status, and
+/// information about how to get started using Polymer.dart in your apps.
+///
+/// * [polymer.dart package](http://pub.dartlang.org/packages/polymer):
+/// More details, such as the current major release number.
+///
+/// * [Upgrading to Polymer.dart](http://www.dartlang.org/polymer-dart/upgrading-to-polymer-from-web-ui.html):
+/// Tips for converting your apps from Web UI to Polymer.dart.
+@HtmlImport('polymer.html')
+library polymer;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:html';
+import 'dart:js' as js show context;
+import 'dart:js' hide context;
+
+import 'package:initialize/initialize.dart' hide run;
+import 'package:logging/logging.dart';
+import 'package:observe/observe.dart';
+import 'package:observe/src/dirty_check.dart' show dirtyCheckZone;
+import 'package:polymer_expressions/polymer_expressions.dart'
+    as polymer_expressions;
+import 'package:polymer_interop/polymer_interop.dart';
+import 'package:smoke/smoke.dart' as smoke;
+import 'package:template_binding/template_binding.dart';
+import 'package:web_components/web_components.dart';
+
+import 'auto_binding.dart';
+import 'deserialize.dart' as deserialize;
+
+export 'package:initialize/initialize.dart' show initMethod;
+export 'package:observe/observe.dart';
+export 'package:observe/html.dart';
+export 'package:web_components/web_components.dart' show HtmlImport;
+export 'auto_binding.dart';
+
+part 'src/declaration.dart';
+part 'src/events.dart';
+part 'src/initializers.dart';
+part 'src/instance.dart';
+part 'src/job.dart';
+part 'src/loader.dart';
+part 'src/property_accessor.dart';
+
+/// Call [configureForDeployment] to change this to true.
+bool _deployMode = false;
diff --git a/polymer/lib/polymer.html b/polymer/lib/polymer.html
new file mode 100644
index 0000000..621aa63
--- /dev/null
+++ b/polymer/lib/polymer.html
@@ -0,0 +1,15 @@
+<!--
+ Copyright 2013 The Polymer Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file.
+-->
+
+<!--
+This file is from the Polymer project (https://github.com/Polymer/polymer/).
+You can replace polymer.html with a different version if desired.
+-->
+<!-- minified for deployment: -->
+<link rel="import" href="../../packages/polymer_interop/polymer.html">
+<script>
+// This empty script tag is necessary to work around dartbug.com/19650
+</script>
diff --git a/polymer/lib/polymer_experimental.html b/polymer/lib/polymer_experimental.html
new file mode 100644
index 0000000..f2909de
--- /dev/null
+++ b/polymer/lib/polymer_experimental.html
@@ -0,0 +1,9 @@
+<!--
+ Copyright 2013 The Polymer Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file.
+-->
+<link rel="import" href="polymer.html">
+
+<!-- Experimental: bootstrap the user application in a new isolate. -->
+<script src="boot.js"></script>
diff --git a/polymer/lib/src/build/build_filter.dart b/polymer/lib/src/build/build_filter.dart
new file mode 100644
index 0000000..d1c4a60
--- /dev/null
+++ b/polymer/lib/src/build/build_filter.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2013, 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.
+
+/// Final phase of the polymer transformation: removes any files that are not
+/// needed for deployment.
+library polymer.src.build.build_filter;
+
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+import 'package:code_transformers/messages/build_logger.dart';
+import 'common.dart';
+
+/// Removes any files not needed for deployment, such as internal build
+/// artifacts and non-entry HTML files.
+class BuildFilter extends Transformer with PolymerTransformer {
+  final TransformOptions options;
+  BuildFilter(this.options);
+
+  isPrimary(AssetId id) {
+    // nothing is filtered in debug mode
+    return options.releaseMode &&
+        // TODO(sigmund): remove this exclusion once we have dev_transformers
+        // (dartbug.com/14187)
+        !id.path.startsWith('lib/') &&
+        // may filter non-entry HTML files and internal artifacts
+        (id.extension == '.html' || id.extension == _DATA_EXTENSION) &&
+        // keep any entry points
+        !options.isHtmlEntryPoint(id);
+  }
+
+  apply(Transform transform) {
+    transform.consumePrimary();
+    if (transform.primaryInput.id.extension == _DATA_EXTENSION) {
+      return null;
+    }
+    var logger = new BuildLogger(transform,
+        convertErrorsToWarnings: !options.releaseMode,
+        detailsUri: 'http://goo.gl/5HPeuP');
+    return readPrimaryAsHtml(transform, logger).then((document) {
+      // Keep .html files that don't use polymer, since the app developer might
+      // have non-polymer entrypoints.
+      if (document.querySelectorAll('polymer-element').isEmpty) {
+        transform.addOutput(transform.primaryInput);
+      }
+    });
+  }
+}
+
+const String _DATA_EXTENSION = '._data';
diff --git a/polymer/lib/src/build/build_log_combiner.dart b/polymer/lib/src/build/build_log_combiner.dart
new file mode 100644
index 0000000..33fc0f4
--- /dev/null
+++ b/polymer/lib/src/build/build_log_combiner.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2013, 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.
+
+/// Logic to combine all the ._buildLog.* logs into one ._buildLog file.
+library polymer.src.build.log_combiner;
+
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+import 'package:code_transformers/messages/build_logger.dart';
+
+import 'common.dart';
+
+/// Logic to combine all the ._buildLog.* logs into one ._buildLog file.
+class BuildLogCombiner extends Transformer with PolymerTransformer {
+  final TransformOptions options;
+
+  BuildLogCombiner(this.options);
+
+  /// Run only on entry point html files and only if
+  /// options.injectBuildLogsInOutput is true.
+  bool isPrimary(idOrAsset) {
+    if (!options.injectBuildLogsInOutput) return false;
+    var id = idOrAsset is AssetId ? idOrAsset : idOrAsset.id;
+    return options.isHtmlEntryPoint(id);
+  }
+
+  Future apply(Transform transform) {
+    // Combine all ._buildLogs* files into one ._buildLogs file.
+    return BuildLogger.combineLogFiles(transform);
+  }
+}
diff --git a/polymer/lib/src/build/common.dart b/polymer/lib/src/build/common.dart
new file mode 100644
index 0000000..31c63e0
--- /dev/null
+++ b/polymer/lib/src/build/common.dart
@@ -0,0 +1,280 @@
+// Copyright (c) 2013, 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.
+
+/// Common methods used by transfomers.
+library polymer.src.build.common;
+
+import 'dart:async';
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/error.dart';
+import 'package:analyzer/src/generated/parser.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+import 'package:barback/barback.dart';
+import 'package:code_transformers/messages/build_logger.dart';
+import 'package:html/dom.dart' show Document;
+import 'package:html/parser.dart' show HtmlParser;
+import 'package:observe/transformer.dart' show ObservableTransformer;
+import 'package:path/path.dart' as path;
+
+import 'constants.dart';
+import 'messages.dart';
+
+export 'constants.dart';
+
+const _ignoredErrors = const [
+  'unexpected-dash-after-double-dash-in-comment',
+  'unexpected-char-in-comment',
+];
+
+/// Parses an HTML file [contents] and returns a DOM-like tree. Adds emitted
+/// error/warning to [logger].
+Document _parseHtml(String contents, String sourcePath, BuildLogger logger,
+    {bool checkDocType: true, bool showWarnings: true}) {
+  // TODO(jmesserly): make HTTP encoding configurable
+  var parser = new HtmlParser(contents,
+      encoding: 'utf8', generateSpans: true, sourceUrl: sourcePath);
+  var document = parser.parse();
+
+  // Note: errors aren't fatal in HTML (unless strict mode is on).
+  // So just print them as warnings.
+  if (showWarnings) {
+    for (var e in parser.errors) {
+      if (_ignoredErrors.contains(e.errorCode)) continue;
+      if (checkDocType || e.errorCode != 'expected-doctype-but-got-start-tag') {
+        logger.warning(HTML5_WARNING.create({'message': e.message}),
+            span: e.span);
+      }
+    }
+  }
+  return document;
+}
+
+/// Additional options used by polymer transformers
+class TransformOptions {
+  /// List of entrypoints paths. The paths are relative to the package root and
+  /// are represented using posix style, which matches the representation used
+  /// in asset ids in barback. If null, anything under 'web/' or 'test/' is
+  /// considered an entry point.
+  final List<String> entryPoints;
+
+  /// Map of stylesheet paths that should or should not be inlined. The paths
+  /// are relative to the package root and are represented using posix style,
+  /// which matches the representation used in asset ids in barback.
+  ///
+  /// There is an additional special key 'default' for the global default.
+  final Map<String, bool> inlineStylesheets;
+
+  /// True to enable Content Security Policy.
+  /// This means the HTML page will not have inlined .js code.
+  final bool contentSecurityPolicy;
+
+  /// True to include the compiled JavaScript directly from the HTML page.
+  /// If enabled this will remove "packages/browser/dart.js" and replace
+  /// `type="application/dart"` scripts with equivalent *.dart.js files.
+  final bool directlyIncludeJS;
+
+  /// Run transformers to create a releasable app. For example, include the
+  /// minified versions of the polyfills rather than the debug versions.
+  final bool releaseMode;
+
+  /// This will make a physical element appear on the page showing build logs.
+  /// It will only appear when ![releaseMode] even if this is true.
+  final bool injectBuildLogsInOutput;
+
+  /// Rules to determine whether to run liner on an html file.
+  // TODO(jmesserly): instead of this flag, we should only run linter on
+  // reachable (entry point+imported) html if deploying. See dartbug.com/17199.
+  final LintOptions lint;
+
+  /// This will automatically inject the polyfills from the `web_components`
+  /// package in all entry points, if it is not already included.
+  final bool injectWebComponentsJs;
+
+  TransformOptions({entryPoints, this.inlineStylesheets,
+      this.contentSecurityPolicy: false, this.directlyIncludeJS: true,
+      this.releaseMode: true, this.lint: const LintOptions(),
+      this.injectBuildLogsInOutput: false, this.injectWebComponentsJs: true})
+      : entryPoints = entryPoints == null
+          ? null
+          : entryPoints.map(systemToAssetPath).toList();
+
+  /// Whether an asset with [id] is an entry point HTML file.
+  bool isHtmlEntryPoint(AssetId id) {
+    if (id.extension != '.html') return false;
+
+    // Note: [id.path] is a relative path from the root of a package.
+    if (entryPoints == null) {
+      return id.path.startsWith('web/') || id.path.startsWith('test/');
+    }
+
+    return entryPoints.contains(id.path);
+  }
+
+  // Whether a stylesheet with [id] should be inlined, the default is true.
+  bool shouldInlineStylesheet(AssetId id) {
+    // Note: [id.path] is a relative path from the root of a package.
+    // Default is to inline everything
+    if (inlineStylesheets == null) return true;
+    // First check for the full asset path overrides.
+    var override = inlineStylesheets[id.toString()];
+    if (override != null) return override;
+    // Then check just the path overrides (if the package was not specified).
+    override = inlineStylesheets[id.path];
+    if (override != null) return override;
+    // Then check the global default setting.
+    var globalDefault = inlineStylesheets['default'];
+    return (globalDefault != null) ? globalDefault : true;
+  }
+
+  // Whether a stylesheet with [id] has an overriden inlining setting.
+  bool stylesheetInliningIsOverridden(AssetId id) {
+    return inlineStylesheets != null &&
+        (inlineStylesheets.containsKey(id.toString()) ||
+            inlineStylesheets.containsKey(id.path));
+  }
+}
+
+class LintOptions {
+  /// Whether lint is enabled.
+  final bool enabled;
+
+  /// Patterns explicitly included/excluded from linting (if any).
+  final List<RegExp> patterns;
+
+  /// When [patterns] is not null, whether they denote inclusion or exclusion.
+  final bool isInclude;
+
+  const LintOptions()
+      : enabled = true,
+        patterns = null,
+        isInclude = true;
+
+  const LintOptions.disabled()
+      : enabled = false,
+        patterns = null,
+        isInclude = true;
+
+  LintOptions.include(List<String> patterns)
+      : enabled = true,
+        isInclude = true,
+        patterns = patterns.map((s) => new RegExp(s)).toList();
+
+  LintOptions.exclude(List<String> patterns)
+      : enabled = true,
+        isInclude = false,
+        patterns = patterns.map((s) => new RegExp(s)).toList();
+
+  bool shouldLint(String fileName) {
+    if (!enabled) return false;
+    if (patterns == null) return isInclude;
+    for (var pattern in patterns) {
+      if (pattern.hasMatch(fileName)) return isInclude;
+    }
+    return !isInclude;
+  }
+}
+
+/// Mixin for polymer transformers.
+abstract class PolymerTransformer {
+  TransformOptions get options;
+
+  Future<Document> readPrimaryAsHtml(Transform transform, BuildLogger logger) {
+    var asset = transform.primaryInput;
+    var id = asset.id;
+    return asset.readAsString().then((content) {
+      return _parseHtml(content, id.path, logger,
+          checkDocType: options.isHtmlEntryPoint(id));
+    });
+  }
+
+  Future<Document> readAsHtml(
+      AssetId id, Transform transform, BuildLogger logger,
+      {bool showWarnings: true}) {
+    var primaryId = transform.primaryInput.id;
+    bool samePackage = id.package == primaryId.package;
+    var url = spanUrlFor(id, transform, logger);
+    return transform.readInputAsString(id).then((content) {
+      return _parseHtml(content, url, logger,
+          checkDocType: samePackage && options.isHtmlEntryPoint(id),
+          showWarnings: showWarnings);
+    });
+  }
+
+  Future<bool> assetExists(AssetId id, Transform transform) =>
+      transform.getInput(id).then((_) => true).catchError((_) => false);
+
+  String toString() => 'polymer ($runtimeType)';
+}
+
+/// Gets the appropriate URL to use in a span to produce messages (e.g.
+/// warnings) for users. This will attempt to format the URL in the most useful
+/// way:
+///
+/// - If the asset is within the primary package, then use the [id.path],
+///   the user will know it is a file from their own code.
+/// - If the asset is from another package, then use [assetUrlFor], this will
+///   likely be a "package:" url to the file in the other package, which is
+///   enough for users to identify where the error is.
+String spanUrlFor(AssetId id, Transform transform, logger) {
+  var primaryId = transform.primaryInput.id;
+  bool samePackage = id.package == primaryId.package;
+  return samePackage
+      ? id.path
+      : assetUrlFor(id, primaryId, logger, allowAssetUrl: true);
+}
+
+/// Transformer phases which should be applied to the Polymer package.
+List<List<Transformer>> get phasesForPolymer =>
+    [[new ObservableTransformer(files: ['lib/src/instance.dart'])]];
+
+/// Generate the import url for a file described by [id], referenced by a file
+/// with [sourceId].
+// TODO(sigmund): this should also be in barback (dartbug.com/12610)
+String assetUrlFor(AssetId id, AssetId sourceId, BuildLogger logger,
+    {bool allowAssetUrl: false}) {
+  // use package: and asset: urls if possible
+  if (id.path.startsWith('lib/')) {
+    return 'package:${id.package}/${id.path.substring(4)}';
+  }
+
+  if (id.path.startsWith('asset/')) {
+    if (!allowAssetUrl) {
+      logger.error(INTERNAL_ERROR_DONT_KNOW_HOW_TO_IMPORT.create({
+        'target': id,
+        'source': sourceId,
+        'extra': ' (asset urls not allowed.)'
+      }));
+      return null;
+    }
+    return 'asset:${id.package}/${id.path.substring(6)}';
+  }
+
+  // Use relative urls only if it's possible.
+  if (id.package != sourceId.package) {
+    logger.error("don't know how to refer to $id from $sourceId");
+    return null;
+  }
+
+  var builder = path.url;
+  return builder.relative(builder.join('/', id.path),
+      from: builder.join('/', builder.dirname(sourceId.path)));
+}
+
+/// Convert system paths to asset paths (asset paths are posix style).
+String systemToAssetPath(String assetPath) {
+  if (path.Style.platform != path.Style.windows) return assetPath;
+  return path.posix.joinAll(path.split(assetPath));
+}
+
+/// Returns true if this is a valid custom element name. See:
+/// <http://w3c.github.io/webcomponents/spec/custom/#dfn-custom-element-type>
+bool isCustomTagName(String name) {
+  if (name == null || !name.contains('-')) return false;
+  return !invalidTagNames.containsKey(name);
+}
+
+/// Regex to split names in the 'attributes' attribute, which supports 'a b c',
+/// 'a,b,c', or even 'a b,c'. This is the same as in `lib/src/declaration.dart`.
+final ATTRIBUTES_REGEX = new RegExp(r'\s|,');
diff --git a/polymer/lib/src/build/constants.dart b/polymer/lib/src/build/constants.dart
new file mode 100644
index 0000000..bc37532
--- /dev/null
+++ b/polymer/lib/src/build/constants.dart
@@ -0,0 +1,20 @@
+// 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 polymer.src.build.constants;
+
+/// These names have meaning in SVG or MathML, so they aren't allowed as custom
+/// tags. See [isCustomTagName].
+const invalidTagNames = const {
+  'annotation-xml': '',
+  'color-profile': '',
+  'font-face': '',
+  'font-face-src': '',
+  'font-face-uri': '',
+  'font-face-format': '',
+  'font-face-name': '',
+  'missing-glyph': '',
+};
+
+const POLYMER_EXPERIMENTAL_HTML = 'packages/polymer/polymer_experimental.html';
diff --git a/polymer/lib/src/build/generated/messages.html b/polymer/lib/src/build/generated/messages.html
new file mode 100644
index 0000000..d4aa6e9
--- /dev/null
+++ b/polymer/lib/src/build/generated/messages.html
@@ -0,0 +1,642 @@
+<!doctype html>
+<!--
+  This file is autogenerated with polymer/tool/create_message_details_page.dart
+-->
+<html>
+<style>
+@font-face {
+  font-family: 'Montserrat';
+  font-style: normal;
+  font-weight: 400;
+  src: url(https://themes.googleusercontent.com/static/fonts/montserrat/v4/zhcz-_WihjSQC0oHJ9TCYL3hpw3pgy2gAi-Ip7WPMi0.woff) format('woff');
+}
+@font-face {
+  font-family: 'Montserrat';
+  font-style: normal;
+  font-weight: 700;
+  src: url(https://themes.googleusercontent.com/static/fonts/montserrat/v4/IQHow_FEYlDC4Gzy_m8fcnbFhgvWbfSbdVg11QabG8w.woff) format('woff');
+}
+@font-face {
+  font-family: 'Roboto';
+  font-style: normal;
+  font-weight: 300;
+  src: url(https://themes.googleusercontent.com/static/fonts/roboto/v10/Hgo13k-tfSpn0qi1SFdUfbO3LdcAZYWl9Si6vvxL-qU.woff) format('woff');
+}
+@font-face {
+  font-family: 'Roboto';
+  font-style: normal;
+  font-weight: 400;
+  src: url(https://themes.googleusercontent.com/static/fonts/roboto/v10/CrYjSnGjrRCn0pd9VQsnFOvvDin1pK8aKteLpeZ5c0A.woff) format('woff');
+}
+
+body {
+  width: 80vw;
+  margin: 20px;
+  font-family: Roboto, sans-serif;
+  background-color: #f0f0f0;
+}
+
+h2 {
+  font-family: Montserrat, sans-serif;
+  box-sizing: border-box;
+  color: rgb(72, 72, 72);
+  display: block;
+  font-style: normal;
+  font-variant: normal;
+  font-weight: normal;
+}
+
+div:target {
+  background-color: #fff;
+  border: 1px solid #888;
+  border-radius: 5px;
+  padding: 0px 10px 2px 10px;
+  box-shadow: 7px 7px 5px #888888;
+  margin-bottom: 15px;
+}
+
+
+h3 {
+  font-family: Montserrat, sans-serif;
+  box-sizing: border-box;
+  color: rgb(72, 72, 72);
+  display: block;
+  font-style: normal;
+  font-variant: normal;
+  font-weight: normal;
+}
+
+div:target > h3 {
+  font-weight: bold;
+}
+
+pre {
+  display: block;
+  padding: 9.5px;
+  margin: 0 0 10px;
+  color: #333;
+  word-break: break-all;
+  word-wrap: break-word;
+  background-color: #f5f5f5;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+}
+
+code {
+   font-family: Menlo,Monaco,Consolas,"Courier New",monospace;
+   box-sizing: border-box;
+   padding: 0;
+   font-size: 90%;
+   color: #0084c5;
+   white-space: nowrap;
+   border-radius: 4px;
+   background-color: #f9f2f4;
+}
+
+pre code {
+   white-space: inherit;
+   color: inherit;
+   background-color: inherit;
+}
+
+a {
+  color: rgb(42, 100, 150);
+}
+
+h3 > a {
+  display: none;
+  font-size: 0.8em;
+}
+
+h3:hover > a {
+  display: inline;
+}
+</style>
+<body>
+<h2>Messages from package <code>code_transformers</code></h2>
+<hr />
+
+<div id="code_transformers_1"><h3>Absolute paths not allowed <a href="#code_transformers_1">#1</a></h3>
+<p>The transformers processing your code were trying to resolve a URL and identify
+a file that they correspond to. Currently only relative paths can be resolved.</p>
+</div><hr />
+
+<div id="code_transformers_2"><h3>Invalid URL to reach another package <a href="#code_transformers_2">#2</a></h3>
+<p>To reach an asset that belongs to another package, use <code>package:</code> URLs in
+Dart code, but in any other language (like HTML or CSS) use relative URLs that
+first go all the way to the <code>packages/</code> directory.</p>
+<p>The rules for correctly writing these imports are subtle and have a lot of
+special cases. Please review
+<a href="https://www.dartlang.org/polymer/app-directories.html">https://www.dartlang.org/polymer/app-directories.html</a> to learn
+more.</p>
+</div><hr />
+
+<div id="code_transformers_3"><h3>Incomplete URL to asset in another package <a href="#code_transformers_3">#3</a></h3>
+<p>URLs that refer to assets in other packages need to explicitly mention the
+<code>packages/</code> directory. In the future this requirement might be removed, but for
+now you must use a canonical URL form for it.</p>
+<p>For example, if <code>packages/a/a.html</code> needs to import <code>packages/b/b.html</code>,
+you might expect a.html to import <code>../b/b.html</code>. Instead, it must import
+<code>../../packages/b/b.html</code>.</p>
+<p>See <a href="http://dartbug.com/15797">issue 15797</a> and
+<a href="https://www.dartlang.org/polymer/app-directories.html">https://www.dartlang.org/polymer/app-directories.html</a> to learn more.</p>
+</div><hr /><h2>Messages from package <code>observe</code></h2>
+<hr />
+
+<div id="observe_1"><h3><code>@observable</code> not supported on libraries <a href="#observe_1">#1</a></h3>
+<p>Only instance fields on <code>Observable</code> classes can be observable,
+and you must explicitly annotate each observable field as <code>@observable</code>.</p>
+<p>Support for using the <code>@observable</code> annotation in libraries, classes, and
+elsewhere is deprecated.</p>
+</div><hr />
+
+<div id="observe_2"><h3><code>@observable</code> not supported on top-level fields <a href="#observe_2">#2</a></h3>
+<p>Only instance fields on <code>Observable</code> classes can be observable,
+and you must explicitly annotate each observable field as <code>@observable</code>.</p>
+<p>Support for using the <code>@observable</code> annotation in libraries, classes, and
+elsewhere is deprecated.</p>
+</div><hr />
+
+<div id="observe_3"><h3><code>@observable</code> not supported on classes <a href="#observe_3">#3</a></h3>
+<p>Only instance fields on <code>Observable</code> classes can be observable,
+and you must explicitly annotate each observable field as <code>@observable</code>.</p>
+<p>Support for using the <code>@observable</code> annotation in libraries, classes, and
+elsewhere is deprecated.</p>
+</div><hr />
+
+<div id="observe_4"><h3><code>@observable</code> not supported on static fields <a href="#observe_4">#4</a></h3>
+<p>Only instance fields on <code>Observable</code> classes can be observable,
+and you must explicitly annotate each observable field as <code>@observable</code>.</p>
+<p>Support for using the <code>@observable</code> annotation in libraries, classes, and
+elsewhere is deprecated.</p>
+</div><hr />
+
+<div id="observe_5"><h3><code>@observable</code> field not in an <code>Observable</code> class <a href="#observe_5">#5</a></h3>
+<p>Only instance fields on <code>Observable</code> classes can be observable,
+and you must explicitly annotate each observable field as <code>@observable</code>.</p>
+<p>Support for using the <code>@observable</code> annotation in libraries, classes, and
+elsewhere is deprecated.</p>
+</div><hr /><h2>Messages from package <code>polymer</code></h2>
+<hr />
+
+<div id="polymer_1"><h3>Import not found <a href="#polymer_1">#1</a></h3>
+<p>An HTML import seems to be broken. This could be because the file doesn't exist
+or because the link URL is incorrect.</p>
+</div><hr />
+
+<div id="polymer_2"><h3>Duplicate definition <a href="#polymer_2">#2</a></h3>
+<p>Custom element names are global and can only be defined once. Some common
+reasons why you might get two definitions:</p><ul><li>Two different elements are declared with the same name.</li><li>
+<p>A single HTML file defining an element, has been imported using two different
+URLs.</p></li></ul>
+</div><hr />
+
+<div id="polymer_3"><h3>Missing import to polymer.html <a href="#polymer_3">#3</a></h3>
+<p>Starting with polymer 0.11.0, each file that uses the definition
+of polymer-element must import it either directly or transitively.</p>
+</div><hr />
+
+<div id="polymer_4"><h3>Invalid import inside &lt;polymer-element> <a href="#polymer_4">#4</a></h3>
+<p>HTML imports are expected at the top of each document, outside of any
+polymer-element definitions. The polymer build process combines all your HTML
+files together so you can deploy a single HTML file with your application. This
+build process ignores imports that appear to be in the wrong location.</p>
+</div><hr />
+
+<div id="polymer_5"><h3>Missing call to <code>initPolymer()</code> <a href="#polymer_5">#5</a></h3>
+<p>Your application entry point didn't have any Dart script tags, so it's missing
+some initialization needed for polymer.dart.</p>
+</div><hr />
+
+<div id="polymer_6"><h3>Script tags with experimental bootstrap <a href="#polymer_6">#6</a></h3>
+<p>This experimental feature is no longer supported.</p>
+</div><hr />
+
+<div id="polymer_7"><h3>Multiple Dart script tags per document <a href="#polymer_7">#7</a></h3>
+<p>Dartium currently allows only one script tag per document. Any
+additional script tags might be ignored or result in an error. This will
+likely change in the future, but for now, combine the script tags together into
+a single Dart library.</p>
+</div><hr />
+
+<div id="polymer_8"><h3>Imports before script tags <a href="#polymer_8">#8</a></h3>
+<p>It is good practice to put all your HTML imports at the beginning of the
+document, above any Dart script tags. Today, the execution of Dart script tags
+is not synchronous in Dartium, so the difference is not noticeable. However,
+Dartium that will eventually change and make the timing of script tags execution
+match how they are in JavaScript. At that point the order of your imports with
+respect to script tags will be important. Following the practice of putting
+imports first protects your app from a future breaking change in this respect.</p>
+</div><hr />
+
+<div id="polymer_9"><h3>Missing href on a <code>&lt;link&gt;</code> tag <a href="#polymer_9">#9</a></h3>
+<p>All <code>&lt;link&gt;</code> tags should have a valid URL to a resource.</p>
+</div><hr />
+
+<div id="polymer_10"><h3><code>&lt;element&gt;</code> is deprecated <a href="#polymer_10">#10</a></h3>
+<p>Long ago <code>&lt;polymer-element&gt;</code> used to be called <code>&lt;element&gt;</code>. You probably ran
+into this error if you were migrating code that was written on a very early
+version of polymer.</p>
+</div><hr />
+
+<div id="polymer_11"><h3>Definition of a custom element not found <a href="#polymer_11">#11</a></h3>
+<p>The polymer build was not able to find the definition of a custom element. This
+can happen if an element is defined with a <code>&lt;polymer-element&gt;</code> tag, but you are
+missing an HTML import or the import link is incorrect.</p>
+<p>This warning can also be a false alarm. For instance, when an element is defined
+programatically using <code>document.registerElement</code>. In that case the polymer build
+will not be able to see the definition and will produce this warning.</p>
+</div><hr />
+
+<div id="polymer_12"><h3>Empty script tag <a href="#polymer_12">#12</a></h3>
+<p>Script tags should either have a <code>src</code> attribute or a non-empty body.</p>
+</div><hr />
+
+<div id="polymer_13"><h3>Expected Dart mime-type <a href="#polymer_13">#13</a></h3>
+<p>You seem to have a <code>.dart</code> extension on a script tag, but the mime-type
+doesn't match <code>application/dart</code>.</p>
+</div><hr />
+
+<div id="polymer_14"><h3>Expected Dart file extension <a href="#polymer_14">#14</a></h3>
+<p>You are using the <code>application/dart</code> mime-type on a script tag, so
+the URL to the script source URL should have a <code>.dart</code> extension.</p>
+</div><hr />
+
+<div id="polymer_15"><h3>Script with both src and inline text <a href="#polymer_15">#15</a></h3>
+<p>You have a script tag that includes both a <code>src</code> attribute and inline script
+text. You must choose one or the other.</p>
+</div><hr />
+
+<div id="polymer_16"><h3>Incorrect instantiation: missing base tag in instantiation <a href="#polymer_16">#16</a></h3>
+<p>When you declare that a custom element extends from a base tag, for example:</p>
+<pre><code>&lt;polymer-element name="my-example" extends="ul"&gt;
+</code></pre>
+<p>or:</p>
+<pre><code>&lt;polymer-element name="my-example2" extends="ul"&gt;
+&lt;polymer-element name="my-example" extends="my-example2"&gt;
+</code></pre>
+<p>You should instantiate <code>my-example</code> by using this syntax:</p>
+<pre><code>&lt;ul is="my-example"&gt;
+</code></pre>
+<p>And not:</p>
+<pre><code>&lt;my-example&gt;
+</code></pre>
+<p>Only elements that don't extend from existing HTML elements are created using
+the latter form.</p>
+<p>This is because browsers first create the base element, and then upgrade it to
+have the extra functionality of your custom element. In the example above, using
+<code>&lt;ul&gt;</code> tells the browser which base type it must create before
+doing the upgrade.</p>
+</div><hr />
+
+<div id="polymer_17"><h3>Incorrect instantiation: extra <code>is</code> attribute or missing <code>extends</code> in declaration <a href="#polymer_17">#17</a></h3>
+<p>Creating a custom element using the syntax:</p>
+<pre><code>&lt;ul is="my-example"&gt;
+</code></pre>
+<p>means that the declaration of <code>my-example</code> extends transitively from <code>ul</code>. This
+error message is shown if the definition of <code>my-example</code> doesn't declare this
+extension. It might be that you no longer extend from the base element, in which
+case the fix is to change the instantiation to:</p>
+<pre><code>&lt;my-example&gt;
+</code></pre>
+<p>Another possibility is that the declaration needs to be fixed to include the
+<code>extends</code> attribute, for example:</p>
+<pre><code>&lt;polymer-element name="my-example" extends="ul"&gt;
+</code></pre>
+</div><hr />
+
+<div id="polymer_18"><h3>Incorrect instantiation: base tag seems wrong <a href="#polymer_18">#18</a></h3>
+<p>It seems you have a declaration like:</p>
+<pre><code>&lt;polymer-element name="my-example" extends="div"&gt;
+</code></pre>
+<p>but an instantiation like:</p>
+<pre><code>&lt;span is="my-example"&gt;
+</code></pre>
+<p>Both the declaration and the instantiation need to match on the base type. So
+either the instantiation needs to be fixed to be more like:</p>
+<pre><code>&lt;span is="my-example"&gt;
+</code></pre>
+<p>or the declaration should be fixed to be like:</p>
+<pre><code>&lt;polymer-element name="my-example" extends="span"&gt;
+</code></pre>
+</div><hr />
+
+<div id="polymer_19"><h3>No dashes allowed in custom attributes <a href="#polymer_19">#19</a></h3>
+<p>Polymer used to recognize attributes with dashes like <code>my-name</code> and convert them
+to match properties where dashes were removed, and words follow the camelCase
+style (for example <code>myName</code>). This feature is no longer available. Now simply
+use the same name as the property.</p>
+<p>Because HTML attributes are case-insensitive, you can also write the name of
+your property entirely in lowercase. Just be sure that your custom-elements
+don't declare two properties with the same name but different capitalization.</p>
+</div><hr />
+
+<div id="polymer_20"><h3>Event handlers not supported here <a href="#polymer_20">#20</a></h3>
+<p>Bindings of the form <code>{{ }}</code> are supported inside <code>&lt;template&gt;</code> nodes, even outside
+of <code>&lt;polymer-element&gt;</code> declarations. However, those bindings only support binding
+values into the content of a node or an attribute.</p>
+<p>Inline event handlers of the form <code>on-click="{{method}}"</code> are a special feature
+of polymer elements, so they are only supported inside <code>&lt;polymer-element&gt;</code>
+definitions.</p>
+</div><hr />
+
+<div id="polymer_21"><h3>No expressions allowed in event handler bindings <a href="#polymer_21">#21</a></h3>
+<p>Unlike data bindings, event handler bindings of the form <code>on-click="{{method}}"</code>
+are not evaluated as expressions. They are meant to just contain a simple name
+that resolves to a method in your polymer element's class definition.</p>
+</div><hr />
+
+<div id="polymer_22"><h3>Nested polymer element definitions not allowed <a href="#polymer_22">#22</a></h3>
+<p>Because custom element names are global, there is no need to have a
+<code>&lt;polymer-element&gt;</code> definition nested within a <code>&lt;polymer-element&gt;</code>. If you have
+a definition inside another, move the second definition out.</p>
+<p>You might see this error if you have an HTML import within a polymer element.
+You should be able to move the import out of the element definition.</p>
+</div><hr />
+
+<div id="polymer_23"><h3>Polymer element definitions without a name <a href="#polymer_23">#23</a></h3>
+<p>Polymer element definitions must have a name. You can include a name by using
+the <code>name</code> attribute in <code>&lt;polymer-element&gt;</code> for example:</p>
+<pre><code>&lt;polymer-element name="my-example"&gt;
+</code></pre>
+</div><hr />
+
+<div id="polymer_24"><h3>Custom element name missing a dash <a href="#polymer_24">#24</a></h3>
+<p>Custom element names must have a dash (<code>-</code>) and can't be any of the following
+reserved names:</p><ul><li><code>annotation-xml</code></li><li><code>color-profile</code></li><li><code>font-face</code></li><li><code>font-face-src</code></li><li><code>font-face-uri</code></li><li><code>font-face-format</code></li><li><code>font-face-name</code></li><li><code>missing-glyph</code></li></ul>
+</div><hr />
+
+<div id="polymer_25"><h3>Error while inlining an import <a href="#polymer_25">#25</a></h3>
+<p>An error occurred while inlining an import in the polymer build. This is often
+the result of a broken HTML import.</p>
+</div><hr />
+
+<div id="polymer_26"><h3>Error while inlining a stylesheet <a href="#polymer_26">#26</a></h3>
+<p>An error occurred while inlining a stylesheet in the polymer build. This is
+often the result of a broken URL in a <code>&lt;link rel="stylesheet" href="..."&gt;</code>.</p>
+</div><hr />
+
+<div id="polymer_27"><h3>URL to a script file might be incorrect <a href="#polymer_27">#27</a></h3>
+<p>An error occurred trying to read a script tag on a given URL. This is often the
+result of a broken URL in a <code>&lt;script src="..."&gt;</code>.</p>
+</div><hr />
+
+<div id="polymer_28"><h3>Attribute missing "_" prefix <a href="#polymer_28">#28</a></h3>
+<p>Not all browsers support bindings to certain attributes, especially URL
+attributes. Some browsers might sanitize attributes and result in an
+incorrect value. For this reason polymer provides a special set of attributes
+that let you bypass any browser internal attribute validation. The name of the
+attribute is the same as the original attribute, but with a leading underscore.
+For example, instead of writing:</p>
+<pre><code>&lt;img src="{{binding}}"&gt;
+</code></pre>
+<p>you can write:</p>
+<pre><code>&lt;img _src="{{binding}}"&gt;
+</code></pre>
+<p>For more information, see <a href="http://goo.gl/5av8cU">http://goo.gl/5av8cU</a>.</p>
+</div><hr />
+
+<div id="polymer_29"><h3>Attribute with extra "_" prefix <a href="#polymer_29">#29</a></h3>
+<p>A special attribute exists to support bindings on URL attributes. For example,
+this correctly binds the <code>src</code> attribute in an image:</p>
+<pre><code>&lt;img _src="{{binding}}"&gt;
+</code></pre>
+<p>However, this special <code>_src</code> attribute is only available for bindings. If you
+just have a URL, use the normal <code>src</code> attribute instead.</p>
+</div><hr />
+
+<div id="polymer_30"><h3>Internal error: don't know how to include a URL <a href="#polymer_30">#30</a></h3>
+<p>Sorry, you just ran into a bug in the polymer transformer code. Please file a
+bug at <a href="http://dartbug.com/new">http://dartbug.com/new</a> including, if possible, some example code that
+can help the team reproduce the issue.</p>
+</div><hr />
+
+<div id="polymer_31"><h3>Internal error: phases run out of order <a href="#polymer_31">#31</a></h3>
+<p>Sorry, you just ran into a bug in the polymer transformer code. Please file a
+bug at <a href="http://dartbug.com/new">http://dartbug.com/new</a> including, if possible, some example code that
+can help the team reproduce the issue.</p>
+</div><hr />
+
+<div id="polymer_32"><h3><code>@CustomTag</code> used on a private class <a href="#polymer_32">#32</a></h3>
+<p>The <code>@CustomTag</code> annotation is currently only supported on public classes. If
+you need to register a custom element whose implementation is a private class
+(that is, a class whose name starts with <code>_</code>), you can still do so by invoking
+<code>Polymer.register</code> within a public method marked with <code>@initMethod</code>.</p>
+</div><hr />
+
+<div id="polymer_33"><h3><code>@initMethod</code> is on a private function <a href="#polymer_33">#33</a></h3>
+<p>The <code>@initMethod</code> annotation is currently only supported on public top-level
+functions.</p>
+</div><hr />
+
+<div id="polymer_34"><h3>Missing argument in annotation <a href="#polymer_34">#34</a></h3>
+<p>The annotation expects one argument, but the argument was not provided.</p>
+</div><hr />
+
+<div id="polymer_35"><h3>Invalid argument in annotation <a href="#polymer_35">#35</a></h3>
+<p>The polymer transformer was not able to extract a constant value for the
+annotation argument. This can happen if your code is currently in a state that
+can't be analyzed (for example, it has parse errors) or if the expression passed
+as an argument is invalid (for example, it is not a compile-time constant).</p>
+</div><hr />
+
+<div id="polymer_36"><h3>No polymer initializers found <a href="#polymer_36">#36</a></h3>
+<p>No polymer initializers were found. Make sure to either 
+annotate your polymer elements with @CustomTag or include a 
+top level method annotated with @initMethod that registers your 
+elements. Both annotations are defined in the polymer library (
+package:polymer/polymer.dart).</p>
+</div><hr />
+
+<div id="polymer_37"><h3>Event bindings with @ are no longer supported <a href="#polymer_37">#37</a></h3>
+<p>For a while there was an undocumented feature that allowed users to include
+expressions in event bindings using the <code>@</code> prefix, for example:</p>
+<pre><code>&lt;div on-click="{{@a.b.c}}"&gt;
+
+</code></pre>
+<p>This feature is no longer supported.</p>
+</div><hr />
+
+<div id="polymer_38"><h3>Private symbol in event handler <a href="#polymer_38">#38</a></h3>
+<p>Currently private members can't be used in event handler bindings. So you can't
+write:</p>
+<pre><code>&lt;div on-click="{{_method}}"&gt;
+</code></pre>
+<p>This restriction might be removed in the future, but for now, you need to make
+your event handlers public.</p>
+</div><hr />
+
+<div id="polymer_39"><h3>Private symbol in binding expression <a href="#polymer_39">#39</a></h3>
+<p>Private members can't be used in binding expressions. For example, you can't
+write:</p>
+<pre><code>&lt;div&gt;{{a.b._c}}&lt;/div&gt;
+</code></pre>
+</div><hr />
+
+<div id="polymer_40"><h3>A warning was found while parsing the HTML document <a href="#polymer_40">#40</a></h3>
+<p>The polymer transformer uses a parser that implements the HTML5 spec
+(<code>html</code>). This message reports a
+warning that the parser detected.</p>
+</div><hr />
+
+<div id="polymer_41"><h3>Possible flash of unstyled content <a href="#polymer_41">#41</a></h3>
+<p>Custom element found in document body without an "unresolved" attribute on it or
+one of its parents. This means your app probably has a flash of unstyled content
+before it finishes loading. See <a href="http://goo.gl/iN03Pj">http://goo.gl/iN03Pj</a> for more info.</p>
+</div><hr />
+
+<div id="polymer_42"><h3>A css file was inlined multiple times. <a href="#polymer_42">#42</a></h3>
+<p>Css files are inlined by default, but if you import the same one in multiple
+places you probably want to change this behavior to prevent duplicate code.</p>
+<p>There are three typical options for dealing with this:</p><ol><li>
+<p><strong>Recommended</strong>: Use the <code>core-style</code> element from the <code>core_elements</code>
+package.</p>
+<p>The easiest way to do this is change your <code>*.css</code> file into a <code>*.html</code> file,
+and wrap the entire thing in a <code>core-style</code> with an id, something like the
+following:</p>
+<pre><code>&lt;core-style id="my-theme"&gt;
+  p {
+    color: red;
+  }
+&lt;/core-style&gt;
+</code></pre>
+<p>Now, in the files where you were previously including the
+<code>&lt;link rel="stylesheet"&gt;</code> tag, add an html import to the top of your
+document pointing to the new html file. Once that is done, replace the
+<code>&lt;link&gt;</code> tag with a <code>&lt;core-style&gt;</code> tag which has a <code>ref</code> attribute that is
+the same as the <code>id</code> attribute on the <code>&lt;core-style&gt;</code> you created. So your
+original html:</p>
+<pre><code>&lt;polymer-element name="my-element"&gt;
+  &lt;template&gt;
+    &lt;link rel="stylesheet" href="my_theme.css"&gt;
+  &lt;/template&gt;
+&lt;/polymer-element&gt;
+</code></pre>
+<p>Becomes:</p>
+<pre><code>&lt;link rel="import" href="my_theme.html"&gt;
+&lt;polymer-element name="my-element"&gt;
+  &lt;template&gt;
+    &lt;core-style ref="my-theme"&gt;&lt;/core-style&gt;
+  &lt;/template&gt;
+&lt;/polymer-element&gt;
+</code></pre></li><li>
+<p>Opt out of the inlining for this file in your pubspec.yaml:</p>
+<pre><code>transformers:
+- polymer:
+    inline_stylesheets:
+      web/my_file.css: false
+</code></pre>
+<p><strong>Warning</strong>: <code>&lt;link rel="stylesheet"&gt;</code> tags are not natively supported in
+shadow-dom. Polymer will do an xhr request for the stylesheet and inject an
+inline style with its contents in each place this stylesheet occurs.</p></li><li>
+<p>Opt into multiple inlining in your pubspec.yaml:</p>
+<pre><code>transformers:
+- polymer:
+    inline_stylesheets:
+      web/my_file.css: true
+</code></pre>
+<p><strong>Warning</strong>: You should only ever do this if your stylesheet is very small.
+Even then stylesheets tend to grow quickly and almost never decrease in size
+so this method is highly discouraged.</p></li></ol>
+</div><hr />
+
+<div id="polymer_43"><h3>"dart_support.js" injected automatically <a href="#polymer_43">#43</a></h3>
+<p>The script <code>packages/web_components/dart_support.js</code> is still used, but you no
+longer need to put it in your application's entrypoint.</p>
+<p>In the past this file served two purposes:</p><ul><li>to make dart2js work well with the web_components polyfills, and</li><li>to support registering Dart APIs for JavaScript custom elements.</li></ul>
+<p>Now, the code from <code>dart_support.js</code> is split in two halves. The half for
+dart2js is now injected by the polymer transformers automatically during <code>pub
+build</code>. The <code>web_components</code> package provides an HTML file containing the other
+half.  Developers of packages that wrap JavaScript custom elements (like
+<code>core_elements</code> and <code>paper_elements</code>) will import that file directly, so
+application developers don't have to worry about it anymore.</p>
+</div><hr />
+
+<div id="polymer_44"><h3>Dart script file included more than once. <a href="#polymer_44">#44</a></h3>
+<p>Duplicate dart scripts often happen if you have multiple html imports that
+include the same script. The simplest workaround for this is to move your dart
+script to its own html file, and import that instead of the script (html imports
+are automatically deduped).</p>
+<p>For example:</p>
+<pre><code>&lt;script type="application/dart" src="foo.dart"&gt;&lt;/script&gt;
+</code></pre>
+<p>Should turn into:</p>
+<pre><code>&lt;link rel="import" href="foo.html"&gt;
+</code></pre>
+<p>And <code>foo.html</code> should look like:</p>
+<pre><code>&lt;script type="application/dart" src="foo.dart"&gt;&lt;/script&gt;
+</code></pre>
+</div><hr />
+
+<div id="polymer_45"><h3>"webcomponents.js" injected automatically <a href="#polymer_45">#45</a></h3>
+<p>The script <code>packages/web_components/webcomponents.js</code> is still used, but you no
+longer need to put it in your application's entrypoint.</p>
+<p>The polyfills provided by this file are no longer required in chrome and will
+automatically be added during <code>pub build</code> and <code>pub serve</code>.</p>
+</div><hr />
+
+<div id="polymer_46"><h3>"platform.js" renamed to "webcomponents.js". <a href="#polymer_46">#46</a></h3>
+<p>The script <code>packages/web_components/platform.js</code> has been renamed to
+<code>packages/web_components/webcomponents.js</code>. This is automatically fixed in
+<code>pub serve</code> and <code>pub build</code> but we may remove this functionality in the next
+breaking version of Polymer.</p>
+<p>In addition, it is no longer required that you include this file directly, as
+<code>pub build</code> and <code>pub serve</code> will inject it for you, and its not required when
+running in dartium with a local server.</p>
+</div><hr />
+
+<div id="polymer_47"><h3>Missing Dart script tag in entry point. <a href="#polymer_47">#47</a></h3>
+<p>All entry points should have a dart script file. This can sometimes happen if
+you are using the default entry_points value in your polymer transformer
+configuration but have files which are not entry points in your <code>web</code> or <code>test</code>
+directory. Moving these files to your <code>lib</code> folder or specifying all your entry
+points in your configuration will fix this.</p>
+</div><hr />
+
+<div id="polymer_48"><h3>polymer.dart not imported. <a href="#polymer_48">#48</a></h3>
+<p>It is required that your application contains an import to
+<code>package:polymer/polymer.dart</code>.</p>
+</div><hr /><h2>Messages from package <code>web_components</code></h2>
+<hr />
+
+<div id="web_components_0"><h3>URL to a script file might be incorrect <a href="#web_components_0">#0</a></h3>
+<p>An error occurred trying to read a script tag on a given URL. This is often the
+result of a broken URL in a <code>&lt;script src="..."&gt;</code>.</p>
+</div><hr />
+
+<div id="web_components_1"><h3>Dart script file included more than once. <a href="#web_components_1">#1</a></h3>
+<p>Duplicate dart scripts often happen if you have multiple html imports that
+include the same script. The simplest workaround for this is to move your dart
+script to its own html file, and import that instead of the script (html imports
+are automatically deduped).</p>
+<p>For example:</p>
+<pre><code>&lt;script type="application/dart" src="foo.dart"&gt;&lt;/script&gt;
+</code></pre>
+<p>Should turn into:</p>
+<pre><code>&lt;link rel="import" href="foo.html"&gt;
+</code></pre>
+<p>And <code>foo.html</code> should look like:</p>
+<pre><code>&lt;script type="application/dart" src="foo.dart"&gt;&lt;/script&gt;
+</code></pre>
+</div><hr />
+
+<div id="web_components_2"><h3>Each entry point html file should contain exactly one dart script tag. <a href="#web_components_2">#2</a></h3>
+<p>Each entry point html file should contain exactly one dart script tag.</p>
+</div><hr />
+
+<div id="web_components_3"><h3>Internal error: don't know how to include a URL <a href="#web_components_3">#3</a></h3>
+<p>Sorry, you just ran into a bug in the web_components transformer code. Please
+file a bug at <a href="https://github.com/dart-lang/web-components/issues/new">https://github.com/dart-lang/web-components/issues/new</a>
+including, if possible, some example code that can help the team reproduce the
+issue.</p>
+</div><hr />
+
+<div id="web_components_4"><h3>Error while inlining an import <a href="#web_components_4">#4</a></h3>
+<p>An error occurred while inlining an import in the web_components build. This is
+often the result of a broken HTML import.</p>
+<p>One possible cause is using an @HtmlImport containing a relative path from
+within an inline script tag, see http://goo.gl/ZgrhaV. The workaround currently
+is to use a <code>package:</code> url instead, move the code to a dart file, or simply
+adding a real html import (since you are already in an html file).</p>
+</div><hr /></body>
+</html>
diff --git a/polymer/lib/src/build/html_finalizer.dart b/polymer/lib/src/build/html_finalizer.dart
new file mode 100644
index 0000000..d476894
--- /dev/null
+++ b/polymer/lib/src/build/html_finalizer.dart
@@ -0,0 +1,333 @@
+// Copyright (c) 2013, 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.
+
+/// Transfomer that finalizes an html file for deployment:
+///   - Extracts inline js scripts in csp mode.
+///   - Inlines css files into the document.
+///   - Validates polymer-element templates.
+library polymer.src.build.html_finalizer;
+
+import 'dart:async';
+import 'dart:collection' show LinkedHashSet;
+
+import 'package:barback/barback.dart';
+import 'package:code_transformers/assets.dart';
+import 'package:code_transformers/messages/build_logger.dart';
+import 'package:path/path.dart' as path;
+import 'package:html/dom.dart'
+    show Document, DocumentFragment, Element, Node;
+import 'package:html/dom_parsing.dart' show TreeVisitor;
+import 'package:source_span/source_span.dart';
+
+import 'common.dart';
+import 'messages.dart';
+
+/// Inlines css files and extracts inline js scripts into files if in csp mode.
+// TODO(jakemac): Move to a different package. Will need to break out the
+// binding-specific logic when this happens (add it to the linter?).
+class _HtmlFinalizer extends PolymerTransformer {
+  final TransformOptions options;
+  final Transform transform;
+  final BuildLogger logger;
+  final AssetId docId;
+  final seen = new Set<AssetId>();
+  final scriptIds = new LinkedHashSet<AssetId>();
+  final inlinedStylesheetIds = new Set<AssetId>();
+  final extractedFiles = new Set<AssetId>();
+
+  /// The number of extracted inline Dart scripts. Used as a counter to give
+  /// unique-ish filenames.
+  int inlineScriptCounter = 0;
+
+  _HtmlFinalizer(TransformOptions options, Transform transform)
+      : options = options,
+        transform = transform,
+        logger = new BuildLogger(transform,
+            convertErrorsToWarnings: !options.releaseMode,
+            detailsUri: 'http://goo.gl/5HPeuP'),
+        docId = transform.primaryInput.id;
+
+  Future apply() {
+    seen.add(docId);
+
+    Document document;
+    bool changed = false;
+
+    return readPrimaryAsHtml(transform, logger).then((doc) {
+      document = doc;
+      new _UrlAttributeValidator(docId, logger).visit(document);
+
+      changed = _extractScripts(document) || changed;
+
+      return _inlineCss(document);
+    }).then((cssInlined) {
+      changed = changed || cssInlined;
+
+      var output = transform.primaryInput;
+      if (changed) output = new Asset.fromString(docId, document.outerHtml);
+      transform.addOutput(output);
+
+      // Write out the logs collected by our [BuildLogger].
+      if (options.injectBuildLogsInOutput) {
+        return logger.writeOutput();
+      }
+    });
+  }
+
+  /// Inlines any css files found into document. Returns a [bool] indicating
+  /// whether or not the document was modified.
+  Future<bool> _inlineCss(Document document) {
+    bool changed = false;
+
+    // Note: we need to preserve the import order in the generated output.
+    var tags = document.querySelectorAll('link[rel="stylesheet"]');
+    return Future.forEach(tags, (Element tag) {
+      var href = tag.attributes['href'];
+      var id = uriToAssetId(docId, href, logger, tag.sourceSpan,
+          errorOnAbsolute: false);
+      if (id == null) return null;
+      if (!options.shouldInlineStylesheet(id)) return null;
+
+      changed = true;
+      if (inlinedStylesheetIds.contains(id) &&
+          !options.stylesheetInliningIsOverridden(id)) {
+        logger.warning(CSS_FILE_INLINED_MULTIPLE_TIMES.create({'url': id.path}),
+            span: tag.sourceSpan);
+      }
+      inlinedStylesheetIds.add(id);
+      return _inlineStylesheet(id, tag);
+    }).then((_) => changed);
+  }
+
+  /// Inlines a single css file by replacing [link] with an inline style tag.
+  Future _inlineStylesheet(AssetId id, Element link) {
+    return transform.readInputAsString(id).catchError((error) {
+      // TODO(jakemac): Move this warning to the linter once we can make it run
+      // always (see http://dartbug.com/17199). Then hide this error and replace
+      // with a comment pointing to the linter error (so we don't double warn).
+      logger.warning(INLINE_STYLE_FAIL.create({'error': error}),
+          span: link.sourceSpan);
+    }).then((css) {
+      if (css == null) return null;
+      css = new _UrlNormalizer(transform, id, logger).visitCss(css);
+      var styleElement = new Element.tag('style')..text = css;
+      // Copy over the extra attributes from the link tag to the style tag.
+      // This adds support for no-shim, shim-shadowdom, etc.
+      link.attributes.forEach((key, value) {
+        if (!IGNORED_LINKED_STYLE_ATTRS.contains(key)) {
+          styleElement.attributes[key] = value;
+        }
+      });
+      link.replaceWith(styleElement);
+    });
+  }
+
+  /// Splits inline js scripts into their own files in csp mode.
+  bool _extractScripts(Document doc) {
+    if (!options.contentSecurityPolicy) return false;
+
+    bool changed = false;
+    for (var script in doc.querySelectorAll('script')) {
+      var src = script.attributes['src'];
+      if (src != null) continue;
+
+      var type = script.attributes['type'];
+      if (type == TYPE_DART) continue;
+
+      var extension = 'js';
+      final filename = path.url.basename(docId.path);
+      final count = inlineScriptCounter++;
+      var code = script.text;
+      // TODO(sigmund): ensure this path is unique (dartbug.com/12618).
+      script.attributes['src'] = src = '$filename.$count.$extension';
+      script.text = '';
+      changed = true;
+
+      var newId = docId.addExtension('.$count.$extension');
+      extractedFiles.add(newId);
+      transform.addOutput(new Asset.fromString(newId, code));
+    }
+    return changed;
+  }
+}
+
+/// Finalizes a single html document for deployment.
+class HtmlFinalizer extends Transformer {
+  final TransformOptions options;
+
+  HtmlFinalizer(this.options);
+
+  /// Only run on entry point .html files.
+  bool isPrimary(AssetId id) => options.isHtmlEntryPoint(id);
+
+  Future apply(Transform transform) =>
+      new _HtmlFinalizer(options, transform).apply();
+}
+
+const TYPE_DART = 'application/dart';
+const TYPE_JS = 'text/javascript';
+
+/// Internally adjusts urls in the html that we are about to inline.
+class _UrlNormalizer {
+  final Transform transform;
+
+  /// Asset where the original content (and original url) was found.
+  final AssetId sourceId;
+
+  /// Path to the top level folder relative to the transform primaryInput.
+  /// This should just be some arbitrary # of ../'s.
+  final String topLevelPath;
+
+  /// Whether or not the normalizer has changed something in the tree.
+  bool changed = false;
+
+  final BuildLogger logger;
+
+  _UrlNormalizer(transform, this.sourceId, this.logger)
+      : transform = transform,
+        topLevelPath = '../' *
+            (path.url.split(transform.primaryInput.id.path).length - 2);
+
+  static final _URL = new RegExp(r'url\(([^)]*)\)', multiLine: true);
+  static final _QUOTE = new RegExp('["\']', multiLine: true);
+
+  /// Visit the CSS text and replace any relative URLs so we can inline it.
+  // Ported from:
+  // https://github.com/Polymer/vulcanize/blob/c14f63696797cda18dc3d372b78aa3378acc691f/lib/vulcan.js#L149
+  // TODO(jmesserly): use csslib here instead? Parsing with RegEx is sadness.
+  // Maybe it's reliable enough for finding URLs in CSS? I'm not sure.
+  String visitCss(String cssText) {
+    var url = spanUrlFor(sourceId, transform, logger);
+    var src = new SourceFile(cssText, url: url);
+    return cssText.replaceAllMapped(_URL, (match) {
+      // Extract the URL, without any surrounding quotes.
+      var span = src.span(match.start, match.end);
+      var href = match[1].replaceAll(_QUOTE, '');
+      href = _newUrl(href, span);
+      return 'url($href)';
+    });
+  }
+
+  String _newUrl(String href, SourceSpan span) {
+    var uri = Uri.parse(href);
+    if (uri.isAbsolute) return href;
+    if (!uri.scheme.isEmpty) return href;
+    if (!uri.host.isEmpty) return href;
+    if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI.
+    if (path.isAbsolute(href)) return href;
+
+    var id = uriToAssetId(sourceId, href, logger, span);
+    if (id == null) return href;
+    var primaryId = transform.primaryInput.id;
+
+    if (id.path.startsWith('lib/')) {
+      return '${topLevelPath}packages/${id.package}/${id.path.substring(4)}';
+    }
+
+    if (id.path.startsWith('asset/')) {
+      return '${topLevelPath}assets/${id.package}/${id.path.substring(6)}';
+    }
+
+    if (primaryId.package != id.package) {
+      // Technically we shouldn't get there
+      logger.error(INTERNAL_ERROR_DONT_KNOW_HOW_TO_IMPORT
+              .create({'target': id, 'source': primaryId, 'extra': ''}),
+          span: span);
+      return href;
+    }
+
+    var builder = path.url;
+    return builder.relative(builder.join('/', id.path),
+        from: builder.join('/', builder.dirname(primaryId.path)));
+  }
+}
+
+/// Validates url-like attributes and throws warnings as appropriate.
+/// TODO(jakemac): Move to the linter.
+class _UrlAttributeValidator extends TreeVisitor {
+  /// Asset where the original content (and original url) was found.
+  final AssetId sourceId;
+
+  final BuildLogger logger;
+
+  _UrlAttributeValidator(this.sourceId, this.logger);
+
+  visit(Node node) {
+    return super.visit(node);
+  }
+
+  visitElement(Element node) {
+    // TODO(jakemac): Support custom elements that extend html elements which
+    // have url-like attributes. This probably means keeping a list of which
+    // html elements support each url-like attribute.
+    if (!isCustomTagName(node.localName)) {
+      node.attributes.forEach((name, value) {
+        if (_urlAttributes.contains(name)) {
+          if (!name.startsWith('_') && value.contains(_BINDING_REGEX)) {
+            logger.warning(USE_UNDERSCORE_PREFIX.create({'name': name}),
+                span: node.sourceSpan, asset: sourceId);
+          } else if (name.startsWith('_') && !value.contains(_BINDING_REGEX)) {
+            logger.warning(
+                DONT_USE_UNDERSCORE_PREFIX.create({'name': name.substring(1)}),
+                span: node.sourceSpan, asset: sourceId);
+          }
+        }
+      });
+    }
+    return super.visitElement(node);
+  }
+}
+
+/// HTML attributes that expect a URL value.
+/// <http://dev.w3.org/html5/spec/section-index.html#attributes-1>
+///
+/// Every one of these attributes is a URL in every context where it is used in
+/// the DOM. The comments show every DOM element where an attribute can be used.
+///
+/// The _* version of each attribute is also supported, see http://goo.gl/5av8cU
+const _urlAttributes = const [
+  // in form
+  'action',
+  '_action',
+  // in body
+  'background',
+  '_background',
+  // in blockquote, del, ins, q
+  'cite',
+  '_cite',
+  // in object
+  'data',
+  '_data',
+  // in button, input
+  'formaction',
+  '_formaction',
+  // in a, area, link, base, command
+  'href',
+  '_href',
+  // in command
+  'icon',
+  '_icon',
+  // in html
+  'manifest',
+  '_manifest',
+  // in video
+  'poster',
+  '_poster',
+  // in audio, embed, iframe, img, input, script, source, track, video
+  'src',
+  '_src',
+];
+
+/// When inlining <link rel="stylesheet"> tags copy over all attributes to the
+/// style tag except these ones.
+const IGNORED_LINKED_STYLE_ATTRS = const [
+  'charset',
+  'href',
+  'href-lang',
+  'rel',
+  'rev'
+];
+
+/// Global RegExp objects.
+final _BINDING_REGEX = new RegExp(r'(({{.*}})|(\[\[.*\]\]))');
diff --git a/polymer/lib/src/build/index_page_builder.dart b/polymer/lib/src/build/index_page_builder.dart
new file mode 100644
index 0000000..39baf2d
--- /dev/null
+++ b/polymer/lib/src/build/index_page_builder.dart
@@ -0,0 +1,103 @@
+// Copyright (c) 2013, 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.
+
+/// Builds an index.html file in each folder containing entry points, if none
+/// already exists. This file simply lists all the entry point files.
+library polymer.src.build.index_page_builder;
+
+import 'dart:async';
+import 'dart:math';
+
+import 'package:barback/barback.dart';
+import 'package:path/path.dart' as path;
+
+import 'common.dart';
+
+/// Builds an index.html file in each folder containing entry points, if none
+/// already exists. This file simply lists all the entry point files.
+class IndexPageBuilder extends AggregateTransformer {
+  final TransformOptions options;
+
+  IndexPageBuilder(this.options);
+
+  classifyPrimary(AssetId id) {
+    if (!options.isHtmlEntryPoint(id)) return null;
+    // Group all entry points together.
+    return 'all_entry_points';
+  }
+
+  Future apply(AggregateTransform transform) {
+    Map<String, List<String>> dirFilesMap = {};
+
+    return transform.primaryInputs.toList().then((assets) {
+      // Add the asset to its directory, and make sure its directory is included
+      // in all its parents.
+      for (var asset in assets) {
+        var dir = path.url.dirname(asset.id.path);
+        while (dir != '.') {
+          dirFilesMap.putIfAbsent(dir, () => []);
+
+          var relativePath = path.url.relative(asset.id.path, from: dir);
+          var relativeDir = path.url.dirname(relativePath);
+          dirFilesMap[dir].add(relativePath);
+          dir = path.url.dirname(dir);
+        }
+      }
+
+      // Create an output index.html file for each directory, if one doesn't
+      // exist already
+      var futures = [];
+      dirFilesMap.forEach((directory, files) {
+        futures.add(_createOutput(directory, files, transform));
+      });
+      return Future.wait(futures);
+    });
+  }
+
+  Future _createOutput(
+      String directory, List<String> files, AggregateTransform transform) {
+    var indexAsset =
+        new AssetId(transform.package, path.url.join(directory, 'index.html'));
+
+    return transform.hasInput(indexAsset).then((exists) {
+      // Don't overwrite existing outputs!
+      if (exists) return;
+
+      // Sort alphabetically by recursive path parts.
+      files.sort((String a, String b) {
+        var aParts = path.url.split(a);
+        var bParts = path.url.split(b);
+        int diff = 0;
+        int minLength = min(aParts.length, bParts.length);
+        for (int i = 0; i < minLength; i++) {
+          // Directories are sorted below files.
+          var aIsDir = i < aParts.length - 1;
+          var bIsDir = i < bParts.length - 1;
+          if (aIsDir && !bIsDir) return 1;
+          if (!aIsDir && bIsDir) return -1;
+
+          // Raw string comparison, if not identical we return.
+          diff = aParts[i].compareTo(bParts[i]);
+          if (diff != 0) return diff;
+        }
+        // Identical files, shouldn't happen in practice.
+        return 0;
+      });
+
+      // Create the document with a list.
+      var doc = new StringBuffer(
+          '<!DOCTYPE html><html><body><h1>Entry points</h1><ul>');
+
+      // Add all the assets to the list.
+      for (var file in files) {
+        doc.write('<li><a href="$file">$file</a></li>');
+      }
+
+      doc.write('</ul></body></html>');
+
+      // Output the index.html file
+      transform.addOutput(new Asset.fromString(indexAsset, doc.toString()));
+    });
+  }
+}
diff --git a/polymer/lib/src/build/linter.dart b/polymer/lib/src/build/linter.dart
new file mode 100644
index 0000000..ef94de6
--- /dev/null
+++ b/polymer/lib/src/build/linter.dart
@@ -0,0 +1,491 @@
+// Copyright (c) 2013, 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.
+
+/// Logic to validate that developers are correctly using Polymer constructs.
+/// This is mainly used to produce warnings for feedback in the editor.
+library polymer.src.build.linter;
+
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:barback/barback.dart';
+import 'package:code_transformers/assets.dart';
+import 'package:code_transformers/messages/build_logger.dart';
+import 'package:code_transformers/messages/messages.dart' show Message;
+import 'package:html/dom.dart';
+import 'package:html/dom_parsing.dart';
+import 'package:path/path.dart' as path;
+import 'package:source_span/source_span.dart';
+
+import 'common.dart';
+import 'utils.dart';
+import 'messages.dart';
+
+/// A linter that checks for common Polymer errors and produces warnings to
+/// show on the editor or the command line. Leaves sources unchanged, but
+/// creates a new asset containing all the warnings.
+class Linter extends Transformer with PolymerTransformer {
+  final TransformOptions options;
+  final bool skipMissingElementWarning;
+
+  Linter(this.options, {this.skipMissingElementWarning: false});
+
+  isPrimary(AssetId id) =>
+      id.extension == '.html' && options.lint.shouldLint(id.path);
+
+  Future apply(Transform transform) {
+    var seen = new Set<AssetId>();
+    var primary = transform.primaryInput;
+    var id = primary.id;
+    transform.addOutput(primary); // this phase is analysis only
+    seen.add(id);
+    bool isEntryPoint = options.isHtmlEntryPoint(id);
+
+    var logger = new BuildLogger(transform,
+        convertErrorsToWarnings: !options.releaseMode,
+        detailsUri: 'http://goo.gl/5HPeuP');
+
+    return readPrimaryAsHtml(transform, logger).then((document) {
+      return _collectElements(document, id, transform, logger, seen)
+          .then((elements) {
+        new _LinterVisitor(id, logger, elements, isEntryPoint,
+            skipMissingElementWarning || !isEntryPoint).run(document);
+
+        // Write out the logs collected by our [BuildLogger].
+        if (options.injectBuildLogsInOutput && logger is BuildLogger) {
+          return (logger as BuildLogger).writeOutput();
+        }
+      });
+    });
+  }
+
+  /// Collect into [elements] any data about each polymer-element defined in
+  /// [document] or any of it's imports, unless they have already been [seen].
+  /// Elements are added in the order they appear, transitive imports are added
+  /// first.
+  Future<Map<String, _ElementSummary>> _collectElements(Document document,
+      AssetId sourceId, Transform transform, BuildLogger logger,
+      Set<AssetId> seen, [Map<String, _ElementSummary> elements]) {
+    if (elements == null) elements = <String, _ElementSummary>{};
+    return _getImportedIds(document, sourceId, transform, logger)
+        // Note: the import order is relevant, so we visit in that order.
+        .then((ids) => Future.forEach(ids, (id) =>
+            _readAndCollectElements(id, transform, logger, seen, elements)))
+        .then((_) {
+      if (sourceId.package == 'polymer' &&
+          sourceId.path == 'lib/src/js/polymer/polymer.html' &&
+          elements['polymer-element'] == null) {
+        elements['polymer-element'] =
+            new _ElementSummary('polymer-element', null, null);
+      }
+      return _addElements(document, logger, elements);
+    }).then((_) => elements);
+  }
+
+  Future _readAndCollectElements(AssetId id, Transform transform,
+      BuildLogger logger, Set<AssetId> seen,
+      Map<String, _ElementSummary> elements) {
+    if (id == null || seen.contains(id)) return new Future.value(null);
+    seen.add(id);
+    return readAsHtml(id, transform, logger, showWarnings: false).then(
+        (doc) => _collectElements(doc, id, transform, logger, seen, elements));
+  }
+
+  Future<List<AssetId>> _getImportedIds(Document document, AssetId sourceId,
+      Transform transform, BuildLogger logger) {
+    var importIds = [];
+    for (var tag in document.querySelectorAll('link')) {
+      if (tag.attributes['rel'] != 'import') continue;
+      var href = tag.attributes['href'];
+      var span = tag.sourceSpan;
+      var id = uriToAssetId(sourceId, href, logger, span);
+      if (id == null) continue;
+      importIds.add(assetExists(id, transform).then((exists) {
+        if (exists) return id;
+        if (sourceId == transform.primaryInput.id) {
+          logger.warning(
+              IMPORT_NOT_FOUND.create({'path': id.path, 'package': id.package}),
+              span: span);
+        }
+      }));
+    }
+    return Future.wait(importIds);
+  }
+
+  void _addElements(Document document, BuildLogger logger,
+      Map<String, _ElementSummary> elements) {
+    for (var tag in document.querySelectorAll('polymer-element')) {
+      var name = tag.attributes['name'];
+      if (name == null) continue;
+      var extendsTag = tag.attributes['extends'];
+      var span = tag.sourceSpan;
+      var existing = elements[name];
+      if (existing != null) {
+
+        // Report warning only once.
+        if (existing.hasConflict) continue;
+        existing.hasConflict = true;
+        logger.warning(
+            DUPLICATE_DEFINITION.create({'name': name, 'second': ''}),
+            span: existing.span);
+        logger.warning(DUPLICATE_DEFINITION
+                .create({'name': name, 'second': ' (second definition).'}),
+            span: span);
+        continue;
+      }
+
+      elements[name] = new _ElementSummary(name, extendsTag, tag.sourceSpan);
+    }
+  }
+}
+
+/// Information needed about other polymer-element tags in order to validate
+/// how they are used and extended.
+///
+/// Note: these are only created for polymer-element, because pure custom
+/// elements don't have a declarative form.
+class _ElementSummary {
+  final String tagName;
+  final String extendsTag;
+  final SourceSpan span;
+
+  _ElementSummary extendsType;
+  bool hasConflict = false;
+
+  String get baseExtendsTag {
+    if (extendsType != null) return extendsType.baseExtendsTag;
+    if (extendsTag != null && !extendsTag.contains('-')) return extendsTag;
+    return null;
+  }
+
+  _ElementSummary(this.tagName, this.extendsTag, this.span);
+
+  String toString() => "($tagName <: $extendsTag)";
+}
+
+class _LinterVisitor extends TreeVisitor {
+  BuildLogger _logger;
+  AssetId _sourceId;
+  bool _inPolymerElement = false;
+  bool _inAutoBindingElement = false;
+  bool _dartTagSeen = false;
+  bool _polymerHtmlSeen = false;
+  bool _polymerExperimentalHtmlSeen = false;
+  bool _isEntryPoint;
+  Map<String, _ElementSummary> _elements;
+  bool _skipMissingElementWarning;
+
+  _LinterVisitor(this._sourceId, this._logger, this._elements,
+      this._isEntryPoint, this._skipMissingElementWarning) {
+    // We normalize the map, so each element has a direct reference to any
+    // element it extends from.
+    for (var tag in _elements.values) {
+      var extendsTag = tag.extendsTag;
+      if (extendsTag == null) continue;
+      tag.extendsType = _elements[extendsTag];
+    }
+  }
+
+  void visitElement(Element node) {
+    switch (node.localName) {
+      case 'link':
+        _validateLinkElement(node);
+        break;
+      case 'element':
+        _validateElementElement(node);
+        break;
+      case 'polymer-element':
+        _validatePolymerElement(node);
+        break;
+      case 'script':
+        _validateScriptElement(node);
+        break;
+      case 'template':
+        var isTag = node.attributes['is'];
+        var oldInAutoBindingElement = _inAutoBindingElement;
+        if (isTag != null && AUTO_BINDING_ELEMENTS.contains(isTag)) {
+          _inAutoBindingElement = true;
+        }
+        _validateNormalElement(node);
+        super.visitElement(node);
+        _inAutoBindingElement = oldInAutoBindingElement;
+        break;
+      default:
+        _validateNormalElement(node);
+        super.visitElement(node);
+        break;
+    }
+  }
+
+  void run(Document doc) {
+    visit(doc);
+
+    if (_isEntryPoint && !_dartTagSeen && !_polymerExperimentalHtmlSeen) {
+      _logger.warning(MISSING_INIT_POLYMER, span: doc.body.sourceSpan);
+    }
+  }
+
+  /// Produce warnings for invalid link-rel tags.
+  void _validateLinkElement(Element node) {
+    var rel = node.attributes['rel'];
+    if (rel != 'import' && rel != 'stylesheet') return;
+
+    if (rel == 'import' && _dartTagSeen) {
+      _logger.warning(MOVE_IMPORTS_UP, span: node.sourceSpan);
+    }
+
+    var href = node.attributes['href'];
+    if (href == null || href == '') {
+      _logger.warning(MISSING_HREF.create({'rel': rel}), span: node.sourceSpan);
+      return;
+    }
+
+    if (rel != 'import') return;
+
+    if (_inPolymerElement) {
+      _logger.error(NO_IMPORT_WITHIN_ELEMENT, span: node.sourceSpan);
+      return;
+    }
+
+    if (href == POLYMER_EXPERIMENTAL_HTML) {
+      _polymerExperimentalHtmlSeen = true;
+    }
+    // TODO(sigmund): warn also if href can't be resolved.
+  }
+
+  /// Produce warnings if using `<element>` instead of `<polymer-element>`.
+  void _validateElementElement(Element node) {
+    _logger.warning(ELEMENT_DEPRECATED_EONS_AGO, span: node.sourceSpan);
+  }
+
+  /// Produce warnings if using `<polymer-element>` in the wrong place or if the
+  /// definition is not complete.
+  void _validatePolymerElement(Element node) {
+    if (!_skipMissingElementWarning &&
+        !_elements.containsKey('polymer-element')) {
+      _logger.warning(usePolymerHtmlMessageFrom(_sourceId),
+          span: node.sourceSpan);
+    }
+
+    if (_inPolymerElement) {
+      _logger.error(NESTED_POLYMER_ELEMENT, span: node.sourceSpan);
+      return;
+    }
+
+    var tagName = node.attributes['name'];
+    var extendsTag = node.attributes['extends'];
+
+    if (tagName == null) {
+      _logger.error(MISSING_TAG_NAME, span: node.sourceSpan);
+    } else if (!isCustomTagName(tagName)) {
+      _logger.error(INVALID_TAG_NAME.create({'name': tagName}),
+          span: node.sourceSpan);
+    }
+
+    if (!_skipMissingElementWarning &&
+        _elements[extendsTag] == null &&
+        isCustomTagName(extendsTag)) {
+      _logger.warning(CUSTOM_ELEMENT_NOT_FOUND.create({'tag': extendsTag}),
+          span: node.sourceSpan);
+    }
+
+    var attrs = node.attributes['attributes'];
+    if (attrs != null) {
+      var attrsSpan = node.attributeSpans['attributes'];
+
+      // names='a b c' or names='a,b,c'
+      // record each name for publishing
+      for (var attr in attrs.split(ATTRIBUTES_REGEX)) {
+        if (!_validateCustomAttributeName(attr.trim(), attrsSpan)) break;
+      }
+    }
+
+    var oldValue = _inPolymerElement;
+    _inPolymerElement = true;
+    super.visitElement(node);
+    _inPolymerElement = oldValue;
+  }
+
+  /// Checks for multiple Dart script tags in the same page, which is invalid.
+  void _validateScriptElement(Element node) {
+    var scriptType = node.attributes['type'];
+    var isDart = scriptType == 'application/dart';
+    var src = node.attributes['src'];
+
+    if (isDart) {
+      if (_dartTagSeen) _logger.warning(ONLY_ONE_TAG, span: node.sourceSpan);
+      if (_isEntryPoint && _polymerExperimentalHtmlSeen) {
+        _logger.warning(NO_DART_SCRIPT_AND_EXPERIMENTAL, span: node.sourceSpan);
+      }
+      _dartTagSeen = true;
+    }
+
+    if (src != null && src.endsWith('web_components/dart_support.js')) {
+      _logger.warning(DART_SUPPORT_NO_LONGER_REQUIRED, span: node.sourceSpan);
+    }
+
+    if (src != null && src.contains('web_components/webcomponents.')) {
+      _logger.warning(WEB_COMPONENTS_NO_LONGER_REQUIRED, span: node.sourceSpan);
+    }
+
+    if (src != null && src.contains('web_components/platform.')) {
+      _logger.warning(PLATFORM_JS_RENAMED, span: node.sourceSpan);
+    }
+
+    var isEmpty = node.innerHtml.trim() == '';
+
+    if (src == null) {
+      if (isDart && isEmpty) {
+        _logger.warning(SCRIPT_TAG_SEEMS_EMPTY, span: node.sourceSpan);
+      }
+      return;
+    }
+
+    if (src.endsWith('.dart') && !isDart) {
+      _logger.warning(EXPECTED_DART_MIME_TYPE, span: node.sourceSpan);
+      return;
+    }
+
+    if (!src.endsWith('.dart') && isDart) {
+      _logger.warning(EXPECTED_DART_EXTENSION, span: node.sourceSpan);
+      return;
+    }
+
+    if (!isEmpty) {
+      _logger.warning(FOUND_BOTH_SCRIPT_SRC_AND_TEXT, span: node.sourceSpan);
+    }
+  }
+
+  /// Produces warnings for misuses of on-foo event handlers, and for instanting
+  /// custom tags incorrectly.
+  void _validateNormalElement(Element node) {
+    // Event handlers only allowed inside polymer-elements
+    node.attributes.forEach((name, value) {
+      if (name is String && name.startsWith('on')) {
+        _validateEventHandler(node, name, value);
+      }
+    });
+
+    // Validate uses of custom-tags
+    var nodeTag = node.localName;
+    var hasIsAttribute;
+    var customTagName;
+    if (isCustomTagName(nodeTag)) {
+      // <fancy-button>
+      customTagName = nodeTag;
+      hasIsAttribute = false;
+    } else {
+      // <button is="fancy-button">
+      customTagName = node.attributes['is'];
+      hasIsAttribute = true;
+    }
+
+    if (customTagName == null ||
+        INTERNALLY_DEFINED_ELEMENTS.contains(customTagName)) {
+      return;
+    }
+
+    var info = _elements[customTagName];
+    if (info == null) {
+      if (!_skipMissingElementWarning && _isEntryPoint) {
+        _logger.warning(CUSTOM_ELEMENT_NOT_FOUND.create({'tag': customTagName}),
+            span: node.sourceSpan);
+      }
+      return;
+    }
+
+    var baseTag = info.baseExtendsTag;
+    if (baseTag != null && !hasIsAttribute) {
+      _logger.warning(BAD_INSTANTIATION_MISSING_BASE_TAG
+              .create({'tag': customTagName, 'base': baseTag}),
+          span: node.sourceSpan);
+      return;
+    }
+
+    if (hasIsAttribute && baseTag == null) {
+      _logger.warning(BAD_INSTANTIATION_BOGUS_BASE_TAG
+              .create({'tag': customTagName, 'base': nodeTag}),
+          span: node.sourceSpan);
+      return;
+    }
+
+    if (hasIsAttribute && baseTag != nodeTag) {
+      _logger.warning(BAD_INSTANTIATION_WRONG_BASE_TAG
+              .create({'tag': customTagName, 'base': baseTag}),
+          span: node.sourceSpan);
+    }
+
+    // FOUC check, if content is supplied
+    if (_isEntryPoint && !node.innerHtml.isEmpty) {
+      var parent = node;
+      var hasFoucFix = false;
+      while (parent != null && !hasFoucFix) {
+        if (parent.localName == 'polymer-element' ||
+            parent.attributes['unresolved'] != null) {
+          hasFoucFix = true;
+        }
+        if (parent.localName == 'body') break;
+        parent = parent.parent;
+      }
+      if (!hasFoucFix) _logger.warning(POSSIBLE_FUOC, span: node.sourceSpan);
+    }
+  }
+
+  /// Validate an attribute on a custom-element. Returns true if valid.
+  bool _validateCustomAttributeName(String name, FileSpan span) {
+    if (name.contains('-')) {
+      var newName = toCamelCase(name);
+      var alternative = '"$newName" or "${newName.toLowerCase()}"';
+      _logger.warning(NO_DASHES_IN_CUSTOM_ATTRIBUTES
+          .create({'name': name, 'alternative': alternative}), span: span);
+      return false;
+    }
+    return true;
+  }
+
+  /// Validate event handlers are used correctly.
+  void _validateEventHandler(Element node, String name, String value) {
+    if (!name.startsWith('on-')) return;
+
+    if (!_inPolymerElement && !_inAutoBindingElement) {
+      _logger.warning(EVENT_HANDLERS_ONLY_WITHIN_POLYMER,
+          span: node.attributeSpans[name]);
+      return;
+    }
+
+    // Valid bindings have {{ }}, don't look like method calls foo(bar), and are
+    // non empty.
+    if (!value.startsWith("{{") ||
+        !value.endsWith("}}") ||
+        value.contains('(') ||
+        value.substring(2, value.length - 2).trim() == '') {
+      _logger.warning(
+          INVALID_EVENT_HANDLER_BODY.create({'value': value, 'name': name}),
+          span: node.attributeSpans[name]);
+    }
+  }
+}
+
+Message usePolymerHtmlMessageFrom(AssetId id) {
+  var segments = path.url.split(id.path);
+  var upDirCount = 0;
+  if (segments[0] == 'lib') {
+    // lib/foo.html => ../../packages/
+    upDirCount = segments.length;
+  } else if (segments.length > 2) {
+    // web/a/foo.html => ../packages/
+    upDirCount = segments.length - 2;
+  }
+  var reachOutPrefix = '../' * upDirCount;
+  return USE_POLYMER_HTML.create({'reachOutPrefix': reachOutPrefix});
+}
+
+const List<String> INTERNALLY_DEFINED_ELEMENTS = const [
+  'auto-binding-dart',
+  'polymer-element'
+];
+const List<String> AUTO_BINDING_ELEMENTS = const [
+  'auto-binding-dart',
+  'auto-binding'
+];
diff --git a/polymer/lib/src/build/log_injector.css b/polymer/lib/src/build/log_injector.css
new file mode 100644
index 0000000..e81eee4
--- /dev/null
+++ b/polymer/lib/src/build/log_injector.css
@@ -0,0 +1,78 @@
+/** Copyright (c) 2013, 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. **/
+.build-logs {
+  position: fixed;
+  bottom: 0;
+  right: 0;
+  max-width: 80vw;
+  z-index: 10000;
+  font-family: sans-serif !important;
+}
+.build-logs .log {
+  padding: 0.6em;
+  background: black;
+  color: white;
+  border: solid 1px #666;
+  border-bottom: 0px;
+  text-overflow: ellipsis;
+  overflow-x: hidden;
+}
+.build-logs .fine {
+  color: green;
+}
+.build-logs .info {
+  color: yellow;
+}
+.build-logs .warning {
+  color: orange;
+}
+.build-logs .error {
+  color: red;
+}
+.build-logs .message {
+  white-space: nowrap;
+  cursor: pointer;
+}
+.build-logs .message.expanded {
+  white-space: normal;
+}
+.build-logs .message a {
+  color: #CCF;
+  text-decoration: bold;
+}
+.build-logs .menu {
+  text-align: right;
+}
+.build-logs .menu div {
+  display: inline-block;
+  background: #666;
+  font-weight: bold;
+  cursor: pointer;
+  border: solid 1px black;
+  padding: 0.6em 1em;
+}
+.build-logs .menu div.active {
+  background: black;
+}
+.build-logs .menu div .num {
+  color: white;
+}
+.build-logs .content {
+  max-height: 75vh;
+  font-size: 1em;
+  overflow-y: auto;
+}
+.build-logs .content > div {
+  display: none;
+}
+.build-logs .content > div.active {
+  display: block;
+}
+
+.build-logs .content span.text {
+  padding: 0.4em 0.2em 0.2em 2em;
+  white-space: pre;
+  display: block;
+  font-family: monospace !important;
+}
diff --git a/polymer/lib/src/build/log_injector.dart b/polymer/lib/src/build/log_injector.dart
new file mode 100644
index 0000000..81af914
--- /dev/null
+++ b/polymer/lib/src/build/log_injector.dart
@@ -0,0 +1,128 @@
+// Copyright (c) 2013, 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.
+
+/// This library provides a single function called injectLogs which when called
+/// will request a logs json file and build a small widget out of them which
+/// groups the logs by level.
+library polymer.build.log_injector;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:html';
+
+import 'package:path/path.dart' as path;
+import 'package:source_span/source_span.dart';
+import 'package:code_transformers/messages/messages.dart';
+
+class LogInjector {
+  Element selectedMenu;
+  Element selectedContent;
+
+  // Gets the logs from a url and inject them into the dom.
+  Future injectLogsFromUrl(String url) =>
+      HttpRequest.getString(url).then((data) => injectLogs(data));
+
+  // Builds the html for the logs element given some logs, and injects that
+  // into the dom. Currently, we do not  use Polymer just to ensure that the
+  // page works regardless of the state of the app. Ideally, we could have
+  // multiple scripts running independently so we could ensure that this would
+  // always be running.
+  injectLogs(String data) {
+    var logs = new LogEntryTable.fromJson(JSON.decode(data));
+    if (logs.entries.isEmpty) return;
+
+    // Group all logs by level.
+    var logsByLevel = {};
+    logs.entries.values.forEach((list) => list.forEach((log) {
+      logsByLevel.putIfAbsent(log.level, () => []);
+      logsByLevel[log.level].add(log);
+    }));
+    if (logsByLevel.isEmpty) return;
+
+    // Build the wrapper, menu, and content divs.
+
+    var menuWrapper = new DivElement()..classes.add('menu');
+    var contentWrapper = new DivElement()..classes.add('content');
+    var wrapperDiv = new DivElement()
+      ..classes.add('build-logs')
+      ..append(menuWrapper)
+      ..append(contentWrapper);
+
+    // For each log level, add a menu item, content section, and all the logs.
+    logsByLevel.forEach((level, logs) {
+      var levelClassName = level.toLowerCase();
+
+      // Add the menu item and content item.
+      var menuItem = new Element.html('<div class="$levelClassName">'
+          '$level <span class="num">(${logs.length})</span>'
+          '</div>');
+      menuWrapper.append(menuItem);
+      var contentItem = new DivElement()..classes.add(levelClassName);
+      contentWrapper.append(contentItem);
+
+      // Set up the click handlers.
+      menuItem.onClick.listen((_) {
+        if (selectedMenu == menuItem) {
+          selectedMenu = null;
+          selectedContent = null;
+        } else {
+          if (selectedMenu != null) {
+            selectedMenu.classes.remove('active');
+            selectedContent.classes.remove('active');
+          }
+
+          selectedMenu = menuItem;
+          selectedContent = contentItem;
+        }
+
+        menuItem.classes.toggle('active');
+        contentItem.classes.toggle('active');
+      });
+
+      // Add the logs to the content item.
+      for (var log in logs) {
+        var logHtml = new StringBuffer();
+        logHtml.write('<div class="log">');
+
+        var id = log.message.id;
+        var hashTag = '${id.package}_${id.id}';
+        var message = new HtmlEscape().convert(log.message.snippet);
+        message.replaceAllMapped(_urlRegex,
+            (m) => '<a href="${m.group(0)}" target="blank">${m.group(0)}</a>');
+        logHtml.write('<div class="message $levelClassName">$message '
+            '<a target="blank" href='
+            '"/packages/polymer/src/build/generated/messages.html#$hashTag">'
+            '(more details)</a></div>');
+        var span = log.span;
+        if (span != null) {
+          logHtml.write('<div class="location">');
+          var text = new HtmlEscape().convert(span.text);
+          logHtml.write(
+              '  <span class="location">${span.start.toolString}</span></div>'
+              '  <span class="text">$text</span>' '</div>');
+          logHtml.write('</div>');
+        }
+        logHtml.write('</div>');
+
+        var logElement = new Element.html(logHtml.toString(),
+            validator: new NodeValidatorBuilder.common()
+          ..allowNavigation(new _OpenUriPolicy()));
+        contentItem.append(logElement);
+        var messageElement = logElement.querySelector('.message');
+        messageElement.onClick.listen((e) {
+          if (e.target == messageElement) {
+            messageElement.classes.toggle('expanded');
+          }
+        });
+      }
+    });
+
+    document.body.append(wrapperDiv);
+  }
+}
+
+final _urlRegex = new RegExp('http://[^ ]*');
+class _OpenUriPolicy implements UriPolicy {
+  bool allowsUri(String uri) => true;
+}
diff --git a/polymer/lib/src/build/messages.dart b/polymer/lib/src/build/messages.dart
new file mode 100644
index 0000000..56d4cf6
--- /dev/null
+++ b/polymer/lib/src/build/messages.dart
@@ -0,0 +1,628 @@
+// 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.
+
+/// Contains all error and warning messages produced by polymer.
+library polymer.src.build.messages;
+
+import 'package:code_transformers/messages/messages.dart';
+import 'constants.dart';
+
+const IMPORT_NOT_FOUND = const MessageTemplate(const MessageId('polymer', 1),
+    'couldn\'t find imported asset "%-path-%" in package "%-package-%".',
+    'Import not found', '''
+An HTML import seems to be broken. This could be because the file doesn't exist
+or because the link URL is incorrect.
+''');
+
+const DUPLICATE_DEFINITION = const MessageTemplate(
+    const MessageId('polymer', 2),
+    'duplicate definition for custom tag "%-name-%".%-second-%',
+    'Duplicate definition', '''
+Custom element names are global and can only be defined once. Some common
+reasons why you might get two definitions:
+
+  * Two different elements are declared with the same name.
+  * A single HTML file defining an element, has been imported using two different
+    URLs.
+''');
+
+const USE_POLYMER_HTML = const MessageTemplate(const MessageId('polymer', 3),
+    'Missing definition for <polymer-element>, please add the following '
+    'HTML import at the top of this file: <link rel="import" '
+    'href="%-reachOutPrefix-%packages/polymer/polymer.html">.',
+    'Missing import to polymer.html', '''
+Starting with polymer 0.11.0, each file that uses the definition
+of polymer-element must import it either directly or transitively.
+''');
+
+const NO_IMPORT_WITHIN_ELEMENT = const MessageTemplate(
+    const MessageId('polymer', 4), 'Polymer.dart\'s implementation of '
+    'HTML imports are not supported within polymer element definitions, yet. '
+    'Please move the import out of this <polymer-element>.',
+    'Invalid import inside <polymer-element>', '''
+HTML imports are expected at the top of each document, outside of any
+polymer-element definitions. The polymer build process combines all your HTML
+files together so you can deploy a single HTML file with your application. This
+build process ignores imports that appear to be in the wrong location.
+''');
+
+const MISSING_INIT_POLYMER = const MessageTemplate(
+    const MessageId('polymer', 5),
+    'To run a polymer application, you need to call `initPolymer()`. You can '
+    'either include a generic script tag that does this for you:'
+    '\'<script type="application/dart">export "package:polymer/init.dart";'
+    '</script>\' or add your own script tag and call that function. '
+    'Make sure the script tag is placed after all HTML imports.',
+    'Missing call to `initPolymer()`', '''
+Your application entry point didn't have any Dart script tags, so it's missing
+some initialization needed for polymer.dart.
+''');
+
+const NO_DART_SCRIPT_AND_EXPERIMENTAL = const MessageTemplate(
+    const MessageId('polymer', 6),
+    'The experimental bootstrap feature doesn\'t support script tags on '
+    'the main document (for now).', 'Script tags with experimental bootstrap',
+    'This experimental feature is no longer supported.');
+
+const ONLY_ONE_TAG = const MessageTemplate(const MessageId('polymer', 7),
+    'Only one "application/dart" script tag per document is allowed.',
+    'Multiple Dart script tags per document', '''
+Dartium currently allows only one script tag per document. Any
+additional script tags might be ignored or result in an error. This will
+likely change in the future, but for now, combine the script tags together into
+a single Dart library.
+''');
+
+const MOVE_IMPORTS_UP = const MessageTemplate(const MessageId('polymer', 8),
+    'Move HTML imports above your Dart script tag.',
+    'Imports before script tags', '''
+It is good practice to put all your HTML imports at the beginning of the
+document, above any Dart script tags. Today, the execution of Dart script tags
+is not synchronous in Dartium, so the difference is not noticeable. However,
+Dartium that will eventually change and make the timing of script tags execution
+match how they are in JavaScript. At that point the order of your imports with
+respect to script tags will be important. Following the practice of putting
+imports first protects your app from a future breaking change in this respect.
+''');
+
+const MISSING_HREF = const MessageTemplate(const MessageId('polymer', 9),
+    'link rel="%-rel-%" missing href.', 'Missing href on a `<link>` tag',
+    'All `<link>` tags should have a valid URL to a resource.');
+
+const ELEMENT_DEPRECATED_EONS_AGO = const MessageTemplate(
+    const MessageId('polymer', 10),
+    '<element> elements are not supported, use <polymer-element> instead.',
+    '`<element>` is deprecated', '''
+Long ago `<polymer-element>` used to be called `<element>`. You probably ran
+into this error if you were migrating code that was written on a very early
+version of polymer.
+''');
+
+// TODO(jmesserly): this warning is wrong if someone is using raw custom
+// elements. Is there another way we can handle this warning that won't
+// generate false positives?
+const CUSTOM_ELEMENT_NOT_FOUND = const MessageTemplate(
+    const MessageId('polymer', 11),
+    'custom element with name "%-tag-%" not found.',
+    'Definition of a custom element not found', '''
+The polymer build was not able to find the definition of a custom element. This
+can happen if an element is defined with a `<polymer-element>` tag, but you are
+missing an HTML import or the import link is incorrect.
+
+This warning can also be a false alarm. For instance, when an element is defined
+programatically using `document.registerElement`. In that case the polymer build
+will not be able to see the definition and will produce this warning.
+''');
+
+const SCRIPT_TAG_SEEMS_EMPTY = const MessageTemplate(
+    const MessageId('polymer', 12), 'script tag seems empty.',
+    'Empty script tag',
+    'Script tags should either have a `src` attribute or a non-empty body.');
+
+const EXPECTED_DART_MIME_TYPE = const MessageTemplate(
+    const MessageId('polymer', 13),
+    'Wrong script type, expected type="application/dart".',
+    'Expected Dart mime-type', '''
+You seem to have a `.dart` extension on a script tag, but the mime-type
+doesn't match `application/dart`.
+''');
+
+const EXPECTED_DART_EXTENSION = const MessageTemplate(
+    const MessageId('polymer', 14),
+    '"application/dart" scripts should use the .dart file extension.',
+    'Expected Dart file extension', '''
+You are using the `application/dart` mime-type on a script tag, so
+the URL to the script source URL should have a `.dart` extension.
+''');
+
+const FOUND_BOTH_SCRIPT_SRC_AND_TEXT = const MessageTemplate(
+    const MessageId('polymer', 15),
+    'script tag has "src" attribute and also has script text.',
+    'Script with both src and inline text', '''
+You have a script tag that includes both a `src` attribute and inline script
+text. You must choose one or the other.
+''');
+
+const BAD_INSTANTIATION_MISSING_BASE_TAG = const MessageTemplate(
+    const MessageId('polymer', 16),
+    'custom element "%-tag-%" extends from "%-base-%", but '
+    'this tag will not include the default properties of "%-base-%". '
+    'To fix this, either write this tag as <%-base-% '
+    'is="%-tag-%"> or remove the "extends" attribute from '
+    'the custom element declaration.',
+    'Incorrect instantiation: missing base tag in instantiation', '''
+When you declare that a custom element extends from a base tag, for example:
+
+    <polymer-element name="my-example" extends="ul">
+
+or:
+
+    <polymer-element name="my-example2" extends="ul">
+    <polymer-element name="my-example" extends="my-example2">
+
+You should instantiate `my-example` by using this syntax:
+
+    <ul is="my-example">
+
+And not:
+
+    <my-example>
+
+Only elements that don't extend from existing HTML elements are created using
+the latter form.
+
+This is because browsers first create the base element, and then upgrade it to
+have the extra functionality of your custom element. In the example above, using
+`<ul>` tells the browser which base type it must create before
+doing the upgrade.
+''');
+
+const BAD_INSTANTIATION_BOGUS_BASE_TAG = const MessageTemplate(
+    const MessageId('polymer', 17),
+    'custom element "%-tag-%" doesn\'t declare any type '
+    'extensions. To fix this, either rewrite this tag as '
+    '<%-tag-%> or add \'extends="%-base-%"\' to '
+    'the custom element declaration.',
+    'Incorrect instantiation: extra `is` attribute or missing `extends` '
+    'in declaration', '''
+Creating a custom element using the syntax:
+
+    <ul is="my-example">
+
+means that the declaration of `my-example` extends transitively from `ul`. This
+error message is shown if the definition of `my-example` doesn't declare this
+extension. It might be that you no longer extend from the base element, in which
+case the fix is to change the instantiation to:
+
+    <my-example>
+
+Another possibility is that the declaration needs to be fixed to include the
+`extends` attribute, for example:
+
+    <polymer-element name="my-example" extends="ul">
+''');
+
+const BAD_INSTANTIATION_WRONG_BASE_TAG = const MessageTemplate(
+    const MessageId('polymer', 18),
+    'custom element "%-tag-%" extends from "%-base-%". '
+    'Did you mean to write <%-base-% is="%-tag-%">?',
+    'Incorrect instantiation: base tag seems wrong', '''
+It seems you have a declaration like:
+
+    <polymer-element name="my-example" extends="div">
+
+but an instantiation like:
+
+    <span is="my-example">
+
+Both the declaration and the instantiation need to match on the base type. So
+either the instantiation needs to be fixed to be more like:
+
+    <span is="my-example">
+
+or the declaration should be fixed to be like:
+
+    <polymer-element name="my-example" extends="span">
+''');
+
+const NO_DASHES_IN_CUSTOM_ATTRIBUTES = const MessageTemplate(
+    const MessageId('polymer', 19),
+    'PolymerElement no longer recognizes attribute names with '
+    'dashes such as "%-name-%". Use %-alternative-% '
+    'instead (both forms are equivalent in HTML).',
+    'No dashes allowed in custom attributes', '''
+Polymer used to recognize attributes with dashes like `my-name` and convert them
+to match properties where dashes were removed, and words follow the camelCase
+style (for example `myName`). This feature is no longer available. Now simply
+use the same name as the property.
+
+Because HTML attributes are case-insensitive, you can also write the name of
+your property entirely in lowercase. Just be sure that your custom-elements
+don't declare two properties with the same name but different capitalization.
+''');
+
+const EVENT_HANDLERS_ONLY_WITHIN_POLYMER = const MessageTemplate(
+    const MessageId('polymer', 20),
+    'Inline event handlers are only supported inside '
+    'declarations of <polymer-element>.', 'Event handlers not supported here',
+    '''
+Bindings of the form `{{ }}` are supported inside `<template>` nodes, even outside
+of `<polymer-element>` declarations. However, those bindings only support binding
+values into the content of a node or an attribute.
+
+Inline event handlers of the form `on-click="{{method}}"` are a special feature
+of polymer elements, so they are only supported inside `<polymer-element>`
+definitions.
+''');
+
+const INVALID_EVENT_HANDLER_BODY = const MessageTemplate(
+    const MessageId('polymer', 21),
+    'Invalid event handler body "%-value-%". Declare a method '
+    'in your custom element "void handlerName(event, detail, target)" '
+    'and use the form %-name-%="{{handlerName}}".',
+    'No expressions allowed in event handler bindings', '''
+Unlike data bindings, event handler bindings of the form `on-click="{{method}}"`
+are not evaluated as expressions. They are meant to just contain a simple name
+that resolves to a method in your polymer element's class definition.
+''');
+
+const NESTED_POLYMER_ELEMENT = const MessageTemplate(
+    const MessageId('polymer', 22),
+    'Nested polymer element definitions are not allowed.',
+    'Nested polymer element definitions not allowed', '''
+Because custom element names are global, there is no need to have a
+`<polymer-element>` definition nested within a `<polymer-element>`. If you have
+a definition inside another, move the second definition out.
+
+You might see this error if you have an HTML import within a polymer element.
+You should be able to move the import out of the element definition.
+''');
+
+const MISSING_TAG_NAME = const MessageTemplate(const MessageId('polymer', 23),
+    'Missing tag name of the custom element. Please include an '
+    'attribute like \'name="your-tag-name"\'.',
+    'Polymer element definitions without a name', '''
+Polymer element definitions must have a name. You can include a name by using
+the `name` attribute in `<polymer-element>` for example:
+
+    <polymer-element name="my-example">
+''');
+
+final INVALID_TAG_NAME = new MessageTemplate(const MessageId('polymer', 24),
+    'Invalid name "%-name-%". Custom element names must have '
+    'at least one dash (-) and can\'t be any of the following names: '
+    '${invalidTagNames.keys.join(", ")}.', 'Custom element name missing a dash',
+    '''
+Custom element names must have a dash (`-`) and can\'t be any of the following
+reserved names:
+
+${invalidTagNames.keys.map((e) => '  * `$e`\n').join('')}
+
+''');
+
+const INLINE_IMPORT_FAIL = const MessageTemplate(const MessageId('polymer', 25),
+    'Failed to inline HTML import: %-error-%', 'Error while inlining an import',
+    '''
+An error occurred while inlining an import in the polymer build. This is often
+the result of a broken HTML import.
+''');
+
+const INLINE_STYLE_FAIL = const MessageTemplate(const MessageId('polymer', 26),
+    'Failed to inline stylesheet: %-error-%',
+    'Error while inlining a stylesheet', '''
+An error occurred while inlining a stylesheet in the polymer build. This is
+often the result of a broken URL in a `<link rel="stylesheet" href="...">`.
+''');
+
+const SCRIPT_FILE_NOT_FOUND = const MessageTemplate(
+    const MessageId('polymer', 27), 'Script file at "%-url-%" not found.',
+    'URL to a script file might be incorrect', '''
+An error occurred trying to read a script tag on a given URL. This is often the
+result of a broken URL in a `<script src="...">`.
+''');
+
+const USE_UNDERSCORE_PREFIX = const MessageTemplate(
+    const MessageId('polymer', 28),
+    'When using bindings with the "%-name-%" attribute you may '
+    'experience errors in certain browsers. Please use the '
+    '"_%-name-%" attribute instead.', 'Attribute missing "_" prefix', '''
+Not all browsers support bindings to certain attributes, especially URL
+attributes. Some browsers might sanitize attributes and result in an
+incorrect value. For this reason polymer provides a special set of attributes
+that let you bypass any browser internal attribute validation. The name of the
+attribute is the same as the original attribute, but with a leading underscore.
+For example, instead of writing:
+
+    <img src="{{binding}}">
+
+you can write:
+
+    <img _src="{{binding}}">
+
+For more information, see <http://goo.gl/5av8cU>.
+''');
+
+const DONT_USE_UNDERSCORE_PREFIX = const MessageTemplate(
+    const MessageId('polymer', 29),
+    'The "_%-name-%" attribute is only supported when using bindings. '
+    'Please change to the "%-name-%" attribute.',
+    'Attribute with extra "_" prefix', '''
+A special attribute exists to support bindings on URL attributes. For example,
+this correctly binds the `src` attribute in an image:
+
+    <img _src="{{binding}}">
+
+However, this special `_src` attribute is only available for bindings. If you
+just have a URL, use the normal `src` attribute instead.
+''');
+
+const INTERNAL_ERROR_DONT_KNOW_HOW_TO_IMPORT = const MessageTemplate(
+    const MessageId('polymer', 30),
+    "internal error: don't know how to include %-target-% from"
+    " %-source-%.%-extra-%", "Internal error: don't know how to include a URL",
+    '''
+Sorry, you just ran into a bug in the polymer transformer code. Please file a
+bug at <http://dartbug.com/new> including, if possible, some example code that
+can help the team reproduce the issue.
+''');
+
+const INTERNAL_ERROR_UNEXPECTED_SCRIPT = const MessageTemplate(
+    const MessageId('polymer', 31),
+    'unexpected script. The ScriptCompactor transformer should run after '
+    'running the ImportInliner', 'Internal error: phases run out of order', '''
+Sorry, you just ran into a bug in the polymer transformer code. Please file a
+bug at <http://dartbug.com/new> including, if possible, some example code that
+can help the team reproduce the issue.
+''');
+
+const PRIVATE_CUSTOM_TAG = const MessageTemplate(const MessageId('polymer', 32),
+    '@CustomTag is not currently supported on private classes:'
+    ' %-name-%. Consider making this class public, or create a '
+    'public initialization method marked with `@initMethod` that calls '
+    '`Polymer.register(%-name-%, %-className-%)`.',
+    '`@CustomTag` used on a private class', '''
+The `@CustomTag` annotation is currently only supported on public classes. If
+you need to register a custom element whose implementation is a private class
+(that is, a class whose name starts with `_`), you can still do so by invoking
+`Polymer.register` within a public method marked with `@initMethod`.
+''');
+
+const PRIVATE_INIT_METHOD = const MessageTemplate(
+    const MessageId('polymer', 33),
+    '@initMethod is no longer supported on private functions: %-name-%',
+    '`@initMethod` is on a private function', '''
+The `@initMethod` annotation is currently only supported on public top-level
+functions.
+''');
+
+const MISSING_ANNOTATION_ARGUMENT = const MessageTemplate(
+    const MessageId('polymer', 34), 'Missing argument in @%-name-% annotation',
+    'Missing argument in annotation',
+    'The annotation expects one argument, but the argument was not provided.');
+
+const INVALID_ANNOTATION_ARGUMENT = const MessageTemplate(
+    const MessageId('polymer', 35),
+    'The parameter to @%-name-% seems to be invalid.',
+    'Invalid argument in annotation', '''
+The polymer transformer was not able to extract a constant value for the
+annotation argument. This can happen if your code is currently in a state that
+can't be analyzed (for example, it has parse errors) or if the expression passed
+as an argument is invalid (for example, it is not a compile-time constant).
+''');
+
+const NO_INITIALIZATION = const MessageTemplate(const MessageId('polymer', 36),
+    'No polymer initializers were found. Make sure to either '
+    'annotate your polymer elements with @CustomTag or include a '
+    'top level method annotated with @initMethod that registers your '
+    'elements. Both annotations are defined in the polymer library ('
+    'package:polymer/polymer.dart).', 'No polymer initializers found', '''
+No polymer initializers were found. Make sure to either 
+annotate your polymer elements with @CustomTag or include a 
+top level method annotated with @initMethod that registers your 
+elements. Both annotations are defined in the polymer library (
+package:polymer/polymer.dart).
+''');
+
+const AT_EXPRESSION_REMOVED = const MessageTemplate(
+    const MessageId('polymer', 37),
+    'event bindings with @ are no longer supported',
+    'Event bindings with @ are no longer supported', '''
+For a while there was an undocumented feature that allowed users to include
+expressions in event bindings using the `@` prefix, for example:
+
+    <div on-click="{{@a.b.c}}">
+    
+This feature is no longer supported.
+''');
+
+const NO_PRIVATE_EVENT_HANDLERS = const MessageTemplate(
+    const MessageId('polymer', 38),
+    'private symbols cannot be used in event handlers',
+    'Private symbol in event handler', '''
+Currently private members can't be used in event handler bindings. So you can't
+write:
+
+    <div on-click="{{_method}}">
+
+This restriction might be removed in the future, but for now, you need to make
+your event handlers public.
+''');
+
+const NO_PRIVATE_SYMBOLS_IN_BINDINGS = const MessageTemplate(
+    const MessageId('polymer', 39), 'private symbols are not supported',
+    'Private symbol in binding expression', '''
+Private members can't be used in binding expressions. For example, you can't
+write:
+
+    <div>{{a.b._c}}</div>
+''');
+
+const HTML5_WARNING = const MessageTemplate(const MessageId('polymer', 40),
+    '(from html) %-message-%',
+    'A warning was found while parsing the HTML document', '''
+The polymer transformer uses a parser that implements the HTML5 spec
+(`html`). This message reports a
+warning that the parser detected.
+''');
+
+const POSSIBLE_FUOC = const MessageTemplate(const MessageId('polymer', 41),
+    'Custom element found in document body without an '
+    '"unresolved" attribute on it or one of its parents. This means '
+    'your app probably has a flash of unstyled content before it '
+    'finishes loading.', 'Possible flash of unstyled content', '''
+Custom element found in document body without an "unresolved" attribute on it or
+one of its parents. This means your app probably has a flash of unstyled content
+before it finishes loading. See <http://goo.gl/iN03Pj> for more info.
+''');
+
+const CSS_FILE_INLINED_MULTIPLE_TIMES = const MessageTemplate(
+    const MessageId('polymer', 42),
+    'The css file %-url-% was inlined multiple times.',
+    'A css file was inlined multiple times.', '''
+Css files are inlined by default, but if you import the same one in multiple
+places you probably want to change this behavior to prevent duplicate code.
+
+There are three typical options for dealing with this:
+
+1. **Recommended**: Use the `core-style` element from the `core_elements`
+    package.
+
+    The easiest way to do this is change your `*.css` file into a `*.html` file,
+    and wrap the entire thing in a `core-style` with an id, something like the
+    following:
+
+        <core-style id="my-theme">
+          p {
+            color: red;
+          }
+        </core-style>
+
+    Now, in the files where you were previously including the
+    `<link rel="stylesheet">` tag, add an html import to the top of your
+    document pointing to the new html file. Once that is done, replace the
+    `<link>` tag with a `<core-style>` tag which has a `ref` attribute that is
+    the same as the `id` attribute on the `<core-style>` you created. So your
+    original html:
+
+        <polymer-element name="my-element">
+          <template>
+            <link rel="stylesheet" href="my_theme.css">
+          </template>
+        </polymer-element>
+
+    Becomes:
+
+        <link rel="import" href="my_theme.html">
+        <polymer-element name="my-element">
+          <template>
+            <core-style ref="my-theme"></core-style>
+          </template>
+        </polymer-element>
+
+2. Opt out of the inlining for this file in your pubspec.yaml:
+
+        transformers:
+        - polymer:
+            inline_stylesheets:
+              web/my_file.css: false
+
+    **Warning**: `<link rel="stylesheet">` tags are not natively supported in
+    shadow-dom. Polymer will do an xhr request for the stylesheet and inject an
+    inline style with its contents in each place this stylesheet occurs.
+
+3. Opt into multiple inlining in your pubspec.yaml:
+
+        transformers:
+        - polymer:
+            inline_stylesheets:
+              web/my_file.css: true
+
+    **Warning**: You should only ever do this if your stylesheet is very small.
+    Even then stylesheets tend to grow quickly and almost never decrease in size
+    so this method is highly discouraged.
+''');
+
+const DART_SUPPORT_NO_LONGER_REQUIRED = const MessageTemplate(
+    const MessageId('polymer', 43),
+    'No need to include "dart_support.js" by hand anymore.',
+    '"dart_support.js" injected automatically', '''
+The script `packages/web_components/dart_support.js` is still used, but you no
+longer need to put it in your application's entrypoint.
+
+In the past this file served two purposes:
+
+  * to make dart2js work well with the web_components polyfills, and
+  * to support registering Dart APIs for JavaScript custom elements.
+
+Now, the code from `dart_support.js` is split in two halves. The half for
+dart2js is now injected by the polymer transformers automatically during `pub
+build`. The `web_components` package provides an HTML file containing the other
+half.  Developers of packages that wrap JavaScript custom elements (like
+`core_elements` and `paper_elements`) will import that file directly, so
+application developers don't have to worry about it anymore.
+''');
+
+const SCRIPT_INCLUDED_MORE_THAN_ONCE = const MessageTemplate(
+    const MessageId('polymer', 44),
+    'The `%-url-%` script was included more than once.',
+    'Dart script file included more than once.', '''
+Duplicate dart scripts often happen if you have multiple html imports that
+include the same script. The simplest workaround for this is to move your dart
+script to its own html file, and import that instead of the script (html imports
+are automatically deduped).
+
+For example:
+
+    <script type="application/dart" src="foo.dart"></script>
+
+Should turn into:
+
+    <link rel="import" href="foo.html">
+
+And `foo.html` should look like:
+
+    <script type="application/dart" src="foo.dart"></script>
+''');
+
+const WEB_COMPONENTS_NO_LONGER_REQUIRED = const MessageTemplate(
+    const MessageId('polymer', 45),
+    'No need to include "webcomponents.js" by hand anymore.',
+    '"webcomponents.js" injected automatically', '''
+The script `packages/web_components/webcomponents.js` is still used, but you no
+longer need to put it in your application's entrypoint.
+
+The polyfills provided by this file are no longer required in chrome and will
+automatically be added during `pub build` and `pub serve`.
+''');
+
+const PLATFORM_JS_RENAMED = const MessageTemplate(
+    const MessageId('polymer', 46),
+    '"platform.js" has been renamed to "webcomponents.js".',
+    '"platform.js" renamed to "webcomponents.js".', '''
+The script `packages/web_components/platform.js` has been renamed to
+`packages/web_components/webcomponents.js`. This is automatically fixed in
+`pub serve` and `pub build` but we may remove this functionality in the next
+breaking version of Polymer.
+
+In addition, it is no longer required that you include this file directly, as
+`pub build` and `pub serve` will inject it for you, and its not required when
+running in dartium with a local server.
+''');
+
+const NO_DART_SCRIPT = const MessageTemplate(const MessageId('polymer', 47),
+    'No dart script was found in the entry point: %-url-%.',
+    'Missing Dart script tag in entry point.', '''
+All entry points should have a dart script file. This can sometimes happen if
+you are using the default entry_points value in your polymer transformer
+configuration but have files which are not entry points in your `web` or `test`
+directory. Moving these files to your `lib` folder or specifying all your entry
+points in your configuration will fix this.
+''');
+
+const MISSING_POLYMER_DART = const MessageTemplate(
+    const MessageId('polymer', 48), 'polymer.dart not imported.',
+    'polymer.dart not imported.', '''
+It is required that your application contains an import to
+`package:polymer/polymer.dart`.
+''');
diff --git a/polymer/lib/src/build/polyfill_injector.dart b/polymer/lib/src/build/polyfill_injector.dart
new file mode 100644
index 0000000..d2772c1
--- /dev/null
+++ b/polymer/lib/src/build/polyfill_injector.dart
@@ -0,0 +1,137 @@
+// Copyright (c) 2013, 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.
+
+/// Includes any additional polyfills that may needed by the deployed app.
+library polymer.src.build.polyfill_injector;
+
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+import 'package:html/dom.dart'
+    show Document, DocumentFragment, Element, Node;
+import 'package:html/parser.dart' show parseFragment;
+import 'package:code_transformers/messages/build_logger.dart';
+import 'package:path/path.dart' as path;
+import 'package:web_components/transformer.dart' show testAttribute;
+import 'common.dart';
+
+/// Ensures that any scripts and polyfills needed to run a polymer application
+/// are included.
+///
+/// This step also replaces "packages/browser/dart.js" and the Dart script tag
+/// with a script tag that loads the dart2js compiled code directly.
+class PolyfillInjector extends Transformer with PolymerTransformer {
+  final TransformOptions options;
+
+  PolyfillInjector(this.options);
+
+  /// Only run on entry point .html files.
+  bool isPrimary(AssetId id) => options.isHtmlEntryPoint(id);
+
+  Future apply(Transform transform) {
+    var logger = new BuildLogger(transform,
+        convertErrorsToWarnings: !options.releaseMode,
+        detailsUri: 'http://goo.gl/5HPeuP');
+    return readPrimaryAsHtml(transform, logger).then((document) {
+      bool dartSupportFound = false;
+      Element webComponentsJs;
+      Element dartJs;
+      final dartScripts = <Element>[];
+
+      for (var tag in document.querySelectorAll('script')) {
+        var src = tag.attributes['src'];
+        if (src != null) {
+          var last = src.split('/').last;
+          if (_webComponentsJS.hasMatch(last)) {
+            webComponentsJs = tag;
+          } else if (_platformJS.hasMatch(last)) {
+            tag.attributes['src'] =
+                src.replaceFirst(_platformJS, 'webcomponents.min.js');
+            webComponentsJs = tag;
+          } else if (_dartSupportJS.hasMatch(last)) {
+            dartSupportFound = true;
+          } else if (_browserDartJS.hasMatch(src)) {
+            dartJs = tag;
+          }
+        }
+
+        if (tag.attributes['type'] == 'application/dart') {
+          dartScripts.add(tag);
+        }
+      }
+
+      if (dartScripts.isEmpty) {
+        // This HTML has no Dart code, there is nothing to do here.
+        transform.addOutput(transform.primaryInput);
+        return;
+      }
+
+      // Remove "packages/browser/dart.js". It is not needed in release mode,
+      // and in debug mode we want to ensure it is the last script on the page.
+      if (dartJs != null) dartJs.remove();
+
+      // TODO(jmesserly): ideally we would generate an HTML that loads
+      // dart2dart too. But for now dart2dart is not a supported deployment
+      // target, so just inline the JS script. This has the nice side effect of
+      // fixing our tests: even if content_shell supports Dart VM, we'll still
+      // test the compiled JS code.
+      if (options.directlyIncludeJS) {
+        // Replace all other Dart script tags with JavaScript versions.
+        for (var script in dartScripts) {
+          /// Don't modify scripts that were originally <link rel="x-test-dart">
+          /// tags.
+          if (script.attributes.containsKey(testAttribute)) continue;
+
+          final src = script.attributes['src'];
+          if (src.endsWith('.dart')) {
+            script.attributes.remove('type');
+            script.attributes['src'] = '$src.js';
+            // TODO(sigmund): we shouldn't need 'async' here. Remove this
+            // workaround for dartbug.com/19653.
+            script.attributes['async'] = '';
+          }
+        }
+      } else {
+        document.body.nodes.add(
+            parseFragment('<script src="packages/browser/dart.js"></script>'));
+      }
+
+      _addScript(urlSegment, [Node parent, int position = 0]) {
+        if (parent == null) parent = document.head;
+        var pathToPackages =
+            '../' * (path.url.split(transform.primaryInput.id.path).length - 2);
+        parent.nodes.insert(position, parseFragment(
+            '<script src="${pathToPackages}packages/$urlSegment"></script>'));
+      }
+
+      // Inserts dart_support.js either at the top of the document or directly
+      // after webcomponents.js if it exists.
+      if (!dartSupportFound) {
+        if (webComponentsJs == null) {
+          _addScript('web_components/dart_support.js');
+        } else {
+          var parentNode = webComponentsJs.parentNode;
+          _addScript('web_components/dart_support.js', parentNode,
+              parentNode.nodes.indexOf(webComponentsJs) + 1);
+        }
+      }
+
+      // By default webcomponents.js should come before all other scripts.
+      if (webComponentsJs == null && options.injectWebComponentsJs) {
+        var suffix = options.releaseMode ? '.min.js' : '.js';
+        _addScript('web_components/webcomponents$suffix');
+      }
+
+      transform.addOutput(
+          new Asset.fromString(transform.primaryInput.id, document.outerHtml));
+    });
+  }
+}
+
+final _platformJS = new RegExp(r'platform.*\.js', caseSensitive: false);
+final _webComponentsJS =
+    new RegExp(r'webcomponents.*\.js', caseSensitive: false);
+final _dartSupportJS = new RegExp(r'dart_support.js', caseSensitive: false);
+final _browserDartJS =
+    new RegExp(r'packages/browser/dart.js', caseSensitive: false);
diff --git a/polymer/lib/src/build/polymer_bootstrap.dart b/polymer/lib/src/build/polymer_bootstrap.dart
new file mode 100644
index 0000000..f622521
--- /dev/null
+++ b/polymer/lib/src/build/polymer_bootstrap.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2013, 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 polymer.src.build.polymer_bootstrap;
+
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+import 'package:code_transformers/messages/build_logger.dart';
+import 'package:code_transformers/assets.dart';
+import 'package:code_transformers/resolver.dart';
+import 'package:code_transformers/src/dart_sdk.dart' as dart_sdk;
+import 'package:path/path.dart' as path;
+import 'package:web_components/build/web_components.dart';
+
+import 'common.dart';
+import 'messages.dart';
+import 'polymer_smoke_generator.dart';
+
+/// The primary polymer transformer that handles everything which requires a
+/// [Resolver] so they can share it.
+// Note: This is effectively tested in `all_phases_test.dart` as it doesn't
+// really deserve its own unit test.
+class PolymerBootstrapTransformer extends Transformer with PolymerTransformer {
+  final Resolvers resolvers;
+  final TransformOptions options;
+
+  PolymerBootstrapTransformer(this.options, {String sdkDir})
+      // TODO(sigmund): consider restoring here a resolver that uses the real
+      // SDK once the analyzer is lazy and only an resolves what it needs:
+      //: resolvers = new Resolvers(sdkDir != null ? sdkDir : dartSdkDirectory);
+      : resolvers = new Resolvers.fromMock(dart_sdk.mockSdkSources);
+
+  /// Only run on entry point .html files.
+  bool isPrimary(AssetId id) => options.isHtmlEntryPoint(id);
+
+  Future apply(Transform transform) {
+    var logger = new BuildLogger(transform,
+        convertErrorsToWarnings: !options.releaseMode,
+        detailsUri: 'http://goo.gl/5HPeuP');
+    var primaryId = transform.primaryInput.id;
+    return readPrimaryAsHtml(transform, logger).then((document) {
+      var script = document.querySelector('script[type="application/dart"]');
+      if (script == null) {
+        logger.warning(NO_DART_SCRIPT.create({'url': primaryId.path}));
+        return null;
+      }
+
+      var entryScriptId = uriToAssetId(
+          primaryId, script.attributes['src'], logger, script.sourceSpan);
+
+      return resolvers.get(transform, [entryScriptId]).then((resolver) {
+        var webComponentsBootstrapId =
+            primaryId.changeExtension('.web_components.bootstrap.dart');
+        var webComponentsBootstrap = generateWebComponentsBootstrap(resolver,
+            transform, document, entryScriptId, webComponentsBootstrapId);
+        transform.addOutput(webComponentsBootstrap);
+
+        var polymerBootstrapId =
+            primaryId.addExtension('.polymer.bootstrap.dart');
+        script.attributes['src'] = path.basename(polymerBootstrapId.path);
+
+        return generatePolymerBootstrap(transform, resolver,
+            webComponentsBootstrapId, polymerBootstrapId, document, options,
+            resolveFromId: entryScriptId).then((polymerBootstrap) {
+          transform.addOutput(polymerBootstrap);
+          transform
+              .addOutput(new Asset.fromString(primaryId, document.outerHtml));
+          resolver.release();
+        });
+      });
+    });
+  }
+}
diff --git a/polymer/lib/src/build/polymer_smoke_generator.dart b/polymer/lib/src/build/polymer_smoke_generator.dart
new file mode 100644
index 0000000..11664f6
--- /dev/null
+++ b/polymer/lib/src/build/polymer_smoke_generator.dart
@@ -0,0 +1,736 @@
+// Copyright (c) 2013, 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.
+
+/// Transfomer that combines multiple Dart script tags into a single one.
+library polymer.src.build.polymer_smoke_generator;
+
+import 'dart:async';
+
+import 'package:html/dom.dart' show Document, Element, Text;
+import 'package:html/dom_parsing.dart';
+import 'package:html/parser.dart' show parseFragment;
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart' hide Element;
+import 'package:analyzer/src/generated/element.dart' as analyzer show Element;
+import 'package:barback/barback.dart';
+import 'package:code_transformers/messages/build_logger.dart';
+import 'package:code_transformers/assets.dart';
+import 'package:code_transformers/src/dart_sdk.dart' as dart_sdk;
+import 'package:path/path.dart' as path;
+import 'package:source_span/source_span.dart';
+import 'package:smoke/codegen/generator.dart';
+import 'package:smoke/codegen/recorder.dart';
+import 'package:code_transformers/resolver.dart';
+import 'package:template_binding/src/mustache_tokens.dart' show MustacheTokens;
+
+import 'package:polymer_expressions/expression.dart' as pe;
+import 'package:polymer_expressions/parser.dart' as pe;
+import 'package:polymer_expressions/visitor.dart' as pe;
+
+import 'package:web_components/build/import_crawler.dart';
+
+import 'common.dart';
+import 'messages.dart';
+
+/// Method to generate a bootstrap file for Polymer given a [Transform] and a
+/// [Resolver]. This can be used inside any transformer to share the [Resolver]
+/// with other steps.
+Future<Asset> generatePolymerBootstrap(Transform transform, Resolver resolver,
+    AssetId entryPointId, AssetId bootstrapId, Document document,
+    TransformOptions options, {AssetId resolveFromId}) {
+  return new PolymerSmokeGenerator(
+      transform, resolver, entryPointId, bootstrapId, document, options,
+      resolveFromId: resolveFromId).apply();
+}
+
+class PolymerSmokeGeneratorTransformer extends Transformer
+    with PolymerTransformer {
+  final Resolvers resolvers;
+  final TransformOptions options;
+
+  PolymerSmokeGeneratorTransformer(this.options, {String sdkDir})
+      // TODO(sigmund): consider restoring here a resolver that uses the real
+      // SDK once the analyzer is lazy and only an resolves what it needs:
+      //: resolvers = new Resolvers(sdkDir != null ? sdkDir : dartSdkDirectory);
+      : resolvers = new Resolvers.fromMock(dart_sdk.mockSdkSources);
+
+  /// Only run on entry point .html files.
+  bool isPrimary(AssetId id) => options.isHtmlEntryPoint(id);
+
+  Future apply(Transform transform) {
+    var logger = new BuildLogger(transform,
+        convertErrorsToWarnings: !options.releaseMode,
+        detailsUri: 'http://goo.gl/5HPeuP');
+    var primaryId = transform.primaryInput.id;
+    return readPrimaryAsHtml(transform, logger).then((document) {
+      var script = document.querySelector('script[type="application/dart"]');
+      if (script == null) return null;
+      var entryScriptId = uriToAssetId(
+          primaryId, script.attributes['src'], logger, script.sourceSpan);
+      var bootstrapId = primaryId.addExtension('_bootstrap.dart');
+      script.attributes['src'] = path.basename(bootstrapId.path);
+
+      return resolvers.get(transform, [entryScriptId]).then((resolver) {
+        return generatePolymerBootstrap(transform, resolver, entryScriptId,
+            bootstrapId, document, options).then((bootstrapAsset) {
+          transform.addOutput(bootstrapAsset);
+          transform
+              .addOutput(new Asset.fromString(primaryId, document.outerHtml));
+          resolver.release();
+        });
+      });
+    });
+  }
+}
+
+/// Class which generates the static smoke configuration for polymer.
+// TODO(jakemac): Investigate further turning this into an [InitializerPlugin].
+// The main difficulty is this actually recognizes any class which extends the
+// [PolymerElement] class, not just things annotated with [CustomTag].
+class PolymerSmokeGenerator {
+  final TransformOptions options;
+  final Transform transform;
+  final BuildLogger logger;
+  final AssetId docId;
+  final AssetId bootstrapId;
+
+  /// Id of the Dart script found in the document (can only be one).
+  AssetId entryScriptId;
+
+  /// Id of the Dart script to start resolution from.
+  AssetId resolveFromId;
+
+  /// HTML document parsed from [docId].
+  Document document;
+
+  /// Attributes published on a custom-tag. We make these available via
+  /// reflection even if @published was not used.
+  final Map<String, List<String>> publishedAttributes = {};
+
+  /// Resolved types used for analyzing the user's sources and generating code.
+  _ResolvedTypes types;
+
+  /// The resolver instance associated with a single run of this transformer.
+  Resolver resolver;
+
+  /// Code generator used to create the static initialization for smoke.
+  final generator = new SmokeCodeGenerator();
+
+  _SubExpressionVisitor expressionVisitor;
+
+  PolymerSmokeGenerator(Transform transform, Resolver resolver,
+      this.entryScriptId, this.bootstrapId, this.document, options,
+      {this.resolveFromId})
+      : transform = transform,
+        options = options,
+        logger = new BuildLogger(transform,
+            convertErrorsToWarnings: !options.releaseMode,
+            detailsUri: 'http://goo.gl/5HPeuP'),
+        docId = transform.primaryInput.id,
+        resolver = resolver {
+    _ResolvedTypes.logger = logger;
+    types = new _ResolvedTypes(resolver);
+    if (resolveFromId == null) resolveFromId = entryScriptId;
+  }
+
+  Future<Asset> apply() {
+    return _extractUsesOfMirrors().then((_) {
+      var bootstrapAsset = _buildBootstrap();
+      _modifyDocument();
+
+      // Write out the logs collected by our [BuildLogger].
+      if (options.injectBuildLogsInOutput) {
+        return logger.writeOutput().then((_) => bootstrapAsset);
+      }
+      return bootstrapAsset;
+    });
+  }
+
+  /// Inspects the entire program to find out anything that polymer accesses
+  /// using mirrors and produces static information that can be used to replace
+  /// the mirror-based loader and the uses of mirrors through the `smoke`
+  /// package. This includes:
+  ///
+  ///   * visiting polymer-expressions to extract getters and setters,
+  ///   * looking for published fields of custom elements, and
+  ///   * looking for event handlers and callbacks of change notifications.
+  ///
+  Future _extractUsesOfMirrors() {
+    // Generate getters and setters needed to evaluate polymer expressions, and
+    // extract information about published attributes.
+    expressionVisitor = new _SubExpressionVisitor(generator, logger);
+
+    return new ImportCrawler(transform, transform.primaryInput.id, logger,
+        primaryDocument: document).crawlImports().then((documentData) {
+      for (var data in documentData.values) {
+        new _HtmlExtractor(
+                logger, generator, publishedAttributes, expressionVisitor)
+            .visit(data.document);
+      }
+
+      // Create a recorder that uses analyzer data to feed data to [generator].
+      var recorder = new Recorder(generator,
+          (lib) => resolver.getImportUri(lib, from: bootstrapId).toString());
+
+      // Process all classes to include special fields and methods in custom
+      // element classes.
+      _visitLibraries(resolver.getLibrary(resolveFromId), recorder);
+    });
+  }
+
+  _visitLibraries(LibraryElement library, Recorder recorder,
+      [Set<LibraryElement> librariesSeen, Set<ClassElement> classesSeen]) {
+    if (librariesSeen == null) librariesSeen = new Set<LibraryElement>();
+    librariesSeen.add(library);
+
+    // Visit all our dependencies.
+    for (var importedLibrary in _libraryDependencies(library)) {
+      // Don't include anything from the sdk.
+      if (importedLibrary.isInSdk) continue;
+      if (librariesSeen.contains(importedLibrary)) continue;
+      _visitLibraries(importedLibrary, recorder, librariesSeen, classesSeen);
+    }
+
+    // After visiting dependencies, then visit classes in this library.
+    if (classesSeen == null) classesSeen = new Set<ClassElement>();
+    var classes = _visibleClassesOf(library);
+    for (var clazz in classes) {
+      _processClass(clazz, recorder);
+    }
+  }
+
+  Iterable<LibraryElement> _libraryDependencies(LibraryElement library) {
+    getLibrary(UriReferencedElement element) {
+      if (element is ImportElement) return element.importedLibrary;
+      if (element is ExportElement) return element.exportedLibrary;
+    }
+
+    return (new List.from(library.imports)..addAll(library.exports))
+        .map(getLibrary);
+  }
+
+  /// Process a class ([cls]). If it contains an appropriate [CustomTag]
+  /// annotation, we make sure to include everything that might be accessed or
+  /// queried from them using the smoke package. In particular, polymer uses
+  /// smoke for the following:
+  ///    * invoke #registerCallback on custom elements classes, if present.
+  ///    * query for methods ending in `*Changed`.
+  ///    * query for methods with the `@ObserveProperty` annotation.
+  ///    * query for non-final properties labeled with `@published`.
+  ///    * read declarations of properties named in the `attributes` attribute.
+  ///    * read/write the value of published properties .
+  ///    * invoke methods in event handlers.
+  _processClass(ClassElement cls, Recorder recorder) {
+    if (!_hasPolymerMixin(cls)) return;
+    if (cls.node is! ClassDeclaration) return;
+    var node = cls.node as ClassDeclaration;
+
+    // Check whether the class has a @CustomTag annotation. Typically we expect
+    // a single @CustomTag, but it's possible to have several.
+    var tagNames = [];
+    for (var meta in node.metadata) {
+      var tagName = _extractTagName(meta, cls);
+      if (tagName != null) tagNames.add(tagName);
+    }
+
+    if (cls.isPrivate && tagNames.isNotEmpty) {
+      var name = tagNames.first;
+      logger.error(PRIVATE_CUSTOM_TAG.create({'name': name, 'class': cls.name}),
+          span: _spanForNode(cls, node.name));
+      return;
+    }
+
+    // Include #registerCallback if it exists. Note that by default lookupMember
+    // and query will also add the corresponding getters and setters.
+    recorder.lookupMember(cls, 'registerCallback');
+
+    // Include methods that end with *Changed.
+    recorder.runQuery(cls, new QueryOptions(
+        includeFields: false,
+        includeProperties: false,
+        includeInherited: true,
+        includeMethods: true,
+        includeUpTo: types.htmlElementElement,
+        matches: (n) => n.endsWith('Changed') && n != 'attributeChanged'));
+
+    // Include methods marked with @ObserveProperty.
+    recorder.runQuery(cls, new QueryOptions(
+        includeFields: false,
+        includeProperties: false,
+        includeInherited: true,
+        includeMethods: true,
+        includeUpTo: types.htmlElementElement,
+        withAnnotations: [types.observePropertyElement]));
+
+    // Include @published and @observable properties.
+    // Symbols in @published are used when resolving bindings on published
+    // attributes, symbols for @observable are used via path observers when
+    // implementing *Changed an @ObserveProperty.
+    // TODO(sigmund): consider including only those symbols mentioned in
+    // *Changed and @ObserveProperty instead.
+    recorder.runQuery(cls, new QueryOptions(
+        includeUpTo: types.htmlElementElement,
+        withAnnotations: [
+      types.publishedElement,
+      types.observableElement,
+      types.computedPropertyElement
+    ]));
+
+    // Include @ComputedProperty and process their expressions
+    var computed = [];
+    recorder.runQuery(cls, new QueryOptions(
+        includeUpTo: types.htmlElementElement,
+        withAnnotations: [types.computedPropertyElement]), results: computed);
+    _processComputedExpressions(computed);
+
+    for (var tagName in tagNames) {
+      // Include also properties published via the `attributes` attribute.
+      var attrs = publishedAttributes[tagName];
+      if (attrs == null) continue;
+      for (var attr in attrs) {
+        recorder.lookupMember(cls, attr,
+            recursive: true, includeUpTo: types.htmlElementElement);
+      }
+    }
+  }
+
+  /// Determines if [cls] or a supertype has a mixin of the Polymer class.
+  bool _hasPolymerMixin(ClassElement cls) {
+    while (cls != types.htmlElementElement) {
+      for (var m in cls.mixins) {
+        if (m.element == types.polymerClassElement) return true;
+      }
+      if (cls.supertype == null) return false;
+      cls = cls.supertype.element;
+    }
+    return false;
+  }
+
+  /// If [meta] is [CustomTag], extract the name associated with the tag.
+  String _extractTagName(Annotation meta, ClassElement cls) {
+    if (meta.element != types.customTagConstructor) return null;
+    return _extractFirstAnnotationArgument(meta, 'CustomTag', cls);
+  }
+
+  /// Extract the first argument of an annotation and validate that it's type is
+  /// String. For instance, return "bar" from `@Foo("bar")`.
+  String _extractFirstAnnotationArgument(
+      Annotation meta, String name, analyzer.Element context) {
+
+    // Read argument from the AST
+    var args = meta.arguments.arguments;
+    if (args == null || args.length == 0) {
+      logger.warning(MISSING_ANNOTATION_ARGUMENT.create({'name': name}),
+          span: _spanForNode(context, meta));
+      return null;
+    }
+
+    var lib = context;
+    while (lib is! LibraryElement) lib = lib.enclosingElement;
+    var res = resolver.evaluateConstant(lib, args[0]);
+    if (!res.isValid || res.value.type != types.stringType) {
+      logger.warning(INVALID_ANNOTATION_ARGUMENT.create({'name': name}),
+          span: _spanForNode(context, args[0]));
+      return null;
+    }
+    return res.value.stringValue;
+  }
+
+  /// Process members that are annotated with `@ComputedProperty` and records
+  /// the accessors of their expressions.
+  _processComputedExpressions(List<analyzer.Element> computed) {
+    var constructor = types.computedPropertyElement.constructors.first;
+    for (var member in computed) {
+      for (var meta in member.node.metadata) {
+        if (meta.element != constructor) continue;
+        var expr =
+            _extractFirstAnnotationArgument(meta, 'ComputedProperty', member);
+        if (expr == null) continue;
+        expressionVisitor.run(pe.parse(expr), true,
+            _spanForNode(member.enclosingElement, meta.arguments.arguments[0]));
+      }
+    }
+  }
+
+  // Builds the bootstrap Dart file asset.
+  Asset _buildBootstrap() {
+    StringBuffer code = new StringBuffer()..writeln(MAIN_HEADER);
+
+    // TODO(jakemac): Inject this at some other stage.
+    // https://github.com/dart-lang/polymer-dart/issues/22
+    if (options.injectBuildLogsInOutput) {
+      code.writeln("import 'package:polymer/src/build/log_injector.dart';");
+    }
+
+    var entryScriptUrl = assetUrlFor(entryScriptId, bootstrapId, logger);
+    code.writeln("import '$entryScriptUrl' as i0;");
+
+    // Include smoke initialization.
+    generator.writeImports(code);
+    generator.writeTopLevelDeclarations(code);
+    code.writeln('\nmain() {');
+    code.write('  useGeneratedCode(');
+    generator.writeStaticConfiguration(code);
+    code.writeln(');');
+
+    // TODO(jakemac): Inject this at some other stage.
+    // https://github.com/dart-lang/polymer-dart/issues/22
+    if (options.injectBuildLogsInOutput) {
+      var buildUrl = "${path.basename(docId.path)}$LOG_EXTENSION";
+      code.writeln("  new LogInjector().injectLogsFromUrl('$buildUrl');");
+    }
+
+    code.writeln('  configureForDeployment();');
+    code.writeln('  return i0.main();');
+
+    // End of main().
+    code.writeln('}');
+    return new Asset.fromString(bootstrapId, code.toString());
+  }
+
+  // Add the styles for the logger widget.
+  // TODO(jakemac): Inject this at some other stage.
+  // https://github.com/dart-lang/polymer-dart/issues/22
+  void _modifyDocument() {
+    if (options.injectBuildLogsInOutput) {
+      document.head.append(parseFragment(
+          '<link rel="stylesheet" type="text/css"'
+          ' href="packages/polymer/src/build/log_injector.css">'));
+    }
+  }
+
+  _spanForNode(analyzer.Element context, AstNode node) {
+    var file = resolver.getSourceFile(context);
+    return file.span(node.offset, node.end);
+  }
+}
+
+const MAIN_HEADER = """
+library app_bootstrap;
+
+import 'package:polymer/polymer.dart';
+""";
+
+/// An html visitor that:
+///   * finds all polymer expressions and records the getters and setters that
+///     will be needed to evaluate them at runtime.
+///   * extracts all attributes declared in the `attribute` attributes of
+///     polymer elements.
+class _HtmlExtractor extends TreeVisitor {
+  final Map<String, List<String>> publishedAttributes;
+  final SmokeCodeGenerator generator;
+  final _SubExpressionVisitor expressionVisitor;
+  final BuildLogger logger;
+  bool _inTemplate = false;
+  bool _inPolymerJs = false;
+
+  _HtmlExtractor(this.logger, this.generator, this.publishedAttributes,
+      this.expressionVisitor);
+
+  void visitElement(Element node) {
+    if (_inTemplate) _processNormalElement(node);
+    var lastInPolymerJs = _inPolymerJs;
+    if (node.localName == 'polymer-element') {
+      // Detect Polymer JS elements, the current logic is any element with only
+      // non-Dart script tags.
+      var scripts = node.querySelectorAll('script');
+      _inPolymerJs = scripts.isNotEmpty &&
+          scripts.every((s) => s.attributes['type'] != 'application/dart');
+      _processPolymerElement(node);
+      _processNormalElement(node);
+    }
+
+    if (node.localName == 'template') {
+      var last = _inTemplate;
+      _inTemplate = true;
+      super.visitElement(node);
+      _inTemplate = last;
+    } else {
+      super.visitElement(node);
+    }
+    _inPolymerJs = lastInPolymerJs;
+  }
+
+  void visitText(Text node) {
+    // Nothing here applies if inside a polymer js element
+    if (!_inTemplate || _inPolymerJs) return;
+    var bindings = _Mustaches.parse(node.data);
+    if (bindings == null) return;
+    for (var e in bindings.expressions) {
+      _addExpression(e, false, false, node.sourceSpan);
+    }
+  }
+
+  /// Registers getters and setters for all published attributes.
+  void _processPolymerElement(Element node) {
+    // Nothing here applies if inside a polymer js element
+    if (_inPolymerJs) return;
+
+    var tagName = node.attributes['name'];
+    var value = node.attributes['attributes'];
+    if (value != null) {
+      publishedAttributes[tagName] =
+          value.split(ATTRIBUTES_REGEX).map((a) => a.trim()).toList();
+    }
+  }
+
+  /// Produces warnings for misuses of on-foo event handlers, and for instanting
+  /// custom tags incorrectly.
+  void _processNormalElement(Element node) {
+    // Nothing here applies if inside a polymer js element
+    if (_inPolymerJs) return;
+
+    var tag = node.localName;
+    var isCustomTag = isCustomTagName(tag) || node.attributes['is'] != null;
+
+    // Event handlers only allowed inside polymer-elements
+    node.attributes.forEach((name, value) {
+      var bindings = _Mustaches.parse(value);
+      if (bindings == null) return;
+      var isEvent = false;
+      var isTwoWay = false;
+      if (name is String) {
+        name = name.toLowerCase();
+        isEvent = name.startsWith('on-');
+        isTwoWay = !isEvent &&
+            bindings.isWhole &&
+            (isCustomTag ||
+                tag == 'input' && (name == 'value' || name == 'checked') ||
+                tag == 'select' &&
+                    (name == 'selectedindex' || name == 'value') ||
+                tag == 'textarea' && name == 'value');
+      }
+      for (var exp in bindings.expressions) {
+        _addExpression(exp, isEvent, isTwoWay, node.sourceSpan);
+      }
+    });
+  }
+
+  void _addExpression(
+      String stringExpression, bool inEvent, bool isTwoWay, SourceSpan span) {
+    if (inEvent) {
+      if (stringExpression.startsWith('@')) {
+        logger.warning(AT_EXPRESSION_REMOVED, span: span);
+        return;
+      }
+
+      if (stringExpression == '') return;
+      if (stringExpression.startsWith('_')) {
+        logger.warning(NO_PRIVATE_EVENT_HANDLERS, span: span);
+        return;
+      }
+      generator.addGetter(stringExpression);
+      generator.addSymbol(stringExpression);
+    }
+    expressionVisitor.run(pe.parse(stringExpression), isTwoWay, span);
+  }
+}
+
+/// A polymer-expression visitor that records every getter and setter that will
+/// be needed to evaluate a single expression at runtime.
+class _SubExpressionVisitor extends pe.RecursiveVisitor {
+  final SmokeCodeGenerator generator;
+  final BuildLogger logger;
+  bool _includeSetter;
+  SourceSpan _currentSpan;
+
+  _SubExpressionVisitor(this.generator, this.logger);
+
+  /// Visit [exp], and record getters and setters that are needed in order to
+  /// evaluate it at runtime. [includeSetter] is only true if this expression
+  /// occured in a context where it could be updated, for example in two-way
+  /// bindings such as `<input value={{exp}}>`.
+  void run(pe.Expression exp, bool includeSetter, span) {
+    _currentSpan = span;
+    _includeSetter = includeSetter;
+    visit(exp);
+  }
+
+  /// Adds a getter and symbol for [name], and optionally a setter.
+  _add(String name) {
+    if (name.startsWith('_')) {
+      logger.warning(NO_PRIVATE_SYMBOLS_IN_BINDINGS, span: _currentSpan);
+      return;
+    }
+    generator.addGetter(name);
+    generator.addSymbol(name);
+    if (_includeSetter) generator.addSetter(name);
+  }
+
+  void preVisitExpression(e) {
+    // For two-way bindings the outermost expression may be updated, so we need
+    // both the getter and the setter, but we only need the getter for
+    // subexpressions. We exclude setters as soon as we go deeper in the tree,
+    // except when we see a filter (that can potentially be a two-way
+    // transformer).
+    if (e is pe.BinaryOperator && e.operator == '|') return;
+    _includeSetter = false;
+  }
+
+  visitIdentifier(pe.Identifier e) {
+    if (e.value != 'this') _add(e.value);
+    super.visitIdentifier(e);
+  }
+
+  visitGetter(pe.Getter e) {
+    _add(e.name);
+    super.visitGetter(e);
+  }
+
+  visitInvoke(pe.Invoke e) {
+    _includeSetter = false; // Invoke is only valid as an r-value.
+    if (e.method != null) _add(e.method);
+    super.visitInvoke(e);
+  }
+}
+
+/// Parses and collects information about bindings found in polymer templates.
+class _Mustaches {
+  /// Each expression that appears within `{{...}}` and `[[...]]`.
+  final List<String> expressions;
+
+  /// Whether the whole text returned by [parse] was a single expression.
+  final bool isWhole;
+
+  _Mustaches(this.isWhole, this.expressions);
+
+  static _Mustaches parse(String text) {
+    if (text == null || text.isEmpty) return null;
+    // Use template-binding's parser, but provide a delegate function factory to
+    // save the expressions without parsing them as [PropertyPath]s.
+    var tokens = MustacheTokens.parse(text, (s) => () => s);
+    if (tokens == null) return null;
+    var length = tokens.length;
+    bool isWhole =
+        length == 1 && tokens.getText(length) == '' && tokens.getText(0) == '';
+    var expressions = new List(length);
+    for (int i = 0; i < length; i++) {
+      expressions[i] = tokens.getPrepareBinding(i)();
+    }
+    return new _Mustaches(isWhole, expressions);
+  }
+}
+
+/// Holds types that are used in queries
+class _ResolvedTypes {
+  /// Element representing `HtmlElement`.
+  final ClassElement htmlElementElement;
+
+  /// Element representing `String`.
+  final InterfaceType stringType;
+
+  /// Element representing `Polymer`.
+  final ClassElement polymerClassElement;
+
+  /// Element representing the constructor of `@CustomTag`.
+  final ConstructorElement customTagConstructor;
+
+  /// Element representing the type of `@published`.
+  final ClassElement publishedElement;
+
+  /// Element representing the type of `@observable`.
+  final ClassElement observableElement;
+
+  /// Element representing the type of `@ObserveProperty`.
+  final ClassElement observePropertyElement;
+
+  /// Element representing the type of `@ComputedProperty`.
+  final ClassElement computedPropertyElement;
+
+  /// Logger for reporting errors.
+  static BuildLogger logger;
+
+  factory _ResolvedTypes(Resolver resolver) {
+    var coreLib = resolver.getLibraryByUri(Uri.parse('dart:core'));
+    // coreLib should never be null, its ok to throw if this fails.
+    var stringType = _lookupType(coreLib, 'String').type;
+
+    // Load class elements that are used in queries for codegen.
+    var polymerLib =
+        resolver.getLibrary(new AssetId('polymer', 'lib/polymer.dart'));
+    if (polymerLib == null) {
+      _definitionError('polymer');
+      return new _ResolvedTypes.internal(
+          null, stringType, null, null, null, null, null, null);
+    }
+
+    var htmlLib = resolver.getLibraryByUri(Uri.parse('dart:html'));
+    var observeLib =
+        resolver.getLibrary(new AssetId('observe', 'lib/src/metadata.dart'));
+
+    var customTagConstructor =
+        _lookupType(polymerLib, 'CustomTag').constructors.first;
+    var publishedElement = _lookupType(polymerLib, 'PublishedProperty');
+    var observePropertyElement = _lookupType(polymerLib, 'ObserveProperty');
+    var computedPropertyElement = _lookupType(polymerLib, 'ComputedProperty');
+    var polymerClassElement = _lookupType(polymerLib, 'Polymer');
+    var observableElement = _lookupType(observeLib, 'ObservableProperty');
+    var htmlElementElement = _lookupType(htmlLib, 'HtmlElement');
+
+    return new _ResolvedTypes.internal(htmlElementElement, stringType,
+        polymerClassElement, customTagConstructor, publishedElement,
+        observableElement, observePropertyElement, computedPropertyElement);
+  }
+
+  _ResolvedTypes.internal(this.htmlElementElement, this.stringType,
+      this.polymerClassElement, this.customTagConstructor,
+      this.publishedElement, this.observableElement,
+      this.observePropertyElement, this.computedPropertyElement);
+
+  static _lookupType(LibraryElement lib, String typeName) {
+    var result = lib.getType(typeName);
+    if (result == null) _definitionError(typeName);
+    return result;
+  }
+
+  static _definitionError(name) {
+    var message = MISSING_POLYMER_DART;
+    if (logger != null) {
+      logger.warning(message);
+    } else {
+      throw new StateError(message.snippet);
+    }
+  }
+}
+
+/// Retrieves all classes that are visible if you were to import [lib]. This
+/// includes exported classes from other libraries.
+List<ClassElement> _visibleClassesOf(LibraryElement lib) {
+  var result = [];
+  result.addAll(lib.units.expand((u) => u.types));
+  for (var e in lib.exports) {
+    var exported = e.exportedLibrary.units.expand((u) => u.types).toList();
+    _filter(exported, e.combinators);
+    result.addAll(exported);
+  }
+  return result;
+}
+
+/// Retrieves all top-level methods that are visible if you were to import
+/// [lib]. This includes exported methods from other libraries too.
+List<FunctionElement> _visibleTopLevelMethodsOf(LibraryElement lib) {
+  var result = [];
+  result.addAll(lib.units.expand((u) => u.functions));
+  for (var e in lib.exports) {
+    var exported = e.exportedLibrary.units.expand((u) => u.functions).toList();
+    _filter(exported, e.combinators);
+    result.addAll(exported);
+  }
+  return result;
+}
+
+/// Filters [elements] that come from an export, according to its show/hide
+/// combinators. This modifies [elements] in place.
+void _filter(
+    List<analyzer.Element> elements, List<NamespaceCombinator> combinators) {
+  for (var c in combinators) {
+    if (c is ShowElementCombinator) {
+      var show = c.shownNames.toSet();
+      elements.retainWhere((e) => show.contains(e.displayName));
+    } else if (c is HideElementCombinator) {
+      var hide = c.hiddenNames.toSet();
+      elements.removeWhere((e) => hide.contains(e.displayName));
+    }
+  }
+}
diff --git a/polymer/lib/src/build/runner.dart b/polymer/lib/src/build/runner.dart
new file mode 100644
index 0000000..5488c07
--- /dev/null
+++ b/polymer/lib/src/build/runner.dart
@@ -0,0 +1,386 @@
+// Copyright (c) 2013, 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.
+
+/// Definitions used to run the polymer linter and deploy tools without using
+/// pub serve or pub deploy.
+library polymer.src.build.runner;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:barback/barback.dart';
+import 'package:path/path.dart' as path;
+import 'package:stack_trace/stack_trace.dart';
+import 'package:yaml/yaml.dart';
+
+/// Collects different parameters needed to configure and run barback.
+class BarbackOptions {
+  /// Phases of transformers to run for the current package.
+  /// Use packagePhases to specify phases for other packages.
+  final List<List<Transformer>> phases;
+
+  /// Package to treat as the current package in barback.
+  final String currentPackage;
+
+  /// Directory root for the current package.
+  final String packageHome;
+
+  /// Mapping between package names and the path in the file system where
+  /// to find the sources of such package.
+  final Map<String, String> packageDirs;
+
+  /// Whether to run transformers on the test folder.
+  final bool transformTests;
+
+  /// Directory where to generate code, if any.
+  final String outDir;
+
+  /// Disregard files that match these filters when copying in non
+  /// transformed files
+  List<String> fileFilter;
+
+  /// Whether to print error messages using a json-format that tools, such as
+  /// the Dart Editor, can process.
+  final bool machineFormat;
+
+  /// Whether to follow symlinks when listing directories. By default this is
+  /// false because directories have symlinks for the packages directory created
+  /// by pub, but it can be turned on for custom uses of this library.
+  final bool followLinks;
+
+  /// Phases of transformers to apply to packages other than the current
+  /// package, keyed by the package name.
+  final Map<String, List<List<Transformer>>> packagePhases;
+
+  BarbackOptions(this.phases, this.outDir, {currentPackage, String packageHome,
+      packageDirs, this.transformTests: false, this.machineFormat: false,
+      this.followLinks: false, this.packagePhases: const {},
+      this.fileFilter: const []})
+      : currentPackage = (currentPackage != null
+          ? currentPackage
+          : readCurrentPackageFromPubspec()),
+        packageHome = packageHome,
+        packageDirs = (packageDirs != null
+            ? packageDirs
+            : readPackageDirsFromPub(packageHome, currentPackage));
+}
+
+/// Creates a barback system as specified by [options] and runs it.  Returns a
+/// future that contains the list of assets generated after barback runs to
+/// completion.
+Future<AssetSet> runBarback(BarbackOptions options) {
+  var barback = new Barback(new _PackageProvider(options.packageDirs));
+  _initBarback(barback, options);
+  _attachListeners(barback, options);
+  if (options.outDir == null) return barback.getAllAssets();
+  return _emitAllFiles(barback, options);
+}
+
+/// Extract the current package from the pubspec.yaml file.
+String readCurrentPackageFromPubspec([String dir]) {
+  var pubspec =
+      new File(dir == null ? 'pubspec.yaml' : path.join(dir, 'pubspec.yaml'));
+  if (!pubspec.existsSync()) {
+    print('error: pubspec.yaml file not found, please run this script from '
+        'your package root directory.');
+    return null;
+  }
+  return loadYaml(pubspec.readAsStringSync())['name'];
+}
+
+/// Extract a mapping between package names and the path in the file system
+/// which has the source of the package. This map will contain an entry for the
+/// current package and everything it depends on (extracted via `pub
+/// list-package-dirs`).
+Map<String, String> readPackageDirsFromPub(
+    [String packageHome, String currentPackage]) {
+  var cachedDir = Directory.current;
+  if (packageHome != null) {
+    Directory.current = new Directory(packageHome);
+  } else {
+    packageHome = cachedDir.path;
+  }
+
+  var dartExec = Platform.executable;
+  // If dartExec == dart, then dart and pub are in standard PATH.
+  var sdkDir = dartExec == 'dart' ? '' : path.dirname(dartExec);
+  var pub = path.join(sdkDir, Platform.isWindows ? 'pub.bat' : 'pub');
+  var result = Process.runSync(pub, ['list-package-dirs']);
+  if (result.exitCode != 0) {
+    print("unexpected error invoking 'pub':");
+    print(result.stdout);
+    print(result.stderr);
+    exit(result.exitCode);
+  }
+  var map = JSON.decode(result.stdout)["packages"];
+  map.forEach((k, v) {
+    map[k] = path.absolute(packageHome, path.dirname(v));
+  });
+
+  if (currentPackage == null) {
+    currentPackage = readCurrentPackageFromPubspec(packageHome);
+  }
+  map[currentPackage] = packageHome;
+
+  Directory.current = cachedDir;
+  return map;
+}
+
+bool shouldSkip(List<String> filters, String path) {
+  return filters.any((filter) => path.contains(filter));
+}
+
+/// Return the relative path of each file under [subDir] in [package].
+Iterable<String> _listPackageDir(
+    String package, String subDir, BarbackOptions options) {
+  var packageDir = options.packageDirs[package];
+  if (packageDir == null) return const [];
+  var dir = new Directory(path.join(packageDir, subDir));
+  if (!dir.existsSync()) return const [];
+  return dir
+      .listSync(recursive: true, followLinks: options.followLinks)
+      .where((f) => f is File)
+      .where((f) => !shouldSkip(options.fileFilter, f.path))
+      .map((f) => path.relative(f.path, from: packageDir));
+}
+
+/// A simple provider that reads files directly from the pub cache.
+class _PackageProvider implements PackageProvider {
+  Map<String, String> packageDirs;
+  Iterable<String> get packages => packageDirs.keys;
+
+  _PackageProvider(this.packageDirs);
+
+  Future<Asset> getAsset(AssetId id) => new Future.value(new Asset.fromPath(
+      id, path.join(packageDirs[id.package], _toSystemPath(id.path))));
+}
+
+/// Convert asset paths to system paths (Assets always use the posix style).
+String _toSystemPath(String assetPath) {
+  if (path.Style.platform != path.Style.windows) return assetPath;
+  return path.joinAll(path.posix.split(assetPath));
+}
+
+/// Tell barback which transformers to use and which assets to process.
+void _initBarback(Barback barback, BarbackOptions options) {
+  var assets = [];
+  void addAssets(String package, String subDir) {
+    for (var filepath in _listPackageDir(package, subDir, options)) {
+      assets.add(new AssetId(package, filepath));
+    }
+  }
+
+  for (var package in options.packageDirs.keys) {
+    // Notify barback to process anything under 'lib' and 'asset'.
+    addAssets(package, 'lib');
+    addAssets(package, 'asset');
+
+    if (options.packagePhases.containsKey(package)) {
+      barback.updateTransformers(package, options.packagePhases[package]);
+    }
+  }
+  barback.updateTransformers(options.currentPackage, options.phases);
+
+  // In case of the current package, include also 'web'.
+  addAssets(options.currentPackage, 'web');
+  if (options.transformTests) addAssets(options.currentPackage, 'test');
+
+  // Add the sources after the transformers so all transformers are present
+  // when barback starts processing the assets.
+  barback.updateSources(assets);
+}
+
+/// Attach error listeners on [barback] so we can report errors.
+void _attachListeners(Barback barback, BarbackOptions options) {
+  // Listen for errors and results
+  barback.errors.listen((e) {
+    var trace = null;
+    if (e is Error) trace = e.stackTrace;
+    if (trace != null) {
+      print(Trace.format(trace));
+    }
+    print('error running barback: $e');
+    exit(1);
+  });
+
+  barback.results.listen((result) {
+    if (!result.succeeded) {
+      print("build failed with errors: ${result.errors}");
+      exit(1);
+    }
+  });
+
+  barback.log.listen((entry) {
+    if (options.machineFormat) {
+      print(_jsonFormatter(entry));
+    } else {
+      print(_consoleFormatter(entry));
+    }
+  });
+}
+
+/// Emits all outputs of [barback] and copies files that we didn't process (like
+/// dependent package's libraries).
+Future _emitAllFiles(Barback barback, BarbackOptions options) {
+  return barback.getAllAssets().then((assets) {
+    // Delete existing output folder before we generate anything
+    var dir = new Directory(options.outDir);
+    if (dir.existsSync()) dir.deleteSync(recursive: true);
+    return _emitPackagesDir(options)
+        .then((_) => _emitTransformedFiles(assets, options))
+        .then((_) => _addPackagesSymlinks(assets, options))
+        .then((_) => assets);
+  });
+}
+
+Future _emitTransformedFiles(AssetSet assets, BarbackOptions options) {
+  // Copy all the assets we transformed
+  var futures = [];
+  var currentPackage = options.currentPackage;
+  var transformTests = options.transformTests;
+  var outPackages = path.join(options.outDir, 'packages');
+
+  return Future.forEach(assets, (asset) {
+    var id = asset.id;
+    var dir = _firstDir(id.path);
+    if (dir == null) return null;
+
+    var filepath;
+    if (dir == 'lib') {
+      // Put lib files directly under the packages folder (e.g. 'lib/foo.dart'
+      // will be emitted at out/packages/package_name/foo.dart).
+      filepath = path.join(
+          outPackages, id.package, _toSystemPath(id.path.substring(4)));
+    } else if (id.package == currentPackage &&
+        (dir == 'web' || (transformTests && dir == 'test'))) {
+      filepath = path.join(options.outDir, _toSystemPath(id.path));
+    } else {
+      // TODO(sigmund): do something about other assets?
+      return null;
+    }
+
+    return _writeAsset(filepath, asset);
+  });
+}
+
+/// Adds a package symlink from each directory under `out/web/foo/` to
+/// `out/packages`.
+void _addPackagesSymlinks(AssetSet assets, BarbackOptions options) {
+  var outPackages = path.join(options.outDir, 'packages');
+  var currentPackage = options.currentPackage;
+  for (var asset in assets) {
+    var id = asset.id;
+    if (id.package != currentPackage) continue;
+    var firstDir = _firstDir(id.path);
+    if (firstDir == null) continue;
+
+    if (firstDir == 'web' || (options.transformTests && firstDir == 'test')) {
+      var dir = path.join(options.outDir, path.dirname(_toSystemPath(id.path)));
+      var linkPath = path.join(dir, 'packages');
+      var link = new Link(linkPath);
+      if (!link.existsSync()) {
+        var targetPath = Platform.operatingSystem == 'windows'
+            ? path.normalize(path.absolute(outPackages))
+            : path.normalize(path.relative(outPackages, from: dir));
+        link.createSync(targetPath);
+      }
+    }
+  }
+}
+
+/// Emits a 'packages' directory directly under `out/packages` with the contents
+/// of every file that was not transformed by barback.
+Future _emitPackagesDir(BarbackOptions options) {
+  var outPackages = path.join(options.outDir, 'packages');
+  _ensureDir(outPackages);
+
+  // Copy all the files we didn't process
+  var dirs = options.packageDirs;
+  return Future.forEach(dirs.keys, (package) {
+    return Future.forEach(_listPackageDir(package, 'lib', options), (relpath) {
+      var inpath = path.join(dirs[package], relpath);
+      var outpath = path.join(outPackages, package, relpath.substring(4));
+      return _copyFile(inpath, outpath);
+    });
+  });
+}
+
+/// Ensure [dirpath] exists.
+void _ensureDir(String dirpath) {
+  new Directory(dirpath).createSync(recursive: true);
+}
+
+/// Returns the first directory name on a url-style path, or null if there are
+/// no slashes.
+String _firstDir(String url) {
+  var firstSlash = url.indexOf('/');
+  if (firstSlash == -1) return null;
+  return url.substring(0, firstSlash);
+}
+
+/// Copy a file from [inpath] to [outpath].
+Future _copyFile(String inpath, String outpath) {
+  _ensureDir(path.dirname(outpath));
+  return new File(inpath).openRead().pipe(new File(outpath).openWrite());
+}
+
+/// Write contents of an [asset] into a file at [filepath].
+Future _writeAsset(String filepath, Asset asset) {
+  _ensureDir(path.dirname(filepath));
+  return asset.read().pipe(new File(filepath).openWrite());
+}
+
+String _kindFromEntry(LogEntry entry) {
+  var level = entry.level;
+  return level == LogLevel.ERROR
+      ? 'error'
+      : (level == LogLevel.WARNING ? 'warning' : 'info');
+}
+
+/// Formatter that generates messages using a format that can be parsed
+/// by tools, such as the Dart Editor, for reporting error messages.
+String _jsonFormatter(LogEntry entry) {
+  var kind = _kindFromEntry(entry);
+  var span = entry.span;
+  return JSON.encode((span == null)
+      ? [{'method': kind, 'params': {'message': entry.message}}]
+      : [
+    {
+      'method': kind,
+      'params': {
+        'file': span.sourceUrl.toString(),
+        'message': entry.message,
+        'line': span.start.line + 1,
+        'charStart': span.start.offset,
+        'charEnd': span.end.offset,
+      }
+    }
+  ]);
+}
+
+/// Formatter that generates messages that are easy to read on the console (used
+/// by default).
+String _consoleFormatter(LogEntry entry) {
+  var kind = _kindFromEntry(entry);
+  var useColors = stdioType(stdout) == StdioType.TERMINAL;
+  var levelColor = (kind == 'error') ? _RED_COLOR : _MAGENTA_COLOR;
+  var output = new StringBuffer();
+  if (useColors) output.write(levelColor);
+  output
+    ..write(kind)
+    ..write(' ');
+  if (useColors) output.write(_NO_COLOR);
+  if (entry.span == null) {
+    output.write(entry.message);
+  } else {
+    output.write(entry.span.message(entry.message,
+        color: useColors ? levelColor : null));
+  }
+  return output.toString();
+}
+
+const String _RED_COLOR = '\u001b[31m';
+const String _MAGENTA_COLOR = '\u001b[35m';
+const String _NO_COLOR = '\u001b[0m';
diff --git a/polymer/lib/src/build/utils.dart b/polymer/lib/src/build/utils.dart
new file mode 100644
index 0000000..b031373
--- /dev/null
+++ b/polymer/lib/src/build/utils.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2013, 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 polymer.src.utils;
+
+/// Converts a string name with hyphens into an identifier, by removing hyphens
+/// and capitalizing the following letter. Optionally [startUppercase] to
+/// captialize the first letter.
+String toCamelCase(String hyphenedName, {bool startUppercase: false}) {
+  var segments = hyphenedName.split('-');
+  int start = startUppercase ? 0 : 1;
+  for (int i = start; i < segments.length; i++) {
+    var segment = segments[i];
+    if (segment.length > 0) {
+      // Character between 'a'..'z' mapped to 'A'..'Z'
+      segments[i] = '${segment[0].toUpperCase()}${segment.substring(1)}';
+    }
+  }
+  return segments.join('');
+}
+
+/// Reverse of [toCamelCase].
+String toHyphenedName(String word) {
+  var sb = new StringBuffer();
+  for (int i = 0; i < word.length; i++) {
+    var lower = word[i].toLowerCase();
+    if (word[i] != lower && i > 0) sb.write('-');
+    sb.write(lower);
+  }
+  return sb.toString();
+}
diff --git a/polymer/lib/src/declaration.dart b/polymer/lib/src/declaration.dart
new file mode 100644
index 0000000..b9cbffc
--- /dev/null
+++ b/polymer/lib/src/declaration.dart
@@ -0,0 +1,614 @@
+// Copyright (c) 2013, 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.
+
+part of polymer;
+
+/// *Warning* this class is experimental and subject to change.
+///
+/// The data associated with a polymer-element declaration, if it is backed
+/// by a Dart class instead of a JavaScript prototype.
+class PolymerDeclaration {
+  /// The one syntax to rule them all.
+  static final BindingDelegate _polymerSyntax = new PolymerExpressions();
+
+  /// The polymer-element for this declaration.
+  final HtmlElement element;
+
+  /// The Dart type corresponding to this custom element declaration.
+  final Type type;
+
+  /// If we extend another custom element, this points to the super declaration.
+  final PolymerDeclaration superDeclaration;
+
+  /// The name of the custom element.
+  final String name;
+
+  /// Map of publish properties. Can be a field or a property getter, but if
+  /// this map contains a getter, is because it also has a corresponding setter.
+  ///
+  /// Note: technically these are always single properties, so we could use a
+  /// Symbol instead of a PropertyPath. However there are lookups between this
+  /// map and [_observe] so it is easier to just track paths.
+  Map<PropertyPath, smoke.Declaration> _publish;
+
+  /// The names of published properties for this polymer-element.
+  Iterable<String> get publishedProperties =>
+      _publish != null ? _publish.keys.map((p) => '$p') : const [];
+
+  /// Same as [_publish] but with lower case names.
+  Map<String, smoke.Declaration> _publishLC;
+
+  Map<PropertyPath, List<Symbol>> _observe;
+
+  /// Name and expression for each computed property.
+  Map<Symbol, String> _computed = {};
+
+  Map<String, Object> _instanceAttributes;
+
+  /// A set of properties that should be automatically reflected to attributes.
+  /// Typically this is used for CSS styling. If none, this variable will be
+  /// left as null.
+  Set<String> _reflect;
+
+  List<Element> _sheets;
+  List<Element> get sheets => _sheets;
+
+  List<Element> _styles;
+  List<Element> get styles => _styles;
+
+  // The default syntax for polymer-elements.
+  PolymerExpressions syntax = _polymerSyntax;
+
+  DocumentFragment get templateContent {
+    final template = fetchTemplate();
+    return template != null ? templateBind(template).content : null;
+  }
+
+  /// Maps event names and their associated method in the element class.
+  final Map<String, String> _eventDelegates = {};
+
+  /// Expected events per element node.
+  // TODO(sigmund): investigate whether we need more than 1 set of local events
+  // per element (why does the js implementation stores 1 per template node?)
+  Expando<Set<String>> _templateDelegates;
+
+  String get extendee =>
+      superDeclaration != null ? superDeclaration.name : null;
+
+  /// The root URI for assets.
+  Uri _rootUri;
+
+  /// List of properties to ignore for observation.
+  static Set<Symbol> _OBSERVATION_BLACKLIST =
+      new HashSet.from(const [#attribute]);
+
+  static bool _canObserveProperty(Symbol property) =>
+      !_OBSERVATION_BLACKLIST.contains(property);
+
+  /// This list contains some property names that people commonly want to use,
+  /// but won't work because of Chrome/Safari bugs. It isn't an exhaustive
+  /// list. In particular it doesn't contain any property names found on
+  /// subtypes of HTMLElement (e.g. name, value). Rather it attempts to catch
+  /// some common cases.
+  ///
+  /// Dart Note: `class` is left out since its an invalid symbol in dart. This
+  /// means that nobody could make a property by this name anyways though.
+  /// Dart Note: We have added `classes` to this list, which is the dart:html
+  /// equivalent of `classList` but more likely to have conflicts.
+  static Set<Symbol> _PROPERTY_NAME_BLACKLIST = new HashSet.from([
+    const Symbol('children'),
+    const Symbol('id'),
+    const Symbol('hidden'),
+    const Symbol('style'),
+    const Symbol('title'),
+    const Symbol('classes')
+  ]);
+
+  bool _checkPropertyBlacklist(Symbol name) {
+    if (_PROPERTY_NAME_BLACKLIST.contains(name)) {
+      print('Cannot define property "$name" for element "${this.name}" '
+          'because it has the same name as an HTMLElement property, and not '
+          'all browsers support overriding that. Consider giving it a '
+          'different name. ');
+      return true;
+    }
+    return false;
+  }
+
+  // Dart note: since polymer-element is handled in JS now, we have a simplified
+  // flow for registering. We don't need to wait for the supertype or the code
+  // to be noticed.
+  PolymerDeclaration(this.element, this.name, this.type, this.superDeclaration);
+
+  void register() {
+    // more declarative features
+    desugar();
+    // register our custom element
+    registerType(name);
+
+    // NOTE: skip in Dart because we don't have mutable global scope.
+    // reference constructor in a global named by 'constructor' attribute
+    // publishConstructor();
+  }
+
+  /// Implement various declarative features.
+  // Dart note: this merges "buildPrototype" "desugarBeforeChaining" and
+  // "desugarAfterChaining", because we don't have prototypes.
+  void desugar() {
+
+    // back reference declaration element
+    _declarations[name] = this;
+
+    // transcribe `attributes` declarations onto own prototype's `publish`
+    publishAttributes(superDeclaration);
+
+    publishProperties();
+
+    inferObservers();
+
+    // desugar compound observer syntax, e.g. @ObserveProperty('a b c')
+    explodeObservers();
+
+    createPropertyAccessors();
+    // install mdv delegate on template
+    installBindingDelegate(fetchTemplate());
+    // install external stylesheets as if they are inline
+    installSheets();
+    // adjust any paths in dom from imports
+    resolveElementPaths(element);
+    // compile list of attributes to copy to instances
+    accumulateInstanceAttributes();
+    // parse on-* delegates declared on `this` element
+    parseHostEvents();
+    // install a helper method this.resolvePath to aid in
+    // setting resource urls. e.g.
+    // this.$.image.src = this.resolvePath('images/foo.png')
+    initResolvePath();
+    // under ShadowDOMPolyfill, transforms to approximate missing CSS features
+    _shimShadowDomStyling(templateContent, name, extendee);
+
+    // TODO(jmesserly): this feels unnatrual in Dart. Since we have convenient
+    // lazy static initialization, can we get by without it?
+    if (smoke.hasStaticMethod(type, #registerCallback)) {
+      smoke.invoke(type, #registerCallback, [this]);
+    }
+  }
+
+  void registerType(String name) {
+    var baseTag;
+    var decl = this;
+    while (decl != null) {
+      baseTag = decl.element.attributes['extends'];
+      decl = decl.superDeclaration;
+    }
+    document.registerElement(name, type, extendsTag: baseTag);
+  }
+
+  // from declaration/mdv.js
+  Element fetchTemplate() => element.querySelector('template');
+
+  void installBindingDelegate(Element template) {
+    if (template != null) {
+      templateBind(template).bindingDelegate = this.syntax;
+    }
+  }
+
+  // from declaration/path.js
+  void resolveElementPaths(Node node) => PolymerJs.resolveElementPaths(node);
+
+  // Dart note: renamed from "addResolvePathApi".
+  void initResolvePath() {
+    // let assetpath attribute modify the resolve path
+    var assetPath = element.attributes['assetpath'];
+    if (assetPath == null) assetPath = '';
+    var base = Uri.parse(element.ownerDocument.baseUri);
+    _rootUri = base.resolve(assetPath);
+  }
+
+  String resolvePath(String urlPath, [baseUrlOrString]) {
+    Uri base;
+    if (baseUrlOrString == null) {
+      // Dart note: this enforces the same invariant as JS, where you need to
+      // call addResolvePathApi first.
+      if (_rootUri == null) {
+        throw new StateError('call initResolvePath before calling resolvePath');
+      }
+      base = _rootUri;
+    } else if (baseUrlOrString is Uri) {
+      base = baseUrlOrString;
+    } else {
+      base = Uri.parse(baseUrlOrString);
+    }
+    return base.resolve(urlPath).toString();
+  }
+
+  void publishAttributes(PolymerDeclaration superDecl) {
+    // get properties to publish
+    if (superDecl != null) {
+      // Dart note: even though we walk the type hierarchy in
+      // _getPublishedProperties, this will additionally include any names
+      // published via the `attributes` attribute.
+      if (superDecl._publish != null) {
+        _publish = new Map.from(superDecl._publish);
+      }
+      if (superDecl._reflect != null) {
+        _reflect = new Set.from(superDecl._reflect);
+      }
+    }
+
+    _getPublishedProperties(type);
+
+    // merge names from 'attributes' attribute into the '_publish' object
+    var attrs = element.attributes['attributes'];
+    if (attrs != null) {
+      // names='a b c' or names='a,b,c'
+      // record each name for publishing
+      for (var attr in attrs.split(_ATTRIBUTES_REGEX)) {
+        // remove excess ws
+        attr = attr.trim();
+
+        // if the user hasn't specified a value, we want to use the
+        // default, unless a superclass has already chosen one
+        if (attr == '') continue;
+
+        var decl, path;
+        var property = smoke.nameToSymbol(attr);
+        if (property != null) {
+          path = new PropertyPath([property]);
+          if (_publish != null && _publish.containsKey(path)) {
+            continue;
+          }
+          decl = smoke.getDeclaration(type, property);
+        }
+
+        if (property == null || decl == null || decl.isMethod || decl.isFinal) {
+          window.console.warn('property for attribute $attr of polymer-element '
+              'name=$name not found.');
+          continue;
+        }
+        if (_publish == null) _publish = {};
+        _publish[path] = decl;
+      }
+    }
+
+    // NOTE: the following is not possible in Dart; fields must be declared.
+    // install 'attributes' as properties on the prototype,
+    // but don't override
+  }
+
+  void _getPublishedProperties(Type type) {
+    var options = const smoke.QueryOptions(
+        includeInherited: true,
+        includeUpTo: HtmlElement,
+        withAnnotations: const [PublishedProperty]);
+    for (var decl in smoke.query(type, options)) {
+      if (decl.isFinal) continue;
+      if (_checkPropertyBlacklist(decl.name)) continue;
+      if (_publish == null) _publish = {};
+      _publish[new PropertyPath([decl.name])] = decl;
+
+      // Should we reflect the property value to the attribute automatically?
+      if (decl.annotations
+          .where((a) => a is PublishedProperty)
+          .any((a) => a.reflect)) {
+        if (_reflect == null) _reflect = new Set();
+        _reflect.add(smoke.symbolToName(decl.name));
+      }
+    }
+  }
+
+  void accumulateInstanceAttributes() {
+    // inherit instance attributes
+    _instanceAttributes = new Map<String, Object>();
+    if (superDeclaration != null) {
+      _instanceAttributes.addAll(superDeclaration._instanceAttributes);
+    }
+
+    // merge attributes from element
+    element.attributes.forEach((name, value) {
+      if (isInstanceAttribute(name)) {
+        _instanceAttributes[name] = value;
+      }
+    });
+  }
+
+  static bool isInstanceAttribute(name) {
+    // do not clone these attributes onto instances
+    final blackList = const {
+      'name': 1,
+      'extends': 1,
+      'constructor': 1,
+      'noscript': 1,
+      'assetpath': 1,
+      'cache-csstext': 1,
+      // add ATTRIBUTES_ATTRIBUTE to the blacklist
+      'attributes': 1,
+    };
+
+    return !blackList.containsKey(name) && !name.startsWith('on-');
+  }
+
+  /// Extracts events from the element tag attributes.
+  void parseHostEvents() {
+    addAttributeDelegates(_eventDelegates);
+  }
+
+  void addAttributeDelegates(Map<String, String> delegates) {
+    element.attributes.forEach((name, value) {
+      if (_hasEventPrefix(name)) {
+        var start = value.indexOf('{{');
+        var end = value.lastIndexOf('}}');
+        if (start >= 0 && end >= 0) {
+          delegates[_removeEventPrefix(name)] =
+              value.substring(start + 2, end).trim();
+        }
+      }
+    });
+  }
+
+  String urlToPath(String url) {
+    if (url == null) return '';
+    return (url.split('/')
+      ..removeLast()
+      ..add('')).join('/');
+  }
+
+  // Dart note: loadStyles, convertSheetsToStyles, copySheetAttribute and
+  // findLoadableStyles are not ported because they're handled by Polymer JS
+  // before we get into [register].
+
+  /// Install external stylesheets loaded in <element> elements into the
+  /// element's template.
+  void installSheets() {
+    cacheSheets();
+    cacheStyles();
+    installLocalSheets();
+    installGlobalStyles();
+  }
+
+  void cacheSheets() {
+    _sheets = findNodes(_SHEET_SELECTOR);
+    for (var s in sheets) s.remove();
+  }
+
+  void cacheStyles() {
+    _styles = findNodes('$_STYLE_SELECTOR[$_SCOPE_ATTR]');
+    for (var s in styles) s.remove();
+  }
+
+  /// Takes external stylesheets loaded in an `<element>` element and moves
+  /// their content into a style element inside the `<element>`'s template.
+  /// The sheet is then removed from the `<element>`. This is done only so
+  /// that if the element is loaded in the main document, the sheet does
+  /// not become active.
+  /// Note, ignores sheets with the attribute 'polymer-scope'.
+  void installLocalSheets() {
+    var sheets =
+        this.sheets.where((s) => !s.attributes.containsKey(_SCOPE_ATTR));
+    var content = templateContent;
+    if (content != null) {
+      var cssText = new StringBuffer();
+      for (var sheet in sheets) {
+        cssText
+          ..write(_cssTextFromSheet(sheet))
+          ..write('\n');
+      }
+      if (cssText.length > 0) {
+        var style = element.ownerDocument.createElement('style')
+          ..text = '$cssText';
+
+        content.insertBefore(style, content.firstChild);
+      }
+    }
+  }
+
+  List<Element> findNodes(String selector, [bool matcher(Element e)]) {
+    var nodes = element.querySelectorAll(selector).toList();
+    var content = templateContent;
+    if (content != null) {
+      nodes = nodes..addAll(content.querySelectorAll(selector));
+    }
+    if (matcher != null) return nodes.where(matcher).toList();
+    return nodes;
+  }
+
+  /// Promotes external stylesheets and style elements with the attribute
+  /// polymer-scope='global' into global scope.
+  /// This is particularly useful for defining @keyframe rules which
+  /// currently do not function in scoped or shadow style elements.
+  /// (See wkb.ug/72462)
+  // TODO(sorvell): remove when wkb.ug/72462 is addressed.
+  void installGlobalStyles() {
+    var style = styleForScope(_STYLE_GLOBAL_SCOPE);
+    Polymer.applyStyleToScope(style, document.head);
+  }
+
+  String cssTextForScope(String scopeDescriptor) {
+    var cssText = new StringBuffer();
+    // handle stylesheets
+    var selector = '[$_SCOPE_ATTR=$scopeDescriptor]';
+    matcher(s) => s.matches(selector);
+
+    for (var sheet in sheets.where(matcher)) {
+      cssText
+        ..write(_cssTextFromSheet(sheet))
+        ..write('\n\n');
+    }
+    // handle cached style elements
+    for (var style in styles.where(matcher)) {
+      cssText
+        ..write(style.text)
+        ..write('\n\n');
+    }
+    return cssText.toString();
+  }
+
+  StyleElement styleForScope(String scopeDescriptor) {
+    var cssText = cssTextForScope(scopeDescriptor);
+    return cssTextToScopeStyle(cssText, scopeDescriptor);
+  }
+
+  StyleElement cssTextToScopeStyle(String cssText, String scopeDescriptor) {
+    if (cssText == '') return null;
+
+    return new StyleElement()
+      ..text = cssText
+      ..attributes[_STYLE_SCOPE_ATTRIBUTE] = '$name-$scopeDescriptor';
+  }
+
+  /// Fetch a list of all *Changed methods so we can observe the associated
+  /// properties.
+  void inferObservers() {
+    for (var decl in smoke.query(type, _changedMethodQueryOptions)) {
+      // TODO(jmesserly): now that we have a better system, should we
+      // deprecate *Changed methods?
+      if (_observe == null) _observe = new HashMap();
+      var name = smoke.symbolToName(decl.name);
+      name = name.substring(0, name.length - 7);
+      if (!_canObserveProperty(decl.name)) continue;
+      _observe[new PropertyPath(name)] = [decl.name];
+    }
+  }
+
+  /// Fetch a list of all methods annotated with [ObserveProperty] so we can
+  /// observe the associated properties.
+  void explodeObservers() {
+    var options = const smoke.QueryOptions(
+        includeFields: false,
+        includeProperties: false,
+        includeMethods: true,
+        includeInherited: true,
+        includeUpTo: HtmlElement,
+        withAnnotations: const [ObserveProperty]);
+    for (var decl in smoke.query(type, options)) {
+      for (var meta in decl.annotations) {
+        if (meta is! ObserveProperty) continue;
+        if (_observe == null) _observe = new HashMap();
+        for (String name in meta.names) {
+          _observe.putIfAbsent(new PropertyPath(name), () => []).add(decl.name);
+        }
+      }
+    }
+  }
+
+  void publishProperties() {
+    // Dart note: _publish was already populated by publishAttributes
+    if (_publish != null) _publishLC = _lowerCaseMap(_publish);
+  }
+
+  Map<String, dynamic> _lowerCaseMap(Map<PropertyPath, dynamic> properties) {
+    final map = new Map<String, dynamic>();
+    properties.forEach((PropertyPath path, value) {
+      map['$path'.toLowerCase()] = value;
+    });
+    return map;
+  }
+
+  void createPropertyAccessors() {
+    // Dart note: since we don't have a prototype in Dart, most of the work of
+    // createPolymerAccessors is done lazily on the first access of properties.
+    // Here we just extract the information from annotations and store it as
+    // properties on the declaration.
+
+    // Dart Note: The js side makes computed properties read only, and does
+    // special logic right here for them. For us they are automatically read
+    // only unless you define a setter for them, so we left that out.
+    var options = const smoke.QueryOptions(
+        includeInherited: true,
+        includeUpTo: HtmlElement,
+        withAnnotations: const [ComputedProperty]);
+    var existing = {};
+    for (var decl in smoke.query(type, options)) {
+      var name = decl.name;
+      if (_checkPropertyBlacklist(name)) continue;
+      var meta = decl.annotations.firstWhere((e) => e is ComputedProperty);
+      var prev = existing[name];
+      // The definition of a child class takes priority.
+      if (prev == null || smoke.isSubclassOf(decl.type, prev.type)) {
+        _computed[name] = meta.expression;
+        existing[name] = decl;
+      }
+    }
+  }
+}
+
+/// maps tag names to prototypes
+final Map _typesByName = new Map<String, Type>();
+
+Type _getRegisteredType(String name) => _typesByName[name];
+
+/// Dart Note: instanceOfType not implemented for dart, its not needed.
+
+/// track document.register'ed tag names and their declarations
+final Map _declarations = new Map<String, PolymerDeclaration>();
+
+bool _isRegistered(String name) => _declarations.containsKey(name);
+PolymerDeclaration _getDeclaration(String name) => _declarations[name];
+
+/// Using Polymer's web_components/src/ShadowCSS.js passing the style tag's
+/// content.
+void _shimShadowDomStyling(
+    DocumentFragment template, String name, String extendee) {
+  if (_ShadowCss == null || !_hasShadowDomPolyfill) return;
+
+  _ShadowCss.callMethod('shimStyling', [template, name, extendee]);
+}
+
+final bool _hasShadowDomPolyfill = js.context.hasProperty('ShadowDOMPolyfill');
+final JsObject _ShadowCss =
+    _WebComponents != null ? _WebComponents['ShadowCSS'] : null;
+
+const _STYLE_SELECTOR = 'style';
+const _SHEET_SELECTOR = 'link[rel=stylesheet]';
+const _STYLE_GLOBAL_SCOPE = 'global';
+const _SCOPE_ATTR = 'polymer-scope';
+const _STYLE_SCOPE_ATTRIBUTE = 'element';
+const _STYLE_CONTROLLER_SCOPE = 'controller';
+
+String _cssTextFromSheet(LinkElement sheet) {
+  if (sheet == null) return '';
+
+  // In deploy mode we should never do a sync XHR; link rel=stylesheet will
+  // be inlined into a <style> tag by ImportInliner.
+  if (_deployMode) return '';
+
+  // TODO(jmesserly): sometimes the href property is wrong after deployment.
+  var href = sheet.href;
+  if (href == '') href = sheet.attributes["href"];
+
+  // TODO(jmesserly): it seems like polymer-js is always polyfilling
+  // HTMLImports, because their code depends on "__resource" to work, so I
+  // don't see how it can work with native HTML Imports. We use a sync-XHR
+  // under the assumption that the file is likely to have been already
+  // downloaded and cached by HTML Imports.
+  try {
+    return (new HttpRequest()
+      ..open('GET', href, async: false)
+      ..send()).responseText;
+  } on DomException catch (e, t) {
+    _sheetLog.fine('failed to XHR stylesheet text href="$href" error: '
+        '$e, trace: $t');
+    return '';
+  }
+}
+
+final Logger _sheetLog = new Logger('polymer.stylesheet');
+
+final smoke.QueryOptions _changedMethodQueryOptions = new smoke.QueryOptions(
+    includeFields: false,
+    includeProperties: false,
+    includeMethods: true,
+    includeInherited: true,
+    includeUpTo: HtmlElement,
+    matches: _isObserverMethod);
+
+bool _isObserverMethod(Symbol symbol) {
+  String name = smoke.symbolToName(symbol);
+  if (name == null) return false;
+  return name.endsWith('Changed') && name != 'attributeChanged';
+}
+
+final _ATTRIBUTES_REGEX = new RegExp(r'\s|,');
+
+final JsObject _WebComponents = js.context['WebComponents'];
diff --git a/polymer/lib/src/events.dart b/polymer/lib/src/events.dart
new file mode 100644
index 0000000..33798ea
--- /dev/null
+++ b/polymer/lib/src/events.dart
@@ -0,0 +1,173 @@
+// Copyright (c) 2013, 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.
+
+/// Code from declaration/events.js
+part of polymer;
+
+/// An extension of [polymer_expressions.PolymerExpressions] that adds support
+/// for binding events using `on-eventName` using [PolymerEventBindings].
+// TODO(jmesserly): the JS layering is a bit odd, with polymer-dev implementing
+// events and polymer-expressions implementing everything else. I don't think
+// this separation is right in the long term, so we're using the same class name
+// until we can sort it out.
+class PolymerExpressions extends BindingDelegate with PolymerEventBindings {
+
+  /// A wrapper around polymer_expressions used to implement forwarding.
+  /// Ideally we would inherit from it, but mixins can't be applied to a type
+  /// that forwards to a superclass with a constructor that has optional or
+  /// named arguments.
+  final polymer_expressions.PolymerExpressions _delegate;
+
+  Map<String, Object> get globals => _delegate.globals;
+
+  PolymerExpressions({Map<String, Object> globals})
+      : _delegate = new polymer_expressions.PolymerExpressions(
+          globals: globals);
+
+  prepareBinding(String path, name, node) {
+    if (_hasEventPrefix(name)) {
+      return prepareEventBinding(path, name, node);
+    }
+    return _delegate.prepareBinding(path, name, node);
+  }
+
+  prepareInstanceModel(Element template) =>
+      _delegate.prepareInstanceModel(template);
+
+  prepareInstancePositionChanged(Element template) =>
+      _delegate.prepareInstancePositionChanged(template);
+
+  static final getExpression =
+      polymer_expressions.PolymerExpressions.getExpression;
+  static final getBinding = polymer_expressions.PolymerExpressions.getBinding;
+}
+
+/// A mixin for a [BindingDelegate] to add Polymer event support.
+/// This is included in [PolymerExpressions].
+abstract class PolymerEventBindings {
+  /// Finds the event controller for this node.
+  Element findController(Node node) {
+    while (node.parentNode != null) {
+      if (node is Polymer && node.eventController != null) {
+        return node.eventController;
+      } else if (node is Element) {
+        // If it is a normal element, js polymer element, or dart wrapper to a
+        // js polymer element, then we try js interop.
+        var eventController =
+            new JsObject.fromBrowserObject(node)['eventController'];
+        if (eventController != null) return eventController;
+      }
+      node = node.parentNode;
+    }
+    return node is ShadowRoot ? node.host : null;
+  }
+
+  EventListener getEventHandler(controller, target, String method) => (e) {
+    if (controller == null || controller is! Polymer) {
+      controller = findController(target);
+    }
+
+    if (controller is Polymer) {
+      var detail = null;
+      if (e is CustomEvent) {
+        detail = e.detail;
+        // TODO(sigmund): this shouldn't be necessary. See issue 19315.
+        if (detail == null) {
+          detail = new JsObject.fromBrowserObject(e)['detail'];
+        }
+      }
+      var args = [e, detail, e.currentTarget];
+      controller.dispatchMethod(controller, method, args);
+    } else {
+      throw new StateError('controller $controller is not a '
+          'Dart polymer-element.');
+    }
+  };
+
+  prepareEventBinding(String path, String name, Node node) {
+    if (!_hasEventPrefix(name)) return null;
+
+    var eventType = _removeEventPrefix(name);
+    var translated = _eventTranslations[eventType];
+    eventType = translated != null ? translated : eventType;
+
+    return (model, node, oneTime) {
+      var eventHandler =
+          Zone.current.bindUnaryCallback(getEventHandler(null, node, path));
+      // TODO(jakemac): Remove this indirection if/when JsFunction gets a
+      // simpler constructor that doesn't pass this, http://dartbug.com/20545.
+      var handler = new JsFunction.withThis((_, e) => eventHandler(e));
+      PolymerGesturesJs.addEventListener(node, eventType, handler);
+
+      if (oneTime) return null;
+      return new _EventBindable(path, node, eventType, handler);
+    };
+  }
+}
+
+class _EventBindable extends Bindable {
+  final String _path;
+  final Node _node;
+  final String _eventType;
+  final JsFunction _handler;
+
+  _EventBindable(this._path, this._node, this._eventType, this._handler);
+
+  // TODO(rafaelw): This is really pointless work. Aside from the cost
+  // of these allocations, NodeBind is going to setAttribute back to its
+  // current value. Fixing this would mean changing the TemplateBinding
+  // binding delegate API.
+  get value => '{{ $_path }}';
+
+  open(callback) => value;
+
+  void close() {
+    PolymerGesturesJs.removeEventListener(_node, _eventType, _handler);
+  }
+}
+
+/// Attribute prefix used for declarative event handlers.
+const _EVENT_PREFIX = 'on-';
+
+/// Whether an attribute declares an event.
+bool _hasEventPrefix(String attr) => attr.startsWith(_EVENT_PREFIX);
+
+String _removeEventPrefix(String name) => name.substring(_EVENT_PREFIX.length);
+
+// Dart note: polymer.js calls this mixedCaseEventTypes. But we have additional
+// things that need translation due to renames.
+final _eventTranslations = const {
+  'domfocusout': 'DOMFocusOut',
+  'domfocusin': 'DOMFocusIn',
+  'dommousescroll': 'DOMMouseScroll',
+
+  // Dart note: handle Dart-specific event names.
+  'animationend': 'webkitAnimationEnd',
+  'animationiteration': 'webkitAnimationIteration',
+  'animationstart': 'webkitAnimationStart',
+  'doubleclick': 'dblclick',
+  'fullscreenchange': 'webkitfullscreenchange',
+  'fullscreenerror': 'webkitfullscreenerror',
+  'keyadded': 'webkitkeyadded',
+  'keyerror': 'webkitkeyerror',
+  'keymessage': 'webkitkeymessage',
+  'needkey': 'webkitneedkey',
+  'speechchange': 'webkitSpeechChange',
+};
+
+final _reverseEventTranslations = () {
+  final map = new Map<String, String>();
+  _eventTranslations.forEach((onName, eventType) {
+    map[eventType] = onName;
+  });
+  return map;
+}();
+
+// Dart note: we need this function because we have additional renames JS does
+// not have. The JS renames are simply case differences, whereas we have ones
+// like doubleclick -> dblclick and stripping the webkit prefix.
+String _eventNameFromType(String eventType) {
+  final result = _reverseEventTranslations[eventType];
+  return result != null ? result : eventType;
+}
diff --git a/polymer/lib/src/initializers.dart b/polymer/lib/src/initializers.dart
new file mode 100644
index 0000000..cec4f8e
--- /dev/null
+++ b/polymer/lib/src/initializers.dart
@@ -0,0 +1,27 @@
+// Copyright (c) 2013, 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.
+part of polymer;
+
+/// Automatically registers a polymer element.
+class CustomTag implements Initializer<Type> {
+  final String tagName;
+  const CustomTag(this.tagName);
+
+  @override
+  initialize(Type t) => Polymer.register(tagName, t);
+}
+
+/// Calls a zero argument [Function] after [Polymer.onReady] completes.
+typedef dynamic _ZeroArg();
+class _WhenPolymerReady implements Initializer<_ZeroArg> {
+  const _WhenPolymerReady();
+
+  @override
+  void initialize(_ZeroArg f) {
+    Polymer.onReady.then((_) => f());
+  }
+}
+
+const whenPolymerReady = const _WhenPolymerReady();
+
diff --git a/polymer/lib/src/instance.dart b/polymer/lib/src/instance.dart
new file mode 100644
index 0000000..ca46d8d
--- /dev/null
+++ b/polymer/lib/src/instance.dart
@@ -0,0 +1,1466 @@
+// Copyright (c) 2013, 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.
+
+part of polymer;
+
+/// Use this annotation to publish a property as an attribute.
+///
+/// You can also use [PublishedProperty] to provide additional information,
+/// such as automatically syncing the property back to the attribute.
+///
+/// For example:
+///
+///     class MyPlaybackElement extends PolymerElement {
+///       // This will be available as an HTML attribute, for example:
+///       //
+///       //     <my-playback volume="11">
+///       //
+///       // It will support initialization and data-binding via <template>:
+///       //
+///       //     <template>
+///       //       <my-playback volume="{{x}}">
+///       //     </template>
+///       //
+///       // If the template is instantiated or given a model, `x` will be
+///       // used for this field and updated whenever `volume` changes.
+///       @published
+///       double get volume => readValue(#volume);
+///       set volume(double newValue) => writeValue(#volume, newValue);
+///
+///       // This will be available as an HTML attribute, like above, but it
+///       // will also serialize values set to the property to the attribute.
+///       // In other words, attributes['volume2'] will contain a serialized
+///       // version of this field.
+///       @PublishedProperty(reflect: true)
+///       double get volume2 => readValue(#volume2);
+///       set volume2(double newValue) => writeValue(#volume2, newValue);
+///     }
+///
+/// **Important note**: the pattern using `readValue` and `writeValue`
+/// guarantees that reading the property will give you the latest value at any
+/// given time, even if change notifications have not been propagated.
+///
+/// We still support using @published on a field, but this will not
+/// provide the same guarantees, so this is discouraged. For example:
+///
+///       // Avoid this if possible. This will be available as an HTML
+///       // attribute too, but you might need to delay reading volume3
+///       // asynchronously to guarantee that you read the latest value
+///       // set through bindings.
+///       @published double volume3;
+const published = const PublishedProperty();
+
+/// An annotation used to publish a field as an attribute. See [published].
+class PublishedProperty extends ObservableProperty {
+  /// Whether the property value should be reflected back to the HTML attribute.
+  final bool reflect;
+
+  const PublishedProperty({this.reflect: false});
+}
+
+/// Use this type to observe a property and have the method be called when it
+/// changes. For example:
+///
+///     @ObserveProperty('foo.bar baz qux')
+///     validate() {
+///       // use this.foo.bar, this.baz, and this.qux in validation
+///       ...
+///     }
+///
+/// Note that you can observe a property path, and more than a single property
+/// can be specified in a space-delimited list or as a constant List.
+class ObserveProperty {
+  final _names;
+
+  List<String> get names {
+    var n = _names;
+    // TODO(jmesserly): the bogus '$n' is to workaround a dart2js bug, otherwise
+    // it generates an incorrect call site.
+    if (n is String) return '$n'.split(' ');
+    if (n is! Iterable) {
+      throw new UnsupportedError('ObserveProperty takes either an Iterable of '
+          'names, or a space separated String, instead of `$n`.');
+    }
+    return n;
+  }
+
+  const ObserveProperty(this._names);
+}
+
+/// Use this to create computed properties that are updated automatically. The
+/// annotation includes a polymer expression that describes how this property
+/// value can be expressed in terms of the values of other properties. For
+/// example:
+///
+///     class MyPlaybackElement extends PolymerElement {
+///       @observable int x;
+///
+///       // Reading xTimes2 will return x * 2.
+///       @ComputedProperty('x * 2')
+///       int get xTimes2 => readValue(#xTimes2);
+///
+/// If the polymer expression is assignable, you can also define a setter for
+/// it. For example:
+///
+///       // Reading c will return a.b, writing c will update a.b.
+///       @ComputedProperty('a.b')
+///       get c => readValue(#c);
+///       set c(newValue) => writeValue(#c, newValue);
+///
+/// The expression can do anything that is allowed in a polymer expresssion,
+/// even making calls to methods in your element. However, dependencies that are
+/// only used within those methods and that are not visible in the polymer
+/// expression, will not be observed. For example:
+///
+///       // Because `x` only appears inside method `m`, we will not notice
+///       // that `d` has changed if `x` is modified. However, `d` will be
+///       // updated whenever `c` changes.
+///       @ComputedProperty('m(c)')
+///       get d => readValue(#d);
+///
+///       m(c) => c + x;
+class ComputedProperty {
+  /// A polymer expression, evaluated in the context of the custom element where
+  /// this annotation is used.
+  final String expression;
+
+  const ComputedProperty(this.expression);
+}
+
+/// Base class for PolymerElements deriving from HtmlElement.
+///
+/// See [Polymer].
+class PolymerElement extends HtmlElement with Polymer, Observable {
+  PolymerElement.created() : super.created() {
+    polymerCreated();
+  }
+}
+
+/// The mixin class for Polymer elements. It provides convenience features on
+/// top of the custom elements web standard.
+///
+/// If this class is used as a mixin,
+/// you must call `polymerCreated()` from the body of your constructor.
+abstract class Polymer implements Element, Observable, NodeBindExtension {
+
+  // TODO(jmesserly): should this really be public?
+  /// Regular expression that matches data-bindings.
+  static final bindPattern = new RegExp(r'\{\{([^{}]*)}}');
+
+  /// Like [document.register] but for Polymer elements.
+  ///
+  /// Use the [name] to specify custom elment's tag name, for example:
+  /// "fancy-button" if the tag is used as `<fancy-button>`.
+  ///
+  /// The [type] is the type to construct. If not supplied, it defaults to
+  /// [PolymerElement].
+  // NOTE: this is called "element" in src/declaration/polymer-element.js, and
+  // exported as "Polymer".
+  static void register(String name, [Type type]) {
+    //console.log('registering [' + name + ']');
+    if (type == null) type = PolymerElement;
+
+    _typesByName[name] = type;
+
+    // Dart note: here we notify JS of the element registration. We don't pass
+    // the Dart type because we will handle that in PolymerDeclaration.
+    // See _hookJsPolymerDeclaration for how this is done.
+    PolymerJs.constructor.apply([name]);
+    (js.context['HTMLElement']['register'] as JsFunction)
+        .apply([name, js.context['HTMLElement']['prototype']]);
+  }
+
+  /// Register a custom element that has no associated `<polymer-element>`.
+  /// Unlike [register] this will always perform synchronous registration and
+  /// by the time this method returns the element will be available using
+  /// [document.createElement] or by modifying the HTML to include the element.
+  static void registerSync(String name, Type type,
+      {String extendsTag, Document doc, Node template}) {
+
+    // Our normal registration, this will queue up the name->type association.
+    register(name, type);
+
+    // Build a polymer-element and initialize it to register
+    if (doc == null) doc = document;
+    var poly = doc.createElement('polymer-element');
+    poly.attributes['name'] = name;
+    if (extendsTag != null) poly.attributes['extends'] = extendsTag;
+    if (template != null) poly.append(template);
+
+    // TODO(jmesserly): conceptually this is just:
+    //     new JsObject.fromBrowserObject(poly).callMethod('init')
+    //
+    // However doing it that way hits an issue with JS-interop in IE10: we get a
+    // JsObject that wraps something other than `poly`, due to improper caching.
+    // By reusing _polymerElementProto that we used for 'register', we can
+    // then call apply on it to invoke init() with the correct `this` pointer.
+    JsFunction init = _polymerElementProto['init'];
+    init.apply([], thisArg: poly);
+  }
+
+  // Warning for when people try to use `importElements` or `import`.
+  static const String _DYNAMIC_IMPORT_WARNING = 'Dynamically loading html '
+      'imports has very limited support right now in dart, see '
+      'http://dartbug.com/17873.';
+
+  /// Loads the set of HTMLImports contained in `node`. Returns a future that
+  /// resolves when all the imports have been loaded. This method can be used to
+  /// lazily load imports. For example, given a template:
+  ///
+  ///     <template>
+  ///       <link rel="import" href="my-import1.html">
+  ///       <link rel="import" href="my-import2.html">
+  ///     </template>
+  ///
+  ///     Polymer.importElements(template.content)
+  ///         .then((_) => print('imports lazily loaded'));
+  ///
+  /// Dart Note: This has very limited support in dart, http://dartbug.com/17873
+  // Dart Note: From src/lib/import.js For now proxy to the JS methods,
+  // because we want to share the loader with polymer.js for interop purposes.
+  static Future importElements(Node elementOrFragment) {
+    print(_DYNAMIC_IMPORT_WARNING);
+    return PolymerJs.importElements(elementOrFragment);
+  }
+
+  /// Loads an HTMLImport for each url specified in the `urls` array. Notifies
+  /// when all the imports have loaded by calling the `callback` function
+  /// argument. This method can be used to lazily load imports. For example,
+  /// For example,
+  ///
+  ///     Polymer.import(['my-import1.html', 'my-import2.html'])
+  ///         .then((_) => print('imports lazily loaded'));
+  ///
+  /// Dart Note: This has very limited support in dart, http://dartbug.com/17873
+  // Dart Note: From src/lib/import.js. For now proxy to the JS methods,
+  // because we want to share the loader with polymer.js for interop purposes.
+  static Future import(List urls) {
+    print(_DYNAMIC_IMPORT_WARNING);
+    return PolymerJs.import(urls);
+  }
+
+  /// Deprecated: Use `import` instead.
+  @deprecated
+  static Future importUrls(List urls) {
+    return import(urls);
+  }
+
+  /// Completes when polymer js is ready.
+  static final Completer _onReady = new Completer();
+
+  /// Completes when all initialization is done.
+  static final Completer _onInitDone = new Completer();
+
+  /// Future indicating that the Polymer library has been loaded and is ready
+  /// for use.
+  static Future get onReady =>
+      Future.wait([_onReady.future, _onInitDone.future]);
+
+  /// Returns a list of elements that have had polymer-elements created but
+  /// are not yet ready to register. The list is an array of element
+  /// definitions.
+  static List<Element> get waitingFor => PolymerJs.waitingFor;
+
+  /// Forces polymer to register any pending elements. Can be used to abort
+  /// waiting for elements that are partially defined.
+  static forceReady([int timeout]) => PolymerJs.forceReady(timeout);
+
+  /// The most derived `<polymer-element>` declaration for this element.
+  PolymerDeclaration get element => _element;
+  PolymerDeclaration _element;
+
+  /// Deprecated: use [element] instead.
+  @deprecated PolymerDeclaration get declaration => _element;
+
+  Map<String, StreamSubscription> _namedObservers;
+  List<Bindable> _observers = [];
+
+  bool _unbound; // lazy-initialized
+  PolymerJob _unbindAllJob;
+
+  CompoundObserver _propertyObserver;
+  bool _readied = false;
+
+  JsObject _jsElem;
+
+  /// Returns the object that should be used as the event controller for
+  /// event bindings in this element's template. If set, this will override the
+  /// normal controller lookup.
+  // TODO(jmesserly): we need to use a JS-writable property as our backing
+  // store, because of elements such as:
+  // https://github.com/Polymer/core-overlay/blob/eeb14853/core-overlay-layer.html#L78
+  get eventController => _jsElem['eventController'];
+  set eventController(value) {
+    _jsElem['eventController'] = value;
+  }
+
+  bool get hasBeenAttached => _hasBeenAttached;
+  bool _hasBeenAttached = false;
+
+  /// Gets the shadow root associated with the corresponding custom element.
+  ///
+  /// This is identical to [shadowRoot], unless there are multiple levels of
+  /// inheritance and they each have their own shadow root. For example,
+  /// this can happen if the base class and subclass both have `<template>` tags
+  /// in their `<polymer-element>` tags.
+  // TODO(jmesserly): should expose this as an immutable map.
+  // Similar issue as $.
+  final Map<String, ShadowRoot> shadowRoots =
+      new LinkedHashMap<String, ShadowRoot>();
+
+  /// Map of items in the shadow root(s) by their [Element.id].
+  // TODO(jmesserly): various issues:
+  // * wrap in UnmodifiableMapView?
+  // * should we have an object that implements noSuchMethod?
+  // * should the map have a key order (e.g. LinkedHash or SplayTree)?
+  // * should this be a live list? Polymer doesn't, maybe due to JS limitations?
+  // Note: this is observable to support $['someId'] being used in templates.
+  // The template is stamped before $ is populated, so we need observation if
+  // we want it to be usable in bindings.
+  final Map<String, dynamic> $ = new ObservableMap<String, dynamic>();
+
+  /// Use to override the default syntax for polymer-elements.
+  /// By default this will be null, which causes [instanceTemplate] to use
+  /// the template's bindingDelegate or the [element.syntax], in that order.
+  PolymerExpressions get syntax => null;
+
+  bool get _elementPrepared => _element != null;
+
+  /// Retrieves the custom element name. It should be used instead
+  /// of localName, see: https://github.com/Polymer/polymer-dev/issues/26
+  String get _name {
+    if (_element != null) return _element.name;
+    var isAttr = attributes['is'];
+    return (isAttr == null || isAttr == '') ? localName : isAttr;
+  }
+
+  /// By default the data bindings will be cleaned up when this custom element
+  /// is detached from the document. Overriding this to return `true` will
+  /// prevent that from happening.
+  bool get preventDispose => false;
+
+  /// Properties exposed by this element.
+  // Dart note: unlike Javascript we can't override the original property on
+  // the object, so we use this mechanism instead to define properties. See more
+  // details in [_PropertyAccessor].
+  Map<Symbol, _PropertyAccessor> _properties = {};
+
+  /// Helper to implement a property with the given [name]. This is used for
+  /// normal and computed properties. Normal properties can provide the initial
+  /// value using the [initialValue] function. Computed properties ignore
+  /// [initialValue], their value is derived from the expression in the
+  /// [ComputedProperty] annotation that appears above the getter that uses this
+  /// helper.
+  readValue(Symbol name, [initialValue()]) {
+    var property = _properties[name];
+    if (property == null) {
+      var value;
+      // Dart note: most computed properties are created in advance in
+      // createComputedProperties, but if one computed property depends on
+      // another, the declaration order might matter. Rather than trying to
+      // register them in order, we include here also the option of lazily
+      // creating the property accessor on the first read.
+      var binding = _getBindingForComputedProperty(name);
+      if (binding == null) {
+        // normal property
+        value = initialValue != null ? initialValue() : null;
+      } else {
+        value = binding.value;
+      }
+      property = _properties[name] = new _PropertyAccessor(name, this, value);
+    }
+    return property.value;
+  }
+
+  /// Helper to implement a setter of a property with the given [name] on a
+  /// polymer element. This can be used on normal properties and also on
+  /// computed properties, as long as the expression used for the computed
+  /// property is assignable (see [ComputedProperty]).
+  writeValue(Symbol name, newValue) {
+    var property = _properties[name];
+    if (property == null) {
+      // Note: computed properties are created in advance in
+      // createComputedProperties, so we should only need to create here
+      // non-computed properties.
+      property = _properties[name] = new _PropertyAccessor(name, this, null);
+    }
+    property.value = newValue;
+  }
+
+  /// If this class is used as a mixin, this method must be called from inside
+  /// of the `created()` constructor.
+  ///
+  /// If this class is a superclass, calling `super.created()` is sufficient.
+  void polymerCreated() {
+    var t = nodeBind(this).templateInstance;
+    if (t != null && t.model != null) {
+      window.console.warn('Attributes on $_name were data bound '
+          'prior to Polymer upgrading the element. This may result in '
+          'incorrect binding types.');
+    }
+    prepareElement();
+    if (!isTemplateStagingDocument(ownerDocument)) {
+      _makeElementReady();
+    }
+  }
+
+  /// *Deprecated* use [shadowRoots] instead.
+  @deprecated
+  ShadowRoot getShadowRoot(String customTagName) => shadowRoots[customTagName];
+
+  void prepareElement() {
+    if (_elementPrepared) {
+      window.console.warn('Element already prepared: $_name');
+      return;
+    }
+    _initJsObject();
+    // Dart note: get the corresponding <polymer-element> declaration.
+    _element = _getDeclaration(_name);
+    // install property storage
+    createPropertyObserver();
+    openPropertyObserver();
+    // install boilerplate attributes
+    copyInstanceAttributes();
+    // process input attributes
+    takeAttributes();
+    // add event listeners
+    addHostListeners();
+  }
+
+  /// Initialize JS interop for this element. For now we just initialize the
+  /// JsObject, but in the future we could also initialize JS APIs here.
+  _initJsObject() {
+    _jsElem = new JsObject.fromBrowserObject(this);
+  }
+
+  /// Deprecated: This is no longer a public method.
+  @deprecated
+  makeElementReady() => _makeElementReady();
+
+  _makeElementReady() {
+    if (_readied) return;
+    _readied = true;
+    createComputedProperties();
+
+    parseDeclarations(_element);
+    // NOTE: Support use of the `unresolved` attribute to help polyfill
+    // custom elements' `:unresolved` feature.
+    attributes.remove('unresolved');
+    // user entry point
+    _readyLog.info(() => '[$this]: ready');
+    ready();
+  }
+
+  /// Lifecycle method called when the element has populated it's `shadowRoot`,
+  /// prepared data-observation, and made itself ready for API interaction.
+  /// To wait until the element has been attached to the default view, use
+  /// [attached] or [domReady].
+  void ready() {}
+
+  /// Implement to access custom elements in dom descendants, ancestors,
+  /// or siblings. Because custom elements upgrade in document order,
+  /// elements accessed in `ready` or `attached` may not be upgraded. When
+  /// `domReady` is called, all registered custom elements are guaranteed
+  /// to have been upgraded.
+  void domReady() {}
+
+  void attached() {
+    if (!_elementPrepared) {
+      // Dart specific message for a common issue.
+      throw new StateError('polymerCreated was not called for custom element '
+          '$_name, this should normally be done in the .created() if Polymer '
+          'is used as a mixin.');
+    }
+
+    cancelUnbindAll();
+    if (!hasBeenAttached) {
+      _hasBeenAttached = true;
+      async((_) => domReady());
+    }
+  }
+
+  void detached() {
+    if (!preventDispose) asyncUnbindAll();
+  }
+
+  /// Walks the prototype-chain of this element and allows specific
+  /// classes a chance to process static declarations.
+  ///
+  /// In particular, each polymer-element has it's own `template`.
+  /// `parseDeclarations` is used to accumulate all element `template`s
+  /// from an inheritance chain.
+  ///
+  /// `parseDeclaration` static methods implemented in the chain are called
+  /// recursively, oldest first, with the `<polymer-element>` associated
+  /// with the current prototype passed as an argument.
+  ///
+  /// An element may override this method to customize shadow-root generation.
+  void parseDeclarations(PolymerDeclaration declaration) {
+    if (declaration != null) {
+      parseDeclarations(declaration.superDeclaration);
+      parseDeclaration(declaration.element);
+    }
+  }
+
+  /// Perform init-time actions based on static information in the
+  /// `<polymer-element>` instance argument.
+  ///
+  /// For example, the standard implementation locates the template associated
+  /// with the given `<polymer-element>` and stamps it into a shadow-root to
+  /// implement shadow inheritance.
+  ///
+  /// An element may override this method for custom behavior.
+  void parseDeclaration(Element elementElement) {
+    var template = fetchTemplate(elementElement);
+
+    if (template != null) {
+      var root = shadowFromTemplate(template);
+
+      var name = elementElement.attributes['name'];
+      if (name == null) return;
+      shadowRoots[name] = root;
+    }
+  }
+
+  /// Given a `<polymer-element>`, find an associated template (if any) to be
+  /// used for shadow-root generation.
+  ///
+  /// An element may override this method for custom behavior.
+  Element fetchTemplate(Element elementElement) =>
+      elementElement.querySelector('template');
+
+  /// Utility function that stamps a `<template>` into light-dom.
+  Node lightFromTemplate(Element template, [Node refNode]) {
+    if (template == null) return null;
+
+    // TODO(sorvell): mark this element as an event controller so that
+    // event listeners on bound nodes inside it will be called on it.
+    // Note, the expectation here is that events on all descendants
+    // should be handled by this element.
+    eventController = this;
+
+    // stamp template
+    // which includes parsing and applying MDV bindings before being
+    // inserted (to avoid {{}} in attribute values)
+    // e.g. to prevent <img src="images/{{icon}}"> from generating a 404.
+    var dom = instanceTemplate(template);
+    // append to shadow dom
+    if (refNode != null) {
+      append(dom);
+    } else {
+      insertBefore(dom, refNode);
+    }
+    // perform post-construction initialization tasks on ahem, light root
+    shadowRootReady(this);
+    // return the created shadow root
+    return dom;
+  }
+
+  /// Utility function that creates a shadow root from a `<template>`.
+  ///
+  /// The base implementation will return a [ShadowRoot], but you can replace it
+  /// with your own code and skip ShadowRoot creation. In that case, you should
+  /// return `null`.
+  ///
+  /// In your overridden method, you can use [instanceTemplate] to stamp the
+  /// template and initialize data binding, and [shadowRootReady] to intialize
+  /// other Polymer features like event handlers. It is fine to call
+  /// shadowRootReady with a node other than a ShadowRoot such as with `this`.
+  ShadowRoot shadowFromTemplate(Element template) {
+    if (template == null) return null;
+    // make a shadow root
+    var root = createShadowRoot();
+    // stamp template
+    // which includes parsing and applying MDV bindings before being
+    // inserted (to avoid {{}} in attribute values)
+    // e.g. to prevent <img src="images/{{icon}}"> from generating a 404.
+    var dom = instanceTemplate(template);
+    // append to shadow dom
+    root.append(dom);
+    // perform post-construction initialization tasks on shadow root
+    shadowRootReady(root);
+    // return the created shadow root
+    return root;
+  }
+
+  void shadowRootReady(Node root) {
+    // locate nodes with id and store references to them in this.$ hash
+    marshalNodeReferences(root);
+  }
+
+  /// Locate nodes with id and store references to them in [$] hash.
+  void marshalNodeReferences(Node root) {
+    if (root == null) return;
+    for (var n in (root as dynamic).querySelectorAll('[id]')) {
+      $[n.id] = n;
+    }
+  }
+
+  void attributeChanged(String name, String oldValue, String newValue) {
+    if (name != 'class' && name != 'style') {
+      attributeToProperty(name, newValue);
+    }
+  }
+
+  // TODO(jmesserly): this could be a top level method.
+  /// Returns a future when `node` changes, or when its children or subtree
+  /// changes.
+  ///
+  /// Use [MutationObserver] if you want to listen to a stream of changes.
+  Future<List<MutationRecord>> onMutation(Node node) {
+    var completer = new Completer();
+    new MutationObserver((mutations, observer) {
+      observer.disconnect();
+      completer.complete(mutations);
+    })..observe(node, childList: true, subtree: true);
+    return completer.future;
+  }
+
+  // copy attributes defined in the element declaration to the instance
+  // e.g. <polymer-element name="x-foo" tabIndex="0"> tabIndex is copied
+  // to the element instance here.
+  void copyInstanceAttributes() {
+    _element._instanceAttributes.forEach((name, value) {
+      attributes.putIfAbsent(name, () => value);
+    });
+  }
+
+  void takeAttributes() {
+    if (_element._publishLC == null) return;
+    attributes.forEach(attributeToProperty);
+  }
+
+  /// If attribute [name] is mapped to a property, deserialize
+  /// [value] into that property.
+  void attributeToProperty(String name, String value) {
+    // try to match this attribute to a property (attributes are
+    // all lower-case, so this is case-insensitive search)
+    var decl = propertyForAttribute(name);
+    if (decl == null) return;
+
+    // filter out 'mustached' values, these are to be
+    // replaced with bound-data and are not yet values
+    // themselves.
+    if (value == null || value.contains(Polymer.bindPattern)) return;
+
+    final currentValue = smoke.read(this, decl.name);
+
+    // deserialize Boolean or Number values from attribute
+    var type = decl.type;
+    if ((type == Object || type == dynamic) && currentValue != null) {
+      // Attempt to infer field type from the current value.
+      type = currentValue.runtimeType;
+    }
+    final newValue = deserializeValue(value, currentValue, type);
+
+    // only act if the value has changed
+    if (!identical(newValue, currentValue)) {
+      // install new value (has side-effects)
+      smoke.write(this, decl.name, newValue);
+    }
+  }
+
+  /// Return the published property matching name, or null.
+  // TODO(jmesserly): should we just return Symbol here?
+  smoke.Declaration propertyForAttribute(String name) {
+    final publishLC = _element._publishLC;
+    if (publishLC == null) return null;
+    return publishLC[name];
+  }
+
+  /// Convert representation of [value] based on [type] and [currentValue].
+  Object deserializeValue(String value, Object currentValue, Type type) =>
+      deserialize.deserializeValue(value, currentValue, type);
+
+  String serializeValue(Object value) {
+    if (value == null) return null;
+
+    if (value is bool) {
+      return _toBoolean(value) ? '' : null;
+    } else if (value is String || value is num) {
+      return '$value';
+    }
+    return null;
+  }
+
+  void reflectPropertyToAttribute(String path) {
+    // TODO(sjmiles): consider memoizing this
+    // try to intelligently serialize property value
+    final propValue = new PropertyPath(path).getValueFrom(this);
+    final serializedValue = serializeValue(propValue);
+    // boolean properties must reflect as boolean attributes
+    if (serializedValue != null) {
+      attributes[path] = serializedValue;
+      // TODO(sorvell): we should remove attr for all properties
+      // that have undefined serialization; however, we will need to
+      // refine the attr reflection system to achieve this; pica, for example,
+      // relies on having inferredType object properties not removed as
+      // attrs.
+    } else if (propValue is bool) {
+      attributes.remove(path);
+    }
+  }
+
+  /// Creates the document fragment to use for each instance of the custom
+  /// element, given the `<template>` node. By default this is equivalent to:
+  ///
+  ///     templateBind(template).createInstance(this, polymerSyntax);
+  ///
+  /// Where polymerSyntax is a singleton [PolymerExpressions] instance.
+  ///
+  /// You can override this method to change the instantiation behavior of the
+  /// template, for example to use a different data-binding syntax.
+  DocumentFragment instanceTemplate(Element template) {
+    // ensure template is decorated (lets things like <tr template ...> work)
+    TemplateBindExtension.decorate(template);
+    var syntax = this.syntax;
+    var t = templateBind(template);
+    if (syntax == null && t.bindingDelegate == null) {
+      syntax = element.syntax;
+    }
+    var dom = t.createInstance(this, syntax);
+    _observers.addAll(getTemplateInstanceBindings(dom));
+    return dom;
+  }
+
+  /// Called by TemplateBinding/NodeBind to setup a binding to the given
+  /// property. It's overridden here to support property bindings in addition to
+  /// attribute bindings that are supported by default.
+  Bindable bind(String name, bindable, {bool oneTime: false}) {
+    var decl = propertyForAttribute(name);
+    if (decl == null) {
+      // Cannot call super.bind because template_binding is its own package
+      return nodeBindFallback(this).bind(name, bindable, oneTime: oneTime);
+    } else {
+      // use n-way Polymer binding
+      var observer = bindProperty(decl.name, bindable, oneTime: oneTime);
+      // NOTE: reflecting binding information is typically required only for
+      // tooling. It has a performance cost so it's opt-in in Node.bind.
+      if (enableBindingsReflection && observer != null) {
+        // Dart note: this is not needed because of how _PolymerBinding works.
+        //observer.path = bindable.path_;
+        _recordBinding(name, observer);
+      }
+      var reflect = _element._reflect;
+
+      // Get back to the (possibly camel-case) name for the property.
+      var propName = smoke.symbolToName(decl.name);
+      if (reflect != null && reflect.contains(propName)) {
+        reflectPropertyToAttribute(propName);
+      }
+      return observer;
+    }
+  }
+
+  _recordBinding(String name, observer) {
+    if (bindings == null) bindings = {};
+    this.bindings[name] = observer;
+  }
+
+  /// Called by TemplateBinding when all bindings on an element have been
+  /// executed. This signals that all element inputs have been gathered and it's
+  /// safe to ready the element, create shadow-root and start data-observation.
+  bindFinished() => _makeElementReady();
+
+  Map<String, Bindable> get bindings => nodeBindFallback(this).bindings;
+  set bindings(Map value) {
+    nodeBindFallback(this).bindings = value;
+  }
+
+  TemplateInstance get templateInstance =>
+      nodeBindFallback(this).templateInstance;
+
+  /// Called at detached time to signal that an element's bindings should be
+  /// cleaned up. This is done asynchronously so that users have the chance to
+  /// call `cancelUnbindAll` to prevent unbinding.
+  void asyncUnbindAll() {
+    if (_unbound == true) return;
+    _unbindLog.fine(() => '[$_name] asyncUnbindAll');
+    _unbindAllJob = scheduleJob(_unbindAllJob, unbindAll);
+  }
+
+  /// This method should rarely be used and only if `cancelUnbindAll` has been
+  /// called to prevent element unbinding. In this case, the element's bindings
+  /// will not be automatically cleaned up and it cannot be garbage collected by
+  /// by the system. If memory pressure is a concern or a large amount of
+  /// elements need to be managed in this way, `unbindAll` can be called to
+  /// deactivate the element's bindings and allow its memory to be reclaimed.
+  void unbindAll() {
+    if (_unbound == true) return;
+    closeObservers();
+    closeNamedObservers();
+    _unbound = true;
+  }
+
+  //// Call in `detached` to prevent the element from unbinding when it is
+  //// detached from the dom. The element is unbound as a cleanup step that
+  //// allows its memory to be reclaimed. If `cancelUnbindAll` is used, consider
+  /// calling `unbindAll` when the element is no longer needed. This will allow
+  /// its memory to be reclaimed.
+  void cancelUnbindAll() {
+    if (_unbound == true) {
+      _unbindLog
+          .warning(() => '[$_name] already unbound, cannot cancel unbindAll');
+      return;
+    }
+    _unbindLog.fine(() => '[$_name] cancelUnbindAll');
+    if (_unbindAllJob != null) {
+      _unbindAllJob.stop();
+      _unbindAllJob = null;
+    }
+  }
+
+  static void _forNodeTree(Node node, void callback(Node node)) {
+    if (node == null) return;
+
+    callback(node);
+    for (var child = node.firstChild; child != null; child = child.nextNode) {
+      _forNodeTree(child, callback);
+    }
+  }
+
+  /// Creates a CompoundObserver to observe property changes.
+  /// NOTE, this is only done if there are any properties in the `_observe`
+  /// object.
+  void createPropertyObserver() {
+    final observe = _element._observe;
+    if (observe != null) {
+      var o = _propertyObserver = new CompoundObserver();
+      // keep track of property observer so we can shut it down
+      _observers.add(o);
+
+      for (var path in observe.keys) {
+        o.addPath(this, path);
+
+        // TODO(jmesserly): on the Polymer side it doesn't look like they
+        // will observe arrays unless it is a length == 1 path.
+        observeArrayValue(path, path.getValueFrom(this), null);
+      }
+    }
+  }
+
+  /// Start observing property changes.
+  void openPropertyObserver() {
+    if (_propertyObserver != null) {
+      _propertyObserver.open(notifyPropertyChanges);
+    }
+
+    // Dart note: we need an extra listener only to continue supporting
+    // @published properties that follow the old syntax until we get rid of it.
+    // This workaround has timing issues so we prefer the new, not so nice,
+    // syntax.
+    if (_element._publish != null) {
+      changes.listen(_propertyChangeWorkaround);
+    }
+  }
+
+  /// Handler for property changes; routes changes to observing methods.
+  /// Note: array valued properties are observed for array splices.
+  void notifyPropertyChanges(List newValues, Map oldValues, List paths) {
+    final observe = _element._observe;
+    final called = new HashSet();
+
+    oldValues.forEach((i, oldValue) {
+      final newValue = newValues[i];
+
+      // Date note: we don't need any special checking for null and undefined.
+
+      // note: paths is of form [object, path, object, path]
+      final path = paths[2 * i + 1];
+      if (observe == null) return;
+
+      var methods = observe[path];
+      if (methods == null) return;
+
+      for (var method in methods) {
+        if (!called.add(method)) continue; // don't invoke more than once.
+
+        observeArrayValue(path, newValue, oldValue);
+        // Dart note: JS passes "arguments", so we pass along our args.
+        // TODO(sorvell): call method with the set of values it's expecting;
+        // e.g. 'foo bar': 'invalidate' expects the new and old values for
+        // foo and bar. Currently we give only one of these and then
+        // deliver all the arguments.
+        smoke.invoke(
+            this, method, [oldValue, newValue, newValues, oldValues, paths],
+            adjust: true);
+      }
+    });
+  }
+
+  /// Force any pending property changes to synchronously deliver to handlers
+  /// specified in the `observe` object.
+  /// Note: normally changes are processed at microtask time.
+  ///
+  // Dart note: had to rename this to avoid colliding with
+  // Observable.deliverChanges. Even worse, super calls aren't possible or
+  // it prevents Polymer from being a mixin, so we can't override it even if
+  // we wanted to.
+  void deliverPropertyChanges() {
+    if (_propertyObserver != null) {
+      _propertyObserver.deliver();
+    }
+  }
+
+  // Dart note: this workaround is only for old-style @published properties,
+  // which have timing issues. See _bindOldStylePublishedProperty below.
+  // TODO(sigmund): deprecate this.
+  void _propertyChangeWorkaround(List<ChangeRecord> records) {
+    for (var record in records) {
+      if (record is! PropertyChangeRecord) continue;
+
+      var name = record.name;
+      // The setter of a new-style property will create an accessor in
+      // _properties[name]. We can skip the workaround for those properties.
+      if (_properties[name] != null) continue;
+      _propertyChange(name, record.newValue, record.oldValue);
+    }
+  }
+
+  void _propertyChange(Symbol nameSymbol, newValue, oldValue) {
+    _watchLog.info(
+        () => '[$this]: $nameSymbol changed from: $oldValue to: $newValue');
+    var name = smoke.symbolToName(nameSymbol);
+    var reflect = _element._reflect;
+    if (reflect != null && reflect.contains(name)) {
+      reflectPropertyToAttribute(name);
+    }
+  }
+
+  void observeArrayValue(PropertyPath name, Object value, Object old) {
+    final observe = _element._observe;
+    if (observe == null) return;
+
+    // we only care if there are registered side-effects
+    var callbacks = observe[name];
+    if (callbacks == null) return;
+
+    // if we are observing the previous value, stop
+    if (old is ObservableList) {
+      _observeLog.fine(() => '[$_name] observeArrayValue: unregister $name');
+
+      closeNamedObserver('${name}__array');
+    }
+    // if the new value is an array, begin observing it
+    if (value is ObservableList) {
+      _observeLog.fine(() => '[$_name] observeArrayValue: register $name');
+      var sub = value.listChanges.listen((changes) {
+        for (var callback in callbacks) {
+          smoke.invoke(this, callback, [changes], adjust: true);
+        }
+      });
+      registerNamedObserver('${name}__array', sub);
+    }
+  }
+
+  emitPropertyChangeRecord(Symbol name, newValue, oldValue) {
+    if (identical(oldValue, newValue)) return;
+    _propertyChange(name, newValue, oldValue);
+  }
+
+  bindToAccessor(Symbol name, Bindable bindable, {resolveBindingValue: false}) {
+    // Dart note: our pattern is to declare the initial value in the getter.  We
+    // read it via smoke to ensure that the value is initialized correctly.
+    var oldValue = smoke.read(this, name);
+    var property = _properties[name];
+    if (property == null) {
+      // We know that _properties[name] is null only for old-style @published
+      // properties. This fallback is here to make it easier to deprecate the
+      // old-style of published properties, which have bad timing guarantees
+      // (see comment in _PolymerBinding).
+      return _bindOldStylePublishedProperty(name, bindable, oldValue);
+    }
+
+    property.bindable = bindable;
+    var value = bindable.open(property.updateValue);
+
+    if (resolveBindingValue) {
+      // capture A's value if B's value is null or undefined,
+      // otherwise use B's value
+      var v = (value == null ? oldValue : value);
+      if (!identical(value, oldValue)) {
+        bindable.value = value = v;
+      }
+    }
+
+    property.updateValue(value);
+    var o = new _CloseOnlyBinding(property);
+    _observers.add(o);
+    return o;
+  }
+
+  // Dart note: this fallback uses our old-style binding mechanism to be able to
+  // link @published properties with bindings. This mechanism is backwards from
+  // what Javascript does because we can't override the original property. This
+  // workaround also brings some timing issues which are described in detail in
+  // dartbug.com/18343.
+  // TODO(sigmund): deprecate old-style @published properties.
+  _bindOldStylePublishedProperty(Symbol name, Bindable bindable, oldValue) {
+    // capture A's value if B's value is null or undefined,
+    // otherwise use B's value
+    if (bindable.value == null) bindable.value = oldValue;
+
+    var o = new _PolymerBinding(this, name, bindable);
+    _observers.add(o);
+    return o;
+  }
+
+  _getBindingForComputedProperty(Symbol name) {
+    var exprString = element._computed[name];
+    if (exprString == null) return null;
+    var expr = PolymerExpressions.getExpression(exprString);
+    return PolymerExpressions.getBinding(expr, this,
+        globals: element.syntax.globals);
+  }
+
+  createComputedProperties() {
+    var computed = this.element._computed;
+    for (var name in computed.keys) {
+      try {
+        // Dart note: this is done in Javascript by modifying the prototype in
+        // declaration/properties.js, we can't do that, so we do it here.
+        var binding = _getBindingForComputedProperty(name);
+
+        // Follow up note: ideally we would only create the accessor object
+        // here, but some computed properties might depend on others and
+        // evaluating `binding.value` could try to read the value of another
+        // computed property that we haven't created yet. For this reason we
+        // also allow to also create the accessor in [readValue].
+        if (_properties[name] == null) {
+          _properties[name] = new _PropertyAccessor(name, this, binding.value);
+        }
+        bindToAccessor(name, binding);
+      } catch (e) {
+        window.console.error('Failed to create computed property $name'
+            ' (${computed[name]}): $e');
+      }
+    }
+  }
+
+  // Dart note: to simplify the code above we made registerObserver calls
+  // directly invoke _observers.add/addAll.
+  void closeObservers() {
+    for (var o in _observers) {
+      if (o != null) o.close();
+    }
+    _observers = [];
+  }
+
+  /// Bookkeeping observers for memory management.
+  void registerNamedObserver(String name, StreamSubscription sub) {
+    if (_namedObservers == null) {
+      _namedObservers = new Map<String, StreamSubscription>();
+    }
+    _namedObservers[name] = sub;
+  }
+
+  bool closeNamedObserver(String name) {
+    var sub = _namedObservers.remove(name);
+    if (sub == null) return false;
+    sub.cancel();
+    return true;
+  }
+
+  void closeNamedObservers() {
+    if (_namedObservers == null) return;
+    for (var sub in _namedObservers.values) {
+      if (sub != null) sub.cancel();
+    }
+    _namedObservers.clear();
+    _namedObservers = null;
+  }
+
+  /// Bind the [name] property in this element to [bindable]. *Note* in Dart it
+  /// is necessary to also define the field:
+  ///
+  ///     var myProperty;
+  ///
+  ///     ready() {
+  ///       super.ready();
+  ///       bindProperty(#myProperty,
+  ///           new PathObserver(this, 'myModel.path.to.otherProp'));
+  ///     }
+  Bindable bindProperty(Symbol name, bindableOrValue, {oneTime: false}) {
+    // Dart note: normally we only reach this code when we know it's a
+    // property, but if someone uses bindProperty directly they might get a
+    // NoSuchMethodError either from the getField below, or from the setField
+    // inside PolymerBinding. That doesn't seem unreasonable, but it's a slight
+    // difference from Polymer.js behavior.
+
+    _bindLog.fine(() => 'bindProperty: [$bindableOrValue] to [$_name].[$name]');
+
+    if (oneTime) {
+      if (bindableOrValue is Bindable) {
+        _bindLog.warning(() =>
+            'bindProperty: expected non-bindable value n a one-time binding to '
+            '[$_name].[$name], but found $bindableOrValue.');
+      }
+      smoke.write(this, name, bindableOrValue);
+      return null;
+    }
+
+    return bindToAccessor(name, bindableOrValue, resolveBindingValue: true);
+  }
+
+  /// Attach event listeners on the host (this) element.
+  void addHostListeners() {
+    var events = _element._eventDelegates;
+    if (events.isEmpty) return;
+
+    _eventsLog.fine(() => '[$_name] addHostListeners: $events');
+
+    // NOTE: host events look like bindings but really are not;
+    // (1) we don't want the attribute to be set and (2) we want to support
+    // multiple event listeners ('host' and 'instance') and Node.bind
+    // by default supports 1 thing being bound.
+    events.forEach((type, methodName) {
+      // Dart note: the getEventHandler method is on our PolymerExpressions.
+      PolymerGesturesJs.addEventListener(
+          this, type,
+          Zone.current.bindUnaryCallback(
+              element.syntax.getEventHandler(this, this, methodName)));
+    });
+  }
+
+  /// Calls [methodOrCallback] with [args] if it is a closure, otherwise, treat
+  /// it as a method name in [object], and invoke it.
+  void dispatchMethod(object, callbackOrMethod, List args) {
+    _eventsLog.info(() => '>>> [$_name]: dispatch $callbackOrMethod');
+
+    if (callbackOrMethod is Function) {
+      int maxArgs = smoke.maxArgs(callbackOrMethod);
+      if (maxArgs == -1) {
+        _eventsLog.warning(
+            'invalid callback: expected callback of 0, 1, 2, or 3 arguments');
+      }
+      args.length = maxArgs;
+      Function.apply(callbackOrMethod, args);
+    } else if (callbackOrMethod is String) {
+      smoke.invoke(object, smoke.nameToSymbol(callbackOrMethod), args,
+          adjust: true);
+    } else {
+      _eventsLog.warning('invalid callback');
+    }
+
+    _eventsLog.fine(() => '<<< [$_name]: dispatch $callbackOrMethod');
+  }
+
+  /// Call [methodName] method on this object with [args].
+  invokeMethod(Symbol methodName, List args) =>
+      smoke.invoke(this, methodName, args, adjust: true);
+
+  /// Invokes a function asynchronously.
+  /// This will call `Polymer.flush()` and then return a `new Timer`
+  /// with the provided [method] and [timeout].
+  ///
+  /// If you would prefer to run the callback using
+  /// [window.requestAnimationFrame], see the [async] method.
+  ///
+  /// To cancel, call [Timer.cancel] on the result of this method.
+  Timer asyncTimer(void method(), Duration timeout) {
+    // Dart note: "async" is split into 2 methods so it can have a sensible type
+    // signatures. Also removed the various features that don't make sense in a
+    // Dart world, like binding to "this" and taking arguments list.
+
+    // when polyfilling Object.observe, ensure changes
+    // propagate before executing the async method
+    scheduleMicrotask(Observable.dirtyCheck);
+    PolymerJs.flush(); // for polymer-js interop
+    return new Timer(timeout, method);
+  }
+
+  /// Invokes a function asynchronously. The context of the callback function is
+  /// function is bound to 'this' automatically. Returns a handle which may be
+  /// passed to cancelAsync to cancel the asynchronous call.
+  ///
+  /// If you would prefer to run the callback after a given duration, see
+  /// the [asyncTimer] method.
+  ///
+  /// If you would like to cancel this, use [cancelAsync].
+  int async(RequestAnimationFrameCallback method) {
+    // when polyfilling Object.observe, ensure changes
+    // propagate before executing the async method
+    scheduleMicrotask(Observable.dirtyCheck);
+    PolymerJs.flush(); // for polymer-js interop
+    return window.requestAnimationFrame(method);
+  }
+
+  /// Cancel an operation scheduled by [async].
+  void cancelAsync(int id) => window.cancelAnimationFrame(id);
+
+  /// Fire a [CustomEvent] targeting [onNode], or `this` if onNode is not
+  /// supplied. Returns the new event.
+  CustomEvent fire(String type,
+      {Object detail, Node onNode, bool canBubble, bool cancelable}) {
+    var node = onNode != null ? onNode : this;
+    var event = new CustomEvent(type,
+        canBubble: canBubble != null ? canBubble : true,
+        cancelable: cancelable != null ? cancelable : true,
+        detail: detail);
+    node.dispatchEvent(event);
+    return event;
+  }
+
+  /// Fire an event asynchronously. See [async] and [fire].
+  asyncFire(String type, {Object detail, Node toNode, bool canBubble}) {
+    // TODO(jmesserly): I'm not sure this method adds much in Dart, it's easy to
+    // add "() =>"
+    async((x) =>
+        fire(type, detail: detail, onNode: toNode, canBubble: canBubble));
+  }
+
+  /// Remove [className] from [old], add class to [anew], if they exist.
+  void classFollows(Element anew, Element old, String className) {
+    if (old != null) {
+      old.classes.remove(className);
+    }
+    if (anew != null) {
+      anew.classes.add(className);
+    }
+  }
+
+  /// Installs external stylesheets and <style> elements with the attribute
+  /// polymer-scope='controller' into the scope of element. This is intended
+  /// to be called during custom element construction.
+  void installControllerStyles() {
+    var scope = findStyleScope();
+    if (scope != null && !scopeHasNamedStyle(scope, localName)) {
+      // allow inherited controller styles
+      var decl = _element;
+      var cssText = new StringBuffer();
+      while (decl != null) {
+        cssText.write(decl.cssTextForScope(_STYLE_CONTROLLER_SCOPE));
+        decl = decl.superDeclaration;
+      }
+      if (cssText.isNotEmpty) {
+        installScopeCssText('$cssText', scope);
+      }
+    }
+  }
+
+  void installScopeStyle(style, [String name, Node scope]) {
+    if (scope == null) scope = findStyleScope();
+    if (name == null) name = '';
+
+    if (scope != null && !scopeHasNamedStyle(scope, '$_name$name')) {
+      var cssText = new StringBuffer();
+      if (style is Iterable) {
+        for (var s in style) {
+          cssText
+            ..writeln(s.text)
+            ..writeln();
+        }
+      } else {
+        cssText = (style as Node).text;
+      }
+      installScopeCssText('$cssText', scope, name);
+    }
+  }
+
+  void installScopeCssText(String cssText, [Node scope, String name]) {
+    if (scope == null) scope = findStyleScope();
+    if (name == null) name = '';
+
+    if (scope == null) return;
+
+    if (_hasShadowDomPolyfill) {
+      cssText = _shimCssText(cssText, scope is ShadowRoot ? scope.host : null);
+    }
+    var style = element.cssTextToScopeStyle(cssText, _STYLE_CONTROLLER_SCOPE);
+    applyStyleToScope(style, scope);
+    // cache that this style has been applied
+    styleCacheForScope(scope).add('$_name$name');
+  }
+
+  Node findStyleScope([node]) {
+    // find the shadow root that contains this element
+    var n = node;
+    if (n == null) n = this;
+    while (n.parentNode != null) {
+      n = n.parentNode;
+    }
+    return n;
+  }
+
+  bool scopeHasNamedStyle(Node scope, String name) =>
+      styleCacheForScope(scope).contains(name);
+
+  Map _polyfillScopeStyleCache = {};
+
+  Set styleCacheForScope(Node scope) {
+    var styles;
+    if (_hasShadowDomPolyfill) {
+      var name = scope is ShadowRoot
+          ? scope.host.localName
+          : (scope as Element).localName;
+      var styles = _polyfillScopeStyleCache[name];
+      if (styles == null) _polyfillScopeStyleCache[name] = styles = new Set();
+    } else {
+      styles = _scopeStyles[scope];
+      if (styles == null) _scopeStyles[scope] = styles = new Set();
+    }
+    return styles;
+  }
+
+  static final _scopeStyles = new Expando();
+
+  static String _shimCssText(String cssText, [Element host]) {
+    var name = '';
+    var is_ = false;
+    if (host != null) {
+      name = host.localName;
+      is_ = host.attributes.containsKey('is');
+    }
+    var selector = _ShadowCss.callMethod('makeScopeSelector', [name, is_]);
+    return _ShadowCss.callMethod('shimCssText', [cssText, selector]);
+  }
+
+  static void applyStyleToScope(StyleElement style, Node scope) {
+    if (style == null) return;
+
+    if (scope == document) scope = document.head;
+
+    if (_hasShadowDomPolyfill) scope = document.head;
+
+    // TODO(sorvell): necessary for IE
+    // see https://connect.microsoft.com/IE/feedback/details/790212/
+    // cloning-a-style-element-and-adding-to-document-produces
+    // -unexpected-result#details
+    // var clone = style.cloneNode(true);
+    var clone = new StyleElement()..text = style.text;
+
+    var attr = style.attributes[_STYLE_SCOPE_ATTRIBUTE];
+    if (attr != null) {
+      clone.attributes[_STYLE_SCOPE_ATTRIBUTE] = attr;
+    }
+
+    // TODO(sorvell): probably too brittle; try to figure out
+    // where to put the element.
+    var refNode = scope.firstChild;
+    if (scope == document.head) {
+      var selector = 'style[$_STYLE_SCOPE_ATTRIBUTE]';
+      var styleElement = document.head.querySelectorAll(selector);
+      if (styleElement.isNotEmpty) {
+        refNode = styleElement.last.nextElementSibling;
+      }
+    }
+    scope.insertBefore(clone, refNode);
+  }
+
+  /// Invoke [callback] in [wait], unless the job is re-registered,
+  /// which resets the timer. If [wait] is not supplied, this will use
+  /// [window.requestAnimationFrame] instead of a [Timer].
+  ///
+  /// For example:
+  ///
+  ///     _myJob = Polymer.scheduleJob(_myJob, callback);
+  ///
+  /// Returns the newly created job.
+  // Dart note: renamed to scheduleJob to be a bit more consistent with Dart.
+  PolymerJob scheduleJob(PolymerJob job, void callback(), [Duration wait]) {
+    if (job == null) job = new PolymerJob._();
+    // Dart note: made start smarter, so we don't need to call stop.
+    return job..start(callback, wait);
+  }
+
+  // Deprecated: Please use injectBoundHtml.
+  @deprecated
+  DocumentFragment injectBoundHTML(String html, [Element element]) =>
+      injectBoundHtml(html, element: element);
+
+  /// Inject HTML which contains markup bound to this element into
+  /// a target element (replacing target element content).
+  DocumentFragment injectBoundHtml(String html, {Element element,
+      NodeValidator validator, NodeTreeSanitizer treeSanitizer}) {
+    var template = new TemplateElement()
+      ..setInnerHtml(html, validator: validator, treeSanitizer: treeSanitizer);
+    var fragment = this.instanceTemplate(template);
+    if (element != null) {
+      element.text = '';
+      element.append(fragment);
+    }
+    return fragment;
+  }
+}
+
+// Dart note: this is related to _bindOldStylePublishedProperty. Polymer
+// addresses n-way bindings by metaprogramming: redefine the property on the
+// PolymerElement instance to always get its value from the model@path. This is
+// supported in Dart using a new style of @published property declaration using
+// the `readValue` and `writeValue` methods above. In the past we used to work
+// around this by listening to changes on both sides and updating the values.
+// This object provides the hooks to do this.
+// TODO(sigmund,jmesserly): delete after a deprecation period.
+class _PolymerBinding extends Bindable {
+  final Polymer _target;
+  final Symbol _property;
+  final Bindable _bindable;
+  StreamSubscription _sub;
+  Object _lastValue;
+
+  _PolymerBinding(this._target, this._property, this._bindable) {
+    _sub = _target.changes.listen(_propertyValueChanged);
+    _updateNode(open(_updateNode));
+  }
+
+  void _updateNode(newValue) {
+    _lastValue = newValue;
+    smoke.write(_target, _property, newValue);
+    // Note: we don't invoke emitPropertyChangeRecord here because that's
+    // done by listening on changes on the PolymerElement.
+  }
+
+  void _propertyValueChanged(List<ChangeRecord> records) {
+    for (var record in records) {
+      if (record is PropertyChangeRecord && record.name == _property) {
+        final newValue = smoke.read(_target, _property);
+        if (!identical(_lastValue, newValue)) {
+          this.value = newValue;
+        }
+        return;
+      }
+    }
+  }
+
+  open(callback(value)) => _bindable.open(callback);
+  get value => _bindable.value;
+  set value(newValue) => _bindable.value = newValue;
+
+  void close() {
+    if (_sub != null) {
+      _sub.cancel();
+      _sub = null;
+    }
+    _bindable.close();
+  }
+}
+
+// Ported from an inline object in instance/properties.js#bindToAccessor.
+class _CloseOnlyBinding extends Bindable {
+  final _PropertyAccessor accessor;
+
+  _CloseOnlyBinding(this.accessor);
+
+  open(callback) {}
+  get value => null;
+  set value(newValue) {}
+  deliver() {}
+
+  void close() {
+    if (accessor.bindable == null) return;
+    accessor.bindable.close();
+    accessor.bindable = null;
+  }
+}
+
+bool _toBoolean(value) => null != value && false != value;
+
+final Logger _observeLog = new Logger('polymer.observe');
+final Logger _eventsLog = new Logger('polymer.events');
+final Logger _unbindLog = new Logger('polymer.unbind');
+final Logger _bindLog = new Logger('polymer.bind');
+final Logger _watchLog = new Logger('polymer.watch');
+final Logger _readyLog = new Logger('polymer.ready');
+
+final Expando _eventHandledTable = new Expando<Set<Node>>();
diff --git a/polymer/lib/src/job.dart b/polymer/lib/src/job.dart
new file mode 100644
index 0000000..aa22f47
--- /dev/null
+++ b/polymer/lib/src/job.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2013, 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.
+
+part of polymer;
+
+/// Like [Timer] but can be restarted, and if no duration is supplied uses
+/// [window.requestAnimationFrame] instead of a 0-duration timer.
+// TODO(jmesserly): need to find a better name here. Also this feels more like a
+// system level API, but doesn't map exactly to any of our other primitives.
+class PolymerJob {
+  Function _callback;
+  Timer _timer;
+  int _id; // for requestAnimationFrame
+
+  PolymerJob._();
+
+  bool get isScheduled => _timer != null || _id != null;
+
+  /// Starts the job. If the job is already running, it will [stop] first.
+  void start(void callback(), [Duration wait]) {
+    stop();
+    _callback = callback;
+    if (wait == null) {
+      _id = window.requestAnimationFrame((_) => complete());
+    } else {
+      _timer = new Timer(wait, complete);
+    }
+  }
+
+  /// Stops the job. It can be restarted by calling [start] with a new callback.
+  void stop() {
+    if (_id != null) {
+      window.cancelAnimationFrame(_id);
+      _id = null;
+    }
+    if (_timer != null) {
+      _timer.cancel();
+      _timer = null;
+    }
+  }
+
+  /// Synchronously completes the job.
+  void complete() {
+    if (isScheduled) {
+      stop();
+      _callback();
+    }
+  }
+}
diff --git a/polymer/lib/src/loader.dart b/polymer/lib/src/loader.dart
new file mode 100644
index 0000000..fb5e87f
--- /dev/null
+++ b/polymer/lib/src/loader.dart
@@ -0,0 +1,169 @@
+// Copyright (c) 2013, 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.
+
+part of polymer;
+
+/// Initializes a polymer application as follows:
+///   * if running in development mode, set up a dirty-checking zone that polls
+///     for observable changes
+///   * initialize template binding and polymer-element
+///   * for each library included transitively from HTML and HTML imports,
+///   register custom elements declared there (labeled with [CustomTag]) and
+///   invoke the initialization method on it (top-level functions annotated with
+///   [initMethod]).
+Future<Zone> initPolymer() {
+  _initializeLogging();
+  if (_deployMode) {
+    return startPolymer().then((_) => Zone.current);
+  }
+  return dirtyCheckZone()
+      .run(() => startPolymer().then((_) => dirtyCheckZone()));
+}
+
+bool _startPolymerCalled = false;
+
+/// Starts polymer by hooking the polymer.js code. **Note**: this function is
+/// not meant to be invoked directly by application developers. It is invoked
+/// by [initPolymer].
+Future startPolymer() {
+  // First wait for all html imports to finish, then run the rest of the
+  // initializers.
+  return initWebComponents(initAll: false).then((_) {
+    // Polymer js is now loaded, hook it before running @CustomTag annotations.
+    if (_startPolymerCalled) throw 'Initialization was already done.';
+    _startPolymerCalled = true;
+    _hookJsPolymer();
+  }).then((_) => initWebComponents()).then((_) {
+    Polymer.registerSync('auto-binding-dart', AutoBindingElement,
+        extendsTag: 'template');
+
+    _watchWaitingFor();
+    Polymer._onInitDone.complete();
+  });
+}
+
+/// Configures [initPolymer] making it optimized for deployment to the internet.
+/// Additionally, after this method is called [initPolymer] omits the [Zone]
+/// that automatically invokes [Observable.dirtyCheck].
+void configureForDeployment() {
+  _deployMode = true;
+}
+
+/// To ensure Dart can interoperate with polymer-element registered by
+/// polymer.js, we need to be able to execute Dart code if we are registering
+/// a Dart class for that element. We trigger Dart logic by patching
+/// polymer-element's register function and:
+///
+/// * if it has a Dart class, run PolymerDeclaration's register.
+/// * otherwise it is a JS prototype, run polymer-element's normal register.
+void _hookJsPolymer() {
+  if (!PolymerJs.checkExists()) {
+    throw new StateError('An error occurred initializing polymer, (could not'
+        'find polymer js). Please file a bug at '
+        'https://github.com/dart-lang/polymer-dart/issues/new.');
+  }
+
+  // TODO(jmesserly): dart:js appears to not callback in the correct zone:
+  // https://code.google.com/p/dart/issues/detail?id=17301
+  var zone = Zone.current;
+
+  PolymerJs.whenPolymerReady(() => Polymer._onReady.complete());
+
+  JsFunction originalRegister = _polymerElementProto['register'];
+  if (originalRegister == null) {
+    throw new StateError('polymer.js must expose "register" function on '
+        'polymer-element to enable polymer.dart to interoperate.');
+  }
+
+  registerDart(jsElem, String name, String extendee) {
+    // By the time we get here, we'll know for sure if it is a Dart object
+    // or not, because polymer-element will wait for us to notify that
+    // the @CustomTag was found.
+    final type = _getRegisteredType(name);
+    if (type != null) {
+      final extendsDecl = _getDeclaration(extendee);
+      return zone.run(() =>
+          new PolymerDeclaration(jsElem, name, type, extendsDecl).register());
+    }
+    // It's a JavaScript polymer element, fall back to the original register.
+    return originalRegister.apply([name, extendee], thisArg: jsElem);
+  }
+
+  _polymerElementProto['register'] = new JsFunction.withThis(registerDart);
+}
+
+// Note: we cache this so we can use it later to look up 'init'.
+// See registerSync.
+JsObject _polymerElementProto = () {
+  var polyElem = document.createElement('polymer-element');
+  var proto = new JsObject.fromBrowserObject(polyElem)['__proto__'];
+  if (proto is Node) proto = new JsObject.fromBrowserObject(proto);
+  return proto;
+}();
+
+// Add support for the polymer js style of enabling logging. The global logging
+// level is respected for specified loggers (see http://goo.gl/btfDe1). All
+// other loggers will be set to [Level.OFF]. Logs will also be printed to the
+// console automatically if any are supplied.
+void _initializeLogging() {
+  hierarchicalLoggingEnabled = true;
+  var webComponents = js.context['WebComponents'];
+  var logFlags = (webComponents == null || webComponents['flags'] == null)
+      ? {}
+      : webComponents['flags']['log'];
+  if (logFlags == null) logFlags = {};
+  var loggers = [
+    _observeLog,
+    _eventsLog,
+    _unbindLog,
+    _bindLog,
+    _watchLog,
+    _readyLog
+  ];
+  var polymerLogger = new Logger('polymer');
+
+  // If no loggers specified then disable globally and return.
+  if (!loggers.any((logger) => logFlags[logger.name] == true)) {
+    polymerLogger.level = Level.OFF;
+    return;
+  }
+
+  // Disable the loggers that were not specified.
+  loggers.where((logger) => logFlags[logger.name] != true).forEach((logger) {
+    logger.level = Level.OFF;
+  });
+
+  // Listen to the polymer logs and print them to the console.
+  polymerLogger.onRecord.listen((rec) {
+    print(rec);
+  });
+}
+
+/// Watches the waitingFor queue and if it fails to make progress then prints
+/// a message to the console.
+void _watchWaitingFor() {
+  int lastWaiting = Polymer.waitingFor.length;
+  int lastAlert;
+  new Timer.periodic(new Duration(seconds: 1), (Timer timer) {
+    var waiting = Polymer.waitingFor;
+    // Done, cancel timer.
+    if (waiting.isEmpty) {
+      timer.cancel();
+      return;
+    }
+    // Made progress, don't alert.
+    if (waiting.length != lastWaiting) {
+      lastWaiting = waiting.length;
+      return;
+    }
+    // Only alert once per waiting state.
+    if (lastAlert == lastWaiting) return;
+    lastAlert = lastWaiting;
+
+    print('No elements registered in a while, but still waiting on '
+        '${waiting.length} elements to be registered. Check that you have a '
+        'class with an @CustomTag annotation for each of the following tags: '
+        '${waiting.map((e) => "'${e.attributes['name']}'").join(', ')}');
+  });
+}
diff --git a/polymer/lib/src/property_accessor.dart b/polymer/lib/src/property_accessor.dart
new file mode 100644
index 0000000..9903d55
--- /dev/null
+++ b/polymer/lib/src/property_accessor.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2013, 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.
+
+/// Code for property accessors from declaration/properties.js
+part of polymer;
+
+// Dart note: this matches the property defined by createPropertyAccessor in
+// polymer-dev/src/declarations/properties.js. Unlike Javascript, we can't
+// override the original property, so we instead ask users to write properties
+// using this pattern:
+//
+//     class Foo extends PolymerElement {
+//       ...
+//       @published
+//       get foo => readValue(#foo);
+//       set foo(v) { writeValue(#foo, v); }
+//
+// and internally readValue/writeValue use an instance of this type to
+// implement the semantics in createPropertyAccessor.
+class _PropertyAccessor<T> {
+  // Name of the property, in order to properly fire change notification events.
+  final Symbol _name;
+
+  /// The underlying value of the property.
+  T _value;
+
+  // Polymer element that contains this property, where change notifications are
+  // expected to be fired from.
+  final Polymer _target;
+
+  /// Non-null when the property is bound.
+  Bindable bindable;
+
+  _PropertyAccessor(this._name, this._target, this._value);
+
+  /// Updates the underlyling value and fires the expected notifications.
+  void updateValue(T newValue) {
+    var oldValue = _value;
+    _value = _target.notifyPropertyChange(_name, oldValue, newValue);
+    _target.emitPropertyChangeRecord(_name, newValue, oldValue);
+  }
+
+  /// The current value of the property. If the property is bound, reading this
+  /// property ensures that the changes are first propagated in order to return
+  /// the latest value. Similarly, when setting this property the binding (if
+  /// any) will be updated too.
+  T get value {
+    if (bindable != null) bindable.deliver();
+    return _value;
+  }
+
+  set value(T newValue) {
+    // Dart Note: The js side makes computed properties read only, and bails
+    // out right here for them (ignoreWrites). For us they are automatically
+    // read only unless you define a setter for them, so we left that out.
+    if (bindable != null) {
+      bindable.value = newValue;
+    } else {
+      updateValue(newValue);
+    }
+  }
+
+  toString() {
+    var name = smoke.symbolToName(_name);
+    var hasBinding = bindable == null ? '(no-binding)' : '(with-binding)';
+    return "[$runtimeType: $_target.$name: $_value $hasBinding]";
+  }
+}
diff --git a/polymer/lib/transformer.dart b/polymer/lib/transformer.dart
new file mode 100644
index 0000000..5e4f3da
--- /dev/null
+++ b/polymer/lib/transformer.dart
@@ -0,0 +1,205 @@
+// Copyright (c) 2013, 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.
+
+/// Transfomer used for pub-serve and pub-deploy.
+library polymer.transformer;
+
+import 'package:barback/barback.dart';
+import 'package:web_components/transformer.dart' as web_components;
+import 'package:observe/transformer.dart';
+
+import 'src/build/build_filter.dart';
+import 'src/build/common.dart';
+import 'src/build/index_page_builder.dart';
+import 'src/build/html_finalizer.dart';
+import 'src/build/linter.dart';
+import 'src/build/build_log_combiner.dart';
+import 'src/build/polyfill_injector.dart';
+import 'src/build/polymer_bootstrap.dart';
+
+/// The Polymer transformer, which internally runs several phases that will:
+///   * Extract inlined script tags into their separate files
+///   * Apply the observable transformer on every Dart script.
+///   * Inline imported html files
+///   * Combine scripts from multiple files into a single script tag
+///   * Inject extra polyfills needed to run on all browsers.
+///
+/// At the end of these phases, this tranformer produces a single entrypoint
+/// HTML file with a single Dart script that can later be compiled with dart2js.
+class PolymerTransformerGroup implements TransformerGroup {
+  final Iterable<Iterable> phases;
+
+  PolymerTransformerGroup(TransformOptions options)
+      : phases = createDeployPhases(options);
+
+  PolymerTransformerGroup.asPlugin(BarbackSettings settings)
+      : this(_parseSettings(settings));
+}
+
+TransformOptions _parseSettings(BarbackSettings settings) {
+  var args = settings.configuration;
+  bool releaseMode = settings.mode == BarbackMode.RELEASE;
+  bool jsOption = args['js'];
+  bool csp = args['csp'] == true; // defaults to false
+  bool injectBuildLogs =
+      !releaseMode && args['inject_build_logs_in_output'] != false;
+  bool injectWebComponentsJs = true;
+  if (args['inject_platform_js'] != null) {
+    print(
+        'Deprecated polymer transformer option `inject_platform_js`. This has '
+        'been renamed `inject_web_components_js` to match the new file name.');
+    injectWebComponentsJs = args['inject_platform_js'] != false;
+  }
+  if (args['inject_webcomponents_js'] != null) {
+    injectWebComponentsJs = args['inject_webcomponents_js'] != false;
+  }
+  return new TransformOptions(
+      entryPoints: readFileList(args['entry_points']),
+      inlineStylesheets: _readInlineStylesheets(args['inline_stylesheets']),
+      directlyIncludeJS: jsOption == null ? releaseMode : jsOption,
+      contentSecurityPolicy: csp,
+      releaseMode: releaseMode,
+      lint: _parseLintOption(args['lint']),
+      injectBuildLogsInOutput: injectBuildLogs,
+      injectWebComponentsJs: injectWebComponentsJs);
+}
+
+// Lint option can be empty (all files), false, true, or a map indicating
+// include/exclude files.
+_parseLintOption(value) {
+  var lint = null;
+  if (value == null || value == true) return new LintOptions();
+  if (value == false) return new LintOptions.disabled();
+  if (value is Map && value.length == 1) {
+    var key = value.keys.single;
+    var files = readFileList(value[key]);
+    if (key == 'include') {
+      return new LintOptions.include(files);
+    } else if (key == 'exclude') {
+      return new LintOptions.exclude(files);
+    }
+  }
+
+  // Any other case it is an error:
+  print('Invalid value for "lint" in the polymer transformer. '
+      'Expected one of the following: \n'
+      '    lint: true  # or\n'
+      '    lint: false # or\n'
+      '    lint: \n'
+      '      include: \n'
+      '        - file1 \n'
+      '        - file2 # or \n'
+      '    lint: \n'
+      '      exclude: \n'
+      '        - file1 \n'
+      '        - file2 \n');
+  return new LintOptions();
+}
+
+readFileList(value) {
+  if (value == null) return null;
+  var files = [];
+  bool error;
+  if (value is List) {
+    files = value;
+    error = value.any((e) => e is! String);
+  } else if (value is String) {
+    files = [value];
+    error = false;
+  } else {
+    error = true;
+  }
+  if (error) {
+    print('Invalid value for "entry_points" in the polymer transformer.');
+  }
+  return files;
+}
+
+Map<String, bool> _readInlineStylesheets(settingValue) {
+  if (settingValue == null) return null;
+  var inlineStylesheets = {};
+  bool error = false;
+  if (settingValue is Map) {
+    settingValue.forEach((key, value) {
+      if (value is! bool || key is! String) {
+        error = true;
+        return;
+      }
+      if (key == 'default') {
+        inlineStylesheets[key] = value;
+        return;
+      }
+
+      key = systemToAssetPath(key);
+      // Special case package urls, convert to AssetId and use serialized form.
+      var packageMatch = _PACKAGE_PATH_REGEX.matchAsPrefix(key);
+      if (packageMatch != null) {
+        var package = packageMatch[1];
+        var path = 'lib/${packageMatch[2]}';
+        key = new AssetId(package, path).toString();
+      }
+      inlineStylesheets[key] = value;
+    });
+  } else if (settingValue is bool) {
+    inlineStylesheets['default'] = settingValue;
+  } else {
+    error = true;
+  }
+  if (error) {
+    print('Invalid value for "inline_stylesheets" in the polymer transformer.');
+  }
+  return inlineStylesheets;
+}
+
+/// Create deploy phases for Polymer. Note that inlining HTML Imports
+/// comes first (other than linter, if [options.linter] is enabled), which
+/// allows the rest of the HTML-processing phases to operate only on HTML that
+/// is actually imported.
+List<List<Transformer>> createDeployPhases(TransformOptions options,
+    {String sdkDir}) {
+  // TODO(sigmund): this should be done differently. We should lint everything
+  // that is reachable and have the option to lint the rest (similar to how
+  // dart2js can analyze reachable code or entire libraries).
+  var phases = [];
+
+  phases.addAll([
+    /// Must happen first, temporarily rewrites <link rel="x-dart-test"> tags to
+    /// <script type="application/dart" _was_test></script> tags.
+    [new web_components.RewriteXDartTestToScript(options.entryPoints)],
+    [new web_components.ScriptCompactorTransformer(options.entryPoints)],
+    [new PolymerBootstrapTransformer(options)],
+  ]);
+
+  // Lint after injecting @HtmlImport imports otherwise we will likely have
+  // incorrect warnings about missing elements.
+  if (options.lint.enabled) phases.add([new Linter(options)]);
+
+  phases.addAll([
+    [
+      new web_components.ImportInlinerTransformer(
+          options.entryPoints, ['[[', '{{'])
+    ],
+    [new HtmlFinalizer(options)],
+    [
+      new ObservableTransformer(
+          releaseMode: options.releaseMode,
+          injectBuildLogsInOutput: options.injectBuildLogsInOutput)
+    ],
+    // TODO(jakemac): Move to web_components.
+    [new PolyfillInjector(options)],
+    [new BuildFilter(options)],
+    [new BuildLogCombiner(options)],
+  ]);
+  if (!options.releaseMode) {
+    phases.add([new IndexPageBuilder(options)]);
+  }
+  /// Must happen last, rewrites
+  /// <script type="application/dart" _was_test></script> tags back to
+  /// <link rel="x-dart-test"> tags.
+  phases.add(
+      [new web_components.RewriteScriptToXDartTest(options.entryPoints)]);
+  return phases;
+}
+
+final RegExp _PACKAGE_PATH_REGEX = new RegExp(r'packages\/([^\/]+)\/(.*)');
diff --git a/polymer/pubspec.yaml b/polymer/pubspec.yaml
new file mode 100644
index 0000000..e865c3d
--- /dev/null
+++ b/polymer/pubspec.yaml
@@ -0,0 +1,87 @@
+name: polymer
+version: 0.16.3+1
+author: Polymer.dart Authors <web-ui-dev@dartlang.org>
+description: >
+  Polymer.dart is a new type of library for the web, built on top of Web
+  Components, and designed to leverage the evolving web platform on modern
+  browsers.
+homepage: https://www.dartlang.org/polymer-dart/
+dependencies:
+  analyzer: '>=0.15.6 <0.26.0'
+  args: '>=0.11.0 <0.14.0'
+  barback: '>=0.14.2 <0.16.0'
+  browser: '>=0.10.0 <0.11.0'
+  code_transformers: '>=0.2.7 <0.3.0'
+  html: '>=0.12.0 <0.13.0'
+  initialize: '>=0.5.1+3 <0.7.0'
+  logging: '>=0.9.2 <0.10.0'
+  path: '>=0.9.0 <2.0.0'
+  polymer_expressions: '>=0.12.0 <0.14.0'
+  polymer_interop: '>=0.1.0+2 <0.2.0'
+  smoke: '>=0.2.0 <0.4.0'
+  source_maps: '>=0.9.4 <0.11.0'
+  source_span: '>=1.0.0 <2.0.0'
+  template_binding: '>=0.12.0 <0.15.0'
+  web_components: '>=0.11.3 <0.12.0'
+  yaml: '>=0.9.0 <3.0.0'
+
+  # Because polymer exports observe, it needs to keep its version constraint
+  # tight to ensure that a constraint on polymer properly constraints all
+  # features it provides.
+  observe: '>=0.13.1 <0.13.2'
+dev_dependencies:
+  unittest: '>=0.10.0 <0.12.0'
+  markdown: '>=0.7.0 <0.8.0'
+transformers:
+- code_transformers/src/delete_file:
+    $include:
+      - lib/src/build/log_injector.css
+      - lib/src/js/polymer/polymer.js
+- observe:
+    files: lib/src/instance.dart
+    $include: lib/src/instance.dart
+
+- polymer:
+    entry_points:
+      - test/attr_deserialize_test.html
+      - test/attr_mustache_test.html
+      - test/auto_binding_test.html
+      - test/bind_mdv_test.html
+      - test/bind_properties_test.html
+      - test/bind_test.html
+      - test/computed_properties_test.html
+      - test/custom_event_test.html
+      - test/entered_view_test.html
+      - test/event_binding_release_handler_test.html
+      - test/event_controller_test.html
+      - test/event_handlers_test.html
+      - test/event_path_declarative_test.html
+      - test/event_path_test.html
+      - test/events_test.html
+      - test/force_ready_test.html
+      - test/import_test.html
+      - test/inject_bound_html_test.html
+      - test/instance_attrs_test.html
+      - test/js_custom_event_test.html
+      - test/js_interop_test.html
+      - test/layout_test.html
+      - test/nested_binding_test.html
+      - test/noscript_test.html
+      - test/prop_attr_bind_reflection_test.html
+      - test/prop_attr_reflection_test.html
+      - test/property_change_test.html
+      - test/property_observe_test.html
+      - test/publish_attributes_test.html
+      - test/publish_inherited_properties_test.html
+      - test/register_test.html
+      - test/sort_registration_test.html
+      - test/take_attributes_test.html
+      - test/template_attr_template_test.html
+      - test/template_distribute_dynamic_test.html
+      - test/two_way_bind_test.html
+      - test/unbind_test.html
+      - test/web_components_less_test.html
+      - test/when_polymer_ready_test.html
+
+environment:
+  sdk: '>=1.4.0 <2.0.0'
diff --git a/polymer_expressions/lib/async.dart b/polymer_expressions/lib/async.dart
new file mode 100644
index 0000000..23e9846
--- /dev/null
+++ b/polymer_expressions/lib/async.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2013, 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 polymer_expressions.async;
+
+import 'dart:async';
+import 'package:observe/observe.dart';
+
+class StreamBinding<T> extends ObservableBox {
+  final Stream<T> stream;
+
+  StreamBinding(this.stream) {
+    stream.listen((T i) { value = i; });
+  }
+
+}
diff --git a/polymer_expressions/lib/eval.dart b/polymer_expressions/lib/eval.dart
new file mode 100644
index 0000000..8f7988b
--- /dev/null
+++ b/polymer_expressions/lib/eval.dart
@@ -0,0 +1,823 @@
+// Copyright (c) 2013, 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 polymer_expressions.eval;
+
+import 'dart:async';
+import 'dart:collection';
+
+import 'package:observe/observe.dart';
+import 'package:smoke/smoke.dart' as smoke;
+
+import 'async.dart';
+import 'expression.dart';
+import 'filter.dart';
+import 'visitor.dart';
+
+final _BINARY_OPERATORS = {
+  '+':   (a, b) => a + b,
+  '-':   (a, b) => a - b,
+  '*':   (a, b) => a * b,
+  '/':   (a, b) => a / b,
+  '%':   (a, b) => a % b,
+  '==':  (a, b) => a == b,
+  '!=':  (a, b) => a != b,
+  '===': (a, b) => identical(a, b),
+  '!==': (a, b) => !identical(a, b),
+  '>':   (a, b) => a > b,
+  '>=':  (a, b) => a >= b,
+  '<':   (a, b) => a < b,
+  '<=':  (a, b) => a <= b,
+  '||':  (a, b) => a || b,
+  '&&':  (a, b) => a && b,
+  '|':   (a, f) {
+    if (f is Transformer) return f.forward(a);
+    if (f is Filter) return f(a);
+    throw new EvalException("Filters must be a one-argument function.");
+  }
+};
+
+final _UNARY_OPERATORS = {
+  '+': (a) => a,
+  '-': (a) => -a,
+  '!': (a) => !a,
+};
+
+final _BOOLEAN_OPERATORS = ['!', '||', '&&'];
+
+/**
+ * Evaluation [expr] in the context of [scope].
+ */
+Object eval(Expression expr, Scope scope) => new EvalVisitor(scope).visit(expr);
+
+/**
+ * Returns an [ExpressionObserver] that evaluates [expr] in the context of
+ * scope] and listens for any changes on [Observable] values that are
+ * returned from sub-expressions. When a value changes the expression is
+ * reevaluated and the new result is sent to the [onUpdate] stream of the
+ * [ExpressionObsserver].
+ */
+ExpressionObserver observe(Expression expr, Scope scope) {
+  var observer = new ObserverBuilder().visit(expr);
+  return observer;
+}
+
+/**
+ * Causes [expr] to be reevaluated a returns it's value.
+ */
+Object update(ExpressionObserver expr, Scope scope, {skipChanges: false}) {
+  new Updater(scope, skipChanges).visit(expr);
+  return expr.currentValue;
+}
+
+/**
+ * Assign [value] to the variable or field referenced by [expr] in the context
+ * of [scope].
+ *
+ * [expr] must be an /assignable/ expression, it must not contain
+ * operators or function invocations, and any index operations must use a
+ * literal index.
+ */
+Object assign(Expression expr, Object value, Scope scope,
+    {bool checkAssignability: true}) {
+
+  Expression expression;
+  var property;
+  bool isIndex = false;
+  var filters = <Expression>[]; // reversed order for assignment
+
+  while (expr is BinaryOperator) {
+    BinaryOperator op = expr;
+    if (op.operator != '|') {
+      break;
+    }
+    filters.add(op.right);
+    expr = op.left;
+  }
+
+  if (expr is Identifier) {
+    expression = empty();
+    property = expr.value;
+  } else if (expr is Index) {
+    expression = expr.receiver;
+    property = expr.argument;
+    isIndex = true;
+  } else if (expr is Getter) {
+    expression = expr.receiver;
+    property = expr.name;
+  } else {
+    if (checkAssignability) {
+      throw new EvalException("Expression is not assignable: $expr");
+    }
+    return null;
+  }
+
+  // transform the values backwards through the filters
+  for (var filterExpr in filters) {
+    var filter = eval(filterExpr, scope);
+    if (filter is! Transformer) {
+      if (checkAssignability) {
+        throw new EvalException("filter must implement Transformer to be "
+            "assignable: $filterExpr");
+      } else {
+        return null;
+      }
+    }
+    value = filter.reverse(value);
+  }
+  // evaluate the receiver
+  var o = eval(expression, scope);
+
+  // can't assign to a property on a null LHS object. Silently fail.
+  if (o == null) return null;
+
+  if (isIndex) {
+    var index = eval(property, scope);
+    o[index] = value;
+  } else {
+    smoke.write(o, smoke.nameToSymbol(property), value);
+  }
+  return value;
+}
+
+
+/**
+ * A scope in polymer expressions that can map names to objects. Scopes contain
+ * a set of named variables and a unique model object. The scope structure
+ * is then used to lookup names using the `[]` operator. The lookup first
+ * searches for the name in local variables, then in global variables,
+ * and then finally looks up the name as a property in the model.
+ */
+abstract class Scope implements Indexable<String, Object> {
+  Scope._();
+
+  /** Create a scope containing a [model] and all of [variables]. */
+  factory Scope({Object model, Map<String, Object> variables}) {
+    var scope = new _ModelScope(model);
+    return variables == null ? scope
+        : new _GlobalsScope(new Map<String, Object>.from(variables), scope);
+  }
+
+  /** Return the unique model in this scope. */
+  Object get model;
+
+  /**
+   * Lookup the value of [name] in the current scope. If [name] is 'this', then
+   * we return the [model]. For any other name, this finds the first variable
+   * matching [name] or, if none exists, the property [name] in the [model].
+   */
+  Object operator [](String name);
+
+  operator []=(String name, Object value) {
+    throw new UnsupportedError('[]= is not supported in Scope.');
+  }
+
+  /**
+   * Returns whether [name] is defined in [model], that is, a lookup
+   * would not find a variable with that name, but there is a non-null model
+   * where we can look it up as a property.
+   */
+  bool _isModelProperty(String name);
+
+  /** Create a new scope extending this scope with an additional variable. */
+  Scope childScope(String name, Object value) =>
+      new _LocalVariableScope(name, value, this);
+}
+
+/**
+ * A scope that looks up names in a model object. This kind of scope has no
+ * parent scope because all our lookup operations stop when we reach the model
+ * object. Any variables added in scope or global variables are added as child
+ * scopes.
+ */
+class _ModelScope extends Scope {
+  final Object model;
+
+  _ModelScope(this.model) : super._();
+
+  Object operator[](String name) {
+    if (name == 'this') return model;
+    var symbol = smoke.nameToSymbol(name);
+    if (model == null || symbol == null) {
+      throw new EvalException("variable '$name' not found");
+    }
+    return _convert(smoke.read(model, symbol));
+  }
+
+  Object _isModelProperty(String name) => name != 'this';
+
+  String toString() => "[model: $model]";
+}
+
+/**
+ * A scope that holds a reference to a single variable. Polymer expressions
+ * introduce variables to the scope one at a time. Each time a variable is
+ * added, a new [_LocalVariableScope] is created.
+ */
+class _LocalVariableScope extends Scope {
+  final Scope parent;
+  final String varName;
+  // TODO(sigmund,justinfagnani): make this @observable?
+  final Object value;
+
+  _LocalVariableScope(this.varName, this.value, this.parent) : super._() {
+    if (varName == 'this') {
+      throw new EvalException("'this' cannot be used as a variable name.");
+    }
+  }
+
+  Object get model => parent != null ? parent.model : null;
+
+  Object operator[](String name) {
+    if (varName == name) return _convert(value);
+    if (parent != null) return parent[name];
+    throw new EvalException("variable '$name' not found");
+  }
+
+  bool _isModelProperty(String name) {
+    if (varName == name) return false;
+    return parent == null ? false : parent._isModelProperty(name);
+  }
+
+  String toString() => "$parent > [local: $varName]";
+}
+
+/** A scope that holds a reference to a global variables. */
+class _GlobalsScope extends Scope {
+  final _ModelScope parent;
+  final Map<String, Object> variables;
+
+  _GlobalsScope(this.variables, this.parent) : super._() {
+    if (variables.containsKey('this')) {
+      throw new EvalException("'this' cannot be used as a variable name.");
+    }
+  }
+
+  Object get model => parent != null ? parent.model : null;
+
+  Object operator[](String name) {
+    if (variables.containsKey(name)) return _convert(variables[name]);
+    if (parent != null) return parent[name];
+    throw new EvalException("variable '$name' not found");
+  }
+
+  bool _isModelProperty(String name) {
+    if (variables.containsKey(name)) return false;
+    return parent == null ? false : parent._isModelProperty(name);
+  }
+
+  String toString() => "$parent > [global: ${variables.keys}]";
+}
+
+Object _convert(v) => v is Stream ? new StreamBinding(v) : v;
+
+abstract class ExpressionObserver<E extends Expression> implements Expression {
+  final E _expr;
+  ExpressionObserver _parent;
+
+  StreamSubscription _subscription;
+  Object _value;
+
+  StreamController _controller = new StreamController.broadcast();
+  Stream get onUpdate => _controller.stream;
+
+  ExpressionObserver(this._expr);
+
+  Expression get expression => _expr;
+
+  Object get currentValue => _value;
+
+  update(Scope scope) => _updateSelf(scope);
+
+  _updateSelf(Scope scope) {}
+
+  _invalidate(Scope scope) {
+    _observe(scope, false);
+    if (_parent != null) {
+      _parent._invalidate(scope);
+    }
+  }
+
+  _unobserve() {
+    if (_subscription != null) {
+      _subscription.cancel();
+      _subscription = null;
+    }
+  }
+
+  _observe(Scope scope, skipChanges) {
+    _unobserve();
+
+    var _oldValue = _value;
+
+    // evaluate
+    _updateSelf(scope);
+
+    if (!skipChanges && !identical(_value, _oldValue)) {
+      _controller.add(_value);
+    }
+  }
+
+  String toString() => _expr.toString();
+}
+
+class Updater extends RecursiveVisitor {
+  final Scope scope;
+  final bool skipChanges;
+
+  Updater(this.scope, [this.skipChanges = false]);
+
+  visitExpression(ExpressionObserver e) {
+    e._observe(scope, skipChanges);
+  }
+}
+
+class Closer extends RecursiveVisitor {
+  static final _instance = new Closer._();
+  factory Closer() => _instance;
+  Closer._();
+
+  visitExpression(ExpressionObserver e) {
+    e._unobserve();
+  }
+}
+
+class EvalVisitor extends Visitor {
+  final Scope scope;
+
+  EvalVisitor(this.scope);
+
+  visitEmptyExpression(EmptyExpression e) => scope.model;
+
+  visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child);
+
+  visitGetter(Getter g) {
+    var receiver = visit(g.receiver);
+    if (receiver == null) return null;
+    var symbol = smoke.nameToSymbol(g.name);
+    return smoke.read(receiver, symbol);
+  }
+
+  visitIndex(Index i) {
+    var receiver = visit(i.receiver);
+    if (receiver == null) return null;
+    var key = visit(i.argument);
+    return receiver[key];
+  }
+
+  visitInvoke(Invoke i) {
+    var receiver = visit(i.receiver);
+    if (receiver == null) return null;
+    var args = (i.arguments == null)
+        ? null
+        : i.arguments.map(visit).toList(growable: false);
+
+    if (i.method == null) {
+      assert(receiver is Function);
+      return Function.apply(receiver, args);
+    }
+
+    var symbol = smoke.nameToSymbol(i.method);
+    return smoke.invoke(receiver, symbol, args);
+  }
+
+  visitLiteral(Literal l) => l.value;
+
+  visitListLiteral(ListLiteral l) => l.items.map(visit).toList();
+
+  visitMapLiteral(MapLiteral l) {
+    var map = {};
+    for (var entry in l.entries) {
+      var key = visit(entry.key);
+      var value = visit(entry.entryValue);
+      map[key] = value;
+    }
+    return map;
+  }
+
+  visitMapLiteralEntry(MapLiteralEntry e) =>
+      throw new UnsupportedError("should never be called");
+
+  visitIdentifier(Identifier i) => scope[i.value];
+
+  visitBinaryOperator(BinaryOperator o) {
+    var operator = o.operator;
+    var left = visit(o.left);
+    var right = visit(o.right);
+
+    var f = _BINARY_OPERATORS[operator];
+    if (operator == '&&' || operator == '||') {
+      // TODO: short-circuit
+      return f(_toBool(left), _toBool(right));
+    } else if (operator == '==' || operator == '!=') {
+      return f(left, right);
+    } else if (left == null || right == null) {
+      return null;
+    }
+    return f(left, right);
+  }
+
+  visitUnaryOperator(UnaryOperator o) {
+    var expr = visit(o.child);
+    var f = _UNARY_OPERATORS[o.operator];
+    if (o.operator == '!') {
+      return f(_toBool(expr));
+    }
+    return (expr == null) ? null : f(expr);
+  }
+
+  visitTernaryOperator(TernaryOperator o) =>
+      visit(o.condition) == true ? visit(o.trueExpr) : visit(o.falseExpr);
+
+  visitInExpression(InExpression i) =>
+      throw new UnsupportedError("can't eval an 'in' expression");
+
+  visitAsExpression(AsExpression i) =>
+      throw new UnsupportedError("can't eval an 'as' expression");
+}
+
+class ObserverBuilder extends Visitor {
+  final Queue parents = new Queue();
+
+  ObserverBuilder();
+
+  visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e);
+
+  visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child);
+
+  visitGetter(Getter g) {
+    var receiver = visit(g.receiver);
+    var getter = new GetterObserver(g, receiver);
+    receiver._parent = getter;
+    return getter;
+  }
+
+  visitIndex(Index i) {
+    var receiver = visit(i.receiver);
+    var arg = visit(i.argument);
+    var index =  new IndexObserver(i, receiver, arg);
+    receiver._parent = index;
+    arg._parent = index;
+    return index;
+  }
+
+  visitInvoke(Invoke i) {
+    var receiver = visit(i.receiver);
+    var args = (i.arguments == null)
+        ? null
+        : i.arguments.map(visit).toList(growable: false);
+    var invoke =  new InvokeObserver(i, receiver, args);
+    receiver._parent = invoke;
+    if (args != null) args.forEach((a) => a._parent = invoke);
+    return invoke;
+  }
+
+  visitLiteral(Literal l) => new LiteralObserver(l);
+
+  visitListLiteral(ListLiteral l) {
+    var items = l.items.map(visit).toList(growable: false);
+    var list = new ListLiteralObserver(l, items);
+    items.forEach((e) => e._parent = list);
+    return list;
+  }
+
+  visitMapLiteral(MapLiteral l) {
+    var entries = l.entries.map(visit).toList(growable: false);
+    var map = new MapLiteralObserver(l, entries);
+    entries.forEach((e) => e._parent = map);
+    return map;
+  }
+
+  visitMapLiteralEntry(MapLiteralEntry e) {
+    var key = visit(e.key);
+    var value = visit(e.entryValue);
+    var entry = new MapLiteralEntryObserver(e, key, value);
+    key._parent = entry;
+    value._parent = entry;
+    return entry;
+  }
+
+  visitIdentifier(Identifier i) => new IdentifierObserver(i);
+
+  visitBinaryOperator(BinaryOperator o) {
+    var left = visit(o.left);
+    var right = visit(o.right);
+    var binary = new BinaryObserver(o, left, right);
+    left._parent = binary;
+    right._parent = binary;
+    return binary;
+  }
+
+  visitUnaryOperator(UnaryOperator o) {
+    var expr = visit(o.child);
+    var unary = new UnaryObserver(o, expr);
+    expr._parent = unary;
+    return unary;
+  }
+
+  visitTernaryOperator(TernaryOperator o) {
+    var condition = visit(o.condition);
+    var trueExpr = visit(o.trueExpr);
+    var falseExpr = visit(o.falseExpr);
+    var ternary = new TernaryObserver(o, condition, trueExpr, falseExpr);
+    condition._parent = ternary;
+    trueExpr._parent = ternary;
+    falseExpr._parent = ternary;
+    return ternary;
+  }
+
+  visitInExpression(InExpression i) {
+    throw new UnsupportedError("can't eval an 'in' expression");
+  }
+
+  visitAsExpression(AsExpression i) {
+    throw new UnsupportedError("can't eval an 'as' expression");
+  }
+}
+
+class EmptyObserver extends ExpressionObserver<EmptyExpression>
+    implements EmptyExpression {
+
+  EmptyObserver(EmptyExpression value) : super(value);
+
+  _updateSelf(Scope scope) {
+    _value = scope.model;
+    // TODO(justin): listen for scope.model changes?
+  }
+
+  accept(Visitor v) => v.visitEmptyExpression(this);
+}
+
+class LiteralObserver extends ExpressionObserver<Literal> implements Literal {
+
+  LiteralObserver(Literal value) : super(value);
+
+  dynamic get value => _expr.value;
+
+  _updateSelf(Scope scope) {
+    _value = _expr.value;
+  }
+
+  accept(Visitor v) => v.visitLiteral(this);
+}
+
+class ListLiteralObserver extends ExpressionObserver<ListLiteral>
+    implements ListLiteral {
+
+  final List<ExpressionObserver> items;
+
+  ListLiteralObserver(ListLiteral value, this.items) : super(value);
+
+  _updateSelf(Scope scope) {
+    _value = items.map((i) => i._value).toList();
+  }
+
+  accept(Visitor v) => v.visitListLiteral(this);
+}
+
+class MapLiteralObserver extends ExpressionObserver<MapLiteral>
+    implements MapLiteral {
+
+  final List<MapLiteralEntryObserver> entries;
+
+  MapLiteralObserver(MapLiteral value, this.entries) : super(value);
+
+  _updateSelf(Scope scope) {
+    _value = entries.fold(new Map(),
+        (m, e) => m..[e.key._value] = e.entryValue._value);
+  }
+
+  accept(Visitor v) => v.visitMapLiteral(this);
+}
+
+class MapLiteralEntryObserver extends ExpressionObserver<MapLiteralEntry>
+    implements MapLiteralEntry {
+
+  final LiteralObserver key;
+  final ExpressionObserver entryValue;
+
+  MapLiteralEntryObserver(MapLiteralEntry value, this.key, this.entryValue)
+      : super(value);
+
+  accept(Visitor v) => v.visitMapLiteralEntry(this);
+}
+
+class IdentifierObserver extends ExpressionObserver<Identifier>
+    implements Identifier {
+
+  IdentifierObserver(Identifier value) : super(value);
+
+  String get value => _expr.value;
+
+  _updateSelf(Scope scope) {
+    _value = scope[value];
+    if (!scope._isModelProperty(value)) return;
+    var model = scope.model;
+    if (model is! Observable) return;
+    var symbol = smoke.nameToSymbol(value);
+    _subscription = (model as Observable).changes.listen((changes) {
+      if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) {
+        _invalidate(scope);
+      }
+    });
+  }
+
+  accept(Visitor v) => v.visitIdentifier(this);
+}
+
+class ParenthesizedObserver extends ExpressionObserver<ParenthesizedExpression>
+    implements ParenthesizedExpression {
+  final ExpressionObserver child;
+
+  ParenthesizedObserver(ParenthesizedExpression expr, this.child) : super(expr);
+
+
+  _updateSelf(Scope scope) {
+    _value = child._value;
+  }
+
+  accept(Visitor v) => v.visitParenthesizedExpression(this);
+}
+
+class UnaryObserver extends ExpressionObserver<UnaryOperator>
+    implements UnaryOperator {
+  final ExpressionObserver child;
+
+  UnaryObserver(UnaryOperator expr, this.child) : super(expr);
+
+  String get operator => _expr.operator;
+
+  _updateSelf(Scope scope) {
+    var f = _UNARY_OPERATORS[_expr.operator];
+    if (operator == '!') {
+      _value = f(_toBool(child._value));
+    } else {
+      _value = (child._value == null) ? null : f(child._value);
+    }
+  }
+
+  accept(Visitor v) => v.visitUnaryOperator(this);
+}
+
+class BinaryObserver extends ExpressionObserver<BinaryOperator>
+    implements BinaryOperator {
+
+  final ExpressionObserver left;
+  final ExpressionObserver right;
+
+  BinaryObserver(BinaryOperator expr, this.left, this.right)
+      : super(expr);
+
+  String get operator => _expr.operator;
+
+  _updateSelf(Scope scope) {
+    var f = _BINARY_OPERATORS[operator];
+    if (operator == '&&' || operator == '||') {
+      _value = f(_toBool(left._value), _toBool(right._value));
+    } else if (operator == '==' || operator == '!=') {
+      _value = f(left._value, right._value);
+    } else if (left._value == null || right._value == null) {
+      _value = null;
+    } else {
+      if (operator == '|' && left._value is ObservableList) {
+        _subscription = (left._value as ObservableList).listChanges
+            .listen((_) => _invalidate(scope));
+      }
+      _value = f(left._value, right._value);
+    }
+  }
+
+  accept(Visitor v) => v.visitBinaryOperator(this);
+
+}
+
+class TernaryObserver extends ExpressionObserver<TernaryOperator>
+    implements TernaryOperator {
+
+  final ExpressionObserver condition;
+  final ExpressionObserver trueExpr;
+  final ExpressionObserver falseExpr;
+
+  TernaryObserver(TernaryOperator expr, this.condition, this.trueExpr,
+      this.falseExpr) : super(expr);
+
+  _updateSelf(Scope scope) {
+    _value = _toBool(condition._value) ? trueExpr._value : falseExpr._value;
+  }
+
+  accept(Visitor v) => v.visitTernaryOperator(this);
+
+}
+
+class GetterObserver extends ExpressionObserver<Getter> implements Getter {
+  final ExpressionObserver receiver;
+
+  GetterObserver(Expression expr, this.receiver) : super(expr);
+
+  String get name => _expr.name;
+
+  _updateSelf(Scope scope) {
+    var receiverValue = receiver._value;
+    if (receiverValue == null) {
+      _value = null;
+      return;
+    }
+    var symbol = smoke.nameToSymbol(_expr.name);
+    _value = smoke.read(receiverValue, symbol);
+
+    if (receiverValue is Observable) {
+      _subscription = (receiverValue as Observable).changes.listen((changes) {
+        if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) {
+          _invalidate(scope);
+        }
+      });
+    }
+  }
+
+  accept(Visitor v) => v.visitGetter(this);
+}
+
+class IndexObserver extends ExpressionObserver<Index> implements Index {
+  final ExpressionObserver receiver;
+  final ExpressionObserver argument;
+
+  IndexObserver(Expression expr, this.receiver, this.argument) : super(expr);
+
+  _updateSelf(Scope scope) {
+    var receiverValue = receiver._value;
+    if (receiverValue == null) {
+      _value = null;
+      return;
+    }
+    var key = argument._value;
+    _value = receiverValue[key];
+
+    if (receiverValue is ObservableList) {
+      _subscription = (receiverValue as ObservableList).listChanges
+          .listen((changes) {
+        if (changes.any((c) => c.indexChanged(key))) _invalidate(scope);
+      });
+    } else if (receiverValue is Observable) {
+      _subscription = (receiverValue as Observable).changes.listen((changes) {
+        if (changes.any((c) => c is MapChangeRecord && c.key == key)) {
+          _invalidate(scope);
+        }
+      });
+    }
+  }
+
+  accept(Visitor v) => v.visitIndex(this);
+}
+
+class InvokeObserver extends ExpressionObserver<Invoke> implements Invoke {
+  final ExpressionObserver receiver;
+  final List<ExpressionObserver> arguments;
+
+  InvokeObserver(Expression expr, this.receiver, this.arguments)
+      : super(expr) {
+    assert(arguments != null);
+  }
+
+  String get method => _expr.method;
+
+  _updateSelf(Scope scope) {
+    var args = arguments.map((a) => a._value).toList();
+    var receiverValue = receiver._value;
+    if (receiverValue == null) {
+      _value = null;
+      return;
+    }
+    if (_expr.method == null) {
+      // top-level function or model method
+      // TODO(justin): listen to model changes to see if the method has
+      // changed? listen to the scope to see if the top-level method has
+      // changed?
+      assert(receiverValue is Function);
+      _value = _convert(Function.apply(receiverValue, args));
+    } else {
+      var symbol = smoke.nameToSymbol(_expr.method);
+      _value = smoke.invoke(receiverValue, symbol, args);
+
+      if (receiverValue is Observable) {
+        _subscription = (receiverValue as Observable).changes.listen(
+            (List<ChangeRecord> changes) {
+              if (changes.any(
+                  (c) => c is PropertyChangeRecord && c.name == symbol)) {
+                _invalidate(scope);
+              }
+            });
+      }
+    }
+  }
+
+  accept(Visitor v) => v.visitInvoke(this);
+}
+
+_toBool(v) => (v == null) ? false : v;
+
+class EvalException implements Exception {
+  final String message;
+  EvalException(this.message);
+  String toString() => "EvalException: $message";
+}
diff --git a/polymer_expressions/lib/expression.dart b/polymer_expressions/lib/expression.dart
new file mode 100644
index 0000000..ac36c8b
--- /dev/null
+++ b/polymer_expressions/lib/expression.dart
@@ -0,0 +1,368 @@
+// Copyright (c) 2013, 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 polymer_expressions.expression;
+
+import 'visitor.dart';
+
+// Helper functions for building expression trees programmatically
+
+EmptyExpression empty() => const EmptyExpression();
+Literal literal(v) => new Literal(v);
+ListLiteral listLiteral(List<Expression> items) => new ListLiteral(items);
+MapLiteral mapLiteral(List<MapLiteralEntry> entries) => new MapLiteral(entries);
+MapLiteralEntry mapLiteralEntry(Literal key, Expression value) =>
+    new MapLiteralEntry(key, value);
+Identifier ident(String v) => new Identifier(v);
+ParenthesizedExpression paren(Expression e) => new ParenthesizedExpression(e);
+UnaryOperator unary(String op, Expression e) => new UnaryOperator(op, e);
+BinaryOperator binary(Expression l, String op, Expression r) =>
+    new BinaryOperator(l, op, r);
+Getter getter(Expression e, String m) => new Getter(e, m);
+Index index(Expression e, Expression a) => new Index(e, a);
+Invoke invoke(Expression e, String m, List<Expression> a) =>
+    new Invoke(e, m, a);
+InExpression inExpr(Expression l, Expression r) => new InExpression(l, r);
+AsExpression asExpr(Expression l, Expression r) => new AsExpression(l, r);
+TernaryOperator ternary(Expression c, Expression t, Expression f) =>
+    new TernaryOperator(c, t, f);
+
+class AstFactory {
+  EmptyExpression empty() => const EmptyExpression();
+
+  Literal literal(v) => new Literal(v);
+
+  MapLiteral mapLiteral(List<MapLiteralEntry> entries) =>
+      new MapLiteral(entries);
+
+  MapLiteralEntry mapLiteralEntry(Literal key, Expression value) =>
+      new MapLiteralEntry(key, value);
+
+  Identifier identifier(String v) => new Identifier(v);
+
+  ParenthesizedExpression parenthesized(Expression e) =>
+      new ParenthesizedExpression(e);
+
+  UnaryOperator unary(String op, Expression e) => new UnaryOperator(op, e);
+
+  BinaryOperator binary(Expression l, String op, Expression r) =>
+      new BinaryOperator(l, op, r);
+
+  TernaryOperator ternary(Expression c, Expression t, Expression f) =>
+      new TernaryOperator(c, t, f);
+
+  Getter getter(Expression g, String n) => new Getter(g, n);
+
+  Index index(Expression e, Expression a) => new Index(e, a);
+
+  Invoke invoke(Expression e, String m, List<Expression> a) =>
+      new Invoke(e, m, a);
+
+  InExpression inExpr(Expression l, Expression r) => new InExpression(l, r);
+
+  AsExpression asExpr(Expression l, Expression r) => new AsExpression(l, r);
+}
+
+/// Base class for all expressions
+abstract class Expression {
+  const Expression();
+  accept(Visitor v);
+}
+
+abstract class HasIdentifier {
+  String get identifier;
+  Expression get expr;
+}
+
+class EmptyExpression extends Expression {
+  const EmptyExpression();
+  accept(Visitor v) => v.visitEmptyExpression(this);
+}
+
+class Literal<T> extends Expression {
+  final T value;
+
+  Literal(this.value);
+
+  accept(Visitor v) => v.visitLiteral(this);
+
+  String toString() => (value is String) ? '"$value"' : '$value';
+
+  bool operator ==(o) => o is Literal<T> && o.value == value;
+
+  int get hashCode => value.hashCode;
+}
+
+class ListLiteral extends Expression {
+  final List<Expression> items;
+
+  ListLiteral(this.items);
+
+  accept(Visitor v) => v.visitListLiteral(this);
+
+  String toString() => "$items";
+
+  bool operator ==(o) => o is ListLiteral && _listEquals(o.items, items);
+
+  int get hashCode => _hashList(items);
+}
+
+class MapLiteral extends Expression {
+  final List<MapLiteralEntry> entries;
+
+  MapLiteral(this.entries);
+
+  accept(Visitor v) => v.visitMapLiteral(this);
+
+  String toString() => "{$entries}";
+
+  bool operator ==(o) => o is MapLiteral && _listEquals(o.entries, entries);
+
+  int get hashCode => _hashList(entries);
+}
+
+class MapLiteralEntry extends Expression {
+  final Literal key;
+  final Expression entryValue;
+
+  MapLiteralEntry(this.key, this.entryValue);
+
+  accept(Visitor v) => v.visitMapLiteralEntry(this);
+
+  String toString() => "$key: $entryValue";
+
+  bool operator ==(o) => o is MapLiteralEntry && o.key == key
+      && o.entryValue == entryValue;
+
+  int get hashCode => _JenkinsSmiHash.hash2(key.hashCode, entryValue.hashCode);
+}
+
+class ParenthesizedExpression extends Expression {
+  final Expression child;
+
+  ParenthesizedExpression(this.child);
+
+  accept(Visitor v) => v.visitParenthesizedExpression(this);
+
+  String toString() => '($child)';
+
+  bool operator ==(o) => o is ParenthesizedExpression && o.child == child;
+
+  int get hashCode => child.hashCode;
+}
+
+class Identifier extends Expression {
+  final String value;
+
+  Identifier(this.value);
+
+  accept(Visitor v) => v.visitIdentifier(this);
+
+  String toString() => value;
+
+  bool operator ==(o) => o is Identifier && o.value == value;
+
+  int get hashCode => value.hashCode;
+}
+
+class UnaryOperator extends Expression {
+  final String operator;
+  final Expression child;
+
+  UnaryOperator(this.operator, this.child);
+
+  accept(Visitor v) => v.visitUnaryOperator(this);
+
+  String toString() => '$operator $child';
+
+  bool operator ==(o) => o is UnaryOperator && o.operator == operator
+      && o.child == child;
+
+  int get hashCode => _JenkinsSmiHash.hash2(operator.hashCode, child.hashCode);
+}
+
+class BinaryOperator extends Expression {
+  final String operator;
+  final Expression left;
+  final Expression right;
+
+  BinaryOperator(this.left, this.operator, this.right);
+
+  accept(Visitor v) => v.visitBinaryOperator(this);
+
+  String toString() => '($left $operator $right)';
+
+  bool operator ==(o) => o is BinaryOperator && o.operator == operator
+      && o.left == left && o.right == right;
+
+  int get hashCode => _JenkinsSmiHash.hash3(operator.hashCode, left.hashCode,
+      right.hashCode);
+}
+
+class TernaryOperator extends Expression {
+  final Expression condition;
+  final Expression trueExpr;
+  final Expression falseExpr;
+
+  TernaryOperator(this.condition, this.trueExpr, this.falseExpr);
+
+  accept(Visitor v) => v.visitTernaryOperator(this);
+
+  String toString() => '($condition ? $trueExpr : $falseExpr)';
+
+  bool operator ==(o) => o is TernaryOperator
+      && o.condition == condition
+      && o.trueExpr == trueExpr
+      && o.falseExpr == falseExpr;
+
+  int get hashCode => _JenkinsSmiHash.hash3(condition.hashCode,
+      trueExpr.hashCode, falseExpr.hashCode);
+}
+
+class InExpression extends Expression implements HasIdentifier {
+  final Identifier left;
+  final Expression right;
+
+  InExpression(this.left, this.right);
+
+  accept(Visitor v) => v.visitInExpression(this);
+
+  String get identifier => left.value;
+
+  Expression get expr => right;
+
+  String toString() => '($left in $right)';
+
+  bool operator ==(o) => o is InExpression && o.left == left
+      && o.right == right;
+
+  int get hashCode => _JenkinsSmiHash.hash2(left.hashCode, right.hashCode);
+}
+
+class AsExpression extends Expression implements HasIdentifier {
+  final Expression left;
+  final Identifier right;
+
+  AsExpression(this.left, this.right);
+
+  accept(Visitor v) => v.visitAsExpression(this);
+
+  String get identifier => right.value;
+
+  Expression get expr => left;
+
+  String toString() => '($left as $right)';
+
+  bool operator ==(o) => o is AsExpression && o.left == left
+      && o.right == right;
+
+  int get hashCode => _JenkinsSmiHash.hash2(left.hashCode, right.hashCode);
+}
+
+class Index extends Expression {
+  final Expression receiver;
+  final Expression argument;
+
+  Index(this.receiver, this.argument);
+
+  accept(Visitor v) => v.visitIndex(this);
+
+  String toString() => '$receiver[$argument]';
+
+  bool operator ==(o) =>
+      o is Index
+      && o.receiver == receiver
+      && o.argument == argument;
+
+  int get hashCode =>
+      _JenkinsSmiHash.hash2(receiver.hashCode, argument.hashCode);
+}
+
+class Getter extends Expression {
+  final Expression receiver;
+  final String name;
+
+  Getter(this.receiver, this.name);
+
+  accept(Visitor v) => v.visitGetter(this);
+
+  String toString() => '$receiver.$name';
+
+  bool operator ==(o) =>
+      o is Getter
+      && o.receiver == receiver
+      && o.name == name;
+
+  int get hashCode => _JenkinsSmiHash.hash2(receiver.hashCode, name.hashCode);
+
+}
+
+/**
+ * Represents a function or method invocation. If [method] is null, then
+ * [receiver] is an expression that should evaluate to a function. If [method]
+ * is not null, then [receiver] is an expression that should evaluate to an
+ * object that has an appropriate method.
+ */
+class Invoke extends Expression {
+  final Expression receiver;
+  final String method;
+  final List<Expression> arguments;
+
+  Invoke(this.receiver, this.method, this.arguments) {
+    assert(arguments != null);
+  }
+
+  accept(Visitor v) => v.visitInvoke(this);
+
+  String toString() => '$receiver.$method($arguments)';
+
+  bool operator ==(o) =>
+      o is Invoke
+      && o.receiver == receiver
+      && o.method == method
+      && _listEquals(o.arguments, arguments);
+
+  int get hashCode => _JenkinsSmiHash.hash3(receiver.hashCode, method.hashCode,
+      _hashList(arguments));
+}
+
+bool _listEquals(List a, List b) {
+  if (a == b) return true;
+  if (a == null || b == null) return false;
+  if (a.length != b.length) return false;
+  for (int i = 0; i < a.length; i++) {
+    if (a[i] != b[i]) return false;
+  }
+  return true;
+}
+
+int _hashList(List l) {
+  var hash = l.fold(0,
+      (h, item) => _JenkinsSmiHash.combine(h, item.hashCode));
+  return _JenkinsSmiHash.finish(hash);
+}
+
+class _JenkinsSmiHash {
+  // TODO: Bug 11617- This class should be optimized and standardized elsewhere.
+
+  static int combine(int hash, int value) {
+    hash = 0x1fffffff & (hash + value);
+    hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
+    return hash ^ (hash >> 6);
+  }
+
+  static int finish(int hash) {
+    hash = 0x1fffffff & (hash + ((0x03ffffff & hash) <<  3));
+    hash = hash ^ (hash >> 11);
+    return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
+  }
+
+  static int hash2(int a, int b) => finish(combine(combine(0, a), b));
+
+  static int hash3(int a, int b, int c) =>
+      finish(combine(combine(combine(0, a), b), c));
+
+  static int hash4(int a, int b, int c, int d) =>
+      finish(combine(combine(combine(combine(0, a), b), c), d));
+}
diff --git a/polymer_expressions/lib/filter.dart b/polymer_expressions/lib/filter.dart
new file mode 100644
index 0000000..c972584
--- /dev/null
+++ b/polymer_expressions/lib/filter.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2013, 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 polymer_expressions.filter;
+
+typedef Object Filter(Object value);
+
+abstract class Transformer<T, V> {
+
+  T forward(V v);
+  V reverse(T t);
+  Transformer<V, T> get inverse => new _InverseTransformer/*<V, T>*/(this);
+}
+
+// TODO(jmesserly): restore types when Issue 14094 is fixed.
+class _InverseTransformer/*<V, T>*/ implements Transformer/*<V, T>*/ {
+  final Transformer/*<T, V>*/ _t;
+  _InverseTransformer(this._t);
+
+  /*V*/ forward(/*T*/ v) => _t.reverse(v);
+  /*T*/ reverse(/*V*/ t) => _t.forward(t);
+  Transformer/*<T, V>*/ get inverse => _t;
+}
diff --git a/polymer_expressions/lib/parser.dart b/polymer_expressions/lib/parser.dart
new file mode 100644
index 0000000..7ab9b4c
--- /dev/null
+++ b/polymer_expressions/lib/parser.dart
@@ -0,0 +1,324 @@
+// Copyright (c) 2013, 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 polymer_expressions.parser;
+
+import 'tokenizer.dart';
+export 'tokenizer.dart' show ParseException;
+import 'expression.dart';
+
+const _UNARY_OPERATORS = const <String>['+', '-', '!'];
+const _BINARY_OPERATORS = const <String>['+', '-', '*', '/', '%', '^', '==',
+    '!=', '>', '<', '>=', '<=', '||', '&&', '&', '===', '!==', '|'];
+
+Expression parse(String expr) => new Parser(expr).parse();
+
+class Parser {
+  final AstFactory _astFactory;
+  final Tokenizer _tokenizer;
+  List<Token> _tokens;
+  Iterator _iterator;
+  Token get _token => _iterator.current;
+
+  Parser(String input, {AstFactory astFactory})
+      : _tokenizer = new Tokenizer(input),
+        _astFactory = (astFactory == null) ? new AstFactory() : astFactory;
+
+  Expression parse() {
+    _tokens = _tokenizer.tokenize();
+    _iterator = _tokens.iterator;
+    _advance();
+    return _parseExpression();
+  }
+
+  _advance([int kind, String value]) {
+    if ((kind != null && (_token == null || _token.kind != kind))
+        || (value != null && (_token == null || _token.value != value))) {
+      throw new ParseException("Expected kind $kind ($value): $_token");
+    }
+    _iterator.moveNext();
+  }
+
+  Expression _parseExpression() {
+    if (_token == null) return _astFactory.empty();
+    var expr = _parseUnary();
+    return (expr == null) ? null : _parsePrecedence(expr, 0);
+  }
+
+  // _parsePrecedence and _parseBinary implement the precedence climbing
+  // algorithm as described in:
+  // http://en.wikipedia.org/wiki/Operator-precedence_parser#Precedence_climbing_method
+  Expression _parsePrecedence(Expression left, int precedence) {
+    assert(left != null);
+    while (_token != null) {
+      if (_token.kind == GROUPER_TOKEN) {
+        if (_token.value == '(') {
+          var args = _parseArguments();
+          assert(args != null);
+          left = _astFactory.invoke(left, null, args);
+        } else if (_token.value == '[') {
+          var indexExpr = _parseIndex();
+          left = _astFactory.index(left, indexExpr);
+        } else {
+          break;
+        }
+      } else if (_token.kind == DOT_TOKEN) {
+        _advance();
+        var right = _parseUnary();
+        left = _makeInvokeOrGetter(left, right);
+      } else if (_token.kind == KEYWORD_TOKEN) {
+        if (_token.value == 'in') {
+          left = _parseInExpression(left);
+        } else if (_token.value == 'as') {
+          left = _parseAsExpression(left);
+        } else {
+          break;
+        }
+      } else if (_token.kind == OPERATOR_TOKEN
+          && _token.precedence >= precedence) {
+        left = _token.value == '?' ? _parseTernary(left) : _parseBinary(left);
+      } else {
+        break;
+      }
+    }
+    return left;
+  }
+
+  // invoke or getter
+  Expression _makeInvokeOrGetter(left, right) {
+    if (right is Identifier) {
+      return _astFactory.getter(left, right.value);
+    } else if (right is Invoke && right.receiver is Identifier) {
+      Identifier method = right.receiver;
+      return _astFactory.invoke(left, method.value, right.arguments);
+    } else {
+      throw new ParseException("expected identifier: $right");
+    }
+  }
+
+  Expression _parseBinary(left) {
+    var op = _token;
+    if (!_BINARY_OPERATORS.contains(op.value)) {
+      throw new ParseException("unknown operator: ${op.value}");
+    }
+    _advance();
+    var right = _parseUnary();
+    while (_token != null
+        && (_token.kind == OPERATOR_TOKEN
+        || _token.kind == DOT_TOKEN
+        || _token.kind == GROUPER_TOKEN)
+        && _token.precedence > op.precedence) {
+      right = _parsePrecedence(right, _token.precedence);
+    }
+    return _astFactory.binary(left, op.value, right);
+  }
+
+  Expression _parseUnary() {
+    if (_token.kind == OPERATOR_TOKEN) {
+      var value = _token.value;
+      if (value == '+' || value == '-') {
+        _advance();
+        if (_token.kind == INTEGER_TOKEN) {
+          return _parseInteger(value);
+        } else if (_token.kind == DECIMAL_TOKEN) {
+          return _parseDecimal(value);
+        } else {
+          var expr = _parsePrecedence(_parsePrimary(), POSTFIX_PRECEDENCE);
+          return _astFactory.unary(value, expr);
+        }
+      } else if (value == '!') {
+        _advance();
+        var expr = _parsePrecedence(_parsePrimary(), POSTFIX_PRECEDENCE);
+        return _astFactory.unary(value, expr);
+      } else {
+        throw new ParseException("unexpected token: $value");
+      }
+    }
+    return _parsePrimary();
+  }
+
+  Expression _parseTernary(condition) {
+    _advance(OPERATOR_TOKEN, '?');
+    var trueExpr = _parseExpression();
+    _advance(COLON_TOKEN);
+    var falseExpr = _parseExpression();
+    return _astFactory.ternary(condition, trueExpr, falseExpr);
+  }
+
+  Expression _parsePrimary() {
+    var kind = _token.kind;
+    switch (kind) {
+      case KEYWORD_TOKEN:
+        var keyword = _token.value;
+        if (keyword == 'this') {
+          _advance();
+          // TODO(justin): return keyword node
+          return _astFactory.identifier('this');
+        } else if (KEYWORDS.contains(keyword)) {
+          throw new ParseException('unexpected keyword: $keyword');
+        }
+        throw new ParseException('unrecognized keyword: $keyword');
+      case IDENTIFIER_TOKEN:
+        return _parseInvokeOrIdentifier();
+      case STRING_TOKEN:
+        return _parseString();
+      case INTEGER_TOKEN:
+        return _parseInteger();
+      case DECIMAL_TOKEN:
+        return _parseDecimal();
+      case GROUPER_TOKEN:
+        if (_token.value == '(') {
+          return _parseParenthesized();
+        } else if (_token.value == '{') {
+          return _parseMapLiteral();
+        } else if (_token.value == '[') {
+          return _parseListLiteral();
+        }
+        return null;
+      case COLON_TOKEN:
+        throw new ParseException('unexpected token ":"');
+      default:
+        return null;
+    }
+  }
+
+  ListLiteral _parseListLiteral() {
+    var items = [];
+    do {
+      _advance();
+      if (_token.kind == GROUPER_TOKEN && _token.value == ']') {
+        break;
+      }
+      items.add(_parseExpression());
+    } while(_token != null && _token.value == ',');
+    _advance(GROUPER_TOKEN, ']');
+    return new ListLiteral(items);
+  }
+
+  MapLiteral _parseMapLiteral() {
+    var entries = [];
+    do {
+      _advance();
+      if (_token.kind == GROUPER_TOKEN && _token.value == '}') {
+        break;
+      }
+      entries.add(_parseMapLiteralEntry());
+    } while(_token != null && _token.value == ',');
+    _advance(GROUPER_TOKEN, '}');
+    return new MapLiteral(entries);
+  }
+
+  MapLiteralEntry _parseMapLiteralEntry() {
+    var key = _parseString();
+    _advance(COLON_TOKEN, ':');
+    var value = _parseExpression();
+    return _astFactory.mapLiteralEntry(key, value);
+  }
+
+  InExpression _parseInExpression(Expression left) {
+    assert(_token.value == 'in');
+    if (left is! Identifier) {
+      throw new ParseException(
+          "in... statements must start with an identifier");
+    }
+    _advance();
+    var right = _parseExpression();
+    return _astFactory.inExpr(left, right);
+  }
+
+  AsExpression _parseAsExpression(Expression left) {
+    assert(_token.value == 'as');
+    _advance();
+    var right = _parseExpression();
+    if (right is! Identifier) {
+      throw new ParseException(
+          "'as' statements must end with an identifier");
+    }
+    return _astFactory.asExpr(left, right);
+  }
+
+  Expression _parseInvokeOrIdentifier() {
+    if (_token.value == 'true') {
+      _advance();
+      return _astFactory.literal(true);
+    }
+    if (_token.value == 'false') {
+      _advance();
+      return _astFactory.literal(false);
+    }
+    if (_token.value == 'null') {
+      _advance();
+      return _astFactory.literal(null);
+    }
+    var identifier = _parseIdentifier();
+    var args = _parseArguments();
+    if (args == null) {
+      return identifier;
+    } else {
+      return _astFactory.invoke(identifier, null, args);
+    }
+  }
+
+  Identifier _parseIdentifier() {
+    if (_token.kind != IDENTIFIER_TOKEN) {
+      throw new ParseException("expected identifier: $_token.value");
+    }
+    var value = _token.value;
+    _advance();
+    return _astFactory.identifier(value);
+  }
+
+  List<Expression> _parseArguments() {
+    if (_token != null && _token.kind == GROUPER_TOKEN && _token.value == '(') {
+      var args = [];
+      do {
+        _advance();
+        if (_token.kind == GROUPER_TOKEN && _token.value == ')') {
+          break;
+        }
+        var expr = _parseExpression();
+        args.add(expr);
+      } while(_token != null && _token.value == ',');
+      _advance(GROUPER_TOKEN, ')');
+      return args;
+    }
+    return null;
+  }
+
+  Expression _parseIndex() {
+    if (_token != null && _token.kind == GROUPER_TOKEN && _token.value == '[') {
+      _advance();
+      var expr = _parseExpression();
+      _advance(GROUPER_TOKEN, ']');
+      return expr;
+    }
+    return null;
+  }
+
+  ParenthesizedExpression _parseParenthesized() {
+    _advance();
+    var expr = _parseExpression();
+    _advance(GROUPER_TOKEN, ')');
+    return _astFactory.parenthesized(expr);
+  }
+
+  Literal<String> _parseString() {
+    var value = _astFactory.literal(_token.value);
+    _advance();
+    return value;
+  }
+
+  Literal<int> _parseInteger([String prefix = '']) {
+    var value = _astFactory.literal(int.parse('$prefix${_token.value}'));
+    _advance();
+    return value;
+  }
+
+  Literal<double> _parseDecimal([String prefix = '']) {
+    var value = _astFactory.literal(double.parse('$prefix${_token.value}'));
+    _advance();
+    return value;
+  }
+
+}
diff --git a/polymer_expressions/lib/polymer_expressions.dart b/polymer_expressions/lib/polymer_expressions.dart
new file mode 100644
index 0000000..332e3a1
--- /dev/null
+++ b/polymer_expressions/lib/polymer_expressions.dart
@@ -0,0 +1,397 @@
+// Copyright (c) 2013, 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.
+
+/**
+ * A binding delegate used with Polymer elements that
+ * allows for complex binding expressions, including
+ * property access, function invocation,
+ * list/map indexing, and two-way filtering.
+ *
+ * When you install polymer.dart,
+ * polymer_expressions is automatically installed as well.
+ *
+ * Polymer expressions are part of the Polymer.dart project.
+ * Refer to the
+ * [Polymer.dart](http://www.dartlang.org/polymer-dart/)
+ * homepage for example code, project status, and
+ * information about how to get started using Polymer.dart in your apps.
+ *
+ * ## Other resources
+ *
+ * The
+ * [Polymer expressions](http://pub.dartlang.org/packages/polymer_expressions)
+ * pub repository contains detailed documentation about using polymer
+ * expressions.
+ */
+
+library polymer_expressions;
+
+import 'dart:async';
+import 'dart:html';
+
+import 'package:observe/observe.dart';
+import 'package:template_binding/template_binding.dart';
+
+import 'eval.dart';
+import 'expression.dart';
+import 'parser.dart';
+import 'src/globals.dart';
+
+Object _classAttributeConverter(v) =>
+    (v is Map) ? v.keys.where((k) => v[k] == true).join(' ') :
+    (v is Iterable) ? v.join(' ') :
+    v;
+
+Object _styleAttributeConverter(v) =>
+    (v is Map) ? v.keys.map((k) => '$k: ${v[k]}').join(';') :
+    (v is Iterable) ? v.join(';') :
+    v;
+
+class PolymerExpressions extends BindingDelegate {
+  /** The default [globals] to use for Polymer expressions. */
+  static const Map DEFAULT_GLOBALS = const { 'enumerate': enumerate };
+
+  final ScopeFactory _scopeFactory;
+  final Map<String, Object> globals;
+
+  // allows access to scopes created for template instances
+  final Expando<Scope> _scopes = new Expando<Scope>();
+  // allows access to scope identifiers (for "in" and "as")
+  final Expando<String> _scopeIdents = new Expando<String>();
+
+  /**
+   * Creates a new binding delegate for Polymer expressions, with the provided
+   * variables used as [globals]. If no globals are supplied, a copy of the
+   * [DEFAULT_GLOBALS] will be used.
+   */
+  PolymerExpressions({Map<String, Object> globals,
+      ScopeFactory scopeFactory: const ScopeFactory()})
+      : globals = globals == null ?
+          new Map<String, Object>.from(DEFAULT_GLOBALS) : globals,
+          _scopeFactory = scopeFactory;
+
+  @override
+  PrepareBindingFunction prepareBinding(String path, name, Node boundNode) {
+    if (path == null) return null;
+    var expr = new Parser(path).parse();
+
+    if (isSemanticTemplate(boundNode) && (name == 'bind' || name == 'repeat')) {
+      if (expr is HasIdentifier) {
+        var identifier = expr.identifier;
+        var bindExpr = expr.expr;
+        return (model, Node node, bool oneTime) {
+          _scopeIdents[node] = identifier;
+          // model may not be a Scope if it was assigned directly via
+          // template.model = x; In that case, prepareInstanceModel will
+          // be called _after_ prepareBinding and will lookup this scope from
+          // _scopes
+          var scope = _scopes[node] = (model is Scope)
+              ? model
+              : _scopeFactory.modelScope(model: model, variables: globals);
+          return new _Binding(bindExpr, scope);
+        };
+      } else {
+        return (model, Node node, bool oneTime) {
+          var scope = _scopes[node] = (model is Scope)
+              ? model
+              : _scopeFactory.modelScope(model: model, variables: globals);
+          if (oneTime) {
+            return _Binding._oneTime(expr, scope);
+          }
+          return new _Binding(expr, scope);
+        };
+      }
+    }
+
+    // For regular bindings, not bindings on a template, the model is always
+    // a Scope created by prepareInstanceModel
+    _Converter converter = null;
+    if (boundNode is Element && name == 'class') {
+      converter = _classAttributeConverter;
+    } else if (boundNode is Element && name == 'style') {
+      converter = _styleAttributeConverter;
+    }
+
+    return (model, Node node, bool oneTime) {
+      var scope = _getScopeForModel(node, model);
+      if (oneTime) {
+        return _Binding._oneTime(expr, scope, converter);
+      }
+      return new _Binding(expr, scope, converter);
+    };
+  }
+
+  prepareInstanceModel(Element template) {
+    var ident = _scopeIdents[template];
+
+    if (ident == null) {
+      return (model) {
+        var existingScope = _scopes[template];
+        // TODO (justinfagnani): make template binding always call
+        // prepareInstanceModel first and get rid of this check
+        if (existingScope != null) {
+          // If there's an existing scope, we created it in prepareBinding
+          // If it has the same model, then we can reuse it, otherwise it's
+          // a repeat with no identifier and we create new scope to occlude
+          // the outer one
+          if (model == existingScope.model) return existingScope;
+          return _scopeFactory.modelScope(model: model, variables: globals);
+        } else {
+          return _getScopeForModel(template, model);
+        }
+      };
+    }
+
+    return (model) {
+      var existingScope = _scopes[template];
+      if (existingScope != null) {
+        // This only happens when a model has been assigned programatically
+        // and prepareBinding is called _before_ prepareInstanceModel.
+        // The scope assigned in prepareBinding wraps the model and is the
+        // scope of the expression. That should be the parent of the templates
+        // scope in the case of bind/as or repeat/in bindings.
+        return _scopeFactory.childScope(existingScope, ident, model);
+      } else {
+        // If there's not an existing scope then we have a bind/as or
+        // repeat/in binding enclosed in an outer scope, so we use that as
+        // the parent
+        var parentScope = _getParentScope(template);
+        return _scopeFactory.childScope(parentScope, ident, model);
+      }
+    };
+  }
+
+  /**
+   * Gets an existing scope for use as a parent, but does not create a new one.
+   */
+  Scope _getParentScope(Node node) {
+    var parent = node.parentNode;
+    if (parent == null) return null;
+
+    if (isSemanticTemplate(node)) {
+      var templateExtension = templateBind(node);
+      var templateInstance = templateExtension.templateInstance;
+      var model = templateInstance == null
+          ? templateExtension.model
+          : templateInstance.model;
+      if (model is Scope) {
+        return model;
+      } else {
+        // A template with a bind binding might have a non-Scope model
+        return _scopes[node];
+      }
+    }
+    if (parent != null) return _getParentScope(parent);
+    return null;
+  }
+
+  /**
+   * Returns the Scope to be used to evaluate expressions in the template
+   * containing [node]. Since all expressions in the same template evaluate
+   * against the same model, [model] is passed in and checked against the
+   * template model to make sure they agree.
+   *
+   * For nested templates, we might have a binding on the nested template that
+   * should be evaluated in the context of the parent template. All scopes are
+   * retreived from an ancestor of [node], since node may be establishing a new
+   * Scope.
+   */
+  Scope _getScopeForModel(Node node, model) {
+    // This only happens in bindings_test because it calls prepareBinding()
+    // directly. Fix the test and throw if node is null?
+    if (node == null) {
+      return _scopeFactory.modelScope(model: model, variables: globals);
+    }
+
+    var id = node is Element ? node.id : '';
+    if (model is Scope) {
+      return model;
+    }
+    if (_scopes[node] != null) {
+      var scope = _scopes[node];
+      assert(scope.model == model);
+      return _scopes[node];
+    } else if (node.parentNode != null) {
+      return _getContainingScope(node.parentNode, model);
+    } else {
+      // here we should be at a top-level template, so there's no parent to
+      // look for a Scope on.
+      if (!isSemanticTemplate(node)) {
+        throw "expected a template instead of $node";
+      }
+      return _getContainingScope(node, model);
+    }
+  }
+
+  Scope _getContainingScope(Node node, model) {
+    if (isSemanticTemplate(node)) {
+      var templateExtension = templateBind(node);
+      var templateInstance = templateExtension.templateInstance;
+      var templateModel = templateInstance == null
+          ? templateExtension.model
+          : templateInstance.model;
+      assert(templateModel == model);
+      var scope = _scopes[node];
+      assert(scope != null);
+      assert(scope.model == model);
+      return scope;
+    } else if (node.parent == null) {
+      var scope = _scopes[node];
+      if (scope != null) {
+        assert(scope.model == model);
+      } else {
+        // only happens in bindings_test
+        scope = _scopeFactory.modelScope(model: model, variables: globals);
+      }
+      return scope;
+    } else {
+      return _getContainingScope(node.parentNode, model);
+    }
+  }
+
+  /// Parse the expression string and return an expression tree.
+  static Expression getExpression(String exprString) =>
+      new Parser(exprString).parse();
+
+  /// Determines the value of evaluating [expr] on the given [model] and returns
+  /// either its value or a binding for it. If [oneTime] is true, it direclty
+  /// returns the value. Otherwise, when [oneTime] is false, it returns a
+  /// [Bindable] that besides evaluating the expression, it will also react to
+  /// observable changes from the model and update the value accordingly.
+  static getBinding(Expression expr, model, {Map<String, Object> globals,
+      oneTime: false}) {
+    if (globals == null) globals = new Map.from(DEFAULT_GLOBALS);
+    var scope = model is Scope ? model
+        : new Scope(model: model, variables: globals);
+    return oneTime ? _Binding._oneTime(expr, scope)
+        : new _Binding(expr, scope);
+  }
+}
+
+typedef Object _Converter(Object);
+
+class _Binding extends Bindable {
+  final Scope _scope;
+  final _Converter _converter;
+  final Expression _expr;
+
+  Function _callback;
+  StreamSubscription _sub;
+  ExpressionObserver _observer;
+  var _value;
+
+  _Binding(this._expr, this._scope, [this._converter]);
+
+  static Object _oneTime(Expression expr, Scope scope, [_Converter converter]) {
+    try {
+      var value = eval(expr, scope);
+      return (converter == null) ? value : converter(value);
+    } catch (e, s) {
+      new Completer().completeError(
+          "Error evaluating expression '$expr': $e", s);
+    }
+    return null;
+  }
+
+  bool _convertAndCheck(newValue, {bool skipChanges: false}) {
+    var oldValue = _value;
+    _value = _converter == null ? newValue : _converter(newValue);
+
+    if (!skipChanges && _callback != null && oldValue != _value) {
+      _callback(_value);
+      return true;
+    }
+    return false;
+  }
+
+  get value {
+    // if there's a callback, then _value has been set, if not we need to
+    // force an evaluation
+    if (_callback != null) {
+      _check(skipChanges: true);
+      return _value;
+    }
+    return _Binding._oneTime(_expr, _scope, _converter);
+  }
+
+  set value(v) {
+    try {
+      assign(_expr, v, _scope, checkAssignability: false);
+    } catch (e, s) {
+      new Completer().completeError(
+          "Error evaluating expression '$_expr': $e", s);
+    }
+  }
+
+  Object open(callback(value)) {
+    if (_callback != null) throw new StateError('already open');
+
+    _callback = callback;
+    _observer = observe(_expr, _scope);
+    _sub = _observer.onUpdate.listen(_convertAndCheck)..onError((e, s) {
+      new Completer().completeError(
+          "Error evaluating expression '$_observer': $e", s);
+    });
+
+    _check(skipChanges: true);
+    return _value;
+  }
+
+  bool _check({bool skipChanges: false}) {
+    try {
+      update(_observer, _scope, skipChanges: skipChanges);
+      return _convertAndCheck(_observer.currentValue, skipChanges: skipChanges);
+    } catch (e, s) {
+      new Completer().completeError(
+          "Error evaluating expression '$_observer': $e", s);
+      return false;
+    }
+  }
+
+  void close() {
+    if (_callback == null) return;
+
+    _sub.cancel();
+    _sub = null;
+    _callback = null;
+
+    new Closer().visit(_observer);
+    _observer = null;
+  }
+
+
+  // TODO(jmesserly): the following code is copy+pasted from path_observer.dart
+  // What seems to be going on is: polymer_expressions.dart has its own _Binding
+  // unlike polymer-expressions.js, which builds on CompoundObserver.
+  // This can lead to subtle bugs and should be reconciled. I'm not sure how it
+  // should go, but CompoundObserver does have some nice optimizations around
+  // ObservedSet which are lacking here. And reuse is nice.
+  void deliver() {
+    if (_callback != null) _dirtyCheck();
+  }
+
+  bool _dirtyCheck() {
+    var cycles = 0;
+    while (cycles < _MAX_DIRTY_CHECK_CYCLES && _check()) {
+      cycles++;
+    }
+    return cycles > 0;
+  }
+
+  static const int _MAX_DIRTY_CHECK_CYCLES = 1000;
+}
+
+_identity(x) => x;
+
+/**
+ * Factory function used for testing.
+ */
+class ScopeFactory {
+  const ScopeFactory();
+  modelScope({Object model, Map<String, Object> variables}) =>
+      new Scope(model: model, variables: variables);
+
+  childScope(Scope parent, String name, Object value) =>
+      parent.childScope(name, value);
+}
diff --git a/polymer_expressions/lib/src/globals.dart b/polymer_expressions/lib/src/globals.dart
new file mode 100644
index 0000000..d2a4ffd
--- /dev/null
+++ b/polymer_expressions/lib/src/globals.dart
@@ -0,0 +1,77 @@
+// Copyright (c) 2013, 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.
+
+/**
+ * Contains functions that are included by default in [PolymerExpressions].
+ *
+ *   - [enumerate]: a convenient way to iterate over items and the indexes.
+ */
+// Code from https://github.com/google/quiver-dart/commit/52edc4baf37e99ff6a8f99c648b29b135fc0b880
+library polymer_expressions.src.globals;
+
+import 'dart:collection';
+import 'package:observe/observe.dart' show reflectable;
+
+/**
+ * Returns an [Iterable] of [IndexedValue]s where the nth value holds the nth
+ * element of [iterable] and its index.
+ */
+Iterable<IndexedValue> enumerate(Iterable iterable) =>
+    new EnumerateIterable(iterable);
+
+@reflectable class IndexedValue<V> {
+  final int index;
+  final V value;
+
+  operator==(o) => o is IndexedValue && o.index == index && o.value == value;
+  int get hashCode => value.hashCode;
+  String toString() => '($index, $value)';
+
+  IndexedValue(this.index, this.value);
+}
+
+/**
+ * An [Iterable] of [IndexedValue]s where the nth value holds the nth
+ * element of [iterable] and its index. See [enumerate].
+ */
+// This was inspired by MappedIterable internal to Dart collections.
+class EnumerateIterable<V> extends IterableBase<IndexedValue<V>> {
+  final Iterable<V> _iterable;
+
+  EnumerateIterable(this._iterable);
+
+  Iterator<IndexedValue<V>> get iterator =>
+      new EnumerateIterator<V>(_iterable.iterator);
+
+  // Length related functions are independent of the mapping.
+  int get length => _iterable.length;
+  bool get isEmpty => _iterable.isEmpty;
+
+  // Index based lookup can be done before transforming.
+  IndexedValue<V> get first => new IndexedValue<V>(0, _iterable.first);
+  IndexedValue<V> get last => new IndexedValue<V>(length - 1, _iterable.last);
+  IndexedValue<V> get single => new IndexedValue<V>(0, _iterable.single);
+  IndexedValue<V> elementAt(int index) =>
+      new IndexedValue<V>(index, _iterable.elementAt(index));
+}
+
+/** The [Iterator] returned by [EnumerateIterable.iterator]. */
+class EnumerateIterator<V> extends Iterator<IndexedValue<V>> {
+  final Iterator<V> _iterator;
+  int _index = 0;
+  IndexedValue<V> _current;
+
+  EnumerateIterator(this._iterator);
+
+  IndexedValue<V> get current => _current;
+
+  bool moveNext() {
+    if (_iterator.moveNext()) {
+      _current = new IndexedValue(_index++, _iterator.current);
+      return true;
+    }
+    _current = null;
+    return false;
+  }
+}
diff --git a/polymer_expressions/lib/tokenizer.dart b/polymer_expressions/lib/tokenizer.dart
new file mode 100644
index 0000000..c8e5b13
--- /dev/null
+++ b/polymer_expressions/lib/tokenizer.dart
@@ -0,0 +1,308 @@
+// Copyright (c) 2013, 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 polymer_expressions.tokenizer;
+
+const int _TAB = 9;
+const int _LF = 10;
+const int _VTAB = 11;
+const int _FF = 12;
+const int _CR = 13;
+const int _SPACE = 32;
+const int _BANG = 33;
+const int _DQ = 34;
+const int _$ = 36;
+const int _PERCENT = 37;
+const int _AMPERSAND = 38;
+const int _SQ = 39;
+const int _OPEN_PAREN = 40;
+const int _CLOSE_PAREN = 41;
+const int _STAR = 42;
+const int _PLUS = 43;
+const int _COMMA = 44;
+const int _MINUS = 45;
+const int _PERIOD = 46;
+const int _SLASH = 47;
+const int _0 = 48;
+const int _9 = 57;
+const int _COLON = 58;
+const int _LT = 60;
+const int _EQ = 61;
+const int _GT = 62;
+const int _QUESTION = 63;
+const int _A = 65;
+const int _Z = 90;
+const int _OPEN_SQUARE_BRACKET = 91;
+const int _BACKSLASH = 92;
+const int _CLOSE_SQUARE_BRACKET = 93;
+const int _CARET = 94;
+const int _US = 95;
+const int _a = 97;
+const int _f = 102;
+const int _n = 110;
+const int _r = 114;
+const int _t = 116;
+const int _v = 118;
+const int _z = 122;
+const int _OPEN_CURLY_BRACKET = 123;
+const int _BAR = 124;
+const int _CLOSE_CURLY_BRACKET = 125;
+const int _NBSP = 160;
+
+const _OPERATORS = const [_PLUS, _MINUS, _STAR, _SLASH, _BANG, _AMPERSAND,
+                          _PERCENT, _LT, _EQ, _GT, _QUESTION, _CARET, _BAR];
+
+const _GROUPERS = const [_OPEN_PAREN, _CLOSE_PAREN,
+                         _OPEN_SQUARE_BRACKET, _CLOSE_SQUARE_BRACKET,
+                         _OPEN_CURLY_BRACKET, _CLOSE_CURLY_BRACKET];
+
+const _TWO_CHAR_OPS = const ['==', '!=', '<=', '>=', '||', '&&'];
+
+const KEYWORDS = const ['as', 'in', 'this'];
+
+const _PRECEDENCE = const {
+  '!':  0,
+  ':':  0,
+  ',':  0,
+  ')':  0,
+  ']':  0,
+  '}':  0, // ?
+  '?':  1,
+  '||': 2,
+  '&&': 3,
+  '|':  4,
+  '^':  5,
+  '&':  6,
+
+  // equality
+  '!=': 7,
+  '==': 7,
+  '!==': 7,
+  '===': 7,
+
+  // relational
+  '>=': 8,
+  '>':  8,
+  '<=': 8,
+  '<':  8,
+
+  // additive
+  '+':  9,
+  '-':  9,
+
+  // multiplicative
+  '%':  10,
+  '/':  10,
+  '*':  10,
+
+  // postfix
+  '(':  11,
+  '[':  11,
+  '.':  11,
+  '{': 11, //not sure this is correct
+};
+
+const POSTFIX_PRECEDENCE = 11;
+
+const int STRING_TOKEN = 1;
+const int IDENTIFIER_TOKEN = 2;
+const int DOT_TOKEN = 3;
+const int COMMA_TOKEN = 4;
+const int COLON_TOKEN = 5;
+const int INTEGER_TOKEN = 6;
+const int DECIMAL_TOKEN = 7;
+const int OPERATOR_TOKEN = 8;
+const int GROUPER_TOKEN = 9;
+const int KEYWORD_TOKEN = 10;
+
+bool isWhitespace(int next) => next == _SPACE || next == _TAB || next == _NBSP;
+
+bool isIdentifierOrKeywordStart(int next) => (_a <= next && next <= _z) ||
+    (_A <= next && next <= _Z) || next == _US || next == _$ || next > 127;
+
+bool isIdentifier(int next) => (_a <= next && next <= _z) ||
+    (_A <= next && next <= _Z) || (_0 <= next && next <= _9) ||
+    next == _US || next == _$ || next > 127;
+
+bool isQuote(int next) => next == _DQ || next == _SQ;
+
+bool isNumber(int next) => _0 <= next && next <= _9;
+
+bool isOperator(int next) => _OPERATORS.contains(next);
+
+bool isGrouper(int next) => _GROUPERS.contains(next);
+
+int escape(int c) {
+  switch (c) {
+    case _f: return _FF;
+    case _n: return _LF;
+    case _r: return _CR;
+    case _t: return _TAB;
+    case _v: return _VTAB;
+    default: return c;
+  }
+}
+
+class Token {
+  final int kind;
+  final String value;
+  final int precedence;
+
+  Token(this.kind, this.value, [this.precedence = 0]);
+
+  String toString() => "($kind, '$value')";
+}
+
+class Tokenizer {
+  final List<Token> _tokens = <Token>[];
+  final StringBuffer _sb = new StringBuffer();
+  final RuneIterator _iterator;
+
+  int _next;
+
+  Tokenizer(String input) : _iterator = new RuneIterator(input);
+
+  _advance() {
+    _next = _iterator.moveNext() ? _iterator.current : null;
+  }
+
+  List<Token> tokenize() {
+    _advance();
+    while(_next != null) {
+      if (isWhitespace(_next)) {
+        _advance();
+      } else if (isQuote(_next)) {
+        tokenizeString();
+      } else if (isIdentifierOrKeywordStart(_next)) {
+        tokenizeIdentifierOrKeyword();
+      } else if (isNumber(_next)) {
+        tokenizeNumber();
+      } else if (_next == _PERIOD) {
+        tokenizeDot();
+      } else if (_next == _COMMA) {
+        tokenizeComma();
+      } else if (_next == _COLON) {
+        tokenizeColon();
+      } else if (isOperator(_next)) {
+        tokenizeOperator();
+      } else if (isGrouper(_next)) {
+        tokenizeGrouper();
+      } else {
+        _advance();
+      }
+    }
+    return _tokens;
+  }
+
+  tokenizeString() {
+    int quoteChar = _next;
+    _advance();
+    while (_next != quoteChar) {
+      if (_next == null) throw new ParseException("unterminated string");
+      if (_next == _BACKSLASH) {
+        _advance();
+        if (_next == null) throw new ParseException("unterminated string");
+        _sb.writeCharCode(escape(_next));
+      } else {
+        _sb.writeCharCode(_next);
+      }
+      _advance();
+    }
+    _tokens.add(new Token(STRING_TOKEN, _sb.toString()));
+    _sb.clear();
+    _advance();
+  }
+
+  tokenizeIdentifierOrKeyword() {
+    while (_next != null && isIdentifier(_next)) {
+      _sb.writeCharCode(_next);
+      _advance();
+    }
+    var value = _sb.toString();
+    if (KEYWORDS.contains(value)) {
+      _tokens.add(new Token(KEYWORD_TOKEN, value));
+    } else {
+      _tokens.add(new Token(IDENTIFIER_TOKEN, value));
+    }
+    _sb.clear();
+  }
+
+  tokenizeNumber() {
+    while (_next != null && isNumber(_next)) {
+      _sb.writeCharCode(_next);
+      _advance();
+    }
+    if (_next == _PERIOD) {
+      tokenizeDot();
+    } else {
+      _tokens.add(new Token(INTEGER_TOKEN, _sb.toString()));
+      _sb.clear();
+    }
+  }
+
+  tokenizeDot() {
+    _advance();
+    if (isNumber(_next)) {
+      tokenizeFraction();
+    } else {
+      _tokens.add(new Token(DOT_TOKEN, '.', POSTFIX_PRECEDENCE));
+    }
+  }
+
+  tokenizeComma() {
+    _advance();
+    _tokens.add(new Token(COMMA_TOKEN, ','));
+  }
+
+  tokenizeColon() {
+    _advance();
+    _tokens.add(new Token(COLON_TOKEN, ':'));
+  }
+
+  tokenizeFraction() {
+    _sb.writeCharCode(_PERIOD);
+    while (_next != null && isNumber(_next)) {
+      _sb.writeCharCode(_next);
+      _advance();
+    }
+    _tokens.add(new Token(DECIMAL_TOKEN, _sb.toString()));
+    _sb.clear();
+  }
+
+  tokenizeOperator() {
+    int startChar = _next;
+    _advance();
+    var op;
+    // check for 2 character operators
+    if (isOperator(_next)) {
+      var op2 = new String.fromCharCodes([startChar, _next]);
+      if (_TWO_CHAR_OPS.contains(op2)) {
+        op = op2;
+        _advance();
+        // kind of hacky check for === and !===, could be better / more general
+        if (_next == _EQ && (startChar == _BANG || startChar == _EQ)) {
+          op = op2 + '=';
+          _advance();
+        }
+      } else {
+        op = new String.fromCharCode(startChar);
+      }
+    } else {
+      op = new String.fromCharCode(startChar);
+    }
+    _tokens.add(new Token(OPERATOR_TOKEN, op, _PRECEDENCE[op]));
+  }
+
+  tokenizeGrouper() {
+    var value = new String.fromCharCode(_next);
+    _tokens.add(new Token(GROUPER_TOKEN, value, _PRECEDENCE[value]));
+    _advance();
+  }
+}
+
+class ParseException implements Exception {
+  final String message;
+  ParseException(this.message);
+  String toString() => "ParseException: $message";
+}
diff --git a/polymer_expressions/lib/visitor.dart b/polymer_expressions/lib/visitor.dart
new file mode 100644
index 0000000..7bf8053
--- /dev/null
+++ b/polymer_expressions/lib/visitor.dart
@@ -0,0 +1,133 @@
+// Copyright (c) 2013, 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 polymer_expressions.visitor;
+
+import 'expression.dart';
+
+abstract class Visitor {
+  visit(Expression s) => s.accept(this);
+  visitEmptyExpression(EmptyExpression e);
+  visitParenthesizedExpression(ParenthesizedExpression e);
+  visitGetter(Getter i);
+  visitIndex(Index i);
+  visitInvoke(Invoke i);
+  visitLiteral(Literal l);
+  visitListLiteral(ListLiteral l);
+  visitMapLiteral(MapLiteral l);
+  visitMapLiteralEntry(MapLiteralEntry l);
+  visitIdentifier(Identifier i);
+  visitBinaryOperator(BinaryOperator o);
+  visitUnaryOperator(UnaryOperator o);
+  visitTernaryOperator(TernaryOperator o);
+  visitInExpression(InExpression c);
+  visitAsExpression(AsExpression c);
+}
+
+class RecursiveVisitor extends Visitor {
+  preVisitExpression(Expression e) {}
+  visitExpression(Expression e) {}
+
+  visitEmptyExpression(EmptyExpression e) {
+    preVisitExpression(e);
+    visitExpression(e);
+  }
+
+  visitParenthesizedExpression(ParenthesizedExpression e) {
+    preVisitExpression(e);
+    visit(e.child);
+    visitExpression(e);
+  }
+
+  visitGetter(Getter i) {
+    preVisitExpression(i);
+    visit(i.receiver);
+    visitExpression(i);
+  }
+
+  visitIndex(Index i) {
+    preVisitExpression(i);
+    visit(i.receiver);
+    visit(i.argument);
+    visitExpression(i);
+  }
+
+  visitInvoke(Invoke i) {
+    preVisitExpression(i);
+    visit(i.receiver);
+    if (i.arguments != null) {
+      for (var a in i.arguments) {
+        visit(a);
+      }
+    }
+    visitExpression(i);
+  }
+
+  visitLiteral(Literal l) {
+    preVisitExpression(l);
+    visitExpression(l);
+  }
+
+  visitListLiteral(ListLiteral l) {
+    preVisitExpression(l);
+    for (var i in l.items) {
+      visit(i);
+    }
+    visitExpression(l);
+  }
+
+  visitMapLiteral(MapLiteral l) {
+    preVisitExpression(l);
+    for (var e in l.entries) {
+      visit(e);
+    }
+    visitExpression(l);
+  }
+
+  visitMapLiteralEntry(MapLiteralEntry e) {
+    preVisitExpression(e);
+    visit(e.key);
+    visit(e.entryValue);
+    visitExpression(e);
+  }
+
+  visitIdentifier(Identifier i) {
+    preVisitExpression(i);
+    visitExpression(i);
+  }
+
+  visitBinaryOperator(BinaryOperator o) {
+    preVisitExpression(o);
+    visit(o.left);
+    visit(o.right);
+    visitExpression(o);
+  }
+
+  visitUnaryOperator(UnaryOperator o) {
+    preVisitExpression(o);
+    visit(o.child);
+    visitExpression(o);
+  }
+
+  visitTernaryOperator(TernaryOperator o) {
+    preVisitExpression(o);
+    visit(o.condition);
+    visit(o.trueExpr);
+    visit(o.falseExpr);
+    visitExpression(o);
+  }
+
+  visitInExpression(InExpression c) {
+    preVisitExpression(c);
+    visit(c.left);
+    visit(c.right);
+    visitExpression(c);
+  }
+
+  visitAsExpression(AsExpression c) {
+    visit(c.left);
+    visit(c.right);
+    visitExpression(c);
+  }
+}
diff --git a/polymer_expressions/pubspec.yaml b/polymer_expressions/pubspec.yaml
new file mode 100644
index 0000000..5313ff3
--- /dev/null
+++ b/polymer_expressions/pubspec.yaml
@@ -0,0 +1,14 @@
+name: polymer_expressions
+version: 0.13.1
+author: Polymer.dart Authors <web-ui-dev@dartlang.org>
+description: An expressive custom binding syntax for HTML templates
+homepage: http://www.dartlang.org/polymer-dart/
+dependencies:
+  browser: ">=0.10.0 <0.11.0"
+  observe: ">=0.11.0 <0.14.0"
+  template_binding: ">=0.13.0 <0.15.0"
+dev_dependencies:
+  unittest: ">=0.10.0 <0.11.0"
+  benchmark_harness: ">=1.0.0 <2.0.0"
+environment:
+  sdk: ">=1.2.0 <2.0.0"
diff --git a/polymer_interop/lib/polymer.html b/polymer_interop/lib/polymer.html
new file mode 100644
index 0000000..d10b4db
--- /dev/null
+++ b/polymer_interop/lib/polymer.html
@@ -0,0 +1,12 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+<link rel="import" href="src/js/polymer.html">
+<script>
+// This empty script tag is necessary to work around dartbug.com/19650
+</script>
diff --git a/polymer_interop/lib/polymer_interop.dart b/polymer_interop/lib/polymer_interop.dart
new file mode 100644
index 0000000..2467c04
--- /dev/null
+++ b/polymer_interop/lib/polymer_interop.dart
@@ -0,0 +1,102 @@
+// 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.
+@HtmlImport('polymer.html')
+library polymer_interop.polymer_interop;
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:js' as js;
+import 'package:web_components/web_components.dart';
+export 'src/polymer_proxy_mixin.dart';
+
+final js.JsObject _polymer = js.context['Polymer'];
+
+/// Wrapper which provides access to many polymer js apis.
+class PolymerJs {
+  static js.JsFunction get constructor => _polymer as js.JsFunction;
+
+  static void resolveElementPaths(Node node) {
+    if (!checkExists()) return;
+    _polymer['urlResolver'].callMethod('resolveDom', [node]);
+  }
+
+  static void flush() {
+    if (!checkExists()) return;
+    _polymer.callMethod('flush');
+  }
+
+  static List<Element> get waitingFor {
+    if (!checkExists()) return null;
+    return _polymer.callMethod('waitingFor', [null]);
+  }
+
+  static void forceReady([int timeout]) {
+    if (!checkExists()) return null;
+    _polymer.callMethod('forceReady', [null, timeout]);
+  }
+
+  static Future importElements(Node elementOrFragment) {
+    if (!checkExists()) return null;
+    var completer = new Completer();
+    _polymer.callMethod(
+        'importElements', [elementOrFragment, () => completer.complete()]);
+    return completer.future;
+  }
+
+  static Future import(List urls) {
+    if (!checkExists()) return null;
+    var completer = new Completer();
+    _polymer.callMethod('import', [urls, () => completer.complete()]);
+    return completer.future;
+  }
+
+  static void whenPolymerReady(f()) {
+    if (!checkExists()) return null;
+    _polymer.callMethod(
+        'whenPolymerReady', [Zone.current.bindCallback(() => f())]);
+  }
+
+  static void endOfMicrotask(f()) {
+    if (!checkExists()) return null;
+    _polymer.callMethod('endOfMicrotask', [() => f()]);
+  }
+
+  static bool outputPolymerError = false;
+  static bool checkExists() {
+    if (_polymer != null) return true;
+    if (!outputPolymerError) {
+      outputPolymerError = true;
+      window.console.error('Unable to find Polymer. Please make sure you are '
+          'waiting on initWebComponents() or initPolymer() before attempting '
+          'to use Polymer.');
+    }
+    return false;
+  }
+}
+
+final js.JsObject _polymerGestures = js.context['PolymerGestures'];
+
+class PolymerGesturesJs {
+  static void addEventListener(Node node, String type, callback) {
+    if (!checkExists()) return null;
+    _polymerGestures.callMethod('addEventListener', [node, type, callback]);
+  }
+
+  static void removeEventListener(Node node, String type, callback) {
+    if (!checkExists()) return null;
+    _polymerGestures.callMethod('removeEventListener', [node, type, callback]);
+  }
+
+  static bool outputPolymerGesturesError = false;
+  static bool checkExists() {
+    if (_polymerGestures != null) return true;
+    if (!outputPolymerGesturesError) {
+      outputPolymerGesturesError = true;
+      window.console.error('Unable to find PolymerGestures. Please make sure '
+          'you are waiting on initWebComponents() or initPolymer() before '
+          'attempting to use PolymerGestures.');
+    }
+    return false;
+  }
+}
diff --git a/polymer_interop/lib/src/build/replace_polymer_js.dart b/polymer_interop/lib/src/build/replace_polymer_js.dart
new file mode 100644
index 0000000..b81f3e7
--- /dev/null
+++ b/polymer_interop/lib/src/build/replace_polymer_js.dart
@@ -0,0 +1,26 @@
+// 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 polymer_interop.src.build.replace_polymer_js.dart;
+
+import 'dart:async';
+import 'package:barback/barback.dart';
+
+/// Replaces `polymer.min.js` with `polymer.js` in development mode.
+class ReplacePolymerJsTransformer extends Transformer {
+  BarbackSettings settings;
+
+  ReplacePolymerJsTransformer.asPlugin(this.settings);
+
+  bool isPrimary(AssetId id) {
+    if (settings.mode == BarbackMode.RELEASE) return false;
+    return id.path == 'lib/src/js/polymer.html';
+  }
+
+  Future apply(Transform transform) async {
+    var contents = await transform.primaryInput.readAsString();
+    contents = contents.replaceFirst('polymer.min.js', 'polymer.js');
+    transform
+        .addOutput(new Asset.fromString(transform.primaryInput.id, contents));
+  }
+}
diff --git a/polymer_interop/lib/src/js/AUTHORS b/polymer_interop/lib/src/js/AUTHORS
new file mode 100644
index 0000000..0617765
--- /dev/null
+++ b/polymer_interop/lib/src/js/AUTHORS
@@ -0,0 +1,9 @@
+# Names should be added to this file with this pattern:
+#
+# For individuals:
+#   Name <email address>
+#
+# For organizations:
+#   Organization <fnmatch pattern>
+#
+Google Inc. <*@google.com>
diff --git a/polymer_interop/lib/src/js/CONTRIBUTING.md b/polymer_interop/lib/src/js/CONTRIBUTING.md
new file mode 100644
index 0000000..1de2f34
--- /dev/null
+++ b/polymer_interop/lib/src/js/CONTRIBUTING.md
@@ -0,0 +1,73 @@
+# Contributing
+
+Want to contribute to Polymer? Great!
+
+We are more than happy to accept external contributions to the project in the form of [feedback](https://groups.google.com/forum/?fromgroups=#!forum/polymer-dev), [bug reports](../../issues), and pull requests.
+
+## Contributor License Agreement
+
+Before we can accept patches, there's a quick web form you need to fill out.
+
+- If you're contributing as an individual (e.g. you own the intellectual property), fill out [this form](http://code.google.com/legal/individual-cla-v1.0.html).
+- If you're contributing under a company, fill out [this form](http://code.google.com/legal/corporate-cla-v1.0.html) instead.
+
+This CLA asserts that contributions are owned by you and that we can license all work under our [license](LICENSE).
+
+Other projects require a similar agreement: jQuery, Firefox, Apache, Node, and many more.
+
+[More about CLAs](https://www.google.com/search?q=Contributor%20License%20Agreement)
+
+## Initial setup
+
+Here's an easy guide that should get you up and running:
+
+1. Setup Grunt: `sudo npm install -g grunt-cli`
+1. Fork the project on github and pull down your copy.
+   > replace the {{ username }} with your username and {{ repository }} with the repository name
+
+        git clone git@github.com:{{ username }}/{{ repository }}.git --recursive
+
+    Note the `--recursive`. This is necessary for submodules to initialize properly. If you don't do a recursive clone, you'll have to init them manually:
+
+        git submodule init
+        git submodule update
+
+    Download and run the `pull-all.sh` script to install the sibling dependencies.
+
+        git clone git://github.com/Polymer/tools.git && tools/bin/pull-all.sh
+
+1. Test your change
+   > in the repo you've made changes to, run the tests:
+
+        cd $REPO
+        npm install
+        grunt test
+
+1. Commit your code and make a pull request.
+
+That's it for the one time setup. Now you're ready to make a change.
+
+## Submitting a pull request
+
+We iterate fast! To avoid potential merge conflicts, it's a good idea to pull from the main project before making a change and submitting a pull request. The easiest way to do this is setup a remote called `upstream` and do a pull before working on a change:
+
+    git remote add upstream git://github.com/Polymer/{{ repository }}.git
+
+Then before making a change, do a pull from the upstream `master` branch:
+
+    git pull upstream master
+
+To make life easier, add a "pull upstream" alias in your `.gitconfig`:
+
+    [alias]
+      pu = !"git fetch origin -v; git fetch upstream -v; git merge upstream/master"
+
+That will pull in changes from your forked repo, the main (upstream) repo, and merge the two. Then it's just a matter of running `git pu` before a change and pushing to your repo:
+
+    git checkout master
+    git pu
+    # make change
+    git commit -a -m 'Awesome things.'
+    git push
+
+Lastly, don't forget to submit the pull request.
diff --git a/polymer_interop/lib/src/js/LICENSE b/polymer_interop/lib/src/js/LICENSE
new file mode 100644
index 0000000..95987ba
--- /dev/null
+++ b/polymer_interop/lib/src/js/LICENSE
@@ -0,0 +1,27 @@
+// Copyright (c) 2014 The Polymer Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/polymer_interop/lib/src/js/PATENTS b/polymer_interop/lib/src/js/PATENTS
new file mode 100644
index 0000000..e120963
--- /dev/null
+++ b/polymer_interop/lib/src/js/PATENTS
@@ -0,0 +1,23 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the Polymer project.
+
+Google hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section)
+patent license to make, have made, use, offer to sell, sell, import,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of Polymer, where such license applies only to those
+patent claims, both currently owned or controlled by Google and acquired
+in the future, licensable by Google that are necessarily infringed by
+this implementation of Polymer.  This grant does not include claims
+that would be infringed only as a consequence of further modification of
+this implementation.  If you or your agent or exclusive licensee
+institute or order or agree to the institution of patent litigation
+against any entity (including a cross-claim or counterclaim in a
+lawsuit) alleging that this implementation of Polymer or any code
+incorporated within this implementation of Polymer constitutes
+direct or contributory patent infringement, or inducement of patent
+infringement, then any patent rights granted to you under this License
+for this implementation of Polymer shall terminate as of the date
+such litigation is filed.
diff --git a/polymer_interop/lib/src/js/README.md b/polymer_interop/lib/src/js/README.md
new file mode 100644
index 0000000..236a88c
--- /dev/null
+++ b/polymer_interop/lib/src/js/README.md
@@ -0,0 +1,17 @@
+# Polymer
+
+[![Analytics](https://ga-beacon.appspot.com/UA-39334307-2/Polymer/polymer/README)](https://github.com/igrigorik/ga-beacon)
+
+Build Status: [http://build.chromium.org/p/client.polymer/waterfall](http://build.chromium.org/p/client.polymer/waterfall)
+
+## Brief Overview
+
+For more detailed info goto [http://polymer-project.org/](http://polymer-project.org/).
+
+Polymer is a new type of library for the web, designed to leverage the existing browser infrastructure to provide the encapsulation and extendability currently only available in JS libraries.
+
+Polymer is based on a set of future technologies, including [Shadow DOM](https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html), [Custom Elements](https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html) and Model Driven Views. Currently these technologies are implemented as polyfills or shims, but as browsers adopt these features natively, the platform code that drives Polymer evacipates, leaving only the value-adds.
+
+## Tools & Testing
+
+For running tests or building minified files, consult the [tooling information](http://www.polymer-project.org/resources/tooling-strategy.html).
diff --git a/polymer_interop/lib/src/js/bower.json b/polymer_interop/lib/src/js/bower.json
new file mode 100644
index 0000000..dbc7596
--- /dev/null
+++ b/polymer_interop/lib/src/js/bower.json
@@ -0,0 +1,23 @@
+{
+  "name": "polymer",
+  "description": "Polymer is a new type of library for the web, built on top of Web Components, and designed to leverage the evolving web platform on modern browsers.",
+  "homepage": "http://www.polymer-project.org/",
+  "keywords": [
+    "util",
+    "client",
+    "browser",
+    "web components",
+    "web-components"
+  ],
+  "author": "Polymer Authors <polymer-dev@googlegroups.com>",
+  "private": true,
+  "dependencies": {
+    "core-component-page": "Polymer/core-component-page#^0.5",
+    "webcomponentsjs": "Polymer/webcomponentsjs#^0.5"
+  },
+  "devDependencies": {
+    "tools": "Polymer/tools#master",
+    "web-component-tester": "Polymer/web-component-tester#^1.4.2"
+  },
+  "version": "0.5.5"
+}
\ No newline at end of file
diff --git a/polymer_interop/lib/src/js/build.log b/polymer_interop/lib/src/js/build.log
new file mode 100644
index 0000000..3eef92a
--- /dev/null
+++ b/polymer_interop/lib/src/js/build.log
@@ -0,0 +1,26 @@
+BUILD LOG
+---------
+Build Time: 2015-02-17T17:25:22
+
+NODEJS INFORMATION
+==================
+nodejs: v0.12.0
+grunt: 0.4.5
+grunt-audit: 1.0.0
+grunt-contrib-concat: 0.5.0
+grunt-contrib-copy: 0.7.0
+grunt-contrib-uglify: 0.6.0
+grunt-string-replace: 1.0.0
+web-component-tester: 1.6.2
+
+REPO REVISIONS
+==============
+polymer-expressions: f2229c2f3db2332aab5c4dba029c17f8bc3f99f2
+polymer-gestures: ccd9dfae58896dff2e4fe9ce123870d321e393d1
+polymer: f53db3fe961a3094844ad46e70e3f721604b0a2b
+
+BUILD HASHES
+============
+dist/polymer.js: 0238138d46a41de3724926295213f1babf66ed41
+dist/polymer.min.js: d5907053e8535c422393aa240e9026ffe0b5fefc
+dist/layout.html: 348d358a91712ecc2f8811efa430fcd954b4590c
\ No newline at end of file
diff --git a/polymer_interop/lib/src/js/layout.html b/polymer_interop/lib/src/js/layout.html
new file mode 100644
index 0000000..55d4d2f
--- /dev/null
+++ b/polymer_interop/lib/src/js/layout.html
@@ -0,0 +1,286 @@
+<!--

+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.

+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt

+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt

+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt

+Code distributed by Google as part of the polymer project is also

+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt

+-->

+<style shim-shadowdom>

+/*******************************

+          Flex Layout

+*******************************/

+

+html /deep/ [layout][horizontal], html /deep/ [layout][vertical] {

+  display: -ms-flexbox;

+  display: -webkit-flex;

+  display: flex;

+}

+

+html /deep/ [layout][horizontal][inline], html /deep/ [layout][vertical][inline] {

+  display: -ms-inline-flexbox;

+  display: -webkit-inline-flex;

+  display: inline-flex;

+}

+

+html /deep/ [layout][horizontal] {

+  -ms-flex-direction: row;

+  -webkit-flex-direction: row;

+  flex-direction: row;

+}

+

+html /deep/ [layout][horizontal][reverse] {

+  -ms-flex-direction: row-reverse;

+  -webkit-flex-direction: row-reverse;

+  flex-direction: row-reverse;

+}

+

+html /deep/ [layout][vertical] {

+  -ms-flex-direction: column;

+  -webkit-flex-direction: column;

+  flex-direction: column;

+}

+

+html /deep/ [layout][vertical][reverse] {

+  -ms-flex-direction: column-reverse;

+  -webkit-flex-direction: column-reverse;

+  flex-direction: column-reverse;

+}

+

+html /deep/ [layout][wrap] {

+  -ms-flex-wrap: wrap;

+  -webkit-flex-wrap: wrap;

+  flex-wrap: wrap;

+}

+

+html /deep/ [layout][wrap-reverse] {

+  -ms-flex-wrap: wrap-reverse;

+  -webkit-flex-wrap: wrap-reverse;

+  flex-wrap: wrap-reverse;

+}

+

+html /deep/ [flex] {

+  -ms-flex: 1 1 0.000000001px;

+  -webkit-flex: 1;

+  flex: 1;

+  -webkit-flex-basis: 0.000000001px;

+  flex-basis: 0.000000001px;

+}

+

+html /deep/ [vertical][layout] > [flex][auto-vertical], html /deep/ [vertical][layout]::shadow [flex][auto-vertical] {

+  -ms-flex: 1 1 auto;

+  -webkit-flex-basis: auto;

+  flex-basis: auto;

+}

+

+html /deep/ [flex][auto] {

+  -ms-flex: 1 1 auto;

+  -webkit-flex-basis: auto;

+  flex-basis: auto;

+}

+

+html /deep/ [flex][none] {

+  -ms-flex: none;

+  -webkit-flex: none;

+  flex: none;

+}

+

+html /deep/ [flex][one] {

+  -ms-flex: 1;

+  -webkit-flex: 1;

+  flex: 1;

+}

+

+html /deep/ [flex][two] {

+  -ms-flex: 2;

+  -webkit-flex: 2;

+  flex: 2;

+}

+

+html /deep/ [flex][three] {

+  -ms-flex: 3;

+  -webkit-flex: 3;

+  flex: 3;

+}

+

+html /deep/ [flex][four] {

+  -ms-flex: 4;

+  -webkit-flex: 4;

+  flex: 4;

+}

+

+html /deep/ [flex][five] {

+  -ms-flex: 5;

+  -webkit-flex: 5;

+  flex: 5;

+}

+

+html /deep/ [flex][six] {

+  -ms-flex: 6;

+  -webkit-flex: 6;

+  flex: 6;

+}

+

+html /deep/ [flex][seven] {

+  -ms-flex: 7;

+  -webkit-flex: 7;

+  flex: 7;

+}

+

+html /deep/ [flex][eight] {

+  -ms-flex: 8;

+  -webkit-flex: 8;

+  flex: 8;

+}

+

+html /deep/ [flex][nine] {

+  -ms-flex: 9;

+  -webkit-flex: 9;

+  flex: 9;

+}

+

+html /deep/ [flex][ten] {

+  -ms-flex: 10;

+  -webkit-flex: 10;

+  flex: 10;

+}

+

+html /deep/ [flex][eleven] {

+  -ms-flex: 11;

+  -webkit-flex: 11;

+  flex: 11;

+}

+

+html /deep/ [flex][twelve] {

+  -ms-flex: 12;

+  -webkit-flex: 12;

+  flex: 12;

+}

+

+/* alignment in cross axis */

+

+html /deep/ [layout][start] {

+  -ms-flex-align: start;

+  -webkit-align-items: flex-start;

+  align-items: flex-start;

+}

+

+html /deep/ [layout][center], html /deep/ [layout][center-center] {

+  -ms-flex-align: center;

+  -webkit-align-items: center;

+  align-items: center;

+}

+

+html /deep/ [layout][end] {

+  -ms-flex-align: end;

+  -webkit-align-items: flex-end;

+  align-items: flex-end;

+}

+

+/* alignment in main axis */

+

+html /deep/ [layout][start-justified] {

+  -ms-flex-pack: start;

+  -webkit-justify-content: flex-start;

+  justify-content: flex-start;

+}

+

+html /deep/ [layout][center-justified], html /deep/ [layout][center-center] {

+  -ms-flex-pack: center;

+  -webkit-justify-content: center;

+  justify-content: center;

+}

+

+html /deep/ [layout][end-justified] {

+  -ms-flex-pack: end;

+  -webkit-justify-content: flex-end;

+  justify-content: flex-end;

+}

+

+html /deep/ [layout][around-justified] {

+  -ms-flex-pack: distribute;

+  -webkit-justify-content: space-around;

+  justify-content: space-around;

+}

+

+html /deep/ [layout][justified] {

+  -ms-flex-pack: justify;

+  -webkit-justify-content: space-between;

+  justify-content: space-between;

+}

+

+/* self alignment */

+

+html /deep/ [self-start] {

+  -ms-align-self: flex-start;

+  -webkit-align-self: flex-start;

+  align-self: flex-start;

+}

+

+html /deep/ [self-center] {

+  -ms-align-self: center;

+  -webkit-align-self: center;

+  align-self: center;

+}

+

+html /deep/ [self-end] {

+  -ms-align-self: flex-end;

+  -webkit-align-self: flex-end;

+  align-self: flex-end;

+}

+

+html /deep/ [self-stretch] {

+  -ms-align-self: stretch;

+  -webkit-align-self: stretch;

+  align-self: stretch;

+}

+

+/*******************************

+          Other Layout

+*******************************/

+

+html /deep/ [block] {

+  display: block;

+}

+

+/* ie support for hidden */

+html /deep/ [hidden] {

+  display: none !important;

+}

+

+html /deep/ [relative] {

+  position: relative;

+}

+

+html /deep/ [fit] {

+  position: absolute;

+  top: 0;

+  right: 0;

+  bottom: 0;

+  left: 0;

+}

+

+body[fullbleed] {

+  margin: 0;

+  height: 100vh;

+}

+

+/*******************************

+            Other

+*******************************/

+

+html /deep/ [segment], html /deep/ segment {

+  display: block;

+  position: relative;

+  -webkit-box-sizing: border-box;

+  -ms-box-sizing: border-box;

+  box-sizing: border-box;

+  margin: 1em 0.5em;

+  padding: 1em;

+  background-color: white;

+  -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);

+  box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);

+  border-radius: 5px 5px 5px 5px;

+}

+

+</style>
\ No newline at end of file
diff --git a/polymer_interop/lib/src/js/polymer.html b/polymer_interop/lib/src/js/polymer.html
new file mode 100644
index 0000000..8b38742
--- /dev/null
+++ b/polymer_interop/lib/src/js/polymer.html
@@ -0,0 +1,12 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+
+<link rel="import" href="layout.html">
+
+<script src="polymer.min.js"></script>
diff --git a/polymer_interop/lib/src/js/polymer.js b/polymer_interop/lib/src/js/polymer.js
new file mode 100644
index 0000000..f3df2e8
--- /dev/null
+++ b/polymer_interop/lib/src/js/polymer.js
@@ -0,0 +1,11859 @@
+/**
+ * @license
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+ * Code distributed by Google as part of the polymer project is also
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+ */
+// @version 0.5.5
+window.PolymerGestures = {};
+
+(function(scope) {
+  var hasFullPath = false;
+
+  // test for full event path support
+  var pathTest = document.createElement('meta');
+  if (pathTest.createShadowRoot) {
+    var sr = pathTest.createShadowRoot();
+    var s = document.createElement('span');
+    sr.appendChild(s);
+    pathTest.addEventListener('testpath', function(ev) {
+      if (ev.path) {
+        // if the span is in the event path, then path[0] is the real source for all events
+        hasFullPath = ev.path[0] === s;
+      }
+      ev.stopPropagation();
+    });
+    var ev = new CustomEvent('testpath', {bubbles: true});
+    // must add node to DOM to trigger event listener
+    document.head.appendChild(pathTest);
+    s.dispatchEvent(ev);
+    pathTest.parentNode.removeChild(pathTest);
+    sr = s = null;
+  }
+  pathTest = null;
+
+  var target = {
+    shadow: function(inEl) {
+      if (inEl) {
+        return inEl.shadowRoot || inEl.webkitShadowRoot;
+      }
+    },
+    canTarget: function(shadow) {
+      return shadow && Boolean(shadow.elementFromPoint);
+    },
+    targetingShadow: function(inEl) {
+      var s = this.shadow(inEl);
+      if (this.canTarget(s)) {
+        return s;
+      }
+    },
+    olderShadow: function(shadow) {
+      var os = shadow.olderShadowRoot;
+      if (!os) {
+        var se = shadow.querySelector('shadow');
+        if (se) {
+          os = se.olderShadowRoot;
+        }
+      }
+      return os;
+    },
+    allShadows: function(element) {
+      var shadows = [], s = this.shadow(element);
+      while(s) {
+        shadows.push(s);
+        s = this.olderShadow(s);
+      }
+      return shadows;
+    },
+    searchRoot: function(inRoot, x, y) {
+      var t, st, sr, os;
+      if (inRoot) {
+        t = inRoot.elementFromPoint(x, y);
+        if (t) {
+          // found element, check if it has a ShadowRoot
+          sr = this.targetingShadow(t);
+        } else if (inRoot !== document) {
+          // check for sibling roots
+          sr = this.olderShadow(inRoot);
+        }
+        // search other roots, fall back to light dom element
+        return this.searchRoot(sr, x, y) || t;
+      }
+    },
+    owner: function(element) {
+      if (!element) {
+        return document;
+      }
+      var s = element;
+      // walk up until you hit the shadow root or document
+      while (s.parentNode) {
+        s = s.parentNode;
+      }
+      // the owner element is expected to be a Document or ShadowRoot
+      if (s.nodeType != Node.DOCUMENT_NODE && s.nodeType != Node.DOCUMENT_FRAGMENT_NODE) {
+        s = document;
+      }
+      return s;
+    },
+    findTarget: function(inEvent) {
+      if (hasFullPath && inEvent.path && inEvent.path.length) {
+        return inEvent.path[0];
+      }
+      var x = inEvent.clientX, y = inEvent.clientY;
+      // if the listener is in the shadow root, it is much faster to start there
+      var s = this.owner(inEvent.target);
+      // if x, y is not in this root, fall back to document search
+      if (!s.elementFromPoint(x, y)) {
+        s = document;
+      }
+      return this.searchRoot(s, x, y);
+    },
+    findTouchAction: function(inEvent) {
+      var n;
+      if (hasFullPath && inEvent.path && inEvent.path.length) {
+        var path = inEvent.path;
+        for (var i = 0; i < path.length; i++) {
+          n = path[i];
+          if (n.nodeType === Node.ELEMENT_NODE && n.hasAttribute('touch-action')) {
+            return n.getAttribute('touch-action');
+          }
+        }
+      } else {
+        n = inEvent.target;
+        while(n) {
+          if (n.nodeType === Node.ELEMENT_NODE && n.hasAttribute('touch-action')) {
+            return n.getAttribute('touch-action');
+          }
+          n = n.parentNode || n.host;
+        }
+      }
+      // auto is default
+      return "auto";
+    },
+    LCA: function(a, b) {
+      if (a === b) {
+        return a;
+      }
+      if (a && !b) {
+        return a;
+      }
+      if (b && !a) {
+        return b;
+      }
+      if (!b && !a) {
+        return document;
+      }
+      // fast case, a is a direct descendant of b or vice versa
+      if (a.contains && a.contains(b)) {
+        return a;
+      }
+      if (b.contains && b.contains(a)) {
+        return b;
+      }
+      var adepth = this.depth(a);
+      var bdepth = this.depth(b);
+      var d = adepth - bdepth;
+      if (d >= 0) {
+        a = this.walk(a, d);
+      } else {
+        b = this.walk(b, -d);
+      }
+      while (a && b && a !== b) {
+        a = a.parentNode || a.host;
+        b = b.parentNode || b.host;
+      }
+      return a;
+    },
+    walk: function(n, u) {
+      for (var i = 0; n && (i < u); i++) {
+        n = n.parentNode || n.host;
+      }
+      return n;
+    },
+    depth: function(n) {
+      var d = 0;
+      while(n) {
+        d++;
+        n = n.parentNode || n.host;
+      }
+      return d;
+    },
+    deepContains: function(a, b) {
+      var common = this.LCA(a, b);
+      // if a is the common ancestor, it must "deeply" contain b
+      return common === a;
+    },
+    insideNode: function(node, x, y) {
+      var rect = node.getBoundingClientRect();
+      return (rect.left <= x) && (x <= rect.right) && (rect.top <= y) && (y <= rect.bottom);
+    },
+    path: function(event) {
+      var p;
+      if (hasFullPath && event.path && event.path.length) {
+        p = event.path;
+      } else {
+        p = [];
+        var n = this.findTarget(event);
+        while (n) {
+          p.push(n);
+          n = n.parentNode || n.host;
+        }
+      }
+      return p;
+    }
+  };
+  scope.targetFinding = target;
+  /**
+   * Given an event, finds the "deepest" node that could have been the original target before ShadowDOM retargetting
+   *
+   * @param {Event} Event An event object with clientX and clientY properties
+   * @return {Element} The probable event origninator
+   */
+  scope.findTarget = target.findTarget.bind(target);
+  /**
+   * Determines if the "container" node deeply contains the "containee" node, including situations where the "containee" is contained by one or more ShadowDOM
+   * roots.
+   *
+   * @param {Node} container
+   * @param {Node} containee
+   * @return {Boolean}
+   */
+  scope.deepContains = target.deepContains.bind(target);
+
+  /**
+   * Determines if the x/y position is inside the given node.
+   *
+   * Example:
+   *
+   *     function upHandler(event) {
+   *       var innode = PolymerGestures.insideNode(event.target, event.clientX, event.clientY);
+   *       if (innode) {
+   *         // wait for tap?
+   *       } else {
+   *         // tap will never happen
+   *       }
+   *     }
+   *
+   * @param {Node} node
+   * @param {Number} x Screen X position
+   * @param {Number} y screen Y position
+   * @return {Boolean}
+   */
+  scope.insideNode = target.insideNode;
+
+})(window.PolymerGestures);
+
+(function() {
+  function shadowSelector(v) {
+    return 'html /deep/ ' + selector(v);
+  }
+  function selector(v) {
+    return '[touch-action="' + v + '"]';
+  }
+  function rule(v) {
+    return '{ -ms-touch-action: ' + v + '; touch-action: ' + v + ';}';
+  }
+  var attrib2css = [
+    'none',
+    'auto',
+    'pan-x',
+    'pan-y',
+    {
+      rule: 'pan-x pan-y',
+      selectors: [
+        'pan-x pan-y',
+        'pan-y pan-x'
+      ]
+    },
+    'manipulation'
+  ];
+  var styles = '';
+  // only install stylesheet if the browser has touch action support
+  var hasTouchAction = typeof document.head.style.touchAction === 'string';
+  // only add shadow selectors if shadowdom is supported
+  var hasShadowRoot = !window.ShadowDOMPolyfill && document.head.createShadowRoot;
+
+  if (hasTouchAction) {
+    attrib2css.forEach(function(r) {
+      if (String(r) === r) {
+        styles += selector(r) + rule(r) + '\n';
+        if (hasShadowRoot) {
+          styles += shadowSelector(r) + rule(r) + '\n';
+        }
+      } else {
+        styles += r.selectors.map(selector) + rule(r.rule) + '\n';
+        if (hasShadowRoot) {
+          styles += r.selectors.map(shadowSelector) + rule(r.rule) + '\n';
+        }
+      }
+    });
+
+    var el = document.createElement('style');
+    el.textContent = styles;
+    document.head.appendChild(el);
+  }
+})();
+
+/**
+ * This is the constructor for new PointerEvents.
+ *
+ * New Pointer Events must be given a type, and an optional dictionary of
+ * initialization properties.
+ *
+ * Due to certain platform requirements, events returned from the constructor
+ * identify as MouseEvents.
+ *
+ * @constructor
+ * @param {String} inType The type of the event to create.
+ * @param {Object} [inDict] An optional dictionary of initial event properties.
+ * @return {Event} A new PointerEvent of type `inType` and initialized with properties from `inDict`.
+ */
+(function(scope) {
+
+  var MOUSE_PROPS = [
+    'bubbles',
+    'cancelable',
+    'view',
+    'detail',
+    'screenX',
+    'screenY',
+    'clientX',
+    'clientY',
+    'ctrlKey',
+    'altKey',
+    'shiftKey',
+    'metaKey',
+    'button',
+    'relatedTarget',
+    'pageX',
+    'pageY'
+  ];
+
+  var MOUSE_DEFAULTS = [
+    false,
+    false,
+    null,
+    null,
+    0,
+    0,
+    0,
+    0,
+    false,
+    false,
+    false,
+    false,
+    0,
+    null,
+    0,
+    0
+  ];
+
+  var NOP_FACTORY = function(){ return function(){}; };
+
+  var eventFactory = {
+    // TODO(dfreedm): this is overridden by tap recognizer, needs review
+    preventTap: NOP_FACTORY,
+    makeBaseEvent: function(inType, inDict) {
+      var e = document.createEvent('Event');
+      e.initEvent(inType, inDict.bubbles || false, inDict.cancelable || false);
+      e.preventTap = eventFactory.preventTap(e);
+      return e;
+    },
+    makeGestureEvent: function(inType, inDict) {
+      inDict = inDict || Object.create(null);
+
+      var e = this.makeBaseEvent(inType, inDict);
+      for (var i = 0, keys = Object.keys(inDict), k; i < keys.length; i++) {
+        k = keys[i];
+        if( k !== 'bubbles' && k !== 'cancelable' ) {
+           e[k] = inDict[k];
+        }
+      }
+      return e;
+    },
+    makePointerEvent: function(inType, inDict) {
+      inDict = inDict || Object.create(null);
+
+      var e = this.makeBaseEvent(inType, inDict);
+      // define inherited MouseEvent properties
+      for(var i = 2, p; i < MOUSE_PROPS.length; i++) {
+        p = MOUSE_PROPS[i];
+        e[p] = inDict[p] || MOUSE_DEFAULTS[i];
+      }
+      e.buttons = inDict.buttons || 0;
+
+      // Spec requires that pointers without pressure specified use 0.5 for down
+      // state and 0 for up state.
+      var pressure = 0;
+      if (inDict.pressure) {
+        pressure = inDict.pressure;
+      } else {
+        pressure = e.buttons ? 0.5 : 0;
+      }
+
+      // add x/y properties aliased to clientX/Y
+      e.x = e.clientX;
+      e.y = e.clientY;
+
+      // define the properties of the PointerEvent interface
+      e.pointerId = inDict.pointerId || 0;
+      e.width = inDict.width || 0;
+      e.height = inDict.height || 0;
+      e.pressure = pressure;
+      e.tiltX = inDict.tiltX || 0;
+      e.tiltY = inDict.tiltY || 0;
+      e.pointerType = inDict.pointerType || '';
+      e.hwTimestamp = inDict.hwTimestamp || 0;
+      e.isPrimary = inDict.isPrimary || false;
+      e._source = inDict._source || '';
+      return e;
+    }
+  };
+
+  scope.eventFactory = eventFactory;
+})(window.PolymerGestures);
+
+/**
+ * This module implements an map of pointer states
+ */
+(function(scope) {
+  var USE_MAP = window.Map && window.Map.prototype.forEach;
+  var POINTERS_FN = function(){ return this.size; };
+  function PointerMap() {
+    if (USE_MAP) {
+      var m = new Map();
+      m.pointers = POINTERS_FN;
+      return m;
+    } else {
+      this.keys = [];
+      this.values = [];
+    }
+  }
+
+  PointerMap.prototype = {
+    set: function(inId, inEvent) {
+      var i = this.keys.indexOf(inId);
+      if (i > -1) {
+        this.values[i] = inEvent;
+      } else {
+        this.keys.push(inId);
+        this.values.push(inEvent);
+      }
+    },
+    has: function(inId) {
+      return this.keys.indexOf(inId) > -1;
+    },
+    'delete': function(inId) {
+      var i = this.keys.indexOf(inId);
+      if (i > -1) {
+        this.keys.splice(i, 1);
+        this.values.splice(i, 1);
+      }
+    },
+    get: function(inId) {
+      var i = this.keys.indexOf(inId);
+      return this.values[i];
+    },
+    clear: function() {
+      this.keys.length = 0;
+      this.values.length = 0;
+    },
+    // return value, key, map
+    forEach: function(callback, thisArg) {
+      this.values.forEach(function(v, i) {
+        callback.call(thisArg, v, this.keys[i], this);
+      }, this);
+    },
+    pointers: function() {
+      return this.keys.length;
+    }
+  };
+
+  scope.PointerMap = PointerMap;
+})(window.PolymerGestures);
+
+(function(scope) {
+  var CLONE_PROPS = [
+    // MouseEvent
+    'bubbles',
+    'cancelable',
+    'view',
+    'detail',
+    'screenX',
+    'screenY',
+    'clientX',
+    'clientY',
+    'ctrlKey',
+    'altKey',
+    'shiftKey',
+    'metaKey',
+    'button',
+    'relatedTarget',
+    // DOM Level 3
+    'buttons',
+    // PointerEvent
+    'pointerId',
+    'width',
+    'height',
+    'pressure',
+    'tiltX',
+    'tiltY',
+    'pointerType',
+    'hwTimestamp',
+    'isPrimary',
+    // event instance
+    'type',
+    'target',
+    'currentTarget',
+    'which',
+    'pageX',
+    'pageY',
+    'timeStamp',
+    // gesture addons
+    'preventTap',
+    'tapPrevented',
+    '_source'
+  ];
+
+  var CLONE_DEFAULTS = [
+    // MouseEvent
+    false,
+    false,
+    null,
+    null,
+    0,
+    0,
+    0,
+    0,
+    false,
+    false,
+    false,
+    false,
+    0,
+    null,
+    // DOM Level 3
+    0,
+    // PointerEvent
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    '',
+    0,
+    false,
+    // event instance
+    '',
+    null,
+    null,
+    0,
+    0,
+    0,
+    0,
+    function(){},
+    false
+  ];
+
+  var HAS_SVG_INSTANCE = (typeof SVGElementInstance !== 'undefined');
+
+  var eventFactory = scope.eventFactory;
+
+  // set of recognizers to run for the currently handled event
+  var currentGestures;
+
+  /**
+   * This module is for normalizing events. Mouse and Touch events will be
+   * collected here, and fire PointerEvents that have the same semantics, no
+   * matter the source.
+   * Events fired:
+   *   - pointerdown: a pointing is added
+   *   - pointerup: a pointer is removed
+   *   - pointermove: a pointer is moved
+   *   - pointerover: a pointer crosses into an element
+   *   - pointerout: a pointer leaves an element
+   *   - pointercancel: a pointer will no longer generate events
+   */
+  var dispatcher = {
+    IS_IOS: false,
+    pointermap: new scope.PointerMap(),
+    requiredGestures: new scope.PointerMap(),
+    eventMap: Object.create(null),
+    // Scope objects for native events.
+    // This exists for ease of testing.
+    eventSources: Object.create(null),
+    eventSourceList: [],
+    gestures: [],
+    // map gesture event -> {listeners: int, index: gestures[int]}
+    dependencyMap: {
+      // make sure down and up are in the map to trigger "register"
+      down: {listeners: 0, index: -1},
+      up: {listeners: 0, index: -1}
+    },
+    gestureQueue: [],
+    /**
+     * Add a new event source that will generate pointer events.
+     *
+     * `inSource` must contain an array of event names named `events`, and
+     * functions with the names specified in the `events` array.
+     * @param {string} name A name for the event source
+     * @param {Object} source A new source of platform events.
+     */
+    registerSource: function(name, source) {
+      var s = source;
+      var newEvents = s.events;
+      if (newEvents) {
+        newEvents.forEach(function(e) {
+          if (s[e]) {
+            this.eventMap[e] = s[e].bind(s);
+          }
+        }, this);
+        this.eventSources[name] = s;
+        this.eventSourceList.push(s);
+      }
+    },
+    registerGesture: function(name, source) {
+      var obj = Object.create(null);
+      obj.listeners = 0;
+      obj.index = this.gestures.length;
+      for (var i = 0, g; i < source.exposes.length; i++) {
+        g = source.exposes[i].toLowerCase();
+        this.dependencyMap[g] = obj;
+      }
+      this.gestures.push(source);
+    },
+    register: function(element, initial) {
+      var l = this.eventSourceList.length;
+      for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
+        // call eventsource register
+        es.register.call(es, element, initial);
+      }
+    },
+    unregister: function(element) {
+      var l = this.eventSourceList.length;
+      for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
+        // call eventsource register
+        es.unregister.call(es, element);
+      }
+    },
+    // EVENTS
+    down: function(inEvent) {
+      this.requiredGestures.set(inEvent.pointerId, currentGestures);
+      this.fireEvent('down', inEvent);
+    },
+    move: function(inEvent) {
+      // pipe move events into gesture queue directly
+      inEvent.type = 'move';
+      this.fillGestureQueue(inEvent);
+    },
+    up: function(inEvent) {
+      this.fireEvent('up', inEvent);
+      this.requiredGestures.delete(inEvent.pointerId);
+    },
+    cancel: function(inEvent) {
+      inEvent.tapPrevented = true;
+      this.fireEvent('up', inEvent);
+      this.requiredGestures.delete(inEvent.pointerId);
+    },
+    addGestureDependency: function(node, currentGestures) {
+      var gesturesWanted = node._pgEvents;
+      if (gesturesWanted && currentGestures) {
+        var gk = Object.keys(gesturesWanted);
+        for (var i = 0, r, ri, g; i < gk.length; i++) {
+          // gesture
+          g = gk[i];
+          if (gesturesWanted[g] > 0) {
+            // lookup gesture recognizer
+            r = this.dependencyMap[g];
+            // recognizer index
+            ri = r ? r.index : -1;
+            currentGestures[ri] = true;
+          }
+        }
+      }
+    },
+    // LISTENER LOGIC
+    eventHandler: function(inEvent) {
+      // This is used to prevent multiple dispatch of events from
+      // platform events. This can happen when two elements in different scopes
+      // are set up to create pointer events, which is relevant to Shadow DOM.
+
+      var type = inEvent.type;
+
+      // only generate the list of desired events on "down"
+      if (type === 'touchstart' || type === 'mousedown' || type === 'pointerdown' || type === 'MSPointerDown') {
+        if (!inEvent._handledByPG) {
+          currentGestures = {};
+        }
+
+        // in IOS mode, there is only a listener on the document, so this is not re-entrant
+        if (this.IS_IOS) {
+          var ev = inEvent;
+          if (type === 'touchstart') {
+            var ct = inEvent.changedTouches[0];
+            // set up a fake event to give to the path builder
+            ev = {target: inEvent.target, clientX: ct.clientX, clientY: ct.clientY, path: inEvent.path};
+          }
+          // use event path if available, otherwise build a path from target finding
+          var nodes = inEvent.path || scope.targetFinding.path(ev);
+          for (var i = 0, n; i < nodes.length; i++) {
+            n = nodes[i];
+            this.addGestureDependency(n, currentGestures);
+          }
+        } else {
+          this.addGestureDependency(inEvent.currentTarget, currentGestures);
+        }
+      }
+
+      if (inEvent._handledByPG) {
+        return;
+      }
+      var fn = this.eventMap && this.eventMap[type];
+      if (fn) {
+        fn(inEvent);
+      }
+      inEvent._handledByPG = true;
+    },
+    // set up event listeners
+    listen: function(target, events) {
+      for (var i = 0, l = events.length, e; (i < l) && (e = events[i]); i++) {
+        this.addEvent(target, e);
+      }
+    },
+    // remove event listeners
+    unlisten: function(target, events) {
+      for (var i = 0, l = events.length, e; (i < l) && (e = events[i]); i++) {
+        this.removeEvent(target, e);
+      }
+    },
+    addEvent: function(target, eventName) {
+      target.addEventListener(eventName, this.boundHandler);
+    },
+    removeEvent: function(target, eventName) {
+      target.removeEventListener(eventName, this.boundHandler);
+    },
+    // EVENT CREATION AND TRACKING
+    /**
+     * Creates a new Event of type `inType`, based on the information in
+     * `inEvent`.
+     *
+     * @param {string} inType A string representing the type of event to create
+     * @param {Event} inEvent A platform event with a target
+     * @return {Event} A PointerEvent of type `inType`
+     */
+    makeEvent: function(inType, inEvent) {
+      var e = eventFactory.makePointerEvent(inType, inEvent);
+      e.preventDefault = inEvent.preventDefault;
+      e.tapPrevented = inEvent.tapPrevented;
+      e._target = e._target || inEvent.target;
+      return e;
+    },
+    // make and dispatch an event in one call
+    fireEvent: function(inType, inEvent) {
+      var e = this.makeEvent(inType, inEvent);
+      return this.dispatchEvent(e);
+    },
+    /**
+     * Returns a snapshot of inEvent, with writable properties.
+     *
+     * @param {Event} inEvent An event that contains properties to copy.
+     * @return {Object} An object containing shallow copies of `inEvent`'s
+     *    properties.
+     */
+    cloneEvent: function(inEvent) {
+      var eventCopy = Object.create(null), p;
+      for (var i = 0; i < CLONE_PROPS.length; i++) {
+        p = CLONE_PROPS[i];
+        eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i];
+        // Work around SVGInstanceElement shadow tree
+        // Return the <use> element that is represented by the instance for Safari, Chrome, IE.
+        // This is the behavior implemented by Firefox.
+        if (p === 'target' || p === 'relatedTarget') {
+          if (HAS_SVG_INSTANCE && eventCopy[p] instanceof SVGElementInstance) {
+            eventCopy[p] = eventCopy[p].correspondingUseElement;
+          }
+        }
+      }
+      // keep the semantics of preventDefault
+      eventCopy.preventDefault = function() {
+        inEvent.preventDefault();
+      };
+      return eventCopy;
+    },
+    /**
+     * Dispatches the event to its target.
+     *
+     * @param {Event} inEvent The event to be dispatched.
+     * @return {Boolean} True if an event handler returns true, false otherwise.
+     */
+    dispatchEvent: function(inEvent) {
+      var t = inEvent._target;
+      if (t) {
+        t.dispatchEvent(inEvent);
+        // clone the event for the gesture system to process
+        // clone after dispatch to pick up gesture prevention code
+        var clone = this.cloneEvent(inEvent);
+        clone.target = t;
+        this.fillGestureQueue(clone);
+      }
+    },
+    gestureTrigger: function() {
+      // process the gesture queue
+      for (var i = 0, e, rg; i < this.gestureQueue.length; i++) {
+        e = this.gestureQueue[i];
+        rg = e._requiredGestures;
+        if (rg) {
+          for (var j = 0, g, fn; j < this.gestures.length; j++) {
+            // only run recognizer if an element in the source event's path is listening for those gestures
+            if (rg[j]) {
+              g = this.gestures[j];
+              fn = g[e.type];
+              if (fn) {
+                fn.call(g, e);
+              }
+            }
+          }
+        }
+      }
+      this.gestureQueue.length = 0;
+    },
+    fillGestureQueue: function(ev) {
+      // only trigger the gesture queue once
+      if (!this.gestureQueue.length) {
+        requestAnimationFrame(this.boundGestureTrigger);
+      }
+      ev._requiredGestures = this.requiredGestures.get(ev.pointerId);
+      this.gestureQueue.push(ev);
+    }
+  };
+  dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher);
+  dispatcher.boundGestureTrigger = dispatcher.gestureTrigger.bind(dispatcher);
+  scope.dispatcher = dispatcher;
+
+  /**
+   * Listen for `gesture` on `node` with the `handler` function
+   *
+   * If `handler` is the first listener for `gesture`, the underlying gesture recognizer is then enabled.
+   *
+   * @param {Element} node
+   * @param {string} gesture
+   * @return Boolean `gesture` is a valid gesture
+   */
+  scope.activateGesture = function(node, gesture) {
+    var g = gesture.toLowerCase();
+    var dep = dispatcher.dependencyMap[g];
+    if (dep) {
+      var recognizer = dispatcher.gestures[dep.index];
+      if (!node._pgListeners) {
+        dispatcher.register(node);
+        node._pgListeners = 0;
+      }
+      // TODO(dfreedm): re-evaluate bookkeeping to avoid using attributes
+      if (recognizer) {
+        var touchAction = recognizer.defaultActions && recognizer.defaultActions[g];
+        var actionNode;
+        switch(node.nodeType) {
+          case Node.ELEMENT_NODE:
+            actionNode = node;
+          break;
+          case Node.DOCUMENT_FRAGMENT_NODE:
+            actionNode = node.host;
+          break;
+          default:
+            actionNode = null;
+          break;
+        }
+        if (touchAction && actionNode && !actionNode.hasAttribute('touch-action')) {
+          actionNode.setAttribute('touch-action', touchAction);
+        }
+      }
+      if (!node._pgEvents) {
+        node._pgEvents = {};
+      }
+      node._pgEvents[g] = (node._pgEvents[g] || 0) + 1;
+      node._pgListeners++;
+    }
+    return Boolean(dep);
+  };
+
+  /**
+   *
+   * Listen for `gesture` from `node` with `handler` function.
+   *
+   * @param {Element} node
+   * @param {string} gesture
+   * @param {Function} handler
+   * @param {Boolean} capture
+   */
+  scope.addEventListener = function(node, gesture, handler, capture) {
+    if (handler) {
+      scope.activateGesture(node, gesture);
+      node.addEventListener(gesture, handler, capture);
+    }
+  };
+
+  /**
+   * Tears down the gesture configuration for `node`
+   *
+   * If `handler` is the last listener for `gesture`, the underlying gesture recognizer is disabled.
+   *
+   * @param {Element} node
+   * @param {string} gesture
+   * @return Boolean `gesture` is a valid gesture
+   */
+  scope.deactivateGesture = function(node, gesture) {
+    var g = gesture.toLowerCase();
+    var dep = dispatcher.dependencyMap[g];
+    if (dep) {
+      if (node._pgListeners > 0) {
+        node._pgListeners--;
+      }
+      if (node._pgListeners === 0) {
+        dispatcher.unregister(node);
+      }
+      if (node._pgEvents) {
+        if (node._pgEvents[g] > 0) {
+          node._pgEvents[g]--;
+        } else {
+          node._pgEvents[g] = 0;
+        }
+      }
+    }
+    return Boolean(dep);
+  };
+
+  /**
+   * Stop listening for `gesture` from `node` with `handler` function.
+   *
+   * @param {Element} node
+   * @param {string} gesture
+   * @param {Function} handler
+   * @param {Boolean} capture
+   */
+  scope.removeEventListener = function(node, gesture, handler, capture) {
+    if (handler) {
+      scope.deactivateGesture(node, gesture);
+      node.removeEventListener(gesture, handler, capture);
+    }
+  };
+})(window.PolymerGestures);
+
+(function(scope) {
+  var dispatcher = scope.dispatcher;
+  var pointermap = dispatcher.pointermap;
+  // radius around touchend that swallows mouse events
+  var DEDUP_DIST = 25;
+
+  var WHICH_TO_BUTTONS = [0, 1, 4, 2];
+
+  var currentButtons = 0;
+
+  var FIREFOX_LINUX = /Linux.*Firefox\//i;
+
+  var HAS_BUTTONS = (function() {
+    // firefox on linux returns spec-incorrect values for mouseup.buttons
+    // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.buttons#See_also
+    // https://codereview.chromium.org/727593003/#msg16
+    if (FIREFOX_LINUX.test(navigator.userAgent)) {
+      return false;
+    }
+    try {
+      return new MouseEvent('test', {buttons: 1}).buttons === 1;
+    } catch (e) {
+      return false;
+    }
+  })();
+
+  // handler block for native mouse events
+  var mouseEvents = {
+    POINTER_ID: 1,
+    POINTER_TYPE: 'mouse',
+    events: [
+      'mousedown',
+      'mousemove',
+      'mouseup'
+    ],
+    exposes: [
+      'down',
+      'up',
+      'move'
+    ],
+    register: function(target) {
+      dispatcher.listen(target, this.events);
+    },
+    unregister: function(target) {
+      if (target.nodeType === Node.DOCUMENT_NODE) {
+        return;
+      }
+      dispatcher.unlisten(target, this.events);
+    },
+    lastTouches: [],
+    // collide with the global mouse listener
+    isEventSimulatedFromTouch: function(inEvent) {
+      var lts = this.lastTouches;
+      var x = inEvent.clientX, y = inEvent.clientY;
+      for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
+        // simulated mouse events will be swallowed near a primary touchend
+        var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
+        if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) {
+          return true;
+        }
+      }
+    },
+    prepareEvent: function(inEvent) {
+      var e = dispatcher.cloneEvent(inEvent);
+      e.pointerId = this.POINTER_ID;
+      e.isPrimary = true;
+      e.pointerType = this.POINTER_TYPE;
+      e._source = 'mouse';
+      if (!HAS_BUTTONS) {
+        var type = inEvent.type;
+        var bit = WHICH_TO_BUTTONS[inEvent.which] || 0;
+        if (type === 'mousedown') {
+          currentButtons |= bit;
+        } else if (type === 'mouseup') {
+          currentButtons &= ~bit;
+        }
+        e.buttons = currentButtons;
+      }
+      return e;
+    },
+    mousedown: function(inEvent) {
+      if (!this.isEventSimulatedFromTouch(inEvent)) {
+        var p = pointermap.has(this.POINTER_ID);
+        var e = this.prepareEvent(inEvent);
+        e.target = scope.findTarget(inEvent);
+        pointermap.set(this.POINTER_ID, e.target);
+        dispatcher.down(e);
+      }
+    },
+    mousemove: function(inEvent) {
+      if (!this.isEventSimulatedFromTouch(inEvent)) {
+        var target = pointermap.get(this.POINTER_ID);
+        if (target) {
+          var e = this.prepareEvent(inEvent);
+          e.target = target;
+          // handle case where we missed a mouseup
+          if ((HAS_BUTTONS ? e.buttons : e.which) === 0) {
+            if (!HAS_BUTTONS) {
+              currentButtons = e.buttons = 0;
+            }
+            dispatcher.cancel(e);
+            this.cleanupMouse(e.buttons);
+          } else {
+            dispatcher.move(e);
+          }
+        }
+      }
+    },
+    mouseup: function(inEvent) {
+      if (!this.isEventSimulatedFromTouch(inEvent)) {
+        var e = this.prepareEvent(inEvent);
+        e.relatedTarget = scope.findTarget(inEvent);
+        e.target = pointermap.get(this.POINTER_ID);
+        dispatcher.up(e);
+        this.cleanupMouse(e.buttons);
+      }
+    },
+    cleanupMouse: function(buttons) {
+      if (buttons === 0) {
+        pointermap.delete(this.POINTER_ID);
+      }
+    }
+  };
+
+  scope.mouseEvents = mouseEvents;
+})(window.PolymerGestures);
+
+(function(scope) {
+  var dispatcher = scope.dispatcher;
+  var allShadows = scope.targetFinding.allShadows.bind(scope.targetFinding);
+  var pointermap = dispatcher.pointermap;
+  var touchMap = Array.prototype.map.call.bind(Array.prototype.map);
+  // This should be long enough to ignore compat mouse events made by touch
+  var DEDUP_TIMEOUT = 2500;
+  var DEDUP_DIST = 25;
+  var CLICK_COUNT_TIMEOUT = 200;
+  var HYSTERESIS = 20;
+  var ATTRIB = 'touch-action';
+  // TODO(dfreedm): disable until http://crbug.com/399765 is resolved
+  // var HAS_TOUCH_ACTION = ATTRIB in document.head.style;
+  var HAS_TOUCH_ACTION = false;
+
+  // handler block for native touch events
+  var touchEvents = {
+    IS_IOS: false,
+    events: [
+      'touchstart',
+      'touchmove',
+      'touchend',
+      'touchcancel'
+    ],
+    exposes: [
+      'down',
+      'up',
+      'move'
+    ],
+    register: function(target, initial) {
+      if (this.IS_IOS ? initial : !initial) {
+        dispatcher.listen(target, this.events);
+      }
+    },
+    unregister: function(target) {
+      if (!this.IS_IOS) {
+        dispatcher.unlisten(target, this.events);
+      }
+    },
+    scrollTypes: {
+      EMITTER: 'none',
+      XSCROLLER: 'pan-x',
+      YSCROLLER: 'pan-y',
+    },
+    touchActionToScrollType: function(touchAction) {
+      var t = touchAction;
+      var st = this.scrollTypes;
+      if (t === st.EMITTER) {
+        return 'none';
+      } else if (t === st.XSCROLLER) {
+        return 'X';
+      } else if (t === st.YSCROLLER) {
+        return 'Y';
+      } else {
+        return 'XY';
+      }
+    },
+    POINTER_TYPE: 'touch',
+    firstTouch: null,
+    isPrimaryTouch: function(inTouch) {
+      return this.firstTouch === inTouch.identifier;
+    },
+    setPrimaryTouch: function(inTouch) {
+      // set primary touch if there no pointers, or the only pointer is the mouse
+      if (pointermap.pointers() === 0 || (pointermap.pointers() === 1 && pointermap.has(1))) {
+        this.firstTouch = inTouch.identifier;
+        this.firstXY = {X: inTouch.clientX, Y: inTouch.clientY};
+        this.firstTarget = inTouch.target;
+        this.scrolling = null;
+        this.cancelResetClickCount();
+      }
+    },
+    removePrimaryPointer: function(inPointer) {
+      if (inPointer.isPrimary) {
+        this.firstTouch = null;
+        this.firstXY = null;
+        this.resetClickCount();
+      }
+    },
+    clickCount: 0,
+    resetId: null,
+    resetClickCount: function() {
+      var fn = function() {
+        this.clickCount = 0;
+        this.resetId = null;
+      }.bind(this);
+      this.resetId = setTimeout(fn, CLICK_COUNT_TIMEOUT);
+    },
+    cancelResetClickCount: function() {
+      if (this.resetId) {
+        clearTimeout(this.resetId);
+      }
+    },
+    typeToButtons: function(type) {
+      var ret = 0;
+      if (type === 'touchstart' || type === 'touchmove') {
+        ret = 1;
+      }
+      return ret;
+    },
+    findTarget: function(touch, id) {
+      if (this.currentTouchEvent.type === 'touchstart') {
+        if (this.isPrimaryTouch(touch)) {
+          var fastPath = {
+            clientX: touch.clientX,
+            clientY: touch.clientY,
+            path: this.currentTouchEvent.path,
+            target: this.currentTouchEvent.target
+          };
+          return scope.findTarget(fastPath);
+        } else {
+          return scope.findTarget(touch);
+        }
+      }
+      // reuse target we found in touchstart
+      return pointermap.get(id);
+    },
+    touchToPointer: function(inTouch) {
+      var cte = this.currentTouchEvent;
+      var e = dispatcher.cloneEvent(inTouch);
+      // Spec specifies that pointerId 1 is reserved for Mouse.
+      // Touch identifiers can start at 0.
+      // Add 2 to the touch identifier for compatibility.
+      var id = e.pointerId = inTouch.identifier + 2;
+      e.target = this.findTarget(inTouch, id);
+      e.bubbles = true;
+      e.cancelable = true;
+      e.detail = this.clickCount;
+      e.buttons = this.typeToButtons(cte.type);
+      e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0;
+      e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0;
+      e.pressure = inTouch.webkitForce || inTouch.force || 0.5;
+      e.isPrimary = this.isPrimaryTouch(inTouch);
+      e.pointerType = this.POINTER_TYPE;
+      e._source = 'touch';
+      // forward touch preventDefaults
+      var self = this;
+      e.preventDefault = function() {
+        self.scrolling = false;
+        self.firstXY = null;
+        cte.preventDefault();
+      };
+      return e;
+    },
+    processTouches: function(inEvent, inFunction) {
+      var tl = inEvent.changedTouches;
+      this.currentTouchEvent = inEvent;
+      for (var i = 0, t, p; i < tl.length; i++) {
+        t = tl[i];
+        p = this.touchToPointer(t);
+        if (inEvent.type === 'touchstart') {
+          pointermap.set(p.pointerId, p.target);
+        }
+        if (pointermap.has(p.pointerId)) {
+          inFunction.call(this, p);
+        }
+        if (inEvent.type === 'touchend' || inEvent._cancel) {
+          this.cleanUpPointer(p);
+        }
+      }
+    },
+    // For single axis scrollers, determines whether the element should emit
+    // pointer events or behave as a scroller
+    shouldScroll: function(inEvent) {
+      if (this.firstXY) {
+        var ret;
+        var touchAction = scope.targetFinding.findTouchAction(inEvent);
+        var scrollAxis = this.touchActionToScrollType(touchAction);
+        if (scrollAxis === 'none') {
+          // this element is a touch-action: none, should never scroll
+          ret = false;
+        } else if (scrollAxis === 'XY') {
+          // this element should always scroll
+          ret = true;
+        } else {
+          var t = inEvent.changedTouches[0];
+          // check the intended scroll axis, and other axis
+          var a = scrollAxis;
+          var oa = scrollAxis === 'Y' ? 'X' : 'Y';
+          var da = Math.abs(t['client' + a] - this.firstXY[a]);
+          var doa = Math.abs(t['client' + oa] - this.firstXY[oa]);
+          // if delta in the scroll axis > delta other axis, scroll instead of
+          // making events
+          ret = da >= doa;
+        }
+        return ret;
+      }
+    },
+    findTouch: function(inTL, inId) {
+      for (var i = 0, l = inTL.length, t; i < l && (t = inTL[i]); i++) {
+        if (t.identifier === inId) {
+          return true;
+        }
+      }
+    },
+    // In some instances, a touchstart can happen without a touchend. This
+    // leaves the pointermap in a broken state.
+    // Therefore, on every touchstart, we remove the touches that did not fire a
+    // touchend event.
+    // To keep state globally consistent, we fire a
+    // pointercancel for this "abandoned" touch
+    vacuumTouches: function(inEvent) {
+      var tl = inEvent.touches;
+      // pointermap.pointers() should be < tl.length here, as the touchstart has not
+      // been processed yet.
+      if (pointermap.pointers() >= tl.length) {
+        var d = [];
+        pointermap.forEach(function(value, key) {
+          // Never remove pointerId == 1, which is mouse.
+          // Touch identifiers are 2 smaller than their pointerId, which is the
+          // index in pointermap.
+          if (key !== 1 && !this.findTouch(tl, key - 2)) {
+            var p = value;
+            d.push(p);
+          }
+        }, this);
+        d.forEach(function(p) {
+          this.cancel(p);
+          pointermap.delete(p.pointerId);
+        }, this);
+      }
+    },
+    touchstart: function(inEvent) {
+      this.vacuumTouches(inEvent);
+      this.setPrimaryTouch(inEvent.changedTouches[0]);
+      this.dedupSynthMouse(inEvent);
+      if (!this.scrolling) {
+        this.clickCount++;
+        this.processTouches(inEvent, this.down);
+      }
+    },
+    down: function(inPointer) {
+      dispatcher.down(inPointer);
+    },
+    touchmove: function(inEvent) {
+      if (HAS_TOUCH_ACTION) {
+        // touchevent.cancelable == false is sent when the page is scrolling under native Touch Action in Chrome 36
+        // https://groups.google.com/a/chromium.org/d/msg/input-dev/wHnyukcYBcA/b9kmtwM1jJQJ
+        if (inEvent.cancelable) {
+          this.processTouches(inEvent, this.move);
+        }
+      } else {
+        if (!this.scrolling) {
+          if (this.scrolling === null && this.shouldScroll(inEvent)) {
+            this.scrolling = true;
+          } else {
+            this.scrolling = false;
+            inEvent.preventDefault();
+            this.processTouches(inEvent, this.move);
+          }
+        } else if (this.firstXY) {
+          var t = inEvent.changedTouches[0];
+          var dx = t.clientX - this.firstXY.X;
+          var dy = t.clientY - this.firstXY.Y;
+          var dd = Math.sqrt(dx * dx + dy * dy);
+          if (dd >= HYSTERESIS) {
+            this.touchcancel(inEvent);
+            this.scrolling = true;
+            this.firstXY = null;
+          }
+        }
+      }
+    },
+    move: function(inPointer) {
+      dispatcher.move(inPointer);
+    },
+    touchend: function(inEvent) {
+      this.dedupSynthMouse(inEvent);
+      this.processTouches(inEvent, this.up);
+    },
+    up: function(inPointer) {
+      inPointer.relatedTarget = scope.findTarget(inPointer);
+      dispatcher.up(inPointer);
+    },
+    cancel: function(inPointer) {
+      dispatcher.cancel(inPointer);
+    },
+    touchcancel: function(inEvent) {
+      inEvent._cancel = true;
+      this.processTouches(inEvent, this.cancel);
+    },
+    cleanUpPointer: function(inPointer) {
+      pointermap['delete'](inPointer.pointerId);
+      this.removePrimaryPointer(inPointer);
+    },
+    // prevent synth mouse events from creating pointer events
+    dedupSynthMouse: function(inEvent) {
+      var lts = scope.mouseEvents.lastTouches;
+      var t = inEvent.changedTouches[0];
+      // only the primary finger will synth mouse events
+      if (this.isPrimaryTouch(t)) {
+        // remember x/y of last touch
+        var lt = {x: t.clientX, y: t.clientY};
+        lts.push(lt);
+        var fn = (function(lts, lt){
+          var i = lts.indexOf(lt);
+          if (i > -1) {
+            lts.splice(i, 1);
+          }
+        }).bind(null, lts, lt);
+        setTimeout(fn, DEDUP_TIMEOUT);
+      }
+    }
+  };
+
+  // prevent "ghost clicks" that come from elements that were removed in a touch handler
+  var STOP_PROP_FN = Event.prototype.stopImmediatePropagation || Event.prototype.stopPropagation;
+  document.addEventListener('click', function(ev) {
+    var x = ev.clientX, y = ev.clientY;
+    // check if a click is within DEDUP_DIST px radius of the touchstart
+    var closeTo = function(touch) {
+      var dx = Math.abs(x - touch.x), dy = Math.abs(y - touch.y);
+      return (dx <= DEDUP_DIST && dy <= DEDUP_DIST);
+    };
+    // if click coordinates are close to touch coordinates, assume the click came from a touch
+    var wasTouched = scope.mouseEvents.lastTouches.some(closeTo);
+    // if the click came from touch, and the touchstart target is not in the path of the click event,
+    // then the touchstart target was probably removed, and the click should be "busted"
+    var path = scope.targetFinding.path(ev);
+    if (wasTouched) {
+      for (var i = 0; i < path.length; i++) {
+        if (path[i] === touchEvents.firstTarget) {
+          return;
+        }
+      }
+      ev.preventDefault();
+      STOP_PROP_FN.call(ev);
+    }
+  }, true);
+
+  scope.touchEvents = touchEvents;
+})(window.PolymerGestures);
+
+(function(scope) {
+  var dispatcher = scope.dispatcher;
+  var pointermap = dispatcher.pointermap;
+  var HAS_BITMAP_TYPE = window.MSPointerEvent && typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE === 'number';
+  var msEvents = {
+    events: [
+      'MSPointerDown',
+      'MSPointerMove',
+      'MSPointerUp',
+      'MSPointerCancel',
+    ],
+    register: function(target) {
+      dispatcher.listen(target, this.events);
+    },
+    unregister: function(target) {
+      if (target.nodeType === Node.DOCUMENT_NODE) {
+        return;
+      }
+      dispatcher.unlisten(target, this.events);
+    },
+    POINTER_TYPES: [
+      '',
+      'unavailable',
+      'touch',
+      'pen',
+      'mouse'
+    ],
+    prepareEvent: function(inEvent) {
+      var e = inEvent;
+      e = dispatcher.cloneEvent(inEvent);
+      if (HAS_BITMAP_TYPE) {
+        e.pointerType = this.POINTER_TYPES[inEvent.pointerType];
+      }
+      e._source = 'ms';
+      return e;
+    },
+    cleanup: function(id) {
+      pointermap['delete'](id);
+    },
+    MSPointerDown: function(inEvent) {
+      var e = this.prepareEvent(inEvent);
+      e.target = scope.findTarget(inEvent);
+      pointermap.set(inEvent.pointerId, e.target);
+      dispatcher.down(e);
+    },
+    MSPointerMove: function(inEvent) {
+      var target = pointermap.get(inEvent.pointerId);
+      if (target) {
+        var e = this.prepareEvent(inEvent);
+        e.target = target;
+        dispatcher.move(e);
+      }
+    },
+    MSPointerUp: function(inEvent) {
+      var e = this.prepareEvent(inEvent);
+      e.relatedTarget = scope.findTarget(inEvent);
+      e.target = pointermap.get(e.pointerId);
+      dispatcher.up(e);
+      this.cleanup(inEvent.pointerId);
+    },
+    MSPointerCancel: function(inEvent) {
+      var e = this.prepareEvent(inEvent);
+      e.relatedTarget = scope.findTarget(inEvent);
+      e.target = pointermap.get(e.pointerId);
+      dispatcher.cancel(e);
+      this.cleanup(inEvent.pointerId);
+    }
+  };
+
+  scope.msEvents = msEvents;
+})(window.PolymerGestures);
+
+(function(scope) {
+  var dispatcher = scope.dispatcher;
+  var pointermap = dispatcher.pointermap;
+  var pointerEvents = {
+    events: [
+      'pointerdown',
+      'pointermove',
+      'pointerup',
+      'pointercancel'
+    ],
+    prepareEvent: function(inEvent) {
+      var e = dispatcher.cloneEvent(inEvent);
+      e._source = 'pointer';
+      return e;
+    },
+    register: function(target) {
+      dispatcher.listen(target, this.events);
+    },
+    unregister: function(target) {
+      if (target.nodeType === Node.DOCUMENT_NODE) {
+        return;
+      }
+      dispatcher.unlisten(target, this.events);
+    },
+    cleanup: function(id) {
+      pointermap['delete'](id);
+    },
+    pointerdown: function(inEvent) {
+      var e = this.prepareEvent(inEvent);
+      e.target = scope.findTarget(inEvent);
+      pointermap.set(e.pointerId, e.target);
+      dispatcher.down(e);
+    },
+    pointermove: function(inEvent) {
+      var target = pointermap.get(inEvent.pointerId);
+      if (target) {
+        var e = this.prepareEvent(inEvent);
+        e.target = target;
+        dispatcher.move(e);
+      }
+    },
+    pointerup: function(inEvent) {
+      var e = this.prepareEvent(inEvent);
+      e.relatedTarget = scope.findTarget(inEvent);
+      e.target = pointermap.get(e.pointerId);
+      dispatcher.up(e);
+      this.cleanup(inEvent.pointerId);
+    },
+    pointercancel: function(inEvent) {
+      var e = this.prepareEvent(inEvent);
+      e.relatedTarget = scope.findTarget(inEvent);
+      e.target = pointermap.get(e.pointerId);
+      dispatcher.cancel(e);
+      this.cleanup(inEvent.pointerId);
+    }
+  };
+
+  scope.pointerEvents = pointerEvents;
+})(window.PolymerGestures);
+
+/**
+ * This module contains the handlers for native platform events.
+ * From here, the dispatcher is called to create unified pointer events.
+ * Included are touch events (v1), mouse events, and MSPointerEvents.
+ */
+(function(scope) {
+
+  var dispatcher = scope.dispatcher;
+  var nav = window.navigator;
+
+  if (window.PointerEvent) {
+    dispatcher.registerSource('pointer', scope.pointerEvents);
+  } else if (nav.msPointerEnabled) {
+    dispatcher.registerSource('ms', scope.msEvents);
+  } else {
+    dispatcher.registerSource('mouse', scope.mouseEvents);
+    if (window.ontouchstart !== undefined) {
+      dispatcher.registerSource('touch', scope.touchEvents);
+    }
+  }
+
+  // Work around iOS bugs https://bugs.webkit.org/show_bug.cgi?id=135628 and https://bugs.webkit.org/show_bug.cgi?id=136506
+  var ua = navigator.userAgent;
+  var IS_IOS = ua.match(/iPad|iPhone|iPod/) && 'ontouchstart' in window;
+
+  dispatcher.IS_IOS = IS_IOS;
+  scope.touchEvents.IS_IOS = IS_IOS;
+
+  dispatcher.register(document, true);
+})(window.PolymerGestures);
+
+/**
+ * This event denotes the beginning of a series of tracking events.
+ *
+ * @module PointerGestures
+ * @submodule Events
+ * @class trackstart
+ */
+/**
+ * Pixels moved in the x direction since trackstart.
+ * @type Number
+ * @property dx
+ */
+/**
+ * Pixes moved in the y direction since trackstart.
+ * @type Number
+ * @property dy
+ */
+/**
+ * Pixels moved in the x direction since the last track.
+ * @type Number
+ * @property ddx
+ */
+/**
+ * Pixles moved in the y direction since the last track.
+ * @type Number
+ * @property ddy
+ */
+/**
+ * The clientX position of the track gesture.
+ * @type Number
+ * @property clientX
+ */
+/**
+ * The clientY position of the track gesture.
+ * @type Number
+ * @property clientY
+ */
+/**
+ * The pageX position of the track gesture.
+ * @type Number
+ * @property pageX
+ */
+/**
+ * The pageY position of the track gesture.
+ * @type Number
+ * @property pageY
+ */
+/**
+ * The screenX position of the track gesture.
+ * @type Number
+ * @property screenX
+ */
+/**
+ * The screenY position of the track gesture.
+ * @type Number
+ * @property screenY
+ */
+/**
+ * The last x axis direction of the pointer.
+ * @type Number
+ * @property xDirection
+ */
+/**
+ * The last y axis direction of the pointer.
+ * @type Number
+ * @property yDirection
+ */
+/**
+ * A shared object between all tracking events.
+ * @type Object
+ * @property trackInfo
+ */
+/**
+ * The element currently under the pointer.
+ * @type Element
+ * @property relatedTarget
+ */
+/**
+ * The type of pointer that make the track gesture.
+ * @type String
+ * @property pointerType
+ */
+/**
+ *
+ * This event fires for all pointer movement being tracked.
+ *
+ * @class track
+ * @extends trackstart
+ */
+/**
+ * This event fires when the pointer is no longer being tracked.
+ *
+ * @class trackend
+ * @extends trackstart
+ */
+
+ (function(scope) {
+   var dispatcher = scope.dispatcher;
+   var eventFactory = scope.eventFactory;
+   var pointermap = new scope.PointerMap();
+   var track = {
+     events: [
+       'down',
+       'move',
+       'up',
+     ],
+     exposes: [
+      'trackstart',
+      'track',
+      'trackx',
+      'tracky',
+      'trackend'
+     ],
+     defaultActions: {
+       'track': 'none',
+       'trackx': 'pan-y',
+       'tracky': 'pan-x'
+     },
+     WIGGLE_THRESHOLD: 4,
+     clampDir: function(inDelta) {
+       return inDelta > 0 ? 1 : -1;
+     },
+     calcPositionDelta: function(inA, inB) {
+       var x = 0, y = 0;
+       if (inA && inB) {
+         x = inB.pageX - inA.pageX;
+         y = inB.pageY - inA.pageY;
+       }
+       return {x: x, y: y};
+     },
+     fireTrack: function(inType, inEvent, inTrackingData) {
+       var t = inTrackingData;
+       var d = this.calcPositionDelta(t.downEvent, inEvent);
+       var dd = this.calcPositionDelta(t.lastMoveEvent, inEvent);
+       if (dd.x) {
+         t.xDirection = this.clampDir(dd.x);
+       } else if (inType === 'trackx') {
+         return;
+       }
+       if (dd.y) {
+         t.yDirection = this.clampDir(dd.y);
+       } else if (inType === 'tracky') {
+         return;
+       }
+       var gestureProto = {
+         bubbles: true,
+         cancelable: true,
+         trackInfo: t.trackInfo,
+         relatedTarget: inEvent.relatedTarget,
+         pointerType: inEvent.pointerType,
+         pointerId: inEvent.pointerId,
+         _source: 'track'
+       };
+       if (inType !== 'tracky') {
+         gestureProto.x = inEvent.x;
+         gestureProto.dx = d.x;
+         gestureProto.ddx = dd.x;
+         gestureProto.clientX = inEvent.clientX;
+         gestureProto.pageX = inEvent.pageX;
+         gestureProto.screenX = inEvent.screenX;
+         gestureProto.xDirection = t.xDirection;
+       }
+       if (inType !== 'trackx') {
+         gestureProto.dy = d.y;
+         gestureProto.ddy = dd.y;
+         gestureProto.y = inEvent.y;
+         gestureProto.clientY = inEvent.clientY;
+         gestureProto.pageY = inEvent.pageY;
+         gestureProto.screenY = inEvent.screenY;
+         gestureProto.yDirection = t.yDirection;
+       }
+       var e = eventFactory.makeGestureEvent(inType, gestureProto);
+       t.downTarget.dispatchEvent(e);
+     },
+     down: function(inEvent) {
+       if (inEvent.isPrimary && (inEvent.pointerType === 'mouse' ? inEvent.buttons === 1 : true)) {
+         var p = {
+           downEvent: inEvent,
+           downTarget: inEvent.target,
+           trackInfo: {},
+           lastMoveEvent: null,
+           xDirection: 0,
+           yDirection: 0,
+           tracking: false
+         };
+         pointermap.set(inEvent.pointerId, p);
+       }
+     },
+     move: function(inEvent) {
+       var p = pointermap.get(inEvent.pointerId);
+       if (p) {
+         if (!p.tracking) {
+           var d = this.calcPositionDelta(p.downEvent, inEvent);
+           var move = d.x * d.x + d.y * d.y;
+           // start tracking only if finger moves more than WIGGLE_THRESHOLD
+           if (move > this.WIGGLE_THRESHOLD) {
+             p.tracking = true;
+             p.lastMoveEvent = p.downEvent;
+             this.fireTrack('trackstart', inEvent, p);
+           }
+         }
+         if (p.tracking) {
+           this.fireTrack('track', inEvent, p);
+           this.fireTrack('trackx', inEvent, p);
+           this.fireTrack('tracky', inEvent, p);
+         }
+         p.lastMoveEvent = inEvent;
+       }
+     },
+     up: function(inEvent) {
+       var p = pointermap.get(inEvent.pointerId);
+       if (p) {
+         if (p.tracking) {
+           this.fireTrack('trackend', inEvent, p);
+         }
+         pointermap.delete(inEvent.pointerId);
+       }
+     }
+   };
+   dispatcher.registerGesture('track', track);
+ })(window.PolymerGestures);
+
+/**
+ * This event is fired when a pointer is held down for 200ms.
+ *
+ * @module PointerGestures
+ * @submodule Events
+ * @class hold
+ */
+/**
+ * Type of pointer that made the holding event.
+ * @type String
+ * @property pointerType
+ */
+/**
+ * Screen X axis position of the held pointer
+ * @type Number
+ * @property clientX
+ */
+/**
+ * Screen Y axis position of the held pointer
+ * @type Number
+ * @property clientY
+ */
+/**
+ * Type of pointer that made the holding event.
+ * @type String
+ * @property pointerType
+ */
+/**
+ * This event is fired every 200ms while a pointer is held down.
+ *
+ * @class holdpulse
+ * @extends hold
+ */
+/**
+ * Milliseconds pointer has been held down.
+ * @type Number
+ * @property holdTime
+ */
+/**
+ * This event is fired when a held pointer is released or moved.
+ *
+ * @class release
+ */
+
+(function(scope) {
+  var dispatcher = scope.dispatcher;
+  var eventFactory = scope.eventFactory;
+  var hold = {
+    // wait at least HOLD_DELAY ms between hold and pulse events
+    HOLD_DELAY: 200,
+    // pointer can move WIGGLE_THRESHOLD pixels before not counting as a hold
+    WIGGLE_THRESHOLD: 16,
+    events: [
+      'down',
+      'move',
+      'up',
+    ],
+    exposes: [
+      'hold',
+      'holdpulse',
+      'release'
+    ],
+    heldPointer: null,
+    holdJob: null,
+    pulse: function() {
+      var hold = Date.now() - this.heldPointer.timeStamp;
+      var type = this.held ? 'holdpulse' : 'hold';
+      this.fireHold(type, hold);
+      this.held = true;
+    },
+    cancel: function() {
+      clearInterval(this.holdJob);
+      if (this.held) {
+        this.fireHold('release');
+      }
+      this.held = false;
+      this.heldPointer = null;
+      this.target = null;
+      this.holdJob = null;
+    },
+    down: function(inEvent) {
+      if (inEvent.isPrimary && !this.heldPointer) {
+        this.heldPointer = inEvent;
+        this.target = inEvent.target;
+        this.holdJob = setInterval(this.pulse.bind(this), this.HOLD_DELAY);
+      }
+    },
+    up: function(inEvent) {
+      if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) {
+        this.cancel();
+      }
+    },
+    move: function(inEvent) {
+      if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) {
+        var x = inEvent.clientX - this.heldPointer.clientX;
+        var y = inEvent.clientY - this.heldPointer.clientY;
+        if ((x * x + y * y) > this.WIGGLE_THRESHOLD) {
+          this.cancel();
+        }
+      }
+    },
+    fireHold: function(inType, inHoldTime) {
+      var p = {
+        bubbles: true,
+        cancelable: true,
+        pointerType: this.heldPointer.pointerType,
+        pointerId: this.heldPointer.pointerId,
+        x: this.heldPointer.clientX,
+        y: this.heldPointer.clientY,
+        _source: 'hold'
+      };
+      if (inHoldTime) {
+        p.holdTime = inHoldTime;
+      }
+      var e = eventFactory.makeGestureEvent(inType, p);
+      this.target.dispatchEvent(e);
+    }
+  };
+  dispatcher.registerGesture('hold', hold);
+})(window.PolymerGestures);
+
+/**
+ * This event is fired when a pointer quickly goes down and up, and is used to
+ * denote activation.
+ *
+ * Any gesture event can prevent the tap event from being created by calling
+ * `event.preventTap`.
+ *
+ * Any pointer event can prevent the tap by setting the `tapPrevented` property
+ * on itself.
+ *
+ * @module PointerGestures
+ * @submodule Events
+ * @class tap
+ */
+/**
+ * X axis position of the tap.
+ * @property x
+ * @type Number
+ */
+/**
+ * Y axis position of the tap.
+ * @property y
+ * @type Number
+ */
+/**
+ * Type of the pointer that made the tap.
+ * @property pointerType
+ * @type String
+ */
+(function(scope) {
+  var dispatcher = scope.dispatcher;
+  var eventFactory = scope.eventFactory;
+  var pointermap = new scope.PointerMap();
+  var tap = {
+    events: [
+      'down',
+      'up'
+    ],
+    exposes: [
+      'tap'
+    ],
+    down: function(inEvent) {
+      if (inEvent.isPrimary && !inEvent.tapPrevented) {
+        pointermap.set(inEvent.pointerId, {
+          target: inEvent.target,
+          buttons: inEvent.buttons,
+          x: inEvent.clientX,
+          y: inEvent.clientY
+        });
+      }
+    },
+    shouldTap: function(e, downState) {
+      var tap = true;
+      if (e.pointerType === 'mouse') {
+        // only allow left click to tap for mouse
+        tap = (e.buttons ^ 1) && (downState.buttons & 1);
+      }
+      return tap && !e.tapPrevented;
+    },
+    up: function(inEvent) {
+      var start = pointermap.get(inEvent.pointerId);
+      if (start && this.shouldTap(inEvent, start)) {
+        // up.relatedTarget is target currently under finger
+        var t = scope.targetFinding.LCA(start.target, inEvent.relatedTarget);
+        if (t) {
+          var e = eventFactory.makeGestureEvent('tap', {
+            bubbles: true,
+            cancelable: true,
+            x: inEvent.clientX,
+            y: inEvent.clientY,
+            detail: inEvent.detail,
+            pointerType: inEvent.pointerType,
+            pointerId: inEvent.pointerId,
+            altKey: inEvent.altKey,
+            ctrlKey: inEvent.ctrlKey,
+            metaKey: inEvent.metaKey,
+            shiftKey: inEvent.shiftKey,
+            _source: 'tap'
+          });
+          t.dispatchEvent(e);
+        }
+      }
+      pointermap.delete(inEvent.pointerId);
+    }
+  };
+  // patch eventFactory to remove id from tap's pointermap for preventTap calls
+  eventFactory.preventTap = function(e) {
+    return function() {
+      e.tapPrevented = true;
+      pointermap.delete(e.pointerId);
+    };
+  };
+  dispatcher.registerGesture('tap', tap);
+})(window.PolymerGestures);
+
+/*
+ * Basic strategy: find the farthest apart points, use as diameter of circle
+ * react to size change and rotation of the chord
+ */
+
+/**
+ * @module pointer-gestures
+ * @submodule Events
+ * @class pinch
+ */
+/**
+ * Scale of the pinch zoom gesture
+ * @property scale
+ * @type Number
+ */
+/**
+ * Center X position of pointers causing pinch
+ * @property centerX
+ * @type Number
+ */
+/**
+ * Center Y position of pointers causing pinch
+ * @property centerY
+ * @type Number
+ */
+
+/**
+ * @module pointer-gestures
+ * @submodule Events
+ * @class rotate
+ */
+/**
+ * Angle (in degrees) of rotation. Measured from starting positions of pointers.
+ * @property angle
+ * @type Number
+ */
+/**
+ * Center X position of pointers causing rotation
+ * @property centerX
+ * @type Number
+ */
+/**
+ * Center Y position of pointers causing rotation
+ * @property centerY
+ * @type Number
+ */
+(function(scope) {
+  var dispatcher = scope.dispatcher;
+  var eventFactory = scope.eventFactory;
+  var pointermap = new scope.PointerMap();
+  var RAD_TO_DEG = 180 / Math.PI;
+  var pinch = {
+    events: [
+      'down',
+      'up',
+      'move',
+      'cancel'
+    ],
+    exposes: [
+      'pinchstart',
+      'pinch',
+      'pinchend',
+      'rotate'
+    ],
+    defaultActions: {
+      'pinch': 'none',
+      'rotate': 'none'
+    },
+    reference: {},
+    down: function(inEvent) {
+      pointermap.set(inEvent.pointerId, inEvent);
+      if (pointermap.pointers() == 2) {
+        var points = this.calcChord();
+        var angle = this.calcAngle(points);
+        this.reference = {
+          angle: angle,
+          diameter: points.diameter,
+          target: scope.targetFinding.LCA(points.a.target, points.b.target)
+        };
+
+        this.firePinch('pinchstart', points.diameter, points);
+      }
+    },
+    up: function(inEvent) {
+      var p = pointermap.get(inEvent.pointerId);
+      var num = pointermap.pointers();
+      if (p) {
+        if (num === 2) {
+          // fire 'pinchend' before deleting pointer
+          var points = this.calcChord();
+          this.firePinch('pinchend', points.diameter, points);
+        }
+        pointermap.delete(inEvent.pointerId);
+      }
+    },
+    move: function(inEvent) {
+      if (pointermap.has(inEvent.pointerId)) {
+        pointermap.set(inEvent.pointerId, inEvent);
+        if (pointermap.pointers() > 1) {
+          this.calcPinchRotate();
+        }
+      }
+    },
+    cancel: function(inEvent) {
+        this.up(inEvent);
+    },
+    firePinch: function(type, diameter, points) {
+      var zoom = diameter / this.reference.diameter;
+      var e = eventFactory.makeGestureEvent(type, {
+        bubbles: true,
+        cancelable: true,
+        scale: zoom,
+        centerX: points.center.x,
+        centerY: points.center.y,
+        _source: 'pinch'
+      });
+      this.reference.target.dispatchEvent(e);
+    },
+    fireRotate: function(angle, points) {
+      var diff = Math.round((angle - this.reference.angle) % 360);
+      var e = eventFactory.makeGestureEvent('rotate', {
+        bubbles: true,
+        cancelable: true,
+        angle: diff,
+        centerX: points.center.x,
+        centerY: points.center.y,
+        _source: 'pinch'
+      });
+      this.reference.target.dispatchEvent(e);
+    },
+    calcPinchRotate: function() {
+      var points = this.calcChord();
+      var diameter = points.diameter;
+      var angle = this.calcAngle(points);
+      if (diameter != this.reference.diameter) {
+        this.firePinch('pinch', diameter, points);
+      }
+      if (angle != this.reference.angle) {
+        this.fireRotate(angle, points);
+      }
+    },
+    calcChord: function() {
+      var pointers = [];
+      pointermap.forEach(function(p) {
+        pointers.push(p);
+      });
+      var dist = 0;
+      // start with at least two pointers
+      var points = {a: pointers[0], b: pointers[1]};
+      var x, y, d;
+      for (var i = 0; i < pointers.length; i++) {
+        var a = pointers[i];
+        for (var j = i + 1; j < pointers.length; j++) {
+          var b = pointers[j];
+          x = Math.abs(a.clientX - b.clientX);
+          y = Math.abs(a.clientY - b.clientY);
+          d = x + y;
+          if (d > dist) {
+            dist = d;
+            points = {a: a, b: b};
+          }
+        }
+      }
+      x = Math.abs(points.a.clientX + points.b.clientX) / 2;
+      y = Math.abs(points.a.clientY + points.b.clientY) / 2;
+      points.center = { x: x, y: y };
+      points.diameter = dist;
+      return points;
+    },
+    calcAngle: function(points) {
+      var x = points.a.clientX - points.b.clientX;
+      var y = points.a.clientY - points.b.clientY;
+      return (360 + Math.atan2(y, x) * RAD_TO_DEG) % 360;
+    }
+  };
+  dispatcher.registerGesture('pinch', pinch);
+})(window.PolymerGestures);
+
+(function (global) {
+    'use strict';
+
+    var Token,
+        TokenName,
+        Syntax,
+        Messages,
+        source,
+        index,
+        length,
+        delegate,
+        lookahead,
+        state;
+
+    Token = {
+        BooleanLiteral: 1,
+        EOF: 2,
+        Identifier: 3,
+        Keyword: 4,
+        NullLiteral: 5,
+        NumericLiteral: 6,
+        Punctuator: 7,
+        StringLiteral: 8
+    };
+
+    TokenName = {};
+    TokenName[Token.BooleanLiteral] = 'Boolean';
+    TokenName[Token.EOF] = '<end>';
+    TokenName[Token.Identifier] = 'Identifier';
+    TokenName[Token.Keyword] = 'Keyword';
+    TokenName[Token.NullLiteral] = 'Null';
+    TokenName[Token.NumericLiteral] = 'Numeric';
+    TokenName[Token.Punctuator] = 'Punctuator';
+    TokenName[Token.StringLiteral] = 'String';
+
+    Syntax = {
+        ArrayExpression: 'ArrayExpression',
+        BinaryExpression: 'BinaryExpression',
+        CallExpression: 'CallExpression',
+        ConditionalExpression: 'ConditionalExpression',
+        EmptyStatement: 'EmptyStatement',
+        ExpressionStatement: 'ExpressionStatement',
+        Identifier: 'Identifier',
+        Literal: 'Literal',
+        LabeledStatement: 'LabeledStatement',
+        LogicalExpression: 'LogicalExpression',
+        MemberExpression: 'MemberExpression',
+        ObjectExpression: 'ObjectExpression',
+        Program: 'Program',
+        Property: 'Property',
+        ThisExpression: 'ThisExpression',
+        UnaryExpression: 'UnaryExpression'
+    };
+
+    // Error messages should be identical to V8.
+    Messages = {
+        UnexpectedToken:  'Unexpected token %0',
+        UnknownLabel: 'Undefined label \'%0\'',
+        Redeclaration: '%0 \'%1\' has already been declared'
+    };
+
+    // Ensure the condition is true, otherwise throw an error.
+    // This is only to have a better contract semantic, i.e. another safety net
+    // to catch a logic error. The condition shall be fulfilled in normal case.
+    // Do NOT use this to enforce a certain condition on any user input.
+
+    function assert(condition, message) {
+        if (!condition) {
+            throw new Error('ASSERT: ' + message);
+        }
+    }
+
+    function isDecimalDigit(ch) {
+        return (ch >= 48 && ch <= 57);   // 0..9
+    }
+
+
+    // 7.2 White Space
+
+    function isWhiteSpace(ch) {
+        return (ch === 32) ||  // space
+            (ch === 9) ||      // tab
+            (ch === 0xB) ||
+            (ch === 0xC) ||
+            (ch === 0xA0) ||
+            (ch >= 0x1680 && '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(String.fromCharCode(ch)) > 0);
+    }
+
+    // 7.3 Line Terminators
+
+    function isLineTerminator(ch) {
+        return (ch === 10) || (ch === 13) || (ch === 0x2028) || (ch === 0x2029);
+    }
+
+    // 7.6 Identifier Names and Identifiers
+
+    function isIdentifierStart(ch) {
+        return (ch === 36) || (ch === 95) ||  // $ (dollar) and _ (underscore)
+            (ch >= 65 && ch <= 90) ||         // A..Z
+            (ch >= 97 && ch <= 122);          // a..z
+    }
+
+    function isIdentifierPart(ch) {
+        return (ch === 36) || (ch === 95) ||  // $ (dollar) and _ (underscore)
+            (ch >= 65 && ch <= 90) ||         // A..Z
+            (ch >= 97 && ch <= 122) ||        // a..z
+            (ch >= 48 && ch <= 57);           // 0..9
+    }
+
+    // 7.6.1.1 Keywords
+
+    function isKeyword(id) {
+        return (id === 'this')
+    }
+
+    // 7.4 Comments
+
+    function skipWhitespace() {
+        while (index < length && isWhiteSpace(source.charCodeAt(index))) {
+           ++index;
+        }
+    }
+
+    function getIdentifier() {
+        var start, ch;
+
+        start = index++;
+        while (index < length) {
+            ch = source.charCodeAt(index);
+            if (isIdentifierPart(ch)) {
+                ++index;
+            } else {
+                break;
+            }
+        }
+
+        return source.slice(start, index);
+    }
+
+    function scanIdentifier() {
+        var start, id, type;
+
+        start = index;
+
+        id = getIdentifier();
+
+        // There is no keyword or literal with only one character.
+        // Thus, it must be an identifier.
+        if (id.length === 1) {
+            type = Token.Identifier;
+        } else if (isKeyword(id)) {
+            type = Token.Keyword;
+        } else if (id === 'null') {
+            type = Token.NullLiteral;
+        } else if (id === 'true' || id === 'false') {
+            type = Token.BooleanLiteral;
+        } else {
+            type = Token.Identifier;
+        }
+
+        return {
+            type: type,
+            value: id,
+            range: [start, index]
+        };
+    }
+
+
+    // 7.7 Punctuators
+
+    function scanPunctuator() {
+        var start = index,
+            code = source.charCodeAt(index),
+            code2,
+            ch1 = source[index],
+            ch2;
+
+        switch (code) {
+
+        // Check for most common single-character punctuators.
+        case 46:   // . dot
+        case 40:   // ( open bracket
+        case 41:   // ) close bracket
+        case 59:   // ; semicolon
+        case 44:   // , comma
+        case 123:  // { open curly brace
+        case 125:  // } close curly brace
+        case 91:   // [
+        case 93:   // ]
+        case 58:   // :
+        case 63:   // ?
+            ++index;
+            return {
+                type: Token.Punctuator,
+                value: String.fromCharCode(code),
+                range: [start, index]
+            };
+
+        default:
+            code2 = source.charCodeAt(index + 1);
+
+            // '=' (char #61) marks an assignment or comparison operator.
+            if (code2 === 61) {
+                switch (code) {
+                case 37:  // %
+                case 38:  // &
+                case 42:  // *:
+                case 43:  // +
+                case 45:  // -
+                case 47:  // /
+                case 60:  // <
+                case 62:  // >
+                case 124: // |
+                    index += 2;
+                    return {
+                        type: Token.Punctuator,
+                        value: String.fromCharCode(code) + String.fromCharCode(code2),
+                        range: [start, index]
+                    };
+
+                case 33: // !
+                case 61: // =
+                    index += 2;
+
+                    // !== and ===
+                    if (source.charCodeAt(index) === 61) {
+                        ++index;
+                    }
+                    return {
+                        type: Token.Punctuator,
+                        value: source.slice(start, index),
+                        range: [start, index]
+                    };
+                default:
+                    break;
+                }
+            }
+            break;
+        }
+
+        // Peek more characters.
+
+        ch2 = source[index + 1];
+
+        // Other 2-character punctuators: && ||
+
+        if (ch1 === ch2 && ('&|'.indexOf(ch1) >= 0)) {
+            index += 2;
+            return {
+                type: Token.Punctuator,
+                value: ch1 + ch2,
+                range: [start, index]
+            };
+        }
+
+        if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
+            ++index;
+            return {
+                type: Token.Punctuator,
+                value: ch1,
+                range: [start, index]
+            };
+        }
+
+        throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+    }
+
+    // 7.8.3 Numeric Literals
+    function scanNumericLiteral() {
+        var number, start, ch;
+
+        ch = source[index];
+        assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'),
+            'Numeric literal must start with a decimal digit or a decimal point');
+
+        start = index;
+        number = '';
+        if (ch !== '.') {
+            number = source[index++];
+            ch = source[index];
+
+            // Hex number starts with '0x'.
+            // Octal number starts with '0'.
+            if (number === '0') {
+                // decimal number starts with '0' such as '09' is illegal.
+                if (ch && isDecimalDigit(ch.charCodeAt(0))) {
+                    throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+                }
+            }
+
+            while (isDecimalDigit(source.charCodeAt(index))) {
+                number += source[index++];
+            }
+            ch = source[index];
+        }
+
+        if (ch === '.') {
+            number += source[index++];
+            while (isDecimalDigit(source.charCodeAt(index))) {
+                number += source[index++];
+            }
+            ch = source[index];
+        }
+
+        if (ch === 'e' || ch === 'E') {
+            number += source[index++];
+
+            ch = source[index];
+            if (ch === '+' || ch === '-') {
+                number += source[index++];
+            }
+            if (isDecimalDigit(source.charCodeAt(index))) {
+                while (isDecimalDigit(source.charCodeAt(index))) {
+                    number += source[index++];
+                }
+            } else {
+                throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+            }
+        }
+
+        if (isIdentifierStart(source.charCodeAt(index))) {
+            throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+        }
+
+        return {
+            type: Token.NumericLiteral,
+            value: parseFloat(number),
+            range: [start, index]
+        };
+    }
+
+    // 7.8.4 String Literals
+
+    function scanStringLiteral() {
+        var str = '', quote, start, ch, octal = false;
+
+        quote = source[index];
+        assert((quote === '\'' || quote === '"'),
+            'String literal must starts with a quote');
+
+        start = index;
+        ++index;
+
+        while (index < length) {
+            ch = source[index++];
+
+            if (ch === quote) {
+                quote = '';
+                break;
+            } else if (ch === '\\') {
+                ch = source[index++];
+                if (!ch || !isLineTerminator(ch.charCodeAt(0))) {
+                    switch (ch) {
+                    case 'n':
+                        str += '\n';
+                        break;
+                    case 'r':
+                        str += '\r';
+                        break;
+                    case 't':
+                        str += '\t';
+                        break;
+                    case 'b':
+                        str += '\b';
+                        break;
+                    case 'f':
+                        str += '\f';
+                        break;
+                    case 'v':
+                        str += '\x0B';
+                        break;
+
+                    default:
+                        str += ch;
+                        break;
+                    }
+                } else {
+                    if (ch ===  '\r' && source[index] === '\n') {
+                        ++index;
+                    }
+                }
+            } else if (isLineTerminator(ch.charCodeAt(0))) {
+                break;
+            } else {
+                str += ch;
+            }
+        }
+
+        if (quote !== '') {
+            throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+        }
+
+        return {
+            type: Token.StringLiteral,
+            value: str,
+            octal: octal,
+            range: [start, index]
+        };
+    }
+
+    function isIdentifierName(token) {
+        return token.type === Token.Identifier ||
+            token.type === Token.Keyword ||
+            token.type === Token.BooleanLiteral ||
+            token.type === Token.NullLiteral;
+    }
+
+    function advance() {
+        var ch;
+
+        skipWhitespace();
+
+        if (index >= length) {
+            return {
+                type: Token.EOF,
+                range: [index, index]
+            };
+        }
+
+        ch = source.charCodeAt(index);
+
+        // Very common: ( and ) and ;
+        if (ch === 40 || ch === 41 || ch === 58) {
+            return scanPunctuator();
+        }
+
+        // String literal starts with single quote (#39) or double quote (#34).
+        if (ch === 39 || ch === 34) {
+            return scanStringLiteral();
+        }
+
+        if (isIdentifierStart(ch)) {
+            return scanIdentifier();
+        }
+
+        // Dot (.) char #46 can also start a floating-point number, hence the need
+        // to check the next character.
+        if (ch === 46) {
+            if (isDecimalDigit(source.charCodeAt(index + 1))) {
+                return scanNumericLiteral();
+            }
+            return scanPunctuator();
+        }
+
+        if (isDecimalDigit(ch)) {
+            return scanNumericLiteral();
+        }
+
+        return scanPunctuator();
+    }
+
+    function lex() {
+        var token;
+
+        token = lookahead;
+        index = token.range[1];
+
+        lookahead = advance();
+
+        index = token.range[1];
+
+        return token;
+    }
+
+    function peek() {
+        var pos;
+
+        pos = index;
+        lookahead = advance();
+        index = pos;
+    }
+
+    // Throw an exception
+
+    function throwError(token, messageFormat) {
+        var error,
+            args = Array.prototype.slice.call(arguments, 2),
+            msg = messageFormat.replace(
+                /%(\d)/g,
+                function (whole, index) {
+                    assert(index < args.length, 'Message reference must be in range');
+                    return args[index];
+                }
+            );
+
+        error = new Error(msg);
+        error.index = index;
+        error.description = msg;
+        throw error;
+    }
+
+    // Throw an exception because of the token.
+
+    function throwUnexpected(token) {
+        throwError(token, Messages.UnexpectedToken, token.value);
+    }
+
+    // Expect the next token to match the specified punctuator.
+    // If not, an exception will be thrown.
+
+    function expect(value) {
+        var token = lex();
+        if (token.type !== Token.Punctuator || token.value !== value) {
+            throwUnexpected(token);
+        }
+    }
+
+    // Return true if the next token matches the specified punctuator.
+
+    function match(value) {
+        return lookahead.type === Token.Punctuator && lookahead.value === value;
+    }
+
+    // Return true if the next token matches the specified keyword
+
+    function matchKeyword(keyword) {
+        return lookahead.type === Token.Keyword && lookahead.value === keyword;
+    }
+
+    function consumeSemicolon() {
+        // Catch the very common case first: immediately a semicolon (char #59).
+        if (source.charCodeAt(index) === 59) {
+            lex();
+            return;
+        }
+
+        skipWhitespace();
+
+        if (match(';')) {
+            lex();
+            return;
+        }
+
+        if (lookahead.type !== Token.EOF && !match('}')) {
+            throwUnexpected(lookahead);
+        }
+    }
+
+    // 11.1.4 Array Initialiser
+
+    function parseArrayInitialiser() {
+        var elements = [];
+
+        expect('[');
+
+        while (!match(']')) {
+            if (match(',')) {
+                lex();
+                elements.push(null);
+            } else {
+                elements.push(parseExpression());
+
+                if (!match(']')) {
+                    expect(',');
+                }
+            }
+        }
+
+        expect(']');
+
+        return delegate.createArrayExpression(elements);
+    }
+
+    // 11.1.5 Object Initialiser
+
+    function parseObjectPropertyKey() {
+        var token;
+
+        skipWhitespace();
+        token = lex();
+
+        // Note: This function is called only from parseObjectProperty(), where
+        // EOF and Punctuator tokens are already filtered out.
+        if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) {
+            return delegate.createLiteral(token);
+        }
+
+        return delegate.createIdentifier(token.value);
+    }
+
+    function parseObjectProperty() {
+        var token, key;
+
+        token = lookahead;
+        skipWhitespace();
+
+        if (token.type === Token.EOF || token.type === Token.Punctuator) {
+            throwUnexpected(token);
+        }
+
+        key = parseObjectPropertyKey();
+        expect(':');
+        return delegate.createProperty('init', key, parseExpression());
+    }
+
+    function parseObjectInitialiser() {
+        var properties = [];
+
+        expect('{');
+
+        while (!match('}')) {
+            properties.push(parseObjectProperty());
+
+            if (!match('}')) {
+                expect(',');
+            }
+        }
+
+        expect('}');
+
+        return delegate.createObjectExpression(properties);
+    }
+
+    // 11.1.6 The Grouping Operator
+
+    function parseGroupExpression() {
+        var expr;
+
+        expect('(');
+
+        expr = parseExpression();
+
+        expect(')');
+
+        return expr;
+    }
+
+
+    // 11.1 Primary Expressions
+
+    function parsePrimaryExpression() {
+        var type, token, expr;
+
+        if (match('(')) {
+            return parseGroupExpression();
+        }
+
+        type = lookahead.type;
+
+        if (type === Token.Identifier) {
+            expr = delegate.createIdentifier(lex().value);
+        } else if (type === Token.StringLiteral || type === Token.NumericLiteral) {
+            expr = delegate.createLiteral(lex());
+        } else if (type === Token.Keyword) {
+            if (matchKeyword('this')) {
+                lex();
+                expr = delegate.createThisExpression();
+            }
+        } else if (type === Token.BooleanLiteral) {
+            token = lex();
+            token.value = (token.value === 'true');
+            expr = delegate.createLiteral(token);
+        } else if (type === Token.NullLiteral) {
+            token = lex();
+            token.value = null;
+            expr = delegate.createLiteral(token);
+        } else if (match('[')) {
+            expr = parseArrayInitialiser();
+        } else if (match('{')) {
+            expr = parseObjectInitialiser();
+        }
+
+        if (expr) {
+            return expr;
+        }
+
+        throwUnexpected(lex());
+    }
+
+    // 11.2 Left-Hand-Side Expressions
+
+    function parseArguments() {
+        var args = [];
+
+        expect('(');
+
+        if (!match(')')) {
+            while (index < length) {
+                args.push(parseExpression());
+                if (match(')')) {
+                    break;
+                }
+                expect(',');
+            }
+        }
+
+        expect(')');
+
+        return args;
+    }
+
+    function parseNonComputedProperty() {
+        var token;
+
+        token = lex();
+
+        if (!isIdentifierName(token)) {
+            throwUnexpected(token);
+        }
+
+        return delegate.createIdentifier(token.value);
+    }
+
+    function parseNonComputedMember() {
+        expect('.');
+
+        return parseNonComputedProperty();
+    }
+
+    function parseComputedMember() {
+        var expr;
+
+        expect('[');
+
+        expr = parseExpression();
+
+        expect(']');
+
+        return expr;
+    }
+
+    function parseLeftHandSideExpression() {
+        var expr, args, property;
+
+        expr = parsePrimaryExpression();
+
+        while (true) {
+            if (match('[')) {
+                property = parseComputedMember();
+                expr = delegate.createMemberExpression('[', expr, property);
+            } else if (match('.')) {
+                property = parseNonComputedMember();
+                expr = delegate.createMemberExpression('.', expr, property);
+            } else if (match('(')) {
+                args = parseArguments();
+                expr = delegate.createCallExpression(expr, args);
+            } else {
+                break;
+            }
+        }
+
+        return expr;
+    }
+
+    // 11.3 Postfix Expressions
+
+    var parsePostfixExpression = parseLeftHandSideExpression;
+
+    // 11.4 Unary Operators
+
+    function parseUnaryExpression() {
+        var token, expr;
+
+        if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) {
+            expr = parsePostfixExpression();
+        } else if (match('+') || match('-') || match('!')) {
+            token = lex();
+            expr = parseUnaryExpression();
+            expr = delegate.createUnaryExpression(token.value, expr);
+        } else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
+            throwError({}, Messages.UnexpectedToken);
+        } else {
+            expr = parsePostfixExpression();
+        }
+
+        return expr;
+    }
+
+    function binaryPrecedence(token) {
+        var prec = 0;
+
+        if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
+            return 0;
+        }
+
+        switch (token.value) {
+        case '||':
+            prec = 1;
+            break;
+
+        case '&&':
+            prec = 2;
+            break;
+
+        case '==':
+        case '!=':
+        case '===':
+        case '!==':
+            prec = 6;
+            break;
+
+        case '<':
+        case '>':
+        case '<=':
+        case '>=':
+        case 'instanceof':
+            prec = 7;
+            break;
+
+        case 'in':
+            prec = 7;
+            break;
+
+        case '+':
+        case '-':
+            prec = 9;
+            break;
+
+        case '*':
+        case '/':
+        case '%':
+            prec = 11;
+            break;
+
+        default:
+            break;
+        }
+
+        return prec;
+    }
+
+    // 11.5 Multiplicative Operators
+    // 11.6 Additive Operators
+    // 11.7 Bitwise Shift Operators
+    // 11.8 Relational Operators
+    // 11.9 Equality Operators
+    // 11.10 Binary Bitwise Operators
+    // 11.11 Binary Logical Operators
+
+    function parseBinaryExpression() {
+        var expr, token, prec, stack, right, operator, left, i;
+
+        left = parseUnaryExpression();
+
+        token = lookahead;
+        prec = binaryPrecedence(token);
+        if (prec === 0) {
+            return left;
+        }
+        token.prec = prec;
+        lex();
+
+        right = parseUnaryExpression();
+
+        stack = [left, token, right];
+
+        while ((prec = binaryPrecedence(lookahead)) > 0) {
+
+            // Reduce: make a binary expression from the three topmost entries.
+            while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) {
+                right = stack.pop();
+                operator = stack.pop().value;
+                left = stack.pop();
+                expr = delegate.createBinaryExpression(operator, left, right);
+                stack.push(expr);
+            }
+
+            // Shift.
+            token = lex();
+            token.prec = prec;
+            stack.push(token);
+            expr = parseUnaryExpression();
+            stack.push(expr);
+        }
+
+        // Final reduce to clean-up the stack.
+        i = stack.length - 1;
+        expr = stack[i];
+        while (i > 1) {
+            expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr);
+            i -= 2;
+        }
+
+        return expr;
+    }
+
+
+    // 11.12 Conditional Operator
+
+    function parseConditionalExpression() {
+        var expr, consequent, alternate;
+
+        expr = parseBinaryExpression();
+
+        if (match('?')) {
+            lex();
+            consequent = parseConditionalExpression();
+            expect(':');
+            alternate = parseConditionalExpression();
+
+            expr = delegate.createConditionalExpression(expr, consequent, alternate);
+        }
+
+        return expr;
+    }
+
+    // Simplification since we do not support AssignmentExpression.
+    var parseExpression = parseConditionalExpression;
+
+    // Polymer Syntax extensions
+
+    // Filter ::
+    //   Identifier
+    //   Identifier "(" ")"
+    //   Identifier "(" FilterArguments ")"
+
+    function parseFilter() {
+        var identifier, args;
+
+        identifier = lex();
+
+        if (identifier.type !== Token.Identifier) {
+            throwUnexpected(identifier);
+        }
+
+        args = match('(') ? parseArguments() : [];
+
+        return delegate.createFilter(identifier.value, args);
+    }
+
+    // Filters ::
+    //   "|" Filter
+    //   Filters "|" Filter
+
+    function parseFilters() {
+        while (match('|')) {
+            lex();
+            parseFilter();
+        }
+    }
+
+    // TopLevel ::
+    //   LabelledExpressions
+    //   AsExpression
+    //   InExpression
+    //   FilterExpression
+
+    // AsExpression ::
+    //   FilterExpression as Identifier
+
+    // InExpression ::
+    //   Identifier, Identifier in FilterExpression
+    //   Identifier in FilterExpression
+
+    // FilterExpression ::
+    //   Expression
+    //   Expression Filters
+
+    function parseTopLevel() {
+        skipWhitespace();
+        peek();
+
+        var expr = parseExpression();
+        if (expr) {
+            if (lookahead.value === ',' || lookahead.value == 'in' &&
+                       expr.type === Syntax.Identifier) {
+                parseInExpression(expr);
+            } else {
+                parseFilters();
+                if (lookahead.value === 'as') {
+                    parseAsExpression(expr);
+                } else {
+                    delegate.createTopLevel(expr);
+                }
+            }
+        }
+
+        if (lookahead.type !== Token.EOF) {
+            throwUnexpected(lookahead);
+        }
+    }
+
+    function parseAsExpression(expr) {
+        lex();  // as
+        var identifier = lex().value;
+        delegate.createAsExpression(expr, identifier);
+    }
+
+    function parseInExpression(identifier) {
+        var indexName;
+        if (lookahead.value === ',') {
+            lex();
+            if (lookahead.type !== Token.Identifier)
+                throwUnexpected(lookahead);
+            indexName = lex().value;
+        }
+
+        lex();  // in
+        var expr = parseExpression();
+        parseFilters();
+        delegate.createInExpression(identifier.name, indexName, expr);
+    }
+
+    function parse(code, inDelegate) {
+        delegate = inDelegate;
+        source = code;
+        index = 0;
+        length = source.length;
+        lookahead = null;
+        state = {
+            labelSet: {}
+        };
+
+        return parseTopLevel();
+    }
+
+    global.esprima = {
+        parse: parse
+    };
+})(this);
+
+// Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+// This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+// The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+// The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+// Code distributed by Google as part of the polymer project is also
+// subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+
+(function (global) {
+  'use strict';
+
+  function prepareBinding(expressionText, name, node, filterRegistry) {
+    var expression;
+    try {
+      expression = getExpression(expressionText);
+      if (expression.scopeIdent &&
+          (node.nodeType !== Node.ELEMENT_NODE ||
+           node.tagName !== 'TEMPLATE' ||
+           (name !== 'bind' && name !== 'repeat'))) {
+        throw Error('as and in can only be used within <template bind/repeat>');
+      }
+    } catch (ex) {
+      console.error('Invalid expression syntax: ' + expressionText, ex);
+      return;
+    }
+
+    return function(model, node, oneTime) {
+      var binding = expression.getBinding(model, filterRegistry, oneTime);
+      if (expression.scopeIdent && binding) {
+        node.polymerExpressionScopeIdent_ = expression.scopeIdent;
+        if (expression.indexIdent)
+          node.polymerExpressionIndexIdent_ = expression.indexIdent;
+      }
+
+      return binding;
+    }
+  }
+
+  // TODO(rafaelw): Implement simple LRU.
+  var expressionParseCache = Object.create(null);
+
+  function getExpression(expressionText) {
+    var expression = expressionParseCache[expressionText];
+    if (!expression) {
+      var delegate = new ASTDelegate();
+      esprima.parse(expressionText, delegate);
+      expression = new Expression(delegate);
+      expressionParseCache[expressionText] = expression;
+    }
+    return expression;
+  }
+
+  function Literal(value) {
+    this.value = value;
+    this.valueFn_ = undefined;
+  }
+
+  Literal.prototype = {
+    valueFn: function() {
+      if (!this.valueFn_) {
+        var value = this.value;
+        this.valueFn_ = function() {
+          return value;
+        }
+      }
+
+      return this.valueFn_;
+    }
+  }
+
+  function IdentPath(name) {
+    this.name = name;
+    this.path = Path.get(name);
+  }
+
+  IdentPath.prototype = {
+    valueFn: function() {
+      if (!this.valueFn_) {
+        var name = this.name;
+        var path = this.path;
+        this.valueFn_ = function(model, observer) {
+          if (observer)
+            observer.addPath(model, path);
+
+          return path.getValueFrom(model);
+        }
+      }
+
+      return this.valueFn_;
+    },
+
+    setValue: function(model, newValue) {
+      if (this.path.length == 1)
+        model = findScope(model, this.path[0]);
+
+      return this.path.setValueFrom(model, newValue);
+    }
+  };
+
+  function MemberExpression(object, property, accessor) {
+    this.computed = accessor == '[';
+
+    this.dynamicDeps = typeof object == 'function' ||
+                       object.dynamicDeps ||
+                       (this.computed && !(property instanceof Literal));
+
+    this.simplePath =
+        !this.dynamicDeps &&
+        (property instanceof IdentPath || property instanceof Literal) &&
+        (object instanceof MemberExpression || object instanceof IdentPath);
+
+    this.object = this.simplePath ? object : getFn(object);
+    this.property = !this.computed || this.simplePath ?
+        property : getFn(property);
+  }
+
+  MemberExpression.prototype = {
+    get fullPath() {
+      if (!this.fullPath_) {
+
+        var parts = this.object instanceof MemberExpression ?
+            this.object.fullPath.slice() : [this.object.name];
+        parts.push(this.property instanceof IdentPath ?
+            this.property.name : this.property.value);
+        this.fullPath_ = Path.get(parts);
+      }
+
+      return this.fullPath_;
+    },
+
+    valueFn: function() {
+      if (!this.valueFn_) {
+        var object = this.object;
+
+        if (this.simplePath) {
+          var path = this.fullPath;
+
+          this.valueFn_ = function(model, observer) {
+            if (observer)
+              observer.addPath(model, path);
+
+            return path.getValueFrom(model);
+          };
+        } else if (!this.computed) {
+          var path = Path.get(this.property.name);
+
+          this.valueFn_ = function(model, observer, filterRegistry) {
+            var context = object(model, observer, filterRegistry);
+
+            if (observer)
+              observer.addPath(context, path);
+
+            return path.getValueFrom(context);
+          }
+        } else {
+          // Computed property.
+          var property = this.property;
+
+          this.valueFn_ = function(model, observer, filterRegistry) {
+            var context = object(model, observer, filterRegistry);
+            var propName = property(model, observer, filterRegistry);
+            if (observer)
+              observer.addPath(context, [propName]);
+
+            return context ? context[propName] : undefined;
+          };
+        }
+      }
+      return this.valueFn_;
+    },
+
+    setValue: function(model, newValue) {
+      if (this.simplePath) {
+        this.fullPath.setValueFrom(model, newValue);
+        return newValue;
+      }
+
+      var object = this.object(model);
+      var propName = this.property instanceof IdentPath ? this.property.name :
+          this.property(model);
+      return object[propName] = newValue;
+    }
+  };
+
+  function Filter(name, args) {
+    this.name = name;
+    this.args = [];
+    for (var i = 0; i < args.length; i++) {
+      this.args[i] = getFn(args[i]);
+    }
+  }
+
+  Filter.prototype = {
+    transform: function(model, observer, filterRegistry, toModelDirection,
+                        initialArgs) {
+      var context = model;
+      var fn = context[this.name];
+
+      if (!fn) {
+        fn = filterRegistry[this.name];
+        if (!fn) {
+          console.error('Cannot find function or filter: ' + this.name);
+          return;
+        }
+      }
+
+      // If toModelDirection is falsey, then the "normal" (dom-bound) direction
+      // is used. Otherwise, it looks for a 'toModel' property function on the
+      // object.
+      if (toModelDirection) {
+        fn = fn.toModel;
+      } else if (typeof fn.toDOM == 'function') {
+        fn = fn.toDOM;
+      }
+
+      if (typeof fn != 'function') {
+        console.error('Cannot find function or filter: ' + this.name);
+        return;
+      }
+
+      var args = initialArgs || [];
+      for (var i = 0; i < this.args.length; i++) {
+        args.push(getFn(this.args[i])(model, observer, filterRegistry));
+      }
+
+      return fn.apply(context, args);
+    }
+  };
+
+  function notImplemented() { throw Error('Not Implemented'); }
+
+  var unaryOperators = {
+    '+': function(v) { return +v; },
+    '-': function(v) { return -v; },
+    '!': function(v) { return !v; }
+  };
+
+  var binaryOperators = {
+    '+': function(l, r) { return l+r; },
+    '-': function(l, r) { return l-r; },
+    '*': function(l, r) { return l*r; },
+    '/': function(l, r) { return l/r; },
+    '%': function(l, r) { return l%r; },
+    '<': function(l, r) { return l<r; },
+    '>': function(l, r) { return l>r; },
+    '<=': function(l, r) { return l<=r; },
+    '>=': function(l, r) { return l>=r; },
+    '==': function(l, r) { return l==r; },
+    '!=': function(l, r) { return l!=r; },
+    '===': function(l, r) { return l===r; },
+    '!==': function(l, r) { return l!==r; },
+    '&&': function(l, r) { return l&&r; },
+    '||': function(l, r) { return l||r; },
+  };
+
+  function getFn(arg) {
+    return typeof arg == 'function' ? arg : arg.valueFn();
+  }
+
+  function ASTDelegate() {
+    this.expression = null;
+    this.filters = [];
+    this.deps = {};
+    this.currentPath = undefined;
+    this.scopeIdent = undefined;
+    this.indexIdent = undefined;
+    this.dynamicDeps = false;
+  }
+
+  ASTDelegate.prototype = {
+    createUnaryExpression: function(op, argument) {
+      if (!unaryOperators[op])
+        throw Error('Disallowed operator: ' + op);
+
+      argument = getFn(argument);
+
+      return function(model, observer, filterRegistry) {
+        return unaryOperators[op](argument(model, observer, filterRegistry));
+      };
+    },
+
+    createBinaryExpression: function(op, left, right) {
+      if (!binaryOperators[op])
+        throw Error('Disallowed operator: ' + op);
+
+      left = getFn(left);
+      right = getFn(right);
+
+      switch (op) {
+        case '||':
+          this.dynamicDeps = true;
+          return function(model, observer, filterRegistry) {
+            return left(model, observer, filterRegistry) ||
+                right(model, observer, filterRegistry);
+          };
+        case '&&':
+          this.dynamicDeps = true;
+          return function(model, observer, filterRegistry) {
+            return left(model, observer, filterRegistry) &&
+                right(model, observer, filterRegistry);
+          };
+      }
+
+      return function(model, observer, filterRegistry) {
+        return binaryOperators[op](left(model, observer, filterRegistry),
+                                   right(model, observer, filterRegistry));
+      };
+    },
+
+    createConditionalExpression: function(test, consequent, alternate) {
+      test = getFn(test);
+      consequent = getFn(consequent);
+      alternate = getFn(alternate);
+
+      this.dynamicDeps = true;
+
+      return function(model, observer, filterRegistry) {
+        return test(model, observer, filterRegistry) ?
+            consequent(model, observer, filterRegistry) :
+            alternate(model, observer, filterRegistry);
+      }
+    },
+
+    createIdentifier: function(name) {
+      var ident = new IdentPath(name);
+      ident.type = 'Identifier';
+      return ident;
+    },
+
+    createMemberExpression: function(accessor, object, property) {
+      var ex = new MemberExpression(object, property, accessor);
+      if (ex.dynamicDeps)
+        this.dynamicDeps = true;
+      return ex;
+    },
+
+    createCallExpression: function(expression, args) {
+      if (!(expression instanceof IdentPath))
+        throw Error('Only identifier function invocations are allowed');
+
+      var filter = new Filter(expression.name, args);
+
+      return function(model, observer, filterRegistry) {
+        return filter.transform(model, observer, filterRegistry, false);
+      };
+    },
+
+    createLiteral: function(token) {
+      return new Literal(token.value);
+    },
+
+    createArrayExpression: function(elements) {
+      for (var i = 0; i < elements.length; i++)
+        elements[i] = getFn(elements[i]);
+
+      return function(model, observer, filterRegistry) {
+        var arr = []
+        for (var i = 0; i < elements.length; i++)
+          arr.push(elements[i](model, observer, filterRegistry));
+        return arr;
+      }
+    },
+
+    createProperty: function(kind, key, value) {
+      return {
+        key: key instanceof IdentPath ? key.name : key.value,
+        value: value
+      };
+    },
+
+    createObjectExpression: function(properties) {
+      for (var i = 0; i < properties.length; i++)
+        properties[i].value = getFn(properties[i].value);
+
+      return function(model, observer, filterRegistry) {
+        var obj = {};
+        for (var i = 0; i < properties.length; i++)
+          obj[properties[i].key] =
+              properties[i].value(model, observer, filterRegistry);
+        return obj;
+      }
+    },
+
+    createFilter: function(name, args) {
+      this.filters.push(new Filter(name, args));
+    },
+
+    createAsExpression: function(expression, scopeIdent) {
+      this.expression = expression;
+      this.scopeIdent = scopeIdent;
+    },
+
+    createInExpression: function(scopeIdent, indexIdent, expression) {
+      this.expression = expression;
+      this.scopeIdent = scopeIdent;
+      this.indexIdent = indexIdent;
+    },
+
+    createTopLevel: function(expression) {
+      this.expression = expression;
+    },
+
+    createThisExpression: notImplemented
+  }
+
+  function ConstantObservable(value) {
+    this.value_ = value;
+  }
+
+  ConstantObservable.prototype = {
+    open: function() { return this.value_; },
+    discardChanges: function() { return this.value_; },
+    deliver: function() {},
+    close: function() {},
+  }
+
+  function Expression(delegate) {
+    this.scopeIdent = delegate.scopeIdent;
+    this.indexIdent = delegate.indexIdent;
+
+    if (!delegate.expression)
+      throw Error('No expression found.');
+
+    this.expression = delegate.expression;
+    getFn(this.expression); // forces enumeration of path dependencies
+
+    this.filters = delegate.filters;
+    this.dynamicDeps = delegate.dynamicDeps;
+  }
+
+  Expression.prototype = {
+    getBinding: function(model, filterRegistry, oneTime) {
+      if (oneTime)
+        return this.getValue(model, undefined, filterRegistry);
+
+      var observer = new CompoundObserver();
+      // captures deps.
+      var firstValue = this.getValue(model, observer, filterRegistry);
+      var firstTime = true;
+      var self = this;
+
+      function valueFn() {
+        // deps cannot have changed on first value retrieval.
+        if (firstTime) {
+          firstTime = false;
+          return firstValue;
+        }
+
+        if (self.dynamicDeps)
+          observer.startReset();
+
+        var value = self.getValue(model,
+                                  self.dynamicDeps ? observer : undefined,
+                                  filterRegistry);
+        if (self.dynamicDeps)
+          observer.finishReset();
+
+        return value;
+      }
+
+      function setValueFn(newValue) {
+        self.setValue(model, newValue, filterRegistry);
+        return newValue;
+      }
+
+      return new ObserverTransform(observer, valueFn, setValueFn, true);
+    },
+
+    getValue: function(model, observer, filterRegistry) {
+      var value = getFn(this.expression)(model, observer, filterRegistry);
+      for (var i = 0; i < this.filters.length; i++) {
+        value = this.filters[i].transform(model, observer, filterRegistry,
+            false, [value]);
+      }
+
+      return value;
+    },
+
+    setValue: function(model, newValue, filterRegistry) {
+      var count = this.filters ? this.filters.length : 0;
+      while (count-- > 0) {
+        newValue = this.filters[count].transform(model, undefined,
+            filterRegistry, true, [newValue]);
+      }
+
+      if (this.expression.setValue)
+        return this.expression.setValue(model, newValue);
+    }
+  }
+
+  /**
+   * Converts a style property name to a css property name. For example:
+   * "WebkitUserSelect" to "-webkit-user-select"
+   */
+  function convertStylePropertyName(name) {
+    return String(name).replace(/[A-Z]/g, function(c) {
+      return '-' + c.toLowerCase();
+    });
+  }
+
+  var parentScopeName = '@' + Math.random().toString(36).slice(2);
+
+  // Single ident paths must bind directly to the appropriate scope object.
+  // I.e. Pushed values in two-bindings need to be assigned to the actual model
+  // object.
+  function findScope(model, prop) {
+    while (model[parentScopeName] &&
+           !Object.prototype.hasOwnProperty.call(model, prop)) {
+      model = model[parentScopeName];
+    }
+
+    return model;
+  }
+
+  function isLiteralExpression(pathString) {
+    switch (pathString) {
+      case '':
+        return false;
+
+      case 'false':
+      case 'null':
+      case 'true':
+        return true;
+    }
+
+    if (!isNaN(Number(pathString)))
+      return true;
+
+    return false;
+  };
+
+  function PolymerExpressions() {}
+
+  PolymerExpressions.prototype = {
+    // "built-in" filters
+    styleObject: function(value) {
+      var parts = [];
+      for (var key in value) {
+        parts.push(convertStylePropertyName(key) + ': ' + value[key]);
+      }
+      return parts.join('; ');
+    },
+
+    tokenList: function(value) {
+      var tokens = [];
+      for (var key in value) {
+        if (value[key])
+          tokens.push(key);
+      }
+      return tokens.join(' ');
+    },
+
+    // binding delegate API
+    prepareInstancePositionChanged: function(template) {
+      var indexIdent = template.polymerExpressionIndexIdent_;
+      if (!indexIdent)
+        return;
+
+      return function(templateInstance, index) {
+        templateInstance.model[indexIdent] = index;
+      };
+    },
+
+    prepareBinding: function(pathString, name, node) {
+      var path = Path.get(pathString);
+
+      if (!isLiteralExpression(pathString) && path.valid) {
+        if (path.length == 1) {
+          return function(model, node, oneTime) {
+            if (oneTime)
+              return path.getValueFrom(model);
+
+            var scope = findScope(model, path[0]);
+            return new PathObserver(scope, path);
+          };
+        }
+        return; // bail out early if pathString is simple path.
+      }
+
+      return prepareBinding(pathString, name, node, this);
+    },
+
+    prepareInstanceModel: function(template) {
+      var scopeName = template.polymerExpressionScopeIdent_;
+      if (!scopeName)
+        return;
+
+      var parentScope = template.templateInstance ?
+          template.templateInstance.model :
+          template.model;
+
+      var indexName = template.polymerExpressionIndexIdent_;
+
+      return function(model) {
+        return createScopeObject(parentScope, model, scopeName, indexName);
+      };
+    }
+  };
+
+  var createScopeObject = ('__proto__' in {}) ?
+    function(parentScope, model, scopeName, indexName) {
+      var scope = {};
+      scope[scopeName] = model;
+      scope[indexName] = undefined;
+      scope[parentScopeName] = parentScope;
+      scope.__proto__ = parentScope;
+      return scope;
+    } :
+    function(parentScope, model, scopeName, indexName) {
+      var scope = Object.create(parentScope);
+      Object.defineProperty(scope, scopeName,
+          { value: model, configurable: true, writable: true });
+      Object.defineProperty(scope, indexName,
+          { value: undefined, configurable: true, writable: true });
+      Object.defineProperty(scope, parentScopeName,
+          { value: parentScope, configurable: true, writable: true });
+      return scope;
+    };
+
+  global.PolymerExpressions = PolymerExpressions;
+  PolymerExpressions.getExpression = getExpression;
+})(this);
+
+Polymer = {
+  version: '0.5.5'
+};
+
+// TODO(sorvell): this ensures Polymer is an object and not a function
+// Platform is currently defining it as a function to allow for async loading
+// of polymer; once we refine the loading process this likely goes away.
+if (typeof window.Polymer === 'function') {
+  Polymer = {};
+}
+
+
+(function(scope) {
+
+  function withDependencies(task, depends) {
+    depends = depends || [];
+    if (!depends.map) {
+      depends = [depends];
+    }
+    return task.apply(this, depends.map(marshal));
+  }
+
+  function module(name, dependsOrFactory, moduleFactory) {
+    var module;
+    switch (arguments.length) {
+      case 0:
+        return;
+      case 1:
+        module = null;
+        break;
+      case 2:
+        // dependsOrFactory is `factory` in this case
+        module = dependsOrFactory.apply(this);
+        break;
+      default:
+        // dependsOrFactory is `depends` in this case
+        module = withDependencies(moduleFactory, dependsOrFactory);
+        break;
+    }
+    modules[name] = module;
+  };
+
+  function marshal(name) {
+    return modules[name];
+  }
+
+  var modules = {};
+
+  function using(depends, task) {
+    HTMLImports.whenImportsReady(function() {
+      withDependencies(task, depends);
+    });
+  };
+
+  // exports
+
+  scope.marshal = marshal;
+  // `module` confuses commonjs detectors
+  scope.modularize = module;
+  scope.using = using;
+
+})(window);
+
+/*
+	Build only script.
+
+  Ensures scripts needed for basic x-platform compatibility
+  will be run when platform.js is not loaded.
+ */
+if (!window.WebComponents) {
+
+/*
+	On supported platforms, platform.js is not needed. To retain compatibility
+	with the polyfills, we stub out minimal functionality.
+ */
+if (!window.WebComponents) {
+
+  WebComponents = {
+  	flush: function() {},
+    flags: {log: {}}
+  };
+
+  Platform = WebComponents;
+
+  CustomElements = {
+  	useNative: true,
+    ready: true,
+    takeRecords: function() {},
+    instanceof: function(obj, base) {
+      return obj instanceof base;
+    }
+  };
+  
+  HTMLImports = {
+  	useNative: true
+  };
+
+  
+  addEventListener('HTMLImportsLoaded', function() {
+    document.dispatchEvent(
+      new CustomEvent('WebComponentsReady', {bubbles: true})
+    );
+  });
+
+
+  // ShadowDOM
+  ShadowDOMPolyfill = null;
+  wrap = unwrap = function(n){
+    return n;
+  };
+
+}
+
+/*
+  Create polyfill scope and feature detect native support.
+*/
+window.HTMLImports = window.HTMLImports || {flags:{}};
+
+(function(scope) {
+
+/**
+  Basic setup and simple module executer. We collect modules and then execute
+  the code later, only if it's necessary for polyfilling.
+*/
+var IMPORT_LINK_TYPE = 'import';
+var useNative = Boolean(IMPORT_LINK_TYPE in document.createElement('link'));
+
+/**
+  Support `currentScript` on all browsers as `document._currentScript.`
+
+  NOTE: We cannot polyfill `document.currentScript` because it's not possible
+  both to override and maintain the ability to capture the native value.
+  Therefore we choose to expose `_currentScript` both when native imports
+  and the polyfill are in use.
+*/
+// NOTE: ShadowDOMPolyfill intrusion.
+var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill);
+var wrap = function(node) {
+  return hasShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) : node;
+};
+var rootDocument = wrap(document);
+
+var currentScriptDescriptor = {
+  get: function() {
+    var script = HTMLImports.currentScript || document.currentScript ||
+        // NOTE: only works when called in synchronously executing code.
+        // readyState should check if `loading` but IE10 is
+        // interactive when scripts run so we cheat.
+        (document.readyState !== 'complete' ?
+        document.scripts[document.scripts.length - 1] : null);
+    return wrap(script);
+  },
+  configurable: true
+};
+
+Object.defineProperty(document, '_currentScript', currentScriptDescriptor);
+Object.defineProperty(rootDocument, '_currentScript', currentScriptDescriptor);
+
+/**
+  Add support for the `HTMLImportsLoaded` event and the `HTMLImports.whenReady`
+  method. This api is necessary because unlike the native implementation,
+  script elements do not force imports to resolve. Instead, users should wrap
+  code in either an `HTMLImportsLoaded` hander or after load time in an
+  `HTMLImports.whenReady(callback)` call.
+
+  NOTE: This module also supports these apis under the native implementation.
+  Therefore, if this file is loaded, the same code can be used under both
+  the polyfill and native implementation.
+ */
+
+var isIE = /Trident/.test(navigator.userAgent);
+
+// call a callback when all HTMLImports in the document at call time
+// (or at least document ready) have loaded.
+// 1. ensure the document is in a ready state (has dom), then
+// 2. watch for loading of imports and call callback when done
+function whenReady(callback, doc) {
+  doc = doc || rootDocument;
+  // if document is loading, wait and try again
+  whenDocumentReady(function() {
+    watchImportsLoad(callback, doc);
+  }, doc);
+}
+
+// call the callback when the document is in a ready state (has dom)
+var requiredReadyState = isIE ? 'complete' : 'interactive';
+var READY_EVENT = 'readystatechange';
+function isDocumentReady(doc) {
+  return (doc.readyState === 'complete' ||
+      doc.readyState === requiredReadyState);
+}
+
+// call <callback> when we ensure the document is in a ready state
+function whenDocumentReady(callback, doc) {
+  if (!isDocumentReady(doc)) {
+    var checkReady = function() {
+      if (doc.readyState === 'complete' ||
+          doc.readyState === requiredReadyState) {
+        doc.removeEventListener(READY_EVENT, checkReady);
+        whenDocumentReady(callback, doc);
+      }
+    };
+    doc.addEventListener(READY_EVENT, checkReady);
+  } else if (callback) {
+    callback();
+  }
+}
+
+function markTargetLoaded(event) {
+  event.target.__loaded = true;
+}
+
+// call <callback> when we ensure all imports have loaded
+function watchImportsLoad(callback, doc) {
+  var imports = doc.querySelectorAll('link[rel=import]');
+  var loaded = 0, l = imports.length;
+  function checkDone(d) {
+    if ((loaded == l) && callback) {
+       callback();
+    }
+  }
+  function loadedImport(e) {
+    markTargetLoaded(e);
+    loaded++;
+    checkDone();
+  }
+  if (l) {
+    for (var i=0, imp; (i<l) && (imp=imports[i]); i++) {
+      if (isImportLoaded(imp)) {
+        loadedImport.call(imp, {target: imp});
+      } else {
+        imp.addEventListener('load', loadedImport);
+        imp.addEventListener('error', loadedImport);
+      }
+    }
+  } else {
+    checkDone();
+  }
+}
+
+// NOTE: test for native imports loading is based on explicitly watching
+// all imports (see below).
+// However, we cannot rely on this entirely without watching the entire document
+// for import links. For perf reasons, currently only head is watched.
+// Instead, we fallback to checking if the import property is available
+// and the document is not itself loading.
+function isImportLoaded(link) {
+  return useNative ? link.__loaded ||
+      (link.import && link.import.readyState !== 'loading') :
+      link.__importParsed;
+}
+
+// TODO(sorvell): Workaround for
+// https://www.w3.org/Bugs/Public/show_bug.cgi?id=25007, should be removed when
+// this bug is addressed.
+// (1) Install a mutation observer to see when HTMLImports have loaded
+// (2) if this script is run during document load it will watch any existing
+// imports for loading.
+//
+// NOTE: The workaround has restricted functionality: (1) it's only compatible
+// with imports that are added to document.head since the mutation observer
+// watches only head for perf reasons, (2) it requires this script
+// to run before any imports have completed loading.
+if (useNative) {
+  new MutationObserver(function(mxns) {
+    for (var i=0, l=mxns.length, m; (i < l) && (m=mxns[i]); i++) {
+      if (m.addedNodes) {
+        handleImports(m.addedNodes);
+      }
+    }
+  }).observe(document.head, {childList: true});
+
+  function handleImports(nodes) {
+    for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) {
+      if (isImport(n)) {
+        handleImport(n);
+      }
+    }
+  }
+
+  function isImport(element) {
+    return element.localName === 'link' && element.rel === 'import';
+  }
+
+  function handleImport(element) {
+    var loaded = element.import;
+    if (loaded) {
+      markTargetLoaded({target: element});
+    } else {
+      element.addEventListener('load', markTargetLoaded);
+      element.addEventListener('error', markTargetLoaded);
+    }
+  }
+
+  // make sure to catch any imports that are in the process of loading
+  // when this script is run.
+  (function() {
+    if (document.readyState === 'loading') {
+      var imports = document.querySelectorAll('link[rel=import]');
+      for (var i=0, l=imports.length, imp; (i<l) && (imp=imports[i]); i++) {
+        handleImport(imp);
+      }
+    }
+  })();
+
+}
+
+// Fire the 'HTMLImportsLoaded' event when imports in document at load time
+// have loaded. This event is required to simulate the script blocking
+// behavior of native imports. A main document script that needs to be sure
+// imports have loaded should wait for this event.
+whenReady(function() {
+  HTMLImports.ready = true;
+  HTMLImports.readyTime = new Date().getTime();
+  rootDocument.dispatchEvent(
+    new CustomEvent('HTMLImportsLoaded', {bubbles: true})
+  );
+});
+
+// exports
+scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
+scope.useNative = useNative;
+scope.rootDocument = rootDocument;
+scope.whenReady = whenReady;
+scope.isIE = isIE;
+
+})(HTMLImports);
+
+(function(scope) {
+
+  // TODO(sorvell): It's desireable to provide a default stylesheet 
+  // that's convenient for styling unresolved elements, but
+  // it's cumbersome to have to include this manually in every page.
+  // It would make sense to put inside some HTMLImport but 
+  // the HTMLImports polyfill does not allow loading of stylesheets 
+  // that block rendering. Therefore this injection is tolerated here.
+  var style = document.createElement('style');
+  style.textContent = ''
+      + 'body {'
+      + 'transition: opacity ease-in 0.2s;' 
+      + ' } \n'
+      + 'body[unresolved] {'
+      + 'opacity: 0; display: block; overflow: hidden;' 
+      + ' } \n'
+      ;
+  var head = document.querySelector('head');
+  head.insertBefore(style, head.firstChild);
+
+})(Platform);
+
+/*
+	Build only script.
+
+  Ensures scripts needed for basic x-platform compatibility
+  will be run when platform.js is not loaded.
+ */
+}
+(function(global) {
+  'use strict';
+
+  var testingExposeCycleCount = global.testingExposeCycleCount;
+
+  // Detect and do basic sanity checking on Object/Array.observe.
+  function detectObjectObserve() {
+    if (typeof Object.observe !== 'function' ||
+        typeof Array.observe !== 'function') {
+      return false;
+    }
+
+    var records = [];
+
+    function callback(recs) {
+      records = recs;
+    }
+
+    var test = {};
+    var arr = [];
+    Object.observe(test, callback);
+    Array.observe(arr, callback);
+    test.id = 1;
+    test.id = 2;
+    delete test.id;
+    arr.push(1, 2);
+    arr.length = 0;
+
+    Object.deliverChangeRecords(callback);
+    if (records.length !== 5)
+      return false;
+
+    if (records[0].type != 'add' ||
+        records[1].type != 'update' ||
+        records[2].type != 'delete' ||
+        records[3].type != 'splice' ||
+        records[4].type != 'splice') {
+      return false;
+    }
+
+    Object.unobserve(test, callback);
+    Array.unobserve(arr, callback);
+
+    return true;
+  }
+
+  var hasObserve = detectObjectObserve();
+
+  function detectEval() {
+    // Don't test for eval if we're running in a Chrome App environment.
+    // We check for APIs set that only exist in a Chrome App context.
+    if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) {
+      return false;
+    }
+
+    // Firefox OS Apps do not allow eval. This feature detection is very hacky
+    // but even if some other platform adds support for this function this code
+    // will continue to work.
+    if (typeof navigator != 'undefined' && navigator.getDeviceStorage) {
+      return false;
+    }
+
+    try {
+      var f = new Function('', 'return true;');
+      return f();
+    } catch (ex) {
+      return false;
+    }
+  }
+
+  var hasEval = detectEval();
+
+  function isIndex(s) {
+    return +s === s >>> 0 && s !== '';
+  }
+
+  function toNumber(s) {
+    return +s;
+  }
+
+  function isObject(obj) {
+    return obj === Object(obj);
+  }
+
+  var numberIsNaN = global.Number.isNaN || function(value) {
+    return typeof value === 'number' && global.isNaN(value);
+  }
+
+  function areSameValue(left, right) {
+    if (left === right)
+      return left !== 0 || 1 / left === 1 / right;
+    if (numberIsNaN(left) && numberIsNaN(right))
+      return true;
+
+    return left !== left && right !== right;
+  }
+
+  var createObject = ('__proto__' in {}) ?
+    function(obj) { return obj; } :
+    function(obj) {
+      var proto = obj.__proto__;
+      if (!proto)
+        return obj;
+      var newObject = Object.create(proto);
+      Object.getOwnPropertyNames(obj).forEach(function(name) {
+        Object.defineProperty(newObject, name,
+                             Object.getOwnPropertyDescriptor(obj, name));
+      });
+      return newObject;
+    };
+
+  var identStart = '[\$_a-zA-Z]';
+  var identPart = '[\$_a-zA-Z0-9]';
+  var identRegExp = new RegExp('^' + identStart + '+' + identPart + '*' + '$');
+
+  function getPathCharType(char) {
+    if (char === undefined)
+      return 'eof';
+
+    var code = char.charCodeAt(0);
+
+    switch(code) {
+      case 0x5B: // [
+      case 0x5D: // ]
+      case 0x2E: // .
+      case 0x22: // "
+      case 0x27: // '
+      case 0x30: // 0
+        return char;
+
+      case 0x5F: // _
+      case 0x24: // $
+        return 'ident';
+
+      case 0x20: // Space
+      case 0x09: // Tab
+      case 0x0A: // Newline
+      case 0x0D: // Return
+      case 0xA0:  // No-break space
+      case 0xFEFF:  // Byte Order Mark
+      case 0x2028:  // Line Separator
+      case 0x2029:  // Paragraph Separator
+        return 'ws';
+    }
+
+    // a-z, A-Z
+    if ((0x61 <= code && code <= 0x7A) || (0x41 <= code && code <= 0x5A))
+      return 'ident';
+
+    // 1-9
+    if (0x31 <= code && code <= 0x39)
+      return 'number';
+
+    return 'else';
+  }
+
+  var pathStateMachine = {
+    'beforePath': {
+      'ws': ['beforePath'],
+      'ident': ['inIdent', 'append'],
+      '[': ['beforeElement'],
+      'eof': ['afterPath']
+    },
+
+    'inPath': {
+      'ws': ['inPath'],
+      '.': ['beforeIdent'],
+      '[': ['beforeElement'],
+      'eof': ['afterPath']
+    },
+
+    'beforeIdent': {
+      'ws': ['beforeIdent'],
+      'ident': ['inIdent', 'append']
+    },
+
+    'inIdent': {
+      'ident': ['inIdent', 'append'],
+      '0': ['inIdent', 'append'],
+      'number': ['inIdent', 'append'],
+      'ws': ['inPath', 'push'],
+      '.': ['beforeIdent', 'push'],
+      '[': ['beforeElement', 'push'],
+      'eof': ['afterPath', 'push']
+    },
+
+    'beforeElement': {
+      'ws': ['beforeElement'],
+      '0': ['afterZero', 'append'],
+      'number': ['inIndex', 'append'],
+      "'": ['inSingleQuote', 'append', ''],
+      '"': ['inDoubleQuote', 'append', '']
+    },
+
+    'afterZero': {
+      'ws': ['afterElement', 'push'],
+      ']': ['inPath', 'push']
+    },
+
+    'inIndex': {
+      '0': ['inIndex', 'append'],
+      'number': ['inIndex', 'append'],
+      'ws': ['afterElement'],
+      ']': ['inPath', 'push']
+    },
+
+    'inSingleQuote': {
+      "'": ['afterElement'],
+      'eof': ['error'],
+      'else': ['inSingleQuote', 'append']
+    },
+
+    'inDoubleQuote': {
+      '"': ['afterElement'],
+      'eof': ['error'],
+      'else': ['inDoubleQuote', 'append']
+    },
+
+    'afterElement': {
+      'ws': ['afterElement'],
+      ']': ['inPath', 'push']
+    }
+  }
+
+  function noop() {}
+
+  function parsePath(path) {
+    var keys = [];
+    var index = -1;
+    var c, newChar, key, type, transition, action, typeMap, mode = 'beforePath';
+
+    var actions = {
+      push: function() {
+        if (key === undefined)
+          return;
+
+        keys.push(key);
+        key = undefined;
+      },
+
+      append: function() {
+        if (key === undefined)
+          key = newChar
+        else
+          key += newChar;
+      }
+    };
+
+    function maybeUnescapeQuote() {
+      if (index >= path.length)
+        return;
+
+      var nextChar = path[index + 1];
+      if ((mode == 'inSingleQuote' && nextChar == "'") ||
+          (mode == 'inDoubleQuote' && nextChar == '"')) {
+        index++;
+        newChar = nextChar;
+        actions.append();
+        return true;
+      }
+    }
+
+    while (mode) {
+      index++;
+      c = path[index];
+
+      if (c == '\\' && maybeUnescapeQuote(mode))
+        continue;
+
+      type = getPathCharType(c);
+      typeMap = pathStateMachine[mode];
+      transition = typeMap[type] || typeMap['else'] || 'error';
+
+      if (transition == 'error')
+        return; // parse error;
+
+      mode = transition[0];
+      action = actions[transition[1]] || noop;
+      newChar = transition[2] === undefined ? c : transition[2];
+      action();
+
+      if (mode === 'afterPath') {
+        return keys;
+      }
+    }
+
+    return; // parse error
+  }
+
+  function isIdent(s) {
+    return identRegExp.test(s);
+  }
+
+  var constructorIsPrivate = {};
+
+  function Path(parts, privateToken) {
+    if (privateToken !== constructorIsPrivate)
+      throw Error('Use Path.get to retrieve path objects');
+
+    for (var i = 0; i < parts.length; i++) {
+      this.push(String(parts[i]));
+    }
+
+    if (hasEval && this.length) {
+      this.getValueFrom = this.compiledGetValueFromFn();
+    }
+  }
+
+  // TODO(rafaelw): Make simple LRU cache
+  var pathCache = {};
+
+  function getPath(pathString) {
+    if (pathString instanceof Path)
+      return pathString;
+
+    if (pathString == null || pathString.length == 0)
+      pathString = '';
+
+    if (typeof pathString != 'string') {
+      if (isIndex(pathString.length)) {
+        // Constructed with array-like (pre-parsed) keys
+        return new Path(pathString, constructorIsPrivate);
+      }
+
+      pathString = String(pathString);
+    }
+
+    var path = pathCache[pathString];
+    if (path)
+      return path;
+
+    var parts = parsePath(pathString);
+    if (!parts)
+      return invalidPath;
+
+    var path = new Path(parts, constructorIsPrivate);
+    pathCache[pathString] = path;
+    return path;
+  }
+
+  Path.get = getPath;
+
+  function formatAccessor(key) {
+    if (isIndex(key)) {
+      return '[' + key + ']';
+    } else {
+      return '["' + key.replace(/"/g, '\\"') + '"]';
+    }
+  }
+
+  Path.prototype = createObject({
+    __proto__: [],
+    valid: true,
+
+    toString: function() {
+      var pathString = '';
+      for (var i = 0; i < this.length; i++) {
+        var key = this[i];
+        if (isIdent(key)) {
+          pathString += i ? '.' + key : key;
+        } else {
+          pathString += formatAccessor(key);
+        }
+      }
+
+      return pathString;
+    },
+
+    getValueFrom: function(obj, directObserver) {
+      for (var i = 0; i < this.length; i++) {
+        if (obj == null)
+          return;
+        obj = obj[this[i]];
+      }
+      return obj;
+    },
+
+    iterateObjects: function(obj, observe) {
+      for (var i = 0; i < this.length; i++) {
+        if (i)
+          obj = obj[this[i - 1]];
+        if (!isObject(obj))
+          return;
+        observe(obj, this[i]);
+      }
+    },
+
+    compiledGetValueFromFn: function() {
+      var str = '';
+      var pathString = 'obj';
+      str += 'if (obj != null';
+      var i = 0;
+      var key;
+      for (; i < (this.length - 1); i++) {
+        key = this[i];
+        pathString += isIdent(key) ? '.' + key : formatAccessor(key);
+        str += ' &&\n     ' + pathString + ' != null';
+      }
+      str += ')\n';
+
+      var key = this[i];
+      pathString += isIdent(key) ? '.' + key : formatAccessor(key);
+
+      str += '  return ' + pathString + ';\nelse\n  return undefined;';
+      return new Function('obj', str);
+    },
+
+    setValueFrom: function(obj, value) {
+      if (!this.length)
+        return false;
+
+      for (var i = 0; i < this.length - 1; i++) {
+        if (!isObject(obj))
+          return false;
+        obj = obj[this[i]];
+      }
+
+      if (!isObject(obj))
+        return false;
+
+      obj[this[i]] = value;
+      return true;
+    }
+  });
+
+  var invalidPath = new Path('', constructorIsPrivate);
+  invalidPath.valid = false;
+  invalidPath.getValueFrom = invalidPath.setValueFrom = function() {};
+
+  var MAX_DIRTY_CHECK_CYCLES = 1000;
+
+  function dirtyCheck(observer) {
+    var cycles = 0;
+    while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check_()) {
+      cycles++;
+    }
+    if (testingExposeCycleCount)
+      global.dirtyCheckCycleCount = cycles;
+
+    return cycles > 0;
+  }
+
+  function objectIsEmpty(object) {
+    for (var prop in object)
+      return false;
+    return true;
+  }
+
+  function diffIsEmpty(diff) {
+    return objectIsEmpty(diff.added) &&
+           objectIsEmpty(diff.removed) &&
+           objectIsEmpty(diff.changed);
+  }
+
+  function diffObjectFromOldObject(object, oldObject) {
+    var added = {};
+    var removed = {};
+    var changed = {};
+
+    for (var prop in oldObject) {
+      var newValue = object[prop];
+
+      if (newValue !== undefined && newValue === oldObject[prop])
+        continue;
+
+      if (!(prop in object)) {
+        removed[prop] = undefined;
+        continue;
+      }
+
+      if (newValue !== oldObject[prop])
+        changed[prop] = newValue;
+    }
+
+    for (var prop in object) {
+      if (prop in oldObject)
+        continue;
+
+      added[prop] = object[prop];
+    }
+
+    if (Array.isArray(object) && object.length !== oldObject.length)
+      changed.length = object.length;
+
+    return {
+      added: added,
+      removed: removed,
+      changed: changed
+    };
+  }
+
+  var eomTasks = [];
+  function runEOMTasks() {
+    if (!eomTasks.length)
+      return false;
+
+    for (var i = 0; i < eomTasks.length; i++) {
+      eomTasks[i]();
+    }
+    eomTasks.length = 0;
+    return true;
+  }
+
+  var runEOM = hasObserve ? (function(){
+    return function(fn) {
+      return Promise.resolve().then(fn);
+    }
+  })() :
+  (function() {
+    return function(fn) {
+      eomTasks.push(fn);
+    };
+  })();
+
+  var observedObjectCache = [];
+
+  function newObservedObject() {
+    var observer;
+    var object;
+    var discardRecords = false;
+    var first = true;
+
+    function callback(records) {
+      if (observer && observer.state_ === OPENED && !discardRecords)
+        observer.check_(records);
+    }
+
+    return {
+      open: function(obs) {
+        if (observer)
+          throw Error('ObservedObject in use');
+
+        if (!first)
+          Object.deliverChangeRecords(callback);
+
+        observer = obs;
+        first = false;
+      },
+      observe: function(obj, arrayObserve) {
+        object = obj;
+        if (arrayObserve)
+          Array.observe(object, callback);
+        else
+          Object.observe(object, callback);
+      },
+      deliver: function(discard) {
+        discardRecords = discard;
+        Object.deliverChangeRecords(callback);
+        discardRecords = false;
+      },
+      close: function() {
+        observer = undefined;
+        Object.unobserve(object, callback);
+        observedObjectCache.push(this);
+      }
+    };
+  }
+
+  /*
+   * The observedSet abstraction is a perf optimization which reduces the total
+   * number of Object.observe observations of a set of objects. The idea is that
+   * groups of Observers will have some object dependencies in common and this
+   * observed set ensures that each object in the transitive closure of
+   * dependencies is only observed once. The observedSet acts as a write barrier
+   * such that whenever any change comes through, all Observers are checked for
+   * changed values.
+   *
+   * Note that this optimization is explicitly moving work from setup-time to
+   * change-time.
+   *
+   * TODO(rafaelw): Implement "garbage collection". In order to move work off
+   * the critical path, when Observers are closed, their observed objects are
+   * not Object.unobserve(d). As a result, it's possible that if the observedSet
+   * is kept open, but some Observers have been closed, it could cause "leaks"
+   * (prevent otherwise collectable objects from being collected). At some
+   * point, we should implement incremental "gc" which keeps a list of
+   * observedSets which may need clean-up and does small amounts of cleanup on a
+   * timeout until all is clean.
+   */
+
+  function getObservedObject(observer, object, arrayObserve) {
+    var dir = observedObjectCache.pop() || newObservedObject();
+    dir.open(observer);
+    dir.observe(object, arrayObserve);
+    return dir;
+  }
+
+  var observedSetCache = [];
+
+  function newObservedSet() {
+    var observerCount = 0;
+    var observers = [];
+    var objects = [];
+    var rootObj;
+    var rootObjProps;
+
+    function observe(obj, prop) {
+      if (!obj)
+        return;
+
+      if (obj === rootObj)
+        rootObjProps[prop] = true;
+
+      if (objects.indexOf(obj) < 0) {
+        objects.push(obj);
+        Object.observe(obj, callback);
+      }
+
+      observe(Object.getPrototypeOf(obj), prop);
+    }
+
+    function allRootObjNonObservedProps(recs) {
+      for (var i = 0; i < recs.length; i++) {
+        var rec = recs[i];
+        if (rec.object !== rootObj ||
+            rootObjProps[rec.name] ||
+            rec.type === 'setPrototype') {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    function callback(recs) {
+      if (allRootObjNonObservedProps(recs))
+        return;
+
+      var observer;
+      for (var i = 0; i < observers.length; i++) {
+        observer = observers[i];
+        if (observer.state_ == OPENED) {
+          observer.iterateObjects_(observe);
+        }
+      }
+
+      for (var i = 0; i < observers.length; i++) {
+        observer = observers[i];
+        if (observer.state_ == OPENED) {
+          observer.check_();
+        }
+      }
+    }
+
+    var record = {
+      objects: objects,
+      get rootObject() { return rootObj; },
+      set rootObject(value) {
+        rootObj = value;
+        rootObjProps = {};
+      },
+      open: function(obs, object) {
+        observers.push(obs);
+        observerCount++;
+        obs.iterateObjects_(observe);
+      },
+      close: function(obs) {
+        observerCount--;
+        if (observerCount > 0) {
+          return;
+        }
+
+        for (var i = 0; i < objects.length; i++) {
+          Object.unobserve(objects[i], callback);
+          Observer.unobservedCount++;
+        }
+
+        observers.length = 0;
+        objects.length = 0;
+        rootObj = undefined;
+        rootObjProps = undefined;
+        observedSetCache.push(this);
+        if (lastObservedSet === this)
+          lastObservedSet = null;
+      },
+    };
+
+    return record;
+  }
+
+  var lastObservedSet;
+
+  function getObservedSet(observer, obj) {
+    if (!lastObservedSet || lastObservedSet.rootObject !== obj) {
+      lastObservedSet = observedSetCache.pop() || newObservedSet();
+      lastObservedSet.rootObject = obj;
+    }
+    lastObservedSet.open(observer, obj);
+    return lastObservedSet;
+  }
+
+  var UNOPENED = 0;
+  var OPENED = 1;
+  var CLOSED = 2;
+  var RESETTING = 3;
+
+  var nextObserverId = 1;
+
+  function Observer() {
+    this.state_ = UNOPENED;
+    this.callback_ = undefined;
+    this.target_ = undefined; // TODO(rafaelw): Should be WeakRef
+    this.directObserver_ = undefined;
+    this.value_ = undefined;
+    this.id_ = nextObserverId++;
+  }
+
+  Observer.prototype = {
+    open: function(callback, target) {
+      if (this.state_ != UNOPENED)
+        throw Error('Observer has already been opened.');
+
+      addToAll(this);
+      this.callback_ = callback;
+      this.target_ = target;
+      this.connect_();
+      this.state_ = OPENED;
+      return this.value_;
+    },
+
+    close: function() {
+      if (this.state_ != OPENED)
+        return;
+
+      removeFromAll(this);
+      this.disconnect_();
+      this.value_ = undefined;
+      this.callback_ = undefined;
+      this.target_ = undefined;
+      this.state_ = CLOSED;
+    },
+
+    deliver: function() {
+      if (this.state_ != OPENED)
+        return;
+
+      dirtyCheck(this);
+    },
+
+    report_: function(changes) {
+      try {
+        this.callback_.apply(this.target_, changes);
+      } catch (ex) {
+        Observer._errorThrownDuringCallback = true;
+        console.error('Exception caught during observer callback: ' +
+                       (ex.stack || ex));
+      }
+    },
+
+    discardChanges: function() {
+      this.check_(undefined, true);
+      return this.value_;
+    }
+  }
+
+  var collectObservers = !hasObserve;
+  var allObservers;
+  Observer._allObserversCount = 0;
+
+  if (collectObservers) {
+    allObservers = [];
+  }
+
+  function addToAll(observer) {
+    Observer._allObserversCount++;
+    if (!collectObservers)
+      return;
+
+    allObservers.push(observer);
+  }
+
+  function removeFromAll(observer) {
+    Observer._allObserversCount--;
+  }
+
+  var runningMicrotaskCheckpoint = false;
+
+  global.Platform = global.Platform || {};
+
+  global.Platform.performMicrotaskCheckpoint = function() {
+    if (runningMicrotaskCheckpoint)
+      return;
+
+    if (!collectObservers)
+      return;
+
+    runningMicrotaskCheckpoint = true;
+
+    var cycles = 0;
+    var anyChanged, toCheck;
+
+    do {
+      cycles++;
+      toCheck = allObservers;
+      allObservers = [];
+      anyChanged = false;
+
+      for (var i = 0; i < toCheck.length; i++) {
+        var observer = toCheck[i];
+        if (observer.state_ != OPENED)
+          continue;
+
+        if (observer.check_())
+          anyChanged = true;
+
+        allObservers.push(observer);
+      }
+      if (runEOMTasks())
+        anyChanged = true;
+    } while (cycles < MAX_DIRTY_CHECK_CYCLES && anyChanged);
+
+    if (testingExposeCycleCount)
+      global.dirtyCheckCycleCount = cycles;
+
+    runningMicrotaskCheckpoint = false;
+  };
+
+  if (collectObservers) {
+    global.Platform.clearObservers = function() {
+      allObservers = [];
+    };
+  }
+
+  function ObjectObserver(object) {
+    Observer.call(this);
+    this.value_ = object;
+    this.oldObject_ = undefined;
+  }
+
+  ObjectObserver.prototype = createObject({
+    __proto__: Observer.prototype,
+
+    arrayObserve: false,
+
+    connect_: function(callback, target) {
+      if (hasObserve) {
+        this.directObserver_ = getObservedObject(this, this.value_,
+                                                 this.arrayObserve);
+      } else {
+        this.oldObject_ = this.copyObject(this.value_);
+      }
+
+    },
+
+    copyObject: function(object) {
+      var copy = Array.isArray(object) ? [] : {};
+      for (var prop in object) {
+        copy[prop] = object[prop];
+      };
+      if (Array.isArray(object))
+        copy.length = object.length;
+      return copy;
+    },
+
+    check_: function(changeRecords, skipChanges) {
+      var diff;
+      var oldValues;
+      if (hasObserve) {
+        if (!changeRecords)
+          return false;
+
+        oldValues = {};
+        diff = diffObjectFromChangeRecords(this.value_, changeRecords,
+                                           oldValues);
+      } else {
+        oldValues = this.oldObject_;
+        diff = diffObjectFromOldObject(this.value_, this.oldObject_);
+      }
+
+      if (diffIsEmpty(diff))
+        return false;
+
+      if (!hasObserve)
+        this.oldObject_ = this.copyObject(this.value_);
+
+      this.report_([
+        diff.added || {},
+        diff.removed || {},
+        diff.changed || {},
+        function(property) {
+          return oldValues[property];
+        }
+      ]);
+
+      return true;
+    },
+
+    disconnect_: function() {
+      if (hasObserve) {
+        this.directObserver_.close();
+        this.directObserver_ = undefined;
+      } else {
+        this.oldObject_ = undefined;
+      }
+    },
+
+    deliver: function() {
+      if (this.state_ != OPENED)
+        return;
+
+      if (hasObserve)
+        this.directObserver_.deliver(false);
+      else
+        dirtyCheck(this);
+    },
+
+    discardChanges: function() {
+      if (this.directObserver_)
+        this.directObserver_.deliver(true);
+      else
+        this.oldObject_ = this.copyObject(this.value_);
+
+      return this.value_;
+    }
+  });
+
+  function ArrayObserver(array) {
+    if (!Array.isArray(array))
+      throw Error('Provided object is not an Array');
+    ObjectObserver.call(this, array);
+  }
+
+  ArrayObserver.prototype = createObject({
+
+    __proto__: ObjectObserver.prototype,
+
+    arrayObserve: true,
+
+    copyObject: function(arr) {
+      return arr.slice();
+    },
+
+    check_: function(changeRecords) {
+      var splices;
+      if (hasObserve) {
+        if (!changeRecords)
+          return false;
+        splices = projectArraySplices(this.value_, changeRecords);
+      } else {
+        splices = calcSplices(this.value_, 0, this.value_.length,
+                              this.oldObject_, 0, this.oldObject_.length);
+      }
+
+      if (!splices || !splices.length)
+        return false;
+
+      if (!hasObserve)
+        this.oldObject_ = this.copyObject(this.value_);
+
+      this.report_([splices]);
+      return true;
+    }
+  });
+
+  ArrayObserver.applySplices = function(previous, current, splices) {
+    splices.forEach(function(splice) {
+      var spliceArgs = [splice.index, splice.removed.length];
+      var addIndex = splice.index;
+      while (addIndex < splice.index + splice.addedCount) {
+        spliceArgs.push(current[addIndex]);
+        addIndex++;
+      }
+
+      Array.prototype.splice.apply(previous, spliceArgs);
+    });
+  };
+
+  function PathObserver(object, path) {
+    Observer.call(this);
+
+    this.object_ = object;
+    this.path_ = getPath(path);
+    this.directObserver_ = undefined;
+  }
+
+  PathObserver.prototype = createObject({
+    __proto__: Observer.prototype,
+
+    get path() {
+      return this.path_;
+    },
+
+    connect_: function() {
+      if (hasObserve)
+        this.directObserver_ = getObservedSet(this, this.object_);
+
+      this.check_(undefined, true);
+    },
+
+    disconnect_: function() {
+      this.value_ = undefined;
+
+      if (this.directObserver_) {
+        this.directObserver_.close(this);
+        this.directObserver_ = undefined;
+      }
+    },
+
+    iterateObjects_: function(observe) {
+      this.path_.iterateObjects(this.object_, observe);
+    },
+
+    check_: function(changeRecords, skipChanges) {
+      var oldValue = this.value_;
+      this.value_ = this.path_.getValueFrom(this.object_);
+      if (skipChanges || areSameValue(this.value_, oldValue))
+        return false;
+
+      this.report_([this.value_, oldValue, this]);
+      return true;
+    },
+
+    setValue: function(newValue) {
+      if (this.path_)
+        this.path_.setValueFrom(this.object_, newValue);
+    }
+  });
+
+  function CompoundObserver(reportChangesOnOpen) {
+    Observer.call(this);
+
+    this.reportChangesOnOpen_ = reportChangesOnOpen;
+    this.value_ = [];
+    this.directObserver_ = undefined;
+    this.observed_ = [];
+  }
+
+  var observerSentinel = {};
+
+  CompoundObserver.prototype = createObject({
+    __proto__: Observer.prototype,
+
+    connect_: function() {
+      if (hasObserve) {
+        var object;
+        var needsDirectObserver = false;
+        for (var i = 0; i < this.observed_.length; i += 2) {
+          object = this.observed_[i]
+          if (object !== observerSentinel) {
+            needsDirectObserver = true;
+            break;
+          }
+        }
+
+        if (needsDirectObserver)
+          this.directObserver_ = getObservedSet(this, object);
+      }
+
+      this.check_(undefined, !this.reportChangesOnOpen_);
+    },
+
+    disconnect_: function() {
+      for (var i = 0; i < this.observed_.length; i += 2) {
+        if (this.observed_[i] === observerSentinel)
+          this.observed_[i + 1].close();
+      }
+      this.observed_.length = 0;
+      this.value_.length = 0;
+
+      if (this.directObserver_) {
+        this.directObserver_.close(this);
+        this.directObserver_ = undefined;
+      }
+    },
+
+    addPath: function(object, path) {
+      if (this.state_ != UNOPENED && this.state_ != RESETTING)
+        throw Error('Cannot add paths once started.');
+
+      var path = getPath(path);
+      this.observed_.push(object, path);
+      if (!this.reportChangesOnOpen_)
+        return;
+      var index = this.observed_.length / 2 - 1;
+      this.value_[index] = path.getValueFrom(object);
+    },
+
+    addObserver: function(observer) {
+      if (this.state_ != UNOPENED && this.state_ != RESETTING)
+        throw Error('Cannot add observers once started.');
+
+      this.observed_.push(observerSentinel, observer);
+      if (!this.reportChangesOnOpen_)
+        return;
+      var index = this.observed_.length / 2 - 1;
+      this.value_[index] = observer.open(this.deliver, this);
+    },
+
+    startReset: function() {
+      if (this.state_ != OPENED)
+        throw Error('Can only reset while open');
+
+      this.state_ = RESETTING;
+      this.disconnect_();
+    },
+
+    finishReset: function() {
+      if (this.state_ != RESETTING)
+        throw Error('Can only finishReset after startReset');
+      this.state_ = OPENED;
+      this.connect_();
+
+      return this.value_;
+    },
+
+    iterateObjects_: function(observe) {
+      var object;
+      for (var i = 0; i < this.observed_.length; i += 2) {
+        object = this.observed_[i]
+        if (object !== observerSentinel)
+          this.observed_[i + 1].iterateObjects(object, observe)
+      }
+    },
+
+    check_: function(changeRecords, skipChanges) {
+      var oldValues;
+      for (var i = 0; i < this.observed_.length; i += 2) {
+        var object = this.observed_[i];
+        var path = this.observed_[i+1];
+        var value;
+        if (object === observerSentinel) {
+          var observable = path;
+          value = this.state_ === UNOPENED ?
+              observable.open(this.deliver, this) :
+              observable.discardChanges();
+        } else {
+          value = path.getValueFrom(object);
+        }
+
+        if (skipChanges) {
+          this.value_[i / 2] = value;
+          continue;
+        }
+
+        if (areSameValue(value, this.value_[i / 2]))
+          continue;
+
+        oldValues = oldValues || [];
+        oldValues[i / 2] = this.value_[i / 2];
+        this.value_[i / 2] = value;
+      }
+
+      if (!oldValues)
+        return false;
+
+      // TODO(rafaelw): Having observed_ as the third callback arg here is
+      // pretty lame API. Fix.
+      this.report_([this.value_, oldValues, this.observed_]);
+      return true;
+    }
+  });
+
+  function identFn(value) { return value; }
+
+  function ObserverTransform(observable, getValueFn, setValueFn,
+                             dontPassThroughSet) {
+    this.callback_ = undefined;
+    this.target_ = undefined;
+    this.value_ = undefined;
+    this.observable_ = observable;
+    this.getValueFn_ = getValueFn || identFn;
+    this.setValueFn_ = setValueFn || identFn;
+    // TODO(rafaelw): This is a temporary hack. PolymerExpressions needs this
+    // at the moment because of a bug in it's dependency tracking.
+    this.dontPassThroughSet_ = dontPassThroughSet;
+  }
+
+  ObserverTransform.prototype = {
+    open: function(callback, target) {
+      this.callback_ = callback;
+      this.target_ = target;
+      this.value_ =
+          this.getValueFn_(this.observable_.open(this.observedCallback_, this));
+      return this.value_;
+    },
+
+    observedCallback_: function(value) {
+      value = this.getValueFn_(value);
+      if (areSameValue(value, this.value_))
+        return;
+      var oldValue = this.value_;
+      this.value_ = value;
+      this.callback_.call(this.target_, this.value_, oldValue);
+    },
+
+    discardChanges: function() {
+      this.value_ = this.getValueFn_(this.observable_.discardChanges());
+      return this.value_;
+    },
+
+    deliver: function() {
+      return this.observable_.deliver();
+    },
+
+    setValue: function(value) {
+      value = this.setValueFn_(value);
+      if (!this.dontPassThroughSet_ && this.observable_.setValue)
+        return this.observable_.setValue(value);
+    },
+
+    close: function() {
+      if (this.observable_)
+        this.observable_.close();
+      this.callback_ = undefined;
+      this.target_ = undefined;
+      this.observable_ = undefined;
+      this.value_ = undefined;
+      this.getValueFn_ = undefined;
+      this.setValueFn_ = undefined;
+    }
+  }
+
+  var expectedRecordTypes = {
+    add: true,
+    update: true,
+    delete: true
+  };
+
+  function diffObjectFromChangeRecords(object, changeRecords, oldValues) {
+    var added = {};
+    var removed = {};
+
+    for (var i = 0; i < changeRecords.length; i++) {
+      var record = changeRecords[i];
+      if (!expectedRecordTypes[record.type]) {
+        console.error('Unknown changeRecord type: ' + record.type);
+        console.error(record);
+        continue;
+      }
+
+      if (!(record.name in oldValues))
+        oldValues[record.name] = record.oldValue;
+
+      if (record.type == 'update')
+        continue;
+
+      if (record.type == 'add') {
+        if (record.name in removed)
+          delete removed[record.name];
+        else
+          added[record.name] = true;
+
+        continue;
+      }
+
+      // type = 'delete'
+      if (record.name in added) {
+        delete added[record.name];
+        delete oldValues[record.name];
+      } else {
+        removed[record.name] = true;
+      }
+    }
+
+    for (var prop in added)
+      added[prop] = object[prop];
+
+    for (var prop in removed)
+      removed[prop] = undefined;
+
+    var changed = {};
+    for (var prop in oldValues) {
+      if (prop in added || prop in removed)
+        continue;
+
+      var newValue = object[prop];
+      if (oldValues[prop] !== newValue)
+        changed[prop] = newValue;
+    }
+
+    return {
+      added: added,
+      removed: removed,
+      changed: changed
+    };
+  }
+
+  function newSplice(index, removed, addedCount) {
+    return {
+      index: index,
+      removed: removed,
+      addedCount: addedCount
+    };
+  }
+
+  var EDIT_LEAVE = 0;
+  var EDIT_UPDATE = 1;
+  var EDIT_ADD = 2;
+  var EDIT_DELETE = 3;
+
+  function ArraySplice() {}
+
+  ArraySplice.prototype = {
+
+    // Note: This function is *based* on the computation of the Levenshtein
+    // "edit" distance. The one change is that "updates" are treated as two
+    // edits - not one. With Array splices, an update is really a delete
+    // followed by an add. By retaining this, we optimize for "keeping" the
+    // maximum array items in the original array. For example:
+    //
+    //   'xxxx123' -> '123yyyy'
+    //
+    // With 1-edit updates, the shortest path would be just to update all seven
+    // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
+    // leaves the substring '123' intact.
+    calcEditDistances: function(current, currentStart, currentEnd,
+                                old, oldStart, oldEnd) {
+      // "Deletion" columns
+      var rowCount = oldEnd - oldStart + 1;
+      var columnCount = currentEnd - currentStart + 1;
+      var distances = new Array(rowCount);
+
+      // "Addition" rows. Initialize null column.
+      for (var i = 0; i < rowCount; i++) {
+        distances[i] = new Array(columnCount);
+        distances[i][0] = i;
+      }
+
+      // Initialize null row
+      for (var j = 0; j < columnCount; j++)
+        distances[0][j] = j;
+
+      for (var i = 1; i < rowCount; i++) {
+        for (var j = 1; j < columnCount; j++) {
+          if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
+            distances[i][j] = distances[i - 1][j - 1];
+          else {
+            var north = distances[i - 1][j] + 1;
+            var west = distances[i][j - 1] + 1;
+            distances[i][j] = north < west ? north : west;
+          }
+        }
+      }
+
+      return distances;
+    },
+
+    // This starts at the final weight, and walks "backward" by finding
+    // the minimum previous weight recursively until the origin of the weight
+    // matrix.
+    spliceOperationsFromEditDistances: function(distances) {
+      var i = distances.length - 1;
+      var j = distances[0].length - 1;
+      var current = distances[i][j];
+      var edits = [];
+      while (i > 0 || j > 0) {
+        if (i == 0) {
+          edits.push(EDIT_ADD);
+          j--;
+          continue;
+        }
+        if (j == 0) {
+          edits.push(EDIT_DELETE);
+          i--;
+          continue;
+        }
+        var northWest = distances[i - 1][j - 1];
+        var west = distances[i - 1][j];
+        var north = distances[i][j - 1];
+
+        var min;
+        if (west < north)
+          min = west < northWest ? west : northWest;
+        else
+          min = north < northWest ? north : northWest;
+
+        if (min == northWest) {
+          if (northWest == current) {
+            edits.push(EDIT_LEAVE);
+          } else {
+            edits.push(EDIT_UPDATE);
+            current = northWest;
+          }
+          i--;
+          j--;
+        } else if (min == west) {
+          edits.push(EDIT_DELETE);
+          i--;
+          current = west;
+        } else {
+          edits.push(EDIT_ADD);
+          j--;
+          current = north;
+        }
+      }
+
+      edits.reverse();
+      return edits;
+    },
+
+    /**
+     * Splice Projection functions:
+     *
+     * A splice map is a representation of how a previous array of items
+     * was transformed into a new array of items. Conceptually it is a list of
+     * tuples of
+     *
+     *   <index, removed, addedCount>
+     *
+     * which are kept in ascending index order of. The tuple represents that at
+     * the |index|, |removed| sequence of items were removed, and counting forward
+     * from |index|, |addedCount| items were added.
+     */
+
+    /**
+     * Lacking individual splice mutation information, the minimal set of
+     * splices can be synthesized given the previous state and final state of an
+     * array. The basic approach is to calculate the edit distance matrix and
+     * choose the shortest path through it.
+     *
+     * Complexity: O(l * p)
+     *   l: The length of the current array
+     *   p: The length of the old array
+     */
+    calcSplices: function(current, currentStart, currentEnd,
+                          old, oldStart, oldEnd) {
+      var prefixCount = 0;
+      var suffixCount = 0;
+
+      var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
+      if (currentStart == 0 && oldStart == 0)
+        prefixCount = this.sharedPrefix(current, old, minLength);
+
+      if (currentEnd == current.length && oldEnd == old.length)
+        suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
+
+      currentStart += prefixCount;
+      oldStart += prefixCount;
+      currentEnd -= suffixCount;
+      oldEnd -= suffixCount;
+
+      if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
+        return [];
+
+      if (currentStart == currentEnd) {
+        var splice = newSplice(currentStart, [], 0);
+        while (oldStart < oldEnd)
+          splice.removed.push(old[oldStart++]);
+
+        return [ splice ];
+      } else if (oldStart == oldEnd)
+        return [ newSplice(currentStart, [], currentEnd - currentStart) ];
+
+      var ops = this.spliceOperationsFromEditDistances(
+          this.calcEditDistances(current, currentStart, currentEnd,
+                                 old, oldStart, oldEnd));
+
+      var splice = undefined;
+      var splices = [];
+      var index = currentStart;
+      var oldIndex = oldStart;
+      for (var i = 0; i < ops.length; i++) {
+        switch(ops[i]) {
+          case EDIT_LEAVE:
+            if (splice) {
+              splices.push(splice);
+              splice = undefined;
+            }
+
+            index++;
+            oldIndex++;
+            break;
+          case EDIT_UPDATE:
+            if (!splice)
+              splice = newSplice(index, [], 0);
+
+            splice.addedCount++;
+            index++;
+
+            splice.removed.push(old[oldIndex]);
+            oldIndex++;
+            break;
+          case EDIT_ADD:
+            if (!splice)
+              splice = newSplice(index, [], 0);
+
+            splice.addedCount++;
+            index++;
+            break;
+          case EDIT_DELETE:
+            if (!splice)
+              splice = newSplice(index, [], 0);
+
+            splice.removed.push(old[oldIndex]);
+            oldIndex++;
+            break;
+        }
+      }
+
+      if (splice) {
+        splices.push(splice);
+      }
+      return splices;
+    },
+
+    sharedPrefix: function(current, old, searchLength) {
+      for (var i = 0; i < searchLength; i++)
+        if (!this.equals(current[i], old[i]))
+          return i;
+      return searchLength;
+    },
+
+    sharedSuffix: function(current, old, searchLength) {
+      var index1 = current.length;
+      var index2 = old.length;
+      var count = 0;
+      while (count < searchLength && this.equals(current[--index1], old[--index2]))
+        count++;
+
+      return count;
+    },
+
+    calculateSplices: function(current, previous) {
+      return this.calcSplices(current, 0, current.length, previous, 0,
+                              previous.length);
+    },
+
+    equals: function(currentValue, previousValue) {
+      return currentValue === previousValue;
+    }
+  };
+
+  var arraySplice = new ArraySplice();
+
+  function calcSplices(current, currentStart, currentEnd,
+                       old, oldStart, oldEnd) {
+    return arraySplice.calcSplices(current, currentStart, currentEnd,
+                                   old, oldStart, oldEnd);
+  }
+
+  function intersect(start1, end1, start2, end2) {
+    // Disjoint
+    if (end1 < start2 || end2 < start1)
+      return -1;
+
+    // Adjacent
+    if (end1 == start2 || end2 == start1)
+      return 0;
+
+    // Non-zero intersect, span1 first
+    if (start1 < start2) {
+      if (end1 < end2)
+        return end1 - start2; // Overlap
+      else
+        return end2 - start2; // Contained
+    } else {
+      // Non-zero intersect, span2 first
+      if (end2 < end1)
+        return end2 - start1; // Overlap
+      else
+        return end1 - start1; // Contained
+    }
+  }
+
+  function mergeSplice(splices, index, removed, addedCount) {
+
+    var splice = newSplice(index, removed, addedCount);
+
+    var inserted = false;
+    var insertionOffset = 0;
+
+    for (var i = 0; i < splices.length; i++) {
+      var current = splices[i];
+      current.index += insertionOffset;
+
+      if (inserted)
+        continue;
+
+      var intersectCount = intersect(splice.index,
+                                     splice.index + splice.removed.length,
+                                     current.index,
+                                     current.index + current.addedCount);
+
+      if (intersectCount >= 0) {
+        // Merge the two splices
+
+        splices.splice(i, 1);
+        i--;
+
+        insertionOffset -= current.addedCount - current.removed.length;
+
+        splice.addedCount += current.addedCount - intersectCount;
+        var deleteCount = splice.removed.length +
+                          current.removed.length - intersectCount;
+
+        if (!splice.addedCount && !deleteCount) {
+          // merged splice is a noop. discard.
+          inserted = true;
+        } else {
+          var removed = current.removed;
+
+          if (splice.index < current.index) {
+            // some prefix of splice.removed is prepended to current.removed.
+            var prepend = splice.removed.slice(0, current.index - splice.index);
+            Array.prototype.push.apply(prepend, removed);
+            removed = prepend;
+          }
+
+          if (splice.index + splice.removed.length > current.index + current.addedCount) {
+            // some suffix of splice.removed is appended to current.removed.
+            var append = splice.removed.slice(current.index + current.addedCount - splice.index);
+            Array.prototype.push.apply(removed, append);
+          }
+
+          splice.removed = removed;
+          if (current.index < splice.index) {
+            splice.index = current.index;
+          }
+        }
+      } else if (splice.index < current.index) {
+        // Insert splice here.
+
+        inserted = true;
+
+        splices.splice(i, 0, splice);
+        i++;
+
+        var offset = splice.addedCount - splice.removed.length
+        current.index += offset;
+        insertionOffset += offset;
+      }
+    }
+
+    if (!inserted)
+      splices.push(splice);
+  }
+
+  function createInitialSplices(array, changeRecords) {
+    var splices = [];
+
+    for (var i = 0; i < changeRecords.length; i++) {
+      var record = changeRecords[i];
+      switch(record.type) {
+        case 'splice':
+          mergeSplice(splices, record.index, record.removed.slice(), record.addedCount);
+          break;
+        case 'add':
+        case 'update':
+        case 'delete':
+          if (!isIndex(record.name))
+            continue;
+          var index = toNumber(record.name);
+          if (index < 0)
+            continue;
+          mergeSplice(splices, index, [record.oldValue], 1);
+          break;
+        default:
+          console.error('Unexpected record type: ' + JSON.stringify(record));
+          break;
+      }
+    }
+
+    return splices;
+  }
+
+  function projectArraySplices(array, changeRecords) {
+    var splices = [];
+
+    createInitialSplices(array, changeRecords).forEach(function(splice) {
+      if (splice.addedCount == 1 && splice.removed.length == 1) {
+        if (splice.removed[0] !== array[splice.index])
+          splices.push(splice);
+
+        return
+      };
+
+      splices = splices.concat(calcSplices(array, splice.index, splice.index + splice.addedCount,
+                                           splice.removed, 0, splice.removed.length));
+    });
+
+    return splices;
+  }
+
+  // Export the observe-js object for **Node.js**, with backwards-compatibility
+  // for the old `require()` API. Also ensure `exports` is not a DOM Element.
+  // If we're in the browser, export as a global object.
+
+  var expose = global;
+
+  if (typeof exports !== 'undefined' && !exports.nodeType) {
+    if (typeof module !== 'undefined' && module.exports) {
+      exports = module.exports;
+    }
+    expose = exports;
+  }
+
+  expose.Observer = Observer;
+  expose.Observer.runEOM_ = runEOM;
+  expose.Observer.observerSentinel_ = observerSentinel; // for testing.
+  expose.Observer.hasObjectObserve = hasObserve;
+  expose.ArrayObserver = ArrayObserver;
+  expose.ArrayObserver.calculateSplices = function(current, previous) {
+    return arraySplice.calculateSplices(current, previous);
+  };
+
+  expose.ArraySplice = ArraySplice;
+  expose.ObjectObserver = ObjectObserver;
+  expose.PathObserver = PathObserver;
+  expose.CompoundObserver = CompoundObserver;
+  expose.Path = Path;
+  expose.ObserverTransform = ObserverTransform;
+  
+})(typeof global !== 'undefined' && global && typeof module !== 'undefined' && module ? global : this || window);
+
+// Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+// This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+// The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+// The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+// Code distributed by Google as part of the polymer project is also
+// subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+
+(function(global) {
+  'use strict';
+
+  var filter = Array.prototype.filter.call.bind(Array.prototype.filter);
+
+  function getTreeScope(node) {
+    while (node.parentNode) {
+      node = node.parentNode;
+    }
+
+    return typeof node.getElementById === 'function' ? node : null;
+  }
+
+  Node.prototype.bind = function(name, observable) {
+    console.error('Unhandled binding to Node: ', this, name, observable);
+  };
+
+  Node.prototype.bindFinished = function() {};
+
+  function updateBindings(node, name, binding) {
+    var bindings = node.bindings_;
+    if (!bindings)
+      bindings = node.bindings_ = {};
+
+    if (bindings[name])
+      binding[name].close();
+
+    return bindings[name] = binding;
+  }
+
+  function returnBinding(node, name, binding) {
+    return binding;
+  }
+
+  function sanitizeValue(value) {
+    return value == null ? '' : value;
+  }
+
+  function updateText(node, value) {
+    node.data = sanitizeValue(value);
+  }
+
+  function textBinding(node) {
+    return function(value) {
+      return updateText(node, value);
+    };
+  }
+
+  var maybeUpdateBindings = returnBinding;
+
+  Object.defineProperty(Platform, 'enableBindingsReflection', {
+    get: function() {
+      return maybeUpdateBindings === updateBindings;
+    },
+    set: function(enable) {
+      maybeUpdateBindings = enable ? updateBindings : returnBinding;
+      return enable;
+    },
+    configurable: true
+  });
+
+  Text.prototype.bind = function(name, value, oneTime) {
+    if (name !== 'textContent')
+      return Node.prototype.bind.call(this, name, value, oneTime);
+
+    if (oneTime)
+      return updateText(this, value);
+
+    var observable = value;
+    updateText(this, observable.open(textBinding(this)));
+    return maybeUpdateBindings(this, name, observable);
+  }
+
+  function updateAttribute(el, name, conditional, value) {
+    if (conditional) {
+      if (value)
+        el.setAttribute(name, '');
+      else
+        el.removeAttribute(name);
+      return;
+    }
+
+    el.setAttribute(name, sanitizeValue(value));
+  }
+
+  function attributeBinding(el, name, conditional) {
+    return function(value) {
+      updateAttribute(el, name, conditional, value);
+    };
+  }
+
+  Element.prototype.bind = function(name, value, oneTime) {
+    var conditional = name[name.length - 1] == '?';
+    if (conditional) {
+      this.removeAttribute(name);
+      name = name.slice(0, -1);
+    }
+
+    if (oneTime)
+      return updateAttribute(this, name, conditional, value);
+
+
+    var observable = value;
+    updateAttribute(this, name, conditional,
+        observable.open(attributeBinding(this, name, conditional)));
+
+    return maybeUpdateBindings(this, name, observable);
+  };
+
+  var checkboxEventType;
+  (function() {
+    // Attempt to feature-detect which event (change or click) is fired first
+    // for checkboxes.
+    var div = document.createElement('div');
+    var checkbox = div.appendChild(document.createElement('input'));
+    checkbox.setAttribute('type', 'checkbox');
+    var first;
+    var count = 0;
+    checkbox.addEventListener('click', function(e) {
+      count++;
+      first = first || 'click';
+    });
+    checkbox.addEventListener('change', function() {
+      count++;
+      first = first || 'change';
+    });
+
+    var event = document.createEvent('MouseEvent');
+    event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false,
+        false, false, false, 0, null);
+    checkbox.dispatchEvent(event);
+    // WebKit/Blink don't fire the change event if the element is outside the
+    // document, so assume 'change' for that case.
+    checkboxEventType = count == 1 ? 'change' : first;
+  })();
+
+  function getEventForInputType(element) {
+    switch (element.type) {
+      case 'checkbox':
+        return checkboxEventType;
+      case 'radio':
+      case 'select-multiple':
+      case 'select-one':
+        return 'change';
+      case 'range':
+        if (/Trident|MSIE/.test(navigator.userAgent))
+          return 'change';
+      default:
+        return 'input';
+    }
+  }
+
+  function updateInput(input, property, value, santizeFn) {
+    input[property] = (santizeFn || sanitizeValue)(value);
+  }
+
+  function inputBinding(input, property, santizeFn) {
+    return function(value) {
+      return updateInput(input, property, value, santizeFn);
+    }
+  }
+
+  function noop() {}
+
+  function bindInputEvent(input, property, observable, postEventFn) {
+    var eventType = getEventForInputType(input);
+
+    function eventHandler() {
+      var isNum = property == 'value' && input.type == 'number';
+      observable.setValue(isNum ? input.valueAsNumber : input[property]);
+      observable.discardChanges();
+      (postEventFn || noop)(input);
+      Platform.performMicrotaskCheckpoint();
+    }
+    input.addEventListener(eventType, eventHandler);
+
+    return {
+      close: function() {
+        input.removeEventListener(eventType, eventHandler);
+        observable.close();
+      },
+
+      observable_: observable
+    }
+  }
+
+  function booleanSanitize(value) {
+    return Boolean(value);
+  }
+
+  // |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
+  // Returns an array containing all radio buttons other than |element| that
+  // have the same |name|, either in the form that |element| belongs to or,
+  // if no form, in the document tree to which |element| belongs.
+  //
+  // This implementation is based upon the HTML spec definition of a
+  // "radio button group":
+  //   http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state.html#radio-button-group
+  //
+  function getAssociatedRadioButtons(element) {
+    if (element.form) {
+      return filter(element.form.elements, function(el) {
+        return el != element &&
+            el.tagName == 'INPUT' &&
+            el.type == 'radio' &&
+            el.name == element.name;
+      });
+    } else {
+      var treeScope = getTreeScope(element);
+      if (!treeScope)
+        return [];
+      var radios = treeScope.querySelectorAll(
+          'input[type="radio"][name="' + element.name + '"]');
+      return filter(radios, function(el) {
+        return el != element && !el.form;
+      });
+    }
+  }
+
+  function checkedPostEvent(input) {
+    // Only the radio button that is getting checked gets an event. We
+    // therefore find all the associated radio buttons and update their
+    // check binding manually.
+    if (input.tagName === 'INPUT' &&
+        input.type === 'radio') {
+      getAssociatedRadioButtons(input).forEach(function(radio) {
+        var checkedBinding = radio.bindings_.checked;
+        if (checkedBinding) {
+          // Set the value directly to avoid an infinite call stack.
+          checkedBinding.observable_.setValue(false);
+        }
+      });
+    }
+  }
+
+  HTMLInputElement.prototype.bind = function(name, value, oneTime) {
+    if (name !== 'value' && name !== 'checked')
+      return HTMLElement.prototype.bind.call(this, name, value, oneTime);
+
+    this.removeAttribute(name);
+    var sanitizeFn = name == 'checked' ? booleanSanitize : sanitizeValue;
+    var postEventFn = name == 'checked' ? checkedPostEvent : noop;
+
+    if (oneTime)
+      return updateInput(this, name, value, sanitizeFn);
+
+
+    var observable = value;
+    var binding = bindInputEvent(this, name, observable, postEventFn);
+    updateInput(this, name,
+                observable.open(inputBinding(this, name, sanitizeFn)),
+                sanitizeFn);
+
+    // Checkboxes may need to update bindings of other checkboxes.
+    return updateBindings(this, name, binding);
+  }
+
+  HTMLTextAreaElement.prototype.bind = function(name, value, oneTime) {
+    if (name !== 'value')
+      return HTMLElement.prototype.bind.call(this, name, value, oneTime);
+
+    this.removeAttribute('value');
+
+    if (oneTime)
+      return updateInput(this, 'value', value);
+
+    var observable = value;
+    var binding = bindInputEvent(this, 'value', observable);
+    updateInput(this, 'value',
+                observable.open(inputBinding(this, 'value', sanitizeValue)));
+    return maybeUpdateBindings(this, name, binding);
+  }
+
+  function updateOption(option, value) {
+    var parentNode = option.parentNode;;
+    var select;
+    var selectBinding;
+    var oldValue;
+    if (parentNode instanceof HTMLSelectElement &&
+        parentNode.bindings_ &&
+        parentNode.bindings_.value) {
+      select = parentNode;
+      selectBinding = select.bindings_.value;
+      oldValue = select.value;
+    }
+
+    option.value = sanitizeValue(value);
+
+    if (select && select.value != oldValue) {
+      selectBinding.observable_.setValue(select.value);
+      selectBinding.observable_.discardChanges();
+      Platform.performMicrotaskCheckpoint();
+    }
+  }
+
+  function optionBinding(option) {
+    return function(value) {
+      updateOption(option, value);
+    }
+  }
+
+  HTMLOptionElement.prototype.bind = function(name, value, oneTime) {
+    if (name !== 'value')
+      return HTMLElement.prototype.bind.call(this, name, value, oneTime);
+
+    this.removeAttribute('value');
+
+    if (oneTime)
+      return updateOption(this, value);
+
+    var observable = value;
+    var binding = bindInputEvent(this, 'value', observable);
+    updateOption(this, observable.open(optionBinding(this)));
+    return maybeUpdateBindings(this, name, binding);
+  }
+
+  HTMLSelectElement.prototype.bind = function(name, value, oneTime) {
+    if (name === 'selectedindex')
+      name = 'selectedIndex';
+
+    if (name !== 'selectedIndex' && name !== 'value')
+      return HTMLElement.prototype.bind.call(this, name, value, oneTime);
+
+    this.removeAttribute(name);
+
+    if (oneTime)
+      return updateInput(this, name, value);
+
+    var observable = value;
+    var binding = bindInputEvent(this, name, observable);
+    updateInput(this, name,
+                observable.open(inputBinding(this, name)));
+
+    // Option update events may need to access select bindings.
+    return updateBindings(this, name, binding);
+  }
+})(this);
+
+// Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+// This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+// The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+// The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+// Code distributed by Google as part of the polymer project is also
+// subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+
+(function(global) {
+  'use strict';
+
+  function assert(v) {
+    if (!v)
+      throw new Error('Assertion failed');
+  }
+
+  var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
+
+  function getFragmentRoot(node) {
+    var p;
+    while (p = node.parentNode) {
+      node = p;
+    }
+
+    return node;
+  }
+
+  function searchRefId(node, id) {
+    if (!id)
+      return;
+
+    var ref;
+    var selector = '#' + id;
+    while (!ref) {
+      node = getFragmentRoot(node);
+
+      if (node.protoContent_)
+        ref = node.protoContent_.querySelector(selector);
+      else if (node.getElementById)
+        ref = node.getElementById(id);
+
+      if (ref || !node.templateCreator_)
+        break
+
+      node = node.templateCreator_;
+    }
+
+    return ref;
+  }
+
+  function getInstanceRoot(node) {
+    while (node.parentNode) {
+      node = node.parentNode;
+    }
+    return node.templateCreator_ ? node : null;
+  }
+
+  var Map;
+  if (global.Map && typeof global.Map.prototype.forEach === 'function') {
+    Map = global.Map;
+  } else {
+    Map = function() {
+      this.keys = [];
+      this.values = [];
+    };
+
+    Map.prototype = {
+      set: function(key, value) {
+        var index = this.keys.indexOf(key);
+        if (index < 0) {
+          this.keys.push(key);
+          this.values.push(value);
+        } else {
+          this.values[index] = value;
+        }
+      },
+
+      get: function(key) {
+        var index = this.keys.indexOf(key);
+        if (index < 0)
+          return;
+
+        return this.values[index];
+      },
+
+      delete: function(key, value) {
+        var index = this.keys.indexOf(key);
+        if (index < 0)
+          return false;
+
+        this.keys.splice(index, 1);
+        this.values.splice(index, 1);
+        return true;
+      },
+
+      forEach: function(f, opt_this) {
+        for (var i = 0; i < this.keys.length; i++)
+          f.call(opt_this || this, this.values[i], this.keys[i], this);
+      }
+    };
+  }
+
+  // JScript does not have __proto__. We wrap all object literals with
+  // createObject which uses Object.create, Object.defineProperty and
+  // Object.getOwnPropertyDescriptor to create a new object that does the exact
+  // same thing. The main downside to this solution is that we have to extract
+  // all those property descriptors for IE.
+  var createObject = ('__proto__' in {}) ?
+      function(obj) { return obj; } :
+      function(obj) {
+        var proto = obj.__proto__;
+        if (!proto)
+          return obj;
+        var newObject = Object.create(proto);
+        Object.getOwnPropertyNames(obj).forEach(function(name) {
+          Object.defineProperty(newObject, name,
+                               Object.getOwnPropertyDescriptor(obj, name));
+        });
+        return newObject;
+      };
+
+  // IE does not support have Document.prototype.contains.
+  if (typeof document.contains != 'function') {
+    Document.prototype.contains = function(node) {
+      if (node === this || node.parentNode === this)
+        return true;
+      return this.documentElement.contains(node);
+    }
+  }
+
+  var BIND = 'bind';
+  var REPEAT = 'repeat';
+  var IF = 'if';
+
+  var templateAttributeDirectives = {
+    'template': true,
+    'repeat': true,
+    'bind': true,
+    'ref': true,
+    'if': true
+  };
+
+  var semanticTemplateElements = {
+    'THEAD': true,
+    'TBODY': true,
+    'TFOOT': true,
+    'TH': true,
+    'TR': true,
+    'TD': true,
+    'COLGROUP': true,
+    'COL': true,
+    'CAPTION': true,
+    'OPTION': true,
+    'OPTGROUP': true
+  };
+
+  var hasTemplateElement = typeof HTMLTemplateElement !== 'undefined';
+  if (hasTemplateElement) {
+    // TODO(rafaelw): Remove when fix for
+    // https://codereview.chromium.org/164803002/
+    // makes it to Chrome release.
+    (function() {
+      var t = document.createElement('template');
+      var d = t.content.ownerDocument;
+      var html = d.appendChild(d.createElement('html'));
+      var head = html.appendChild(d.createElement('head'));
+      var base = d.createElement('base');
+      base.href = document.baseURI;
+      head.appendChild(base);
+    })();
+  }
+
+  var allTemplatesSelectors = 'template, ' +
+      Object.keys(semanticTemplateElements).map(function(tagName) {
+        return tagName.toLowerCase() + '[template]';
+      }).join(', ');
+
+  function isSVGTemplate(el) {
+    return el.tagName == 'template' &&
+           el.namespaceURI == 'http://www.w3.org/2000/svg';
+  }
+
+  function isHTMLTemplate(el) {
+    return el.tagName == 'TEMPLATE' &&
+           el.namespaceURI == 'http://www.w3.org/1999/xhtml';
+  }
+
+  function isAttributeTemplate(el) {
+    return Boolean(semanticTemplateElements[el.tagName] &&
+                   el.hasAttribute('template'));
+  }
+
+  function isTemplate(el) {
+    if (el.isTemplate_ === undefined)
+      el.isTemplate_ = el.tagName == 'TEMPLATE' || isAttributeTemplate(el);
+
+    return el.isTemplate_;
+  }
+
+  // FIXME: Observe templates being added/removed from documents
+  // FIXME: Expose imperative API to decorate and observe templates in
+  // "disconnected tress" (e.g. ShadowRoot)
+  document.addEventListener('DOMContentLoaded', function(e) {
+    bootstrapTemplatesRecursivelyFrom(document);
+    // FIXME: Is this needed? Seems like it shouldn't be.
+    Platform.performMicrotaskCheckpoint();
+  }, false);
+
+  function forAllTemplatesFrom(node, fn) {
+    var subTemplates = node.querySelectorAll(allTemplatesSelectors);
+
+    if (isTemplate(node))
+      fn(node)
+    forEach(subTemplates, fn);
+  }
+
+  function bootstrapTemplatesRecursivelyFrom(node) {
+    function bootstrap(template) {
+      if (!HTMLTemplateElement.decorate(template))
+        bootstrapTemplatesRecursivelyFrom(template.content);
+    }
+
+    forAllTemplatesFrom(node, bootstrap);
+  }
+
+  if (!hasTemplateElement) {
+    /**
+     * This represents a <template> element.
+     * @constructor
+     * @extends {HTMLElement}
+     */
+    global.HTMLTemplateElement = function() {
+      throw TypeError('Illegal constructor');
+    };
+  }
+
+  var hasProto = '__proto__' in {};
+
+  function mixin(to, from) {
+    Object.getOwnPropertyNames(from).forEach(function(name) {
+      Object.defineProperty(to, name,
+                            Object.getOwnPropertyDescriptor(from, name));
+    });
+  }
+
+  // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#dfn-template-contents-owner
+  function getOrCreateTemplateContentsOwner(template) {
+    var doc = template.ownerDocument
+    if (!doc.defaultView)
+      return doc;
+    var d = doc.templateContentsOwner_;
+    if (!d) {
+      // TODO(arv): This should either be a Document or HTMLDocument depending
+      // on doc.
+      d = doc.implementation.createHTMLDocument('');
+      while (d.lastChild) {
+        d.removeChild(d.lastChild);
+      }
+      doc.templateContentsOwner_ = d;
+    }
+    return d;
+  }
+
+  function getTemplateStagingDocument(template) {
+    if (!template.stagingDocument_) {
+      var owner = template.ownerDocument;
+      if (!owner.stagingDocument_) {
+        owner.stagingDocument_ = owner.implementation.createHTMLDocument('');
+        owner.stagingDocument_.isStagingDocument = true;
+        // TODO(rafaelw): Remove when fix for
+        // https://codereview.chromium.org/164803002/
+        // makes it to Chrome release.
+        var base = owner.stagingDocument_.createElement('base');
+        base.href = document.baseURI;
+        owner.stagingDocument_.head.appendChild(base);
+
+        owner.stagingDocument_.stagingDocument_ = owner.stagingDocument_;
+      }
+
+      template.stagingDocument_ = owner.stagingDocument_;
+    }
+
+    return template.stagingDocument_;
+  }
+
+  // For non-template browsers, the parser will disallow <template> in certain
+  // locations, so we allow "attribute templates" which combine the template
+  // element with the top-level container node of the content, e.g.
+  //
+  //   <tr template repeat="{{ foo }}"" class="bar"><td>Bar</td></tr>
+  //
+  // becomes
+  //
+  //   <template repeat="{{ foo }}">
+  //   + #document-fragment
+  //     + <tr class="bar">
+  //       + <td>Bar</td>
+  //
+  function extractTemplateFromAttributeTemplate(el) {
+    var template = el.ownerDocument.createElement('template');
+    el.parentNode.insertBefore(template, el);
+
+    var attribs = el.attributes;
+    var count = attribs.length;
+    while (count-- > 0) {
+      var attrib = attribs[count];
+      if (templateAttributeDirectives[attrib.name]) {
+        if (attrib.name !== 'template')
+          template.setAttribute(attrib.name, attrib.value);
+        el.removeAttribute(attrib.name);
+      }
+    }
+
+    return template;
+  }
+
+  function extractTemplateFromSVGTemplate(el) {
+    var template = el.ownerDocument.createElement('template');
+    el.parentNode.insertBefore(template, el);
+
+    var attribs = el.attributes;
+    var count = attribs.length;
+    while (count-- > 0) {
+      var attrib = attribs[count];
+      template.setAttribute(attrib.name, attrib.value);
+      el.removeAttribute(attrib.name);
+    }
+
+    el.parentNode.removeChild(el);
+    return template;
+  }
+
+  function liftNonNativeTemplateChildrenIntoContent(template, el, useRoot) {
+    var content = template.content;
+    if (useRoot) {
+      content.appendChild(el);
+      return;
+    }
+
+    var child;
+    while (child = el.firstChild) {
+      content.appendChild(child);
+    }
+  }
+
+  var templateObserver;
+  if (typeof MutationObserver == 'function') {
+    templateObserver = new MutationObserver(function(records) {
+      for (var i = 0; i < records.length; i++) {
+        records[i].target.refChanged_();
+      }
+    });
+  }
+
+  /**
+   * Ensures proper API and content model for template elements.
+   * @param {HTMLTemplateElement} opt_instanceRef The template element which
+   *     |el| template element will return as the value of its ref(), and whose
+   *     content will be used as source when createInstance() is invoked.
+   */
+  HTMLTemplateElement.decorate = function(el, opt_instanceRef) {
+    if (el.templateIsDecorated_)
+      return false;
+
+    var templateElement = el;
+    templateElement.templateIsDecorated_ = true;
+
+    var isNativeHTMLTemplate = isHTMLTemplate(templateElement) &&
+                               hasTemplateElement;
+    var bootstrapContents = isNativeHTMLTemplate;
+    var liftContents = !isNativeHTMLTemplate;
+    var liftRoot = false;
+
+    if (!isNativeHTMLTemplate) {
+      if (isAttributeTemplate(templateElement)) {
+        assert(!opt_instanceRef);
+        templateElement = extractTemplateFromAttributeTemplate(el);
+        templateElement.templateIsDecorated_ = true;
+        isNativeHTMLTemplate = hasTemplateElement;
+        liftRoot = true;
+      } else if (isSVGTemplate(templateElement)) {
+        templateElement = extractTemplateFromSVGTemplate(el);
+        templateElement.templateIsDecorated_ = true;
+        isNativeHTMLTemplate = hasTemplateElement;
+      }
+    }
+
+    if (!isNativeHTMLTemplate) {
+      fixTemplateElementPrototype(templateElement);
+      var doc = getOrCreateTemplateContentsOwner(templateElement);
+      templateElement.content_ = doc.createDocumentFragment();
+    }
+
+    if (opt_instanceRef) {
+      // template is contained within an instance, its direct content must be
+      // empty
+      templateElement.instanceRef_ = opt_instanceRef;
+    } else if (liftContents) {
+      liftNonNativeTemplateChildrenIntoContent(templateElement,
+                                               el,
+                                               liftRoot);
+    } else if (bootstrapContents) {
+      bootstrapTemplatesRecursivelyFrom(templateElement.content);
+    }
+
+    return true;
+  };
+
+  // TODO(rafaelw): This used to decorate recursively all templates from a given
+  // node. This happens by default on 'DOMContentLoaded', but may be needed
+  // in subtrees not descendent from document (e.g. ShadowRoot).
+  // Review whether this is the right public API.
+  HTMLTemplateElement.bootstrap = bootstrapTemplatesRecursivelyFrom;
+
+  var htmlElement = global.HTMLUnknownElement || HTMLElement;
+
+  var contentDescriptor = {
+    get: function() {
+      return this.content_;
+    },
+    enumerable: true,
+    configurable: true
+  };
+
+  if (!hasTemplateElement) {
+    // Gecko is more picky with the prototype than WebKit. Make sure to use the
+    // same prototype as created in the constructor.
+    HTMLTemplateElement.prototype = Object.create(htmlElement.prototype);
+
+    Object.defineProperty(HTMLTemplateElement.prototype, 'content',
+                          contentDescriptor);
+  }
+
+  function fixTemplateElementPrototype(el) {
+    if (hasProto)
+      el.__proto__ = HTMLTemplateElement.prototype;
+    else
+      mixin(el, HTMLTemplateElement.prototype);
+  }
+
+  function ensureSetModelScheduled(template) {
+    if (!template.setModelFn_) {
+      template.setModelFn_ = function() {
+        template.setModelFnScheduled_ = false;
+        var map = getBindings(template,
+            template.delegate_ && template.delegate_.prepareBinding);
+        processBindings(template, map, template.model_);
+      };
+    }
+
+    if (!template.setModelFnScheduled_) {
+      template.setModelFnScheduled_ = true;
+      Observer.runEOM_(template.setModelFn_);
+    }
+  }
+
+  mixin(HTMLTemplateElement.prototype, {
+    bind: function(name, value, oneTime) {
+      if (name != 'ref')
+        return Element.prototype.bind.call(this, name, value, oneTime);
+
+      var self = this;
+      var ref = oneTime ? value : value.open(function(ref) {
+        self.setAttribute('ref', ref);
+        self.refChanged_();
+      });
+
+      this.setAttribute('ref', ref);
+      this.refChanged_();
+      if (oneTime)
+        return;
+
+      if (!this.bindings_) {
+        this.bindings_ = { ref: value };
+      } else {
+        this.bindings_.ref = value;
+      }
+
+      return value;
+    },
+
+    processBindingDirectives_: function(directives) {
+      if (this.iterator_)
+        this.iterator_.closeDeps();
+
+      if (!directives.if && !directives.bind && !directives.repeat) {
+        if (this.iterator_) {
+          this.iterator_.close();
+          this.iterator_ = undefined;
+        }
+
+        return;
+      }
+
+      if (!this.iterator_) {
+        this.iterator_ = new TemplateIterator(this);
+      }
+
+      this.iterator_.updateDependencies(directives, this.model_);
+
+      if (templateObserver) {
+        templateObserver.observe(this, { attributes: true,
+                                         attributeFilter: ['ref'] });
+      }
+
+      return this.iterator_;
+    },
+
+    createInstance: function(model, bindingDelegate, delegate_) {
+      if (bindingDelegate)
+        delegate_ = this.newDelegate_(bindingDelegate);
+      else if (!delegate_)
+        delegate_ = this.delegate_;
+
+      if (!this.refContent_)
+        this.refContent_ = this.ref_.content;
+      var content = this.refContent_;
+      if (content.firstChild === null)
+        return emptyInstance;
+
+      var map = getInstanceBindingMap(content, delegate_);
+      var stagingDocument = getTemplateStagingDocument(this);
+      var instance = stagingDocument.createDocumentFragment();
+      instance.templateCreator_ = this;
+      instance.protoContent_ = content;
+      instance.bindings_ = [];
+      instance.terminator_ = null;
+      var instanceRecord = instance.templateInstance_ = {
+        firstNode: null,
+        lastNode: null,
+        model: model
+      };
+
+      var i = 0;
+      var collectTerminator = false;
+      for (var child = content.firstChild; child; child = child.nextSibling) {
+        // The terminator of the instance is the clone of the last child of the
+        // content. If the last child is an active template, it may produce
+        // instances as a result of production, so simply collecting the last
+        // child of the instance after it has finished producing may be wrong.
+        if (child.nextSibling === null)
+          collectTerminator = true;
+
+        var clone = cloneAndBindInstance(child, instance, stagingDocument,
+                                         map.children[i++],
+                                         model,
+                                         delegate_,
+                                         instance.bindings_);
+        clone.templateInstance_ = instanceRecord;
+        if (collectTerminator)
+          instance.terminator_ = clone;
+      }
+
+      instanceRecord.firstNode = instance.firstChild;
+      instanceRecord.lastNode = instance.lastChild;
+      instance.templateCreator_ = undefined;
+      instance.protoContent_ = undefined;
+      return instance;
+    },
+
+    get model() {
+      return this.model_;
+    },
+
+    set model(model) {
+      this.model_ = model;
+      ensureSetModelScheduled(this);
+    },
+
+    get bindingDelegate() {
+      return this.delegate_ && this.delegate_.raw;
+    },
+
+    refChanged_: function() {
+      if (!this.iterator_ || this.refContent_ === this.ref_.content)
+        return;
+
+      this.refContent_ = undefined;
+      this.iterator_.valueChanged();
+      this.iterator_.updateIteratedValue(this.iterator_.getUpdatedValue());
+    },
+
+    clear: function() {
+      this.model_ = undefined;
+      this.delegate_ = undefined;
+      if (this.bindings_ && this.bindings_.ref)
+        this.bindings_.ref.close()
+      this.refContent_ = undefined;
+      if (!this.iterator_)
+        return;
+      this.iterator_.valueChanged();
+      this.iterator_.close()
+      this.iterator_ = undefined;
+    },
+
+    setDelegate_: function(delegate) {
+      this.delegate_ = delegate;
+      this.bindingMap_ = undefined;
+      if (this.iterator_) {
+        this.iterator_.instancePositionChangedFn_ = undefined;
+        this.iterator_.instanceModelFn_ = undefined;
+      }
+    },
+
+    newDelegate_: function(bindingDelegate) {
+      if (!bindingDelegate)
+        return;
+
+      function delegateFn(name) {
+        var fn = bindingDelegate && bindingDelegate[name];
+        if (typeof fn != 'function')
+          return;
+
+        return function() {
+          return fn.apply(bindingDelegate, arguments);
+        };
+      }
+
+      return {
+        bindingMaps: {},
+        raw: bindingDelegate,
+        prepareBinding: delegateFn('prepareBinding'),
+        prepareInstanceModel: delegateFn('prepareInstanceModel'),
+        prepareInstancePositionChanged:
+            delegateFn('prepareInstancePositionChanged')
+      };
+    },
+
+    set bindingDelegate(bindingDelegate) {
+      if (this.delegate_) {
+        throw Error('Template must be cleared before a new bindingDelegate ' +
+                    'can be assigned');
+      }
+
+      this.setDelegate_(this.newDelegate_(bindingDelegate));
+    },
+
+    get ref_() {
+      var ref = searchRefId(this, this.getAttribute('ref'));
+      if (!ref)
+        ref = this.instanceRef_;
+
+      if (!ref)
+        return this;
+
+      var nextRef = ref.ref_;
+      return nextRef ? nextRef : ref;
+    }
+  });
+
+  // Returns
+  //   a) undefined if there are no mustaches.
+  //   b) [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+] if there is at least one mustache.
+  function parseMustaches(s, name, node, prepareBindingFn) {
+    if (!s || !s.length)
+      return;
+
+    var tokens;
+    var length = s.length;
+    var startIndex = 0, lastIndex = 0, endIndex = 0;
+    var onlyOneTime = true;
+    while (lastIndex < length) {
+      var startIndex = s.indexOf('{{', lastIndex);
+      var oneTimeStart = s.indexOf('[[', lastIndex);
+      var oneTime = false;
+      var terminator = '}}';
+
+      if (oneTimeStart >= 0 &&
+          (startIndex < 0 || oneTimeStart < startIndex)) {
+        startIndex = oneTimeStart;
+        oneTime = true;
+        terminator = ']]';
+      }
+
+      endIndex = startIndex < 0 ? -1 : s.indexOf(terminator, startIndex + 2);
+
+      if (endIndex < 0) {
+        if (!tokens)
+          return;
+
+        tokens.push(s.slice(lastIndex)); // TEXT
+        break;
+      }
+
+      tokens = tokens || [];
+      tokens.push(s.slice(lastIndex, startIndex)); // TEXT
+      var pathString = s.slice(startIndex + 2, endIndex).trim();
+      tokens.push(oneTime); // ONE_TIME?
+      onlyOneTime = onlyOneTime && oneTime;
+      var delegateFn = prepareBindingFn &&
+                       prepareBindingFn(pathString, name, node);
+      // Don't try to parse the expression if there's a prepareBinding function
+      if (delegateFn == null) {
+        tokens.push(Path.get(pathString)); // PATH
+      } else {
+        tokens.push(null);
+      }
+      tokens.push(delegateFn); // DELEGATE_FN
+      lastIndex = endIndex + 2;
+    }
+
+    if (lastIndex === length)
+      tokens.push(''); // TEXT
+
+    tokens.hasOnePath = tokens.length === 5;
+    tokens.isSimplePath = tokens.hasOnePath &&
+                          tokens[0] == '' &&
+                          tokens[4] == '';
+    tokens.onlyOneTime = onlyOneTime;
+
+    tokens.combinator = function(values) {
+      var newValue = tokens[0];
+
+      for (var i = 1; i < tokens.length; i += 4) {
+        var value = tokens.hasOnePath ? values : values[(i - 1) / 4];
+        if (value !== undefined)
+          newValue += value;
+        newValue += tokens[i + 3];
+      }
+
+      return newValue;
+    }
+
+    return tokens;
+  };
+
+  function processOneTimeBinding(name, tokens, node, model) {
+    if (tokens.hasOnePath) {
+      var delegateFn = tokens[3];
+      var value = delegateFn ? delegateFn(model, node, true) :
+                               tokens[2].getValueFrom(model);
+      return tokens.isSimplePath ? value : tokens.combinator(value);
+    }
+
+    var values = [];
+    for (var i = 1; i < tokens.length; i += 4) {
+      var delegateFn = tokens[i + 2];
+      values[(i - 1) / 4] = delegateFn ? delegateFn(model, node) :
+          tokens[i + 1].getValueFrom(model);
+    }
+
+    return tokens.combinator(values);
+  }
+
+  function processSinglePathBinding(name, tokens, node, model) {
+    var delegateFn = tokens[3];
+    var observer = delegateFn ? delegateFn(model, node, false) :
+        new PathObserver(model, tokens[2]);
+
+    return tokens.isSimplePath ? observer :
+        new ObserverTransform(observer, tokens.combinator);
+  }
+
+  function processBinding(name, tokens, node, model) {
+    if (tokens.onlyOneTime)
+      return processOneTimeBinding(name, tokens, node, model);
+
+    if (tokens.hasOnePath)
+      return processSinglePathBinding(name, tokens, node, model);
+
+    var observer = new CompoundObserver();
+
+    for (var i = 1; i < tokens.length; i += 4) {
+      var oneTime = tokens[i];
+      var delegateFn = tokens[i + 2];
+
+      if (delegateFn) {
+        var value = delegateFn(model, node, oneTime);
+        if (oneTime)
+          observer.addPath(value)
+        else
+          observer.addObserver(value);
+        continue;
+      }
+
+      var path = tokens[i + 1];
+      if (oneTime)
+        observer.addPath(path.getValueFrom(model))
+      else
+        observer.addPath(model, path);
+    }
+
+    return new ObserverTransform(observer, tokens.combinator);
+  }
+
+  function processBindings(node, bindings, model, instanceBindings) {
+    for (var i = 0; i < bindings.length; i += 2) {
+      var name = bindings[i]
+      var tokens = bindings[i + 1];
+      var value = processBinding(name, tokens, node, model);
+      var binding = node.bind(name, value, tokens.onlyOneTime);
+      if (binding && instanceBindings)
+        instanceBindings.push(binding);
+    }
+
+    node.bindFinished();
+    if (!bindings.isTemplate)
+      return;
+
+    node.model_ = model;
+    var iter = node.processBindingDirectives_(bindings);
+    if (instanceBindings && iter)
+      instanceBindings.push(iter);
+  }
+
+  function parseWithDefault(el, name, prepareBindingFn) {
+    var v = el.getAttribute(name);
+    return parseMustaches(v == '' ? '{{}}' : v, name, el, prepareBindingFn);
+  }
+
+  function parseAttributeBindings(element, prepareBindingFn) {
+    assert(element);
+
+    var bindings = [];
+    var ifFound = false;
+    var bindFound = false;
+
+    for (var i = 0; i < element.attributes.length; i++) {
+      var attr = element.attributes[i];
+      var name = attr.name;
+      var value = attr.value;
+
+      // Allow bindings expressed in attributes to be prefixed with underbars.
+      // We do this to allow correct semantics for browsers that don't implement
+      // <template> where certain attributes might trigger side-effects -- and
+      // for IE which sanitizes certain attributes, disallowing mustache
+      // replacements in their text.
+      while (name[0] === '_') {
+        name = name.substring(1);
+      }
+
+      if (isTemplate(element) &&
+          (name === IF || name === BIND || name === REPEAT)) {
+        continue;
+      }
+
+      var tokens = parseMustaches(value, name, element,
+                                  prepareBindingFn);
+      if (!tokens)
+        continue;
+
+      bindings.push(name, tokens);
+    }
+
+    if (isTemplate(element)) {
+      bindings.isTemplate = true;
+      bindings.if = parseWithDefault(element, IF, prepareBindingFn);
+      bindings.bind = parseWithDefault(element, BIND, prepareBindingFn);
+      bindings.repeat = parseWithDefault(element, REPEAT, prepareBindingFn);
+
+      if (bindings.if && !bindings.bind && !bindings.repeat)
+        bindings.bind = parseMustaches('{{}}', BIND, element, prepareBindingFn);
+    }
+
+    return bindings;
+  }
+
+  function getBindings(node, prepareBindingFn) {
+    if (node.nodeType === Node.ELEMENT_NODE)
+      return parseAttributeBindings(node, prepareBindingFn);
+
+    if (node.nodeType === Node.TEXT_NODE) {
+      var tokens = parseMustaches(node.data, 'textContent', node,
+                                  prepareBindingFn);
+      if (tokens)
+        return ['textContent', tokens];
+    }
+
+    return [];
+  }
+
+  function cloneAndBindInstance(node, parent, stagingDocument, bindings, model,
+                                delegate,
+                                instanceBindings,
+                                instanceRecord) {
+    var clone = parent.appendChild(stagingDocument.importNode(node, false));
+
+    var i = 0;
+    for (var child = node.firstChild; child; child = child.nextSibling) {
+      cloneAndBindInstance(child, clone, stagingDocument,
+                            bindings.children[i++],
+                            model,
+                            delegate,
+                            instanceBindings);
+    }
+
+    if (bindings.isTemplate) {
+      HTMLTemplateElement.decorate(clone, node);
+      if (delegate)
+        clone.setDelegate_(delegate);
+    }
+
+    processBindings(clone, bindings, model, instanceBindings);
+    return clone;
+  }
+
+  function createInstanceBindingMap(node, prepareBindingFn) {
+    var map = getBindings(node, prepareBindingFn);
+    map.children = {};
+    var index = 0;
+    for (var child = node.firstChild; child; child = child.nextSibling) {
+      map.children[index++] = createInstanceBindingMap(child, prepareBindingFn);
+    }
+
+    return map;
+  }
+
+  var contentUidCounter = 1;
+
+  // TODO(rafaelw): Setup a MutationObserver on content which clears the id
+  // so that bindingMaps regenerate when the template.content changes.
+  function getContentUid(content) {
+    var id = content.id_;
+    if (!id)
+      id = content.id_ = contentUidCounter++;
+    return id;
+  }
+
+  // Each delegate is associated with a set of bindingMaps, one for each
+  // content which may be used by a template. The intent is that each binding
+  // delegate gets the opportunity to prepare the instance (via the prepare*
+  // delegate calls) once across all uses.
+  // TODO(rafaelw): Separate out the parse map from the binding map. In the
+  // current implementation, if two delegates need a binding map for the same
+  // content, the second will have to reparse.
+  function getInstanceBindingMap(content, delegate_) {
+    var contentId = getContentUid(content);
+    if (delegate_) {
+      var map = delegate_.bindingMaps[contentId];
+      if (!map) {
+        map = delegate_.bindingMaps[contentId] =
+            createInstanceBindingMap(content, delegate_.prepareBinding) || [];
+      }
+      return map;
+    }
+
+    var map = content.bindingMap_;
+    if (!map) {
+      map = content.bindingMap_ =
+          createInstanceBindingMap(content, undefined) || [];
+    }
+    return map;
+  }
+
+  Object.defineProperty(Node.prototype, 'templateInstance', {
+    get: function() {
+      var instance = this.templateInstance_;
+      return instance ? instance :
+          (this.parentNode ? this.parentNode.templateInstance : undefined);
+    }
+  });
+
+  var emptyInstance = document.createDocumentFragment();
+  emptyInstance.bindings_ = [];
+  emptyInstance.terminator_ = null;
+
+  function TemplateIterator(templateElement) {
+    this.closed = false;
+    this.templateElement_ = templateElement;
+    this.instances = [];
+    this.deps = undefined;
+    this.iteratedValue = [];
+    this.presentValue = undefined;
+    this.arrayObserver = undefined;
+  }
+
+  TemplateIterator.prototype = {
+    closeDeps: function() {
+      var deps = this.deps;
+      if (deps) {
+        if (deps.ifOneTime === false)
+          deps.ifValue.close();
+        if (deps.oneTime === false)
+          deps.value.close();
+      }
+    },
+
+    updateDependencies: function(directives, model) {
+      this.closeDeps();
+
+      var deps = this.deps = {};
+      var template = this.templateElement_;
+
+      var ifValue = true;
+      if (directives.if) {
+        deps.hasIf = true;
+        deps.ifOneTime = directives.if.onlyOneTime;
+        deps.ifValue = processBinding(IF, directives.if, template, model);
+
+        ifValue = deps.ifValue;
+
+        // oneTime if & predicate is false. nothing else to do.
+        if (deps.ifOneTime && !ifValue) {
+          this.valueChanged();
+          return;
+        }
+
+        if (!deps.ifOneTime)
+          ifValue = ifValue.open(this.updateIfValue, this);
+      }
+
+      if (directives.repeat) {
+        deps.repeat = true;
+        deps.oneTime = directives.repeat.onlyOneTime;
+        deps.value = processBinding(REPEAT, directives.repeat, template, model);
+      } else {
+        deps.repeat = false;
+        deps.oneTime = directives.bind.onlyOneTime;
+        deps.value = processBinding(BIND, directives.bind, template, model);
+      }
+
+      var value = deps.value;
+      if (!deps.oneTime)
+        value = value.open(this.updateIteratedValue, this);
+
+      if (!ifValue) {
+        this.valueChanged();
+        return;
+      }
+
+      this.updateValue(value);
+    },
+
+    /**
+     * Gets the updated value of the bind/repeat. This can potentially call
+     * user code (if a bindingDelegate is set up) so we try to avoid it if we
+     * already have the value in hand (from Observer.open).
+     */
+    getUpdatedValue: function() {
+      var value = this.deps.value;
+      if (!this.deps.oneTime)
+        value = value.discardChanges();
+      return value;
+    },
+
+    updateIfValue: function(ifValue) {
+      if (!ifValue) {
+        this.valueChanged();
+        return;
+      }
+
+      this.updateValue(this.getUpdatedValue());
+    },
+
+    updateIteratedValue: function(value) {
+      if (this.deps.hasIf) {
+        var ifValue = this.deps.ifValue;
+        if (!this.deps.ifOneTime)
+          ifValue = ifValue.discardChanges();
+        if (!ifValue) {
+          this.valueChanged();
+          return;
+        }
+      }
+
+      this.updateValue(value);
+    },
+
+    updateValue: function(value) {
+      if (!this.deps.repeat)
+        value = [value];
+      var observe = this.deps.repeat &&
+                    !this.deps.oneTime &&
+                    Array.isArray(value);
+      this.valueChanged(value, observe);
+    },
+
+    valueChanged: function(value, observeValue) {
+      if (!Array.isArray(value))
+        value = [];
+
+      if (value === this.iteratedValue)
+        return;
+
+      this.unobserve();
+      this.presentValue = value;
+      if (observeValue) {
+        this.arrayObserver = new ArrayObserver(this.presentValue);
+        this.arrayObserver.open(this.handleSplices, this);
+      }
+
+      this.handleSplices(ArrayObserver.calculateSplices(this.presentValue,
+                                                        this.iteratedValue));
+    },
+
+    getLastInstanceNode: function(index) {
+      if (index == -1)
+        return this.templateElement_;
+      var instance = this.instances[index];
+      var terminator = instance.terminator_;
+      if (!terminator)
+        return this.getLastInstanceNode(index - 1);
+
+      if (terminator.nodeType !== Node.ELEMENT_NODE ||
+          this.templateElement_ === terminator) {
+        return terminator;
+      }
+
+      var subtemplateIterator = terminator.iterator_;
+      if (!subtemplateIterator)
+        return terminator;
+
+      return subtemplateIterator.getLastTemplateNode();
+    },
+
+    getLastTemplateNode: function() {
+      return this.getLastInstanceNode(this.instances.length - 1);
+    },
+
+    insertInstanceAt: function(index, fragment) {
+      var previousInstanceLast = this.getLastInstanceNode(index - 1);
+      var parent = this.templateElement_.parentNode;
+      this.instances.splice(index, 0, fragment);
+
+      parent.insertBefore(fragment, previousInstanceLast.nextSibling);
+    },
+
+    extractInstanceAt: function(index) {
+      var previousInstanceLast = this.getLastInstanceNode(index - 1);
+      var lastNode = this.getLastInstanceNode(index);
+      var parent = this.templateElement_.parentNode;
+      var instance = this.instances.splice(index, 1)[0];
+
+      while (lastNode !== previousInstanceLast) {
+        var node = previousInstanceLast.nextSibling;
+        if (node == lastNode)
+          lastNode = previousInstanceLast;
+
+        instance.appendChild(parent.removeChild(node));
+      }
+
+      return instance;
+    },
+
+    getDelegateFn: function(fn) {
+      fn = fn && fn(this.templateElement_);
+      return typeof fn === 'function' ? fn : null;
+    },
+
+    handleSplices: function(splices) {
+      if (this.closed || !splices.length)
+        return;
+
+      var template = this.templateElement_;
+
+      if (!template.parentNode) {
+        this.close();
+        return;
+      }
+
+      ArrayObserver.applySplices(this.iteratedValue, this.presentValue,
+                                 splices);
+
+      var delegate = template.delegate_;
+      if (this.instanceModelFn_ === undefined) {
+        this.instanceModelFn_ =
+            this.getDelegateFn(delegate && delegate.prepareInstanceModel);
+      }
+
+      if (this.instancePositionChangedFn_ === undefined) {
+        this.instancePositionChangedFn_ =
+            this.getDelegateFn(delegate &&
+                               delegate.prepareInstancePositionChanged);
+      }
+
+      // Instance Removals
+      var instanceCache = new Map;
+      var removeDelta = 0;
+      for (var i = 0; i < splices.length; i++) {
+        var splice = splices[i];
+        var removed = splice.removed;
+        for (var j = 0; j < removed.length; j++) {
+          var model = removed[j];
+          var instance = this.extractInstanceAt(splice.index + removeDelta);
+          if (instance !== emptyInstance) {
+            instanceCache.set(model, instance);
+          }
+        }
+
+        removeDelta -= splice.addedCount;
+      }
+
+      // Instance Insertions
+      for (var i = 0; i < splices.length; i++) {
+        var splice = splices[i];
+        var addIndex = splice.index;
+        for (; addIndex < splice.index + splice.addedCount; addIndex++) {
+          var model = this.iteratedValue[addIndex];
+          var instance = instanceCache.get(model);
+          if (instance) {
+            instanceCache.delete(model);
+          } else {
+            if (this.instanceModelFn_) {
+              model = this.instanceModelFn_(model);
+            }
+
+            if (model === undefined) {
+              instance = emptyInstance;
+            } else {
+              instance = template.createInstance(model, undefined, delegate);
+            }
+          }
+
+          this.insertInstanceAt(addIndex, instance);
+        }
+      }
+
+      instanceCache.forEach(function(instance) {
+        this.closeInstanceBindings(instance);
+      }, this);
+
+      if (this.instancePositionChangedFn_)
+        this.reportInstancesMoved(splices);
+    },
+
+    reportInstanceMoved: function(index) {
+      var instance = this.instances[index];
+      if (instance === emptyInstance)
+        return;
+
+      this.instancePositionChangedFn_(instance.templateInstance_, index);
+    },
+
+    reportInstancesMoved: function(splices) {
+      var index = 0;
+      var offset = 0;
+      for (var i = 0; i < splices.length; i++) {
+        var splice = splices[i];
+        if (offset != 0) {
+          while (index < splice.index) {
+            this.reportInstanceMoved(index);
+            index++;
+          }
+        } else {
+          index = splice.index;
+        }
+
+        while (index < splice.index + splice.addedCount) {
+          this.reportInstanceMoved(index);
+          index++;
+        }
+
+        offset += splice.addedCount - splice.removed.length;
+      }
+
+      if (offset == 0)
+        return;
+
+      var length = this.instances.length;
+      while (index < length) {
+        this.reportInstanceMoved(index);
+        index++;
+      }
+    },
+
+    closeInstanceBindings: function(instance) {
+      var bindings = instance.bindings_;
+      for (var i = 0; i < bindings.length; i++) {
+        bindings[i].close();
+      }
+    },
+
+    unobserve: function() {
+      if (!this.arrayObserver)
+        return;
+
+      this.arrayObserver.close();
+      this.arrayObserver = undefined;
+    },
+
+    close: function() {
+      if (this.closed)
+        return;
+      this.unobserve();
+      for (var i = 0; i < this.instances.length; i++) {
+        this.closeInstanceBindings(this.instances[i]);
+      }
+
+      this.instances.length = 0;
+      this.closeDeps();
+      this.templateElement_.iterator_ = undefined;
+      this.closed = true;
+    }
+  };
+
+  // Polyfill-specific API.
+  HTMLTemplateElement.forAllTemplatesFrom_ = forAllTemplatesFrom;
+})(this);
+
+(function(scope) {
+  'use strict';
+
+  // feature detect for URL constructor
+  var hasWorkingUrl = false;
+  if (!scope.forceJURL) {
+    try {
+      var u = new URL('b', 'http://a');
+      u.pathname = 'c%20d';
+      hasWorkingUrl = u.href === 'http://a/c%20d';
+    } catch(e) {}
+  }
+
+  if (hasWorkingUrl)
+    return;
+
+  var relative = Object.create(null);
+  relative['ftp'] = 21;
+  relative['file'] = 0;
+  relative['gopher'] = 70;
+  relative['http'] = 80;
+  relative['https'] = 443;
+  relative['ws'] = 80;
+  relative['wss'] = 443;
+
+  var relativePathDotMapping = Object.create(null);
+  relativePathDotMapping['%2e'] = '.';
+  relativePathDotMapping['.%2e'] = '..';
+  relativePathDotMapping['%2e.'] = '..';
+  relativePathDotMapping['%2e%2e'] = '..';
+
+  function isRelativeScheme(scheme) {
+    return relative[scheme] !== undefined;
+  }
+
+  function invalid() {
+    clear.call(this);
+    this._isInvalid = true;
+  }
+
+  function IDNAToASCII(h) {
+    if ('' == h) {
+      invalid.call(this)
+    }
+    // XXX
+    return h.toLowerCase()
+  }
+
+  function percentEscape(c) {
+    var unicode = c.charCodeAt(0);
+    if (unicode > 0x20 &&
+       unicode < 0x7F &&
+       // " # < > ? `
+       [0x22, 0x23, 0x3C, 0x3E, 0x3F, 0x60].indexOf(unicode) == -1
+      ) {
+      return c;
+    }
+    return encodeURIComponent(c);
+  }
+
+  function percentEscapeQuery(c) {
+    // XXX This actually needs to encode c using encoding and then
+    // convert the bytes one-by-one.
+
+    var unicode = c.charCodeAt(0);
+    if (unicode > 0x20 &&
+       unicode < 0x7F &&
+       // " # < > ` (do not escape '?')
+       [0x22, 0x23, 0x3C, 0x3E, 0x60].indexOf(unicode) == -1
+      ) {
+      return c;
+    }
+    return encodeURIComponent(c);
+  }
+
+  var EOF = undefined,
+      ALPHA = /[a-zA-Z]/,
+      ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
+
+  function parse(input, stateOverride, base) {
+    function err(message) {
+      errors.push(message)
+    }
+
+    var state = stateOverride || 'scheme start',
+        cursor = 0,
+        buffer = '',
+        seenAt = false,
+        seenBracket = false,
+        errors = [];
+
+    loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) {
+      var c = input[cursor];
+      switch (state) {
+        case 'scheme start':
+          if (c && ALPHA.test(c)) {
+            buffer += c.toLowerCase(); // ASCII-safe
+            state = 'scheme';
+          } else if (!stateOverride) {
+            buffer = '';
+            state = 'no scheme';
+            continue;
+          } else {
+            err('Invalid scheme.');
+            break loop;
+          }
+          break;
+
+        case 'scheme':
+          if (c && ALPHANUMERIC.test(c)) {
+            buffer += c.toLowerCase(); // ASCII-safe
+          } else if (':' == c) {
+            this._scheme = buffer;
+            buffer = '';
+            if (stateOverride) {
+              break loop;
+            }
+            if (isRelativeScheme(this._scheme)) {
+              this._isRelative = true;
+            }
+            if ('file' == this._scheme) {
+              state = 'relative';
+            } else if (this._isRelative && base && base._scheme == this._scheme) {
+              state = 'relative or authority';
+            } else if (this._isRelative) {
+              state = 'authority first slash';
+            } else {
+              state = 'scheme data';
+            }
+          } else if (!stateOverride) {
+            buffer = '';
+            cursor = 0;
+            state = 'no scheme';
+            continue;
+          } else if (EOF == c) {
+            break loop;
+          } else {
+            err('Code point not allowed in scheme: ' + c)
+            break loop;
+          }
+          break;
+
+        case 'scheme data':
+          if ('?' == c) {
+            query = '?';
+            state = 'query';
+          } else if ('#' == c) {
+            this._fragment = '#';
+            state = 'fragment';
+          } else {
+            // XXX error handling
+            if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
+              this._schemeData += percentEscape(c);
+            }
+          }
+          break;
+
+        case 'no scheme':
+          if (!base || !(isRelativeScheme(base._scheme))) {
+            err('Missing scheme.');
+            invalid.call(this);
+          } else {
+            state = 'relative';
+            continue;
+          }
+          break;
+
+        case 'relative or authority':
+          if ('/' == c && '/' == input[cursor+1]) {
+            state = 'authority ignore slashes';
+          } else {
+            err('Expected /, got: ' + c);
+            state = 'relative';
+            continue
+          }
+          break;
+
+        case 'relative':
+          this._isRelative = true;
+          if ('file' != this._scheme)
+            this._scheme = base._scheme;
+          if (EOF == c) {
+            this._host = base._host;
+            this._port = base._port;
+            this._path = base._path.slice();
+            this._query = base._query;
+            break loop;
+          } else if ('/' == c || '\\' == c) {
+            if ('\\' == c)
+              err('\\ is an invalid code point.');
+            state = 'relative slash';
+          } else if ('?' == c) {
+            this._host = base._host;
+            this._port = base._port;
+            this._path = base._path.slice();
+            this._query = '?';
+            state = 'query';
+          } else if ('#' == c) {
+            this._host = base._host;
+            this._port = base._port;
+            this._path = base._path.slice();
+            this._query = base._query;
+            this._fragment = '#';
+            state = 'fragment';
+          } else {
+            var nextC = input[cursor+1]
+            var nextNextC = input[cursor+2]
+            if (
+              'file' != this._scheme || !ALPHA.test(c) ||
+              (nextC != ':' && nextC != '|') ||
+              (EOF != nextNextC && '/' != nextNextC && '\\' != nextNextC && '?' != nextNextC && '#' != nextNextC)) {
+              this._host = base._host;
+              this._port = base._port;
+              this._path = base._path.slice();
+              this._path.pop();
+            }
+            state = 'relative path';
+            continue;
+          }
+          break;
+
+        case 'relative slash':
+          if ('/' == c || '\\' == c) {
+            if ('\\' == c) {
+              err('\\ is an invalid code point.');
+            }
+            if ('file' == this._scheme) {
+              state = 'file host';
+            } else {
+              state = 'authority ignore slashes';
+            }
+          } else {
+            if ('file' != this._scheme) {
+              this._host = base._host;
+              this._port = base._port;
+            }
+            state = 'relative path';
+            continue;
+          }
+          break;
+
+        case 'authority first slash':
+          if ('/' == c) {
+            state = 'authority second slash';
+          } else {
+            err("Expected '/', got: " + c);
+            state = 'authority ignore slashes';
+            continue;
+          }
+          break;
+
+        case 'authority second slash':
+          state = 'authority ignore slashes';
+          if ('/' != c) {
+            err("Expected '/', got: " + c);
+            continue;
+          }
+          break;
+
+        case 'authority ignore slashes':
+          if ('/' != c && '\\' != c) {
+            state = 'authority';
+            continue;
+          } else {
+            err('Expected authority, got: ' + c);
+          }
+          break;
+
+        case 'authority':
+          if ('@' == c) {
+            if (seenAt) {
+              err('@ already seen.');
+              buffer += '%40';
+            }
+            seenAt = true;
+            for (var i = 0; i < buffer.length; i++) {
+              var cp = buffer[i];
+              if ('\t' == cp || '\n' == cp || '\r' == cp) {
+                err('Invalid whitespace in authority.');
+                continue;
+              }
+              // XXX check URL code points
+              if (':' == cp && null === this._password) {
+                this._password = '';
+                continue;
+              }
+              var tempC = percentEscape(cp);
+              (null !== this._password) ? this._password += tempC : this._username += tempC;
+            }
+            buffer = '';
+          } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
+            cursor -= buffer.length;
+            buffer = '';
+            state = 'host';
+            continue;
+          } else {
+            buffer += c;
+          }
+          break;
+
+        case 'file host':
+          if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
+            if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ':' || buffer[1] == '|')) {
+              state = 'relative path';
+            } else if (buffer.length == 0) {
+              state = 'relative path start';
+            } else {
+              this._host = IDNAToASCII.call(this, buffer);
+              buffer = '';
+              state = 'relative path start';
+            }
+            continue;
+          } else if ('\t' == c || '\n' == c || '\r' == c) {
+            err('Invalid whitespace in file host.');
+          } else {
+            buffer += c;
+          }
+          break;
+
+        case 'host':
+        case 'hostname':
+          if (':' == c && !seenBracket) {
+            // XXX host parsing
+            this._host = IDNAToASCII.call(this, buffer);
+            buffer = '';
+            state = 'port';
+            if ('hostname' == stateOverride) {
+              break loop;
+            }
+          } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
+            this._host = IDNAToASCII.call(this, buffer);
+            buffer = '';
+            state = 'relative path start';
+            if (stateOverride) {
+              break loop;
+            }
+            continue;
+          } else if ('\t' != c && '\n' != c && '\r' != c) {
+            if ('[' == c) {
+              seenBracket = true;
+            } else if (']' == c) {
+              seenBracket = false;
+            }
+            buffer += c;
+          } else {
+            err('Invalid code point in host/hostname: ' + c);
+          }
+          break;
+
+        case 'port':
+          if (/[0-9]/.test(c)) {
+            buffer += c;
+          } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c || stateOverride) {
+            if ('' != buffer) {
+              var temp = parseInt(buffer, 10);
+              if (temp != relative[this._scheme]) {
+                this._port = temp + '';
+              }
+              buffer = '';
+            }
+            if (stateOverride) {
+              break loop;
+            }
+            state = 'relative path start';
+            continue;
+          } else if ('\t' == c || '\n' == c || '\r' == c) {
+            err('Invalid code point in port: ' + c);
+          } else {
+            invalid.call(this);
+          }
+          break;
+
+        case 'relative path start':
+          if ('\\' == c)
+            err("'\\' not allowed in path.");
+          state = 'relative path';
+          if ('/' != c && '\\' != c) {
+            continue;
+          }
+          break;
+
+        case 'relative path':
+          if (EOF == c || '/' == c || '\\' == c || (!stateOverride && ('?' == c || '#' == c))) {
+            if ('\\' == c) {
+              err('\\ not allowed in relative path.');
+            }
+            var tmp;
+            if (tmp = relativePathDotMapping[buffer.toLowerCase()]) {
+              buffer = tmp;
+            }
+            if ('..' == buffer) {
+              this._path.pop();
+              if ('/' != c && '\\' != c) {
+                this._path.push('');
+              }
+            } else if ('.' == buffer && '/' != c && '\\' != c) {
+              this._path.push('');
+            } else if ('.' != buffer) {
+              if ('file' == this._scheme && this._path.length == 0 && buffer.length == 2 && ALPHA.test(buffer[0]) && buffer[1] == '|') {
+                buffer = buffer[0] + ':';
+              }
+              this._path.push(buffer);
+            }
+            buffer = '';
+            if ('?' == c) {
+              this._query = '?';
+              state = 'query';
+            } else if ('#' == c) {
+              this._fragment = '#';
+              state = 'fragment';
+            }
+          } else if ('\t' != c && '\n' != c && '\r' != c) {
+            buffer += percentEscape(c);
+          }
+          break;
+
+        case 'query':
+          if (!stateOverride && '#' == c) {
+            this._fragment = '#';
+            state = 'fragment';
+          } else if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
+            this._query += percentEscapeQuery(c);
+          }
+          break;
+
+        case 'fragment':
+          if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
+            this._fragment += c;
+          }
+          break;
+      }
+
+      cursor++;
+    }
+  }
+
+  function clear() {
+    this._scheme = '';
+    this._schemeData = '';
+    this._username = '';
+    this._password = null;
+    this._host = '';
+    this._port = '';
+    this._path = [];
+    this._query = '';
+    this._fragment = '';
+    this._isInvalid = false;
+    this._isRelative = false;
+  }
+
+  // Does not process domain names or IP addresses.
+  // Does not handle encoding for the query parameter.
+  function jURL(url, base /* , encoding */) {
+    if (base !== undefined && !(base instanceof jURL))
+      base = new jURL(String(base));
+
+    this._url = url;
+    clear.call(this);
+
+    var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, '');
+    // encoding = encoding || 'utf-8'
+
+    parse.call(this, input, null, base);
+  }
+
+  jURL.prototype = {
+    get href() {
+      if (this._isInvalid)
+        return this._url;
+
+      var authority = '';
+      if ('' != this._username || null != this._password) {
+        authority = this._username +
+            (null != this._password ? ':' + this._password : '') + '@';
+      }
+
+      return this.protocol +
+          (this._isRelative ? '//' + authority + this.host : '') +
+          this.pathname + this._query + this._fragment;
+    },
+    set href(href) {
+      clear.call(this);
+      parse.call(this, href);
+    },
+
+    get protocol() {
+      return this._scheme + ':';
+    },
+    set protocol(protocol) {
+      if (this._isInvalid)
+        return;
+      parse.call(this, protocol + ':', 'scheme start');
+    },
+
+    get host() {
+      return this._isInvalid ? '' : this._port ?
+          this._host + ':' + this._port : this._host;
+    },
+    set host(host) {
+      if (this._isInvalid || !this._isRelative)
+        return;
+      parse.call(this, host, 'host');
+    },
+
+    get hostname() {
+      return this._host;
+    },
+    set hostname(hostname) {
+      if (this._isInvalid || !this._isRelative)
+        return;
+      parse.call(this, hostname, 'hostname');
+    },
+
+    get port() {
+      return this._port;
+    },
+    set port(port) {
+      if (this._isInvalid || !this._isRelative)
+        return;
+      parse.call(this, port, 'port');
+    },
+
+    get pathname() {
+      return this._isInvalid ? '' : this._isRelative ?
+          '/' + this._path.join('/') : this._schemeData;
+    },
+    set pathname(pathname) {
+      if (this._isInvalid || !this._isRelative)
+        return;
+      this._path = [];
+      parse.call(this, pathname, 'relative path start');
+    },
+
+    get search() {
+      return this._isInvalid || !this._query || '?' == this._query ?
+          '' : this._query;
+    },
+    set search(search) {
+      if (this._isInvalid || !this._isRelative)
+        return;
+      this._query = '?';
+      if ('?' == search[0])
+        search = search.slice(1);
+      parse.call(this, search, 'query');
+    },
+
+    get hash() {
+      return this._isInvalid || !this._fragment || '#' == this._fragment ?
+          '' : this._fragment;
+    },
+    set hash(hash) {
+      if (this._isInvalid)
+        return;
+      this._fragment = '#';
+      if ('#' == hash[0])
+        hash = hash.slice(1);
+      parse.call(this, hash, 'fragment');
+    },
+
+    get origin() {
+      var host;
+      if (this._isInvalid || !this._scheme) {
+        return '';
+      }
+      // javascript: Gecko returns String(""), WebKit/Blink String("null")
+      // Gecko throws error for "data://"
+      // data: Gecko returns "", Blink returns "data://", WebKit returns "null"
+      // Gecko returns String("") for file: mailto:
+      // WebKit/Blink returns String("SCHEME://") for file: mailto:
+      switch (this._scheme) {
+        case 'data':
+        case 'file':
+        case 'javascript':
+        case 'mailto':
+          return 'null';
+      }
+      host = this.host;
+      if (!host) {
+        return '';
+      }
+      return this._scheme + '://' + host;
+    }
+  };
+
+  // Copy over the static methods
+  var OriginalURL = scope.URL;
+  if (OriginalURL) {
+    jURL.createObjectURL = function(blob) {
+      // IE extension allows a second optional options argument.
+      // http://msdn.microsoft.com/en-us/library/ie/hh772302(v=vs.85).aspx
+      return OriginalURL.createObjectURL.apply(OriginalURL, arguments);
+    };
+    jURL.revokeObjectURL = function(url) {
+      OriginalURL.revokeObjectURL(url);
+    };
+  }
+
+  scope.URL = jURL;
+
+})(this);
+
+(function(scope) {
+
+var iterations = 0;
+var callbacks = [];
+var twiddle = document.createTextNode('');
+
+function endOfMicrotask(callback) {
+  twiddle.textContent = iterations++;
+  callbacks.push(callback);
+}
+
+function atEndOfMicrotask() {
+  while (callbacks.length) {
+    callbacks.shift()();
+  }
+}
+
+new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask)
+  .observe(twiddle, {characterData: true})
+  ;
+
+// exports
+scope.endOfMicrotask = endOfMicrotask;
+// bc 
+Platform.endOfMicrotask = endOfMicrotask;
+
+})(Polymer);
+
+
+(function(scope) {
+
+/**
+ * @class Polymer
+ */
+
+// imports
+var endOfMicrotask = scope.endOfMicrotask;
+
+// logging
+var log = window.WebComponents ? WebComponents.flags.log : {};
+
+// inject style sheet
+var style = document.createElement('style');
+style.textContent = 'template {display: none !important;} /* injected by platform.js */';
+var head = document.querySelector('head');
+head.insertBefore(style, head.firstChild);
+
+
+/**
+ * Force any pending data changes to be observed before 
+ * the next task. Data changes are processed asynchronously but are guaranteed
+ * to be processed, for example, before painting. This method should rarely be 
+ * needed. It does nothing when Object.observe is available; 
+ * when Object.observe is not available, Polymer automatically flushes data 
+ * changes approximately every 1/10 second. 
+ * Therefore, `flush` should only be used when a data mutation should be 
+ * observed sooner than this.
+ * 
+ * @method flush
+ */
+// flush (with logging)
+var flushing;
+function flush() {
+  if (!flushing) {
+    flushing = true;
+    endOfMicrotask(function() {
+      flushing = false;
+      log.data && console.group('flush');
+      Platform.performMicrotaskCheckpoint();
+      log.data && console.groupEnd();
+    });
+  }
+};
+
+// polling dirty checker
+// flush periodically if platform does not have object observe.
+if (!Observer.hasObjectObserve) {
+  var FLUSH_POLL_INTERVAL = 125;
+  window.addEventListener('WebComponentsReady', function() {
+    flush();
+    // watch document visiblity to toggle dirty-checking
+    var visibilityHandler = function() {
+      // only flush if the page is visibile
+      if (document.visibilityState === 'hidden') {
+        if (scope.flushPoll) {
+          clearInterval(scope.flushPoll);
+        }
+      } else {
+        scope.flushPoll = setInterval(flush, FLUSH_POLL_INTERVAL);
+      }
+    };
+    if (typeof document.visibilityState === 'string') {
+      document.addEventListener('visibilitychange', visibilityHandler);
+    }
+    visibilityHandler();
+  });
+} else {
+  // make flush a no-op when we have Object.observe
+  flush = function() {};
+}
+
+if (window.CustomElements && !CustomElements.useNative) {
+  var originalImportNode = Document.prototype.importNode;
+  Document.prototype.importNode = function(node, deep) {
+    var imported = originalImportNode.call(this, node, deep);
+    CustomElements.upgradeAll(imported);
+    return imported;
+  };
+}
+
+// exports
+scope.flush = flush;
+// bc
+Platform.flush = flush;
+
+})(window.Polymer);
+
+
+(function(scope) {
+
+var urlResolver = {
+  resolveDom: function(root, url) {
+    url = url || baseUrl(root);
+    this.resolveAttributes(root, url);
+    this.resolveStyles(root, url);
+    // handle template.content
+    var templates = root.querySelectorAll('template');
+    if (templates) {
+      for (var i = 0, l = templates.length, t; (i < l) && (t = templates[i]); i++) {
+        if (t.content) {
+          this.resolveDom(t.content, url);
+        }
+      }
+    }
+  },
+  resolveTemplate: function(template) {
+    this.resolveDom(template.content, baseUrl(template));
+  },
+  resolveStyles: function(root, url) {
+    var styles = root.querySelectorAll('style');
+    if (styles) {
+      for (var i = 0, l = styles.length, s; (i < l) && (s = styles[i]); i++) {
+        this.resolveStyle(s, url);
+      }
+    }
+  },
+  resolveStyle: function(style, url) {
+    url = url || baseUrl(style);
+    style.textContent = this.resolveCssText(style.textContent, url);
+  },
+  resolveCssText: function(cssText, baseUrl, keepAbsolute) {
+    cssText = replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_URL_REGEXP);
+    return replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_IMPORT_REGEXP);
+  },
+  resolveAttributes: function(root, url) {
+    if (root.hasAttributes && root.hasAttributes()) {
+      this.resolveElementAttributes(root, url);
+    }
+    // search for attributes that host urls
+    var nodes = root && root.querySelectorAll(URL_ATTRS_SELECTOR);
+    if (nodes) {
+      for (var i = 0, l = nodes.length, n; (i < l) && (n = nodes[i]); i++) {
+        this.resolveElementAttributes(n, url);
+      }
+    }
+  },
+  resolveElementAttributes: function(node, url) {
+    url = url || baseUrl(node);
+    URL_ATTRS.forEach(function(v) {
+      var attr = node.attributes[v];
+      var value = attr && attr.value;
+      var replacement;
+      if (value && value.search(URL_TEMPLATE_SEARCH) < 0) {
+        if (v === 'style') {
+          replacement = replaceUrlsInCssText(value, url, false, CSS_URL_REGEXP);
+        } else {
+          replacement = resolveRelativeUrl(url, value);
+        }
+        attr.value = replacement;
+      }
+    });
+  }
+};
+
+var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g;
+var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
+var URL_ATTRS = ['href', 'src', 'action', 'style', 'url'];
+var URL_ATTRS_SELECTOR = '[' + URL_ATTRS.join('],[') + ']';
+var URL_TEMPLATE_SEARCH = '{{.*}}';
+var URL_HASH = '#';
+
+function baseUrl(node) {
+  var u = new URL(node.ownerDocument.baseURI);
+  u.search = '';
+  u.hash = '';
+  return u;
+}
+
+function replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, regexp) {
+  return cssText.replace(regexp, function(m, pre, url, post) {
+    var urlPath = url.replace(/["']/g, '');
+    urlPath = resolveRelativeUrl(baseUrl, urlPath, keepAbsolute);
+    return pre + '\'' + urlPath + '\'' + post;
+  });
+}
+
+function resolveRelativeUrl(baseUrl, url, keepAbsolute) {
+  // do not resolve '/' absolute urls
+  if (url && url[0] === '/') {
+    return url;
+  }
+  // do not resolve '#' links, they are used for routing
+  if (url && url[0] === '#') {
+    return url;
+  }
+  var u = new URL(url, baseUrl);
+  return keepAbsolute ? u.href : makeDocumentRelPath(u.href);
+}
+
+function makeDocumentRelPath(url) {
+  var root = baseUrl(document.documentElement);
+  var u = new URL(url, root);
+  if (u.host === root.host && u.port === root.port &&
+      u.protocol === root.protocol) {
+    return makeRelPath(root, u);
+  } else {
+    return url;
+  }
+}
+
+// make a relative path from source to target
+function makeRelPath(sourceUrl, targetUrl) {
+  var source = sourceUrl.pathname;
+  var target = targetUrl.pathname;
+  var s = source.split('/');
+  var t = target.split('/');
+  while (s.length && s[0] === t[0]){
+    s.shift();
+    t.shift();
+  }
+  for (var i = 0, l = s.length - 1; i < l; i++) {
+    t.unshift('..');
+  }
+  // empty '#' is discarded but we need to preserve it.
+  var hash = (targetUrl.href.slice(-1) === URL_HASH) ? URL_HASH : targetUrl.hash;
+  return t.join('/') + targetUrl.search + hash;
+}
+
+// exports
+scope.urlResolver = urlResolver;
+
+})(Polymer);
+
+(function(scope) {
+  var endOfMicrotask = Polymer.endOfMicrotask;
+
+  // Generic url loader
+  function Loader(regex) {
+    this.cache = Object.create(null);
+    this.map = Object.create(null);
+    this.requests = 0;
+    this.regex = regex;
+  }
+  Loader.prototype = {
+
+    // TODO(dfreedm): there may be a better factoring here
+    // extract absolute urls from the text (full of relative urls)
+    extractUrls: function(text, base) {
+      var matches = [];
+      var matched, u;
+      while ((matched = this.regex.exec(text))) {
+        u = new URL(matched[1], base);
+        matches.push({matched: matched[0], url: u.href});
+      }
+      return matches;
+    },
+    // take a text blob, a root url, and a callback and load all the urls found within the text
+    // returns a map of absolute url to text
+    process: function(text, root, callback) {
+      var matches = this.extractUrls(text, root);
+
+      // every call to process returns all the text this loader has ever received
+      var done = callback.bind(null, this.map);
+      this.fetch(matches, done);
+    },
+    // build a mapping of url -> text from matches
+    fetch: function(matches, callback) {
+      var inflight = matches.length;
+
+      // return early if there is no fetching to be done
+      if (!inflight) {
+        return callback();
+      }
+
+      // wait for all subrequests to return
+      var done = function() {
+        if (--inflight === 0) {
+          callback();
+        }
+      };
+
+      // start fetching all subrequests
+      var m, req, url;
+      for (var i = 0; i < inflight; i++) {
+        m = matches[i];
+        url = m.url;
+        req = this.cache[url];
+        // if this url has already been requested, skip requesting it again
+        if (!req) {
+          req = this.xhr(url);
+          req.match = m;
+          this.cache[url] = req;
+        }
+        // wait for the request to process its subrequests
+        req.wait(done);
+      }
+    },
+    handleXhr: function(request) {
+      var match = request.match;
+      var url = match.url;
+
+      // handle errors with an empty string
+      var response = request.response || request.responseText || '';
+      this.map[url] = response;
+      this.fetch(this.extractUrls(response, url), request.resolve);
+    },
+    xhr: function(url) {
+      this.requests++;
+      var request = new XMLHttpRequest();
+      request.open('GET', url, true);
+      request.send();
+      request.onerror = request.onload = this.handleXhr.bind(this, request);
+
+      // queue of tasks to run after XHR returns
+      request.pending = [];
+      request.resolve = function() {
+        var pending = request.pending;
+        for(var i = 0; i < pending.length; i++) {
+          pending[i]();
+        }
+        request.pending = null;
+      };
+
+      // if we have already resolved, pending is null, async call the callback
+      request.wait = function(fn) {
+        if (request.pending) {
+          request.pending.push(fn);
+        } else {
+          endOfMicrotask(fn);
+        }
+      };
+
+      return request;
+    }
+  };
+
+  scope.Loader = Loader;
+})(Polymer);
+
+(function(scope) {
+
+var urlResolver = scope.urlResolver;
+var Loader = scope.Loader;
+
+function StyleResolver() {
+  this.loader = new Loader(this.regex);
+}
+StyleResolver.prototype = {
+  regex: /@import\s+(?:url)?["'\(]*([^'"\)]*)['"\)]*;/g,
+  // Recursively replace @imports with the text at that url
+  resolve: function(text, url, callback) {
+    var done = function(map) {
+      callback(this.flatten(text, url, map));
+    }.bind(this);
+    this.loader.process(text, url, done);
+  },
+  // resolve the textContent of a style node
+  resolveNode: function(style, url, callback) {
+    var text = style.textContent;
+    var done = function(text) {
+      style.textContent = text;
+      callback(style);
+    };
+    this.resolve(text, url, done);
+  },
+  // flatten all the @imports to text
+  flatten: function(text, base, map) {
+    var matches = this.loader.extractUrls(text, base);
+    var match, url, intermediate;
+    for (var i = 0; i < matches.length; i++) {
+      match = matches[i];
+      url = match.url;
+      // resolve any css text to be relative to the importer, keep absolute url
+      intermediate = urlResolver.resolveCssText(map[url], url, true);
+      // flatten intermediate @imports
+      intermediate = this.flatten(intermediate, base, map);
+      text = text.replace(match.matched, intermediate);
+    }
+    return text;
+  },
+  loadStyles: function(styles, base, callback) {
+    var loaded=0, l = styles.length;
+    // called in the context of the style
+    function loadedStyle(style) {
+      loaded++;
+      if (loaded === l && callback) {
+        callback();
+      }
+    }
+    for (var i=0, s; (i<l) && (s=styles[i]); i++) {
+      this.resolveNode(s, base, loadedStyle);
+    }
+  }
+};
+
+var styleResolver = new StyleResolver();
+
+// exports
+scope.styleResolver = styleResolver;
+
+})(Polymer);
+
+(function(scope) {
+
+  // copy own properties from 'api' to 'prototype, with name hinting for 'super'
+  function extend(prototype, api) {
+    if (prototype && api) {
+      // use only own properties of 'api'
+      Object.getOwnPropertyNames(api).forEach(function(n) {
+        // acquire property descriptor
+        var pd = Object.getOwnPropertyDescriptor(api, n);
+        if (pd) {
+          // clone property via descriptor
+          Object.defineProperty(prototype, n, pd);
+          // cache name-of-method for 'super' engine
+          if (typeof pd.value == 'function') {
+            // hint the 'super' engine
+            pd.value.nom = n;
+          }
+        }
+      });
+    }
+    return prototype;
+  }
+
+
+  // mixin
+
+  // copy all properties from inProps (et al) to inObj
+  function mixin(inObj/*, inProps, inMoreProps, ...*/) {
+    var obj = inObj || {};
+    for (var i = 1; i < arguments.length; i++) {
+      var p = arguments[i];
+      try {
+        for (var n in p) {
+          copyProperty(n, p, obj);
+        }
+      } catch(x) {
+      }
+    }
+    return obj;
+  }
+
+  // copy property inName from inSource object to inTarget object
+  function copyProperty(inName, inSource, inTarget) {
+    var pd = getPropertyDescriptor(inSource, inName);
+    Object.defineProperty(inTarget, inName, pd);
+  }
+
+  // get property descriptor for inName on inObject, even if
+  // inName exists on some link in inObject's prototype chain
+  function getPropertyDescriptor(inObject, inName) {
+    if (inObject) {
+      var pd = Object.getOwnPropertyDescriptor(inObject, inName);
+      return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName);
+    }
+  }
+
+  // exports
+
+  scope.extend = extend;
+  scope.mixin = mixin;
+
+  // for bc
+  Platform.mixin = mixin;
+
+})(Polymer);
+
+(function(scope) {
+  
+  // usage
+  
+  // invoke cb.call(this) in 100ms, unless the job is re-registered,
+  // which resets the timer
+  // 
+  // this.myJob = this.job(this.myJob, cb, 100)
+  //
+  // returns a job handle which can be used to re-register a job
+
+  var Job = function(inContext) {
+    this.context = inContext;
+    this.boundComplete = this.complete.bind(this)
+  };
+  Job.prototype = {
+    go: function(callback, wait) {
+      this.callback = callback;
+      var h;
+      if (!wait) {
+        h = requestAnimationFrame(this.boundComplete);
+        this.handle = function() {
+          cancelAnimationFrame(h);
+        }
+      } else {
+        h = setTimeout(this.boundComplete, wait);
+        this.handle = function() {
+          clearTimeout(h);
+        }
+      }
+    },
+    stop: function() {
+      if (this.handle) {
+        this.handle();
+        this.handle = null;
+      }
+    },
+    complete: function() {
+      if (this.handle) {
+        this.stop();
+        this.callback.call(this.context);
+      }
+    }
+  };
+  
+  function job(job, callback, wait) {
+    if (job) {
+      job.stop();
+    } else {
+      job = new Job(this);
+    }
+    job.go(callback, wait);
+    return job;
+  }
+  
+  // exports 
+
+  scope.job = job;
+  
+})(Polymer);
+
+(function(scope) {
+
+  // dom polyfill, additions, and utility methods
+
+  var registry = {};
+
+  HTMLElement.register = function(tag, prototype) {
+    registry[tag] = prototype;
+  };
+
+  // get prototype mapped to node <tag>
+  HTMLElement.getPrototypeForTag = function(tag) {
+    var prototype = !tag ? HTMLElement.prototype : registry[tag];
+    // TODO(sjmiles): creating <tag> is likely to have wasteful side-effects
+    return prototype || Object.getPrototypeOf(document.createElement(tag));
+  };
+
+  // we have to flag propagation stoppage for the event dispatcher
+  var originalStopPropagation = Event.prototype.stopPropagation;
+  Event.prototype.stopPropagation = function() {
+    this.cancelBubble = true;
+    originalStopPropagation.apply(this, arguments);
+  };
+  
+  
+  // polyfill DOMTokenList
+  // * add/remove: allow these methods to take multiple classNames
+  // * toggle: add a 2nd argument which forces the given state rather
+  //  than toggling.
+
+  var add = DOMTokenList.prototype.add;
+  var remove = DOMTokenList.prototype.remove;
+  DOMTokenList.prototype.add = function() {
+    for (var i = 0; i < arguments.length; i++) {
+      add.call(this, arguments[i]);
+    }
+  };
+  DOMTokenList.prototype.remove = function() {
+    for (var i = 0; i < arguments.length; i++) {
+      remove.call(this, arguments[i]);
+    }
+  };
+  DOMTokenList.prototype.toggle = function(name, bool) {
+    if (arguments.length == 1) {
+      bool = !this.contains(name);
+    }
+    bool ? this.add(name) : this.remove(name);
+  };
+  DOMTokenList.prototype.switch = function(oldName, newName) {
+    oldName && this.remove(oldName);
+    newName && this.add(newName);
+  };
+
+  // add array() to NodeList, NamedNodeMap, HTMLCollection
+
+  var ArraySlice = function() {
+    return Array.prototype.slice.call(this);
+  };
+
+  var namedNodeMap = (window.NamedNodeMap || window.MozNamedAttrMap || {});
+
+  NodeList.prototype.array = ArraySlice;
+  namedNodeMap.prototype.array = ArraySlice;
+  HTMLCollection.prototype.array = ArraySlice;
+
+  // utility
+
+  function createDOM(inTagOrNode, inHTML, inAttrs) {
+    var dom = typeof inTagOrNode == 'string' ?
+        document.createElement(inTagOrNode) : inTagOrNode.cloneNode(true);
+    dom.innerHTML = inHTML;
+    if (inAttrs) {
+      for (var n in inAttrs) {
+        dom.setAttribute(n, inAttrs[n]);
+      }
+    }
+    return dom;
+  }
+
+  // exports
+
+  scope.createDOM = createDOM;
+
+})(Polymer);
+
+(function(scope) {

+    // super

+

+    // `arrayOfArgs` is an optional array of args like one might pass

+    // to `Function.apply`

+

+    // TODO(sjmiles):

+    //    $super must be installed on an instance or prototype chain

+    //    as `super`, and invoked via `this`, e.g.

+    //      `this.super();`

+

+    //    will not work if function objects are not unique, for example,

+    //    when using mixins.

+    //    The memoization strategy assumes each function exists on only one 

+    //    prototype chain i.e. we use the function object for memoizing)

+    //    perhaps we can bookkeep on the prototype itself instead

+    function $super(arrayOfArgs) {

+      // since we are thunking a method call, performance is important here: 

+      // memoize all lookups, once memoized the fast path calls no other 

+      // functions

+      //

+      // find the caller (cannot be `strict` because of 'caller')

+      var caller = $super.caller;

+      // memoized 'name of method' 

+      var nom = caller.nom;

+      // memoized next implementation prototype

+      var _super = caller._super;

+      if (!_super) {

+        if (!nom) {

+          nom = caller.nom = nameInThis.call(this, caller);

+        }

+        if (!nom) {

+          console.warn('called super() on a method not installed declaratively (has no .nom property)');

+        }

+        // super prototype is either cached or we have to find it

+        // by searching __proto__ (at the 'top')

+        // invariant: because we cache _super on fn below, we never reach 

+        // here from inside a series of calls to super(), so it's ok to 

+        // start searching from the prototype of 'this' (at the 'top')

+        // we must never memoize a null super for this reason

+        _super = memoizeSuper(caller, nom, getPrototypeOf(this));

+      }

+      // our super function

+      var fn = _super[nom];

+      if (fn) {

+        // memoize information so 'fn' can call 'super'

+        if (!fn._super) {

+          // must not memoize null, or we lose our invariant above

+          memoizeSuper(fn, nom, _super);

+        }

+        // invoke the inherited method

+        // if 'fn' is not function valued, this will throw

+        return fn.apply(this, arrayOfArgs || []);

+      }

+    }

+

+    function nameInThis(value) {

+      var p = this.__proto__;

+      while (p && p !== HTMLElement.prototype) {

+        // TODO(sjmiles): getOwnPropertyNames is absurdly expensive

+        var n$ = Object.getOwnPropertyNames(p);

+        for (var i=0, l=n$.length, n; i<l && (n=n$[i]); i++) {

+          var d = Object.getOwnPropertyDescriptor(p, n);

+          if (typeof d.value === 'function' && d.value === value) {

+            return n;

+          }

+        }

+        p = p.__proto__;

+      }

+    }

+

+    function memoizeSuper(method, name, proto) {

+      // find and cache next prototype containing `name`

+      // we need the prototype so we can do another lookup

+      // from here

+      var s = nextSuper(proto, name, method);

+      if (s[name]) {

+        // `s` is a prototype, the actual method is `s[name]`

+        // tag super method with it's name for quicker lookups

+        s[name].nom = name;

+      }

+      return method._super = s;

+    }

+

+    function nextSuper(proto, name, caller) {

+      // look for an inherited prototype that implements name

+      while (proto) {

+        if ((proto[name] !== caller) && proto[name]) {

+          return proto;

+        }

+        proto = getPrototypeOf(proto);

+      }

+      // must not return null, or we lose our invariant above

+      // in this case, a super() call was invoked where no superclass

+      // method exists

+      // TODO(sjmiles): thow an exception?

+      return Object;

+    }

+

+    // NOTE: In some platforms (IE10) the prototype chain is faked via 

+    // __proto__. Therefore, always get prototype via __proto__ instead of

+    // the more standard Object.getPrototypeOf.

+    function getPrototypeOf(prototype) {

+      return prototype.__proto__;

+    }

+

+    // utility function to precompute name tags for functions

+    // in a (unchained) prototype

+    function hintSuper(prototype) {

+      // tag functions with their prototype name to optimize

+      // super call invocations

+      for (var n in prototype) {

+        var pd = Object.getOwnPropertyDescriptor(prototype, n);

+        if (pd && typeof pd.value === 'function') {

+          pd.value.nom = n;

+        }

+      }

+    }

+

+    // exports

+

+    scope.super = $super;

+

+})(Polymer);

+
+(function(scope) {
+
+  function noopHandler(value) {
+    return value;
+  }
+
+  // helper for deserializing properties of various types to strings
+  var typeHandlers = {
+    string: noopHandler,
+    'undefined': noopHandler,
+    date: function(value) {
+      return new Date(Date.parse(value) || Date.now());
+    },
+    boolean: function(value) {
+      if (value === '') {
+        return true;
+      }
+      return value === 'false' ? false : !!value;
+    },
+    number: function(value) {
+      var n = parseFloat(value);
+      // hex values like "0xFFFF" parseFloat as 0
+      if (n === 0) {
+        n = parseInt(value);
+      }
+      return isNaN(n) ? value : n;
+      // this code disabled because encoded values (like "0xFFFF")
+      // do not round trip to their original format
+      //return (String(floatVal) === value) ? floatVal : value;
+    },
+    object: function(value, currentValue) {
+      if (currentValue === null) {
+        return value;
+      }
+      try {
+        // If the string is an object, we can parse is with the JSON library.
+        // include convenience replace for single-quotes. If the author omits
+        // quotes altogether, parse will fail.
+        return JSON.parse(value.replace(/'/g, '"'));
+      } catch(e) {
+        // The object isn't valid JSON, return the raw value
+        return value;
+      }
+    },
+    // avoid deserialization of functions
+    'function': function(value, currentValue) {
+      return currentValue;
+    }
+  };
+
+  function deserializeValue(value, currentValue) {
+    // attempt to infer type from default value
+    var inferredType = typeof currentValue;
+    // invent 'date' type value for Date
+    if (currentValue instanceof Date) {
+      inferredType = 'date';
+    }
+    // delegate deserialization via type string
+    return typeHandlers[inferredType](value, currentValue);
+  }
+
+  // exports
+
+  scope.deserializeValue = deserializeValue;
+
+})(Polymer);
+
+(function(scope) {
+
+  // imports
+
+  var extend = scope.extend;
+
+  // module
+
+  var api = {};
+
+  api.declaration = {};
+  api.instance = {};
+
+  api.publish = function(apis, prototype) {
+    for (var n in apis) {
+      extend(prototype, apis[n]);
+    }
+  };
+
+  // exports
+
+  scope.api = api;
+
+})(Polymer);
+
+(function(scope) {
+
+  /**
+   * @class polymer-base
+   */
+
+  var utils = {
+
+    /**
+      * Invokes a function asynchronously. The context of the callback
+      * function is bound to 'this' automatically. Returns a handle which may 
+      * be passed to <a href="#cancelAsync">cancelAsync</a> to cancel the 
+      * asynchronous call.
+      *
+      * @method async
+      * @param {Function|String} method
+      * @param {any|Array} args
+      * @param {number} timeout
+      */
+    async: function(method, args, timeout) {
+      // when polyfilling Object.observe, ensure changes 
+      // propagate before executing the async method
+      Polymer.flush();
+      // second argument to `apply` must be an array
+      args = (args && args.length) ? args : [args];
+      // function to invoke
+      var fn = function() {
+        (this[method] || method).apply(this, args);
+      }.bind(this);
+      // execute `fn` sooner or later
+      var handle = timeout ? setTimeout(fn, timeout) :
+          requestAnimationFrame(fn);
+      // NOTE: switch on inverting handle to determine which time is used.
+      return timeout ? handle : ~handle;
+    },
+
+    /**
+      * Cancels a pending callback that was scheduled via 
+      * <a href="#async">async</a>. 
+      *
+      * @method cancelAsync
+      * @param {handle} handle Handle of the `async` to cancel.
+      */
+    cancelAsync: function(handle) {
+      if (handle < 0) {
+        cancelAnimationFrame(~handle);
+      } else {
+        clearTimeout(handle);
+      }
+    },
+
+    /**
+      * Fire an event.
+      *
+      * @method fire
+      * @returns {Object} event
+      * @param {string} type An event name.
+      * @param {any} detail
+      * @param {Node} onNode Target node.
+      * @param {Boolean} bubbles Set false to prevent bubbling, defaults to true
+      * @param {Boolean} cancelable Set false to prevent cancellation, defaults to true
+      */
+    fire: function(type, detail, onNode, bubbles, cancelable) {
+      var node = onNode || this;
+      var detail = detail === null || detail === undefined ? {} : detail;
+      var event = new CustomEvent(type, {
+        bubbles: bubbles !== undefined ? bubbles : true,
+        cancelable: cancelable !== undefined ? cancelable : true,
+        detail: detail
+      });
+      node.dispatchEvent(event);
+      return event;
+    },
+
+    /**
+      * Fire an event asynchronously.
+      *
+      * @method asyncFire
+      * @param {string} type An event name.
+      * @param detail
+      * @param {Node} toNode Target node.
+      */
+    asyncFire: function(/*inType, inDetail*/) {
+      this.async("fire", arguments);
+    },
+
+    /**
+      * Remove class from old, add class to anew, if they exist.
+      *
+      * @param classFollows
+      * @param anew A node.
+      * @param old A node
+      * @param className
+      */
+    classFollows: function(anew, old, className) {
+      if (old) {
+        old.classList.remove(className);
+      }
+      if (anew) {
+        anew.classList.add(className);
+      }
+    },
+
+    /**
+      * Inject HTML which contains markup bound to this element into
+      * a target element (replacing target element content).
+      *
+      * @param String html to inject
+      * @param Element target element
+      */
+    injectBoundHTML: function(html, element) {
+      var template = document.createElement('template');
+      template.innerHTML = html;
+      var fragment = this.instanceTemplate(template);
+      if (element) {
+        element.textContent = '';
+        element.appendChild(fragment);
+      }
+      return fragment;
+    }
+  };
+
+  // no-operation function for handy stubs
+  var nop = function() {};
+
+  // null-object for handy stubs
+  var nob = {};
+
+  // deprecated
+
+  utils.asyncMethod = utils.async;
+
+  // exports
+
+  scope.api.instance.utils = utils;
+  scope.nop = nop;
+  scope.nob = nob;
+
+})(Polymer);
+
+(function(scope) {
+
+  // imports
+
+  var log = window.WebComponents ? WebComponents.flags.log : {};
+  var EVENT_PREFIX = 'on-';
+
+  // instance events api
+  var events = {
+    // read-only
+    EVENT_PREFIX: EVENT_PREFIX,
+    // event listeners on host
+    addHostListeners: function() {
+      var events = this.eventDelegates;
+      log.events && (Object.keys(events).length > 0) && console.log('[%s] addHostListeners:', this.localName, events);
+      // NOTE: host events look like bindings but really are not;
+      // (1) we don't want the attribute to be set and (2) we want to support
+      // multiple event listeners ('host' and 'instance') and Node.bind
+      // by default supports 1 thing being bound.
+      for (var type in events) {
+        var methodName = events[type];
+        PolymerGestures.addEventListener(this, type, this.element.getEventHandler(this, this, methodName));
+      }
+    },
+    // call 'method' or function method on 'obj' with 'args', if the method exists
+    dispatchMethod: function(obj, method, args) {
+      if (obj) {
+        log.events && console.group('[%s] dispatch [%s]', obj.localName, method);
+        var fn = typeof method === 'function' ? method : obj[method];
+        if (fn) {
+          fn[args ? 'apply' : 'call'](obj, args);
+        }
+        log.events && console.groupEnd();
+        // NOTE: dirty check right after calling method to ensure 
+        // changes apply quickly; in a very complicated app using high 
+        // frequency events, this can be a perf concern; in this case,
+        // imperative handlers can be used to avoid flushing.
+        Polymer.flush();
+      }
+    }
+  };
+
+  // exports
+
+  scope.api.instance.events = events;
+
+  /**
+   * @class Polymer
+   */
+
+  /**
+   * Add a gesture aware event handler to the given `node`. Can be used 
+   * in place of `element.addEventListener` and ensures gestures will function
+   * as expected on mobile platforms. Please note that Polymer's declarative
+   * event handlers include this functionality by default.
+   * 
+   * @method addEventListener
+   * @param {Node} node node on which to listen
+   * @param {String} eventType name of the event
+   * @param {Function} handlerFn event handler function
+   * @param {Boolean} capture set to true to invoke event capturing
+   * @type Function
+   */
+  // alias PolymerGestures event listener logic
+  scope.addEventListener = function(node, eventType, handlerFn, capture) {
+    PolymerGestures.addEventListener(wrap(node), eventType, handlerFn, capture);
+  };
+
+  /**
+   * Remove a gesture aware event handler on the given `node`. To remove an
+   * event listener, the exact same arguments are required that were passed
+   * to `Polymer.addEventListener`.
+   * 
+   * @method removeEventListener
+   * @param {Node} node node on which to listen
+   * @param {String} eventType name of the event
+   * @param {Function} handlerFn event handler function
+   * @param {Boolean} capture set to true to invoke event capturing
+   * @type Function
+   */
+  scope.removeEventListener = function(node, eventType, handlerFn, capture) {
+    PolymerGestures.removeEventListener(wrap(node), eventType, handlerFn, capture);
+  };
+
+})(Polymer);
+
+(function(scope) {

+

+  // instance api for attributes

+

+  var attributes = {

+    // copy attributes defined in the element declaration to the instance

+    // e.g. <polymer-element name="x-foo" tabIndex="0"> tabIndex is copied

+    // to the element instance here.

+    copyInstanceAttributes: function () {

+      var a$ = this._instanceAttributes;

+      for (var k in a$) {

+        if (!this.hasAttribute(k)) {

+          this.setAttribute(k, a$[k]);

+        }

+      }

+    },

+    // for each attribute on this, deserialize value to property as needed

+    takeAttributes: function() {

+      // if we have no publish lookup table, we have no attributes to take

+      // TODO(sjmiles): ad hoc

+      if (this._publishLC) {

+        for (var i=0, a$=this.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++) {

+          this.attributeToProperty(a.name, a.value);

+        }

+      }

+    },

+    // if attribute 'name' is mapped to a property, deserialize

+    // 'value' into that property

+    attributeToProperty: function(name, value) {

+      // try to match this attribute to a property (attributes are

+      // all lower-case, so this is case-insensitive search)

+      var name = this.propertyForAttribute(name);

+      if (name) {

+        // filter out 'mustached' values, these are to be

+        // replaced with bound-data and are not yet values

+        // themselves

+        if (value && value.search(scope.bindPattern) >= 0) {

+          return;

+        }

+        // get original value

+        var currentValue = this[name];

+        // deserialize Boolean or Number values from attribute

+        var value = this.deserializeValue(value, currentValue);

+        // only act if the value has changed

+        if (value !== currentValue) {

+          // install new value (has side-effects)

+          this[name] = value;

+        }

+      }

+    },

+    // return the published property matching name, or undefined

+    propertyForAttribute: function(name) {

+      var match = this._publishLC && this._publishLC[name];

+      return match;

+    },

+    // convert representation of `stringValue` based on type of `currentValue`

+    deserializeValue: function(stringValue, currentValue) {

+      return scope.deserializeValue(stringValue, currentValue);

+    },

+    // convert to a string value based on the type of `inferredType`

+    serializeValue: function(value, inferredType) {

+      if (inferredType === 'boolean') {

+        return value ? '' : undefined;

+      } else if (inferredType !== 'object' && inferredType !== 'function'

+          && value !== undefined) {

+        return value;

+      }

+    },

+    // serializes `name` property value and updates the corresponding attribute

+    // note that reflection is opt-in.

+    reflectPropertyToAttribute: function(name) {

+      var inferredType = typeof this[name];

+      // try to intelligently serialize property value

+      var serializedValue = this.serializeValue(this[name], inferredType);

+      // boolean properties must reflect as boolean attributes

+      if (serializedValue !== undefined) {

+        this.setAttribute(name, serializedValue);

+        // TODO(sorvell): we should remove attr for all properties

+        // that have undefined serialization; however, we will need to

+        // refine the attr reflection system to achieve this; pica, for example,

+        // relies on having inferredType object properties not removed as

+        // attrs.

+      } else if (inferredType === 'boolean') {

+        this.removeAttribute(name);

+      }

+    }

+  };

+

+  // exports

+

+  scope.api.instance.attributes = attributes;

+

+})(Polymer);

+
+(function(scope) {
+
+  /**
+   * @class polymer-base
+   */
+
+  // imports
+
+  var log = window.WebComponents ? WebComponents.flags.log : {};
+
+  // magic words
+
+  var OBSERVE_SUFFIX = 'Changed';
+
+  // element api
+
+  var empty = [];
+
+  var updateRecord = {
+    object: undefined,
+    type: 'update',
+    name: undefined,
+    oldValue: undefined
+  };
+
+  var numberIsNaN = Number.isNaN || function(value) {
+    return typeof value === 'number' && isNaN(value);
+  };
+
+  function areSameValue(left, right) {
+    if (left === right)
+      return left !== 0 || 1 / left === 1 / right;
+    if (numberIsNaN(left) && numberIsNaN(right))
+      return true;
+    return left !== left && right !== right;
+  }
+
+  // capture A's value if B's value is null or undefined,
+  // otherwise use B's value
+  function resolveBindingValue(oldValue, value) {
+    if (value === undefined && oldValue === null) {
+      return value;
+    }
+    return (value === null || value === undefined) ? oldValue : value;
+  }
+
+  var properties = {
+
+    // creates a CompoundObserver to observe property changes
+    // NOTE, this is only done there are any properties in the `observe` object
+    createPropertyObserver: function() {
+      var n$ = this._observeNames;
+      if (n$ && n$.length) {
+        var o = this._propertyObserver = new CompoundObserver(true);
+        this.registerObserver(o);
+        // TODO(sorvell): may not be kosher to access the value here (this[n]);
+        // previously we looked at the descriptor on the prototype
+        // this doesn't work for inheritance and not for accessors without
+        // a value property
+        for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
+          o.addPath(this, n);
+          this.observeArrayValue(n, this[n], null);
+        }
+      }
+    },
+
+    // start observing property changes
+    openPropertyObserver: function() {
+      if (this._propertyObserver) {
+        this._propertyObserver.open(this.notifyPropertyChanges, this);
+      }
+    },
+
+    // handler for property changes; routes changes to observing methods
+    // note: array valued properties are observed for array splices
+    notifyPropertyChanges: function(newValues, oldValues, paths) {
+      var name, method, called = {};
+      for (var i in oldValues) {
+        // note: paths is of form [object, path, object, path]
+        name = paths[2 * i + 1];
+        method = this.observe[name];
+        if (method) {
+          var ov = oldValues[i], nv = newValues[i];
+          // observes the value if it is an array
+          this.observeArrayValue(name, nv, ov);
+          if (!called[method]) {
+            // only invoke change method if one of ov or nv is not (undefined | null)
+            if ((ov !== undefined && ov !== null) || (nv !== undefined && nv !== null)) {
+              called[method] = true;
+              // TODO(sorvell): call method with the set of values it's expecting;
+              // e.g. 'foo bar': 'invalidate' expects the new and old values for
+              // foo and bar. Currently we give only one of these and then
+              // deliver all the arguments.
+              this.invokeMethod(method, [ov, nv, arguments]);
+            }
+          }
+        }
+      }
+    },
+
+    // call method iff it exists.
+    invokeMethod: function(method, args) {
+      var fn = this[method] || method;
+      if (typeof fn === 'function') {
+        fn.apply(this, args);
+      }
+    },
+
+    /**
+     * Force any pending property changes to synchronously deliver to
+     * handlers specified in the `observe` object.
+     * Note, normally changes are processed at microtask time.
+     *
+     * @method deliverChanges
+     */
+    deliverChanges: function() {
+      if (this._propertyObserver) {
+        this._propertyObserver.deliver();
+      }
+    },
+
+    observeArrayValue: function(name, value, old) {
+      // we only care if there are registered side-effects
+      var callbackName = this.observe[name];
+      if (callbackName) {
+        // if we are observing the previous value, stop
+        if (Array.isArray(old)) {
+          log.observe && console.log('[%s] observeArrayValue: unregister observer [%s]', this.localName, name);
+          this.closeNamedObserver(name + '__array');
+        }
+        // if the new value is an array, being observing it
+        if (Array.isArray(value)) {
+          log.observe && console.log('[%s] observeArrayValue: register observer [%s]', this.localName, name, value);
+          var observer = new ArrayObserver(value);
+          observer.open(function(splices) {
+            this.invokeMethod(callbackName, [splices]);
+          }, this);
+          this.registerNamedObserver(name + '__array', observer);
+        }
+      }
+    },
+
+    emitPropertyChangeRecord: function(name, value, oldValue) {
+      var object = this;
+      if (areSameValue(value, oldValue)) {
+        return;
+      }
+      // invoke property change side effects
+      this._propertyChanged(name, value, oldValue);
+      // emit change record
+      if (!Observer.hasObjectObserve) {
+        return;
+      }
+      var notifier = this._objectNotifier;
+      if (!notifier) {
+        notifier = this._objectNotifier = Object.getNotifier(this);
+      }
+      updateRecord.object = this;
+      updateRecord.name = name;
+      updateRecord.oldValue = oldValue;
+      notifier.notify(updateRecord);
+    },
+
+    _propertyChanged: function(name, value, oldValue) {
+      if (this.reflect[name]) {
+        this.reflectPropertyToAttribute(name);
+      }
+    },
+
+    // creates a property binding (called via bind) to a published property.
+    bindProperty: function(property, observable, oneTime) {
+      if (oneTime) {
+        this[property] = observable;
+        return;
+      }
+      var computed = this.element.prototype.computed;
+      // Binding an "out-only" value to a computed property. Note that
+      // since this observer isn't opened, it doesn't need to be closed on
+      // cleanup.
+      if (computed && computed[property]) {
+        var privateComputedBoundValue = property + 'ComputedBoundObservable_';
+        this[privateComputedBoundValue] = observable;
+        return;
+      }
+      return this.bindToAccessor(property, observable, resolveBindingValue);
+    },
+
+    // NOTE property `name` must be published. This makes it an accessor.
+    bindToAccessor: function(name, observable, resolveFn) {
+      var privateName = name + '_';
+      var privateObservable  = name + 'Observable_';
+      // Present for properties which are computed and published and have a
+      // bound value.
+      var privateComputedBoundValue = name + 'ComputedBoundObservable_';
+      this[privateObservable] = observable;
+      var oldValue = this[privateName];
+      // observable callback
+      var self = this;
+      function updateValue(value, oldValue) {
+        self[privateName] = value;
+        var setObserveable = self[privateComputedBoundValue];
+        if (setObserveable && typeof setObserveable.setValue == 'function') {
+          setObserveable.setValue(value);
+        }
+        self.emitPropertyChangeRecord(name, value, oldValue);
+      }
+      // resolve initial value
+      var value = observable.open(updateValue);
+      if (resolveFn && !areSameValue(oldValue, value)) {
+        var resolvedValue = resolveFn(oldValue, value);
+        if (!areSameValue(value, resolvedValue)) {
+          value = resolvedValue;
+          if (observable.setValue) {
+            observable.setValue(value);
+          }
+        }
+      }
+      updateValue(value, oldValue);
+      // register and return observable
+      var observer = {
+        close: function() {
+          observable.close();
+          self[privateObservable] = undefined;
+          self[privateComputedBoundValue] = undefined;
+        }
+      };
+      this.registerObserver(observer);
+      return observer;
+    },
+
+    createComputedProperties: function() {
+      if (!this._computedNames) {
+        return;
+      }
+      for (var i = 0; i < this._computedNames.length; i++) {
+        var name = this._computedNames[i];
+        var expressionText = this.computed[name];
+        try {
+          var expression = PolymerExpressions.getExpression(expressionText);
+          var observable = expression.getBinding(this, this.element.syntax);
+          this.bindToAccessor(name, observable);
+        } catch (ex) {
+          console.error('Failed to create computed property', ex);
+        }
+      }
+    },
+
+    // property bookkeeping
+    registerObserver: function(observer) {
+      if (!this._observers) {
+        this._observers = [observer];
+        return;
+      }
+      this._observers.push(observer);
+    },
+
+    closeObservers: function() {
+      if (!this._observers) {
+        return;
+      }
+      // observer array items are arrays of observers.
+      var observers = this._observers;
+      for (var i = 0; i < observers.length; i++) {
+        var observer = observers[i];
+        if (observer && typeof observer.close == 'function') {
+          observer.close();
+        }
+      }
+      this._observers = [];
+    },
+
+    // bookkeeping observers for memory management
+    registerNamedObserver: function(name, observer) {
+      var o$ = this._namedObservers || (this._namedObservers = {});
+      o$[name] = observer;
+    },
+
+    closeNamedObserver: function(name) {
+      var o$ = this._namedObservers;
+      if (o$ && o$[name]) {
+        o$[name].close();
+        o$[name] = null;
+        return true;
+      }
+    },
+
+    closeNamedObservers: function() {
+      if (this._namedObservers) {
+        for (var i in this._namedObservers) {
+          this.closeNamedObserver(i);
+        }
+        this._namedObservers = {};
+      }
+    }
+
+  };
+
+  // logging
+  var LOG_OBSERVE = '[%s] watching [%s]';
+  var LOG_OBSERVED = '[%s#%s] watch: [%s] now [%s] was [%s]';
+  var LOG_CHANGED = '[%s#%s] propertyChanged: [%s] now [%s] was [%s]';
+
+  // exports
+
+  scope.api.instance.properties = properties;
+
+})(Polymer);
+
+(function(scope) {
+
+  /**
+   * @class polymer-base
+   */
+
+  // imports
+
+  var log = window.WebComponents ? WebComponents.flags.log : {};
+
+  // element api supporting mdv
+  var mdv = {
+
+    /**
+     * Creates dom cloned from the given template, instantiating bindings
+     * with this element as the template model and `PolymerExpressions` as the
+     * binding delegate.
+     *
+     * @method instanceTemplate
+     * @param {Template} template source template from which to create dom.
+     */
+    instanceTemplate: function(template) {
+      // ensure template is decorated (lets' things like <tr template ...> work)
+      HTMLTemplateElement.decorate(template);
+      // ensure a default bindingDelegate
+      var syntax = this.syntax || (!template.bindingDelegate &&
+          this.element.syntax);
+      var dom = template.createInstance(this, syntax);
+      var observers = dom.bindings_;
+      for (var i = 0; i < observers.length; i++) {
+        this.registerObserver(observers[i]);
+      }
+      return dom;
+    },
+
+    // Called by TemplateBinding/NodeBind to setup a binding to the given
+    // property. It's overridden here to support property bindings
+    // in addition to attribute bindings that are supported by default.
+    bind: function(name, observable, oneTime) {
+      var property = this.propertyForAttribute(name);
+      if (!property) {
+        // TODO(sjmiles): this mixin method must use the special form
+        // of `super` installed by `mixinMethod` in declaration/prototype.js
+        return this.mixinSuper(arguments);
+      } else {
+        // use n-way Polymer binding
+        var observer = this.bindProperty(property, observable, oneTime);
+        // NOTE: reflecting binding information is typically required only for
+        // tooling. It has a performance cost so it's opt-in in Node.bind.
+        if (Platform.enableBindingsReflection && observer) {
+          observer.path = observable.path_;
+          this._recordBinding(property, observer);
+        }
+        if (this.reflect[property]) {
+          this.reflectPropertyToAttribute(property);
+        }
+        return observer;
+      }
+    },
+
+    _recordBinding: function(name, observer) {
+      this.bindings_ = this.bindings_ || {};
+      this.bindings_[name] = observer;
+    },
+
+    // Called by TemplateBinding when all bindings on an element have been 
+    // executed. This signals that all element inputs have been gathered
+    // and it's safe to ready the element, create shadow-root and start
+    // data-observation.
+    bindFinished: function() {
+      this.makeElementReady();
+    },
+
+    // called at detached time to signal that an element's bindings should be
+    // cleaned up. This is done asynchronously so that users have the chance
+    // to call `cancelUnbindAll` to prevent unbinding.
+    asyncUnbindAll: function() {
+      if (!this._unbound) {
+        log.unbind && console.log('[%s] asyncUnbindAll', this.localName);
+        this._unbindAllJob = this.job(this._unbindAllJob, this.unbindAll, 0);
+      }
+    },
+    
+    /**
+     * This method should rarely be used and only if 
+     * <a href="#cancelUnbindAll">`cancelUnbindAll`</a> has been called to 
+     * prevent element unbinding. In this case, the element's bindings will 
+     * not be automatically cleaned up and it cannot be garbage collected 
+     * by the system. If memory pressure is a concern or a 
+     * large amount of elements need to be managed in this way, `unbindAll`
+     * can be called to deactivate the element's bindings and allow its 
+     * memory to be reclaimed.
+     *
+     * @method unbindAll
+     */
+    unbindAll: function() {
+      if (!this._unbound) {
+        this.closeObservers();
+        this.closeNamedObservers();
+        this._unbound = true;
+      }
+    },
+
+    /**
+     * Call in `detached` to prevent the element from unbinding when it is 
+     * detached from the dom. The element is unbound as a cleanup step that 
+     * allows its memory to be reclaimed. 
+     * If `cancelUnbindAll` is used, consider calling 
+     * <a href="#unbindAll">`unbindAll`</a> when the element is no longer
+     * needed. This will allow its memory to be reclaimed.
+     * 
+     * @method cancelUnbindAll
+     */
+    cancelUnbindAll: function() {
+      if (this._unbound) {
+        log.unbind && console.warn('[%s] already unbound, cannot cancel unbindAll', this.localName);
+        return;
+      }
+      log.unbind && console.log('[%s] cancelUnbindAll', this.localName);
+      if (this._unbindAllJob) {
+        this._unbindAllJob = this._unbindAllJob.stop();
+      }
+    }
+
+  };
+
+  function unbindNodeTree(node) {
+    forNodeTree(node, _nodeUnbindAll);
+  }
+
+  function _nodeUnbindAll(node) {
+    node.unbindAll();
+  }
+
+  function forNodeTree(node, callback) {
+    if (node) {
+      callback(node);
+      for (var child = node.firstChild; child; child = child.nextSibling) {
+        forNodeTree(child, callback);
+      }
+    }
+  }
+
+  var mustachePattern = /\{\{([^{}]*)}}/;
+
+  // exports
+
+  scope.bindPattern = mustachePattern;
+  scope.api.instance.mdv = mdv;
+
+})(Polymer);
+
+(function(scope) {
+
+  /**
+   * Common prototype for all Polymer Elements.
+   * 
+   * @class polymer-base
+   * @homepage polymer.github.io
+   */
+  var base = {
+    /**
+     * Tags this object as the canonical Base prototype.
+     *
+     * @property PolymerBase
+     * @type boolean
+     * @default true
+     */
+    PolymerBase: true,
+
+    /**
+     * Debounce signals. 
+     * 
+     * Call `job` to defer a named signal, and all subsequent matching signals, 
+     * until a wait time has elapsed with no new signal.
+     * 
+     *     debouncedClickAction: function(e) {
+     *       // processClick only when it's been 100ms since the last click
+     *       this.job('click', function() {
+     *        this.processClick;
+     *       }, 100);
+     *     }
+     *
+     * @method job
+     * @param String {String} job A string identifier for the job to debounce.
+     * @param Function {Function} callback A function that is called (with `this` context) when the wait time elapses.
+     * @param Number {Number} wait Time in milliseconds (ms) after the last signal that must elapse before invoking `callback`
+     * @type Handle
+     */
+    job: function(job, callback, wait) {
+      if (typeof job === 'string') {
+        var n = '___' + job;
+        this[n] = Polymer.job.call(this, this[n], callback, wait);
+      } else {
+        // TODO(sjmiles): suggest we deprecate this call signature
+        return Polymer.job.call(this, job, callback, wait);
+      }
+    },
+
+    /**
+     * Invoke a superclass method. 
+     * 
+     * Use `super()` to invoke the most recently overridden call to the 
+     * currently executing function. 
+     * 
+     * To pass arguments through, use the literal `arguments` as the parameter 
+     * to `super()`.
+     *
+     *     nextPageAction: function(e) {
+     *       // invoke the superclass version of `nextPageAction`
+     *       this.super(arguments); 
+     *     }
+     *
+     * To pass custom arguments, arrange them in an array.
+     *
+     *     appendSerialNo: function(value, serial) {
+     *       // prefix the superclass serial number with our lot # before
+     *       // invoking the superlcass
+     *       return this.super([value, this.lotNo + serial])
+     *     }
+     *
+     * @method super
+     * @type Any
+     * @param {args) An array of arguments to use when calling the superclass method, or null.
+     */
+    super: Polymer.super,
+
+    /**
+     * Lifecycle method called when the element is instantiated.
+     * 
+     * Override `created` to perform custom create-time tasks. No need to call 
+     * super-class `created` unless you are extending another Polymer element.
+     * Created is called before the element creates `shadowRoot` or prepares
+     * data-observation.
+     * 
+     * @method created
+     * @type void
+     */
+    created: function() {
+    },
+
+    /**
+     * Lifecycle method called when the element has populated it's `shadowRoot`,
+     * prepared data-observation, and made itself ready for API interaction.
+     * 
+     * @method ready
+     * @type void
+     */
+    ready: function() {
+    },
+
+    /**
+     * Low-level lifecycle method called as part of standard Custom Elements
+     * operation. Polymer implements this method to provide basic default 
+     * functionality. For custom create-time tasks, implement `created` 
+     * instead, which is called immediately after `createdCallback`. 
+     * 
+     * @method createdCallback
+     */
+    createdCallback: function() {
+      if (this.templateInstance && this.templateInstance.model) {
+        console.warn('Attributes on ' + this.localName + ' were data bound ' +
+            'prior to Polymer upgrading the element. This may result in ' +
+            'incorrect binding types.');
+      }
+      this.created();
+      this.prepareElement();
+      if (!this.ownerDocument.isStagingDocument) {
+        this.makeElementReady();
+      }
+    },
+
+    // system entry point, do not override
+    prepareElement: function() {
+      if (this._elementPrepared) {
+        console.warn('Element already prepared', this.localName);
+        return;
+      }
+      this._elementPrepared = true;
+      // storage for shadowRoots info
+      this.shadowRoots = {};
+      // install property observers
+      this.createPropertyObserver();
+      this.openPropertyObserver();
+      // install boilerplate attributes
+      this.copyInstanceAttributes();
+      // process input attributes
+      this.takeAttributes();
+      // add event listeners
+      this.addHostListeners();
+    },
+
+    // system entry point, do not override
+    makeElementReady: function() {
+      if (this._readied) {
+        return;
+      }
+      this._readied = true;
+      this.createComputedProperties();
+      this.parseDeclarations(this.__proto__);
+      // NOTE: Support use of the `unresolved` attribute to help polyfill
+      // custom elements' `:unresolved` feature.
+      this.removeAttribute('unresolved');
+      // user entry point
+      this.ready();
+    },
+
+    /**
+     * Low-level lifecycle method called as part of standard Custom Elements
+     * operation. Polymer implements this method to provide basic default 
+     * functionality. For custom tasks in your element, implement `attributeChanged` 
+     * instead, which is called immediately after `attributeChangedCallback`. 
+     * 
+     * @method attributeChangedCallback
+     */
+    attributeChangedCallback: function(name, oldValue) {
+      // TODO(sjmiles): adhoc filter
+      if (name !== 'class' && name !== 'style') {
+        this.attributeToProperty(name, this.getAttribute(name));
+      }
+      if (this.attributeChanged) {
+        this.attributeChanged.apply(this, arguments);
+      }
+    },
+
+    /**
+     * Low-level lifecycle method called as part of standard Custom Elements
+     * operation. Polymer implements this method to provide basic default 
+     * functionality. For custom create-time tasks, implement `attached` 
+     * instead, which is called immediately after `attachedCallback`. 
+     * 
+     * @method attachedCallback
+     */
+     attachedCallback: function() {
+      // when the element is attached, prevent it from unbinding.
+      this.cancelUnbindAll();
+      // invoke user action
+      if (this.attached) {
+        this.attached();
+      }
+      if (!this.hasBeenAttached) {
+        this.hasBeenAttached = true;
+        if (this.domReady) {
+          this.async('domReady');
+        }
+      }
+    },
+
+     /**
+     * Implement to access custom elements in dom descendants, ancestors, 
+     * or siblings. Because custom elements upgrade in document order, 
+     * elements accessed in `ready` or `attached` may not be upgraded. When
+     * `domReady` is called, all registered custom elements are guaranteed
+     * to have been upgraded.
+     * 
+     * @method domReady
+     */
+
+    /**
+     * Low-level lifecycle method called as part of standard Custom Elements
+     * operation. Polymer implements this method to provide basic default 
+     * functionality. For custom create-time tasks, implement `detached` 
+     * instead, which is called immediately after `detachedCallback`. 
+     * 
+     * @method detachedCallback
+     */
+    detachedCallback: function() {
+      if (!this.preventDispose) {
+        this.asyncUnbindAll();
+      }
+      // invoke user action
+      if (this.detached) {
+        this.detached();
+      }
+      // TODO(sorvell): bc
+      if (this.leftView) {
+        this.leftView();
+      }
+    },
+
+    /**
+     * Walks the prototype-chain of this element and allows specific
+     * classes a chance to process static declarations.
+     * 
+     * In particular, each polymer-element has it's own `template`.
+     * `parseDeclarations` is used to accumulate all element `template`s
+     * from an inheritance chain.
+     *
+     * `parseDeclaration` static methods implemented in the chain are called
+     * recursively, oldest first, with the `<polymer-element>` associated
+     * with the current prototype passed as an argument.
+     * 
+     * An element may override this method to customize shadow-root generation. 
+     * 
+     * @method parseDeclarations
+     */
+    parseDeclarations: function(p) {
+      if (p && p.element) {
+        this.parseDeclarations(p.__proto__);
+        p.parseDeclaration.call(this, p.element);
+      }
+    },
+
+    /**
+     * Perform init-time actions based on static information in the
+     * `<polymer-element>` instance argument.
+     *
+     * For example, the standard implementation locates the template associated
+     * with the given `<polymer-element>` and stamps it into a shadow-root to
+     * implement shadow inheritance.
+     *  
+     * An element may override this method for custom behavior. 
+     * 
+     * @method parseDeclaration
+     */
+    parseDeclaration: function(elementElement) {
+      var template = this.fetchTemplate(elementElement);
+      if (template) {
+        var root = this.shadowFromTemplate(template);
+        this.shadowRoots[elementElement.name] = root;
+      }
+    },
+
+    /**
+     * Given a `<polymer-element>`, find an associated template (if any) to be
+     * used for shadow-root generation.
+     *
+     * An element may override this method for custom behavior. 
+     * 
+     * @method fetchTemplate
+     */
+    fetchTemplate: function(elementElement) {
+      return elementElement.querySelector('template');
+    },
+
+    /**
+     * Create a shadow-root in this host and stamp `template` as it's 
+     * content. 
+     *
+     * An element may override this method for custom behavior. 
+     * 
+     * @method shadowFromTemplate
+     */
+    shadowFromTemplate: function(template) {
+      if (template) {
+        // make a shadow root
+        var root = this.createShadowRoot();
+        // stamp template
+        // which includes parsing and applying MDV bindings before being
+        // inserted (to avoid {{}} in attribute values)
+        // e.g. to prevent <img src="images/{{icon}}"> from generating a 404.
+        var dom = this.instanceTemplate(template);
+        // append to shadow dom
+        root.appendChild(dom);
+        // perform post-construction initialization tasks on shadow root
+        this.shadowRootReady(root, template);
+        // return the created shadow root
+        return root;
+      }
+    },
+
+    // utility function that stamps a <template> into light-dom
+    lightFromTemplate: function(template, refNode) {
+      if (template) {
+        // TODO(sorvell): mark this element as an eventController so that
+        // event listeners on bound nodes inside it will be called on it.
+        // Note, the expectation here is that events on all descendants
+        // should be handled by this element.
+        this.eventController = this;
+        // stamp template
+        // which includes parsing and applying MDV bindings before being
+        // inserted (to avoid {{}} in attribute values)
+        // e.g. to prevent <img src="images/{{icon}}"> from generating a 404.
+        var dom = this.instanceTemplate(template);
+        // append to shadow dom
+        if (refNode) {
+          this.insertBefore(dom, refNode);
+        } else {
+          this.appendChild(dom);
+        }
+        // perform post-construction initialization tasks on ahem, light root
+        this.shadowRootReady(this);
+        // return the created shadow root
+        return dom;
+      }
+    },
+
+    shadowRootReady: function(root) {
+      // locate nodes with id and store references to them in this.$ hash
+      this.marshalNodeReferences(root);
+    },
+
+    // locate nodes with id and store references to them in this.$ hash
+    marshalNodeReferences: function(root) {
+      // establish $ instance variable
+      var $ = this.$ = this.$ || {};
+      // populate $ from nodes with ID from the LOCAL tree
+      if (root) {
+        var n$ = root.querySelectorAll("[id]");
+        for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
+          $[n.id] = n;
+        };
+      }
+    },
+
+    /**
+     * Register a one-time callback when a child-list or sub-tree mutation
+     * occurs on node. 
+     *
+     * For persistent callbacks, call onMutation from your listener. 
+     * 
+     * @method onMutation
+     * @param Node {Node} node Node to watch for mutations.
+     * @param Function {Function} listener Function to call on mutation. The function is invoked as `listener.call(this, observer, mutations);` where `observer` is the MutationObserver that triggered the notification, and `mutations` is the native mutation list.
+     */
+    onMutation: function(node, listener) {
+      var observer = new MutationObserver(function(mutations) {
+        listener.call(this, observer, mutations);
+        observer.disconnect();
+      }.bind(this));
+      observer.observe(node, {childList: true, subtree: true});
+    }
+  };
+
+  /**
+   * @class Polymer
+   */
+  
+  /**
+   * Returns true if the object includes <a href="#polymer-base">polymer-base</a> in it's prototype chain.
+   * 
+   * @method isBase
+   * @param Object {Object} object Object to test.
+   * @type Boolean
+   */
+  function isBase(object) {
+    return object.hasOwnProperty('PolymerBase')
+  }
+
+  // name a base constructor for dev tools
+
+  /**
+   * The Polymer base-class constructor.
+   * 
+   * @property Base
+   * @type Function
+   */
+  function PolymerBase() {};
+  PolymerBase.prototype = base;
+  base.constructor = PolymerBase;
+
+  // exports
+
+  scope.Base = PolymerBase;
+  scope.isBase = isBase;
+  scope.api.instance.base = base;
+
+})(Polymer);
+
+(function(scope) {
+
+  // imports
+
+  var log = window.WebComponents ? WebComponents.flags.log : {};
+  var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
+
+  // magic words
+  
+  var STYLE_SCOPE_ATTRIBUTE = 'element';
+  var STYLE_CONTROLLER_SCOPE = 'controller';
+  
+  var styles = {
+    STYLE_SCOPE_ATTRIBUTE: STYLE_SCOPE_ATTRIBUTE,
+    /**
+     * Installs external stylesheets and <style> elements with the attribute 
+     * polymer-scope='controller' into the scope of element. This is intended
+     * to be a called during custom element construction.
+    */
+    installControllerStyles: function() {
+      // apply controller styles, but only if they are not yet applied
+      var scope = this.findStyleScope();
+      if (scope && !this.scopeHasNamedStyle(scope, this.localName)) {
+        // allow inherited controller styles
+        var proto = getPrototypeOf(this), cssText = '';
+        while (proto && proto.element) {
+          cssText += proto.element.cssTextForScope(STYLE_CONTROLLER_SCOPE);
+          proto = getPrototypeOf(proto);
+        }
+        if (cssText) {
+          this.installScopeCssText(cssText, scope);
+        }
+      }
+    },
+    installScopeStyle: function(style, name, scope) {
+      var scope = scope || this.findStyleScope(), name = name || '';
+      if (scope && !this.scopeHasNamedStyle(scope, this.localName + name)) {
+        var cssText = '';
+        if (style instanceof Array) {
+          for (var i=0, l=style.length, s; (i<l) && (s=style[i]); i++) {
+            cssText += s.textContent + '\n\n';
+          }
+        } else {
+          cssText = style.textContent;
+        }
+        this.installScopeCssText(cssText, scope, name);
+      }
+    },
+    installScopeCssText: function(cssText, scope, name) {
+      scope = scope || this.findStyleScope();
+      name = name || '';
+      if (!scope) {
+        return;
+      }
+      if (hasShadowDOMPolyfill) {
+        cssText = shimCssText(cssText, scope.host);
+      }
+      var style = this.element.cssTextToScopeStyle(cssText,
+          STYLE_CONTROLLER_SCOPE);
+      Polymer.applyStyleToScope(style, scope);
+      // cache that this style has been applied
+      this.styleCacheForScope(scope)[this.localName + name] = true;
+    },
+    findStyleScope: function(node) {
+      // find the shadow root that contains this element
+      var n = node || this;
+      while (n.parentNode) {
+        n = n.parentNode;
+      }
+      return n;
+    },
+    scopeHasNamedStyle: function(scope, name) {
+      var cache = this.styleCacheForScope(scope);
+      return cache[name];
+    },
+    styleCacheForScope: function(scope) {
+      if (hasShadowDOMPolyfill) {
+        var scopeName = scope.host ? scope.host.localName : scope.localName;
+        return polyfillScopeStyleCache[scopeName] || (polyfillScopeStyleCache[scopeName] = {});
+      } else {
+        return scope._scopeStyles = (scope._scopeStyles || {});
+      }
+    }
+  };
+
+  var polyfillScopeStyleCache = {};
+  
+  // NOTE: use raw prototype traversal so that we ensure correct traversal
+  // on platforms where the protoype chain is simulated via __proto__ (IE10)
+  function getPrototypeOf(prototype) {
+    return prototype.__proto__;
+  }
+
+  function shimCssText(cssText, host) {
+    var name = '', is = false;
+    if (host) {
+      name = host.localName;
+      is = host.hasAttribute('is');
+    }
+    var selector = WebComponents.ShadowCSS.makeScopeSelector(name, is);
+    return WebComponents.ShadowCSS.shimCssText(cssText, selector);
+  }
+
+  // exports
+
+  scope.api.instance.styles = styles;
+  
+})(Polymer);
+
+(function(scope) {
+
+  // imports
+
+  var extend = scope.extend;
+  var api = scope.api;
+
+  // imperative implementation: Polymer()
+
+  // specify an 'own' prototype for tag `name`
+  function element(name, prototype) {
+    if (typeof name !== 'string') {
+      var script = prototype || document._currentScript;
+      prototype = name;
+      name = script && script.parentNode && script.parentNode.getAttribute ?
+          script.parentNode.getAttribute('name') : '';
+      if (!name) {
+        throw 'Element name could not be inferred.';
+      }
+    }
+    if (getRegisteredPrototype(name)) {
+      throw 'Already registered (Polymer) prototype for element ' + name;
+    }
+    // cache the prototype
+    registerPrototype(name, prototype);
+    // notify the registrar waiting for 'name', if any
+    notifyPrototype(name);
+  }
+
+  // async prototype source
+
+  function waitingForPrototype(name, client) {
+    waitPrototype[name] = client;
+  }
+
+  var waitPrototype = {};
+
+  function notifyPrototype(name) {
+    if (waitPrototype[name]) {
+      waitPrototype[name].registerWhenReady();
+      delete waitPrototype[name];
+    }
+  }
+
+  // utility and bookkeeping
+
+  // maps tag names to prototypes, as registered with
+  // Polymer. Prototypes associated with a tag name
+  // using document.registerElement are available from
+  // HTMLElement.getPrototypeForTag().
+  // If an element was fully registered by Polymer, then
+  // Polymer.getRegisteredPrototype(name) === 
+  //   HTMLElement.getPrototypeForTag(name)
+
+  var prototypesByName = {};
+
+  function registerPrototype(name, prototype) {
+    return prototypesByName[name] = prototype || {};
+  }
+
+  function getRegisteredPrototype(name) {
+    return prototypesByName[name];
+  }
+
+  function instanceOfType(element, type) {
+    if (typeof type !== 'string') {
+      return false;
+    }
+    var proto = HTMLElement.getPrototypeForTag(type);
+    var ctor = proto && proto.constructor;
+    if (!ctor) {
+      return false;
+    }
+    if (CustomElements.instanceof) {
+      return CustomElements.instanceof(element, ctor);
+    }
+    return element instanceof ctor;
+  }
+
+  // exports
+
+  scope.getRegisteredPrototype = getRegisteredPrototype;
+  scope.waitingForPrototype = waitingForPrototype;
+  scope.instanceOfType = instanceOfType;
+
+  // namespace shenanigans so we can expose our scope on the registration 
+  // function
+
+  // make window.Polymer reference `element()`
+
+  window.Polymer = element;
+
+  // TODO(sjmiles): find a way to do this that is less terrible
+  // copy window.Polymer properties onto `element()`
+
+  extend(Polymer, scope);
+
+  // Under the HTMLImports polyfill, scripts in the main document
+  // do not block on imports; we want to allow calls to Polymer in the main
+  // document. WebComponents collects those calls until we can process them, which
+  // we do here.
+
+  if (WebComponents.consumeDeclarations) {
+    WebComponents.consumeDeclarations(function(declarations) {
+      if (declarations) {
+        for (var i=0, l=declarations.length, d; (i<l) && (d=declarations[i]); i++) {
+          element.apply(null, d);
+        }
+      }
+    });
+  }
+
+})(Polymer);
+
+(function(scope) {
+
+/**
+ * @class polymer-base
+ */
+
+ /**
+  * Resolve a url path to be relative to a `base` url. If unspecified, `base`
+  * defaults to the element's ownerDocument url. Can be used to resolve
+  * paths from element's in templates loaded in HTMLImports to be relative
+  * to the document containing the element. Polymer automatically does this for
+  * url attributes in element templates; however, if a url, for
+  * example, contains a binding, then `resolvePath` can be used to ensure it is 
+  * relative to the element document. For example, in an element's template,
+  *
+  *     <a href="{{resolvePath(path)}}">Resolved</a>
+  * 
+  * @method resolvePath
+  * @param {String} url Url path to resolve.
+  * @param {String} base Optional base url against which to resolve, defaults
+  * to the element's ownerDocument url.
+  * returns {String} resolved url.
+  */
+
+var path = {
+  resolveElementPaths: function(node) {
+    Polymer.urlResolver.resolveDom(node);
+  },
+  addResolvePathApi: function() {
+    // let assetpath attribute modify the resolve path
+    var assetPath = this.getAttribute('assetpath') || '';
+    var root = new URL(assetPath, this.ownerDocument.baseURI);
+    this.prototype.resolvePath = function(urlPath, base) {
+      var u = new URL(urlPath, base || root);
+      return u.href;
+    };
+  }
+};
+
+// exports
+scope.api.declaration.path = path;
+
+})(Polymer);
+
+(function(scope) {
+
+  // imports
+
+  var log = window.WebComponents ? WebComponents.flags.log : {};
+  var api = scope.api.instance.styles;
+  var STYLE_SCOPE_ATTRIBUTE = api.STYLE_SCOPE_ATTRIBUTE;
+
+  var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
+
+  // magic words
+
+  var STYLE_SELECTOR = 'style';
+  var STYLE_LOADABLE_MATCH = '@import';
+  var SHEET_SELECTOR = 'link[rel=stylesheet]';
+  var STYLE_GLOBAL_SCOPE = 'global';
+  var SCOPE_ATTR = 'polymer-scope';
+
+  var styles = {
+    // returns true if resources are loading
+    loadStyles: function(callback) {
+      var template = this.fetchTemplate();
+      var content = template && this.templateContent();
+      if (content) {
+        this.convertSheetsToStyles(content);
+        var styles = this.findLoadableStyles(content);
+        if (styles.length) {
+          var templateUrl = template.ownerDocument.baseURI;
+          return Polymer.styleResolver.loadStyles(styles, templateUrl, callback);
+        }
+      }
+      if (callback) {
+        callback();
+      }
+    },
+    convertSheetsToStyles: function(root) {
+      var s$ = root.querySelectorAll(SHEET_SELECTOR);
+      for (var i=0, l=s$.length, s, c; (i<l) && (s=s$[i]); i++) {
+        c = createStyleElement(importRuleForSheet(s, this.ownerDocument.baseURI),
+            this.ownerDocument);
+        this.copySheetAttributes(c, s);
+        s.parentNode.replaceChild(c, s);
+      }
+    },
+    copySheetAttributes: function(style, link) {
+      for (var i=0, a$=link.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++) {
+        if (a.name !== 'rel' && a.name !== 'href') {
+          style.setAttribute(a.name, a.value);
+        }
+      }
+    },
+    findLoadableStyles: function(root) {
+      var loadables = [];
+      if (root) {
+        var s$ = root.querySelectorAll(STYLE_SELECTOR);
+        for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) {
+          if (s.textContent.match(STYLE_LOADABLE_MATCH)) {
+            loadables.push(s);
+          }
+        }
+      }
+      return loadables;
+    },
+    /**
+     * Install external stylesheets loaded in <polymer-element> elements into the 
+     * element's template.
+     * @param elementElement The <element> element to style.
+     */
+    installSheets: function() {
+      this.cacheSheets();
+      this.cacheStyles();
+      this.installLocalSheets();
+      this.installGlobalStyles();
+    },
+    /**
+     * Remove all sheets from element and store for later use.
+     */
+    cacheSheets: function() {
+      this.sheets = this.findNodes(SHEET_SELECTOR);
+      this.sheets.forEach(function(s) {
+        if (s.parentNode) {
+          s.parentNode.removeChild(s);
+        }
+      });
+    },
+    cacheStyles: function() {
+      this.styles = this.findNodes(STYLE_SELECTOR + '[' + SCOPE_ATTR + ']');
+      this.styles.forEach(function(s) {
+        if (s.parentNode) {
+          s.parentNode.removeChild(s);
+        }
+      });
+    },
+    /**
+     * Takes external stylesheets loaded in an <element> element and moves
+     * their content into a <style> element inside the <element>'s template.
+     * The sheet is then removed from the <element>. This is done only so 
+     * that if the element is loaded in the main document, the sheet does
+     * not become active.
+     * Note, ignores sheets with the attribute 'polymer-scope'.
+     * @param elementElement The <element> element to style.
+     */
+    installLocalSheets: function () {
+      var sheets = this.sheets.filter(function(s) {
+        return !s.hasAttribute(SCOPE_ATTR);
+      });
+      var content = this.templateContent();
+      if (content) {
+        var cssText = '';
+        sheets.forEach(function(sheet) {
+          cssText += cssTextFromSheet(sheet) + '\n';
+        });
+        if (cssText) {
+          var style = createStyleElement(cssText, this.ownerDocument);
+          content.insertBefore(style, content.firstChild);
+        }
+      }
+    },
+    findNodes: function(selector, matcher) {
+      var nodes = this.querySelectorAll(selector).array();
+      var content = this.templateContent();
+      if (content) {
+        var templateNodes = content.querySelectorAll(selector).array();
+        nodes = nodes.concat(templateNodes);
+      }
+      return matcher ? nodes.filter(matcher) : nodes;
+    },
+    /**
+     * Promotes external stylesheets and <style> elements with the attribute 
+     * polymer-scope='global' into global scope.
+     * This is particularly useful for defining @keyframe rules which 
+     * currently do not function in scoped or shadow style elements.
+     * (See wkb.ug/72462)
+     * @param elementElement The <element> element to style.
+    */
+    // TODO(sorvell): remove when wkb.ug/72462 is addressed.
+    installGlobalStyles: function() {
+      var style = this.styleForScope(STYLE_GLOBAL_SCOPE);
+      applyStyleToScope(style, document.head);
+    },
+    cssTextForScope: function(scopeDescriptor) {
+      var cssText = '';
+      // handle stylesheets
+      var selector = '[' + SCOPE_ATTR + '=' + scopeDescriptor + ']';
+      var matcher = function(s) {
+        return matchesSelector(s, selector);
+      };
+      var sheets = this.sheets.filter(matcher);
+      sheets.forEach(function(sheet) {
+        cssText += cssTextFromSheet(sheet) + '\n\n';
+      });
+      // handle cached style elements
+      var styles = this.styles.filter(matcher);
+      styles.forEach(function(style) {
+        cssText += style.textContent + '\n\n';
+      });
+      return cssText;
+    },
+    styleForScope: function(scopeDescriptor) {
+      var cssText = this.cssTextForScope(scopeDescriptor);
+      return this.cssTextToScopeStyle(cssText, scopeDescriptor);
+    },
+    cssTextToScopeStyle: function(cssText, scopeDescriptor) {
+      if (cssText) {
+        var style = createStyleElement(cssText);
+        style.setAttribute(STYLE_SCOPE_ATTRIBUTE, this.getAttribute('name') +
+            '-' + scopeDescriptor);
+        return style;
+      }
+    }
+  };
+
+  function importRuleForSheet(sheet, baseUrl) {
+    var href = new URL(sheet.getAttribute('href'), baseUrl).href;
+    return '@import \'' + href + '\';';
+  }
+
+  function applyStyleToScope(style, scope) {
+    if (style) {
+      if (scope === document) {
+        scope = document.head;
+      }
+      if (hasShadowDOMPolyfill) {
+        scope = document.head;
+      }
+      // TODO(sorvell): necessary for IE
+      // see https://connect.microsoft.com/IE/feedback/details/790212/
+      // cloning-a-style-element-and-adding-to-document-produces
+      // -unexpected-result#details
+      // var clone = style.cloneNode(true);
+      var clone = createStyleElement(style.textContent);
+      var attr = style.getAttribute(STYLE_SCOPE_ATTRIBUTE);
+      if (attr) {
+        clone.setAttribute(STYLE_SCOPE_ATTRIBUTE, attr);
+      }
+      // TODO(sorvell): probably too brittle; try to figure out 
+      // where to put the element.
+      var refNode = scope.firstElementChild;
+      if (scope === document.head) {
+        var selector = 'style[' + STYLE_SCOPE_ATTRIBUTE + ']';
+        var s$ = document.head.querySelectorAll(selector);
+        if (s$.length) {
+          refNode = s$[s$.length-1].nextElementSibling;
+        }
+      }
+      scope.insertBefore(clone, refNode);
+    }
+  }
+
+  function createStyleElement(cssText, scope) {
+    scope = scope || document;
+    scope = scope.createElement ? scope : scope.ownerDocument;
+    var style = scope.createElement('style');
+    style.textContent = cssText;
+    return style;
+  }
+
+  function cssTextFromSheet(sheet) {
+    return (sheet && sheet.__resource) || '';
+  }
+
+  function matchesSelector(node, inSelector) {
+    if (matches) {
+      return matches.call(node, inSelector);
+    }
+  }
+  var p = HTMLElement.prototype;
+  var matches = p.matches || p.matchesSelector || p.webkitMatchesSelector 
+      || p.mozMatchesSelector;
+  
+  // exports
+
+  scope.api.declaration.styles = styles;
+  scope.applyStyleToScope = applyStyleToScope;
+  
+})(Polymer);
+
+(function(scope) {
+
+  // imports
+
+  var log = window.WebComponents ? WebComponents.flags.log : {};
+  var api = scope.api.instance.events;
+  var EVENT_PREFIX = api.EVENT_PREFIX;
+
+  var mixedCaseEventTypes = {};
+  [
+    'webkitAnimationStart',
+    'webkitAnimationEnd',
+    'webkitTransitionEnd',
+    'DOMFocusOut',
+    'DOMFocusIn',
+    'DOMMouseScroll'
+  ].forEach(function(e) {
+    mixedCaseEventTypes[e.toLowerCase()] = e;
+  });
+
+  // polymer-element declarative api: events feature
+  var events = {
+    parseHostEvents: function() {
+      // our delegates map
+      var delegates = this.prototype.eventDelegates;
+      // extract data from attributes into delegates
+      this.addAttributeDelegates(delegates);
+    },
+    addAttributeDelegates: function(delegates) {
+      // for each attribute
+      for (var i=0, a; a=this.attributes[i]; i++) {
+        // does it have magic marker identifying it as an event delegate?
+        if (this.hasEventPrefix(a.name)) {
+          // if so, add the info to delegates
+          delegates[this.removeEventPrefix(a.name)] = a.value.replace('{{', '')
+              .replace('}}', '').trim();
+        }
+      }
+    },
+    // starts with 'on-'
+    hasEventPrefix: function (n) {
+      return n && (n[0] === 'o') && (n[1] === 'n') && (n[2] === '-');
+    },
+    removeEventPrefix: function(n) {
+      return n.slice(prefixLength);
+    },
+    findController: function(node) {
+      while (node.parentNode) {
+        if (node.eventController) {
+          return node.eventController;
+        }
+        node = node.parentNode;
+      }
+      return node.host;
+    },
+    getEventHandler: function(controller, target, method) {
+      var events = this;
+      return function(e) {
+        if (!controller || !controller.PolymerBase) {
+          controller = events.findController(target);
+        }
+
+        var args = [e, e.detail, e.currentTarget];
+        controller.dispatchMethod(controller, method, args);
+      };
+    },
+    prepareEventBinding: function(pathString, name, node) {
+      if (!this.hasEventPrefix(name))
+        return;
+
+      var eventType = this.removeEventPrefix(name);
+      eventType = mixedCaseEventTypes[eventType] || eventType;
+
+      var events = this;
+
+      return function(model, node, oneTime) {
+        var handler = events.getEventHandler(undefined, node, pathString);
+        PolymerGestures.addEventListener(node, eventType, handler);
+
+        if (oneTime)
+          return;
+
+        // TODO(rafaelw): This is really pointless work. Aside from the cost
+        // of these allocations, NodeBind is going to setAttribute back to its
+        // current value. Fixing this would mean changing the TemplateBinding
+        // binding delegate API.
+        function bindingValue() {
+          return '{{ ' + pathString + ' }}';
+        }
+
+        return {
+          open: bindingValue,
+          discardChanges: bindingValue,
+          close: function() {
+            PolymerGestures.removeEventListener(node, eventType, handler);
+          }
+        };
+      };
+    }
+  };
+
+  var prefixLength = EVENT_PREFIX.length;
+
+  // exports
+  scope.api.declaration.events = events;
+
+})(Polymer);
+
+(function(scope) {
+
+  // element api
+
+  var observationBlacklist = ['attribute'];
+
+  var properties = {
+    inferObservers: function(prototype) {
+      // called before prototype.observe is chained to inherited object
+      var observe = prototype.observe, property;
+      for (var n in prototype) {
+        if (n.slice(-7) === 'Changed') {
+          property = n.slice(0, -7);
+          if (this.canObserveProperty(property)) {
+            if (!observe) {
+              observe  = (prototype.observe = {});
+            }
+            observe[property] = observe[property] || n;
+          }
+        }
+      }
+    },
+    canObserveProperty: function(property) {
+      return (observationBlacklist.indexOf(property) < 0);
+    },
+    explodeObservers: function(prototype) {
+      // called before prototype.observe is chained to inherited object
+      var o = prototype.observe;
+      if (o) {
+        var exploded = {};
+        for (var n in o) {
+          var names = n.split(' ');
+          for (var i=0, ni; ni=names[i]; i++) {
+            exploded[ni] = o[n];
+          }
+        }
+        prototype.observe = exploded;
+      }
+    },
+    optimizePropertyMaps: function(prototype) {
+      if (prototype.observe) {
+        // construct name list
+        var a = prototype._observeNames = [];
+        for (var n in prototype.observe) {
+          var names = n.split(' ');
+          for (var i=0, ni; ni=names[i]; i++) {
+            a.push(ni);
+          }
+        }
+      }
+      if (prototype.publish) {
+        // construct name list
+        var a = prototype._publishNames = [];
+        for (var n in prototype.publish) {
+          a.push(n);
+        }
+      }
+      if (prototype.computed) {
+        // construct name list
+        var a = prototype._computedNames = [];
+        for (var n in prototype.computed) {
+          a.push(n);
+        }
+      }
+    },
+    publishProperties: function(prototype, base) {
+      // if we have any properties to publish
+      var publish = prototype.publish;
+      if (publish) {
+        // transcribe `publish` entries onto own prototype
+        this.requireProperties(publish, prototype, base);
+        // warn and remove accessor names that are broken on some browsers
+        this.filterInvalidAccessorNames(publish);
+        // construct map of lower-cased property names
+        prototype._publishLC = this.lowerCaseMap(publish);
+      }
+      var computed = prototype.computed;
+      if (computed) {
+        // warn and remove accessor names that are broken on some browsers
+        this.filterInvalidAccessorNames(computed);
+      }
+    },
+    // Publishing/computing a property where the name might conflict with a
+    // browser property is not currently supported to help users of Polymer
+    // avoid browser bugs:
+    //
+    // https://code.google.com/p/chromium/issues/detail?id=43394
+    // https://bugs.webkit.org/show_bug.cgi?id=49739
+    //
+    // We can lift this restriction when those bugs are fixed.
+    filterInvalidAccessorNames: function(propertyNames) {
+      for (var name in propertyNames) {
+        // Check if the name is in our blacklist.
+        if (this.propertyNameBlacklist[name]) {
+          console.warn('Cannot define property "' + name + '" for element "' +
+            this.name + '" because it has the same name as an HTMLElement ' +
+            'property, and not all browsers support overriding that. ' +
+            'Consider giving it a different name.');
+          // Remove the invalid accessor from the list.
+          delete propertyNames[name];
+        }
+      }
+    },
+    //
+    // `name: value` entries in the `publish` object may need to generate 
+    // matching properties on the prototype.
+    //
+    // Values that are objects may have a `reflect` property, which
+    // signals that the value describes property control metadata.
+    // In metadata objects, the prototype default value (if any)
+    // is encoded in the `value` property.
+    //
+    // publish: {
+    //   foo: 5, 
+    //   bar: {value: true, reflect: true},
+    //   zot: {}
+    // }
+    //
+    // `reflect` metadata property controls whether changes to the property
+    // are reflected back to the attribute (default false). 
+    //
+    // A value is stored on the prototype unless it's === `undefined`,
+    // in which case the base chain is checked for a value.
+    // If the basal value is also undefined, `null` is stored on the prototype.
+    //
+    // The reflection data is stored on another prototype object, `reflect`
+    // which also can be specified directly.
+    //
+    // reflect: {
+    //   foo: true
+    // }
+    //
+    requireProperties: function(propertyInfos, prototype, base) {
+      // per-prototype storage for reflected properties
+      prototype.reflect = prototype.reflect || {};
+      // ensure a prototype value for each property
+      // and update the property's reflect to attribute status
+      for (var n in propertyInfos) {
+        var value = propertyInfos[n];
+        // value has metadata if it has a `reflect` property
+        if (value && value.reflect !== undefined) {
+          prototype.reflect[n] = Boolean(value.reflect);
+          value = value.value;
+        }
+        // only set a value if one is specified
+        if (value !== undefined) {
+          prototype[n] = value;
+        }
+      }
+    },
+    lowerCaseMap: function(properties) {
+      var map = {};
+      for (var n in properties) {
+        map[n.toLowerCase()] = n;
+      }
+      return map;
+    },
+    createPropertyAccessor: function(name, ignoreWrites) {
+      var proto = this.prototype;
+
+      var privateName = name + '_';
+      var privateObservable  = name + 'Observable_';
+      proto[privateName] = proto[name];
+
+      Object.defineProperty(proto, name, {
+        get: function() {
+          var observable = this[privateObservable];
+          if (observable)
+            observable.deliver();
+
+          return this[privateName];
+        },
+        set: function(value) {
+          if (ignoreWrites) {
+            return this[privateName];
+          }
+
+          var observable = this[privateObservable];
+          if (observable) {
+            observable.setValue(value);
+            return;
+          }
+
+          var oldValue = this[privateName];
+          this[privateName] = value;
+          this.emitPropertyChangeRecord(name, value, oldValue);
+
+          return value;
+        },
+        configurable: true
+      });
+    },
+    createPropertyAccessors: function(prototype) {
+      var n$ = prototype._computedNames;
+      if (n$ && n$.length) {
+        for (var i=0, l=n$.length, n, fn; (i<l) && (n=n$[i]); i++) {
+          this.createPropertyAccessor(n, true);
+        }
+      }
+      var n$ = prototype._publishNames;
+      if (n$ && n$.length) {
+        for (var i=0, l=n$.length, n, fn; (i<l) && (n=n$[i]); i++) {
+          // If the property is computed and published, the accessor is created
+          // above.
+          if (!prototype.computed || !prototype.computed[n]) {
+            this.createPropertyAccessor(n);
+          }
+        }
+      }
+    },
+    // This list contains some property names that people commonly want to use,
+    // but won't work because of Chrome/Safari bugs. It isn't an exhaustive
+    // list. In particular it doesn't contain any property names found on
+    // subtypes of HTMLElement (e.g. name, value). Rather it attempts to catch
+    // some common cases.
+    propertyNameBlacklist: {
+      children: 1,
+      'class': 1,
+      id: 1,
+      hidden: 1,
+      style: 1,
+      title: 1,
+    }
+  };
+
+  // exports
+
+  scope.api.declaration.properties = properties;
+
+})(Polymer);
+
+(function(scope) {
+
+  // magic words
+
+  var ATTRIBUTES_ATTRIBUTE = 'attributes';
+  var ATTRIBUTES_REGEX = /\s|,/;
+
+  // attributes api
+
+  var attributes = {
+    
+    inheritAttributesObjects: function(prototype) {
+      // chain our lower-cased publish map to the inherited version
+      this.inheritObject(prototype, 'publishLC');
+      // chain our instance attributes map to the inherited version
+      this.inheritObject(prototype, '_instanceAttributes');
+    },
+
+    publishAttributes: function(prototype, base) {
+      // merge names from 'attributes' attribute into the 'publish' object
+      var attributes = this.getAttribute(ATTRIBUTES_ATTRIBUTE);
+      if (attributes) {
+        // create a `publish` object if needed.
+        // the `publish` object is only relevant to this prototype, the 
+        // publishing logic in `declaration/properties.js` is responsible for
+        // managing property values on the prototype chain.
+        // TODO(sjmiles): the `publish` object is later chained to it's 
+        //                ancestor object, presumably this is only for 
+        //                reflection or other non-library uses. 
+        var publish = prototype.publish || (prototype.publish = {}); 
+        // names='a b c' or names='a,b,c'
+        var names = attributes.split(ATTRIBUTES_REGEX);
+        // record each name for publishing
+        for (var i=0, l=names.length, n; i<l; i++) {
+          // remove excess ws
+          n = names[i].trim();
+          // looks weird, but causes n to exist on `publish` if it does not;
+          // a more careful test would need expensive `in` operator
+          if (n && publish[n] === undefined) {
+            publish[n] = undefined;
+          }
+        }
+      }
+    },
+
+    // record clonable attributes from <element>
+    accumulateInstanceAttributes: function() {
+      // inherit instance attributes
+      var clonable = this.prototype._instanceAttributes;
+      // merge attributes from element
+      var a$ = this.attributes;
+      for (var i=0, l=a$.length, a; (i<l) && (a=a$[i]); i++) {  
+        if (this.isInstanceAttribute(a.name)) {
+          clonable[a.name] = a.value;
+        }
+      }
+    },
+
+    isInstanceAttribute: function(name) {
+      return !this.blackList[name] && name.slice(0,3) !== 'on-';
+    },
+
+    // do not clone these attributes onto instances
+    blackList: {
+      name: 1,
+      'extends': 1,
+      constructor: 1,
+      noscript: 1,
+      assetpath: 1,
+      'cache-csstext': 1
+    }
+    
+  };
+
+  // add ATTRIBUTES_ATTRIBUTE to the blacklist
+  attributes.blackList[ATTRIBUTES_ATTRIBUTE] = 1;
+
+  // exports
+
+  scope.api.declaration.attributes = attributes;
+
+})(Polymer);
+
+(function(scope) {
+
+  // imports
+  var events = scope.api.declaration.events;
+
+  var syntax = new PolymerExpressions();
+  var prepareBinding = syntax.prepareBinding;
+
+  // Polymer takes a first crack at the binding to see if it's a declarative
+  // event handler.
+  syntax.prepareBinding = function(pathString, name, node) {
+    return events.prepareEventBinding(pathString, name, node) ||
+           prepareBinding.call(syntax, pathString, name, node);
+  };
+
+  // declaration api supporting mdv
+  var mdv = {
+    syntax: syntax,
+    fetchTemplate: function() {
+      return this.querySelector('template');
+    },
+    templateContent: function() {
+      var template = this.fetchTemplate();
+      return template && template.content;
+    },
+    installBindingDelegate: function(template) {
+      if (template) {
+        template.bindingDelegate = this.syntax;
+      }
+    }
+  };
+
+  // exports
+  scope.api.declaration.mdv = mdv;
+
+})(Polymer);
+
+(function(scope) {
+
+  // imports
+  
+  var api = scope.api;
+  var isBase = scope.isBase;
+  var extend = scope.extend;
+
+  var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
+
+  // prototype api
+
+  var prototype = {
+
+    register: function(name, extendeeName) {
+      // build prototype combining extendee, Polymer base, and named api
+      this.buildPrototype(name, extendeeName);
+      // register our custom element with the platform
+      this.registerPrototype(name, extendeeName);
+      // reference constructor in a global named by 'constructor' attribute
+      this.publishConstructor();
+    },
+
+    buildPrototype: function(name, extendeeName) {
+      // get our custom prototype (before chaining)
+      var extension = scope.getRegisteredPrototype(name);
+      // get basal prototype
+      var base = this.generateBasePrototype(extendeeName);
+      // implement declarative features
+      this.desugarBeforeChaining(extension, base);
+      // join prototypes
+      this.prototype = this.chainPrototypes(extension, base);
+      // more declarative features
+      this.desugarAfterChaining(name, extendeeName);
+    },
+
+    desugarBeforeChaining: function(prototype, base) {
+      // back reference declaration element
+      // TODO(sjmiles): replace `element` with `elementElement` or `declaration`
+      prototype.element = this;
+      // transcribe `attributes` declarations onto own prototype's `publish`
+      this.publishAttributes(prototype, base);
+      // `publish` properties to the prototype and to attribute watch
+      this.publishProperties(prototype, base);
+      // infer observers for `observe` list based on method names
+      this.inferObservers(prototype);
+      // desugar compound observer syntax, e.g. 'a b c' 
+      this.explodeObservers(prototype);
+    },
+
+    chainPrototypes: function(prototype, base) {
+      // chain various meta-data objects to inherited versions
+      this.inheritMetaData(prototype, base);
+      // chain custom api to inherited
+      var chained = this.chainObject(prototype, base);
+      // x-platform fixup
+      ensurePrototypeTraversal(chained);
+      return chained;
+    },
+
+    inheritMetaData: function(prototype, base) {
+      // chain observe object to inherited
+      this.inheritObject('observe', prototype, base);
+      // chain publish object to inherited
+      this.inheritObject('publish', prototype, base);
+      // chain reflect object to inherited
+      this.inheritObject('reflect', prototype, base);
+      // chain our lower-cased publish map to the inherited version
+      this.inheritObject('_publishLC', prototype, base);
+      // chain our instance attributes map to the inherited version
+      this.inheritObject('_instanceAttributes', prototype, base);
+      // chain our event delegates map to the inherited version
+      this.inheritObject('eventDelegates', prototype, base);
+    },
+
+    // implement various declarative features
+    desugarAfterChaining: function(name, extendee) {
+      // build side-chained lists to optimize iterations
+      this.optimizePropertyMaps(this.prototype);
+      this.createPropertyAccessors(this.prototype);
+      // install mdv delegate on template
+      this.installBindingDelegate(this.fetchTemplate());
+      // install external stylesheets as if they are inline
+      this.installSheets();
+      // adjust any paths in dom from imports
+      this.resolveElementPaths(this);
+      // compile list of attributes to copy to instances
+      this.accumulateInstanceAttributes();
+      // parse on-* delegates declared on `this` element
+      this.parseHostEvents();
+      //
+      // install a helper method this.resolvePath to aid in 
+      // setting resource urls. e.g.
+      // this.$.image.src = this.resolvePath('images/foo.png')
+      this.addResolvePathApi();
+      // under ShadowDOMPolyfill, transforms to approximate missing CSS features
+      if (hasShadowDOMPolyfill) {
+        WebComponents.ShadowCSS.shimStyling(this.templateContent(), name,
+          extendee);
+      }
+      // allow custom element access to the declarative context
+      if (this.prototype.registerCallback) {
+        this.prototype.registerCallback(this);
+      }
+    },
+
+    // if a named constructor is requested in element, map a reference
+    // to the constructor to the given symbol
+    publishConstructor: function() {
+      var symbol = this.getAttribute('constructor');
+      if (symbol) {
+        window[symbol] = this.ctor;
+      }
+    },
+
+    // build prototype combining extendee, Polymer base, and named api
+    generateBasePrototype: function(extnds) {
+      var prototype = this.findBasePrototype(extnds);
+      if (!prototype) {
+        // create a prototype based on tag-name extension
+        var prototype = HTMLElement.getPrototypeForTag(extnds);
+        // insert base api in inheritance chain (if needed)
+        prototype = this.ensureBaseApi(prototype);
+        // memoize this base
+        memoizedBases[extnds] = prototype;
+      }
+      return prototype;
+    },
+
+    findBasePrototype: function(name) {
+      return memoizedBases[name];
+    },
+
+    // install Polymer instance api into prototype chain, as needed 
+    ensureBaseApi: function(prototype) {
+      if (prototype.PolymerBase) {
+        return prototype;
+      }
+      var extended = Object.create(prototype);
+      // we need a unique copy of base api for each base prototype
+      // therefore we 'extend' here instead of simply chaining
+      api.publish(api.instance, extended);
+      // TODO(sjmiles): sharing methods across prototype chains is
+      // not supported by 'super' implementation which optimizes
+      // by memoizing prototype relationships.
+      // Probably we should have a version of 'extend' that is 
+      // share-aware: it could study the text of each function,
+      // look for usage of 'super', and wrap those functions in
+      // closures.
+      // As of now, there is only one problematic method, so 
+      // we just patch it manually.
+      // To avoid re-entrancy problems, the special super method
+      // installed is called `mixinSuper` and the mixin method
+      // must use this method instead of the default `super`.
+      this.mixinMethod(extended, prototype, api.instance.mdv, 'bind');
+      // return buffed-up prototype
+      return extended;
+    },
+
+    mixinMethod: function(extended, prototype, api, name) {
+      var $super = function(args) {
+        return prototype[name].apply(this, args);
+      };
+      extended[name] = function() {
+        this.mixinSuper = $super;
+        return api[name].apply(this, arguments);
+      }
+    },
+
+    // ensure prototype[name] inherits from a prototype.prototype[name]
+    inheritObject: function(name, prototype, base) {
+      // require an object
+      var source = prototype[name] || {};
+      // chain inherited properties onto a new object
+      prototype[name] = this.chainObject(source, base[name]);
+    },
+
+    // register 'prototype' to custom element 'name', store constructor 
+    registerPrototype: function(name, extendee) { 
+      var info = {
+        prototype: this.prototype
+      }
+      // native element must be specified in extends
+      var typeExtension = this.findTypeExtension(extendee);
+      if (typeExtension) {
+        info.extends = typeExtension;
+      }
+      // register the prototype with HTMLElement for name lookup
+      HTMLElement.register(name, this.prototype);
+      // register the custom type
+      this.ctor = document.registerElement(name, info);
+    },
+
+    findTypeExtension: function(name) {
+      if (name && name.indexOf('-') < 0) {
+        return name;
+      } else {
+        var p = this.findBasePrototype(name);
+        if (p.element) {
+          return this.findTypeExtension(p.element.extends);
+        }
+      }
+    }
+
+  };
+
+  // memoize base prototypes
+  var memoizedBases = {};
+
+  // implementation of 'chainObject' depends on support for __proto__
+  if (Object.__proto__) {
+    prototype.chainObject = function(object, inherited) {
+      if (object && inherited && object !== inherited) {
+        object.__proto__ = inherited;
+      }
+      return object;
+    }
+  } else {
+    prototype.chainObject = function(object, inherited) {
+      if (object && inherited && object !== inherited) {
+        var chained = Object.create(inherited);
+        object = extend(chained, object);
+      }
+      return object;
+    }
+  }
+
+  // On platforms that do not support __proto__ (versions of IE), the prototype
+  // chain of a custom element is simulated via installation of __proto__.
+  // Although custom elements manages this, we install it here so it's
+  // available during desugaring.
+  function ensurePrototypeTraversal(prototype) {
+    if (!Object.__proto__) {
+      var ancestor = Object.getPrototypeOf(prototype);
+      prototype.__proto__ = ancestor;
+      if (isBase(ancestor)) {
+        ancestor.__proto__ = Object.getPrototypeOf(ancestor);
+      }
+    }
+  }
+
+  // exports
+
+  api.declaration.prototype = prototype;
+
+})(Polymer);
+
+(function(scope) {
+
+  /*
+
+    Elements are added to a registration queue so that they register in 
+    the proper order at the appropriate time. We do this for a few reasons:
+
+    * to enable elements to load resources (like stylesheets) 
+    asynchronously. We need to do this until the platform provides an efficient
+    alternative. One issue is that remote @import stylesheets are 
+    re-fetched whenever stamped into a shadowRoot.
+
+    * to ensure elements loaded 'at the same time' (e.g. via some set of
+    imports) are registered as a batch. This allows elements to be enured from
+    upgrade ordering as long as they query the dom tree 1 task after
+    upgrade (aka domReady). This is a performance tradeoff. On the one hand,
+    elements that could register while imports are loading are prevented from 
+    doing so. On the other, grouping upgrades into a single task means less
+    incremental work (for example style recalcs),  Also, we can ensure the 
+    document is in a known state at the single quantum of time when 
+    elements upgrade.
+
+  */
+  var queue = {
+
+    // tell the queue to wait for an element to be ready
+    wait: function(element) {
+      if (!element.__queue) {
+        element.__queue = {};
+        elements.push(element);
+      }
+    },
+
+    // enqueue an element to the next spot in the queue.
+    enqueue: function(element, check, go) {
+      var shouldAdd = element.__queue && !element.__queue.check;
+      if (shouldAdd) {
+        queueForElement(element).push(element);
+        element.__queue.check = check;
+        element.__queue.go = go;
+      }
+      return (this.indexOf(element) !== 0);
+    },
+
+    indexOf: function(element) {
+      var i = queueForElement(element).indexOf(element);
+      if (i >= 0 && document.contains(element)) {
+        i += (HTMLImports.useNative || HTMLImports.ready) ? 
+          importQueue.length : 1e9;
+      }
+      return i;  
+    },
+
+    // tell the queue an element is ready to be registered
+    go: function(element) {
+      var readied = this.remove(element);
+      if (readied) {
+        element.__queue.flushable = true;
+        this.addToFlushQueue(readied);
+        this.check();
+      }
+    },
+
+    remove: function(element) {
+      var i = this.indexOf(element);
+      if (i !== 0) {
+        //console.warn('queue order wrong', i);
+        return;
+      }
+      return queueForElement(element).shift();
+    },
+
+    check: function() {
+      // next
+      var element = this.nextElement();
+      if (element) {
+        element.__queue.check.call(element);
+      }
+      if (this.canReady()) {
+        this.ready();
+        return true;
+      }
+    },
+
+    nextElement: function() {
+      return nextQueued();
+    },
+
+    canReady: function() {
+      return !this.waitToReady && this.isEmpty();
+    },
+
+    isEmpty: function() {
+      for (var i=0, l=elements.length, e; (i<l) && 
+          (e=elements[i]); i++) {
+        if (e.__queue && !e.__queue.flushable) {
+          return;
+        }
+      }
+      return true;
+    },
+
+    addToFlushQueue: function(element) {
+      flushQueue.push(element);  
+    },
+
+    flush: function() {
+      // prevent re-entrance
+      if (this.flushing) {
+        return;
+      }
+      this.flushing = true;
+      var element;
+      while (flushQueue.length) {
+        element = flushQueue.shift();
+        element.__queue.go.call(element);
+        element.__queue = null;
+      }
+      this.flushing = false;
+    },
+
+    ready: function() {
+      // TODO(sorvell): As an optimization, turn off CE polyfill upgrading
+      // while registering. This way we avoid having to upgrade each document
+      // piecemeal per registration and can instead register all elements
+      // and upgrade once in a batch. Without this optimization, upgrade time
+      // degrades significantly when SD polyfill is used. This is mainly because
+      // querying the document tree for elements is slow under the SD polyfill.
+      var polyfillWasReady = CustomElements.ready;
+      CustomElements.ready = false;
+      this.flush();
+      if (!CustomElements.useNative) {
+        CustomElements.upgradeDocumentTree(document);
+      }
+      CustomElements.ready = polyfillWasReady;
+      Polymer.flush();
+      requestAnimationFrame(this.flushReadyCallbacks);
+    },
+
+    addReadyCallback: function(callback) {
+      if (callback) {
+        readyCallbacks.push(callback);
+      }
+    },
+
+    flushReadyCallbacks: function() {
+      if (readyCallbacks) {
+        var fn;
+        while (readyCallbacks.length) {
+          fn = readyCallbacks.shift();
+          fn();
+        }
+      }
+    },
+  
+    /**
+    Returns a list of elements that have had polymer-elements created but 
+    are not yet ready to register. The list is an array of element definitions.
+    */
+    waitingFor: function() {
+      var e$ = [];
+      for (var i=0, l=elements.length, e; (i<l) && 
+          (e=elements[i]); i++) {
+        if (e.__queue && !e.__queue.flushable) {
+          e$.push(e);
+        }
+      }
+      return e$;
+    },
+
+    waitToReady: true
+
+  };
+
+  var elements = [];
+  var flushQueue = [];
+  var importQueue = [];
+  var mainQueue = [];
+  var readyCallbacks = [];
+
+  function queueForElement(element) {
+    return document.contains(element) ? mainQueue : importQueue;
+  }
+
+  function nextQueued() {
+    return importQueue.length ? importQueue[0] : mainQueue[0];
+  }
+
+  function whenReady(callback) {
+    queue.waitToReady = true;
+    Polymer.endOfMicrotask(function() {
+      HTMLImports.whenReady(function() {
+        queue.addReadyCallback(callback);
+        queue.waitToReady = false;
+        queue.check();
+    });
+    });
+  }
+
+  /**
+    Forces polymer to register any pending elements. Can be used to abort
+    waiting for elements that are partially defined.
+    @param timeout {Integer} Optional timeout in milliseconds
+  */
+  function forceReady(timeout) {
+    if (timeout === undefined) {
+      queue.ready();
+      return;
+    }
+    var handle = setTimeout(function() {
+      queue.ready();
+    }, timeout);
+    Polymer.whenReady(function() {
+      clearTimeout(handle);
+    });
+  }
+
+  // exports
+  scope.elements = elements;
+  scope.waitingFor = queue.waitingFor.bind(queue);
+  scope.forceReady = forceReady;
+  scope.queue = queue;
+  scope.whenReady = scope.whenPolymerReady = whenReady;
+})(Polymer);
+
+(function(scope) {
+
+  // imports
+
+  var extend = scope.extend;
+  var api = scope.api;
+  var queue = scope.queue;
+  var whenReady = scope.whenReady;
+  var getRegisteredPrototype = scope.getRegisteredPrototype;
+  var waitingForPrototype = scope.waitingForPrototype;
+
+  // declarative implementation: <polymer-element>
+
+  var prototype = extend(Object.create(HTMLElement.prototype), {
+
+    createdCallback: function() {
+      if (this.getAttribute('name')) {
+        this.init();
+      }
+    },
+
+    init: function() {
+      // fetch declared values
+      this.name = this.getAttribute('name');
+      this.extends = this.getAttribute('extends');
+      queue.wait(this);
+      // initiate any async resource fetches
+      this.loadResources();
+      // register when all constraints are met
+      this.registerWhenReady();
+    },
+
+    // TODO(sorvell): we currently queue in the order the prototypes are 
+    // registered, but we should queue in the order that polymer-elements
+    // are registered. We are currently blocked from doing this based on 
+    // crbug.com/395686.
+    registerWhenReady: function() {
+     if (this.registered
+       || this.waitingForPrototype(this.name)
+       || this.waitingForQueue()
+       || this.waitingForResources()) {
+          return;
+      }
+      queue.go(this);
+    },
+
+    _register: function() {
+      //console.log('registering', this.name);
+      // warn if extending from a custom element not registered via Polymer
+      if (isCustomTag(this.extends) && !isRegistered(this.extends)) {
+        console.warn('%s is attempting to extend %s, an unregistered element ' +
+            'or one that was not registered with Polymer.', this.name,
+            this.extends);
+      }
+      this.register(this.name, this.extends);
+      this.registered = true;
+    },
+
+    waitingForPrototype: function(name) {
+      if (!getRegisteredPrototype(name)) {
+        // then wait for a prototype
+        waitingForPrototype(name, this);
+        // emulate script if user is not supplying one
+        this.handleNoScript(name);
+        // prototype not ready yet
+        return true;
+      }
+    },
+
+    handleNoScript: function(name) {
+      // if explicitly marked as 'noscript'
+      if (this.hasAttribute('noscript') && !this.noscript) {
+        this.noscript = true;
+        // imperative element registration
+        Polymer(name);
+      }
+    },
+
+    waitingForResources: function() {
+      return this._needsResources;
+    },
+
+    // NOTE: Elements must be queued in proper order for inheritance/composition
+    // dependency resolution. Previously this was enforced for inheritance,
+    // and by rule for composition. It's now entirely by rule.
+    waitingForQueue: function() {
+      return queue.enqueue(this, this.registerWhenReady, this._register);
+    },
+
+    loadResources: function() {
+      this._needsResources = true;
+      this.loadStyles(function() {
+        this._needsResources = false;
+        this.registerWhenReady();
+      }.bind(this));
+    }
+
+  });
+
+  // semi-pluggable APIs 
+
+  // TODO(sjmiles): should be fully pluggable (aka decoupled, currently
+  // the various plugins are allowed to depend on each other directly)
+  api.publish(api.declaration, prototype);
+
+  // utility and bookkeeping
+
+  function isRegistered(name) {
+    return Boolean(HTMLElement.getPrototypeForTag(name));
+  }
+
+  function isCustomTag(name) {
+    return (name && name.indexOf('-') >= 0);
+  }
+
+  // boot tasks
+
+  whenReady(function() {
+    document.body.removeAttribute('unresolved');
+    document.dispatchEvent(
+      new CustomEvent('polymer-ready', {bubbles: true})
+    );
+  });
+
+  // register polymer-element with document
+
+  document.registerElement('polymer-element', {prototype: prototype});
+
+})(Polymer);
+
+(function(scope) {
+
+/**
+ * @class Polymer
+ */
+
+var whenReady = scope.whenReady;
+
+/**
+ * Loads the set of HTMLImports contained in `node`. Notifies when all
+ * the imports have loaded by calling the `callback` function argument.
+ * This method can be used to lazily load imports. For example, given a 
+ * template:
+ *     
+ *     <template>
+ *       <link rel="import" href="my-import1.html">
+ *       <link rel="import" href="my-import2.html">
+ *     </template>
+ *
+ *     Polymer.importElements(template.content, function() {
+ *       console.log('imports lazily loaded'); 
+ *     });
+ * 
+ * @method importElements
+ * @param {Node} node Node containing the HTMLImports to load.
+ * @param {Function} callback Callback called when all imports have loaded.
+ */
+function importElements(node, callback) {
+  if (node) {
+    document.head.appendChild(node);
+    whenReady(callback);
+  } else if (callback) {
+    callback();
+  }
+}
+
+/**
+ * Loads an HTMLImport for each url specified in the `urls` array.
+ * Notifies when all the imports have loaded by calling the `callback` 
+ * function argument. This method can be used to lazily load imports. 
+ * For example,
+ *
+ *     Polymer.import(['my-import1.html', 'my-import2.html'], function() {
+ *       console.log('imports lazily loaded'); 
+ *     });
+ * 
+ * @method import
+ * @param {Array} urls Array of urls to load as HTMLImports.
+ * @param {Function} callback Callback called when all imports have loaded.
+ */
+function _import(urls, callback) {
+  if (urls && urls.length) {
+      var frag = document.createDocumentFragment();
+      for (var i=0, l=urls.length, url, link; (i<l) && (url=urls[i]); i++) {
+        link = document.createElement('link');
+        link.rel = 'import';
+        link.href = url;
+        frag.appendChild(link);
+      }
+      importElements(frag, callback);
+  } else if (callback) {
+    callback();
+  }
+}
+
+// exports
+scope.import = _import;
+scope.importElements = importElements;
+
+})(Polymer);
+
+/**
+ * The `auto-binding` element extends the template element. It provides a quick 
+ * and easy way to do data binding without the need to setup a model. 
+ * The `auto-binding` element itself serves as the model and controller for the 
+ * elements it contains. Both data and event handlers can be bound. 
+ *
+ * The `auto-binding` element acts just like a template that is bound to 
+ * a model. It stamps its content in the dom adjacent to itself. When the 
+ * content is stamped, the `template-bound` event is fired.
+ *
+ * Example:
+ *
+ *     <template is="auto-binding">
+ *       <div>Say something: <input value="{{value}}"></div>
+ *       <div>You said: {{value}}</div>
+ *       <button on-tap="{{buttonTap}}">Tap me!</button>
+ *     </template>
+ *     <script>
+ *       var template = document.querySelector('template');
+ *       template.value = 'something';
+ *       template.buttonTap = function() {
+ *         console.log('tap!');
+ *       };
+ *     </script>
+ *
+ * @module Polymer
+ * @status stable
+*/
+
+(function() {
+
+  var element = document.createElement('polymer-element');
+  element.setAttribute('name', 'auto-binding');
+  element.setAttribute('extends', 'template');
+  element.init();
+
+  Polymer('auto-binding', {
+
+    createdCallback: function() {
+      this.syntax = this.bindingDelegate = this.makeSyntax();
+      // delay stamping until polymer-ready so that auto-binding is not
+      // required to load last.
+      Polymer.whenPolymerReady(function() {
+        this.model = this;
+        this.setAttribute('bind', '');
+        // we don't bother with an explicit signal here, we could ust a MO
+        // if necessary
+        this.async(function() {
+          // note: this will marshall *all* the elements in the parentNode
+          // rather than just stamped ones. We'd need to use createInstance
+          // to fix this or something else fancier.
+          this.marshalNodeReferences(this.parentNode);
+          // template stamping is asynchronous so stamping isn't complete
+          // by polymer-ready; fire an event so users can use stamped elements
+          this.fire('template-bound');
+        });
+      }.bind(this));
+    },
+
+    makeSyntax: function() {
+      var events = Object.create(Polymer.api.declaration.events);
+      var self = this;
+      events.findController = function() { return self.model; };
+
+      var syntax = new PolymerExpressions();
+      var prepareBinding = syntax.prepareBinding;  
+      syntax.prepareBinding = function(pathString, name, node) {
+        return events.prepareEventBinding(pathString, name, node) ||
+               prepareBinding.call(syntax, pathString, name, node);
+      };
+      return syntax;
+    }
+
+  });
+
+})();
diff --git a/polymer_interop/lib/src/js/polymer.min.js b/polymer_interop/lib/src/js/polymer.min.js
new file mode 100644
index 0000000..dcd0bd6
--- /dev/null
+++ b/polymer_interop/lib/src/js/polymer.min.js
@@ -0,0 +1,14 @@
+/**
+ * @license
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+ * Code distributed by Google as part of the polymer project is also
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+ */
+// @version 0.5.5
+window.PolymerGestures={},function(a){var b=!1,c=document.createElement("meta");if(c.createShadowRoot){var d=c.createShadowRoot(),e=document.createElement("span");d.appendChild(e),c.addEventListener("testpath",function(a){a.path&&(b=a.path[0]===e),a.stopPropagation()});var f=new CustomEvent("testpath",{bubbles:!0});document.head.appendChild(c),e.dispatchEvent(f),c.parentNode.removeChild(c),d=e=null}c=null;var g={shadow:function(a){return a?a.shadowRoot||a.webkitShadowRoot:void 0},canTarget:function(a){return a&&Boolean(a.elementFromPoint)},targetingShadow:function(a){var b=this.shadow(a);return this.canTarget(b)?b:void 0},olderShadow:function(a){var b=a.olderShadowRoot;if(!b){var c=a.querySelector("shadow");c&&(b=c.olderShadowRoot)}return b},allShadows:function(a){for(var b=[],c=this.shadow(a);c;)b.push(c),c=this.olderShadow(c);return b},searchRoot:function(a,b,c){var d,e;return a?(d=a.elementFromPoint(b,c),d?e=this.targetingShadow(d):a!==document&&(e=this.olderShadow(a)),this.searchRoot(e,b,c)||d):void 0},owner:function(a){if(!a)return document;for(var b=a;b.parentNode;)b=b.parentNode;return b.nodeType!=Node.DOCUMENT_NODE&&b.nodeType!=Node.DOCUMENT_FRAGMENT_NODE&&(b=document),b},findTarget:function(a){if(b&&a.path&&a.path.length)return a.path[0];var c=a.clientX,d=a.clientY,e=this.owner(a.target);return e.elementFromPoint(c,d)||(e=document),this.searchRoot(e,c,d)},findTouchAction:function(a){var c;if(b&&a.path&&a.path.length){for(var d=a.path,e=0;e<d.length;e++)if(c=d[e],c.nodeType===Node.ELEMENT_NODE&&c.hasAttribute("touch-action"))return c.getAttribute("touch-action")}else for(c=a.target;c;){if(c.nodeType===Node.ELEMENT_NODE&&c.hasAttribute("touch-action"))return c.getAttribute("touch-action");c=c.parentNode||c.host}return"auto"},LCA:function(a,b){if(a===b)return a;if(a&&!b)return a;if(b&&!a)return b;if(!b&&!a)return document;if(a.contains&&a.contains(b))return a;if(b.contains&&b.contains(a))return b;var c=this.depth(a),d=this.depth(b),e=c-d;for(e>=0?a=this.walk(a,e):b=this.walk(b,-e);a&&b&&a!==b;)a=a.parentNode||a.host,b=b.parentNode||b.host;return a},walk:function(a,b){for(var c=0;a&&b>c;c++)a=a.parentNode||a.host;return a},depth:function(a){for(var b=0;a;)b++,a=a.parentNode||a.host;return b},deepContains:function(a,b){var c=this.LCA(a,b);return c===a},insideNode:function(a,b,c){var d=a.getBoundingClientRect();return d.left<=b&&b<=d.right&&d.top<=c&&c<=d.bottom},path:function(a){var c;if(b&&a.path&&a.path.length)c=a.path;else{c=[];for(var d=this.findTarget(a);d;)c.push(d),d=d.parentNode||d.host}return c}};a.targetFinding=g,a.findTarget=g.findTarget.bind(g),a.deepContains=g.deepContains.bind(g),a.insideNode=g.insideNode}(window.PolymerGestures),function(){function a(a){return"html /deep/ "+b(a)}function b(a){return'[touch-action="'+a+'"]'}function c(a){return"{ -ms-touch-action: "+a+"; touch-action: "+a+";}"}var d=["none","auto","pan-x","pan-y",{rule:"pan-x pan-y",selectors:["pan-x pan-y","pan-y pan-x"]},"manipulation"],e="",f="string"==typeof document.head.style.touchAction,g=!window.ShadowDOMPolyfill&&document.head.createShadowRoot;if(f){d.forEach(function(d){String(d)===d?(e+=b(d)+c(d)+"\n",g&&(e+=a(d)+c(d)+"\n")):(e+=d.selectors.map(b)+c(d.rule)+"\n",g&&(e+=d.selectors.map(a)+c(d.rule)+"\n"))});var h=document.createElement("style");h.textContent=e,document.head.appendChild(h)}}(),function(a){var b=["bubbles","cancelable","view","detail","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","pageX","pageY"],c=[!1,!1,null,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0],d=function(){return function(){}},e={preventTap:d,makeBaseEvent:function(a,b){var c=document.createEvent("Event");return c.initEvent(a,b.bubbles||!1,b.cancelable||!1),c.preventTap=e.preventTap(c),c},makeGestureEvent:function(a,b){b=b||Object.create(null);for(var c,d=this.makeBaseEvent(a,b),e=0,f=Object.keys(b);e<f.length;e++)c=f[e],"bubbles"!==c&&"cancelable"!==c&&(d[c]=b[c]);return d},makePointerEvent:function(a,d){d=d||Object.create(null);for(var e,f=this.makeBaseEvent(a,d),g=2;g<b.length;g++)e=b[g],f[e]=d[e]||c[g];f.buttons=d.buttons||0;var h=0;return h=d.pressure?d.pressure:f.buttons?.5:0,f.x=f.clientX,f.y=f.clientY,f.pointerId=d.pointerId||0,f.width=d.width||0,f.height=d.height||0,f.pressure=h,f.tiltX=d.tiltX||0,f.tiltY=d.tiltY||0,f.pointerType=d.pointerType||"",f.hwTimestamp=d.hwTimestamp||0,f.isPrimary=d.isPrimary||!1,f._source=d._source||"",f}};a.eventFactory=e}(window.PolymerGestures),function(a){function b(){if(c){var a=new Map;return a.pointers=d,a}this.keys=[],this.values=[]}var c=window.Map&&window.Map.prototype.forEach,d=function(){return this.size};b.prototype={set:function(a,b){var c=this.keys.indexOf(a);c>-1?this.values[c]=b:(this.keys.push(a),this.values.push(b))},has:function(a){return this.keys.indexOf(a)>-1},"delete":function(a){var b=this.keys.indexOf(a);b>-1&&(this.keys.splice(b,1),this.values.splice(b,1))},get:function(a){var b=this.keys.indexOf(a);return this.values[b]},clear:function(){this.keys.length=0,this.values.length=0},forEach:function(a,b){this.values.forEach(function(c,d){a.call(b,c,this.keys[d],this)},this)},pointers:function(){return this.keys.length}},a.PointerMap=b}(window.PolymerGestures),function(a){var b,c=["bubbles","cancelable","view","detail","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","buttons","pointerId","width","height","pressure","tiltX","tiltY","pointerType","hwTimestamp","isPrimary","type","target","currentTarget","which","pageX","pageY","timeStamp","preventTap","tapPrevented","_source"],d=[!1,!1,null,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0,0,0,0,0,0,"",0,!1,"",null,null,0,0,0,0,function(){},!1],e="undefined"!=typeof SVGElementInstance,f=a.eventFactory,g={IS_IOS:!1,pointermap:new a.PointerMap,requiredGestures:new a.PointerMap,eventMap:Object.create(null),eventSources:Object.create(null),eventSourceList:[],gestures:[],dependencyMap:{down:{listeners:0,index:-1},up:{listeners:0,index:-1}},gestureQueue:[],registerSource:function(a,b){var c=b,d=c.events;d&&(d.forEach(function(a){c[a]&&(this.eventMap[a]=c[a].bind(c))},this),this.eventSources[a]=c,this.eventSourceList.push(c))},registerGesture:function(a,b){var c=Object.create(null);c.listeners=0,c.index=this.gestures.length;for(var d,e=0;e<b.exposes.length;e++)d=b.exposes[e].toLowerCase(),this.dependencyMap[d]=c;this.gestures.push(b)},register:function(a,b){for(var c,d=this.eventSourceList.length,e=0;d>e&&(c=this.eventSourceList[e]);e++)c.register.call(c,a,b)},unregister:function(a){for(var b,c=this.eventSourceList.length,d=0;c>d&&(b=this.eventSourceList[d]);d++)b.unregister.call(b,a)},down:function(a){this.requiredGestures.set(a.pointerId,b),this.fireEvent("down",a)},move:function(a){a.type="move",this.fillGestureQueue(a)},up:function(a){this.fireEvent("up",a),this.requiredGestures["delete"](a.pointerId)},cancel:function(a){a.tapPrevented=!0,this.fireEvent("up",a),this.requiredGestures["delete"](a.pointerId)},addGestureDependency:function(a,b){var c=a._pgEvents;if(c&&b)for(var d,e,f,g=Object.keys(c),h=0;h<g.length;h++)f=g[h],c[f]>0&&(d=this.dependencyMap[f],e=d?d.index:-1,b[e]=!0)},eventHandler:function(c){var d=c.type;if("touchstart"===d||"mousedown"===d||"pointerdown"===d||"MSPointerDown"===d)if(c._handledByPG||(b={}),this.IS_IOS){var e=c;if("touchstart"===d){var f=c.changedTouches[0];e={target:c.target,clientX:f.clientX,clientY:f.clientY,path:c.path}}for(var g,h=c.path||a.targetFinding.path(e),i=0;i<h.length;i++)g=h[i],this.addGestureDependency(g,b)}else this.addGestureDependency(c.currentTarget,b);if(!c._handledByPG){var j=this.eventMap&&this.eventMap[d];j&&j(c),c._handledByPG=!0}},listen:function(a,b){for(var c,d=0,e=b.length;e>d&&(c=b[d]);d++)this.addEvent(a,c)},unlisten:function(a,b){for(var c,d=0,e=b.length;e>d&&(c=b[d]);d++)this.removeEvent(a,c)},addEvent:function(a,b){a.addEventListener(b,this.boundHandler)},removeEvent:function(a,b){a.removeEventListener(b,this.boundHandler)},makeEvent:function(a,b){var c=f.makePointerEvent(a,b);return c.preventDefault=b.preventDefault,c.tapPrevented=b.tapPrevented,c._target=c._target||b.target,c},fireEvent:function(a,b){var c=this.makeEvent(a,b);return this.dispatchEvent(c)},cloneEvent:function(a){for(var b,f=Object.create(null),g=0;g<c.length;g++)b=c[g],f[b]=a[b]||d[g],("target"===b||"relatedTarget"===b)&&e&&f[b]instanceof SVGElementInstance&&(f[b]=f[b].correspondingUseElement);return f.preventDefault=function(){a.preventDefault()},f},dispatchEvent:function(a){var b=a._target;if(b){b.dispatchEvent(a);var c=this.cloneEvent(a);c.target=b,this.fillGestureQueue(c)}},gestureTrigger:function(){for(var a,b,c=0;c<this.gestureQueue.length;c++)if(a=this.gestureQueue[c],b=a._requiredGestures)for(var d,e,f=0;f<this.gestures.length;f++)b[f]&&(d=this.gestures[f],e=d[a.type],e&&e.call(d,a));this.gestureQueue.length=0},fillGestureQueue:function(a){this.gestureQueue.length||requestAnimationFrame(this.boundGestureTrigger),a._requiredGestures=this.requiredGestures.get(a.pointerId),this.gestureQueue.push(a)}};g.boundHandler=g.eventHandler.bind(g),g.boundGestureTrigger=g.gestureTrigger.bind(g),a.dispatcher=g,a.activateGesture=function(a,b){var c=b.toLowerCase(),d=g.dependencyMap[c];if(d){var e=g.gestures[d.index];if(a._pgListeners||(g.register(a),a._pgListeners=0),e){var f,h=e.defaultActions&&e.defaultActions[c];switch(a.nodeType){case Node.ELEMENT_NODE:f=a;break;case Node.DOCUMENT_FRAGMENT_NODE:f=a.host;break;default:f=null}h&&f&&!f.hasAttribute("touch-action")&&f.setAttribute("touch-action",h)}a._pgEvents||(a._pgEvents={}),a._pgEvents[c]=(a._pgEvents[c]||0)+1,a._pgListeners++}return Boolean(d)},a.addEventListener=function(b,c,d,e){d&&(a.activateGesture(b,c),b.addEventListener(c,d,e))},a.deactivateGesture=function(a,b){var c=b.toLowerCase(),d=g.dependencyMap[c];return d&&(a._pgListeners>0&&a._pgListeners--,0===a._pgListeners&&g.unregister(a),a._pgEvents&&(a._pgEvents[c]>0?a._pgEvents[c]--:a._pgEvents[c]=0)),Boolean(d)},a.removeEventListener=function(b,c,d,e){d&&(a.deactivateGesture(b,c),b.removeEventListener(c,d,e))}}(window.PolymerGestures),function(a){var b=a.dispatcher,c=b.pointermap,d=25,e=[0,1,4,2],f=0,g=/Linux.*Firefox\//i,h=function(){if(g.test(navigator.userAgent))return!1;try{return 1===new MouseEvent("test",{buttons:1}).buttons}catch(a){return!1}}(),i={POINTER_ID:1,POINTER_TYPE:"mouse",events:["mousedown","mousemove","mouseup"],exposes:["down","up","move"],register:function(a){b.listen(a,this.events)},unregister:function(a){a.nodeType!==Node.DOCUMENT_NODE&&b.unlisten(a,this.events)},lastTouches:[],isEventSimulatedFromTouch:function(a){for(var b,c=this.lastTouches,e=a.clientX,f=a.clientY,g=0,h=c.length;h>g&&(b=c[g]);g++){var i=Math.abs(e-b.x),j=Math.abs(f-b.y);if(d>=i&&d>=j)return!0}},prepareEvent:function(a){var c=b.cloneEvent(a);if(c.pointerId=this.POINTER_ID,c.isPrimary=!0,c.pointerType=this.POINTER_TYPE,c._source="mouse",!h){var d=a.type,g=e[a.which]||0;"mousedown"===d?f|=g:"mouseup"===d&&(f&=~g),c.buttons=f}return c},mousedown:function(d){if(!this.isEventSimulatedFromTouch(d)){var e=(c.has(this.POINTER_ID),this.prepareEvent(d));e.target=a.findTarget(d),c.set(this.POINTER_ID,e.target),b.down(e)}},mousemove:function(a){if(!this.isEventSimulatedFromTouch(a)){var d=c.get(this.POINTER_ID);if(d){var e=this.prepareEvent(a);e.target=d,0===(h?e.buttons:e.which)?(h||(f=e.buttons=0),b.cancel(e),this.cleanupMouse(e.buttons)):b.move(e)}}},mouseup:function(d){if(!this.isEventSimulatedFromTouch(d)){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(this.POINTER_ID),b.up(e),this.cleanupMouse(e.buttons)}},cleanupMouse:function(a){0===a&&c["delete"](this.POINTER_ID)}};a.mouseEvents=i}(window.PolymerGestures),function(a){var b=a.dispatcher,c=(a.targetFinding.allShadows.bind(a.targetFinding),b.pointermap),d=(Array.prototype.map.call.bind(Array.prototype.map),2500),e=25,f=200,g=20,h=!1,i={IS_IOS:!1,events:["touchstart","touchmove","touchend","touchcancel"],exposes:["down","up","move"],register:function(a,c){(this.IS_IOS?c:!c)&&b.listen(a,this.events)},unregister:function(a){this.IS_IOS||b.unlisten(a,this.events)},scrollTypes:{EMITTER:"none",XSCROLLER:"pan-x",YSCROLLER:"pan-y"},touchActionToScrollType:function(a){var b=a,c=this.scrollTypes;return b===c.EMITTER?"none":b===c.XSCROLLER?"X":b===c.YSCROLLER?"Y":"XY"},POINTER_TYPE:"touch",firstTouch:null,isPrimaryTouch:function(a){return this.firstTouch===a.identifier},setPrimaryTouch:function(a){(0===c.pointers()||1===c.pointers()&&c.has(1))&&(this.firstTouch=a.identifier,this.firstXY={X:a.clientX,Y:a.clientY},this.firstTarget=a.target,this.scrolling=null,this.cancelResetClickCount())},removePrimaryPointer:function(a){a.isPrimary&&(this.firstTouch=null,this.firstXY=null,this.resetClickCount())},clickCount:0,resetId:null,resetClickCount:function(){var a=function(){this.clickCount=0,this.resetId=null}.bind(this);this.resetId=setTimeout(a,f)},cancelResetClickCount:function(){this.resetId&&clearTimeout(this.resetId)},typeToButtons:function(a){var b=0;return("touchstart"===a||"touchmove"===a)&&(b=1),b},findTarget:function(b,d){if("touchstart"===this.currentTouchEvent.type){if(this.isPrimaryTouch(b)){var e={clientX:b.clientX,clientY:b.clientY,path:this.currentTouchEvent.path,target:this.currentTouchEvent.target};return a.findTarget(e)}return a.findTarget(b)}return c.get(d)},touchToPointer:function(a){var c=this.currentTouchEvent,d=b.cloneEvent(a),e=d.pointerId=a.identifier+2;d.target=this.findTarget(a,e),d.bubbles=!0,d.cancelable=!0,d.detail=this.clickCount,d.buttons=this.typeToButtons(c.type),d.width=a.webkitRadiusX||a.radiusX||0,d.height=a.webkitRadiusY||a.radiusY||0,d.pressure=a.webkitForce||a.force||.5,d.isPrimary=this.isPrimaryTouch(a),d.pointerType=this.POINTER_TYPE,d._source="touch";var f=this;return d.preventDefault=function(){f.scrolling=!1,f.firstXY=null,c.preventDefault()},d},processTouches:function(a,b){var d=a.changedTouches;this.currentTouchEvent=a;for(var e,f,g=0;g<d.length;g++)e=d[g],f=this.touchToPointer(e),"touchstart"===a.type&&c.set(f.pointerId,f.target),c.has(f.pointerId)&&b.call(this,f),("touchend"===a.type||a._cancel)&&this.cleanUpPointer(f)},shouldScroll:function(b){if(this.firstXY){var c,d=a.targetFinding.findTouchAction(b),e=this.touchActionToScrollType(d);if("none"===e)c=!1;else if("XY"===e)c=!0;else{var f=b.changedTouches[0],g=e,h="Y"===e?"X":"Y",i=Math.abs(f["client"+g]-this.firstXY[g]),j=Math.abs(f["client"+h]-this.firstXY[h]);c=i>=j}return c}},findTouch:function(a,b){for(var c,d=0,e=a.length;e>d&&(c=a[d]);d++)if(c.identifier===b)return!0},vacuumTouches:function(a){var b=a.touches;if(c.pointers()>=b.length){var d=[];c.forEach(function(a,c){if(1!==c&&!this.findTouch(b,c-2)){var e=a;d.push(e)}},this),d.forEach(function(a){this.cancel(a),c["delete"](a.pointerId)},this)}},touchstart:function(a){this.vacuumTouches(a),this.setPrimaryTouch(a.changedTouches[0]),this.dedupSynthMouse(a),this.scrolling||(this.clickCount++,this.processTouches(a,this.down))},down:function(a){b.down(a)},touchmove:function(a){if(h)a.cancelable&&this.processTouches(a,this.move);else if(this.scrolling){if(this.firstXY){var b=a.changedTouches[0],c=b.clientX-this.firstXY.X,d=b.clientY-this.firstXY.Y,e=Math.sqrt(c*c+d*d);e>=g&&(this.touchcancel(a),this.scrolling=!0,this.firstXY=null)}}else null===this.scrolling&&this.shouldScroll(a)?this.scrolling=!0:(this.scrolling=!1,a.preventDefault(),this.processTouches(a,this.move))},move:function(a){b.move(a)},touchend:function(a){this.dedupSynthMouse(a),this.processTouches(a,this.up)},up:function(c){c.relatedTarget=a.findTarget(c),b.up(c)},cancel:function(a){b.cancel(a)},touchcancel:function(a){a._cancel=!0,this.processTouches(a,this.cancel)},cleanUpPointer:function(a){c["delete"](a.pointerId),this.removePrimaryPointer(a)},dedupSynthMouse:function(b){var c=a.mouseEvents.lastTouches,e=b.changedTouches[0];if(this.isPrimaryTouch(e)){var f={x:e.clientX,y:e.clientY};c.push(f);var g=function(a,b){var c=a.indexOf(b);c>-1&&a.splice(c,1)}.bind(null,c,f);setTimeout(g,d)}}},j=Event.prototype.stopImmediatePropagation||Event.prototype.stopPropagation;document.addEventListener("click",function(b){var c=b.clientX,d=b.clientY,f=function(a){var b=Math.abs(c-a.x),f=Math.abs(d-a.y);return e>=b&&e>=f},g=a.mouseEvents.lastTouches.some(f),h=a.targetFinding.path(b);if(g){for(var k=0;k<h.length;k++)if(h[k]===i.firstTarget)return;b.preventDefault(),j.call(b)}},!0),a.touchEvents=i}(window.PolymerGestures),function(a){var b=a.dispatcher,c=b.pointermap,d=window.MSPointerEvent&&"number"==typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE,e={events:["MSPointerDown","MSPointerMove","MSPointerUp","MSPointerCancel"],register:function(a){b.listen(a,this.events)},unregister:function(a){a.nodeType!==Node.DOCUMENT_NODE&&b.unlisten(a,this.events)},POINTER_TYPES:["","unavailable","touch","pen","mouse"],prepareEvent:function(a){var c=a;return c=b.cloneEvent(a),d&&(c.pointerType=this.POINTER_TYPES[a.pointerType]),c._source="ms",c},cleanup:function(a){c["delete"](a)},MSPointerDown:function(d){var e=this.prepareEvent(d);e.target=a.findTarget(d),c.set(d.pointerId,e.target),b.down(e)},MSPointerMove:function(a){var d=c.get(a.pointerId);if(d){var e=this.prepareEvent(a);e.target=d,b.move(e)}},MSPointerUp:function(d){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(e.pointerId),b.up(e),this.cleanup(d.pointerId)},MSPointerCancel:function(d){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(e.pointerId),b.cancel(e),this.cleanup(d.pointerId)}};a.msEvents=e}(window.PolymerGestures),function(a){var b=a.dispatcher,c=b.pointermap,d={events:["pointerdown","pointermove","pointerup","pointercancel"],prepareEvent:function(a){var c=b.cloneEvent(a);return c._source="pointer",c},register:function(a){b.listen(a,this.events)},unregister:function(a){a.nodeType!==Node.DOCUMENT_NODE&&b.unlisten(a,this.events)},cleanup:function(a){c["delete"](a)},pointerdown:function(d){var e=this.prepareEvent(d);e.target=a.findTarget(d),c.set(e.pointerId,e.target),b.down(e)},pointermove:function(a){var d=c.get(a.pointerId);if(d){var e=this.prepareEvent(a);e.target=d,b.move(e)}},pointerup:function(d){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(e.pointerId),b.up(e),this.cleanup(d.pointerId)},pointercancel:function(d){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(e.pointerId),b.cancel(e),this.cleanup(d.pointerId)}};a.pointerEvents=d}(window.PolymerGestures),function(a){var b=a.dispatcher,c=window.navigator;window.PointerEvent?b.registerSource("pointer",a.pointerEvents):c.msPointerEnabled?b.registerSource("ms",a.msEvents):(b.registerSource("mouse",a.mouseEvents),void 0!==window.ontouchstart&&b.registerSource("touch",a.touchEvents));var d=navigator.userAgent,e=d.match(/iPad|iPhone|iPod/)&&"ontouchstart"in window;b.IS_IOS=e,a.touchEvents.IS_IOS=e,b.register(document,!0)}(window.PolymerGestures),function(a){var b=a.dispatcher,c=a.eventFactory,d=new a.PointerMap,e={events:["down","move","up"],exposes:["trackstart","track","trackx","tracky","trackend"],defaultActions:{track:"none",trackx:"pan-y",tracky:"pan-x"},WIGGLE_THRESHOLD:4,clampDir:function(a){return a>0?1:-1},calcPositionDelta:function(a,b){var c=0,d=0;return a&&b&&(c=b.pageX-a.pageX,d=b.pageY-a.pageY),{x:c,y:d}},fireTrack:function(a,b,d){var e=d,f=this.calcPositionDelta(e.downEvent,b),g=this.calcPositionDelta(e.lastMoveEvent,b);if(g.x)e.xDirection=this.clampDir(g.x);else if("trackx"===a)return;if(g.y)e.yDirection=this.clampDir(g.y);else if("tracky"===a)return;var h={bubbles:!0,cancelable:!0,trackInfo:e.trackInfo,relatedTarget:b.relatedTarget,pointerType:b.pointerType,pointerId:b.pointerId,_source:"track"};"tracky"!==a&&(h.x=b.x,h.dx=f.x,h.ddx=g.x,h.clientX=b.clientX,h.pageX=b.pageX,h.screenX=b.screenX,h.xDirection=e.xDirection),"trackx"!==a&&(h.dy=f.y,h.ddy=g.y,h.y=b.y,h.clientY=b.clientY,h.pageY=b.pageY,h.screenY=b.screenY,h.yDirection=e.yDirection);var i=c.makeGestureEvent(a,h);e.downTarget.dispatchEvent(i)},down:function(a){if(a.isPrimary&&("mouse"===a.pointerType?1===a.buttons:!0)){var b={downEvent:a,downTarget:a.target,trackInfo:{},lastMoveEvent:null,xDirection:0,yDirection:0,tracking:!1};d.set(a.pointerId,b)}},move:function(a){var b=d.get(a.pointerId);if(b){if(!b.tracking){var c=this.calcPositionDelta(b.downEvent,a),e=c.x*c.x+c.y*c.y;e>this.WIGGLE_THRESHOLD&&(b.tracking=!0,b.lastMoveEvent=b.downEvent,this.fireTrack("trackstart",a,b))}b.tracking&&(this.fireTrack("track",a,b),this.fireTrack("trackx",a,b),this.fireTrack("tracky",a,b)),b.lastMoveEvent=a}},up:function(a){var b=d.get(a.pointerId);b&&(b.tracking&&this.fireTrack("trackend",a,b),d["delete"](a.pointerId))}};b.registerGesture("track",e)}(window.PolymerGestures),function(a){var b=a.dispatcher,c=a.eventFactory,d={HOLD_DELAY:200,WIGGLE_THRESHOLD:16,events:["down","move","up"],exposes:["hold","holdpulse","release"],heldPointer:null,holdJob:null,pulse:function(){var a=Date.now()-this.heldPointer.timeStamp,b=this.held?"holdpulse":"hold";this.fireHold(b,a),this.held=!0},cancel:function(){clearInterval(this.holdJob),this.held&&this.fireHold("release"),this.held=!1,this.heldPointer=null,this.target=null,this.holdJob=null},down:function(a){a.isPrimary&&!this.heldPointer&&(this.heldPointer=a,this.target=a.target,this.holdJob=setInterval(this.pulse.bind(this),this.HOLD_DELAY))},up:function(a){this.heldPointer&&this.heldPointer.pointerId===a.pointerId&&this.cancel()},move:function(a){if(this.heldPointer&&this.heldPointer.pointerId===a.pointerId){var b=a.clientX-this.heldPointer.clientX,c=a.clientY-this.heldPointer.clientY;b*b+c*c>this.WIGGLE_THRESHOLD&&this.cancel()}},fireHold:function(a,b){var d={bubbles:!0,cancelable:!0,pointerType:this.heldPointer.pointerType,pointerId:this.heldPointer.pointerId,x:this.heldPointer.clientX,y:this.heldPointer.clientY,_source:"hold"};b&&(d.holdTime=b);var e=c.makeGestureEvent(a,d);this.target.dispatchEvent(e)}};b.registerGesture("hold",d)}(window.PolymerGestures),function(a){var b=a.dispatcher,c=a.eventFactory,d=new a.PointerMap,e={events:["down","up"],exposes:["tap"],down:function(a){a.isPrimary&&!a.tapPrevented&&d.set(a.pointerId,{target:a.target,buttons:a.buttons,x:a.clientX,y:a.clientY})},shouldTap:function(a,b){var c=!0;return"mouse"===a.pointerType&&(c=1^a.buttons&&1&b.buttons),c&&!a.tapPrevented},up:function(b){var e=d.get(b.pointerId);if(e&&this.shouldTap(b,e)){var f=a.targetFinding.LCA(e.target,b.relatedTarget);if(f){var g=c.makeGestureEvent("tap",{bubbles:!0,cancelable:!0,x:b.clientX,y:b.clientY,detail:b.detail,pointerType:b.pointerType,pointerId:b.pointerId,altKey:b.altKey,ctrlKey:b.ctrlKey,metaKey:b.metaKey,shiftKey:b.shiftKey,_source:"tap"});f.dispatchEvent(g)}}d["delete"](b.pointerId)}};c.preventTap=function(a){return function(){a.tapPrevented=!0,d["delete"](a.pointerId)}},b.registerGesture("tap",e)}(window.PolymerGestures),function(a){var b=a.dispatcher,c=a.eventFactory,d=new a.PointerMap,e=180/Math.PI,f={events:["down","up","move","cancel"],exposes:["pinchstart","pinch","pinchend","rotate"],defaultActions:{pinch:"none",rotate:"none"},reference:{},down:function(b){if(d.set(b.pointerId,b),2==d.pointers()){var c=this.calcChord(),e=this.calcAngle(c);this.reference={angle:e,diameter:c.diameter,target:a.targetFinding.LCA(c.a.target,c.b.target)},this.firePinch("pinchstart",c.diameter,c)}},up:function(a){var b=d.get(a.pointerId),c=d.pointers();if(b){if(2===c){var e=this.calcChord();this.firePinch("pinchend",e.diameter,e)}d["delete"](a.pointerId)}},move:function(a){d.has(a.pointerId)&&(d.set(a.pointerId,a),d.pointers()>1&&this.calcPinchRotate())},cancel:function(a){this.up(a)},firePinch:function(a,b,d){var e=b/this.reference.diameter,f=c.makeGestureEvent(a,{bubbles:!0,cancelable:!0,scale:e,centerX:d.center.x,centerY:d.center.y,_source:"pinch"});this.reference.target.dispatchEvent(f)},fireRotate:function(a,b){var d=Math.round((a-this.reference.angle)%360),e=c.makeGestureEvent("rotate",{bubbles:!0,cancelable:!0,angle:d,centerX:b.center.x,centerY:b.center.y,_source:"pinch"});this.reference.target.dispatchEvent(e)},calcPinchRotate:function(){var a=this.calcChord(),b=a.diameter,c=this.calcAngle(a);b!=this.reference.diameter&&this.firePinch("pinch",b,a),c!=this.reference.angle&&this.fireRotate(c,a)},calcChord:function(){var a=[];d.forEach(function(b){a.push(b)});for(var b,c,e,f=0,g={a:a[0],b:a[1]},h=0;h<a.length;h++)for(var i=a[h],j=h+1;j<a.length;j++){var k=a[j];b=Math.abs(i.clientX-k.clientX),c=Math.abs(i.clientY-k.clientY),e=b+c,e>f&&(f=e,g={a:i,b:k})}return b=Math.abs(g.a.clientX+g.b.clientX)/2,c=Math.abs(g.a.clientY+g.b.clientY)/2,g.center={x:b,y:c},g.diameter=f,g},calcAngle:function(a){var b=a.a.clientX-a.b.clientX,c=a.a.clientY-a.b.clientY;return(360+Math.atan2(c,b)*e)%360}};b.registerGesture("pinch",f)}(window.PolymerGestures),function(a){"use strict";function b(a,b){if(!a)throw new Error("ASSERT: "+b)}function c(a){return a>=48&&57>=a}function d(a){return 32===a||9===a||11===a||12===a||160===a||a>=5760&&" ᠎              ".indexOf(String.fromCharCode(a))>0}function e(a){return 10===a||13===a||8232===a||8233===a}function f(a){return 36===a||95===a||a>=65&&90>=a||a>=97&&122>=a}function g(a){return 36===a||95===a||a>=65&&90>=a||a>=97&&122>=a||a>=48&&57>=a}function h(a){return"this"===a}function i(){for(;Y>X&&d(W.charCodeAt(X));)++X}function j(){var a,b;for(a=X++;Y>X&&(b=W.charCodeAt(X),g(b));)++X;return W.slice(a,X)}function k(){var a,b,c;return a=X,b=j(),c=1===b.length?S.Identifier:h(b)?S.Keyword:"null"===b?S.NullLiteral:"true"===b||"false"===b?S.BooleanLiteral:S.Identifier,{type:c,value:b,range:[a,X]}}function l(){var a,b,c=X,d=W.charCodeAt(X),e=W[X];switch(d){case 46:case 40:case 41:case 59:case 44:case 123:case 125:case 91:case 93:case 58:case 63:return++X,{type:S.Punctuator,value:String.fromCharCode(d),range:[c,X]};default:if(a=W.charCodeAt(X+1),61===a)switch(d){case 37:case 38:case 42:case 43:case 45:case 47:case 60:case 62:case 124:return X+=2,{type:S.Punctuator,value:String.fromCharCode(d)+String.fromCharCode(a),range:[c,X]};case 33:case 61:return X+=2,61===W.charCodeAt(X)&&++X,{type:S.Punctuator,value:W.slice(c,X),range:[c,X]}}}return b=W[X+1],e===b&&"&|".indexOf(e)>=0?(X+=2,{type:S.Punctuator,value:e+b,range:[c,X]}):"<>=!+-*%&|^/".indexOf(e)>=0?(++X,{type:S.Punctuator,value:e,range:[c,X]}):void s({},V.UnexpectedToken,"ILLEGAL")}function m(){var a,d,e;if(e=W[X],b(c(e.charCodeAt(0))||"."===e,"Numeric literal must start with a decimal digit or a decimal point"),d=X,a="","."!==e){for(a=W[X++],e=W[X],"0"===a&&e&&c(e.charCodeAt(0))&&s({},V.UnexpectedToken,"ILLEGAL");c(W.charCodeAt(X));)a+=W[X++];e=W[X]}if("."===e){for(a+=W[X++];c(W.charCodeAt(X));)a+=W[X++];e=W[X]}if("e"===e||"E"===e)if(a+=W[X++],e=W[X],("+"===e||"-"===e)&&(a+=W[X++]),c(W.charCodeAt(X)))for(;c(W.charCodeAt(X));)a+=W[X++];else s({},V.UnexpectedToken,"ILLEGAL");return f(W.charCodeAt(X))&&s({},V.UnexpectedToken,"ILLEGAL"),{type:S.NumericLiteral,value:parseFloat(a),range:[d,X]}}function n(){var a,c,d,f="",g=!1;for(a=W[X],b("'"===a||'"'===a,"String literal must starts with a quote"),c=X,++X;Y>X;){if(d=W[X++],d===a){a="";break}if("\\"===d)if(d=W[X++],d&&e(d.charCodeAt(0)))"\r"===d&&"\n"===W[X]&&++X;else switch(d){case"n":f+="\n";break;case"r":f+="\r";break;case"t":f+="	";break;case"b":f+="\b";break;case"f":f+="\f";break;case"v":f+="";break;default:f+=d}else{if(e(d.charCodeAt(0)))break;f+=d}}return""!==a&&s({},V.UnexpectedToken,"ILLEGAL"),{type:S.StringLiteral,value:f,octal:g,range:[c,X]}}function o(a){return a.type===S.Identifier||a.type===S.Keyword||a.type===S.BooleanLiteral||a.type===S.NullLiteral}function p(){var a;return i(),X>=Y?{type:S.EOF,range:[X,X]}:(a=W.charCodeAt(X),40===a||41===a||58===a?l():39===a||34===a?n():f(a)?k():46===a?c(W.charCodeAt(X+1))?m():l():c(a)?m():l())}function q(){var a;return a=$,X=a.range[1],$=p(),X=a.range[1],a}function r(){var a;a=X,$=p(),X=a}function s(a,c){var d,e=Array.prototype.slice.call(arguments,2),f=c.replace(/%(\d)/g,function(a,c){return b(c<e.length,"Message reference must be in range"),e[c]});throw d=new Error(f),d.index=X,d.description=f,d}function t(a){s(a,V.UnexpectedToken,a.value)}function u(a){var b=q();(b.type!==S.Punctuator||b.value!==a)&&t(b)}function v(a){return $.type===S.Punctuator&&$.value===a}function w(a){return $.type===S.Keyword&&$.value===a}function x(){var a=[];for(u("[");!v("]");)v(",")?(q(),a.push(null)):(a.push(bb()),v("]")||u(","));return u("]"),Z.createArrayExpression(a)}function y(){var a;return i(),a=q(),a.type===S.StringLiteral||a.type===S.NumericLiteral?Z.createLiteral(a):Z.createIdentifier(a.value)}function z(){var a,b;return a=$,i(),(a.type===S.EOF||a.type===S.Punctuator)&&t(a),b=y(),u(":"),Z.createProperty("init",b,bb())}function A(){var a=[];for(u("{");!v("}");)a.push(z()),v("}")||u(",");return u("}"),Z.createObjectExpression(a)}function B(){var a;return u("("),a=bb(),u(")"),a}function C(){var a,b,c;return v("(")?B():(a=$.type,a===S.Identifier?c=Z.createIdentifier(q().value):a===S.StringLiteral||a===S.NumericLiteral?c=Z.createLiteral(q()):a===S.Keyword?w("this")&&(q(),c=Z.createThisExpression()):a===S.BooleanLiteral?(b=q(),b.value="true"===b.value,c=Z.createLiteral(b)):a===S.NullLiteral?(b=q(),b.value=null,c=Z.createLiteral(b)):v("[")?c=x():v("{")&&(c=A()),c?c:void t(q()))}function D(){var a=[];if(u("("),!v(")"))for(;Y>X&&(a.push(bb()),!v(")"));)u(",");return u(")"),a}function E(){var a;return a=q(),o(a)||t(a),Z.createIdentifier(a.value)}function F(){return u("."),E()}function G(){var a;return u("["),a=bb(),u("]"),a}function H(){var a,b,c;for(a=C();;)if(v("["))c=G(),a=Z.createMemberExpression("[",a,c);else if(v("."))c=F(),a=Z.createMemberExpression(".",a,c);else{if(!v("("))break;b=D(),a=Z.createCallExpression(a,b)}return a}function I(){var a,b;return $.type!==S.Punctuator&&$.type!==S.Keyword?b=ab():v("+")||v("-")||v("!")?(a=q(),b=I(),b=Z.createUnaryExpression(a.value,b)):w("delete")||w("void")||w("typeof")?s({},V.UnexpectedToken):b=ab(),b}function J(a){var b=0;if(a.type!==S.Punctuator&&a.type!==S.Keyword)return 0;switch(a.value){case"||":b=1;break;case"&&":b=2;break;case"==":case"!=":case"===":case"!==":b=6;break;case"<":case">":case"<=":case">=":case"instanceof":b=7;break;case"in":b=7;break;case"+":case"-":b=9;break;case"*":case"/":case"%":b=11}return b}function K(){var a,b,c,d,e,f,g,h;if(g=I(),b=$,c=J(b),0===c)return g;for(b.prec=c,q(),e=I(),d=[g,b,e];(c=J($))>0;){for(;d.length>2&&c<=d[d.length-2].prec;)e=d.pop(),f=d.pop().value,g=d.pop(),a=Z.createBinaryExpression(f,g,e),d.push(a);b=q(),b.prec=c,d.push(b),a=I(),d.push(a)}for(h=d.length-1,a=d[h];h>1;)a=Z.createBinaryExpression(d[h-1].value,d[h-2],a),h-=2;return a}function L(){var a,b,c;return a=K(),v("?")&&(q(),b=L(),u(":"),c=L(),a=Z.createConditionalExpression(a,b,c)),a}function M(){var a,b;return a=q(),a.type!==S.Identifier&&t(a),b=v("(")?D():[],Z.createFilter(a.value,b)}function N(){for(;v("|");)q(),M()}function O(){i(),r();var a=bb();a&&(","===$.value||"in"==$.value&&a.type===U.Identifier?Q(a):(N(),"as"===$.value?P(a):Z.createTopLevel(a))),$.type!==S.EOF&&t($)}function P(a){q();var b=q().value;Z.createAsExpression(a,b)}function Q(a){var b;","===$.value&&(q(),$.type!==S.Identifier&&t($),b=q().value),q();var c=bb();N(),Z.createInExpression(a.name,b,c)}function R(a,b){return Z=b,W=a,X=0,Y=W.length,$=null,_={labelSet:{}},O()}var S,T,U,V,W,X,Y,Z,$,_;S={BooleanLiteral:1,EOF:2,Identifier:3,Keyword:4,NullLiteral:5,NumericLiteral:6,Punctuator:7,StringLiteral:8},T={},T[S.BooleanLiteral]="Boolean",T[S.EOF]="<end>",T[S.Identifier]="Identifier",T[S.Keyword]="Keyword",T[S.NullLiteral]="Null",T[S.NumericLiteral]="Numeric",T[S.Punctuator]="Punctuator",T[S.StringLiteral]="String",U={ArrayExpression:"ArrayExpression",BinaryExpression:"BinaryExpression",CallExpression:"CallExpression",ConditionalExpression:"ConditionalExpression",EmptyStatement:"EmptyStatement",ExpressionStatement:"ExpressionStatement",Identifier:"Identifier",Literal:"Literal",LabeledStatement:"LabeledStatement",LogicalExpression:"LogicalExpression",MemberExpression:"MemberExpression",ObjectExpression:"ObjectExpression",Program:"Program",Property:"Property",ThisExpression:"ThisExpression",UnaryExpression:"UnaryExpression"},V={UnexpectedToken:"Unexpected token %0",UnknownLabel:"Undefined label '%0'",Redeclaration:"%0 '%1' has already been declared"};
+var ab=H,bb=L;a.esprima={parse:R}}(this),function(a){"use strict";function b(a,b,d,e){var f;try{if(f=c(a),f.scopeIdent&&(d.nodeType!==Node.ELEMENT_NODE||"TEMPLATE"!==d.tagName||"bind"!==b&&"repeat"!==b))throw Error("as and in can only be used within <template bind/repeat>")}catch(g){return void console.error("Invalid expression syntax: "+a,g)}return function(a,b,c){var d=f.getBinding(a,e,c);return f.scopeIdent&&d&&(b.polymerExpressionScopeIdent_=f.scopeIdent,f.indexIdent&&(b.polymerExpressionIndexIdent_=f.indexIdent)),d}}function c(a){var b=q[a];if(!b){var c=new j;esprima.parse(a,c),b=new l(c),q[a]=b}return b}function d(a){this.value=a,this.valueFn_=void 0}function e(a){this.name=a,this.path=Path.get(a)}function f(a,b,c){this.computed="["==c,this.dynamicDeps="function"==typeof a||a.dynamicDeps||this.computed&&!(b instanceof d),this.simplePath=!this.dynamicDeps&&(b instanceof e||b instanceof d)&&(a instanceof f||a instanceof e),this.object=this.simplePath?a:i(a),this.property=!this.computed||this.simplePath?b:i(b)}function g(a,b){this.name=a,this.args=[];for(var c=0;c<b.length;c++)this.args[c]=i(b[c])}function h(){throw Error("Not Implemented")}function i(a){return"function"==typeof a?a:a.valueFn()}function j(){this.expression=null,this.filters=[],this.deps={},this.currentPath=void 0,this.scopeIdent=void 0,this.indexIdent=void 0,this.dynamicDeps=!1}function k(a){this.value_=a}function l(a){if(this.scopeIdent=a.scopeIdent,this.indexIdent=a.indexIdent,!a.expression)throw Error("No expression found.");this.expression=a.expression,i(this.expression),this.filters=a.filters,this.dynamicDeps=a.dynamicDeps}function m(a){return String(a).replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()})}function n(a,b){for(;a[t]&&!Object.prototype.hasOwnProperty.call(a,b);)a=a[t];return a}function o(a){switch(a){case"":return!1;case"false":case"null":case"true":return!0}return isNaN(Number(a))?!1:!0}function p(){}var q=Object.create(null);d.prototype={valueFn:function(){if(!this.valueFn_){var a=this.value;this.valueFn_=function(){return a}}return this.valueFn_}},e.prototype={valueFn:function(){if(!this.valueFn_){var a=(this.name,this.path);this.valueFn_=function(b,c){return c&&c.addPath(b,a),a.getValueFrom(b)}}return this.valueFn_},setValue:function(a,b){return 1==this.path.length&&(a=n(a,this.path[0])),this.path.setValueFrom(a,b)}},f.prototype={get fullPath(){if(!this.fullPath_){var a=this.object instanceof f?this.object.fullPath.slice():[this.object.name];a.push(this.property instanceof e?this.property.name:this.property.value),this.fullPath_=Path.get(a)}return this.fullPath_},valueFn:function(){if(!this.valueFn_){var a=this.object;if(this.simplePath){var b=this.fullPath;this.valueFn_=function(a,c){return c&&c.addPath(a,b),b.getValueFrom(a)}}else if(this.computed){var c=this.property;this.valueFn_=function(b,d,e){var f=a(b,d,e),g=c(b,d,e);return d&&d.addPath(f,[g]),f?f[g]:void 0}}else{var b=Path.get(this.property.name);this.valueFn_=function(c,d,e){var f=a(c,d,e);return d&&d.addPath(f,b),b.getValueFrom(f)}}}return this.valueFn_},setValue:function(a,b){if(this.simplePath)return this.fullPath.setValueFrom(a,b),b;var c=this.object(a),d=this.property instanceof e?this.property.name:this.property(a);return c[d]=b}},g.prototype={transform:function(a,b,c,d,e){var f=a,g=f[this.name];if(!g&&(g=c[this.name],!g))return void console.error("Cannot find function or filter: "+this.name);if(d?g=g.toModel:"function"==typeof g.toDOM&&(g=g.toDOM),"function"!=typeof g)return void console.error("Cannot find function or filter: "+this.name);for(var h=e||[],j=0;j<this.args.length;j++)h.push(i(this.args[j])(a,b,c));return g.apply(f,h)}};var r={"+":function(a){return+a},"-":function(a){return-a},"!":function(a){return!a}},s={"+":function(a,b){return a+b},"-":function(a,b){return a-b},"*":function(a,b){return a*b},"/":function(a,b){return a/b},"%":function(a,b){return a%b},"<":function(a,b){return b>a},">":function(a,b){return a>b},"<=":function(a,b){return b>=a},">=":function(a,b){return a>=b},"==":function(a,b){return a==b},"!=":function(a,b){return a!=b},"===":function(a,b){return a===b},"!==":function(a,b){return a!==b},"&&":function(a,b){return a&&b},"||":function(a,b){return a||b}};j.prototype={createUnaryExpression:function(a,b){if(!r[a])throw Error("Disallowed operator: "+a);return b=i(b),function(c,d,e){return r[a](b(c,d,e))}},createBinaryExpression:function(a,b,c){if(!s[a])throw Error("Disallowed operator: "+a);switch(b=i(b),c=i(c),a){case"||":return this.dynamicDeps=!0,function(a,d,e){return b(a,d,e)||c(a,d,e)};case"&&":return this.dynamicDeps=!0,function(a,d,e){return b(a,d,e)&&c(a,d,e)}}return function(d,e,f){return s[a](b(d,e,f),c(d,e,f))}},createConditionalExpression:function(a,b,c){return a=i(a),b=i(b),c=i(c),this.dynamicDeps=!0,function(d,e,f){return a(d,e,f)?b(d,e,f):c(d,e,f)}},createIdentifier:function(a){var b=new e(a);return b.type="Identifier",b},createMemberExpression:function(a,b,c){var d=new f(b,c,a);return d.dynamicDeps&&(this.dynamicDeps=!0),d},createCallExpression:function(a,b){if(!(a instanceof e))throw Error("Only identifier function invocations are allowed");var c=new g(a.name,b);return function(a,b,d){return c.transform(a,b,d,!1)}},createLiteral:function(a){return new d(a.value)},createArrayExpression:function(a){for(var b=0;b<a.length;b++)a[b]=i(a[b]);return function(b,c,d){for(var e=[],f=0;f<a.length;f++)e.push(a[f](b,c,d));return e}},createProperty:function(a,b,c){return{key:b instanceof e?b.name:b.value,value:c}},createObjectExpression:function(a){for(var b=0;b<a.length;b++)a[b].value=i(a[b].value);return function(b,c,d){for(var e={},f=0;f<a.length;f++)e[a[f].key]=a[f].value(b,c,d);return e}},createFilter:function(a,b){this.filters.push(new g(a,b))},createAsExpression:function(a,b){this.expression=a,this.scopeIdent=b},createInExpression:function(a,b,c){this.expression=c,this.scopeIdent=a,this.indexIdent=b},createTopLevel:function(a){this.expression=a},createThisExpression:h},k.prototype={open:function(){return this.value_},discardChanges:function(){return this.value_},deliver:function(){},close:function(){}},l.prototype={getBinding:function(a,b,c){function d(){if(h)return h=!1,g;i.dynamicDeps&&f.startReset();var c=i.getValue(a,i.dynamicDeps?f:void 0,b);return i.dynamicDeps&&f.finishReset(),c}function e(c){return i.setValue(a,c,b),c}if(c)return this.getValue(a,void 0,b);var f=new CompoundObserver,g=this.getValue(a,f,b),h=!0,i=this;return new ObserverTransform(f,d,e,!0)},getValue:function(a,b,c){for(var d=i(this.expression)(a,b,c),e=0;e<this.filters.length;e++)d=this.filters[e].transform(a,b,c,!1,[d]);return d},setValue:function(a,b,c){for(var d=this.filters?this.filters.length:0;d-->0;)b=this.filters[d].transform(a,void 0,c,!0,[b]);return this.expression.setValue?this.expression.setValue(a,b):void 0}};var t="@"+Math.random().toString(36).slice(2);p.prototype={styleObject:function(a){var b=[];for(var c in a)b.push(m(c)+": "+a[c]);return b.join("; ")},tokenList:function(a){var b=[];for(var c in a)a[c]&&b.push(c);return b.join(" ")},prepareInstancePositionChanged:function(a){var b=a.polymerExpressionIndexIdent_;if(b)return function(a,c){a.model[b]=c}},prepareBinding:function(a,c,d){var e=Path.get(a);{if(o(a)||!e.valid)return b(a,c,d,this);if(1==e.length)return function(a,b,c){if(c)return e.getValueFrom(a);var d=n(a,e[0]);return new PathObserver(d,e)}}},prepareInstanceModel:function(a){var b=a.polymerExpressionScopeIdent_;if(b){var c=a.templateInstance?a.templateInstance.model:a.model,d=a.polymerExpressionIndexIdent_;return function(a){return u(c,a,b,d)}}}};var u="__proto__"in{}?function(a,b,c,d){var e={};return e[c]=b,e[d]=void 0,e[t]=a,e.__proto__=a,e}:function(a,b,c,d){var e=Object.create(a);return Object.defineProperty(e,c,{value:b,configurable:!0,writable:!0}),Object.defineProperty(e,d,{value:void 0,configurable:!0,writable:!0}),Object.defineProperty(e,t,{value:a,configurable:!0,writable:!0}),e};a.PolymerExpressions=p,p.getExpression=c}(this),Polymer={version:"0.5.5"},"function"==typeof window.Polymer&&(Polymer={}),function(a){function b(a,b){return b=b||[],b.map||(b=[b]),a.apply(this,b.map(d))}function c(a,c,d){var e;switch(arguments.length){case 0:return;case 1:e=null;break;case 2:e=c.apply(this);break;default:e=b(d,c)}f[a]=e}function d(a){return f[a]}function e(a,c){HTMLImports.whenImportsReady(function(){b(c,a)})}var f={};a.marshal=d,a.modularize=c,a.using=e}(window),window.WebComponents||(window.WebComponents||(WebComponents={flush:function(){},flags:{log:{}}},Platform=WebComponents,CustomElements={useNative:!0,ready:!0,takeRecords:function(){},"instanceof":function(a,b){return a instanceof b}},HTMLImports={useNative:!0},addEventListener("HTMLImportsLoaded",function(){document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))}),ShadowDOMPolyfill=null,wrap=unwrap=function(a){return a}),window.HTMLImports=window.HTMLImports||{flags:{}},function(a){function b(a,b){b=b||o,d(function(){f(a,b)},b)}function c(a){return"complete"===a.readyState||a.readyState===r}function d(a,b){if(c(b))a&&a();else{var e=function(){("complete"===b.readyState||b.readyState===r)&&(b.removeEventListener(s,e),d(a,b))};b.addEventListener(s,e)}}function e(a){a.target.__loaded=!0}function f(a,b){function c(){h==i&&a&&a()}function d(a){e(a),h++,c()}var f=b.querySelectorAll("link[rel=import]"),h=0,i=f.length;if(i)for(var j,k=0;i>k&&(j=f[k]);k++)g(j)?d.call(j,{target:j}):(j.addEventListener("load",d),j.addEventListener("error",d));else c()}function g(a){return l?a.__loaded||a["import"]&&"loading"!==a["import"].readyState:a.__importParsed}function h(a){for(var b,c=0,d=a.length;d>c&&(b=a[c]);c++)i(b)&&j(b)}function i(a){return"link"===a.localName&&"import"===a.rel}function j(a){var b=a["import"];b?e({target:a}):(a.addEventListener("load",e),a.addEventListener("error",e))}var k="import",l=Boolean(k in document.createElement("link")),m=Boolean(window.ShadowDOMPolyfill),n=function(a){return m?ShadowDOMPolyfill.wrapIfNeeded(a):a},o=n(document),p={get:function(){var a=HTMLImports.currentScript||document.currentScript||("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null);return n(a)},configurable:!0};Object.defineProperty(document,"_currentScript",p),Object.defineProperty(o,"_currentScript",p);var q=/Trident/.test(navigator.userAgent),r=q?"complete":"interactive",s="readystatechange";l&&(new MutationObserver(function(a){for(var b,c=0,d=a.length;d>c&&(b=a[c]);c++)b.addedNodes&&h(b.addedNodes)}).observe(document.head,{childList:!0}),function(){if("loading"===document.readyState)for(var a,b=document.querySelectorAll("link[rel=import]"),c=0,d=b.length;d>c&&(a=b[c]);c++)j(a)}()),b(function(){HTMLImports.ready=!0,HTMLImports.readyTime=(new Date).getTime(),o.dispatchEvent(new CustomEvent("HTMLImportsLoaded",{bubbles:!0}))}),a.IMPORT_LINK_TYPE=k,a.useNative=l,a.rootDocument=o,a.whenReady=b,a.isIE=q}(HTMLImports),function(){var a=document.createElement("style");a.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; } \n";var b=document.querySelector("head");b.insertBefore(a,b.firstChild)}(Platform)),function(a){"use strict";function b(){function a(a){b=a}if("function"!=typeof Object.observe||"function"!=typeof Array.observe)return!1;var b=[],c={},d=[];return Object.observe(c,a),Array.observe(d,a),c.id=1,c.id=2,delete c.id,d.push(1,2),d.length=0,Object.deliverChangeRecords(a),5!==b.length?!1:"add"!=b[0].type||"update"!=b[1].type||"delete"!=b[2].type||"splice"!=b[3].type||"splice"!=b[4].type?!1:(Object.unobserve(c,a),Array.unobserve(d,a),!0)}function c(){if("undefined"!=typeof chrome&&chrome.app&&chrome.app.runtime)return!1;if("undefined"!=typeof navigator&&navigator.getDeviceStorage)return!1;try{var a=new Function("","return true;");return a()}catch(b){return!1}}function d(a){return+a===a>>>0&&""!==a}function e(a){return+a}function f(a){return a===Object(a)}function g(a,b){return a===b?0!==a||1/a===1/b:R(a)&&R(b)?!0:a!==a&&b!==b}function h(a){if(void 0===a)return"eof";var b=a.charCodeAt(0);switch(b){case 91:case 93:case 46:case 34:case 39:case 48:return a;case 95:case 36:return"ident";case 32:case 9:case 10:case 13:case 160:case 65279:case 8232:case 8233:return"ws"}return b>=97&&122>=b||b>=65&&90>=b?"ident":b>=49&&57>=b?"number":"else"}function i(){}function j(a){function b(){if(!(m>=a.length)){var b=a[m+1];return"inSingleQuote"==n&&"'"==b||"inDoubleQuote"==n&&'"'==b?(m++,d=b,o.append(),!0):void 0}}for(var c,d,e,f,g,j,k,l=[],m=-1,n="beforePath",o={push:function(){void 0!==e&&(l.push(e),e=void 0)},append:function(){void 0===e?e=d:e+=d}};n;)if(m++,c=a[m],"\\"!=c||!b(n)){if(f=h(c),k=W[n],g=k[f]||k["else"]||"error","error"==g)return;if(n=g[0],j=o[g[1]]||i,d=void 0===g[2]?c:g[2],j(),"afterPath"===n)return l}}function k(a){return V.test(a)}function l(a,b){if(b!==X)throw Error("Use Path.get to retrieve path objects");for(var c=0;c<a.length;c++)this.push(String(a[c]));Q&&this.length&&(this.getValueFrom=this.compiledGetValueFromFn())}function m(a){if(a instanceof l)return a;if((null==a||0==a.length)&&(a=""),"string"!=typeof a){if(d(a.length))return new l(a,X);a=String(a)}var b=Y[a];if(b)return b;var c=j(a);if(!c)return Z;var b=new l(c,X);return Y[a]=b,b}function n(a){return d(a)?"["+a+"]":'["'+a.replace(/"/g,'\\"')+'"]'}function o(b){for(var c=0;_>c&&b.check_();)c++;return O&&(a.dirtyCheckCycleCount=c),c>0}function p(a){for(var b in a)return!1;return!0}function q(a){return p(a.added)&&p(a.removed)&&p(a.changed)}function r(a,b){var c={},d={},e={};for(var f in b){var g=a[f];(void 0===g||g!==b[f])&&(f in a?g!==b[f]&&(e[f]=g):d[f]=void 0)}for(var f in a)f in b||(c[f]=a[f]);return Array.isArray(a)&&a.length!==b.length&&(e.length=a.length),{added:c,removed:d,changed:e}}function s(){if(!ab.length)return!1;for(var a=0;a<ab.length;a++)ab[a]();return ab.length=0,!0}function t(){function a(a){b&&b.state_===fb&&!d&&b.check_(a)}var b,c,d=!1,e=!0;return{open:function(c){if(b)throw Error("ObservedObject in use");e||Object.deliverChangeRecords(a),b=c,e=!1},observe:function(b,d){c=b,d?Array.observe(c,a):Object.observe(c,a)},deliver:function(b){d=b,Object.deliverChangeRecords(a),d=!1},close:function(){b=void 0,Object.unobserve(c,a),cb.push(this)}}}function u(a,b,c){var d=cb.pop()||t();return d.open(a),d.observe(b,c),d}function v(){function a(b,f){b&&(b===d&&(e[f]=!0),h.indexOf(b)<0&&(h.push(b),Object.observe(b,c)),a(Object.getPrototypeOf(b),f))}function b(a){for(var b=0;b<a.length;b++){var c=a[b];if(c.object!==d||e[c.name]||"setPrototype"===c.type)return!1}return!0}function c(c){if(!b(c)){for(var d,e=0;e<g.length;e++)d=g[e],d.state_==fb&&d.iterateObjects_(a);for(var e=0;e<g.length;e++)d=g[e],d.state_==fb&&d.check_()}}var d,e,f=0,g=[],h=[],i={objects:h,get rootObject(){return d},set rootObject(a){d=a,e={}},open:function(b){g.push(b),f++,b.iterateObjects_(a)},close:function(){if(f--,!(f>0)){for(var a=0;a<h.length;a++)Object.unobserve(h[a],c),x.unobservedCount++;g.length=0,h.length=0,d=void 0,e=void 0,db.push(this),$===this&&($=null)}}};return i}function w(a,b){return $&&$.rootObject===b||($=db.pop()||v(),$.rootObject=b),$.open(a,b),$}function x(){this.state_=eb,this.callback_=void 0,this.target_=void 0,this.directObserver_=void 0,this.value_=void 0,this.id_=ib++}function y(a){x._allObserversCount++,kb&&jb.push(a)}function z(){x._allObserversCount--}function A(a){x.call(this),this.value_=a,this.oldObject_=void 0}function B(a){if(!Array.isArray(a))throw Error("Provided object is not an Array");A.call(this,a)}function C(a,b){x.call(this),this.object_=a,this.path_=m(b),this.directObserver_=void 0}function D(a){x.call(this),this.reportChangesOnOpen_=a,this.value_=[],this.directObserver_=void 0,this.observed_=[]}function E(a){return a}function F(a,b,c,d){this.callback_=void 0,this.target_=void 0,this.value_=void 0,this.observable_=a,this.getValueFn_=b||E,this.setValueFn_=c||E,this.dontPassThroughSet_=d}function G(a,b,c){for(var d={},e={},f=0;f<b.length;f++){var g=b[f];nb[g.type]?(g.name in c||(c[g.name]=g.oldValue),"update"!=g.type&&("add"!=g.type?g.name in d?(delete d[g.name],delete c[g.name]):e[g.name]=!0:g.name in e?delete e[g.name]:d[g.name]=!0)):(console.error("Unknown changeRecord type: "+g.type),console.error(g))}for(var h in d)d[h]=a[h];for(var h in e)e[h]=void 0;var i={};for(var h in c)if(!(h in d||h in e)){var j=a[h];c[h]!==j&&(i[h]=j)}return{added:d,removed:e,changed:i}}function H(a,b,c){return{index:a,removed:b,addedCount:c}}function I(){}function J(a,b,c,d,e,f){return sb.calcSplices(a,b,c,d,e,f)}function K(a,b,c,d){return c>b||a>d?-1:b==c||d==a?0:c>a?d>b?b-c:d-c:b>d?d-a:b-a}function L(a,b,c,d){for(var e=H(b,c,d),f=!1,g=0,h=0;h<a.length;h++){var i=a[h];if(i.index+=g,!f){var j=K(e.index,e.index+e.removed.length,i.index,i.index+i.addedCount);if(j>=0){a.splice(h,1),h--,g-=i.addedCount-i.removed.length,e.addedCount+=i.addedCount-j;var k=e.removed.length+i.removed.length-j;if(e.addedCount||k){var c=i.removed;if(e.index<i.index){var l=e.removed.slice(0,i.index-e.index);Array.prototype.push.apply(l,c),c=l}if(e.index+e.removed.length>i.index+i.addedCount){var m=e.removed.slice(i.index+i.addedCount-e.index);Array.prototype.push.apply(c,m)}e.removed=c,i.index<e.index&&(e.index=i.index)}else f=!0}else if(e.index<i.index){f=!0,a.splice(h,0,e),h++;var n=e.addedCount-e.removed.length;i.index+=n,g+=n}}}f||a.push(e)}function M(a,b){for(var c=[],f=0;f<b.length;f++){var g=b[f];switch(g.type){case"splice":L(c,g.index,g.removed.slice(),g.addedCount);break;case"add":case"update":case"delete":if(!d(g.name))continue;var h=e(g.name);if(0>h)continue;L(c,h,[g.oldValue],1);break;default:console.error("Unexpected record type: "+JSON.stringify(g))}}return c}function N(a,b){var c=[];return M(a,b).forEach(function(b){return 1==b.addedCount&&1==b.removed.length?void(b.removed[0]!==a[b.index]&&c.push(b)):void(c=c.concat(J(a,b.index,b.index+b.addedCount,b.removed,0,b.removed.length)))}),c}var O=a.testingExposeCycleCount,P=b(),Q=c(),R=a.Number.isNaN||function(b){return"number"==typeof b&&a.isNaN(b)},S="__proto__"in{}?function(a){return a}:function(a){var b=a.__proto__;if(!b)return a;var c=Object.create(b);return Object.getOwnPropertyNames(a).forEach(function(b){Object.defineProperty(c,b,Object.getOwnPropertyDescriptor(a,b))}),c},T="[$_a-zA-Z]",U="[$_a-zA-Z0-9]",V=new RegExp("^"+T+"+"+U+"*$"),W={beforePath:{ws:["beforePath"],ident:["inIdent","append"],"[":["beforeElement"],eof:["afterPath"]},inPath:{ws:["inPath"],".":["beforeIdent"],"[":["beforeElement"],eof:["afterPath"]},beforeIdent:{ws:["beforeIdent"],ident:["inIdent","append"]},inIdent:{ident:["inIdent","append"],0:["inIdent","append"],number:["inIdent","append"],ws:["inPath","push"],".":["beforeIdent","push"],"[":["beforeElement","push"],eof:["afterPath","push"]},beforeElement:{ws:["beforeElement"],0:["afterZero","append"],number:["inIndex","append"],"'":["inSingleQuote","append",""],'"':["inDoubleQuote","append",""]},afterZero:{ws:["afterElement","push"],"]":["inPath","push"]},inIndex:{0:["inIndex","append"],number:["inIndex","append"],ws:["afterElement"],"]":["inPath","push"]},inSingleQuote:{"'":["afterElement"],eof:["error"],"else":["inSingleQuote","append"]},inDoubleQuote:{'"':["afterElement"],eof:["error"],"else":["inDoubleQuote","append"]},afterElement:{ws:["afterElement"],"]":["inPath","push"]}},X={},Y={};l.get=m,l.prototype=S({__proto__:[],valid:!0,toString:function(){for(var a="",b=0;b<this.length;b++){var c=this[b];a+=k(c)?b?"."+c:c:n(c)}return a},getValueFrom:function(a){for(var b=0;b<this.length;b++){if(null==a)return;a=a[this[b]]}return a},iterateObjects:function(a,b){for(var c=0;c<this.length;c++){if(c&&(a=a[this[c-1]]),!f(a))return;b(a,this[c])}},compiledGetValueFromFn:function(){var a="",b="obj";a+="if (obj != null";for(var c,d=0;d<this.length-1;d++)c=this[d],b+=k(c)?"."+c:n(c),a+=" &&\n     "+b+" != null";a+=")\n";var c=this[d];return b+=k(c)?"."+c:n(c),a+="  return "+b+";\nelse\n  return undefined;",new Function("obj",a)},setValueFrom:function(a,b){if(!this.length)return!1;for(var c=0;c<this.length-1;c++){if(!f(a))return!1;a=a[this[c]]}return f(a)?(a[this[c]]=b,!0):!1}});var Z=new l("",X);Z.valid=!1,Z.getValueFrom=Z.setValueFrom=function(){};var $,_=1e3,ab=[],bb=P?function(){return function(a){return Promise.resolve().then(a)}}():function(){return function(a){ab.push(a)}}(),cb=[],db=[],eb=0,fb=1,gb=2,hb=3,ib=1;x.prototype={open:function(a,b){if(this.state_!=eb)throw Error("Observer has already been opened.");return y(this),this.callback_=a,this.target_=b,this.connect_(),this.state_=fb,this.value_},close:function(){this.state_==fb&&(z(this),this.disconnect_(),this.value_=void 0,this.callback_=void 0,this.target_=void 0,this.state_=gb)},deliver:function(){this.state_==fb&&o(this)},report_:function(a){try{this.callback_.apply(this.target_,a)}catch(b){x._errorThrownDuringCallback=!0,console.error("Exception caught during observer callback: "+(b.stack||b))}},discardChanges:function(){return this.check_(void 0,!0),this.value_}};var jb,kb=!P;x._allObserversCount=0,kb&&(jb=[]);var lb=!1;a.Platform=a.Platform||{},a.Platform.performMicrotaskCheckpoint=function(){if(!lb&&kb){lb=!0;var b,c,d=0;do{d++,c=jb,jb=[],b=!1;for(var e=0;e<c.length;e++){var f=c[e];f.state_==fb&&(f.check_()&&(b=!0),jb.push(f))}s()&&(b=!0)}while(_>d&&b);O&&(a.dirtyCheckCycleCount=d),lb=!1}},kb&&(a.Platform.clearObservers=function(){jb=[]}),A.prototype=S({__proto__:x.prototype,arrayObserve:!1,connect_:function(){P?this.directObserver_=u(this,this.value_,this.arrayObserve):this.oldObject_=this.copyObject(this.value_)},copyObject:function(a){var b=Array.isArray(a)?[]:{};for(var c in a)b[c]=a[c];return Array.isArray(a)&&(b.length=a.length),b},check_:function(a){var b,c;if(P){if(!a)return!1;c={},b=G(this.value_,a,c)}else c=this.oldObject_,b=r(this.value_,this.oldObject_);return q(b)?!1:(P||(this.oldObject_=this.copyObject(this.value_)),this.report_([b.added||{},b.removed||{},b.changed||{},function(a){return c[a]}]),!0)},disconnect_:function(){P?(this.directObserver_.close(),this.directObserver_=void 0):this.oldObject_=void 0},deliver:function(){this.state_==fb&&(P?this.directObserver_.deliver(!1):o(this))},discardChanges:function(){return this.directObserver_?this.directObserver_.deliver(!0):this.oldObject_=this.copyObject(this.value_),this.value_}}),B.prototype=S({__proto__:A.prototype,arrayObserve:!0,copyObject:function(a){return a.slice()},check_:function(a){var b;if(P){if(!a)return!1;b=N(this.value_,a)}else b=J(this.value_,0,this.value_.length,this.oldObject_,0,this.oldObject_.length);return b&&b.length?(P||(this.oldObject_=this.copyObject(this.value_)),this.report_([b]),!0):!1}}),B.applySplices=function(a,b,c){c.forEach(function(c){for(var d=[c.index,c.removed.length],e=c.index;e<c.index+c.addedCount;)d.push(b[e]),e++;Array.prototype.splice.apply(a,d)})},C.prototype=S({__proto__:x.prototype,get path(){return this.path_},connect_:function(){P&&(this.directObserver_=w(this,this.object_)),this.check_(void 0,!0)},disconnect_:function(){this.value_=void 0,this.directObserver_&&(this.directObserver_.close(this),this.directObserver_=void 0)},iterateObjects_:function(a){this.path_.iterateObjects(this.object_,a)},check_:function(a,b){var c=this.value_;return this.value_=this.path_.getValueFrom(this.object_),b||g(this.value_,c)?!1:(this.report_([this.value_,c,this]),!0)},setValue:function(a){this.path_&&this.path_.setValueFrom(this.object_,a)}});var mb={};D.prototype=S({__proto__:x.prototype,connect_:function(){if(P){for(var a,b=!1,c=0;c<this.observed_.length;c+=2)if(a=this.observed_[c],a!==mb){b=!0;break}b&&(this.directObserver_=w(this,a))}this.check_(void 0,!this.reportChangesOnOpen_)},disconnect_:function(){for(var a=0;a<this.observed_.length;a+=2)this.observed_[a]===mb&&this.observed_[a+1].close();this.observed_.length=0,this.value_.length=0,this.directObserver_&&(this.directObserver_.close(this),this.directObserver_=void 0)},addPath:function(a,b){if(this.state_!=eb&&this.state_!=hb)throw Error("Cannot add paths once started.");var b=m(b);if(this.observed_.push(a,b),this.reportChangesOnOpen_){var c=this.observed_.length/2-1;this.value_[c]=b.getValueFrom(a)}},addObserver:function(a){if(this.state_!=eb&&this.state_!=hb)throw Error("Cannot add observers once started.");if(this.observed_.push(mb,a),this.reportChangesOnOpen_){var b=this.observed_.length/2-1;this.value_[b]=a.open(this.deliver,this)}},startReset:function(){if(this.state_!=fb)throw Error("Can only reset while open");this.state_=hb,this.disconnect_()},finishReset:function(){if(this.state_!=hb)throw Error("Can only finishReset after startReset");return this.state_=fb,this.connect_(),this.value_},iterateObjects_:function(a){for(var b,c=0;c<this.observed_.length;c+=2)b=this.observed_[c],b!==mb&&this.observed_[c+1].iterateObjects(b,a)},check_:function(a,b){for(var c,d=0;d<this.observed_.length;d+=2){var e,f=this.observed_[d],h=this.observed_[d+1];if(f===mb){var i=h;e=this.state_===eb?i.open(this.deliver,this):i.discardChanges()}else e=h.getValueFrom(f);b?this.value_[d/2]=e:g(e,this.value_[d/2])||(c=c||[],c[d/2]=this.value_[d/2],this.value_[d/2]=e)}return c?(this.report_([this.value_,c,this.observed_]),!0):!1}}),F.prototype={open:function(a,b){return this.callback_=a,this.target_=b,this.value_=this.getValueFn_(this.observable_.open(this.observedCallback_,this)),this.value_},observedCallback_:function(a){if(a=this.getValueFn_(a),!g(a,this.value_)){var b=this.value_;this.value_=a,this.callback_.call(this.target_,this.value_,b)}},discardChanges:function(){return this.value_=this.getValueFn_(this.observable_.discardChanges()),this.value_},deliver:function(){return this.observable_.deliver()},setValue:function(a){return a=this.setValueFn_(a),!this.dontPassThroughSet_&&this.observable_.setValue?this.observable_.setValue(a):void 0},close:function(){this.observable_&&this.observable_.close(),this.callback_=void 0,this.target_=void 0,this.observable_=void 0,this.value_=void 0,this.getValueFn_=void 0,this.setValueFn_=void 0}};var nb={add:!0,update:!0,"delete":!0},ob=0,pb=1,qb=2,rb=3;I.prototype={calcEditDistances:function(a,b,c,d,e,f){for(var g=f-e+1,h=c-b+1,i=new Array(g),j=0;g>j;j++)i[j]=new Array(h),i[j][0]=j;for(var k=0;h>k;k++)i[0][k]=k;for(var j=1;g>j;j++)for(var k=1;h>k;k++)if(this.equals(a[b+k-1],d[e+j-1]))i[j][k]=i[j-1][k-1];else{var l=i[j-1][k]+1,m=i[j][k-1]+1;i[j][k]=m>l?l:m}return i},spliceOperationsFromEditDistances:function(a){for(var b=a.length-1,c=a[0].length-1,d=a[b][c],e=[];b>0||c>0;)if(0!=b)if(0!=c){var f,g=a[b-1][c-1],h=a[b-1][c],i=a[b][c-1];f=i>h?g>h?h:g:g>i?i:g,f==g?(g==d?e.push(ob):(e.push(pb),d=g),b--,c--):f==h?(e.push(rb),b--,d=h):(e.push(qb),c--,d=i)}else e.push(rb),b--;else e.push(qb),c--;return e.reverse(),e},calcSplices:function(a,b,c,d,e,f){var g=0,h=0,i=Math.min(c-b,f-e);if(0==b&&0==e&&(g=this.sharedPrefix(a,d,i)),c==a.length&&f==d.length&&(h=this.sharedSuffix(a,d,i-g)),b+=g,e+=g,c-=h,f-=h,c-b==0&&f-e==0)return[];if(b==c){for(var j=H(b,[],0);f>e;)j.removed.push(d[e++]);return[j]}if(e==f)return[H(b,[],c-b)];for(var k=this.spliceOperationsFromEditDistances(this.calcEditDistances(a,b,c,d,e,f)),j=void 0,l=[],m=b,n=e,o=0;o<k.length;o++)switch(k[o]){case ob:j&&(l.push(j),j=void 0),m++,n++;break;case pb:j||(j=H(m,[],0)),j.addedCount++,m++,j.removed.push(d[n]),n++;break;case qb:j||(j=H(m,[],0)),j.addedCount++,m++;break;case rb:j||(j=H(m,[],0)),j.removed.push(d[n]),n++}return j&&l.push(j),l},sharedPrefix:function(a,b,c){for(var d=0;c>d;d++)if(!this.equals(a[d],b[d]))return d;return c},sharedSuffix:function(a,b,c){for(var d=a.length,e=b.length,f=0;c>f&&this.equals(a[--d],b[--e]);)f++;return f},calculateSplices:function(a,b){return this.calcSplices(a,0,a.length,b,0,b.length)},equals:function(a,b){return a===b}};var sb=new I,tb=a;"undefined"==typeof exports||exports.nodeType||("undefined"!=typeof module&&module.exports&&(exports=module.exports),tb=exports),tb.Observer=x,tb.Observer.runEOM_=bb,tb.Observer.observerSentinel_=mb,tb.Observer.hasObjectObserve=P,tb.ArrayObserver=B,tb.ArrayObserver.calculateSplices=function(a,b){return sb.calculateSplices(a,b)},tb.ArraySplice=I,tb.ObjectObserver=A,tb.PathObserver=C,tb.CompoundObserver=D,tb.Path=l,tb.ObserverTransform=F}("undefined"!=typeof global&&global&&"undefined"!=typeof module&&module?global:this||window),function(){"use strict";function a(a){for(;a.parentNode;)a=a.parentNode;return"function"==typeof a.getElementById?a:null}function b(a,b,c){var d=a.bindings_;return d||(d=a.bindings_={}),d[b]&&c[b].close(),d[b]=c}function c(a,b,c){return c}function d(a){return null==a?"":a}function e(a,b){a.data=d(b)}function f(a){return function(b){return e(a,b)}}function g(a,b,c,e){return c?void(e?a.setAttribute(b,""):a.removeAttribute(b)):void a.setAttribute(b,d(e))}function h(a,b,c){return function(d){g(a,b,c,d)}}function i(a){switch(a.type){case"checkbox":return u;case"radio":case"select-multiple":case"select-one":return"change";case"range":if(/Trident|MSIE/.test(navigator.userAgent))return"change";default:return"input"}}function j(a,b,c,e){a[b]=(e||d)(c)}function k(a,b,c){return function(d){return j(a,b,d,c)}}function l(){}function m(a,b,c,d){function e(){var e="value"==b&&"number"==a.type;c.setValue(e?a.valueAsNumber:a[b]),c.discardChanges(),(d||l)(a),Platform.performMicrotaskCheckpoint()}var f=i(a);return a.addEventListener(f,e),{close:function(){a.removeEventListener(f,e),c.close()},observable_:c}}function n(a){return Boolean(a)}function o(b){if(b.form)return s(b.form.elements,function(a){return a!=b&&"INPUT"==a.tagName&&"radio"==a.type&&a.name==b.name});var c=a(b);if(!c)return[];var d=c.querySelectorAll('input[type="radio"][name="'+b.name+'"]');return s(d,function(a){return a!=b&&!a.form})}function p(a){"INPUT"===a.tagName&&"radio"===a.type&&o(a).forEach(function(a){var b=a.bindings_.checked;b&&b.observable_.setValue(!1)})}function q(a,b){var c,e,f,g=a.parentNode;g instanceof HTMLSelectElement&&g.bindings_&&g.bindings_.value&&(c=g,e=c.bindings_.value,f=c.value),a.value=d(b),c&&c.value!=f&&(e.observable_.setValue(c.value),e.observable_.discardChanges(),Platform.performMicrotaskCheckpoint())}function r(a){return function(b){q(a,b)}}var s=Array.prototype.filter.call.bind(Array.prototype.filter);Node.prototype.bind=function(a,b){console.error("Unhandled binding to Node: ",this,a,b)},Node.prototype.bindFinished=function(){};var t=c;Object.defineProperty(Platform,"enableBindingsReflection",{get:function(){return t===b},set:function(a){return t=a?b:c,a},configurable:!0}),Text.prototype.bind=function(a,b,c){if("textContent"!==a)return Node.prototype.bind.call(this,a,b,c);if(c)return e(this,b);var d=b;return e(this,d.open(f(this))),t(this,a,d)},Element.prototype.bind=function(a,b,c){var d="?"==a[a.length-1];if(d&&(this.removeAttribute(a),a=a.slice(0,-1)),c)return g(this,a,d,b);var e=b;return g(this,a,d,e.open(h(this,a,d))),t(this,a,e)};var u;!function(){var a=document.createElement("div"),b=a.appendChild(document.createElement("input"));b.setAttribute("type","checkbox");var c,d=0;b.addEventListener("click",function(){d++,c=c||"click"}),b.addEventListener("change",function(){d++,c=c||"change"});var e=document.createEvent("MouseEvent");e.initMouseEvent("click",!0,!0,window,0,0,0,0,0,!1,!1,!1,!1,0,null),b.dispatchEvent(e),u=1==d?"change":c}(),HTMLInputElement.prototype.bind=function(a,c,e){if("value"!==a&&"checked"!==a)return HTMLElement.prototype.bind.call(this,a,c,e);this.removeAttribute(a);var f="checked"==a?n:d,g="checked"==a?p:l;if(e)return j(this,a,c,f);var h=c,i=m(this,a,h,g);return j(this,a,h.open(k(this,a,f)),f),b(this,a,i)},HTMLTextAreaElement.prototype.bind=function(a,b,c){if("value"!==a)return HTMLElement.prototype.bind.call(this,a,b,c);if(this.removeAttribute("value"),c)return j(this,"value",b);var e=b,f=m(this,"value",e);return j(this,"value",e.open(k(this,"value",d))),t(this,a,f)},HTMLOptionElement.prototype.bind=function(a,b,c){if("value"!==a)return HTMLElement.prototype.bind.call(this,a,b,c);if(this.removeAttribute("value"),c)return q(this,b);var d=b,e=m(this,"value",d);
+return q(this,d.open(r(this))),t(this,a,e)},HTMLSelectElement.prototype.bind=function(a,c,d){if("selectedindex"===a&&(a="selectedIndex"),"selectedIndex"!==a&&"value"!==a)return HTMLElement.prototype.bind.call(this,a,c,d);if(this.removeAttribute(a),d)return j(this,a,c);var e=c,f=m(this,a,e);return j(this,a,e.open(k(this,a))),b(this,a,f)}}(this),function(a){"use strict";function b(a){if(!a)throw new Error("Assertion failed")}function c(a){for(var b;b=a.parentNode;)a=b;return a}function d(a,b){if(b){for(var d,e="#"+b;!d&&(a=c(a),a.protoContent_?d=a.protoContent_.querySelector(e):a.getElementById&&(d=a.getElementById(b)),!d&&a.templateCreator_);)a=a.templateCreator_;return d}}function e(a){return"template"==a.tagName&&"http://www.w3.org/2000/svg"==a.namespaceURI}function f(a){return"TEMPLATE"==a.tagName&&"http://www.w3.org/1999/xhtml"==a.namespaceURI}function g(a){return Boolean(L[a.tagName]&&a.hasAttribute("template"))}function h(a){return void 0===a.isTemplate_&&(a.isTemplate_="TEMPLATE"==a.tagName||g(a)),a.isTemplate_}function i(a,b){var c=a.querySelectorAll(N);h(a)&&b(a),G(c,b)}function j(a){function b(a){HTMLTemplateElement.decorate(a)||j(a.content)}i(a,b)}function k(a,b){Object.getOwnPropertyNames(b).forEach(function(c){Object.defineProperty(a,c,Object.getOwnPropertyDescriptor(b,c))})}function l(a){var b=a.ownerDocument;if(!b.defaultView)return b;var c=b.templateContentsOwner_;if(!c){for(c=b.implementation.createHTMLDocument("");c.lastChild;)c.removeChild(c.lastChild);b.templateContentsOwner_=c}return c}function m(a){if(!a.stagingDocument_){var b=a.ownerDocument;if(!b.stagingDocument_){b.stagingDocument_=b.implementation.createHTMLDocument(""),b.stagingDocument_.isStagingDocument=!0;var c=b.stagingDocument_.createElement("base");c.href=document.baseURI,b.stagingDocument_.head.appendChild(c),b.stagingDocument_.stagingDocument_=b.stagingDocument_}a.stagingDocument_=b.stagingDocument_}return a.stagingDocument_}function n(a){var b=a.ownerDocument.createElement("template");a.parentNode.insertBefore(b,a);for(var c=a.attributes,d=c.length;d-->0;){var e=c[d];K[e.name]&&("template"!==e.name&&b.setAttribute(e.name,e.value),a.removeAttribute(e.name))}return b}function o(a){var b=a.ownerDocument.createElement("template");a.parentNode.insertBefore(b,a);for(var c=a.attributes,d=c.length;d-->0;){var e=c[d];b.setAttribute(e.name,e.value),a.removeAttribute(e.name)}return a.parentNode.removeChild(a),b}function p(a,b,c){var d=a.content;if(c)return void d.appendChild(b);for(var e;e=b.firstChild;)d.appendChild(e)}function q(a){P?a.__proto__=HTMLTemplateElement.prototype:k(a,HTMLTemplateElement.prototype)}function r(a){a.setModelFn_||(a.setModelFn_=function(){a.setModelFnScheduled_=!1;var b=z(a,a.delegate_&&a.delegate_.prepareBinding);w(a,b,a.model_)}),a.setModelFnScheduled_||(a.setModelFnScheduled_=!0,Observer.runEOM_(a.setModelFn_))}function s(a,b,c,d){if(a&&a.length){for(var e,f=a.length,g=0,h=0,i=0,j=!0;f>h;){var g=a.indexOf("{{",h),k=a.indexOf("[[",h),l=!1,m="}}";if(k>=0&&(0>g||g>k)&&(g=k,l=!0,m="]]"),i=0>g?-1:a.indexOf(m,g+2),0>i){if(!e)return;e.push(a.slice(h));break}e=e||[],e.push(a.slice(h,g));var n=a.slice(g+2,i).trim();e.push(l),j=j&&l;var o=d&&d(n,b,c);e.push(null==o?Path.get(n):null),e.push(o),h=i+2}return h===f&&e.push(""),e.hasOnePath=5===e.length,e.isSimplePath=e.hasOnePath&&""==e[0]&&""==e[4],e.onlyOneTime=j,e.combinator=function(a){for(var b=e[0],c=1;c<e.length;c+=4){var d=e.hasOnePath?a:a[(c-1)/4];void 0!==d&&(b+=d),b+=e[c+3]}return b},e}}function t(a,b,c,d){if(b.hasOnePath){var e=b[3],f=e?e(d,c,!0):b[2].getValueFrom(d);return b.isSimplePath?f:b.combinator(f)}for(var g=[],h=1;h<b.length;h+=4){var e=b[h+2];g[(h-1)/4]=e?e(d,c):b[h+1].getValueFrom(d)}return b.combinator(g)}function u(a,b,c,d){var e=b[3],f=e?e(d,c,!1):new PathObserver(d,b[2]);return b.isSimplePath?f:new ObserverTransform(f,b.combinator)}function v(a,b,c,d){if(b.onlyOneTime)return t(a,b,c,d);if(b.hasOnePath)return u(a,b,c,d);for(var e=new CompoundObserver,f=1;f<b.length;f+=4){var g=b[f],h=b[f+2];if(h){var i=h(d,c,g);g?e.addPath(i):e.addObserver(i)}else{var j=b[f+1];g?e.addPath(j.getValueFrom(d)):e.addPath(d,j)}}return new ObserverTransform(e,b.combinator)}function w(a,b,c,d){for(var e=0;e<b.length;e+=2){var f=b[e],g=b[e+1],h=v(f,g,a,c),i=a.bind(f,h,g.onlyOneTime);i&&d&&d.push(i)}if(a.bindFinished(),b.isTemplate){a.model_=c;var j=a.processBindingDirectives_(b);d&&j&&d.push(j)}}function x(a,b,c){var d=a.getAttribute(b);return s(""==d?"{{}}":d,b,a,c)}function y(a,c){b(a);for(var d=[],e=0;e<a.attributes.length;e++){for(var f=a.attributes[e],g=f.name,i=f.value;"_"===g[0];)g=g.substring(1);if(!h(a)||g!==J&&g!==H&&g!==I){var j=s(i,g,a,c);j&&d.push(g,j)}}return h(a)&&(d.isTemplate=!0,d["if"]=x(a,J,c),d.bind=x(a,H,c),d.repeat=x(a,I,c),!d["if"]||d.bind||d.repeat||(d.bind=s("{{}}",H,a,c))),d}function z(a,b){if(a.nodeType===Node.ELEMENT_NODE)return y(a,b);if(a.nodeType===Node.TEXT_NODE){var c=s(a.data,"textContent",a,b);if(c)return["textContent",c]}return[]}function A(a,b,c,d,e,f,g){for(var h=b.appendChild(c.importNode(a,!1)),i=0,j=a.firstChild;j;j=j.nextSibling)A(j,h,c,d.children[i++],e,f,g);return d.isTemplate&&(HTMLTemplateElement.decorate(h,a),f&&h.setDelegate_(f)),w(h,d,e,g),h}function B(a,b){var c=z(a,b);c.children={};for(var d=0,e=a.firstChild;e;e=e.nextSibling)c.children[d++]=B(e,b);return c}function C(a){var b=a.id_;return b||(b=a.id_=S++),b}function D(a,b){var c=C(a);if(b){var d=b.bindingMaps[c];return d||(d=b.bindingMaps[c]=B(a,b.prepareBinding)||[]),d}var d=a.bindingMap_;return d||(d=a.bindingMap_=B(a,void 0)||[]),d}function E(a){this.closed=!1,this.templateElement_=a,this.instances=[],this.deps=void 0,this.iteratedValue=[],this.presentValue=void 0,this.arrayObserver=void 0}var F,G=Array.prototype.forEach.call.bind(Array.prototype.forEach);a.Map&&"function"==typeof a.Map.prototype.forEach?F=a.Map:(F=function(){this.keys=[],this.values=[]},F.prototype={set:function(a,b){var c=this.keys.indexOf(a);0>c?(this.keys.push(a),this.values.push(b)):this.values[c]=b},get:function(a){var b=this.keys.indexOf(a);if(!(0>b))return this.values[b]},"delete":function(a){var b=this.keys.indexOf(a);return 0>b?!1:(this.keys.splice(b,1),this.values.splice(b,1),!0)},forEach:function(a,b){for(var c=0;c<this.keys.length;c++)a.call(b||this,this.values[c],this.keys[c],this)}});"function"!=typeof document.contains&&(Document.prototype.contains=function(a){return a===this||a.parentNode===this?!0:this.documentElement.contains(a)});var H="bind",I="repeat",J="if",K={template:!0,repeat:!0,bind:!0,ref:!0,"if":!0},L={THEAD:!0,TBODY:!0,TFOOT:!0,TH:!0,TR:!0,TD:!0,COLGROUP:!0,COL:!0,CAPTION:!0,OPTION:!0,OPTGROUP:!0},M="undefined"!=typeof HTMLTemplateElement;M&&!function(){var a=document.createElement("template"),b=a.content.ownerDocument,c=b.appendChild(b.createElement("html")),d=c.appendChild(b.createElement("head")),e=b.createElement("base");e.href=document.baseURI,d.appendChild(e)}();var N="template, "+Object.keys(L).map(function(a){return a.toLowerCase()+"[template]"}).join(", ");document.addEventListener("DOMContentLoaded",function(){j(document),Platform.performMicrotaskCheckpoint()},!1),M||(a.HTMLTemplateElement=function(){throw TypeError("Illegal constructor")});var O,P="__proto__"in{};"function"==typeof MutationObserver&&(O=new MutationObserver(function(a){for(var b=0;b<a.length;b++)a[b].target.refChanged_()})),HTMLTemplateElement.decorate=function(a,c){if(a.templateIsDecorated_)return!1;var d=a;d.templateIsDecorated_=!0;var h=f(d)&&M,i=h,k=!h,m=!1;if(h||(g(d)?(b(!c),d=n(a),d.templateIsDecorated_=!0,h=M,m=!0):e(d)&&(d=o(a),d.templateIsDecorated_=!0,h=M)),!h){q(d);var r=l(d);d.content_=r.createDocumentFragment()}return c?d.instanceRef_=c:k?p(d,a,m):i&&j(d.content),!0},HTMLTemplateElement.bootstrap=j;var Q=a.HTMLUnknownElement||HTMLElement,R={get:function(){return this.content_},enumerable:!0,configurable:!0};M||(HTMLTemplateElement.prototype=Object.create(Q.prototype),Object.defineProperty(HTMLTemplateElement.prototype,"content",R)),k(HTMLTemplateElement.prototype,{bind:function(a,b,c){if("ref"!=a)return Element.prototype.bind.call(this,a,b,c);var d=this,e=c?b:b.open(function(a){d.setAttribute("ref",a),d.refChanged_()});return this.setAttribute("ref",e),this.refChanged_(),c?void 0:(this.bindings_?this.bindings_.ref=b:this.bindings_={ref:b},b)},processBindingDirectives_:function(a){return this.iterator_&&this.iterator_.closeDeps(),a["if"]||a.bind||a.repeat?(this.iterator_||(this.iterator_=new E(this)),this.iterator_.updateDependencies(a,this.model_),O&&O.observe(this,{attributes:!0,attributeFilter:["ref"]}),this.iterator_):void(this.iterator_&&(this.iterator_.close(),this.iterator_=void 0))},createInstance:function(a,b,c){b?c=this.newDelegate_(b):c||(c=this.delegate_),this.refContent_||(this.refContent_=this.ref_.content);var d=this.refContent_;if(null===d.firstChild)return T;var e=D(d,c),f=m(this),g=f.createDocumentFragment();g.templateCreator_=this,g.protoContent_=d,g.bindings_=[],g.terminator_=null;for(var h=g.templateInstance_={firstNode:null,lastNode:null,model:a},i=0,j=!1,k=d.firstChild;k;k=k.nextSibling){null===k.nextSibling&&(j=!0);var l=A(k,g,f,e.children[i++],a,c,g.bindings_);l.templateInstance_=h,j&&(g.terminator_=l)}return h.firstNode=g.firstChild,h.lastNode=g.lastChild,g.templateCreator_=void 0,g.protoContent_=void 0,g},get model(){return this.model_},set model(a){this.model_=a,r(this)},get bindingDelegate(){return this.delegate_&&this.delegate_.raw},refChanged_:function(){this.iterator_&&this.refContent_!==this.ref_.content&&(this.refContent_=void 0,this.iterator_.valueChanged(),this.iterator_.updateIteratedValue(this.iterator_.getUpdatedValue()))},clear:function(){this.model_=void 0,this.delegate_=void 0,this.bindings_&&this.bindings_.ref&&this.bindings_.ref.close(),this.refContent_=void 0,this.iterator_&&(this.iterator_.valueChanged(),this.iterator_.close(),this.iterator_=void 0)},setDelegate_:function(a){this.delegate_=a,this.bindingMap_=void 0,this.iterator_&&(this.iterator_.instancePositionChangedFn_=void 0,this.iterator_.instanceModelFn_=void 0)},newDelegate_:function(a){function b(b){var c=a&&a[b];if("function"==typeof c)return function(){return c.apply(a,arguments)}}if(a)return{bindingMaps:{},raw:a,prepareBinding:b("prepareBinding"),prepareInstanceModel:b("prepareInstanceModel"),prepareInstancePositionChanged:b("prepareInstancePositionChanged")}},set bindingDelegate(a){if(this.delegate_)throw Error("Template must be cleared before a new bindingDelegate can be assigned");this.setDelegate_(this.newDelegate_(a))},get ref_(){var a=d(this,this.getAttribute("ref"));if(a||(a=this.instanceRef_),!a)return this;var b=a.ref_;return b?b:a}});var S=1;Object.defineProperty(Node.prototype,"templateInstance",{get:function(){var a=this.templateInstance_;return a?a:this.parentNode?this.parentNode.templateInstance:void 0}});var T=document.createDocumentFragment();T.bindings_=[],T.terminator_=null,E.prototype={closeDeps:function(){var a=this.deps;a&&(a.ifOneTime===!1&&a.ifValue.close(),a.oneTime===!1&&a.value.close())},updateDependencies:function(a,b){this.closeDeps();var c=this.deps={},d=this.templateElement_,e=!0;if(a["if"]){if(c.hasIf=!0,c.ifOneTime=a["if"].onlyOneTime,c.ifValue=v(J,a["if"],d,b),e=c.ifValue,c.ifOneTime&&!e)return void this.valueChanged();c.ifOneTime||(e=e.open(this.updateIfValue,this))}a.repeat?(c.repeat=!0,c.oneTime=a.repeat.onlyOneTime,c.value=v(I,a.repeat,d,b)):(c.repeat=!1,c.oneTime=a.bind.onlyOneTime,c.value=v(H,a.bind,d,b));var f=c.value;return c.oneTime||(f=f.open(this.updateIteratedValue,this)),e?void this.updateValue(f):void this.valueChanged()},getUpdatedValue:function(){var a=this.deps.value;return this.deps.oneTime||(a=a.discardChanges()),a},updateIfValue:function(a){return a?void this.updateValue(this.getUpdatedValue()):void this.valueChanged()},updateIteratedValue:function(a){if(this.deps.hasIf){var b=this.deps.ifValue;if(this.deps.ifOneTime||(b=b.discardChanges()),!b)return void this.valueChanged()}this.updateValue(a)},updateValue:function(a){this.deps.repeat||(a=[a]);var b=this.deps.repeat&&!this.deps.oneTime&&Array.isArray(a);this.valueChanged(a,b)},valueChanged:function(a,b){Array.isArray(a)||(a=[]),a!==this.iteratedValue&&(this.unobserve(),this.presentValue=a,b&&(this.arrayObserver=new ArrayObserver(this.presentValue),this.arrayObserver.open(this.handleSplices,this)),this.handleSplices(ArrayObserver.calculateSplices(this.presentValue,this.iteratedValue)))},getLastInstanceNode:function(a){if(-1==a)return this.templateElement_;var b=this.instances[a],c=b.terminator_;if(!c)return this.getLastInstanceNode(a-1);if(c.nodeType!==Node.ELEMENT_NODE||this.templateElement_===c)return c;var d=c.iterator_;return d?d.getLastTemplateNode():c},getLastTemplateNode:function(){return this.getLastInstanceNode(this.instances.length-1)},insertInstanceAt:function(a,b){var c=this.getLastInstanceNode(a-1),d=this.templateElement_.parentNode;this.instances.splice(a,0,b),d.insertBefore(b,c.nextSibling)},extractInstanceAt:function(a){for(var b=this.getLastInstanceNode(a-1),c=this.getLastInstanceNode(a),d=this.templateElement_.parentNode,e=this.instances.splice(a,1)[0];c!==b;){var f=b.nextSibling;f==c&&(c=b),e.appendChild(d.removeChild(f))}return e},getDelegateFn:function(a){return a=a&&a(this.templateElement_),"function"==typeof a?a:null},handleSplices:function(a){if(!this.closed&&a.length){var b=this.templateElement_;if(!b.parentNode)return void this.close();ArrayObserver.applySplices(this.iteratedValue,this.presentValue,a);var c=b.delegate_;void 0===this.instanceModelFn_&&(this.instanceModelFn_=this.getDelegateFn(c&&c.prepareInstanceModel)),void 0===this.instancePositionChangedFn_&&(this.instancePositionChangedFn_=this.getDelegateFn(c&&c.prepareInstancePositionChanged));for(var d=new F,e=0,f=0;f<a.length;f++){for(var g=a[f],h=g.removed,i=0;i<h.length;i++){var j=h[i],k=this.extractInstanceAt(g.index+e);k!==T&&d.set(j,k)}e-=g.addedCount}for(var f=0;f<a.length;f++)for(var g=a[f],l=g.index;l<g.index+g.addedCount;l++){var j=this.iteratedValue[l],k=d.get(j);k?d["delete"](j):(this.instanceModelFn_&&(j=this.instanceModelFn_(j)),k=void 0===j?T:b.createInstance(j,void 0,c)),this.insertInstanceAt(l,k)}d.forEach(function(a){this.closeInstanceBindings(a)},this),this.instancePositionChangedFn_&&this.reportInstancesMoved(a)}},reportInstanceMoved:function(a){var b=this.instances[a];b!==T&&this.instancePositionChangedFn_(b.templateInstance_,a)},reportInstancesMoved:function(a){for(var b=0,c=0,d=0;d<a.length;d++){var e=a[d];if(0!=c)for(;b<e.index;)this.reportInstanceMoved(b),b++;else b=e.index;for(;b<e.index+e.addedCount;)this.reportInstanceMoved(b),b++;c+=e.addedCount-e.removed.length}if(0!=c)for(var f=this.instances.length;f>b;)this.reportInstanceMoved(b),b++},closeInstanceBindings:function(a){for(var b=a.bindings_,c=0;c<b.length;c++)b[c].close()},unobserve:function(){this.arrayObserver&&(this.arrayObserver.close(),this.arrayObserver=void 0)},close:function(){if(!this.closed){this.unobserve();for(var a=0;a<this.instances.length;a++)this.closeInstanceBindings(this.instances[a]);this.instances.length=0,this.closeDeps(),this.templateElement_.iterator_=void 0,this.closed=!0}}},HTMLTemplateElement.forAllTemplatesFrom_=i}(this),function(a){"use strict";function b(a){return void 0!==m[a]}function c(){h.call(this),this._isInvalid=!0}function d(a){return""==a&&c.call(this),a.toLowerCase()}function e(a){var b=a.charCodeAt(0);return b>32&&127>b&&-1==[34,35,60,62,63,96].indexOf(b)?a:encodeURIComponent(a)}function f(a){var b=a.charCodeAt(0);return b>32&&127>b&&-1==[34,35,60,62,96].indexOf(b)?a:encodeURIComponent(a)}function g(a,g,h){function i(a){t.push(a)}var j=g||"scheme start",k=0,l="",r=!1,s=!1,t=[];a:for(;(a[k-1]!=o||0==k)&&!this._isInvalid;){var u=a[k];switch(j){case"scheme start":if(!u||!p.test(u)){if(g){i("Invalid scheme.");break a}l="",j="no scheme";continue}l+=u.toLowerCase(),j="scheme";break;case"scheme":if(u&&q.test(u))l+=u.toLowerCase();else{if(":"!=u){if(g){if(o==u)break a;i("Code point not allowed in scheme: "+u);break a}l="",k=0,j="no scheme";continue}if(this._scheme=l,l="",g)break a;b(this._scheme)&&(this._isRelative=!0),j="file"==this._scheme?"relative":this._isRelative&&h&&h._scheme==this._scheme?"relative or authority":this._isRelative?"authority first slash":"scheme data"}break;case"scheme data":"?"==u?(query="?",j="query"):"#"==u?(this._fragment="#",j="fragment"):o!=u&&"	"!=u&&"\n"!=u&&"\r"!=u&&(this._schemeData+=e(u));break;case"no scheme":if(h&&b(h._scheme)){j="relative";continue}i("Missing scheme."),c.call(this);break;case"relative or authority":if("/"!=u||"/"!=a[k+1]){i("Expected /, got: "+u),j="relative";continue}j="authority ignore slashes";break;case"relative":if(this._isRelative=!0,"file"!=this._scheme&&(this._scheme=h._scheme),o==u){this._host=h._host,this._port=h._port,this._path=h._path.slice(),this._query=h._query;break a}if("/"==u||"\\"==u)"\\"==u&&i("\\ is an invalid code point."),j="relative slash";else if("?"==u)this._host=h._host,this._port=h._port,this._path=h._path.slice(),this._query="?",j="query";else{if("#"!=u){var v=a[k+1],w=a[k+2];("file"!=this._scheme||!p.test(u)||":"!=v&&"|"!=v||o!=w&&"/"!=w&&"\\"!=w&&"?"!=w&&"#"!=w)&&(this._host=h._host,this._port=h._port,this._path=h._path.slice(),this._path.pop()),j="relative path";continue}this._host=h._host,this._port=h._port,this._path=h._path.slice(),this._query=h._query,this._fragment="#",j="fragment"}break;case"relative slash":if("/"!=u&&"\\"!=u){"file"!=this._scheme&&(this._host=h._host,this._port=h._port),j="relative path";continue}"\\"==u&&i("\\ is an invalid code point."),j="file"==this._scheme?"file host":"authority ignore slashes";break;case"authority first slash":if("/"!=u){i("Expected '/', got: "+u),j="authority ignore slashes";continue}j="authority second slash";break;case"authority second slash":if(j="authority ignore slashes","/"!=u){i("Expected '/', got: "+u);continue}break;case"authority ignore slashes":if("/"!=u&&"\\"!=u){j="authority";continue}i("Expected authority, got: "+u);break;case"authority":if("@"==u){r&&(i("@ already seen."),l+="%40"),r=!0;for(var x=0;x<l.length;x++){var y=l[x];if("	"!=y&&"\n"!=y&&"\r"!=y)if(":"!=y||null!==this._password){var z=e(y);null!==this._password?this._password+=z:this._username+=z}else this._password="";else i("Invalid whitespace in authority.")}l=""}else{if(o==u||"/"==u||"\\"==u||"?"==u||"#"==u){k-=l.length,l="",j="host";continue}l+=u}break;case"file host":if(o==u||"/"==u||"\\"==u||"?"==u||"#"==u){2!=l.length||!p.test(l[0])||":"!=l[1]&&"|"!=l[1]?0==l.length?j="relative path start":(this._host=d.call(this,l),l="",j="relative path start"):j="relative path";continue}"	"==u||"\n"==u||"\r"==u?i("Invalid whitespace in file host."):l+=u;break;case"host":case"hostname":if(":"!=u||s){if(o==u||"/"==u||"\\"==u||"?"==u||"#"==u){if(this._host=d.call(this,l),l="",j="relative path start",g)break a;continue}"	"!=u&&"\n"!=u&&"\r"!=u?("["==u?s=!0:"]"==u&&(s=!1),l+=u):i("Invalid code point in host/hostname: "+u)}else if(this._host=d.call(this,l),l="",j="port","hostname"==g)break a;break;case"port":if(/[0-9]/.test(u))l+=u;else{if(o==u||"/"==u||"\\"==u||"?"==u||"#"==u||g){if(""!=l){var A=parseInt(l,10);A!=m[this._scheme]&&(this._port=A+""),l=""}if(g)break a;j="relative path start";continue}"	"==u||"\n"==u||"\r"==u?i("Invalid code point in port: "+u):c.call(this)}break;case"relative path start":if("\\"==u&&i("'\\' not allowed in path."),j="relative path","/"!=u&&"\\"!=u)continue;break;case"relative path":if(o!=u&&"/"!=u&&"\\"!=u&&(g||"?"!=u&&"#"!=u))"	"!=u&&"\n"!=u&&"\r"!=u&&(l+=e(u));else{"\\"==u&&i("\\ not allowed in relative path.");var B;(B=n[l.toLowerCase()])&&(l=B),".."==l?(this._path.pop(),"/"!=u&&"\\"!=u&&this._path.push("")):"."==l&&"/"!=u&&"\\"!=u?this._path.push(""):"."!=l&&("file"==this._scheme&&0==this._path.length&&2==l.length&&p.test(l[0])&&"|"==l[1]&&(l=l[0]+":"),this._path.push(l)),l="","?"==u?(this._query="?",j="query"):"#"==u&&(this._fragment="#",j="fragment")}break;case"query":g||"#"!=u?o!=u&&"	"!=u&&"\n"!=u&&"\r"!=u&&(this._query+=f(u)):(this._fragment="#",j="fragment");break;case"fragment":o!=u&&"	"!=u&&"\n"!=u&&"\r"!=u&&(this._fragment+=u)}k++}}function h(){this._scheme="",this._schemeData="",this._username="",this._password=null,this._host="",this._port="",this._path=[],this._query="",this._fragment="",this._isInvalid=!1,this._isRelative=!1}function i(a,b){void 0===b||b instanceof i||(b=new i(String(b))),this._url=a,h.call(this);var c=a.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g,"");g.call(this,c,null,b)}var j=!1;if(!a.forceJURL)try{var k=new URL("b","http://a");k.pathname="c%20d",j="http://a/c%20d"===k.href}catch(l){}if(!j){var m=Object.create(null);m.ftp=21,m.file=0,m.gopher=70,m.http=80,m.https=443,m.ws=80,m.wss=443;var n=Object.create(null);n["%2e"]=".",n[".%2e"]="..",n["%2e."]="..",n["%2e%2e"]="..";var o=void 0,p=/[a-zA-Z]/,q=/[a-zA-Z0-9\+\-\.]/;i.prototype={get href(){if(this._isInvalid)return this._url;var a="";return(""!=this._username||null!=this._password)&&(a=this._username+(null!=this._password?":"+this._password:"")+"@"),this.protocol+(this._isRelative?"//"+a+this.host:"")+this.pathname+this._query+this._fragment},set href(a){h.call(this),g.call(this,a)},get protocol(){return this._scheme+":"},set protocol(a){this._isInvalid||g.call(this,a+":","scheme start")},get host(){return this._isInvalid?"":this._port?this._host+":"+this._port:this._host},set host(a){!this._isInvalid&&this._isRelative&&g.call(this,a,"host")},get hostname(){return this._host},set hostname(a){!this._isInvalid&&this._isRelative&&g.call(this,a,"hostname")},get port(){return this._port},set port(a){!this._isInvalid&&this._isRelative&&g.call(this,a,"port")},get pathname(){return this._isInvalid?"":this._isRelative?"/"+this._path.join("/"):this._schemeData},set pathname(a){!this._isInvalid&&this._isRelative&&(this._path=[],g.call(this,a,"relative path start"))},get search(){return this._isInvalid||!this._query||"?"==this._query?"":this._query},set search(a){!this._isInvalid&&this._isRelative&&(this._query="?","?"==a[0]&&(a=a.slice(1)),g.call(this,a,"query"))},get hash(){return this._isInvalid||!this._fragment||"#"==this._fragment?"":this._fragment},set hash(a){this._isInvalid||(this._fragment="#","#"==a[0]&&(a=a.slice(1)),g.call(this,a,"fragment"))},get origin(){var a;if(this._isInvalid||!this._scheme)return"";switch(this._scheme){case"data":case"file":case"javascript":case"mailto":return"null"}return a=this.host,a?this._scheme+"://"+a:""}};var r=a.URL;r&&(i.createObjectURL=function(){return r.createObjectURL.apply(r,arguments)},i.revokeObjectURL=function(a){r.revokeObjectURL(a)}),a.URL=i}}(this),function(a){function b(a){f.textContent=d++,e.push(a)}function c(){for(;e.length;)e.shift()()}var d=0,e=[],f=document.createTextNode("");new(window.MutationObserver||JsMutationObserver)(c).observe(f,{characterData:!0}),a.endOfMicrotask=b,Platform.endOfMicrotask=b}(Polymer),function(a){function b(){g||(g=!0,c(function(){g=!1,d.data&&console.group("flush"),Platform.performMicrotaskCheckpoint(),d.data&&console.groupEnd()}))}var c=a.endOfMicrotask,d=window.WebComponents?WebComponents.flags.log:{},e=document.createElement("style");e.textContent="template {display: none !important;} /* injected by platform.js */";var f=document.querySelector("head");f.insertBefore(e,f.firstChild);var g;if(Observer.hasObjectObserve)b=function(){};else{var h=125;window.addEventListener("WebComponentsReady",function(){b();var c=function(){"hidden"===document.visibilityState?a.flushPoll&&clearInterval(a.flushPoll):a.flushPoll=setInterval(b,h)};"string"==typeof document.visibilityState&&document.addEventListener("visibilitychange",c),c()})}if(window.CustomElements&&!CustomElements.useNative){var i=Document.prototype.importNode;Document.prototype.importNode=function(a,b){var c=i.call(this,a,b);return CustomElements.upgradeAll(c),c}}a.flush=b,Platform.flush=b}(window.Polymer),function(a){function b(a){var b=new URL(a.ownerDocument.baseURI);return b.search="",b.hash="",b}function c(a,b,c,e){return a.replace(e,function(a,e,f,g){var h=f.replace(/["']/g,"");return h=d(b,h,c),e+"'"+h+"'"+g})}function d(a,b,c){if(b&&"/"===b[0])return b;if(b&&"#"===b[0])return b;var d=new URL(b,a);return c?d.href:e(d.href)}function e(a){var c=b(document.documentElement),d=new URL(a,c);return d.host===c.host&&d.port===c.port&&d.protocol===c.protocol?f(c,d):a}function f(a,b){for(var c=a.pathname,d=b.pathname,e=c.split("/"),f=d.split("/");e.length&&e[0]===f[0];)e.shift(),f.shift();for(var g=0,h=e.length-1;h>g;g++)f.unshift("..");var i=b.href.slice(-1)===m?m:b.hash;return f.join("/")+b.search+i}var g={resolveDom:function(a,c){c=c||b(a),this.resolveAttributes(a,c),this.resolveStyles(a,c);var d=a.querySelectorAll("template");if(d)for(var e,f=0,g=d.length;g>f&&(e=d[f]);f++)e.content&&this.resolveDom(e.content,c)},resolveTemplate:function(a){this.resolveDom(a.content,b(a))},resolveStyles:function(a,b){var c=a.querySelectorAll("style");if(c)for(var d,e=0,f=c.length;f>e&&(d=c[e]);e++)this.resolveStyle(d,b)},resolveStyle:function(a,c){c=c||b(a),a.textContent=this.resolveCssText(a.textContent,c)},resolveCssText:function(a,b,d){return a=c(a,b,d,h),c(a,b,d,i)},resolveAttributes:function(a,b){a.hasAttributes&&a.hasAttributes()&&this.resolveElementAttributes(a,b);var c=a&&a.querySelectorAll(k);if(c)for(var d,e=0,f=c.length;f>e&&(d=c[e]);e++)this.resolveElementAttributes(d,b)},resolveElementAttributes:function(a,e){e=e||b(a),j.forEach(function(b){var f,g=a.attributes[b],i=g&&g.value;i&&i.search(l)<0&&(f="style"===b?c(i,e,!1,h):d(e,i),g.value=f)})}},h=/(url\()([^)]*)(\))/g,i=/(@import[\s]+(?!url\())([^;]*)(;)/g,j=["href","src","action","style","url"],k="["+j.join("],[")+"]",l="{{.*}}",m="#";a.urlResolver=g}(Polymer),function(a){function b(a){this.cache=Object.create(null),this.map=Object.create(null),this.requests=0,this.regex=a}var c=Polymer.endOfMicrotask;b.prototype={extractUrls:function(a,b){for(var c,d,e=[];c=this.regex.exec(a);)d=new URL(c[1],b),e.push({matched:c[0],url:d.href});return e},process:function(a,b,c){var d=this.extractUrls(a,b),e=c.bind(null,this.map);this.fetch(d,e)},fetch:function(a,b){var c=a.length;if(!c)return b();for(var d,e,f,g=function(){0===--c&&b()},h=0;c>h;h++)d=a[h],f=d.url,e=this.cache[f],e||(e=this.xhr(f),e.match=d,this.cache[f]=e),e.wait(g)},handleXhr:function(a){var b=a.match,c=b.url,d=a.response||a.responseText||"";this.map[c]=d,this.fetch(this.extractUrls(d,c),a.resolve)},xhr:function(a){this.requests++;var b=new XMLHttpRequest;return b.open("GET",a,!0),b.send(),b.onerror=b.onload=this.handleXhr.bind(this,b),b.pending=[],b.resolve=function(){for(var a=b.pending,c=0;c<a.length;c++)a[c]();b.pending=null},b.wait=function(a){b.pending?b.pending.push(a):c(a)},b}},a.Loader=b}(Polymer),function(a){function b(){this.loader=new d(this.regex)}var c=a.urlResolver,d=a.Loader;b.prototype={regex:/@import\s+(?:url)?["'\(]*([^'"\)]*)['"\)]*;/g,resolve:function(a,b,c){var d=function(d){c(this.flatten(a,b,d))}.bind(this);this.loader.process(a,b,d)},resolveNode:function(a,b,c){var d=a.textContent,e=function(b){a.textContent=b,c(a)};this.resolve(d,b,e)},flatten:function(a,b,d){for(var e,f,g,h=this.loader.extractUrls(a,b),i=0;i<h.length;i++)e=h[i],f=e.url,g=c.resolveCssText(d[f],f,!0),g=this.flatten(g,b,d),a=a.replace(e.matched,g);return a},loadStyles:function(a,b,c){function d(){f++,f===g&&c&&c()}for(var e,f=0,g=a.length,h=0;g>h&&(e=a[h]);h++)this.resolveNode(e,b,d)}};var e=new b;a.styleResolver=e}(Polymer),function(a){function b(a,b){return a&&b&&Object.getOwnPropertyNames(b).forEach(function(c){var d=Object.getOwnPropertyDescriptor(b,c);d&&(Object.defineProperty(a,c,d),"function"==typeof d.value&&(d.value.nom=c))}),a}function c(a){for(var b=a||{},c=1;c<arguments.length;c++){var e=arguments[c];try{for(var f in e)d(f,e,b)}catch(g){}}return b}function d(a,b,c){var d=e(b,a);Object.defineProperty(c,a,d)}function e(a,b){if(a){var c=Object.getOwnPropertyDescriptor(a,b);return c||e(Object.getPrototypeOf(a),b)}}a.extend=b,a.mixin=c,Platform.mixin=c}(Polymer),function(a){function b(a,b,d){return a?a.stop():a=new c(this),a.go(b,d),a}var c=function(a){this.context=a,this.boundComplete=this.complete.bind(this)};c.prototype={go:function(a,b){this.callback=a;var c;b?(c=setTimeout(this.boundComplete,b),this.handle=function(){clearTimeout(c)}):(c=requestAnimationFrame(this.boundComplete),this.handle=function(){cancelAnimationFrame(c)})},stop:function(){this.handle&&(this.handle(),this.handle=null)},complete:function(){this.handle&&(this.stop(),this.callback.call(this.context))}},a.job=b}(Polymer),function(a){function b(a,b,c){var d="string"==typeof a?document.createElement(a):a.cloneNode(!0);if(d.innerHTML=b,c)for(var e in c)d.setAttribute(e,c[e]);return d}var c={};HTMLElement.register=function(a,b){c[a]=b},HTMLElement.getPrototypeForTag=function(a){var b=a?c[a]:HTMLElement.prototype;return b||Object.getPrototypeOf(document.createElement(a))};var d=Event.prototype.stopPropagation;Event.prototype.stopPropagation=function(){this.cancelBubble=!0,d.apply(this,arguments)};var e=DOMTokenList.prototype.add,f=DOMTokenList.prototype.remove;DOMTokenList.prototype.add=function(){for(var a=0;a<arguments.length;a++)e.call(this,arguments[a])},DOMTokenList.prototype.remove=function(){for(var a=0;a<arguments.length;a++)f.call(this,arguments[a])},DOMTokenList.prototype.toggle=function(a,b){1==arguments.length&&(b=!this.contains(a)),b?this.add(a):this.remove(a)},DOMTokenList.prototype["switch"]=function(a,b){a&&this.remove(a),b&&this.add(b)};var g=function(){return Array.prototype.slice.call(this)},h=window.NamedNodeMap||window.MozNamedAttrMap||{};NodeList.prototype.array=g,h.prototype.array=g,HTMLCollection.prototype.array=g,a.createDOM=b}(Polymer),function(a){function b(a){var e=b.caller,g=e.nom,h=e._super;h||(g||(g=e.nom=c.call(this,e)),g||console.warn("called super() on a method not installed declaratively (has no .nom property)"),h=d(e,g,f(this)));var i=h[g];return i?(i._super||d(i,g,h),i.apply(this,a||[])):void 0}function c(a){for(var b=this.__proto__;b&&b!==HTMLElement.prototype;){for(var c,d=Object.getOwnPropertyNames(b),e=0,f=d.length;f>e&&(c=d[e]);e++){var g=Object.getOwnPropertyDescriptor(b,c);if("function"==typeof g.value&&g.value===a)return c}b=b.__proto__}}function d(a,b,c){var d=e(c,b,a);return d[b]&&(d[b].nom=b),a._super=d}function e(a,b,c){for(;a;){if(a[b]!==c&&a[b])return a;a=f(a)}return Object}function f(a){return a.__proto__}a["super"]=b}(Polymer),function(a){function b(a){return a}function c(a,b){var c=typeof b;return b instanceof Date&&(c="date"),d[c](a,b)}var d={string:b,undefined:b,date:function(a){return new Date(Date.parse(a)||Date.now())},"boolean":function(a){return""===a?!0:"false"===a?!1:!!a},number:function(a){var b=parseFloat(a);return 0===b&&(b=parseInt(a)),isNaN(b)?a:b},object:function(a,b){if(null===b)return a;try{return JSON.parse(a.replace(/'/g,'"'))}catch(c){return a}},"function":function(a,b){return b}};a.deserializeValue=c}(Polymer),function(a){var b=a.extend,c={};c.declaration={},c.instance={},c.publish=function(a,c){for(var d in a)b(c,a[d])},a.api=c}(Polymer),function(a){var b={async:function(a,b,c){Polymer.flush(),b=b&&b.length?b:[b];var d=function(){(this[a]||a).apply(this,b)}.bind(this),e=c?setTimeout(d,c):requestAnimationFrame(d);return c?e:~e},cancelAsync:function(a){0>a?cancelAnimationFrame(~a):clearTimeout(a)},fire:function(a,b,c,d,e){var f=c||this,b=null===b||void 0===b?{}:b,g=new CustomEvent(a,{bubbles:void 0!==d?d:!0,cancelable:void 0!==e?e:!0,detail:b});return f.dispatchEvent(g),g},asyncFire:function(){this.async("fire",arguments)},classFollows:function(a,b,c){b&&b.classList.remove(c),a&&a.classList.add(c)},injectBoundHTML:function(a,b){var c=document.createElement("template");c.innerHTML=a;var d=this.instanceTemplate(c);return b&&(b.textContent="",b.appendChild(d)),d}},c=function(){},d={};b.asyncMethod=b.async,a.api.instance.utils=b,a.nop=c,a.nob=d}(Polymer),function(a){var b=window.WebComponents?WebComponents.flags.log:{},c="on-",d={EVENT_PREFIX:c,addHostListeners:function(){var a=this.eventDelegates;
+b.events&&Object.keys(a).length>0&&console.log("[%s] addHostListeners:",this.localName,a);for(var c in a){var d=a[c];PolymerGestures.addEventListener(this,c,this.element.getEventHandler(this,this,d))}},dispatchMethod:function(a,c,d){if(a){b.events&&console.group("[%s] dispatch [%s]",a.localName,c);var e="function"==typeof c?c:a[c];e&&e[d?"apply":"call"](a,d),b.events&&console.groupEnd(),Polymer.flush()}}};a.api.instance.events=d,a.addEventListener=function(a,b,c,d){PolymerGestures.addEventListener(wrap(a),b,c,d)},a.removeEventListener=function(a,b,c,d){PolymerGestures.removeEventListener(wrap(a),b,c,d)}}(Polymer),function(a){var b={copyInstanceAttributes:function(){var a=this._instanceAttributes;for(var b in a)this.hasAttribute(b)||this.setAttribute(b,a[b])},takeAttributes:function(){if(this._publishLC)for(var a,b=0,c=this.attributes,d=c.length;(a=c[b])&&d>b;b++)this.attributeToProperty(a.name,a.value)},attributeToProperty:function(b,c){var b=this.propertyForAttribute(b);if(b){if(c&&c.search(a.bindPattern)>=0)return;var d=this[b],c=this.deserializeValue(c,d);c!==d&&(this[b]=c)}},propertyForAttribute:function(a){var b=this._publishLC&&this._publishLC[a];return b},deserializeValue:function(b,c){return a.deserializeValue(b,c)},serializeValue:function(a,b){return"boolean"===b?a?"":void 0:"object"!==b&&"function"!==b&&void 0!==a?a:void 0},reflectPropertyToAttribute:function(a){var b=typeof this[a],c=this.serializeValue(this[a],b);void 0!==c?this.setAttribute(a,c):"boolean"===b&&this.removeAttribute(a)}};a.api.instance.attributes=b}(Polymer),function(a){function b(a,b){return a===b?0!==a||1/a===1/b:f(a)&&f(b)?!0:a!==a&&b!==b}function c(a,b){return void 0===b&&null===a?b:null===b||void 0===b?a:b}var d=window.WebComponents?WebComponents.flags.log:{},e={object:void 0,type:"update",name:void 0,oldValue:void 0},f=Number.isNaN||function(a){return"number"==typeof a&&isNaN(a)},g={createPropertyObserver:function(){var a=this._observeNames;if(a&&a.length){var b=this._propertyObserver=new CompoundObserver(!0);this.registerObserver(b);for(var c,d=0,e=a.length;e>d&&(c=a[d]);d++)b.addPath(this,c),this.observeArrayValue(c,this[c],null)}},openPropertyObserver:function(){this._propertyObserver&&this._propertyObserver.open(this.notifyPropertyChanges,this)},notifyPropertyChanges:function(a,b,c){var d,e,f={};for(var g in b)if(d=c[2*g+1],e=this.observe[d]){var h=b[g],i=a[g];this.observeArrayValue(d,i,h),f[e]||(void 0!==h&&null!==h||void 0!==i&&null!==i)&&(f[e]=!0,this.invokeMethod(e,[h,i,arguments]))}},invokeMethod:function(a,b){var c=this[a]||a;"function"==typeof c&&c.apply(this,b)},deliverChanges:function(){this._propertyObserver&&this._propertyObserver.deliver()},observeArrayValue:function(a,b,c){var e=this.observe[a];if(e&&(Array.isArray(c)&&(d.observe&&console.log("[%s] observeArrayValue: unregister observer [%s]",this.localName,a),this.closeNamedObserver(a+"__array")),Array.isArray(b))){d.observe&&console.log("[%s] observeArrayValue: register observer [%s]",this.localName,a,b);var f=new ArrayObserver(b);f.open(function(a){this.invokeMethod(e,[a])},this),this.registerNamedObserver(a+"__array",f)}},emitPropertyChangeRecord:function(a,c,d){if(!b(c,d)&&(this._propertyChanged(a,c,d),Observer.hasObjectObserve)){var f=this._objectNotifier;f||(f=this._objectNotifier=Object.getNotifier(this)),e.object=this,e.name=a,e.oldValue=d,f.notify(e)}},_propertyChanged:function(a){this.reflect[a]&&this.reflectPropertyToAttribute(a)},bindProperty:function(a,b,d){if(d)return void(this[a]=b);var e=this.element.prototype.computed;if(e&&e[a]){var f=a+"ComputedBoundObservable_";return void(this[f]=b)}return this.bindToAccessor(a,b,c)},bindToAccessor:function(a,c,d){function e(b,c){j[f]=b;var d=j[h];d&&"function"==typeof d.setValue&&d.setValue(b),j.emitPropertyChangeRecord(a,b,c)}var f=a+"_",g=a+"Observable_",h=a+"ComputedBoundObservable_";this[g]=c;var i=this[f],j=this,k=c.open(e);if(d&&!b(i,k)){var l=d(i,k);b(k,l)||(k=l,c.setValue&&c.setValue(k))}e(k,i);var m={close:function(){c.close(),j[g]=void 0,j[h]=void 0}};return this.registerObserver(m),m},createComputedProperties:function(){if(this._computedNames)for(var a=0;a<this._computedNames.length;a++){var b=this._computedNames[a],c=this.computed[b];try{var d=PolymerExpressions.getExpression(c),e=d.getBinding(this,this.element.syntax);this.bindToAccessor(b,e)}catch(f){console.error("Failed to create computed property",f)}}},registerObserver:function(a){return this._observers?void this._observers.push(a):void(this._observers=[a])},closeObservers:function(){if(this._observers){for(var a=this._observers,b=0;b<a.length;b++){var c=a[b];c&&"function"==typeof c.close&&c.close()}this._observers=[]}},registerNamedObserver:function(a,b){var c=this._namedObservers||(this._namedObservers={});c[a]=b},closeNamedObserver:function(a){var b=this._namedObservers;return b&&b[a]?(b[a].close(),b[a]=null,!0):void 0},closeNamedObservers:function(){if(this._namedObservers){for(var a in this._namedObservers)this.closeNamedObserver(a);this._namedObservers={}}}};a.api.instance.properties=g}(Polymer),function(a){var b=window.WebComponents?WebComponents.flags.log:{},c={instanceTemplate:function(a){HTMLTemplateElement.decorate(a);for(var b=this.syntax||!a.bindingDelegate&&this.element.syntax,c=a.createInstance(this,b),d=c.bindings_,e=0;e<d.length;e++)this.registerObserver(d[e]);return c},bind:function(a,b,c){var d=this.propertyForAttribute(a);if(d){var e=this.bindProperty(d,b,c);return Platform.enableBindingsReflection&&e&&(e.path=b.path_,this._recordBinding(d,e)),this.reflect[d]&&this.reflectPropertyToAttribute(d),e}return this.mixinSuper(arguments)},_recordBinding:function(a,b){this.bindings_=this.bindings_||{},this.bindings_[a]=b},bindFinished:function(){this.makeElementReady()},asyncUnbindAll:function(){this._unbound||(b.unbind&&console.log("[%s] asyncUnbindAll",this.localName),this._unbindAllJob=this.job(this._unbindAllJob,this.unbindAll,0))},unbindAll:function(){this._unbound||(this.closeObservers(),this.closeNamedObservers(),this._unbound=!0)},cancelUnbindAll:function(){return this._unbound?void(b.unbind&&console.warn("[%s] already unbound, cannot cancel unbindAll",this.localName)):(b.unbind&&console.log("[%s] cancelUnbindAll",this.localName),void(this._unbindAllJob&&(this._unbindAllJob=this._unbindAllJob.stop())))}},d=/\{\{([^{}]*)}}/;a.bindPattern=d,a.api.instance.mdv=c}(Polymer),function(a){function b(a){return a.hasOwnProperty("PolymerBase")}function c(){}var d={PolymerBase:!0,job:function(a,b,c){if("string"!=typeof a)return Polymer.job.call(this,a,b,c);var d="___"+a;this[d]=Polymer.job.call(this,this[d],b,c)},"super":Polymer["super"],created:function(){},ready:function(){},createdCallback:function(){this.templateInstance&&this.templateInstance.model&&console.warn("Attributes on "+this.localName+" were data bound prior to Polymer upgrading the element. This may result in incorrect binding types."),this.created(),this.prepareElement(),this.ownerDocument.isStagingDocument||this.makeElementReady()},prepareElement:function(){return this._elementPrepared?void console.warn("Element already prepared",this.localName):(this._elementPrepared=!0,this.shadowRoots={},this.createPropertyObserver(),this.openPropertyObserver(),this.copyInstanceAttributes(),this.takeAttributes(),void this.addHostListeners())},makeElementReady:function(){this._readied||(this._readied=!0,this.createComputedProperties(),this.parseDeclarations(this.__proto__),this.removeAttribute("unresolved"),this.ready())},attributeChangedCallback:function(a){"class"!==a&&"style"!==a&&this.attributeToProperty(a,this.getAttribute(a)),this.attributeChanged&&this.attributeChanged.apply(this,arguments)},attachedCallback:function(){this.cancelUnbindAll(),this.attached&&this.attached(),this.hasBeenAttached||(this.hasBeenAttached=!0,this.domReady&&this.async("domReady"))},detachedCallback:function(){this.preventDispose||this.asyncUnbindAll(),this.detached&&this.detached(),this.leftView&&this.leftView()},parseDeclarations:function(a){a&&a.element&&(this.parseDeclarations(a.__proto__),a.parseDeclaration.call(this,a.element))},parseDeclaration:function(a){var b=this.fetchTemplate(a);if(b){var c=this.shadowFromTemplate(b);this.shadowRoots[a.name]=c}},fetchTemplate:function(a){return a.querySelector("template")},shadowFromTemplate:function(a){if(a){var b=this.createShadowRoot(),c=this.instanceTemplate(a);return b.appendChild(c),this.shadowRootReady(b,a),b}},lightFromTemplate:function(a,b){if(a){this.eventController=this;var c=this.instanceTemplate(a);return b?this.insertBefore(c,b):this.appendChild(c),this.shadowRootReady(this),c}},shadowRootReady:function(a){this.marshalNodeReferences(a)},marshalNodeReferences:function(a){var b=this.$=this.$||{};if(a)for(var c,d=a.querySelectorAll("[id]"),e=0,f=d.length;f>e&&(c=d[e]);e++)b[c.id]=c},onMutation:function(a,b){var c=new MutationObserver(function(a){b.call(this,c,a),c.disconnect()}.bind(this));c.observe(a,{childList:!0,subtree:!0})}};c.prototype=d,d.constructor=c,a.Base=c,a.isBase=b,a.api.instance.base=d}(Polymer),function(a){function b(a){return a.__proto__}function c(a,b){var c="",d=!1;b&&(c=b.localName,d=b.hasAttribute("is"));var e=WebComponents.ShadowCSS.makeScopeSelector(c,d);return WebComponents.ShadowCSS.shimCssText(a,e)}var d=(window.WebComponents?WebComponents.flags.log:{},window.ShadowDOMPolyfill),e="element",f="controller",g={STYLE_SCOPE_ATTRIBUTE:e,installControllerStyles:function(){var a=this.findStyleScope();if(a&&!this.scopeHasNamedStyle(a,this.localName)){for(var c=b(this),d="";c&&c.element;)d+=c.element.cssTextForScope(f),c=b(c);d&&this.installScopeCssText(d,a)}},installScopeStyle:function(a,b,c){var c=c||this.findStyleScope(),b=b||"";if(c&&!this.scopeHasNamedStyle(c,this.localName+b)){var d="";if(a instanceof Array)for(var e,f=0,g=a.length;g>f&&(e=a[f]);f++)d+=e.textContent+"\n\n";else d=a.textContent;this.installScopeCssText(d,c,b)}},installScopeCssText:function(a,b,e){if(b=b||this.findStyleScope(),e=e||"",b){d&&(a=c(a,b.host));var g=this.element.cssTextToScopeStyle(a,f);Polymer.applyStyleToScope(g,b),this.styleCacheForScope(b)[this.localName+e]=!0}},findStyleScope:function(a){for(var b=a||this;b.parentNode;)b=b.parentNode;return b},scopeHasNamedStyle:function(a,b){var c=this.styleCacheForScope(a);return c[b]},styleCacheForScope:function(a){if(d){var b=a.host?a.host.localName:a.localName;return h[b]||(h[b]={})}return a._scopeStyles=a._scopeStyles||{}}},h={};a.api.instance.styles=g}(Polymer),function(a){function b(a,b){if("string"!=typeof a){var c=b||document._currentScript;if(b=a,a=c&&c.parentNode&&c.parentNode.getAttribute?c.parentNode.getAttribute("name"):"",!a)throw"Element name could not be inferred."}if(f(a))throw"Already registered (Polymer) prototype for element "+a;e(a,b),d(a)}function c(a,b){i[a]=b}function d(a){i[a]&&(i[a].registerWhenReady(),delete i[a])}function e(a,b){return j[a]=b||{}}function f(a){return j[a]}function g(a,b){if("string"!=typeof b)return!1;var c=HTMLElement.getPrototypeForTag(b),d=c&&c.constructor;return d?CustomElements["instanceof"]?CustomElements["instanceof"](a,d):a instanceof d:!1}var h=a.extend,i=(a.api,{}),j={};a.getRegisteredPrototype=f,a.waitingForPrototype=c,a.instanceOfType=g,window.Polymer=b,h(Polymer,a),WebComponents.consumeDeclarations&&WebComponents.consumeDeclarations(function(a){if(a)for(var c,d=0,e=a.length;e>d&&(c=a[d]);d++)b.apply(null,c)})}(Polymer),function(a){var b={resolveElementPaths:function(a){Polymer.urlResolver.resolveDom(a)},addResolvePathApi:function(){var a=this.getAttribute("assetpath")||"",b=new URL(a,this.ownerDocument.baseURI);this.prototype.resolvePath=function(a,c){var d=new URL(a,c||b);return d.href}}};a.api.declaration.path=b}(Polymer),function(a){function b(a,b){var c=new URL(a.getAttribute("href"),b).href;return"@import '"+c+"';"}function c(a,b){if(a){b===document&&(b=document.head),i&&(b=document.head);var c=d(a.textContent),e=a.getAttribute(h);e&&c.setAttribute(h,e);var f=b.firstElementChild;if(b===document.head){var g="style["+h+"]",j=document.head.querySelectorAll(g);j.length&&(f=j[j.length-1].nextElementSibling)}b.insertBefore(c,f)}}function d(a,b){b=b||document,b=b.createElement?b:b.ownerDocument;var c=b.createElement("style");return c.textContent=a,c}function e(a){return a&&a.__resource||""}function f(a,b){return q?q.call(a,b):void 0}var g=(window.WebComponents?WebComponents.flags.log:{},a.api.instance.styles),h=g.STYLE_SCOPE_ATTRIBUTE,i=window.ShadowDOMPolyfill,j="style",k="@import",l="link[rel=stylesheet]",m="global",n="polymer-scope",o={loadStyles:function(a){var b=this.fetchTemplate(),c=b&&this.templateContent();if(c){this.convertSheetsToStyles(c);var d=this.findLoadableStyles(c);if(d.length){var e=b.ownerDocument.baseURI;return Polymer.styleResolver.loadStyles(d,e,a)}}a&&a()},convertSheetsToStyles:function(a){for(var c,e,f=a.querySelectorAll(l),g=0,h=f.length;h>g&&(c=f[g]);g++)e=d(b(c,this.ownerDocument.baseURI),this.ownerDocument),this.copySheetAttributes(e,c),c.parentNode.replaceChild(e,c)},copySheetAttributes:function(a,b){for(var c,d=0,e=b.attributes,f=e.length;(c=e[d])&&f>d;d++)"rel"!==c.name&&"href"!==c.name&&a.setAttribute(c.name,c.value)},findLoadableStyles:function(a){var b=[];if(a)for(var c,d=a.querySelectorAll(j),e=0,f=d.length;f>e&&(c=d[e]);e++)c.textContent.match(k)&&b.push(c);return b},installSheets:function(){this.cacheSheets(),this.cacheStyles(),this.installLocalSheets(),this.installGlobalStyles()},cacheSheets:function(){this.sheets=this.findNodes(l),this.sheets.forEach(function(a){a.parentNode&&a.parentNode.removeChild(a)})},cacheStyles:function(){this.styles=this.findNodes(j+"["+n+"]"),this.styles.forEach(function(a){a.parentNode&&a.parentNode.removeChild(a)})},installLocalSheets:function(){var a=this.sheets.filter(function(a){return!a.hasAttribute(n)}),b=this.templateContent();if(b){var c="";if(a.forEach(function(a){c+=e(a)+"\n"}),c){var f=d(c,this.ownerDocument);b.insertBefore(f,b.firstChild)}}},findNodes:function(a,b){var c=this.querySelectorAll(a).array(),d=this.templateContent();if(d){var e=d.querySelectorAll(a).array();c=c.concat(e)}return b?c.filter(b):c},installGlobalStyles:function(){var a=this.styleForScope(m);c(a,document.head)},cssTextForScope:function(a){var b="",c="["+n+"="+a+"]",d=function(a){return f(a,c)},g=this.sheets.filter(d);g.forEach(function(a){b+=e(a)+"\n\n"});var h=this.styles.filter(d);return h.forEach(function(a){b+=a.textContent+"\n\n"}),b},styleForScope:function(a){var b=this.cssTextForScope(a);return this.cssTextToScopeStyle(b,a)},cssTextToScopeStyle:function(a,b){if(a){var c=d(a);return c.setAttribute(h,this.getAttribute("name")+"-"+b),c}}},p=HTMLElement.prototype,q=p.matches||p.matchesSelector||p.webkitMatchesSelector||p.mozMatchesSelector;a.api.declaration.styles=o,a.applyStyleToScope=c}(Polymer),function(a){var b=(window.WebComponents?WebComponents.flags.log:{},a.api.instance.events),c=b.EVENT_PREFIX,d={};["webkitAnimationStart","webkitAnimationEnd","webkitTransitionEnd","DOMFocusOut","DOMFocusIn","DOMMouseScroll"].forEach(function(a){d[a.toLowerCase()]=a});var e={parseHostEvents:function(){var a=this.prototype.eventDelegates;this.addAttributeDelegates(a)},addAttributeDelegates:function(a){for(var b,c=0;b=this.attributes[c];c++)this.hasEventPrefix(b.name)&&(a[this.removeEventPrefix(b.name)]=b.value.replace("{{","").replace("}}","").trim())},hasEventPrefix:function(a){return a&&"o"===a[0]&&"n"===a[1]&&"-"===a[2]},removeEventPrefix:function(a){return a.slice(f)},findController:function(a){for(;a.parentNode;){if(a.eventController)return a.eventController;a=a.parentNode}return a.host},getEventHandler:function(a,b,c){var d=this;return function(e){a&&a.PolymerBase||(a=d.findController(b));var f=[e,e.detail,e.currentTarget];a.dispatchMethod(a,c,f)}},prepareEventBinding:function(a,b){if(this.hasEventPrefix(b)){var c=this.removeEventPrefix(b);c=d[c]||c;var e=this;return function(b,d,f){function g(){return"{{ "+a+" }}"}var h=e.getEventHandler(void 0,d,a);return PolymerGestures.addEventListener(d,c,h),f?void 0:{open:g,discardChanges:g,close:function(){PolymerGestures.removeEventListener(d,c,h)}}}}}},f=c.length;a.api.declaration.events=e}(Polymer),function(a){var b=["attribute"],c={inferObservers:function(a){var b,c=a.observe;for(var d in a)"Changed"===d.slice(-7)&&(b=d.slice(0,-7),this.canObserveProperty(b)&&(c||(c=a.observe={}),c[b]=c[b]||d))},canObserveProperty:function(a){return b.indexOf(a)<0},explodeObservers:function(a){var b=a.observe;if(b){var c={};for(var d in b)for(var e,f=d.split(" "),g=0;e=f[g];g++)c[e]=b[d];a.observe=c}},optimizePropertyMaps:function(a){if(a.observe){var b=a._observeNames=[];for(var c in a.observe)for(var d,e=c.split(" "),f=0;d=e[f];f++)b.push(d)}if(a.publish){var b=a._publishNames=[];for(var c in a.publish)b.push(c)}if(a.computed){var b=a._computedNames=[];for(var c in a.computed)b.push(c)}},publishProperties:function(a,b){var c=a.publish;c&&(this.requireProperties(c,a,b),this.filterInvalidAccessorNames(c),a._publishLC=this.lowerCaseMap(c));var d=a.computed;d&&this.filterInvalidAccessorNames(d)},filterInvalidAccessorNames:function(a){for(var b in a)this.propertyNameBlacklist[b]&&(console.warn('Cannot define property "'+b+'" for element "'+this.name+'" because it has the same name as an HTMLElement property, and not all browsers support overriding that. Consider giving it a different name.'),delete a[b])},requireProperties:function(a,b){b.reflect=b.reflect||{};for(var c in a){var d=a[c];d&&void 0!==d.reflect&&(b.reflect[c]=Boolean(d.reflect),d=d.value),void 0!==d&&(b[c]=d)}},lowerCaseMap:function(a){var b={};for(var c in a)b[c.toLowerCase()]=c;return b},createPropertyAccessor:function(a,b){var c=this.prototype,d=a+"_",e=a+"Observable_";c[d]=c[a],Object.defineProperty(c,a,{get:function(){var a=this[e];return a&&a.deliver(),this[d]},set:function(c){if(b)return this[d];var f=this[e];if(f)return void f.setValue(c);var g=this[d];return this[d]=c,this.emitPropertyChangeRecord(a,c,g),c},configurable:!0})},createPropertyAccessors:function(a){var b=a._computedNames;if(b&&b.length)for(var c,d=0,e=b.length;e>d&&(c=b[d]);d++)this.createPropertyAccessor(c,!0);var b=a._publishNames;if(b&&b.length)for(var c,d=0,e=b.length;e>d&&(c=b[d]);d++)a.computed&&a.computed[c]||this.createPropertyAccessor(c)},propertyNameBlacklist:{children:1,"class":1,id:1,hidden:1,style:1,title:1}};a.api.declaration.properties=c}(Polymer),function(a){var b="attributes",c=/\s|,/,d={inheritAttributesObjects:function(a){this.inheritObject(a,"publishLC"),this.inheritObject(a,"_instanceAttributes")},publishAttributes:function(a){var d=this.getAttribute(b);if(d)for(var e,f=a.publish||(a.publish={}),g=d.split(c),h=0,i=g.length;i>h;h++)e=g[h].trim(),e&&void 0===f[e]&&(f[e]=void 0)},accumulateInstanceAttributes:function(){for(var a,b=this.prototype._instanceAttributes,c=this.attributes,d=0,e=c.length;e>d&&(a=c[d]);d++)this.isInstanceAttribute(a.name)&&(b[a.name]=a.value)},isInstanceAttribute:function(a){return!this.blackList[a]&&"on-"!==a.slice(0,3)},blackList:{name:1,"extends":1,constructor:1,noscript:1,assetpath:1,"cache-csstext":1}};d.blackList[b]=1,a.api.declaration.attributes=d}(Polymer),function(a){var b=a.api.declaration.events,c=new PolymerExpressions,d=c.prepareBinding;c.prepareBinding=function(a,e,f){return b.prepareEventBinding(a,e,f)||d.call(c,a,e,f)};var e={syntax:c,fetchTemplate:function(){return this.querySelector("template")},templateContent:function(){var a=this.fetchTemplate();return a&&a.content},installBindingDelegate:function(a){a&&(a.bindingDelegate=this.syntax)}};a.api.declaration.mdv=e}(Polymer),function(a){function b(a){if(!Object.__proto__){var b=Object.getPrototypeOf(a);a.__proto__=b,d(b)&&(b.__proto__=Object.getPrototypeOf(b))}}var c=a.api,d=a.isBase,e=a.extend,f=window.ShadowDOMPolyfill,g={register:function(a,b){this.buildPrototype(a,b),this.registerPrototype(a,b),this.publishConstructor()},buildPrototype:function(b,c){var d=a.getRegisteredPrototype(b),e=this.generateBasePrototype(c);this.desugarBeforeChaining(d,e),this.prototype=this.chainPrototypes(d,e),this.desugarAfterChaining(b,c)},desugarBeforeChaining:function(a,b){a.element=this,this.publishAttributes(a,b),this.publishProperties(a,b),this.inferObservers(a),this.explodeObservers(a)},chainPrototypes:function(a,c){this.inheritMetaData(a,c);var d=this.chainObject(a,c);return b(d),d},inheritMetaData:function(a,b){this.inheritObject("observe",a,b),this.inheritObject("publish",a,b),this.inheritObject("reflect",a,b),this.inheritObject("_publishLC",a,b),this.inheritObject("_instanceAttributes",a,b),this.inheritObject("eventDelegates",a,b)},desugarAfterChaining:function(a,b){this.optimizePropertyMaps(this.prototype),this.createPropertyAccessors(this.prototype),this.installBindingDelegate(this.fetchTemplate()),this.installSheets(),this.resolveElementPaths(this),this.accumulateInstanceAttributes(),this.parseHostEvents(),this.addResolvePathApi(),f&&WebComponents.ShadowCSS.shimStyling(this.templateContent(),a,b),this.prototype.registerCallback&&this.prototype.registerCallback(this)},publishConstructor:function(){var a=this.getAttribute("constructor");a&&(window[a]=this.ctor)},generateBasePrototype:function(a){var b=this.findBasePrototype(a);if(!b){var b=HTMLElement.getPrototypeForTag(a);b=this.ensureBaseApi(b),h[a]=b}return b},findBasePrototype:function(a){return h[a]},ensureBaseApi:function(a){if(a.PolymerBase)return a;var b=Object.create(a);return c.publish(c.instance,b),this.mixinMethod(b,a,c.instance.mdv,"bind"),b},mixinMethod:function(a,b,c,d){var e=function(a){return b[d].apply(this,a)};a[d]=function(){return this.mixinSuper=e,c[d].apply(this,arguments)}},inheritObject:function(a,b,c){var d=b[a]||{};b[a]=this.chainObject(d,c[a])},registerPrototype:function(a,b){var c={prototype:this.prototype},d=this.findTypeExtension(b);d&&(c["extends"]=d),HTMLElement.register(a,this.prototype),this.ctor=document.registerElement(a,c)},findTypeExtension:function(a){if(a&&a.indexOf("-")<0)return a;var b=this.findBasePrototype(a);return b.element?this.findTypeExtension(b.element["extends"]):void 0}},h={};g.chainObject=Object.__proto__?function(a,b){return a&&b&&a!==b&&(a.__proto__=b),a}:function(a,b){if(a&&b&&a!==b){var c=Object.create(b);a=e(c,a)}return a},c.declaration.prototype=g}(Polymer),function(a){function b(a){return document.contains(a)?j:i}function c(){return i.length?i[0]:j[0]}function d(a){f.waitToReady=!0,Polymer.endOfMicrotask(function(){HTMLImports.whenReady(function(){f.addReadyCallback(a),f.waitToReady=!1,f.check()})})}function e(a){if(void 0===a)return void f.ready();var b=setTimeout(function(){f.ready()},a);Polymer.whenReady(function(){clearTimeout(b)})}var f={wait:function(a){a.__queue||(a.__queue={},g.push(a))},enqueue:function(a,c,d){var e=a.__queue&&!a.__queue.check;return e&&(b(a).push(a),a.__queue.check=c,a.__queue.go=d),0!==this.indexOf(a)},indexOf:function(a){var c=b(a).indexOf(a);return c>=0&&document.contains(a)&&(c+=HTMLImports.useNative||HTMLImports.ready?i.length:1e9),c},go:function(a){var b=this.remove(a);b&&(a.__queue.flushable=!0,this.addToFlushQueue(b),this.check())},remove:function(a){var c=this.indexOf(a);if(0===c)return b(a).shift()},check:function(){var a=this.nextElement();return a&&a.__queue.check.call(a),this.canReady()?(this.ready(),!0):void 0},nextElement:function(){return c()},canReady:function(){return!this.waitToReady&&this.isEmpty()},isEmpty:function(){for(var a,b=0,c=g.length;c>b&&(a=g[b]);b++)if(a.__queue&&!a.__queue.flushable)return;return!0},addToFlushQueue:function(a){h.push(a)},flush:function(){if(!this.flushing){this.flushing=!0;for(var a;h.length;)a=h.shift(),a.__queue.go.call(a),a.__queue=null;this.flushing=!1}},ready:function(){var a=CustomElements.ready;CustomElements.ready=!1,this.flush(),CustomElements.useNative||CustomElements.upgradeDocumentTree(document),CustomElements.ready=a,Polymer.flush(),requestAnimationFrame(this.flushReadyCallbacks)},addReadyCallback:function(a){a&&k.push(a)},flushReadyCallbacks:function(){if(k)for(var a;k.length;)(a=k.shift())()},waitingFor:function(){for(var a,b=[],c=0,d=g.length;d>c&&(a=g[c]);c++)a.__queue&&!a.__queue.flushable&&b.push(a);return b},waitToReady:!0},g=[],h=[],i=[],j=[],k=[];a.elements=g,a.waitingFor=f.waitingFor.bind(f),a.forceReady=e,a.queue=f,a.whenReady=a.whenPolymerReady=d}(Polymer),function(a){function b(a){return Boolean(HTMLElement.getPrototypeForTag(a))}function c(a){return a&&a.indexOf("-")>=0}var d=a.extend,e=a.api,f=a.queue,g=a.whenReady,h=a.getRegisteredPrototype,i=a.waitingForPrototype,j=d(Object.create(HTMLElement.prototype),{createdCallback:function(){this.getAttribute("name")&&this.init()},init:function(){this.name=this.getAttribute("name"),this["extends"]=this.getAttribute("extends"),f.wait(this),this.loadResources(),this.registerWhenReady()},registerWhenReady:function(){this.registered||this.waitingForPrototype(this.name)||this.waitingForQueue()||this.waitingForResources()||f.go(this)},_register:function(){c(this["extends"])&&!b(this["extends"])&&console.warn("%s is attempting to extend %s, an unregistered element or one that was not registered with Polymer.",this.name,this["extends"]),this.register(this.name,this["extends"]),this.registered=!0},waitingForPrototype:function(a){return h(a)?void 0:(i(a,this),this.handleNoScript(a),!0)},handleNoScript:function(a){this.hasAttribute("noscript")&&!this.noscript&&(this.noscript=!0,Polymer(a))},waitingForResources:function(){return this._needsResources},waitingForQueue:function(){return f.enqueue(this,this.registerWhenReady,this._register)},loadResources:function(){this._needsResources=!0,this.loadStyles(function(){this._needsResources=!1,this.registerWhenReady()}.bind(this))}});e.publish(e.declaration,j),g(function(){document.body.removeAttribute("unresolved"),document.dispatchEvent(new CustomEvent("polymer-ready",{bubbles:!0}))}),document.registerElement("polymer-element",{prototype:j})}(Polymer),function(a){function b(a,b){a?(document.head.appendChild(a),d(b)):b&&b()}function c(a,c){if(a&&a.length){for(var d,e,f=document.createDocumentFragment(),g=0,h=a.length;h>g&&(d=a[g]);g++)e=document.createElement("link"),e.rel="import",e.href=d,f.appendChild(e);b(f,c)}else c&&c()}var d=a.whenReady;a["import"]=c,a.importElements=b}(Polymer),function(){var a=document.createElement("polymer-element");a.setAttribute("name","auto-binding"),a.setAttribute("extends","template"),a.init(),Polymer("auto-binding",{createdCallback:function(){this.syntax=this.bindingDelegate=this.makeSyntax(),Polymer.whenPolymerReady(function(){this.model=this,this.setAttribute("bind",""),this.async(function(){this.marshalNodeReferences(this.parentNode),this.fire("template-bound")})}.bind(this))},makeSyntax:function(){var a=Object.create(Polymer.api.declaration.events),b=this;a.findController=function(){return b.model};var c=new PolymerExpressions,d=c.prepareBinding;return c.prepareBinding=function(b,e,f){return a.prepareEventBinding(b,e,f)||d.call(c,b,e,f)},c}})}();
\ No newline at end of file
diff --git a/polymer_interop/lib/src/polymer_proxy_mixin.dart b/polymer_interop/lib/src/polymer_proxy_mixin.dart
new file mode 100644
index 0000000..cd9f0f8
--- /dev/null
+++ b/polymer_interop/lib/src/polymer_proxy_mixin.dart
@@ -0,0 +1,63 @@
+// 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 polymer_interop.src.js_element_proxy;
+
+import 'dart:html' show Element, DocumentFragment;
+import 'dart:js' as js;
+import 'package:web_components/web_components.dart'
+    show CustomElementProxyMixin;
+
+/// A mixin to make it easier to interoperate with Polymer JS elements. This
+/// exposes only a subset of the public api that is most useful from external
+/// elements.
+///
+/// Since mixins can't mixin or extend other mixins, you must also
+/// mixin the [CustomElementProxyMixin] class from `web_components`. The
+/// implements statement here enforces that.
+abstract class PolymerProxyMixin implements CustomElementProxyMixin {
+  /// The underlying Js Element's `$` property.
+  js.JsObject get $ => jsElement[r'$'];
+
+  /// By default the data bindings will be cleaned up when this custom element
+  /// is detached from the document. Overriding this to return `true` will
+  /// prevent that from happening.
+  bool get preventDispose => jsElement['preventDispose'];
+  set preventDispose(bool newValue) => jsElement['preventDispose'] = newValue;
+
+  /// Force any pending property changes to synchronously deliver to handlers
+  /// specified in the `observe` object. Note, normally changes are processed at
+  /// microtask time.
+  ///
+  // Dart note: renamed to `deliverPropertyChanges` to be more consistent with
+  // other polymer.dart elements.
+  void deliverPropertyChanges() {
+    jsElement.callMethod('deliverChanges', []);
+  }
+
+  /// Inject HTML which contains markup bound to this element into a target
+  /// element (replacing target element content).
+  DocumentFragment injectBoundHTML(String html, [Element element]) =>
+      jsElement.callMethod('injectBoundHTML', [html, element]);
+
+  /// Creates dom cloned from the given template, instantiating bindings with
+  /// this element as the template model and `PolymerExpressions` as the binding
+  /// delegate.
+  DocumentFragment instanceTemplate(Element template) =>
+      jsElement.callMethod('instanceTemplate', [template]);
+
+  /// This method should rarely be used and only if `cancelUnbindAll` has been
+  /// called to prevent element unbinding. In this case, the element's bindings
+  /// will not be automatically cleaned up and it cannot be garbage collected by
+  /// by the system. If memory pressure is a concern or a large amount of
+  /// elements need to be managed in this way, `unbindAll` can be called to
+  /// deactivate the element's bindings and allow its memory to be reclaimed.
+  void unbindAll() => jsElement.callMethod('unbindAll', []);
+
+  /// Call in `detached` to prevent the element from unbinding when it is
+  /// detached from the dom. The element is unbound as a cleanup step that
+  /// allows its memory to be reclaimed. If `cancelUnbindAll` is used, consider
+  ///calling `unbindAll` when the element is no longer needed. This will allow
+  ///its memory to be reclaimed.
+  void cancelUnbindAll() => jsElement.callMethod('cancelUnbindAll', []);
+}
diff --git a/polymer_interop/pubspec.yaml b/polymer_interop/pubspec.yaml
new file mode 100644
index 0000000..4e96534
--- /dev/null
+++ b/polymer_interop/pubspec.yaml
@@ -0,0 +1,14 @@
+name: polymer_interop
+version: 0.1.0+2
+author: Polymer.dart Authors <web-ui-dev@dartlang.org>
+description: Common package containing the original polymer js sources
+homepage: https://github.com/dart-lang/polymer_interop
+dependencies:
+  barback: '>=0.142 <0.16.0'
+  web_components: '>=0.11.2 <0.12.0'
+environment:
+  sdk: ">=1.9.0 <2.0.0"
+transformers:
+- polymer_interop/src/build/replace_polymer_js:
+    $include:
+      - lib/src/js/polymer.html
diff --git a/pool/lib/pool.dart b/pool/lib/pool.dart
new file mode 100644
index 0000000..61482e3
--- /dev/null
+++ b/pool/lib/pool.dart
@@ -0,0 +1,142 @@
+// 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 pool;
+
+import 'dart:async';
+import 'dart:collection';
+
+import 'package:stack_trace/stack_trace.dart';
+
+/// Manages an abstract pool of resources with a limit on how many may be in use
+/// at once.
+///
+/// When a resource is needed, the user should call [request]. When the returned
+/// future completes with a [PoolResource], the resource may be allocated. Once
+/// the resource has been released, the user should call [PoolResource.release].
+/// The pool will ensure that only a certain number of [PoolResource]s may be
+/// allocated at once.
+class Pool {
+  /// Completers for requests beyond the first [_maxAllocatedResources].
+  ///
+  /// When an item is released, the next element of [_requestedResources] will
+  /// be completed.
+  final _requestedResources = new Queue<Completer<PoolResource>>();
+
+  /// The maximum number of resources that may be allocated at once.
+  final int _maxAllocatedResources;
+
+  /// The number of resources that are currently allocated.
+  int _allocatedResources = 0;
+
+  /// The timeout timer.
+  ///
+  /// If [_timeout] isn't null, this timer is set as soon as the resource limit
+  /// is reached and is reset every time an resource is released or a new
+  /// resource is requested. If it fires, that indicates that the caller became
+  /// deadlocked, likely due to files waiting for additional files to be read
+  /// before they could be closed.
+  Timer _timer;
+
+  /// The amount of time to wait before timing out the pending resources.
+  final Duration _timeout;
+
+  /// Creates a new pool with the given limit on how many resources may be
+  /// allocated at once.
+  ///
+  /// If [timeout] is passed, then if that much time passes without any activity
+  /// all pending [request] futures will throw a [TimeoutException]. This is
+  /// intended to avoid deadlocks.
+  Pool(this._maxAllocatedResources, {Duration timeout})
+      : _timeout = timeout;
+
+  /// Request a [PoolResource].
+  ///
+  /// If the maximum number of resources is already allocated, this will delay
+  /// until one of them is released.
+  Future<PoolResource> request() {
+    if (_allocatedResources < _maxAllocatedResources) {
+      _allocatedResources++;
+      return new Future.value(new PoolResource._(this));
+    } else {
+      var completer = new Completer<PoolResource>();
+      _requestedResources.add(completer);
+      _resetTimer();
+      return completer.future;
+    }
+  }
+
+  /// Requests a resource for the duration of [callback], which may return a
+  /// Future.
+  ///
+  /// The return value of [callback] is piped to the returned Future.
+  Future withResource(callback()) {
+    return request().then((resource) =>
+        Chain.track(new Future.sync(callback)).whenComplete(resource.release));
+  }
+
+  /// If there are any pending requests, this will fire the oldest one.
+  void _onResourceReleased() {
+    if (_requestedResources.isEmpty) {
+      _allocatedResources--;
+      if (_timer != null) {
+        _timer.cancel();
+        _timer = null;
+      }
+      return;
+    }
+
+    _resetTimer();
+    var pending = _requestedResources.removeFirst();
+    pending.complete(new PoolResource._(this));
+  }
+
+  /// A resource has been requested, allocated, or released.
+  void _resetTimer() {
+    if (_timer != null) _timer.cancel();
+    if (_timeout == null) {
+      _timer = null;
+    } else {
+      _timer = new Timer(_timeout, _onTimeout);
+    }
+  }
+
+  /// Handles [_timer] timing out by causing all pending resource completers to
+  /// emit exceptions.
+  void _onTimeout() {
+    for (var completer in _requestedResources) {
+      completer.completeError(
+          new TimeoutException("Pool deadlock: all resources have been "
+              "allocated for too long.",
+              _timeout),
+          new Chain.current());
+    }
+    _requestedResources.clear();
+    _timer = null;
+  }
+}
+
+/// A member of a [Pool].
+///
+/// A [PoolResource] is a token that indicates that a resource is allocated.
+/// When the associated resource is released, the user should call [release].
+class PoolResource {
+  final Pool _pool;
+
+  /// Whether [this] has been released yet.
+  bool _released = false;
+
+  PoolResource._(this._pool);
+
+  /// Tells the parent [Pool] that the resource associated with this resource is
+  /// no longer allocated, and that a new [PoolResource] may be allocated.
+  void release() {
+    if (_released) {
+      throw new StateError("A PoolResource may only be released once.");
+    }
+    _released = true;
+    _pool._onResourceReleased();
+  }
+}
+
diff --git a/pool/pubspec.yaml b/pool/pubspec.yaml
new file mode 100644
index 0000000..9972cbb
--- /dev/null
+++ b/pool/pubspec.yaml
@@ -0,0 +1,10 @@
+name: pool
+version: 1.0.1
+author: Dart Team <misc@dartlang.org>
+description: A class for managing a finite pool of resources.
+homepage: http://www.dartlang.org
+dependencies:
+  stack_trace: ">=0.9.2 <2.0.0"
+dev_dependencies:
+  fake_async: ">=0.1.0 <0.2.0"
+  unittest: ">=0.11.0 <0.12.0"
diff --git a/quiver/lib/async.dart b/quiver/lib/async.dart
new file mode 100644
index 0000000..1cc8e12
--- /dev/null
+++ b/quiver/lib/async.dart
@@ -0,0 +1,48 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+library quiver.async;
+
+import 'dart:async';
+
+import 'package:quiver/time.dart';
+
+part 'src/async/countdown_timer.dart';
+part 'src/async/future_group.dart';
+part 'src/async/future_stream.dart';
+part 'src/async/iteration.dart';
+part 'src/async/metronome.dart';
+part 'src/async/stream_router.dart';
+
+/**
+ * The signature of a one-shot [Timer] factory.
+ */
+typedef Timer CreateTimer(Duration duration, void callback());
+
+/**
+ * Creates a new one-shot [Timer] using `new Timer(duration, callback)`.
+ */
+Timer createTimer(Duration duration, void callback()) =>
+    new Timer(duration, callback);
+/**
+ * The signature of a periodic timer factory.
+ */
+typedef Timer CreateTimerPeriodic(Duration duration, void callback(Timer));
+
+/**
+ * Creates a new periodic [Timer] using
+ * `new Timer.periodic(duration, callback)`.
+ */
+Timer createTimerPeriodic(Duration duration, void callback(Timer)) =>
+    new Timer.periodic(duration, callback);
diff --git a/quiver/lib/cache.dart b/quiver/lib/cache.dart
new file mode 100644
index 0000000..b962bce
--- /dev/null
+++ b/quiver/lib/cache.dart
@@ -0,0 +1,23 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+library quiver.cache;
+
+import 'dart:async';
+import 'dart:collection';
+
+import 'package:quiver/collection.dart' show LruMap;
+
+part 'src/cache/cache.dart';
+part 'src/cache/map_cache.dart';
diff --git a/quiver/lib/check.dart b/quiver/lib/check.dart
new file mode 100644
index 0000000..abbefb6
--- /dev/null
+++ b/quiver/lib/check.dart
@@ -0,0 +1,79 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/// A simple set of pre/post-condition checkers based on the
+/// [Guava](https://code.google.com/p/guava-libraries/) Preconditions
+/// class in Java.
+///
+/// These checks are stronger than 'assert' statements, which can be
+/// switched off, so they must only be used in situations where we actively
+/// want the program to break when the check fails.
+///
+/// ## Performance
+/// Performance may be an issue with these checks if complex logic is computed
+/// in order to make the method call. You should be careful with its use in
+/// these cases - this library is aimed at improving maintainability and
+/// readability rather than performance. They are also useful when the program
+/// should fail early - for example, null-checking a parameter that might not
+/// be used until the end of the method call.
+///
+/// ## Error messages
+/// The message parameter can be either a `() => Object` or any other `Object`.
+/// The object will be converted to an error message by calling its
+/// `toString()`. The `Function` should be preferred if the message is complex
+/// to construct (i.e., it uses `String` interpolation), because it is only
+/// called when the check fails.
+///
+/// If the message parameter is `null` or returns `null`, a default error
+/// message will be used.
+library quiver.check;
+
+/// Throws an [ArgumentError] if the given [expression] is `false`.
+void checkArgument(bool expression, {message}) {
+  if (!expression) {
+    throw new ArgumentError(_resolveMessage(message, null));
+  }
+}
+
+/// Throws a [RangeError] if the given [index] is not a valid index for a list
+/// with [size] elements. Otherwise, returns the [index] parameter.
+int checkListIndex(int index, int size, {message}) {
+  if (index < 0 || index >= size) {
+    throw new RangeError(_resolveMessage(
+        message, 'index $index not valid for list of size $size'));
+  }
+  return index;
+}
+
+/// Throws an [ArgumentError] if the given [reference] is `null`. Otherwise,
+/// returns the [reference] parameter.
+dynamic checkNotNull(reference, {message}) {
+  if (reference == null) {
+    throw new ArgumentError(_resolveMessage(message, 'null pointer'));
+  }
+  return reference;
+}
+
+/// Throws a [StateError] if the given [expression] is `false`.
+void checkState(bool expression, {message}) {
+  if (!expression) {
+    throw new StateError(_resolveMessage(message, 'failed precondition'));
+  }
+}
+
+String _resolveMessage(message, String defaultMessage) {
+  if (message is Function) message = message();
+  if (message == null) return defaultMessage;
+  return message.toString();
+}
diff --git a/quiver/lib/collection.dart b/quiver/lib/collection.dart
new file mode 100644
index 0000000..269aaac
--- /dev/null
+++ b/quiver/lib/collection.dart
@@ -0,0 +1,87 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Collection classes and related utilities.
+ */
+library quiver.collection;
+
+import 'dart:collection';
+import 'dart:math';
+
+import 'package:quiver/core.dart';
+import 'package:quiver/iterables.dart';
+
+part 'src/collection/bimap.dart';
+part 'src/collection/lru_map.dart';
+part 'src/collection/multimap.dart';
+part 'src/collection/treeset.dart';
+part 'src/collection/delegates/iterable.dart';
+part 'src/collection/delegates/list.dart';
+part 'src/collection/delegates/map.dart';
+part 'src/collection/delegates/queue.dart';
+part 'src/collection/delegates/set.dart';
+
+/**
+ * Checks [List]s [a] and [b] for equality.
+ *
+ * Returns `true` if [a] and [b] are both null, or they are the same length and
+ * every element of [a] is equal to the corresponding element at the same index
+ * in [b].
+ */
+bool listsEqual(List a, List b) {
+  if (a == b) return true;
+  if (a == null || b == null) return false;
+  if (a.length != b.length) return false;
+
+  for (int i = 0; i < a.length; i++) {
+    if (a[i] != b[i]) return false;
+  }
+
+  return true;
+}
+
+/**
+ * Checks [Map]s [a] and [b] for equality.
+ *
+ * Returns `true` if [a] and [b] are both null, or they are the same length and
+ * every key `k` in [a] exists in [b] and the values `a[k] == b[k]`.
+ */
+bool mapsEqual(Map a, Map b) {
+  if (a == b) return true;
+  if (a == null || b == null) return false;
+  if (a.length != b.length) return false;
+
+  for (var k in a.keys) {
+    var bValue = b[k];
+    if (bValue == null && !b.containsKey(k)) return false;
+    if (bValue != a[k]) return false;
+  }
+
+  return true;
+}
+
+/**
+ * Checks [Set]s [a] and [b] for equality.
+ *
+ * Returns `true` if [a] and [b] are both null, or they are the same length and
+ * every element in [b] exists in [a].
+ */
+bool setsEqual(Set a, Set b) {
+  if (a == b) return true;
+  if (a == null || b == null) return false;
+  if (a.length != b.length) return false;
+
+  return a.containsAll(b);
+}
diff --git a/quiver/lib/core.dart b/quiver/lib/core.dart
new file mode 100644
index 0000000..8239f2d
--- /dev/null
+++ b/quiver/lib/core.dart
@@ -0,0 +1,35 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Simple code with broad use cases.
+ */
+library quiver.core;
+
+part 'src/core/hash.dart';
+part 'src/core/optional.dart';
+
+/**
+ * Returns the first non-null argument. If all arguments are null, throws
+ * an [ArgumentError].
+ *
+ * Note: if [o1] is an [Optional], this can be accomplished with `o1.or(o2)`.
+ */
+firstNonNull(o1, o2, [o3, o4]) {
+  if (o1 != null) return o1;
+  if (o2 != null) return o2;
+  if (o3 != null) return o3;
+  if (o4 != null) return o4;
+  throw new ArgumentError('All arguments were null');
+}
diff --git a/quiver/lib/io.dart b/quiver/lib/io.dart
new file mode 100644
index 0000000..e5e5e4e
--- /dev/null
+++ b/quiver/lib/io.dart
@@ -0,0 +1,78 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+library quiver.io;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:quiver/async.dart';
+
+/**
+ * Converts a [Stream] of byte lists to a [String].
+ */
+Future<String> byteStreamToString(Stream<List<int>> stream,
+    {Encoding encoding: UTF8}) {
+  return stream.transform(encoding.decoder).join();
+}
+
+/**
+ * Gets the full path of [path] by using [File.fullPathSync].
+ */
+String getFullPath(path) => new File(path).resolveSymbolicLinksSync();
+
+/**
+ * Lists the sub-directories and files of this Directory, optionally recursing
+ * into sub-directories based on the return value of [visit].
+ *
+ * [visit] is called with a [File], [Directory] or [Link] to a directory,
+ * never a Symlink to a File. If [visit] returns true, then it's argument is
+ * listed recursively.
+ */
+Future visitDirectory(Directory dir, Future<bool> visit(FileSystemEntity f)) {
+  var futureGroup = new FutureGroup();
+
+  void _list(Directory dir) {
+    var completer = new Completer();
+    futureGroup.add(completer.future);
+    dir.list(followLinks: false).listen((FileSystemEntity entity) {
+      var future = visit(entity);
+      if (future != null) {
+        futureGroup.add(future.then((bool recurse) {
+          // recurse on directories, but not cyclic symlinks
+          if (entity is! File && recurse == true) {
+            if (entity is Link) {
+              if (FileSystemEntity.typeSync(entity.path, followLinks: true) ==
+                  FileSystemEntityType.DIRECTORY) {
+                var fullPath = getFullPath(entity.path).toString();
+                var dirFullPath = getFullPath(dir.path).toString();
+                if (!dirFullPath.startsWith(fullPath)) {
+                  _list(new Directory(entity.path));
+                }
+              }
+            } else {
+              _list(entity);
+            }
+          }
+        }));
+      }
+    }, onDone: () {
+      completer.complete(null);
+    }, cancelOnError: true);
+  }
+  _list(dir);
+
+  return futureGroup.future;
+}
diff --git a/quiver/lib/iterables.dart b/quiver/lib/iterables.dart
new file mode 100644
index 0000000..ed1690e
--- /dev/null
+++ b/quiver/lib/iterables.dart
@@ -0,0 +1,29 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+library quiver.iterables;
+
+import 'dart:collection';
+
+part 'src/iterables/concat.dart';
+part 'src/iterables/count.dart';
+part 'src/iterables/cycle.dart';
+part 'src/iterables/enumerate.dart';
+part 'src/iterables/infinite_iterable.dart';
+part 'src/iterables/merge.dart';
+part 'src/iterables/min_max.dart';
+part 'src/iterables/partition.dart';
+part 'src/iterables/generating_iterable.dart';
+part 'src/iterables/range.dart';
+part 'src/iterables/zip.dart';
diff --git a/quiver/lib/mirrors.dart b/quiver/lib/mirrors.dart
new file mode 100644
index 0000000..9f73e9f
--- /dev/null
+++ b/quiver/lib/mirrors.dart
@@ -0,0 +1,90 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+library quiver.mirrors;
+
+import 'dart:mirrors';
+
+/**
+ * Returns the qualified name of [t].
+ */
+Symbol getTypeName(Type t) => reflectClass(t).qualifiedName;
+
+/**
+ * Returns true if [o] implements [type].
+ */
+bool implements(Object o, Type type) =>
+    classImplements(reflect(o).type, reflectClass(type));
+
+/**
+ * Returns true if the class represented by [classMirror] implements the class
+ * represented by [interfaceMirror].
+ */
+bool classImplements(ClassMirror classMirror, ClassMirror interfaceMirror) {
+  if (classMirror == null) return false;
+  // TODO: change to comparing mirrors when dartbug.com/19781 is fixed
+  if (classMirror.qualifiedName == interfaceMirror.qualifiedName) return true;
+  if (classImplements(classMirror.superclass, interfaceMirror)) return true;
+  if (classMirror.superinterfaces
+      .any((i) => classImplements(i, interfaceMirror))) return true;
+  return false;
+}
+
+/**
+ * Walks up the class hierarchy to find a method declaration with the given
+ * [name].
+ *
+ * Note that it's not possible to tell if there's an implementation via
+ * noSuchMethod().
+ */
+DeclarationMirror getDeclaration(ClassMirror classMirror, Symbol name) {
+  if (classMirror.declarations.containsKey(name)) {
+    return classMirror.declarations[name];
+  }
+  if (classMirror.superclass != null) {
+    var mirror = getDeclaration(classMirror.superclass, name);
+    if (mirror != null) {
+      return mirror;
+    }
+  }
+  for (ClassMirror supe in classMirror.superinterfaces) {
+    var mirror = getDeclaration(supe, name);
+    if (mirror != null) {
+      return mirror;
+    }
+  }
+  return null;
+}
+
+/**
+ * Closurzes a method reflectively.
+ */
+class Method /* implements Function */ {
+  final InstanceMirror mirror;
+  final Symbol symbol;
+
+  Method(this.mirror, this.symbol);
+
+  dynamic noSuchMethod(Invocation i) {
+    if (i.isMethod && i.memberName == const Symbol('call')) {
+      if (i.namedArguments != null && i.namedArguments.isNotEmpty) {
+        // this will fail until named argument support is implemented
+        return mirror.invoke(
+            symbol, i.positionalArguments, i.namedArguments).reflectee;
+      }
+      return mirror.invoke(symbol, i.positionalArguments).reflectee;
+    }
+    return super.noSuchMethod(i);
+  }
+}
diff --git a/quiver/lib/pattern.dart b/quiver/lib/pattern.dart
new file mode 100644
index 0000000..937b0a0
--- /dev/null
+++ b/quiver/lib/pattern.dart
@@ -0,0 +1,85 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * This library contains utilities for working with [RegExp]s and other
+ * [Pattern]s.
+ */
+library quiver.pattern;
+
+part 'src/pattern/glob.dart';
+
+// From the PatternCharacter rule here:
+// http://ecma-international.org/ecma-262/5.1/#sec-15.10
+final _specialChars = new RegExp(r'([\\\^\$\.\|\+\[\]\(\)\{\}])');
+
+/**
+ * Escapes special regex characters in [str] so that it can be used as a
+ * literal match inside of a [RegExp].
+ *
+ * The special characters are: \ ^ $ . | + [ ] ( ) { }
+ * as defined here: http://ecma-international.org/ecma-262/5.1/#sec-15.10
+ */
+String escapeRegex(String str) => str.splitMapJoin(_specialChars,
+    onMatch: (Match m) => '\\${m.group(0)}', onNonMatch: (s) => s);
+
+/**
+ * Returns a [Pattern] that matches against every pattern in [include] and
+ * returns all the matches. If the input string matches against any pattern in
+ * [exclude] no matches are returned.
+ */
+Pattern matchAny(Iterable<Pattern> include, {Iterable<Pattern> exclude}) =>
+    new _MultiPattern(include, exclude: exclude);
+
+class _MultiPattern extends Pattern {
+  final Iterable<Pattern> include;
+  final Iterable<Pattern> exclude;
+
+  _MultiPattern(Iterable<Pattern> this.include,
+      {Iterable<Pattern> this.exclude});
+
+  Iterable<Match> allMatches(String str, [int start = 0]) {
+    var _allMatches = [];
+    for (var pattern in include) {
+      var matches = pattern.allMatches(str, start);
+      if (_hasMatch(matches)) {
+        if (exclude != null) {
+          for (var excludePattern in exclude) {
+            if (_hasMatch(excludePattern.allMatches(str, start))) {
+              return [];
+            }
+          }
+        }
+        _allMatches.add(matches);
+      }
+    }
+    return _allMatches.expand((x) => x);
+  }
+
+  Match matchAsPrefix(String str, [int start = 0]) {
+    return allMatches(str).firstWhere((match) => match.start == start,
+        orElse: () => null);
+  }
+}
+
+/**
+ * Returns true if [pattern] has a single match in [str] that matches the whole
+ * string, not a substring.
+ */
+bool matchesFull(Pattern pattern, String str) {
+  var match = pattern.matchAsPrefix(str);
+  return match != null && match.end == str.length;
+}
+
+bool _hasMatch(Iterable<Match> matches) => matches.iterator.moveNext();
diff --git a/quiver/lib/src/async/countdown_timer.dart b/quiver/lib/src/async/countdown_timer.dart
new file mode 100644
index 0000000..cd052e7
--- /dev/null
+++ b/quiver/lib/src/async/countdown_timer.dart
@@ -0,0 +1,75 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.async;
+
+/**
+ * A simple countdown timer that fires events in regular increments until a
+ * duration has passed.
+ *
+ * CountdownTimer implements [Stream] and sends itself as the event. From the
+ * timer you can get the [remaining] and [elapsed] time, or [cancel] the timer.
+ */
+class CountdownTimer extends Stream<CountdownTimer> {
+  static const _THRESHOLD_MS = 4;
+
+  final Duration _duration;
+  final Duration _increment;
+  final Stopwatch _stopwatch;
+  final StreamController<CountdownTimer> _controller;
+  Timer _timer;
+
+  /**
+   * Creates a new [CountdownTimer] that fires events in increments of
+   * [increment], until the [duration] has passed.
+   *
+   * [stopwatch] is for testing purposes. If you're using CountdownTimer and
+   * need to control time in a test, pass a mock or a fake. See [FakeAsync] and
+   * [FakeStopwatch].
+   */
+  CountdownTimer(Duration duration, Duration increment, {Stopwatch stopwatch})
+      : _duration = duration,
+        _increment = increment,
+        _stopwatch = stopwatch == null ? new Stopwatch() : stopwatch,
+        _controller = new StreamController<CountdownTimer>.broadcast(
+            sync: true) {
+    _timer = new Timer.periodic(increment, _tick);
+    _stopwatch.start();
+  }
+
+  StreamSubscription<CountdownTimer> listen(void onData(CountdownTimer event),
+          {Function onError, void onDone(), bool cancelOnError}) =>
+      _controller.stream.listen(onData, onError: onError, onDone: onDone);
+
+  Duration get elapsed => _stopwatch.elapsed;
+
+  Duration get remaining => _duration - _stopwatch.elapsed;
+
+  bool get isRunning => _stopwatch.isRunning;
+
+  cancel() {
+    _stopwatch.stop();
+    _timer.cancel();
+    _controller.close();
+  }
+
+  _tick(Timer timer) {
+    var t = remaining;
+    _controller.add(this);
+    // timers may have a 4ms resolution
+    if (t.inMilliseconds < _THRESHOLD_MS) {
+      cancel();
+    }
+  }
+}
diff --git a/quiver/lib/src/async/future_group.dart b/quiver/lib/src/async/future_group.dart
new file mode 100644
index 0000000..c96ee1a
--- /dev/null
+++ b/quiver/lib/src/async/future_group.dart
@@ -0,0 +1,74 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.async;
+
+/**
+ * A collection of [Future]s that signals when all added Futures complete. New
+ * Futures can be added to the group as long as it hasn't completed.
+ *
+ * FutureGroup is useful for managing a set of async tasks that may spawn new
+ * async tasks as they execute.
+ */
+class FutureGroup<E> {
+  static const _FINISHED = -1;
+
+  int _pending = 0;
+  Future _failedTask;
+  final Completer<List> _completer = new Completer<List>();
+  final List results = [];
+
+  /** Gets the task that failed, if any. */
+  Future get failedTask => _failedTask;
+
+  /**
+   * Wait for [task] to complete.
+   *
+   * If this group has already been marked as completed, a [StateError] will be
+   * thrown.
+   *
+   * If this group has a [failedTask], new tasks will be ignored, because the
+   * error has already been signaled.
+   */
+  void add(Future task) {
+    if (_failedTask != null) return;
+    if (_pending == _FINISHED) throw new StateError("Future already completed");
+
+    _pending++;
+    var i = results.length;
+    results.add(null);
+    task.then((res) {
+      results[i] = res;
+      if (_failedTask != null) return;
+      _pending--;
+      if (_pending == 0) {
+        _pending = _FINISHED;
+        _completer.complete(results);
+      }
+    }, onError: (e, s) {
+      if (_failedTask != null) return;
+      _failedTask = task;
+      _completer.completeError(e, s);
+    });
+  }
+
+  /**
+   * A Future that complets with a List of the values from all the added
+   * tasks, when they have all completed.
+   *
+   * If any task fails, this Future will receive the error. Only the first
+   * error will be sent to the Future.
+   */
+  Future<List<E>> get future => _completer.future;
+}
diff --git a/quiver/lib/src/async/future_stream.dart b/quiver/lib/src/async/future_stream.dart
new file mode 100644
index 0000000..58b8629
--- /dev/null
+++ b/quiver/lib/src/async/future_stream.dart
@@ -0,0 +1,82 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.async;
+
+/**
+ * A Stream that will emit the same values as the stream returned by [future]
+ * once [future] completes.
+ *
+ * If [future] completes to an error, the return value will emit that error and
+ * then close.
+ *
+ * If [broadcast] is true, this will be a broadcast stream. This assumes that
+ * the stream returned by [future] will be a broadcast stream as well.
+ * [broadcast] defaults to false.
+ *
+ * # Example
+ *
+ * This class is useful when you need to retreive some object via a `Future`,
+ * then return a `Stream` from that object:
+ *
+ *     var futureOfStream = getResource().then((resource) => resource.stream);
+ *     return new FutureStream(futureOfStream);
+ */
+class FutureStream<T> extends Stream<T> {
+  Future<Stream<T>> _future;
+  StreamController<T> _controller;
+  StreamSubscription _subscription;
+
+  FutureStream(Future<Stream<T>> future, {bool broadcast: false}) {
+    _future = future.catchError((e, stackTrace) {
+      // Since [controller] is synchronous, it's likely that emitting an error
+      // will cause it to be cancelled before we call close.
+      if (_controller != null) {
+        _controller.addError(e, stackTrace);
+        _controller.close();
+      }
+      _controller = null;
+    });
+
+    if (broadcast == true) {
+      _controller = new StreamController.broadcast(
+          sync: true, onListen: _onListen, onCancel: _onCancel);
+    } else {
+      _controller = new StreamController(
+          sync: true, onListen: _onListen, onCancel: _onCancel);
+    }
+  }
+
+  _onListen() {
+    _future.then((stream) {
+      if (_controller == null) return;
+      _subscription = stream.listen(_controller.add,
+          onError: _controller.addError, onDone: _controller.close);
+    });
+  }
+
+  _onCancel() {
+    if (_subscription != null) _subscription.cancel();
+    _subscription = null;
+    _controller = null;
+  }
+
+  StreamSubscription<T> listen(void onData(T event),
+      {Function onError, void onDone(), bool cancelOnError}) {
+    return _controller.stream.listen(onData,
+        onError: onError, onDone: onDone, cancelOnError: cancelOnError);
+  }
+
+  bool get isBroadcast => _controller.stream.isBroadcast;
+}
diff --git a/quiver/lib/src/async/iteration.dart b/quiver/lib/src/async/iteration.dart
new file mode 100644
index 0000000..4132573
--- /dev/null
+++ b/quiver/lib/src/async/iteration.dart
@@ -0,0 +1,107 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.async;
+
+/**
+ * An asynchronous callback that returns a value.
+ */
+typedef Future<T> AsyncAction<T>(e);
+
+/**
+ * An asynchronous funcuntion that combines an element [e] with a previous value
+ * [previous], for use with [reduceAsync].
+ */
+typedef Future<T> AsyncCombiner<T>(T previous, e);
+
+/**
+ * Calls [action] for each item in [iterable] in turn, waiting for the Future
+ * returned by action to complete.
+ *
+ * If the Future completes to [true], iteration continues.
+ *
+ * The Future returned completes to [true] if the entire iterable was processed,
+ * otherwise [false].
+ */
+Future doWhileAsync(Iterable iterable, AsyncAction<bool> action) =>
+    _doWhileAsync(iterable.iterator, action);
+
+Future _doWhileAsync(
+    Iterator iterator, AsyncAction<bool> action) => (iterator.moveNext())
+    ? action(iterator.current).then((bool result) =>
+        (result) ? _doWhileAsync(iterator, action) : new Future.value(false))
+    : new Future.value(true);
+
+/**
+ * Reduces a collection to a single value by iteratively combining elements
+ * of the collection using the provided [combine] function. Similar to
+ * [Iterable.reduce], except that [combine] is an async function that returns a
+ * [Future].
+ */
+Future reduceAsync(Iterable iterable, initialValue, AsyncCombiner combine) =>
+    _reduceAsync(iterable.iterator, initialValue, combine);
+
+Future _reduceAsync(Iterator iterator, currentValue, AsyncCombiner combine) {
+  if (iterator.moveNext()) {
+    return combine(currentValue, iterator.current)
+        .then((result) => _reduceAsync(iterator, result, combine));
+  }
+  return new Future.value(currentValue);
+}
+
+/**
+ * Schedules calls to [action] for each element in [iterable]. No more than
+ * [maxTasks] calls to [action] will be pending at once.
+ */
+Future forEachAsync(Iterable iterable, AsyncAction action, {int maxTasks: 1}) {
+  if (maxTasks == null || maxTasks < 1) {
+    throw new ArgumentError("maxTasks must be greater than 0, was: $maxTasks");
+  }
+
+  if (iterable == null) {
+    throw new ArgumentError("iterable must not be null");
+  }
+
+  if (iterable.isEmpty) return new Future.value();
+
+  var completer = new Completer();
+  var iterator = iterable.iterator;
+  int pending = 0;
+  bool failed = false;
+
+  bool scheduleTask() {
+    if (pending < maxTasks && iterator.moveNext()) {
+      pending++;
+      var item = iterator.current;
+      scheduleMicrotask(() {
+        var task = action(item);
+        task.then((_) {
+          pending--;
+          if (failed) return;
+          if (!scheduleTask() && pending == 0) {
+            completer.complete();
+          }
+        }).catchError((e) {
+          if (failed) return;
+          failed = true;
+          completer.completeError(e);
+        });
+      });
+      return true;
+    }
+    return false;
+  }
+  while (scheduleTask()) {}
+  return completer.future;
+}
diff --git a/quiver/lib/src/async/metronome.dart b/quiver/lib/src/async/metronome.dart
new file mode 100644
index 0000000..41dc697
--- /dev/null
+++ b/quiver/lib/src/async/metronome.dart
@@ -0,0 +1,106 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.async;
+
+/**
+ * A stream of [DateTime] events at [interval]s centered on [anchor].
+ *
+ * This stream accounts for drift but only guarantees that events are
+ * delivered on or after the interval. If the system is busy for longer than
+ * two [interval]s, only one will be delivered.
+ *
+ * [anchor] defaults to [clock.now], which means the stream represents a
+ * self-correcting periodic timer. If anchor is the epoch, then the stream is
+ * synchronized to wall-clock time. It can be anchored anywhere in time, but
+ * this does not delay the first delivery.
+ *
+ * Examples:
+ *
+ *     new Metronome.epoch(aMinute).listen((d) => print(d));
+ *
+ * Could print the following stream of events, anchored by epoch,
+ * till the stream is canceled:
+ *     2014-05-04 14:06:00.001
+ *     2014-05-04 14:07:00.000
+ *     2014-05-04 14:08:00.003
+ *     ...
+ *
+ * Example anchored in the future (now = 2014-05-05 20:06:00.123)
+ *     new IsochronousStream.periodic(aMillisecond * 100,
+ *         anchorMs: DateTime.parse("2014-05-05 21:07:00"))
+ *         .listen((d) => print(d));
+ *
+ *     2014-05-04 20:06:00.223
+ *     2014-05-04 20:06:00.324
+ *     2014-05-04 20:06:00.423
+ *     ...
+ */
+class Metronome extends Stream<DateTime> {
+  static final DateTime _EPOCH = new DateTime.fromMillisecondsSinceEpoch(0);
+
+  final Clock clock;
+  final Duration interval;
+  final DateTime anchor;
+
+  Timer _timer;
+  StreamController _controller;
+  final int _intervalMs;
+  final int _anchorMs;
+
+  bool get isBroadcast => true;
+
+  Metronome.epoch(Duration interval, {Clock clock: const Clock()})
+      : this._(interval, clock: clock, anchor: _EPOCH);
+
+  Metronome.periodic(Duration interval,
+      {Clock clock: const Clock(), DateTime anchor})
+      : this._(interval, clock: clock, anchor: anchor);
+
+  Metronome._(Duration interval, {Clock clock: const Clock(), DateTime anchor})
+      : this.clock = clock,
+        this.anchor = anchor,
+        this.interval = interval,
+        this._intervalMs = interval.inMilliseconds,
+        this._anchorMs = (anchor == null
+            ? clock.now()
+            : anchor).millisecondsSinceEpoch {
+    _controller = new StreamController<DateTime>.broadcast(
+        sync: true, onCancel: () {
+      _timer.cancel();
+    }, onListen: () {
+      _startTimer(clock.now());
+    });
+  }
+
+  StreamSubscription<DateTime> listen(void onData(DateTime event),
+          {Function onError, void onDone(), bool cancelOnError}) =>
+      _controller.stream.listen(onData,
+          onError: onError, onDone: onDone, cancelOnError: cancelOnError);
+
+  _startTimer(DateTime now) {
+    var delay =
+        _intervalMs - ((now.millisecondsSinceEpoch - _anchorMs) % _intervalMs);
+    _timer = new Timer(new Duration(milliseconds: delay), _tickDate);
+  }
+
+  _tickDate() {
+    // Hey now, what's all this hinky clock.now() calls? Simple, if the workers
+    // on the receiving end of _controller.add() take a non-zero amount of time
+    // to do their thing (e.g. rendering a large scene with canvas), the next
+    // timer must be adjusted to account for the lapsed time.
+    _controller.add(clock.now());
+    _startTimer(clock.now());
+  }
+}
diff --git a/quiver/lib/src/async/stream_router.dart b/quiver/lib/src/async/stream_router.dart
new file mode 100644
index 0000000..0707f3d
--- /dev/null
+++ b/quiver/lib/src/async/stream_router.dart
@@ -0,0 +1,83 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.async;
+
+/**
+ * Splits a [Stream] of events into multiple Streams based on a set of
+ * predicates.
+ *
+ * Using StreamRouter differs from [Stream.where] because events are only sent
+ * to one Stream. If more than one predicate matches the event, the event is
+ * sent to the stream created by the earlier call to [route]. Events not matched
+ * by a call to [route] are sent to the [defaultStream].
+ *
+ * Example:
+ *   import 'dart:html';
+ *   import 'package:quiver/async.dart';
+ *
+ *   var router = new StreamRouter(window.onClick);
+ *   var onRightClick = router.route((e) => e.button == 2);
+ *   var onAltClick = router.route((e) => e.altKey);
+ *   var onOtherClick router.defaultStream;
+ */
+class StreamRouter<T> {
+  final Stream<T> _incoming;
+  StreamSubscription _subscription;
+
+  final List<_Route> _routes = <_Route>[];
+  final StreamController<T> _defaultController =
+      new StreamController<T>.broadcast();
+
+  /**
+   * Create a new StreamRouter that listens to the [incoming] stream.
+   */
+  StreamRouter(Stream<T> incoming) : _incoming = incoming {
+    _subscription = _incoming.listen(_handle, onDone: close);
+  }
+
+  /**
+   * Events that match [predicate] are sent to the stream created by this
+   * method, and not sent to any other router streams.
+   */
+  Stream<T> route(bool predicate(T event)) {
+    var controller = new StreamController<T>.broadcast();
+    _routes.add(new _Route(predicate, controller));
+    return controller.stream;
+  }
+
+  Stream<T> get defaultStream => _defaultController.stream;
+
+  Future close() {
+    return Future.wait(_routes.map((r) => r.controller.close())).then((_) {
+      _subscription.cancel();
+    });
+  }
+
+  void _handle(T event) {
+    var route =
+        _routes.firstWhere((r) => r.predicate(event), orElse: () => null);
+    var controller = (route != null) ? route.controller : _defaultController;
+    controller.add(event);
+  }
+}
+
+typedef bool _Predicate(event);
+
+class _Route {
+  final _Predicate predicate;
+  final StreamController controller;
+
+  _Route(this.predicate, this.controller);
+}
diff --git a/quiver/lib/src/cache/cache.dart b/quiver/lib/src/cache/cache.dart
new file mode 100644
index 0000000..55ae643
--- /dev/null
+++ b/quiver/lib/src/cache/cache.dart
@@ -0,0 +1,51 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.cache;
+
+/**
+ * A function that produces a value for [key], for when a [Cache] needs to
+ * populate an entry.
+ *
+ * The loader function should either return a value synchronously or a [Future]
+ * which completes with the value asynchronously.
+ */
+typedef dynamic Loader<K>(K key);
+
+/**
+ * A semi-persistent mapping of keys to values.
+ *
+ * All access to a Cache is asynchronous because many implementations will store
+ * their entries in remote systems, isolates, or otherwise have to do async IO
+ * to read and write.
+ */
+abstract class Cache<K, V> {
+
+  /**
+   * Returns the value associated with [key].
+   */
+  Future<V> get(K key, {Loader<K> ifAbsent});
+
+  /**
+   * Sets the value associated with [key]. The Future completes with null when
+   * the operation is complete.
+   */
+  Future set(K key, V value);
+
+  /**
+   * Removes the value associated with [key]. The Future completes with null
+   * when the operation is complete.
+   */
+  Future invalidate(K key);
+}
diff --git a/quiver/lib/src/cache/map_cache.dart b/quiver/lib/src/cache/map_cache.dart
new file mode 100644
index 0000000..3544422
--- /dev/null
+++ b/quiver/lib/src/cache/map_cache.dart
@@ -0,0 +1,61 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.cache;
+
+/**
+ * A [Cache] that's backed by a [Map].
+ */
+class MapCache<K, V> implements Cache<K, V> {
+  final Map<K, V> _map;
+
+  /**
+   * Creates a new [MapCache], optionally using [map] as the backing [Map].
+   */
+  MapCache({Map<K, V> map}) : _map = map != null ? map : new HashMap<K, V>();
+
+  /**
+   * Creates a new [MapCache], using [LruMap] as the backing [Map].
+   * Optionally specify [maximumSize].
+   */
+  factory MapCache.lru({int maximumSize}) {
+    return new MapCache<K, V>(map: new LruMap(maximumSize: maximumSize));
+  }
+
+  Future<V> get(K key, {Loader<K> ifAbsent}) {
+    if (!_map.containsKey(key) && ifAbsent != null) {
+      var valOrFuture = ifAbsent(key);
+      if (valOrFuture is Future) {
+        return valOrFuture.then((v) {
+          _map[key] = v;
+          return v;
+        });
+      } else {
+        _map[key] = valOrFuture;
+        return new Future.value(valOrFuture);
+      }
+    }
+    return new Future.value(_map[key]);
+  }
+
+  Future set(K key, V value) {
+    _map[key] = value;
+    return new Future.value();
+  }
+
+  Future invalidate(K key) {
+    _map.remove(key);
+    return new Future.value();
+  }
+}
diff --git a/quiver/lib/src/collection/bimap.dart b/quiver/lib/src/collection/bimap.dart
new file mode 100644
index 0000000..0757aa5
--- /dev/null
+++ b/quiver/lib/src/collection/bimap.dart
@@ -0,0 +1,121 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.collection;
+
+/**
+ * A bi-directional map whose key-value pairs form a one-to-one correspondence.
+ * BiMaps support an `inverse` property which gives access to an inverted view
+ * of the map, such that there is a mapping (v, k) for each pair (k, v) in the
+ * original map. Since a one-to-one key-value invariant applies, it is an error
+ * to insert duplicate values into this map. It is also an error to insert null
+ * keys or values into this map.
+ */
+abstract class BiMap<K, V> implements Map<K, V> {
+  /**
+   * Creates a BiMap instance with the default implementation.
+   */
+  factory BiMap() => new HashBiMap();
+
+  /**
+   * Adds an association between key and value.
+   *
+   * Throws [ArgumentError] if an association involving [value] exists in the
+   * map; otherwise, the association is inserted, overwriting any existing
+   * association for the key.
+   */
+  void operator []=(K key, V value);
+
+  /**
+   * Replaces any existing associations(s) involving key and value.
+   *
+   * If an association involving [key] or [value] exists in the map, it is
+   * removed.
+   */
+  void replace(K key, V value);
+
+  /**
+   * Returns the inverse of this map, with key-value pairs (v, k) for each
+   * pair (k, v) in this map.
+   */
+  BiMap<V, K> get inverse;
+}
+
+/**
+ * A hash-table based implementation of BiMap.
+ */
+class HashBiMap<K, V> implements BiMap<K, V> {
+  final Map<K, V> _map;
+  final Map<V, K> _inverse;
+  BiMap<V, K> _cached;
+
+  HashBiMap() : this._from(new HashMap(), new HashMap());
+  HashBiMap._from(this._map, this._inverse);
+
+  V operator [](Object key) => _map[key];
+  void operator []=(K key, V value) {
+    _add(key, value, false);
+  }
+  void replace(K key, V value) {
+    _add(key, value, true);
+  }
+  void addAll(Map<K, V> other) => other.forEach((k, v) => _add(k, v, false));
+  bool containsKey(Object key) => _map.containsKey(key);
+  bool containsValue(Object value) => _inverse.containsKey(value);
+  void forEach(void f(K key, V value)) => _map.forEach(f);
+  bool get isEmpty => _map.isEmpty;
+  bool get isNotEmpty => _map.isNotEmpty;
+  Iterable<K> get keys => _map.keys;
+  int get length => _map.length;
+  Iterable<V> get values => _inverse.keys;
+
+  BiMap<V, K> get inverse {
+    if (_cached == null) {
+      _cached = new HashBiMap._from(_inverse, _map);
+    }
+    return _cached;
+  }
+
+  V putIfAbsent(K key, V ifAbsent()) {
+    var value = _map[key];
+    if (value != null) return value;
+    if (!_map.containsKey(key)) return _add(key, ifAbsent(), false);
+    return null;
+  }
+
+  V remove(Object key) {
+    _inverse.remove(_map[key]);
+    return _map.remove(key);
+  }
+
+  void clear() {
+    _map.clear();
+    _inverse.clear();
+  }
+
+  V _add(K key, V value, bool replace) {
+    if (key == null) throw new ArgumentError("null key");
+    if (value == null) throw new ArgumentError("null value");
+    var oldValue = _map[key];
+    if (oldValue == value) return value;
+    if (_inverse.containsKey(value)) {
+      if (!replace) throw new ArgumentError("Mapping for $value exists");
+      _map.remove(_inverse[value]);
+    }
+    _inverse.remove(oldValue);
+    _map[key] = value;
+    _inverse[value] = key;
+    return value;
+  }
+}
diff --git a/quiver/lib/src/collection/delegates/iterable.dart b/quiver/lib/src/collection/delegates/iterable.dart
new file mode 100644
index 0000000..14f5ed1
--- /dev/null
+++ b/quiver/lib/src/collection/delegates/iterable.dart
@@ -0,0 +1,89 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.collection;
+
+/**
+ * An implementation of [Iterable] that delegates all methods to another
+ * [Iterable].
+ * For instance you can create a FruitIterable like this :
+ *
+ *     class FruitIterable extends DelegatingIterable<Fruit> {
+ *       final Iterable<Fruit> _fruits = [];
+ *
+ *       Iterable<Fruit> get delegate => _fruits;
+ *
+ *       // custom methods
+ *     }
+ */
+abstract class DelegatingIterable<E> implements Iterable<E> {
+  Iterable<E> get delegate;
+
+  bool any(bool test(E element)) => delegate.any(test);
+
+  bool contains(Object element) => delegate.contains(element);
+
+  E elementAt(int index) => delegate.elementAt(index);
+
+  bool every(bool test(E element)) => delegate.every(test);
+
+  Iterable expand(Iterable f(E element)) => delegate.expand(f);
+
+  E get first => delegate.first;
+
+  E firstWhere(bool test(E element), {E orElse()}) =>
+      delegate.firstWhere(test, orElse: orElse);
+
+  fold(initialValue, combine(previousValue, E element)) =>
+      delegate.fold(initialValue, combine);
+
+  void forEach(void f(E element)) => delegate.forEach(f);
+
+  bool get isEmpty => delegate.isEmpty;
+
+  bool get isNotEmpty => delegate.isNotEmpty;
+
+  Iterator<E> get iterator => delegate.iterator;
+
+  String join([String separator = ""]) => delegate.join(separator);
+
+  E get last => delegate.last;
+
+  E lastWhere(bool test(E element), {E orElse()}) =>
+      delegate.lastWhere(test, orElse: orElse);
+
+  int get length => delegate.length;
+
+  Iterable map(f(E element)) => delegate.map(f);
+
+  E reduce(E combine(E value, E element)) => delegate.reduce(combine);
+
+  E get single => delegate.single;
+
+  E singleWhere(bool test(E element)) => delegate.singleWhere(test);
+
+  Iterable<E> skip(int n) => delegate.skip(n);
+
+  Iterable<E> skipWhile(bool test(E value)) => delegate.skipWhile(test);
+
+  Iterable<E> take(int n) => delegate.take(n);
+
+  Iterable<E> takeWhile(bool test(E value)) => delegate.takeWhile(test);
+
+  List<E> toList({bool growable: true}) => delegate.toList(growable: growable);
+
+  Set<E> toSet() => delegate.toSet();
+
+  Iterable<E> where(bool test(E element)) => delegate.where(test);
+}
diff --git a/quiver/lib/src/collection/delegates/list.dart b/quiver/lib/src/collection/delegates/list.dart
new file mode 100644
index 0000000..7863539
--- /dev/null
+++ b/quiver/lib/src/collection/delegates/list.dart
@@ -0,0 +1,95 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.collection;
+
+/**
+ * An implementation of [List] that delegates all methods to another [List].
+ * For instance you can create a FruitList like this :
+ *
+ *     class FruitList extends DelegatingList<Fruit> {
+ *       final List<Fruit> _fruits = [];
+ *
+ *       List<Fruit> get delegate => _fruits;
+ *
+ *       // custom methods
+ *     }
+ */
+abstract class DelegatingList<E> extends DelegatingIterable<E>
+    implements List<E> {
+  List<E> get delegate;
+
+  E operator [](int index) => delegate[index];
+
+  void operator []=(int index, E value) {
+    delegate[index] = value;
+  }
+
+  void add(E value) => delegate.add(value);
+
+  void addAll(Iterable<E> iterable) => delegate.addAll(iterable);
+
+  Map<int, E> asMap() => delegate.asMap();
+
+  void clear() => delegate.clear();
+
+  void fillRange(int start, int end, [E fillValue]) =>
+      delegate.fillRange(start, end, fillValue);
+
+  Iterable<E> getRange(int start, int end) => delegate.getRange(start, end);
+
+  int indexOf(E element, [int start = 0]) => delegate.indexOf(element, start);
+
+  void insert(int index, E element) => delegate.insert(index, element);
+
+  void insertAll(int index, Iterable<E> iterable) =>
+      delegate.insertAll(index, iterable);
+
+  int lastIndexOf(E element, [int start]) =>
+      delegate.lastIndexOf(element, start);
+
+  void set length(int newLength) {
+    delegate.length = newLength;
+  }
+
+  bool remove(Object value) => delegate.remove(value);
+
+  E removeAt(int index) => delegate.removeAt(index);
+
+  E removeLast() => delegate.removeLast();
+
+  void removeRange(int start, int end) => delegate.removeRange(start, end);
+
+  void removeWhere(bool test(E element)) => delegate.removeWhere(test);
+
+  void replaceRange(int start, int end, Iterable<E> iterable) =>
+      delegate.replaceRange(start, end, iterable);
+
+  void retainWhere(bool test(E element)) => delegate.retainWhere(test);
+
+  Iterable<E> get reversed => delegate.reversed;
+
+  void setAll(int index, Iterable<E> iterable) =>
+      delegate.setAll(index, iterable);
+
+  void setRange(int start, int end, Iterable<E> iterable,
+          [int skipCount = 0]) =>
+      delegate.setRange(start, end, iterable, skipCount);
+
+  void shuffle([Random random]) => delegate.shuffle(random);
+
+  void sort([int compare(E a, E b)]) => delegate.sort(compare);
+
+  List<E> sublist(int start, [int end]) => delegate.sublist(start, end);
+}
diff --git a/quiver/lib/src/collection/delegates/map.dart b/quiver/lib/src/collection/delegates/map.dart
new file mode 100644
index 0000000..befd5aa
--- /dev/null
+++ b/quiver/lib/src/collection/delegates/map.dart
@@ -0,0 +1,61 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.collection;
+
+/**
+ * An implementation of [Map] that delegates all methods to another [Map].
+ * For instance you can create a FruitMap like this :
+ *
+ *     class FruitMap extends DelegatingMap<String, Fruit> {
+ *       final Map<String, Fruit> _fruits = {};
+ *
+ *       Map<String, Fruit> get delegate => _fruits;
+ *
+ *       // custom methods
+ *     }
+ */
+abstract class DelegatingMap<K, V> implements Map<K, V> {
+  Map<K, V> get delegate;
+
+  V operator [](Object key) => delegate[key];
+
+  void operator []=(K key, V value) {
+    delegate[key] = value;
+  }
+
+  void addAll(Map<K, V> other) => delegate.addAll(other);
+
+  void clear() => delegate.clear();
+
+  bool containsKey(Object key) => delegate.containsKey(key);
+
+  bool containsValue(Object value) => delegate.containsValue(value);
+
+  void forEach(void f(K key, V value)) => delegate.forEach(f);
+
+  bool get isEmpty => delegate.isEmpty;
+
+  bool get isNotEmpty => delegate.isNotEmpty;
+
+  Iterable<K> get keys => delegate.keys;
+
+  int get length => delegate.length;
+
+  V putIfAbsent(K key, V ifAbsent()) => delegate.putIfAbsent(key, ifAbsent);
+
+  V remove(Object key) => delegate.remove(key);
+
+  Iterable<V> get values => delegate.values;
+}
diff --git a/quiver/lib/src/collection/delegates/queue.dart b/quiver/lib/src/collection/delegates/queue.dart
new file mode 100644
index 0000000..1c85fc7
--- /dev/null
+++ b/quiver/lib/src/collection/delegates/queue.dart
@@ -0,0 +1,52 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.collection;
+
+/**
+ * An implementation of [Queue] that delegates all methods to another [Queue].
+ * For instance you can create a FruitQueue like this :
+ *
+ *     class FruitQueue extends DelegatingQueue<Fruit> {
+ *       final Queue<Fruit> _fruits = new Queue<Fruit>();
+ *
+ *       Queue<Fruit> get delegate => _fruits;
+ *
+ *       // custom methods
+ *     }
+ */
+abstract class DelegatingQueue<E> extends DelegatingIterable<E>
+    implements Queue<E> {
+  Queue<E> get delegate;
+
+  void add(E value) => delegate.add(value);
+
+  void addAll(Iterable<E> iterable) => delegate.addAll(iterable);
+
+  void addFirst(E value) => delegate.addFirst(value);
+
+  void addLast(E value) => delegate.addLast(value);
+
+  void clear() => delegate.clear();
+
+  bool remove(Object object) => delegate.remove(object);
+
+  E removeFirst() => delegate.removeFirst();
+
+  E removeLast() => delegate.removeLast();
+
+  void removeWhere(bool test(E element)) => delegate.removeWhere(test);
+
+  void retainWhere(bool test(E element)) => delegate.retainWhere(test);
+}
diff --git a/quiver/lib/src/collection/delegates/set.dart b/quiver/lib/src/collection/delegates/set.dart
new file mode 100644
index 0000000..87e3527
--- /dev/null
+++ b/quiver/lib/src/collection/delegates/set.dart
@@ -0,0 +1,58 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.collection;
+
+/**
+ * An implementation of [Set] that delegates all methods to another [Set].
+ * For instance you can create a FruitSet like this :
+ *
+ *     class FruitSet extends DelegatingSet<Fruit> {
+ *       final Set<Fruit> _fruits = new Set<Fruit>();
+ *
+ *       Set<Fruit> get delegate => _fruits;
+ *
+ *       // custom methods
+ *     }
+ */
+abstract class DelegatingSet<E> extends DelegatingIterable<E>
+    implements Set<E> {
+  Set<E> get delegate;
+
+  bool add(E value) => delegate.add(value);
+
+  void addAll(Iterable<E> elements) => delegate.addAll(elements);
+
+  void clear() => delegate.clear();
+
+  bool containsAll(Iterable<Object> other) => delegate.containsAll(other);
+
+  Set<E> difference(Set<E> other) => delegate.difference(other);
+
+  Set<E> intersection(Set<Object> other) => delegate.intersection(other);
+
+  E lookup(Object object) => delegate.lookup(object);
+
+  bool remove(Object value) => delegate.remove(value);
+
+  void removeAll(Iterable<Object> elements) => delegate.removeAll(elements);
+
+  void removeWhere(bool test(E element)) => delegate.removeWhere(test);
+
+  void retainAll(Iterable<Object> elements) => delegate.retainAll(elements);
+
+  void retainWhere(bool test(E element)) => delegate.retainWhere(test);
+
+  Set<E> union(Set<E> other) => delegate.union(other);
+}
diff --git a/quiver/lib/src/collection/lru_map.dart b/quiver/lib/src/collection/lru_map.dart
new file mode 100644
index 0000000..13e094a
--- /dev/null
+++ b/quiver/lib/src/collection/lru_map.dart
@@ -0,0 +1,303 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.collection;
+
+/**
+ * An implementation of a [Map] which has a maximum size and uses a (Least
+ * Recently Used)[http://en.wikipedia.org/wiki/Cache_algorithms#LRU] algorithm
+ * to remove items from the [Map] when the [maximumSize] is reached and new
+ * items are added.
+ *
+ * It is safe to access the [keys] and [values] collections without affecting
+ * the "used" ordering - as well as using [forEach]. Other types of access,
+ * including bracket, and [putIfAbsent], promotes the key-value pair to the MRU
+ * position.
+ */
+abstract class LruMap<K, V> implements Map<K, V> {
+  /**
+   * Creates a [LruMap] instance with the default implementation.
+   */
+  factory LruMap({int maximumSize}) = LinkedLruHashMap;
+
+  /**
+   * Maximum size of the [Map]. If [length] exceeds this value at any time,
+   * n entries accessed the earliest are removed, where n is [length] -
+   * [maximumSize].
+   */
+  int maximumSize;
+}
+
+/**
+ * Simple implementation of a linked-list entry that contains a [key] and
+ * [value].
+ */
+class _LinkedEntry<K, V> {
+  K key;
+  V value;
+
+  _LinkedEntry<K, V> next;
+  _LinkedEntry<K, V> previous;
+
+  _LinkedEntry([this.key, this.value]);
+}
+
+/**
+ * A linked hash-table based implementation of [LruMap].
+ */
+class LinkedLruHashMap<K, V> implements LruMap<K, V> {
+  static const _DEFAULT_MAXIMUM_SIZE = 100;
+
+  final Map<K, _LinkedEntry<K, V>> _entries;
+
+  int _maximumSize;
+
+  _LinkedEntry<K, V> _head;
+  _LinkedEntry<K, V> _tail;
+
+  /**
+   * Create a new LinkedLruHashMap with a [maximumSize].
+   */
+  factory LinkedLruHashMap({int maximumSize}) =>
+      new LinkedLruHashMap._fromMap(new HashMap<K, _LinkedEntry<K, V>>(),
+          maximumSize: maximumSize);
+
+  LinkedLruHashMap._fromMap(
+      this._entries, {
+      int maximumSize})
+          // This pattern is used instead of a default value because we want to
+          // be able to respect null values coming in from MapCache.lru.
+          : _maximumSize = firstNonNull(maximumSize, _DEFAULT_MAXIMUM_SIZE);
+
+  /**
+   * Adds all key-value pairs of [other] to this map.
+   *
+   * The operation is equivalent to doing this[key] = value for each key and
+   * associated value in other. It iterates over other, which must therefore not
+   * change during the iteration.
+   *
+   * If a key of [other] is already in this map, its value is overwritten. If
+   * the number of unique keys is greater than [maximumSize] then the least
+   * recently use keys are evicted. For keys written to by [other], the least
+   * recently user order is determined by [other]'s iteration order.
+   */
+  @override
+  void addAll(Map<K, V> other) => other.forEach((k, v) => this[k] = v);
+
+  @override
+  void clear() {
+    _entries.clear();
+    _head = _tail = null;
+  }
+
+  @override
+  bool containsKey(K key) => _entries.containsKey(key);
+
+  @override
+  bool containsValue(V value) => values.contains(value);
+
+  /**
+   * Applies [action] to each key-value pair of the map in order of MRU to LRU.
+   *
+   * Calling `action` must not add or remove keys from the map.
+   */
+  @override
+  void forEach(void action(K key, V value)) {
+    var head = _head;
+    while (head != null) {
+      action(head.key, head.value);
+      head = head.next;
+    }
+  }
+
+  @override
+  int get length => _entries.length;
+
+  @override
+  bool get isEmpty => _entries.isEmpty;
+
+  @override
+  bool get isNotEmpty => _entries.isNotEmpty;
+
+  /**
+   * Creates an [Iterable] around the entries of the map.
+   */
+  Iterable<_LinkedEntry<K, V>> _iterable() {
+    return new GeneratingIterable<_LinkedEntry<K, V>>(
+        () => _head, (n) => n.next);
+  }
+
+  /**
+   * The keys of [this] - in order of MRU to LRU.
+   *
+   * The returned iterable does *not* have efficient `length` or `contains`.
+   */
+  @override
+  Iterable<K> get keys => _iterable().map((e) => e.key);
+
+  /**
+   * The values of [this] - in order of MRU to LRU.
+   *
+   * The returned iterable does *not* have efficient `length` or `contains`.
+   */
+  @override
+  Iterable<V> get values => _iterable().map((e) => e.value);
+
+  @override
+  int get maximumSize => _maximumSize;
+
+  @override
+  void set maximumSize(int maximumSize) {
+    if (maximumSize == null) throw new ArgumentError.notNull('maximumSize');
+    while (length > maximumSize) {
+      _removeLru();
+    }
+    _maximumSize = maximumSize;
+  }
+
+  /**
+   * Look up the value associated with [key], or add a new value if it isn't
+   * there. The pair is promoted to the MRU position.
+   *
+   * Otherwise calls [ifAbsent] to get a new value, associates [key] to that
+   * [value], and then returns the new [value], with the key-value pair in the
+   * MRU position. If this causes [length] to exceed [maximumSize], then the
+   * LRU position is removed.
+   */
+  @override
+  V putIfAbsent(K key, V ifAbsent()) {
+    final entry =  _entries.putIfAbsent(
+        key, () => _createEntry(key, ifAbsent()));
+    if (length > maximumSize) {
+      _removeLru();
+    }
+    _promoteEntry(entry);
+    return entry.value;
+  }
+
+  /**
+   * Get the value for a [key] in the [Map]. The [key] will be promoted to the
+   * 'Most Recently Used' position. *NOTE*: Calling [] inside an iteration over
+   * keys/values is currently unsupported; use [keys] or [values] if you
+   * need information about entries without modifying their position.
+   */
+  @override
+  V operator[](K key) {
+    final entry = _entries[key];
+    if (entry != null) {
+      _promoteEntry(entry);
+      return entry.value;
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * If [key] already exists, promotes it to the MRU position & assigns [value].
+   *
+   * Otherwise, adds [key] and [value] to the MRU position.
+   * If [length] exceeds [maximumSize] while adding, removes the LRU position.
+   */
+  @override
+  void operator []=(K key, V value) {
+    // Add this item to the MRU position.
+    _insertMru(_createEntry(key, value));
+
+    // Remove the LRU item if the size would be exceeded by adding this item.
+    if (length > maximumSize) {
+      assert(length == maximumSize + 1);
+      _removeLru();
+    }
+  }
+
+  @override
+  V remove(K key) {
+    final entry = _entries.remove(key);
+    if (entry != null) {
+      if (entry == _head) {
+        _head = _head.next;
+      } else if (entry == _tail) {
+        _tail.previous.next = null;
+        _tail = _tail.previous;
+      } else {
+        entry.previous.next = entry.next;
+      }
+      return entry.value;
+    }
+    return null;
+  }
+
+  @override
+  String toString() => Maps.mapToString(this);
+
+  /**
+   * Moves [entry] to the MRU position, shifting the linked list if necessary.
+   */
+  void _promoteEntry(_LinkedEntry<K, V> entry) {
+    if (entry.previous != null) {
+      // If already existed in the map, link previous to next.
+      entry.previous.next = entry.next;
+
+      // If this was the tail element, assign a new tail.
+      if (_tail == entry) {
+        _tail = entry.previous;
+      }
+    }
+
+    // Replace head with this element.
+    if (_head != null) {
+      _head.previous = entry;
+    }
+    entry.previous = null;
+    entry.next = _head;
+    _head = entry;
+
+    // Add a tail if this is the first element.
+    if (_tail == null) {
+      assert(length == 1);
+      _tail = _head;
+    }
+  }
+
+  /**
+   * Creates and returns an entry from [key] and [value].
+   */
+  _LinkedEntry<K, V> _createEntry(K key, V value) {
+    return new _LinkedEntry<K, V>(key, value);
+  }
+
+  /**
+   * If [entry] does not exist, inserts it into the backing map.
+   * If it does, replaces the existing [_LinkedEntry.value] with [entry.value].
+   * Then, in either case, promotes [entry] to the MRU position.
+   */
+  void _insertMru(_LinkedEntry<K, V> entry) {
+    // Insert a new entry if necessary (only 1 hash lookup in entire function).
+    // Otherwise, just updates the existing value.
+    final value = entry.value;
+    _promoteEntry(_entries.putIfAbsent(entry.key, () => entry)..value = value);
+  }
+
+  /**
+   * Removes the LRU position, shifting the linked list if necessary.
+   */
+  void _removeLru() {
+    // Remove the tail from the internal map.
+    _entries.remove(_tail.key);
+
+    // Remove the tail element itself.
+    _tail = _tail.previous;
+    _tail.next = null;
+  }
+}
diff --git a/quiver/lib/src/collection/multimap.dart b/quiver/lib/src/collection/multimap.dart
new file mode 100644
index 0000000..9144478
--- /dev/null
+++ b/quiver/lib/src/collection/multimap.dart
@@ -0,0 +1,717 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.collection;
+
+/**
+ * An associative container that maps a key to multiple values.
+ *
+ * Key lookups return mutable collections that are views of the multimap.
+ * Updates to the multimap are reflected in these collections and similarly,
+ * modifications to the returned collections are reflected in the multimap.
+ */
+abstract class Multimap<K, V> {
+  /**
+   * Constructs a new list-backed multimap.
+   */
+  factory Multimap() => new ListMultimap<K, V>();
+
+  /**
+   * Returns whether this multimap contains the given [value].
+   */
+  bool containsValue(Object value);
+
+  /**
+   * Returns whether this multimap contains the given [key].
+   */
+  bool containsKey(Object key);
+
+  /**
+   * Returns the values for the given [key]. An empty iterable is returned if
+   * [key] is not mapped. The returned collection is a view on the multimap.
+   * Updates to the collection modify the multimap and likewise, modifications
+   * to the multimap are reflected in the returned collection.
+   */
+  Iterable<V> operator [](Object key);
+
+  /**
+   * Adds an association from the given key to the given value.
+   */
+  void add(K key, V value);
+
+  /**
+   * Adds an association from the given key to each of the given values.
+   */
+  void addValues(K key, Iterable<V> values);
+
+  /**
+   * Adds all associations of [other] to this multimap.
+   *
+   * The operation is equivalent to doing `this[key] = value` for each key
+   * and associated value in other. It iterates over [other], which must
+   * therefore not change during the iteration.
+   */
+  void addAll(Multimap<K, V> other);
+
+  /**
+   * Removes the association between the given [key] and [value]. Returns
+   * `true` if the association existed, `false` otherwise.
+   */
+  bool remove(Object key, V value);
+
+  /**
+   * Removes the association for the given [key]. Returns the collection of
+   * removed values, or an empty iterable if [key] was unmapped.
+   */
+  Iterable<V> removeAll(Object key);
+
+  /**
+   * Removes all data from the multimap.
+   */
+  void clear();
+
+  /**
+   * Applies [f] to each {key, Iterable<value>} pair of the multimap.
+   *
+   * It is an error to add or remove keys from the map during iteration.
+   */
+  void forEachKey(void f(K key, Iterable<V> value));
+
+  /**
+   * Applies [f] to each {key, value} pair of the multimap.
+   *
+   * It is an error to add or remove keys from the map during iteration.
+   */
+  void forEach(void f(K key, V value));
+
+  /**
+   * The keys of [this].
+   */
+  Iterable<K> get keys;
+
+  /**
+   * The values of [this].
+   */
+  Iterable<V> get values;
+
+  /**
+   * Returns a view of this multimap as a map.
+   */
+  Map<K, Iterable<V>> asMap();
+
+  /**
+   * Returns a view of this multimap as a map.
+   *
+   * DEPRECATED: this method is replaced with `asMap`.
+   */
+  @Deprecated('Will be removed in 0.22.0')
+  Map<K, Iterable<V>> toMap();
+
+  /**
+   * The number of keys in the multimap.
+   */
+  int get length;
+
+  /**
+   * Returns true if there is no key in the multimap.
+   */
+  bool get isEmpty;
+
+  /**
+   * Returns true if there is at least one key in the multimap.
+   */
+  bool get isNotEmpty;
+}
+
+/**
+ * Abstract base class for multimap implementations.
+ */
+abstract class _BaseMultimap<K, V, C extends Iterable<V>>
+    implements Multimap<K, V> {
+  final Map<K, Iterable<V>> _map = new HashMap();
+
+  Iterable<V> _create();
+  void _add(C iterable, V value);
+  void _addAll(C iterable, Iterable<V> value);
+  void _clear(C iterable);
+  bool _remove(C iterable, Object value);
+  Iterable<V> _wrap(Object key, C iterable);
+
+  bool containsValue(Object value) => values.contains(value);
+  bool containsKey(Object key) => _map.keys.contains(key);
+
+  Iterable<V> operator [](Object key) {
+    var values = _map[key];
+    if (values == null) {
+      values = _create();
+    }
+    return _wrap(key, values);
+  }
+
+  void add(K key, V value) {
+    _map.putIfAbsent(key, _create);
+    _add(_map[key], value);
+  }
+
+  void addValues(K key, Iterable<V> values) {
+    _map.putIfAbsent(key, _create);
+    _addAll(_map[key], values);
+  }
+
+  /**
+   * Adds all associations of [other] to this multimap.
+   *
+   * The operation is equivalent to doing `this[key] = value` for each key
+   * and associated value in other. It iterates over [other], which must
+   * therefore not change during the iteration.
+   *
+   * This implementation iterates through each key of [other] and adds the
+   * associated values to this instance via [addValues].
+   */
+  void addAll(Multimap<K, V> other) => other.forEachKey(addValues);
+
+  bool remove(Object key, V value) {
+    if (!_map.containsKey(key)) return false;
+    bool removed = _remove(_map[key], value);
+    if (removed && _map[key].isEmpty) _map.remove(key);
+    return removed;
+  }
+
+  Iterable<V> removeAll(Object key) {
+    // Cast to dynamic to remove warnings
+    var values = _map.remove(key) as dynamic;
+    var retValues = _create() as dynamic;
+    if (values != null) {
+      retValues.addAll(values);
+      values.clear();
+    }
+    return retValues;
+  }
+
+  void clear() {
+    _map.forEach((K key, Iterable<V> value) => _clear(value));
+    _map.clear();
+  }
+
+  void forEachKey(void f(K key, Iterable<V> value)) => _map.forEach(f);
+
+  void forEach(void f(K key, V value)) {
+    _map.forEach((K key, Iterable<V> values) {
+      values.forEach((V value) => f(key, value));
+    });
+  }
+
+  Iterable<K> get keys => _map.keys;
+  Iterable<V> get values => _map.values.expand((x) => x);
+  Iterable<Iterable<V>> get _groupedValues => _map.values;
+  int get length => _map.length;
+  bool get isEmpty => _map.isEmpty;
+  bool get isNotEmpty => _map.isNotEmpty;
+}
+
+/**
+ * A multimap implementation that uses [List]s to store the values associated
+ * with each key.
+ */
+class ListMultimap<K, V> extends _BaseMultimap<K, V, List<V>> {
+  ListMultimap() : super();
+  @override
+  List<V> _create() => new List<V>();
+  @override
+  void _add(List<V> iterable, V value) {
+    iterable.add(value);
+  }
+  @override
+  void _addAll(List<V> iterable, Iterable<V> value) => iterable.addAll(value);
+  @override
+  void _clear(List<V> iterable) => iterable.clear();
+  @override
+  bool _remove(List<V> iterable, Object value) => iterable.remove(value);
+  @override
+  List<V> _wrap(Object key, List<V> iterable) =>
+      new _WrappedList(_map, key, iterable);
+  List<V> operator [](Object key) => super[key];
+  List<V> removeAll(Object key) => super.removeAll(key);
+  Map<K, List<V>> asMap() => new _WrappedMap<K, V, List<V>>(this);
+  @Deprecated('Will be removed in 0.22.0')
+  Map<K, List<V>> toMap() => asMap();
+}
+
+/**
+ * A multimap implementation that uses [Set]s to store the values associated
+ * with each key.
+ */
+class SetMultimap<K, V> extends _BaseMultimap<K, V, Set<V>> {
+  SetMultimap() : super();
+  @override
+  Set<V> _create() => new Set<V>();
+  @override
+  void _add(Set<V> iterable, V value) {
+    iterable.add(value);
+  }
+  @override
+  void _addAll(Set<V> iterable, Iterable<V> value) => iterable.addAll(value);
+  @override
+  void _clear(Set<V> iterable) => iterable.clear();
+  @override
+  bool _remove(Set<V> iterable, Object value) => iterable.remove(value);
+  @override
+  Set<V> _wrap(Object key, Iterable<V> iterable) =>
+      new _WrappedSet(_map, key, iterable);
+  Set<V> operator [](Object key) => super[key];
+  Set<V> removeAll(Object key) => super.removeAll(key);
+  Map<K, Set<V>> asMap() => new _WrappedMap<K, V, Set<V>>(this);
+  @Deprecated('Will be removed in 0.22.0')
+  Map<K, Set<V>> toMap() => asMap();
+}
+
+/**
+ * A [Map] that delegates its operations to an underlying multimap.
+ */
+class _WrappedMap<K, V, C extends Iterable<V>> implements Map<K, C> {
+  final _BaseMultimap<K, V, C> _multimap;
+
+  _WrappedMap(this._multimap);
+
+  C operator [](Object key) => _multimap[key];
+
+  void operator []=(K key, C value) {
+    throw new UnsupportedError("Insert unsupported on map view");
+  }
+
+  void addAll(Map<K, C> other) {
+    throw new UnsupportedError("Insert unsupported on map view");
+  }
+
+  C putIfAbsent(K key, C ifAbsent()) {
+    throw new UnsupportedError("Insert unsupported on map view");
+  }
+
+  void clear() => _multimap.clear();
+  bool containsKey(Object key) => _multimap.containsKey(key);
+  bool containsValue(Object value) => _multimap.containsValue(value);
+  void forEach(void f(K key, C value)) => _multimap.forEachKey(f);
+  bool get isEmpty => _multimap.isEmpty;
+  bool get isNotEmpty => _multimap.isNotEmpty;
+  Iterable<K> get keys => _multimap.keys;
+  int get length => _multimap.length;
+  C remove(Object key) => _multimap.removeAll(key);
+  Iterable<C> get values => _multimap._groupedValues;
+}
+
+/**
+ * Iterable wrapper that syncs to an underlying map.
+ */
+class _WrappedIterable<K, V, C extends Iterable<V>> implements Iterable<V> {
+  final K _key;
+  final Map<K, C> _map;
+  C _delegate;
+
+  _WrappedIterable(this._map, this._key, this._delegate);
+
+  _addToMap() => _map[_key] = _delegate;
+
+  /**
+   * Ensures we hold an up-to-date delegate. In the case where all mappings for
+   * _key are removed from the multimap, the Iterable referenced by _delegate is
+   * removed from the underlying map. At that point, any new addition via the
+   * multimap triggers the creation of a new Iterable, and the empty delegate
+   * we hold would be stale. As such, we check the underlying map and update
+   * our delegate when the one we hold is empty.
+   */
+  _syncDelegate() {
+    if (_delegate.isEmpty) {
+      var updated = _map[_key];
+      if (updated != null) {
+        _delegate = updated;
+      }
+    }
+  }
+
+  bool any(bool test(V element)) {
+    _syncDelegate();
+    return _delegate.any(test);
+  }
+
+  bool contains(Object element) {
+    _syncDelegate();
+    return _delegate.contains(element);
+  }
+
+  V elementAt(int index) {
+    _syncDelegate();
+    return _delegate.elementAt(index);
+  }
+
+  bool every(bool test(V element)) {
+    _syncDelegate();
+    return _delegate.every(test);
+  }
+
+  Iterable expand(Iterable f(V element)) {
+    _syncDelegate();
+    return _delegate.expand(f);
+  }
+
+  V get first {
+    _syncDelegate();
+    return _delegate.first;
+  }
+
+  V firstWhere(bool test(V element), {V orElse()}) {
+    _syncDelegate();
+    return _delegate.firstWhere(test, orElse: orElse);
+  }
+
+  fold(initialValue, combine(previousValue, V element)) {
+    _syncDelegate();
+    return _delegate.fold(initialValue, combine);
+  }
+
+  void forEach(void f(V element)) {
+    _syncDelegate();
+    _delegate.forEach(f);
+  }
+
+  bool get isEmpty {
+    _syncDelegate();
+    return _delegate.isEmpty;
+  }
+
+  bool get isNotEmpty {
+    _syncDelegate();
+    return _delegate.isNotEmpty;
+  }
+
+  Iterator<V> get iterator {
+    _syncDelegate();
+    return _delegate.iterator;
+  }
+
+  String join([String separator = ""]) {
+    _syncDelegate();
+    return _delegate.join(separator);
+  }
+
+  V get last {
+    _syncDelegate();
+    return _delegate.last;
+  }
+
+  V lastWhere(bool test(V element), {V orElse()}) {
+    _syncDelegate();
+    return _delegate.lastWhere(test, orElse: orElse);
+  }
+
+  int get length {
+    _syncDelegate();
+    return _delegate.length;
+  }
+
+  Iterable map(f(V element)) {
+    _syncDelegate();
+    return _delegate.map(f);
+  }
+
+  V reduce(V combine(V value, V element)) {
+    _syncDelegate();
+    return _delegate.reduce(combine);
+  }
+
+  V get single {
+    _syncDelegate();
+    return _delegate.single;
+  }
+
+  V singleWhere(bool test(V element)) {
+    _syncDelegate();
+    return _delegate.singleWhere(test);
+  }
+
+  Iterable<V> skip(int n) {
+    _syncDelegate();
+    return _delegate.skip(n);
+  }
+
+  Iterable<V> skipWhile(bool test(V value)) {
+    _syncDelegate();
+    return _delegate.skipWhile(test);
+  }
+
+  Iterable<V> take(int n) {
+    _syncDelegate();
+    return _delegate.take(n);
+  }
+
+  Iterable<V> takeWhile(bool test(V value)) {
+    _syncDelegate();
+    return _delegate.takeWhile(test);
+  }
+
+  List<V> toList({bool growable: true}) {
+    _syncDelegate();
+    return _delegate.toList(growable: growable);
+  }
+
+  Set<V> toSet() {
+    _syncDelegate();
+    return _delegate.toSet();
+  }
+
+  String toString() {
+    _syncDelegate();
+    return _delegate.toString();
+  }
+
+  Iterable<V> where(bool test(V element)) {
+    _syncDelegate();
+    return _delegate.where(test);
+  }
+}
+
+class _WrappedList<K, V> extends _WrappedIterable<K, V, List<V>>
+    implements List<V> {
+  _WrappedList(Map<K, List<V>> map, K key, List<V> delegate)
+      : super(map, key, delegate);
+
+  V operator [](int index) => elementAt(index);
+
+  void operator []=(int index, V value) {
+    _syncDelegate();
+    _delegate[index] = value;
+  }
+
+  void add(V value) {
+    _syncDelegate();
+    var wasEmpty = _delegate.isEmpty;
+    _delegate.add(value);
+    if (wasEmpty) _addToMap();
+  }
+
+  void addAll(Iterable<V> iterable) {
+    _syncDelegate();
+    var wasEmpty = _delegate.isEmpty;
+    _delegate.addAll(iterable);
+    if (wasEmpty) _addToMap();
+  }
+
+  Map<int, V> asMap() {
+    _syncDelegate();
+    return _delegate.asMap();
+  }
+
+  void clear() {
+    _syncDelegate();
+    _delegate.clear();
+    _map.remove(_key);
+  }
+
+  void fillRange(int start, int end, [V fillValue]) {
+    _syncDelegate();
+    _delegate.fillRange(start, end, fillValue);
+  }
+
+  Iterable<V> getRange(int start, int end) {
+    _syncDelegate();
+    return _delegate.getRange(start, end);
+  }
+
+  int indexOf(V element, [int start = 0]) {
+    _syncDelegate();
+    return _delegate.indexOf(element, start);
+  }
+
+  void insert(int index, V element) {
+    _syncDelegate();
+    var wasEmpty = _delegate.isEmpty;
+    _delegate.insert(index, element);
+    if (wasEmpty) _addToMap();
+  }
+
+  void insertAll(int index, Iterable<V> iterable) {
+    _syncDelegate();
+    var wasEmpty = _delegate.isEmpty;
+    _delegate.insertAll(index, iterable);
+    if (wasEmpty) _addToMap();
+  }
+
+  int lastIndexOf(V element, [int start]) {
+    _syncDelegate();
+    return _delegate.lastIndexOf(element, start);
+  }
+
+  void set length(int newLength) {
+    _syncDelegate();
+    var wasEmpty = _delegate.isEmpty;
+    _delegate.length = newLength;
+    if (wasEmpty) _addToMap();
+  }
+
+  bool remove(Object value) {
+    _syncDelegate();
+    bool removed = _delegate.remove(value);
+    if (_delegate.isEmpty) _map.remove(_key);
+    return removed;
+  }
+
+  V removeAt(int index) {
+    _syncDelegate();
+    V removed = _delegate.removeAt(index);
+    if (_delegate.isEmpty) _map.remove(_key);
+    return removed;
+  }
+
+  V removeLast() {
+    _syncDelegate();
+    V removed = _delegate.removeLast();
+    if (_delegate.isEmpty) _map.remove(_key);
+    return removed;
+  }
+
+  void removeRange(int start, int end) {
+    _syncDelegate();
+    _delegate.removeRange(start, end);
+    if (_delegate.isEmpty) _map.remove(_key);
+  }
+
+  void removeWhere(bool test(V element)) {
+    _syncDelegate();
+    _delegate.removeWhere(test);
+    if (_delegate.isEmpty) _map.remove(_key);
+  }
+
+  void replaceRange(int start, int end, Iterable<V> iterable) {
+    _syncDelegate();
+    _delegate.replaceRange(start, end, iterable);
+    if (_delegate.isEmpty) _map.remove(_key);
+  }
+
+  void retainWhere(bool test(V element)) {
+    _syncDelegate();
+    _delegate.retainWhere(test);
+    if (_delegate.isEmpty) _map.remove(_key);
+  }
+
+  Iterable<V> get reversed {
+    _syncDelegate();
+    return _delegate.reversed;
+  }
+
+  void setAll(int index, Iterable<V> iterable) {
+    _syncDelegate();
+    _delegate.setAll(index, iterable);
+  }
+
+  void setRange(int start, int end, Iterable<V> iterable, [int skipCount = 0]) {
+    _syncDelegate();
+  }
+
+  void shuffle([Random random]) {
+    _syncDelegate();
+    _delegate.shuffle(random);
+  }
+
+  void sort([int compare(V a, V b)]) {
+    _syncDelegate();
+    _delegate.sort(compare);
+  }
+
+  List<V> sublist(int start, [int end]) {
+    _syncDelegate();
+    return _delegate.sublist(start, end);
+  }
+}
+
+class _WrappedSet<K, V> extends _WrappedIterable<K, V, Set<V>>
+    implements Set<V> {
+  _WrappedSet(Map<K, Iterable<V>> map, K key, Iterable<V> delegate)
+      : super(map, key, delegate);
+
+  bool add(V value) {
+    _syncDelegate();
+    var wasEmpty = _delegate.isEmpty;
+    bool wasAdded = _delegate.add(value);
+    if (wasEmpty) _addToMap();
+    return wasAdded;
+  }
+
+  void addAll(Iterable<V> elements) {
+    _syncDelegate();
+    var wasEmpty = _delegate.isEmpty;
+    _delegate.addAll(elements);
+    if (wasEmpty) _addToMap();
+  }
+
+  void clear() {
+    _syncDelegate();
+    _delegate.clear();
+    _map.remove(_key);
+  }
+
+  bool containsAll(Iterable<Object> other) {
+    _syncDelegate();
+    return _delegate.containsAll(other);
+  }
+
+  Set<V> difference(Set<V> other) {
+    _syncDelegate();
+    return _delegate.difference(other);
+  }
+
+  Set<V> intersection(Set<Object> other) {
+    _syncDelegate();
+    return _delegate.intersection(other);
+  }
+
+  V lookup(Object object) {
+    _syncDelegate();
+    return _delegate.lookup(object);
+  }
+
+  bool remove(Object value) {
+    _syncDelegate();
+    bool removed = _delegate.remove(value);
+    if (_delegate.isEmpty) _map.remove(_key);
+    return removed;
+  }
+
+  void removeAll(Iterable<Object> elements) {
+    _syncDelegate();
+    _delegate.removeAll(elements);
+    if (_delegate.isEmpty) _map.remove(_key);
+  }
+
+  void removeWhere(bool test(V element)) {
+    _syncDelegate();
+    _delegate.removeWhere(test);
+    if (_delegate.isEmpty) _map.remove(_key);
+  }
+
+  void retainAll(Iterable<Object> elements) {
+    _syncDelegate();
+    _delegate.retainAll(elements);
+    if (_delegate.isEmpty) _map.remove(_key);
+  }
+
+  void retainWhere(bool test(V element)) {
+    _syncDelegate();
+    _delegate.retainWhere(test);
+    if (_delegate.isEmpty) _map.remove(_key);
+  }
+
+  Set<V> union(Set<V> other) {
+    _syncDelegate();
+    return _delegate.union(other);
+  }
+}
diff --git a/quiver/lib/src/collection/treeset.dart b/quiver/lib/src/collection/treeset.dart
new file mode 100644
index 0000000..200c21f
--- /dev/null
+++ b/quiver/lib/src/collection/treeset.dart
@@ -0,0 +1,1050 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.collection;
+
+/**
+ * A [Set] of items stored in a binary tree according to [comparator].
+ * Supports bidirectional iteration.
+ */
+abstract class TreeSet<V> extends IterableBase<V> implements Set<V> {
+  final Comparator<V> comparator;
+
+  int get length;
+
+  /**
+   * Create a new [TreeSet] with an ordering defined by [comparator] or the
+   * default `(a, b) => a.compareTo(b)`.
+   */
+  factory TreeSet({Comparator<V> comparator}) {
+    if (comparator == null) {
+      comparator = (a, b) => a.compareTo(b);
+    }
+    return new AvlTreeSet(comparator: comparator);
+  }
+
+  TreeSet._(this.comparator);
+
+  /**
+   * Returns an [BidirectionalIterator] that iterates over this tree.
+   */
+  BidirectionalIterator<V> get iterator;
+
+  /**
+   * Returns an [BidirectionalIterator] that iterates over this tree, in reverse.
+   */
+  BidirectionalIterator<V> get reverseIterator;
+
+  /**
+   * Returns an [BidirectionalIterator] that starts at [anchor].
+   * By default, the iterator includes the anchor with the first movement;
+   * set [inclusive] to false if you want to exclude the anchor. Set [reversed]
+   * to true to change the direction of of moveNext and movePrevious.
+   *
+   * Note: This iterator allows you to walk the entire set. It does not present
+   * a subview.
+   */
+  BidirectionalIterator<V> fromIterator(V anchor,
+      {bool reversed: false, bool inclusive: true});
+
+  /**
+   * Search the tree for the matching [object] or the [nearestOption]
+   * if missing.  See [TreeSearch].
+   */
+  V nearest(V object, {TreeSearch nearestOption: TreeSearch.NEAREST});
+
+  // TODO: toString or not toString, that is the question.
+}
+
+/**
+ * Controls the results for [TreeSet.searchNearest]()
+ */
+class TreeSearch {
+
+  /**
+   * If result not found, always chose the smaler element
+   */
+  static const LESS_THAN = const TreeSearch._(1);
+
+  /**
+   * If result not found, chose the nearest based on comparison
+   */
+  static const NEAREST = const TreeSearch._(2);
+
+  /**
+   * If result not found, always chose the greater element
+   */
+  static const GREATER_THAN = const TreeSearch._(3);
+
+  final int _val;
+  const TreeSearch._(this._val);
+}
+
+/**
+ * A node in the [TreeSet].
+ */
+abstract class _TreeNode<V> {
+  _TreeNode<V> get left;
+  _TreeNode<V> get right;
+
+  //TODO(codefu): Remove need for [parent]; this is just an implementation note
+  _TreeNode<V> get parent;
+  V object;
+
+  /**
+   * TreeNodes are always allocated as leafs.
+   */
+  _TreeNode({this.object});
+
+  /**
+   *  Return the minimum node for the subtree
+   */
+  _TreeNode<V> get minimumNode {
+    var node = this;
+    while (node.left != null) {
+      node = node.left;
+    }
+    return node;
+  }
+
+  /**
+   *  Return the maximum node for the subtree
+   */
+  _TreeNode<V> get maximumNode {
+    var node = this;
+    while (node.right != null) {
+      node = node.right;
+    }
+    return node;
+  }
+
+  /**
+   *  Return the next greatest element (or null)
+   */
+  _TreeNode<V> get successor {
+    var node = this;
+    if (node.right != null) {
+      return node.right.minimumNode;
+    }
+    while (node.parent != null && node == node.parent.right) {
+      node = node.parent;
+    }
+    return node.parent;
+  }
+
+  /**
+   *  Return the next smaller element (or null)
+   */
+  _TreeNode<V> get predecessor {
+    var node = this;
+    if (node.left != null) {
+      return node.left.maximumNode;
+    }
+    while (node.parent != null && node.parent._left == node) {
+      node = node.parent;
+    }
+    return node.parent;
+  }
+}
+
+/**
+ * AVL implementation of a self-balancing binary tree. Optimized for lookup
+ * operations.
+ *
+ * Notes: Adapted from "Introduction to Algorithms", second edition,
+ *        by Thomas H. Cormen, Charles E. Leiserson,
+ *           Ronald L. Rivest, Clifford Stein.
+ *        chapter 13.2
+ */
+class AvlTreeSet<V> extends TreeSet<V> {
+  int _length = 0;
+  AvlNode<V> _root;
+  // Modification count to the tree, monotonically increasing
+  int _modCount = 0;
+
+  int get length => _length;
+
+  AvlTreeSet({Comparator<V> comparator}) : super._(comparator);
+
+  /**
+   * Add the element to the tree.
+   */
+  bool add(V element) {
+    if (_root == null) {
+      AvlNode<V> node = new AvlNode<V>(object: element);
+      _root = node;
+      ++_length;
+      ++_modCount;
+      return true;
+    }
+
+    AvlNode<V> x = _root;
+    while (true) {
+      int compare = comparator(element, x.object);
+      if (compare == 0) {
+        return false;
+      } else if (compare < 0) {
+        if (x._left == null) {
+          AvlNode<V> node = new AvlNode<V>(object: element).._parent = x;
+          x
+            .._left = node
+            .._balanceFactor -= 1;
+          break;
+        }
+        x = x.left;
+      } else {
+        if (x._right == null) {
+          AvlNode<V> node = new AvlNode<V>(object: element).._parent = x;
+          x
+            .._right = node
+            .._balanceFactor += 1;
+          break;
+        }
+        x = x.right;
+      }
+    }
+
+    ++_modCount;
+
+    // AVL balancing act (for height balanced trees)
+    // Now that we've inserted, we've unbalanced some trees, we need
+    //  to follow the tree back up to the _root double checking that the tree
+    //  is still balanced and _maybe_ perform a single or double rotation.
+    //  Note: Left additions == -1, Right additions == +1
+    //  Balanced Node = { -1, 0, 1 }, out of balance = { -2, 2 }
+    //  Single rotation when Parent & Child share signed balance,
+    //  Double rotation when sign differs!
+    AvlNode<V> node = x;
+    while (node._balanceFactor != 0 && node.parent != null) {
+      // Find out which side of the parent we're on
+      if (node.parent._left == node) {
+        node.parent._balanceFactor -= 1;
+      } else {
+        node.parent._balanceFactor += 1;
+      }
+
+      node = node.parent;
+      if (node._balanceFactor == 2) {
+        // Heavy on the right side - Test for which rotation to perform
+        if (node.right._balanceFactor == 1) {
+          // Single (left) rotation; this will balance everything to zero
+          _rotateLeft(node);
+          node._balanceFactor = node.parent._balanceFactor = 0;
+          node = node.parent;
+        } else {
+          // Double (Right/Left) rotation
+          // node will now be old node.right.left
+          _rotateRightLeft(node);
+          node = node.parent; // Update to new parent (old grandchild)
+          if (node._balanceFactor == 1) {
+            node.right._balanceFactor = 0;
+            node.left._balanceFactor = -1;
+          } else if (node._balanceFactor == 0) {
+            node.right._balanceFactor = 0;
+            node.left._balanceFactor = 0;
+          } else {
+            node.right._balanceFactor = 1;
+            node.left._balanceFactor = 0;
+          }
+          node._balanceFactor = 0;
+        }
+        break; // out of loop, we're balanced
+      } else if (node._balanceFactor == -2) {
+        // Heavy on the left side - Test for which rotation to perform
+        if (node.left._balanceFactor == -1) {
+          _rotateRight(node);
+          node._balanceFactor = node.parent._balanceFactor = 0;
+          node = node.parent;
+        } else {
+          // Double (Left/Right) rotation
+          // node will now be old node.left.right
+          _rotateLeftRight(node);
+          node = node.parent;
+          if (node._balanceFactor == -1) {
+            node.right._balanceFactor = 1;
+            node.left._balanceFactor = 0;
+          } else if (node._balanceFactor == 0) {
+            node.right._balanceFactor = 0;
+            node.left._balanceFactor = 0;
+          } else {
+            node.right._balanceFactor = 0;
+            node.left._balanceFactor = -1;
+          }
+          node._balanceFactor = 0;
+        }
+        break; // out of loop, we're balanced
+      }
+    } // end of while (balancing)
+    _length++;
+    return true;
+  }
+
+  /**
+   * Test to see if an element is stored in the tree
+   */
+  AvlNode<V> _getNode(V element) {
+    if (element == null) return null;
+    AvlNode<V> x = _root;
+    while (x != null) {
+      int compare = comparator(element, x.object);
+      if (compare == 0) {
+        // This only means our node matches; we need to search for the exact
+        // element. We could have been glutons and used a hashmap to back.
+        return x;
+      } else if (compare < 0) {
+        x = x.left;
+      } else {
+        x = x.right;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * This function will right rotate/pivot N with its left child, placing
+   * it on the right of its left child.
+   *
+   *      N                      Y
+   *     / \                    / \
+   *    Y   A                  Z   N
+   *   / \          ==>       / \ / \
+   *  Z   B                  D  CB   A
+   * / \
+   *D   C
+   *
+   * Assertion: must have a left element
+   */
+  void _rotateRight(AvlNode<V> node) {
+    AvlNode<V> y = node.left;
+    if (y == null) throw new AssertionError();
+
+    // turn Y's right subtree(B) into N's left subtree.
+    node._left = y.right;
+    if (node.left != null) {
+      node.left._parent = node;
+    }
+    y._parent = node.parent;
+    if (y._parent == null) {
+      _root = y;
+    } else {
+      if (node.parent._left == node) {
+        node.parent._left = y;
+      } else {
+        node.parent._right = y;
+      }
+    }
+    y._right = node;
+    node._parent = y;
+  }
+
+  /**
+   * This function will left rotate/pivot N with its right child, placing
+   * it on the left of its right child.
+   *
+   *      N                      Y
+   *     / \                    / \
+   *    A   Y                  N   Z
+   *       / \      ==>       / \ / \
+   *      B   Z              A  BC   D
+   *         / \
+   *        C   D
+   *
+   * Assertion: must have a right element
+   */
+  void _rotateLeft(AvlNode<V> node) {
+    AvlNode<V> y = node.right;
+    if (y == null) throw new AssertionError();
+
+    // turn Y's left subtree(B) into N's right subtree.
+    node._right = y.left;
+    if (node.right != null) {
+      node.right._parent = node;
+    }
+    y._parent = node.parent;
+    if (y._parent == null) {
+      _root = y;
+    } else {
+      if (node.parent._left == node) {
+        y.parent._left = y;
+      } else {
+        y.parent._right = y;
+      }
+    }
+    y._left = node;
+    node._parent = y;
+  }
+
+  /**
+   *  This function will double rotate node with right/left operations.
+   *  node is S.
+   *
+   *    S                      G
+   *   / \                    / \
+   *  A   C                  S   C
+   *     / \      ==>       / \ / \
+   *    G   B              A  DC   B
+   *   / \
+   *  D   C
+   */
+  void _rotateRightLeft(AvlNode<V> node) {
+    _rotateRight(node.right);
+    _rotateLeft(node);
+  }
+
+  /**
+   * This function will double rotate node with left/right operations.
+   * node is S.
+   *
+   *    S                      G
+   *   / \                    / \
+   *  C   A                  C   S
+   * / \          ==>       / \ / \
+   *B   G                  B  CD   A
+   *   / \
+   *  C   D
+   */
+  void _rotateLeftRight(AvlNode<V> node) {
+    _rotateLeft(node.left);
+    _rotateRight(node);
+  }
+
+  bool addAll(Iterable<V> items) {
+    bool modified = false;
+    for (V ele in items) {
+      modified = add(ele) ? true : modified;
+    }
+    return modified;
+  }
+
+  void clear() {
+    _length = 0;
+    _root = null;
+    ++_modCount;
+  }
+
+  bool containsAll(Iterable<Object> items) {
+    for (var ele in items) {
+      if (!contains(ele)) return false;
+    }
+    return true;
+  }
+
+  bool remove(Object item) {
+    AvlNode<V> x = _getNode(item);
+    if (x != null) {
+      _removeNode(x);
+      return true;
+    }
+    return false;
+  }
+
+  void _removeNode(AvlNode<V> node) {
+    AvlNode<V> y, w;
+
+    ++_modCount;
+    --_length;
+
+    // note: if you read wikipedia, it states remove the node if its a leaf,
+    // otherwise, replace it with its predecessor or successor. We're not.
+    if (node._right == null || node.right._left == null) {
+      // simple solutions
+      if (node.right != null) {
+        y = node.right;
+        y._parent = node.parent;
+        y._balanceFactor = node._balanceFactor - 1;
+        y._left = node.left;
+        if (y.left != null) {
+          y.left._parent = y;
+        }
+      } else if (node.left != null) {
+        y = node.left;
+        y._parent = node.parent;
+        y._balanceFactor = node._balanceFactor + 1;
+      } else {
+        y = null;
+      }
+      if (_root == node) {
+        _root = y;
+      } else if (node.parent._left == node) {
+        node.parent._left = y;
+        if (y == null) {
+          // account for leaf deletions changing the balance
+          node.parent._balanceFactor += 1;
+          y = node.parent; // start searching from here;
+        }
+      } else {
+        node.parent._right = y;
+        if (y == null) {
+          node.parent._balanceFactor -= 1;
+          y = node.parent;
+        }
+      }
+      w = y;
+    } else {
+      // This node is not a leaf; we should find the successor node, swap
+      //it with this* and then update the balance factors.
+      y = node.successor;
+      y._left = node.left;
+      if (y.left != null) {
+        y.left._parent = y;
+      }
+
+      w = y.parent;
+      w._left = y.right;
+      if (w.left != null) {
+        w.left._parent = w;
+      }
+      // known: we're removing from the left
+      w._balanceFactor += 1;
+
+      // known due to test for n->r->l above
+      y._right = node.right;
+      y.right._parent = y;
+      y._balanceFactor = node._balanceFactor;
+
+      y._parent = node.parent;
+      if (_root == node) {
+        _root = y;
+      } else if (node.parent._left == node) {
+        node.parent._left = y;
+      } else {
+        node.parent._right = y;
+      }
+    }
+
+    // Safe to kill node now; its free to go.
+    node._balanceFactor = 0;
+    node._left = node._right = node._parent = null;
+    node.object = null;
+
+    // Recalculate max values all the way to the top.
+    node = w;
+    while (node != null) {
+      node = node.parent;
+    }
+
+    // Re-balance to the top, ending early if OK
+    node = w;
+    while (node != null) {
+      if (node._balanceFactor == -1 || node._balanceFactor == 1) {
+        // The height of node hasn't changed; done!
+        break;
+      }
+      if (node._balanceFactor == 2) {
+        // Heavy on the right side; figure out which rotation to perform
+        if (node.right._balanceFactor == -1) {
+          _rotateRightLeft(node);
+          node = node.parent; // old grand-child!
+          if (node._balanceFactor == 1) {
+            node.right._balanceFactor = 0;
+            node.left._balanceFactor = -1;
+          } else if (node._balanceFactor == 0) {
+            node.right._balanceFactor = 0;
+            node.left._balanceFactor = 0;
+          } else {
+            node.right._balanceFactor = 1;
+            node.left._balanceFactor = 0;
+          }
+          node._balanceFactor = 0;
+        } else {
+          // single left-rotation
+          _rotateLeft(node);
+          if (node.parent._balanceFactor == 0) {
+            node.parent._balanceFactor = -1;
+            node._balanceFactor = 1;
+            break;
+          } else {
+            node.parent._balanceFactor = 0;
+            node._balanceFactor = 0;
+            node = node.parent;
+            continue;
+          }
+        }
+      } else if (node._balanceFactor == -2) {
+        // Heavy on the left
+        if (node.left._balanceFactor == 1) {
+          _rotateLeftRight(node);
+          node = node.parent; // old grand-child!
+          if (node._balanceFactor == -1) {
+            node.right._balanceFactor = 1;
+            node.left._balanceFactor = 0;
+          } else if (node._balanceFactor == 0) {
+            node.right._balanceFactor = 0;
+            node.left._balanceFactor = 0;
+          } else {
+            node.right._balanceFactor = 0;
+            node.left._balanceFactor = -1;
+          }
+          node._balanceFactor = 0;
+        } else {
+          _rotateRight(node);
+          if (node.parent._balanceFactor == 0) {
+            node.parent._balanceFactor = 1;
+            node._balanceFactor = -1;
+            break;
+          } else {
+            node.parent._balanceFactor = 0;
+            node._balanceFactor = 0;
+            node = node.parent;
+            continue;
+          }
+        }
+      }
+
+      // continue up the tree for testing
+      if (node.parent != null) {
+        // The concept of balance here is reverse from addition; since
+        // we are taking away weight from one side or the other (thus
+        // the balance changes in favor of the other side)
+        if (node.parent.left == node) {
+          node.parent._balanceFactor += 1;
+        } else {
+          node.parent._balanceFactor -= 1;
+        }
+      }
+      node = node.parent;
+    }
+  }
+
+  /**
+   * See [Set.removeAll]
+   */
+  void removeAll(Iterable items) {
+    for (var ele in items) {
+      remove(ele);
+    }
+  }
+
+  /**
+   * See [Set.retainAll]
+   */
+  void retainAll(Iterable<Object> elements) {
+    List<V> chosen = [];
+    for (var target in elements) {
+      if (contains(target)) {
+        chosen.add(target);
+      }
+    }
+    clear();
+    addAll(chosen);
+  }
+
+  /**
+   * See [Set.retainWhere]
+   */
+  void retainWhere(bool test(V element)) {
+    List<V> chosen = [];
+    for (var target in this) {
+      if (test(target)) {
+        chosen.add(target);
+      }
+    }
+    clear();
+    addAll(chosen);
+  }
+
+  /**
+   * See [Set.removeWhere]
+   */
+  void removeWhere(bool test(V element)) {
+    List<V> damned = [];
+    for (var target in this) {
+      if (test(target)) {
+        damned.add(target);
+      }
+    }
+    removeAll(damned);
+  }
+
+  /**
+   * See [IterableBase.first]
+   */
+  V get first {
+    if (_root == null) return null;
+    AvlNode<V> min = _root.minimumNode;
+    return min != null ? min.object : null;
+  }
+
+  /**
+   * See [IterableBase.last]
+   */
+  V get last {
+    if (_root == null) return null;
+    AvlNode<V> max = _root.maximumNode;
+    return max != null ? max.object : null;
+  }
+
+  /**
+   * See [Set.lookup]
+   */
+  V lookup(Object element) {
+    if (element == null || _root == null) return null;
+    AvlNode<V> x = _root;
+    int compare = 0;
+    while (x != null) {
+      compare = comparator(element, x.object);
+      if (compare == 0) {
+        return x.object;
+      } else if (compare < 0) {
+        x = x.left;
+      } else {
+        x = x.right;
+      }
+    }
+    return null;
+  }
+
+  V nearest(V object, {TreeSearch nearestOption: TreeSearch.NEAREST}) {
+    AvlNode<V> found = _searchNearest(object, option: nearestOption);
+    return (found != null) ? found.object : null;
+  }
+
+  /**
+   * Search the tree for the matching element, or the 'nearest' node.
+   * NOTE: [BinaryTree.comparator] needs to have finer granulatity than -1,0,1
+   * in order for this to return something that's meaningful.
+   */
+  AvlNode<V> _searchNearest(V element,
+      {TreeSearch option: TreeSearch.NEAREST}) {
+    if (element == null || _root == null) {
+      return null;
+    }
+    AvlNode<V> x = _root;
+    AvlNode<V> previous;
+    int compare = 0;
+    while (x != null) {
+      previous = x;
+      compare = comparator(element, x.object);
+      if (compare == 0) {
+        return x;
+      } else if (compare < 0) {
+        x = x.left;
+      } else {
+        x = x.right;
+      }
+    }
+
+    if (option == TreeSearch.GREATER_THAN) {
+      return (compare < 0) ? previous : previous.successor;
+    } else if (option == TreeSearch.LESS_THAN) {
+      return (compare < 0) ? previous.predecessor : previous;
+    }
+    // Default: nearest absolute value
+    // Fell off the tree looking for the exact match; now we need
+    // to find the nearest element.
+    x = (compare < 0) ? previous.predecessor : previous.successor;
+    if (x == null) {
+      return previous;
+    }
+    int otherCompare = comparator(element, x.object);
+    if (compare < 0) {
+      return compare.abs() < otherCompare ? previous : x;
+    }
+    return otherCompare.abs() < compare ? x : previous;
+  }
+
+  //
+  // [IterableBase]<V> Methods
+  //
+
+  /**
+   * See [IterableBase.iterator]
+   */
+  BidirectionalIterator<V> get iterator => new _AvlTreeIterator._(this);
+
+  /**
+   * See [TreeSet.reverseIterator]
+   */
+  BidirectionalIterator<V> get reverseIterator =>
+      new _AvlTreeIterator._(this, reversed: true);
+
+  /**
+   * See [TreeSet.fromIterator]
+   */
+  BidirectionalIterator<V> fromIterator(V anchor,
+          {bool reversed: false, bool inclusive: true}) =>
+      new _AvlTreeIterator<V>._(this,
+          anchorObject: anchor, reversed: reversed, inclusive: inclusive);
+
+  /**
+   * See [IterableBase.contains]
+   */
+  bool contains(Object object) {
+    AvlNode<V> x = _getNode(object as V);
+    return x != null;
+  }
+
+  //
+  // [Set] methods
+  //
+
+  /**
+   * See [Set.intersection]
+   */
+  Set<V> intersection(Set<Object> other) {
+    TreeSet<V> set = new TreeSet(comparator: comparator);
+
+    // Opitimized for sorted sets
+    if (other is TreeSet) {
+      var i1 = iterator;
+      var i2 = other.iterator;
+      var hasMore1 = i1.moveNext();
+      var hasMore2 = i2.moveNext();
+      while (hasMore1 && hasMore2) {
+        var c = comparator(i1.current, i2.current);
+        if (c == 0) {
+          set.add(i1.current);
+          hasMore1 = i1.moveNext();
+          hasMore2 = i2.moveNext();
+        } else if (c < 0) {
+          hasMore1 = i1.moveNext();
+        } else {
+          hasMore2 = i2.moveNext();
+        }
+      }
+      return set;
+    }
+
+    // Non-optimized version.
+    for (var target in this) {
+      if (other.contains(target)) {
+        set.add(target);
+      }
+    }
+    return set;
+  }
+
+  /**
+   * See [Set.union]
+   */
+  Set<V> union(Set<V> other) {
+    TreeSet<V> set = new TreeSet(comparator: comparator);
+
+    if (other is TreeSet) {
+      var i1 = iterator;
+      var i2 = other.iterator;
+      var hasMore1 = i1.moveNext();
+      var hasMore2 = i2.moveNext();
+      while (hasMore1 && hasMore2) {
+        var c = comparator(i1.current, i2.current);
+        if (c == 0) {
+          set.add(i1.current);
+          hasMore1 = i1.moveNext();
+          hasMore2 = i2.moveNext();
+        } else if (c < 0) {
+          set.add(i1.current);
+          hasMore1 = i1.moveNext();
+        } else {
+          set.add(i2.current);
+          hasMore2 = i2.moveNext();
+        }
+      }
+      if (hasMore1 || hasMore2) {
+        i1 = hasMore1 ? i1 : i2;
+        do {
+          set.add(i1.current);
+        } while (i1.moveNext());
+      }
+      return set;
+    }
+
+    // Non-optimized version.
+    return set
+      ..addAll(this)
+      ..addAll(other);
+  }
+
+  /**
+   * See [Set.difference]
+   */
+  Set<V> difference(Set<V> other) {
+    TreeSet<V> set = new TreeSet(comparator: comparator);
+
+    if (other is TreeSet) {
+      var i1 = iterator;
+      var i2 = other.iterator;
+      var hasMore1 = i1.moveNext();
+      var hasMore2 = i2.moveNext();
+      while (hasMore1 && hasMore2) {
+        var c = comparator(i1.current, i2.current);
+        if (c == 0) {
+          hasMore1 = i1.moveNext();
+          hasMore2 = i2.moveNext();
+        } else if (c < 0) {
+          set.add(i1.current);
+          hasMore1 = i1.moveNext();
+        } else {
+          hasMore2 = i2.moveNext();
+        }
+      }
+      if (hasMore1) {
+        do {
+          set.add(i1.current);
+        } while (i1.moveNext());
+      }
+      return set;
+    }
+
+    // Non-optimized version.
+    for (var target in this) {
+      if (!other.contains(target)) {
+        set.add(target);
+      }
+    }
+    return set;
+  }
+
+  /**
+   * Visible for testing only.
+   */
+  AvlNode<V> getNode(V object) => _getNode(object);
+}
+
+typedef bool _IteratorMove();
+
+/**
+ * This iterator either starts at the beginning or end (see [TreeSet.iterator]
+ * and [TreeSet.reverseIterator]) or from an anchor point in the set (see
+ * [TreeSet.fromIterator]). When using fromIterator, the inital
+ * anchor point is included in the first movement (either [moveNext] or
+ * [movePrevious]) but can optionally be excluded in the constructor.
+ */
+class _AvlTreeIterator<V> implements BidirectionalIterator<V> {
+  static const LEFT = -1;
+  static const WALK = 0;
+  static const RIGHT = 1;
+
+  final bool reversed;
+  final AvlTreeSet<V> tree;
+  final int _modCountGuard;
+  final Object anchorObject;
+  final bool inclusive;
+
+  _IteratorMove _moveNext;
+  _IteratorMove _movePrevious;
+
+  int state;
+  _TreeNode<V> _current;
+
+  _AvlTreeIterator._(AvlTreeSet<V> tree,
+      {reversed: false, this.inclusive: true, this.anchorObject: null})
+      : this.tree = tree,
+        this._modCountGuard = tree._modCount,
+        this.reversed = reversed {
+    if (anchorObject == null || tree.length == 0) {
+      // If the anchor is far left or right, we're just a normal iterator.
+      state = reversed ? RIGHT : LEFT;
+      _moveNext = reversed ? _movePreviousNormal : _moveNextNormal;
+      _movePrevious = reversed ? _moveNextNormal : _movePreviousNormal;
+      return;
+    }
+
+    state = WALK;
+    // Else we've got an anchor we have to worry about initalizing from.
+    // This isn't known till the caller actually performs a previous/next.
+    _moveNext = () {
+      _current = tree._searchNearest(anchorObject,
+          option: reversed ? TreeSearch.LESS_THAN : TreeSearch.GREATER_THAN);
+      _moveNext = reversed ? _movePreviousNormal : _moveNextNormal;
+      _movePrevious = reversed ? _moveNextNormal : _movePreviousNormal;
+      if (_current == null) {
+        state = reversed ? LEFT : RIGHT;
+      } else if (tree.comparator(_current.object, anchorObject) == 0 &&
+          !inclusive) {
+        _moveNext();
+      }
+      return state == WALK;
+    };
+
+    _movePrevious = () {
+      _current = tree._searchNearest(anchorObject,
+          option: reversed ? TreeSearch.GREATER_THAN : TreeSearch.LESS_THAN);
+      _moveNext = reversed ? _movePreviousNormal : _moveNextNormal;
+      _movePrevious = reversed ? _moveNextNormal : _movePreviousNormal;
+      if (_current == null) {
+        state = reversed ? RIGHT : LEFT;
+      } else if (tree.comparator(_current.object, anchorObject) == 0 &&
+          !inclusive) {
+        _movePrevious();
+      }
+      return state == WALK;
+    };
+  }
+
+  V get current => (state != WALK || _current == null) ? null : _current.object;
+
+  bool moveNext() => _moveNext();
+  bool movePrevious() => _movePrevious();
+
+  bool _moveNextNormal() {
+    if (_modCountGuard != tree._modCount) {
+      throw new ConcurrentModificationError(tree);
+    }
+    if (state == RIGHT || tree.length == 0) return false;
+    switch (state) {
+      case LEFT:
+        _current = tree._root.minimumNode;
+        state = WALK;
+        return true;
+      case WALK:
+      default:
+        _current = _current.successor;
+        if (_current == null) {
+          state = RIGHT;
+        }
+        return state == WALK;
+    }
+  }
+
+  bool _movePreviousNormal() {
+    if (_modCountGuard != tree._modCount) {
+      throw new ConcurrentModificationError(tree);
+    }
+    if (state == LEFT || tree.length == 0) return false;
+    switch (state) {
+      case RIGHT:
+        _current = tree._root.maximumNode;
+        state = WALK;
+        return true;
+      case WALK:
+      default:
+        _current = _current.predecessor;
+        if (_current == null) {
+          state = LEFT;
+        }
+        return state == WALK;
+    }
+  }
+}
+
+/**
+ * Private class used to track element insertions in the [TreeSet].
+ */
+class AvlNode<V> extends _TreeNode<V> {
+  AvlNode<V> _left;
+  AvlNode<V> _right;
+  //TODO(codefu): Remove need for [parent]; this is just an implementation note
+  AvlNode<V> _parent;
+  int _balanceFactor = 0;
+
+  AvlNode<V> get left => _left;
+  AvlNode<V> get right => _right;
+  AvlNode<V> get parent => _parent;
+  int get balance => _balanceFactor;
+
+  AvlNode({V object}) : super(object: object);
+
+  String toString() =>
+      "(b:$balance o: $object l:${left != null} r:${right != null})";
+}
diff --git a/quiver/lib/src/core/hash.dart b/quiver/lib/src/core/hash.dart
new file mode 100644
index 0000000..de5317c
--- /dev/null
+++ b/quiver/lib/src/core/hash.dart
@@ -0,0 +1,53 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.core;
+
+/**
+ * Generates a hash code for multiple [objects].
+ */
+int hashObjects(Iterable objects) =>
+    _finish(objects.fold(0, (h, i) => _combine(h, i.hashCode)));
+
+/**
+ * Generates a hash code for two objects.
+ */
+int hash2(a, b) => _finish(_combine(_combine(0, a.hashCode), b.hashCode));
+
+/**
+ * Generates a hash code for three objects.
+ */
+int hash3(a, b, c) => _finish(
+    _combine(_combine(_combine(0, a.hashCode), b.hashCode), c.hashCode));
+
+/**
+ * Generates a hash code for four objects.
+ */
+int hash4(a, b, c, d) => _finish(_combine(
+    _combine(_combine(_combine(0, a.hashCode), b.hashCode), c.hashCode),
+    d.hashCode));
+
+// Jenkins hash functions
+
+int _combine(int hash, int value) {
+  hash = 0x1fffffff & (hash + value);
+  hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
+  return hash ^ (hash >> 6);
+}
+
+int _finish(int hash) {
+  hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
+  hash = hash ^ (hash >> 11);
+  return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
+}
diff --git a/quiver/lib/src/core/optional.dart b/quiver/lib/src/core/optional.dart
new file mode 100644
index 0000000..6802229
--- /dev/null
+++ b/quiver/lib/src/core/optional.dart
@@ -0,0 +1,130 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.core;
+
+/**
+ * A value that might be absent.
+ *
+ * Use Optional as an alternative to allowing fields, parameters or return
+ * values to be null. It signals that a value is not required and provides
+ * convenience methods for dealing with the absent case.
+ */
+class Optional<T> {
+  final T _value;
+
+  /**
+   * Constructs an empty Optional.
+   */
+  const Optional.absent() : _value = null;
+
+  /**
+   * Constructs an Optional of the given [value].
+   *
+   * Throws [ArgumentError] if [value] is null.
+   */
+  Optional.of(T value) : this._value = value {
+    if (this._value == null) throw new ArgumentError('Must not be null.');
+  }
+
+  /**
+   * Constructs an Optional of the given [value].
+   *
+   * If [value] is null, returns [absent()].
+   */
+  const Optional.fromNullable(T value) : this._value = value;
+
+  /**
+   * Whether the Optional contains a value.
+   */
+  bool get isPresent => _value != null;
+
+  /**
+   * Gets the Optional value.
+   *
+   * Throws [StateError] if [value] is null.
+   */
+  T get value {
+    if (this._value == null) {
+      throw new StateError('value called on absent Optional.');
+    }
+    return _value;
+  }
+
+  /**
+   * Executes a function if the Optional value is present.
+   */
+  void ifPresent(void ifPresent(T value)) {
+    if (isPresent) {
+      ifPresent(_value);
+    }
+  }
+
+  /**
+   * Execution a function if the Optional value is absent.
+   */
+  void ifAbsent(void ifAbsent()) {
+    if (!isPresent) {
+      ifAbsent();
+    }
+  }
+
+  /**
+   * Gets the Optional value with a default.
+   *
+   * The default is returned if the Optional is [absent()].
+   *
+   * Throws [ArgumentError] if [defaultValue] is null.
+   */
+  T or(T defaultValue) {
+    if (defaultValue == null) {
+      throw new ArgumentError('defaultValue must not be null.');
+    }
+    return _value == null ? defaultValue : _value;
+  }
+
+  /**
+   * Gets the Optional value, or [null] if there is none.
+   */
+  T get orNull => _value;
+
+  /**
+   * Transforms the Optional value.
+   *
+   * If the Optional is [absent()], returns [absent()] without applying the transformer.
+   *
+   * The transformer must not return [null]. If it does, an [ArgumentError] is thrown.
+   */
+  Optional transform(dynamic transformer(T value)) {
+    return _value == null
+        ? new Optional.absent()
+        : new Optional.of(transformer(_value));
+  }
+
+  /**
+   * Delegates to the underlying [value] hashCode.
+   */
+  int get hashCode => _value.hashCode;
+
+  /**
+   * Delegates to the underlying [value] operator==.
+   */
+  bool operator ==(o) => o is Optional && o._value == _value;
+
+  String toString() {
+    return _value == null
+        ? 'Optional { absent }'
+        : 'Optional { value: ${_value} }';
+  }
+}
diff --git a/quiver/lib/src/iterables/concat.dart b/quiver/lib/src/iterables/concat.dart
new file mode 100644
index 0000000..b4ab751
--- /dev/null
+++ b/quiver/lib/src/iterables/concat.dart
@@ -0,0 +1,22 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.iterables;
+
+/**
+ * Returns the concatentation of the input iterables.
+ *
+ * The returned iterable is a lazily-evaluated view on the input iterables.
+ */
+Iterable concat(Iterable<Iterable> iterables) => iterables.expand((x) => x);
diff --git a/quiver/lib/src/iterables/count.dart b/quiver/lib/src/iterables/count.dart
new file mode 100644
index 0000000..66e949a
--- /dev/null
+++ b/quiver/lib/src/iterables/count.dart
@@ -0,0 +1,46 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.iterables;
+
+/**
+ * Returns an infinite [Iterable] of [num]s, starting from [start] and
+ * increasing by [step].
+ */
+Iterable<num> count([num start = 0, num step = 1]) => new _Count(start, step);
+
+class _Count extends InfiniteIterable<num> {
+  final num start, step;
+
+  _Count(num this.start, num this.step);
+
+  Iterator<num> get iterator => new _CountIterator(start, step);
+
+  // TODO(justin): return an infinite list for toList() and a special Set
+  // implmentation for toSet()?
+}
+
+class _CountIterator implements Iterator<num> {
+  final num _start, _step;
+  num _current;
+
+  _CountIterator(num this._start, this._step);
+
+  num get current => _current;
+
+  bool moveNext() {
+    _current = (_current == null) ? _start : _current + _step;
+    return true;
+  }
+}
diff --git a/quiver/lib/src/iterables/cycle.dart b/quiver/lib/src/iterables/cycle.dart
new file mode 100644
index 0000000..5cf3b12
--- /dev/null
+++ b/quiver/lib/src/iterables/cycle.dart
@@ -0,0 +1,54 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.iterables;
+
+/**
+ * Returns an [Iterable] that infinitely cycles through the elements of
+ * [iterable]. If [iterable] is empty, the returned Iterable will also be empty.
+ */
+Iterable cycle(Iterable iterable) => new _Cycle(iterable);
+
+class _Cycle<T> extends InfiniteIterable<T> {
+  final Iterable<T> _iterable;
+
+  _Cycle(this._iterable);
+
+  Iterator<T> get iterator => new _CycleIterator(_iterable);
+
+  bool get isEmpty => _iterable.isEmpty;
+
+  bool get isNotEmpty => _iterable.isNotEmpty;
+
+  // TODO(justin): add methods that can be answered by the wrapped iterable
+}
+
+class _CycleIterator<T> implements Iterator<T> {
+  final Iterable<T> _iterable;
+  Iterator<T> _iterator;
+
+  _CycleIterator(_iterable)
+      : _iterable = _iterable,
+        _iterator = _iterable.iterator;
+
+  T get current => _iterator.current;
+
+  bool moveNext() {
+    if (!_iterator.moveNext()) {
+      _iterator = _iterable.iterator;
+      return _iterator.moveNext();
+    }
+    return true;
+  }
+}
diff --git a/quiver/lib/src/iterables/enumerate.dart b/quiver/lib/src/iterables/enumerate.dart
new file mode 100644
index 0000000..72a02a9
--- /dev/null
+++ b/quiver/lib/src/iterables/enumerate.dart
@@ -0,0 +1,78 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.iterables;
+
+/**
+ * Returns an [Iterable] of [IndexedValue]s where the nth value holds the nth
+ * element of [iterable] and its index.
+ */
+Iterable<IndexedValue> enumerate(Iterable iterable) =>
+    new EnumerateIterable(iterable);
+
+class IndexedValue<V> {
+  final int index;
+  final V value;
+
+  IndexedValue(this.index, this.value);
+
+  operator ==(o) => o is IndexedValue && o.index == index && o.value == value;
+  int get hashCode => index * 31 + value.hashCode;
+  String toString() => '($index, $value)';
+}
+
+/**
+ * An [Iterable] of [IndexedValue]s where the nth value holds the nth
+ * element of [iterable] and its index. See [enumerate].
+ */
+// This was inspired by MappedIterable internal to Dart collections.
+class EnumerateIterable<V> extends IterableBase<IndexedValue<V>> {
+  final Iterable<V> _iterable;
+
+  EnumerateIterable(this._iterable);
+
+  Iterator<IndexedValue<V>> get iterator =>
+      new EnumerateIterator<V>(_iterable.iterator);
+
+  // Length related functions are independent of the mapping.
+  int get length => _iterable.length;
+  bool get isEmpty => _iterable.isEmpty;
+
+  // Index based lookup can be done before transforming.
+  IndexedValue<V> get first => new IndexedValue<V>(0, _iterable.first);
+  IndexedValue<V> get last => new IndexedValue<V>(length - 1, _iterable.last);
+  IndexedValue<V> get single => new IndexedValue<V>(0, _iterable.single);
+  IndexedValue<V> elementAt(int index) =>
+      new IndexedValue<V>(index, _iterable.elementAt(index));
+}
+
+/** The [Iterator] returned by [EnumerateIterable.iterator]. */
+class EnumerateIterator<V> extends Iterator<IndexedValue<V>> {
+  final Iterator<V> _iterator;
+  int _index = 0;
+  IndexedValue<V> _current;
+
+  EnumerateIterator(this._iterator);
+
+  IndexedValue<V> get current => _current;
+
+  bool moveNext() {
+    if (_iterator.moveNext()) {
+      _current = new IndexedValue(_index++, _iterator.current);
+      return true;
+    }
+    _current = null;
+    return false;
+  }
+}
diff --git a/quiver/lib/src/iterables/generating_iterable.dart b/quiver/lib/src/iterables/generating_iterable.dart
new file mode 100644
index 0000000..d11d198
--- /dev/null
+++ b/quiver/lib/src/iterables/generating_iterable.dart
@@ -0,0 +1,80 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.iterables;
+
+Iterable generate(initial(), next(o)) => new GeneratingIterable(initial, next);
+
+/**
+ * An Iterable who's first value is [object] and who's subsequent values are
+ * generated by passing the current value to the [next] function.
+ *
+ * The class is useful for creating lazy iterables from object hierarchies and
+ * graphs.
+ *
+ * It's important that for the given initial value and next function that the
+ * sequence of items eventually terminates. Otherwise calling methods that
+ * expect a finite sequence, like `length` or `last`, will cause an infinite
+ * loop.
+ *
+ * Example:
+ *
+ *     class Node {
+ *       Node parent;
+ *
+ *       /**
+ *        * An iterable of node and all ancestors up to the root.
+ *        */
+ *       Iterable<Node> ancestors =
+ *           new GeneratingIterable<Node>(() => this, (n) => n.parent);
+ *
+ *       /**
+ *        * An iterable of the root and the path of nodes to this. The reverse
+ *        * of ancestors.
+ *        */
+ *       Iterable<Node> path = ancestors.toList().reversed();
+ *     }
+ *
+ */
+class GeneratingIterable<T> extends IterableBase<T> {
+  final initial;
+  final next;
+
+  GeneratingIterable(T this.initial(), T this.next(T o));
+
+  @override
+  Iterator<T> get iterator => new _GeneratingIterator(initial(), next);
+}
+
+class _GeneratingIterator<T> implements Iterator<T> {
+  final next;
+  T object;
+  bool started = false;
+
+  _GeneratingIterator(T this.object, T this.next(T o));
+
+  @override
+  T get current => started ? object : null;
+
+  @override
+  bool moveNext() {
+    if (object == null) return false;
+    if (started) {
+      object = next(object);
+    } else {
+      started = true;
+    }
+    return object != null;
+  }
+}
diff --git a/quiver/lib/src/iterables/infinite_iterable.dart b/quiver/lib/src/iterables/infinite_iterable.dart
new file mode 100644
index 0000000..cfe0bd2
--- /dev/null
+++ b/quiver/lib/src/iterables/infinite_iterable.dart
@@ -0,0 +1,50 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.iterables;
+
+/**
+ * A base class for [Iterable]s of infinite length that throws
+ * [UnsupportedError] for methods that would require the Iterable to terminate.
+ */
+abstract class InfiniteIterable<T> extends IterableBase<T> {
+  bool get isEmpty => false;
+
+  bool get isNotEmpty => true;
+
+  T get last => throw new UnsupportedError('last');
+
+  int get length => throw new UnsupportedError('length');
+
+  T get single => throw new StateError('single');
+
+  bool every(bool f(T element)) => throw new UnsupportedError('every');
+
+  bool fold(initialValue, combine(previousValue, T element)) =>
+      throw new UnsupportedError('fold');
+
+  void forEach(void f(T element)) => throw new UnsupportedError('forEach');
+
+  String join([String separator = '']) => throw new UnsupportedError('join');
+
+  T lastWhere(bool test(T value), {T orElse()}) =>
+      throw new UnsupportedError('lastWhere');
+
+  T reduce(T combine(T value, T element)) =>
+      throw new UnsupportedError('reduce');
+
+  List<T> toList({bool growable: true}) => throw new UnsupportedError('toList');
+
+  Set<T> toSet() => throw new UnsupportedError('toSet');
+}
diff --git a/quiver/lib/src/iterables/merge.dart b/quiver/lib/src/iterables/merge.dart
new file mode 100644
index 0000000..53441e1
--- /dev/null
+++ b/quiver/lib/src/iterables/merge.dart
@@ -0,0 +1,89 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.iterables;
+
+/**
+ * Returns the result of merging an [Iterable] of [Iterable]s, according to
+ * the order specified by the [compare] function. This function assumes the
+ * provided iterables are already sorted according to the provided [compare]
+ * function. It will not check for this condition or sort the iterables.
+ *
+ * The compare function must act as a [Comparator]. If [compare] is omitted,
+ * [Comparable.compare] is used.
+ *
+ * If any of the [iterables] contain null elements, an exception will be
+ * thrown.
+ */
+Iterable merge(Iterable<Iterable> iterables,
+        [Comparator compare = Comparable.compare]) =>
+    (iterables.isEmpty) ? const [] : new _Merge(iterables, compare);
+
+class _Merge extends IterableBase {
+  final Iterable<Iterable> _iterables;
+  final Comparator _compare;
+
+  _Merge(this._iterables, this._compare);
+
+  Iterator get iterator => new _MergeIterator(
+      _iterables.map((i) => i.iterator).toList(growable: false), _compare);
+
+  String toString() => this.toList().toString();
+}
+
+/// Like [Iterator] but one element ahead.
+class _IteratorPeeker {
+  final Iterator _iterator;
+  bool _hasCurrent;
+
+  _IteratorPeeker(Iterator iterator)
+      : _iterator = iterator,
+        _hasCurrent = iterator.moveNext();
+
+  moveNext() {
+    _hasCurrent = _iterator.moveNext();
+  }
+
+  get current => _iterator.current;
+}
+
+class _MergeIterator implements Iterator {
+  final List<_IteratorPeeker> _peekers;
+  final Comparator _compare;
+  var _current;
+
+  _MergeIterator(List<Iterator> iterators, this._compare)
+      : _peekers = iterators.map((i) => new _IteratorPeeker(i)).toList();
+
+  bool moveNext() {
+    // Pick the peeker that's peeking at the puniest piece
+    _IteratorPeeker minIter = null;
+    for (var p in _peekers) {
+      if (p._hasCurrent) {
+        if (minIter == null || _compare(p.current, minIter.current) < 0) {
+          minIter = p;
+        }
+      }
+    }
+
+    if (minIter == null) {
+      return false;
+    }
+    _current = minIter.current;
+    minIter.moveNext();
+    return true;
+  }
+
+  get current => _current;
+}
diff --git a/quiver/lib/src/iterables/min_max.dart b/quiver/lib/src/iterables/min_max.dart
new file mode 100644
index 0000000..c1e3069
--- /dev/null
+++ b/quiver/lib/src/iterables/min_max.dart
@@ -0,0 +1,69 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.iterables;
+
+/**
+ * Returns the maximum value in [i], according to the order specified by the
+ * [compare] function, or `null` if [i] is empty.
+ *
+ * The compare function must act as a [Comparator]. If [compare] is omitted,
+ * [Comparable.compare] is used. If [i] contains null elements, an exception
+ * will be thrown.
+ *
+ */
+dynamic max(Iterable i, [Comparator compare = Comparable.compare]) =>
+    i.isEmpty ? null : i.reduce((a, b) => compare(a, b) > 0 ? a : b);
+
+/**
+ * Returns the minimum value in [i], according to the order specified by the
+ * [compare] function, or `null` if [i] is empty.
+ *
+ * The compare function must act as a [Comparator]. If [compare] is omitted,
+ * [Comparable.compare] is used. If [i] contains null elements, an exception
+ * will be thrown.
+ */
+dynamic min(Iterable i, [Comparator compare = Comparable.compare]) =>
+    i.isEmpty ? null : i.reduce((a, b) => compare(a, b) < 0 ? a : b);
+
+/**
+ * Returns the minimum and maximum values in [i], according to the order
+ * specified by the [compare] function, in an [Extent] instance. Always returns
+ * an [Extent], but [Extent.min] and [Extent.max] may be `null` if [i] is empty.
+ *
+ * The compare function must act as a [Comparator]. If [compare] is omitted,
+ * [Comparable.compare] is used. If [i] contains null elements, an exception
+ * will be thrown.
+ *
+ * If [i] is empty, an [Extent] is returned with [:null:] values for [:min:] and
+ * [:max:], since there are no valid values for them.
+ */
+Extent extent(Iterable i, [Comparator compare = Comparable.compare]) {
+  var iterator = i.iterator;
+  var hasNext = iterator.moveNext();
+  if (!hasNext) return new Extent(null, null);
+  var max = iterator.current;
+  var min = iterator.current;
+  while (iterator.moveNext()) {
+    if (compare(max, iterator.current) < 0) max = iterator.current;
+    if (compare(min, iterator.current) > 0) min = iterator.current;
+  }
+  return new Extent(min, max);
+}
+
+class Extent {
+  final min;
+  final max;
+  Extent(this.min, this.max);
+}
diff --git a/quiver/lib/src/iterables/partition.dart b/quiver/lib/src/iterables/partition.dart
new file mode 100644
index 0000000..cb96443
--- /dev/null
+++ b/quiver/lib/src/iterables/partition.dart
@@ -0,0 +1,56 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.iterables;
+
+/**
+ * Partitions the input iterable into lists of the specified size.
+ */
+Iterable<List> partition(Iterable iterable, int size) {
+  return iterable.isEmpty ? [] : new _Partition(iterable, size);
+}
+
+class _Partition extends IterableBase<List> {
+  final Iterable _iterable;
+  final int _size;
+
+  _Partition(this._iterable, this._size) {
+    if (_size <= 0) throw new ArgumentError(_size);
+  }
+
+  Iterator<List> get iterator =>
+      new _PartitionIterator(_iterable.iterator, _size);
+}
+
+class _PartitionIterator implements Iterator<List> {
+  final Iterator _iterator;
+  final int _size;
+  List _current;
+
+  _PartitionIterator(this._iterator, this._size);
+
+  @override
+  List get current => _current;
+
+  @override
+  bool moveNext() {
+    var newValue = [];
+    var count = 0;
+    for (; count < _size && _iterator.moveNext(); count++) {
+      newValue.add(_iterator.current);
+    }
+    _current = (count > 0) ? newValue : null;
+    return _current != null;
+  }
+}
diff --git a/quiver/lib/src/iterables/range.dart b/quiver/lib/src/iterables/range.dart
new file mode 100644
index 0000000..c707748
--- /dev/null
+++ b/quiver/lib/src/iterables/range.dart
@@ -0,0 +1,74 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.iterables;
+
+/**
+ * Returns an [Iterable] sequence of [num]s.
+ *
+ * If only one argument is provided, [start_or_stop] is the upper bound for the
+ * sequence. If two or more arguments are provided, [stop] is the upper bound.
+ *
+ * The sequence starts at 0 if one argument is provided, or [start_or_stop] if
+ * two or more arguments are provided. The sequence increments by 1, or [step]
+ * if provided. [step] can be negative, in which case the sequence counts down
+ * from the starting point and [stop] must be less than the starting point so
+ * that it becomes the lower bound.
+ */
+Iterable<num> range(num start_or_stop, [num stop, num step]) =>
+    new _Range(start_or_stop, stop, step);
+
+class _Range extends IterableBase<num> {
+  final num start, stop, step;
+
+  _Range(num start_or_stop, num _stop, num _step)
+      : start = (_stop == null) ? 0 : start_or_stop,
+        stop = (_stop == null) ? start_or_stop : _stop,
+        step = (_step == null) ? 1 : _step {
+    if (step == 0) {
+      throw new ArgumentError("step cannot be 0");
+    }
+    if ((step > 0) && (stop < start)) {
+      throw new ArgumentError("if step is positive,"
+          " stop must be greater than start");
+    }
+    if ((step < 0) && (stop > start)) {
+      throw new ArgumentError("if step is negative,"
+          " stop must be less than start");
+    }
+  }
+
+  Iterator<num> get iterator => new _RangeIterator(start, stop, step);
+}
+
+class _RangeIterator implements Iterator<num> {
+  final num _stop, _step;
+  num _value;
+  bool _hasNext;
+  bool _inRange;
+
+  _RangeIterator(num start, num stop, this._step)
+      : _value = start,
+        _stop = stop,
+        _hasNext = true,
+        _inRange = false;
+
+  num get current => _inRange ? _value : null;
+
+  bool moveNext() {
+    if (_hasNext && _inRange) _value += _step;
+    _inRange = _hasNext = (_step > 0) ? (_value < _stop) : (_value > _stop);
+    return _hasNext;
+  }
+}
diff --git a/quiver/lib/src/iterables/zip.dart b/quiver/lib/src/iterables/zip.dart
new file mode 100644
index 0000000..69d397e
--- /dev/null
+++ b/quiver/lib/src/iterables/zip.dart
@@ -0,0 +1,54 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.iterables;
+
+/**
+ * Returns an [Iterable] of [List]s where the nth element in the returned
+ * iterable contains the nth element from every Iterable in [iterables]. The
+ * returned Iterable is as long as the shortest Iterable in the argument. If
+ * [iterables] is empty, it returns an empty list.
+ */
+Iterable<List> zip(Iterable<Iterable> iterables) =>
+    (iterables.isEmpty) ? const [] : new _Zip(iterables);
+
+class _Zip extends IterableBase<List> {
+  final Iterable<Iterable> iterables;
+
+  _Zip(Iterable<Iterable> this.iterables);
+
+  Iterator<List> get iterator => new _ZipIterator(
+      iterables.map((i) => i.iterator).toList(growable: false));
+}
+
+class _ZipIterator implements Iterator<List> {
+  final List<Iterator> _iterators;
+  List _current;
+
+  _ZipIterator(List<Iterator> this._iterators);
+
+  List get current => _current;
+
+  bool moveNext() {
+    bool hasNext = true;
+    var newValue = new List(_iterators.length);
+    for (int i = 0; i < _iterators.length; i++) {
+      var iter = _iterators[i];
+      hasNext = hasNext && iter.moveNext();
+      newValue[i] = iter.current;
+    }
+    _current = (hasNext) ? newValue : null;
+    return hasNext;
+  }
+}
diff --git a/quiver/lib/src/pattern/glob.dart b/quiver/lib/src/pattern/glob.dart
new file mode 100644
index 0000000..d855227
--- /dev/null
+++ b/quiver/lib/src/pattern/glob.dart
@@ -0,0 +1,76 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.pattern;
+
+// TODO(justin): add more detailed documentation and explain how matching
+// differs or is similar to globs in Python and various shells.
+/**
+ * A [Pattern] that matches against filesystem path-like strings with
+ * wildcards.
+ *
+ * The pattern matches strings as follows:
+ *   * The whole string must match, not a substring
+ *   * Any non wildcard is matched as a literal
+ *   * '*' matches one or more characters except '/'
+ *   * '?' matches exactly one character except '/'
+ *   * '**' matches one or more characters including '/'
+ */
+class Glob implements Pattern {
+  final RegExp regex;
+  final String pattern;
+
+  Glob(String pattern)
+      : pattern = pattern,
+        regex = _regexpFromGlobPattern(pattern);
+
+  Iterable<Match> allMatches(String str, [int start = 0]) =>
+      regex.allMatches(str, start);
+
+  Match matchAsPrefix(String string, [int start = 0]) =>
+      regex.matchAsPrefix(string, start);
+
+  bool hasMatch(String str) => regex.hasMatch(str);
+
+  String toString() => pattern;
+
+  int get hashCode => pattern.hashCode;
+
+  bool operator ==(other) => other is Glob && pattern == other.pattern;
+}
+
+RegExp _regexpFromGlobPattern(String pattern) {
+  var sb = new StringBuffer();
+  sb.write('^');
+  var chars = pattern.split('');
+  for (var i = 0; i < chars.length; i++) {
+    var c = chars[i];
+    if (_specialChars.hasMatch(c)) {
+      sb.write('\\$c');
+    } else if (c == '*') {
+      if ((i + 1 < chars.length) && (chars[i + 1] == '*')) {
+        sb.write('.*');
+        i++;
+      } else {
+        sb.write('[^/]*');
+      }
+    } else if (c == '?') {
+      sb.write('[^/]');
+    } else {
+      sb.write(c);
+    }
+  }
+  sb.write(r'$');
+  return new RegExp(sb.toString());
+}
diff --git a/quiver/lib/src/streams/collect.dart b/quiver/lib/src/streams/collect.dart
new file mode 100644
index 0000000..0b7719e
--- /dev/null
+++ b/quiver/lib/src/streams/collect.dart
@@ -0,0 +1,37 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.streams;
+
+/**
+ * Returns a stream of completion events for the input [futures].
+ *
+ * Successfully completed futures yield data events, while futures completed
+ * with errors yield error events.
+ *
+ * The iterator obtained from [futures] is only advanced once the previous
+ * future completes and yields an event.  Thus, lazily creating the futures is
+ * supported, for example:
+ *
+ *     collect(files.map((file) => file.readAsString()));
+ *
+ * If you need to modify [futures], or a backing collection thereof, before the
+ * returned stream is done, pass a copy instead to avoid a
+ * [ConcurrentModificationError]:
+ *
+ *     collect(files.toList().map((file) => file.readAsString()));
+ *
+ */
+Stream collect(Iterable futures) =>
+    new Stream.fromIterable(futures).asyncMap((f) => f);
diff --git a/quiver/lib/src/streams/concat.dart b/quiver/lib/src/streams/concat.dart
new file mode 100644
index 0000000..d8c44e0
--- /dev/null
+++ b/quiver/lib/src/streams/concat.dart
@@ -0,0 +1,85 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.streams;
+
+/**
+ * Returns the concatentation of the input streams.
+ *
+ * When the returned stream is listened to, the [streams] are iterated through
+ * asynchronously, forwarding all events (both data and error) for the current
+ * stream to the returned stream before advancing the iterator and listening to
+ * the next stream.  If advancing the iterator throws an error, the returned
+ * stream ends immediately with that error.
+ *
+ * Pausing and resuming the returned stream's subscriptions will pause and
+ * resume the subscription of the current stream being listened to.
+ *
+ * Note: Events from pre-existing broadcast streams which occur before
+ * the stream is reached by the iteration will be dropped.
+ *
+ * Example:
+ *
+ *     concat(files.map((file) =>
+ *         file.openRead().transform(const LineSplitter())))
+ *
+ */
+Stream concat(Iterable<Stream> streams) => new _ConcatStream(streams);
+
+class _ConcatStream extends Stream {
+  final Iterable<Stream> _streams;
+
+  _ConcatStream(Iterable<Stream> streams) : _streams = streams;
+
+  StreamSubscription listen(void onData(var data),
+      {Function onError, void onDone(), bool cancelOnError}) {
+    cancelOnError = true == cancelOnError;
+    StreamSubscription currentSubscription;
+    StreamController controller;
+    Iterator iterator = _streams.iterator;
+
+    void nextStream() {
+      bool hasNext;
+      try {
+        hasNext = iterator.moveNext();
+      } catch (e, s) {
+        controller
+          ..addError(e, s)
+          ..close();
+        return;
+      }
+      if (hasNext) {
+        currentSubscription = iterator.current.listen(controller.add,
+            onError: controller.addError,
+            onDone: nextStream,
+            cancelOnError: cancelOnError);
+      } else {
+        controller.close();
+      }
+    }
+
+    controller = new StreamController(onPause: () {
+      if (currentSubscription != null) currentSubscription.pause();
+    }, onResume: () {
+      if (currentSubscription != null) currentSubscription.resume();
+    }, onCancel: () {
+      if (currentSubscription != null) return currentSubscription.cancel();
+    });
+
+    nextStream();
+
+    return controller.stream.listen(onData,
+        onError: onError, onDone: onDone, cancelOnError: cancelOnError);
+  }
+}
diff --git a/quiver/lib/src/streams/enumerate.dart b/quiver/lib/src/streams/enumerate.dart
new file mode 100644
index 0000000..7978a05
--- /dev/null
+++ b/quiver/lib/src/streams/enumerate.dart
@@ -0,0 +1,24 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.streams;
+
+/**
+ * Returns a [Stream] of [IndexedValue]s where the nth value holds the nth
+ * element of [stream] and its index.
+ */
+Stream<IndexedValue> enumerate(Stream stream) {
+  var index = 0;
+  return stream.map((value) => new IndexedValue(index++, value));
+}
diff --git a/quiver/lib/src/streams/streambuffer.dart b/quiver/lib/src/streams/streambuffer.dart
new file mode 100644
index 0000000..bca6108
--- /dev/null
+++ b/quiver/lib/src/streams/streambuffer.dart
@@ -0,0 +1,196 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.streams;
+
+/**
+ * Underflow errors happen when the socket feeding a buffer is finished while
+ * there are still blocked readers. Each reader will complete with this error.
+ */
+class UnderflowError extends Error {
+  final message;
+
+  /// The [message] describes the underflow.
+  UnderflowError([this.message]);
+
+  String toString() {
+    if (message != null) {
+      return "StreamBuffer Underflow: $message";
+    }
+    return "StreamBuffer Underflow";
+  }
+}
+
+/**
+ * Allow orderly reading of elements from a datastream, such as Socket, which
+ * might not receive List<int> bytes regular chunks.
+ *
+ * Example usage:
+ *     StreamBuffer<int> buffer = new StreamBuffer();
+ *     Socket.connect('127.0.0.1', 5555).then((sock) => sock.pipe(buffer));
+ *     buffer.read(100).then((bytes) {
+ *       // do something with 100 bytes;
+ *     });
+ *
+ * Throws [UnderflowError] if [throwOnError] is true. Useful for unexpected
+ * [Socket] disconnects.
+ */
+class StreamBuffer<T> implements StreamConsumer {
+  List _chunks = [];
+  int _offset = 0;
+  int _counter = 0; // sum(_chunks[*].length) - _offset
+  List<_ReaderInWaiting<List<T>>> _readers = [];
+  StreamSubscription<T> _sub;
+  Completer _streamDone;
+
+  final bool _throwOnError;
+
+  Stream _currentStream;
+
+  int _limit = 0;
+
+  set limit(int limit) {
+    _limit = limit;
+    if (_sub != null) {
+      if (!limited || _counter < limit) {
+        _sub.resume();
+      } else {
+        _sub.pause();
+      }
+    }
+  }
+
+  int get limit => _limit;
+
+  bool get limited => _limit > 0;
+
+  /**
+   * Create a stream buffer with optional, soft [limit] to the amount of data
+   * the buffer will hold before pausing the underlying stream. A limit of 0
+   * means no buffer limits.
+   */
+  StreamBuffer({bool throwOnError: false, int limit: 0})
+      : this._throwOnError = throwOnError,
+        this._limit = limit;
+
+  /**
+   * The amount of unread data buffered.
+   */
+  int get buffered => _counter;
+
+  List<T> _consume(int size) {
+    var follower = 0;
+    var ret = new List(size);
+    var leftToRead = size;
+    while (leftToRead > 0) {
+      var chunk = _chunks.first;
+      var listCap = (chunk is List) ? chunk.length - _offset : 1;
+      var subsize = leftToRead > listCap ? listCap : leftToRead;
+      if (chunk is List) {
+        ret.setRange(follower, follower + subsize,
+            chunk.getRange(_offset, _offset + subsize));
+      } else {
+        ret[follower] = chunk;
+      }
+      follower += subsize;
+      _offset += subsize;
+      _counter -= subsize;
+      leftToRead -= subsize;
+      if (chunk is! List || _offset >= chunk.length) {
+        _offset = 0;
+        _chunks.removeAt(0);
+      }
+    }
+    if (limited && _sub.isPaused && _counter < limit) {
+      _sub.resume();
+    }
+    return ret;
+  }
+
+  /**
+   * Read fully [size] bytes from the stream and return in the future.
+   *
+   * Throws [ArgumentError] if size is larger than optional buffer [limit].
+   */
+  Future<List<T>> read(int size) {
+    if (limited && size > limit) {
+      throw new ArgumentError("Cannot read $size with limit $limit");
+    }
+
+    // If we have enough data to consume and there are no other readers, then
+    // we can return immediately.
+    if (size <= buffered && _readers.isEmpty) {
+      return new Future.value(_consume(size));
+    }
+    Completer completer = new Completer<List<T>>();
+    _readers.add(new _ReaderInWaiting<List<T>>(size, completer));
+    return completer.future;
+  }
+
+  @override
+  Future addStream(Stream<T> stream) {
+    var lastStream = _currentStream == null ? stream : _currentStream;
+    if (_sub != null) {
+      _sub.cancel();
+      _streamDone.complete();
+    }
+    _currentStream = stream;
+    Completer streamDone = new Completer();
+    _sub = stream.listen((items) {
+      _chunks.add(items);
+      _counter += items is List ? items.length : 1;
+      if (limited && _counter >= limit) {
+        _sub.pause();
+      }
+
+      while (_readers.isNotEmpty && _readers.first.size <= _counter) {
+        var waiting = _readers.removeAt(0);
+        waiting.completer.complete(_consume(waiting.size));
+      }
+    }, onDone: () {
+      // User is piping in a new stream
+      if (stream == lastStream && _throwOnError) {
+        _closed(new UnderflowError());
+      }
+      streamDone.complete();
+    }, onError: (e) {
+      _closed(e);
+    });
+    return streamDone.future;
+  }
+
+  _closed(e) {
+    for (var reader in _readers) {
+      if (!reader.completer.isCompleted) {
+        reader.completer.completeError(e);
+      }
+    }
+    _readers.clear();
+  }
+
+  Future close() {
+    var ret;
+    if (_sub != null) {
+      ret = _sub.cancel();
+      _sub = null;
+    }
+    return ret is Future ? ret : new Future.value();
+  }
+}
+
+class _ReaderInWaiting<T> {
+  int size;
+  Completer<T> completer;
+  _ReaderInWaiting(this.size, this.completer);
+}
diff --git a/quiver/lib/src/time/clock.dart b/quiver/lib/src/time/clock.dart
new file mode 100644
index 0000000..dcc8a2c
--- /dev/null
+++ b/quiver/lib/src/time/clock.dart
@@ -0,0 +1,180 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.time;
+
+/// Returns current time.
+typedef DateTime TimeFunction();
+
+/// Return current system time.
+DateTime systemTime() => new DateTime.now();
+
+/// Days in a month. This array uses 1-based month numbers, i.e. January is
+/// the 1-st element in the array, not the 0-th.
+const _DAYS_IN_MONTH =
+    const [ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
+
+int _daysInMonth(int year, int month) => (month == DateTime.FEBRUARY &&
+    _isLeapYear(year)) ? 29 : _DAYS_IN_MONTH[month];
+
+bool _isLeapYear(int year) =>
+    (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
+
+/// Takes a [date] that may be outside the allowed range of dates for a given
+/// [month] in a given [year] and returns the closest date that is within the
+/// allowed range.
+///
+/// For example:
+///
+/// February 31, 2013 => February 28, 2013
+///
+/// When jumping from month to month or from leap year to common year we may
+/// end up in a month that has fewer days than the month we are jumping from.
+/// In that case it is impossible to preserve the exact date. So we "clamp" the
+/// date value to fit within the month. For example, jumping from March 31 one
+/// month back takes us to February 28 (or 29 during a leap year), as February
+/// doesn't have 31-st date.
+int _clampDate(int date, int year, int month) =>
+    date.clamp(1, _daysInMonth(year, month));
+
+/// Provides points in time relative to the current point in time, for example:
+/// now, 2 days ago, 4 weeks from now, etc.
+///
+/// This class is designed with testability in mind. The current point in time
+/// (or [now()]) is defined by a [TimeFunction]. By supplying your own time
+/// function or by using fixed clock (see constructors), you can control
+/// exactly what time a [Clock] returns and base your test expectations on
+/// that. See specific constructors for how to supply time functions.
+class Clock {
+  final TimeFunction _time;
+
+  /// Creates a clock based on the given [timeFunc].
+  ///
+  /// If [timeFunc] is not provided, creates [Clock] based on system clock.
+  ///
+  /// Custom [timeFunc] can be useful in unit-tests. For example, you might
+  /// want to control what time it is now and set date and time expectations in
+  /// your test cases.
+  const Clock([TimeFunction timeFunc = systemTime]) : _time = timeFunc;
+
+  /// Creates [Clock] that returns fixed [time] value. Useful in unit-tests.
+  Clock.fixed(DateTime time) : _time = (() => time);
+
+  /// Returns current time.
+  DateTime now() => _time();
+
+  /// Returns the point in time [Duration] amount of time ago.
+  DateTime agoBy(Duration duration) => now().subtract(duration);
+
+  /// Returns the point in time [Duration] amount of time from now.
+  DateTime fromNowBy(Duration duration) => now().add(duration);
+
+  /// Returns the point in time that's given amount of time ago. The
+  /// amount of time is the sum of individual parts. Parts are compatible with
+  /// ones defined in [Duration].
+  DateTime ago({int days: 0, int hours: 0, int minutes: 0, int seconds: 0,
+      int milliseconds: 0, int microseconds: 0}) => agoBy(new Duration(
+          days: days,
+          hours: hours,
+          minutes: minutes,
+          seconds: seconds,
+          milliseconds: milliseconds,
+          microseconds: microseconds));
+
+  /// Returns the point in time that's given amount of time from now. The
+  /// amount of time is the sum of individual parts. Parts are compatible with
+  /// ones defined in [Duration].
+  DateTime fromNow({int days: 0, int hours: 0, int minutes: 0, int seconds: 0,
+      int milliseconds: 0, int microseconds: 0}) => fromNowBy(new Duration(
+          days: days,
+          hours: hours,
+          minutes: minutes,
+          seconds: seconds,
+          milliseconds: milliseconds,
+          microseconds: microseconds));
+
+  /// Return the point in time [micros] microseconds ago.
+  DateTime microsAgo(int micros) => ago(microseconds: micros);
+
+  /// Return the point in time [micros] microseconds from now.
+  DateTime microsFromNow(int micros) => fromNow(microseconds: micros);
+
+  /// Return the point in time [millis] milliseconds ago.
+  DateTime millisAgo(int millis) => ago(milliseconds: millis);
+
+  /// Return the point in time [millis] milliseconds from now.
+  DateTime millisFromNow(int millis) => fromNow(milliseconds: millis);
+
+  /// Return the point in time [seconds] ago.
+  DateTime secondsAgo(int seconds) => ago(seconds: seconds);
+
+  /// Return the point in time [seconds] from now.
+  DateTime secondsFromNow(int seconds) => fromNow(seconds: seconds);
+
+  /// Return the point in time [minutes] ago.
+  DateTime minutesAgo(int minutes) => ago(minutes: minutes);
+
+  /// Return the point in time [minutes] from now.
+  DateTime minutesFromNow(int minutes) => fromNow(minutes: minutes);
+
+  /// Return the point in time [hours] ago.
+  DateTime hoursAgo(int hours) => ago(hours: hours);
+
+  /// Return the point in time [hours] from now.
+  DateTime hoursFromNow(int hours) => fromNow(hours: hours);
+
+  /// Return the point in time [days] ago.
+  DateTime daysAgo(int days) => ago(days: days);
+
+  /// Return the point in time [days] from now.
+  DateTime daysFromNow(int days) => fromNow(days: days);
+
+  /// Return the point in time [weeks] ago.
+  DateTime weeksAgo(int weeks) => ago(days: 7 * weeks);
+
+  /// Return the point in time [weeks] from now.
+  DateTime weeksFromNow(int weeks) => fromNow(days: 7 * weeks);
+
+  /// Return the point in time [months] ago on the same date.
+  DateTime monthsAgo(int months) {
+    var time = now();
+    var m = (time.month - months - 1) % 12 + 1;
+    var y = time.year - (months + 12 - time.month) ~/ 12;
+    var d = _clampDate(time.day, y, m);
+    return new DateTime(
+        y, m, d, time.hour, time.minute, time.second, time.millisecond);
+  }
+
+  /// Return the point in time [months] from now on the same date.
+  DateTime monthsFromNow(int months) {
+    var time = now();
+    var m = (time.month + months - 1) % 12 + 1;
+    var y = time.year + (months + time.month - 1) ~/ 12;
+    var d = _clampDate(time.day, y, m);
+    return new DateTime(
+        y, m, d, time.hour, time.minute, time.second, time.millisecond);
+  }
+
+  /// Return the point in time [years] ago on the same date.
+  DateTime yearsAgo(int years) {
+    var time = now();
+    var y = time.year - years;
+    var d = _clampDate(time.day, y, time.month);
+    return new DateTime(y, time.month, d, time.hour, time.minute, time.second,
+        time.millisecond);
+  }
+
+  /// Return the point in time [years] from now on the same date.
+  DateTime yearsFromNow(int years) => yearsAgo(-years);
+}
diff --git a/quiver/lib/src/time/duration_unit_constants.dart b/quiver/lib/src/time/duration_unit_constants.dart
new file mode 100644
index 0000000..f4a2fc1
--- /dev/null
+++ b/quiver/lib/src/time/duration_unit_constants.dart
@@ -0,0 +1,23 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.time;
+
+const Duration aMicrosecond = const Duration(microseconds: 1);
+const Duration aMillisecond = const Duration(milliseconds: 1);
+const Duration aSecond = const Duration(seconds: 1);
+const Duration aMinute = const Duration(minutes: 1);
+const Duration anHour = const Duration(hours: 1);
+const Duration aDay = const Duration(days: 1);
+const Duration aWeek = const Duration(days: 7);
diff --git a/quiver/lib/streams.dart b/quiver/lib/streams.dart
new file mode 100644
index 0000000..0480a29
--- /dev/null
+++ b/quiver/lib/streams.dart
@@ -0,0 +1,23 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+library quiver.streams;
+
+import 'dart:async';
+import 'package:quiver/iterables.dart' show IndexedValue;
+
+part 'src/streams/collect.dart';
+part 'src/streams/concat.dart';
+part 'src/streams/enumerate.dart';
+part 'src/streams/streambuffer.dart';
diff --git a/quiver/lib/strings.dart b/quiver/lib/strings.dart
new file mode 100644
index 0000000..013ba3a
--- /dev/null
+++ b/quiver/lib/strings.dart
@@ -0,0 +1,252 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+library quiver.strings;
+
+/**
+ * Returns [true] if [s] is either null, empty or is solely made of whitespace
+ * characters (as defined by [String.trim]).
+ */
+bool isBlank(String s) => s == null || s.trim().isEmpty;
+
+/**
+ * Returns [true] if [s] is either null or empty.
+ */
+bool isEmpty(String s) => s == null || s.isEmpty;
+
+/**
+ * Returns a string with characters from the given [s] in reverse order.
+ */
+String flip(String s) {
+  if (s == null || s == '') return s;
+  StringBuffer sb = new StringBuffer();
+  var runes = s.runes;
+  for (int i = runes.length - 1; i >= 0; i--) {
+    sb.writeCharCode(runes.elementAt(i));
+  }
+  return sb.toString();
+}
+
+/**
+ * If [s] is [null] returns empty string, otherwise returns [s] as is.
+ */
+String nullToEmpty(String s) => s == null ? '' : s;
+
+/**
+ * If [s] is an empty string returns [null], otherwise returns [s] as is.
+ */
+String emptyToNull(String s) => s == '' ? null : s;
+
+/**
+ * Concatenates [s] to itself a given number of [times]. Empty and null
+ * strings will always result in empty and null strings respectively no matter
+ * how many [times] they are [repeat]ed.
+ *
+ * If [times] is negative, returns the [flip]ped string repeated given number
+ * of [times].
+ */
+String repeat(String s, int times) {
+  if (s == null || s == '') return s;
+  if (times < 0) {
+    return repeat(flip(s), -times);
+  }
+  StringBuffer sink = new StringBuffer();
+  _repeat(sink, s, times);
+  return sink.toString();
+}
+
+/**
+ * Loops over [s] and returns traversed characters. Takes arbitrary [from] and
+ * [to] indices. Works as a substitute for [String.substring], except it never
+ * throws [RangeError]. Supports negative indices. Think of an index as a
+ * coordinate in an infinite in both directions vector filled with repeating
+ * string [s], whose 0-th coordinate coincides with the 0-th character in [s].
+ * Then [loop] returns the sub-vector defined by the interval ([from], [to]).
+ * [from] is inclusive. [to] is exclusive.
+ *
+ * This method throws exceptions on [null] and empty strings.
+ *
+ * If [to] is omitted or is [null] the traversing ends at the end of the loop.
+ *
+ * If [to] < [from], traverses [s] in the opposite direction.
+ *
+ * For example:
+ *
+ * loop('Hello, World!', 7) == 'World!'
+ * loop('ab', 0, 6) == 'ababab'
+ * loop('test.txt', -3) == 'txt'
+ * loop('ldwor', -3, 2) == 'world'
+ */
+String loop(String s, int from, [int to]) {
+  if (s == null || s == '') {
+    throw new ArgumentError('Input string cannot be null or empty');
+  }
+  if (to != null && to < from) {
+    return loop(flip(s), -from, -to);
+  }
+  int len = s.length;
+  int leftFrag = from >= 0 ? from ~/ len : ((from - len) ~/ len);
+  if (to == null) {
+    to = (leftFrag + 1) * len;
+  }
+  int rightFrag = to - 1 >= 0 ? to ~/ len : ((to - len) ~/ len);
+  int fragOffset = rightFrag - leftFrag - 1;
+  if (fragOffset == -1) {
+    return s.substring(from - leftFrag * len, to - rightFrag * len);
+  }
+  StringBuffer sink = new StringBuffer(s.substring(from - leftFrag * len));
+  _repeat(sink, s, fragOffset);
+  sink.write(s.substring(0, to - rightFrag * len));
+  return sink.toString();
+}
+
+void _repeat(StringBuffer sink, String s, int times) {
+  for (int i = 0; i < times; i++) {
+    sink.write(s);
+  }
+}
+
+/**
+ * Returns a [String] of length [width] padded on the left with characters from
+ * [fill] if `input.length` is less than [width]. [fill] is repeated if
+ * neccessary to pad.
+ *
+ * Returns [input] if `input.length` is equal to or greater than width. [input]
+ * can be `null` and is treated as an empty string.
+ */
+@Deprecated('Will be removed in 0.22.0')
+String padLeft(String input, int width, String fill) {
+  if (fill == null || fill.length == 0) {
+    throw new ArgumentError('fill cannot be null or empty');
+  }
+  if (input == null || input.length == 0) return loop(fill, 0, width);
+  if (input.length >= width) return input;
+  return loop(fill, 0, width - input.length) + input;
+}
+
+/**
+ * Returns a [String] of length [width] padded on the right with characters from
+ * [fill] if `input.length` is less than [width]. Characters are selected from
+ * [fill] starting at the end, so that the last character in [fill] is the last
+ * character in the result. [fill] is repeated if neccessary to pad.
+ *
+ * Returns [input] if `input.length` is equal to or greater than width. [input]
+ * can be `null` and is treated as an empty string.
+ */
+@Deprecated('Will be removed in 0.22.0')
+String padRight(String input, int width, String fill) {
+  if (fill == null || fill.length == 0) {
+    throw new ArgumentError('fill cannot be null or empty');
+  }
+  if (input == null || input.length == 0) {
+    return loop(fill, -width, 0);
+  }
+  if (input.length >= width) return input;
+  return input + loop(fill, input.length - width, 0);
+}
+
+/**
+ * Removes leading whitespace from a string.
+ *
+ * Whitespace is defined to be the same as [String.trim].
+ */
+@Deprecated('Will be removed in 0.22.0')
+String trimLeft(String input) {
+  int i = 0;
+  for (var rune in input.runes) {
+    if (isWhitespace(rune)) {
+      i++;
+    } else {
+      break;
+    }
+  }
+  return input.substring(i);
+}
+
+/**
+ * Removes trailing whitespace from a string.
+ *
+ * Whitespace is defined to be the same as [String.trim].
+ */
+@Deprecated('Will be removed in 0.22.0')
+String trimRight(String input) {
+  int i = 0;
+  int lastNonWhitespace = -1;
+  for (var rune in input.runes) {
+    i++;
+    if (!isWhitespace(rune)) {
+      lastNonWhitespace = i;
+    }
+  }
+  if (lastNonWhitespace == -1) return '';
+  return input.substring(0, lastNonWhitespace);
+}
+
+/**
+ * Returns `true` if [rune] represents a whitespace character.
+ *
+ * The definition of whitespace matches that used in [String.trim] which is
+ * based on Unicode 6.2. This maybe be a different set of characters than the
+ * environment's [RegExp] definition for whitespace, which is given by the
+ * ECMAScript standard: http://ecma-international.org/ecma-262/5.1/#sec-15.10
+ */
+bool isWhitespace(int rune) => ((rune >= 0x0009 && rune <= 0x000D) ||
+    rune == 0x0020 ||
+    rune == 0x0085 ||
+    rune == 0x00A0 ||
+    rune == 0x1680 ||
+    rune == 0x180E ||
+    (rune >= 0x2000 && rune <= 0x200A) ||
+    rune == 0x2028 ||
+    rune == 0x2029 ||
+    rune == 0x202F ||
+    rune == 0x205F ||
+    rune == 0x3000 ||
+    rune == 0xFEFF);
+
+/**
+ * Returns a [String] of length [width] padded with the same number of
+ * characters on the left and right from [fill].  On the right, characters are
+ * selected from [fill] starting at the end so that the last character in [fill]
+ * is the last character in the result. [fill] is repeated if neccessary to pad.
+ *
+ * Returns [input] if `input.length` is equal to or greater than width. [input]
+ * can be `null` and is treated as an empty string.
+ *
+ * If there are an odd number of characters to pad, then the right will be
+ * padded with one more than the left.
+ */
+String center(String input, int width, String fill) {
+  if (fill == null || fill.length == 0) {
+    throw new ArgumentError('fill cannot be null or empty');
+  }
+  if (input == null) input = '';
+  var leftWidth = input.length + (width - input.length) ~/ 2;
+  return padRight(padLeft(input, leftWidth, fill), width, fill);
+}
+
+/**
+ * Returns `true` if [a] and [b] are equal after being converted to lower case,
+ * or are both null.
+ */
+bool equalsIgnoreCase(String a, String b) => (a == null && b == null) ||
+    (a != null && b != null && a.toLowerCase() == b.toLowerCase());
+
+/**
+ * Compares [a] and [b] after converting to lower case.
+ *
+ * Both [a] and [b] must not be null.
+ */
+int compareIgnoreCase(String a, String b) =>
+    a.toLowerCase().compareTo(b.toLowerCase());
diff --git a/quiver/lib/testing/async.dart b/quiver/lib/testing/async.dart
new file mode 100644
index 0000000..409f97d
--- /dev/null
+++ b/quiver/lib/testing/async.dart
@@ -0,0 +1,27 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Testing support for dart:async.
+ */
+library quiver.testing.async;
+
+import 'dart:async';
+import 'dart:collection';
+
+import 'package:quiver/iterables.dart';
+import 'package:quiver/time.dart';
+
+part 'src/async/async.dart';
+part 'src/async/fake_async.dart';
diff --git a/quiver/lib/testing/equality.dart b/quiver/lib/testing/equality.dart
new file mode 100644
index 0000000..32e4f70
--- /dev/null
+++ b/quiver/lib/testing/equality.dart
@@ -0,0 +1,23 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Utilities for testing the equality of Dart object
+ */
+library quiver.testing.equality;
+
+import 'package:matcher/matcher.dart';
+import 'package:quiver/iterables.dart';
+
+part 'src/equality/equality.dart';
diff --git a/quiver/lib/testing/runtime.dart b/quiver/lib/testing/runtime.dart
new file mode 100644
index 0000000..b72fd86
--- /dev/null
+++ b/quiver/lib/testing/runtime.dart
@@ -0,0 +1,20 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Testing support related to the Dart runtime.
+ */
+library quiver.testing.runtime;
+
+part 'src/runtime/checked_mode.dart';
diff --git a/quiver/lib/testing/src/async/async.dart b/quiver/lib/testing/src/async/async.dart
new file mode 100644
index 0000000..881efc7
--- /dev/null
+++ b/quiver/lib/testing/src/async/async.dart
@@ -0,0 +1,50 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.testing.async;
+
+/**
+ * DEPRECATED: Use a much more feature-rich FakeAsync instead.
+ *
+ * A [Timer] implementation that stores its duration and callback for access
+ * in tests.
+ */
+@deprecated
+class FakeTimer implements Timer {
+  Duration duration;
+  var callback;
+  var onCancel;
+
+  /**
+   * Sets this timers [duration] and [callback] and returns [this].
+   *
+   * This method is usable as a [CreateTimer] or [CreateTimerPeriodic]
+   * function. In tests, construct a FakeTimer so that you have a refernece to
+   * it, then pass [create] to the function or class under test.
+   */
+  FakeTimer create(Duration duration, callback) {
+    if (this.duration != null) {
+      throw new StateError("FakeTimer.create already called");
+    }
+    this.duration = duration;
+    this.callback = callback;
+    return this;
+  }
+
+  void cancel() {
+    if (onCancel != null) onCancel();
+  }
+
+  bool isActive = true;
+}
diff --git a/quiver/lib/testing/src/async/fake_async.dart b/quiver/lib/testing/src/async/fake_async.dart
new file mode 100644
index 0000000..486f130
--- /dev/null
+++ b/quiver/lib/testing/src/async/fake_async.dart
@@ -0,0 +1,258 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.testing.async;
+
+/// A mechanism to make time-dependent units testable.
+///
+/// Test code can be passed as a callback to [run], which causes it to be run in
+/// a [Zone] which fakes timer and microtask creation, such that they are run
+/// during calls to [elapse] which simulates the asynchronous passage of time.
+///
+/// The synchronous passage of time (blocking or expensive calls) can also be
+/// simulated using [elapseBlocking].
+///
+/// To allow the unit under test to tell time, it can receive a [Clock] as a
+/// dependency, and default it to [const Clock()] in production, but then use
+/// [clock] in test code.
+///
+/// Example:
+///
+///     test('testedFunc', () {
+///       new FakeAsync().run((async) {
+///         testedFunc(clock: async.getClock(initialTime));
+///         async.elapse(duration);
+///         expect(...)
+///       });
+///     });
+abstract class FakeAsync {
+  factory FakeAsync() = _FakeAsync;
+
+  FakeAsync._();
+
+  /// Returns a fake [Clock] whose time can is elapsed by calls to [elapse] and
+  /// [elapseBlocking].
+  ///
+  /// The returned clock starts at [initialTime], and calls to [elapse] and
+  /// [elapseBlocking] advance the clock, even if they occured before the call
+  /// to this method.
+  ///
+  /// The clock can be passed as a dependency to the unit under test.
+  Clock getClock(DateTime initialTime);
+
+  /// Simulates the asynchronous passage of time.
+  ///
+  /// **This should only be called from within the zone used by [run].**
+  ///
+  /// If [duration] is negative, the returned future completes with an
+  /// [ArgumentError].
+  ///
+  /// If a previous call to [elapse] has not yet completed, throws a
+  /// [StateError].
+  ///
+  /// Any Timers created within the zone used by [run] which are to expire
+  /// at or before the new time after [duration] has elapsed are run.
+  /// The microtask queue is processed surrounding each timer.  When a timer is
+  /// run, the [clock] will have been advanced by the timer's specified
+  /// duration.  Calls to [elapseBlocking] from within these timers and
+  /// microtasks which cause the [clock] to elapse more than the specified
+  /// [duration], can cause more timers to expire and thus be called.
+  ///
+  /// Once all expired timers are processed, the [clock] is advanced (if
+  /// necessary) to the time this method was called + [duration].
+  void elapse(Duration duration);
+
+  /// Simulates the synchronous passage of time, resulting from blocking or
+  /// expensive calls.
+  ///
+  /// Neither timers nor microtasks are run during this call.  Upon return, the
+  /// [clock] will have been advanced by [duration].
+  ///
+  /// If [duration] is negative, throws an [ArgumentError].
+  void elapseBlocking(Duration duration);
+
+  /// Runs [callback] in a [Zone] with fake timer and microtask scheduling.
+  ///
+  /// Uses
+  /// [ZoneSpecification.createTimer], [ZoneSpecification.createPeriodicTimer],
+  /// and [ZoneSpecification.scheduleMicrotask] to store callbacks for later
+  /// execution within the zone via calls to [elapse].
+  ///
+  /// [callback] is called with `this` as argument.
+  run(callback(FakeAsync self));
+
+  /// Runs all remaining microtasks, including those scheduled as a result of
+  /// running them, until there are no more microtasks scheduled.
+  ///
+  /// Does not run timers.
+  void flushMicrotasks();
+
+  /// Runs all timers until no timers remain (subject to [flushPeriodicTimers]
+  /// option), including those scheduled as a result of running them.
+  ///
+  /// [timeout] lets you set the maximum amount of time the flushing will take.
+  /// Throws a [StateError] if the [timeout] is exceeded. The default timeout
+  /// is 1 hour. [timeout] is relative to the elapsed time.
+  void flushTimers({Duration timeout: const Duration(hours: 1),
+      bool flushPeriodicTimers: true});
+}
+
+class _FakeAsync extends FakeAsync {
+  Duration _elapsed = Duration.ZERO;
+  Duration _elapsingTo;
+  Queue<Function> _microtasks = new Queue();
+  Set<_FakeTimer> _timers = new Set<_FakeTimer>();
+
+  _FakeAsync() : super._() {
+    _elapsed;
+  }
+
+  Clock getClock(DateTime initialTime) =>
+      new Clock(() => initialTime.add(_elapsed));
+
+  void elapse(Duration duration) {
+    if (duration.inMicroseconds < 0) {
+      throw new ArgumentError('Cannot call elapse with negative duration');
+    }
+    if (_elapsingTo != null) {
+      throw new StateError('Cannot elapse until previous elapse is complete.');
+    }
+    _elapsingTo = _elapsed + duration;
+    _drainTimersWhile((_FakeTimer next) => next._nextCall <= _elapsingTo);
+    _elapseTo(_elapsingTo);
+    _elapsingTo = null;
+  }
+
+  void elapseBlocking(Duration duration) {
+    if (duration.inMicroseconds < 0) {
+      throw new ArgumentError('Cannot call elapse with negative duration');
+    }
+    _elapsed += duration;
+    if (_elapsingTo != null && _elapsed > _elapsingTo) {
+      _elapsingTo = _elapsed;
+    }
+  }
+
+  @override
+  void flushMicrotasks() {
+    _drainMicrotasks();
+  }
+
+  @override
+  void flushTimers({Duration timeout: const Duration(hours: 1),
+      bool flushPeriodicTimers: true}) {
+    final absoluteTimeout = _elapsed + timeout;
+    _drainTimersWhile((_FakeTimer timer) {
+      if (timer._nextCall > absoluteTimeout) {
+        throw new StateError(
+            'Exceeded timeout ${timeout} while flushing timers');
+      }
+      if (flushPeriodicTimers) {
+        return _timers.isNotEmpty;
+      } else {
+        // translation: keep draining while non-periodic timers exist
+        return _timers.any((_FakeTimer timer) => !timer._isPeriodic);
+      }
+    });
+  }
+
+  run(callback(FakeAsync self)) {
+    if (_zone == null) {
+      _zone = Zone.current.fork(specification: _zoneSpec);
+    }
+    return _zone.runGuarded(() => callback(this));
+  }
+  Zone _zone;
+
+  ZoneSpecification get _zoneSpec => new ZoneSpecification(
+      createTimer: (_, __, ___, Duration duration, Function callback) {
+    return _createTimer(duration, callback, false);
+  }, createPeriodicTimer: (_, __, ___, Duration duration, Function callback) {
+    return _createTimer(duration, callback, true);
+  }, scheduleMicrotask: (_, __, ___, Function microtask) {
+    _microtasks.add(microtask);
+  });
+
+  _drainTimersWhile(bool predicate(_FakeTimer)) {
+    _drainMicrotasks();
+    _FakeTimer next;
+    while ((next = _getNextTimer()) != null && predicate(next)) {
+      _runTimer(next);
+      _drainMicrotasks();
+    }
+  }
+
+  _elapseTo(Duration to) {
+    if (to > _elapsed) {
+      _elapsed = to;
+    }
+  }
+
+  Timer _createTimer(Duration duration, Function callback, bool isPeriodic) {
+    var timer = new _FakeTimer._(duration, callback, isPeriodic, this);
+    _timers.add(timer);
+    return timer;
+  }
+
+  _FakeTimer _getNextTimer() {
+    return min(_timers,
+        (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall));
+  }
+
+  _runTimer(_FakeTimer timer) {
+    assert(timer.isActive);
+    _elapseTo(timer._nextCall);
+    if (timer._isPeriodic) {
+      timer._callback(timer);
+      timer._nextCall += timer._duration;
+    } else {
+      timer._callback();
+      _timers.remove(timer);
+    }
+  }
+
+  _drainMicrotasks() {
+    while (_microtasks.isNotEmpty) {
+      _microtasks.removeFirst()();
+    }
+  }
+
+  _hasTimer(_FakeTimer timer) => _timers.contains(timer);
+
+  _cancelTimer(_FakeTimer timer) => _timers.remove(timer);
+}
+
+class _FakeTimer implements Timer {
+  final Duration _duration;
+  final Function _callback;
+  final bool _isPeriodic;
+  final _FakeAsync _time;
+  Duration _nextCall;
+
+  // TODO: In browser JavaScript, timers can only run every 4 milliseconds once
+  // sufficiently nested:
+  //     http://www.w3.org/TR/html5/webappapis.html#timer-nesting-level
+  // Without some sort of delay this can lead to infinitely looping timers.
+  // What do the dart VM and dart2js timers do here?
+  static const _minDuration = Duration.ZERO;
+
+  _FakeTimer._(Duration duration, this._callback, this._isPeriodic, this._time)
+      : _duration = duration < _minDuration ? _minDuration : duration {
+    _nextCall = _time._elapsed + _duration;
+  }
+
+  bool get isActive => _time._hasTimer(this);
+
+  cancel() => _time._cancelTimer(this);
+}
diff --git a/quiver/lib/testing/src/equality/equality.dart b/quiver/lib/testing/src/equality/equality.dart
new file mode 100644
index 0000000..2053e57
--- /dev/null
+++ b/quiver/lib/testing/src/equality/equality.dart
@@ -0,0 +1,215 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.testing.equality;
+
+/**
+ * Matcher for == and hashCode methods of a class.
+ *
+ * To use, invoke areEqualityGroups with a list of equality groups where each
+ * group contains objects that are supposed to be equal to each other, and
+ * objects of different groups are expected to be unequal. For example:
+ *
+ *     expect({
+ *      'hello': ["hello", "h" + "ello"],
+ *      'world': ["world", "wor" + "ld"],
+ *      'three': [2, 1 + 1]
+ *     }, areEqualityGroups);
+ *
+ * This tests that:
+ *
+ * * comparing each object against itself returns true
+ * * comparing each object against an instance of an incompatible class
+ *     returns false
+ * * comparing each pair of objects within the same equality group returns
+ *     true
+ * * comparing each pair of objects from different equality groups returns
+ *     false
+ * * the hash codes of any two equal objects are equal
+ * * equals implementation is idempotent
+ *
+ * The format of the Map passed to expect is such that the map keys are used in
+ * error messages to identify the group described by the map value.
+ *
+ * When a test fails, the error message labels the objects involved in
+ * the failed comparison as follows:
+ *
+ *      "`[group x, item j]`" refers to the ith item in the xth equality group,
+ *       where both equality groups and the items within equality groups are
+ *       numbered starting from 1.  When either a constructor argument or an
+ *       equal object is provided, that becomes group 1.
+ *
+ */
+const Matcher areEqualityGroups = const _EqualityGroupMatcher();
+
+const _repetitions = 3;
+
+class _EqualityGroupMatcher extends Matcher {
+  static const failureReason = 'failureReason';
+  const _EqualityGroupMatcher();
+
+  @override
+  Description describe(Description description) =>
+      description.add('to be equality groups');
+
+  @override
+  bool matches(Map<String, List> item, Map matchState) {
+    try {
+      _verifyEqualityGroups(item, matchState);
+      return true;
+    } on MatchError catch (e) {
+      matchState[failureReason] = e.toString();
+      return false;
+    }
+  }
+
+  Description describeMismatch(item, Description mismatchDescription,
+          Map matchState, bool verbose) =>
+      mismatchDescription.add(" ${matchState[failureReason]}");
+
+  void _verifyEqualityGroups(Map<String, List> equalityGroups, Map matchState) {
+    if (equalityGroups == null) {
+      throw new MatchError('Equality Group must not be null');
+    }
+    var equalityGroupsCopy = {};
+    equalityGroups.forEach((String groupName, List group) {
+      if (groupName == null) {
+        throw new MatchError('Group name must not be null');
+      }
+      if (group == null) {
+        throw new MatchError('Group must not be null');
+      }
+      equalityGroupsCopy[groupName] = new List.from(group);
+    });
+
+    // Run the test multiple times to ensure deterministic equals
+    for (var run in range(_repetitions)) {
+      _checkBasicIdentity(equalityGroupsCopy, matchState);
+      _checkGroupBasedEquality(equalityGroupsCopy);
+    }
+  }
+
+  void _checkBasicIdentity(Map<String, List> equalityGroups, Map matchState) {
+    var flattened = equalityGroups.values.expand((group) => group);
+    for (var item in flattened) {
+      if (item == _NotAnInstance.equalToNothing) {
+        throw new MatchError(
+            "$item must not be equal to an arbitrary object of another class");
+      }
+
+      if (item != item) {
+        throw new MatchError("$item must be equal to itself");
+      }
+
+      if (item.hashCode != item.hashCode) {
+        throw new MatchError("the implementation of hashCode of $item must "
+            "be idempotent");
+      }
+    }
+  }
+
+  void _checkGroupBasedEquality(Map<String, List> equalityGroups) {
+    equalityGroups.forEach((String groupName, List group) {
+      var groupLength = group.length;
+      for (var itemNumber = 0; itemNumber < groupLength; itemNumber++) {
+        _checkEqualToOtherGroup(
+            equalityGroups, groupLength, itemNumber, groupName);
+        _checkUnequalToOtherGroups(equalityGroups, groupName, itemNumber);
+      }
+    });
+  }
+
+  void _checkUnequalToOtherGroups(
+      Map<String, List> equalityGroups, String groupName, int itemNumber) {
+    equalityGroups.forEach((String unrelatedGroupName, List unrelatedGroup) {
+      if (groupName != unrelatedGroupName) {
+        for (var unrelatedItemNumber = 0;
+            unrelatedItemNumber < unrelatedGroup.length;
+            unrelatedItemNumber++) {
+          _expectUnrelated(equalityGroups, groupName, itemNumber,
+              unrelatedGroupName, unrelatedItemNumber);
+        }
+      }
+    });
+  }
+
+  void _checkEqualToOtherGroup(Map<String, List> equalityGroups,
+      int groupLength, int itemNumber, String groupName) {
+    for (var relatedItemNumber = 0;
+        relatedItemNumber < groupLength;
+        relatedItemNumber++) {
+      if (itemNumber != relatedItemNumber) {
+        _expectRelated(
+            equalityGroups, groupName, itemNumber, relatedItemNumber);
+      }
+    }
+  }
+
+  void _expectRelated(Map<String, List> equalityGroups, String groupName,
+      int itemNumber, int relatedItemNumber) {
+    var itemInfo = _createItem(equalityGroups, groupName, itemNumber);
+    var relatedInfo = _createItem(equalityGroups, groupName, relatedItemNumber);
+
+    if (itemInfo.value != relatedInfo.value) {
+      throw new MatchError("$itemInfo must be equal to $relatedInfo");
+    }
+
+    if (itemInfo.value.hashCode != relatedInfo.value.hashCode) {
+      throw new MatchError(
+          "the hashCode (${itemInfo.value.hashCode}) of $itemInfo must "
+          "be equal to the hashCode (${relatedInfo.value.hashCode}) of "
+          "$relatedInfo}");
+    }
+  }
+
+  void _expectUnrelated(Map<String, List> equalityGroups, String groupName,
+      int itemNumber, String unrelatedGroupName, int unrelatedItemNumber) {
+    var itemInfo = _createItem(equalityGroups, groupName, itemNumber);
+    var unrelatedInfo =
+        _createItem(equalityGroups, unrelatedGroupName, unrelatedItemNumber);
+
+    if (itemInfo.value == unrelatedInfo.value) {
+      throw new MatchError("$itemInfo must not be equal to " "$unrelatedInfo)");
+    }
+  }
+
+  _Item _createItem(
+          Map<String, List> equalityGroups, String groupName, int itemNumber) =>
+      new _Item(equalityGroups[groupName][itemNumber], groupName, itemNumber);
+}
+
+class _NotAnInstance {
+  static const equalToNothing = const _NotAnInstance._();
+  const _NotAnInstance._();
+}
+
+class _Item {
+  final Object value;
+  final String groupName;
+  final int itemNumber;
+
+  _Item(this.value, this.groupName, this.itemNumber);
+
+  @override
+  String toString() => "$value [group '$groupName', item ${itemNumber + 1}]";
+}
+
+class MatchError extends Error {
+  final message;
+
+  /// The [message] describes the match error.
+  MatchError([this.message]);
+
+  String toString() => message;
+}
diff --git a/quiver/lib/testing/src/runtime/checked_mode.dart b/quiver/lib/testing/src/runtime/checked_mode.dart
new file mode 100644
index 0000000..05dfaae
--- /dev/null
+++ b/quiver/lib/testing/src/runtime/checked_mode.dart
@@ -0,0 +1,42 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.testing.runtime;
+
+/**
+ * Asserts that the current runtime has checked mode enabled.
+ *
+ * Otherwise, throws [StateError].
+ */
+void assertCheckedMode() {
+  if (_isCheckedMode == null) _isCheckedMode = _checkForCheckedMode();
+
+  if (!_isCheckedMode) {
+    throw new StateError('Not in checked mode.');
+  }
+}
+
+bool _isCheckedMode = null;
+
+bool _checkForCheckedMode() {
+  Object sentinal = new Object();
+  try {
+    var i = 1 as dynamic;
+    String string = i;
+    throw sentinal;
+  } catch (e) {
+    if (e == sentinal) return false;
+  }
+  return true;
+}
diff --git a/quiver/lib/testing/src/time/time.dart b/quiver/lib/testing/src/time/time.dart
new file mode 100644
index 0000000..01049cf
--- /dev/null
+++ b/quiver/lib/testing/src/time/time.dart
@@ -0,0 +1,74 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+part of quiver.testing.time;
+
+/**
+ * Returns the current test time in microseconds.
+ */
+typedef int Now();
+
+/**
+ * A [Stopwatch] implementation that gets the current time in microseconds
+ * via a user-supplied function.
+ */
+class FakeStopwatch implements Stopwatch {
+  Now _now;
+  int frequency;
+  int _start;
+  int _stop;
+
+  FakeStopwatch(int now(), int this.frequency)
+      : _now = now,
+        _start = null,
+        _stop = null;
+
+  void start() {
+    if (isRunning) return;
+    if (_start == null) {
+      _start = _now();
+    } else {
+      _start = _now() - (_stop - _start);
+      _stop = null;
+    }
+  }
+
+  void stop() {
+    if (!isRunning) return;
+    _stop = _now();
+  }
+
+  void reset() {
+    if (_start == null) return;
+    _start = _now();
+    if (_stop != null) {
+      _stop = _start;
+    }
+  }
+
+  int get elapsedTicks {
+    if (_start == null) {
+      return 0;
+    }
+    return (_stop == null) ? (_now() - _start) : (_stop - _start);
+  }
+
+  Duration get elapsed => new Duration(microseconds: elapsedMicroseconds);
+
+  int get elapsedMicroseconds => (elapsedTicks * 1000000) ~/ frequency;
+
+  int get elapsedMilliseconds => (elapsedTicks * 1000) ~/ frequency;
+
+  bool get isRunning => _start != null && _stop == null;
+}
diff --git a/quiver/lib/testing/time.dart b/quiver/lib/testing/time.dart
new file mode 100644
index 0000000..a06f656
--- /dev/null
+++ b/quiver/lib/testing/time.dart
@@ -0,0 +1,20 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Testing support for dart:core time functionality.
+ */
+library quiver.testing.time;
+
+part 'src/time/time.dart';
diff --git a/quiver/lib/time.dart b/quiver/lib/time.dart
new file mode 100644
index 0000000..e71b2d3
--- /dev/null
+++ b/quiver/lib/time.dart
@@ -0,0 +1,18 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+library quiver.time;
+
+part 'src/time/clock.dart';
+part 'src/time/duration_unit_constants.dart';
diff --git a/quiver/pubspec.yaml b/quiver/pubspec.yaml
new file mode 100644
index 0000000..f35397a
--- /dev/null
+++ b/quiver/pubspec.yaml
@@ -0,0 +1,19 @@
+name: quiver
+version: 0.21.3
+authors:
+- Justin Fagnani <justinfagnani@google.com>
+- Yegor Jbanov <yjbanov@google.com>
+- Chris Bracken <cbracken@google.com>
+- Alexandre Ardhuin <alexandre.ardhuin@gmail.com>
+- David Morgan <davidmorgan@google.com>
+- John McDole <codefu@google.com>
+- Matan Lurey <matanl@google.com>
+description: A set of utility libraries for Dart
+homepage: https://github.com/google/quiver-dart
+environment:
+  sdk: '>=1.0.0 <2.0.0'
+dependencies:
+  matcher: '>=0.10.0 <0.12.0'
+dev_dependencies:
+  path: '>=1.0.0 <2.0.0'
+  unittest: '>=0.11.0'
diff --git a/smoke/lib/codegen/generator.dart b/smoke/lib/codegen/generator.dart
new file mode 100644
index 0000000..a332db8
--- /dev/null
+++ b/smoke/lib/codegen/generator.dart
@@ -0,0 +1,516 @@
+// 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 to generate code that can initialize the `StaticConfiguration` in
+/// `package:smoke/static.dart`.
+///
+/// This library doesn't have any specific logic to extract information from
+/// Dart source code. To extract code using the analyzer, take a look at the
+/// `smoke.codegen.recorder` library.
+library smoke.codegen.generator;
+
+import 'dart:collection' show SplayTreeMap, SplayTreeSet;
+
+import 'package:smoke/src/common.dart' show compareLists, compareMaps;
+
+/// Collects the necessary information and generates code to initialize a
+/// `StaticConfiguration`. After setting up the generator by calling
+/// [addGetter], [addSetter], [addSymbol], and [addDeclaration], you can
+/// retrieve the generated code using the following three methods:
+///
+///   * [writeImports] writes a list of imports directives,
+///   * [writeTopLevelDeclarations] writes additional declarations used to
+///     represent mixin classes by name in the generated code.
+///   * [writeStaticConfiguration] writes the actual code that allocates the
+///     static configuration.
+///
+/// You'd need to include all three in your generated code, since the
+/// initialization code refers to symbols that are only available from the
+/// generated imports or the generated top-level declarations.
+class SmokeCodeGenerator {
+  // Note: we use SplayTreeSet/Map here and below to keep generated code sorted.
+  /// Names used as getters via smoke.
+  final Set<String> _getters = new SplayTreeSet();
+
+  /// Names used as setters via smoke.
+  final Set<String> _setters = new SplayTreeSet();
+
+  /// Subclass relations needed to run smoke queries.
+  final Map<TypeIdentifier, TypeIdentifier> _parents = new SplayTreeMap();
+
+  /// Declarations requested via `smoke.getDeclaration or `smoke.query`.
+  final Map<TypeIdentifier, Map<String, _DeclarationCode>> _declarations =
+      new SplayTreeMap();
+
+  /// Static methods used on each type.
+  final Map<TypeIdentifier, Set<String>> _staticMethods = new SplayTreeMap();
+
+  /// Names that are used both as strings and symbols.
+  final Set<String> _names = new SplayTreeSet();
+
+  /// Prefixes associated with imported libraries.
+  final Map<String, String> _libraryPrefix = {};
+
+  /// Register that [name] is used as a getter in the code.
+  void addGetter(String name) {
+    _getters.add(name);
+  }
+
+  /// Register that [name] is used as a setter in the code.
+  void addSetter(String name) {
+    _setters.add(name);
+  }
+
+  /// Register that [name] might be needed as a symbol.
+  void addSymbol(String name) {
+    _names.add(name);
+  }
+
+  /// Register that `cls.name` is used as a static method in the code.
+  void addStaticMethod(TypeIdentifier cls, String name) {
+    var methods =
+        _staticMethods.putIfAbsent(cls, () => new SplayTreeSet<String>());
+    _addLibrary(cls.importUrl);
+    methods.add(name);
+  }
+
+  int _mixins = 0;
+
+  /// Creates a new type to represent a mixin. Use [comment] to help users
+  /// figure out what mixin is being represented.
+  TypeIdentifier createMixinType([String comment = '']) =>
+      new TypeIdentifier(null, '_M${_mixins++}', comment);
+
+  /// Register that we care to know that [child] extends [parent].
+  void addParent(TypeIdentifier child, TypeIdentifier parent) {
+    var existing = _parents[child];
+    if (existing != null) {
+      if (existing == parent) return;
+      throw new StateError('$child already has a different parent associated'
+          '($existing instead of $parent)');
+    }
+    _addLibrary(child.importUrl);
+    _addLibrary(parent.importUrl);
+    _parents[child] = parent;
+  }
+
+  /// Register a declaration of a field, property, or method. Note that one and
+  /// only one of [isField], [isMethod], or [isProperty] should be set at a
+  /// given time.
+  void addDeclaration(TypeIdentifier cls, String name, TypeIdentifier type,
+      {bool isField: false, bool isProperty: false, bool isMethod: false,
+      bool isFinal: false, bool isStatic: false,
+      List<ConstExpression> annotations: const []}) {
+    final count = (isField ? 1 : 0) + (isProperty ? 1 : 0) + (isMethod ? 1 : 0);
+    if (count != 1) {
+      throw new ArgumentError('Declaration must be one (and only one) of the '
+          'following: a field, a property, or a method.');
+    }
+    var kind = isField ? 'FIELD' : isProperty ? 'PROPERTY' : 'METHOD';
+    _declarations.putIfAbsent(
+        cls, () => new SplayTreeMap<String, _DeclarationCode>());
+    _addLibrary(cls.importUrl);
+    var map = _declarations[cls];
+
+    for (var exp in annotations) {
+      for (var lib in exp.librariesUsed) {
+        _addLibrary(lib);
+      }
+    }
+
+    _addLibrary(type.importUrl);
+    var decl =
+        new _DeclarationCode(name, type, kind, isFinal, isStatic, annotations);
+    if (map.containsKey(name) && map[name] != decl) {
+      throw new StateError('$type.$name already has a different declaration'
+          ' (${map[name]} instead of $decl).');
+    }
+    map[name] = decl;
+  }
+
+  /// Register that we might try to read declarations of [type], even if no
+  /// declaration exists. This informs the smoke system that querying for a
+  /// member in this class might be intentional and not an error.
+  void addEmptyDeclaration(TypeIdentifier type) {
+    _addLibrary(type.importUrl);
+    _declarations.putIfAbsent(
+        type, () => new SplayTreeMap<String, _DeclarationCode>());
+  }
+
+  /// Writes to [buffer] a line for each import that is needed by the generated
+  /// code. The code added by [writeStaticConfiguration] depends on these
+  /// imports.
+  void writeImports(StringBuffer buffer) {
+    DEFAULT_IMPORTS.forEach((i) => buffer.writeln(i));
+    _libraryPrefix.forEach((url, prefix) {
+      buffer.writeln("import '$url' as $prefix;");
+    });
+  }
+
+  /// Writes to [buffer] top-level declarations that are used by the code
+  /// generated in [writeStaticConfiguration]. These are typically declarations
+  /// of empty classes that are then used as placeholders for mixin
+  /// superclasses.
+  void writeTopLevelDeclarations(StringBuffer buffer) {
+    var types = new Set()
+      ..addAll(_parents.keys)
+      ..addAll(_parents.values)
+      ..addAll(_declarations.keys)
+      ..addAll(_declarations.values.expand((m) => m.values.map((d) => d.type)))
+      ..removeWhere((t) => t.importUrl != null);
+    for (var type in types) {
+      buffer.write('abstract class ${type.name} {}');
+      if (type.comment != null) buffer.write(' // ${type.comment}');
+      buffer.writeln();
+    }
+  }
+
+  /// Appends to [buffer] code that will create smoke's static configuration.
+  /// For example, the code might be of the form:
+  ///
+  ///    new StaticConfiguration(
+  ///      getters: {
+  ///         #i: (o) => o.i,
+  ///         ...
+  ///      names: {
+  ///         #i: "i",
+  ///      })
+  ///
+  /// Callers of this code can assign this expression to a variable, and should
+  /// generate code that invokes `useGeneratedCode`.
+  ///
+  /// The optional [indent] argument is used for formatting purposes. All
+  /// entries in each map (getters, setters, names, declarations, parents) are
+  /// sorted alphabetically.
+  ///
+  /// **Note**: this code assumes that imports from [writeImports] and top-level
+  /// declarations from [writeTopLevelDeclarations] are included in the same
+  /// library where this code will live.
+  void writeStaticConfiguration(StringBuffer buffer, [int indent = 2]) {
+    final spaces = ' ' * (indent + 4);
+    var args = {};
+
+    if (_getters.isNotEmpty) {
+      args['getters'] = _getters.map((n) => '${_symbol(n)}: (o) => o.$n');
+    }
+    if (_setters.isNotEmpty) {
+      args['setters'] =
+          _setters.map((n) => '${_symbol(n)}: (o, v) { o.$n = v; }');
+    }
+
+    if (_parents.isNotEmpty) {
+      var parentsMap = [];
+      _parents.forEach((child, parent) {
+        var parent = _parents[child];
+        parentsMap.add('${child.asCode(_libraryPrefix)}: '
+            '${parent.asCode(_libraryPrefix)}');
+      });
+      args['parents'] = parentsMap;
+    }
+
+    if (_declarations.isNotEmpty) {
+      var declarations = [];
+      _declarations.forEach((type, members) {
+        final sb = new StringBuffer()
+          ..write(type.asCode(_libraryPrefix))
+          ..write(': ');
+        if (members.isEmpty) {
+          sb.write('{}');
+        } else {
+          sb.write('{\n');
+          members.forEach((name, decl) {
+            var decl = members[name].asCode(_libraryPrefix);
+            sb.write('${spaces}    ${_symbol(name)}: $decl,\n');
+          });
+          sb.write('${spaces}  }');
+        }
+        declarations.add(sb.toString());
+      });
+      args['declarations'] = declarations;
+    }
+
+    if (_staticMethods.isNotEmpty) {
+      var methods = [];
+      _staticMethods.forEach((type, members) {
+        var className = type.asCode(_libraryPrefix);
+        final sb = new StringBuffer()
+          ..write(className)
+          ..write(': ');
+        if (members.isEmpty) {
+          sb.write('{}');
+        } else {
+          sb.write('{\n');
+          for (var name in members) {
+            sb.write('${spaces}    ${_symbol(name)}: $className.$name,\n');
+          }
+          sb.write('${spaces}  }');
+        }
+        methods.add(sb.toString());
+      });
+      args['staticMethods'] = methods;
+    }
+
+    if (_names.isNotEmpty) {
+      args['names'] = _names.map((n) => "${_symbol(n)}: r'$n'");
+    }
+
+    buffer
+      ..writeln('new StaticConfiguration(')
+      ..write('${spaces}checkedMode: false');
+
+    args.forEach((name, mapContents) {
+      buffer.writeln(',');
+      // TODO(sigmund): use const map when Type can be keys (dartbug.com/17123)
+      buffer.writeln('${spaces}$name: {');
+      for (var entry in mapContents) {
+        buffer.writeln('${spaces}  $entry,');
+      }
+      buffer.write('${spaces}}');
+    });
+    buffer.write(')');
+  }
+
+  /// Adds a library that needs to be imported.
+  void _addLibrary(String url) {
+    if (url == null || url == 'dart:core') return;
+    _libraryPrefix.putIfAbsent(url, () => 'smoke_${_libraryPrefix.length}');
+  }
+}
+
+/// Information used to generate code that allocates a `Declaration` object.
+class _DeclarationCode extends ConstExpression {
+  final String name;
+  final TypeIdentifier type;
+  final String kind;
+  final bool isFinal;
+  final bool isStatic;
+  final List<ConstExpression> annotations;
+
+  _DeclarationCode(this.name, this.type, this.kind, this.isFinal, this.isStatic,
+      this.annotations);
+
+  List<String> get librariesUsed => []
+    ..addAll(type.librariesUsed)
+    ..addAll(annotations.expand((a) => a.librariesUsed));
+
+  String asCode(Map<String, String> libraryPrefixes) {
+    var sb = new StringBuffer();
+    sb.write('const Declaration(${_symbol(name)}, '
+        '${type.asCode(libraryPrefixes)}');
+    if (kind != 'FIELD') sb.write(', kind: $kind');
+    if (isFinal) sb.write(', isFinal: true');
+    if (isStatic) sb.write(', isStatic: true');
+    if (annotations != null && annotations.isNotEmpty) {
+      sb.write(', annotations: const [');
+      bool first = true;
+      for (var e in annotations) {
+        if (!first) sb.write(', ');
+        first = false;
+        sb.write(e.asCode(libraryPrefixes));
+      }
+      sb.write(']');
+    }
+    sb.write(')');
+    return sb.toString();
+  }
+
+  String toString() =>
+      '(decl: $type.$name - $kind, $isFinal, $isStatic, $annotations)';
+  operator ==(other) => other is _DeclarationCode &&
+      name == other.name &&
+      type == other.type &&
+      kind == other.kind &&
+      isFinal == other.isFinal &&
+      isStatic == other.isStatic &&
+      compareLists(annotations, other.annotations);
+  int get hashCode => name.hashCode + (31 * type.hashCode);
+}
+
+/// A constant expression that can be used as an annotation.
+abstract class ConstExpression {
+
+  /// Returns the library URLs that needs to be imported for this
+  /// [ConstExpression] to be a valid annotation.
+  List<String> get librariesUsed;
+
+  /// Return a string representation of the code in this expression.
+  /// [libraryPrefixes] describes what prefix has been associated with each
+  /// import url mentioned in [libraryUsed].
+  String asCode(Map<String, String> libraryPrefixes);
+
+  ConstExpression();
+
+  /// Create a string expression of the form `'string'`, where [string] is
+  /// normalized so we can correctly wrap it in single quotes.
+  factory ConstExpression.string(String string) {
+    var value = string.replaceAll(r'\', r'\\').replaceAll(r"'", r"\'");
+    return new CodeAsConstExpression("'$value'");
+  }
+
+  /// Create an expression of the form `prefix.variable_name`.
+  factory ConstExpression.identifier(String importUrl, String name) =>
+      new TopLevelIdentifier(importUrl, name);
+
+  /// Create an expression of the form `prefix.Constructor(v1, v2, p3: v3)`.
+  factory ConstExpression.constructor(String importUrl, String name,
+          List<ConstExpression> positionalArgs,
+          Map<String, ConstExpression> namedArgs) =>
+      new ConstructorExpression(importUrl, name, positionalArgs, namedArgs);
+}
+
+/// A constant expression written as a String. Used when the code is self
+/// contained and it doesn't depend on any imported libraries.
+class CodeAsConstExpression extends ConstExpression {
+  String code;
+  List<String> get librariesUsed => const [];
+
+  CodeAsConstExpression(this.code);
+
+  String asCode(Map<String, String> libraryPrefixes) => code;
+
+  String toString() => '(code: $code)';
+  operator ==(other) => other is CodeAsConstExpression && code == other.code;
+  int get hashCode => code.hashCode;
+}
+
+/// Describes a reference to some symbol that is exported from a library. This
+/// is typically used to refer to a type or a top-level variable from that
+/// library.
+class TopLevelIdentifier extends ConstExpression {
+  final String importUrl;
+  final String name;
+  TopLevelIdentifier(this.importUrl, this.name);
+
+  List<String> get librariesUsed => [importUrl];
+  String asCode(Map<String, String> libraryPrefixes) {
+    if (importUrl == 'dart:core' || importUrl == null) {
+      // TODO(jmesserly): analyzer doesn't consider `dynamic` to be a constant,
+      // so use Object as a workaround:
+      // https://code.google.com/p/dart/issues/detail?id=22989
+      return name == 'dynamic' ? 'Object' : name;
+    }
+    return '${libraryPrefixes[importUrl]}.$name';
+  }
+
+  String toString() => '(identifier: $importUrl, $name)';
+  operator ==(other) => other is TopLevelIdentifier &&
+      name == other.name &&
+      importUrl == other.importUrl;
+  int get hashCode => 31 * importUrl.hashCode + name.hashCode;
+}
+
+/// Represents an expression that invokes a const constructor.
+class ConstructorExpression extends ConstExpression {
+  final String importUrl;
+  final String name;
+  final List<ConstExpression> positionalArgs;
+  final Map<String, ConstExpression> namedArgs;
+  ConstructorExpression(
+      this.importUrl, this.name, this.positionalArgs, this.namedArgs);
+
+  List<String> get librariesUsed => [importUrl]
+    ..addAll(positionalArgs.expand((e) => e.librariesUsed))
+    ..addAll(namedArgs.values.expand((e) => e.librariesUsed));
+
+  String asCode(Map<String, String> libraryPrefixes) {
+    var sb = new StringBuffer();
+    sb.write('const ');
+    if (importUrl != 'dart:core' && importUrl != null) {
+      sb.write('${libraryPrefixes[importUrl]}.');
+    }
+    sb.write('$name(');
+    bool first = true;
+    for (var e in positionalArgs) {
+      if (!first) sb.write(', ');
+      first = false;
+      sb.write(e.asCode(libraryPrefixes));
+    }
+    namedArgs.forEach((name, value) {
+      if (!first) sb.write(', ');
+      first = false;
+      sb.write('$name: ');
+      sb.write(value.asCode(libraryPrefixes));
+    });
+    sb.write(')');
+    return sb.toString();
+  }
+
+  String toString() => '(ctor: $importUrl, $name, $positionalArgs, $namedArgs)';
+  operator ==(other) => other is ConstructorExpression &&
+      name == other.name &&
+      importUrl == other.importUrl &&
+      compareLists(positionalArgs, other.positionalArgs) &&
+      compareMaps(namedArgs, other.namedArgs);
+  int get hashCode => 31 * importUrl.hashCode + name.hashCode;
+}
+
+/// Describes a type identifier, with the library URL where the type is defined.
+// TODO(sigmund): consider adding support for imprecise TypeIdentifiers, which
+// may be used by tools that want to generate code without using the analyzer
+// (they can syntactically tell the type comes from one of N imports).
+class TypeIdentifier extends TopLevelIdentifier
+    implements Comparable<TypeIdentifier> {
+  final String comment;
+  TypeIdentifier(importUrl, typeName, [this.comment])
+      : super(importUrl, typeName);
+
+  // We implement [Comparable] to sort out entries in the generated code.
+  int compareTo(TypeIdentifier other) {
+    if (importUrl == null && other.importUrl != null) return 1;
+    if (importUrl != null && other.importUrl == null) return -1;
+    var c1 = importUrl == null ? 0 : importUrl.compareTo(other.importUrl);
+    return c1 != 0 ? c1 : name.compareTo(other.name);
+  }
+
+  String toString() => '(type-identifier: $importUrl, $name, $comment)';
+  bool operator ==(other) => other is TypeIdentifier &&
+      importUrl == other.importUrl &&
+      name == other.name &&
+      comment == other.comment;
+  int get hashCode => super.hashCode;
+}
+
+/// Default set of imports added by [SmokeCodeGenerator].
+const DEFAULT_IMPORTS = const [
+  "import 'package:smoke/smoke.dart' show Declaration, PROPERTY, METHOD;",
+  "import 'package:smoke/static.dart' show "
+      "useGeneratedCode, StaticConfiguration;",
+];
+
+_symbol(String name) {
+  if (!_publicSymbolPattern.hasMatch(name)) {
+    throw new StateError('invalid symbol name: "$name"');
+  }
+  return _literalSymbolPattern.hasMatch(name)
+      ? '#$name'
+      : "const Symbol('$name')";
+}
+
+// TODO(sigmund): is this included in some library we can import? I derived the
+// definitions below from sdk/lib/internal/symbol.dart.
+
+/// Reserved words in Dart.
+const String _reservedWordRE =
+    r'(?:assert|break|c(?:a(?:se|tch)|lass|on(?:st|tinue))|d(?:efault|o)|'
+    r'e(?:lse|num|xtends)|f(?:alse|inal(?:ly)?|or)|i[fns]|n(?:ew|ull)|'
+    r'ret(?:hrow|urn)|s(?:uper|witch)|t(?:h(?:is|row)|r(?:ue|y))|'
+    r'v(?:ar|oid)|w(?:hile|ith))';
+
+/// Public identifier: a valid identifier (not a reserved word) that doesn't
+/// start with '_'.
+const String _publicIdentifierRE =
+    r'(?!' '$_reservedWordRE' r'\b(?!\$))[a-zA-Z$][\w$]*';
+
+/// Pattern that matches operators only.
+final RegExp _literalSymbolPattern =
+    new RegExp('^(?:$_publicIdentifierRE(?:\$|[.](?!\$)))+?\$');
+
+/// Operator names allowed as symbols. The name of the oeprators is the same as
+/// the operator itself except for unary minus, where the name is "unary-".
+const String _operatorRE =
+    r'(?:[\-+*/%&|^]|\[\]=?|==|~/?|<[<=]?|>[>=]?|unary-)';
+
+/// Pattern that matches public symbols.
+final RegExp _publicSymbolPattern = new RegExp(
+    '^(?:$_operatorRE\$|$_publicIdentifierRE(?:=?\$|[.](?!\$)))+?\$');
diff --git a/smoke/lib/codegen/recorder.dart b/smoke/lib/codegen/recorder.dart
new file mode 100644
index 0000000..545455a
--- /dev/null
+++ b/smoke/lib/codegen/recorder.dart
@@ -0,0 +1,405 @@
+// 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.
+
+/// Records accesses to Dart program declarations and generates code that will
+/// allow to do the same accesses at runtime using `package:smoke/static.dart`.
+/// Internally, this library relies on the `analyzer` to extract data from the
+/// program, and then uses [SmokeCodeGenerator] to produce the code needed by
+/// the smoke system.
+///
+/// This library only uses the analyzer to consume data previously produced by
+/// running the resolver. This library does not provide any hooks to integrate
+/// running the analyzer itself. See `package:code_transformers` to integrate
+/// the analyzer into pub transformers.
+library smoke.codegen.recorder;
+
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/ast.dart';
+import 'generator.dart';
+
+typedef String ImportUrlResolver(LibraryElement lib);
+
+/// A recorder that tracks how elements are accessed in order to generate code
+/// that replicates those accesses with the smoke runtime.
+class Recorder {
+  /// Underlying code generator.
+  SmokeCodeGenerator generator;
+
+  /// Function that provides the import url for a library element. This may
+  /// internally use the resolver to resolve the import url.
+  ImportUrlResolver importUrlFor;
+
+  Recorder(this.generator, this.importUrlFor);
+
+  /// Stores mixins that have been recorded and associates a type identifier
+  /// with them. Mixins don't have an associated identifier in the code, so we
+  /// generate a unique identifier for them and use it throughout the code.
+  Map<TypeIdentifier, Map<ClassElement, TypeIdentifier>> _mixins = {};
+
+  /// Adds the superclass information of [type] (including intermediate mixins).
+  /// This will not generate data for direct subtypes of Object, as that is
+  /// considered redundant information.
+  void lookupParent(ClassElement type) {
+    var parent = type.supertype;
+    var mixins = type.mixins;
+    if (parent == null && mixins.isEmpty) return; // type is Object
+    var baseType = parent.element;
+    var baseId = _typeFor(baseType);
+    if (mixins.isNotEmpty) {
+      _mixins.putIfAbsent(baseId, () => {});
+      for (var m in mixins) {
+        var mixinType = m.element;
+        var mixinId = _mixins[baseId].putIfAbsent(mixinType, () {
+          var comment = '${baseId.name} & ${mixinType.name}';
+          return generator.createMixinType(comment);
+        });
+        if (!baseType.type.isObject) generator.addParent(mixinId, baseId);
+        baseType = mixinType;
+        baseId = mixinId;
+        _mixins.putIfAbsent(mixinId, () => {});
+      }
+    }
+    if (!baseType.type.isObject) generator.addParent(_typeFor(type), baseId);
+  }
+
+  TypeIdentifier _typeFor(Element type) => new TypeIdentifier(
+      type.library == null ? 'dart:core' : importUrlFor(type.library),
+      type.displayName);
+
+  /// Adds any declaration and superclass information that is needed to answer a
+  /// query on [type] that matches [options]. Also adds symbols, getters, and
+  /// setters if [includeAccessors] is true. If [results] is not null, it will
+  /// be filled up with the members that match the query.
+  void runQuery(ClassElement type, QueryOptions options,
+      {bool includeAccessors: true, List results}) {
+    if (type.type.isObject) return; // We don't include Object in query results.
+    var id = _typeFor(type);
+    var parent = type.supertype != null ? type.supertype.element : null;
+    if (options.includeInherited &&
+        parent != null &&
+        parent != options.includeUpTo) {
+      lookupParent(type);
+      runQuery(parent, options, includeAccessors: includeAccessors);
+      var parentId = _typeFor(parent);
+      for (var m in type.mixins) {
+        var mixinClass = m.element;
+        var mixinId = _mixins[parentId][mixinClass];
+        _runQueryInternal(
+            mixinClass, mixinId, options, includeAccessors, results);
+        parentId = mixinId;
+      }
+    }
+    _runQueryInternal(type, id, options, includeAccessors, results);
+  }
+
+  /// Helper for [runQuery]. This runs the query only on a specific [type],
+  /// which could be a class or a mixin labeled by [id].
+  // TODO(sigmund): currently we materialize mixins in smoke/static.dart,
+  // we should consider to include the mixin declaration information directly,
+  // and remove the duplication we have for mixins today.
+  void _runQueryInternal(ClassElement type, TypeIdentifier id,
+      QueryOptions options, bool includeAccessors, List results) {
+    skipBecauseOfAnnotations(Element e) {
+      if (options.withAnnotations == null) return false;
+      return !_matchesAnnotation(e.metadata, options.withAnnotations);
+    }
+
+    if (options.includeFields) {
+      for (var f in type.fields) {
+        if (f.isStatic) continue;
+        if (f.isSynthetic) continue; // exclude getters
+        if (options.excludeFinal && f.isFinal) continue;
+        var name = f.displayName;
+        if (options.matches != null && !options.matches(name)) continue;
+        if (skipBecauseOfAnnotations(f)) continue;
+        if (results != null) results.add(f);
+        generator.addDeclaration(id, name, _typeFor(f.type.element),
+            isField: true,
+            isFinal: f.isFinal,
+            annotations: _copyAnnotations(f));
+        if (includeAccessors) _addAccessors(name, !f.isFinal);
+      }
+    }
+
+    if (options.includeProperties) {
+      for (var a in type.accessors) {
+        if (a is! PropertyAccessorElement) continue;
+        if (a.isStatic || !a.isGetter) continue;
+        var v = a.variable;
+        if (v is FieldElement && !v.isSynthetic) continue; // exclude fields
+        if (options.excludeFinal && v.isFinal) continue;
+        var name = v.displayName;
+        if (options.matches != null && !options.matches(name)) continue;
+        if (skipBecauseOfAnnotations(a)) continue;
+        if (results != null) results.add(a);
+        generator.addDeclaration(id, name, _typeFor(a.type.returnType.element),
+            isProperty: true,
+            isFinal: v.isFinal,
+            annotations: _copyAnnotations(a));
+        if (includeAccessors) _addAccessors(name, !v.isFinal);
+      }
+    }
+
+    if (options.includeMethods) {
+      for (var m in type.methods) {
+        if (m.isStatic) continue;
+        var name = m.displayName;
+        if (options.matches != null && !options.matches(name)) continue;
+        if (skipBecauseOfAnnotations(m)) continue;
+        if (results != null) results.add(m);
+        generator.addDeclaration(
+            id, name, new TypeIdentifier('dart:core', 'Function'),
+            isMethod: true, annotations: _copyAnnotations(m));
+        if (includeAccessors) _addAccessors(name, false);
+      }
+    }
+  }
+
+  /// Adds the declaration of [name] if it was found in [type]. If [recursive]
+  /// is true, then we continue looking up [name] in the parent classes until we
+  /// find it or we reach [includeUpTo] or Object. Returns whether the
+  /// declaration was found.  When a declaration is found, add also a symbol,
+  /// getter, and setter if [includeAccessors] is true.
+  bool lookupMember(ClassElement type, String name, {bool recursive: false,
+          bool includeAccessors: true, ClassElement includeUpTo}) =>
+      _lookupMemberInternal(
+          type, _typeFor(type), name, recursive, includeAccessors, includeUpTo);
+
+  /// Helper for [lookupMember] that walks up the type hierarchy including mixin
+  /// classes.
+  bool _lookupMemberInternal(ClassElement type, TypeIdentifier id, String name,
+      bool recursive, bool includeAccessors, ClassElement includeUpTo) {
+    // Exclude members from [Object].
+    if (type.type.isObject) return false;
+    generator.addEmptyDeclaration(id);
+    for (var f in type.fields) {
+      if (f.displayName != name) continue;
+      if (f.isSynthetic) continue; // exclude getters
+      generator.addDeclaration(id, name, _typeFor(f.type.element),
+          isField: true,
+          isFinal: f.isFinal,
+          isStatic: f.isStatic,
+          annotations: _copyAnnotations(f));
+      if (includeAccessors && !f.isStatic) _addAccessors(name, !f.isFinal);
+      return true;
+    }
+
+    for (var a in type.accessors) {
+      if (a is! PropertyAccessorElement) continue;
+      // TODO(sigmund): support setters without getters.
+      if (!a.isGetter) continue;
+      if (a.displayName != name) continue;
+      var v = a.variable;
+      if (v is FieldElement && !v.isSynthetic) continue; // exclude fields
+      generator.addDeclaration(id, name, _typeFor(a.type.returnType.element),
+          isProperty: true,
+          isFinal: v.isFinal,
+          isStatic: a.isStatic,
+          annotations: _copyAnnotations(a));
+      if (includeAccessors && !v.isStatic) _addAccessors(name, !v.isFinal);
+      return true;
+    }
+
+    for (var m in type.methods) {
+      if (m.displayName != name) continue;
+      generator.addDeclaration(
+          id, name, new TypeIdentifier('dart:core', 'Function'),
+          isMethod: true,
+          isStatic: m.isStatic,
+          annotations: _copyAnnotations(m));
+      if (includeAccessors) {
+        if (m.isStatic) {
+          generator.addStaticMethod(id, name);
+          generator.addSymbol(name);
+        } else {
+          _addAccessors(name, false);
+        }
+      }
+      return true;
+    }
+
+    if (recursive) {
+      lookupParent(type);
+      var parent = type.supertype != null ? type.supertype.element : null;
+      if (parent == null || parent == includeUpTo) return false;
+      var parentId = _typeFor(parent);
+      for (var m in type.mixins) {
+        var mixinClass = m.element;
+        var mixinId = _mixins[parentId][mixinClass];
+        if (_lookupMemberInternal(
+            mixinClass, mixinId, name, false, includeAccessors, includeUpTo)) {
+          return true;
+        }
+        parentId = mixinId;
+      }
+      return _lookupMemberInternal(
+          parent, parentId, name, true, includeAccessors, includeUpTo);
+    }
+    return false;
+  }
+
+  /// Add information so smoke can invoke the static method [type].[name].
+  void addStaticMethod(ClassElement type, String name) {
+    generator.addStaticMethod(_typeFor(type), name);
+  }
+
+  /// Adds [name] as a symbol, a getter, and optionally a setter in [generator].
+  _addAccessors(String name, bool includeSetter) {
+    generator.addSymbol(name);
+    generator.addGetter(name);
+    if (includeSetter) generator.addSetter(name);
+  }
+
+  /// Copy metadata associated with the declaration of [target].
+  List<ConstExpression> _copyAnnotations(Element target) {
+    var node = target.node;
+    // [node] is the initialization expression, we walk up to get to the actual
+    // member declaration where the metadata is attached to.
+    while (node is! ClassMember) node = node.parent;
+    return node.metadata.map(_convertAnnotation).toList();
+  }
+
+  /// Converts annotations into [ConstExpression]s supported by the codegen
+  /// library.
+  ConstExpression _convertAnnotation(Annotation annotation) {
+    var element = annotation.element;
+    if (element is ConstructorElement) {
+      if (!element.name.isEmpty) {
+        throw new UnimplementedError(
+            'named constructors are not implemented in smoke.codegen.recorder');
+      }
+
+      var positionalArgs = [];
+      var namedArgs = {};
+      for (var arg in annotation.arguments.arguments) {
+        if (arg is NamedExpression) {
+          namedArgs[arg.name.label.name] = _convertExpression(arg.expression);
+        } else {
+          positionalArgs.add(_convertExpression(arg));
+        }
+      }
+
+      return new ConstructorExpression(importUrlFor(element.library),
+          element.enclosingElement.name, positionalArgs, namedArgs);
+    }
+
+    if (element is PropertyAccessorElement) {
+      return new TopLevelIdentifier(
+          importUrlFor(element.library), element.name);
+    }
+
+    throw new UnsupportedError('unsupported annotation $annotation');
+  }
+
+  /// Converts [expression] into a [ConstExpression].
+  ConstExpression _convertExpression(Expression expression) {
+    if (expression is StringLiteral) {
+      return new ConstExpression.string(expression.stringValue);
+    }
+
+    if (expression is BooleanLiteral ||
+        expression is DoubleLiteral ||
+        expression is IntegerLiteral ||
+        expression is NullLiteral) {
+      return new CodeAsConstExpression("${(expression as dynamic).value}");
+    }
+
+    if (expression is Identifier) {
+      var element = expression.bestElement;
+      if (element == null || !element.isPublic) {
+        throw new UnsupportedError('private constants are not supported');
+      }
+
+      var url = importUrlFor(element.library);
+      if (element is ClassElement) {
+        return new TopLevelIdentifier(url, element.name);
+      }
+
+      if (element is PropertyAccessorElement) {
+        var variable = element.variable;
+        if (variable is FieldElement) {
+          var cls = variable.enclosingElement;
+          return new TopLevelIdentifier(url, '${cls.name}.${variable.name}');
+        } else if (variable is TopLevelVariableElement) {
+          return new TopLevelIdentifier(url, variable.name);
+        }
+      }
+    }
+
+    throw new UnimplementedError('expression convertion not implemented in '
+        'smoke.codegen.recorder (${expression.runtimeType} $expression)');
+  }
+}
+
+/// Returns whether [metadata] contains any annotation that is either equal to
+/// an annotation in [queryAnnotations] or whose type is a subclass of a type
+/// listed in [queryAnnotations]. This is equivalent to the check done in
+/// `src/common.dart#matchesAnnotation`, except that this is applied to
+/// static metadata as it was provided by the analyzer.
+bool _matchesAnnotation(
+    Iterable<ElementAnnotation> metadata, Iterable<Element> queryAnnotations) {
+  for (var meta in metadata) {
+    var element = meta.element;
+    var exp;
+    var type;
+    if (element is PropertyAccessorElement) {
+      exp = element.variable;
+      type = exp.evaluationResult.value.type;
+    } else if (element is ConstructorElement) {
+      exp = element;
+      type = element.enclosingElement.type;
+    } else {
+      throw new UnimplementedError('Unsupported annotation: ${meta}');
+    }
+    for (var queryMeta in queryAnnotations) {
+      if (exp == queryMeta) return true;
+      if (queryMeta is ClassElement && type.isSubtypeOf(queryMeta.type)) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+/// Options equivalent to `smoke.dart#QueryOptions`, except that type
+/// information and annotations are denoted by resolver's elements.
+class QueryOptions {
+  /// Whether to include fields (default is true).
+  final bool includeFields;
+
+  /// Whether to include getters and setters (default is true). Note that to
+  /// include fields you also need to enable [includeFields].
+  final bool includeProperties;
+
+  /// Whether to include symbols from the given type and its superclasses
+  /// (except [Object]).
+  final bool includeInherited;
+
+  /// If [includeInherited], walk up the type hierarchy up to this type
+  /// (defaults to [Object]).
+  final ClassElement includeUpTo;
+
+  /// Whether to include final fields and getter-only properties.
+  final bool excludeFinal;
+
+  /// Whether to include methods (default is false).
+  final bool includeMethods;
+
+  /// If [withAnnotation] is not null, then it should be a list of types, so
+  /// only symbols that are annotated with instances of those types are
+  /// included.
+  final List<Element> withAnnotations;
+
+  /// If [matches] is not null, then only those fields, properties, or methods
+  /// that match will be included.
+  final NameMatcher matches;
+
+  const QueryOptions({this.includeFields: true, this.includeProperties: true,
+      this.includeInherited: true, this.includeUpTo: null,
+      this.excludeFinal: false, this.includeMethods: false,
+      this.withAnnotations: null, this.matches: null});
+}
+
+/// Predicate that tells whether [name] should be included in query results.
+typedef bool NameMatcher(String name);
diff --git a/smoke/lib/mirrors.dart b/smoke/lib/mirrors.dart
new file mode 100644
index 0000000..be72cb1
--- /dev/null
+++ b/smoke/lib/mirrors.dart
@@ -0,0 +1,318 @@
+// 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.
+
+/// Implementation of the smoke services using mirrors.
+library smoke.mirrors;
+
+import 'dart:mirrors';
+import 'package:smoke/smoke.dart';
+import 'package:logging/logging.dart';
+import 'src/common.dart';
+
+/// Set up the smoke package to use a mirror-based implementation. To tune what
+/// is preserved by `dart:mirrors`, use a @MirrorsUsed annotation and include
+/// 'smoke.mirrors' in your override arguments.
+useMirrors() {
+  configure(new ReflectiveObjectAccessorService(),
+      new ReflectiveTypeInspectorService(),
+      new ReflectiveSymbolConverterService());
+}
+
+var _logger = new Logger('smoke.mirrors');
+
+/// Implements [ObjectAccessorService] using mirrors.
+class ReflectiveObjectAccessorService implements ObjectAccessorService {
+  read(Object object, Symbol name) => reflect(object).getField(name).reflectee;
+
+  void write(Object object, Symbol name, value) {
+    reflect(object).setField(name, value);
+  }
+
+  invoke(receiver, Symbol methodName, List args,
+      {Map namedArgs, bool adjust: false}) {
+    var receiverMirror;
+    var method;
+    if (receiver is Type && methodName != #toString) {
+      receiverMirror = reflectType(receiver);
+      method = receiverMirror.declarations[methodName];
+    } else {
+      receiverMirror = reflect(receiver);
+      method = _findMethod(receiverMirror.type, methodName);
+    }
+    if (method != null && adjust) {
+      var required = 0;
+      var optional = 0;
+      for (var p in method.parameters) {
+        if (p.isOptional) {
+          if (!p.isNamed) optional++;
+        } else {
+          required++;
+        }
+      }
+      args = adjustList(args, required, required + optional);
+    }
+    return receiverMirror.invoke(methodName, args, namedArgs).reflectee;
+  }
+}
+
+/// Implements [TypeInspectorService] using mirrors.
+class ReflectiveTypeInspectorService implements TypeInspectorService {
+  bool isSubclassOf(Type type, Type supertype) {
+    if (type == supertype || supertype == Object) return true;
+    // TODO(sigmund): change to mirror.isSubclassOf when it gets implemented in
+    // dart2js. (dartbug.com/12439)
+    var mirror = reflectClass(type);
+    var top = reflectClass(supertype);
+    while (mirror != _objectType) {
+      mirror = _safeSuperclass(mirror);
+      if (mirror == top) return true;
+    }
+    return false;
+  }
+
+  bool hasGetter(Type type, Symbol name) {
+    var mirror = reflectType(type);
+    if (mirror is! ClassMirror) return false;
+    while (mirror != _objectType) {
+      final members = mirror.declarations;
+      if (members.containsKey(name)) return true;
+      mirror = _safeSuperclass(mirror);
+    }
+    return false;
+  }
+
+  bool hasSetter(Type type, Symbol name) {
+    var mirror = reflectType(type);
+    if (mirror is! ClassMirror) return false;
+    var setterName = _setterName(name);
+    while (mirror != _objectType) {
+      final members = mirror.declarations;
+      var declaration = members[name];
+      if (declaration is VariableMirror && !declaration.isFinal) return true;
+      if (members.containsKey(setterName)) return true;
+      mirror = _safeSuperclass(mirror);
+    }
+    return false;
+  }
+
+  bool hasInstanceMethod(Type type, Symbol name) {
+    var mirror = reflectType(type);
+    if (mirror is! ClassMirror) return false;
+    while (mirror != _objectType) {
+      final m = mirror.declarations[name];
+      if (m is MethodMirror && m.isRegularMethod && !m.isStatic) return true;
+      mirror = _safeSuperclass(mirror);
+    }
+    return false;
+  }
+
+  bool hasStaticMethod(Type type, Symbol name) {
+    var mirror = reflectType(type);
+    if (mirror is! ClassMirror) return false;
+    final m = mirror.declarations[name];
+    return m is MethodMirror && m.isRegularMethod && m.isStatic;
+  }
+
+  Declaration getDeclaration(Type type, Symbol name) {
+    var mirror = reflectType(type);
+    if (mirror is! ClassMirror) return null;
+
+    var declaration;
+    while (mirror != _objectType) {
+      final members = mirror.declarations;
+      if (members.containsKey(name)) {
+        declaration = members[name];
+        break;
+      }
+      mirror = _safeSuperclass(mirror);
+    }
+    if (declaration == null) {
+      _logger.severe("declaration doesn't exists ($type.$name).");
+      return null;
+    }
+    return new _MirrorDeclaration(mirror, declaration);
+  }
+
+  List<Declaration> query(Type type, QueryOptions options) {
+    var mirror = reflectType(type);
+    if (mirror is! ClassMirror) return null;
+    return _query(mirror, options);
+  }
+
+  List<Declaration> _query(ClassMirror cls, QueryOptions options) {
+    final visitParent = options.includeInherited && cls.superclass != null &&
+        // TODO(sigmund): use _toType(cls.superclass) != options.includeUpTo
+        // when dartbug.com/16925 gets fixed (_toType fails in dart2js if
+        // applied to classes with type-arguments).
+        cls.superclass != reflectClass(options.includeUpTo);
+    var result = visitParent ? _query(cls.superclass, options) : [];
+    for (var member in cls.declarations.values) {
+      if (member is! VariableMirror && member is! MethodMirror) continue;
+      if (member.isStatic || member.isPrivate) continue;
+      var name = member.simpleName;
+      if (member is VariableMirror) {
+        if (!options.includeFields) continue;
+        if (options.excludeFinal && member.isFinal) continue;
+      }
+
+      // TODO(sigmund): what if we have a setter but no getter?
+      if (member is MethodMirror && member.isSetter) continue;
+      if (member is MethodMirror && member.isConstructor) continue;
+
+      if (member is MethodMirror && member.isGetter) {
+        if (!options.includeProperties) continue;
+        if (options.excludeFinal && !_hasSetter(cls, member)) continue;
+      }
+
+      if (member is MethodMirror && member.isRegularMethod) {
+        if (!options.includeMethods) continue;
+      }
+
+      if (options.matches != null && !options.matches(name)) continue;
+
+      var annotations = member.metadata.map((m) => m.reflectee).toList();
+      if (options.withAnnotations != null &&
+          !matchesAnnotation(annotations, options.withAnnotations)) {
+        continue;
+      }
+
+      // TODO(sigmund): should we cache parts of this declaration so we don't
+      // compute them twice?  For example, this chould be `new Declaration(name,
+      // type, ...)` and we could reuse what we computed above to implement the
+      // query filtering.  Note, when I tried to eagerly compute everything, I
+      // run into trouble with type (`type = _toType(member.type)`), dart2js
+      // failed when the underlying types had type-arguments (see
+      // dartbug.com/16925).
+      result.add(new _MirrorDeclaration(cls, member));
+    }
+
+    return result;
+  }
+}
+
+/// Implements [SymbolConverterService] using mirrors.
+class ReflectiveSymbolConverterService implements SymbolConverterService {
+  String symbolToName(Symbol symbol) => MirrorSystem.getName(symbol);
+  Symbol nameToSymbol(String name) => new Symbol(name);
+}
+
+// TODO(jmesserly): workaround for:
+// https://code.google.com/p/dart/issues/detail?id=10029
+Symbol _setterName(Symbol getter) =>
+    new Symbol('${MirrorSystem.getName(getter)}=');
+
+ClassMirror _safeSuperclass(ClassMirror type) {
+  try {
+    var t = type.superclass;
+    // TODO(sigmund): workaround for darbug.com/17779.
+    // Interceptor is leaked by dart2js. It has the same methods as Object
+    // (including noSuchMethod), and our code above assumes that it doesn't
+    // exist. Most queries exclude Object, so they should exclude Interceptor
+    // too. We don't check for t.simpleName == #Interceptor because depending on
+    // dart2js optimizations it may be #Interceptor or #num/Interceptor.
+    // Checking for a private library seems to reliably filter this out.
+    if (t != null && t.owner != null && t.owner.isPrivate) {
+      t = _objectType;
+    }
+    return t;
+  } on UnsupportedError catch (e) {
+    // Note: dart2js throws UnsupportedError when the type is not reflectable.
+    return _objectType;
+  }
+}
+
+MethodMirror _findMethod(ClassMirror type, Symbol name) {
+  do {
+    var member = type.declarations[name];
+    if (member is MethodMirror) return member;
+    type = type.superclass;
+  } while (type != null);
+  return null;
+}
+
+// When recursively looking for symbols up the type-hierarchy it's generally a
+// good idea to stop at Object, since we know it doesn't have what we want.
+// TODO(jmesserly): This is also a workaround for what appears to be a V8
+// bug introduced between Chrome 31 and 32. After 32
+// JsClassMirror.declarations on Object calls
+// JsClassMirror.typeVariables, which tries to get the _jsConstructor's
+// .prototype["<>"]. This ends up getting the "" property instead, maybe
+// because "<>" doesn't exist, and gets ";" which then blows up because
+// the code later on expects a List of ints.
+final _objectType = reflectClass(Object);
+
+bool _hasSetter(ClassMirror cls, MethodMirror getter) {
+  var mirror = cls.declarations[_setterName(getter.simpleName)];
+  return mirror is MethodMirror && mirror.isSetter;
+}
+
+Type _toType(TypeMirror t) {
+  // TODO(sigmund): this line can go away after dartbug.com/16962
+  if (t == _objectType) return Object;
+  if (t is ClassMirror) return t.reflectedType;
+  if (t == null || t.qualifiedName != #dynamic) {
+    _logger.warning('unknown type ($t).');
+  }
+  return dynamic;
+}
+
+class _MirrorDeclaration implements Declaration {
+  final ClassMirror _cls;
+  final _original;
+
+  _MirrorDeclaration(this._cls, DeclarationMirror this._original);
+
+  Symbol get name => _original.simpleName;
+
+  DeclarationKind get kind => isField ? FIELD : isProperty ? PROPERTY : METHOD;
+
+  bool get isField => _original is VariableMirror;
+
+  bool get isProperty =>
+      _original is MethodMirror && !_original.isRegularMethod;
+
+  bool get isMethod => !isField && !isProperty;
+
+  /// If this is a property, whether it's read only (final fields or properties
+  /// with no setter).
+  bool get isFinal => (_original is VariableMirror && _original.isFinal) ||
+      (_original is MethodMirror &&
+          _original.isGetter &&
+          !_hasSetter(_cls, _original));
+
+  /// If this is a property, it's declared type (including dynamic if it's not
+  /// declared). For methods, the returned type.
+  Type get type {
+    if (_original is MethodMirror && _original.isRegularMethod) {
+      return Function;
+    }
+    var typeMirror =
+        _original is VariableMirror ? _original.type : _original.returnType;
+    return _toType(typeMirror);
+  }
+
+  /// Whether this symbol is static.
+  bool get isStatic => _original.isStatic;
+
+  /// List of annotations in this declaration.
+  List get annotations => _original.metadata.map((a) => a.reflectee).toList();
+
+  int get hashCode => name.hashCode;
+  operator ==(other) => other is Declaration &&
+      name == other.name &&
+      kind == other.kind &&
+      isFinal == other.isFinal &&
+      type == other.type &&
+      isStatic == other.isStatic &&
+      compareLists(annotations, other.annotations);
+  String toString() => (new StringBuffer()
+    ..write('(mirror-based-declaration ')
+    ..write(name)
+    ..write(
+        isField ? ' (field) ' : (isProperty ? ' (property) ' : ' (method) '))
+    ..write(isFinal ? 'final ' : '')
+    ..write(isStatic ? 'static ' : '')
+    ..write(annotations)
+    ..write(')')).toString();
+}
diff --git a/smoke/lib/smoke.dart b/smoke/lib/smoke.dart
new file mode 100644
index 0000000..749f73f
--- /dev/null
+++ b/smoke/lib/smoke.dart
@@ -0,0 +1,267 @@
+// 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.
+
+/// Collects services that can be used to access objects dynamically, inspect
+/// type information, and convert between symbols and strings.
+library smoke;
+
+import 'src/implementation.dart' as implementation;
+
+export 'src/common.dart' show minArgs, maxArgs, canAcceptNArgs, SUPPORTED_ARGS;
+import 'src/common.dart' show compareLists;
+
+/// Configures this library to use [objectAccessor] for all read/write/invoke
+/// APIs, [typeInspector] for all type query APIs, and [symbolConverter] for all
+/// symbol convertion operations.
+///
+/// This function doesn't need to be called during development, but frameworks
+/// should autogenerate a call to this function when running in deployment.
+void configure(ObjectAccessorService objectAccessor,
+    TypeInspectorService typeInspector,
+    SymbolConverterService symbolConverter) {
+  implementation.objectAccessor = objectAccessor;
+  implementation.typeInspector = typeInspector;
+  implementation.symbolConverter = symbolConverter;
+}
+
+/// Return the value of [field] in [object].
+read(Object object, Symbol field) =>
+    implementation.objectAccessor.read(object, field);
+
+/// Update the [value] of [field] in [object].
+void write(Object object, Symbol field, value) =>
+    implementation.objectAccessor.write(object, field, value);
+
+/// Invoke [method] in [receiver] with [args]. The [receiver] can be either an
+/// object (to invoke instance methods) or a type (to invoke static methods).
+/// This function optionally [adjust]s the list of arguments to match the number
+/// of formal parameters by either adding nulls for missing arguments, or by
+/// truncating the list.
+invoke(receiver, Symbol method, List args,
+    {Map namedArgs, bool adjust: false}) => implementation.objectAccessor
+    .invoke(receiver, method, args, namedArgs: namedArgs, adjust: adjust);
+
+/// Tells whether [type] is transitively a subclass of [supertype].
+bool isSubclassOf(Type type, Type supertype) =>
+    implementation.typeInspector.isSubclassOf(type, supertype);
+
+// TODO(sigmund): consider adding also:
+// * isImplementationOf(type, subtype) to tells whether [type] declares that it
+//   implements the [supertype] interface.
+// * isSubtypeOf(type, subtype): Tells whether [type]'s interface is a sybtype
+//   of [supertype]. That is, whether it is a subclass or if [type] implements
+//   [supertype].
+
+/// Tells whether [type] has a field or getter for [field].
+bool hasGetter(Type type, Symbol field) =>
+    implementation.typeInspector.hasGetter(type, field);
+
+/// Tells whether [type] has a field or setter for [field].
+bool hasSetter(Type type, Symbol field) =>
+    implementation.typeInspector.hasSetter(type, field);
+
+/// Tells whether [type] or a superclass (other than [Object]) defines
+/// `noSuchMethod`.
+bool hasNoSuchMethod(Type type) => hasInstanceMethod(type, #noSuchMethod);
+
+/// Tells whether [type] has or a superclass contains a specific instance
+/// [method] (excluding methods in [Object]).
+bool hasInstanceMethod(Type type, Symbol method) =>
+    implementation.typeInspector.hasInstanceMethod(type, method);
+
+/// Tells whether [type] has a specific static [method].
+bool hasStaticMethod(Type type, Symbol method) =>
+    implementation.typeInspector.hasStaticMethod(type, method);
+
+/// Get the declaration associated with field [name] found in [type] or a
+/// superclass of [type].
+Declaration getDeclaration(Type type, Symbol name) =>
+    implementation.typeInspector.getDeclaration(type, name);
+
+/// Retrieve all symbols of [type] that match [options].
+List<Declaration> query(Type type, QueryOptions options) =>
+    implementation.typeInspector.query(type, options);
+
+/// Returns the name associated with a [symbol].
+String symbolToName(Symbol symbol) =>
+    implementation.symbolConverter.symbolToName(symbol);
+
+/// Returns the symbol associated with a [name].
+Symbol nameToSymbol(String name) =>
+    implementation.symbolConverter.nameToSymbol(name);
+
+/// Establishes the parameters for [query] to search for symbols in a type
+/// hierarchy. For now only public instance symbols can be queried (no private,
+/// no static).
+class QueryOptions {
+  /// Whether to include fields (default is true).
+  final bool includeFields;
+
+  /// Whether to include getters and setters (default is true). Note that to
+  /// include fields you also need to enable [includeFields].
+  final bool includeProperties;
+
+  /// Whether to include symbols from the given type and its superclasses
+  /// (except [Object]).
+  final bool includeInherited;
+
+  /// If [includeInherited], walk up the type hierarchy up to this type
+  /// (defaults to [Object]).
+  final Type includeUpTo;
+
+  /// Whether to include final fields and getter-only properties.
+  final bool excludeFinal;
+
+  /// Whether to include methods (default is false).
+  final bool includeMethods;
+
+  /// If [withAnnotation] is not null, then it should be a list of types, so
+  /// only symbols that are annotated with instances of those types are
+  /// included.
+  final List withAnnotations;
+
+  /// If [matches] is not null, then include only those fields, properties, or
+  /// methods that match the predicate.
+  final NameMatcher matches;
+
+  const QueryOptions({this.includeFields: true, this.includeProperties: true,
+      this.includeInherited: true, this.includeUpTo: Object,
+      this.excludeFinal: false, this.includeMethods: false,
+      this.withAnnotations: null, this.matches: null});
+
+  String toString() => (new StringBuffer()
+    ..write('(options:')
+    ..write(includeFields ? 'fields ' : '')
+    ..write(includeProperties ? 'properties ' : '')
+    ..write(includeMethods ? 'methods ' : '')
+    ..write(includeInherited ? 'inherited ' : '_')
+    ..write(excludeFinal ? 'no finals ' : '')
+    ..write('annotations: $withAnnotations')
+    ..write(matches != null ? 'with matcher' : '')
+    ..write(')')).toString();
+}
+
+/// Used to filter query results based on a predicate on [name]. Returns true if
+/// [name] should be included in the query results.
+typedef bool NameMatcher(Symbol name);
+
+/// Information associated with a symbol declaration (like a property or
+/// method).
+class Declaration {
+  /// Name of the property or method
+  final Symbol name;
+
+  /// Kind of declaration (field, property, or method).
+  final DeclarationKind kind;
+
+  /// Whether the symbol is a field (and not a getter/setter property).
+  bool get isField => kind == FIELD;
+
+  /// Whether the symbol is a getter/setter and not a field.
+  bool get isProperty => kind == PROPERTY;
+
+  /// Whether the symbol is a method.
+  bool get isMethod => kind == METHOD;
+
+  /// For fields, whether they are final, for properties, whether they are
+  /// read-only (they have no setter).
+  final bool isFinal;
+
+  /// If this is a field or property, it's declared type (including dynamic if
+  /// it's not declared). For methods, the returned type.
+  final Type type;
+
+  /// Whether this symbol is static.
+  final bool isStatic;
+
+  /// List of annotations in this declaration.
+  final List annotations;
+
+  const Declaration(this.name, this.type, {this.kind: FIELD,
+      this.isFinal: false, this.isStatic: false, this.annotations: const []});
+
+  int get hashCode => name.hashCode;
+  operator ==(other) => other is Declaration &&
+      name == other.name &&
+      kind == other.kind &&
+      isFinal == other.isFinal &&
+      type == other.type &&
+      isStatic == other.isStatic &&
+      compareLists(annotations, other.annotations);
+
+  String toString() {
+    return (new StringBuffer()
+      ..write('(declaration ')
+      ..write(name)
+      ..write(isProperty ? ' (property) ' : ' (method) ')
+      ..write(isFinal ? 'final ' : '')
+      ..write(isStatic ? 'static ' : '')
+      ..write(annotations)
+      ..write(')')).toString();
+  }
+}
+
+/// Enumeration for declaration kinds (field, property, or method)
+class DeclarationKind {
+  final int kind;
+  const DeclarationKind(this.kind);
+}
+
+/// Declaration kind used to denote a raw field.
+const FIELD = const DeclarationKind(0);
+
+/// Declaration kind used to denote a getter/setter.
+const PROPERTY = const DeclarationKind(1);
+
+/// Declaration kind used to denote a method.
+const METHOD = const DeclarationKind(2);
+
+/// A service that provides a way to implement simple operations on objects like
+/// read, write, and invoke.
+abstract class ObjectAccessorService {
+  /// Return the value of [field] in [object].
+  read(Object object, Symbol field);
+
+  /// Update the [value] of [field] in [object].
+  void write(Object object, Symbol field, value);
+
+  /// Invoke [method] in [object] with [args]. It optionally [adjust]s the list
+  /// of arguments to match the number of formal parameters by either adding
+  /// nulls for missing arguments, or by truncating the list.
+  invoke(Object object, Symbol method, List args,
+      {Map namedArgs, bool adjust: false});
+}
+
+/// A service that provides partial inspection into Dart types.
+abstract class TypeInspectorService {
+  /// Tells whether [type] is transitively a subclass of [supertype].
+  bool isSubclassOf(Type type, Type supertype);
+
+  /// Tells whether [type] has a field or getter for [name].
+  bool hasGetter(Type type, Symbol name);
+
+  /// Tells whether [type] has a field or setter for [name].
+  bool hasSetter(Type type, Symbol name);
+
+  /// Tells whether [type] has a specific instance [method].
+  bool hasInstanceMethod(Type type, Symbol method);
+
+  /// Tells whether [type] has a specific static [method].
+  bool hasStaticMethod(Type type, Symbol method);
+
+  /// Get the declaration associated with field [name] in [type].
+  Declaration getDeclaration(Type type, Symbol name);
+
+  /// Retrieve all symbols of [type] that match [options].
+  List<Declaration> query(Type type, QueryOptions options);
+}
+
+/// A service that converts between [Symbol]s and [String]s.
+abstract class SymbolConverterService {
+  /// Returns the name associated with a [symbol].
+  String symbolToName(Symbol symbol);
+
+  /// Returns the symbol associated with a [name].
+  Symbol nameToSymbol(String name);
+}
diff --git a/smoke/lib/src/common.dart b/smoke/lib/src/common.dart
new file mode 100644
index 0000000..5cec74c
--- /dev/null
+++ b/smoke/lib/src/common.dart
@@ -0,0 +1,210 @@
+// 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.
+
+/// Some common utilities used by other libraries in this package.
+library smoke.src.common;
+
+import 'package:smoke/smoke.dart' as smoke show isSubclassOf;
+
+/// Returns [input] adjusted to be within [min] and [max] length. Truncating it
+/// if it's longer, or padding it with nulls if it's shorter. The returned list
+/// is a new copy if any modification is needed, otherwise [input] is returned.
+List adjustList(List input, int min, int max) {
+  if (input.length < min) {
+    return new List(min)..setRange(0, input.length, input);
+  }
+
+  if (input.length > max) {
+    return new List(max)..setRange(0, max, input);
+  }
+  return input;
+}
+
+/// Returns whether [metadata] contains any annotation that is either equal to
+/// an annotation in [queryAnnotations] or whose type is listed in
+/// [queryAnnotations].
+bool matchesAnnotation(Iterable metadata, Iterable queryAnnotations) {
+  for (var meta in metadata) {
+    for (var queryMeta in queryAnnotations) {
+      if (meta == queryMeta) return true;
+      if (queryMeta is Type &&
+          smoke.isSubclassOf(meta.runtimeType, queryMeta)) return true;
+    }
+  }
+  return false;
+}
+
+/// Number of arguments supported by [minArgs] and [maxArgs].
+const SUPPORTED_ARGS = 15;
+
+typedef _Func0();
+typedef _Func1(a);
+typedef _Func2(a, b);
+typedef _Func3(a, b, c);
+typedef _Func4(a, b, c, d);
+typedef _Func5(a, b, c, d, e);
+typedef _Func6(a, b, c, d, e, f);
+typedef _Func7(a, b, c, d, e, f, g);
+typedef _Func8(a, b, c, d, e, f, g, h);
+typedef _Func9(a, b, c, d, e, f, g, h, i);
+typedef _Func10(a, b, c, d, e, f, g, h, i, j);
+typedef _Func11(a, b, c, d, e, f, g, h, i, j, k);
+typedef _Func12(a, b, c, d, e, f, g, h, i, j, k, l);
+typedef _Func13(a, b, c, d, e, f, g, h, i, j, k, l, m);
+typedef _Func14(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
+typedef _Func15(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
+
+/// Returns the minimum number of arguments that [f] takes as input, in other
+/// words, the total number of required arguments of [f]. If [f] expects more
+/// than [SUPPORTED_ARGS], this function returns `SUPPORTED_ARGS + 1`.
+///
+/// For instance, the current implementation only supports calculating the
+/// number of arguments between `0` and `3`. If the function takes `4` or more,
+/// this function automatically returns `4`.
+int minArgs(Function f) {
+  if (f is _Func0) return 0;
+  if (f is _Func1) return 1;
+  if (f is _Func2) return 2;
+  if (f is _Func3) return 3;
+  if (f is _Func4) return 4;
+  if (f is _Func5) return 5;
+  if (f is _Func6) return 6;
+  if (f is _Func7) return 7;
+  if (f is _Func8) return 8;
+  if (f is _Func9) return 9;
+  if (f is _Func10) return 10;
+  if (f is _Func11) return 11;
+  if (f is _Func12) return 12;
+  if (f is _Func13) return 13;
+  if (f is _Func14) return 14;
+  if (f is _Func15) return 15;
+  return SUPPORTED_ARGS + 1;
+}
+
+/// Returns the maximum number of arguments that [f] takes as input, which is
+/// the total number of required and optional arguments of [f]. If
+/// [f] may take more than [SUPPORTED_ARGS] required arguments, this function
+/// returns `-1`. However, if it takes less required arguments, but more than
+/// [SUPPORTED_ARGS] arguments including optional arguments, the result will be
+/// [SUPPORTED_ARGS].
+///
+/// For instance, the current implementation only supports calculating the
+/// number of arguments between `0` and [SUPPORTED_ARGS].  If the function
+/// takes more than [SUPPORTED_ARGS] mandatory arguments, this function
+/// returns `-1`, but if the funtion takes
+/// `8` mandatory arguments and `10` optional arguments, this function returns
+/// [SUPPORTED_ARGS].
+int maxArgs(Function f) {
+  // We could perform a full modified binary search but we really only care
+  // about performance for functions with fewer than 4 arguments.
+  if (f is! _Func2) {
+    if (f is _Func1) return 1;
+    if (f is _Func0) return 0;
+    if (f is! _Func4 && f is _Func3) return 3;
+    // Fall through to the slow case as the function has has maxArgs > 3.
+  } else if (f is! _Func4) {
+    return f is _Func3 ? 3 : 2;
+  }
+
+  if (f is _Func15) return 15;
+  if (f is _Func14) return 14;
+  if (f is _Func13) return 13;
+  if (f is _Func12) return 12;
+  if (f is _Func11) return 11;
+  if (f is _Func10) return 10;
+  if (f is _Func9) return 9;
+  if (f is _Func8) return 8;
+  if (f is _Func7) return 7;
+  if (f is _Func6) return 6;
+  if (f is _Func5) return 5;
+  if (f is _Func4) return 4;
+  if (f is _Func3) return 3;
+  if (f is _Func2) return 2;
+  if (f is _Func1) return 1;
+  if (f is _Func0) return 0;
+  return -1;
+}
+
+/// Returns whether [f] can accept [n] arguments.
+/// This is equivalent to
+/// `n >= minArgs(f) && n <= maxArgs(f)`
+/// when [f] accepts at most [SUPPORTED_ARGS].
+bool canAcceptNArgs(Function f, int n) {
+  switch (n) {
+    case 0:
+      return f is _Func0;
+    case 1:
+      return f is _Func1;
+    case 2:
+      return f is _Func2;
+    case 3:
+      return f is _Func3;
+    case 4:
+      return f is _Func4;
+    case 5:
+      return f is _Func5;
+    case 6:
+      return f is _Func6;
+    case 7:
+      return f is _Func7;
+    case 8:
+      return f is _Func8;
+    case 9:
+      return f is _Func9;
+    case 10:
+      return f is _Func10;
+    case 11:
+      return f is _Func11;
+    case 12:
+      return f is _Func12;
+    case 13:
+      return f is _Func13;
+    case 14:
+      return f is _Func14;
+    case 15:
+      return f is _Func15;
+  }
+  return false;
+}
+
+/// Shallow comparison of two lists.
+bool compareLists(List a, List b, {bool unordered: false}) {
+  if (a == null && b != null) return false;
+  if (a != null && b == null) return false;
+  if (a.length != b.length) return false;
+  if (unordered) {
+    var countMap = {};
+    for (var x in b) {
+      var count = countMap[x];
+      if (count == null) count = 0;
+      countMap[x] = count + 1;
+    }
+    for (var x in a) {
+      var count = countMap[x];
+      if (count == null) return false;
+      if (count == 1) {
+        countMap.remove(x);
+      } else {
+        countMap[x] = count - 1;
+      }
+    }
+    return countMap.isEmpty;
+  } else {
+    for (int i = 0; i < a.length; i++) {
+      if (a[i] != b[i]) return false;
+    }
+  }
+  return true;
+}
+
+/// Shallow comparison of two maps.
+bool compareMaps(Map a, Map b) {
+  if (a == null && b != null) return false;
+  if (a != null && b == null) return false;
+  if (a.length != b.length) return false;
+  for (var k in a.keys) {
+    if (!b.containsKey(k) || a[k] != b[k]) return false;
+  }
+  return true;
+}
diff --git a/smoke/lib/src/default_transformer.dart b/smoke/lib/src/default_transformer.dart
new file mode 100644
index 0000000..5c634a2
--- /dev/null
+++ b/smoke/lib/src/default_transformer.dart
@@ -0,0 +1,43 @@
+// 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.
+
+/// Transformer that replaces the default mirror-based implementation of smoke,
+/// so that during deploy smoke doesn't include any dependency on dart:mirrors.
+library smoke.src.default_transformer;
+
+import 'dart:async';
+import 'package:barback/barback.dart';
+
+/// Replaces the default mirror-based implementation of smoke in
+/// `pacakge:smoke/implementation.dart`, so that during deploy smoke doesn't
+/// include any dependency on dart:mirrors.
+// TODO(sigmund): include tests that run this transformation automatically.
+class DefaultTransformer extends Transformer {
+  DefaultTransformer.asPlugin();
+
+  /// Only apply to `lib/src/implementation.dart`.
+  // TODO(nweiz): This should just take an AssetId when barback <0.13.0 support
+  // is dropped.
+  Future<bool> isPrimary(idOrAsset) {
+    var id = idOrAsset is AssetId ? idOrAsset : idOrAsset.id;
+    return new Future.value(
+        id.package == 'smoke' && id.path == 'lib/src/implementation.dart');
+  }
+
+  Future apply(Transform transform) {
+    var id = transform.primaryInput.id;
+    return transform.primaryInput.readAsString().then((code) {
+      // Note: this rewrite is highly-coupled with how implementation.dart is
+      // written. Make sure both are updated in sync.
+      transform.addOutput(new Asset.fromString(id, code
+          .replaceAll(
+              new RegExp('new Reflective[^;]*;'), 'throwNotConfiguredError();')
+          .replaceAll("import 'package:smoke/mirrors.dart';", '')));
+    });
+  }
+}
+
+/** Transformer phases which should be applied to the smoke package. */
+List<List<Transformer>> get phasesForSmoke =>
+    [[new DefaultTransformer.asPlugin()]];
diff --git a/smoke/lib/src/implementation.dart b/smoke/lib/src/implementation.dart
new file mode 100644
index 0000000..51d8b9c
--- /dev/null
+++ b/smoke/lib/src/implementation.dart
@@ -0,0 +1,34 @@
+// 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.
+
+/// A library that is used to select the default implementation of smoke. During
+/// development we use a default mirror-based implementation, for deployment we
+/// let the main program set programatically what implementation to use (likely
+/// one based on static code generation).
+library smoke.src.implementation;
+
+// IMPORTANT NOTE: This file is edited by a transformer in this package
+// (default_transformer.dart), so any edits here should be coordinated with
+// changes there.
+
+import 'package:smoke/mirrors.dart';
+import 'package:smoke/smoke.dart';
+
+/// Implementation of [ObjectAccessorService] in use, initialized lazily so it
+/// can be replaced at deployment time with an efficient alternative.
+ObjectAccessorService objectAccessor = new ReflectiveObjectAccessorService();
+
+/// Implementation of [TypeInspectorService] in use, initialized lazily so it
+/// can be replaced at deployment time with an efficient alternative.
+TypeInspectorService typeInspector = new ReflectiveTypeInspectorService();
+
+/// Implementation of [SymbolConverterService] in use, initialized lazily so it
+/// can be replaced at deployment time with an efficient alternative.
+SymbolConverterService symbolConverter = new ReflectiveSymbolConverterService();
+
+throwNotConfiguredError() {
+  throw new Exception('The "smoke" library has not been configured. '
+      'Make sure you import and configure one of the implementations ('
+      'package:smoke/mirrors.dart or package:smoke/static.dart).');
+}
diff --git a/smoke/lib/static.dart b/smoke/lib/static.dart
new file mode 100644
index 0000000..d31f369
--- /dev/null
+++ b/smoke/lib/static.dart
@@ -0,0 +1,289 @@
+// 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.
+
+/// Static implementation of smoke services using code-generated data.
+library smoke.static;
+
+import 'dart:math' as math;
+
+import 'package:smoke/smoke.dart';
+
+import 'src/common.dart';
+
+typedef T Getter<T>(object);
+typedef void Setter<T>(object, value);
+
+class StaticConfiguration {
+  /// Maps symbol to a function that reads that symbol of an object. For
+  /// instance, `#i: (o) => o.i`.
+  final Map<Symbol, Getter> getters;
+
+  /// Maps symbol to a function that updates that symbol of an object. For
+  /// instance, `#i: (o, v) { o.i = v; }`.
+  final Map<Symbol, Setter> setters;
+
+  /// Maps a type to its super class. For example, String: Object.
+  final Map<Type, Type> parents;
+
+  /// For each type, a map of declarations per symbol (property or method).
+  final Map<Type, Map<Symbol, Declaration>> declarations;
+
+  /// Static methods for each type.
+  // TODO(sigmund): should we add static getters & setters too?
+  final Map<Type, Map<Symbol, Function>> staticMethods;
+
+  /// A map from symbol to strings.
+  final Map<Symbol, String> names;
+
+  /// A map from strings to symbols (the reverse of [names]).
+  final Map<String, Symbol> _symbols = {};
+
+  /// Whether to check for missing declarations, otherwise, return default
+  /// values (for example a missing parent class can be treated as Object)
+  final bool checkedMode;
+
+  StaticConfiguration({Map<Symbol, Getter> getters, Map<Symbol, Setter> setters,
+      Map<Type, Type> parents, Map<Type, Map<Symbol, Declaration>> declarations,
+      Map<Type, Map<Symbol, Function>> staticMethods, Map<Symbol, String> names,
+      this.checkedMode: true})
+      : getters = getters != null ? getters : {},
+        setters = setters != null ? setters : {},
+        parents = parents != null ? parents : {},
+        declarations = declarations != null ? declarations : {},
+        staticMethods = staticMethods != null ? staticMethods : {},
+        names = names != null ? names : {} {
+    this.names.forEach((k, v) {
+      _symbols[v] = k;
+    });
+  }
+
+  void addAll(StaticConfiguration other) {
+    getters.addAll(other.getters);
+    setters.addAll(other.setters);
+    parents.addAll(other.parents);
+    _nestedAddAll(declarations, other.declarations);
+    _nestedAddAll(staticMethods, other.staticMethods);
+    names.addAll(other.names);
+    other.names.forEach((k, v) {
+      _symbols[v] = k;
+    });
+  }
+
+  static _nestedAddAll(Map a, Map b) {
+    for (var key in b.keys) {
+      a.putIfAbsent(key, () => {});
+      a[key].addAll(b[key]);
+    }
+  }
+}
+
+/// Set up the smoke package to use a static implementation based on the given
+/// [configuration].
+useGeneratedCode(StaticConfiguration configuration) {
+  configure(new GeneratedObjectAccessorService(configuration),
+      new GeneratedTypeInspectorService(configuration),
+      new GeneratedSymbolConverterService(configuration));
+}
+
+/// Implements [ObjectAccessorService] using a static configuration.
+class GeneratedObjectAccessorService implements ObjectAccessorService {
+  final StaticConfiguration _configuration;
+  Map<Symbol, Getter> get _getters => _configuration.getters;
+  Map<Symbol, Setter> get _setters => _configuration.setters;
+  Map<Type, Map<Symbol, Function>> get _staticMethods =>
+      _configuration.staticMethods;
+
+  GeneratedObjectAccessorService(this._configuration);
+
+  read(Object object, Symbol name) {
+    var getter = _getters[name];
+    if (getter == null) {
+      throw new MissingCodeException('getter "$name" in $object');
+    }
+    return getter(object);
+  }
+
+  void write(Object object, Symbol name, value) {
+    var setter = _setters[name];
+    if (setter == null) {
+      throw new MissingCodeException('setter "$name" in $object');
+    }
+    setter(object, value);
+  }
+
+  invoke(object, Symbol name, List args, {Map namedArgs, bool adjust: false}) {
+    var method;
+    if (object is Type && name != #toString) {
+      var classMethods = _staticMethods[object];
+      method = classMethods == null ? null : classMethods[name];
+    } else {
+      var getter = _getters[name];
+      method = getter == null ? null : getter(object);
+    }
+    if (method == null) {
+      throw new MissingCodeException('method "$name" in $object');
+    }
+    var tentativeError;
+    if (adjust) {
+      var min = minArgs(method);
+      if (min > SUPPORTED_ARGS) {
+        tentativeError = 'we tried to adjust the arguments for calling "$name"'
+            ', but we couldn\'t determine the exact number of arguments it '
+            'expects (it is more than $SUPPORTED_ARGS).';
+        // The argument list might be correct, so we still invoke the function
+        // and let the user see the error.
+        args = adjustList(args, min, math.max(min, args.length));
+      } else {
+        var max = maxArgs(method);
+        args = adjustList(args, min, max >= 0 ? max : args.length);
+      }
+    }
+    if (namedArgs != null) {
+      throw new UnsupportedError(
+          'smoke.static doesn\'t support namedArguments in invoke');
+    }
+    try {
+      return Function.apply(method, args);
+    } on NoSuchMethodError catch (e) {
+      // TODO(sigmund): consider whether this should just be in a logger or if
+      // we should wrap `e` as a new exception (what's the best way to let users
+      // know about this tentativeError?)
+      if (tentativeError != null) print(tentativeError);
+      rethrow;
+    }
+  }
+}
+
+/// Implements [TypeInspectorService] using a static configuration.
+class GeneratedTypeInspectorService implements TypeInspectorService {
+  final StaticConfiguration _configuration;
+
+  Map<Type, Type> get _parents => _configuration.parents;
+  Map<Type, Map<Symbol, Declaration>> get _declarations =>
+      _configuration.declarations;
+  bool get _checkedMode => _configuration.checkedMode;
+
+  GeneratedTypeInspectorService(this._configuration);
+
+  bool isSubclassOf(Type type, Type supertype) {
+    if (type == supertype || supertype == Object) return true;
+    while (type != Object) {
+      var parentType = _parents[type];
+      if (parentType == supertype) return true;
+      if (parentType == null) {
+        if (!_checkedMode) return false;
+        throw new MissingCodeException('superclass of "$type" ($parentType)');
+      }
+      type = parentType;
+    }
+    return false;
+  }
+
+  bool hasGetter(Type type, Symbol name) {
+    var decl = _findDeclaration(type, name);
+    // No need to check decl.isProperty because methods are also automatically
+    // considered getters (auto-closures).
+    return decl != null && !decl.isStatic;
+  }
+
+  bool hasSetter(Type type, Symbol name) {
+    var decl = _findDeclaration(type, name);
+    return decl != null && !decl.isMethod && !decl.isFinal && !decl.isStatic;
+  }
+
+  bool hasInstanceMethod(Type type, Symbol name) {
+    var decl = _findDeclaration(type, name);
+    return decl != null && decl.isMethod && !decl.isStatic;
+  }
+
+  bool hasStaticMethod(Type type, Symbol name) {
+    final map = _declarations[type];
+    if (map == null) {
+      if (!_checkedMode) return false;
+      throw new MissingCodeException('declarations for $type');
+    }
+    final decl = map[name];
+    return decl != null && decl.isMethod && decl.isStatic;
+  }
+
+  Declaration getDeclaration(Type type, Symbol name) {
+    var decl = _findDeclaration(type, name);
+    if (decl == null) {
+      if (!_checkedMode) return null;
+      throw new MissingCodeException('declaration for $type.$name');
+    }
+    return decl;
+  }
+
+  List<Declaration> query(Type type, QueryOptions options) {
+    var result = [];
+    if (options.includeInherited) {
+      var superclass = _parents[type];
+      if (superclass == null) {
+        if (_checkedMode) {
+          throw new MissingCodeException('superclass of "$type"');
+        }
+      } else if (superclass != options.includeUpTo) {
+        result = query(superclass, options);
+      }
+    }
+    var map = _declarations[type];
+    if (map == null) {
+      if (!_checkedMode) return result;
+      throw new MissingCodeException('declarations for $type');
+    }
+    for (var decl in map.values) {
+      if (!options.includeFields && decl.isField) continue;
+      if (!options.includeProperties && decl.isProperty) continue;
+      if (options.excludeFinal && decl.isFinal) continue;
+      if (!options.includeMethods && decl.isMethod) continue;
+      if (options.matches != null && !options.matches(decl.name)) continue;
+      if (options.withAnnotations != null &&
+          !matchesAnnotation(decl.annotations, options.withAnnotations)) {
+        continue;
+      }
+      result.add(decl);
+    }
+    return result;
+  }
+
+  Declaration _findDeclaration(Type type, Symbol name) {
+    while (type != Object) {
+      final declarations = _declarations[type];
+      if (declarations != null) {
+        final declaration = declarations[name];
+        if (declaration != null) return declaration;
+      }
+      var parentType = _parents[type];
+      if (parentType == null) {
+        if (!_checkedMode) return null;
+        throw new MissingCodeException('superclass of "$type"');
+      }
+      type = parentType;
+    }
+    return null;
+  }
+}
+
+/// Implements [SymbolConverterService] using a static configuration.
+class GeneratedSymbolConverterService implements SymbolConverterService {
+  final StaticConfiguration _configuration;
+  Map<Symbol, String> get _names => _configuration.names;
+  Map<String, Symbol> get _symbols => _configuration._symbols;
+
+  GeneratedSymbolConverterService(this._configuration);
+
+  String symbolToName(Symbol symbol) => _names[symbol];
+  Symbol nameToSymbol(String name) => _symbols[name];
+}
+
+/// Exception thrown when trynig to access something that should be there, but
+/// the code generator didn't include it.
+class MissingCodeException implements Exception {
+  final String description;
+  MissingCodeException(this.description);
+
+  String toString() => 'Missing $description. '
+      'Code generation for the smoke package seems incomplete.';
+}
diff --git a/smoke/lib/static_debug.dart b/smoke/lib/static_debug.dart
new file mode 100644
index 0000000..a51b13e
--- /dev/null
+++ b/smoke/lib/static_debug.dart
@@ -0,0 +1,120 @@
+// 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.
+
+/// Static implementation of smoke services that uses code-generated data and
+/// verifies that the results match what we would get with a mirror-based
+/// implementation.
+library smoke.static_debug;
+
+export 'package:smoke/static.dart' show StaticConfiguration, Getter, Setter;
+import 'package:smoke/static.dart';
+import 'package:smoke/mirrors.dart';
+import 'package:smoke/smoke.dart';
+
+import 'src/common.dart' show compareLists;
+
+/// Set up the smoke package to use a static implementation based on the given
+/// [configuration].
+useGeneratedCode(StaticConfiguration configuration) {
+  configure(new _DebugObjectAccessorService(configuration),
+      new _DebugTypeInspectorService(configuration),
+      new _DebugSymbolConverterService(configuration));
+}
+
+/// Implements [ObjectAccessorService] using a static configuration.
+class _DebugObjectAccessorService implements ObjectAccessorService {
+  GeneratedObjectAccessorService _static;
+  ReflectiveObjectAccessorService _mirrors;
+
+  _DebugObjectAccessorService(StaticConfiguration configuration)
+      : _static = new GeneratedObjectAccessorService(configuration),
+        _mirrors = new ReflectiveObjectAccessorService();
+
+  read(Object object, Symbol name) => _check('read', [
+    object,
+    name
+  ], _static.read(object, name), _mirrors.read(object, name));
+
+  // Note: we can't verify operations with side-effects like write or invoke.
+  void write(Object object, Symbol name, value) =>
+      _static.write(object, name, value);
+
+  invoke(object, Symbol name, List args, {Map namedArgs, bool adjust: false}) =>
+      _static.invoke(object, name, args, namedArgs: namedArgs, adjust: adjust);
+}
+
+/// Implements [TypeInspectorService] using a static configuration.
+class _DebugTypeInspectorService implements TypeInspectorService {
+  GeneratedTypeInspectorService _static;
+  ReflectiveTypeInspectorService _mirrors;
+
+  _DebugTypeInspectorService(StaticConfiguration configuration)
+      : _static = new GeneratedTypeInspectorService(configuration),
+        _mirrors = new ReflectiveTypeInspectorService();
+
+  bool isSubclassOf(Type type, Type supertype) => _check('isSubclassOf', [
+    type,
+    supertype
+  ], _static.isSubclassOf(type, supertype),
+      _mirrors.isSubclassOf(type, supertype));
+
+  bool hasGetter(Type type, Symbol name) => _check('hasGetter', [
+    type,
+    name
+  ], _static.hasGetter(type, name), _mirrors.hasGetter(type, name));
+
+  bool hasSetter(Type type, Symbol name) => _check('hasSetter', [
+    type,
+    name
+  ], _static.hasSetter(type, name), _mirrors.hasSetter(type, name));
+
+  bool hasInstanceMethod(Type type, Symbol name) => _check('hasInstanceMethod',
+      [type, name], _static.hasInstanceMethod(type, name),
+      _mirrors.hasInstanceMethod(type, name));
+
+  bool hasStaticMethod(Type type, Symbol name) => _check('hasStaticMethod', [
+    type,
+    name
+  ], _static.hasStaticMethod(type, name), _mirrors.hasStaticMethod(type, name));
+
+  Declaration getDeclaration(Type type, Symbol name) => _check('getDeclaration',
+      [
+    type,
+    name
+  ], _static.getDeclaration(type, name), _mirrors.getDeclaration(type, name));
+
+  List<Declaration> query(Type type, QueryOptions options) => _check('query', [
+    type,
+    options
+  ], _static.query(type, options), _mirrors.query(type, options));
+}
+
+/// Implements [SymbolConverterService] using a static configuration.
+class _DebugSymbolConverterService implements SymbolConverterService {
+  GeneratedSymbolConverterService _static;
+  ReflectiveSymbolConverterService _mirrors;
+
+  _DebugSymbolConverterService(StaticConfiguration configuration)
+      : _static = new GeneratedSymbolConverterService(configuration),
+        _mirrors = new ReflectiveSymbolConverterService();
+
+  String symbolToName(Symbol symbol) => _check('symbolToName', [symbol],
+      _static.symbolToName(symbol), _mirrors.symbolToName(symbol));
+
+  Symbol nameToSymbol(String name) => _check('nameToSymbol', [name],
+      _static.nameToSymbol(name), _mirrors.nameToSymbol(name));
+}
+
+_check(String operation, List arguments, staticResult, mirrorResult) {
+  if (staticResult == mirrorResult) return staticResult;
+  if (staticResult is List &&
+      mirrorResult is List &&
+      compareLists(staticResult, mirrorResult, unordered: true)) {
+    return staticResult;
+  }
+  print('warning: inconsistent result on $operation(${arguments.join(', ')})\n'
+      'smoke.mirrors result: $mirrorResult\n'
+      'smoke.static result:  $staticResult\n');
+  return staticResult;
+}
diff --git a/smoke/pubspec.yaml b/smoke/pubspec.yaml
new file mode 100644
index 0000000..9e18079
--- /dev/null
+++ b/smoke/pubspec.yaml
@@ -0,0 +1,24 @@
+name: smoke
+version: 0.3.3
+author: Polymer.dart Authors <web-ui-dev@dartlang.org>
+homepage: https://github.com/dart-lang/smoke
+description: >
+  A restricted reflective system that uses mirrors at development time, but that
+  can be replaced with non-reflective calls using code generation. See README.md
+  for mode details.
+dependencies:
+  barback: ">=0.9.0 <0.16.0"
+  logging: ">=0.9.0 <0.10.0"
+  analyzer: ">=0.13.0 <0.26.0"
+# TODO(sigmund): once we have some easier way to do global app-level
+# transformers, we might want to remove this transformer here and only apply it
+# in apps that need it.
+transformers:
+- smoke/src/default_transformer:
+    $include: lib/src/implementation.dart
+dev_dependencies:
+  unittest: ">=0.10.0 <0.12.0"
+  path: ">=1.0.0 <2.0.0"
+  code_transformers: ">=0.2.0 <0.3.0"
+environment:
+  sdk: ">=1.3.0-dev.7.9 <2.0.0"
diff --git a/source_maps/lib/builder.dart b/source_maps/lib/builder.dart
new file mode 100644
index 0000000..091e220
--- /dev/null
+++ b/source_maps/lib/builder.dart
@@ -0,0 +1,88 @@
+// Copyright (c) 2013, 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.
+
+/// Contains a builder object useful for creating source maps programatically.
+library source_maps.builder;
+
+// TODO(sigmund): add a builder for multi-section mappings.
+
+import 'dart:convert';
+
+import 'package:source_span/source_span.dart';
+
+import 'parser.dart';
+import 'src/source_map_span.dart';
+
+/// Builds a source map given a set of mappings.
+class SourceMapBuilder {
+
+  final List<Entry> _entries = <Entry>[];
+
+  /// Adds an entry mapping the [targetOffset] to [source].
+  void addFromOffset(SourceLocation source, SourceFile targetFile,
+      int targetOffset, String identifier) {
+    if (targetFile == null) {
+      throw new ArgumentError('targetFile cannot be null');
+    }
+    _entries.add(
+        new Entry(source, targetFile.location(targetOffset), identifier));
+  }
+
+  /// Adds an entry mapping [target] to [source].
+  ///
+  /// If [isIdentifier] is true or if [target] is a [SourceMapSpan] with
+  /// `isIdentifier` set to true, this entry is considered to represent an
+  /// identifier whose value will be stored in the source map. [isIdenfier]
+  /// takes precedence over [target]'s `isIdentifier` value.
+  void addSpan(SourceSpan source, SourceSpan target, {bool isIdentifier}) {
+    if (isIdentifier == null) {
+      isIdentifier = source is SourceMapSpan ? source.isIdentifier : false;
+    }
+
+    var name = isIdentifier ? source.text : null;
+    _entries.add(new Entry(source.start, target.start, name));
+  }
+
+  /// Adds an entry mapping [target] to [source].
+  void addLocation(SourceLocation source, SourceLocation target,
+      String identifier) {
+    _entries.add(new Entry(source, target, identifier));
+  }
+
+  /// Encodes all mappings added to this builder as a json map.
+  Map build(String fileUrl) {
+    return new SingleMapping.fromEntries(this._entries, fileUrl).toJson();
+  }
+
+  /// Encodes all mappings added to this builder as a json string.
+  String toJson(String fileUrl) => JSON.encode(build(fileUrl));
+}
+
+/// An entry in the source map builder.
+class Entry implements Comparable {
+  /// Span denoting the original location in the input source file
+  final SourceLocation source;
+
+  /// Span indicating the corresponding location in the target file.
+  final SourceLocation target;
+
+  /// An identifier name, when this location is the start of an identifier.
+  final String identifierName;
+
+  /// Creates a new [Entry] mapping [target] to [source].
+  Entry(this.source, this.target, this.identifierName);
+
+  /// Implements [Comparable] to ensure that entries are ordered by their
+  /// location in the target file. We sort primarily by the target offset
+  /// because source map files are encoded by printing each mapping in order as
+  /// they appear in the target file.
+  int compareTo(Entry other) {
+    int res = target.compareTo(other.target);
+    if (res != 0) return res;
+    res = source.sourceUrl.toString().compareTo(
+        other.source.sourceUrl.toString());
+    if (res != 0) return res;
+    return source.compareTo(other.source);
+  }
+}
diff --git a/source_maps/lib/parser.dart b/source_maps/lib/parser.dart
new file mode 100644
index 0000000..a9fcff5
--- /dev/null
+++ b/source_maps/lib/parser.dart
@@ -0,0 +1,531 @@
+// Copyright (c) 2013, 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.
+
+/// Contains the top-level function to parse source maps version 3.
+library source_maps.parser;
+
+import 'dart:collection';
+import 'dart:convert';
+
+import 'package:source_span/source_span.dart';
+
+import 'builder.dart' as builder;
+import 'src/source_map_span.dart';
+import 'src/utils.dart';
+import 'src/vlq.dart';
+
+/// Parses a source map directly from a json string.
+///
+/// [mapUrl], which may be either a [String] or a [Uri], indicates the URL of
+/// the source map file itself. If it's passed, any URLs in the source
+/// map will be interpreted as relative to this URL when generating spans.
+// TODO(sigmund): evaluate whether other maps should have the json parsed, or
+// the string represenation.
+// TODO(tjblasi): Ignore the first line of [jsonMap] if the JSON safety string
+// `)]}'` begins the string representation of the map.
+Mapping parse(String jsonMap, {Map<String, Map> otherMaps, mapUrl}) =>
+  parseJson(JSON.decode(jsonMap), otherMaps: otherMaps, mapUrl: mapUrl);
+
+/// Parses a source map directly from a json map object.
+///
+/// [mapUrl], which may be either a [String] or a [Uri], indicates the URL of
+/// the source map file itself. If it's passed, any URLs in the source
+/// map will be interpreted as relative to this URL when generating spans.
+Mapping parseJson(Map map, {Map<String, Map> otherMaps, mapUrl}) {
+  if (map['version'] != 3) {
+    throw new ArgumentError(
+        'unexpected source map version: ${map["version"]}. '
+        'Only version 3 is supported.');
+  }
+
+  if (map.containsKey('sections')) {
+    if (map.containsKey('mappings') || map.containsKey('sources') ||
+        map.containsKey('names')) {
+      throw new FormatException('map containing "sections" '
+          'cannot contain "mappings", "sources", or "names".');
+    }
+    return new MultiSectionMapping.fromJson(map['sections'], otherMaps,
+        mapUrl: mapUrl);
+  }
+  return new SingleMapping.fromJson(map, mapUrl: mapUrl);
+}
+
+
+/// A mapping parsed out of a source map.
+abstract class Mapping {
+  /// Returns the span associated with [line] and [column].
+  SourceMapSpan spanFor(int line, int column, {Map<String, SourceFile> files});
+
+  /// Returns the span associated with [location].
+  SourceMapSpan spanForLocation(SourceLocation location,
+      {Map<String, SourceFile> files}) {
+    return spanFor(location.line, location.column, files: files);
+  }
+}
+
+/// A meta-level map containing sections.
+class MultiSectionMapping extends Mapping {
+  /// For each section, the start line offset.
+  final List<int> _lineStart = <int>[];
+
+  /// For each section, the start column offset.
+  final List<int> _columnStart = <int>[];
+
+  /// For each section, the actual source map information, which is not adjusted
+  /// for offsets.
+  final List<Mapping> _maps = <Mapping>[];
+
+  /// Creates a section mapping from json.
+  MultiSectionMapping.fromJson(List sections, Map<String, Map> otherMaps,
+      {mapUrl}) {
+    for (var section in sections) {
+      var offset = section['offset'];
+      if (offset == null) throw new FormatException('section missing offset');
+
+      var line = section['offset']['line'];
+      if (line == null) throw new FormatException('offset missing line');
+
+      var column = section['offset']['column'];
+      if (column == null) throw new FormatException('offset missing column');
+
+      _lineStart.add(line);
+      _columnStart.add(column);
+
+      var url = section['url'];
+      var map = section['map'];
+
+      if (url != null && map != null) {
+        throw new FormatException("section can't use both url and map entries");
+      } else if (url != null) {
+        if (otherMaps == null || otherMaps[url] == null) {
+          throw new FormatException(
+              'section contains refers to $url, but no map was '
+              'given for it. Make sure a map is passed in "otherMaps"');
+        }
+        _maps.add(parseJson(otherMaps[url], otherMaps: otherMaps, mapUrl: url));
+      } else if (map != null) {
+        _maps.add(parseJson(map, otherMaps: otherMaps, mapUrl: mapUrl));
+      } else {
+        throw new FormatException('section missing url or map');
+      }
+    }
+    if (_lineStart.length == 0) {
+      throw new FormatException('expected at least one section');
+    }
+  }
+
+  int _indexFor(line, column) {
+    for(int i = 0; i < _lineStart.length; i++) {
+      if (line < _lineStart[i]) return i - 1;
+      if (line == _lineStart[i] && column < _columnStart[i]) return i - 1;
+    }
+    return _lineStart.length - 1;
+  }
+
+  SourceMapSpan spanFor(int line, int column, {Map<String, SourceFile> files}) {
+    int index = _indexFor(line, column);
+    return _maps[index].spanFor(
+        line - _lineStart[index], column - _columnStart[index], files: files);
+  }
+
+  String toString() {
+    var buff = new StringBuffer("$runtimeType : [");
+    for (int i = 0; i < _lineStart.length; i++) {
+      buff..write('(')
+          ..write(_lineStart[i])
+          ..write(',')
+          ..write(_columnStart[i])
+          ..write(':')
+          ..write(_maps[i])
+          ..write(')');
+    }
+    buff.write(']');
+    return buff.toString();
+  }
+}
+
+/// A map containing direct source mappings.
+class SingleMapping extends Mapping {
+  /// Source urls used in the mapping, indexed by id.
+  final List<String> urls;
+
+  /// Source names used in the mapping, indexed by id.
+  final List<String> names;
+
+  /// Entries indicating the beginning of each span.
+  final List<TargetLineEntry> lines;
+
+  /// Url of the target file.
+  String targetUrl;
+
+  /// Source root prepended to all entries in [urls].
+  String sourceRoot;
+
+  final Uri _mapUrl;
+
+  SingleMapping._(this.targetUrl, this.urls, this.names, this.lines)
+      : _mapUrl = null;
+
+  factory SingleMapping.fromEntries(
+      Iterable<builder.Entry> entries, [String fileUrl]) {
+    // The entries needs to be sorted by the target offsets.
+    var sourceEntries = new List.from(entries)..sort();
+    var lines = <TargetLineEntry>[];
+
+    // Indices associated with file urls that will be part of the source map. We
+    // use a linked hash-map so that `_urls.keys[_urls[u]] == u`
+    var urls = new LinkedHashMap<String, int>();
+
+    // Indices associated with identifiers that will be part of the source map.
+    // We use a linked hash-map so that `_names.keys[_names[n]] == n`
+    var names = new LinkedHashMap<String, int>();
+
+    var lineNum;
+    var targetEntries;
+    for (var sourceEntry in sourceEntries) {
+      if (lineNum == null || sourceEntry.target.line > lineNum) {
+        lineNum = sourceEntry.target.line;
+        targetEntries = <TargetEntry>[];
+        lines.add(new TargetLineEntry(lineNum, targetEntries));
+      }
+
+      if (sourceEntry.source == null) {
+        targetEntries.add(new TargetEntry(sourceEntry.target.column));
+      } else {
+        var sourceUrl = sourceEntry.source.sourceUrl;
+        var urlId = urls.putIfAbsent(
+            sourceUrl == null ? '' : sourceUrl.toString(), () => urls.length);
+        var srcNameId = sourceEntry.identifierName == null ? null :
+            names.putIfAbsent(sourceEntry.identifierName, () => names.length);
+        targetEntries.add(new TargetEntry(
+            sourceEntry.target.column,
+            urlId,
+            sourceEntry.source.line,
+            sourceEntry.source.column,
+            srcNameId));
+      }
+    }
+    return new SingleMapping._(
+        fileUrl, urls.keys.toList(), names.keys.toList(), lines);
+  }
+
+  SingleMapping.fromJson(Map map, {mapUrl})
+      : targetUrl = map['file'],
+        urls = map['sources'],
+        names = map['names'],
+        sourceRoot = map['sourceRoot'],
+        lines = <TargetLineEntry>[],
+        _mapUrl = mapUrl is String ? Uri.parse(mapUrl) : mapUrl {
+    int line = 0;
+    int column = 0;
+    int srcUrlId = 0;
+    int srcLine = 0;
+    int srcColumn = 0;
+    int srcNameId = 0;
+    var tokenizer = new _MappingTokenizer(map['mappings']);
+    var entries = <TargetEntry>[];
+
+    while (tokenizer.hasTokens) {
+      if (tokenizer.nextKind.isNewLine) {
+        if (!entries.isEmpty) {
+          lines.add(new TargetLineEntry(line, entries));
+          entries = <TargetEntry>[];
+        }
+        line++;
+        column = 0;
+        tokenizer._consumeNewLine();
+        continue;
+      }
+
+      // Decode the next entry, using the previous encountered values to
+      // decode the relative values.
+      //
+      // We expect 1, 4, or 5 values. If present, values are expected in the
+      // following order:
+      //   0: the starting column in the current line of the generated file
+      //   1: the id of the original source file
+      //   2: the starting line in the original source
+      //   3: the starting column in the original source
+      //   4: the id of the original symbol name
+      // The values are relative to the previous encountered values.
+      if (tokenizer.nextKind.isNewSegment) throw _segmentError(0, line);
+      column += tokenizer._consumeValue();
+      if (!tokenizer.nextKind.isValue) {
+        entries.add(new TargetEntry(column));
+      } else {
+        srcUrlId += tokenizer._consumeValue();
+        if (srcUrlId >= urls.length) {
+          throw new StateError(
+              'Invalid source url id. $targetUrl, $line, $srcUrlId');
+        }
+        if (!tokenizer.nextKind.isValue) throw _segmentError(2, line);
+        srcLine += tokenizer._consumeValue();
+        if (!tokenizer.nextKind.isValue) throw _segmentError(3, line);
+        srcColumn += tokenizer._consumeValue();
+        if (!tokenizer.nextKind.isValue) {
+          entries.add(new TargetEntry(column, srcUrlId, srcLine, srcColumn));
+        } else {
+          srcNameId += tokenizer._consumeValue();
+          if (srcNameId >= names.length) {
+            throw new StateError(
+                'Invalid name id: $targetUrl, $line, $srcNameId');
+          }
+          entries.add(new TargetEntry(column, srcUrlId, srcLine, srcColumn,
+              srcNameId));
+        }
+      }
+      if (tokenizer.nextKind.isNewSegment) tokenizer._consumeNewSegment();
+    }
+    if (!entries.isEmpty) {
+      lines.add(new TargetLineEntry(line, entries));
+    }
+  }
+
+  /// Encodes the Mapping mappings as a json map.
+  Map toJson() {
+    var buff = new StringBuffer();
+    var line = 0;
+    var column = 0;
+    var srcLine = 0;
+    var srcColumn = 0;
+    var srcUrlId = 0;
+    var srcNameId = 0;
+    var first = true;
+
+    for (var entry in lines) {
+      int nextLine = entry.line;
+      if (nextLine > line) {
+        for (int i = line; i < nextLine; ++i) {
+          buff.write(';');
+        }
+        line = nextLine;
+        column = 0;
+        first = true;
+      }
+
+      for (var segment in entry.entries) {
+        if (!first) buff.write(',');
+        first = false;
+        column = _append(buff, column, segment.column);
+
+        // Encoding can be just the column offset if there is no source
+        // information.
+        var newUrlId = segment.sourceUrlId;
+        if (newUrlId == null) continue;
+        srcUrlId = _append(buff, srcUrlId, newUrlId);
+        srcLine = _append(buff, srcLine, segment.sourceLine);
+        srcColumn = _append(buff, srcColumn, segment.sourceColumn);
+
+        if (segment.sourceNameId == null) continue;
+        srcNameId = _append(buff, srcNameId, segment.sourceNameId);
+      }
+    }
+
+    var result = {
+      'version': 3,
+      'sourceRoot': sourceRoot == null ? '' : sourceRoot,
+      'sources': urls,
+      'names' : names,
+      'mappings' : buff.toString()
+    };
+    if (targetUrl != null) {
+      result['file'] = targetUrl;
+    }
+    return result;
+  }
+
+  /// Appends to [buff] a VLQ encoding of [newValue] using the difference
+  /// between [oldValue] and [newValue]
+  static int _append(StringBuffer buff, int oldValue, int newValue) {
+    buff.writeAll(encodeVlq(newValue - oldValue));
+    return newValue;
+  }
+
+  _segmentError(int seen, int line) => new StateError(
+      'Invalid entry in sourcemap, expected 1, 4, or 5'
+      ' values, but got $seen.\ntargeturl: $targetUrl, line: $line');
+
+  /// Returns [TargetLineEntry] which includes the location in the target [line]
+  /// number. In particular, the resulting entry is the last entry whose line
+  /// number is lower or equal to [line].
+  TargetLineEntry _findLine(int line) {
+    int index = binarySearch(lines, (e) => e.line > line);
+    return (index <= 0) ? null : lines[index - 1];
+  }
+
+  /// Returns [TargetEntry] which includes the location denoted by
+  /// [line], [column]. If [lineEntry] corresponds to [line], then this will be
+  /// the last entry whose column is lower or equal than [column]. If
+  /// [lineEntry] corresponds to a line prior to [line], then the result will be
+  /// the very last entry on that line.
+  TargetEntry _findColumn(int line, int column, TargetLineEntry lineEntry) {
+    if (lineEntry == null || lineEntry.entries.length == 0) return null;
+    if (lineEntry.line != line) return lineEntry.entries.last;
+    var entries = lineEntry.entries;
+    int index = binarySearch(entries, (e) => e.column > column);
+    return (index <= 0) ? null : entries[index - 1];
+  }
+
+  SourceMapSpan spanFor(int line, int column, {Map<String, SourceFile> files}) {
+    var entry = _findColumn(line, column, _findLine(line));
+    if (entry == null || entry.sourceUrlId == null) return null;
+    var url = urls[entry.sourceUrlId];
+    if (sourceRoot != null) {
+      url = '${sourceRoot}${url}';
+    }
+    if (files != null && files[url] != null) {
+      var file = files[url];
+      var start = file.getOffset(entry.sourceLine, entry.sourceColumn);
+      if (entry.sourceNameId != null) {
+        var text = names[entry.sourceNameId];
+        return new SourceMapFileSpan(
+            files[url].span(start, start + text.length),
+            isIdentifier: true);
+      } else {
+        return new SourceMapFileSpan(files[url].location(start).pointSpan());
+      }
+    } else {
+      var start = new SourceLocation(0,
+          sourceUrl: _mapUrl == null ? url : _mapUrl.resolve(url),
+          line: entry.sourceLine,
+          column: entry.sourceColumn);
+
+      // Offset and other context is not available.
+      if (entry.sourceNameId != null) {
+        return new SourceMapSpan.identifier(start, names[entry.sourceNameId]);
+      } else {
+        return new SourceMapSpan(start, start, '');
+      }
+    }
+  }
+
+  String toString() {
+    return (new StringBuffer("$runtimeType : [")
+        ..write('targetUrl: ')
+        ..write(targetUrl)
+        ..write(', sourceRoot: ')
+        ..write(sourceRoot)
+        ..write(', urls: ')
+        ..write(urls)
+        ..write(', names: ')
+        ..write(names)
+        ..write(', lines: ')
+        ..write(lines)
+        ..write(']')).toString();
+  }
+
+  String get debugString {
+    var buff = new StringBuffer();
+    for (var lineEntry in lines) {
+      var line = lineEntry.line;
+      for (var entry in lineEntry.entries) {
+        buff..write(targetUrl)
+            ..write(': ')
+            ..write(line)
+            ..write(':')
+            ..write(entry.column);
+        if (entry.sourceUrlId != null) {
+          buff..write('   -->   ')
+              ..write(sourceRoot)
+              ..write(urls[entry.sourceUrlId])
+              ..write(': ')
+              ..write(entry.sourceLine)
+              ..write(':')
+              ..write(entry.sourceColumn);
+        }
+        if (entry.sourceNameId != null) {
+          buff..write(' (')
+              ..write(names[entry.sourceNameId])
+              ..write(')');
+        }
+        buff.write('\n');
+      }
+    }
+    return buff.toString();
+  }
+}
+
+/// A line entry read from a source map.
+class TargetLineEntry {
+  final int line;
+  List<TargetEntry> entries;
+  TargetLineEntry(this.line, this.entries);
+
+  String toString() => '$runtimeType: $line $entries';
+}
+
+/// A target segment entry read from a source map
+class TargetEntry {
+  final int column;
+  final int sourceUrlId;
+  final int sourceLine;
+  final int sourceColumn;
+  final int sourceNameId;
+
+  TargetEntry(this.column, [this.sourceUrlId, this.sourceLine,
+      this.sourceColumn, this.sourceNameId]);
+
+  String toString() => '$runtimeType: '
+      '($column, $sourceUrlId, $sourceLine, $sourceColumn, $sourceNameId)';
+}
+
+/** A character iterator over a string that can peek one character ahead. */
+class _MappingTokenizer implements Iterator<String> {
+  final String _internal;
+  final int _length;
+  int index = -1;
+  _MappingTokenizer(String internal)
+      : _internal = internal,
+        _length = internal.length;
+
+  // Iterator API is used by decodeVlq to consume VLQ entries.
+  bool moveNext() => ++index < _length;
+  String get current =>
+      (index >= 0 && index < _length) ?  _internal[index] : null;
+
+  bool get hasTokens => index < _length - 1 && _length > 0;
+
+  _TokenKind get nextKind {
+    if (!hasTokens) return _TokenKind.EOF;
+    var next = _internal[index + 1];
+    if (next == ';') return _TokenKind.LINE;
+    if (next == ',') return _TokenKind.SEGMENT;
+    return _TokenKind.VALUE;
+  }
+
+  int _consumeValue() => decodeVlq(this);
+  void _consumeNewLine() { ++index; }
+  void _consumeNewSegment() { ++index; }
+
+  // Print the state of the iterator, with colors indicating the current
+  // position.
+  String toString() {
+    var buff = new StringBuffer();
+    for (int i = 0; i < index; i++) {
+      buff.write(_internal[i]);
+    }
+    buff.write('');
+    buff.write(current == null ? '' : current);
+    buff.write('');
+    for (int i = index + 1; i < _internal.length; i++) {
+      buff.write(_internal[i]);
+    }
+    buff.write(' ($index)');
+    return buff.toString();
+  }
+}
+
+class _TokenKind {
+  static const _TokenKind LINE = const _TokenKind(isNewLine: true);
+  static const _TokenKind SEGMENT = const _TokenKind(isNewSegment: true);
+  static const _TokenKind EOF = const _TokenKind(isEof: true);
+  static const _TokenKind VALUE = const _TokenKind();
+  final bool isNewLine;
+  final bool isNewSegment;
+  final bool isEof;
+  bool get isValue => !isNewLine && !isNewSegment && !isEof;
+
+  const _TokenKind(
+      {this.isNewLine: false, this.isNewSegment: false, this.isEof: false});
+}
diff --git a/source_maps/lib/printer.dart b/source_maps/lib/printer.dart
new file mode 100644
index 0000000..906e260
--- /dev/null
+++ b/source_maps/lib/printer.dart
@@ -0,0 +1,252 @@
+// Copyright (c) 2013, 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.
+
+/// Contains a code printer that generates code by recording the source maps.
+library source_maps.printer;
+
+import 'package:source_span/source_span.dart';
+
+import 'builder.dart';
+import 'src/source_map_span.dart';
+
+const int _LF = 10;
+const int _CR = 13;
+
+/// A simple printer that keeps track of offset locations and records source
+/// maps locations.
+class Printer {
+  final String filename;
+  final StringBuffer _buff = new StringBuffer();
+  final SourceMapBuilder _maps = new SourceMapBuilder();
+  String get text => _buff.toString();
+  String get map => _maps.toJson(filename);
+
+  /// Current source location mapping.
+  SourceLocation _loc;
+
+  /// Current line in the buffer;
+  int _line = 0;
+
+  /// Current column in the buffer.
+  int _column = 0;
+
+  Printer(this.filename);
+
+  /// Add [str] contents to the output, tracking new lines to track correct
+  /// positions for span locations. When [projectMarks] is true, this method
+  /// adds a source map location on each new line, projecting that every new
+  /// line in the target file (printed here) corresponds to a new line in the
+  /// source file.
+  void add(String str, {projectMarks: false}) {
+    var chars = str.runes.toList();
+    var length = chars.length;
+    for (int i = 0; i < length; i++) {
+      var c = chars[i];
+      if (c == _LF || (c == _CR && (i + 1 == length || chars[i + 1] != _LF))) {
+        // Return not followed by line-feed is treated as a new line.
+        _line++;
+        _column = 0;
+        if (projectMarks && _loc != null) {
+          if (_loc is FileLocation) {
+            var file = (_loc as FileLocation).file;
+            mark(file.location(file.getOffset(_loc.line + 1)));
+          } else {
+            mark(new SourceLocation(0,
+                sourceUrl: _loc.sourceUrl, line: _loc.line + 1, column: 0));
+          }
+        }
+      } else {
+        _column++;
+      }
+    }
+    _buff.write(str);
+  }
+
+
+  /// Append a [total] number of spaces in the target file. Typically used for
+  /// formatting indentation.
+  void addSpaces(int total) {
+    for (int i = 0; i < total; i++) _buff.write(' ');
+    _column += total;
+  }
+
+  /// Marks that the current point in the target file corresponds to the [mark]
+  /// in the source file, which can be either a [SourceLocation] or a
+  /// [SourceSpan]. When the mark is a [SourceMapSpan] with `isIdentifier` set,
+  /// this also records the name of the identifier in the source map
+  /// information.
+  void mark(mark) {
+    var loc;
+    var identifier = null;
+    if (mark is SourceLocation) {
+      loc = mark;
+    } else if (mark is SourceSpan) {
+      loc = mark.start;
+      if (mark is SourceMapSpan && mark.isIdentifier) identifier = mark.text;
+    }
+    _maps.addLocation(
+        loc,
+        new SourceLocation(_buff.length, line: _line, column: _column),
+        identifier);
+    _loc = loc;
+  }
+}
+
+/// A more advanced printer that keeps track of offset locations to record
+/// source maps, but additionally allows nesting of different kind of items,
+/// including [NestedPrinter]s, and it let's you automatically indent text.
+///
+/// This class is especially useful when doing code generation, where different
+/// peices of the code are generated independently on separate printers, and are
+/// finally put together in the end.
+class NestedPrinter implements NestedItem {
+
+  /// Items recoded by this printer, which can be [String] literals,
+  /// [NestedItem]s, and source map information like [SourceLocation] and
+  /// [SourceSpan].
+  List _items = [];
+
+  /// Internal buffer to merge consecutive strings added to this printer.
+  StringBuffer _buff;
+
+  /// Current indentation, which can be updated from outside this class.
+  int indent;
+
+  /// Item used to indicate that the following item is copied from the original
+  /// source code, and hence we should preserve source-maps on every new line.
+  static final _ORIGINAL = new Object();
+
+  NestedPrinter([this.indent = 0]);
+
+  /// Adds [object] to this printer. [object] can be a [String],
+  /// [NestedPrinter], or anything implementing [NestedItem]. If [object] is a
+  /// [String], the value is appended directly, without doing any formatting
+  /// changes. If you wish to add a line of code with automatic indentation, use
+  /// [addLine] instead.  [NestedPrinter]s and [NestedItem]s are not processed
+  /// until [build] gets called later on. We ensure that [build] emits every
+  /// object in the order that they were added to this printer.
+  ///
+  /// The [location] and [span] parameters indicate the corresponding source map
+  /// location of [object] in the original input. Only one, [location] or
+  /// [span], should be provided at a time.
+  ///
+  /// Indicate [isOriginal] when [object] is copied directly from the user code.
+  /// Setting [isOriginal] will make this printer propagate source map locations
+  /// on every line-break.
+  void add(object, {SourceLocation location, SourceSpan span,
+      bool isOriginal: false}) {
+    if (object is! String || location != null || span != null || isOriginal) {
+      _flush();
+      assert(location == null || span == null);
+      if (location != null) _items.add(location);
+      if (span != null) _items.add(span);
+      if (isOriginal) _items.add(_ORIGINAL);
+    }
+
+    if (object is String) {
+      _appendString(object);
+    } else {
+      _items.add(object);
+    }
+  }
+
+  /// Append `2 * indent` spaces to this printer.
+  void insertIndent() => _indent(indent);
+
+  /// Add a [line], autoindenting to the current value of [indent]. Note,
+  /// indentation is not inferred from the contents added to this printer. If a
+  /// line starts or ends an indentation block, you need to also update [indent]
+  /// accordingly. Also, indentation is not adapted for nested printers. If
+  /// you add a [NestedPrinter] to this printer, its indentation is set
+  /// separately and will not include any the indentation set here.
+  ///
+  /// The [location] and [span] parameters indicate the corresponding source map
+  /// location of [object] in the original input. Only one, [location] or
+  /// [span], should be provided at a time.
+  void addLine(String line, {SourceLocation location, SourceSpan span}) {
+    if (location != null || span != null) {
+      _flush();
+      assert(location == null || span == null);
+      if (location != null) _items.add(location);
+      if (span != null) _items.add(span);
+    }
+    if (line == null) return;
+    if (line != '') {
+      // We don't indent empty lines.
+      _indent(indent);
+      _appendString(line);
+    }
+    _appendString('\n');
+  }
+
+  /// Appends a string merging it with any previous strings, if possible.
+  void _appendString(String s) {
+    if (_buff == null) _buff = new StringBuffer();
+    _buff.write(s);
+  }
+
+  /// Adds all of the current [_buff] contents as a string item.
+  void _flush() {
+    if (_buff != null) {
+      _items.add(_buff.toString());
+      _buff = null;
+    }
+  }
+
+  void _indent(int indent) {
+    for (int i = 0; i < indent; i++) _appendString('  ');
+  }
+
+  /// Returns a string representation of all the contents appended to this
+  /// printer, including source map location tokens.
+  String toString() {
+    _flush();
+    return (new StringBuffer()..writeAll(_items)).toString();
+  }
+
+  /// [Printer] used during the last call to [build], if any.
+  Printer printer;
+
+  /// Returns the text produced after calling [build].
+  String get text => printer.text;
+
+  /// Returns the source-map information produced after calling [build].
+  String get map => printer.map;
+
+  /// Builds the output of this printer and source map information. After
+  /// calling this function, you can use [text] and [map] to retrieve the
+  /// geenrated code and source map information, respectively.
+  void build(String filename) {
+    writeTo(printer = new Printer(filename));
+  }
+
+  /// Implements the [NestedItem] interface.
+  void writeTo(Printer printer) {
+    _flush();
+    bool propagate = false;
+    for (var item in _items) {
+      if (item is NestedItem) {
+        item.writeTo(printer);
+      } else if (item is String) {
+        printer.add(item, projectMarks: propagate);
+        propagate = false;
+      } else if (item is SourceLocation || item is SourceSpan) {
+        printer.mark(item);
+      } else if (item == _ORIGINAL) {
+        // we insert booleans when we are about to quote text that was copied
+        // from the original source. In such case, we will propagate marks on
+        // every new-line.
+        propagate = true;
+      } else {
+        throw new UnsupportedError('Unknown item type: $item');
+      }
+    }
+  }
+}
+
+/// An item added to a [NestedPrinter].
+abstract class NestedItem {
+  /// Write the contents of this item into [printer].
+  void writeTo(Printer printer);
+}
diff --git a/source_maps/lib/refactor.dart b/source_maps/lib/refactor.dart
new file mode 100644
index 0000000..a33b86b
--- /dev/null
+++ b/source_maps/lib/refactor.dart
@@ -0,0 +1,133 @@
+// Copyright (c) 2013, 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.
+
+/// Tools to help implement refactoring like transformations to Dart code.
+///
+/// [TextEditTransaction] supports making a series of changes to a text buffer.
+/// [guessIndent] helps to guess the appropriate indentiation for the new code.
+library source_maps.refactor;
+
+import 'package:source_span/source_span.dart';
+
+import 'printer.dart';
+
+/// Editable text transaction.
+///
+/// Applies a series of edits using original location
+/// information, and composes them into the edited string.
+class TextEditTransaction {
+  final SourceFile file;
+  final String original;
+  final _edits = <_TextEdit>[];
+
+  /// Creates a new transaction.
+  TextEditTransaction(this.original, this.file);
+
+  bool get hasEdits => _edits.length > 0;
+
+  /// Edit the original text, replacing text on the range [begin] and [end]
+  /// with the [replacement]. [replacement] can be either a string or a
+  /// [NestedPrinter].
+  void edit(int begin, int end, replacement) {
+    _edits.add(new _TextEdit(begin, end, replacement));
+  }
+
+  /// Create a source map [SourceLocation] for [offset].
+  SourceLocation _loc(int offset) =>
+      file != null ? file.location(offset) : null;
+
+  /// Applies all pending [edit]s and returns a [NestedPrinter] containing the
+  /// rewritten string and source map information. [filename] is given to the
+  /// underlying printer to indicate the name of the generated file that will
+  /// contains the source map information.
+  /// 
+  /// Throws [UnsupportedError] if the edits were overlapping. If no edits were
+  /// made, the printer simply contains the original string.
+  NestedPrinter commit() {
+    var printer = new NestedPrinter();
+    if (_edits.length == 0) {
+      return printer..add(original, location: _loc(0), isOriginal: true);
+    }
+
+    // Sort edits by start location.
+    _edits.sort();
+
+    int consumed = 0;
+    for (var edit in _edits) {
+      if (consumed > edit.begin) {
+        var sb = new StringBuffer();
+        sb..write(file.location(edit.begin).toolString)
+            ..write(': overlapping edits. Insert at offset ')
+            ..write(edit.begin)
+            ..write(' but have consumed ')
+            ..write(consumed)
+            ..write(' input characters. List of edits:');
+        for (var e in _edits) sb..write('\n    ')..write(e);
+        throw new UnsupportedError(sb.toString());
+      }
+
+      // Add characters from the original string between this edit and the last
+      // one, if any.
+      var betweenEdits = original.substring(consumed, edit.begin);
+      printer..add(betweenEdits, location: _loc(consumed), isOriginal: true)
+             ..add(edit.replace, location: _loc(edit.begin));
+      consumed = edit.end;
+    }
+
+    // Add any text from the end of the original string that was not replaced.
+    printer.add(original.substring(consumed),
+        location: _loc(consumed), isOriginal: true);
+    return printer;
+  }
+}
+
+class _TextEdit implements Comparable<_TextEdit> {
+  final int begin;
+  final int end;
+
+  /// The replacement used by the edit, can be a string or a [NestedPrinter].
+  final replace;
+
+  _TextEdit(this.begin, this.end, this.replace);
+
+  int get length => end - begin;
+
+  String toString() => '(Edit @ $begin,$end: "$replace")';
+
+  int compareTo(_TextEdit other) {
+    int diff = begin - other.begin;
+    if (diff != 0) return diff;
+    return end - other.end;
+  }
+}
+
+/// Returns all whitespace characters at the start of [charOffset]'s line.
+String guessIndent(String code, int charOffset) {
+  // Find the beginning of the line
+  int lineStart = 0;
+  for (int i = charOffset - 1; i >= 0; i--) {
+    var c = code.codeUnitAt(i);
+    if (c == _LF || c == _CR) {
+      lineStart = i + 1;
+      break;
+    }
+  }
+
+  // Grab all the whitespace
+  int whitespaceEnd = code.length;
+  for (int i = lineStart; i < code.length; i++) {
+    var c = code.codeUnitAt(i);
+    if (c != _SPACE && c != _TAB) {
+      whitespaceEnd = i;
+      break;
+    }
+  }
+
+  return code.substring(lineStart, whitespaceEnd);
+}
+
+const int _CR = 13;
+const int _LF = 10;
+const int _TAB = 9;
+const int _SPACE = 32;
diff --git a/source_maps/lib/source_maps.dart b/source_maps/lib/source_maps.dart
new file mode 100644
index 0000000..9531903
--- /dev/null
+++ b/source_maps/lib/source_maps.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2013, 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 to create and parse source maps.
+///
+/// Create a source map using [SourceMapBuilder]. For example:
+///     var json = (new SourceMapBuilder()
+///         ..add(inputSpan1, outputSpan1)
+///         ..add(inputSpan2, outputSpan2)
+///         ..add(inputSpan3, outputSpan3)
+///         .toJson(outputFile);
+///
+/// Use the source_span package's [SourceSpan] and [SourceFile] classes to
+/// specify span locations.
+///
+/// Parse a source map using [parse], and call `spanFor` on the returned mapping
+/// object. For example:
+///     var mapping = parse(json);
+///     mapping.spanFor(outputSpan1.line, outputSpan1.column)
+///
+/// ## Getting the code ##
+///
+/// This library is distributed as a [pub][] package. To install this package,
+/// add the following to your `pubspec.yaml` file:
+///
+///     dependencies:
+///       source_maps: any
+///
+/// After you run `pub install`, you should be able to access this library by
+/// importing `package:source_maps/source_maps.dart`.
+///
+/// For more information, see the
+/// [source_maps package on pub.dartlang.org][pkg].
+///
+/// [pub]: http://pub.dartlang.org
+/// [pkg]: http://pub.dartlang.org/packages/source_maps
+library source_maps;
+
+export "builder.dart";
+export "parser.dart";
+export "printer.dart";
+export "refactor.dart";
+export 'src/source_map_span.dart';
diff --git a/source_maps/lib/src/source_map_span.dart b/source_maps/lib/src/source_map_span.dart
new file mode 100644
index 0000000..b70bdfe
--- /dev/null
+++ b/source_maps/lib/src/source_map_span.dart
@@ -0,0 +1,60 @@
+// 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 source_maps.source_map_span;
+
+import 'package:source_span/source_span.dart';
+
+/// A [SourceSpan] for spans coming from or being written to source maps.
+///
+/// These spans have an extra piece of metadata: whether or not they represent
+/// an identifier (see [isIdentifier]).
+class SourceMapSpan extends SourceSpanBase {
+  /// Whether this span represents an identifier.
+  ///
+  /// If this is `true`, [text] is the value of the identifier.
+  final bool isIdentifier;
+
+  SourceMapSpan(SourceLocation start, SourceLocation end, String text,
+          {this.isIdentifier: false})
+      : super(start, end, text);
+
+  /// Creates a [SourceMapSpan] for an identifier with value [text] starting at
+  /// [start].
+  ///
+  /// The [end] location is determined by adding [text] to [start].
+  SourceMapSpan.identifier(SourceLocation start, String text)
+      : this(
+          start,
+          new SourceLocation(start.offset + text.length,
+              sourceUrl: start.sourceUrl,
+              line: start.line,
+              column: start.column + text.length),
+          text,
+          isIdentifier: true);
+}
+
+/// A wrapper aruond a [FileSpan] that implements [SourceMapSpan].
+class SourceMapFileSpan implements SourceMapSpan, FileSpan {
+  final FileSpan _inner;
+  final bool isIdentifier;
+
+  SourceFile get file => _inner.file;
+  FileLocation get start => _inner.start;
+  FileLocation get end => _inner.end;
+  String get text => _inner.text;
+  String get context => _inner.context;
+  Uri get sourceUrl => _inner.sourceUrl;
+  int get length => _inner.length;
+
+  SourceMapFileSpan(this._inner, {this.isIdentifier: false});
+
+  int compareTo(SourceSpan other) => _inner.compareTo(other);
+  SourceSpan union(SourceSpan other) => _inner.union(other);
+  FileSpan expand(FileSpan other) => _inner.expand(other);
+  String message(String message, {color}) =>
+      _inner.message(message, color: color);
+  String toString() => _inner.toString()
+      .replaceAll("FileSpan", "SourceMapFileSpan");
+}
diff --git a/source_maps/lib/src/utils.dart b/source_maps/lib/src/utils.dart
new file mode 100644
index 0000000..78f098e
--- /dev/null
+++ b/source_maps/lib/src/utils.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2013, 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.
+
+/// Utilities that shouldn't be in this package.
+library source_maps.utils;
+
+/// Find the first entry in a sorted [list] that matches a monotonic predicate.
+/// Given a result `n`, that all items before `n` will not match, `n` matches,
+/// and all items after `n` match too. The result is -1 when there are no
+/// items, 0 when all items match, and list.length when none does.
+// TODO(sigmund): remove this function after dartbug.com/5624 is fixed.
+int binarySearch(List list, bool matches(item)) {
+  if (list.length == 0) return -1;
+  if (matches(list.first)) return 0;
+  if (!matches(list.last)) return list.length;
+
+  int min = 0;
+  int max = list.length - 1;
+  while (min < max) {
+    var half = min + ((max - min) ~/ 2);
+    if (matches(list[half])) {
+      max = half;
+    } else {
+      min = half + 1;
+    }
+  }
+  return max;
+}
diff --git a/source_maps/lib/src/vlq.dart b/source_maps/lib/src/vlq.dart
new file mode 100644
index 0000000..e4ab4eb
--- /dev/null
+++ b/source_maps/lib/src/vlq.dart
@@ -0,0 +1,103 @@
+// Copyright (c) 2013, 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.
+
+
+/// Utilities to encode and decode VLQ values used in source maps.
+///
+/// Sourcemaps are encoded with variable length numbers as base64 encoded
+/// strings with the least significant digit coming first. Each base64 digit
+/// encodes a 5-bit value (0-31) and a continuation bit. Signed values can be
+/// represented by using the least significant bit of the value as the sign bit.
+///
+/// For more details see the source map [version 3 documentation][spec].
+/// [spec]: https://docs.google.com/a/google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
+library source_maps.src.vlq;
+
+import 'dart:math';
+
+const int VLQ_BASE_SHIFT = 5;
+
+const int VLQ_BASE_MASK = (1 << 5) - 1;
+
+const int VLQ_CONTINUATION_BIT = 1 << 5;
+
+const int VLQ_CONTINUATION_MASK = 1 << 5;
+
+const String BASE64_DIGITS =
+    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+
+final Map<String, int> _digits = () {
+  var map = <String, int>{};
+  for (int i = 0; i < 64; i++) {
+    map[BASE64_DIGITS[i]] = i;
+  }
+  return map;
+}();
+
+final int MAX_INT32 = pow(2, 31) - 1;
+final int MIN_INT32 = -pow(2, 31);
+
+/// Creates the VLQ encoding of [value] as a sequence of characters
+Iterable<String> encodeVlq(int value) {
+  if (value < MIN_INT32 || value > MAX_INT32) {
+    throw new ArgumentError('expected 32 bit int, got: $value');
+  }
+  var res = <String>[];
+  int signBit = 0;
+  if (value < 0) {
+    signBit = 1;
+    value = -value;
+  }
+  value = (value << 1) | signBit;
+  do {
+    int digit = value & VLQ_BASE_MASK;
+    value >>= VLQ_BASE_SHIFT;
+    if (value > 0) {
+      digit |= VLQ_CONTINUATION_BIT;
+    }
+    res.add(BASE64_DIGITS[digit]);
+  } while (value > 0);
+  return res;
+}
+
+/// Decodes a value written as a sequence of VLQ characters. The first input
+/// character will be `chars.current` after calling `chars.moveNext` once. The
+/// iterator is advanced until a stop character is found (a character without
+/// the [VLQ_CONTINUATION_BIT]).
+int decodeVlq(Iterator<String> chars) {
+  int result = 0;
+  bool stop = false;
+  int shift = 0;
+  while (!stop) {
+    if (!chars.moveNext()) throw new StateError('incomplete VLQ value');
+    var char = chars.current;
+    if (!_digits.containsKey(char)) {
+      throw new FormatException('invalid character in VLQ encoding: $char');
+    }
+    var digit = _digits[char];
+    stop = (digit & VLQ_CONTINUATION_BIT) == 0;
+    digit &= VLQ_BASE_MASK;
+    result += (digit << shift);
+    shift += VLQ_BASE_SHIFT;
+  }
+
+  // Result uses the least significant bit as a sign bit. We convert it into a
+  // two-complement value. For example,
+  //   2 (10 binary) becomes 1
+  //   3 (11 binary) becomes -1
+  //   4 (100 binary) becomes 2
+  //   5 (101 binary) becomes -2
+  //   6 (110 binary) becomes 3
+  //   7 (111 binary) becomes -3
+  bool negate = (result & 1) == 1;
+  result = result >> 1;
+  result = negate ? -result : result;
+
+  // TODO(sigmund): can we detect this earlier?
+  if (result < MIN_INT32 || result > MAX_INT32) {
+    throw new FormatException(
+        'expected an encoded 32 bit int, but we got: $result');
+  }
+  return result;
+}
diff --git a/source_maps/pubspec.yaml b/source_maps/pubspec.yaml
new file mode 100644
index 0000000..fc0fe9b
--- /dev/null
+++ b/source_maps/pubspec.yaml
@@ -0,0 +1,12 @@
+name: source_maps
+version: 0.10.1
+author: Dart Team <misc@dartlang.org>
+description: Library to programmatically manipulate source map files.
+homepage: http://github.com/dart-lang/source_maps
+dependencies:
+  path: ^1.2.0
+  source_span: ^1.1.1
+environment:
+  sdk: '>=1.8.0 <2.0.0'
+dev_dependencies:
+  unittest: '>=0.9.0 <0.12.0'
diff --git a/source_span/lib/source_span.dart b/source_span/lib/source_span.dart
new file mode 100644
index 0000000..89b1650
--- /dev/null
+++ b/source_span/lib/source_span.dart
@@ -0,0 +1,12 @@
+// 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 source_span;
+
+export "src/file.dart";
+export "src/location.dart";
+export "src/span.dart";
+export "src/span_exception.dart";
+export "src/span_mixin.dart";
+export "src/span_with_context.dart";
diff --git a/source_span/lib/src/colors.dart b/source_span/lib/src/colors.dart
new file mode 100644
index 0000000..274fc92
--- /dev/null
+++ b/source_span/lib/src/colors.dart
@@ -0,0 +1,13 @@
+// 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.
+
+// Color constants used for generating messages.
+library source_span.colors;
+
+const String RED = '\u001b[31m';
+
+const String YELLOW = '\u001b[33m';
+
+const String NONE = '\u001b[0m';
+
diff --git a/source_span/lib/src/file.dart b/source_span/lib/src/file.dart
new file mode 100644
index 0000000..4b4e026
--- /dev/null
+++ b/source_span/lib/src/file.dart
@@ -0,0 +1,267 @@
+// 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 source_span.file;
+
+import 'dart:math' as math;
+import 'dart:typed_data';
+
+import 'package:path/path.dart' as p;
+
+import 'colors.dart' as colors;
+import 'location.dart';
+import 'span.dart';
+import 'span_mixin.dart';
+import 'span_with_context.dart';
+import 'utils.dart';
+
+// Constants to determine end-of-lines.
+const int _LF = 10;
+const int _CR = 13;
+
+/// A class representing a source file.
+///
+/// This doesn't necessarily have to correspond to a file on disk, just a chunk
+/// of text usually with a URL associated with it.
+class SourceFile {
+  /// The URL where the source file is located.
+  ///
+  /// This may be null, indicating that the URL is unknown or unavailable.
+  final Uri url;
+
+  /// An array of offsets for each line beginning in the file.
+  ///
+  /// Each offset refers to the first character *after* the newline. If the
+  /// source file has a trailing newline, the final offset won't actually be in
+  /// the file.
+  final _lineStarts = <int>[0];
+
+  /// The code points of the characters in the file.
+  final Uint32List _decodedChars;
+
+  /// The length of the file in characters.
+  int get length => _decodedChars.length;
+
+  /// The number of lines in the file.
+  int get lines => _lineStarts.length;
+
+  /// Creates a new source file from [text].
+  ///
+  /// [url] may be either a [String], a [Uri], or `null`.
+  SourceFile(String text, {url})
+      : this.decoded(text.runes, url: url);
+
+  /// Creates a new source file from a list of decoded characters.
+  ///
+  /// [url] may be either a [String], a [Uri], or `null`.
+  SourceFile.decoded(Iterable<int> decodedChars, {url})
+      : url = url is String ? Uri.parse(url) : url,
+        _decodedChars = new Uint32List.fromList(decodedChars.toList()) {
+    for (var i = 0; i < _decodedChars.length; i++) {
+      var c = _decodedChars[i];
+      if (c == _CR) {
+        // Return not followed by newline is treated as a newline
+        var j = i + 1;
+        if (j >= _decodedChars.length || _decodedChars[j] != _LF) c = _LF;
+      }
+      if (c == _LF) _lineStarts.add(i + 1);
+    }
+  }
+
+  /// Returns a span in [this] from [start] to [end] (exclusive).
+  ///
+  /// If [end] isn't passed, it defaults to the end of the file.
+  FileSpan span(int start, [int end]) {
+    if (end == null) end = length - 1;
+    return new FileSpan._(this, start, end);
+  }
+
+  /// Returns a location in [this] at [offset].
+  FileLocation location(int offset) => new FileLocation._(this, offset);
+
+  /// Gets the 0-based line corresponding to [offset].
+  int getLine(int offset) {
+    if (offset < 0) {
+      throw new RangeError("Offset may not be negative, was $offset.");
+    } else if (offset > length) {
+      throw new RangeError("Offset $offset must not be greater than the number "
+          "of characters in the file, $length.");
+    }
+    return binarySearch(_lineStarts, (o) => o > offset) - 1;
+  }
+
+  /// Gets the 0-based column corresponding to [offset].
+  ///
+  /// If [line] is passed, it's assumed to be the line containing [offset] and
+  /// is used to more efficiently compute the column.
+  int getColumn(int offset, {int line}) {
+    if (offset < 0) {
+      throw new RangeError("Offset may not be negative, was $offset.");
+    } else if (offset > length) {
+      throw new RangeError("Offset $offset must be not be greater than the "
+          "number of characters in the file, $length.");
+    }
+
+    if (line == null) {
+      line = getLine(offset);
+    } else if (line < 0) {
+      throw new RangeError("Line may not be negative, was $line.");
+    } else if (line >= lines) {
+      throw new RangeError("Line $line must be less than the number of "
+          "lines in the file, $lines.");
+    }
+
+    var lineStart = _lineStarts[line];
+    if (lineStart > offset) {
+      throw new RangeError("Line $line comes after offset $offset.");
+    }
+
+    return offset - lineStart;
+  }
+
+  /// Gets the offset for a [line] and [column].
+  ///
+  /// [column] defaults to 0.
+  int getOffset(int line, [int column]) {
+    if (column == null) column = 0;
+
+    if (line < 0) {
+      throw new RangeError("Line may not be negative, was $line.");
+    } else if (line >= lines) {
+      throw new RangeError("Line $line must be less than the number of "
+          "lines in the file, $lines.");
+    } else if (column < 0) {
+      throw new RangeError("Column may not be negative, was $column.");
+    }
+
+    var result = _lineStarts[line] + column;
+    if (result > length ||
+        (line + 1 < lines && result >= _lineStarts[line + 1])) {
+      throw new RangeError("Line $line doesn't have $column columns.");
+    }
+
+    return result;
+  }
+
+  /// Returns the text of the file from [start] to [end] (exclusive).
+  ///
+  /// If [end] isn't passed, it defaults to the end of the file.
+  String getText(int start, [int end]) =>
+      new String.fromCharCodes(_decodedChars.sublist(start, end));
+}
+
+/// A [SourceLocation] within a [SourceFile].
+///
+/// Unlike the base [SourceLocation], [FileLocation] lazily computes its line
+/// and column values based on its offset and the contents of [file].
+///
+/// A [FileLocation] can be created using [SourceFile.location].
+class FileLocation extends SourceLocation {
+  /// The [file] that [this] belongs to.
+  final SourceFile file;
+
+  Uri get sourceUrl => file.url;
+  int get line => file.getLine(offset);
+  int get column => file.getColumn(offset);
+
+  FileLocation._(this.file, int offset)
+      : super(offset) {
+    if (offset > file.length) {
+      throw new RangeError("Offset $offset must not be greater than the number "
+          "of characters in the file, ${file.length}.");
+    }
+  }
+
+  FileSpan pointSpan() => new FileSpan._(file, offset, offset);
+}
+
+/// A [SourceSpan] within a [SourceFile].
+///
+/// Unlike the base [SourceSpan], [FileSpan] lazily computes its line and column
+/// values based on its offset and the contents of [file]. [FileSpan.message] is
+/// also able to provide more context then [SourceSpan.message], and
+/// [FileSpan.union] will return a [FileSpan] if possible.
+///
+/// A [FileSpan] can be created using [SourceFile.span].
+class FileSpan extends SourceSpanMixin implements SourceSpanWithContext {
+  /// The [file] that [this] belongs to.
+  final SourceFile file;
+
+  /// The offset of the beginning of the span.
+  ///
+  /// [start] is lazily generated from this to avoid allocating unnecessary
+  /// objects.
+  final int _start;
+
+  /// The offset of the end of the span.
+  ///
+  /// [end] is lazily generated from this to avoid allocating unnecessary
+  /// objects.
+  final int _end;
+
+  Uri get sourceUrl => file.url;
+  int get length => _end - _start;
+  FileLocation get start => new FileLocation._(file, _start);
+  FileLocation get end => new FileLocation._(file, _end);
+  String get text => file.getText(_start, _end);
+  String get context => file.getText(file.getOffset(start.line),
+      end.line == file.lines - 1 ? null : file.getOffset(end.line + 1));
+
+  FileSpan._(this.file, this._start, this._end) {
+    if (_end < _start) {
+      throw new ArgumentError('End $_end must come after start $_start.');
+    } else if (_end > file.length) {
+      throw new RangeError("End $_end must not be greater than the number "
+          "of characters in the file, ${file.length}.");
+    } else if (_start < 0) {
+      throw new RangeError("Start may not be negative, was $_start.");
+    }
+  }
+
+  int compareTo(SourceSpan other) {
+    if (other is! FileSpan) return super.compareTo(other);
+
+    FileSpan otherFile = other;
+    var result = _start.compareTo(otherFile._start);
+    return result == 0 ? _end.compareTo(otherFile._end) : result;
+  }
+
+  SourceSpan union(SourceSpan other) {
+    if (other is! FileSpan) return super.union(other);
+
+    var span = expand(other);
+    var beginSpan = span._start == _start ? this : other;
+    var endSpan = span._end == _end ? this : other;
+
+    if (beginSpan._end < endSpan._start) {
+      throw new ArgumentError("Spans $this and $other are disjoint.");
+    }
+
+    return span;
+  }
+
+  bool operator ==(other) {
+    if (other is! FileSpan) return super == other;
+    return _start == other._start && _end == other._end &&
+        sourceUrl == other.sourceUrl;
+  }
+
+  int get hashCode => _start.hashCode + 5 * _end.hashCode +
+      7 * sourceUrl.hashCode;
+
+  /// Returns a new span that covers both [this] and [other].
+  ///
+  /// Unlike [union], [other] may be disjoint from [this]. If it is, the text
+  /// between the two will be covered by the returned span.
+  FileSpan expand(FileSpan other) {
+    if (sourceUrl != other.sourceUrl) {
+      throw new ArgumentError("Source URLs \"${sourceUrl}\" and "
+          " \"${other.sourceUrl}\" don't match.");
+    }
+
+    var start = math.min(this._start, other._start);
+    var end = math.max(this._end, other._end);
+    return new FileSpan._(file, start, end);
+  }
+}
diff --git a/source_span/lib/src/location.dart b/source_span/lib/src/location.dart
new file mode 100644
index 0000000..2705742
--- /dev/null
+++ b/source_span/lib/src/location.dart
@@ -0,0 +1,87 @@
+// 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 source_span.location;
+
+import 'span.dart';
+
+// A class that describes a single location within a source file.
+class SourceLocation implements Comparable<SourceLocation> {
+  /// URL of the source containing this location.
+  ///
+  /// This may be null, indicating that the source URL is unknown or
+  /// unavailable.
+  final Uri sourceUrl;
+
+  /// The 0-based offset of this location in the source.
+  final int offset;
+
+  /// The 0-based line of this location in the source.
+  final int line;
+
+  /// The 0-based column of this location in the source
+  final int column;
+
+  /// Returns a representation of this location in the `source:line:column`
+  /// format used by text editors.
+  ///
+  /// This prints 1-based lines and columns.
+  String get toolString {
+    var source = sourceUrl == null ? 'unknown source' : sourceUrl;
+    return '$source:${line + 1}:${column + 1}';
+  }
+
+  /// Creates a new location indicating [offset] within [sourceUrl].
+  ///
+  /// [line] and [column] default to assuming the source is a single line. This
+  /// means that [line] defaults to 0 and [column] defaults to [offset].
+  ///
+  /// [sourceUrl] may be either a [String], a [Uri], or `null`.
+  SourceLocation(int offset, {sourceUrl, int line, int column})
+      : sourceUrl = sourceUrl is String ? Uri.parse(sourceUrl) : sourceUrl,
+        offset = offset,
+        line = line == null ? 0 : line,
+        column = column == null ? offset : column {
+    if (this.offset < 0) {
+      throw new RangeError("Offset may not be negative, was $offset.");
+    } else if (this.line < 0) {
+      throw new RangeError("Line may not be negative, was $line.");
+    } else if (this.column < 0) {
+      throw new RangeError("Column may not be negative, was $column.");
+    }
+  }
+
+  /// Returns the distance in characters between [this] and [other].
+  ///
+  /// This always returns a non-negative value.
+  int distance(SourceLocation other) {
+    if (sourceUrl != other.sourceUrl) {
+      throw new ArgumentError("Source URLs \"${sourceUrl}\" and "
+          "\"${other.sourceUrl}\" don't match.");
+    }
+    return (offset - other.offset).abs();
+  }
+
+  /// Returns a span that covers only a single point: this location.
+  SourceSpan pointSpan() => new SourceSpan(this, this, "");
+
+  /// Compares two locations.
+  ///
+  /// [other] must have the same source URL as [this].
+  int compareTo(SourceLocation other) {
+    if (sourceUrl != other.sourceUrl) {
+      throw new ArgumentError("Source URLs \"${sourceUrl}\" and "
+          "\"${other.sourceUrl}\" don't match.");
+    }
+    return offset - other.offset;
+  }
+
+  bool operator ==(other) =>
+      other is SourceLocation && sourceUrl == other.sourceUrl &&
+      offset == other.offset;
+
+  int get hashCode => sourceUrl.hashCode + offset;
+
+  String toString() => '<$runtimeType: $offset $toolString>';
+}
diff --git a/source_span/lib/src/span.dart b/source_span/lib/src/span.dart
new file mode 100644
index 0000000..9f15048
--- /dev/null
+++ b/source_span/lib/src/span.dart
@@ -0,0 +1,78 @@
+// 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 source_span.span;
+
+import 'location.dart';
+import 'span_mixin.dart';
+
+/// A class that describes a segment of source text.
+abstract class SourceSpan implements Comparable<SourceSpan> {
+  /// The start location of this span.
+  final SourceLocation start;
+
+  /// The end location of this span, exclusive.
+  final SourceLocation end;
+
+  /// The source text for this span.
+  final String text;
+
+  /// The URL of the source (typically a file) of this span.
+  ///
+  /// This may be null, indicating that the source URL is unknown or
+  /// unavailable.
+  final Uri sourceUrl;
+
+  /// The length of this span, in characters.
+  final int length;
+
+  /// Creates a new span from [start] to [end] (exclusive) containing [text].
+  ///
+  /// [start] and [end] must have the same source URL and [start] must come
+  /// before [end]. [text] must have a number of characters equal to the
+  /// distance between [start] and [end].
+  factory SourceSpan(SourceLocation start, SourceLocation end, String text) =>
+      new SourceSpanBase(start, end, text);
+
+  /// Creates a new span that's the union of [this] and [other].
+  ///
+  /// The two spans must have the same source URL and may not be disjoint.
+  /// [text] is computed by combining [this.text] and [other.text].
+  SourceSpan union(SourceSpan other);
+
+  /// Compares two spans.
+  ///
+  /// [other] must have the same source URL as [this]. This orders spans by
+  /// [start] then [length].
+  int compareTo(SourceSpan other);
+
+  /// Formats [message] in a human-friendly way associated with this span.
+  ///
+  /// [color] may either be a [String], a [bool], or `null`. If it's a string,
+  /// it indicates an ANSII terminal color escape that should be used to
+  /// highlight the span's text. If it's `true`, it indicates that the text
+  /// should be highlighted using the default color. If it's `false` or `null`,
+  /// it indicates that the text shouldn't be highlighted.
+  String message(String message, {color});
+}
+
+/// A base class for source spans with [start], [end], and [text] known at
+/// construction time.
+class SourceSpanBase extends SourceSpanMixin {
+  final SourceLocation start;
+  final SourceLocation end;
+  final String text;
+
+  SourceSpanBase(this.start, this.end, this.text) {
+    if (end.sourceUrl != start.sourceUrl) {
+      throw new ArgumentError("Source URLs \"${start.sourceUrl}\" and "
+          " \"${end.sourceUrl}\" don't match.");
+    } else if (end.offset < start.offset) {
+      throw new ArgumentError('End $end must come after start $start.');
+    } else if (text.length != start.distance(end)) {
+      throw new ArgumentError('Text "$text" must be ${start.distance(end)} '
+          'characters long.');
+    }
+  }
+}
diff --git a/source_span/lib/src/span_exception.dart b/source_span/lib/src/span_exception.dart
new file mode 100644
index 0000000..36f2488
--- /dev/null
+++ b/source_span/lib/src/span_exception.dart
@@ -0,0 +1,43 @@
+// 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 source_span.span_exception;
+
+import 'span.dart';
+
+/// A class for exceptions that have source span information attached.
+class SourceSpanException implements Exception {
+  /// A message describing the exception.
+  final String message;
+
+  /// The span associated with this exception.
+  ///
+  /// This may be `null` if the source location can't be determined.
+  final SourceSpan span;
+
+  SourceSpanException(this.message, this.span);
+
+  /// Returns a string representation of [this].
+  ///
+  /// [color] may either be a [String], a [bool], or `null`. If it's a string,
+  /// it indicates an ANSII terminal color escape that should be used to
+  /// highlight the span's text. If it's `true`, it indicates that the text
+  /// should be highlighted using the default color. If it's `false` or `null`,
+  /// it indicates that the text shouldn't be highlighted.
+  String toString({color}) {
+    if (span == null) return message;
+    return "Error on " + span.message(message, color: color);
+  }
+}
+
+/// A [SourceSpanException] that's also a [FormatException].
+class SourceSpanFormatException extends SourceSpanException
+    implements FormatException {
+  final source;
+
+  int get offset => span == null ? null : span.start.offset;
+
+  SourceSpanFormatException(String message, SourceSpan span, [this.source])
+      : super(message, span);
+}
diff --git a/source_span/lib/src/span_mixin.dart b/source_span/lib/src/span_mixin.dart
new file mode 100644
index 0000000..b4503fa
--- /dev/null
+++ b/source_span/lib/src/span_mixin.dart
@@ -0,0 +1,106 @@
+// 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 source_span.span_mixin;
+
+import 'dart:math' as math;
+import 'package:path/path.dart' as p;
+
+import 'colors.dart' as colors;
+import 'span.dart';
+import 'span_with_context.dart';
+import 'utils.dart';
+
+/// A mixin for easily implementing [SourceSpan].
+///
+/// This implements the [SourceSpan] methods in terms of [start], [end], and
+/// [text]. This assumes that [start] and [end] have the same source URL, that
+/// [start] comes before [end], and that [text] has a number of characters equal
+/// to the distance between [start] and [end].
+abstract class SourceSpanMixin implements SourceSpan {
+  Uri get sourceUrl => start.sourceUrl;
+  int get length => end.offset - start.offset;
+
+  int compareTo(SourceSpan other) {
+    var result = start.compareTo(other.start);
+    return result == 0 ? end.compareTo(other.end) : result;
+  }
+
+  SourceSpan union(SourceSpan other) {
+    if (sourceUrl != other.sourceUrl) {
+      throw new ArgumentError("Source URLs \"${sourceUrl}\" and "
+          " \"${other.sourceUrl}\" don't match.");
+    }
+
+    var start = min(this.start, other.start);
+    var end = max(this.end, other.end);
+    var beginSpan = start == this.start ? this : other;
+    var endSpan = end == this.end ? this : other;
+
+    if (beginSpan.end.compareTo(endSpan.start) < 0) {
+      throw new ArgumentError("Spans $this and $other are disjoint.");
+    }
+
+    var text = beginSpan.text +
+        endSpan.text.substring(beginSpan.end.distance(endSpan.start));
+    return new SourceSpan(start, end, text);
+  }
+
+  String message(String message, {color}) {
+    if (color == true) color = colors.RED;
+    if (color == false) color = null;
+
+    var line = start.line;
+    var column = start.column;
+
+    var buffer = new StringBuffer();
+    buffer.write('line ${line + 1}, column ${column + 1}');
+    if (sourceUrl != null) buffer.write(' of ${p.prettyUri(sourceUrl)}');
+    buffer.write(': $message');
+
+    if (length == 0 && this is! SourceSpanWithContext) return buffer.toString();
+    buffer.write("\n");
+
+    var textLine;
+    if (this is SourceSpanWithContext) {
+      var context = (this as SourceSpanWithContext).context;
+      var lineStart = findLineStart(context, text, column);
+      if (lineStart != null && lineStart > 0) {
+        buffer.write(context.substring(0, lineStart));
+        context = context.substring(lineStart);
+      }
+      var endIndex = context.indexOf('\n');
+      textLine = endIndex == -1 ? context : context.substring(0, endIndex + 1);
+      column = math.min(column, textLine.length - 1);
+    } else {
+      textLine = text.split("\n").first;
+      column = 0;
+    }
+
+    var toColumn =
+        math.min(column + end.offset - start.offset, textLine.length);
+    if (color != null) {
+      buffer.write(textLine.substring(0, column));
+      buffer.write(color);
+      buffer.write(textLine.substring(column, toColumn));
+      buffer.write(colors.NONE);
+      buffer.write(textLine.substring(toColumn));
+    } else {
+      buffer.write(textLine);
+    }
+    if (!textLine.endsWith('\n')) buffer.write('\n');
+    buffer.write(' ' * column);
+    if (color != null) buffer.write(color);
+    buffer.write('^' * math.max(toColumn - column, 1));
+    if (color != null) buffer.write(colors.NONE);
+    return buffer.toString();
+  }
+
+  bool operator ==(other) => other is SourceSpan &&
+      start == other.start && end == other.end;
+
+  int get hashCode => start.hashCode + (31 * end.hashCode);
+
+  String toString() => '<$runtimeType: from $start to $end "$text">';
+}
diff --git a/source_span/lib/src/span_with_context.dart b/source_span/lib/src/span_with_context.dart
new file mode 100644
index 0000000..0012e3f
--- /dev/null
+++ b/source_span/lib/src/span_with_context.dart
@@ -0,0 +1,37 @@
+// 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 source_span.span_with_context;
+
+import 'location.dart';
+import 'span.dart';
+import 'utils.dart';
+
+/// A class that describes a segment of source text with additional context.
+class SourceSpanWithContext extends SourceSpanBase {
+  /// Text around the span, which includes the line containing this span.
+  final String context;
+
+  /// Creates a new span from [start] to [end] (exclusive) containing [text], in
+  /// the given [context].
+  ///
+  /// [start] and [end] must have the same source URL and [start] must come
+  /// before [end]. [text] must have a number of characters equal to the
+  /// distance between [start] and [end]. [context] must contain [text], and
+  /// [text] should start at `start.column` from the beginning of a line in
+  /// [context].
+  SourceSpanWithContext(
+      SourceLocation start, SourceLocation end, String text, this.context)
+      : super(start, end, text) {
+    if (!context.contains(text)) {
+      throw new ArgumentError(
+          'The context line "$context" must contain "$text".');
+    }
+
+    if (findLineStart(context, text, start.column) == null) {
+      throw new ArgumentError('The span text "$text" must start at '
+          'column ${start.column + 1} in a line within "$context".');
+    }
+  }
+}
diff --git a/source_span/lib/src/utils.dart b/source_span/lib/src/utils.dart
new file mode 100644
index 0000000..2d33865
--- /dev/null
+++ b/source_span/lib/src/utils.dart
@@ -0,0 +1,56 @@
+// 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 source_span.utils;
+
+/// Returns the minimum of [obj1] and [obj2] according to
+/// [Comparable.compareTo].
+Comparable min(Comparable obj1, Comparable obj2) =>
+    obj1.compareTo(obj2) > 0 ? obj2 : obj1;
+
+/// Returns the maximum of [obj1] and [obj2] according to
+/// [Comparable.compareTo].
+Comparable max(Comparable obj1, Comparable obj2) =>
+    obj1.compareTo(obj2) > 0 ? obj1 : obj2;
+
+/// Find the first entry in a sorted [list] that matches a monotonic predicate.
+///
+/// Given a result `n`, that all items before `n` will not match, `n` matches,
+/// and all items after `n` match too. The result is -1 when there are no
+/// items, 0 when all items match, and list.length when none does.
+int binarySearch(List list, bool matches(item)) {
+  if (list.length == 0) return -1;
+  if (matches(list.first)) return 0;
+  if (!matches(list.last)) return list.length;
+
+  int min = 0;
+  int max = list.length - 1;
+  while (min < max) {
+    var half = min + ((max - min) ~/ 2);
+    if (matches(list[half])) {
+      max = half;
+    } else {
+      min = half + 1;
+    }
+  }
+  return max;
+}
+
+/// Finds a line in [context] containing [text] at the specified [column].
+///
+/// Returns the index in [context] where that line begins, or null if none
+/// exists.
+int findLineStart(String context, String text, int column) {
+  var isEmpty = text == '';
+  var index = context.indexOf(text);
+  while (index != -1) {
+    var lineStart = context.lastIndexOf('\n', index) + 1;
+    var textColumn = index - lineStart;
+    if (column == textColumn || (isEmpty && column == textColumn + 1)) {
+      return lineStart;
+    }
+    index = context.indexOf(text, index + 1);
+  }
+  return null;
+}
diff --git a/source_span/pubspec.yaml b/source_span/pubspec.yaml
new file mode 100644
index 0000000..16eee7c
--- /dev/null
+++ b/source_span/pubspec.yaml
@@ -0,0 +1,11 @@
+name: source_span
+version: 1.1.2
+author: Dart Team <misc@dartlang.org>
+description: A library for identifying source spans and locations.
+homepage: http://github.com/dart-lang/source_span
+dependencies:
+  path: '>=1.2.0 <2.0.0'
+environment:
+  sdk: '>=0.8.10+6 <2.0.0'
+dev_dependencies:
+  unittest: '>=0.9.0 <0.12.0'
diff --git a/stack_trace/lib/src/chain.dart b/stack_trace/lib/src/chain.dart
new file mode 100644
index 0000000..87cc6be
--- /dev/null
+++ b/stack_trace/lib/src/chain.dart
@@ -0,0 +1,200 @@
+// Copyright (c) 2013, 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 stack_trace.chain;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:math' as math;
+
+import 'frame.dart';
+import 'stack_zone_specification.dart';
+import 'trace.dart';
+import 'utils.dart';
+
+/// A function that handles errors in the zone wrapped by [Chain.capture].
+typedef void ChainHandler(error, Chain chain);
+
+/// The line used in the string representation of stack chains to represent
+/// the gap between traces.
+const _gap = '===== asynchronous gap ===========================\n';
+
+/// A chain of stack traces.
+///
+/// A stack chain is a collection of one or more stack traces that collectively
+/// represent the path from [main] through nested function calls to a particular
+/// code location, usually where an error was thrown. Multiple stack traces are
+/// necessary when using asynchronous functions, since the program's stack is
+/// reset before each asynchronous callback is run.
+///
+/// Stack chains can be automatically tracked using [Chain.capture]. This sets
+/// up a new [Zone] in which the current stack chain is tracked and can be
+/// accessed using [new Chain.current]. Any errors that would be top-leveled in
+/// the zone can be handled, along with their associated chains, with the
+/// `onError` callback. For example:
+///
+///     Chain.capture(() {
+///       // ...
+///     }, onError: (error, stackChain) {
+///       print("Caught error $error\n"
+///             "$stackChain");
+///     });
+class Chain implements StackTrace {
+
+  /// The stack traces that make up this chain.
+  ///
+  /// Like the frames in a stack trace, the traces are ordered from most local
+  /// to least local. The first one is the trace where the actual exception was
+  /// raised, the second one is where that callback was scheduled, and so on.
+  final List<Trace> traces;
+
+  /// The [StackZoneSpecification] for the current zone.
+  static StackZoneSpecification get _currentSpec =>
+    Zone.current[#stack_trace.stack_zone.spec];
+
+  /// Runs [callback] in a [Zone] in which the current stack chain is tracked
+  /// and automatically associated with (most) errors.
+  ///
+  /// If [onError] is passed, any error in the zone that would otherwise go
+  /// unhandled is passed to it, along with the [Chain] associated with that
+  /// error. Note that if [callback] produces multiple unhandled errors,
+  /// [onError] may be called more than once. If [onError] isn't passed, the
+  /// parent Zone's `unhandledErrorHandler` will be called with the error and
+  /// its chain.
+  ///
+  /// Note that even if [onError] isn't passed, this zone will still be an error
+  /// zone. This means that any errors that would cross the zone boundary are
+  /// considered unhandled.
+  ///
+  /// If [callback] returns a value, it will be returned by [capture] as well.
+  ///
+  /// Currently, capturing stack chains doesn't work when using dart2js due to
+  /// issues [15171] and [15105]. Stack chains reported on dart2js will contain
+  /// only one trace.
+  ///
+  /// [15171]: https://code.google.com/p/dart/issues/detail?id=15171
+  /// [15105]: https://code.google.com/p/dart/issues/detail?id=15105
+  static capture(callback(), {ChainHandler onError}) {
+    var spec = new StackZoneSpecification(onError);
+    return runZoned(() {
+      try {
+        return callback();
+      } catch (error, stackTrace) {
+        // TODO(nweiz): Don't special-case this when issue 19566 is fixed.
+        return Zone.current.handleUncaughtError(error, stackTrace);
+      }
+    }, zoneSpecification: spec.toSpec(), zoneValues: {
+      #stack_trace.stack_zone.spec: spec
+    });
+  }
+
+  /// Returns [futureOrStream] unmodified.
+  ///
+  /// Prior to Dart 1.7, this was necessary to ensure that stack traces for
+  /// exceptions reported with [Completer.completeError] and
+  /// [StreamController.addError] were tracked correctly.
+  @Deprecated("Chain.track is not necessary in Dart 1.7+.")
+  static track(futureOrStream) => futureOrStream;
+
+  /// Returns the current stack chain.
+  ///
+  /// By default, the first frame of the first trace will be the line where
+  /// [Chain.current] is called. If [level] is passed, the first trace will
+  /// start that many frames up instead.
+  ///
+  /// If this is called outside of a [capture] zone, it just returns a
+  /// single-trace chain.
+  factory Chain.current([int level=0]) {
+    if (_currentSpec != null) return _currentSpec.currentChain(level + 1);
+    return new Chain([new Trace.current(level + 1)]);
+  }
+
+  /// Returns the stack chain associated with [trace].
+  ///
+  /// The first stack trace in the returned chain will always be [trace]
+  /// (converted to a [Trace] if necessary). If there is no chain associated
+  /// with [trace] or if this is called outside of a [capture] zone, this just
+  /// returns a single-trace chain containing [trace].
+  ///
+  /// If [trace] is already a [Chain], it will be returned as-is.
+  factory Chain.forTrace(StackTrace trace) {
+    if (trace is Chain) return trace;
+    if (_currentSpec == null) return new Chain([new Trace.from(trace)]);
+    return _currentSpec.chainFor(trace);
+  }
+
+  /// Parses a string representation of a stack chain.
+  ///
+  /// Specifically, this parses the output of [Chain.toString].
+  factory Chain.parse(String chain) =>
+    new Chain(chain.split(_gap).map((trace) => new Trace.parseFriendly(trace)));
+
+  /// Returns a new [Chain] comprised of [traces].
+  Chain(Iterable<Trace> traces)
+      : traces = new UnmodifiableListView<Trace>(traces.toList());
+
+  /// Returns a terser version of [this].
+  ///
+  /// This calls [Trace.terse] on every trace in [traces], and discards any
+  /// trace that contain only internal frames.
+  Chain get terse => foldFrames((_) => false, terse: true);
+
+  /// Returns a new [Chain] based on [this] where multiple stack frames matching
+  /// [predicate] are folded together.
+  ///
+  /// This means that whenever there are multiple frames in a row that match
+  /// [predicate], only the last one is kept. In addition, traces that are
+  /// composed entirely of frames matching [predicate] are omitted.
+  ///
+  /// This is useful for limiting the amount of library code that appears in a
+  /// stack trace by only showing user code and code that's called by user code.
+  ///
+  /// If [terse] is true, this will also fold together frames from the core
+  /// library or from this package, and simplify core library frames as in
+  /// [Trace.terse].
+  Chain foldFrames(bool predicate(Frame frame), {bool terse: false}) {
+    var foldedTraces = traces.map(
+        (trace) => trace.foldFrames(predicate, terse: terse));
+    var nonEmptyTraces = foldedTraces.where((trace) {
+      // Ignore traces that contain only folded frames.
+      if (trace.frames.length > 1) return true;
+
+      // In terse mode, the trace may have removed an outer folded frame,
+      // leaving a single non-folded frame. We can detect a folded frame because
+      // it has no line information.
+      if (!terse) return false;
+      return trace.frames.single.line != null;
+    });
+
+    // If all the traces contain only internal processing, preserve the last
+    // (top-most) one so that the chain isn't empty.
+    if (nonEmptyTraces.isEmpty && foldedTraces.isNotEmpty) {
+      return new Chain([foldedTraces.last]);
+    }
+
+    return new Chain(nonEmptyTraces);
+  }
+
+  /// Converts [this] to a [Trace].
+  ///
+  /// The trace version of a chain is just the concatenation of all the traces
+  /// in the chain.
+  Trace toTrace() => new Trace(flatten(traces.map((trace) => trace.frames)));
+
+  String toString() {
+    // Figure out the longest path so we know how much to pad.
+    var longest = traces.map((trace) {
+      return trace.frames.map((frame) => frame.location.length)
+          .fold(0, math.max);
+    }).fold(0, math.max);
+
+    // Don't call out to [Trace.toString] here because that doesn't ensure that
+    // padding is consistent across all traces.
+    return traces.map((trace) {
+      return trace.frames.map((frame) {
+        return '${padRight(frame.location, longest)}  ${frame.member}\n';
+      }).join();
+    }).join(_gap);
+  }
+}
diff --git a/stack_trace/lib/src/frame.dart b/stack_trace/lib/src/frame.dart
new file mode 100644
index 0000000..9043d2b
--- /dev/null
+++ b/stack_trace/lib/src/frame.dart
@@ -0,0 +1,296 @@
+// Copyright (c) 2013, 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 frame;
+
+
+import 'package:path/path.dart' as path;
+
+import 'trace.dart';
+
+// #1      Foo._bar (file:///home/nweiz/code/stuff.dart:42:21)
+// #1      Foo._bar (file:///home/nweiz/code/stuff.dart:42)
+// #1      Foo._bar (file:///home/nweiz/code/stuff.dart)
+final _vmFrame = new RegExp(r'^#\d+\s+(\S.*) \((.+?)((?::\d+){0,2})\)$');
+
+//     at VW.call$0 (http://pub.dartlang.org/stuff.dart.js:560:28)
+//     at VW.call$0 (eval as fn
+//         (http://pub.dartlang.org/stuff.dart.js:560:28), efn:3:28)
+//     at http://pub.dartlang.org/stuff.dart.js:560:28
+final _v8Frame = new RegExp(
+    r'^\s*at (?:(\S.*?)(?: \[as [^\]]+\])? \((.*)\)|(.*))$');
+
+// http://pub.dartlang.org/stuff.dart.js:560:28
+final _v8UrlLocation = new RegExp(r'^(.*):(\d+):(\d+)$');
+
+// eval as function (http://pub.dartlang.org/stuff.dart.js:560:28), efn:3:28
+// eval as function (http://pub.dartlang.org/stuff.dart.js:560:28)
+// eval as function (eval as otherFunction
+//     (http://pub.dartlang.org/stuff.dart.js:560:28))
+final _v8EvalLocation = new RegExp(
+    r'^eval at (?:\S.*?) \((.*)\)(?:, .*?:\d+:\d+)?$');
+
+// .VW.call$0@http://pub.dartlang.org/stuff.dart.js:560
+// .VW.call$0("arg")@http://pub.dartlang.org/stuff.dart.js:560
+// .VW.call$0/name<@http://pub.dartlang.org/stuff.dart.js:560
+// .VW.call$0@http://pub.dartlang.org/stuff.dart.js:560:36
+// http://pub.dartlang.org/stuff.dart.js:560
+final _firefoxSafariFrame = new RegExp(
+    r'^'
+    r'(?:' // Member description. Not present in some Safari frames.
+      r'([^@(/]*)' // The actual name of the member.
+      r'(?:\(.*\))?' // Arguments to the member, sometimes captured by Firefox.
+      r'((?:/[^/]*)*)' // Extra characters indicating a nested closure.
+      r'(?:\(.*\))?' // Arguments to the closure.
+      r'@'
+    r')?'
+    r'(.*?)' // The frame's URL.
+    r':'
+    r'(\d*)' // The line number. Empty in Safari if it's unknown.
+    r'(?::(\d*))?' // The column number. Not present in older browsers and
+                   // empty in Safari if it's unknown.
+    r'$');
+
+// foo/bar.dart 10:11 in Foo._bar
+// http://dartlang.org/foo/bar.dart in Foo._bar
+final _friendlyFrame = new RegExp(
+    r'^(\S+)(?: (\d+)(?::(\d+))?)?\s+([^\d]\S*)$');
+
+/// A regular expression that matches asynchronous member names generated by the
+/// VM.
+final _asyncBody = new RegExp(r'<(<anonymous closure>|[^>]+)_async_body>');
+
+final _initialDot = new RegExp(r"^\.");
+
+/// A single stack frame. Each frame points to a precise location in Dart code.
+class Frame {
+  /// The URI of the file in which the code is located.
+  ///
+  /// This URI will usually have the scheme `dart`, `file`, `http`, or `https`.
+  final Uri uri;
+
+  /// The line number on which the code location is located.
+  ///
+  /// This can be null, indicating that the line number is unknown or
+  /// unimportant.
+  final int line;
+
+  /// The column number of the code location.
+  ///
+  /// This can be null, indicating that the column number is unknown or
+  /// unimportant.
+  final int column;
+
+  /// The name of the member in which the code location occurs.
+  ///
+  /// Anonymous closures are represented as `<fn>` in this member string.
+  final String member;
+
+  /// Whether this stack frame comes from the Dart core libraries.
+  bool get isCore => uri.scheme == 'dart';
+
+  /// Returns a human-friendly description of the library that this stack frame
+  /// comes from.
+  ///
+  /// This will usually be the string form of [uri], but a relative URI will be
+  /// used if possible.
+  String get library => path.prettyUri(uri);
+
+  /// Returns the name of the package this stack frame comes from, or `null` if
+  /// this stack frame doesn't come from a `package:` URL.
+  String get package {
+    if (uri.scheme != 'package') return null;
+    return uri.path.split('/').first;
+  }
+
+  /// A human-friendly description of the code location.
+  String get location {
+    if (line == null) return library;
+    if (column == null) return '$library $line';
+    return '$library $line:$column';
+  }
+
+  /// Returns a single frame of the current stack.
+  ///
+  /// By default, this will return the frame above the current method. If
+  /// [level] is `0`, it will return the current method's frame; if [level] is
+  /// higher than `1`, it will return higher frames.
+  factory Frame.caller([int level=1]) {
+    if (level < 0) {
+      throw new ArgumentError("Argument [level] must be greater than or equal "
+          "to 0.");
+    }
+
+    return new Trace.current(level + 1).frames.first;
+  }
+
+  /// Parses a string representation of a Dart VM stack frame.
+  factory Frame.parseVM(String frame) {
+    // The VM sometimes folds multiple stack frames together and replaces them
+    // with "...".
+    if (frame == '...') {
+      return new Frame(new Uri(), null, null, '...');
+    }
+
+    var match = _vmFrame.firstMatch(frame);
+    if (match == null) {
+      throw new FormatException("Couldn't parse VM stack trace line '$frame'.");
+    }
+
+    // Get the pieces out of the regexp match. Function, URI and line should
+    // always be found. The column is optional.
+    var member = match[1]
+        .replaceAll(_asyncBody, "<async>")
+        .replaceAll("<anonymous closure>", "<fn>");
+    var uri = Uri.parse(match[2]);
+
+    var lineAndColumn = match[3].split(':');
+    var line = lineAndColumn.length > 1 ? int.parse(lineAndColumn[1]) : null;
+    var column = lineAndColumn.length > 2 ? int.parse(lineAndColumn[2]) : null;
+    return new Frame(uri, line, column, member);
+  }
+
+  /// Parses a string representation of a Chrome/V8 stack frame.
+  factory Frame.parseV8(String frame) {
+    var match = _v8Frame.firstMatch(frame);
+    if (match == null) {
+      throw new FormatException("Couldn't parse V8 stack trace line '$frame'.");
+    }
+
+    // v8 location strings can be arbitrarily-nested, since it adds a layer of
+    // nesting for each eval performed on that line.
+    parseLocation(location, member) {
+      var evalMatch = _v8EvalLocation.firstMatch(location);
+      while (evalMatch != null) {
+        location = evalMatch[1];
+        evalMatch = _v8EvalLocation.firstMatch(location);
+      }
+
+      var urlMatch = _v8UrlLocation.firstMatch(location);
+      if (urlMatch == null) {
+        throw new FormatException(
+            "Couldn't parse V8 stack trace line '$frame'.");
+      }
+
+      return new Frame(
+          _uriOrPathToUri(urlMatch[1]),
+          int.parse(urlMatch[2]),
+          int.parse(urlMatch[3]),
+          member);
+    }
+
+    // V8 stack frames can be in two forms.
+    if (match[2] != null) {
+      // The first form looks like " at FUNCTION (LOCATION)". V8 proper lists
+      // anonymous functions within eval as "<anonymous>", while IE10 lists them
+      // as "Anonymous function".
+      return parseLocation(match[2],
+          match[1].replaceAll("<anonymous>", "<fn>")
+                  .replaceAll("Anonymous function", "<fn>"));
+    } else {
+      // The second form looks like " at LOCATION", and is used for anonymous
+      // functions.
+      return parseLocation(match[3], "<fn>");
+    }
+  }
+
+  /// Parses a string representation of a JavaScriptCore stack trace.
+  factory Frame.parseJSCore(String frame) => new Frame.parseV8(frame);
+
+  /// Parses a string representation of an IE stack frame.
+  ///
+  /// IE10+ frames look just like V8 frames. Prior to IE10, stack traces can't
+  /// be retrieved.
+  factory Frame.parseIE(String frame) => new Frame.parseV8(frame);
+
+  /// Parses a string representation of a Firefox stack frame.
+  factory Frame.parseFirefox(String frame) {
+    var match = _firefoxSafariFrame.firstMatch(frame);
+    if (match == null) {
+      throw new FormatException(
+          "Couldn't parse Firefox/Safari stack trace line '$frame'.");
+    }
+
+    // Normally this is a URI, but in a jsshell trace it can be a path.
+    var uri = _uriOrPathToUri(match[3]);
+
+    var member;
+    if (match[1] != null) {
+      member = match[1];
+      member +=
+          new List.filled('/'.allMatches(match[2]).length, ".<fn>").join();
+      if (member == '') member = '<fn>';
+
+      // Some Firefox members have initial dots. We remove them for consistency
+      // with other platforms.
+      member = member.replaceFirst(_initialDot, '');
+    } else {
+      member = '<fn>';
+    }
+
+    var line = match[4] == '' ? null : int.parse(match[4]);
+    var column = match[5] == null || match[5] == '' ?
+        null : int.parse(match[5]);
+    return new Frame(uri, line, column, member);
+  }
+
+  /// Parses a string representation of a Safari 6.0 stack frame.
+  @Deprecated("Use Frame.parseSafari instead.")
+  factory Frame.parseSafari6_0(String frame) => new Frame.parseFirefox(frame);
+
+  /// Parses a string representation of a Safari 6.1+ stack frame.
+  @Deprecated("Use Frame.parseSafari instead.")
+  factory Frame.parseSafari6_1(String frame) => new Frame.parseFirefox(frame);
+
+  /// Parses a string representation of a Safari stack frame.
+  factory Frame.parseSafari(String frame) => new Frame.parseFirefox(frame);
+
+  /// Parses this package's string representation of a stack frame.
+  factory Frame.parseFriendly(String frame) {
+    var match = _friendlyFrame.firstMatch(frame);
+    if (match == null) {
+      throw new FormatException(
+          "Couldn't parse package:stack_trace stack trace line '$frame'.");
+    }
+
+    var uri = Uri.parse(match[1]);
+    // If there's no scheme, this is a relative URI. We should interpret it as
+    // relative to the current working directory.
+    if (uri.scheme == '') {
+      uri = path.toUri(path.absolute(path.fromUri(uri)));
+    }
+
+    var line = match[2] == null ? null : int.parse(match[2]);
+    var column = match[3] == null ? null : int.parse(match[3]);
+    return new Frame(uri, line, column, match[4]);
+  }
+
+  /// A regular expression matching an absolute URI.
+  static final _uriRegExp = new RegExp(r'^[a-zA-Z][-+.a-zA-Z\d]*://');
+
+  /// A regular expression matching a Windows path.
+  static final _windowsRegExp = new RegExp(r'^([a-zA-Z]:[\\/]|\\\\)');
+
+  /// Converts [uriOrPath], which can be a URI, a Windows path, or a Posix path,
+  /// to a URI (absolute if possible).
+  static Uri _uriOrPathToUri(String uriOrPath) {
+    if (uriOrPath.contains(_uriRegExp)) {
+      return Uri.parse(uriOrPath);
+    } else if (uriOrPath.contains(_windowsRegExp)) {
+      return new Uri.file(uriOrPath, windows: true);
+    } else if (uriOrPath.startsWith('/')) {
+      return new Uri.file(uriOrPath, windows: false);
+    }
+
+    // As far as I've seen, Firefox and V8 both always report absolute paths in
+    // their stack frames. However, if we do get a relative path, we should
+    // handle it gracefully.
+    if (uriOrPath.contains('\\')) return path.windows.toUri(uriOrPath);
+    return Uri.parse(uriOrPath);
+  }
+
+  Frame(this.uri, this.line, this.column, this.member);
+
+  String toString() => '$location in $member';
+}
diff --git a/stack_trace/lib/src/lazy_trace.dart b/stack_trace/lib/src/lazy_trace.dart
new file mode 100644
index 0000000..6bfe51c
--- /dev/null
+++ b/stack_trace/lib/src/lazy_trace.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2013, 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 lazy_trace;
+
+import 'frame.dart';
+import 'trace.dart';
+
+/// A thunk for lazily constructing a [Trace].
+typedef Trace TraceThunk();
+
+/// A wrapper around a [TraceThunk]. This works around issue 9579 by avoiding
+/// the conversion of native [StackTrace]s to strings until it's absolutely
+/// necessary.
+class LazyTrace implements Trace {
+  final TraceThunk _thunk;
+  Trace _inner;
+
+  LazyTrace(this._thunk);
+
+  Trace get _trace {
+    if (_inner == null) _inner = _thunk();
+    return _inner;
+  }
+
+  List<Frame> get frames => _trace.frames;
+  StackTrace get vmTrace => _trace.vmTrace;
+  Trace get terse => new LazyTrace(() => _trace.terse);
+  Trace foldFrames(bool predicate(Frame frame), {bool terse: false}) =>
+    new LazyTrace(() => _trace.foldFrames(predicate, terse: terse));
+  String toString() => _trace.toString();
+
+  // Work around issue 14075.
+  set frames(_) => throw new UnimplementedError();
+}
diff --git a/stack_trace/lib/src/stack_zone_specification.dart b/stack_trace/lib/src/stack_zone_specification.dart
new file mode 100644
index 0000000..0c1c7d2
--- /dev/null
+++ b/stack_trace/lib/src/stack_zone_specification.dart
@@ -0,0 +1,241 @@
+// Copyright (c) 2013, 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 stack_trace.stack_zone_specification;
+
+import 'dart:async';
+
+import 'trace.dart';
+import 'chain.dart';
+
+/// A class encapsulating the zone specification for a [Chain.capture] zone.
+///
+/// Until they're materialized and exposed to the user, stack chains are tracked
+/// as linked lists of [Trace]s using the [_Node] class. These nodes are stored
+/// in three distinct ways:
+///
+/// * When a callback is registered, a node is created and stored as a captured
+///   local variable until the callback is run.
+///
+/// * When a callback is run, its captured node is set as the [_currentNode] so
+///   it can be available to [Chain.current] and to be linked into additional
+///   chains when more callbacks are scheduled.
+///
+/// * When a callback throws an error or a Future or Stream emits an error, the
+///   current node is associated with that error's stack trace using the
+///   [_chains] expando.
+///
+/// Since [ZoneSpecification] can't be extended or even implemented, in order to
+/// get a real [ZoneSpecification] instance it's necessary to call [toSpec].
+class StackZoneSpecification {
+  /// The expando that associates stack chains with [StackTrace]s.
+  ///
+  /// The chains are associated with stack traces rather than errors themselves
+  /// because it's a common practice to throw strings as errors, which can't be
+  /// used with expandos.
+  ///
+  /// The chain associated with a given stack trace doesn't contain a node for
+  /// that stack trace.
+  final _chains = new Expando<_Node>("stack chains");
+
+  /// The error handler for the zone.
+  ///
+  /// If this is null, that indicates that any unhandled errors should be passed
+  /// to the parent zone.
+  final ChainHandler _onError;
+
+  /// The most recent node of the current stack chain.
+  _Node _currentNode;
+
+  StackZoneSpecification([this._onError]);
+
+  /// Converts [this] to a real [ZoneSpecification].
+  ZoneSpecification toSpec() {
+    return new ZoneSpecification(
+        handleUncaughtError: handleUncaughtError,
+        registerCallback: registerCallback,
+        registerUnaryCallback: registerUnaryCallback,
+        registerBinaryCallback: registerBinaryCallback,
+        errorCallback: errorCallback);
+  }
+
+  /// Returns the current stack chain.
+  ///
+  /// By default, the first frame of the first trace will be the line where
+  /// [currentChain] is called. If [level] is passed, the first trace will start
+  /// that many frames up instead.
+  Chain currentChain([int level=0]) => _createNode(level + 1).toChain();
+
+  /// Returns the stack chain associated with [trace], if one exists.
+  ///
+  /// The first stack trace in the returned chain will always be [trace]
+  /// (converted to a [Trace] if necessary). If there is no chain associated
+  /// with [trace], this just returns a single-trace chain containing [trace].
+  Chain chainFor(StackTrace trace) {
+    if (trace is Chain) return trace;
+    var previous = trace == null ? null : _chains[trace];
+    return new _Node(trace, previous).toChain();
+  }
+
+  /// Ensures that an error emitted by [future] has the correct stack
+  /// information associated with it.
+  ///
+  /// By default, the first frame of the first trace will be the line where
+  /// [trackFuture] is called. If [level] is passed, the first trace will start
+  /// that many frames up instead.
+  Future trackFuture(Future future, [int level=0]) {
+    var completer = new Completer.sync();
+    var node = _createNode(level + 1);
+    future.then(completer.complete).catchError((e, stackTrace) {
+      if (stackTrace == null) stackTrace = new Trace.current();
+      if (stackTrace is! Chain && _chains[stackTrace] == null) {
+        _chains[stackTrace] = node;
+      }
+      completer.completeError(e, stackTrace);
+    });
+    return completer.future;
+  }
+
+  /// Ensures that any errors emitted by [stream] have the correct stack
+  /// information associated with them.
+  ///
+  /// By default, the first frame of the first trace will be the line where
+  /// [trackStream] is called. If [level] is passed, the first trace will start
+  /// that many frames up instead.
+  Stream trackStream(Stream stream, [int level=0]) {
+    var node = _createNode(level + 1);
+    return stream.transform(new StreamTransformer.fromHandlers(
+        handleError: (error, stackTrace, sink) {
+      if (stackTrace == null) stackTrace = new Trace.current();
+      if (stackTrace is! Chain && _chains[stackTrace] == null) {
+        _chains[stackTrace] = node;
+      }
+      sink.addError(error, stackTrace);
+    }));
+  }
+
+  /// Tracks the current stack chain so it can be set to [_currentChain] when
+  /// [f] is run.
+  ZoneCallback registerCallback(Zone self, ZoneDelegate parent, Zone zone,
+      Function f) {
+    if (f == null) return parent.registerCallback(zone, null);
+    var node = _createNode(1);
+    return parent.registerCallback(zone, () => _run(f, node));
+  }
+
+  /// Tracks the current stack chain so it can be set to [_currentChain] when
+  /// [f] is run.
+  ZoneUnaryCallback registerUnaryCallback(Zone self, ZoneDelegate parent,
+      Zone zone, Function f) {
+    if (f == null) return parent.registerUnaryCallback(zone, null);
+    var node = _createNode(1);
+    return parent.registerUnaryCallback(zone, (arg) {
+      return _run(() => f(arg), node);
+    });
+  }
+
+  /// Tracks the current stack chain so it can be set to [_currentChain] when
+  /// [f] is run.
+  ZoneBinaryCallback registerBinaryCallback(Zone self, ZoneDelegate parent,
+      Zone zone, Function f) {
+    if (f == null) return parent.registerBinaryCallback(zone, null);
+    var node = _createNode(1);
+    return parent.registerBinaryCallback(zone, (arg1, arg2) {
+      return _run(() => f(arg1, arg2), node);
+    });
+  }
+
+  /// Looks up the chain associated with [stackTrace] and passes it either to
+  /// [_onError] or [parent]'s error handler.
+  handleUncaughtError(Zone self, ZoneDelegate parent, Zone zone, error,
+      StackTrace stackTrace) {
+    var stackChain = chainFor(stackTrace);
+    if (_onError == null) {
+      return parent.handleUncaughtError(zone, error, stackChain);
+    }
+
+    // TODO(nweiz): Currently this copies a lot of logic from [runZoned]. Just
+    // allow [runBinary] to throw instead once issue 18134 is fixed.
+    try {
+      return parent.runBinary(zone, _onError, error, stackChain);
+    } catch (newError, newStackTrace) {
+      if (identical(newError, error)) {
+        return parent.handleUncaughtError(zone, error, stackChain);
+      } else {
+        return parent.handleUncaughtError(zone, newError, newStackTrace);
+      }
+    }
+  }
+
+  /// Attaches the current stack chain to [stackTrace], replacing it if
+  /// necessary.
+  AsyncError errorCallback(Zone self, ZoneDelegate parent, Zone zone,
+      Object error, StackTrace stackTrace) {
+    var asyncError = parent.errorCallback(zone, error, stackTrace);
+    if (asyncError != null) {
+      error = asyncError.error;
+      stackTrace = asyncError.stackTrace;
+    }
+
+    // Go up two levels to get through [_CustomZone.errorCallback].
+    if (stackTrace == null) {
+      stackTrace = _createNode(2).toChain();
+    } else {
+      if (_chains[stackTrace] == null) _chains[stackTrace] = _createNode(2);
+    }
+
+    return new AsyncError(error, stackTrace);
+  }
+
+  /// Creates a [_Node] with the current stack trace and linked to
+  /// [_currentNode].
+  ///
+  /// By default, the first frame of the first trace will be the line where
+  /// [_createNode] is called. If [level] is passed, the first trace will start
+  /// that many frames up instead.
+  _Node _createNode([int level=0]) =>
+    new _Node(new Trace.current(level + 1), _currentNode);
+
+  // TODO(nweiz): use a more robust way of detecting and tracking errors when
+  // issue 15105 is fixed.
+  /// Runs [f] with [_currentNode] set to [node].
+  ///
+  /// If [f] throws an error, this associates [node] with that error's stack
+  /// trace.
+  _run(Function f, _Node node) {
+    var previousNode = _currentNode;
+    _currentNode = node;
+    try {
+      return f();
+    } catch (e, stackTrace) {
+      _chains[stackTrace] = node;
+      rethrow;
+    } finally {
+      _currentNode = previousNode;
+    }
+  }
+}
+
+/// A linked list node representing a single entry in a stack chain.
+class _Node {
+  /// The stack trace for this link of the chain.
+  final Trace trace;
+
+  /// The previous node in the chain.
+  final _Node previous;
+
+  _Node(StackTrace trace, [this.previous])
+      : trace = trace == null ? new Trace.current() : new Trace.from(trace);
+
+  /// Converts this to a [Chain].
+  Chain toChain() {
+    var nodes = <Trace>[];
+    var node = this;
+    while (node != null) {
+      nodes.add(node.trace);
+      node = node.previous;
+    }
+    return new Chain(nodes);
+  }
+}
diff --git a/stack_trace/lib/src/trace.dart b/stack_trace/lib/src/trace.dart
new file mode 100644
index 0000000..84a19ef
--- /dev/null
+++ b/stack_trace/lib/src/trace.dart
@@ -0,0 +1,283 @@
+// Copyright (c) 2013, 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 trace;
+
+import 'dart:collection';
+import 'dart:math' as math;
+
+import 'chain.dart';
+import 'frame.dart';
+import 'lazy_trace.dart';
+import 'utils.dart';
+import 'vm_trace.dart';
+
+final _terseRegExp = new RegExp(r"(-patch)?(/.*)?$");
+
+/// A RegExp to match V8's stack traces.
+///
+/// V8's traces start with a line that's either just "Error" or else is a
+/// description of the exception that occurred. That description can be multiple
+/// lines, so we just look for any line other than the first that begins with
+/// three or four spaces and "at".
+final _v8Trace = new RegExp(r"\n    ?at ");
+
+/// A RegExp to match indidual lines of V8's stack traces.
+///
+/// This is intended to filter out the leading exception details of the trace
+/// though it is possible for the message to match this as well.
+final _v8TraceLine = new RegExp(r"    ?at ");
+
+/// A RegExp to match Firefox and Safari's stack traces.
+///
+/// Firefox and Safari have very similar stack trace formats, so we use the same
+/// logic for parsing them.
+///
+/// Firefox's trace frames start with the name of the function in which the
+/// error occurred, possibly including its parameters inside `()`. For example,
+/// `.VW.call$0("arg")@http://pub.dartlang.org/stuff.dart.js:560`.
+///
+/// Safari traces occasionally don't include the initial method name followed by
+/// "@", and they always have both the line and column number (or just a
+/// trailing colon if no column number is available). They can also contain
+/// empty lines or lines consisting only of `[native code]`.
+final _firefoxSafariTrace = new RegExp(
+    r"^"
+    r"(" // Member description. Not present in some Safari frames.
+      r"([.0-9A-Za-z_$/<]|\(.*\))*" // Member name and arguments.
+      r"@"
+    r")?"
+    r"[^\s]*" // Frame URL.
+    r":\d*" // Line or column number. Some older frames only have a line number.
+    r"$", multiLine: true);
+
+/// A RegExp to match this package's stack traces.
+final _friendlyTrace = new RegExp(r"^[^\s]+( \d+(:\d+)?)?[ \t]+[^\s]+$",
+    multiLine: true);
+
+/// A stack trace, comprised of a list of stack frames.
+class Trace implements StackTrace {
+  /// The stack frames that comprise this stack trace.
+  final List<Frame> frames;
+
+  /// Returns a human-readable representation of [stackTrace]. If [terse] is
+  /// set, this folds together multiple stack frames from the Dart core
+  /// libraries, so that only the core library method directly called from user
+  /// code is visible (see [Trace.terse]).
+  static String format(StackTrace stackTrace, {bool terse: true}) {
+    var trace = new Trace.from(stackTrace);
+    if (terse) trace = trace.terse;
+    return trace.toString();
+  }
+
+  /// Returns the current stack trace.
+  ///
+  /// By default, the first frame of this trace will be the line where
+  /// [Trace.current] is called. If [level] is passed, the trace will start that
+  /// many frames up instead.
+  factory Trace.current([int level=0]) {
+    if (level < 0) {
+      throw new ArgumentError("Argument [level] must be greater than or equal "
+          "to 0.");
+    }
+
+    try {
+      throw '';
+    } catch (_, nativeTrace) {
+      var trace = new Trace.from(nativeTrace);
+      return new LazyTrace(() => new Trace(trace.frames.skip(level + 1)));
+    }
+  }
+
+  /// Returns a new stack trace containing the same data as [trace].
+  ///
+  /// If [trace] is a native [StackTrace], its data will be parsed out; if it's
+  /// a [Trace], it will be returned as-is.
+  factory Trace.from(StackTrace trace) {
+    // Normally explicitly validating null arguments is bad Dart style, but here
+    // the natural failure will only occur when the LazyTrace is materialized,
+    // and we want to provide an error that's more local to the actual problem.
+    if (trace == null) {
+      throw new ArgumentError("Cannot create a Trace from null.");
+    }
+
+    if (trace is Trace) return trace;
+    if (trace is Chain) return trace.toTrace();
+    return new LazyTrace(() => new Trace.parse(trace.toString()));
+  }
+
+  /// Parses a string representation of a stack trace.
+  ///
+  /// [trace] should be formatted in the same way as a Dart VM or browser stack
+  /// trace.
+  factory Trace.parse(String trace) {
+    try {
+      if (trace.isEmpty) return new Trace(<Frame>[]);
+      if (trace.contains(_v8Trace)) return new Trace.parseV8(trace);
+      if (trace.startsWith("\tat ")) return new Trace.parseJSCore(trace);
+      if (trace.contains(_firefoxSafariTrace)) {
+        return new Trace.parseFirefox(trace);
+      }
+      if (trace.contains(_friendlyTrace)) {
+        return new Trace.parseFriendly(trace);
+      }
+
+      // Default to parsing the stack trace as a VM trace. This is also hit on
+      // IE and Safari, where the stack trace is just an empty string (issue
+      // 11257).
+      return new Trace.parseVM(trace);
+    } on FormatException catch (error) {
+      throw new FormatException('${error.message}\nStack trace:\n$trace');
+    }
+  }
+
+  /// Parses a string representation of a Dart VM stack trace.
+  Trace.parseVM(String trace)
+      : this(trace.trim().split("\n").
+            // TODO(nweiz): remove this when issue 15920 is fixed.
+            where((line) => line.isNotEmpty).
+            map((line) => new Frame.parseVM(line)));
+
+  /// Parses a string representation of a Chrome/V8 stack trace.
+  Trace.parseV8(String trace)
+      : this(trace.split("\n").skip(1)
+          // It's possible that an Exception's description contains a line that
+          // looks like a V8 trace line, which will screw this up.
+          // Unfortunately, that's impossible to detect.
+          .skipWhile((line) => !line.startsWith(_v8TraceLine))
+          .map((line) => new Frame.parseV8(line)));
+
+  /// Parses a string representation of a JavaScriptCore stack trace.
+  Trace.parseJSCore(String trace)
+      : this(trace.split("\n")
+            .where((line) => line != "\tat ")
+            .map((line) => new Frame.parseV8(line)));
+
+  /// Parses a string representation of an Internet Explorer stack trace.
+  ///
+  /// IE10+ traces look just like V8 traces. Prior to IE10, stack traces can't
+  /// be retrieved.
+  Trace.parseIE(String trace)
+      : this.parseV8(trace);
+
+  /// Parses a string representation of a Firefox stack trace.
+  Trace.parseFirefox(String trace)
+      : this(trace.trim().split("\n")
+          .where((line) => line.isNotEmpty && line != '[native code]')
+          .map((line) => new Frame.parseFirefox(line)));
+
+  /// Parses a string representation of a Safari stack trace.
+  Trace.parseSafari(String trace)
+      : this.parseFirefox(trace);
+
+  /// Parses a string representation of a Safari 6.1+ stack trace.
+  @Deprecated("Use Trace.parseSafari instead.")
+  Trace.parseSafari6_1(String trace)
+      : this.parseSafari(trace);
+
+  /// Parses a string representation of a Safari 6.0 stack trace.
+  @Deprecated("Use Trace.parseSafari instead.")
+  Trace.parseSafari6_0(String trace)
+      : this(trace.trim().split("\n")
+          .where((line) => line != '[native code]')
+          .map((line) => new Frame.parseFirefox(line)));
+
+  /// Parses this package's string representation of a stack trace.
+  ///
+  /// This also parses string representations of [Chain]s. They parse to the
+  /// same trace that [Chain.toTrace] would return.
+  Trace.parseFriendly(String trace)
+      : this(trace.trim().split("\n")
+          // Filter out asynchronous gaps from [Chain]s.
+          .where((line) => !line.startsWith('====='))
+          .map((line) => new Frame.parseFriendly(line)));
+
+  /// Returns a new [Trace] comprised of [frames].
+  Trace(Iterable<Frame> frames)
+      : frames = new UnmodifiableListView<Frame>(frames.toList());
+
+  /// Returns a VM-style [StackTrace] object.
+  ///
+  /// The return value's [toString] method will always return a string
+  /// representation in the Dart VM's stack trace format, regardless of what
+  /// platform is being used.
+  StackTrace get vmTrace => new VMTrace(frames);
+
+  /// Returns a terser version of [this].
+  ///
+  /// This is accomplished by folding together multiple stack frames from the
+  /// core library or from this package, as in [foldFrames]. Remaining core
+  /// library frames have their libraries, "-patch" suffixes, and line numbers
+  /// removed. If the outermost frame of the stack trace is a core library
+  /// frame, it's removed entirely.
+  ///
+  /// For custom folding, see [foldFrames].
+  Trace get terse => foldFrames((_) => false, terse: true);
+
+  /// Returns a new [Trace] based on [this] where multiple stack frames matching
+  /// [predicate] are folded together.
+  ///
+  /// This means that whenever there are multiple frames in a row that match
+  /// [predicate], only the last one is kept. This is useful for limiting the
+  /// amount of library code that appears in a stack trace by only showing user
+  /// code and code that's called by user code.
+  ///
+  /// If [terse] is true, this will also fold together frames from the core
+  /// library or from this package, simplify core library frames, and
+  /// potentially remove the outermost frame as in [Trace.terse].
+  Trace foldFrames(bool predicate(Frame frame), {bool terse: false}) {
+    if (terse) {
+      var oldPredicate = predicate;
+      predicate = (frame) {
+        if (oldPredicate(frame)) return true;
+
+        if (frame.isCore) return true;
+        if (frame.package == 'stack_trace') return true;
+
+        // Ignore async stack frames without any line or column information.
+        // These come from the VM's async/await implementation and represent
+        // internal frames. They only ever show up in stack chains and are
+        // always surrounded by other traces that are actually useful, so we can
+        // just get rid of them.
+        // TODO(nweiz): Get rid of this logic some time after issue 22009 is
+        // fixed.
+        if (!frame.member.contains('<async>')) return false;
+        return frame.line == null;
+      };
+    }
+
+    var newFrames = [];
+    for (var frame in frames.reversed) {
+      if (!predicate(frame)) {
+        newFrames.add(frame);
+      } else if (newFrames.isEmpty || !predicate(newFrames.last)) {
+        newFrames.add(new Frame(
+            frame.uri, frame.line, frame.column, frame.member));
+      }
+    }
+
+    if (terse) {
+      newFrames = newFrames.map((frame) {
+        if (!predicate(frame)) return frame;
+        var library = frame.library.replaceAll(_terseRegExp, '');
+        return new Frame(Uri.parse(library), null, null, frame.member);
+      }).toList();
+      if (newFrames.length > 1 && newFrames.first.isCore) newFrames.removeAt(0);
+    }
+
+    return new Trace(newFrames.reversed);
+  }
+
+  /// Returns a human-readable string representation of [this].
+  String toString() {
+    // Figure out the longest path so we know how much to pad.
+    var longest = frames.map((frame) => frame.location.length)
+        .fold(0, math.max);
+
+    // Print out the stack trace nicely formatted.
+    return frames.map((frame) {
+      return '${padRight(frame.location, longest)}  ${frame.member}\n';
+    }).join();
+  }
+}
diff --git a/stack_trace/lib/src/utils.dart b/stack_trace/lib/src/utils.dart
new file mode 100644
index 0000000..62a2820
--- /dev/null
+++ b/stack_trace/lib/src/utils.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2013, 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 stack_trace.src.utils;
+
+/// Returns [string] with enough spaces added to the end to make it [length]
+/// characters long.
+String padRight(String string, int length) {
+  if (string.length >= length) return string;
+
+  var result = new StringBuffer();
+  result.write(string);
+  for (var i = 0; i < length - string.length; i++) {
+    result.write(' ');
+  }
+
+  return result.toString();
+}
+
+/// Flattens nested lists inside an iterable into a single list containing only
+/// non-list elements.
+List flatten(Iterable nested) {
+  var result = [];
+  helper(list) {
+    for (var element in list) {
+      if (element is List) {
+        helper(element);
+      } else {
+        result.add(element);
+      }
+    }
+  }
+  helper(nested);
+  return result;
+}
diff --git a/stack_trace/lib/src/vm_trace.dart b/stack_trace/lib/src/vm_trace.dart
new file mode 100644
index 0000000..7991160
--- /dev/null
+++ b/stack_trace/lib/src/vm_trace.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2013, 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 vm_trace;
+
+import 'frame.dart';
+import 'utils.dart';
+
+/// An implementation of [StackTrace] that emulates the behavior of the VM's
+/// implementation.
+///
+/// In particular, when [toString] is called, this returns a string in the VM's
+/// stack trace format.
+class VMTrace implements StackTrace {
+  /// The stack frames that comprise this stack trace.
+  final List<Frame> frames;
+
+  VMTrace(this.frames);
+
+  String toString() {
+    var i = 1;
+    return frames.map((frame) {
+      var number = padRight("#${i++}", 8);
+      var member = frame.member
+          .replaceAllMapped(new RegExp(r"[^.]+\.<async>"),
+              (match) => "${match[1]}.<${match[1]}_async_body>")
+          .replaceAll("<fn>", "<anonymous closure>");
+      var line = frame.line == null ? 0 : frame.line;
+      var column = frame.column == null ? 0 : frame.column;
+      return "$number$member (${frame.uri}:$line:$column)\n";
+    }).join();
+  }
+}
diff --git a/stack_trace/lib/stack_trace.dart b/stack_trace/lib/stack_trace.dart
new file mode 100644
index 0000000..ac875a9
--- /dev/null
+++ b/stack_trace/lib/stack_trace.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2013, 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.
+
+/**
+ * Stack trace generation and parsing.
+ *
+ * ## Installing ##
+ *
+ * Use [pub][] to install this package. Add the following to your `pubspec.yaml`
+ * file.
+ *
+ *     dependencies:
+ *       stack_trace: any
+ *
+ * Then run `pub install`.
+ *
+ * For more information, see the
+ * [stack_trace package on pub.dartlang.org][pkg].
+ *
+ * [pub]: http://pub.dartlang.org
+ * [pkg]: http://pub.dartlang.org/packages/stack_trace
+ */
+library stack_trace;
+
+export 'src/trace.dart';
+export 'src/frame.dart';
+export 'src/chain.dart';
diff --git a/stack_trace/pubspec.yaml b/stack_trace/pubspec.yaml
new file mode 100644
index 0000000..bc3bc8d
--- /dev/null
+++ b/stack_trace/pubspec.yaml
@@ -0,0 +1,22 @@
+name: stack_trace
+
+# Note! This version number is referenced directly in the pub source code in
+# lib/src/barback.dart. Pub implicitly places a version constraint on
+# stack_trace to ensure users only select a version of stack_trace that works
+# with their current version of pub.
+#
+# When the major version is upgraded, you *must* update that version constraint
+# in pub to stay in sync with this.
+version: 1.3.2
+author: "Dart Team <misc@dartlang.org>"
+homepage: http://github.com/dart-lang/stack_trace
+description: >
+  A package for manipulating stack traces and printing them readably.
+
+dependencies:
+  path: ">=1.2.0 <2.0.0"
+
+dev_dependencies:
+  unittest: ">=0.9.0 <0.12.0"
+environment:
+  sdk: ">=1.7.0-dev.4.0 <2.0.0"
diff --git a/string_scanner/lib/src/exception.dart b/string_scanner/lib/src/exception.dart
new file mode 100644
index 0000000..50177d6
--- /dev/null
+++ b/string_scanner/lib/src/exception.dart
@@ -0,0 +1,20 @@
+// 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 string_scanner.exception;
+
+import 'package:source_span/source_span.dart';
+
+/// An exception thrown by a [StringScanner] that failed to parse a string.
+class StringScannerException extends SourceSpanFormatException {
+  String get source => super.source;
+
+  /// The URL of the source file being parsed.
+  ///
+  /// This may be `null`, indicating that the source URL is unknown.
+  Uri get sourceUrl => span.sourceUrl;
+
+  StringScannerException(String message, SourceSpan span, String source)
+      : super(message, span, source);
+}
diff --git a/string_scanner/lib/src/line_scanner.dart b/string_scanner/lib/src/line_scanner.dart
new file mode 100644
index 0000000..54ecf7b
--- /dev/null
+++ b/string_scanner/lib/src/line_scanner.dart
@@ -0,0 +1,107 @@
+// 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 string_scanner.line_scanner;
+
+import 'string_scanner.dart';
+
+/// A subclass of [StringScanner] that tracks line and column information.
+class LineScanner extends StringScanner {
+  /// The scanner's current (zero-based) line number.
+  int get line => _line;
+  int _line = 0;
+
+  /// The scanner's current (zero-based) column number.
+  int get column => _column;
+  int _column = 0;
+
+  /// The scanner's state, including line and column information.
+  ///
+  /// This can be used to efficiently save and restore the state of the scanner
+  /// when backtracking. A given [LineScannerState] is only valid for the
+  /// [LineScanner] that created it.
+  LineScannerState get state =>
+      new LineScannerState._(this, position, line, column);
+
+  set state(LineScannerState state) {
+    if (!identical(state._scanner, this)) {
+      throw new ArgumentError("The given LineScannerState was not returned by "
+          "this LineScanner.");
+    }
+
+    super.position = state.position;
+    _line = state.line;
+    _column = state.column;
+  }
+
+  set position(int newPosition) {
+    var oldPosition = position;
+    super.position = newPosition;
+
+    if (newPosition > oldPosition) {
+      var newlines =
+          "\n".allMatches(string.substring(oldPosition, newPosition)).toList();
+      _line += newlines.length;
+      if (newlines.isEmpty) {
+        _column += newPosition - oldPosition;
+      } else {
+        _column = newPosition - newlines.last.end;
+      }
+    } else {
+      var newlines =
+          "\n".allMatches(string.substring(newPosition, oldPosition)).toList();
+      _line -= newlines.length;
+      if (newlines.isEmpty) {
+        _column -= oldPosition - newPosition;
+      } else {
+        _column = newPosition - string.lastIndexOf("\n", newPosition) - 1;
+      }
+    }
+  }
+
+  LineScanner(String string, {sourceUrl, int position})
+      : super(string, sourceUrl: sourceUrl, position: position);
+
+  int readChar() {
+    var char = super.readChar();
+    if (char == 0xA) {
+      _line += 1;
+      _column = 0;
+    } else {
+      _column += 1;
+    }
+    return char;
+  }
+
+  bool scan(Pattern pattern) {
+    if (!super.scan(pattern)) return false;
+
+    var newlines = "\n".allMatches(lastMatch[0]).toList();
+    _line += newlines.length;
+    if (newlines.isEmpty) {
+      _column += lastMatch[0].length;
+    } else {
+      _column = lastMatch[0].length - newlines.last.end;
+    }
+
+    return true;
+  }
+}
+
+/// A class representing the state of a [LineScanner].
+class LineScannerState {
+  /// The [LineScanner] that created this.
+  final LineScanner _scanner;
+
+  /// The position of the scanner in this state.
+  final int position;
+
+  /// The zero-based line number of the scanner in this state.
+  final int line;
+
+  /// The zero-based column number of the scanner in this state.
+  final int column;
+
+  LineScannerState._(this._scanner, this.position, this.line, this.column);
+}
diff --git a/string_scanner/lib/src/span_scanner.dart b/string_scanner/lib/src/span_scanner.dart
new file mode 100644
index 0000000..2a78b5b
--- /dev/null
+++ b/string_scanner/lib/src/span_scanner.dart
@@ -0,0 +1,100 @@
+// 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 string_scanner.span_scanner;
+
+import 'package:source_span/source_span.dart';
+
+import 'exception.dart';
+import 'line_scanner.dart';
+import 'string_scanner.dart';
+import 'utils.dart';
+
+/// A subclass of [LineScanner] that exposes matched ranges as source map
+/// [Span]s.
+class SpanScanner extends StringScanner implements LineScanner {
+  /// The source of the scanner.
+  ///
+  /// This caches line break information and is used to generate [Span]s.
+  final SourceFile _sourceFile;
+
+  int get line => _sourceFile.getLine(position);
+  int get column => _sourceFile.getColumn(position);
+
+  LineScannerState get state => new _SpanScannerState(this, position);
+
+  set state(LineScannerState state) {
+    if (state is! _SpanScannerState ||
+        !identical((state as _SpanScannerState)._scanner, this)) {
+      throw new ArgumentError("The given LineScannerState was not returned by "
+          "this LineScanner.");
+    }
+
+    this.position = state.position;
+  }
+
+  /// The [FileSpan] for [lastMatch].
+  ///
+  /// This is the span for the entire match. There's no way to get spans for
+  /// subgroups since [Match] exposes no information about their positions.
+  FileSpan get lastSpan => _lastSpan;
+  FileSpan _lastSpan;
+
+  /// The current location of the scanner.
+  FileLocation get location => _sourceFile.location(position);
+
+  /// Returns an empty span at the current location.
+  FileSpan get emptySpan => location.pointSpan();
+
+  /// Creates a new [SpanScanner] that starts scanning from [position].
+  ///
+  /// [sourceUrl] is used as [SourceLocation.sourceUrl] for the returned
+  /// [FileSpan]s as well as for error reporting. It can be a [String], a
+  /// [Uri], or `null`.
+  SpanScanner(String string, {sourceUrl, int position})
+      : _sourceFile = new SourceFile(string, url: sourceUrl),
+        super(string, sourceUrl: sourceUrl, position: position);
+
+  /// Creates a [FileSpan] representing the source range between [startState]
+  /// and the current position.
+  FileSpan spanFrom(LineScannerState startState, [LineScannerState endState]) {
+    var endPosition = endState == null ? position : endState.position;
+    return _sourceFile.span(startState.position, endPosition);
+  }
+
+  bool matches(Pattern pattern) {
+    if (!super.matches(pattern)) {
+      _lastSpan = null;
+      return false;
+    }
+
+    _lastSpan = _sourceFile.span(position, lastMatch.end);
+    return true;
+  }
+
+  void error(String message, {Match match, int position, int length}) {
+    validateErrorArgs(string, match, position, length);
+
+    if (match == null && position == null && length == null) match = lastMatch;
+    if (position == null) {
+      position = match == null ? this.position : match.start;
+    }
+    if (length == null) length = match == null ? 1 : match.end - match.start;
+
+    var span = _sourceFile.span(position, position + length);
+    throw new StringScannerException(message, span, string);
+  }
+}
+
+/// A class representing the state of a [SpanScanner].
+class _SpanScannerState implements LineScannerState {
+  /// The [SpanScanner] that created this.
+  final SpanScanner _scanner;
+
+  final int position;
+  int get line => _scanner._sourceFile.getLine(position);
+  int get column => _scanner._sourceFile.getColumn(position);
+
+  _SpanScannerState(this._scanner, this.position);
+}
diff --git a/string_scanner/lib/src/string_scanner.dart b/string_scanner/lib/src/string_scanner.dart
new file mode 100644
index 0000000..d9c1c2f
--- /dev/null
+++ b/string_scanner/lib/src/string_scanner.dart
@@ -0,0 +1,176 @@
+// 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 string_scanner.string_scanner;
+
+import 'package:source_span/source_span.dart';
+
+import 'exception.dart';
+import 'utils.dart';
+
+/// When compiled to JS, forward slashes are always escaped in [RegExp.pattern].
+///
+/// See issue 17998.
+final _slashAutoEscape = new RegExp("/").pattern == "\\/";
+
+/// A class that scans through a string using [Pattern]s.
+class StringScanner {
+  /// The URL of the source of the string being scanned.
+  ///
+  /// This is used for error reporting. It may be `null`, indicating that the
+  /// source URL is unknown or unavailable.
+  final Uri sourceUrl;
+
+  /// The string being scanned through.
+  final String string;
+
+  /// The current position of the scanner in the string, in characters.
+  int get position => _position;
+  set position(int position) {
+    if (position < 0 || position > string.length) {
+      throw new ArgumentError("Invalid position $position");
+    }
+
+    _position = position;
+  }
+  int _position = 0;
+
+  /// The data about the previous match made by the scanner.
+  ///
+  /// If the last match failed, this will be `null`.
+  Match get lastMatch => _lastMatch;
+  Match _lastMatch;
+
+  /// The portion of the string that hasn't yet been scanned.
+  String get rest => string.substring(position);
+
+  /// Whether the scanner has completely consumed [string].
+  bool get isDone => position == string.length;
+
+  /// Creates a new [StringScanner] that starts scanning from [position].
+  ///
+  /// [position] defaults to 0, the beginning of the string. [sourceUrl] is the
+  /// URL of the source of the string being scanned, if available. It can be
+  /// a [String], a [Uri], or `null`.
+  StringScanner(this.string, {sourceUrl, int position})
+      : sourceUrl = sourceUrl is String ? Uri.parse(sourceUrl) : sourceUrl {
+    if (position != null) this.position = position;
+  }
+
+  /// Consumes a single character and returns its character code.
+  ///
+  /// This throws a [FormatException] if the string has been fully consumed. It
+  /// doesn't affect [lastMatch].
+  int readChar() {
+    if (isDone) _fail("more input");
+    return string.codeUnitAt(_position++);
+  }
+
+  /// Returns the character code of the character [offset] away from [position].
+  ///
+  /// [offset] defaults to zero, and may be negative to inspect already-consumed
+  /// characters.
+  ///
+  /// This returns `null` if [offset] points outside the string. It doesn't
+  /// affect [lastMatch].
+  int peekChar([int offset]) {
+    if (offset == null) offset = 0;
+    var index = position + offset;
+    if (index < 0 || index >= string.length) return null;
+    return string.codeUnitAt(index);
+  }
+
+  /// If [pattern] matches at the current position of the string, scans forward
+  /// until the end of the match.
+  ///
+  /// Returns whether or not [pattern] matched.
+  bool scan(Pattern pattern) {
+    var success = matches(pattern);
+    if (success) _position = _lastMatch.end;
+    return success;
+  }
+
+  /// If [pattern] matches at the current position of the string, scans forward
+  /// until the end of the match.
+  ///
+  /// If [pattern] did not match, throws a [FormatException] describing the
+  /// position of the failure. [name] is used in this error as the expected name
+  /// of the pattern being matched; if it's `null`, the pattern itself is used
+  /// instead.
+  void expect(Pattern pattern, {String name}) {
+    if (scan(pattern)) return;
+
+    if (name == null) {
+      if (pattern is RegExp) {
+        var source = pattern.pattern;
+        if (!_slashAutoEscape) source = source.replaceAll("/", "\\/");
+        name = "/$source/";
+      } else {
+        name =
+            pattern.toString().replaceAll("\\", "\\\\").replaceAll('"', '\\"');
+        name = '"$name"';
+      }
+    }
+    _fail(name);
+  }
+
+  /// If the string has not been fully consumed, this throws a
+  /// [FormatException].
+  void expectDone() {
+    if (isDone) return;
+    _fail("no more input");
+  }
+
+  /// Returns whether or not [pattern] matches at the current position of the
+  /// string.
+  ///
+  /// This doesn't move the scan pointer forward.
+  bool matches(Pattern pattern) {
+    _lastMatch = pattern.matchAsPrefix(string, position);
+    return _lastMatch != null;
+  }
+
+  /// Returns the substring of [string] between [start] and [end].
+  ///
+  /// Unlike [String.substring], [end] defaults to [position] rather than the
+  /// end of the string.
+  String substring(int start, [int end]) {
+    if (end == null) end = position;
+    return string.substring(start, end);
+  }
+
+  /// Throws a [FormatException] with [message] as well as a detailed
+  /// description of the location of the error in the string.
+  ///
+  /// [match] is the match information for the span of the string with which the
+  /// error is associated. This should be a match returned by this scanner's
+  /// [lastMatch] property. By default, the error is associated with the last
+  /// match.
+  ///
+  /// If [position] and/or [length] are passed, they are used as the error span
+  /// instead. If only [length] is passed, [position] defaults to the current
+  /// position; if only [position] is passed, [length] defaults to 1.
+  ///
+  /// It's an error to pass [match] at the same time as [position] or [length].
+  void error(String message, {Match match, int position, int length}) {
+    validateErrorArgs(string, match, position, length);
+
+    if (match == null && position == null && length == null) match = lastMatch;
+    if (position == null) {
+      position = match == null ? this.position : match.start;
+    }
+    if (length == null) length = match == null ? 1 : match.end - match.start;
+
+    var sourceFile = new SourceFile(string, url: sourceUrl);
+    var span = sourceFile.span(position, position + length);
+    throw new StringScannerException(message, span, string);
+  }
+
+  // TODO(nweiz): Make this handle long lines more gracefully.
+  /// Throws a [FormatException] describing that [name] is expected at the
+  /// current position in the string.
+  void _fail(String name) {
+    error("expected $name.", position: this.position, length: 0);
+  }
+}
diff --git a/string_scanner/lib/src/utils.dart b/string_scanner/lib/src/utils.dart
new file mode 100644
index 0000000..107c4c5
--- /dev/null
+++ b/string_scanner/lib/src/utils.dart
@@ -0,0 +1,30 @@
+// 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 string_scanner.utils;
+
+/// Validates the arguments passed to [StringScanner.error].
+void validateErrorArgs(String string, Match match, int position, int length) {
+  if (match != null && (position != null || length != null)) {
+    throw new ArgumentError("Can't pass both match and position/length.");
+  }
+
+  if (position != null) {
+    if (position < 0) {
+      throw new RangeError("position must be greater than or equal to 0.");
+    } else if (position > string.length) {
+      throw new RangeError("position must be less than or equal to the "
+          "string length.");
+    }
+  }
+
+  if (length != null && length < 0) {
+    throw new RangeError("length must be greater than or equal to 0.");
+  }
+
+  if (position != null && length != null && position + length > string.length) {
+    throw new RangeError("position plus length must not go beyond the end of "
+        "the string.");
+  }
+}
diff --git a/string_scanner/lib/string_scanner.dart b/string_scanner/lib/string_scanner.dart
new file mode 100644
index 0000000..be294f4
--- /dev/null
+++ b/string_scanner/lib/string_scanner.dart
@@ -0,0 +1,11 @@
+// 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.
+
+/// A library for parsing strings using a sequence of patterns.
+library string_scanner;
+
+export 'src/exception.dart';
+export 'src/line_scanner.dart';
+export 'src/span_scanner.dart';
+export 'src/string_scanner.dart';
diff --git a/string_scanner/pubspec.yaml b/string_scanner/pubspec.yaml
new file mode 100644
index 0000000..ffd4ccc
--- /dev/null
+++ b/string_scanner/pubspec.yaml
@@ -0,0 +1,13 @@
+name: string_scanner
+version: 0.1.3+1
+author: "Dart Team <misc@dartlang.org>"
+homepage: https://github.com/dart-lang/string_scanner
+description: >
+  A class for parsing strings using a sequence of patterns.
+dependencies:
+  path: ">=1.2.0 <2.0.0"
+  source_span: ">=1.0.0 <2.0.0"
+dev_dependencies:
+  unittest: ">=0.10.0 <0.12.0"
+environment:
+  sdk: ">=1.2.0 <2.0.0"
diff --git a/template_binding/lib/js/flush.js b/template_binding/lib/js/flush.js
new file mode 100644
index 0000000..61da3a8
--- /dev/null
+++ b/template_binding/lib/js/flush.js
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+ * Code distributed by Google as part of the polymer project is also
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+ */
+
+(function(scope) {
+
+/**
+ * @class Polymer
+ */
+
+// imports
+var endOfMicrotask = scope.endOfMicrotask;
+
+// logging
+var log = window.WebComponents ? WebComponents.flags.log : {};
+
+// inject style sheet
+var style = document.createElement('style');
+style.textContent = 'template {display: none !important;} /* injected by platform.js */';
+var head = document.querySelector('head');
+head.insertBefore(style, head.firstChild);
+
+
+/**
+ * Force any pending data changes to be observed before 
+ * the next task. Data changes are processed asynchronously but are guaranteed
+ * to be processed, for example, before paintin. This method should rarely be 
+ * needed. It does nothing when Object.observe is available; 
+ * when Object.observe is not available, Polymer automatically flushes data 
+ * changes approximately every 1/10 second. 
+ * Therefore, `flush` should only be used when a data mutation should be 
+ * observed sooner than this.
+ * 
+ * @method flush
+ */
+// flush (with logging)
+var flushing;
+function flush() {
+  if (!flushing) {
+    flushing = true;
+    endOfMicrotask(function() {
+      flushing = false;
+      log.data && console.group('flush');
+      Platform.performMicrotaskCheckpoint();
+      log.data && console.groupEnd();
+    });
+  }
+};
+
+// polling dirty checker
+// flush periodically if platform does not have object observe.
+if (!Observer.hasObjectObserve) {
+  var FLUSH_POLL_INTERVAL = 125;
+  window.addEventListener('WebComponentsReady', function() {
+    flush();
+    // watch document visiblity to toggle dirty-checking
+    var visibilityHandler = function() {
+      // only flush if the page is visibile
+      if (document.visibilityState === 'hidden') {
+        if (scope.flushPoll) {
+          clearInterval(scope.flushPoll);
+        }
+      } else {
+        scope.flushPoll = setInterval(flush, FLUSH_POLL_INTERVAL);
+      }
+    };
+    if (typeof document.visibilityState === 'string') {
+      document.addEventListener('visibilitychange', visibilityHandler);
+    }
+    visibilityHandler();
+  });
+} else {
+  // make flush a no-op when we have Object.observe
+  flush = function() {};
+}
+
+if (window.CustomElements && !CustomElements.useNative) {
+  var originalImportNode = Document.prototype.importNode;
+  Document.prototype.importNode = function(node, deep) {
+    var imported = originalImportNode.call(this, node, deep);
+    CustomElements.upgradeAll(imported);
+    return imported;
+  };
+}
+
+// exports
+scope.flush = flush;
+// bc
+Platform.flush = flush;
+
+})(window.Polymer);
+
diff --git a/template_binding/lib/js/microtask.js b/template_binding/lib/js/microtask.js
new file mode 100644
index 0000000..ad954e3
--- /dev/null
+++ b/template_binding/lib/js/microtask.js
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+ * Code distributed by Google as part of the polymer project is also
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+ */
+
+(function(scope) {
+
+var iterations = 0;
+var callbacks = [];
+var twiddle = document.createTextNode('');
+
+function endOfMicrotask(callback) {
+  twiddle.textContent = iterations++;
+  callbacks.push(callback);
+}
+
+function atEndOfMicrotask() {
+  while (callbacks.length) {
+    callbacks.shift()();
+  }
+}
+
+new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask)
+  .observe(twiddle, {characterData: true})
+  ;
+
+// exports
+scope.endOfMicrotask = endOfMicrotask;
+// bc 
+Platform.endOfMicrotask = endOfMicrotask;
+
+})(Polymer);
+
diff --git a/template_binding/lib/js/node_bind.js b/template_binding/lib/js/node_bind.js
new file mode 100644
index 0000000..270c557
--- /dev/null
+++ b/template_binding/lib/js/node_bind.js
@@ -0,0 +1,343 @@
+// Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+// This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+// The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+// The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+// Code distributed by Google as part of the polymer project is also
+// subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+
+(function(global) {
+  'use strict';
+
+  var filter = Array.prototype.filter.call.bind(Array.prototype.filter);
+
+  function getTreeScope(node) {
+    while (node.parentNode) {
+      node = node.parentNode;
+    }
+
+    return typeof node.getElementById === 'function' ? node : null;
+  }
+
+  Node.prototype.bind = function(name, observable) {
+    console.error('Unhandled binding to Node: ', this, name, observable);
+  };
+
+  Node.prototype.bindFinished = function() {};
+
+  function updateBindings(node, name, binding) {
+    var bindings = node.bindings_;
+    if (!bindings)
+      bindings = node.bindings_ = {};
+
+    if (bindings[name])
+      binding[name].close();
+
+    return bindings[name] = binding;
+  }
+
+  function returnBinding(node, name, binding) {
+    return binding;
+  }
+
+  function sanitizeValue(value) {
+    return value == null ? '' : value;
+  }
+
+  function updateText(node, value) {
+    node.data = sanitizeValue(value);
+  }
+
+  function textBinding(node) {
+    return function(value) {
+      return updateText(node, value);
+    };
+  }
+
+  var maybeUpdateBindings = returnBinding;
+
+  Object.defineProperty(Platform, 'enableBindingsReflection', {
+    get: function() {
+      return maybeUpdateBindings === updateBindings;
+    },
+    set: function(enable) {
+      maybeUpdateBindings = enable ? updateBindings : returnBinding;
+      return enable;
+    },
+    configurable: true
+  });
+
+  Text.prototype.bind = function(name, value, oneTime) {
+    if (name !== 'textContent')
+      return Node.prototype.bind.call(this, name, value, oneTime);
+
+    if (oneTime)
+      return updateText(this, value);
+
+    var observable = value;
+    updateText(this, observable.open(textBinding(this)));
+    return maybeUpdateBindings(this, name, observable);
+  }
+
+  function updateAttribute(el, name, conditional, value) {
+    if (conditional) {
+      if (value)
+        el.setAttribute(name, '');
+      else
+        el.removeAttribute(name);
+      return;
+    }
+
+    el.setAttribute(name, sanitizeValue(value));
+  }
+
+  function attributeBinding(el, name, conditional) {
+    return function(value) {
+      updateAttribute(el, name, conditional, value);
+    };
+  }
+
+  Element.prototype.bind = function(name, value, oneTime) {
+    var conditional = name[name.length - 1] == '?';
+    if (conditional) {
+      this.removeAttribute(name);
+      name = name.slice(0, -1);
+    }
+
+    if (oneTime)
+      return updateAttribute(this, name, conditional, value);
+
+
+    var observable = value;
+    updateAttribute(this, name, conditional,
+        observable.open(attributeBinding(this, name, conditional)));
+
+    return maybeUpdateBindings(this, name, observable);
+  };
+
+  var checkboxEventType;
+  (function() {
+    // Attempt to feature-detect which event (change or click) is fired first
+    // for checkboxes.
+    var div = document.createElement('div');
+    var checkbox = div.appendChild(document.createElement('input'));
+    checkbox.setAttribute('type', 'checkbox');
+    var first;
+    var count = 0;
+    checkbox.addEventListener('click', function(e) {
+      count++;
+      first = first || 'click';
+    });
+    checkbox.addEventListener('change', function() {
+      count++;
+      first = first || 'change';
+    });
+
+    var event = document.createEvent('MouseEvent');
+    event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false,
+        false, false, false, 0, null);
+    checkbox.dispatchEvent(event);
+    // WebKit/Blink don't fire the change event if the element is outside the
+    // document, so assume 'change' for that case.
+    checkboxEventType = count == 1 ? 'change' : first;
+  })();
+
+  function getEventForInputType(element) {
+    switch (element.type) {
+      case 'checkbox':
+        return checkboxEventType;
+      case 'radio':
+      case 'select-multiple':
+      case 'select-one':
+        return 'change';
+      case 'range':
+        if (/Trident|MSIE/.test(navigator.userAgent))
+          return 'change';
+      default:
+        return 'input';
+    }
+  }
+
+  function updateInput(input, property, value, santizeFn) {
+    input[property] = (santizeFn || sanitizeValue)(value);
+  }
+
+  function inputBinding(input, property, santizeFn) {
+    return function(value) {
+      return updateInput(input, property, value, santizeFn);
+    }
+  }
+
+  function noop() {}
+
+  function bindInputEvent(input, property, observable, postEventFn) {
+    var eventType = getEventForInputType(input);
+
+    function eventHandler() {
+      observable.setValue(input[property]);
+      observable.discardChanges();
+      (postEventFn || noop)(input);
+      Platform.performMicrotaskCheckpoint();
+    }
+    input.addEventListener(eventType, eventHandler);
+
+    return {
+      close: function() {
+        input.removeEventListener(eventType, eventHandler);
+        observable.close();
+      },
+
+      observable_: observable
+    }
+  }
+
+  function booleanSanitize(value) {
+    return Boolean(value);
+  }
+
+  // |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
+  // Returns an array containing all radio buttons other than |element| that
+  // have the same |name|, either in the form that |element| belongs to or,
+  // if no form, in the document tree to which |element| belongs.
+  //
+  // This implementation is based upon the HTML spec definition of a
+  // "radio button group":
+  //   http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state.html#radio-button-group
+  //
+  function getAssociatedRadioButtons(element) {
+    if (element.form) {
+      return filter(element.form.elements, function(el) {
+        return el != element &&
+            el.tagName == 'INPUT' &&
+            el.type == 'radio' &&
+            el.name == element.name;
+      });
+    } else {
+      var treeScope = getTreeScope(element);
+      if (!treeScope)
+        return [];
+      var radios = treeScope.querySelectorAll(
+          'input[type="radio"][name="' + element.name + '"]');
+      return filter(radios, function(el) {
+        return el != element && !el.form;
+      });
+    }
+  }
+
+  function checkedPostEvent(input) {
+    // Only the radio button that is getting checked gets an event. We
+    // therefore find all the associated radio buttons and update their
+    // check binding manually.
+    if (input.tagName === 'INPUT' &&
+        input.type === 'radio') {
+      getAssociatedRadioButtons(input).forEach(function(radio) {
+        var checkedBinding = radio.bindings_.checked;
+        if (checkedBinding) {
+          // Set the value directly to avoid an infinite call stack.
+          checkedBinding.observable_.setValue(false);
+        }
+      });
+    }
+  }
+
+  HTMLInputElement.prototype.bind = function(name, value, oneTime) {
+    if (name !== 'value' && name !== 'checked')
+      return HTMLElement.prototype.bind.call(this, name, value, oneTime);
+
+    this.removeAttribute(name);
+    var sanitizeFn = name == 'checked' ? booleanSanitize : sanitizeValue;
+    var postEventFn = name == 'checked' ? checkedPostEvent : noop;
+
+    if (oneTime)
+      return updateInput(this, name, value, sanitizeFn);
+
+
+    var observable = value;
+    var binding = bindInputEvent(this, name, observable, postEventFn);
+    updateInput(this, name,
+                observable.open(inputBinding(this, name, sanitizeFn)),
+                sanitizeFn);
+
+    // Checkboxes may need to update bindings of other checkboxes.
+    return updateBindings(this, name, binding);
+  }
+
+  HTMLTextAreaElement.prototype.bind = function(name, value, oneTime) {
+    if (name !== 'value')
+      return HTMLElement.prototype.bind.call(this, name, value, oneTime);
+
+    this.removeAttribute('value');
+
+    if (oneTime)
+      return updateInput(this, 'value', value);
+
+    var observable = value;
+    var binding = bindInputEvent(this, 'value', observable);
+    updateInput(this, 'value',
+                observable.open(inputBinding(this, 'value', sanitizeValue)));
+    return maybeUpdateBindings(this, name, binding);
+  }
+
+  function updateOption(option, value) {
+    var parentNode = option.parentNode;;
+    var select;
+    var selectBinding;
+    var oldValue;
+    if (parentNode instanceof HTMLSelectElement &&
+        parentNode.bindings_ &&
+        parentNode.bindings_.value) {
+      select = parentNode;
+      selectBinding = select.bindings_.value;
+      oldValue = select.value;
+    }
+
+    option.value = sanitizeValue(value);
+
+    if (select && select.value != oldValue) {
+      selectBinding.observable_.setValue(select.value);
+      selectBinding.observable_.discardChanges();
+      Platform.performMicrotaskCheckpoint();
+    }
+  }
+
+  function optionBinding(option) {
+    return function(value) {
+      updateOption(option, value);
+    }
+  }
+
+  HTMLOptionElement.prototype.bind = function(name, value, oneTime) {
+    if (name !== 'value')
+      return HTMLElement.prototype.bind.call(this, name, value, oneTime);
+
+    this.removeAttribute('value');
+
+    if (oneTime)
+      return updateOption(this, value);
+
+    var observable = value;
+    var binding = bindInputEvent(this, 'value', observable);
+    updateOption(this, observable.open(optionBinding(this)));
+    return maybeUpdateBindings(this, name, binding);
+  }
+
+  HTMLSelectElement.prototype.bind = function(name, value, oneTime) {
+    if (name === 'selectedindex')
+      name = 'selectedIndex';
+
+    if (name !== 'selectedIndex' && name !== 'value')
+      return HTMLElement.prototype.bind.call(this, name, value, oneTime);
+
+    this.removeAttribute(name);
+
+    if (oneTime)
+      return updateInput(this, name, value);
+
+    var observable = value;
+    var binding = bindInputEvent(this, name, observable);
+    updateInput(this, name,
+                observable.open(inputBinding(this, name)));
+
+    // Option update events may need to access select bindings.
+    return updateBindings(this, name, binding);
+  }
+})(this);
diff --git a/template_binding/lib/js/observe.js b/template_binding/lib/js/observe.js
new file mode 100644
index 0000000..6cd2ccb
--- /dev/null
+++ b/template_binding/lib/js/observe.js
@@ -0,0 +1,1711 @@
+/*
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+ * Code distributed by Google as part of the polymer project is also
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+ */
+
+(function(global) {
+  'use strict';
+
+  var testingExposeCycleCount = global.testingExposeCycleCount;
+
+  // Detect and do basic sanity checking on Object/Array.observe.
+  function detectObjectObserve() {
+    if (typeof Object.observe !== 'function' ||
+        typeof Array.observe !== 'function') {
+      return false;
+    }
+
+    var records = [];
+
+    function callback(recs) {
+      records = recs;
+    }
+
+    var test = {};
+    var arr = [];
+    Object.observe(test, callback);
+    Array.observe(arr, callback);
+    test.id = 1;
+    test.id = 2;
+    delete test.id;
+    arr.push(1, 2);
+    arr.length = 0;
+
+    Object.deliverChangeRecords(callback);
+    if (records.length !== 5)
+      return false;
+
+    if (records[0].type != 'add' ||
+        records[1].type != 'update' ||
+        records[2].type != 'delete' ||
+        records[3].type != 'splice' ||
+        records[4].type != 'splice') {
+      return false;
+    }
+
+    Object.unobserve(test, callback);
+    Array.unobserve(arr, callback);
+
+    return true;
+  }
+
+  var hasObserve = detectObjectObserve();
+
+  function detectEval() {
+    // Don't test for eval if we're running in a Chrome App environment.
+    // We check for APIs set that only exist in a Chrome App context.
+    if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) {
+      return false;
+    }
+
+    // Firefox OS Apps do not allow eval. This feature detection is very hacky
+    // but even if some other platform adds support for this function this code
+    // will continue to work.
+    if (typeof navigator != 'undefined' && navigator.getDeviceStorage) {
+      return false;
+    }
+
+    try {
+      var f = new Function('', 'return true;');
+      return f();
+    } catch (ex) {
+      return false;
+    }
+  }
+
+  var hasEval = detectEval();
+
+  function isIndex(s) {
+    return +s === s >>> 0 && s !== '';
+  }
+
+  function toNumber(s) {
+    return +s;
+  }
+
+  function isObject(obj) {
+    return obj === Object(obj);
+  }
+
+  var numberIsNaN = global.Number.isNaN || function(value) {
+    return typeof value === 'number' && global.isNaN(value);
+  }
+
+  function areSameValue(left, right) {
+    if (left === right)
+      return left !== 0 || 1 / left === 1 / right;
+    if (numberIsNaN(left) && numberIsNaN(right))
+      return true;
+
+    return left !== left && right !== right;
+  }
+
+  var createObject = ('__proto__' in {}) ?
+    function(obj) { return obj; } :
+    function(obj) {
+      var proto = obj.__proto__;
+      if (!proto)
+        return obj;
+      var newObject = Object.create(proto);
+      Object.getOwnPropertyNames(obj).forEach(function(name) {
+        Object.defineProperty(newObject, name,
+                             Object.getOwnPropertyDescriptor(obj, name));
+      });
+      return newObject;
+    };
+
+  var identStart = '[\$_a-zA-Z]';
+  var identPart = '[\$_a-zA-Z0-9]';
+  var identRegExp = new RegExp('^' + identStart + '+' + identPart + '*' + '$');
+
+  function getPathCharType(char) {
+    if (char === undefined)
+      return 'eof';
+
+    var code = char.charCodeAt(0);
+
+    switch(code) {
+      case 0x5B: // [
+      case 0x5D: // ]
+      case 0x2E: // .
+      case 0x22: // "
+      case 0x27: // '
+      case 0x30: // 0
+        return char;
+
+      case 0x5F: // _
+      case 0x24: // $
+        return 'ident';
+
+      case 0x20: // Space
+      case 0x09: // Tab
+      case 0x0A: // Newline
+      case 0x0D: // Return
+      case 0xA0:  // No-break space
+      case 0xFEFF:  // Byte Order Mark
+      case 0x2028:  // Line Separator
+      case 0x2029:  // Paragraph Separator
+        return 'ws';
+    }
+
+    // a-z, A-Z
+    if ((0x61 <= code && code <= 0x7A) || (0x41 <= code && code <= 0x5A))
+      return 'ident';
+
+    // 1-9
+    if (0x31 <= code && code <= 0x39)
+      return 'number';
+
+    return 'else';
+  }
+
+  var pathStateMachine = {
+    'beforePath': {
+      'ws': ['beforePath'],
+      'ident': ['inIdent', 'append'],
+      '[': ['beforeElement'],
+      'eof': ['afterPath']
+    },
+
+    'inPath': {
+      'ws': ['inPath'],
+      '.': ['beforeIdent'],
+      '[': ['beforeElement'],
+      'eof': ['afterPath']
+    },
+
+    'beforeIdent': {
+      'ws': ['beforeIdent'],
+      'ident': ['inIdent', 'append']
+    },
+
+    'inIdent': {
+      'ident': ['inIdent', 'append'],
+      '0': ['inIdent', 'append'],
+      'number': ['inIdent', 'append'],
+      'ws': ['inPath', 'push'],
+      '.': ['beforeIdent', 'push'],
+      '[': ['beforeElement', 'push'],
+      'eof': ['afterPath', 'push']
+    },
+
+    'beforeElement': {
+      'ws': ['beforeElement'],
+      '0': ['afterZero', 'append'],
+      'number': ['inIndex', 'append'],
+      "'": ['inSingleQuote', 'append', ''],
+      '"': ['inDoubleQuote', 'append', '']
+    },
+
+    'afterZero': {
+      'ws': ['afterElement', 'push'],
+      ']': ['inPath', 'push']
+    },
+
+    'inIndex': {
+      '0': ['inIndex', 'append'],
+      'number': ['inIndex', 'append'],
+      'ws': ['afterElement'],
+      ']': ['inPath', 'push']
+    },
+
+    'inSingleQuote': {
+      "'": ['afterElement'],
+      'eof': ['error'],
+      'else': ['inSingleQuote', 'append']
+    },
+
+    'inDoubleQuote': {
+      '"': ['afterElement'],
+      'eof': ['error'],
+      'else': ['inDoubleQuote', 'append']
+    },
+
+    'afterElement': {
+      'ws': ['afterElement'],
+      ']': ['inPath', 'push']
+    }
+  }
+
+  function noop() {}
+
+  function parsePath(path) {
+    var keys = [];
+    var index = -1;
+    var c, newChar, key, type, transition, action, typeMap, mode = 'beforePath';
+
+    var actions = {
+      push: function() {
+        if (key === undefined)
+          return;
+
+        keys.push(key);
+        key = undefined;
+      },
+
+      append: function() {
+        if (key === undefined)
+          key = newChar
+        else
+          key += newChar;
+      }
+    };
+
+    function maybeUnescapeQuote() {
+      if (index >= path.length)
+        return;
+
+      var nextChar = path[index + 1];
+      if ((mode == 'inSingleQuote' && nextChar == "'") ||
+          (mode == 'inDoubleQuote' && nextChar == '"')) {
+        index++;
+        newChar = nextChar;
+        actions.append();
+        return true;
+      }
+    }
+
+    while (mode) {
+      index++;
+      c = path[index];
+
+      if (c == '\\' && maybeUnescapeQuote(mode))
+        continue;
+
+      type = getPathCharType(c);
+      typeMap = pathStateMachine[mode];
+      transition = typeMap[type] || typeMap['else'] || 'error';
+
+      if (transition == 'error')
+        return; // parse error;
+
+      mode = transition[0];
+      action = actions[transition[1]] || noop;
+      newChar = transition[2] === undefined ? c : transition[2];
+      action();
+
+      if (mode === 'afterPath') {
+        return keys;
+      }
+    }
+
+    return; // parse error
+  }
+
+  function isIdent(s) {
+    return identRegExp.test(s);
+  }
+
+  var constructorIsPrivate = {};
+
+  function Path(parts, privateToken) {
+    if (privateToken !== constructorIsPrivate)
+      throw Error('Use Path.get to retrieve path objects');
+
+    for (var i = 0; i < parts.length; i++) {
+      this.push(String(parts[i]));
+    }
+
+    if (hasEval && this.length) {
+      this.getValueFrom = this.compiledGetValueFromFn();
+    }
+  }
+
+  // TODO(rafaelw): Make simple LRU cache
+  var pathCache = {};
+
+  function getPath(pathString) {
+    if (pathString instanceof Path)
+      return pathString;
+
+    if (pathString == null || pathString.length == 0)
+      pathString = '';
+
+    if (typeof pathString != 'string') {
+      if (isIndex(pathString.length)) {
+        // Constructed with array-like (pre-parsed) keys
+        return new Path(pathString, constructorIsPrivate);
+      }
+
+      pathString = String(pathString);
+    }
+
+    var path = pathCache[pathString];
+    if (path)
+      return path;
+
+    var parts = parsePath(pathString);
+    if (!parts)
+      return invalidPath;
+
+    var path = new Path(parts, constructorIsPrivate);
+    pathCache[pathString] = path;
+    return path;
+  }
+
+  Path.get = getPath;
+
+  function formatAccessor(key) {
+    if (isIndex(key)) {
+      return '[' + key + ']';
+    } else {
+      return '["' + key.replace(/"/g, '\\"') + '"]';
+    }
+  }
+
+  Path.prototype = createObject({
+    __proto__: [],
+    valid: true,
+
+    toString: function() {
+      var pathString = '';
+      for (var i = 0; i < this.length; i++) {
+        var key = this[i];
+        if (isIdent(key)) {
+          pathString += i ? '.' + key : key;
+        } else {
+          pathString += formatAccessor(key);
+        }
+      }
+
+      return pathString;
+    },
+
+    getValueFrom: function(obj, directObserver) {
+      for (var i = 0; i < this.length; i++) {
+        if (obj == null)
+          return;
+        obj = obj[this[i]];
+      }
+      return obj;
+    },
+
+    iterateObjects: function(obj, observe) {
+      for (var i = 0; i < this.length; i++) {
+        if (i)
+          obj = obj[this[i - 1]];
+        if (!isObject(obj))
+          return;
+        observe(obj, this[i]);
+      }
+    },
+
+    compiledGetValueFromFn: function() {
+      var str = '';
+      var pathString = 'obj';
+      str += 'if (obj != null';
+      var i = 0;
+      var key;
+      for (; i < (this.length - 1); i++) {
+        key = this[i];
+        pathString += isIdent(key) ? '.' + key : formatAccessor(key);
+        str += ' &&\n     ' + pathString + ' != null';
+      }
+      str += ')\n';
+
+      var key = this[i];
+      pathString += isIdent(key) ? '.' + key : formatAccessor(key);
+
+      str += '  return ' + pathString + ';\nelse\n  return undefined;';
+      return new Function('obj', str);
+    },
+
+    setValueFrom: function(obj, value) {
+      if (!this.length)
+        return false;
+
+      for (var i = 0; i < this.length - 1; i++) {
+        if (!isObject(obj))
+          return false;
+        obj = obj[this[i]];
+      }
+
+      if (!isObject(obj))
+        return false;
+
+      obj[this[i]] = value;
+      return true;
+    }
+  });
+
+  var invalidPath = new Path('', constructorIsPrivate);
+  invalidPath.valid = false;
+  invalidPath.getValueFrom = invalidPath.setValueFrom = function() {};
+
+  var MAX_DIRTY_CHECK_CYCLES = 1000;
+
+  function dirtyCheck(observer) {
+    var cycles = 0;
+    while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check_()) {
+      cycles++;
+    }
+    if (testingExposeCycleCount)
+      global.dirtyCheckCycleCount = cycles;
+
+    return cycles > 0;
+  }
+
+  function objectIsEmpty(object) {
+    for (var prop in object)
+      return false;
+    return true;
+  }
+
+  function diffIsEmpty(diff) {
+    return objectIsEmpty(diff.added) &&
+           objectIsEmpty(diff.removed) &&
+           objectIsEmpty(diff.changed);
+  }
+
+  function diffObjectFromOldObject(object, oldObject) {
+    var added = {};
+    var removed = {};
+    var changed = {};
+
+    for (var prop in oldObject) {
+      var newValue = object[prop];
+
+      if (newValue !== undefined && newValue === oldObject[prop])
+        continue;
+
+      if (!(prop in object)) {
+        removed[prop] = undefined;
+        continue;
+      }
+
+      if (newValue !== oldObject[prop])
+        changed[prop] = newValue;
+    }
+
+    for (var prop in object) {
+      if (prop in oldObject)
+        continue;
+
+      added[prop] = object[prop];
+    }
+
+    if (Array.isArray(object) && object.length !== oldObject.length)
+      changed.length = object.length;
+
+    return {
+      added: added,
+      removed: removed,
+      changed: changed
+    };
+  }
+
+  var eomTasks = [];
+  function runEOMTasks() {
+    if (!eomTasks.length)
+      return false;
+
+    for (var i = 0; i < eomTasks.length; i++) {
+      eomTasks[i]();
+    }
+    eomTasks.length = 0;
+    return true;
+  }
+
+  var runEOM = hasObserve ? (function(){
+    return function(fn) {
+      return Promise.resolve().then(fn);
+    }
+  })() :
+  (function() {
+    return function(fn) {
+      eomTasks.push(fn);
+    };
+  })();
+
+  var observedObjectCache = [];
+
+  function newObservedObject() {
+    var observer;
+    var object;
+    var discardRecords = false;
+    var first = true;
+
+    function callback(records) {
+      if (observer && observer.state_ === OPENED && !discardRecords)
+        observer.check_(records);
+    }
+
+    return {
+      open: function(obs) {
+        if (observer)
+          throw Error('ObservedObject in use');
+
+        if (!first)
+          Object.deliverChangeRecords(callback);
+
+        observer = obs;
+        first = false;
+      },
+      observe: function(obj, arrayObserve) {
+        object = obj;
+        if (arrayObserve)
+          Array.observe(object, callback);
+        else
+          Object.observe(object, callback);
+      },
+      deliver: function(discard) {
+        discardRecords = discard;
+        Object.deliverChangeRecords(callback);
+        discardRecords = false;
+      },
+      close: function() {
+        observer = undefined;
+        Object.unobserve(object, callback);
+        observedObjectCache.push(this);
+      }
+    };
+  }
+
+  /*
+   * The observedSet abstraction is a perf optimization which reduces the total
+   * number of Object.observe observations of a set of objects. The idea is that
+   * groups of Observers will have some object dependencies in common and this
+   * observed set ensures that each object in the transitive closure of
+   * dependencies is only observed once. The observedSet acts as a write barrier
+   * such that whenever any change comes through, all Observers are checked for
+   * changed values.
+   *
+   * Note that this optimization is explicitly moving work from setup-time to
+   * change-time.
+   *
+   * TODO(rafaelw): Implement "garbage collection". In order to move work off
+   * the critical path, when Observers are closed, their observed objects are
+   * not Object.unobserve(d). As a result, it's possible that if the observedSet
+   * is kept open, but some Observers have been closed, it could cause "leaks"
+   * (prevent otherwise collectable objects from being collected). At some
+   * point, we should implement incremental "gc" which keeps a list of
+   * observedSets which may need clean-up and does small amounts of cleanup on a
+   * timeout until all is clean.
+   */
+
+  function getObservedObject(observer, object, arrayObserve) {
+    var dir = observedObjectCache.pop() || newObservedObject();
+    dir.open(observer);
+    dir.observe(object, arrayObserve);
+    return dir;
+  }
+
+  var observedSetCache = [];
+
+  function newObservedSet() {
+    var observerCount = 0;
+    var observers = [];
+    var objects = [];
+    var rootObj;
+    var rootObjProps;
+
+    function observe(obj, prop) {
+      if (!obj)
+        return;
+
+      if (obj === rootObj)
+        rootObjProps[prop] = true;
+
+      if (objects.indexOf(obj) < 0) {
+        objects.push(obj);
+        Object.observe(obj, callback);
+      }
+
+      observe(Object.getPrototypeOf(obj), prop);
+    }
+
+    function allRootObjNonObservedProps(recs) {
+      for (var i = 0; i < recs.length; i++) {
+        var rec = recs[i];
+        if (rec.object !== rootObj ||
+            rootObjProps[rec.name] ||
+            rec.type === 'setPrototype') {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    function callback(recs) {
+      if (allRootObjNonObservedProps(recs))
+        return;
+
+      var observer;
+      for (var i = 0; i < observers.length; i++) {
+        observer = observers[i];
+        if (observer.state_ == OPENED) {
+          observer.iterateObjects_(observe);
+        }
+      }
+
+      for (var i = 0; i < observers.length; i++) {
+        observer = observers[i];
+        if (observer.state_ == OPENED) {
+          observer.check_();
+        }
+      }
+    }
+
+    var record = {
+      objects: objects,
+      get rootObject() { return rootObj; },
+      set rootObject(value) {
+        rootObj = value;
+        rootObjProps = {};
+      },
+      open: function(obs, object) {
+        observers.push(obs);
+        observerCount++;
+        obs.iterateObjects_(observe);
+      },
+      close: function(obs) {
+        observerCount--;
+        if (observerCount > 0) {
+          return;
+        }
+
+        for (var i = 0; i < objects.length; i++) {
+          Object.unobserve(objects[i], callback);
+          Observer.unobservedCount++;
+        }
+
+        observers.length = 0;
+        objects.length = 0;
+        rootObj = undefined;
+        rootObjProps = undefined;
+        observedSetCache.push(this);
+        if (lastObservedSet === this)
+          lastObservedSet = null;
+      },
+    };
+
+    return record;
+  }
+
+  var lastObservedSet;
+
+  function getObservedSet(observer, obj) {
+    if (!lastObservedSet || lastObservedSet.rootObject !== obj) {
+      lastObservedSet = observedSetCache.pop() || newObservedSet();
+      lastObservedSet.rootObject = obj;
+    }
+    lastObservedSet.open(observer, obj);
+    return lastObservedSet;
+  }
+
+  var UNOPENED = 0;
+  var OPENED = 1;
+  var CLOSED = 2;
+  var RESETTING = 3;
+
+  var nextObserverId = 1;
+
+  function Observer() {
+    this.state_ = UNOPENED;
+    this.callback_ = undefined;
+    this.target_ = undefined; // TODO(rafaelw): Should be WeakRef
+    this.directObserver_ = undefined;
+    this.value_ = undefined;
+    this.id_ = nextObserverId++;
+  }
+
+  Observer.prototype = {
+    open: function(callback, target) {
+      if (this.state_ != UNOPENED)
+        throw Error('Observer has already been opened.');
+
+      addToAll(this);
+      this.callback_ = callback;
+      this.target_ = target;
+      this.connect_();
+      this.state_ = OPENED;
+      return this.value_;
+    },
+
+    close: function() {
+      if (this.state_ != OPENED)
+        return;
+
+      removeFromAll(this);
+      this.disconnect_();
+      this.value_ = undefined;
+      this.callback_ = undefined;
+      this.target_ = undefined;
+      this.state_ = CLOSED;
+    },
+
+    deliver: function() {
+      if (this.state_ != OPENED)
+        return;
+
+      dirtyCheck(this);
+    },
+
+    report_: function(changes) {
+      try {
+        this.callback_.apply(this.target_, changes);
+      } catch (ex) {
+        Observer._errorThrownDuringCallback = true;
+        console.error('Exception caught during observer callback: ' +
+                       (ex.stack || ex));
+      }
+    },
+
+    discardChanges: function() {
+      this.check_(undefined, true);
+      return this.value_;
+    }
+  }
+
+  var collectObservers = !hasObserve;
+  var allObservers;
+  Observer._allObserversCount = 0;
+
+  if (collectObservers) {
+    allObservers = [];
+  }
+
+  function addToAll(observer) {
+    Observer._allObserversCount++;
+    if (!collectObservers)
+      return;
+
+    allObservers.push(observer);
+  }
+
+  function removeFromAll(observer) {
+    Observer._allObserversCount--;
+  }
+
+  var runningMicrotaskCheckpoint = false;
+
+  global.Platform = global.Platform || {};
+
+  global.Platform.performMicrotaskCheckpoint = function() {
+    if (runningMicrotaskCheckpoint)
+      return;
+
+    if (!collectObservers)
+      return;
+
+    runningMicrotaskCheckpoint = true;
+
+    var cycles = 0;
+    var anyChanged, toCheck;
+
+    do {
+      cycles++;
+      toCheck = allObservers;
+      allObservers = [];
+      anyChanged = false;
+
+      for (var i = 0; i < toCheck.length; i++) {
+        var observer = toCheck[i];
+        if (observer.state_ != OPENED)
+          continue;
+
+        if (observer.check_())
+          anyChanged = true;
+
+        allObservers.push(observer);
+      }
+      if (runEOMTasks())
+        anyChanged = true;
+    } while (cycles < MAX_DIRTY_CHECK_CYCLES && anyChanged);
+
+    if (testingExposeCycleCount)
+      global.dirtyCheckCycleCount = cycles;
+
+    runningMicrotaskCheckpoint = false;
+  };
+
+  if (collectObservers) {
+    global.Platform.clearObservers = function() {
+      allObservers = [];
+    };
+  }
+
+  function ObjectObserver(object) {
+    Observer.call(this);
+    this.value_ = object;
+    this.oldObject_ = undefined;
+  }
+
+  ObjectObserver.prototype = createObject({
+    __proto__: Observer.prototype,
+
+    arrayObserve: false,
+
+    connect_: function(callback, target) {
+      if (hasObserve) {
+        this.directObserver_ = getObservedObject(this, this.value_,
+                                                 this.arrayObserve);
+      } else {
+        this.oldObject_ = this.copyObject(this.value_);
+      }
+
+    },
+
+    copyObject: function(object) {
+      var copy = Array.isArray(object) ? [] : {};
+      for (var prop in object) {
+        copy[prop] = object[prop];
+      };
+      if (Array.isArray(object))
+        copy.length = object.length;
+      return copy;
+    },
+
+    check_: function(changeRecords, skipChanges) {
+      var diff;
+      var oldValues;
+      if (hasObserve) {
+        if (!changeRecords)
+          return false;
+
+        oldValues = {};
+        diff = diffObjectFromChangeRecords(this.value_, changeRecords,
+                                           oldValues);
+      } else {
+        oldValues = this.oldObject_;
+        diff = diffObjectFromOldObject(this.value_, this.oldObject_);
+      }
+
+      if (diffIsEmpty(diff))
+        return false;
+
+      if (!hasObserve)
+        this.oldObject_ = this.copyObject(this.value_);
+
+      this.report_([
+        diff.added || {},
+        diff.removed || {},
+        diff.changed || {},
+        function(property) {
+          return oldValues[property];
+        }
+      ]);
+
+      return true;
+    },
+
+    disconnect_: function() {
+      if (hasObserve) {
+        this.directObserver_.close();
+        this.directObserver_ = undefined;
+      } else {
+        this.oldObject_ = undefined;
+      }
+    },
+
+    deliver: function() {
+      if (this.state_ != OPENED)
+        return;
+
+      if (hasObserve)
+        this.directObserver_.deliver(false);
+      else
+        dirtyCheck(this);
+    },
+
+    discardChanges: function() {
+      if (this.directObserver_)
+        this.directObserver_.deliver(true);
+      else
+        this.oldObject_ = this.copyObject(this.value_);
+
+      return this.value_;
+    }
+  });
+
+  function ArrayObserver(array) {
+    if (!Array.isArray(array))
+      throw Error('Provided object is not an Array');
+    ObjectObserver.call(this, array);
+  }
+
+  ArrayObserver.prototype = createObject({
+
+    __proto__: ObjectObserver.prototype,
+
+    arrayObserve: true,
+
+    copyObject: function(arr) {
+      return arr.slice();
+    },
+
+    check_: function(changeRecords) {
+      var splices;
+      if (hasObserve) {
+        if (!changeRecords)
+          return false;
+        splices = projectArraySplices(this.value_, changeRecords);
+      } else {
+        splices = calcSplices(this.value_, 0, this.value_.length,
+                              this.oldObject_, 0, this.oldObject_.length);
+      }
+
+      if (!splices || !splices.length)
+        return false;
+
+      if (!hasObserve)
+        this.oldObject_ = this.copyObject(this.value_);
+
+      this.report_([splices]);
+      return true;
+    }
+  });
+
+  ArrayObserver.applySplices = function(previous, current, splices) {
+    splices.forEach(function(splice) {
+      var spliceArgs = [splice.index, splice.removed.length];
+      var addIndex = splice.index;
+      while (addIndex < splice.index + splice.addedCount) {
+        spliceArgs.push(current[addIndex]);
+        addIndex++;
+      }
+
+      Array.prototype.splice.apply(previous, spliceArgs);
+    });
+  };
+
+  function PathObserver(object, path) {
+    Observer.call(this);
+
+    this.object_ = object;
+    this.path_ = getPath(path);
+    this.directObserver_ = undefined;
+  }
+
+  PathObserver.prototype = createObject({
+    __proto__: Observer.prototype,
+
+    get path() {
+      return this.path_;
+    },
+
+    connect_: function() {
+      if (hasObserve)
+        this.directObserver_ = getObservedSet(this, this.object_);
+
+      this.check_(undefined, true);
+    },
+
+    disconnect_: function() {
+      this.value_ = undefined;
+
+      if (this.directObserver_) {
+        this.directObserver_.close(this);
+        this.directObserver_ = undefined;
+      }
+    },
+
+    iterateObjects_: function(observe) {
+      this.path_.iterateObjects(this.object_, observe);
+    },
+
+    check_: function(changeRecords, skipChanges) {
+      var oldValue = this.value_;
+      this.value_ = this.path_.getValueFrom(this.object_);
+      if (skipChanges || areSameValue(this.value_, oldValue))
+        return false;
+
+      this.report_([this.value_, oldValue, this]);
+      return true;
+    },
+
+    setValue: function(newValue) {
+      if (this.path_)
+        this.path_.setValueFrom(this.object_, newValue);
+    }
+  });
+
+  function CompoundObserver(reportChangesOnOpen) {
+    Observer.call(this);
+
+    this.reportChangesOnOpen_ = reportChangesOnOpen;
+    this.value_ = [];
+    this.directObserver_ = undefined;
+    this.observed_ = [];
+  }
+
+  var observerSentinel = {};
+
+  CompoundObserver.prototype = createObject({
+    __proto__: Observer.prototype,
+
+    connect_: function() {
+      if (hasObserve) {
+        var object;
+        var needsDirectObserver = false;
+        for (var i = 0; i < this.observed_.length; i += 2) {
+          object = this.observed_[i]
+          if (object !== observerSentinel) {
+            needsDirectObserver = true;
+            break;
+          }
+        }
+
+        if (needsDirectObserver)
+          this.directObserver_ = getObservedSet(this, object);
+      }
+
+      this.check_(undefined, !this.reportChangesOnOpen_);
+    },
+
+    disconnect_: function() {
+      for (var i = 0; i < this.observed_.length; i += 2) {
+        if (this.observed_[i] === observerSentinel)
+          this.observed_[i + 1].close();
+      }
+      this.observed_.length = 0;
+      this.value_.length = 0;
+
+      if (this.directObserver_) {
+        this.directObserver_.close(this);
+        this.directObserver_ = undefined;
+      }
+    },
+
+    addPath: function(object, path) {
+      if (this.state_ != UNOPENED && this.state_ != RESETTING)
+        throw Error('Cannot add paths once started.');
+
+      var path = getPath(path);
+      this.observed_.push(object, path);
+      if (!this.reportChangesOnOpen_)
+        return;
+      var index = this.observed_.length / 2 - 1;
+      this.value_[index] = path.getValueFrom(object);
+    },
+
+    addObserver: function(observer) {
+      if (this.state_ != UNOPENED && this.state_ != RESETTING)
+        throw Error('Cannot add observers once started.');
+
+      this.observed_.push(observerSentinel, observer);
+      if (!this.reportChangesOnOpen_)
+        return;
+      var index = this.observed_.length / 2 - 1;
+      this.value_[index] = observer.open(this.deliver, this);
+    },
+
+    startReset: function() {
+      if (this.state_ != OPENED)
+        throw Error('Can only reset while open');
+
+      this.state_ = RESETTING;
+      this.disconnect_();
+    },
+
+    finishReset: function() {
+      if (this.state_ != RESETTING)
+        throw Error('Can only finishReset after startReset');
+      this.state_ = OPENED;
+      this.connect_();
+
+      return this.value_;
+    },
+
+    iterateObjects_: function(observe) {
+      var object;
+      for (var i = 0; i < this.observed_.length; i += 2) {
+        object = this.observed_[i]
+        if (object !== observerSentinel)
+          this.observed_[i + 1].iterateObjects(object, observe)
+      }
+    },
+
+    check_: function(changeRecords, skipChanges) {
+      var oldValues;
+      for (var i = 0; i < this.observed_.length; i += 2) {
+        var object = this.observed_[i];
+        var path = this.observed_[i+1];
+        var value;
+        if (object === observerSentinel) {
+          var observable = path;
+          value = this.state_ === UNOPENED ?
+              observable.open(this.deliver, this) :
+              observable.discardChanges();
+        } else {
+          value = path.getValueFrom(object);
+        }
+
+        if (skipChanges) {
+          this.value_[i / 2] = value;
+          continue;
+        }
+
+        if (areSameValue(value, this.value_[i / 2]))
+          continue;
+
+        oldValues = oldValues || [];
+        oldValues[i / 2] = this.value_[i / 2];
+        this.value_[i / 2] = value;
+      }
+
+      if (!oldValues)
+        return false;
+
+      // TODO(rafaelw): Having observed_ as the third callback arg here is
+      // pretty lame API. Fix.
+      this.report_([this.value_, oldValues, this.observed_]);
+      return true;
+    }
+  });
+
+  function identFn(value) { return value; }
+
+  function ObserverTransform(observable, getValueFn, setValueFn,
+                             dontPassThroughSet) {
+    this.callback_ = undefined;
+    this.target_ = undefined;
+    this.value_ = undefined;
+    this.observable_ = observable;
+    this.getValueFn_ = getValueFn || identFn;
+    this.setValueFn_ = setValueFn || identFn;
+    // TODO(rafaelw): This is a temporary hack. PolymerExpressions needs this
+    // at the moment because of a bug in it's dependency tracking.
+    this.dontPassThroughSet_ = dontPassThroughSet;
+  }
+
+  ObserverTransform.prototype = {
+    open: function(callback, target) {
+      this.callback_ = callback;
+      this.target_ = target;
+      this.value_ =
+          this.getValueFn_(this.observable_.open(this.observedCallback_, this));
+      return this.value_;
+    },
+
+    observedCallback_: function(value) {
+      value = this.getValueFn_(value);
+      if (areSameValue(value, this.value_))
+        return;
+      var oldValue = this.value_;
+      this.value_ = value;
+      this.callback_.call(this.target_, this.value_, oldValue);
+    },
+
+    discardChanges: function() {
+      this.value_ = this.getValueFn_(this.observable_.discardChanges());
+      return this.value_;
+    },
+
+    deliver: function() {
+      return this.observable_.deliver();
+    },
+
+    setValue: function(value) {
+      value = this.setValueFn_(value);
+      if (!this.dontPassThroughSet_ && this.observable_.setValue)
+        return this.observable_.setValue(value);
+    },
+
+    close: function() {
+      if (this.observable_)
+        this.observable_.close();
+      this.callback_ = undefined;
+      this.target_ = undefined;
+      this.observable_ = undefined;
+      this.value_ = undefined;
+      this.getValueFn_ = undefined;
+      this.setValueFn_ = undefined;
+    }
+  }
+
+  var expectedRecordTypes = {
+    add: true,
+    update: true,
+    delete: true
+  };
+
+  function diffObjectFromChangeRecords(object, changeRecords, oldValues) {
+    var added = {};
+    var removed = {};
+
+    for (var i = 0; i < changeRecords.length; i++) {
+      var record = changeRecords[i];
+      if (!expectedRecordTypes[record.type]) {
+        console.error('Unknown changeRecord type: ' + record.type);
+        console.error(record);
+        continue;
+      }
+
+      if (!(record.name in oldValues))
+        oldValues[record.name] = record.oldValue;
+
+      if (record.type == 'update')
+        continue;
+
+      if (record.type == 'add') {
+        if (record.name in removed)
+          delete removed[record.name];
+        else
+          added[record.name] = true;
+
+        continue;
+      }
+
+      // type = 'delete'
+      if (record.name in added) {
+        delete added[record.name];
+        delete oldValues[record.name];
+      } else {
+        removed[record.name] = true;
+      }
+    }
+
+    for (var prop in added)
+      added[prop] = object[prop];
+
+    for (var prop in removed)
+      removed[prop] = undefined;
+
+    var changed = {};
+    for (var prop in oldValues) {
+      if (prop in added || prop in removed)
+        continue;
+
+      var newValue = object[prop];
+      if (oldValues[prop] !== newValue)
+        changed[prop] = newValue;
+    }
+
+    return {
+      added: added,
+      removed: removed,
+      changed: changed
+    };
+  }
+
+  function newSplice(index, removed, addedCount) {
+    return {
+      index: index,
+      removed: removed,
+      addedCount: addedCount
+    };
+  }
+
+  var EDIT_LEAVE = 0;
+  var EDIT_UPDATE = 1;
+  var EDIT_ADD = 2;
+  var EDIT_DELETE = 3;
+
+  function ArraySplice() {}
+
+  ArraySplice.prototype = {
+
+    // Note: This function is *based* on the computation of the Levenshtein
+    // "edit" distance. The one change is that "updates" are treated as two
+    // edits - not one. With Array splices, an update is really a delete
+    // followed by an add. By retaining this, we optimize for "keeping" the
+    // maximum array items in the original array. For example:
+    //
+    //   'xxxx123' -> '123yyyy'
+    //
+    // With 1-edit updates, the shortest path would be just to update all seven
+    // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
+    // leaves the substring '123' intact.
+    calcEditDistances: function(current, currentStart, currentEnd,
+                                old, oldStart, oldEnd) {
+      // "Deletion" columns
+      var rowCount = oldEnd - oldStart + 1;
+      var columnCount = currentEnd - currentStart + 1;
+      var distances = new Array(rowCount);
+
+      // "Addition" rows. Initialize null column.
+      for (var i = 0; i < rowCount; i++) {
+        distances[i] = new Array(columnCount);
+        distances[i][0] = i;
+      }
+
+      // Initialize null row
+      for (var j = 0; j < columnCount; j++)
+        distances[0][j] = j;
+
+      for (var i = 1; i < rowCount; i++) {
+        for (var j = 1; j < columnCount; j++) {
+          if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
+            distances[i][j] = distances[i - 1][j - 1];
+          else {
+            var north = distances[i - 1][j] + 1;
+            var west = distances[i][j - 1] + 1;
+            distances[i][j] = north < west ? north : west;
+          }
+        }
+      }
+
+      return distances;
+    },
+
+    // This starts at the final weight, and walks "backward" by finding
+    // the minimum previous weight recursively until the origin of the weight
+    // matrix.
+    spliceOperationsFromEditDistances: function(distances) {
+      var i = distances.length - 1;
+      var j = distances[0].length - 1;
+      var current = distances[i][j];
+      var edits = [];
+      while (i > 0 || j > 0) {
+        if (i == 0) {
+          edits.push(EDIT_ADD);
+          j--;
+          continue;
+        }
+        if (j == 0) {
+          edits.push(EDIT_DELETE);
+          i--;
+          continue;
+        }
+        var northWest = distances[i - 1][j - 1];
+        var west = distances[i - 1][j];
+        var north = distances[i][j - 1];
+
+        var min;
+        if (west < north)
+          min = west < northWest ? west : northWest;
+        else
+          min = north < northWest ? north : northWest;
+
+        if (min == northWest) {
+          if (northWest == current) {
+            edits.push(EDIT_LEAVE);
+          } else {
+            edits.push(EDIT_UPDATE);
+            current = northWest;
+          }
+          i--;
+          j--;
+        } else if (min == west) {
+          edits.push(EDIT_DELETE);
+          i--;
+          current = west;
+        } else {
+          edits.push(EDIT_ADD);
+          j--;
+          current = north;
+        }
+      }
+
+      edits.reverse();
+      return edits;
+    },
+
+    /**
+     * Splice Projection functions:
+     *
+     * A splice map is a representation of how a previous array of items
+     * was transformed into a new array of items. Conceptually it is a list of
+     * tuples of
+     *
+     *   <index, removed, addedCount>
+     *
+     * which are kept in ascending index order of. The tuple represents that at
+     * the |index|, |removed| sequence of items were removed, and counting forward
+     * from |index|, |addedCount| items were added.
+     */
+
+    /**
+     * Lacking individual splice mutation information, the minimal set of
+     * splices can be synthesized given the previous state and final state of an
+     * array. The basic approach is to calculate the edit distance matrix and
+     * choose the shortest path through it.
+     *
+     * Complexity: O(l * p)
+     *   l: The length of the current array
+     *   p: The length of the old array
+     */
+    calcSplices: function(current, currentStart, currentEnd,
+                          old, oldStart, oldEnd) {
+      var prefixCount = 0;
+      var suffixCount = 0;
+
+      var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
+      if (currentStart == 0 && oldStart == 0)
+        prefixCount = this.sharedPrefix(current, old, minLength);
+
+      if (currentEnd == current.length && oldEnd == old.length)
+        suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
+
+      currentStart += prefixCount;
+      oldStart += prefixCount;
+      currentEnd -= suffixCount;
+      oldEnd -= suffixCount;
+
+      if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
+        return [];
+
+      if (currentStart == currentEnd) {
+        var splice = newSplice(currentStart, [], 0);
+        while (oldStart < oldEnd)
+          splice.removed.push(old[oldStart++]);
+
+        return [ splice ];
+      } else if (oldStart == oldEnd)
+        return [ newSplice(currentStart, [], currentEnd - currentStart) ];
+
+      var ops = this.spliceOperationsFromEditDistances(
+          this.calcEditDistances(current, currentStart, currentEnd,
+                                 old, oldStart, oldEnd));
+
+      var splice = undefined;
+      var splices = [];
+      var index = currentStart;
+      var oldIndex = oldStart;
+      for (var i = 0; i < ops.length; i++) {
+        switch(ops[i]) {
+          case EDIT_LEAVE:
+            if (splice) {
+              splices.push(splice);
+              splice = undefined;
+            }
+
+            index++;
+            oldIndex++;
+            break;
+          case EDIT_UPDATE:
+            if (!splice)
+              splice = newSplice(index, [], 0);
+
+            splice.addedCount++;
+            index++;
+
+            splice.removed.push(old[oldIndex]);
+            oldIndex++;
+            break;
+          case EDIT_ADD:
+            if (!splice)
+              splice = newSplice(index, [], 0);
+
+            splice.addedCount++;
+            index++;
+            break;
+          case EDIT_DELETE:
+            if (!splice)
+              splice = newSplice(index, [], 0);
+
+            splice.removed.push(old[oldIndex]);
+            oldIndex++;
+            break;
+        }
+      }
+
+      if (splice) {
+        splices.push(splice);
+      }
+      return splices;
+    },
+
+    sharedPrefix: function(current, old, searchLength) {
+      for (var i = 0; i < searchLength; i++)
+        if (!this.equals(current[i], old[i]))
+          return i;
+      return searchLength;
+    },
+
+    sharedSuffix: function(current, old, searchLength) {
+      var index1 = current.length;
+      var index2 = old.length;
+      var count = 0;
+      while (count < searchLength && this.equals(current[--index1], old[--index2]))
+        count++;
+
+      return count;
+    },
+
+    calculateSplices: function(current, previous) {
+      return this.calcSplices(current, 0, current.length, previous, 0,
+                              previous.length);
+    },
+
+    equals: function(currentValue, previousValue) {
+      return currentValue === previousValue;
+    }
+  };
+
+  var arraySplice = new ArraySplice();
+
+  function calcSplices(current, currentStart, currentEnd,
+                       old, oldStart, oldEnd) {
+    return arraySplice.calcSplices(current, currentStart, currentEnd,
+                                   old, oldStart, oldEnd);
+  }
+
+  function intersect(start1, end1, start2, end2) {
+    // Disjoint
+    if (end1 < start2 || end2 < start1)
+      return -1;
+
+    // Adjacent
+    if (end1 == start2 || end2 == start1)
+      return 0;
+
+    // Non-zero intersect, span1 first
+    if (start1 < start2) {
+      if (end1 < end2)
+        return end1 - start2; // Overlap
+      else
+        return end2 - start2; // Contained
+    } else {
+      // Non-zero intersect, span2 first
+      if (end2 < end1)
+        return end2 - start1; // Overlap
+      else
+        return end1 - start1; // Contained
+    }
+  }
+
+  function mergeSplice(splices, index, removed, addedCount) {
+
+    var splice = newSplice(index, removed, addedCount);
+
+    var inserted = false;
+    var insertionOffset = 0;
+
+    for (var i = 0; i < splices.length; i++) {
+      var current = splices[i];
+      current.index += insertionOffset;
+
+      if (inserted)
+        continue;
+
+      var intersectCount = intersect(splice.index,
+                                     splice.index + splice.removed.length,
+                                     current.index,
+                                     current.index + current.addedCount);
+
+      if (intersectCount >= 0) {
+        // Merge the two splices
+
+        splices.splice(i, 1);
+        i--;
+
+        insertionOffset -= current.addedCount - current.removed.length;
+
+        splice.addedCount += current.addedCount - intersectCount;
+        var deleteCount = splice.removed.length +
+                          current.removed.length - intersectCount;
+
+        if (!splice.addedCount && !deleteCount) {
+          // merged splice is a noop. discard.
+          inserted = true;
+        } else {
+          var removed = current.removed;
+
+          if (splice.index < current.index) {
+            // some prefix of splice.removed is prepended to current.removed.
+            var prepend = splice.removed.slice(0, current.index - splice.index);
+            Array.prototype.push.apply(prepend, removed);
+            removed = prepend;
+          }
+
+          if (splice.index + splice.removed.length > current.index + current.addedCount) {
+            // some suffix of splice.removed is appended to current.removed.
+            var append = splice.removed.slice(current.index + current.addedCount - splice.index);
+            Array.prototype.push.apply(removed, append);
+          }
+
+          splice.removed = removed;
+          if (current.index < splice.index) {
+            splice.index = current.index;
+          }
+        }
+      } else if (splice.index < current.index) {
+        // Insert splice here.
+
+        inserted = true;
+
+        splices.splice(i, 0, splice);
+        i++;
+
+        var offset = splice.addedCount - splice.removed.length
+        current.index += offset;
+        insertionOffset += offset;
+      }
+    }
+
+    if (!inserted)
+      splices.push(splice);
+  }
+
+  function createInitialSplices(array, changeRecords) {
+    var splices = [];
+
+    for (var i = 0; i < changeRecords.length; i++) {
+      var record = changeRecords[i];
+      switch(record.type) {
+        case 'splice':
+          mergeSplice(splices, record.index, record.removed.slice(), record.addedCount);
+          break;
+        case 'add':
+        case 'update':
+        case 'delete':
+          if (!isIndex(record.name))
+            continue;
+          var index = toNumber(record.name);
+          if (index < 0)
+            continue;
+          mergeSplice(splices, index, [record.oldValue], 1);
+          break;
+        default:
+          console.error('Unexpected record type: ' + JSON.stringify(record));
+          break;
+      }
+    }
+
+    return splices;
+  }
+
+  function projectArraySplices(array, changeRecords) {
+    var splices = [];
+
+    createInitialSplices(array, changeRecords).forEach(function(splice) {
+      if (splice.addedCount == 1 && splice.removed.length == 1) {
+        if (splice.removed[0] !== array[splice.index])
+          splices.push(splice);
+
+        return
+      };
+
+      splices = splices.concat(calcSplices(array, splice.index, splice.index + splice.addedCount,
+                                           splice.removed, 0, splice.removed.length));
+    });
+
+    return splices;
+  }
+
+  // Export the observe-js object for **Node.js**, with
+  // backwards-compatibility for the old `require()` API. If we're in
+  // the browser, export as a global object.
+
+  var expose = global;
+
+  if (typeof exports !== 'undefined') {
+    if (typeof module !== 'undefined' && module.exports) {
+      expose = exports = module.exports;
+    }
+    expose = exports;
+  } 
+
+  expose.Observer = Observer;
+  expose.Observer.runEOM_ = runEOM;
+  expose.Observer.observerSentinel_ = observerSentinel; // for testing.
+  expose.Observer.hasObjectObserve = hasObserve;
+  expose.ArrayObserver = ArrayObserver;
+  expose.ArrayObserver.calculateSplices = function(current, previous) {
+    return arraySplice.calculateSplices(current, previous);
+  };
+
+  expose.ArraySplice = ArraySplice;
+  expose.ObjectObserver = ObjectObserver;
+  expose.PathObserver = PathObserver;
+  expose.CompoundObserver = CompoundObserver;
+  expose.Path = Path;
+  expose.ObserverTransform = ObserverTransform;
+  
+})(typeof global !== 'undefined' && global && typeof module !== 'undefined' && module ? global : this || window);
diff --git a/template_binding/lib/src/binding_delegate.dart b/template_binding/lib/src/binding_delegate.dart
new file mode 100644
index 0000000..215c9f3
--- /dev/null
+++ b/template_binding/lib/src/binding_delegate.dart
@@ -0,0 +1,78 @@
+// Copyright (c) 2013, 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.
+
+part of template_binding;
+
+/**
+ * Template Bindings native features enables a wide-range of use cases,
+ * but (by design) don't attempt to implement a wide array of specialized
+ * behaviors.
+ *
+ * Enabling these features is a matter of implementing and registering a
+ * BindingDelegate. A binding delegate is an object which contains one or more
+ * delegation functions which implement specialized behavior. This object is
+ * registered via [TemplateBindExtension.bindingDelegate]:
+ *
+ * HTML:
+ *     <template bind>
+ *       {{ What!Ever('crazy')->thing^^^I+Want(data) }}
+ *     </template>
+ *
+ * Dart:
+ *     class MySyntax extends BindingDelegate {
+ *       prepareBinding(path, name, node) {
+ *         // The magic happens here!
+ *       }
+ *     }
+ *     ...
+ *     templateBind(query('template'))
+ *         ..bindingDelegate = new MySyntax()
+ *         ..model = new MyModel();
+ *
+ *
+ * See
+ * <http://www.polymer-project.org/platform/template.html#binding-delegate-api>
+ * for more information about the binding delegate.
+ */
+// TODO(jmesserly): need better api docs here. The link above seems out of date.
+class BindingDelegate {
+  /**
+   * Prepares a binding. This is called immediately after parsing a mustache
+   * token with `{{ path }}` in the context of the [node] and the property named
+   * [name]. This should return a function that will be passed the actual
+   * node and model, and either returns null or an object with a `value`
+   * property. This allows the syntax to reinterpret the model for each binding.
+   */
+  PrepareBindingFunction prepareBinding(String path, String name, Node node)
+      => null;
+
+  /**
+   * Returns a function that can optionally replace the model that will be
+   * passed to [TemplateBindExtension.createInstance]. This can be used to
+   * implement syntax such as `<template repeat="{{ item in items }}">` by
+   * ensuring that the returned model has the "item" name available.
+   */
+  PrepareInstanceModelFunction prepareInstanceModel(Element template) => null;
+
+  /**
+   * Returns a function that will be called whenever the position of an item
+   * inside this template changes.
+   */
+  PrepareInstancePositionChangedFunction prepareInstancePositionChanged(
+      Element template) => null;
+
+  Expando<_InstanceBindingMap> _bindingMaps;
+
+  // TODO(jmesserly): if we have use this everywhere, we can avoid many
+  // delegate != null checks throughout the code, simplifying things.
+  // For now we just use it for _bindingMaps.
+  static final _DEFAULT = new BindingDelegate();
+}
+
+typedef PrepareBindingFunction(model, Node node, bool oneTime);
+
+typedef PrepareInstanceModelFunction(model);
+
+typedef PrepareInstancePositionChangedFunction(TemplateInstance instance,
+    int index);
diff --git a/template_binding/lib/src/instance_binding_map.dart b/template_binding/lib/src/instance_binding_map.dart
new file mode 100644
index 0000000..bc4105a
--- /dev/null
+++ b/template_binding/lib/src/instance_binding_map.dart
@@ -0,0 +1,87 @@
+// Copyright (c) 2013, 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.
+
+part of template_binding;
+
+class _InstanceBindingMap {
+  final List bindings;
+  List<_InstanceBindingMap> children;
+  DocumentFragment content;
+
+  bool get isTemplate => false;
+
+  _InstanceBindingMap(this.bindings);
+
+  _InstanceBindingMap getChild(int index) {
+    if (children == null || index >= children.length) return null;
+    return children[index];
+  }
+}
+
+class _TemplateBindingMap extends _InstanceBindingMap {
+  bool get isTemplate => true;
+
+  MustacheTokens _if, _bind, _repeat;
+
+  _TemplateBindingMap(List bindings) : super(bindings);
+}
+
+_InstanceBindingMap _createInstanceBindingMap(Node node,
+    BindingDelegate delegate) {
+
+  _InstanceBindingMap map = _getBindings(node, delegate);
+  if (map == null) map = new _InstanceBindingMap([]);
+
+  List children = null;
+  int index = 0;
+  for (var c = node.firstChild; c != null; c = c.nextNode, index++) {
+    var childMap = _createInstanceBindingMap(c, delegate);
+    if (childMap == null) continue;
+
+    // TODO(jmesserly): use a sparse map instead?
+    if (children == null) children = new List(node.nodes.length);
+    children[index] = childMap;
+  }
+  map.children = children;
+
+  return map;
+}
+
+Node _cloneAndBindInstance(Node node, Node parent, Document stagingDocument,
+    _InstanceBindingMap bindings, model, BindingDelegate delegate,
+    List instanceBindings, [TemplateInstance instanceRecord]) {
+
+  var clone = parent.append(stagingDocument.importNode(node, false));
+
+  int i = 0;
+  for (var c = node.firstChild; c != null; c = c.nextNode, i++) {
+    var childMap = bindings != null ? bindings.getChild(i) : null;
+    _cloneAndBindInstance(c, clone, stagingDocument, childMap, model, delegate,
+        instanceBindings);
+  }
+
+  if (bindings.isTemplate) {
+    TemplateBindExtension.decorate(clone, node);
+    if (delegate != null) {
+      templateBindFallback(clone).bindingDelegate = delegate;
+    }
+  }
+
+  _processBindings(clone, bindings, model, instanceBindings);
+  return clone;
+}
+
+// TODO(rafaelw): Setup a MutationObserver on content which clears the expando
+// so that bindingMaps regenerate when template.content changes.
+_getInstanceBindingMap(DocumentFragment content, BindingDelegate delegate) {
+  if (delegate == null) delegate = BindingDelegate._DEFAULT;
+
+  if (delegate._bindingMaps == null) delegate._bindingMaps = new Expando();
+  var map = delegate._bindingMaps[content];
+  if (map == null) {
+    map = _createInstanceBindingMap(content, delegate);
+    delegate._bindingMaps[content] = map;
+  }
+  return map;
+}
diff --git a/template_binding/lib/src/mustache_tokens.dart b/template_binding/lib/src/mustache_tokens.dart
new file mode 100644
index 0000000..c05dac1
--- /dev/null
+++ b/template_binding/lib/src/mustache_tokens.dart
@@ -0,0 +1,153 @@
+// Copyright (c) 2013, 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 template_binding.src.mustache_tokens;
+
+import 'package:observe/observe.dart';
+
+// Dart note: this was added to decouple the parse function below from the rest
+// of template_binding. This allows using this code in command-line tools as
+// well.
+typedef Function DelegateFunctionFactory(String pathString);
+
+/**
+ * Represents a set of parsed tokens from a {{ mustache binding expression }}.
+ * This can be created by calling [parse].
+ *
+ * For performance reasons the data is stored in one linear array in [_tokens].
+ * This class wraps that array and provides accessors in an attempt to make the
+ * pattern easier to understand. See [length] and [getText] for example.
+ */
+class MustacheTokens {
+  // Constants for indexing into the exploded structs in [_tokens] .
+  static const _TOKEN_TEXT = 0;
+  static const _TOKEN_ONETIME = 1;
+  static const _TOKEN_PATH = 2;
+  static const _TOKEN_PREPAREFN = 3;
+  static const _TOKEN_SIZE = 4;
+
+  // There is 1 extra entry for the end text.
+  static const _TOKEN_ENDTEXT = 1;
+
+  bool get hasOnePath => _tokens.length == _TOKEN_SIZE + _TOKEN_ENDTEXT;
+  bool get isSimplePath => hasOnePath &&
+      _tokens[_TOKEN_TEXT] == '' && _tokens[_TOKEN_SIZE + _TOKEN_TEXT] == '';
+
+  /**
+   * [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+] if there is at least one
+   * mustache.
+   */
+  final List _tokens;
+
+  final bool onlyOneTime;
+
+  // Dart note: I think this is cached in JavaScript to avoid an extra
+  // allocation per template instance. Seems reasonable, so we do the same.
+  Function _combinator;
+  Function get combinator => _combinator;
+
+  MustacheTokens._(this._tokens, this.onlyOneTime) {
+    // Should be: [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+].
+    assert((_tokens.length - _TOKEN_ENDTEXT) % _TOKEN_SIZE == 0);
+
+    _combinator = hasOnePath ? _singleCombinator : _listCombinator;
+  }
+
+  int get length => _tokens.length ~/ _TOKEN_SIZE;
+
+  /**
+   * Gets the [i]th text entry. Note that [length] can be passed to get the
+   * final text entry.
+   */
+  String getText(int i) => _tokens[i * _TOKEN_SIZE + _TOKEN_TEXT];
+
+  /** Gets the oneTime flag for the [i]th token. */
+  bool getOneTime(int i) => _tokens[i * _TOKEN_SIZE + _TOKEN_ONETIME];
+
+  /** Gets the path for the [i]th token. */
+  PropertyPath getPath(int i) => _tokens[i * _TOKEN_SIZE + _TOKEN_PATH];
+
+  /** Gets the prepareBinding function for the [i]th token. */
+  Function getPrepareBinding(int i) =>
+      _tokens[i * _TOKEN_SIZE + _TOKEN_PREPAREFN];
+
+
+  /**
+   * Parses {{ mustache }} bindings.
+   *
+   * Returns null if there are no matches. Otherwise returns the parsed tokens.
+   */
+  static MustacheTokens parse(String s, [DelegateFunctionFactory fnFactory]) {
+    if (s == null || s.isEmpty) return null;
+
+    var tokens = null;
+    var length = s.length;
+    var lastIndex = 0;
+    var onlyOneTime = true;
+    while (lastIndex < length) {
+      var startIndex = s.indexOf('{{', lastIndex);
+      var oneTimeStart = s.indexOf('[[', lastIndex);
+      var oneTime = false;
+      var terminator = '}}';
+
+      if (oneTimeStart >= 0 &&
+          (startIndex < 0 || oneTimeStart < startIndex)) {
+        startIndex = oneTimeStart;
+        oneTime = true;
+        terminator = ']]';
+      }
+
+      var endIndex = -1;
+      if (startIndex >= 0) {
+        endIndex = s.indexOf(terminator, startIndex + 2);
+      }
+
+      if (endIndex < 0) {
+        if (tokens == null) return null;
+
+        tokens.add(s.substring(lastIndex)); // TEXT
+        break;
+      }
+
+      if (tokens == null) tokens = [];
+      tokens.add(s.substring(lastIndex, startIndex)); // TEXT
+      var pathString = s.substring(startIndex + 2, endIndex).trim();
+      tokens.add(oneTime); // ONETIME?
+      onlyOneTime = onlyOneTime && oneTime;
+      var delegateFn = fnFactory == null ? null : fnFactory(pathString);
+      // Don't try to parse the expression if there's a prepareBinding function
+      if (delegateFn == null) {
+        tokens.add(new PropertyPath(pathString)); // PATH
+      } else {
+        tokens.add(null);
+      }
+      tokens.add(delegateFn); // DELEGATE_FN
+
+      lastIndex = endIndex + 2;
+    }
+
+    if (lastIndex == length) tokens.add(''); // TEXT
+
+    return new MustacheTokens._(tokens, onlyOneTime);
+  }
+
+
+  // Dart note: split "combinator" into the single/list variants, so the
+  // argument can be typed.
+  String _singleCombinator(Object value) {
+    if (value == null) value = '';
+    return '${getText(0)}$value${getText(length)}';
+  }
+
+  String _listCombinator(List<Object> values) {
+    var newValue = new StringBuffer(getText(0));
+    int len = this.length;
+    for (var i = 0; i < len; i++) {
+      var value = values[i];
+      if (value != null) newValue.write(value);
+      newValue.write(getText(i + 1));
+    }
+    return newValue.toString();
+  }
+}
diff --git a/template_binding/lib/src/node.dart b/template_binding/lib/src/node.dart
new file mode 100644
index 0000000..fbcf96a
--- /dev/null
+++ b/template_binding/lib/src/node.dart
@@ -0,0 +1,200 @@
+// Copyright (c) 2013, 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.
+
+part of template_binding;
+
+/** Extensions to the [Node] API. */
+class NodeBindExtension {
+  final Node _node;
+  final JsObject _js;
+
+  NodeBindExtension._(node)
+      : _node = node,
+        _js = new JsObject.fromBrowserObject(node);
+
+  /**
+   * Gets the data bindings that are associated with this node, if any.
+   *
+   * This starts out null, and if [enableBindingsReflection] is enabled, calls
+   * to [bind] will initialize this field and the binding.
+   */
+  // Dart note: in JS this has a trailing underscore, meaning "private".
+  // But in dart if we made it _bindings, it wouldn't be accessible at all.
+  // It is unfortunately needed to implement Node.bind correctly.
+  Map<String, Bindable> get bindings {
+    var b = _js['bindings_'];
+    if (b == null) return null;
+    // TODO(jmesserly): should cache this for identity.
+    return new _NodeBindingsMap(_node, b);
+  }
+  
+  set bindings(Map<String, Bindable> value) {
+    if (value == null) {
+      _js.deleteProperty('bindings_');
+      return;
+    }
+    var b = bindings;
+    if (b == null) {
+      _js['bindings_'] = new JsObject.jsify({});
+      b = bindings;
+    }
+    b.addAll(value);
+  }
+
+  /**
+   * Binds the attribute [name] to [value]. [value] can be a simple value when
+   * [oneTime] is true, or a [Bindable] like [PathObserver].
+   * Returns the [Bindable] instance.
+   */
+  Bindable bind(String name, value, {bool oneTime: false}) {
+    name = _dartToJsName(_node, name);
+
+    if (!oneTime && value is Bindable) {
+      value = bindableToJsObject(value);
+    }
+    return jsObjectToBindable(_js.callMethod('bind', [name, value, oneTime]));
+  }
+
+  /**
+   * Called when all [bind] calls are finished for a given template expansion.
+   */
+  bindFinished() => _js.callMethod('bindFinished');
+
+  // Note: confusingly this is on NodeBindExtension because it can be on any
+  // Node. It's really an API added by TemplateBinding. Therefore it stays
+  // implemented in Dart because TemplateBinding still is.
+  TemplateInstance _templateInstance;
+
+  /** Gets the template instance that instantiated this node, if any. */
+  TemplateInstance get templateInstance =>
+      _templateInstance != null ? _templateInstance :
+      (_node.parent != null ? nodeBind(_node.parent).templateInstance : null);
+}
+
+class _NodeBindingsMap extends MapBase<String, Bindable> {
+  final Node _node;
+  final JsObject _bindings;
+
+  _NodeBindingsMap(this._node, this._bindings);
+
+  // TODO(jmesserly): this should be lazy
+  Iterable<String> get keys =>
+      js.context['Object'].callMethod('keys', [_bindings]).map(
+          (name) => _jsToDartName(_node, name));
+
+  Bindable operator[](String name) =>
+      jsObjectToBindable(_bindings[_dartToJsName(_node, name)]);
+
+  operator[]=(String name, Bindable value) {
+    _bindings[_dartToJsName(_node, name)] = bindableToJsObject(value);
+  }
+
+  @override Bindable remove(String name) {
+    name = _dartToJsName(_node, name);
+    var old = this[name];
+    _bindings.deleteProperty(name);
+    return old;
+  }
+
+  @override void clear() {
+    // Notes: this implementation only works because our "keys" returns a copy.
+    // We could also make it O(1) by assigning a new JS object to the bindings_
+    // property, if performance is an issue.
+    keys.forEach(remove);
+  }
+}
+
+// TODO(jmesserly): perhaps we should switch Dart's Node.bind API back to
+// 'textContent' for consistency? This only affects the raw Node.bind API when
+// called on Text nodes, which is unlikely to be used except by TemplateBinding.
+// Seems like a lot of magic to support it. I don't think Node.bind promises any
+// strong relationship between properties and [name], so textContent seems fine.
+String _dartToJsName(Node node, String name) {
+  if (node is Text && name == 'text') name = 'textContent';
+  return name;
+}
+
+
+String _jsToDartName(Node node, String name) {
+  if (node is Text && name == 'textContent') name = 'text';
+  return name;
+}
+
+
+/// Given a bindable [JsObject], wraps it in a Dart [Bindable].
+/// See [bindableToJsObject] to go in the other direction.
+Bindable jsObjectToBindable(JsObject obj) {
+  if (obj == null) return null;
+  var b = obj['__dartBindable'];
+  // For performance, unwrap the Dart bindable if we find one.
+  // Note: in the unlikely event some code messes with our __dartBindable
+  // property we can simply fallback to a _JsBindable wrapper.
+  return b is Bindable ? b : new _JsBindable(obj);
+}
+
+class _JsBindable extends Bindable {
+  final JsObject _js;
+  _JsBindable(JsObject obj) : _js = obj;
+
+  open(callback) => _js.callMethod('open',
+      [Zone.current.bindUnaryCallback(callback)]);
+
+  close() => _js.callMethod('close');
+
+  get value => _js.callMethod('discardChanges');
+
+  set value(newValue) {
+    _js.callMethod('setValue', [newValue]);
+  }
+
+  deliver() => _js.callMethod('deliver');
+}
+
+/// Given a [bindable], create a JS object proxy for it.
+/// This is the inverse of [jsObjectToBindable].
+JsObject bindableToJsObject(Bindable bindable) {
+  if (bindable is _JsBindable) return bindable._js;
+
+  var zone = Zone.current;
+  inZone(f) => zone.bindCallback(f, runGuarded: false);
+  inZoneUnary(f) => zone.bindUnaryCallback(f, runGuarded: false);
+
+  return new JsObject.jsify({
+    'open': inZoneUnary(
+        (callback) => bindable.open((x) => callback.apply([x]))),
+    'close': inZone(() => bindable.close()),
+    'discardChanges': inZone(() => bindable.value),
+    'setValue': inZoneUnary((x) => bindable.value = x),
+    // NOTE: this is not used by Node.bind, but it's used by Polymer:
+    // https://github.com/Polymer/polymer-dev/blob/ba2b68fe5a5721f60b5994135f3270e63588809a/src/declaration/properties.js#L130
+    // Technically this works because 'deliver' is on PathObserver and
+    // CompoundObserver. But ideally Polymer-JS would not assume that.
+    'deliver': inZone(() => bindable.deliver()),
+    // Save this so we can return it from [jsObjectToBindable]
+    '__dartBindable': bindable
+  });
+}
+
+/** Information about the instantiated template. */
+class TemplateInstance {
+  // TODO(rafaelw): firstNode & lastNode should be read-synchronous
+  // in cases where script has modified the template instance boundary.
+
+  /** The first node of this template instantiation. */
+  Node get firstNode => _firstNode;
+
+  /**
+   * The last node of this template instantiation.
+   * This could be identical to [firstNode] if the template only expanded to a
+   * single node.
+   */
+  Node get lastNode => _lastNode;
+
+  /** The model used to instantiate the template. */
+  final model;
+
+  Node _firstNode, _lastNode;
+
+  TemplateInstance(this.model);
+}
diff --git a/template_binding/lib/src/template.dart b/template_binding/lib/src/template.dart
new file mode 100644
index 0000000..2dc7ac5
--- /dev/null
+++ b/template_binding/lib/src/template.dart
@@ -0,0 +1,536 @@
+// Copyright (c) 2013, 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.
+
+part of template_binding;
+
+/** Extensions to [Element]s that behave as templates. */
+class TemplateBindExtension extends NodeBindExtension {
+  var _model;
+  BindingDelegate _bindingDelegate;
+  _TemplateIterator _iterator;
+  bool _setModelScheduled = false;
+
+  Element _templateInstanceRef;
+
+  // Note: only used if `this is! TemplateElement`
+  DocumentFragment _content;
+  bool _templateIsDecorated;
+
+  HtmlDocument _stagingDocument;
+
+  _InstanceBindingMap _bindingMap;
+
+  Node _refContent;
+
+  TemplateBindExtension._(Element node) : super._(node);
+
+  Element get _node => super._node;
+
+  TemplateBindExtension get _self => _node is TemplateBindExtension
+      ? _node : this;
+
+  Bindable bind(String name, value, {bool oneTime: false}) {
+    if (name != 'ref') return super.bind(name, value, oneTime: oneTime);
+
+    var ref = oneTime ? value : value.open((ref) {
+      _node.attributes['ref'] = ref;
+      _refChanged();
+    });
+
+    _node.attributes['ref'] = ref;
+    _refChanged();
+    if (oneTime) return null;
+
+    if (bindings == null) bindings = {};
+    return bindings['ref'] = value;
+  }
+
+  _TemplateIterator _processBindingDirectives(_TemplateBindingMap directives) {
+    if (_iterator != null) _iterator._closeDependencies();
+
+    if (directives._if == null &&
+        directives._bind == null &&
+        directives._repeat == null) {
+
+      if (_iterator != null) {
+        _iterator.close();
+        _iterator = null;
+      }
+      return null;
+    }
+
+    if (_iterator == null) {
+      _iterator = new _TemplateIterator(this);
+    }
+
+    _iterator._updateDependencies(directives, model);
+
+    _templateObserver.observe(_node,
+        attributes: true, attributeFilter: ['ref']);
+
+    return _iterator;
+  }
+
+  /**
+   * Creates an instance of the template, using the provided [model] and
+   * optional binding [delegate].
+   *
+   * If [instanceBindings] is supplied, each [Bindable] in the returned
+   * instance will be added to the list. This makes it easy to close all of the
+   * bindings without walking the tree. This is not normally necessary, but is
+   * used internally by the system.
+   */
+  DocumentFragment createInstance([model, BindingDelegate delegate]) {
+    if (delegate == null) delegate = _bindingDelegate;
+    if (_refContent == null) _refContent = templateBind(_ref).content;
+
+    var content = _refContent;
+    if (content.firstChild == null) return _emptyInstance;
+
+    final map = _getInstanceBindingMap(content, delegate);
+    final staging = _getTemplateStagingDocument();
+    final instance = _stagingDocument.createDocumentFragment();
+
+    final instanceExt = new _InstanceExtension();
+    _instanceExtension[instance] = instanceExt
+      .._templateCreator = _node
+      .._protoContent = content;
+
+    final instanceRecord = new TemplateInstance(model);
+    nodeBindFallback(instance)._templateInstance = instanceRecord;
+
+    var i = 0;
+    bool collectTerminator = false;
+    for (var c = content.firstChild; c != null; c = c.nextNode, i++) {
+      // The terminator of the instance is the clone of the last child of the
+      // content. If the last child is an active template, it may produce
+      // instances as a result of production, so simply collecting the last
+      // child of the instance after it has finished producing may be wrong.
+      if (c.nextNode == null) collectTerminator = true;
+
+      final childMap = map != null ? map.getChild(i) : null;
+      var clone = _cloneAndBindInstance(c, instance, _stagingDocument,
+          childMap, model, delegate, instanceExt._bindings);
+
+      nodeBindFallback(clone)._templateInstance = instanceRecord;
+      if (collectTerminator) instanceExt._terminator = clone;
+    }
+
+    instanceRecord._firstNode = instance.firstChild;
+    instanceRecord._lastNode = instance.lastChild;
+
+    instanceExt._protoContent = null;
+    instanceExt._templateCreator = null;
+    return instance;
+  }
+
+  /** The data model which is inherited through the tree. */
+  get model => _model;
+
+  void set model(value) {
+    _model = value;
+    _ensureSetModelScheduled();
+  }
+
+  static Node _deepCloneIgnoreTemplateContent(Node node, stagingDocument) {
+    var clone = stagingDocument.importNode(node, false);
+    if (isSemanticTemplate(clone)) return clone;
+
+    for (var c = node.firstChild; c != null; c = c.nextNode) {
+      clone.append(_deepCloneIgnoreTemplateContent(c, stagingDocument));
+    }
+    return clone;
+  }
+
+  /**
+   * The binding delegate which is inherited through the tree. It can be used
+   * to configure custom syntax for `{{bindings}}` inside this template.
+   */
+  BindingDelegate get bindingDelegate => _bindingDelegate;
+
+
+  void set bindingDelegate(BindingDelegate value) {
+    if (_bindingDelegate != null) {
+      throw new StateError('Template must be cleared before a new '
+          'bindingDelegate can be assigned');
+    }
+    _bindingDelegate = value;
+
+    // Clear cached state based on the binding delegate.
+    _bindingMap = null;
+    if (_iterator != null) {
+      _iterator._initPrepareFunctions = false;
+      _iterator._instanceModelFn = null;
+      _iterator._instancePositionChangedFn = null;
+    }
+  }
+
+  _ensureSetModelScheduled() {
+    if (_setModelScheduled) return;
+    _decorate();
+    _setModelScheduled = true;
+    scheduleMicrotask(_setModel);
+  }
+
+  void _setModel() {
+    _setModelScheduled = false;
+    var map = _getBindings(_node, _bindingDelegate);
+    _processBindings(_node, map, _model);
+  }
+
+  _refChanged() {
+    if (_iterator == null || _refContent == templateBind(_ref).content) return;
+
+    _refContent = null;
+    _iterator._valueChanged(null);
+    _iterator._updateIteratedValue(this._iterator._getUpdatedValue());
+  }
+
+  void clear() {
+    _model = null;
+    _bindingDelegate = null;
+    if (bindings != null) {
+      var ref = bindings.remove('ref');
+      if (ref != null) ref.close();
+    }
+    _refContent = null;
+    if (_iterator == null) return;
+    _iterator._valueChanged(null);
+    _iterator.close();
+    _iterator = null;
+  }
+
+  /** Gets the template this node refers to. */
+  Element get _ref {
+    _decorate();
+
+    var ref = _searchRefId(_node, _node.attributes['ref']);
+    if (ref == null) {
+      ref = _templateInstanceRef;
+      if (ref == null) return _node;
+    }
+
+    var nextRef = templateBindFallback(ref)._ref;
+    return nextRef != null ? nextRef : ref;
+  }
+
+  /**
+   * Gets the content of this template.
+   */
+  DocumentFragment get content {
+    _decorate();
+    return _content != null ? _content : (_node as TemplateElement).content;
+  }
+
+  /**
+   * Ensures proper API and content model for template elements.
+   *
+   * [instanceRef] can be used to set the [Element.ref] property of [template],
+   * and use the ref's content will be used as source when createInstance() is
+   * invoked.
+   *
+   * Returns true if this template was just decorated, or false if it was
+   * already decorated.
+   */
+  static bool decorate(Element template, [Element instanceRef]) =>
+      templateBindFallback(template)._decorate(instanceRef);
+
+  bool _decorate([Element instanceRef]) {
+    // == true check because it starts as a null field.
+    if (_templateIsDecorated == true) return false;
+
+    _injectStylesheet();
+    _globalBaseUriWorkaround();
+
+    var templateElementExt = this;
+    _templateIsDecorated = true;
+    var isNativeHtmlTemplate = _node is TemplateElement;
+    final bootstrapContents = isNativeHtmlTemplate;
+    final liftContents = !isNativeHtmlTemplate;
+    var liftRoot = false;
+
+    if (!isNativeHtmlTemplate) {
+      if (_isAttributeTemplate(_node)) {
+        if (instanceRef != null) {
+          // Dart note: this is just an assert in JS.
+          throw new ArgumentError('instanceRef should not be supplied for '
+              'attribute templates.');
+        }
+        templateElementExt = templateBind(
+            _extractTemplateFromAttributeTemplate(_node));
+        templateElementExt._templateIsDecorated = true;
+        isNativeHtmlTemplate = templateElementExt._node is TemplateElement;
+        liftRoot = true;
+      } else if (_isSvgTemplate(_node)) {
+        templateElementExt = templateBind(
+            _extractTemplateFromSvgTemplate(_node));
+        templateElementExt._templateIsDecorated = true;
+        isNativeHtmlTemplate = templateElementExt._node is TemplateElement;
+      }
+    }
+
+    if (!isNativeHtmlTemplate) {
+      var doc = _getOrCreateTemplateContentsOwner(templateElementExt._node);
+      templateElementExt._content = doc.createDocumentFragment();
+    }
+
+    if (instanceRef != null) {
+      // template is contained within an instance, its direct content must be
+      // empty
+      templateElementExt._templateInstanceRef = instanceRef;
+    } else if (liftContents) {
+      _liftNonNativeChildrenIntoContent(templateElementExt, _node, liftRoot);
+    } else if (bootstrapContents) {
+      bootstrap(templateElementExt.content);
+    }
+
+    return true;
+  }
+
+  static final _contentsOwner = new Expando();
+  static final _ownerStagingDocument = new Expando();
+
+  // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#dfn-template-contents-owner
+  static HtmlDocument _getOrCreateTemplateContentsOwner(Element template) {
+    var doc = template.ownerDocument;
+    if (doc.window == null) return doc;
+
+    var d = _contentsOwner[doc];
+    if (d == null) {
+      // TODO(arv): This should either be a Document or HTMLDocument depending
+      // on doc.
+      d = doc.implementation.createHtmlDocument('');
+      while (d.lastChild != null) {
+        d.lastChild.remove();
+      }
+      _contentsOwner[doc] = d;
+    }
+    return d;
+  }
+
+  HtmlDocument _getTemplateStagingDocument() {
+    if (_stagingDocument == null) {
+      var owner = _node.ownerDocument;
+      var doc = _ownerStagingDocument[owner];
+      if (doc == null) {
+        doc = owner.implementation.createHtmlDocument('');
+        _isStagingDocument[doc] = true;
+        _baseUriWorkaround(doc);
+        _ownerStagingDocument[owner] = doc;
+      }
+      _stagingDocument = doc;
+    }
+    return _stagingDocument;
+  }
+
+  // For non-template browsers, the parser will disallow <template> in certain
+  // locations, so we allow "attribute templates" which combine the template
+  // element with the top-level container node of the content, e.g.
+  //
+  //   <tr template repeat="{{ foo }}"" class="bar"><td>Bar</td></tr>
+  //
+  // becomes
+  //
+  //   <template repeat="{{ foo }}">
+  //   + #document-fragment
+  //     + <tr class="bar">
+  //       + <td>Bar</td>
+  //
+  static Element _extractTemplateFromAttributeTemplate(Element el) {
+    var template = el.ownerDocument.createElement('template');
+    el.parentNode.insertBefore(template, el);
+
+    for (var name in el.attributes.keys.toList()) {
+      switch (name) {
+        case 'template':
+          el.attributes.remove(name);
+          break;
+        case 'repeat':
+        case 'bind':
+        case 'ref':
+          template.attributes[name] = el.attributes.remove(name);
+          break;
+      }
+    }
+
+    return template;
+  }
+
+  static Element _extractTemplateFromSvgTemplate(Element el) {
+    var template = el.ownerDocument.createElement('template');
+    el.parentNode.insertBefore(template, el);
+    template.attributes.addAll(el.attributes);
+
+    el.attributes.clear();
+    el.remove();
+    return template;
+  }
+
+  static void _liftNonNativeChildrenIntoContent(TemplateBindExtension template,
+      Element el, bool useRoot) {
+
+    var content = template.content;
+    if (useRoot) {
+      content.append(el);
+      return;
+    }
+
+    var child;
+    while ((child = el.firstChild) != null) {
+      content.append(child);
+    }
+  }
+
+  /**
+   * This used to decorate recursively all templates from a given node.
+   *
+   * By default [decorate] will be called on templates lazily when certain
+   * properties such as [model] are accessed, but it can be run eagerly to
+   * decorate an entire tree recursively.
+   */
+  // TODO(rafaelw): Review whether this is the right public API.
+  static void bootstrap(Node content) {
+    void _bootstrap(template) {
+      if (!TemplateBindExtension.decorate(template)) {
+        bootstrap(templateBind(template).content);
+      }
+    }
+
+    // Need to do this first as the contents may get lifted if |node| is
+    // template.
+    // TODO(jmesserly): content is DocumentFragment or Element
+    var descendents =
+        (content as dynamic).querySelectorAll(_allTemplatesSelectors);
+    if (isSemanticTemplate(content)) {
+      _bootstrap(content);
+    }
+
+    descendents.forEach(_bootstrap);
+  }
+
+  static final String _allTemplatesSelectors =
+      'template, ' +
+      _SEMANTIC_TEMPLATE_TAGS.keys.map((k) => "$k[template]").join(", ");
+
+  static bool _initStyles;
+
+  // This is to replicate template_element.css
+  // TODO(jmesserly): move this to an opt-in CSS file?
+  static void _injectStylesheet() {
+    if (_initStyles == true) return;
+    _initStyles = true;
+
+    var style = new StyleElement()
+        ..text = '$_allTemplatesSelectors { display: none; }';
+    document.head.append(style);
+  }
+
+  static bool _initBaseUriWorkaround;
+
+  static void _globalBaseUriWorkaround() {
+    if (_initBaseUriWorkaround == true) return;
+    _initBaseUriWorkaround = true;
+
+    var t = document.createElement('template');
+    if (t is TemplateElement) {
+      var d = t.content.ownerDocument;
+      if (d.documentElement == null) {
+        d.append(d.createElement('html')).append(d.createElement('head'));
+      }
+      // don't patch this if TemplateBinding.js already has.
+      if (d.head.querySelector('base') == null) {
+        _baseUriWorkaround(d);
+      }
+    }
+  }
+
+  // TODO(rafaelw): Remove when fix for
+  // https://codereview.chromium.org/164803002/
+  // makes it to Chrome release.
+  static void _baseUriWorkaround(HtmlDocument doc) {
+    BaseElement base = doc.createElement('base');
+    base.href = document.baseUri;
+    doc.head.append(base);
+  }
+
+  static final _templateObserver = new MutationObserver((records, _) {
+    for (MutationRecord record in records) {
+      templateBindFallback(record.target)._refChanged();
+    }
+  });
+
+}
+
+final DocumentFragment _emptyInstance = () {
+  var empty = new DocumentFragment();
+  _instanceExtension[empty] = new _InstanceExtension();
+  return empty;
+}();
+
+// TODO(jmesserly): if we merged with wtih TemplateInstance, it seems like it
+// would speed up some operations (e.g. _getInstanceRoot wouldn't need to walk
+// the parent chain).
+class _InstanceExtension {
+  final List _bindings = [];
+  Node _terminator;
+  Element _templateCreator;
+  DocumentFragment _protoContent;
+}
+
+// TODO(jmesserly): this is private in JS but public for us because pkg:polymer
+// uses it.
+List getTemplateInstanceBindings(DocumentFragment fragment) {
+  var ext = _instanceExtension[fragment];
+  return ext != null ? ext._bindings : ext;
+}
+
+/// Gets the root of the current node's parent chain
+_getFragmentRoot(Node node) {
+  var p;
+  while ((p = node.parentNode) != null) {
+    node = p;
+  }
+  return node;
+}
+
+Node _searchRefId(Node node, String id) {
+  if (id == null || id == '') return null;
+
+  final selector = '#$id';
+  while (true) {
+    node = _getFragmentRoot(node);
+
+    Node ref = null;
+
+    _InstanceExtension instance = _instanceExtension[node];
+    if (instance != null && instance._protoContent != null) {
+      ref = instance._protoContent.querySelector(selector);
+    } else if (_hasGetElementById(node)) {
+      ref = (node as dynamic).getElementById(id);
+    }
+
+    if (ref != null) return ref;
+
+    if (instance == null) return null;
+    node = instance._templateCreator;
+    if (node == null) return null;
+  }
+}
+
+_getInstanceRoot(node) {
+  while (node.parentNode != null) {
+    node = node.parentNode;
+  }
+  _InstanceExtension instance = _instanceExtension[node];
+  return instance != null && instance._templateCreator != null ? node : null;
+}
+
+// Note: JS code tests that getElementById is present. We can't do that
+// easily, so instead check for the types known to implement it.
+bool _hasGetElementById(Node node) =>
+    node is Document || node is ShadowRoot || node is SvgSvgElement;
+
+final Expando<_InstanceExtension> _instanceExtension = new Expando();
+
+final _isStagingDocument = new Expando();
diff --git a/template_binding/lib/src/template_iterator.dart b/template_binding/lib/src/template_iterator.dart
new file mode 100644
index 0000000..7100486
--- /dev/null
+++ b/template_binding/lib/src/template_iterator.dart
@@ -0,0 +1,556 @@
+// Copyright (c) 2013, 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.
+
+part of template_binding;
+
+// This code is a port of what was formerly known as Model-Driven-Views, now
+// located at:
+//     https://github.com/polymer/TemplateBinding
+//     https://github.com/polymer/NodeBind
+
+// TODO(jmesserly): not sure what kind of boolean conversion rules to
+// apply for template data-binding. HTML attributes are true if they're
+// present. However Dart only treats "true" as true. Since this is HTML we'll
+// use something closer to the HTML rules: null (missing) and false are false,
+// everything else is true.
+// See: https://github.com/polymer/TemplateBinding/issues/59
+bool _toBoolean(value) => null != value && false != value;
+
+// Dart note: this was added to decouple the MustacheTokens.parse function from
+// the rest of template_binding.
+_getDelegateFactory(name, node, delegate) {
+  if (delegate == null) return null;
+  return (pathString) => delegate.prepareBinding(pathString, name, node);
+}
+
+_InstanceBindingMap _getBindings(Node node, BindingDelegate delegate) {
+  if (node is Element) {
+    return _parseAttributeBindings(node, delegate);
+  }
+
+  if (node is Text) {
+    var tokens = MustacheTokens.parse(node.text,
+        _getDelegateFactory('text', node, delegate));
+    if (tokens != null) return new _InstanceBindingMap(['text', tokens]);
+  }
+
+  return null;
+}
+
+void _addBindings(Node node, model, [BindingDelegate delegate]) {
+  final bindings = _getBindings(node, delegate);
+  if (bindings != null) {
+    _processBindings(node, bindings, model);
+  }
+
+  for (var c = node.firstChild; c != null; c = c.nextNode) {
+    _addBindings(c, model, delegate);
+  }
+}
+
+MustacheTokens _parseWithDefault(Element element, String name,
+    BindingDelegate delegate) {
+
+  var v = element.attributes[name];
+  if (v == '') v = '{{}}';
+  return MustacheTokens.parse(v, _getDelegateFactory(name, element, delegate));
+}
+
+_InstanceBindingMap _parseAttributeBindings(Element element,
+    BindingDelegate delegate) {
+
+  var bindings = null;
+  var ifFound = false;
+  var bindFound = false;
+  var isTemplateNode = isSemanticTemplate(element);
+
+  element.attributes.forEach((name, value) {
+    // Allow bindings expressed in attributes to be prefixed with underbars.
+    // We do this to allow correct semantics for browsers that don't implement
+    // <template> where certain attributes might trigger side-effects -- and
+    // for IE which sanitizes certain attributes, disallowing mustache
+    // replacements in their text.
+    while (name[0] == '_') {
+      name = name.substring(1);
+    }
+
+    if (isTemplateNode &&
+        (name == 'bind' || name == 'if' || name == 'repeat')) {
+      return;
+    }
+
+    var tokens = MustacheTokens.parse(value,
+        _getDelegateFactory(name, element, delegate));
+    if (tokens != null) {
+      if (bindings == null) bindings = [];
+      bindings..add(name)..add(tokens);
+    }
+  });
+
+  if (isTemplateNode) {
+    if (bindings == null) bindings = [];
+    var result = new _TemplateBindingMap(bindings)
+        .._if = _parseWithDefault(element, 'if', delegate)
+        .._bind = _parseWithDefault(element, 'bind', delegate)
+        .._repeat = _parseWithDefault(element, 'repeat', delegate);
+
+    // Treat <template if> as <template bind if>
+    if (result._if != null && result._bind == null && result._repeat == null) {
+      result._bind = MustacheTokens.parse('{{}}',
+          _getDelegateFactory('bind', element, delegate));
+    }
+
+    return result;
+  }
+
+  return bindings == null ? null : new _InstanceBindingMap(bindings);
+}
+
+_processOneTimeBinding(String name, MustacheTokens tokens, Node node, model) {
+
+  if (tokens.hasOnePath) {
+    var delegateFn = tokens.getPrepareBinding(0);
+    var value = delegateFn != null ? delegateFn(model, node, true) :
+        tokens.getPath(0).getValueFrom(model);
+    return tokens.isSimplePath ? value : tokens.combinator(value);
+  }
+
+  // Tokens uses a striding scheme to essentially store a sequence of structs in
+  // the list. See _MustacheTokens for more information.
+  var values = new List(tokens.length);
+  for (int i = 0; i < tokens.length; i++) {
+    Function delegateFn = tokens.getPrepareBinding(i);
+    values[i] = delegateFn != null ?
+        delegateFn(model, node, false) :
+        tokens.getPath(i).getValueFrom(model);
+  }
+  return tokens.combinator(values);
+}
+
+_processSinglePathBinding(String name, MustacheTokens tokens, Node node,
+    model) {
+  Function delegateFn = tokens.getPrepareBinding(0);
+  var observer = delegateFn != null ?
+      delegateFn(model, node, false) :
+      new PathObserver(model, tokens.getPath(0));
+
+  return tokens.isSimplePath ? observer :
+      new ObserverTransform(observer, tokens.combinator);
+}
+
+_processBinding(String name, MustacheTokens tokens, Node node, model) {
+  if (tokens.onlyOneTime) {
+    return _processOneTimeBinding(name, tokens, node, model);
+  }
+  if (tokens.hasOnePath) {
+    return _processSinglePathBinding(name, tokens, node, model);
+  }
+
+  var observer = new CompoundObserver();
+
+  for (int i = 0; i < tokens.length; i++) {
+    bool oneTime = tokens.getOneTime(i);
+    Function delegateFn = tokens.getPrepareBinding(i);
+
+    if (delegateFn != null) {
+      var value = delegateFn(model, node, oneTime);
+      if (oneTime) {
+        observer.addPath(value);
+      } else {
+        observer.addObserver(value);
+      }
+      continue;
+    }
+
+    PropertyPath path = tokens.getPath(i);
+    if (oneTime) {
+      observer.addPath(path.getValueFrom(model));
+    } else {
+      observer.addPath(model, path);
+    }
+  }
+
+  return new ObserverTransform(observer, tokens.combinator);
+}
+
+void _processBindings(Node node, _InstanceBindingMap map, model,
+    [List<Bindable> instanceBindings]) {
+
+  final bindings = map.bindings;
+  final nodeExt = nodeBind(node);
+  for (var i = 0; i < bindings.length; i += 2) {
+    var name = bindings[i];
+    var tokens = bindings[i + 1];
+
+    var value = _processBinding(name, tokens, node, model);
+    var binding = nodeExt.bind(name, value, oneTime: tokens.onlyOneTime);
+    if (binding != null && instanceBindings != null) {
+      instanceBindings.add(binding);
+    }
+  }
+
+  nodeExt.bindFinished();
+  if (map is! _TemplateBindingMap) return;
+
+  final templateExt = nodeBindFallback(node);
+  templateExt._model = model;
+
+  var iter = templateExt._processBindingDirectives(map);
+  if (iter != null && instanceBindings != null) {
+    instanceBindings.add(iter);
+  }
+}
+
+
+// Note: this doesn't really implement most of Bindable. See:
+// https://github.com/Polymer/TemplateBinding/issues/147
+class _TemplateIterator extends Bindable {
+  final TemplateBindExtension _templateExt;
+
+  final List<DocumentFragment> _instances = [];
+
+  /** A copy of the last rendered [_presentValue] list state. */
+  final List _iteratedValue = [];
+
+  List _presentValue;
+
+  bool _closed = false;
+
+  // Dart note: instead of storing these in a Map like JS, or using a separate
+  // object (extra memory overhead) we just inline the fields.
+  var _ifValue, _value;
+
+  // TODO(jmesserly): lots of booleans in this object. Bitmask?
+  bool _hasIf, _hasRepeat;
+  bool _ifOneTime, _oneTime;
+
+  StreamSubscription _listSub;
+
+  bool _initPrepareFunctions = false;
+  PrepareInstanceModelFunction _instanceModelFn;
+  PrepareInstancePositionChangedFunction _instancePositionChangedFn;
+
+  _TemplateIterator(this._templateExt);
+
+  open(callback) => throw new StateError('binding already opened');
+  get value => _value;
+
+  Element get _templateElement => _templateExt._node;
+
+  void _closeDependencies() {
+    if (_ifValue is Bindable) {
+      _ifValue.close();
+      _ifValue = null;
+    }
+    if (_value is Bindable) {
+      _value.close();
+      _value = null;
+    }
+  }
+
+  void _updateDependencies(_TemplateBindingMap directives, model) {
+    _closeDependencies();
+
+    final template = _templateElement;
+
+    _hasIf = directives._if != null;
+    _hasRepeat = directives._repeat != null;
+
+    var ifValue = true;
+    if (_hasIf) {
+      _ifOneTime = directives._if.onlyOneTime;
+      _ifValue = _processBinding('if', directives._if, template, model);
+      ifValue = _ifValue;
+
+      // oneTime if & predicate is false. nothing else to do.
+      if (_ifOneTime && !_toBoolean(ifValue)) {
+        _valueChanged(null);
+        return;
+      }
+
+      if (!_ifOneTime) {
+        ifValue = (ifValue as Bindable).open(_updateIfValue);
+      }
+    }
+
+    if (_hasRepeat) {
+      _oneTime = directives._repeat.onlyOneTime;
+      _value = _processBinding('repeat', directives._repeat, template, model);
+    } else {
+      _oneTime = directives._bind.onlyOneTime;
+      _value = _processBinding('bind', directives._bind, template, model);
+    }
+
+    var value = _value;
+    if (!_oneTime) {
+      value = _value.open(_updateIteratedValue);
+    }
+
+    if (!_toBoolean(ifValue)) {
+      _valueChanged(null);
+      return;
+    }
+
+    _updateValue(value);
+  }
+
+  /// Gets the updated value of the bind/repeat. This can potentially call
+  /// user code (if a bindingDelegate is set up) so we try to avoid it if we
+  /// already have the value in hand (from Observer.open).
+  Object _getUpdatedValue() {
+    var value = _value;
+    // Dart note: x.discardChanges() is x.value in Dart.
+    if (!_toBoolean(_oneTime)) value = value.value;
+    return value;
+  }
+
+  void _updateIfValue(ifValue) {
+    if (!_toBoolean(ifValue)) {
+      _valueChanged(null);
+      return;
+    }
+    _updateValue(_getUpdatedValue());
+  }
+
+  void _updateIteratedValue(value) {
+    if (_hasIf) {
+      var ifValue = _ifValue;
+      if (!_ifOneTime) ifValue = (ifValue as Bindable).value;
+      if (!_toBoolean(ifValue)) {
+        _valueChanged([]);
+        return;
+      }
+    }
+
+    _updateValue(value);
+  }
+
+  void _updateValue(Object value) {
+    if (!_hasRepeat) value = [value];
+    _valueChanged(value);
+  }
+
+  void _valueChanged(Object value) {
+    if (value is! List) {
+      if (value is Iterable) {
+        // Dart note: we support Iterable by calling toList.
+        // But we need to be careful to observe the original iterator if it
+        // supports that.
+        value = (value as Iterable).toList();
+      } else {
+        value = [];
+      }
+    }
+
+    if (identical(value, _iteratedValue)) return;
+
+    _unobserve();
+    _presentValue = value;
+
+    if (value is ObservableList && _hasRepeat && !_oneTime) {
+      // Make sure any pending changes aren't delivered, since we're getting
+      // a snapshot at this point in time.
+      value.discardListChages();
+      _listSub = value.listChanges.listen(_handleSplices);
+    }
+
+    _handleSplices(ObservableList.calculateChangeRecords(
+        _iteratedValue != null ? _iteratedValue : [],
+        _presentValue != null ? _presentValue : []));
+  }
+
+  Node _getLastInstanceNode(int index) {
+    if (index == -1) return _templateElement;
+    // TODO(jmesserly): we could avoid this expando lookup by caching the
+    // instance extension instead of the instance.
+    var instance = _instanceExtension[_instances[index]];
+    var terminator = instance._terminator;
+    if (terminator == null) return _getLastInstanceNode(index - 1);
+
+    if (!isSemanticTemplate(terminator) ||
+        identical(terminator, _templateElement)) {
+      return terminator;
+    }
+
+    var subtemplateIterator = templateBindFallback(terminator)._iterator;
+    if (subtemplateIterator == null) return terminator;
+
+    return subtemplateIterator._getLastTemplateNode();
+  }
+
+  Node _getLastTemplateNode() => _getLastInstanceNode(_instances.length - 1);
+
+  void _insertInstanceAt(int index, DocumentFragment fragment) {
+    var previousInstanceLast = _getLastInstanceNode(index - 1);
+    var parent = _templateElement.parentNode;
+
+    _instances.insert(index, fragment);
+    parent.insertBefore(fragment, previousInstanceLast.nextNode);
+  }
+
+  DocumentFragment _extractInstanceAt(int index) {
+    var previousInstanceLast = _getLastInstanceNode(index - 1);
+    var lastNode = _getLastInstanceNode(index);
+    var parent = _templateElement.parentNode;
+    var instance = _instances.removeAt(index);
+
+    while (lastNode != previousInstanceLast) {
+      var node = previousInstanceLast.nextNode;
+      if (node == lastNode) lastNode = previousInstanceLast;
+
+      instance.append(node..remove());
+    }
+
+    return instance;
+  }
+
+  void _handleSplices(List<ListChangeRecord> splices) {
+    if (_closed || splices.isEmpty) return;
+
+    final template = _templateElement;
+
+    if (template.parentNode == null) {
+      close();
+      return;
+    }
+
+    ObservableList.applyChangeRecords(_iteratedValue, _presentValue, splices);
+
+    final delegate = _templateExt.bindingDelegate;
+
+    // Dart note: the JavaScript code relies on the distinction between null
+    // and undefined to track whether the functions are prepared. We use a bool.
+    if (!_initPrepareFunctions) {
+      _initPrepareFunctions = true;
+      final delegate = _templateExt._self.bindingDelegate;
+      if (delegate != null) {
+        _instanceModelFn = delegate.prepareInstanceModel(template);
+        _instancePositionChangedFn =
+            delegate.prepareInstancePositionChanged(template);
+      }
+    }
+
+    // Instance Removals.
+    var instanceCache = new HashMap(equals: identical);
+    var removeDelta = 0;
+    for (var splice in splices) {
+      for (var model in splice.removed) {
+        var instance = _extractInstanceAt(splice.index + removeDelta);
+        if (instance != _emptyInstance) {
+          instanceCache[model] = instance;
+        }
+      }
+
+      removeDelta -= splice.addedCount;
+    }
+
+    for (var splice in splices) {
+      for (var addIndex = splice.index;
+          addIndex < splice.index + splice.addedCount;
+          addIndex++) {
+
+        var model = _iteratedValue[addIndex];
+        DocumentFragment instance = instanceCache.remove(model);
+        if (instance == null) {
+          try {
+            if (_instanceModelFn != null) {
+              model = _instanceModelFn(model);
+            }
+            if (model == null) {
+              instance = _emptyInstance;
+            } else {
+              instance = _templateExt.createInstance(model, delegate);
+            }
+          } catch (e, s) {
+            // Dart note: we propagate errors asynchronously here to avoid
+            // disrupting the rendering flow. This is different than in the JS
+            // implementation but it should probably be fixed there too. Dart
+            // hits this case more because non-existing properties in
+            // [PropertyPath] are treated as errors, while JS treats them as
+            // null/undefined.
+            // TODO(sigmund): this should be a synchronous throw when this is
+            // called from createInstance, but that requires enough refactoring
+            // that it should be done upstream first. See dartbug.com/17789.
+            new Completer().completeError(e, s);
+            instance = _emptyInstance;
+          }
+        }
+
+        _insertInstanceAt(addIndex, instance);
+      }
+    }
+
+    for (var instance in instanceCache.values) {
+      _closeInstanceBindings(instance);
+    }
+
+    if (_instancePositionChangedFn != null) _reportInstancesMoved(splices);
+  }
+
+  void _reportInstanceMoved(int index) {
+    var instance = _instances[index];
+    if (instance == _emptyInstance) return;
+
+    _instancePositionChangedFn(nodeBind(instance).templateInstance, index);
+  }
+
+  void _reportInstancesMoved(List<ListChangeRecord> splices) {
+    var index = 0;
+    var offset = 0;
+    for (var splice in splices) {
+      if (offset != 0) {
+        while (index < splice.index) {
+          _reportInstanceMoved(index);
+          index++;
+        }
+      } else {
+        index = splice.index;
+      }
+
+      while (index < splice.index + splice.addedCount) {
+        _reportInstanceMoved(index);
+        index++;
+      }
+
+      offset += splice.addedCount - splice.removed.length;
+    }
+
+    if (offset == 0) return;
+
+    var length = _instances.length;
+    while (index < length) {
+      _reportInstanceMoved(index);
+      index++;
+    }
+  }
+
+  void _closeInstanceBindings(DocumentFragment instance) {
+    var bindings = _instanceExtension[instance]._bindings;
+    for (var binding in bindings) binding.close();
+  }
+
+  void _unobserve() {
+    if (_listSub == null) return;
+    _listSub.cancel();
+    _listSub = null;
+  }
+
+  void close() {
+    if (_closed) return;
+
+    _unobserve();
+    _instances.forEach(_closeInstanceBindings);
+    _instances.clear();
+    _closeDependencies();
+    _templateExt._iterator = null;
+    _closed = true;
+  }
+}
+
+// Dart note: the JavaScript version just puts an expando on the array.
+class _BoundNodes {
+  final List<Node> nodes;
+  final List<Bindable> instanceBindings;
+  _BoundNodes(this.nodes, this.instanceBindings);
+}
diff --git a/template_binding/lib/template_binding.dart b/template_binding/lib/template_binding.dart
new file mode 100644
index 0000000..88fa17b
--- /dev/null
+++ b/template_binding/lib/template_binding.dart
@@ -0,0 +1,180 @@
+// 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.
+
+/**
+ * This library provides access to the Polymer project's
+ * [Data Binding](http://www.polymer-project.org/docs/polymer/databinding.html)
+ * Find more information at the
+ * [Polymer.dart homepage](https://www.dartlang.org/polymer-dart/).
+ *
+ * Extends the capabilities of the HTML Template Element by enabling it to
+ * create, manage, and remove instances of content bound to data defined in
+ * Dart.
+ *
+ * Node.bind() is a new method added to all DOM nodes which instructs them to
+ * bind the named property to the data provided. These allows applications to
+ * create a data model in Dart or JavaScript that DOM reacts to.
+ */
+library template_binding;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:html';
+import 'dart:js' as js show context;
+import 'dart:js' show JsObject;
+import 'dart:svg' show SvgSvgElement;
+import 'package:observe/observe.dart';
+
+import 'src/mustache_tokens.dart';
+
+part 'src/binding_delegate.dart';
+part 'src/instance_binding_map.dart';
+part 'src/node.dart';
+part 'src/template.dart';
+part 'src/template_iterator.dart';
+
+// TODO(jmesserly): ideally we would split TemplateBinding and Node.bind into
+// two packages, but this is not easy when we are faking extension methods.
+// Since TemplateElement needs to override Node.bind, it seems like the
+// Node.bind layer must have some innate knowledge of TemplateBinding.
+// NOTE: I've heard NodeBind might become an internal API, which is all the more
+// reason to have it in this package.
+
+/**
+ * Provides access to the data binding APIs for the [node]. For example:
+ *
+ *     templateBind(node).model = new MyModel();
+ *
+ * This is equivalent to [nodeBind], but provides access to
+ * [TemplateBindExtension] APIs. [node] should be a [TemplateElement], or
+ * equivalent semantic template such as:
+ *
+ *     <table template repeat="{{row in rows}}">
+ *       <tr template repeat="{{item in row}}">
+ *         <td>{{item}}</td>
+ *       </tr>
+ *     </table>
+ */
+TemplateBindExtension templateBind(Element node) => nodeBind(node);
+
+/**
+ * Like [templateBind], but intended to be used only within a custom element
+ * that implements [TemplateBindExtension]. This method can be used to simulate
+ * a super call. For example:
+ *
+ *     class CoolTemplate extends TemplateElement
+ *         implements TemplateBindExtension {
+ *
+ *       createInstance(model, delegate) {
+ *         // do something cool...
+ *         // otherwise, fall back to superclass
+ *         return templateBindFallback(this).createInstance(model, delegate);
+ *       }
+ *       ...
+ *     }
+ */
+TemplateBindExtension templateBindFallback(Element node) =>
+    nodeBindFallback(node);
+
+/**
+ * Provides access to the data binding APIs for the [node]. For example:
+ *
+ *     nodeBind(node).bind('checked', model, 'path.to.some.value');
+ */
+NodeBindExtension nodeBind(Node node) {
+  return node is NodeBindExtension ? node : nodeBindFallback(node);
+}
+
+/**
+ * Like [nodeBind], but intended to be used only within a custom element that
+ * implements [NodeBindExtension]. This method can be used to simulate a super
+ * call. For example:
+ *
+ *     class FancyButton extends ButtonElement implements NodeBindExtension {
+ *       bind(name, model, path) {
+ *         if (name == 'fancy-prop') ... // do fancy binding
+ *         // otherwise, fall back to superclass
+ *         return nodeBindFallback(this).bind(name, model, path);
+ *       }
+ *       ...
+ *     }
+ */
+NodeBindExtension nodeBindFallback(Node node) {
+  var extension = _expando[node];
+  if (extension != null) return extension;
+
+  if (isSemanticTemplate(node)) {
+    extension = new TemplateBindExtension._(node);
+  } else {
+    extension = new NodeBindExtension._(node);
+  }
+  _expando[node] = extension;
+  return extension;
+}
+
+
+bool _isAttributeTemplate(Element n) => n.attributes.containsKey('template') &&
+    _SEMANTIC_TEMPLATE_TAGS.containsKey(n.localName);
+
+bool _isSvgTemplate(Element el) => el.tagName == 'template' &&
+    el.namespaceUri == 'http://www.w3.org/2000/svg';
+
+bool _isHtmlTemplate(Element el) => el.tagName == 'TEMPLATE' &&
+    el.namespaceUri == 'http://www.w3.org/1999/xhtml';
+
+/**
+ * Returns true if this node is semantically a template.
+ *
+ * A node is a template if [tagName] is TEMPLATE, or the node has the
+ * 'template' attribute and this tag supports attribute form for backwards
+ * compatibility with existing HTML parsers. The nodes that can use attribute
+ * form are table elements (THEAD, TBODY, TFOOT, TH, TR, TD, CAPTION, COLGROUP
+ * and COL), OPTION, and OPTGROUP.
+ */
+bool isSemanticTemplate(Node n) => n is Element &&
+    (_isHtmlTemplate(n) || _isAttributeTemplate(n) || _isSvgTemplate(n));
+
+/** Returns true if this is the staging document for a template. */
+bool isTemplateStagingDocument(Document d) => _isStagingDocument[d] == true;
+
+
+/**
+ * True to enable [NodeBindingExtension.bindings]. This can be used by tools
+ * such as UI builders to easily inspect live bindings. Defaults to false for
+ * performance reasons.
+ */
+bool get enableBindingsReflection =>
+    js.context['Platform']['enableBindingsReflection'] == true;
+
+set enableBindingsReflection(bool value) {
+  js.context['Platform']['enableBindingsReflection'] = value;
+}
+
+// TODO(jmesserly): const set would be better
+const _SEMANTIC_TEMPLATE_TAGS = const {
+  'caption': null,
+  'col': null,
+  'colgroup': null,
+  'option': null,
+  'optgroup': null,
+  'tbody': null,
+  'td': null,
+  'tfoot': null,
+  'th': null,
+  'thead': null,
+  'tr': null,
+};
+
+
+// TODO(jmesserly): investigate if expandos give us enough performance.
+
+// The expando for storing our MDV extensions.
+//
+// In general, we need state associated with the nodes. Rather than having a
+// bunch of individual expandos, we keep one per node.
+//
+// Aside from the potentially helping performance, it also keeps things simpler
+// if we decide to integrate MDV into the DOM later, and means less code needs
+// to worry about expandos.
+final Expando _expando = new Expando('template_binding');
diff --git a/template_binding/pubspec.yaml b/template_binding/pubspec.yaml
new file mode 100644
index 0000000..47dac00
--- /dev/null
+++ b/template_binding/pubspec.yaml
@@ -0,0 +1,15 @@
+name: template_binding
+version: 0.14.0+2
+author: Polymer.dart Team <web-ui-dev@dartlang.org>
+description: >
+  Extends the capabilities of the HTML Template Element by enabling it to
+  create, manage, and remove instances of content bound to data defined in Dart.
+homepage: https://www.dartlang.org/polymer-dart/
+dependencies:
+  observe: ">=0.11.0 <0.14.0"
+  web_components: ">=0.7.0 <0.12.0"
+dev_dependencies:
+  unittest: ">=0.10.0 <0.12.0"
+  browser: ">=0.10.0 <0.11.0"
+environment:
+  sdk: ">=1.2.0 <2.0.0"
diff --git a/unittest/lib/compact_vm_config.dart b/unittest/lib/compact_vm_config.dart
new file mode 100644
index 0000000..1fadfc1
--- /dev/null
+++ b/unittest/lib/compact_vm_config.dart
@@ -0,0 +1,214 @@
+// Copyright (c) 2013, 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.
+
+/// A test configuration that generates a compact 1-line progress bar. The bar
+/// is updated in-place before and after each test is executed. If all tests
+/// pass, only a couple of lines are printed in the terminal. If a test fails,
+/// the failure is shown and the progress bar continues to be updated below it.
+library unittest.compact_vm_config;
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:isolate';
+
+import 'unittest.dart';
+import 'src/utils.dart';
+import 'vm_config.dart';
+
+const String _GREEN = '\u001b[32m';
+const String _RED = '\u001b[31m';
+const String _NONE = '\u001b[0m';
+
+const int MAX_LINE = 80;
+
+class CompactVMConfiguration extends VMConfiguration {
+  // The VM won't shut down if a receive port is open. Use this to make sure
+  // we correctly wait for asynchronous tests.
+  ReceivePort _receivePort;
+
+  DateTime _start;
+  Set<int> _passing = new Set();
+  Set<int> _failing = new Set();
+  int get _pass => _passing.length;
+  int get _fail => _failing.length;
+
+  void onInit() {
+    _receivePort = new ReceivePort();
+    // Override and don't call the superclass onInit() to avoid printing the
+    // "unittest-suite-..." boilerplate.
+  }
+
+  void onStart() {
+    _start = new DateTime.now();
+  }
+
+  void onTestStart(TestCase test) {
+    super.onTestStart(test);
+    _progressLine(test.description);
+  }
+
+  void onTestResult(TestCase test) {
+    super.onTestResult(test);
+    if (test.result == PASS) {
+      _passing.add(test.id);
+      _progressLine(test.description);
+    } else {
+      _failing.add(test.id);
+      _progressLine(test.description);
+      _print();
+      if (test.message != '') {
+        _print(indent(test.message));
+      }
+
+      if (test.stackTrace != null) {
+        _print(indent(test.stackTrace.toString()));
+      }
+    }
+  }
+
+  void onTestResultChanged(TestCase test) {
+    _passing.remove(test.id);
+    _failing.add(test.id);
+    _progressLine(test.description);
+    _print();
+    if (test.message != '') {
+      _print(indent(test.message));
+    }
+
+    if (test.stackTrace != null) {
+      _print(indent(test.stackTrace.toString()));
+    }
+  }
+
+  void onDone(bool success) {
+    // Override and don't call the superclass onDone() to avoid printing the
+    // "unittest-suite-..." boilerplate.
+    Future.wait([stdout.close(), stderr.close()]).then((_) {
+      _receivePort.close();
+      exit(success ? 0 : 1);
+    });
+  }
+
+  void onSummary(int passed, int failed, int errors, List<TestCase> results,
+      String uncaughtError) {
+    if (passed == 0 && failed == 0 && errors == 0 && uncaughtError == null) {
+      _print('\nNo tests ran.');
+    } else if (failed == 0 && errors == 0 && uncaughtError == null) {
+      _progressLine('All tests passed!', _NONE);
+      _print();
+    } else {
+      _progressLine('Some tests failed.', _RED);
+      _print();
+      if (uncaughtError != null) {
+        _print('Top-level uncaught error: $uncaughtError');
+      }
+      _print('$passed PASSED, $failed FAILED, $errors ERRORS');
+    }
+  }
+
+  int _lastLength = 0;
+
+  final int _nonVisiblePrefix = 1 + _GREEN.length + _NONE.length;
+
+  void _progressLine(String message, [String color = _NONE]) {
+    var duration = (new DateTime.now()).difference(_start);
+    var buffer = new StringBuffer();
+    // \r moves back to the beginning of the current line.
+    buffer.write('\r${_timeString(duration)} ');
+    buffer.write(_GREEN);
+    buffer.write('+');
+    buffer.write(_pass);
+    buffer.write(_NONE);
+    if (_fail != 0) {
+      buffer.write(_RED);
+      buffer.write(' -');
+      buffer.write(_fail);
+      buffer.write(_NONE);
+    }
+    buffer.write(': ');
+    buffer.write(color);
+
+    // Ensure the line fits under MAX_LINE. [buffer] includes the color escape
+    // sequences too. Because these sequences are not visible characters, we
+    // make sure they are not counted towards the limit.
+    int nonVisible = _nonVisiblePrefix +
+        color.length +
+        (_fail != 0 ? (_RED.length + _NONE.length) : 0);
+    int len = buffer.length - nonVisible;
+    buffer.write(_snippet(message, MAX_LINE - len));
+    buffer.write(_NONE);
+
+    // Pad the rest of the line so that it looks erased.
+    len = buffer.length - nonVisible - _NONE.length;
+    if (len > _lastLength) {
+      _lastLength = len;
+    } else {
+      while (len < _lastLength) {
+        buffer.write(' ');
+        _lastLength--;
+      }
+    }
+    stdout.write(buffer.toString());
+  }
+
+  String _padTime(int time) =>
+      (time == 0) ? '00' : ((time < 10) ? '0$time' : '$time');
+
+  String _timeString(Duration duration) {
+    var min = duration.inMinutes;
+    var sec = duration.inSeconds % 60;
+    return '${_padTime(min)}:${_padTime(sec)}';
+  }
+
+  String _snippet(String text, int maxLength) {
+    // Return the full message if it fits
+    if (text.length <= maxLength) return text;
+
+    // If we can fit the first and last three words, do so.
+    var words = text.split(' ');
+    if (words.length > 1) {
+      int i = words.length;
+      var len = words.first.length + 4;
+      do {
+        len += 1 + words[--i].length;
+      } while (len <= maxLength && i > 0);
+      if (len > maxLength || i == 0) i++;
+      if (i < words.length - 4) {
+        // Require at least 3 words at the end.
+        var buffer = new StringBuffer();
+        buffer.write(words.first);
+        buffer.write(' ...');
+        for ( ; i < words.length; i++) {
+          buffer.write(' ');
+          buffer.write(words[i]);
+        }
+        return buffer.toString();
+      }
+    }
+
+    // Otherwise truncate to return the trailing text, but attempt to start at
+    // the beginning of a word.
+    var res = text.substring(text.length - maxLength + 4);
+    var firstSpace = res.indexOf(' ');
+    if (firstSpace > 0) {
+      res = res.substring(firstSpace);
+    }
+    return '...$res';
+  }
+}
+
+// TODO(sigmund): delete when dartbug.com/17269 is fixed (use `print` instead).
+_print([value = '']) => stdout.write('$value\n');
+
+void useCompactVMConfiguration() {
+  // If the test is running on the Dart buildbots, we don't want to use this
+  // config since it's output may not be what the bots expect.
+  if (Platform.environment['LOGNAME'] == 'chrome-bot') {
+    return;
+  }
+
+  unittestConfiguration = _singleton;
+}
+
+final _singleton = new CompactVMConfiguration();
diff --git a/unittest/lib/coverage_controller.js b/unittest/lib/coverage_controller.js
new file mode 100644
index 0000000..1cf21ae
--- /dev/null
+++ b/unittest/lib/coverage_controller.js
@@ -0,0 +1,31 @@
+// Copyright (c) 2011, 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.
+
+/**
+ * Coverage controller logic - used by coverage test harness to embed tests in
+ * content shell and extract coverage information.
+ */
+
+var LONG_LINE = 60000;
+
+function onReceive(e) {
+  if (e.data == 'unittest-suite-done') {
+    var s = JSON.stringify(top._$jscoverage);
+    var res = '';
+    // conent shell has a bug on lines longer than 2^16, so we split them
+    while (s.length > LONG_LINE) {
+      res += s.substr(0, LONG_LINE) + '<br>\n';
+      s = s.substr(LONG_LINE);
+    }
+    res += s;
+    window.document.body.innerHTML = res;
+    window.layoutTestController.notifyDone();
+  }
+}
+
+if (window.layoutTestController) {
+  window.layoutTestController.dumpAsText();
+  window.layoutTestController.waitUntilDone();
+  window.addEventListener("message", onReceive, false);
+}
diff --git a/unittest/lib/html_config.dart b/unittest/lib/html_config.dart
new file mode 100644
index 0000000..c472d58
--- /dev/null
+++ b/unittest/lib/html_config.dart
@@ -0,0 +1,184 @@
+// Copyright (c) 2013, 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.
+
+/// A simple unit test library for running tests in a browser.
+library unittest.html_config;
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:html';
+import 'dart:js' as js;
+import 'unittest.dart';
+
+/// Creates a table showing tests results in HTML.
+void _showResultsInPage(int passed, int failed, int errors,
+    List<TestCase> results, bool isLayoutTest, String uncaughtError) {
+  if (isLayoutTest && (passed == results.length) && uncaughtError == null) {
+    document.body.innerHtml = "PASS";
+  } else {
+    var newBody = new StringBuffer();
+    newBody.write("<table class='unittest-table'><tbody>");
+    newBody.write(passed == results.length && uncaughtError == null
+        ? "<tr><td colspan='3' class='unittest-pass'>PASS</td></tr>"
+        : "<tr><td colspan='3' class='unittest-fail'>FAIL</td></tr>");
+
+    for (final test_ in results) {
+      newBody.write(_toHtml(test_));
+    }
+
+    if (uncaughtError != null) {
+      newBody.write('''<tr>
+          <td>--</td>
+          <td class="unittest-error">ERROR</td>
+          <td>Uncaught error: $uncaughtError</td>
+        </tr>''');
+    }
+
+    if (passed == results.length && uncaughtError == null) {
+      newBody.write("""
+          <tr><td colspan='3' class='unittest-pass'>
+            All ${passed} tests passed
+          </td></tr>""");
+    } else {
+      newBody.write("""
+          <tr><td colspan='3'>Total
+            <span class='unittest-pass'>${passed} passed</span>,
+            <span class='unittest-fail'>${failed} failed</span>
+            <span class='unittest-error'>
+            ${errors + (uncaughtError == null ? 0 : 1)} errors</span>
+          </td></tr>""");
+    }
+    newBody.write("</tbody></table>");
+    document.body.innerHtml = newBody.toString();
+
+    window.onHashChange.listen((_) {
+      // Location may change from individual tests setting the hash tag.
+      if (window.location.hash != null &&
+          window.location.hash.contains('testFilter')) {
+        window.location.reload();
+      }
+    });
+  }
+}
+
+String _toHtml(TestCase testCase) {
+  if (!testCase.isComplete) {
+    return '''
+        <tr>
+          <td>${testCase.id}</td>
+          <td class="unittest-error">NO STATUS</td>
+          <td>Test did not complete</td>
+        </tr>''';
+  }
+
+  var html = '''
+      <tr>
+        <td>${testCase.id}</td>
+        <td class="unittest-${testCase.result}">
+          ${testCase.result.toUpperCase()}
+        </td>
+        <td>
+          <p>Expectation: 
+            <a href="#testFilter=${testCase.description}">
+              ${testCase.description}
+            </a>.
+          </p>
+          <pre>${HTML_ESCAPE.convert(testCase.message)}</pre>
+        </td>
+      </tr>''';
+
+  if (testCase.stackTrace != null) {
+    html = '$html<tr><td></td><td colspan="2"><pre>' +
+        HTML_ESCAPE.convert(testCase.stackTrace.toString()) +
+        '</pre></td></tr>';
+  }
+
+  return html;
+}
+
+class HtmlConfiguration extends SimpleConfiguration {
+  /// Whether this is run within dartium layout tests.
+  final bool _isLayoutTest;
+  HtmlConfiguration(this._isLayoutTest);
+
+  StreamSubscription<Event> _onErrorSubscription;
+  StreamSubscription<Event> _onMessageSubscription;
+
+  void _installHandlers() {
+    if (_onErrorSubscription == null) {
+      _onErrorSubscription = window.onError.listen((e) {
+        // Some tests may expect this and have no way to suppress the error.
+        if (js.context['testExpectsGlobalError'] != true) {
+          handleExternalError(e, '(DOM callback has errors)');
+        }
+      });
+    }
+    if (_onMessageSubscription == null) {
+      _onMessageSubscription =
+          window.onMessage.listen((e) => processMessage(e));
+    }
+  }
+
+  void _uninstallHandlers() {
+    if (_onErrorSubscription != null) {
+      _onErrorSubscription.cancel();
+      _onErrorSubscription = null;
+    }
+    if (_onMessageSubscription != null) {
+      _onMessageSubscription.cancel();
+      _onMessageSubscription = null;
+    }
+  }
+
+  void processMessage(e) {
+    if ('unittest-suite-external-error' == e.data) {
+      handleExternalError('<unknown>', '(external error detected)');
+    }
+  }
+
+  void onInit() {
+    // For Dart internal tests, we want to turn off stack frame
+    // filtering, which we do with this meta-header.
+    var meta = querySelector('meta[name="dart.unittest"]');
+    filterStacks =
+        meta == null ? true : !meta.content.contains('full-stack-traces');
+    _installHandlers();
+    window.postMessage('unittest-suite-wait-for-done', '*');
+  }
+
+  void onStart() {
+    // If the URL has a #testFilter=testName then filter tests to that.
+    // This is used to make it easy to run a single test- but is only intended
+    // for interactive debugging scenarios.
+    var hash = window.location.hash;
+    if (hash != null && hash.length > 1) {
+      var params = hash.substring(1).split('&');
+      for (var param in params) {
+        var parts = param.split('=');
+        if (parts.length == 2 && parts[0] == 'testFilter') {
+          filterTests('^${parts[1]}');
+        }
+      }
+    }
+    super.onStart();
+  }
+
+  void onSummary(int passed, int failed, int errors, List<TestCase> results,
+      String uncaughtError) {
+    _showResultsInPage(
+        passed, failed, errors, results, _isLayoutTest, uncaughtError);
+  }
+
+  void onDone(bool success) {
+    _uninstallHandlers();
+    window.postMessage('unittest-suite-done', '*');
+  }
+}
+
+void useHtmlConfiguration([bool isLayoutTest = false]) {
+  unittestConfiguration = isLayoutTest ? _singletonLayout : _singletonNotLayout;
+}
+
+final _singletonLayout = new HtmlConfiguration(true);
+final _singletonNotLayout = new HtmlConfiguration(false);
diff --git a/unittest/lib/html_enhanced_config.dart b/unittest/lib/html_enhanced_config.dart
new file mode 100644
index 0000000..0178ccb
--- /dev/null
+++ b/unittest/lib/html_enhanced_config.dart
@@ -0,0 +1,405 @@
+// Copyright (c) 2013, 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.
+
+/// A simple unit test library for running tests in a browser.
+///
+/// Provides enhanced HTML output with collapsible group headers
+/// and other at-a-glance information about the test results.
+library unittest.html_enhanced_config;
+
+import 'dart:collection' show LinkedHashMap;
+import 'dart:convert';
+import 'dart:html';
+import 'unittest.dart';
+
+class HtmlEnhancedConfiguration extends SimpleConfiguration {
+  /// Whether this is run within dartium layout tests.
+  final bool _isLayoutTest;
+  HtmlEnhancedConfiguration(this._isLayoutTest);
+
+  var _onErrorSubscription = null;
+  var _onMessageSubscription = null;
+
+  void _installOnErrorHandler() {
+    if (_onErrorSubscription == null) {
+      // Listen for uncaught errors.
+      _onErrorSubscription = window.onError
+          .listen((e) => handleExternalError(e, '(DOM callback has errors)'));
+    }
+  }
+
+  void _installOnMessageHandler() {
+    if (_onMessageSubscription == null) {
+      // Listen for errors from JS.
+      _onMessageSubscription =
+          window.onMessage.listen((e) => processMessage(e));
+    }
+  }
+
+  void _installHandlers() {
+    _installOnErrorHandler();
+    _installOnMessageHandler();
+  }
+
+  void _uninstallHandlers() {
+    if (_onErrorSubscription != null) {
+      _onErrorSubscription.cancel();
+      _onErrorSubscription = null;
+    }
+    if (_onMessageSubscription != null) {
+      _onMessageSubscription.cancel();
+      _onMessageSubscription = null;
+    }
+  }
+
+  void processMessage(e) {
+    if ('unittest-suite-external-error' == e.data) {
+      handleExternalError('<unknown>', '(external error detected)');
+    }
+  }
+
+  void onInit() {
+    _installHandlers();
+    //initialize and load CSS
+    final String _CSSID = '_unittestcss_';
+
+    var cssElement = document.head.querySelector('#${_CSSID}');
+    if (cssElement == null) {
+      cssElement = new StyleElement();
+      cssElement.id = _CSSID;
+      document.head.append(cssElement);
+    }
+
+    cssElement.text = _htmlTestCSS;
+    window.postMessage('unittest-suite-wait-for-done', '*');
+  }
+
+  void onStart() {
+    // Listen for uncaught errors.
+    _installOnErrorHandler();
+  }
+
+  void onSummary(int passed, int failed, int errors, List<TestCase> results,
+      String uncaughtError) {
+    _showInteractiveResultsInPage(
+        passed, failed, errors, results, _isLayoutTest, uncaughtError);
+  }
+
+  void onDone(bool success) {
+    _uninstallHandlers();
+    window.postMessage('unittest-suite-done', '*');
+  }
+
+  void _showInteractiveResultsInPage(int passed, int failed, int errors,
+      List<TestCase> results, bool isLayoutTest, String uncaughtError) {
+    if (isLayoutTest && passed == results.length) {
+      document.body.innerHtml = "PASS";
+    } else {
+      // changed the StringBuffer to an Element fragment
+      Element te = new Element.html('<div class="unittest-table"></div>');
+
+      te.children.add(new Element.html(passed == results.length
+          ? "<div class='unittest-overall unittest-pass'>PASS</div>"
+          : "<div class='unittest-overall unittest-fail'>FAIL</div>"));
+
+      // moved summary to the top since web browsers
+      // don't auto-scroll to the bottom like consoles typically do.
+      if (passed == results.length && uncaughtError == null) {
+        te.children.add(new Element.html("""
+          <div class='unittest-pass'>All ${passed} tests passed</div>"""));
+      } else {
+        if (uncaughtError != null) {
+          te.children.add(new Element.html("""
+            <div class='unittest-summary'>
+              <span class='unittest-error'>Uncaught error: $uncaughtError</span>
+            </div>"""));
+        }
+
+        te.children.add(new Element.html("""
+          <div class='unittest-summary'>
+            <span class='unittest-pass'>Total ${passed} passed</span>,
+            <span class='unittest-fail'>${failed} failed</span>,
+            <span class='unittest-error'>
+            ${errors + (uncaughtError == null ? 0 : 1)} errors</span>
+          </div>"""));
+      }
+
+      te.children.add(new Element.html("""
+        <div><button id='btnCollapseAll'>Collapse All</button></div>
+       """));
+
+      // handle the click event for the collapse all button
+      te.querySelector('#btnCollapseAll').onClick.listen((_) {
+        document
+            .querySelectorAll('.unittest-row')
+            .forEach((el) => el.attributes['class'] = el.attributes['class']
+                .replaceAll('unittest-row ', 'unittest-row-hidden '));
+      });
+
+      var previousGroup = '';
+      var groupPassFail = true;
+
+      // order by group and sort numerically within each group
+      var groupedBy = new LinkedHashMap<String, List<TestCase>>();
+
+      for (final t in results) {
+        if (!groupedBy.containsKey(t.currentGroup)) {
+          groupedBy[t.currentGroup] = new List<TestCase>();
+        }
+
+        groupedBy[t.currentGroup].add(t);
+      }
+
+      // flatten the list again with tests ordered
+      List<TestCase> flattened = new List<TestCase>();
+
+      groupedBy.values.forEach((tList) {
+        tList.sort((tcA, tcB) => tcA.id - tcB.id);
+        flattened.addAll(tList);
+      });
+
+      var nonAlphanumeric = new RegExp('[^a-z0-9A-Z]');
+
+      // output group headers and test rows
+      for (final test_ in flattened) {
+
+        // replace everything but numbers and letters from the group name with
+        // '_' so we can use in id and class properties.
+        var safeGroup = test_.currentGroup.replaceAll(nonAlphanumeric, '_');
+
+        if (test_.currentGroup != previousGroup) {
+          previousGroup = test_.currentGroup;
+
+          var testsInGroup = results
+              .where((TestCase t) => t.currentGroup == previousGroup)
+              .toList();
+          var groupTotalTestCount = testsInGroup.length;
+          var groupTestPassedCount =
+              testsInGroup.where((TestCase t) => t.result == 'pass').length;
+          groupPassFail = groupTotalTestCount == groupTestPassedCount;
+          var passFailClass = "unittest-group-status unittest-group-"
+              "status-${groupPassFail ? 'pass' : 'fail'}";
+
+          te.children.add(new Element.html("""
+            <div>
+              <div id='${safeGroup}'
+                   class='unittest-group ${safeGroup} test${safeGroup}'>
+                <div ${_isIE ? "style='display:inline-block' ": ""}
+                     class='unittest-row-status'>
+                  <div class='$passFailClass'></div>
+                </div>
+                <div ${_isIE ? "style='display:inline-block' ": ""}>
+                    ${test_.currentGroup}</div>
+                &nbsp;
+                <div ${_isIE ? "style='display:inline-block' ": ""}>
+                    (${groupTestPassedCount}/${groupTotalTestCount})</div>
+              </div>
+            </div>"""));
+
+          // 'safeGroup' could be empty
+          var grp =
+              (safeGroup == '') ? null : te.querySelector('#${safeGroup}');
+          if (grp != null) {
+            grp.onClick.listen((_) {
+              var row = document.querySelector('.unittest-row-${safeGroup}');
+              if (row.attributes['class'].contains('unittest-row ')) {
+                document.querySelectorAll('.unittest-row-${safeGroup}').forEach(
+                    (e) => e.attributes['class'] = e.attributes['class']
+                        .replaceAll('unittest-row ', 'unittest-row-hidden '));
+              } else {
+                document.querySelectorAll('.unittest-row-${safeGroup}').forEach(
+                    (e) => e.attributes['class'] = e.attributes['class']
+                        .replaceAll('unittest-row-hidden', 'unittest-row'));
+              }
+            });
+          }
+        }
+
+        _buildRow(test_, te, safeGroup, !groupPassFail);
+      }
+
+      document.body.children.clear();
+      document.body.children.add(te);
+    }
+  }
+
+  void _buildRow(TestCase test_, Element te, String groupID, bool isVisible) {
+    var background = 'unittest-row-${test_.id % 2 == 0 ? "even" : "odd"}';
+    var display = '${isVisible ? "unittest-row" : "unittest-row-hidden"}';
+
+    addRowElement(id, status, description) {
+      te.children.add(new Element.html(''' <div>
+                <div class='$display unittest-row-${groupID} $background'>
+                  <div ${_isIE ? "style='display:inline-block' ": ""}
+                       class='unittest-row-id'>$id</div>
+                  <div ${_isIE ? "style='display:inline-block' ": ""}
+                       class="unittest-row-status unittest-${test_.result}">
+                       $status</div>
+                  <div ${_isIE ? "style='display:inline-block' ": ""}
+                       class='unittest-row-description'>$description</div>
+                </div>
+              </div>'''));
+    }
+
+    if (!test_.isComplete) {
+      addRowElement('${test_.id}', 'NO STATUS', 'Test did not complete.');
+      return;
+    }
+
+    addRowElement('${test_.id}', '${test_.result.toUpperCase()}',
+        '${test_.description}. ${HTML_ESCAPE.convert(test_.message)}');
+
+    if (test_.stackTrace != null) {
+      addRowElement('', '',
+          '<pre>${HTML_ESCAPE.convert(test_.stackTrace.toString())}</pre>');
+    }
+  }
+
+  static bool get _isIE => window.navigator.userAgent.contains('MSIE');
+
+  String get _htmlTestCSS => '''
+  body{
+    font-size: 14px;
+    font-family: 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande','''
+      ''' sans-serif;
+    background: WhiteSmoke;
+  }
+
+  .unittest-group
+  {
+    background: rgb(75,75,75);
+    width:98%;
+    color: WhiteSmoke;
+    font-weight: bold;
+    padding: 6px;
+    cursor: pointer;
+
+    /* Provide some visual separation between groups for IE */
+    ${_isIE ? "border-bottom:solid black 1px;": ""}
+    ${_isIE ? "border-top:solid #777777 1px;": ""}
+
+    background-image: -webkit-linear-gradient(bottom, rgb(50,50,50) 0%, '''
+      '''rgb(100,100,100) 100%);
+    background-image: -moz-linear-gradient(bottom, rgb(50,50,50) 0%, '''
+      '''rgb(100,100,100) 100%);
+    background-image: -ms-linear-gradient(bottom, rgb(50,50,50) 0%, '''
+      '''rgb(100,100,100) 100%);
+    background-image: linear-gradient(bottom, rgb(50,50,50) 0%, '''
+      '''rgb(100,100,100) 100%);
+
+    display: -webkit-box;
+    display: -moz-box;
+    display: -ms-box;
+    display: box;
+
+    -webkit-box-orient: horizontal;
+    -moz-box-orient: horizontal;
+    -ms-box-orient: horizontal;
+    box-orient: horizontal;
+
+    -webkit-box-align: center;
+    -moz-box-align: center;
+    -ms-box-align: center;
+    box-align: center;
+   }
+
+  .unittest-group-status
+  {
+    width: 20px;
+    height: 20px;
+    border-radius: 20px;
+    margin-left: 10px;
+  }
+
+  .unittest-group-status-pass{
+    background: Green;
+    background: '''
+      '''-webkit-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
+    background: '''
+      '''-moz-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
+    background: '''
+      '''-ms-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
+    background: '''
+      '''radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
+  }
+
+  .unittest-group-status-fail{
+    background: Red;
+    background: '''
+      '''-webkit-radial-gradient(center, ellipse cover, #FFAAAA 0%,Red 100%);
+    background: '''
+      '''-moz-radial-gradient(center, ellipse cover, #FFAAAA 0%,Red 100%);
+    background: '''
+      '''-ms-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%);
+    background: radial-gradient(center, ellipse cover, #FFAAAA 0%,Red 100%);
+  }
+
+  .unittest-overall{
+    font-size: 20px;
+  }
+
+  .unittest-summary{
+    font-size: 18px;
+  }
+
+  .unittest-pass{
+    color: Green;
+  }
+
+  .unittest-fail, .unittest-error
+  {
+    color: Red;
+  }
+
+  .unittest-row
+  {
+    display: -webkit-box;
+    display: -moz-box;
+    display: -ms-box;
+    display: box;
+    -webkit-box-orient: horizontal;
+    -moz-box-orient: horizontal;
+    -ms-box-orient: horizontal;
+    box-orient: horizontal;
+    width: 100%;
+  }
+
+  .unittest-row-hidden
+  {
+    display: none;
+  }
+
+  .unittest-row-odd
+  {
+    background: WhiteSmoke;
+  }
+
+  .unittest-row-even
+  {
+    background: #E5E5E5;
+  }
+
+  .unittest-row-id
+  {
+    width: 3em;
+  }
+
+  .unittest-row-status
+  {
+    width: 4em;
+  }
+
+  .unittest-row-description
+  {
+  }
+
+  ''';
+}
+
+void useHtmlEnhancedConfiguration([bool isLayoutTest = false]) {
+  unittestConfiguration = isLayoutTest ? _singletonLayout : _singletonNotLayout;
+}
+
+final _singletonLayout = new HtmlEnhancedConfiguration(true);
+final _singletonNotLayout = new HtmlEnhancedConfiguration(false);
diff --git a/unittest/lib/html_individual_config.dart b/unittest/lib/html_individual_config.dart
new file mode 100644
index 0000000..263adf9
--- /dev/null
+++ b/unittest/lib/html_individual_config.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2013, 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.
+
+/// A unit test library for running groups of tests in a browser, instead of the
+/// entire test file. This is especially used for large tests files that have
+/// many subtests, so we can mark groups as failing at a finer granularity than
+/// the entire test file.
+///
+/// To use, import this file, and call [useHtmlIndividualConfiguration] at the
+/// start of your set sequence. Important constraint: your group descriptions
+/// MUST NOT contain spaces.
+library unittest.html_individual_config;
+
+import 'dart:html';
+import 'unittest.dart' as unittest;
+import 'html_config.dart' as htmlconfig;
+
+class HtmlIndividualConfiguration extends htmlconfig.HtmlConfiguration {
+  HtmlIndividualConfiguration(bool isLayoutTest) : super(isLayoutTest);
+
+  void onStart() {
+    var search = window.location.search;
+    if (search != '') {
+      var groups = search
+          .substring(1)
+          .split('&')
+          .where((p) => p.startsWith('group='))
+          .toList();
+
+      if (!groups.isEmpty) {
+        if (groups.length > 1) {
+          throw new ArgumentError('More than one "group" parameter provided.');
+        }
+
+        var testGroupName = groups.single.split('=')[1];
+        var startsWith = "$testGroupName${unittest.groupSep}";
+        unittest.filterTests(
+            (unittest.TestCase tc) => tc.description.startsWith(startsWith));
+      }
+    }
+    super.onStart();
+  }
+}
+
+void useHtmlIndividualConfiguration([bool isLayoutTest = false]) {
+  unittest.unittestConfiguration =
+      isLayoutTest ? _singletonLayout : _singletonNotLayout;
+}
+
+final _singletonLayout = new HtmlIndividualConfiguration(true);
+final _singletonNotLayout = new HtmlIndividualConfiguration(false);
diff --git a/unittest/lib/src/configuration.dart b/unittest/lib/src/configuration.dart
new file mode 100644
index 0000000..1aef043
--- /dev/null
+++ b/unittest/lib/src/configuration.dart
@@ -0,0 +1,69 @@
+// Copyright (c) 2013, 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 unittest.configuration;
+
+import 'simple_configuration.dart';
+import 'test_case.dart';
+
+/// Describes the interface used by the unit test system for communicating the
+/// results of a test run.
+abstract class Configuration {
+  /// Creates an instance of [SimpleConfiguration].
+  factory Configuration() => new SimpleConfiguration();
+
+  /// Creates an [Configuration] instances that does nothing.
+  ///
+  /// For use by subclasses which wish to implement only a subset of features.
+  Configuration.blank();
+
+  /// If `true`, tests are started automatically once they're finished being defined.
+  ///
+  /// Otherwise, [runTests] must be called explicitly after tests are set up.
+  final autoStart = true;
+
+  /// How long a [TestCase] can run before it is considered an error.
+  /// A [timeout] value of [:null:] means that the limit is infinite.
+  Duration timeout = const Duration(minutes: 2);
+
+  /// Called as soon as the unittest framework becomes initialized.
+  ///
+  /// This is done even before tests are added to the test framework. It might
+  /// be used to determine/debug errors that occur before the test harness
+  /// starts executing. It is also used to tell the vm or browser that tests are
+  /// going to be run asynchronously and that the process should wait until they
+  /// are done.
+  void onInit() {}
+
+  /// Called as soon as the unittest framework starts running.
+  void onStart() {}
+
+  /// Called when each test starts. Useful to show intermediate progress on
+  /// a test suite.
+  void onTestStart(TestCase testCase) {}
+
+  /// Called when each test is first completed. Useful to show intermediate
+  /// progress on a test suite.
+  void onTestResult(TestCase testCase) {}
+
+  /// Called when an already completed test changes state. For example: a test
+  /// that was marked as passing may later be marked as being in error because
+  /// it still had callbacks being invoked.
+  void onTestResultChanged(TestCase testCase) {}
+
+  /// Handles the logging of messages by a test case.
+  void onLogMessage(TestCase testCase, String message) {}
+
+  /// Called when the unittest framework is done running. [success] indicates
+  /// whether all tests passed successfully.
+  void onDone(bool success) {}
+
+  /// Called with the result of all test cases. Browser tests commonly override
+  /// this to reformat the output.
+  ///
+  /// When [uncaughtError] is not null, it contains an error that occured outside
+  /// of tests (e.g. setting up the test).
+  void onSummary(int passed, int failed, int errors, List<TestCase> results,
+      String uncaughtError) {}
+}
diff --git a/unittest/lib/src/expected_function.dart b/unittest/lib/src/expected_function.dart
new file mode 100644
index 0000000..7373c75
--- /dev/null
+++ b/unittest/lib/src/expected_function.dart
@@ -0,0 +1,203 @@
+// Copyright (c) 2015, 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 unittest.expected_function;
+
+import '../unittest.dart';
+
+import 'internal_test_case.dart';
+
+/// An object used to detect unpassed arguments.
+const _PLACEHOLDER = const Object();
+
+// Functions used to check how many arguments a callback takes.
+typedef _Func0();
+typedef _Func1(a);
+typedef _Func2(a, b);
+typedef _Func3(a, b, c);
+typedef _Func4(a, b, c, d);
+typedef _Func5(a, b, c, d, e);
+typedef _Func6(a, b, c, d, e, f);
+
+typedef bool _IsDoneCallback();
+
+/// A wrapper for a function that ensures that it's called the appropriate
+/// number of times.
+///
+/// The containing test won't be considered to have completed successfully until
+/// this function has been called the appropriate number of times.
+///
+/// The wrapper function is accessible via [func]. It supports up to six
+/// optional and/or required positional arguments, but no named arguments.
+class ExpectedFunction {
+  /// The wrapped callback.
+  final Function _callback;
+
+  /// The minimum number of calls that are expected to be made to the function.
+  ///
+  /// If fewer calls than this are made, the test will fail.
+  final int _minExpectedCalls;
+
+  /// The maximum number of calls that are expected to be made to the function.
+  ///
+  /// If more calls than this are made, the test will fail.
+  final int _maxExpectedCalls;
+
+  /// A callback that should return whether the function is not expected to have
+  /// any more calls.
+  ///
+  /// This will be called after every time the function is run. The test case
+  /// won't be allowed to terminate until it returns `true`.
+  ///
+  /// This may be `null`. If so, the function is considered to be done after
+  /// it's been run once.
+  final _IsDoneCallback _isDone;
+
+  /// A descriptive name for the function.
+  final String _id;
+
+  /// An optional description of why the function is expected to be called.
+  ///
+  /// If not passed, this will be an empty string.
+  final String _reason;
+
+  /// The number of times the function has been called.
+  int _actualCalls = 0;
+
+  /// The test case in which this function was wrapped.
+  final InternalTestCase _testCase;
+
+  /// Whether this function has been called the requisite number of times.
+  bool _complete;
+
+  /// Wraps [callback] in a function that asserts that it's called at least
+  /// [minExpected] times and no more than [maxExpected] times.
+  ///
+  /// If passed, [id] is used as a descriptive name fo the function and [reason]
+  /// as a reason it's expected to be called. If [isDone] is passed, the test
+  /// won't be allowed to complete until it returns `true`.
+  ExpectedFunction(Function callback, int minExpected, int maxExpected,
+      {String id, String reason, bool isDone()})
+      : this._callback = callback,
+        _minExpectedCalls = minExpected,
+        _maxExpectedCalls = (maxExpected == 0 && minExpected > 0)
+            ? minExpected
+            : maxExpected,
+        this._isDone = isDone,
+        this._reason = reason == null ? '' : '\n$reason',
+        this._testCase = currentTestCase as InternalTestCase,
+        this._id = _makeCallbackId(id, callback) {
+    ensureInitialized();
+    if (_testCase == null) {
+      throw new StateError("No valid test. Did you forget to run your test "
+          "inside a call to test()?");
+    }
+
+    if (isDone != null || minExpected > 0) {
+      _testCase.callbackFunctionsOutstanding++;
+      _complete = false;
+    } else {
+      _complete = true;
+    }
+  }
+
+  /// Tries to find a reasonable name for [callback].
+  ///
+  /// If [id] is passed, uses that. Otherwise, tries to determine a name from
+  /// calling `toString`. If no name can be found, returns the empty string.
+  static String _makeCallbackId(String id, Function callback) {
+    if (id != null) return "$id ";
+
+    // If the callback is not an anonymous closure, try to get the
+    // name.
+    var toString = callback.toString();
+    var prefix = "Function '";
+    var start = toString.indexOf(prefix);
+    if (start == -1) return '';
+
+    start += prefix.length;
+    var end = toString.indexOf("'", start);
+    if (end == -1) return '';
+    return "${toString.substring(start, end)} ";
+  }
+
+  /// Returns a function that has the same number of positional arguments as the
+  /// wrapped function (up to a total of 6).
+  Function get func {
+    if (_callback is _Func6) return _max6;
+    if (_callback is _Func5) return _max5;
+    if (_callback is _Func4) return _max4;
+    if (_callback is _Func3) return _max3;
+    if (_callback is _Func2) return _max2;
+    if (_callback is _Func1) return _max1;
+    if (_callback is _Func0) return _max0;
+
+    throw new ArgumentError(
+        'The wrapped function has more than 6 required arguments');
+  }
+
+  // This indirection is critical. It ensures the returned function has an
+  // argument count of zero.
+  _max0() => _max6();
+
+  _max1([a0 = _PLACEHOLDER]) => _max6(a0);
+
+  _max2([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER]) => _max6(a0, a1);
+
+  _max3([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER, a2 = _PLACEHOLDER]) =>
+      _max6(a0, a1, a2);
+
+  _max4([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER, a2 = _PLACEHOLDER,
+      a3 = _PLACEHOLDER]) => _max6(a0, a1, a2, a3);
+
+  _max5([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER, a2 = _PLACEHOLDER,
+      a3 = _PLACEHOLDER, a4 = _PLACEHOLDER]) => _max6(a0, a1, a2, a3, a4);
+
+  _max6([a0 = _PLACEHOLDER, a1 = _PLACEHOLDER, a2 = _PLACEHOLDER,
+      a3 = _PLACEHOLDER, a4 = _PLACEHOLDER, a5 = _PLACEHOLDER]) =>
+      _run([a0, a1, a2, a3, a4, a5].where((a) => a != _PLACEHOLDER));
+
+  /// Runs the wrapped function with [args] and returns its return value.
+  ///
+  /// This will pass any errors on to [_testCase] and return `null`.
+  _run(Iterable args) {
+    try {
+      _actualCalls++;
+      if (_testCase.isComplete) {
+        // Don't run the callback if the test is done. We don't throw here as
+        // this is not the current test, but we do mark the old test as having
+        // an error if it previously passed.
+        if (_testCase.result == PASS) {
+          _testCase.error(
+              'Callback ${_id}called ($_actualCalls) after test case '
+              '${_testCase.description} had already been marked as '
+              '${_testCase.result}.$_reason');
+        }
+        return null;
+      } else if (_maxExpectedCalls >= 0 && _actualCalls > _maxExpectedCalls) {
+        throw new TestFailure('Callback ${_id}called more times than expected '
+                              '($_maxExpectedCalls).$_reason');
+      }
+
+      return Function.apply(_callback, args.toList());
+    } catch (error, stackTrace) {
+      _testCase.registerException(error, stackTrace);
+      return null;
+    } finally {
+      _afterRun();
+    }
+  }
+
+  /// After each time the function is run, check to see if it's complete.
+  void _afterRun() {
+    if (_complete) return;
+    if (_minExpectedCalls > 0 && _actualCalls < _minExpectedCalls) return;
+    if (_isDone != null && !_isDone()) return;
+
+    // Mark this callback as complete and remove it from the test case's
+    // oustanding callback count; if that hits zero the test is done.
+    _complete = true;
+    _testCase.markCallbackComplete();
+  }
+}
diff --git a/unittest/lib/src/group_context.dart b/unittest/lib/src/group_context.dart
new file mode 100644
index 0000000..78f347b
--- /dev/null
+++ b/unittest/lib/src/group_context.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2015, 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 unittest.group_context;
+
+import 'dart:async';
+
+import '../unittest.dart';
+
+/// Setup and teardown functions for a group and its parents, the latter
+/// for chaining.
+class GroupContext {
+  /// The parent context, or `null`.
+  final GroupContext parent;
+
+  /// Whether this is the root context.
+  bool get isRoot => parent == null;
+
+  /// Description text of the current test group.
+  final String _name;
+
+  /// The set-up function called before each test in a group.
+  Function get testSetUp => _testSetUp;
+  Function _testSetUp;
+
+  set testSetUp(Function setUp) {
+    if (parent == null || parent.testSetUp == null) {
+      _testSetUp = setUp;
+      return;
+    }
+
+    _testSetUp = () {
+      var f = parent.testSetUp();
+      if (f is Future) {
+        return f.then((_) => setUp());
+      } else {
+        return setUp();
+      }
+    };
+  }
+
+  /// The tear-down function called after each test in a group.
+  Function get testTearDown => _testTearDown;
+  Function _testTearDown;
+
+  set testTearDown(Function tearDown) {
+    if (parent == null || parent.testTearDown == null) {
+      _testTearDown = tearDown;
+      return;
+    }
+
+    _testTearDown = () {
+      var f = tearDown();
+      if (f is Future) {
+        return f.then((_) => parent.testTearDown());
+      } else {
+        return parent.testTearDown();
+      }
+    };
+  }
+
+  /// Returns the fully-qualified name of this context.
+  String get fullName =>
+      (isRoot || parent.isRoot) ? _name : "${parent.fullName}$groupSep$_name";
+
+  GroupContext.root()
+      : parent = null,
+        _name = '';
+
+  GroupContext(this.parent, this._name) {
+    _testSetUp = parent.testSetUp;
+    _testTearDown = parent.testTearDown;
+  }
+}
diff --git a/unittest/lib/src/internal_test_case.dart b/unittest/lib/src/internal_test_case.dart
new file mode 100644
index 0000000..9763666
--- /dev/null
+++ b/unittest/lib/src/internal_test_case.dart
@@ -0,0 +1,227 @@
+// 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 unittest.internal_test_case;
+
+import 'dart:async';
+
+import '../unittest.dart';
+import 'test_environment.dart';
+import 'utils.dart';
+
+/// An implementation of [TestCase] that exposes internal properties for other
+/// unittest use.
+class InternalTestCase implements TestCase {
+  final int id;
+  final String description;
+
+  /// The setup function to call before the test, if any.
+  Function _setUp;
+
+  /// The teardown function to call after the test, if any.
+  Function _tearDown;
+
+  /// The body of the test case.
+  TestFunction _testFunction;
+
+  /// Remaining number of callback functions that must reach a 'done' state
+  /// before the test completes.
+  int callbackFunctionsOutstanding = 0;
+
+  /// The error or failure message for the tests.
+  ///
+  /// Initially an empty string.
+  String message = '';
+
+  /// The result of the test case.
+  ///
+  /// If the test case has is completed, this will be one of [PASS], [FAIL], or
+  /// [ERROR]. Otherwise, it will be `null`.
+  String result;
+
+  /// Returns whether this test case passed.
+  bool get passed => result == PASS;
+
+  /// The stack trace for the error that caused this test case to fail, or
+  /// `null` if it succeeded.
+  StackTrace stackTrace;
+
+  /// The name of the group within which this test is running.
+  final String currentGroup;
+
+  /// The time the test case started running.
+  ///
+  /// `null` if the test hasn't yet begun running.
+  DateTime get startTime => _startTime;
+  DateTime _startTime;
+
+  /// The amount of time the test case took.
+  ///
+  /// `null` if the test hasn't finished running.
+  Duration get runningTime => _runningTime;
+  Duration _runningTime;
+
+  /// Whether this test is enabled.
+  ///
+  /// Disabled tests won't be run.
+  bool enabled = true;
+
+  /// A completer that will complete when the test is finished.
+  ///
+  /// This is only non-`null` when outstanding callbacks exist.
+  Completer _testComplete;
+
+  /// Whether this test case has finished running.
+  bool get isComplete => !enabled || result != null;
+
+  InternalTestCase(this.id, this.description, this._testFunction)
+      : currentGroup = environment.currentContext.fullName,
+        _setUp = environment.currentContext.testSetUp,
+        _tearDown = environment.currentContext.testTearDown;
+
+  /// A function that returns another function to handle errors from [Future]s.
+  ///
+  /// [stage] is a string description of the stage of testing that failed.
+  Function _errorHandler(String stage) => (e, stack) {
+    if (stack == null && e is Error) {
+      stack = e.stackTrace;
+    }
+    if (result == null || result == PASS) {
+      if (e is TestFailure) {
+        fail("$e", stack);
+      } else {
+        error("$stage failed: Caught $e", stack);
+      }
+    }
+  };
+
+  /// Performs any associated [_setUp] function and runs the test.
+  ///
+  /// Returns a [Future] that can be used to schedule the next test. If the test
+  /// runs to completion synchronously, or is disabled, null is returned, to
+  /// tell unittest to schedule the next test immediately.
+  Future run() {
+    if (!enabled) return new Future.value();
+
+    result = stackTrace = null;
+    message = '';
+
+    // Avoid calling [new Future] to avoid issue 11911.
+    return new Future.value().then((_) {
+      if (_setUp != null) return _setUp();
+    }).catchError(_errorHandler('Setup')).then((_) {
+      // Skip the test if setup failed.
+      if (result != null) return new Future.value();
+      config.onTestStart(this);
+      _startTime = new DateTime.now();
+      _runningTime = null;
+      callbackFunctionsOutstanding++;
+      var testReturn = _testFunction();
+      // If _testFunction() returned a future, we want to wait for it like we
+      // would a callback, so if a failure occurs while waiting, we can abort.
+      if (testReturn is Future) {
+        callbackFunctionsOutstanding++;
+        testReturn
+            .catchError(_errorHandler('Test'))
+            .whenComplete(markCallbackComplete);
+      }
+    }).catchError(_errorHandler('Test')).then((_) {
+      markCallbackComplete();
+      if (result == null) {
+        // Outstanding callbacks exist; we need to return a Future.
+        _testComplete = new Completer();
+        return _testComplete.future.whenComplete(() {
+          if (_tearDown != null) {
+            return _tearDown();
+          }
+        }).catchError(_errorHandler('Teardown'));
+      } else if (_tearDown != null) {
+        return _tearDown();
+      }
+    }).catchError(_errorHandler('Teardown')).whenComplete(() {
+      _setUp = null;
+      _tearDown = null;
+      _testFunction = null;
+    });
+  }
+
+  /// Marks the test as having completed with [testResult], which should be one
+  /// of [PASS], [FAIL], or [ERROR].
+  void _complete(String testResult,
+      [String messageText = '', StackTrace stack]) {
+    if (runningTime == null) {
+      // The startTime can be `null` if an error happened during setup. In this
+      // case we simply report a running time of 0.
+      if (startTime != null) {
+        _runningTime = new DateTime.now().difference(startTime);
+      } else {
+        _runningTime = const Duration(seconds: 0);
+      }
+    }
+    _setResult(testResult, messageText, stack);
+    if (_testComplete != null) {
+      var t = _testComplete;
+      _testComplete = null;
+      t.complete(this);
+    }
+  }
+
+  // Sets [this]'s fields to reflect the test result, and notifies the current
+  // configuration that the test has completed.
+  //
+  // Returns true if this is the first time the result has been set.
+  void _setResult(String testResult, String messageText, StackTrace stack) {
+    message = messageText;
+    stackTrace = getTrace(stack, formatStacks, filterStacks);
+    if (stackTrace == null) stackTrace = stack;
+    if (result == null) {
+      result = testResult;
+      config.onTestResult(this);
+    } else {
+      result = testResult;
+      config.onTestResultChanged(this);
+    }
+  }
+
+  /// Marks the test as having passed.
+  void pass() {
+    _complete(PASS);
+  }
+
+  void registerException(error, [StackTrace stackTrace]) {
+    var message = error is TestFailure ? error.message : 'Caught $error';
+    if (result == null) {
+      fail(message, stackTrace);
+    } else {
+      this.error(message, stackTrace);
+    }
+  }
+
+  /// Marks the test as having failed.
+  void fail(String messageText, [StackTrace stack]) {
+    if (result != null) {
+      var newMessage = result == PASS
+          ? 'Test failed after initially passing: $messageText'
+          : 'Test failed more than once: $messageText';
+      // TODO(gram): Should we combine the stack with the old one?
+      _complete(ERROR, newMessage, stack);
+    } else {
+      _complete(FAIL, messageText, stack);
+    }
+  }
+
+  /// Marks the test as having had an unexpected error.
+  void error(String messageText, [StackTrace stack]) {
+    _complete(ERROR, messageText, stack);
+  }
+
+  /// Indicates that an asynchronous callback has completed, and marks the test
+  /// as passing if all outstanding callbacks are complete.
+  void markCallbackComplete() {
+    callbackFunctionsOutstanding--;
+    if (callbackFunctionsOutstanding == 0 && !isComplete) pass();
+  }
+
+  String toString() => result != null ? "$description: $result" : description;
+}
diff --git a/unittest/lib/src/simple_configuration.dart b/unittest/lib/src/simple_configuration.dart
new file mode 100644
index 0000000..6157663
--- /dev/null
+++ b/unittest/lib/src/simple_configuration.dart
@@ -0,0 +1,207 @@
+// Copyright (c) 2013, 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 unittest.simple_configuration;
+
+import 'dart:isolate';
+
+import 'package:matcher/matcher.dart'
+    show DefaultFailureHandler, configureExpectFailureHandler, TestFailure;
+
+import '../unittest.dart';
+import 'internal_test_case.dart';
+import 'utils.dart';
+
+// A custom failure handler for [expect] that routes failures to the
+// [SimpleConfiguration].
+class _ExpectFailureHandler extends DefaultFailureHandler {
+  final SimpleConfiguration _config;
+
+  _ExpectFailureHandler(this._config);
+
+  void fail(String reason) {
+    _config.onExpectFailure(reason);
+  }
+}
+
+/// A configuration that provides hooks to configure the unittest library for
+/// different platforms.
+///
+/// This class implements the [Configuration] API in a platform-independent way.
+/// Tests that want to take advantage of the platform can create a subclass and
+/// override methods from this class.
+class SimpleConfiguration extends Configuration {
+  /// A port that keeps the VM alive while we wait for asynchronous tests to
+  /// finish.
+  ///
+  /// The VM won't shut down as long as there's an open receive port.
+  ReceivePort _receivePort;
+
+  /// The name of the configuration.
+  ///
+  /// Subclasses can override this with something useful for diagnostics. It's
+  /// particularly useful for parent/child configurations such as layout tests.
+  final name = 'Configuration';
+
+  /// If true (the default), throw an exception once all tests have run if any failed.
+  bool throwOnTestFailures = true;
+
+  /// If true (the default), then tests will stop after the first failed
+  /// [expect].
+  ///
+  /// If false, failed [expect]s will not cause the test to stop. Other
+  /// exceptions will still terminate the test.
+  bool stopTestOnExpectFailure = true;
+
+  // If [stopTestOnExpectFailure] is false, the list of failed [expect]s.
+  final _testLogBuffer = <Pair<String, StackTrace>>[];
+
+  /// The constructor sets up a failure handler for [expect] that redirects
+  /// [expect] failures to [onExpectFailure].
+  SimpleConfiguration() : super.blank() {
+    configureExpectFailureHandler(new _ExpectFailureHandler(this));
+  }
+
+  void onInit() {
+    // For Dart internal tests, we don't want stack frame filtering.
+    // We turn it off here in the default config, but by default turn
+    // it back on in the vm and html configs.
+    filterStacks = false;
+    _receivePort = new ReceivePort();
+    _postMessage('unittest-suite-wait-for-done');
+  }
+
+  /// Called when a test starts.
+  ///
+  /// Derived classes should call this first before their own override code.
+  void onTestStart(TestCase testCase) => _testLogBuffer.clear();
+
+  /// Called when a test completes.
+  ///
+  /// Derived classes should call this first before their own override code.
+  void onTestResult(TestCase externalTestCase) {
+    if (stopTestOnExpectFailure || _testLogBuffer.isEmpty) return;
+
+    var testCase = externalTestCase as InternalTestCase;
+
+    // Write the message/stack pairs up to the last pairs.
+    var reason = new StringBuffer();
+    for (var reasonAndTrace in _testLogBuffer.take(_testLogBuffer.length - 1)) {
+      reason.write(reasonAndTrace.first);
+      reason.write('\n');
+      reason.write(reasonAndTrace.last);
+      reason.write('\n');
+    }
+
+    var lastReasonAndTrace = _testLogBuffer.last;
+    // Write the last message.
+    reason.write(lastReasonAndTrace.first);
+    if (testCase.result == PASS) {
+      testCase.result = FAIL;
+      testCase.message = reason.toString();
+      // Use the last stack as the overall failure stack.
+      testCase.stackTrace = lastReasonAndTrace.last;
+    } else {
+      // Add the last stack to the message; we have a further stack
+      // caused by some other failure.
+      reason.write(lastReasonAndTrace.last);
+      reason.write('\n');
+      // Add the existing reason to the end of the expect log to
+      // create the final message.
+      testCase.message = '${reason.toString()}\n${testCase.message}';
+    }
+  }
+
+  /// Handles the logging of messages by a test case.
+  ///
+  /// The default in this base configuration is to call [print].
+  void onLogMessage(TestCase testCase, String message) {
+    print(message);
+  }
+
+  /// Handles failures from [expect].
+  ///
+  /// If [stopTestOnExpectFailure] is true, this throws a [TestFailure].
+  /// Otherwise, this stores the error.
+  void onExpectFailure(String reason) {
+    if (stopTestOnExpectFailure) throw new TestFailure(reason);
+
+    try {
+      throw '';
+    } catch (_, stack) {
+      var trace = getTrace(stack, formatStacks, filterStacks);
+      if (trace == null) trace = stack;
+      _testLogBuffer.add(new Pair<String, StackTrace>(reason, trace));
+    }
+  }
+
+  /// Returns a formatted string description of a test result.
+  String formatResult(TestCase testCase) {
+    var result = new StringBuffer();
+    result.write(testCase.result.toUpperCase());
+    result.write(": ");
+    result.write(testCase.description);
+    result.write("\n");
+
+    if (testCase.message != '') {
+      result.write(indent(testCase.message));
+      result.write("\n");
+    }
+
+    if (testCase.stackTrace != null) {
+      result.write(indent(testCase.stackTrace.toString()));
+      result.write("\n");
+    }
+    return result.toString();
+  }
+
+  /// Called with the result of all test cases.
+  ///
+  /// The default implementation prints the result summary using [print],
+  /// formatted with [formatResult]. Browser tests commonly override this to
+  /// reformat the output.
+  ///
+  /// When [uncaughtError] is not null, it contains an error that occured
+  /// outside of tests (e.g. setting up the test).
+  void onSummary(int passed, int failed, int errors, List<TestCase> results,
+      String uncaughtError) {
+    // Print each test's result.
+    for (var test in results) {
+      print(formatResult(test).trim());
+    }
+
+    // Show the summary.
+    print('');
+
+    if (passed == 0 && failed == 0 && errors == 0 && uncaughtError == null) {
+      print('No tests found.');
+      // This is considered a failure too.
+    } else if (failed == 0 && errors == 0 && uncaughtError == null) {
+      print('All $passed tests passed.');
+    } else {
+      if (uncaughtError != null) {
+        print('Top-level uncaught error: $uncaughtError');
+      }
+      print('$passed PASSED, $failed FAILED, $errors ERRORS');
+    }
+  }
+
+  void onDone(bool success) {
+    if (success) {
+      _postMessage('unittest-suite-success');
+      _receivePort.close();
+    } else {
+      _receivePort.close();
+      if (throwOnTestFailures) {
+        throw new Exception('Some tests failed.');
+      }
+    }
+  }
+
+  void _postMessage(String message) {
+    // In dart2js browser tests, the JavaScript-based test controller
+    // intercepts calls to print and listens for "secret" messages.
+    print(message);
+  }
+}
diff --git a/unittest/lib/src/test_case.dart b/unittest/lib/src/test_case.dart
new file mode 100644
index 0000000..37596ff
--- /dev/null
+++ b/unittest/lib/src/test_case.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2013, 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 unittest.test_case;
+
+import '../unittest.dart';
+
+/// An individual unit test.
+abstract class TestCase {
+  /// A unique numeric identifier for this test case.
+  int get id;
+
+  /// A description of what the test is specifying.
+  String get description;
+
+  /// The error or failure message for the tests.
+  ///
+  /// Initially an empty string.
+  String get message;
+
+  /// The result of the test case.
+  ///
+  /// If the test case has is completed, this will be one of [PASS], [FAIL], or
+  /// [ERROR]. Otherwise, it will be `null`.
+  String get result;
+
+  /// Returns whether this test case passed.
+  bool get passed;
+
+  /// The stack trace for the error that caused this test case to fail, or
+  /// `null` if it succeeded.
+  StackTrace get stackTrace;
+
+  /// The name of the group within which this test is running.
+  String get currentGroup;
+
+  /// The time the test case started running.
+  ///
+  /// `null` if the test hasn't yet begun running.
+  DateTime get startTime;
+
+  /// The amount of time the test case took.
+  ///
+  /// `null` if the test hasn't finished running.
+  Duration get runningTime;
+
+  /// Whether this test is enabled.
+  ///
+  /// Disabled tests won't be run.
+  bool get enabled;
+
+  /// Whether this test case has finished running.
+  bool get isComplete => !enabled || result != null;
+}
diff --git a/unittest/lib/src/test_environment.dart b/unittest/lib/src/test_environment.dart
new file mode 100644
index 0000000..6c205a9
--- /dev/null
+++ b/unittest/lib/src/test_environment.dart
@@ -0,0 +1,74 @@
+// 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 unittest.test_environment;
+
+import 'dart:async';
+
+import 'configuration.dart';
+import 'group_context.dart';
+import 'internal_test_case.dart';
+
+/// The default unittest environment.
+final _defaultEnvironment = new TestEnvironment();
+
+/// The current unittest environment.
+TestEnvironment get environment {
+  var environment = Zone.current[#unittest.environment];
+  return environment == null ? _defaultEnvironment : environment;
+}
+
+// The current environment's configuration.
+Configuration get config => environment.config;
+
+/// Encapsulates the state of the test environment.
+///
+/// This is used by the [withTestEnvironment] method to support multiple
+/// invocations of the unittest library within the same application
+/// instance.
+class TestEnvironment {
+  /// The environment's configuration.
+  Configuration config;
+
+  /// The top-level group context.
+  ///
+  /// We use a 'dummy' context for the top level to eliminate null checks when
+  /// querying the context. This allows us to easily support top-level
+  /// [setUp]/[tearDown] functions as well.
+  final rootContext = new GroupContext.root();
+
+  /// The current group context.
+  GroupContext currentContext;
+
+  /// The [currentTestCaseIndex] represents the index of the currently running
+  /// test case.
+  ///
+  /// If this is -1 it implies the test system is not running.
+  /// It will be set to [number of test cases] as a short-lived state flagging
+  /// that the last test has completed.
+  int currentTestCaseIndex = -1;
+
+  /// The [initialized] variable specifies whether the framework
+  /// has been initialized.
+  bool initialized = false;
+
+  /// The time since we last gave asynchronous code a chance to be scheduled.
+  int lastBreath = new DateTime.now().millisecondsSinceEpoch;
+
+  /// The number of [solo_group]s deep we are currently.
+  int soloNestingLevel = 0;
+
+  /// Whether we've seen a [solo_test].
+  bool soloTestSeen = false;
+
+  /// The list of test cases to run.
+  final testCases = new List<InternalTestCase>();
+
+  /// The error message that is printed in the test summary.
+  String uncaughtErrorMessage;
+
+  TestEnvironment() {
+    currentContext = rootContext;
+  }
+}
diff --git a/unittest/lib/src/utils.dart b/unittest/lib/src/utils.dart
new file mode 100644
index 0000000..ea1e92e
--- /dev/null
+++ b/unittest/lib/src/utils.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2013, 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 unittest.utils;
+
+import 'package:stack_trace/stack_trace.dart';
+
+/// Indent each line in [str] by two spaces.
+String indent(String str) =>
+    str.replaceAll(new RegExp("^", multiLine: true), "  ");
+
+/// A pair of values.
+class Pair<E, F> {
+  final E first;
+  final F last;
+
+  Pair(this.first, this.last);
+
+  String toString() => '($first, $last)';
+
+  bool operator ==(other) {
+    if (other is! Pair) return false;
+    return other.first == first && other.last == last;
+  }
+
+  int get hashCode => first.hashCode ^ last.hashCode;
+}
+
+/// Returns a Trace object from a StackTrace object or a String, or the
+/// unchanged input if formatStacks is false;
+Trace getTrace(stack, bool formatStacks, bool filterStacks) {
+  Trace trace;
+  if (stack == null || !formatStacks) return null;
+  if (stack is String) {
+    trace = new Trace.parse(stack);
+  } else if (stack is StackTrace) {
+    trace = new Trace.from(stack);
+  } else {
+    throw new Exception('Invalid stack type ${stack.runtimeType} for $stack.');
+  }
+
+  if (!filterStacks) return trace;
+
+  // Format the stack trace by removing everything above TestCase._runTest,
+  // which is usually going to be irrelevant. Also fold together unittest and
+  // core library calls so only the function the user called is visible.
+  return new Trace(trace.frames.takeWhile((frame) {
+    return frame.package != 'unittest' || frame.member != 'TestCase._runTest';
+  })).terse.foldFrames((frame) => frame.package == 'unittest' || frame.isCore);
+}
diff --git a/unittest/lib/test_controller.js b/unittest/lib/test_controller.js
new file mode 100644
index 0000000..40693af
--- /dev/null
+++ b/unittest/lib/test_controller.js
@@ -0,0 +1,237 @@
+// Copyright (c) 2011, 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.
+
+/**
+ * Test controller logic - used by unit test harness to embed tests in
+ * conent shell.
+ */
+
+// Clear the console before every test run - this is Firebug specific code.
+if (typeof console == "object" && typeof console.clear == "function") {
+  console.clear();
+}
+
+// Some tests may expect and have no way to suppress global errors.
+var testExpectsGlobalError = false;
+var testSuppressedGlobalErrors = [];
+
+// Set window onerror to make sure that we catch test harness errors across all
+// browsers.
+window.onerror = function (message, url, lineNumber) {
+  if (testExpectsGlobalError) {
+    testSuppressedGlobalErrors.push({
+      message: message
+    });
+    return;
+  }
+  if (url) {
+    showErrorAndExit(
+        "\n\n" + url + ":" + lineNumber + ":\n" + message + "\n\n");
+  } else {
+    showErrorAndExit(message);
+  }
+  window.postMessage('unittest-suite-external-error', '*');
+};
+
+// Start Dartium/content_shell, unless we are waiting for HTML Imports to load.
+// HTML Imports allows a document to link to other HTMLs documents via
+// <link rel=import>. It also allows for those other documents to contain
+// <script> tags, which must be run before scripts on the main page.
+// We have package:web_components to polyfill this feature, and it will handle
+// starting Dartium/content_shell in that case. HTML Imports is used by Polymer,
+// but it could be used by itself too. See the specification:
+// https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/imports/index.html
+if (navigator.webkitStartDart && !window.HTMLImports) {
+  navigator.webkitStartDart();
+}
+
+// testRunner is provided by content shell.
+// It is not available in browser tests.
+var testRunner = window.testRunner || window.layoutTestController;
+
+var waitForDone = false;
+
+// Returns the driving window object if available
+function getDriverWindow() {
+  if (window != window.parent) {
+    // We're running in an iframe.
+    return window.parent;
+  } else if (window.opener) {
+    // We were opened by another window.
+    return window.opener;
+  }
+  return null;
+}
+
+function notifyStart() {
+  var driver = getDriverWindow();
+  if (driver) {
+    driver.postMessage("STARTING", "*");
+  }
+}
+// We call notifyStart here to notify the encapsulating browser.
+notifyStart();
+
+function notifyDone() {
+  if (testRunner) testRunner.notifyDone();
+
+  // TODO(ricow): REMOVE, debug info, see issue 13292
+  if (!testRunner) {
+    printMessage('Calling notifyDone()');
+  }
+  // To support in browser launching of tests we post back start and result
+  // messages to the window.opener.
+  var driver = getDriverWindow();
+  if (driver) {
+    driver.postMessage(window.document.body.innerHTML, "*");
+  }
+}
+
+function processMessage(msg) {
+  if (typeof msg != 'string') return;
+  // TODO(ricow): REMOVE, debug info, see issue 13292
+  if (!testRunner) {
+    // Filter out ShadowDOM polyfill messages which are random floats.
+    if (msg != parseFloat(msg)) {
+      printMessage('processMessage(): ' + msg);
+    }
+  }
+  if (msg == 'unittest-suite-done') {
+    notifyDone();
+  } else if (msg == 'unittest-suite-wait-for-done') {
+    waitForDone = true;
+    if (testRunner) {
+      testRunner.startedDartTest = true;
+    }
+  } else if (msg == 'dart-calling-main') {
+    if (testRunner) {
+      testRunner.startedDartTest = true;
+    }
+  } else if (msg == 'dart-main-done') {
+    if (!waitForDone) {
+      printMessage('PASS');
+      notifyDone();
+    }
+  } else if (msg == 'unittest-suite-success') {
+    printMessage('PASS');
+    notifyDone();
+  } else if (msg == 'unittest-suite-fail') {
+    showErrorAndExit('Some tests failed.');
+  }
+}
+
+function onReceive(e) {
+  processMessage(e.data);
+}
+
+if (testRunner) {
+  testRunner.dumpAsText();
+  testRunner.waitUntilDone();
+}
+window.addEventListener("message", onReceive, false);
+
+function showErrorAndExit(message) {
+  if (message) {
+    printMessage('Error: ' + String(message));
+  }
+  // dart/tools/testing/test_runner.dart is looking for either PASS or
+  // FAIL in a browser test's output.
+  printMessage('FAIL');
+  notifyDone();
+}
+
+function onLoad(e) {
+  // needed for dartium compilation errors.
+  if (window.compilationError) {
+    showErrorAndExit(window.compilationError);
+  }
+}
+
+window.addEventListener("DOMContentLoaded", onLoad, false);
+
+// Note: before renaming this function, note that it is also included in an
+// inlined error handler in the HTML files that wrap DRT tests.
+// See: tools/testing/dart/browser_test.dart
+function externalError(e) {
+  // needed for dartium compilation errors.
+  showErrorAndExit(e && e.message);
+  window.postMessage('unittest-suite-external-error', '*');
+}
+
+document.addEventListener('readystatechange', function () {
+  // Most browsers set readyState to "loaded", but some (such as Chrome content
+  // shell) set it to "complete" instead.
+  if (document.readyState != "loaded" && document.readyState != "complete") {
+    return;
+  }
+  // If 'startedDartTest' is not set, that means that the test did not have
+  // a chance to load. This will happen when a load error occurs in the VM.
+  // Give the machine time to start up.
+  setTimeout(function() {
+    // A window.postMessage might have been enqueued after this timeout.
+    // Just sleep another time to give the browser the time to process the
+    // posted message.
+    setTimeout(function() {
+      if (testRunner && !testRunner.startedDartTest) {
+        notifyDone();
+      }
+    }, 0);
+  }, 50);
+});
+
+// dart2js will generate code to call this function to handle the Dart
+// [print] method.
+//
+// dartium will invoke this method for [print] calls if the environment variable
+// "DART_FORWARDING_PRINT" was set when launching dartium.
+//
+// Our tests will be wrapped, so we can detect when [main] is called and when
+// it has ended.
+// The wrapping happens either via "dartMainRunner" (for dart2js) or wrapped
+// tests for dartium.
+//
+// The following messages are handled specially:
+//   dart-calling-main:  signals that the dart [main] function will be invoked
+//   dart-main-done:  signals that the dart [main] function has finished
+//   unittest-suite-wait-for-done:  signals the start of an asynchronous test
+//   unittest-suite-success:  signals the end of an asynchrounous test
+//
+// These messages are used to communicate with the test and will be posted so
+// [processMessage] above can see it.
+function dartPrint(msg) {
+  if ((msg === 'unittest-suite-success')
+      || (msg === 'unittest-suite-done')
+      || (msg === 'unittest-suite-wait-for-done')
+      || (msg === 'dart-calling-main')
+      || (msg === 'dart-main-done')) {
+    window.postMessage(msg, '*');
+    return;
+  }
+  printMessage(msg);
+}
+
+// Prints 'msg' to the console (if available) and to the body of the html
+// document.
+function printMessage(msg) {
+  if (typeof console === 'object') console.warn(msg);
+  var pre = document.createElement('pre');
+  pre.appendChild(document.createTextNode(String(msg)));
+  document.body.appendChild(pre);
+  document.body.appendChild(document.createTextNode('\n'));
+}
+
+// dart2js will generate code to call this function instead of calling
+// Dart [main] directly. The argument is a closure that invokes main.
+function dartMainRunner(main) {
+  dartPrint('dart-calling-main');
+  try {
+    main();
+  } catch (e) {
+    dartPrint(e);
+    if (e.stack) dartPrint(e.stack);
+    window.postMessage('unittest-suite-fail', '*');
+    return;
+  }
+  dartPrint('dart-main-done');
+}
diff --git a/unittest/lib/unittest.dart b/unittest/lib/unittest.dart
new file mode 100644
index 0000000..a22b271
--- /dev/null
+++ b/unittest/lib/unittest.dart
@@ -0,0 +1,442 @@
+// Copyright (c) 2013, 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 unittest;
+
+import 'dart:async';
+import 'dart:collection';
+
+import 'package:matcher/matcher.dart' show TestFailure, wrapAsync;
+
+import 'src/configuration.dart';
+import 'src/expected_function.dart';
+import 'src/group_context.dart';
+import 'src/internal_test_case.dart';
+import 'src/test_case.dart';
+import 'src/test_environment.dart';
+
+export 'package:matcher/matcher.dart';
+
+export 'src/configuration.dart';
+export 'src/simple_configuration.dart';
+export 'src/test_case.dart';
+
+/// The signature for a function passed to [test].
+typedef dynamic TestFunction();
+
+/// [Configuration] used by the unittest library.
+///
+/// Note that if a configuration has not been set, calling this getter will
+/// create a default configuration.
+Configuration get unittestConfiguration {
+  if (config == null) environment.config = new Configuration();
+  return config;
+}
+
+/// If `true`, stack traces are reformatted to be more readable.
+bool formatStacks = true;
+
+/// If `true`, irrelevant frames are filtered from the stack trace.
+///
+/// This does nothing if [formatStacks] is false.
+bool filterStacks = true;
+
+/// Separator used between group names and test names.
+String groupSep = ' ';
+
+/// Sets the [Configuration] used by the unittest library.
+///
+/// Throws a [StateError] if there is an existing, incompatible value.
+void set unittestConfiguration(Configuration value) {
+  if (identical(config, value)) return;
+  if (config != null) {
+    logMessage('Warning: The unittestConfiguration has already been set. New '
+        'unittestConfiguration ignored.');
+  } else {
+    environment.config = value;
+  }
+}
+
+/// Logs [message] associated with the current test case.
+///
+/// Tests should use this instead of [print].
+void logMessage(String message) =>
+    config.onLogMessage(currentTestCase, message);
+
+/// The test cases that have been defined so far.
+List<TestCase> get testCases =>
+    new UnmodifiableListView<TestCase>(environment.testCases);
+
+/// The interval (in milliseconds) after which a non-microtask asynchronous
+/// delay will be scheduled between tests.
+///
+/// This is used to avoid starving the DOM or other non-microtask events.
+const int BREATH_INTERVAL = 200;
+
+/// The [TestCase] currently being executed.
+TestCase get currentTestCase => (environment.currentTestCaseIndex >= 0 &&
+        environment.currentTestCaseIndex < testCases.length)
+    ? testCases[environment.currentTestCaseIndex]
+    : null;
+
+/// The same as [currentTestCase], but typed as an [InternalTestCase].
+InternalTestCase get _currentTestCase => currentTestCase as InternalTestCase;
+
+/// The result string for a passing test case.
+const PASS = 'pass';
+
+/// The result string for a failing test case.
+const FAIL = 'fail';
+
+/// The result string for an test case with an error.
+const ERROR = 'error';
+
+/// Creates a new test case with the given description and body.
+///
+/// The description will be added to the descriptions of any surrounding
+/// [group]s.
+void test(String description, TestFunction body) {
+  _requireNotRunning();
+  ensureInitialized();
+
+  if (environment.soloTestSeen && environment.soloNestingLevel == 0) return;
+  var testCase = new InternalTestCase(
+      testCases.length + 1, _fullDescription(description), body);
+  environment.testCases.add(testCase);
+}
+
+/// Returns [description] with all of its group prefixes prepended.
+String _fullDescription(String description) {
+  var group = environment.currentContext.fullName;
+  if (description == null) return group;
+  return group != '' ? '$group$groupSep$description' : description;
+}
+
+/// A convenience function for skipping a test.
+void skip_test(String spec, TestFunction body) {}
+
+/// Creates a new test case with the given description and body.
+///
+/// If [solo_test] is used instead of [test], then all non-solo tests will be
+/// disabled. Note that if [solo_group] is used as well, all tests in the group
+/// will be enabled, regardless of whether they use [test] or [solo_test], or
+/// whether they are in a nested [group] versus [solo_group]. Put another way,
+/// if there are any calls to [solo_test] or [solo_group] in a test file, all
+/// tests that are not inside a [solo_group] will be disabled unless they are
+/// [solo_test]s.
+void solo_test(String spec, TestFunction body) {
+  _requireNotRunning();
+  ensureInitialized();
+  if (!environment.soloTestSeen) {
+    environment.soloTestSeen = true;
+    // This is the first solo-ed test. Discard all tests up to now.
+    environment.testCases.clear();
+  }
+  environment.soloNestingLevel++;
+  try {
+    test(spec, body);
+  } finally {
+    environment.soloNestingLevel--;
+  }
+}
+
+/// Indicate that [callback] is expected to be called [count] number of times
+/// (by default 1).
+///
+/// The unittest framework will wait for the callback to run the [count] times
+/// before it considers the current test to be complete. Using [expectAsync]
+/// will also ensure that errors that occur within [callback] are tracked and
+/// reported. [callback] may take up to six optional or required positional
+/// arguments; named arguments are not supported.
+///
+/// [max] can be used to specify an upper bound on the number of calls; if this
+/// is exceeded the test will fail. If [max] is `0` (the default), the callback
+/// is expected to be called exactly [count] times. If [max] is `-1`, the
+/// callback is allowed to be called any number of times greater than [count].
+///
+/// Both [id] and [reason] are optional and provide extra information about the
+/// callback when debugging. [id] should be the name of the callback, while
+/// [reason] should be the reason the callback is expected to be called.
+Function expectAsync(Function callback,
+    {int count: 1, int max: 0, String id, String reason}) =>
+        new ExpectedFunction(callback, count, max, id: id, reason: reason).func;
+
+/// Indicate that [callback] is expected to be called until [isDone] returns
+/// true.
+///
+/// [isDone] is called after each time the function is run. Only when it returns
+/// true will the callback be considered complete. Using [expectAsyncUntil] will
+/// also ensure that errors that occur within [callback] are tracked and
+/// reported. [callback] may take up to six optional or required positional
+/// arguments; named arguments are not supported.
+///
+/// Both [id] and [reason] are optional and provide extra information about the
+/// callback when debugging. [id] should be the name of the callback, while
+/// [reason] should be the reason the callback is expected to be called.
+Function expectAsyncUntil(Function callback, bool isDone(),
+    {String id, String reason}) => new ExpectedFunction(callback, 0, -1,
+        id: id, reason: reason, isDone: isDone).func;
+
+/// Creates a group of tests.
+///
+/// A group's description is included in the descriptions of any tests or
+/// sub-groups it contains. [setUp] and [tearDown] are also scoped to the
+/// containing group.
+void group(String description, void body()) {
+  ensureInitialized();
+  _requireNotRunning();
+  environment.currentContext =
+      new GroupContext(environment.currentContext, description);
+  try {
+    body();
+  } catch (e, trace) {
+    var stack = (trace == null) ? '' : ': ${trace.toString()}';
+    environment.uncaughtErrorMessage = "${e.toString()}$stack";
+  } finally {
+    // Now that the group is over, restore the previous one.
+    environment.currentContext = environment.currentContext.parent;
+  }
+}
+
+/// A convenience function for skipping a group of tests.
+void skip_group(String description, void body()) {}
+
+/// Creates a group of tests.
+///
+/// If [solo_group] is used instead of [group], then all tests not declared with
+/// [solo_test] or in a [solo_group] will be disabled. Note that all tests in a
+/// [solo_group] will be run, regardless of whether they're declared with [test]
+/// or [solo_test].
+///
+/// [skip_test] and [skip_group] take precedence over [solo_group].
+void solo_group(String description, void body()) {
+  _requireNotRunning();
+  ensureInitialized();
+  if (!environment.soloTestSeen) {
+    environment.soloTestSeen = true;
+    // This is the first solo-ed group. Discard all tests up to now.
+    environment.testCases.clear();
+  }
+  ++environment.soloNestingLevel;
+  try {
+    group(description, body);
+  } finally {
+    --environment.soloNestingLevel;
+  }
+}
+
+/// Registers a function to be run before tests.
+///
+/// This function will be called before each test is run. [callback] may be
+/// asynchronous; if so, it must return a [Future].
+///
+/// If this is called within a test group, it applies only to tests in that
+/// group. [callback] will be run after any set-up callbacks in parent groups or
+/// at the top level.
+void setUp(Function callback) {
+  _requireNotRunning();
+  environment.currentContext.testSetUp = callback;
+}
+
+/// Registers a function to be run after tests.
+///
+/// This function will be called after each test is run. [callback] may be
+/// asynchronous; if so, it must return a [Future].
+///
+/// If this is called within a test group, it applies only to tests in that
+/// group. [callback] will be run before any tear-down callbacks in parent groups or
+/// at the top level.
+void tearDown(Function callback) {
+  _requireNotRunning();
+  environment.currentContext.testTearDown = callback;
+}
+
+/// Advance to the next test case.
+void _nextTestCase() {
+  environment.currentTestCaseIndex++;
+  _runTest();
+}
+
+/// Handle an error that occurs outside of any test.
+void handleExternalError(e, String message, [stackTrace]) {
+  var msg = '$message\nCaught $e';
+
+  if (currentTestCase != null) {
+    _currentTestCase.error(msg, stackTrace);
+  } else {
+    environment.uncaughtErrorMessage = "$msg: $stackTrace";
+  }
+}
+
+/// Remove any tests that match [testFilter].
+///
+/// [testFilter] can be a predicate function, a [RegExp], or a [String]. If it's
+/// a function, it's called with each [TestCase]. If it's a [String], it's
+/// parsed as a [RegExp] and matched against each [TestCase.description].
+///
+/// This is different from enabling or disabling tests in that it removes the
+/// tests completely.
+void filterTests(testFilter) {
+  var filterFunction;
+  if (testFilter is String) {
+    var re = new RegExp(testFilter);
+    filterFunction = (t) => re.hasMatch(t.description);
+  } else if (testFilter is RegExp) {
+    filterFunction = (t) => testFilter.hasMatch(t.description);
+  } else if (testFilter is Function) {
+    filterFunction = testFilter;
+  }
+  environment.testCases.retainWhere(filterFunction);
+}
+
+/// Runs all queued tests, one at a time.
+void runTests() {
+  _requireNotRunning();
+  _ensureInitialized(false);
+  environment.currentTestCaseIndex = 0;
+  config.onStart();
+  _runTest();
+}
+
+/// Registers an exception that was caught for the current test.
+void registerException(error, [StackTrace stackTrace]) =>
+    _currentTestCase.registerException(error, stackTrace);
+
+/// Runs the next test.
+void _runTest() {
+  if (environment.currentTestCaseIndex >= testCases.length) {
+    assert(environment.currentTestCaseIndex == testCases.length);
+    _completeTests();
+    return;
+  }
+
+  var testCase = _currentTestCase;
+  var f = runZoned(testCase.run, onError: (error, stack) {
+    // TODO(kevmoo) Do a better job of flagging these are async errors.
+    // https://code.google.com/p/dart/issues/detail?id=16530
+    testCase.registerException(error, stack);
+  });
+
+  var timer;
+  var timeout = unittestConfiguration.timeout;
+  if (timeout != null) {
+    try {
+      timer = new Timer(timeout, () {
+        testCase.error("Test timed out after ${timeout.inSeconds} seconds.");
+        _nextTestCase();
+      });
+    } on UnsupportedError catch (e) {
+      if (e.message != "Timer greater than 0.") rethrow;
+      // Support running on d8 and jsshell which don't support timers.
+    }
+  }
+
+  f.whenComplete(() {
+    if (timer != null) timer.cancel();
+    var now = new DateTime.now().millisecondsSinceEpoch;
+    if (now - environment.lastBreath >= BREATH_INTERVAL) {
+      environment.lastBreath = now;
+      Timer.run(_nextTestCase);
+    } else {
+      scheduleMicrotask(_nextTestCase); // Schedule the next test.
+    }
+  });
+}
+
+/// Notify the configuration that the testing has finished.
+void _completeTests() {
+  if (!environment.initialized) return;
+
+  var passed = 0;
+  var failed = 0;
+  var errors = 0;
+  for (var testCase in testCases) {
+    switch (testCase.result) {
+      case PASS:
+        passed++;
+        break;
+      case FAIL:
+        failed++;
+        break;
+      case ERROR:
+        errors++;
+        break;
+    }
+  }
+
+  config.onSummary(
+      passed, failed, errors, testCases, environment.uncaughtErrorMessage);
+  config.onDone(passed > 0 &&
+      failed == 0 &&
+      errors == 0 &&
+      environment.uncaughtErrorMessage == null);
+  environment.initialized = false;
+  environment.currentTestCaseIndex = -1;
+}
+
+/// Initializes the test environment if it hasn't already been initialized.
+void ensureInitialized() {
+  _ensureInitialized(true);
+}
+
+/// Initializes the test environment.
+///
+/// If [configAutoStart] is `true`, schedule a microtask to run the tests. This
+/// microtask is expected to run after all the tests are defined.
+void _ensureInitialized(bool configAutoStart) {
+  if (environment.initialized) return;
+
+  environment.initialized = true;
+  // Hook our async guard into the matcher library.
+  wrapAsync = (f, [id]) => expectAsync(f, id: id);
+
+  environment.uncaughtErrorMessage = null;
+
+  unittestConfiguration.onInit();
+
+  // Immediately queue the suite up. It will run after a timeout (i.e. after
+  // main() has returned).
+  if (configAutoStart && config.autoStart) scheduleMicrotask(runTests);
+}
+
+/// Remove all tests other than the one identified by [id].
+void setSoloTest(int id) =>
+    environment.testCases.retainWhere((t) => t.id == id);
+
+/// Enable the test identified by [id].
+void enableTest(int id) => _setTestEnabledState(id, enable: true);
+
+/// Disable the test by [id].
+void disableTest(int id) => _setTestEnabledState(id, enable: false);
+
+/// Enable or disable the test identified by [id].
+void _setTestEnabledState(int id, {bool enable: true}) {
+  // Try fast path first.
+  if (testCases.length > id && testCases[id].id == id) {
+    environment.testCases[id].enabled = enable;
+  } else {
+    for (var i = 0; i < testCases.length; i++) {
+      if (testCases[i].id != id) continue;
+      environment.testCases[i].enabled = enable;
+      break;
+    }
+  }
+}
+
+/// Throws a [StateError] if tests are running.
+void _requireNotRunning() {
+  if (environment.currentTestCaseIndex == -1) return;
+  throw new StateError('Not allowed when tests are running.');
+}
+
+/// Creates a test environment running in its own zone scope.
+///
+/// This allows for multiple invocations of the unittest library in the same
+/// application instance. This is useful when, for example, creating a test
+/// runner application which needs to create a new pristine test environment on
+/// each invocation to run a given set of tests.
+withTestEnvironment(callback()) {
+  return runZoned(callback,
+      zoneValues: {#unittest.environment: new TestEnvironment()});
+}
diff --git a/unittest/lib/vm_config.dart b/unittest/lib/vm_config.dart
new file mode 100644
index 0000000..77e91e3
--- /dev/null
+++ b/unittest/lib/vm_config.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2013, 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.
+
+/// A simple unit test library for running tests on the VM.
+library unittest.vm_config;
+
+import 'dart:async';
+import 'dart:io';
+import 'unittest.dart';
+
+class VMConfiguration extends SimpleConfiguration {
+  // Color constants used for generating messages.
+  final String GREEN_COLOR = '\u001b[32m';
+  final String RED_COLOR = '\u001b[31m';
+  final String MAGENTA_COLOR = '\u001b[35m';
+  final String NO_COLOR = '\u001b[0m';
+
+  // We make this public so the user can turn it off if they want.
+  bool useColor;
+
+  VMConfiguration()
+      : super(),
+        useColor = stdioType(stdout) == StdioType.TERMINAL;
+
+  String formatResult(TestCase testCase) {
+    String result = super.formatResult(testCase);
+    if (useColor) {
+      if (testCase.result == PASS) {
+        return "${GREEN_COLOR}${result}${NO_COLOR}";
+      } else if (testCase.result == FAIL) {
+        return "${RED_COLOR}${result}${NO_COLOR}";
+      } else if (testCase.result == ERROR) {
+        return "${MAGENTA_COLOR}${result}${NO_COLOR}";
+      }
+    }
+    return result;
+  }
+
+  void onInit() {
+    super.onInit();
+    filterStacks = formatStacks = true;
+  }
+
+  void onDone(bool success) {
+    int status;
+    try {
+      super.onDone(success);
+      status = 0;
+    } catch (ex) {
+      // A non-zero exit code is used by the test infrastructure to detect
+      // failure.
+      status = 1;
+    }
+    Future.wait([stdout.close(), stderr.close()]).then((_) {
+      exit(status);
+    });
+  }
+}
+
+void useVMConfiguration() {
+  unittestConfiguration = _singleton;
+}
+
+final _singleton = new VMConfiguration();
diff --git a/unittest/pubspec.yaml b/unittest/pubspec.yaml
new file mode 100644
index 0000000..d347ad9
--- /dev/null
+++ b/unittest/pubspec.yaml
@@ -0,0 +1,16 @@
+name: unittest
+version: 0.11.5+4
+author: Dart Team <misc@dartlang.org>
+description: A library for writing dart unit tests.
+homepage: https://github.com/dart-lang/unittest
+environment:
+  sdk: '>=1.0.0 <2.0.0'
+dependencies:
+  stack_trace: '>=0.9.0 <2.0.0'
+
+  # Because unittest exports matcher, it needs to keep its version constraint
+  # tight to ensure that a constraint on unittest properly constraints all
+  # features it provides.
+  matcher: '>=0.11.4 <0.11.5'
+dev_dependencies:
+  metatest: '>=0.1.0 <0.2.0'
diff --git a/usage/lib/src/usage_impl.dart b/usage/lib/src/usage_impl.dart
new file mode 100644
index 0000000..fc2c8a7
--- /dev/null
+++ b/usage/lib/src/usage_impl.dart
@@ -0,0 +1,232 @@
+// 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 usage_impl;
+
+import 'dart:async';
+import 'dart:math' as math;
+
+import 'uuid.dart';
+import '../usage.dart';
+
+final int _MAX_EXCEPTION_LENGTH = 100;
+
+String postEncode(Map<String, dynamic> map) {
+  // &foo=bar
+  return map.keys.map((key) {
+    String value = '${map[key]}';
+    return "${key}=${Uri.encodeComponent(value)}";
+  }).join('&');
+}
+
+/**
+ * A throttling algorithim. This models the throttling after a bucket with
+ * water dripping into it at the rate of 1 drop per second. If the bucket has
+ * water when an operation is requested, 1 drop of water is removed and the
+ * operation is performed. If not the operation is skipped. This algorithim
+ * lets operations be peformed in bursts without throttling, but holds the
+ * overall average rate of operations to 1 per second.
+ */
+class ThrottlingBucket {
+  final int startingCount;
+  int drops;
+  int _lastReplenish;
+
+  ThrottlingBucket(this.startingCount) {
+    drops = startingCount;
+    _lastReplenish = new DateTime.now().millisecondsSinceEpoch;
+  }
+
+  bool removeDrop() {
+    _checkReplenish();
+
+    if (drops <= 0) {
+      return false;
+    } else {
+      drops--;
+      return true;
+    }
+  }
+
+  void _checkReplenish() {
+    int now = new DateTime.now().millisecondsSinceEpoch;
+
+    if (_lastReplenish + 1000 >= now) {
+      int inc = (now - _lastReplenish) ~/ 1000;
+      drops = math.min(drops + inc, startingCount);
+      _lastReplenish += (1000 * inc);
+    }
+  }
+}
+
+abstract class AnalyticsImpl extends Analytics {
+  static const String _GA_URL = 'https://www.google-analytics.com/collect';
+
+  /// Tracking ID / Property ID.
+  final String trackingId;
+
+  final PersistentProperties properties;
+  final PostHandler postHandler;
+
+  final ThrottlingBucket _bucket = new ThrottlingBucket(20);
+  final Map<String, dynamic> _variableMap = {};
+
+  final List<Future> _futures = [];
+
+  AnalyticsImpl(this.trackingId, this.properties, this.postHandler,
+      {String applicationName, String applicationVersion}) {
+    assert(trackingId != null);
+
+    if (applicationName != null) setSessionValue('an', applicationName);
+    if (applicationVersion != null) setSessionValue('av', applicationVersion);
+  }
+
+  bool get optIn => properties['optIn'] == true;
+
+  set optIn(bool value) {
+    properties['optIn'] = value;
+  }
+
+  bool get hasSetOptIn => properties['optIn'] != null;
+
+  Future sendScreenView(String viewName) {
+    Map args = {'cd': viewName};
+    return _sendPayload('screenview', args);
+  }
+
+  Future sendEvent(String category, String action, {String label, int value}) {
+    if (!optIn) return new Future.value();
+
+    Map args = {'ec': category, 'ea': action};
+    if (label != null) args['el'] = label;
+    if (value != null) args['ev'] = value;
+    return _sendPayload('event', args);
+  }
+
+  Future sendSocial(String network, String action, String target) {
+    if (!optIn) return new Future.value();
+
+    Map args = {'sn': network, 'sa': action, 'st': target};
+    return _sendPayload('social', args);
+  }
+
+  Future sendTiming(String variableName, int time, {String category,
+        String label}) {
+    if (!optIn) return new Future.value();
+
+    Map args = {'utv': variableName, 'utt': time};
+    if (label != null) args['utl'] = label;
+    if (category != null) args['utc'] = category;
+    return _sendPayload('timing', args);
+  }
+
+  AnalyticsTimer startTimer(String variableName, {String category, String label}) {
+    return new AnalyticsTimer(this,
+        variableName, category: category, label: label);
+  }
+
+  Future sendException(String description, {bool fatal}) {
+    if (!optIn) return new Future.value();
+
+    // In order to ensure that the client of this API is not sending any PII
+    // data, we strip out any stack trace that may reference a path on the
+    // user's drive (file:/...).
+    if (description.contains('file:/')) {
+      description = description.substring(0, description.indexOf('file:/'));
+    }
+
+    if (description != null && description.length > _MAX_EXCEPTION_LENGTH) {
+      description = description.substring(0, _MAX_EXCEPTION_LENGTH);
+    }
+
+    Map args = {'exd': description};
+    if (fatal != null && fatal) args['exf'] = '1';
+    return _sendPayload('exception', args);
+  }
+
+  void setSessionValue(String param, dynamic value) {
+    if (value == null) {
+      _variableMap.remove(param);
+    } else {
+      _variableMap[param] = value;
+    }
+  }
+
+  Future waitForLastPing({Duration timeout}) {
+    Future f = Future.wait(_futures).catchError((e) => null);
+
+    if (timeout != null) {
+      f = f.timeout(timeout, onTimeout: () => null);
+    }
+
+    return f;
+  }
+
+  /**
+   * Anonymous Client ID. The value of this field should be a random UUID v4.
+   */
+  String get _clientId => properties['clientId'];
+
+  void _initClientId() {
+    if (_clientId == null) {
+      properties['clientId'] = new Uuid().generateV4();
+    }
+  }
+
+  // Valid values for [hitType] are: 'pageview', 'screenview', 'event',
+  // 'transaction', 'item', 'social', 'exception', and 'timing'.
+  Future _sendPayload(String hitType, Map args) {
+    if (_bucket.removeDrop()) {
+      _initClientId();
+
+      _variableMap.forEach((key, value) {
+        args[key] = value;
+      });
+
+      args['v'] = '1'; // protocol version
+      args['tid'] = trackingId;
+      args['cid'] = _clientId;
+      args['t'] = hitType;
+
+      return _recordFuture(postHandler.sendPost(_GA_URL, args));
+    } else {
+      return new Future.value();
+    }
+  }
+
+  Future _recordFuture(Future f) {
+    _futures.add(f);
+    return f.whenComplete(() => _futures.remove(f));
+  }
+}
+
+/**
+ * A persistent key/value store. An [AnalyticsImpl] instance expects to have one
+ * of these injected into it. There are default implementations for `dart:io`
+ * and `dart:html` clients.
+ *
+ * The [name] paramater is used to uniquely store these properties on disk /
+ * persistent storage.
+ */
+abstract class PersistentProperties {
+  final String name;
+
+  PersistentProperties(this.name);
+
+  dynamic operator[](String key);
+  void operator[]=(String key, dynamic value);
+}
+
+/**
+ * A utility class to perform HTTP POSTs. An [AnalyticsImpl] instance expects to
+ * have one of these injected into it. There are default implementations for
+ * `dart:io` and `dart:html` clients.
+ *
+ * The POST information should be sent on a best-effort basis. The `Future` from
+ * [sendPost] should complete when the operation is finished, but failures to
+ * send the information should be silent.
+ */
+abstract class PostHandler {
+  Future sendPost(String url, Map<String, String> parameters);
+}
diff --git a/usage/lib/src/usage_impl_html.dart b/usage/lib/src/usage_impl_html.dart
new file mode 100644
index 0000000..fafd77a
--- /dev/null
+++ b/usage/lib/src/usage_impl_html.dart
@@ -0,0 +1,53 @@
+// 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 usage_impl_html;
+
+import 'dart:async';
+import 'dart:convert' show JSON;
+import 'dart:html';
+
+import 'usage_impl.dart';
+
+class HtmlPostHandler extends PostHandler {
+  final Function mockRequestor;
+
+  HtmlPostHandler({Function this.mockRequestor});
+
+  Future sendPost(String url, Map<String, String> parameters) {
+    int viewportWidth = document.documentElement.clientWidth;
+    int viewportHeight = document.documentElement.clientHeight;
+
+    parameters['vp'] = '${viewportWidth}x$viewportHeight';
+
+    String data = postEncode(parameters);
+    var request = mockRequestor == null ? HttpRequest.request : mockRequestor;
+    return request(url, method: 'POST', sendData: data).catchError((e) {
+      // Catch errors that can happen during a request, but that we can't do
+      // anything about, e.g. a missing internet conenction.
+    });
+  }
+}
+
+class HtmlPersistentProperties extends PersistentProperties {
+  Map _map;
+
+  HtmlPersistentProperties(String name) : super(name) {
+    String str = window.localStorage[name];
+    if (str == null || str.isEmpty) str = '{}';
+    _map = JSON.decode(str);
+  }
+
+  dynamic operator[](String key) => _map[key];
+
+  void operator[]=(String key, dynamic value) {
+    if (value == null) {
+      _map.remove(key);
+    } else {
+      _map[key] = value;
+    }
+
+    window.localStorage[name] = JSON.encode(_map);
+  }
+}
diff --git a/usage/lib/src/usage_impl_io.dart b/usage/lib/src/usage_impl_io.dart
new file mode 100644
index 0000000..66fe0cb
--- /dev/null
+++ b/usage/lib/src/usage_impl_io.dart
@@ -0,0 +1,87 @@
+// 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 usage_impl_io;
+
+import 'dart:async';
+import 'dart:convert' show JSON;
+import 'dart:io';
+
+import 'package:path/path.dart' as path;
+
+import 'usage_impl.dart';
+
+String _createUserAgent() {
+  // Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en)
+  // Dart/1.8.0-edge.41170 (macos; macos; macos; null)
+  String os = Platform.operatingSystem;
+  String locale = Platform.environment['LANG'];
+  return "Dart/${_dartVersion()} (${os}; ${os}; ${os}; ${locale})";
+}
+
+String _userHomeDir() {
+  String envKey = Platform.operatingSystem == 'windows' ? 'APPDATA' : 'HOME';
+  String value = Platform.environment[envKey];
+  return value == null ? '.' : value;
+}
+
+String _dartVersion() {
+  String ver = Platform.version;
+  int index = ver.indexOf(' ');
+  if (index != -1) ver = ver.substring(0, index);
+  return ver;
+}
+
+class IOPostHandler extends PostHandler {
+  final String _userAgent;
+  final HttpClient mockClient;
+
+  IOPostHandler({HttpClient this.mockClient}) : _userAgent = _createUserAgent();
+
+  Future sendPost(String url, Map<String, String> parameters) {
+    // Add custom parameters for OS and the Dart version.
+    parameters['cd1'] = Platform.operatingSystem;
+    parameters['cd2'] = 'dart ${_dartVersion()}';
+
+    String data = postEncode(parameters);
+
+    HttpClient client = mockClient != null ? mockClient : new HttpClient();
+    client.userAgent = _userAgent;
+    return client.postUrl(Uri.parse(url)).then((HttpClientRequest req) {
+      req.write(data);
+      return req.close();
+    }).then((HttpClientResponse response) {
+      response.drain();
+    }).catchError((e) {
+      // Catch errors that can happen during a request, but that we can't do
+      // anything about, e.g. a missing internet conenction.
+    });
+  }
+}
+
+class IOPersistentProperties extends PersistentProperties {
+  File _file;
+  Map _map;
+
+  IOPersistentProperties(String name) : super(name) {
+    String fileName = '.${name.replaceAll(' ', '_')}';
+    _file = new File(path.join(_userHomeDir(), fileName));
+    _file.createSync();
+    String contents = _file.readAsStringSync();
+    if (contents.isEmpty) contents = '{}';
+    _map = JSON.decode(contents);
+  }
+
+  dynamic operator[](String key) => _map[key];
+
+  void operator[]=(String key, dynamic value) {
+    if (value == null) {
+      _map.remove(key);
+    } else {
+      _map[key] = value;
+    }
+
+    _file.writeAsStringSync(JSON.encode(_map) + '\n');
+  }
+}
diff --git a/usage/lib/src/uuid.dart b/usage/lib/src/uuid.dart
new file mode 100644
index 0000000..66e99ac
--- /dev/null
+++ b/usage/lib/src/uuid.dart
@@ -0,0 +1,48 @@
+// 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.
+
+/**
+ * A UUID generator library.
+ */
+library usage.uuid;
+
+import 'dart:math' show Random;
+
+/**
+ * A UUID generator. This will generate unique IDs in the format:
+ *
+ *     f47ac10b-58cc-4372-a567-0e02b2c3d479
+ *
+ * The generated uuids are 128 bit numbers encoded in a specific string format.
+ *
+ * For more information, see
+ * http://en.wikipedia.org/wiki/Universally_unique_identifier.
+ */
+class Uuid {
+  Random _random = new Random();
+
+  /**
+   * Generate a version 4 (random) uuid. This is a uuid scheme that only uses
+   * random numbers as the source of the generated uuid.
+   */
+  String generateV4() {
+    // Generate xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx / 8-4-4-4-12.
+    int special = 8 + _random.nextInt(4);
+
+    return
+        '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}-'
+        '${_bitsDigits(16, 4)}-'
+        '4${_bitsDigits(12, 3)}-'
+        '${_printDigits(special,  1)}${_bitsDigits(12, 3)}-'
+        '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}';
+  }
+
+  String _bitsDigits(int bitCount, int digitCount) =>
+      _printDigits(_generateBits(bitCount), digitCount);
+
+  int _generateBits(int bitCount) => _random.nextInt(1 << bitCount);
+
+  String _printDigits(int value, int count) =>
+      value.toRadixString(16).padLeft(count, '0');
+}
diff --git a/usage/lib/usage.dart b/usage/lib/usage.dart
new file mode 100644
index 0000000..9a4279b
--- /dev/null
+++ b/usage/lib/usage.dart
@@ -0,0 +1,250 @@
+// 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.
+
+/**
+ * `usage` is a wrapper around Google Analytics for both command-line apps
+ * and web apps.
+ *
+ * In order to use this library as a web app, import the `analytics_html.dart`
+ * library and instantiate the [AnalyticsHtml] class.
+ *
+ * In order to use this library as a command-line app, import the
+ * `analytics_io.dart` library and instantiate the [AnalyticsIO] class.
+ *
+ * For both classes, you need to provide a Google Analytics tracking ID, the
+ * application name, and the application version.
+ *
+ * Your application should provide an opt-in option for the user. If they
+ * opt-in, set the [optIn] field to `true`. This setting will persist across
+ * sessions automatically.
+ *
+ * For more information, please see the Google Analytics Measurement Protocol
+ * [Policy](https://developers.google.com/analytics/devguides/collection/protocol/policy).
+ */
+library usage;
+
+import 'dart:async';
+
+// Matches file:/, non-ws, /, non-ws, .dart
+final RegExp _pathRegex = new RegExp(r'file:/\S+/(\S+\.dart)');
+
+/**
+ * An interface to a Google Analytics session. [AnalyticsHtml] and [AnalyticsIO]
+ * are concrete implementations of this interface. [AnalyticsMock] can be used
+ * for testing or for some varients of an opt-in workflow.
+ *
+ * The analytics information is sent on a best-effort basis. So, failures to
+ * send the GA information will not result in errors from the asynchronous
+ * `send` methods.
+ */
+abstract class Analytics {
+  /**
+   * Tracking ID / Property ID.
+   */
+  String get trackingId;
+
+  /**
+   * Whether the user has opt-ed in to additional analytics.
+   */
+  bool optIn;
+
+  /**
+   * Whether the [optIn] value has been explicitly set (either `true` or
+   * `false`).
+   */
+  bool get hasSetOptIn;
+
+  /**
+   * Sends a screen view hit to Google Analytics.
+   */
+  Future sendScreenView(String viewName);
+
+  /**
+   * Sends an Event hit to Google Analytics. [label] specifies the event label.
+   * [value] specifies the event value. Values must be non-negative.
+   */
+  Future sendEvent(String category, String action, {String label, int value});
+
+  /**
+   * Sends a Social hit to Google Analytics. [network] specifies the social
+   * network, for example Facebook or Google Plus. [action] specifies the social
+   * interaction action. For example on Google Plus when a user clicks the +1
+   * button, the social action is 'plus'. [target] specifies the target of a
+   * social interaction. This value is typically a URL but can be any text.
+   */
+  Future sendSocial(String network, String action, String target);
+
+  /**
+   * Sends a Timing hit to Google Analytics. [variableName] specifies the
+   * variable name of the timing. [time] specifies the user timing value (in
+   * milliseconds). [category] specifies the category of the timing. [label]
+   * specifies the label of the timing.
+   */
+  Future sendTiming(String variableName, int time, {String category,
+      String label});
+
+  /**
+   * Start a timer. The time won't be calculated, and the analytics information
+   * sent, until the [AnalyticsTimer.finish] method is called.
+   */
+  AnalyticsTimer startTimer(String variableName,
+      {String category, String label});
+
+  /**
+   * In order to avoid sending any personally identifying information, the
+   * [description] field must not contain the exception message. In addition,
+   * only the first 100 chars of the description will be sent.
+   */
+  Future sendException(String description, {bool fatal});
+
+  /**
+   * Sets a session variable value. The value is persistent for the life of the
+   * [Analytics] instance. This variable will be sent in with every analytics
+   * hit. A list of valid variable names can be found here:
+   * https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters.
+   */
+  void setSessionValue(String param, dynamic value);
+
+  /**
+   * Wait for all of the outstanding analytics pings to complete. The returned
+   * `Future` will always complete without errors. You can pass in an optional
+   * `Duration` to specify to only wait for a certain amount of time.
+   *
+   * This method is particularly useful for command-line clients. Outstanding
+   * I/O requests will cause the VM to delay terminating the process. Generally,
+   * users won't want their CLI app to pause at the end of the process waiting
+   * for Google analytics requests to complete. This method allows CLI apps to
+   * delay for a short time waiting for GA requests to complete, and then do
+   * something like call `exit()` explicitly themselves.
+   */
+  Future waitForLastPing({Duration timeout});
+}
+
+/**
+ * An object, returned by [Analytics.startTimer], that is used to measure an
+ * asynchronous process.
+ */
+class AnalyticsTimer {
+  final Analytics analytics;
+  final String variableName;
+  final String category;
+  final String label;
+
+  int _startMillis;
+  int _endMillis;
+
+  AnalyticsTimer(this.analytics, this.variableName,
+      {this.category, this.label}) {
+    _startMillis = new DateTime.now().millisecondsSinceEpoch;
+  }
+
+  int get currentElapsedMillis {
+    if (_endMillis == null) {
+      return new DateTime.now().millisecondsSinceEpoch - _startMillis;
+    } else {
+      return _endMillis - _startMillis;
+    }
+  }
+
+  /**
+   * Finish the timer, calculate the elapsed time, and send the information to
+   * analytics. Once this is called, any future invocations are no-ops.
+   */
+  Future finish() {
+    if (_endMillis != null) return new Future.value();
+
+    _endMillis = new DateTime.now().millisecondsSinceEpoch;
+    return analytics.sendTiming(
+        variableName, currentElapsedMillis, category: category, label: label);
+  }
+}
+
+/**
+ * A no-op implementation of the [Analytics] class. This can be used as a
+ * stand-in for that will never ping the GA server, or as a mock in test code.
+ */
+class AnalyticsMock implements Analytics {
+  String get trackingId => 'UA-0';
+  final bool logCalls;
+
+  bool optIn = false;
+  bool hasSetOptIn = true;
+
+  /**
+   * Create a new [AnalyticsMock]. If [logCalls] is true, all calls will be
+   * logged to stdout.
+   */
+  AnalyticsMock([this.logCalls = false]);
+
+  Future sendScreenView(String viewName) =>
+      _log('screenView', {'viewName': viewName});
+
+  Future sendEvent(String category, String action, {String label, int value}) {
+    return _log('event', {'category': category, 'action': action,
+      'label': label, 'value': value});
+  }
+
+  Future sendSocial(String network, String action, String target) =>
+      _log('social', {'network': network, 'action': action, 'target': target});
+
+  Future sendTiming(String variableName, int time, {String category,
+      String label}) {
+    return _log('timing', {'variableName': variableName, 'time': time,
+      'category': category, 'label': label});
+  }
+
+  AnalyticsTimer startTimer(String variableName,
+      {String category, String label}) {
+    return new AnalyticsTimer(this,
+        variableName, category: category, label: label);
+  }
+
+  Future sendException(String description, {bool fatal}) =>
+      _log('exception', {'description': description, 'fatal': fatal});
+
+  void setSessionValue(String param, dynamic value) { }
+
+  Future waitForLastPing({Duration timeout}) => new Future.value();
+
+  Future _log(String hitType, Map m) {
+    if (logCalls) {
+      print('analytics: ${hitType} ${m}');
+    }
+
+    return new Future.value();
+  }
+}
+
+/**
+ * Sanitize a stacktrace. This will shorten file paths in order to remove any
+ * PII that may be contained in the full file path. For example, this will
+ * shorten `file:///Users/foobar/tmp/error.dart` to `error.dart`.
+ *
+ * If [shorten] is `true`, this method will also attempt to compress the text
+ * of the stacktrace. GA has a 100 char limit on the text that can be sent for
+ * an exception. This will try and make those first 100 chars contain
+ * information useful to debugging the issue.
+ */
+String sanitizeStacktrace(dynamic st, {bool shorten: true}) {
+  String str = '${st}';
+
+  Iterable<Match> iter = _pathRegex.allMatches(str);
+  iter = iter.toList().reversed;
+
+  for (Match match in iter) {
+    String replacement = match.group(1);
+    str = str.substring(0, match.start)
+        + replacement + str.substring(match.end);
+  }
+
+  if (shorten) {
+    // Shorten the stacktrace up a bit.
+    str = str
+        .replaceAll('(package:', '(')
+        .replaceAll('(dart:', '(')
+        .replaceAll(new RegExp(r'\s+'), ' ');
+  }
+
+  return str;
+}
diff --git a/usage/lib/usage_html.dart b/usage/lib/usage_html.dart
new file mode 100644
index 0000000..be0641d
--- /dev/null
+++ b/usage/lib/usage_html.dart
@@ -0,0 +1,39 @@
+// 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.
+
+/**
+ * In order to use this library import the `usage_html.dart` file and
+ * instantiate the [AnalyticsHtml] class.
+ *
+ * You'll need to provide a Google Analytics tracking ID, the application name,
+ * and the application version.
+ */
+library usage_html;
+
+import 'dart:html';
+
+import 'src/usage_impl.dart';
+import 'src/usage_impl_html.dart';
+
+export 'usage.dart';
+
+/**
+ * An interface to a Google Analytics session, suitable for use in web apps.
+ */
+class AnalyticsHtml extends AnalyticsImpl {
+  AnalyticsHtml(String trackingId, String applicationName, String applicationVersion) :
+    super(
+      trackingId,
+      new HtmlPersistentProperties(applicationName),
+      new HtmlPostHandler(),
+      applicationName: applicationName,
+      applicationVersion: applicationVersion) {
+    int screenWidth = window.screen.width;
+    int screenHeight = window.screen.height;
+
+    setSessionValue('sr', '${screenWidth}x$screenHeight');
+    setSessionValue('sd', '${window.screen.pixelDepth}-bits');
+    setSessionValue('ul', window.navigator.language);
+  }
+}
diff --git a/usage/lib/usage_io.dart b/usage/lib/usage_io.dart
new file mode 100644
index 0000000..b68b216
--- /dev/null
+++ b/usage/lib/usage_io.dart
@@ -0,0 +1,31 @@
+// 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.
+
+/**
+ * In order to use this library import the `usage_io.dart` file and
+ * instantiate the [AnalyticsIO] class.
+ *
+ * You'll need to provide a Google Analytics tracking ID, the application name,
+ * and the application version.
+ */
+library usage_io;
+
+import 'src/usage_impl.dart';
+import 'src/usage_impl_io.dart';
+
+export 'usage.dart';
+
+/**
+ * An interface to a Google Analytics session, suitable for use in command-line
+ * applications.
+ */
+class AnalyticsIO extends AnalyticsImpl {
+  AnalyticsIO(String trackingId, String applicationName, String applicationVersion) :
+    super(
+      trackingId,
+      new IOPersistentProperties(applicationName),
+      new IOPostHandler(),
+      applicationName: applicationName,
+      applicationVersion: applicationVersion);
+}
diff --git a/usage/pubspec.yaml b/usage/pubspec.yaml
new file mode 100644
index 0000000..28905c5
--- /dev/null
+++ b/usage/pubspec.yaml
@@ -0,0 +1,17 @@
+# 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.
+
+name: usage
+version: 1.0.0+1
+description: A Google Analytics wrapper for both command-line and web apps.
+homepage: https://github.com/dart-lang/usage
+author: Dart Team <misc@dartlang.org>
+
+environment:
+  sdk: '>=1.0.0 <2.0.0'
+
+dev_dependencies:
+  browser: any
+  grinder: '>=0.6.6+3 <0.7.0'
+  unittest: any
diff --git a/utf/lib/src/constants.dart b/utf/lib/src/constants.dart
new file mode 100644
index 0000000..3dfea39
--- /dev/null
+++ b/utf/lib/src/constants.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2012, 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 utf.constants;
+
+/**
+ * Invalid codepoints or encodings may be substituted with the value U+fffd.
+ */
+const int UNICODE_REPLACEMENT_CHARACTER_CODEPOINT = 0xfffd;
+const int UNICODE_BOM = 0xfeff;
+const int UNICODE_UTF_BOM_LO = 0xff;
+const int UNICODE_UTF_BOM_HI = 0xfe;
+
+const int UNICODE_BYTE_ZERO_MASK = 0xff;
+const int UNICODE_BYTE_ONE_MASK = 0xff00;
+const int UNICODE_VALID_RANGE_MAX = 0x10ffff;
+const int UNICODE_PLANE_ONE_MAX = 0xffff;
+const int UNICODE_UTF16_RESERVED_LO = 0xd800;
+const int UNICODE_UTF16_RESERVED_HI = 0xdfff;
+const int UNICODE_UTF16_OFFSET = 0x10000;
+const int UNICODE_UTF16_SURROGATE_UNIT_0_BASE = 0xd800;
+const int UNICODE_UTF16_SURROGATE_UNIT_1_BASE = 0xdc00;
+const int UNICODE_UTF16_HI_MASK = 0xffc00;
+const int UNICODE_UTF16_LO_MASK = 0x3ff;
diff --git a/utf/lib/src/list_range.dart b/utf/lib/src/list_range.dart
new file mode 100644
index 0000000..2f3b34d
--- /dev/null
+++ b/utf/lib/src/list_range.dart
@@ -0,0 +1,79 @@
+// Copyright (c) 2012, 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 utf.list_range;
+
+import 'dart:collection';
+
+/**
+ * _ListRange in an internal type used to create a lightweight Interable on a
+ * range within a source list. DO NOT MODIFY the underlying list while
+ * iterating over it. The results of doing so are undefined.
+ */
+// TODO(floitsch): Consider removing the extend and switch to implements since
+// that's cheaper to allocate.
+class ListRange extends IterableBase {
+  final List _source;
+  final int _offset;
+  final int _length;
+
+  ListRange(source, [offset = 0, length]) :
+      this._source = source,
+      this._offset = offset,
+      this._length = (length == null ? source.length - offset : length) {
+    if (_offset < 0 || _offset > _source.length) {
+      throw new RangeError.value(_offset);
+    }
+    if (_length != null && (_length < 0)) {
+      throw new RangeError.value(_length);
+    }
+    if (_length + _offset > _source.length) {
+      throw new RangeError.value(_length + _offset);
+    }
+  }
+
+  ListRangeIterator get iterator =>
+      new _ListRangeIteratorImpl(_source, _offset, _offset + _length);
+
+  int get length => _length;
+}
+
+/**
+ * The ListRangeIterator provides more capabilities than a standard iterator,
+ * including the ability to get the current position, count remaining items,
+ * and move forward/backward within the iterator.
+ */
+abstract class ListRangeIterator implements Iterator<int> {
+  bool moveNext();
+  int get current;
+  int get position;
+  void backup([int by]);
+  int get remaining;
+  void skip([int count]);
+}
+
+class _ListRangeIteratorImpl implements ListRangeIterator {
+  final List<int> _source;
+  int _offset;
+  final int _end;
+
+  _ListRangeIteratorImpl(this._source, int offset, this._end)
+      : _offset = offset - 1;
+
+  int get current => _source[_offset];
+
+  bool moveNext() => ++_offset < _end;
+
+  int get position => _offset;
+
+  void backup([int by = 1]) {
+    _offset -= by;
+  }
+
+  int get remaining => _end - _offset - 1;
+
+  void skip([int count = 1]) {
+    _offset += count;
+  }
+}
diff --git a/utf/lib/src/utf/utf16.dart b/utf/lib/src/utf/utf16.dart
new file mode 100644
index 0000000..8ddd4dd
--- /dev/null
+++ b/utf/lib/src/utf/utf16.dart
@@ -0,0 +1,361 @@
+// Copyright (c) 2012, 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.
+
+part of utf;
+
+// TODO(jmesserly): would be nice to have this on String (dartbug.com/6501).
+/**
+ * Provide a list of Unicode codepoints for a given string.
+ */
+List<int> stringToCodepoints(String str) {
+  // Note: str.codeUnits gives us 16-bit code units on all Dart implementations.
+  // So we need to convert.
+  return utf16CodeUnitsToCodepoints(str.codeUnits);
+}
+
+/**
+ * Generate a string from the provided Unicode codepoints.
+ *
+ * *Deprecated* Use [String.fromCharCodes] instead.
+ */
+@deprecated
+String codepointsToString(List<int> codepoints) {
+  return new String.fromCharCodes(codepoints);
+}
+/**
+ * Decodes the UTF-16 bytes as an iterable. Thus, the consumer can only convert
+ * as much of the input as needed. Determines the byte order from the BOM,
+ * or uses big-endian as a default. This method always strips a leading BOM.
+ * Set the [replacementCodepoint] to null to throw an ArgumentError
+ * rather than replace the bad value. The default value for
+ * [replacementCodepoint] is U+FFFD.
+ */
+IterableUtf16Decoder decodeUtf16AsIterable(List<int> bytes, [int offset = 0,
+    int length, int replacementCodepoint =
+    UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  return new IterableUtf16Decoder._(
+      () => new Utf16BytesToCodeUnitsDecoder(bytes, offset, length,
+      replacementCodepoint), replacementCodepoint);
+}
+
+/**
+ * Decodes the UTF-16BE bytes as an iterable. Thus, the consumer can only
+ * convert as much of the input as needed. This method strips a leading BOM by
+ * default, but can be overridden by setting the optional parameter [stripBom]
+ * to false. Set the [replacementCodepoint] to null to throw an
+ * ArgumentError rather than replace the bad value. The default
+ * value for the [replacementCodepoint] is U+FFFD.
+ */
+IterableUtf16Decoder decodeUtf16beAsIterable(List<int> bytes, [int offset = 0,
+    int length, bool stripBom = true, int replacementCodepoint =
+    UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  return new IterableUtf16Decoder._(
+      () => new Utf16beBytesToCodeUnitsDecoder(bytes, offset, length, stripBom,
+      replacementCodepoint), replacementCodepoint);
+}
+
+/**
+ * Decodes the UTF-16LE bytes as an iterable. Thus, the consumer can only
+ * convert as much of the input as needed. This method strips a leading BOM by
+ * default, but can be overridden by setting the optional parameter [stripBom]
+ * to false. Set the [replacementCodepoint] to null to throw an
+ * ArgumentError rather than replace the bad value. The default
+ * value for the [replacementCodepoint] is U+FFFD.
+ */
+IterableUtf16Decoder decodeUtf16leAsIterable(List<int> bytes, [int offset = 0,
+    int length, bool stripBom = true, int replacementCodepoint =
+    UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  return new IterableUtf16Decoder._(
+      () => new Utf16leBytesToCodeUnitsDecoder(bytes, offset, length, stripBom,
+      replacementCodepoint), replacementCodepoint);
+}
+
+/**
+ * Produce a String from a sequence of UTF-16 encoded bytes. This method always
+ * strips a leading BOM. Set the [replacementCodepoint] to null to throw  an
+ * ArgumentError rather than replace the bad value. The default
+ * value for the [replacementCodepoint] is U+FFFD.
+ */
+String decodeUtf16(List<int> bytes, [int offset = 0, int length,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  Utf16BytesToCodeUnitsDecoder decoder = new Utf16BytesToCodeUnitsDecoder(bytes,
+      offset, length, replacementCodepoint);
+  List<int> codeunits = decoder.decodeRest();
+  return new String.fromCharCodes(
+      utf16CodeUnitsToCodepoints(codeunits, 0, null, replacementCodepoint));
+}
+
+/**
+ * Produce a String from a sequence of UTF-16BE encoded bytes. This method
+ * strips a leading BOM by default, but can be overridden by setting the
+ * optional parameter [stripBom] to false. Set the [replacementCodepoint] to
+ * null to throw an ArgumentError rather than replace the bad value.
+ * The default value for the [replacementCodepoint] is U+FFFD.
+ */
+String decodeUtf16be(List<int> bytes, [int offset = 0, int length,
+    bool stripBom = true,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  List<int> codeunits = (new Utf16beBytesToCodeUnitsDecoder(bytes, offset,
+      length, stripBom, replacementCodepoint)).decodeRest();
+  return new String.fromCharCodes(
+      utf16CodeUnitsToCodepoints(codeunits, 0, null, replacementCodepoint));
+}
+
+/**
+ * Produce a String from a sequence of UTF-16LE encoded bytes. This method
+ * strips a leading BOM by default, but can be overridden by setting the
+ * optional parameter [stripBom] to false. Set the [replacementCodepoint] to
+ * null to throw an ArgumentError rather than replace the bad value.
+ * The default value for the [replacementCodepoint] is U+FFFD.
+ */
+String decodeUtf16le(List<int> bytes, [int offset = 0, int length,
+    bool stripBom = true,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  List<int> codeunits = (new Utf16leBytesToCodeUnitsDecoder(bytes, offset,
+      length, stripBom, replacementCodepoint)).decodeRest();
+  return new String.fromCharCodes(
+      utf16CodeUnitsToCodepoints(codeunits, 0, null, replacementCodepoint));
+}
+
+/**
+ * Produce a list of UTF-16 encoded bytes. This method prefixes the resulting
+ * bytes with a big-endian byte-order-marker.
+ */
+List<int> encodeUtf16(String str) =>
+    encodeUtf16be(str, true);
+
+/**
+ * Produce a list of UTF-16BE encoded bytes. By default, this method produces
+ * UTF-16BE bytes with no BOM.
+ */
+List<int> encodeUtf16be(String str, [bool writeBOM = false]) {
+  List<int> utf16CodeUnits = _stringToUtf16CodeUnits(str);
+  List<int> encoding =
+      new List<int>(2 * utf16CodeUnits.length + (writeBOM ? 2 : 0));
+  int i = 0;
+  if (writeBOM) {
+    encoding[i++] = UNICODE_UTF_BOM_HI;
+    encoding[i++] = UNICODE_UTF_BOM_LO;
+  }
+  for (int unit in utf16CodeUnits) {
+    encoding[i++] = (unit & UNICODE_BYTE_ONE_MASK) >> 8;
+    encoding[i++] = unit & UNICODE_BYTE_ZERO_MASK;
+  }
+  return encoding;
+}
+
+/**
+ * Produce a list of UTF-16LE encoded bytes. By default, this method produces
+ * UTF-16LE bytes with no BOM.
+ */
+List<int> encodeUtf16le(String str, [bool writeBOM = false]) {
+  List<int> utf16CodeUnits = _stringToUtf16CodeUnits(str);
+  List<int> encoding =
+      new List<int>(2 * utf16CodeUnits.length + (writeBOM ? 2 : 0));
+  int i = 0;
+  if (writeBOM) {
+    encoding[i++] = UNICODE_UTF_BOM_LO;
+    encoding[i++] = UNICODE_UTF_BOM_HI;
+  }
+  for (int unit in utf16CodeUnits) {
+    encoding[i++] = unit & UNICODE_BYTE_ZERO_MASK;
+    encoding[i++] = (unit & UNICODE_BYTE_ONE_MASK) >> 8;
+  }
+  return encoding;
+}
+
+/**
+ * Identifies whether a List of bytes starts (based on offset) with a
+ * byte-order marker (BOM).
+ */
+bool hasUtf16Bom(List<int> utf32EncodedBytes, [int offset = 0, int length]) {
+  return hasUtf16beBom(utf32EncodedBytes, offset, length) ||
+      hasUtf16leBom(utf32EncodedBytes, offset, length);
+}
+
+/**
+ * Identifies whether a List of bytes starts (based on offset) with a
+ * big-endian byte-order marker (BOM).
+ */
+bool hasUtf16beBom(List<int> utf16EncodedBytes, [int offset = 0, int length]) {
+  int end = length != null ? offset + length : utf16EncodedBytes.length;
+  return (offset + 2) <= end &&
+      utf16EncodedBytes[offset] == UNICODE_UTF_BOM_HI &&
+      utf16EncodedBytes[offset + 1] == UNICODE_UTF_BOM_LO;
+}
+
+/**
+ * Identifies whether a List of bytes starts (based on offset) with a
+ * little-endian byte-order marker (BOM).
+ */
+bool hasUtf16leBom(List<int> utf16EncodedBytes, [int offset = 0, int length]) {
+  int end = length != null ? offset + length : utf16EncodedBytes.length;
+  return (offset + 2) <= end &&
+      utf16EncodedBytes[offset] == UNICODE_UTF_BOM_LO &&
+      utf16EncodedBytes[offset + 1] == UNICODE_UTF_BOM_HI;
+}
+
+List<int> _stringToUtf16CodeUnits(String str) {
+  return codepointsToUtf16CodeUnits(str.codeUnits);
+}
+
+typedef ListRangeIterator _CodeUnitsProvider();
+
+/**
+ * Return type of [decodeUtf16AsIterable] and variants. The Iterable type
+ * provides an iterator on demand and the iterator will only translate bytes
+ * as requested by the user of the iterator. (Note: results are not cached.)
+ */
+// TODO(floitsch): Consider removing the extend and switch to implements since
+// that's cheaper to allocate.
+class IterableUtf16Decoder extends IterableBase<int> {
+  final _CodeUnitsProvider codeunitsProvider;
+  final int replacementCodepoint;
+
+  IterableUtf16Decoder._(this.codeunitsProvider, this.replacementCodepoint);
+
+  Utf16CodeUnitDecoder get iterator =>
+      new Utf16CodeUnitDecoder.fromListRangeIterator(codeunitsProvider(),
+          replacementCodepoint);
+}
+
+/**
+ * Convert UTF-16 encoded bytes to UTF-16 code units by grouping 1-2 bytes
+ * to produce the code unit (0-(2^16)-1). Relies on BOM to determine
+ * endian-ness, and defaults to BE.
+ */
+abstract class Utf16BytesToCodeUnitsDecoder implements ListRangeIterator {
+  // TODO(kevmoo): should this field be private?
+  final ListRangeIterator utf16EncodedBytesIterator;
+  final int replacementCodepoint;
+  int _current = null;
+
+  Utf16BytesToCodeUnitsDecoder._fromListRangeIterator(
+      this.utf16EncodedBytesIterator, this.replacementCodepoint);
+
+  factory Utf16BytesToCodeUnitsDecoder(List<int> utf16EncodedBytes, [
+      int offset = 0, int length,
+      int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+    if (length == null) {
+      length = utf16EncodedBytes.length - offset;
+    }
+    if (hasUtf16beBom(utf16EncodedBytes, offset, length)) {
+      return new Utf16beBytesToCodeUnitsDecoder(utf16EncodedBytes, offset + 2,
+          length - 2, false, replacementCodepoint);
+    } else if (hasUtf16leBom(utf16EncodedBytes, offset, length)) {
+      return new Utf16leBytesToCodeUnitsDecoder(utf16EncodedBytes, offset + 2,
+          length - 2, false, replacementCodepoint);
+    } else {
+      return new Utf16beBytesToCodeUnitsDecoder(utf16EncodedBytes, offset,
+          length, false, replacementCodepoint);
+    }
+  }
+
+  /**
+   * Provides a fast way to decode the rest of the source bytes in a single
+   * call. This method trades memory for improved speed in that it potentially
+   * over-allocates the List containing results.
+   */
+  List<int> decodeRest() {
+    List<int> codeunits = new List<int>(remaining);
+    int i = 0;
+    while (moveNext()) {
+      codeunits[i++] = current;
+    }
+    if (i == codeunits.length) {
+      return codeunits;
+    } else {
+      List<int> truncCodeunits = new List<int>(i);
+      truncCodeunits.setRange(0, i, codeunits);
+      return truncCodeunits;
+    }
+  }
+
+  int get current => _current;
+
+  bool moveNext() {
+    _current = null;
+    int remaining = utf16EncodedBytesIterator.remaining;
+    if (remaining == 0) {
+      _current = null;
+      return false;
+    }
+    if (remaining == 1) {
+      utf16EncodedBytesIterator.moveNext();
+      if (replacementCodepoint != null) {
+        _current = replacementCodepoint;
+        return true;
+      } else {
+        throw new ArgumentError(
+            "Invalid UTF16 at ${utf16EncodedBytesIterator.position}");
+      }
+    }
+    _current = decode();
+    return true;
+  }
+
+  int get position => utf16EncodedBytesIterator.position ~/ 2;
+
+  void backup([int by = 1]) {
+    utf16EncodedBytesIterator.backup(2 * by);
+  }
+
+  int get remaining => (utf16EncodedBytesIterator.remaining + 1) ~/ 2;
+
+  void skip([int count = 1]) {
+    utf16EncodedBytesIterator.skip(2 * count);
+  }
+
+  int decode();
+}
+
+/**
+ * Convert UTF-16BE encoded bytes to utf16 code units by grouping 1-2 bytes
+ * to produce the code unit (0-(2^16)-1).
+ */
+class Utf16beBytesToCodeUnitsDecoder extends Utf16BytesToCodeUnitsDecoder {
+  Utf16beBytesToCodeUnitsDecoder(List<int> utf16EncodedBytes, [
+      int offset = 0, int length, bool stripBom = true,
+      int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) :
+      super._fromListRangeIterator(
+          (new ListRange(utf16EncodedBytes, offset, length)).iterator,
+          replacementCodepoint) {
+    if (stripBom && hasUtf16beBom(utf16EncodedBytes, offset, length)) {
+      skip();
+    }
+  }
+
+  int decode() {
+    utf16EncodedBytesIterator.moveNext();
+    int hi = utf16EncodedBytesIterator.current;
+    utf16EncodedBytesIterator.moveNext();
+    int lo = utf16EncodedBytesIterator.current;
+    return (hi << 8) + lo;
+  }
+}
+
+/**
+ * Convert UTF-16LE encoded bytes to utf16 code units by grouping 1-2 bytes
+ * to produce the code unit (0-(2^16)-1).
+ */
+class Utf16leBytesToCodeUnitsDecoder extends Utf16BytesToCodeUnitsDecoder {
+  Utf16leBytesToCodeUnitsDecoder(List<int> utf16EncodedBytes, [
+      int offset = 0, int length, bool stripBom = true,
+      int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) :
+      super._fromListRangeIterator(
+          (new ListRange(utf16EncodedBytes, offset, length)).iterator,
+          replacementCodepoint) {
+    if (stripBom && hasUtf16leBom(utf16EncodedBytes, offset, length)) {
+      skip();
+    }
+  }
+
+  int decode() {
+    utf16EncodedBytesIterator.moveNext();
+    int lo = utf16EncodedBytesIterator.current;
+    utf16EncodedBytesIterator.moveNext();
+    int hi = utf16EncodedBytesIterator.current;
+    return (hi << 8) + lo;
+  }
+}
diff --git a/utf/lib/src/utf/utf32.dart b/utf/lib/src/utf/utf32.dart
new file mode 100644
index 0000000..e51009d
--- /dev/null
+++ b/utf/lib/src/utf/utf32.dart
@@ -0,0 +1,343 @@
+// Copyright (c) 2012, 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.
+
+part of utf;
+
+/**
+ * Decodes the UTF-32 bytes as an iterable. Thus, the consumer can only convert
+ * as much of the input as needed. Determines the byte order from the BOM,
+ * or uses big-endian as a default. This method always strips a leading BOM.
+ * Set the replacementCharacter to null to throw an ArgumentError
+ * rather than replace the bad value.
+ */
+IterableUtf32Decoder decodeUtf32AsIterable(List<int> bytes, [
+    int offset = 0, int length,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  return new IterableUtf32Decoder._(
+      () => new Utf32BytesDecoder(bytes, offset, length, replacementCodepoint));
+}
+
+/**
+ * Decodes the UTF-32BE bytes as an iterable. Thus, the consumer can only convert
+ * as much of the input as needed. This method strips a leading BOM by default,
+ * but can be overridden by setting the optional parameter [stripBom] to false.
+ * Set the replacementCharacter to null to throw an ArgumentError
+ * rather than replace the bad value.
+ */
+IterableUtf32Decoder decodeUtf32beAsIterable(List<int> bytes, [
+    int offset = 0, int length, bool stripBom = true,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  return new IterableUtf32Decoder._(
+      () => new Utf32beBytesDecoder(bytes, offset, length, stripBom,
+          replacementCodepoint));
+}
+
+/**
+ * Decodes the UTF-32LE bytes as an iterable. Thus, the consumer can only convert
+ * as much of the input as needed. This method strips a leading BOM by default,
+ * but can be overridden by setting the optional parameter [stripBom] to false.
+ * Set the replacementCharacter to null to throw an ArgumentError
+ * rather than replace the bad value.
+ */
+IterableUtf32Decoder decodeUtf32leAsIterable(List<int> bytes, [
+    int offset = 0, int length, bool stripBom = true,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  return new IterableUtf32Decoder._(
+      () => new Utf32leBytesDecoder(bytes, offset, length, stripBom,
+          replacementCodepoint));
+}
+
+/**
+ * Produce a String from a sequence of UTF-32 encoded bytes. The parameters
+ * allow an offset into a list of bytes (as int), limiting the length of the
+ * values be decoded and the ability of override the default Unicode
+ * replacement character. Set the replacementCharacter to null to throw an
+ * ArgumentError rather than replace the bad value.
+ */
+String decodeUtf32(List<int> bytes, [int offset = 0, int length,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  return new String.fromCharCodes((new Utf32BytesDecoder(bytes, offset, length,
+      replacementCodepoint)).decodeRest());
+}
+/**
+ * Produce a String from a sequence of UTF-32BE encoded bytes. The parameters
+ * allow an offset into a list of bytes (as int), limiting the length of the
+ * values be decoded and the ability of override the default Unicode
+ * replacement character. Set the replacementCharacter to null to throw an
+ * ArgumentError rather than replace the bad value.
+ */
+String decodeUtf32be(
+    List<int> bytes, [int offset = 0, int length, bool stripBom = true,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) =>
+  new String.fromCharCodes((new Utf32beBytesDecoder(bytes, offset, length,
+    stripBom, replacementCodepoint)).decodeRest());
+
+/**
+ * Produce a String from a sequence of UTF-32LE encoded bytes. The parameters
+ * allow an offset into a list of bytes (as int), limiting the length of the
+ * values be decoded and the ability of override the default Unicode
+ * replacement character. Set the replacementCharacter to null to throw an
+ * ArgumentError rather than replace the bad value.
+ */
+String decodeUtf32le(
+    List<int> bytes, [int offset = 0, int length, bool stripBom = true,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) =>
+    new String.fromCharCodes((new Utf32leBytesDecoder(bytes, offset, length,
+      stripBom, replacementCodepoint)).decodeRest());
+
+/**
+ * Produce a list of UTF-32 encoded bytes. This method prefixes the resulting
+ * bytes with a big-endian byte-order-marker.
+ */
+List<int> encodeUtf32(String str) =>
+    encodeUtf32be(str, true);
+
+/**
+ * Produce a list of UTF-32BE encoded bytes. By default, this method produces
+ * UTF-32BE bytes with no BOM.
+ */
+List<int> encodeUtf32be(String str, [bool writeBOM = false]) {
+  List<int> utf32CodeUnits = stringToCodepoints(str);
+  List<int> encoding = new List<int>(4 * utf32CodeUnits.length +
+      (writeBOM ? 4 : 0));
+  int i = 0;
+  if (writeBOM) {
+    encoding[i++] = 0;
+    encoding[i++] = 0;
+    encoding[i++] = UNICODE_UTF_BOM_HI;
+    encoding[i++] = UNICODE_UTF_BOM_LO;
+  }
+  for (int unit in utf32CodeUnits) {
+    encoding[i++] = (unit >> 24) & UNICODE_BYTE_ZERO_MASK;
+    encoding[i++] = (unit >> 16) & UNICODE_BYTE_ZERO_MASK;
+    encoding[i++] = (unit >> 8) & UNICODE_BYTE_ZERO_MASK;
+    encoding[i++] = unit & UNICODE_BYTE_ZERO_MASK;
+  }
+  return encoding;
+}
+
+/**
+ * Produce a list of UTF-32LE encoded bytes. By default, this method produces
+ * UTF-32BE bytes with no BOM.
+ */
+List<int> encodeUtf32le(String str, [bool writeBOM = false]) {
+  List<int> utf32CodeUnits = stringToCodepoints(str);
+  List<int> encoding = new List<int>(4 * utf32CodeUnits.length +
+      (writeBOM ? 4 : 0));
+  int i = 0;
+  if (writeBOM) {
+    encoding[i++] = UNICODE_UTF_BOM_LO;
+    encoding[i++] = UNICODE_UTF_BOM_HI;
+    encoding[i++] = 0;
+    encoding[i++] = 0;
+  }
+  for (int unit in utf32CodeUnits) {
+    encoding[i++] = unit & UNICODE_BYTE_ZERO_MASK;
+    encoding[i++] = (unit >> 8) & UNICODE_BYTE_ZERO_MASK;
+    encoding[i++] = (unit >> 16) & UNICODE_BYTE_ZERO_MASK;
+    encoding[i++] = (unit >> 24) & UNICODE_BYTE_ZERO_MASK;
+  }
+  return encoding;
+}
+
+/**
+ * Identifies whether a List of bytes starts (based on offset) with a
+ * byte-order marker (BOM).
+ */
+bool hasUtf32Bom(
+    List<int> utf32EncodedBytes, [int offset = 0, int length]) {
+  return hasUtf32beBom(utf32EncodedBytes, offset, length) ||
+      hasUtf32leBom(utf32EncodedBytes, offset, length);
+}
+
+/**
+ * Identifies whether a List of bytes starts (based on offset) with a
+ * big-endian byte-order marker (BOM).
+ */
+bool hasUtf32beBom(List<int> utf32EncodedBytes, [int offset = 0, int length]) {
+  int end = length != null ? offset + length : utf32EncodedBytes.length;
+  return (offset + 4) <= end &&
+      utf32EncodedBytes[offset] == 0 && utf32EncodedBytes[offset + 1] == 0 &&
+      utf32EncodedBytes[offset + 2] == UNICODE_UTF_BOM_HI &&
+      utf32EncodedBytes[offset + 3] == UNICODE_UTF_BOM_LO;
+}
+
+/**
+ * Identifies whether a List of bytes starts (based on offset) with a
+ * little-endian byte-order marker (BOM).
+ */
+bool hasUtf32leBom(List<int> utf32EncodedBytes, [int offset = 0, int length]) {
+  int end = length != null ? offset + length : utf32EncodedBytes.length;
+  return (offset + 4) <= end &&
+      utf32EncodedBytes[offset] == UNICODE_UTF_BOM_LO &&
+      utf32EncodedBytes[offset + 1] == UNICODE_UTF_BOM_HI &&
+      utf32EncodedBytes[offset + 2] == 0 && utf32EncodedBytes[offset + 3] == 0;
+}
+
+typedef Utf32BytesDecoder Utf32BytesDecoderProvider();
+
+/**
+ * Return type of [decodeUtf32AsIterable] and variants. The Iterable type
+ * provides an iterator on demand and the iterator will only translate bytes
+ * as requested by the user of the iterator. (Note: results are not cached.)
+ */
+// TODO(floitsch): Consider removing the extend and switch to implements since
+// that's cheaper to allocate.
+class IterableUtf32Decoder extends IterableBase<int> {
+  final Utf32BytesDecoderProvider codeunitsProvider;
+
+  IterableUtf32Decoder._(this.codeunitsProvider);
+
+  Utf32BytesDecoder get iterator => codeunitsProvider();
+}
+
+/**
+ * Abstrace parent class converts encoded bytes to codepoints.
+ */
+abstract class Utf32BytesDecoder implements ListRangeIterator {
+  // TODO(kevmoo): should this field be private?
+  final ListRangeIterator utf32EncodedBytesIterator;
+  final int replacementCodepoint;
+  int _current = null;
+
+  Utf32BytesDecoder._fromListRangeIterator(
+      this.utf32EncodedBytesIterator, this.replacementCodepoint);
+
+  factory Utf32BytesDecoder(List<int> utf32EncodedBytes, [
+      int offset = 0, int length,
+      int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+    if (length == null) {
+      length = utf32EncodedBytes.length - offset;
+    }
+    if (hasUtf32beBom(utf32EncodedBytes, offset, length)) {
+      return new Utf32beBytesDecoder(utf32EncodedBytes, offset + 4, length - 4,
+          false, replacementCodepoint);
+    } else if (hasUtf32leBom(utf32EncodedBytes, offset, length)) {
+      return new Utf32leBytesDecoder(utf32EncodedBytes, offset + 4, length - 4,
+          false, replacementCodepoint);
+    } else {
+      return new Utf32beBytesDecoder(utf32EncodedBytes, offset, length, false,
+          replacementCodepoint);
+    }
+  }
+
+  List<int> decodeRest() {
+    List<int> codeunits = new List<int>(remaining);
+    int i = 0;
+    while (moveNext()) {
+      codeunits[i++] = current;
+    }
+    return codeunits;
+  }
+
+  int get current => _current;
+
+  bool moveNext() {
+    _current = null;
+    int remaining = utf32EncodedBytesIterator.remaining;
+    if (remaining == 0) {
+      _current = null;
+      return false;
+    }
+    if (remaining < 4) {
+      utf32EncodedBytesIterator.skip(utf32EncodedBytesIterator.remaining);
+      if (replacementCodepoint != null) {
+          _current = replacementCodepoint;
+          return true;
+      } else {
+        throw new ArgumentError(
+            "Invalid UTF32 at ${utf32EncodedBytesIterator.position}");
+      }
+    }
+    int codepoint = decode();
+    if (_validCodepoint(codepoint)) {
+      _current = codepoint;
+      return true;
+    } else if (replacementCodepoint != null) {
+      _current = replacementCodepoint;
+      return true;
+    } else {
+      throw new ArgumentError(
+          "Invalid UTF32 at ${utf32EncodedBytesIterator.position}");
+    }
+  }
+
+  int get position => utf32EncodedBytesIterator.position ~/ 4;
+
+  void backup([int by = 1]) {
+    utf32EncodedBytesIterator.backup(4 * by);
+  }
+
+  int get remaining => (utf32EncodedBytesIterator.remaining + 3) ~/ 4;
+
+  void skip([int count = 1]) {
+    utf32EncodedBytesIterator.skip(4 * count);
+  }
+
+  int decode();
+}
+
+/**
+ * Convert UTF-32BE encoded bytes to codepoints by grouping 4 bytes
+ * to produce the unicode codepoint.
+ */
+class Utf32beBytesDecoder extends Utf32BytesDecoder {
+  Utf32beBytesDecoder(List<int> utf32EncodedBytes, [int offset = 0,
+      int length, bool stripBom = true,
+      int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) :
+      super._fromListRangeIterator(
+          (new ListRange(utf32EncodedBytes, offset, length)).iterator,
+          replacementCodepoint) {
+    if (stripBom && hasUtf32beBom(utf32EncodedBytes, offset, length)) {
+      skip();
+    }
+  }
+
+  int decode() {
+    utf32EncodedBytesIterator.moveNext();
+    int value = utf32EncodedBytesIterator.current;
+    utf32EncodedBytesIterator.moveNext();
+    value = (value << 8) + utf32EncodedBytesIterator.current;
+    utf32EncodedBytesIterator.moveNext();
+    value = (value << 8) + utf32EncodedBytesIterator.current;
+    utf32EncodedBytesIterator.moveNext();
+    value = (value << 8) + utf32EncodedBytesIterator.current;
+    return value;
+  }
+}
+
+/**
+ * Convert UTF-32BE encoded bytes to codepoints by grouping 4 bytes
+ * to produce the unicode codepoint.
+ */
+class Utf32leBytesDecoder extends Utf32BytesDecoder {
+  Utf32leBytesDecoder(List<int> utf32EncodedBytes, [int offset = 0,
+      int length, bool stripBom = true,
+      int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) :
+      super._fromListRangeIterator(
+          (new ListRange(utf32EncodedBytes, offset, length)).iterator,
+          replacementCodepoint) {
+    if (stripBom && hasUtf32leBom(utf32EncodedBytes, offset, length)) {
+      skip();
+    }
+  }
+
+  int decode() {
+    utf32EncodedBytesIterator.moveNext();
+    int value = utf32EncodedBytesIterator.current;
+    utf32EncodedBytesIterator.moveNext();
+    value += (utf32EncodedBytesIterator.current << 8);
+    utf32EncodedBytesIterator.moveNext();
+    value += (utf32EncodedBytesIterator.current << 16);
+    utf32EncodedBytesIterator.moveNext();
+    value += (utf32EncodedBytesIterator.current << 24);
+    return value;
+  }
+}
+
+bool _validCodepoint(int codepoint) {
+  return (codepoint >= 0 && codepoint < UNICODE_UTF16_RESERVED_LO) ||
+      (codepoint > UNICODE_UTF16_RESERVED_HI &&
+      codepoint < UNICODE_VALID_RANGE_MAX);
+}
diff --git a/utf/lib/src/utf/utf8.dart b/utf/lib/src/utf/utf8.dart
new file mode 100644
index 0000000..ff1b1ed
--- /dev/null
+++ b/utf/lib/src/utf/utf8.dart
@@ -0,0 +1,276 @@
+// Copyright (c) 2012, 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.
+
+part of utf;
+
+const int _UTF8_ONE_BYTE_MAX = 0x7f;
+const int _UTF8_TWO_BYTE_MAX = 0x7ff;
+const int _UTF8_THREE_BYTE_MAX = 0xffff;
+
+const int _UTF8_LO_SIX_BIT_MASK = 0x3f;
+
+const int _UTF8_FIRST_BYTE_OF_TWO_BASE = 0xc0;
+const int _UTF8_FIRST_BYTE_OF_THREE_BASE = 0xe0;
+const int _UTF8_FIRST_BYTE_OF_FOUR_BASE = 0xf0;
+const int _UTF8_FIRST_BYTE_OF_FIVE_BASE = 0xf8;
+const int _UTF8_FIRST_BYTE_OF_SIX_BASE = 0xfc;
+
+const int _UTF8_FIRST_BYTE_OF_TWO_MASK = 0x1f;
+const int _UTF8_FIRST_BYTE_OF_THREE_MASK = 0xf;
+const int _UTF8_FIRST_BYTE_OF_FOUR_MASK = 0x7;
+
+const int _UTF8_FIRST_BYTE_BOUND_EXCL = 0xfe;
+const int _UTF8_SUBSEQUENT_BYTE_BASE = 0x80;
+
+/**
+ * Decodes the UTF-8 bytes as an iterable. Thus, the consumer can only convert
+ * as much of the input as needed. Set the replacementCharacter to null to
+ * throw an ArgumentError rather than replace the bad value.
+ */
+IterableUtf8Decoder decodeUtf8AsIterable(List<int> bytes, [int offset = 0,
+    int length,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  return new IterableUtf8Decoder(bytes, offset, length, replacementCodepoint);
+}
+
+/**
+ * Produce a String from a List of UTF-8 encoded bytes. The parameters
+ * can set an offset into a list of bytes (as int), limit the length of the
+ * values to be decoded, and override the default Unicode replacement character.
+ * Set the replacementCharacter to null to throw an ArgumentError
+ * rather than replace the bad value.
+ */
+String decodeUtf8(List<int> bytes, [int offset = 0, int length,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  return new String.fromCharCodes(
+      (new Utf8Decoder(bytes, offset, length, replacementCodepoint))
+      .decodeRest());
+}
+
+/**
+ * Produce a sequence of UTF-8 encoded bytes from the provided string.
+ */
+List<int> encodeUtf8(String str) =>
+  codepointsToUtf8(stringToCodepoints(str));
+
+int _addToEncoding(int offset, int bytes, int value, List<int> buffer) {
+  while (bytes > 0) {
+    buffer[offset + bytes] = _UTF8_SUBSEQUENT_BYTE_BASE |
+        (value & _UTF8_LO_SIX_BIT_MASK);
+    value = value >> 6;
+    bytes--;
+  }
+  return value;
+}
+
+/**
+ * Encode code points as UTF-8 code units.
+ */
+List<int> codepointsToUtf8(
+    List<int> codepoints, [int offset = 0, int length]) {
+  ListRange source = new ListRange(codepoints, offset, length);
+
+  int encodedLength = 0;
+  for (int value in source) {
+    if (value < 0 || value > UNICODE_VALID_RANGE_MAX) {
+      encodedLength += 3;
+    } else if (value <= _UTF8_ONE_BYTE_MAX) {
+      encodedLength++;
+    } else if (value <= _UTF8_TWO_BYTE_MAX) {
+      encodedLength += 2;
+    } else if (value <= _UTF8_THREE_BYTE_MAX) {
+      encodedLength += 3;
+    } else if (value <= UNICODE_VALID_RANGE_MAX) {
+      encodedLength += 4;
+    }
+  }
+
+  List<int> encoded = new List<int>(encodedLength);
+  int insertAt = 0;
+  for (int value in source) {
+    if (value < 0 || value > UNICODE_VALID_RANGE_MAX) {
+      encoded.setRange(insertAt, insertAt + 3, [0xef, 0xbf, 0xbd]);
+      insertAt += 3;
+    } else if (value <= _UTF8_ONE_BYTE_MAX) {
+      encoded[insertAt] = value;
+      insertAt++;
+    } else if (value <= _UTF8_TWO_BYTE_MAX) {
+      encoded[insertAt] = _UTF8_FIRST_BYTE_OF_TWO_BASE | (
+          _UTF8_FIRST_BYTE_OF_TWO_MASK &
+          _addToEncoding(insertAt, 1, value, encoded));
+      insertAt += 2;
+    } else if (value <= _UTF8_THREE_BYTE_MAX) {
+      encoded[insertAt] = _UTF8_FIRST_BYTE_OF_THREE_BASE | (
+          _UTF8_FIRST_BYTE_OF_THREE_MASK &
+          _addToEncoding(insertAt, 2, value, encoded));
+      insertAt += 3;
+    } else if (value <= UNICODE_VALID_RANGE_MAX) {
+      encoded[insertAt] = _UTF8_FIRST_BYTE_OF_FOUR_BASE | (
+          _UTF8_FIRST_BYTE_OF_FOUR_MASK &
+          _addToEncoding(insertAt, 3, value, encoded));
+      insertAt += 4;
+    }
+  }
+  return encoded;
+}
+
+// Because UTF-8 specifies byte order, we do not have to follow the pattern
+// used by UTF-16 & UTF-32 regarding byte order.
+List<int> utf8ToCodepoints(
+    List<int> utf8EncodedBytes, [int offset = 0, int length,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  return new Utf8Decoder(utf8EncodedBytes, offset, length,
+      replacementCodepoint).decodeRest();
+}
+
+/**
+ * Return type of [decodeUtf8AsIterable] and variants. The Iterable type
+ * provides an iterator on demand and the iterator will only translate bytes
+ * as requested by the user of the iterator. (Note: results are not cached.)
+ */
+// TODO(floitsch): Consider removing the extend and switch to implements since
+// that's cheaper to allocate.
+class IterableUtf8Decoder extends IterableBase<int> {
+  final List<int> bytes;
+  final int offset;
+  final int length;
+  final int replacementCodepoint;
+
+  IterableUtf8Decoder(this.bytes, [this.offset = 0, this.length = null,
+      this.replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]);
+
+  Utf8Decoder get iterator =>
+      new Utf8Decoder(bytes, offset, length, replacementCodepoint);
+}
+
+/**
+ * Provides an iterator of Unicode codepoints from UTF-8 encoded bytes. The
+ * parameters can set an offset into a list of bytes (as int), limit the length
+ * of the values to be decoded, and override the default Unicode replacement
+ * character. Set the replacementCharacter to null to throw an
+ * ArgumentError rather than replace the bad value. The return value
+ * from this method can be used as an Iterable (e.g. in a for-loop).
+ */
+class Utf8Decoder implements Iterator<int> {
+  // TODO(kevmoo): should this field be private?
+  final ListRangeIterator utf8EncodedBytesIterator;
+  final int replacementCodepoint;
+  int _current = null;
+
+  Utf8Decoder(List<int> utf8EncodedBytes, [int offset = 0, int length,
+      this.replacementCodepoint =
+      UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) :
+      utf8EncodedBytesIterator =
+          (new ListRange(utf8EncodedBytes, offset, length)).iterator;
+
+
+  Utf8Decoder._fromListRangeIterator(ListRange source, [
+      this.replacementCodepoint =
+      UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) :
+      utf8EncodedBytesIterator = source.iterator;
+
+  /** Decode the remaininder of the characters in this decoder
+    * into a [List<int>].
+    */
+  List<int> decodeRest() {
+    List<int> codepoints = new List<int>(utf8EncodedBytesIterator.remaining);
+    int i = 0;
+    while (moveNext()) {
+      codepoints[i++] = current;
+    }
+    if (i == codepoints.length) {
+      return codepoints;
+    } else {
+      List<int> truncCodepoints = new List<int>(i);
+      truncCodepoints.setRange(0, i, codepoints);
+      return truncCodepoints;
+    }
+  }
+
+  int get current => _current;
+
+  bool moveNext() {
+    _current = null;
+
+    if (!utf8EncodedBytesIterator.moveNext()) return false;
+
+    int value = utf8EncodedBytesIterator.current;
+    int additionalBytes = 0;
+
+    if (value < 0) {
+      if (replacementCodepoint != null) {
+        _current = replacementCodepoint;
+        return true;
+      } else {
+        throw new ArgumentError(
+            "Invalid UTF8 at ${utf8EncodedBytesIterator.position}");
+      }
+    } else if (value <= _UTF8_ONE_BYTE_MAX) {
+      _current = value;
+      return true;
+    } else if (value < _UTF8_FIRST_BYTE_OF_TWO_BASE) {
+      if (replacementCodepoint != null) {
+        _current = replacementCodepoint;
+        return true;
+      } else {
+        throw new ArgumentError(
+            "Invalid UTF8 at ${utf8EncodedBytesIterator.position}");
+      }
+    } else if (value < _UTF8_FIRST_BYTE_OF_THREE_BASE) {
+      value -= _UTF8_FIRST_BYTE_OF_TWO_BASE;
+      additionalBytes = 1;
+    } else if (value < _UTF8_FIRST_BYTE_OF_FOUR_BASE) {
+      value -= _UTF8_FIRST_BYTE_OF_THREE_BASE;
+      additionalBytes = 2;
+    } else if (value < _UTF8_FIRST_BYTE_OF_FIVE_BASE) {
+      value -= _UTF8_FIRST_BYTE_OF_FOUR_BASE;
+      additionalBytes = 3;
+    } else if (value < _UTF8_FIRST_BYTE_OF_SIX_BASE) {
+      value -= _UTF8_FIRST_BYTE_OF_FIVE_BASE;
+      additionalBytes = 4;
+    } else if (value < _UTF8_FIRST_BYTE_BOUND_EXCL) {
+      value -= _UTF8_FIRST_BYTE_OF_SIX_BASE;
+      additionalBytes = 5;
+    } else if (replacementCodepoint != null) {
+      _current = replacementCodepoint;
+      return true;
+    } else {
+      throw new ArgumentError(
+          "Invalid UTF8 at ${utf8EncodedBytesIterator.position}");
+    }
+    int j = 0;
+    while (j < additionalBytes && utf8EncodedBytesIterator.moveNext()) {
+      int nextValue = utf8EncodedBytesIterator.current;
+      if (nextValue > _UTF8_ONE_BYTE_MAX &&
+          nextValue < _UTF8_FIRST_BYTE_OF_TWO_BASE) {
+        value = ((value << 6) | (nextValue & _UTF8_LO_SIX_BIT_MASK));
+      } else {
+        // if sequence-starting code unit, reposition cursor to start here
+        if (nextValue >= _UTF8_FIRST_BYTE_OF_TWO_BASE) {
+          utf8EncodedBytesIterator.backup();
+        }
+        break;
+      }
+      j++;
+    }
+    bool validSequence = (j == additionalBytes && (
+        value < UNICODE_UTF16_RESERVED_LO ||
+        value > UNICODE_UTF16_RESERVED_HI));
+    bool nonOverlong =
+        (additionalBytes == 1 && value > _UTF8_ONE_BYTE_MAX) ||
+        (additionalBytes == 2 && value > _UTF8_TWO_BYTE_MAX) ||
+        (additionalBytes == 3 && value > _UTF8_THREE_BYTE_MAX);
+    bool inRange = value <= UNICODE_VALID_RANGE_MAX;
+    if (validSequence && nonOverlong && inRange) {
+      _current = value;
+      return true;
+    } else if (replacementCodepoint != null) {
+      _current = replacementCodepoint;
+      return true;
+    } else {
+      throw new ArgumentError(
+          "Invalid UTF8 at ${utf8EncodedBytesIterator.position - j}");
+    }
+  }
+}
diff --git a/utf/lib/src/utf/utf_stream.dart b/utf/lib/src/utf/utf_stream.dart
new file mode 100644
index 0000000..0936616
--- /dev/null
+++ b/utf/lib/src/utf/utf_stream.dart
@@ -0,0 +1,237 @@
+// Copyright (c) 2013, 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.
+
+part of utf;
+
+// TODO(floitsch): make this transformer reusable.
+abstract class _StringDecoder
+    implements StreamTransformer<List<int>, String>, EventSink<List<int>> {
+  List<int> _carry;
+  List<int> _buffer;
+  int _replacementChar;
+
+  EventSink<String> _outSink;
+
+  _StringDecoder(int this._replacementChar);
+
+  Stream<String> bind(Stream<List<int>> stream) {
+    return new Stream.eventTransformed(
+        stream,
+        (EventSink<String> sink) {
+          if (_outSink != null) {
+            throw new StateError("String decoder already used");
+          }
+          _outSink = sink;
+          return this;
+        });
+  }
+
+  void add(List<int> bytes) {
+    try {
+      _buffer = <int>[];
+      List<int> carry = _carry;
+      _carry = null;
+      int pos = 0;
+      int available = bytes.length;
+      // If we have carry-over data, start from negative index, indicating carry
+      // index.
+      int goodChars = 0;
+      if (carry != null) pos = -carry.length;
+      while (pos < available) {
+        int currentPos = pos;
+        int getNext() {
+          if (pos < 0) {
+            return carry[pos++ + carry.length];
+          } else if (pos < available) {
+            return bytes[pos++];
+          }
+          return null;
+        }
+        int consumed = _processBytes(getNext);
+        if (consumed > 0) {
+          goodChars = _buffer.length;
+        } else if (consumed == 0) {
+          _buffer.length = goodChars;
+          if (currentPos < 0) {
+            _carry = [];
+            _carry.addAll(carry);
+            _carry.addAll(bytes);
+          } else {
+            _carry = bytes.sublist(currentPos);
+          }
+          break;
+        } else {
+          // Invalid byte at position pos - 1
+          _buffer.length = goodChars;
+          _addChar(-1);
+          goodChars = _buffer.length;
+        }
+      }
+      if (_buffer.length > 0) {
+        // Limit to 'goodChars', if lower than actual charCodes in the buffer.
+        _outSink.add(new String.fromCharCodes(_buffer));
+      }
+      _buffer = null;
+    } catch (e, stackTrace) {
+      _outSink.addError(e, stackTrace);
+    }
+  }
+
+  void addError(error, [StackTrace stackTrace]) {
+    _outSink.addError(error, stackTrace);
+  }
+
+  void close() {
+    if (_carry != null) {
+      if (_replacementChar != null) {
+        _outSink.add(new String.fromCharCodes(
+            new List.filled(_carry.length, _replacementChar)));
+      } else {
+        throw new ArgumentError('Invalid codepoint');
+      }
+    }
+    _outSink.close();
+  }
+
+  int _processBytes(int getNext());
+
+  void _addChar(int char) {
+    void error() {
+      if (_replacementChar != null) {
+        char = _replacementChar;
+      } else {
+        throw new ArgumentError('Invalid codepoint');
+      }
+    }
+    if (char < 0) error();
+    if (char >= 0xD800 && char <= 0xDFFF) error();
+    if (char > 0x10FFFF) error();
+    _buffer.add(char);
+  }
+}
+
+/**
+ * StringTransformer that decodes a stream of UTF-8 encoded bytes.
+ */
+class Utf8DecoderTransformer extends _StringDecoder {
+  Utf8DecoderTransformer(
+      [int replacementChar = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT])
+    : super(replacementChar);
+
+  int _processBytes(int getNext()) {
+    int value = getNext();
+    if ((value & 0xFF) != value) return -1;  // Not a byte.
+    if ((value & 0x80) == 0x80) {
+      int additionalBytes;
+      int min;
+      if ((value & 0xe0) == 0xc0) {  // 110xxxxx
+        value = value & 0x1F;
+        additionalBytes = 1;
+        min = 0x80;
+      } else if ((value & 0xf0) == 0xe0) {  // 1110xxxx
+        value = value & 0x0F;
+        additionalBytes = 2;
+        min = 0x800;
+      } else if ((value & 0xf8) == 0xf0) {  // 11110xxx
+        value = value & 0x07;
+        additionalBytes = 3;
+        min = 0x10000;
+      } else if ((value & 0xfc) == 0xf8) {  // 111110xx
+        value = value & 0x03;
+        additionalBytes = 4;
+        min = 0x200000;
+      } else if ((value & 0xfe) == 0xfc) {  // 1111110x
+        value = value & 0x01;
+        additionalBytes = 5;
+        min = 0x4000000;
+      } else {
+        return -1;
+      }
+      for (int i = 0; i < additionalBytes; i++) {
+        int next = getNext();
+        if (next == null) return 0;  // Not enough chars, reset.
+        if ((next & 0xc0) != 0x80 || (next & 0xff) != next) return -1;
+        value = value << 6 | (next & 0x3f);
+        if (additionalBytes >= 3 && i == 0 && value << 12 > 0x10FFFF) {
+          _addChar(-1);
+        }
+      }
+      // Invalid charCode if less then minimum expected.
+      if (value < min) value = -1;
+      _addChar(value);
+      return 1 + additionalBytes;
+    }
+    _addChar(value);
+    return 1;
+  }
+}
+
+
+abstract class _StringEncoder
+    implements StreamTransformer<String, List<int>>, EventSink<String> {
+
+  EventSink<List<int>> _outSink;
+
+  Stream<List<int>> bind(Stream<String> stream) {
+    return new Stream.eventTransformed(
+        stream,
+        (EventSink<List<int>> sink) {
+          if (_outSink != null) {
+            throw new StateError("String encoder already used");
+          }
+          _outSink = sink;
+          return this;
+        });
+  }
+
+  void add(String data) {
+    _outSink.add(_processString(data));
+  }
+
+  void addError(error, [StackTrace stackTrace]) {
+    _outSink.addError(error, stackTrace);
+  }
+
+  void close() { _outSink.close(); }
+
+  List<int> _processString(String string);
+}
+
+/**
+ * StringTransformer that UTF-8 encodes a stream of strings.
+ */
+class Utf8EncoderTransformer extends _StringEncoder {
+  List<int> _processString(String string) {
+    var bytes = <int>[];
+    int pos = 0;
+    List<int> codepoints = utf16CodeUnitsToCodepoints(string.codeUnits);
+    int length = codepoints.length;
+    for (int i = 0; i < length; i++) {
+      int additionalBytes;
+      int charCode = codepoints[i];
+      if (charCode <= 0x007F) {
+        additionalBytes = 0;
+        bytes.add(charCode);
+      } else if (charCode <= 0x07FF) {
+        // 110xxxxx (xxxxx is top 5 bits).
+        bytes.add(((charCode >> 6) & 0x1F) | 0xC0);
+        additionalBytes = 1;
+      } else if (charCode <= 0xFFFF) {
+        // 1110xxxx (xxxx is top 4 bits)
+        bytes.add(((charCode >> 12) & 0x0F)| 0xE0);
+        additionalBytes = 2;
+      } else {
+        // 11110xxx (xxx is top 3 bits)
+        bytes.add(((charCode >> 18) & 0x07) | 0xF0);
+        additionalBytes = 3;
+      }
+      for (int i = additionalBytes; i > 0; i--) {
+        // 10xxxxxx (xxxxxx is next 6 bits from the top).
+        bytes.add(((charCode >> (6 * (i - 1))) & 0x3F) | 0x80);
+      }
+      pos += additionalBytes + 1;
+    }
+    return bytes;
+  }
+}
diff --git a/utf/lib/src/utf_16_code_unit_decoder.dart b/utf/lib/src/utf_16_code_unit_decoder.dart
new file mode 100644
index 0000000..a0a4b3c
--- /dev/null
+++ b/utf/lib/src/utf_16_code_unit_decoder.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2012, 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 utf.utf_16_code_unit_decoder;
+
+import 'constants.dart';
+import 'list_range.dart';
+
+/**
+ * An Iterator<int> of codepoints built on an Iterator of UTF-16 code units.
+ * The parameters can override the default Unicode replacement character. Set
+ * the replacementCharacter to null to throw an ArgumentError
+ * rather than replace the bad value.
+ */
+class Utf16CodeUnitDecoder implements Iterator<int> {
+  // TODO(kevmoo): should this field be private?
+  final ListRangeIterator utf16CodeUnitIterator;
+  final int replacementCodepoint;
+  int _current = null;
+
+  Utf16CodeUnitDecoder(List<int> utf16CodeUnits, [int offset = 0, int length,
+      int this.replacementCodepoint =
+      UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) :
+      utf16CodeUnitIterator =
+          (new ListRange(utf16CodeUnits, offset, length)).iterator;
+
+  Utf16CodeUnitDecoder.fromListRangeIterator(
+      ListRangeIterator this.utf16CodeUnitIterator,
+      int this.replacementCodepoint);
+
+  Iterator<int> get iterator => this;
+
+  int get current => _current;
+
+  bool moveNext() {
+    _current = null;
+    if (!utf16CodeUnitIterator.moveNext()) return false;
+
+    int value = utf16CodeUnitIterator.current;
+    if (value < 0) {
+      if (replacementCodepoint != null) {
+        _current = replacementCodepoint;
+      } else {
+        throw new ArgumentError(
+            "Invalid UTF16 at ${utf16CodeUnitIterator.position}");
+      }
+    } else if (value < UNICODE_UTF16_RESERVED_LO ||
+        (value > UNICODE_UTF16_RESERVED_HI && value <= UNICODE_PLANE_ONE_MAX)) {
+      // transfer directly
+      _current = value;
+    } else if (value < UNICODE_UTF16_SURROGATE_UNIT_1_BASE &&
+        utf16CodeUnitIterator.moveNext()) {
+      // merge surrogate pair
+      int nextValue = utf16CodeUnitIterator.current;
+      if (nextValue >= UNICODE_UTF16_SURROGATE_UNIT_1_BASE &&
+          nextValue <= UNICODE_UTF16_RESERVED_HI) {
+        value = (value - UNICODE_UTF16_SURROGATE_UNIT_0_BASE) << 10;
+        value += UNICODE_UTF16_OFFSET +
+            (nextValue - UNICODE_UTF16_SURROGATE_UNIT_1_BASE);
+        _current = value;
+      } else {
+        if (nextValue >= UNICODE_UTF16_SURROGATE_UNIT_0_BASE &&
+           nextValue < UNICODE_UTF16_SURROGATE_UNIT_1_BASE) {
+          utf16CodeUnitIterator.backup();
+        }
+        if (replacementCodepoint != null) {
+          _current = replacementCodepoint;
+        } else {
+          throw new ArgumentError(
+              "Invalid UTF16 at ${utf16CodeUnitIterator.position}");
+        }
+      }
+    } else if (replacementCodepoint != null) {
+      _current = replacementCodepoint;
+    } else {
+      throw new ArgumentError(
+          "Invalid UTF16 at ${utf16CodeUnitIterator.position}");
+    }
+    return true;
+  }
+}
+
diff --git a/utf/lib/src/util.dart b/utf/lib/src/util.dart
new file mode 100644
index 0000000..17427d5
--- /dev/null
+++ b/utf/lib/src/util.dart
@@ -0,0 +1,78 @@
+// Copyright (c) 2012, 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 utf.util;
+
+import 'constants.dart';
+import 'list_range.dart';
+import 'utf_16_code_unit_decoder.dart';
+
+/**
+ * Decodes the utf16 codeunits to codepoints.
+ */
+List<int> utf16CodeUnitsToCodepoints(
+    List<int> utf16CodeUnits, [int offset = 0, int length,
+    int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+  ListRangeIterator source =
+      (new ListRange(utf16CodeUnits, offset, length)).iterator;
+  Utf16CodeUnitDecoder decoder = new Utf16CodeUnitDecoder
+      .fromListRangeIterator(source, replacementCodepoint);
+  List<int> codepoints = new List<int>(source.remaining);
+  int i = 0;
+  while (decoder.moveNext()) {
+    codepoints[i++] = decoder.current;
+  }
+  if (i == codepoints.length) {
+    return codepoints;
+  } else {
+    List<int> codepointTrunc = new List<int>(i);
+    codepointTrunc.setRange(0, i, codepoints);
+    return codepointTrunc;
+  }
+}
+
+/**
+ * Encode code points as UTF16 code units.
+ */
+List<int> codepointsToUtf16CodeUnits(
+    List<int> codepoints,
+    [int offset = 0,
+     int length,
+     int replacementCodepoint = UNICODE_REPLACEMENT_CHARACTER_CODEPOINT]) {
+
+  ListRange listRange = new ListRange(codepoints, offset, length);
+  int encodedLength = 0;
+  for (int value in listRange) {
+    if ((value >= 0 && value < UNICODE_UTF16_RESERVED_LO) ||
+        (value > UNICODE_UTF16_RESERVED_HI && value <= UNICODE_PLANE_ONE_MAX)) {
+      encodedLength++;
+    } else if (value > UNICODE_PLANE_ONE_MAX &&
+        value <= UNICODE_VALID_RANGE_MAX) {
+      encodedLength += 2;
+    } else {
+      encodedLength++;
+    }
+  }
+
+  List<int> codeUnitsBuffer = new List<int>(encodedLength);
+  int j = 0;
+  for (int value in listRange) {
+    if ((value >= 0 && value < UNICODE_UTF16_RESERVED_LO) ||
+        (value > UNICODE_UTF16_RESERVED_HI && value <= UNICODE_PLANE_ONE_MAX)) {
+      codeUnitsBuffer[j++] = value;
+    } else if (value > UNICODE_PLANE_ONE_MAX &&
+        value <= UNICODE_VALID_RANGE_MAX) {
+      int base = value - UNICODE_UTF16_OFFSET;
+      codeUnitsBuffer[j++] = UNICODE_UTF16_SURROGATE_UNIT_0_BASE +
+          ((base & UNICODE_UTF16_HI_MASK) >> 10);
+      codeUnitsBuffer[j++] = UNICODE_UTF16_SURROGATE_UNIT_1_BASE +
+          (base & UNICODE_UTF16_LO_MASK);
+    } else if (replacementCodepoint != null) {
+      codeUnitsBuffer[j++] = replacementCodepoint;
+    } else {
+      throw new ArgumentError("Invalid encoding");
+    }
+  }
+  return codeUnitsBuffer;
+}
diff --git a/utf/lib/utf.dart b/utf/lib/utf.dart
new file mode 100644
index 0000000..30d5db5
--- /dev/null
+++ b/utf/lib/utf.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2012, 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.
+
+/**
+ * Support for encoding and decoding Unicode characters in UTF-8, UTF-16, and
+ * UTF-32.
+ */
+library utf;
+
+import "dart:async";
+import "dart:collection";
+
+import "src/constants.dart";
+import 'src/utf_16_code_unit_decoder.dart';
+import 'src/list_range.dart';
+import 'src/util.dart';
+
+export 'src/constants.dart';
+export 'src/utf_16_code_unit_decoder.dart';
+
+part "src/utf/utf_stream.dart";
+part "src/utf/utf8.dart";
+part "src/utf/utf16.dart";
+part "src/utf/utf32.dart";
diff --git a/utf/pubspec.yaml b/utf/pubspec.yaml
new file mode 100644
index 0000000..519bed4
--- /dev/null
+++ b/utf/pubspec.yaml
@@ -0,0 +1,9 @@
+name: utf
+version: 0.9.0+2
+author: Dart Team <misc@dartlang.org>
+description: >
+ A Unicode library. Intended for advanced use where the built-in facilities
+ are too limiting.
+homepage: https://pub.dartlang.org/packages/utf
+environment:
+  sdk: '>=1.0.0 <2.0.0'
diff --git a/watcher/lib/src/async_queue.dart b/watcher/lib/src/async_queue.dart
new file mode 100644
index 0000000..b83493d
--- /dev/null
+++ b/watcher/lib/src/async_queue.dart
@@ -0,0 +1,73 @@
+// Copyright (c) 2013, 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 watcher.async_queue;
+
+import 'dart:async';
+import 'dart:collection';
+
+typedef Future ItemProcessor<T>(T item);
+
+/// A queue of items that are sequentially, asynchronously processed.
+///
+/// Unlike [Stream.map] or [Stream.forEach], the callback used to process each
+/// item returns a [Future], and it will not advance to the next item until the
+/// current item is finished processing.
+///
+/// Items can be added at any point in time and processing will be started as
+/// needed. When all items are processed, it stops processing until more items
+/// are added.
+class AsyncQueue<T> {
+  final _items = new Queue<T>();
+
+  /// Whether or not the queue is currently waiting on a processing future to
+  /// complete.
+  bool _isProcessing = false;
+
+  /// The callback to invoke on each queued item.
+  ///
+  /// The next item in the queue will not be processed until the [Future]
+  /// returned by this completes.
+  final ItemProcessor<T> _processor;
+
+  /// The handler for errors thrown during processing.
+  ///
+  /// Used to avoid top-leveling asynchronous errors.
+  final Function _errorHandler;
+
+  AsyncQueue(this._processor, {Function onError})
+      : _errorHandler = onError;
+
+  /// Enqueues [item] to be processed and starts asynchronously processing it
+  /// if a process isn't already running.
+  void add(T item) {
+    _items.add(item);
+
+    // Start up the asynchronous processing if not already running.
+    if (_isProcessing) return;
+    _isProcessing = true;
+
+    _processNextItem().catchError(_errorHandler);
+  }
+
+  /// Removes all remaining items to be processed.
+  void clear() {
+    _items.clear();
+  }
+
+  /// Pulls the next item off [_items] and processes it.
+  ///
+  /// When complete, recursively calls itself to continue processing unless
+  /// the process was cancelled.
+  Future _processNextItem() {
+    var item = _items.removeFirst();
+    return _processor(item).then((_) {
+      if (_items.isNotEmpty) return _processNextItem();
+
+      // We have drained the queue, stop processing and wait until something
+      // has been enqueued.
+      _isProcessing = false;
+    });
+  }
+}
diff --git a/watcher/lib/src/constructable_file_system_event.dart b/watcher/lib/src/constructable_file_system_event.dart
new file mode 100644
index 0000000..d00a1dc
--- /dev/null
+++ b/watcher/lib/src/constructable_file_system_event.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2013, 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 watcher.constructable_file_system_event;
+
+import 'dart:io';
+
+abstract class _ConstructableFileSystemEvent implements FileSystemEvent {
+  final bool isDirectory;
+  final String path;
+  int get type;
+
+  _ConstructableFileSystemEvent(this.path, this.isDirectory);
+}
+
+class ConstructableFileSystemCreateEvent extends _ConstructableFileSystemEvent
+    implements FileSystemCreateEvent {
+  final type = FileSystemEvent.CREATE;
+
+  ConstructableFileSystemCreateEvent(String path, bool isDirectory)
+      : super(path, isDirectory);
+
+  String toString() => "FileSystemCreateEvent('$path')";
+}
+
+class ConstructableFileSystemDeleteEvent extends _ConstructableFileSystemEvent
+    implements FileSystemDeleteEvent {
+  final type = FileSystemEvent.DELETE;
+
+  ConstructableFileSystemDeleteEvent(String path, bool isDirectory)
+      : super(path, isDirectory);
+
+  String toString() => "FileSystemDeleteEvent('$path')";
+}
+
+class ConstructableFileSystemModifyEvent extends _ConstructableFileSystemEvent
+    implements FileSystemModifyEvent {
+  final bool contentChanged;
+  final type = FileSystemEvent.MODIFY;
+
+  ConstructableFileSystemModifyEvent(String path, bool isDirectory,
+      this.contentChanged)
+      : super(path, isDirectory);
+
+  String toString() =>
+      "FileSystemModifyEvent('$path', contentChanged=$contentChanged)";
+}
+
+class ConstructableFileSystemMoveEvent extends _ConstructableFileSystemEvent
+    implements FileSystemMoveEvent {
+  final String destination;
+  final type = FileSystemEvent.MOVE;
+
+  ConstructableFileSystemMoveEvent(String path, bool isDirectory,
+      this.destination)
+      : super(path, isDirectory);
+
+  String toString() => "FileSystemMoveEvent('$path', '$destination')";
+}
diff --git a/watcher/lib/src/directory_watcher.dart b/watcher/lib/src/directory_watcher.dart
new file mode 100644
index 0000000..605eaea
--- /dev/null
+++ b/watcher/lib/src/directory_watcher.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2013, 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 watcher.directory_watcher;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'watch_event.dart';
+import 'directory_watcher/linux.dart';
+import 'directory_watcher/mac_os.dart';
+import 'directory_watcher/windows.dart';
+import 'directory_watcher/polling.dart';
+
+/// Watches the contents of a directory and emits [WatchEvent]s when something
+/// in the directory has changed.
+abstract class DirectoryWatcher {
+  /// The directory whose contents are being monitored.
+  String get directory;
+
+  /// The broadcast [Stream] of events that have occurred to files in
+  /// [directory].
+  ///
+  /// Changes will only be monitored while this stream has subscribers. Any
+  /// file changes that occur during periods when there are no subscribers
+  /// will not be reported the next time a subscriber is added.
+  Stream<WatchEvent> get events;
+
+  /// Whether the watcher is initialized and watching for file changes.
+  ///
+  /// This is true if and only if [ready] is complete.
+  bool get isReady;
+
+  /// A [Future] that completes when the watcher is initialized and watching
+  /// for file changes.
+  ///
+  /// If the watcher is not currently monitoring the directory (because there
+  /// are no subscribers to [events]), this returns a future that isn't
+  /// complete yet. It will complete when a subscriber starts listening and
+  /// the watcher finishes any initialization work it needs to do.
+  ///
+  /// If the watcher is already monitoring, this returns an already complete
+  /// future.
+  Future get ready;
+
+  /// Creates a new [DirectoryWatcher] monitoring [directory].
+  ///
+  /// If a native directory watcher is available for this platform, this will
+  /// use it. Otherwise, it will fall back to a [PollingDirectoryWatcher].
+  ///
+  /// If [_pollingDelay] is passed, it specifies the amount of time the watcher
+  /// will pause between successive polls of the directory contents. Making this
+  /// shorter will give more immediate feedback at the expense of doing more IO
+  /// and higher CPU usage. Defaults to one second. Ignored for non-polling
+  /// watchers.
+  factory DirectoryWatcher(String directory, {Duration pollingDelay}) {
+    if (FileSystemEntity.isWatchSupported) {
+      if (Platform.isLinux) return new LinuxDirectoryWatcher(directory);
+      if (Platform.isMacOS) return new MacOSDirectoryWatcher(directory);
+      if (Platform.isWindows) return new WindowsDirectoryWatcher(directory);
+    }
+    return new PollingDirectoryWatcher(directory, pollingDelay: pollingDelay);
+  }
+}
diff --git a/watcher/lib/src/directory_watcher/linux.dart b/watcher/lib/src/directory_watcher/linux.dart
new file mode 100644
index 0000000..870faa7
--- /dev/null
+++ b/watcher/lib/src/directory_watcher/linux.dart
@@ -0,0 +1,308 @@
+// Copyright (c) 2013, 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 watcher.directory_watcher.linux;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:stack_trace/stack_trace.dart';
+
+import '../utils.dart';
+import '../watch_event.dart';
+import 'resubscribable.dart';
+
+/// Uses the inotify subsystem to watch for filesystem events.
+///
+/// Inotify doesn't suport recursively watching subdirectories, nor does
+/// [Directory.watch] polyfill that functionality. This class polyfills it
+/// instead.
+///
+/// This class also compensates for the non-inotify-specific issues of
+/// [Directory.watch] producing multiple events for a single logical action
+/// (issue 14372) and providing insufficient information about move events
+/// (issue 14424).
+class LinuxDirectoryWatcher extends ResubscribableDirectoryWatcher {
+  LinuxDirectoryWatcher(String directory)
+      : super(directory, () => new _LinuxDirectoryWatcher(directory));
+}
+
+class _LinuxDirectoryWatcher implements ManuallyClosedDirectoryWatcher {
+  final String directory;
+
+  Stream<WatchEvent> get events => _eventsController.stream;
+  final _eventsController = new StreamController<WatchEvent>.broadcast();
+
+  bool get isReady => _readyCompleter.isCompleted;
+
+  Future get ready => _readyCompleter.future;
+  final _readyCompleter = new Completer();
+
+  /// The last known state for each entry in this directory.
+  ///
+  /// The keys in this map are the paths to the directory entries; the values
+  /// are [_EntryState]s indicating whether the entries are files or
+  /// directories.
+  final _entries = new Map<String, _EntryState>();
+
+  /// The watchers for subdirectories of [directory].
+  final _subWatchers = new Map<String, _LinuxDirectoryWatcher>();
+
+  /// A set of all subscriptions that this watcher subscribes to.
+  ///
+  /// These are gathered together so that they may all be canceled when the
+  /// watcher is closed.
+  final _subscriptions = new Set<StreamSubscription>();
+
+  _LinuxDirectoryWatcher(String directory)
+      : directory = directory {
+    // Batch the inotify changes together so that we can dedup events.
+    var innerStream = Chain.track(new Directory(directory).watch())
+        .transform(new BatchedStreamTransformer<FileSystemEvent>());
+    _listen(innerStream, _onBatch,
+        onError: _eventsController.addError,
+        onDone: _onDone);
+
+    _listen(Chain.track(new Directory(directory).list()), (entity) {
+      _entries[entity.path] = new _EntryState(entity is Directory);
+      if (entity is! Directory) return;
+      _watchSubdir(entity.path);
+    }, onError: (error, stackTrace) {
+      _eventsController.addError(error, stackTrace);
+      close();
+    }, onDone: () {
+      _waitUntilReady().then((_) => _readyCompleter.complete());
+    }, cancelOnError: true);
+  }
+
+  /// Returns a [Future] that completes once all the subdirectory watchers are
+  /// fully initialized.
+  Future _waitUntilReady() {
+    return Future.wait(_subWatchers.values.map((watcher) => watcher.ready))
+        .then((_) {
+      if (_subWatchers.values.every((watcher) => watcher.isReady)) return null;
+      return _waitUntilReady();
+    });
+  }
+
+  void close() {
+    for (var subscription in _subscriptions) {
+      subscription.cancel();
+    }
+    for (var watcher in _subWatchers.values) {
+      watcher.close();
+    }
+
+    _subWatchers.clear();
+    _subscriptions.clear();
+    _eventsController.close();
+  }
+
+  /// Returns all files (not directories) that this watcher knows of are
+  /// recursively in the watched directory.
+  Set<String> get _allFiles {
+    var files = new Set<String>();
+    _getAllFiles(files);
+    return files;
+  }
+
+  /// Helper function for [_allFiles].
+  ///
+  /// Adds all files that this watcher knows of to [files].
+  void _getAllFiles(Set<String> files) {
+    files.addAll(_entries.keys
+        .where((path) => _entries[path] == _EntryState.FILE).toSet());
+    for (var watcher in _subWatchers.values) {
+      watcher._getAllFiles(files);
+    }
+  }
+
+  /// Watch a subdirectory of [directory] for changes.
+  ///
+  /// If the subdirectory was added after [this] began emitting events, its
+  /// contents will be emitted as ADD events.
+  void _watchSubdir(String path) {
+    if (_subWatchers.containsKey(path)) return;
+    var watcher = new _LinuxDirectoryWatcher(path);
+    _subWatchers[path] = watcher;
+
+    // TODO(nweiz): Catch any errors here that indicate that the directory in
+    // question doesn't exist and silently stop watching it instead of
+    // propagating the errors.
+    _listen(watcher.events, (event) {
+      if (isReady) _eventsController.add(event);
+    }, onError: (error, stackTrace) {
+      _eventsController.addError(error, stackTrace);
+      close();
+    }, onDone: () {
+      if (_subWatchers[path] == watcher) _subWatchers.remove(path);
+
+      // It's possible that a directory was removed and recreated very quickly.
+      // If so, make sure we're still watching it.
+      if (new Directory(path).existsSync()) _watchSubdir(path);
+    });
+
+    // TODO(nweiz): Right now it's possible for the watcher to emit an event for
+    // a file before the directory list is complete. This could lead to the user
+    // seeing a MODIFY or REMOVE event for a file before they see an ADD event,
+    // which is bad. We should handle that.
+    //
+    // One possibility is to provide a general means (e.g.
+    // `DirectoryWatcher.eventsAndExistingFiles`) to tell a watcher to emit
+    // events for all the files that already exist. This would be useful for
+    // top-level clients such as barback as well, and could be implemented with
+    // a wrapper similar to how listening/canceling works now.
+
+    // If a directory is added after we're finished with the initial scan, emit
+    // an event for each entry in it. This gives the user consistently gets an
+    // event for every new file.
+    watcher.ready.then((_) {
+      if (!isReady || _eventsController.isClosed) return;
+      _listen(Chain.track(new Directory(path).list(recursive: true)), (entry) {
+        if (entry is Directory) return;
+        _eventsController.add(new WatchEvent(ChangeType.ADD, entry.path));
+      }, onError: (error, stackTrace) {
+        // Ignore an exception caused by the dir not existing. It's fine if it
+        // was added and then quickly removed.
+        if (error is FileSystemException) return;
+
+        _eventsController.addError(error, stackTrace);
+        close();
+      }, cancelOnError: true);
+    });
+  }
+
+  /// The callback that's run when a batch of changes comes in.
+  void _onBatch(List<FileSystemEvent> batch) {
+    var changedEntries = new Set<String>();
+    var oldEntries = new Map.from(_entries);
+
+    // inotify event batches are ordered by occurrence, so we treat them as a
+    // log of what happened to a file.
+    for (var event in batch) {
+      // If the watched directory is deleted or moved, we'll get a deletion
+      // event for it. Ignore it; we handle closing [this] when the underlying
+      // stream is closed.
+      if (event.path == directory) continue;
+
+      changedEntries.add(event.path);
+
+      if (event is FileSystemMoveEvent) {
+        changedEntries.add(event.destination);
+        _changeEntryState(event.path, ChangeType.REMOVE, event.isDirectory);
+        _changeEntryState(event.destination, ChangeType.ADD, event.isDirectory);
+      } else {
+        _changeEntryState(event.path, _changeTypeFor(event), event.isDirectory);
+      }
+    }
+
+    for (var path in changedEntries) {
+      emitEvent(ChangeType type) {
+        if (isReady) _eventsController.add(new WatchEvent(type, path));
+      }
+
+      var oldState = oldEntries[path];
+      var newState = _entries[path];
+
+      if (oldState != _EntryState.FILE && newState == _EntryState.FILE) {
+        emitEvent(ChangeType.ADD);
+      } else if (oldState == _EntryState.FILE && newState == _EntryState.FILE) {
+        emitEvent(ChangeType.MODIFY);
+      } else if (oldState == _EntryState.FILE && newState != _EntryState.FILE) {
+        emitEvent(ChangeType.REMOVE);
+      }
+
+      if (oldState == _EntryState.DIRECTORY) {
+        var watcher = _subWatchers.remove(path);
+        if (watcher == null) continue;
+        for (var path in watcher._allFiles) {
+          _eventsController.add(new WatchEvent(ChangeType.REMOVE, path));
+        }
+        watcher.close();
+      }
+
+      if (newState == _EntryState.DIRECTORY) _watchSubdir(path);
+    }
+  }
+
+  /// Changes the known state of the entry at [path] based on [change] and
+  /// [isDir].
+  void _changeEntryState(String path, ChangeType change, bool isDir) {
+    if (change == ChangeType.ADD || change == ChangeType.MODIFY) {
+      _entries[path] = new _EntryState(isDir);
+    } else {
+      assert(change == ChangeType.REMOVE);
+      _entries.remove(path);
+    }
+  }
+
+  /// Determines the [ChangeType] associated with [event].
+  ChangeType _changeTypeFor(FileSystemEvent event) {
+    if (event is FileSystemDeleteEvent) return ChangeType.REMOVE;
+    if (event is FileSystemCreateEvent) return ChangeType.ADD;
+
+    assert(event is FileSystemModifyEvent);
+    return ChangeType.MODIFY;
+  }
+
+  /// Handles the underlying event stream closing, indicating that the directory
+  /// being watched was removed.
+  void _onDone() {
+    // Most of the time when a directory is removed, its contents will get
+    // individual REMOVE events before the watch stream is closed -- in that
+    // case, [_entries] will be empty here. However, if the directory's removal
+    // is caused by a MOVE, we need to manually emit events.
+    if (isReady) {
+      _entries.forEach((path, state) {
+        if (state == _EntryState.DIRECTORY) return;
+        _eventsController.add(new WatchEvent(ChangeType.REMOVE, path));
+      });
+    }
+
+    // The parent directory often gets a close event before the subdirectories
+    // are done emitting events. We wait for them to finish before we close
+    // [events] so that we can be sure to emit a remove event for every file
+    // that used to exist.
+    Future.wait(_subWatchers.values.map((watcher) {
+      try {
+        return watcher.events.toList();
+      } on StateError catch (_) {
+        // It's possible that [watcher.events] is closed but the onDone event
+        // hasn't reached us yet. It's fine if so.
+        return new Future.value();
+      }
+    })).then((_) => close());
+  }
+
+  /// Like [Stream.listen], but automatically adds the subscription to
+  /// [_subscriptions] so that it can be canceled when [close] is called.
+  void _listen(Stream stream, void onData(event), {Function onError,
+      void onDone(), bool cancelOnError}) {
+    var subscription;
+    subscription = stream.listen(onData, onError: onError, onDone: () {
+      _subscriptions.remove(subscription);
+      if (onDone != null) onDone();
+    }, cancelOnError: cancelOnError);
+    _subscriptions.add(subscription);
+  }
+}
+
+/// An enum for the possible states of entries in a watched directory.
+class _EntryState {
+  final String _name;
+
+  /// The entry is a file.
+  static const FILE = const _EntryState._("file");
+
+  /// The entry is a directory.
+  static const DIRECTORY = const _EntryState._("directory");
+
+  const _EntryState._(this._name);
+
+  /// Returns [DIRECTORY] if [isDir] is true, and [FILE] otherwise.
+  factory _EntryState(bool isDir) =>
+      isDir ? _EntryState.DIRECTORY : _EntryState.FILE;
+
+  String toString() => _name;
+}
diff --git a/watcher/lib/src/directory_watcher/mac_os.dart b/watcher/lib/src/directory_watcher/mac_os.dart
new file mode 100644
index 0000000..c242c75
--- /dev/null
+++ b/watcher/lib/src/directory_watcher/mac_os.dart
@@ -0,0 +1,508 @@
+// Copyright (c) 2013, 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 watcher.directory_watcher.mac_os;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:path/path.dart' as p;
+import 'package:stack_trace/stack_trace.dart';
+
+import '../constructable_file_system_event.dart';
+import '../path_set.dart';
+import '../utils.dart';
+import '../watch_event.dart';
+import 'resubscribable.dart';
+
+/// Uses the FSEvents subsystem to watch for filesystem events.
+///
+/// FSEvents has two main idiosyncrasies that this class works around. First, it
+/// will occasionally report events that occurred before the filesystem watch
+/// was initiated. Second, if multiple events happen to the same file in close
+/// succession, it won't report them in the order they occurred. See issue
+/// 14373.
+///
+/// This also works around issues 16003 and 14849 in the implementation of
+/// [Directory.watch].
+class MacOSDirectoryWatcher extends ResubscribableDirectoryWatcher {
+  // TODO(nweiz): remove these when issue 15042 is fixed.
+  static var logDebugInfo = false;
+  static var _count = 0;
+
+  MacOSDirectoryWatcher(String directory)
+      : super(directory, () => new _MacOSDirectoryWatcher(directory, _count++));
+}
+
+class _MacOSDirectoryWatcher implements ManuallyClosedDirectoryWatcher {
+  // TODO(nweiz): remove these when issue 15042 is fixed.
+  static var _count = 0;
+  final String _id;
+
+  final String directory;
+
+  Stream<WatchEvent> get events => _eventsController.stream;
+  final _eventsController = new StreamController<WatchEvent>.broadcast();
+
+  bool get isReady => _readyCompleter.isCompleted;
+
+  Future get ready => _readyCompleter.future;
+  final _readyCompleter = new Completer();
+
+  /// The set of files that are known to exist recursively within the watched
+  /// directory.
+  ///
+  /// The state of files on the filesystem is compared against this to determine
+  /// the real change that occurred when working around issue 14373. This is
+  /// also used to emit REMOVE events when subdirectories are moved out of the
+  /// watched directory.
+  final PathSet _files;
+
+  /// The subscription to the stream returned by [Directory.watch].
+  ///
+  /// This is separate from [_subscriptions] because this stream occasionally
+  /// needs to be resubscribed in order to work around issue 14849.
+  StreamSubscription<FileSystemEvent> _watchSubscription;
+
+  /// The subscription to the [Directory.list] call for the initial listing of
+  /// the directory to determine its initial state.
+  StreamSubscription<FileSystemEntity> _initialListSubscription;
+
+  /// The subscriptions to [Directory.list] calls for listing the contents of a
+  /// subdirectory that was moved into the watched directory.
+  final _listSubscriptions = new Set<StreamSubscription<FileSystemEntity>>();
+
+  /// The timer for tracking how long we wait for an initial batch of bogus
+  /// events (see issue 14373).
+  Timer _bogusEventTimer;
+
+  _MacOSDirectoryWatcher(String directory, int parentId)
+      : directory = directory,
+        _files = new PathSet(directory),
+        _id = "$parentId/${_count++}" {
+    _startWatch();
+
+    // Before we're ready to emit events, wait for [_listDir] to complete and
+    // for enough time to elapse that if bogus events (issue 14373) would be
+    // emitted, they will be.
+    //
+    // If we do receive a batch of events, [_onBatch] will ensure that these
+    // futures don't fire and that the directory is re-listed.
+    Future.wait([
+      _listDir().then((_) {
+        if (MacOSDirectoryWatcher.logDebugInfo) {
+          print("[$_id] finished initial directory list");
+        }
+      }),
+      _waitForBogusEvents()
+    ]).then((_) {
+      if (MacOSDirectoryWatcher.logDebugInfo) {
+        print("[$_id] watcher is ready, known files:");
+        for (var file in _files.toSet()) {
+          print("[$_id]   ${p.relative(file, from: directory)}");
+        }
+      }
+      _readyCompleter.complete();
+    });
+  }
+
+  void close() {
+    if (MacOSDirectoryWatcher.logDebugInfo) {
+      print("[$_id] watcher is closed\n${new Chain.current().terse}");
+    }
+    if (_watchSubscription != null) _watchSubscription.cancel();
+    if (_initialListSubscription != null) _initialListSubscription.cancel();
+    _watchSubscription = null;
+    _initialListSubscription = null;
+
+    for (var subscription in _listSubscriptions) {
+      subscription.cancel();
+    }
+    _listSubscriptions.clear();
+
+    _eventsController.close();
+  }
+
+  /// The callback that's run when [Directory.watch] emits a batch of events.
+  void _onBatch(List<FileSystemEvent> batch) {
+    if (MacOSDirectoryWatcher.logDebugInfo) {
+      print("[$_id] ======== batch:");
+      for (var event in batch) {
+        print("[$_id]   ${_formatEvent(event)}");
+      }
+
+      print("[$_id] known files:");
+      for (var file in _files.toSet()) {
+        print("[$_id]   ${p.relative(file, from: directory)}");
+      }
+    }
+
+    // If we get a batch of events before we're ready to begin emitting events,
+    // it's probable that it's a batch of pre-watcher events (see issue 14373).
+    // Ignore those events and re-list the directory.
+    if (!isReady) {
+      if (MacOSDirectoryWatcher.logDebugInfo) {
+        print("[$_id] not ready to emit events, re-listing directory");
+      }
+
+      // Cancel the timer because bogus events only occur in the first batch, so
+      // we can fire [ready] as soon as we're done listing the directory.
+      _bogusEventTimer.cancel();
+      _listDir().then((_) {
+        if (MacOSDirectoryWatcher.logDebugInfo) {
+          print("[$_id] watcher is ready, known files:");
+          for (var file in _files.toSet()) {
+            print("[$_id]   ${p.relative(file, from: directory)}");
+          }
+        }
+        _readyCompleter.complete();
+      });
+      return;
+    }
+
+    _sortEvents(batch).forEach((path, events) {
+      var relativePath = p.relative(path, from: directory);
+      if (MacOSDirectoryWatcher.logDebugInfo) {
+        print("[$_id] events for $relativePath:");
+        for (var event in events) {
+          print("[$_id]   ${_formatEvent(event)}");
+        }
+      }
+
+      var canonicalEvent = _canonicalEvent(events);
+      events = canonicalEvent == null ?
+          _eventsBasedOnFileSystem(path) : [canonicalEvent];
+      if (MacOSDirectoryWatcher.logDebugInfo) {
+        print("[$_id] canonical event for $relativePath: "
+            "${_formatEvent(canonicalEvent)}");
+        print("[$_id] actionable events for $relativePath: "
+            "${events.map(_formatEvent)}");
+      }
+
+      for (var event in events) {
+        if (event is FileSystemCreateEvent) {
+          if (!event.isDirectory) {
+            // If we already know about the file, treat it like a modification.
+            // This can happen if a file is copied on top of an existing one.
+            // We'll see an ADD event for the latter file when from the user's
+            // perspective, the file's contents just changed.
+            var type = _files.contains(path)
+                ? ChangeType.MODIFY
+                : ChangeType.ADD;
+
+            _emitEvent(type, path);
+            _files.add(path);
+            continue;
+          }
+
+          if (_files.containsDir(path)) continue;
+
+          var subscription;
+          subscription = Chain.track(new Directory(path).list(recursive: true))
+              .listen((entity) {
+            if (entity is Directory) return;
+            if (_files.contains(path)) return;
+
+            _emitEvent(ChangeType.ADD, entity.path);
+            _files.add(entity.path);
+          }, onError: (e, stackTrace) {
+            if (MacOSDirectoryWatcher.logDebugInfo) {
+              print("[$_id] got error listing $relativePath: $e");
+            }
+            _emitError(e, stackTrace);
+          }, onDone: () {
+            _listSubscriptions.remove(subscription);
+          }, cancelOnError: true);
+          _listSubscriptions.add(subscription);
+        } else if (event is FileSystemModifyEvent) {
+          assert(!event.isDirectory);
+          _emitEvent(ChangeType.MODIFY, path);
+        } else {
+          assert(event is FileSystemDeleteEvent);
+          for (var removedPath in _files.remove(path)) {
+            _emitEvent(ChangeType.REMOVE, removedPath);
+          }
+        }
+      }
+    });
+
+    if (MacOSDirectoryWatcher.logDebugInfo) {
+      print("[$_id] ======== batch complete");
+    }
+  }
+
+  /// Sort all the events in a batch into sets based on their path.
+  ///
+  /// A single input event may result in multiple events in the returned map;
+  /// for example, a MOVE event becomes a DELETE event for the source and a
+  /// CREATE event for the destination.
+  ///
+  /// The returned events won't contain any [FileSystemMoveEvent]s, nor will it
+  /// contain any events relating to [directory].
+  Map<String, Set<FileSystemEvent>> _sortEvents(List<FileSystemEvent> batch) {
+    var eventsForPaths = {};
+
+    // FSEvents can report past events, including events on the root directory
+    // such as it being created. We want to ignore these. If the directory is
+    // really deleted, that's handled by [_onDone].
+    batch = batch.where((event) => event.path != directory).toList();
+
+    // Events within directories that already have events are superfluous; the
+    // directory's full contents will be examined anyway, so we ignore such
+    // events. Emitting them could cause useless or out-of-order events.
+    var directories = unionAll(batch.map((event) {
+      if (!event.isDirectory) return new Set();
+      if (event is! FileSystemMoveEvent) return new Set.from([event.path]);
+      return new Set.from([event.path, event.destination]);
+    }));
+
+    isInModifiedDirectory(path) =>
+        directories.any((dir) => path != dir && path.startsWith(dir));
+
+    addEvent(path, event) {
+      if (isInModifiedDirectory(path)) return;
+      var set = eventsForPaths.putIfAbsent(path, () => new Set());
+      set.add(event);
+    }
+
+    for (var event in batch) {
+      // The Mac OS watcher doesn't emit move events. See issue 14806.
+      assert(event is! FileSystemMoveEvent);
+      addEvent(event.path, event);
+    }
+
+    return eventsForPaths;
+  }
+
+  /// Returns the canonical event from a batch of events on the same path, if
+  /// one exists.
+  ///
+  /// If [batch] doesn't contain any contradictory events (e.g. DELETE and
+  /// CREATE, or events with different values for [isDirectory]), this returns a
+  /// single event that describes what happened to the path in question.
+  ///
+  /// If [batch] does contain contradictory events, this returns `null` to
+  /// indicate that the state of the path on the filesystem should be checked to
+  /// determine what occurred.
+  FileSystemEvent _canonicalEvent(Set<FileSystemEvent> batch) {
+    // An empty batch indicates that we've learned earlier that the batch is
+    // contradictory (e.g. because of a move).
+    if (batch.isEmpty) return null;
+
+    var type = batch.first.type;
+    var isDir = batch.first.isDirectory;
+    var hadModifyEvent = false;
+
+    for (var event in batch.skip(1)) {
+      // If one event reports that the file is a directory and another event
+      // doesn't, that's a contradiction.
+      if (isDir != event.isDirectory) return null;
+
+      // Modify events don't contradict either CREATE or REMOVE events. We can
+      // safely assume the file was modified after a CREATE or before the
+      // REMOVE; otherwise there will also be a REMOVE or CREATE event
+      // (respectively) that will be contradictory.
+      if (event is FileSystemModifyEvent) {
+        hadModifyEvent = true;
+        continue;
+      }
+      assert(event is FileSystemCreateEvent || event is FileSystemDeleteEvent);
+
+      // If we previously thought this was a MODIFY, we now consider it to be a
+      // CREATE or REMOVE event. This is safe for the same reason as above.
+      if (type == FileSystemEvent.MODIFY) {
+        type = event.type;
+        continue;
+      }
+
+      // A CREATE event contradicts a REMOVE event and vice versa.
+      assert(type == FileSystemEvent.CREATE || type == FileSystemEvent.DELETE);
+      if (type != event.type) return null;
+    }
+
+    // If we got a CREATE event for a file we already knew about, that comes
+    // from FSEvents reporting an add that happened prior to the watch
+    // beginning. If we also received a MODIFY event, we want to report that,
+    // but not the CREATE.
+    if (type == FileSystemEvent.CREATE && hadModifyEvent &&
+        _files.contains(batch.first.path)) {
+      type = FileSystemEvent.MODIFY;
+    }
+
+    switch (type) {
+      case FileSystemEvent.CREATE:
+        // Issue 16003 means that a CREATE event for a directory can indicate
+        // that the directory was moved and then re-created.
+        // [_eventsBasedOnFileSystem] will handle this correctly by producing a
+        // DELETE event followed by a CREATE event if the directory exists.
+        if (isDir) return null;
+        return new ConstructableFileSystemCreateEvent(batch.first.path, false);
+      case FileSystemEvent.DELETE:
+        return new ConstructableFileSystemDeleteEvent(batch.first.path, isDir);
+      case FileSystemEvent.MODIFY:
+        return new ConstructableFileSystemModifyEvent(
+            batch.first.path, isDir, false);
+      default: assert(false);
+    }
+  }
+
+  /// Returns one or more events that describe the change between the last known
+  /// state of [path] and its current state on the filesystem.
+  ///
+  /// This returns a list whose order should be reflected in the events emitted
+  /// to the user, unlike the batched events from [Directory.watch]. The
+  /// returned list may be empty, indicating that no changes occurred to [path]
+  /// (probably indicating that it was created and then immediately deleted).
+  List<FileSystemEvent> _eventsBasedOnFileSystem(String path) {
+    var fileExisted = _files.contains(path);
+    var dirExisted = _files.containsDir(path);
+    var fileExists = new File(path).existsSync();
+    var dirExists = new Directory(path).existsSync();
+
+    if (MacOSDirectoryWatcher.logDebugInfo) {
+      print("[$_id] checking file system for "
+          "${p.relative(path, from: directory)}");
+      print("[$_id]   file existed: $fileExisted");
+      print("[$_id]   dir existed: $dirExisted");
+      print("[$_id]   file exists: $fileExists");
+      print("[$_id]   dir exists: $dirExists");
+    }
+
+    var events = [];
+    if (fileExisted) {
+      if (fileExists) {
+        events.add(new ConstructableFileSystemModifyEvent(path, false, false));
+      } else {
+        events.add(new ConstructableFileSystemDeleteEvent(path, false));
+      }
+    } else if (dirExisted) {
+      if (dirExists) {
+        // If we got contradictory events for a directory that used to exist and
+        // still exists, we need to rescan the whole thing in case it was
+        // replaced with a different directory.
+        events.add(new ConstructableFileSystemDeleteEvent(path, true));
+        events.add(new ConstructableFileSystemCreateEvent(path, true));
+      } else {
+        events.add(new ConstructableFileSystemDeleteEvent(path, true));
+      }
+    }
+
+    if (!fileExisted && fileExists) {
+      events.add(new ConstructableFileSystemCreateEvent(path, false));
+    } else if (!dirExisted && dirExists) {
+      events.add(new ConstructableFileSystemCreateEvent(path, true));
+    }
+
+    return events;
+  }
+
+  /// The callback that's run when the [Directory.watch] stream is closed.
+  void _onDone() {
+    if (MacOSDirectoryWatcher.logDebugInfo) print("[$_id] stream closed");
+
+    _watchSubscription = null;
+
+    // If the directory still exists and we're still expecting bogus events,
+    // this is probably issue 14849 rather than a real close event. We should
+    // just restart the watcher.
+    if (!isReady && new Directory(directory).existsSync()) {
+      if (MacOSDirectoryWatcher.logDebugInfo) {
+        print("[$_id] fake closure (issue 14849), re-opening stream");
+      }
+      _startWatch();
+      return;
+    }
+
+    // FSEvents can fail to report the contents of the directory being removed
+    // when the directory itself is removed, so we need to manually mark the
+    // files as removed.
+    for (var file in _files.toSet()) {
+      _emitEvent(ChangeType.REMOVE, file);
+    }
+    _files.clear();
+    close();
+  }
+
+  /// Start or restart the underlying [Directory.watch] stream.
+  void _startWatch() {
+    // Batch the FSEvent changes together so that we can dedup events.
+    var innerStream =
+        Chain.track(new Directory(directory).watch(recursive: true))
+        .transform(new BatchedStreamTransformer<FileSystemEvent>());
+    _watchSubscription = innerStream.listen(_onBatch,
+        onError: _eventsController.addError,
+        onDone: _onDone);
+  }
+
+  /// Starts or restarts listing the watched directory to get an initial picture
+  /// of its state.
+  Future _listDir() {
+    assert(!isReady);
+    if (_initialListSubscription != null) _initialListSubscription.cancel();
+
+    _files.clear();
+    var completer = new Completer();
+    var stream = Chain.track(new Directory(directory).list(recursive: true));
+    _initialListSubscription = stream.listen((entity) {
+      if (entity is! Directory) _files.add(entity.path);
+    },
+        onError: _emitError,
+        onDone: completer.complete,
+        cancelOnError: true);
+    return completer.future;
+  }
+
+  /// Wait 200ms for a batch of bogus events (issue 14373) to come in.
+  ///
+  /// 200ms is short in terms of human interaction, but longer than any Mac OS
+  /// watcher tests take on the bots, so it should be safe to assume that any
+  /// bogus events will be signaled in that time frame.
+  Future _waitForBogusEvents() {
+    var completer = new Completer();
+    _bogusEventTimer = new Timer(
+        new Duration(milliseconds: 200),
+        completer.complete);
+    return completer.future;
+  }
+
+  /// Emit an event with the given [type] and [path].
+  void _emitEvent(ChangeType type, String path) {
+    if (!isReady) return;
+
+    if (MacOSDirectoryWatcher.logDebugInfo) {
+      print("[$_id] emitting $type ${p.relative(path, from: directory)}");
+    }
+
+    _eventsController.add(new WatchEvent(type, path));
+  }
+
+  /// Emit an error, then close the watcher.
+  void _emitError(error, StackTrace stackTrace) {
+    if (MacOSDirectoryWatcher.logDebugInfo) {
+      print("[$_id] emitting error: $error\n" +
+          "${new Chain.forTrace(stackTrace).terse}");
+    }
+    _eventsController.addError(error, stackTrace);
+    close();
+  }
+
+  // TODO(nweiz): remove this when issue 15042 is fixed.
+  /// Return a human-friendly string representation of [event].
+  String _formatEvent(FileSystemEvent event) {
+    if (event == null) return 'null';
+
+    var path = p.relative(event.path, from: directory);
+    var type = event.isDirectory ? 'directory' : 'file';
+    if (event is FileSystemCreateEvent) {
+      return "create $type $path";
+    } else if (event is FileSystemDeleteEvent) {
+      return "delete $type $path";
+    } else if (event is FileSystemModifyEvent) {
+      return "modify $type $path";
+    } else if (event is FileSystemMoveEvent) {
+      return "move $type $path to "
+          "${p.relative(event.destination, from: directory)}";
+    }
+  }
+}
diff --git a/watcher/lib/src/directory_watcher/polling.dart b/watcher/lib/src/directory_watcher/polling.dart
new file mode 100644
index 0000000..12a3245
--- /dev/null
+++ b/watcher/lib/src/directory_watcher/polling.dart
@@ -0,0 +1,178 @@
+// Copyright (c) 2013, 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 watcher.directory_watcher.polling;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:stack_trace/stack_trace.dart';
+
+import '../async_queue.dart';
+import '../stat.dart';
+import '../utils.dart';
+import '../watch_event.dart';
+import 'resubscribable.dart';
+
+/// Periodically polls a directory for changes.
+class PollingDirectoryWatcher extends ResubscribableDirectoryWatcher {
+  /// Creates a new polling watcher monitoring [directory].
+  ///
+  /// If [_pollingDelay] is passed, it specifies the amount of time the watcher
+  /// will pause between successive polls of the directory contents. Making this
+  /// shorter will give more immediate feedback at the expense of doing more IO
+  /// and higher CPU usage. Defaults to one second.
+  PollingDirectoryWatcher(String directory, {Duration pollingDelay})
+      : super(directory, () {
+        return new _PollingDirectoryWatcher(directory,
+            pollingDelay != null ? pollingDelay : new Duration(seconds: 1));
+      });
+}
+
+class _PollingDirectoryWatcher implements ManuallyClosedDirectoryWatcher {
+  final String directory;
+
+  Stream<WatchEvent> get events => _events.stream;
+  final _events = new StreamController<WatchEvent>.broadcast();
+
+  bool get isReady => _ready.isCompleted;
+
+  Future get ready => _ready.future;
+  final _ready = new Completer();
+
+  /// The amount of time the watcher pauses between successive polls of the
+  /// directory contents.
+  final Duration _pollingDelay;
+
+  /// The previous modification times of the files in the directory.
+  ///
+  /// Used to tell which files have been modified.
+  final _lastModifieds = new Map<String, DateTime>();
+
+  /// The subscription used while [directory] is being listed.
+  ///
+  /// Will be `null` if a list is not currently happening.
+  StreamSubscription<FileSystemEntity> _listSubscription;
+
+  /// The queue of files waiting to be processed to see if they have been
+  /// modified.
+  ///
+  /// Processing a file is asynchronous, as is listing the directory, so the
+  /// queue exists to let each of those proceed at their own rate. The lister
+  /// will enqueue files as quickly as it can. Meanwhile, files are dequeued
+  /// and processed sequentially.
+  AsyncQueue<String> _filesToProcess;
+
+  /// The set of files that have been seen in the current directory listing.
+  ///
+  /// Used to tell which files have been removed: files that are in [_statuses]
+  /// but not in here when a poll completes have been removed.
+  final _polledFiles = new Set<String>();
+
+  _PollingDirectoryWatcher(this.directory, this._pollingDelay) {
+    _filesToProcess = new AsyncQueue<String>(_processFile,
+        onError: (e, stackTrace) {
+      if (!_events.isClosed) _events.addError(e, stackTrace);
+    });
+
+    _poll();
+  }
+
+  void close() {
+    _events.close();
+
+    // If we're in the middle of listing the directory, stop.
+    if (_listSubscription != null) _listSubscription.cancel();
+
+    // Don't process any remaining files.
+    _filesToProcess.clear();
+    _polledFiles.clear();
+    _lastModifieds.clear();
+  }
+
+  /// Scans the contents of the directory once to see which files have been
+  /// added, removed, and modified.
+  void _poll() {
+    _filesToProcess.clear();
+    _polledFiles.clear();
+
+    endListing() {
+      assert(!_events.isClosed);
+      _listSubscription = null;
+
+      // Null tells the queue consumer that we're done listing.
+      _filesToProcess.add(null);
+    }
+
+    var stream = Chain.track(new Directory(directory).list(recursive: true));
+    _listSubscription = stream.listen((entity) {
+      assert(!_events.isClosed);
+
+      if (entity is! File) return;
+      _filesToProcess.add(entity.path);
+    }, onError: (error, stackTrace) {
+      if (!isDirectoryNotFoundException(error)) {
+        // It's some unknown error. Pipe it over to the event stream so the
+        // user can see it.
+        _events.addError(error, stackTrace);
+      }
+
+      // When an error occurs, we end the listing normally, which has the
+      // desired effect of marking all files that were in the directory as
+      // being removed.
+      endListing();
+    }, onDone: endListing, cancelOnError: true);
+  }
+
+  /// Processes [file] to determine if it has been modified since the last
+  /// time it was scanned.
+  Future _processFile(String file) {
+    // `null` is the sentinel which means the directory listing is complete.
+    if (file == null) return _completePoll();
+
+    return getModificationTime(file).then((modified) {
+      if (_events.isClosed) return null;
+
+      var lastModified = _lastModifieds[file];
+
+      // If its modification time hasn't changed, assume the file is unchanged.
+      if (lastModified != null && lastModified == modified) {
+        // The file is still here.
+        _polledFiles.add(file);
+        return null;
+      }
+
+      if (_events.isClosed) return null;
+
+      _lastModifieds[file] = modified;
+      _polledFiles.add(file);
+
+      // Only notify if we're ready to emit events.
+      if (!isReady) return null;
+
+      var type = lastModified == null ? ChangeType.ADD : ChangeType.MODIFY;
+      _events.add(new WatchEvent(type, file));
+    });
+  }
+
+  /// After the directory listing is complete, this determines which files were
+  /// removed and then restarts the next poll.
+  Future _completePoll() {
+    // Any files that were not seen in the last poll but that we have a
+    // status for must have been removed.
+    var removedFiles = _lastModifieds.keys.toSet().difference(_polledFiles);
+    for (var removed in removedFiles) {
+      if (isReady) _events.add(new WatchEvent(ChangeType.REMOVE, removed));
+      _lastModifieds.remove(removed);
+    }
+
+    if (!isReady) _ready.complete();
+
+    // Wait and then poll again.
+    return new Future.delayed(_pollingDelay).then((_) {
+      if (_events.isClosed) return;
+      _poll();
+    });
+  }
+}
diff --git a/watcher/lib/src/directory_watcher/resubscribable.dart b/watcher/lib/src/directory_watcher/resubscribable.dart
new file mode 100644
index 0000000..7d99fc0
--- /dev/null
+++ b/watcher/lib/src/directory_watcher/resubscribable.dart
@@ -0,0 +1,77 @@
+// Copyright (c) 2013, 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 watcher.directory_watcher.resubscribable;
+
+import 'dart:async';
+
+import '../directory_watcher.dart';
+import '../watch_event.dart';
+
+typedef ManuallyClosedDirectoryWatcher WatcherFactory();
+
+/// A wrapper for [ManuallyClosedDirectoryWatcher] that encapsulates support for
+/// closing the watcher when it has no subscribers and re-opening it when it's
+/// re-subscribed.
+///
+/// It's simpler to implement watchers without worrying about this behavior.
+/// This class wraps a watcher class which can be written with the simplifying
+/// assumption that it can continue emitting events until an explicit `close`
+/// method is called, at which point it will cease emitting events entirely. The
+/// [ManuallyClosedDirectoryWatcher] interface is used for these watchers.
+///
+/// This would be more cleanly implemented as a function that takes a class and
+/// emits a new class, but Dart doesn't support that sort of thing. Instead it
+/// takes a factory function that produces instances of the inner class.
+abstract class ResubscribableDirectoryWatcher implements DirectoryWatcher {
+  /// The factory function that produces instances of the inner class.
+  final WatcherFactory _factory;
+
+  final String directory;
+
+  Stream<WatchEvent> get events => _eventsController.stream;
+  StreamController<WatchEvent> _eventsController;
+
+  bool get isReady => _readyCompleter.isCompleted;
+
+  Future get ready => _readyCompleter.future;
+  var _readyCompleter = new Completer();
+
+  /// Creates a new [ResubscribableDirectoryWatcher] wrapping the watchers
+  /// emitted by [_factory].
+  ResubscribableDirectoryWatcher(this.directory, this._factory) {
+    var watcher;
+    var subscription;
+
+    _eventsController = new StreamController<WatchEvent>.broadcast(
+        onListen: () {
+      watcher = _factory();
+      subscription = watcher.events.listen(_eventsController.add,
+          onError: _eventsController.addError,
+          onDone: _eventsController.close);
+
+      // It's important that we complete the value of [_readyCompleter] at the
+      // time [onListen] is called, as opposed to the value when [watcher.ready]
+      // fires. A new completer may be created by that time.
+      watcher.ready.then(_readyCompleter.complete);
+    }, onCancel: () {
+      // Cancel the subscription before closing the watcher so that the
+      // watcher's `onDone` event doesn't close [events].
+      subscription.cancel();
+      watcher.close();
+      _readyCompleter = new Completer();
+    }, sync: true);
+  }
+}
+
+/// An interface for watchers with an explicit, manual [close] method.
+///
+/// See [ResubscribableDirectoryWatcher].
+abstract class ManuallyClosedDirectoryWatcher implements DirectoryWatcher {
+  /// Closes the watcher.
+  ///
+  /// Subclasses should close their [events] stream and release any internal
+  /// resources.
+  void close();
+}
diff --git a/watcher/lib/src/directory_watcher/windows.dart b/watcher/lib/src/directory_watcher/windows.dart
new file mode 100644
index 0000000..4f41d33
--- /dev/null
+++ b/watcher/lib/src/directory_watcher/windows.dart
@@ -0,0 +1,409 @@
+// 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.

+// TODO(rnystrom): Merge with mac_os version.

+

+library watcher.directory_watcher.windows;

+

+import 'dart:async';

+import 'dart:collection';

+import 'dart:io';

+

+import 'package:path/path.dart' as p;

+import 'package:stack_trace/stack_trace.dart';

+

+import '../constructable_file_system_event.dart';

+import '../path_set.dart';

+import '../utils.dart';

+import '../watch_event.dart';

+import 'resubscribable.dart';

+

+class WindowsDirectoryWatcher extends ResubscribableDirectoryWatcher {

+  WindowsDirectoryWatcher(String directory)

+      : super(directory, () => new _WindowsDirectoryWatcher(directory));

+}

+

+class _EventBatcher {

+  static const Duration _BATCH_DELAY = const Duration(milliseconds: 100);

+  final List<FileSystemEvent> events = [];

+  Timer timer;

+

+  void addEvent(FileSystemEvent event, void callback()) {

+    events.add(event);

+    if (timer != null) {

+      timer.cancel();

+    }

+    timer = new Timer(_BATCH_DELAY, callback);

+  }

+

+  void cancelTimer() {

+    timer.cancel();

+  }

+}

+

+class _WindowsDirectoryWatcher implements ManuallyClosedDirectoryWatcher {

+  final String directory;

+

+  Stream<WatchEvent> get events => _eventsController.stream;

+  final _eventsController = new StreamController<WatchEvent>.broadcast();

+

+  bool get isReady => _readyCompleter.isCompleted;

+

+  Future get ready => _readyCompleter.future;

+  final _readyCompleter = new Completer();

+

+  final Map<String, _EventBatcher> _eventBatchers =

+      new HashMap<String, _EventBatcher>();

+

+  /// The set of files that are known to exist recursively within the watched

+  /// directory.

+  ///

+  /// The state of files on the filesystem is compared against this to determine

+  /// the real change that occurred. This is also used to emit REMOVE events

+  /// when subdirectories are moved out of the watched directory.

+  final PathSet _files;

+

+  /// The subscription to the stream returned by [Directory.watch].

+  StreamSubscription<FileSystemEvent> _watchSubscription;

+

+  /// The subscription to the stream returned by [Directory.watch] of the

+  /// parent directory to [directory]. This is needed to detect changes to

+  /// [directory], as they are not included on Windows.

+  StreamSubscription<FileSystemEvent> _parentWatchSubscription;

+

+  /// The subscription to the [Directory.list] call for the initial listing of

+  /// the directory to determine its initial state.

+  StreamSubscription<FileSystemEntity> _initialListSubscription;

+

+  /// The subscriptions to the [Directory.list] calls for listing the contents

+  /// of subdirectories that were moved into the watched directory.

+  final Set<StreamSubscription<FileSystemEntity>> _listSubscriptions

+      = new HashSet<StreamSubscription<FileSystemEntity>>();

+

+  _WindowsDirectoryWatcher(String directory)

+      : directory = directory, _files = new PathSet(directory) {

+    // Before we're ready to emit events, wait for [_listDir] to complete.

+    _listDir().then((_) {

+      _startWatch();

+      _startParentWatcher();

+      _readyCompleter.complete();

+    });

+  }

+

+  void close() {

+    if (_watchSubscription != null) _watchSubscription.cancel();

+    if (_parentWatchSubscription != null) _parentWatchSubscription.cancel();

+    if (_initialListSubscription != null) _initialListSubscription.cancel();

+    for (var sub in _listSubscriptions) {

+      sub.cancel();

+    }

+    _listSubscriptions.clear();

+    for (var batcher in _eventBatchers.values) {

+      batcher.cancelTimer();

+    }

+    _eventBatchers.clear();

+    _watchSubscription = null;

+    _parentWatchSubscription = null;

+    _initialListSubscription = null;

+    _eventsController.close();

+  }

+

+  /// On Windows, if [directory] is deleted, we will not receive any event.

+  ///

+  /// Instead, we add a watcher on the parent folder (if any), that can notify

+  /// us about [directory]. This also includes events such as moves.

+  void _startParentWatcher() {

+    var absoluteDir = p.absolute(directory);

+    var parent = p.dirname(absoluteDir);

+    // Check if [directory] is already the root directory.

+    if (FileSystemEntity.identicalSync(parent, directory)) return;

+    var parentStream = Chain.track(

+        new Directory(parent).watch(recursive: false));

+    _parentWatchSubscription = parentStream.listen((event) {

+      // Only look at events for 'directory'.

+      if (p.basename(event.path) != p.basename(absoluteDir)) return;

+      // Test if the directory is removed. FileSystemEntity.typeSync will

+      // return NOT_FOUND if it's unable to decide upon the type, including

+      // access denied issues, which may happen when the directory is deleted.

+      // FileSystemMoveEvent and FileSystemDeleteEvent events will always mean

+      // the directory is now gone.

+      if (event is FileSystemMoveEvent ||

+          event is FileSystemDeleteEvent ||

+          (FileSystemEntity.typeSync(directory) ==

+           FileSystemEntityType.NOT_FOUND)) {

+        for (var path in _files.toSet()) {

+          _emitEvent(ChangeType.REMOVE, path);

+        }

+        _files.clear();

+        close();

+      }

+    }, onError: (error) {

+      // Ignore errors, simply close the stream. The user listens on

+      // [directory], and while it can fail to listen on the parent, we may

+      // still be able to listen on the path requested.

+      _parentWatchSubscription.cancel();

+      _parentWatchSubscription = null;

+    });

+  }

+

+  void _onEvent(FileSystemEvent event) {

+    assert(isReady);

+    final batcher = _eventBatchers.putIfAbsent(

+        event.path, () => new _EventBatcher());

+    batcher.addEvent(event, () {

+      _eventBatchers.remove(event.path);

+      _onBatch(batcher.events);

+    });

+  }

+

+  /// The callback that's run when [Directory.watch] emits a batch of events.

+  void _onBatch(List<FileSystemEvent> batch) {

+    _sortEvents(batch).forEach((path, events) {

+      var relativePath = p.relative(path, from: directory);

+

+      var canonicalEvent = _canonicalEvent(events);

+      events = canonicalEvent == null ?

+          _eventsBasedOnFileSystem(path) : [canonicalEvent];

+

+      for (var event in events) {

+        if (event is FileSystemCreateEvent) {

+          if (!event.isDirectory) {

+            if (_files.contains(path)) continue;

+

+            _emitEvent(ChangeType.ADD, path);

+            _files.add(path);

+            continue;

+          }

+

+          if (_files.containsDir(path)) continue;

+

+          var stream = Chain.track(new Directory(path).list(recursive: true));

+          var sub;

+          sub = stream.listen((entity) {

+            if (entity is Directory) return;

+            if (_files.contains(path)) return;

+

+            _emitEvent(ChangeType.ADD, entity.path);

+            _files.add(entity.path);

+          }, onDone: () {

+            _listSubscriptions.remove(sub);

+          }, onError: (e, stackTrace) {

+            _listSubscriptions.remove(sub);

+            _emitError(e, stackTrace);

+          }, cancelOnError: true);

+          _listSubscriptions.add(sub);

+        } else if (event is FileSystemModifyEvent) {

+          if (!event.isDirectory) {

+            _emitEvent(ChangeType.MODIFY, path);

+          }

+        } else {

+          assert(event is FileSystemDeleteEvent);

+          for (var removedPath in _files.remove(path)) {

+            _emitEvent(ChangeType.REMOVE, removedPath);

+          }

+        }

+      }

+    });

+  }

+

+  /// Sort all the events in a batch into sets based on their path.

+  ///

+  /// A single input event may result in multiple events in the returned map;

+  /// for example, a MOVE event becomes a DELETE event for the source and a

+  /// CREATE event for the destination.

+  ///

+  /// The returned events won't contain any [FileSystemMoveEvent]s, nor will it

+  /// contain any events relating to [directory].

+  Map<String, Set<FileSystemEvent>> _sortEvents(List<FileSystemEvent> batch) {

+    var eventsForPaths = {};

+

+    // Events within directories that already have events are superfluous; the

+    // directory's full contents will be examined anyway, so we ignore such

+    // events. Emitting them could cause useless or out-of-order events.

+    var directories = unionAll(batch.map((event) {

+      if (!event.isDirectory) return new Set();

+      if (event is! FileSystemMoveEvent) return new Set.from([event.path]);

+      return new Set.from([event.path, event.destination]);

+    }));

+

+    isInModifiedDirectory(path) =>

+        directories.any((dir) => path != dir && path.startsWith(dir));

+

+    addEvent(path, event) {

+      if (isInModifiedDirectory(path)) return;

+      var set = eventsForPaths.putIfAbsent(path, () => new Set());

+      set.add(event);

+    }

+

+    for (var event in batch) {

+      if (event is FileSystemMoveEvent) {

+        addEvent(event.destination, event);

+      }

+      addEvent(event.path, event);

+    }

+

+    return eventsForPaths;

+  }

+

+  /// Returns the canonical event from a batch of events on the same path, if

+  /// one exists.

+  ///

+  /// If [batch] doesn't contain any contradictory events (e.g. DELETE and

+  /// CREATE, or events with different values for [isDirectory]), this returns a

+  /// single event that describes what happened to the path in question.

+  ///

+  /// If [batch] does contain contradictory events, this returns `null` to

+  /// indicate that the state of the path on the filesystem should be checked to

+  /// determine what occurred.

+  FileSystemEvent _canonicalEvent(Set<FileSystemEvent> batch) {

+    // An empty batch indicates that we've learned earlier that the batch is

+    // contradictory (e.g. because of a move).

+    if (batch.isEmpty) return null;

+

+    var type = batch.first.type;

+    var isDir = batch.first.isDirectory;

+    var hadModifyEvent = false;

+

+    for (var event in batch.skip(1)) {

+      // If one event reports that the file is a directory and another event

+      // doesn't, that's a contradiction.

+      if (isDir != event.isDirectory) return null;

+

+      // Modify events don't contradict either CREATE or REMOVE events. We can

+      // safely assume the file was modified after a CREATE or before the

+      // REMOVE; otherwise there will also be a REMOVE or CREATE event

+      // (respectively) that will be contradictory.

+      if (event is FileSystemModifyEvent) {

+        hadModifyEvent = true;

+        continue;

+      }

+      assert(event is FileSystemCreateEvent ||

+             event is FileSystemDeleteEvent ||

+             event is FileSystemMoveEvent);

+

+      // If we previously thought this was a MODIFY, we now consider it to be a

+      // CREATE or REMOVE event. This is safe for the same reason as above.

+      if (type == FileSystemEvent.MODIFY) {

+        type = event.type;

+        continue;

+      }

+

+      // A CREATE event contradicts a REMOVE event and vice versa.

+      assert(type == FileSystemEvent.CREATE ||

+             type == FileSystemEvent.DELETE ||

+             type == FileSystemEvent.MOVE);

+      if (type != event.type) return null;

+    }

+

+    switch (type) {

+      case FileSystemEvent.CREATE:

+        return new ConstructableFileSystemCreateEvent(batch.first.path, isDir);

+      case FileSystemEvent.DELETE:

+        return new ConstructableFileSystemDeleteEvent(batch.first.path, isDir);

+      case FileSystemEvent.MODIFY:

+        return new ConstructableFileSystemModifyEvent(

+            batch.first.path, isDir, false);

+      case FileSystemEvent.MOVE:

+        return null;

+      default: assert(false);

+    }

+  }

+

+  /// Returns one or more events that describe the change between the last known

+  /// state of [path] and its current state on the filesystem.

+  ///

+  /// This returns a list whose order should be reflected in the events emitted

+  /// to the user, unlike the batched events from [Directory.watch]. The

+  /// returned list may be empty, indicating that no changes occurred to [path]

+  /// (probably indicating that it was created and then immediately deleted).

+  List<FileSystemEvent> _eventsBasedOnFileSystem(String path) {

+    var fileExisted = _files.contains(path);

+    var dirExisted = _files.containsDir(path);

+    var fileExists = new File(path).existsSync();

+    var dirExists = new Directory(path).existsSync();

+

+    var events = [];

+    if (fileExisted) {

+      if (fileExists) {

+        events.add(new ConstructableFileSystemModifyEvent(path, false, false));

+      } else {

+        events.add(new ConstructableFileSystemDeleteEvent(path, false));

+      }

+    } else if (dirExisted) {

+      if (dirExists) {

+        // If we got contradictory events for a directory that used to exist and

+        // still exists, we need to rescan the whole thing in case it was

+        // replaced with a different directory.

+        events.add(new ConstructableFileSystemDeleteEvent(path, true));

+        events.add(new ConstructableFileSystemCreateEvent(path, true));

+      } else {

+        events.add(new ConstructableFileSystemDeleteEvent(path, true));

+      }

+    }

+

+    if (!fileExisted && fileExists) {

+      events.add(new ConstructableFileSystemCreateEvent(path, false));

+    } else if (!dirExisted && dirExists) {

+      events.add(new ConstructableFileSystemCreateEvent(path, true));

+    }

+

+    return events;

+  }

+

+  /// The callback that's run when the [Directory.watch] stream is closed.

+  /// Note that this is unlikely to happen on Windows, unless the system itself

+  /// closes the handle.

+  void _onDone() {

+    _watchSubscription = null;

+

+    // Emit remove events for any remaining files.

+    for (var file in _files.toSet()) {

+      _emitEvent(ChangeType.REMOVE, file);

+    }

+    _files.clear();

+    close();

+  }

+

+  /// Start or restart the underlying [Directory.watch] stream.

+  void _startWatch() {

+    // Batch the events together so that we can dedup events.

+    var innerStream =

+        Chain.track(new Directory(directory).watch(recursive: true));

+    _watchSubscription = innerStream.listen(_onEvent,

+        onError: _eventsController.addError,

+        onDone: _onDone);

+  }

+

+  /// Starts or restarts listing the watched directory to get an initial picture

+  /// of its state.

+  Future _listDir() {

+    assert(!isReady);

+    if (_initialListSubscription != null) _initialListSubscription.cancel();

+

+    _files.clear();

+    var completer = new Completer();

+    var stream = Chain.track(new Directory(directory).list(recursive: true));

+    void handleEntity(entity) {

+      if (entity is! Directory) _files.add(entity.path);

+    }

+    _initialListSubscription = stream.listen(

+        handleEntity,

+        onError: _emitError,

+        onDone: completer.complete,

+        cancelOnError: true);

+    return completer.future;

+  }

+

+  /// Emit an event with the given [type] and [path].

+  void _emitEvent(ChangeType type, String path) {

+    if (!isReady) return;

+

+    _eventsController.add(new WatchEvent(type, path));

+  }

+

+  /// Emit an error, then close the watcher.

+  void _emitError(error, StackTrace stackTrace) {

+    _eventsController.addError(error, stackTrace);

+    close();

+  }

+}

diff --git a/watcher/lib/src/path_set.dart b/watcher/lib/src/path_set.dart
new file mode 100644
index 0000000..e9f7d32
--- /dev/null
+++ b/watcher/lib/src/path_set.dart
@@ -0,0 +1,168 @@
+// Copyright (c) 2013, 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 watcher.path_dart;
+
+import 'dart:collection';
+
+import 'package:path/path.dart' as p;
+
+/// A set of paths, organized into a directory hierarchy.
+///
+/// When a path is [add]ed, it creates an implicit directory structure above
+/// that path. Directories can be inspected using [containsDir] and removed
+/// using [remove]. If they're removed, their contents are removed as well.
+///
+/// The paths in the set are normalized so that they all begin with [root].
+class PathSet {
+  /// The root path, which all paths in the set must be under.
+  final String root;
+
+  /// The path set's directory hierarchy.
+  ///
+  /// Each level of this hierarchy has the same structure: a map from strings to
+  /// other maps, which are further levels of the hierarchy. A map with no
+  /// elements indicates a path that was added to the set that has no paths
+  /// beneath it. Such a path should not be treated as a directory by
+  /// [containsDir].
+  final _entries = new Map<String, Map>();
+
+  /// The set of paths that were explicitly added to this set.
+  ///
+  /// This is needed to disambiguate a directory that was explicitly added to
+  /// the set from a directory that was implicitly added by adding a path
+  /// beneath it.
+  final _paths = new Set<String>();
+
+  PathSet(this.root);
+
+  /// Adds [path] to the set.
+  void add(String path) {
+    path = _normalize(path);
+    _paths.add(path);
+
+    var parts = _split(path);
+    var dir = _entries;
+    for (var part in parts) {
+      dir = dir.putIfAbsent(part, () => {});
+    }
+  }
+
+  /// Removes [path] and any paths beneath it from the set and returns the
+  /// removed paths.
+  ///
+  /// Even if [path] itself isn't in the set, if it's a directory containing
+  /// paths that are in the set those paths will be removed and returned.
+  ///
+  /// If neither [path] nor any paths beneath it are in the set, returns an
+  /// empty set.
+  Set<String> remove(String path) {
+    path = _normalize(path);
+    var parts = new Queue.from(_split(path));
+
+    // Remove the children of [dir], as well as [dir] itself if necessary.
+    //
+    // [partialPath] is the path to [dir], and a prefix of [path]; the remaining
+    // components of [path] are in [parts].
+    recurse(dir, partialPath) {
+      if (parts.length > 1) {
+        // If there's more than one component left in [path], recurse down to
+        // the next level.
+        var part = parts.removeFirst();
+        var entry = dir[part];
+        if (entry == null || entry.isEmpty) return new Set();
+
+        partialPath = p.join(partialPath, part);
+        var paths = recurse(entry, partialPath);
+        // After removing this entry's children, if it has no more children and
+        // it's not in the set in its own right, remove it as well.
+        if (entry.isEmpty && !_paths.contains(partialPath)) dir.remove(part);
+        return paths;
+      }
+
+      // If there's only one component left in [path], we should remove it.
+      var entry = dir.remove(parts.first);
+      if (entry == null) return new Set();
+
+      if (entry.isEmpty) {
+        _paths.remove(path);
+        return new Set.from([path]);
+      }
+
+      var set = _removePathsIn(entry, path);
+      if (_paths.contains(path)) {
+        _paths.remove(path);
+        set.add(path);
+      }
+      return set;
+    }
+
+    return recurse(_entries, root);
+  }
+
+  /// Recursively removes and returns all paths in [dir].
+  ///
+  /// [root] should be the path to [dir].
+  Set<String> _removePathsIn(Map dir, String root) {
+    var removedPaths = new Set();
+    recurse(dir, path) {
+      dir.forEach((name, entry) {
+        var entryPath = p.join(path, name);
+        if (_paths.remove(entryPath)) removedPaths.add(entryPath);
+        recurse(entry, entryPath);
+      });
+    }
+
+    recurse(dir, root);
+    return removedPaths;
+  }
+
+  /// Returns whether [this] contains [path].
+  ///
+  /// This only returns true for paths explicitly added to [this].
+  /// Implicitly-added directories can be inspected using [containsDir].
+  bool contains(String path) => _paths.contains(_normalize(path));
+
+  /// Returns whether [this] contains paths beneath [path].
+  bool containsDir(String path) {
+    path = _normalize(path);
+    var dir = _entries;
+
+    for (var part in _split(path)) {
+      dir = dir[part];
+      if (dir == null) return false;
+    }
+
+    return !dir.isEmpty;
+  }
+
+  /// Returns a [Set] of all paths in [this].
+  Set<String> toSet() => _paths.toSet();
+
+  /// Removes all paths from [this].
+  void clear() {
+    _paths.clear();
+    _entries.clear();
+  }
+
+  String toString() => _paths.toString();
+
+  /// Returns a normalized version of [path].
+  ///
+  /// This removes any extra ".." or "."s and ensure that the returned path
+  /// begins with [root]. It's an error if [path] isn't within [root].
+  String _normalize(String path) {
+    var relative = p.relative(p.normalize(path), from: root);
+    var parts = p.split(relative);
+    // TODO(nweiz): replace this with [p.isWithin] when that exists (issue
+    // 14980).
+    if (!p.isRelative(relative) || parts.first == '..' || parts.first == '.') {
+      throw new ArgumentError('Path "$path" is not inside "$root".');
+    }
+    return p.join(root, relative);
+  }
+
+  /// Returns the segments of [path] beneath [root].
+  List<String> _split(String path) => p.split(p.relative(path, from: root));
+}
diff --git a/watcher/lib/src/stat.dart b/watcher/lib/src/stat.dart
new file mode 100644
index 0000000..166d789
--- /dev/null
+++ b/watcher/lib/src/stat.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2013, 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 watcher.stat;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:stack_trace/stack_trace.dart';
+
+/// A function that takes a file path and returns the last modified time for
+/// the file at that path.
+typedef DateTime MockTimeCallback(String path);
+
+MockTimeCallback _mockTimeCallback;
+
+/// Overrides the default behavior for accessing a file's modification time
+/// with [callback].
+///
+/// The OS file modification time has pretty rough granularity (like a few
+/// seconds) which can make for slow tests that rely on modtime. This lets you
+/// replace it with something you control.
+void mockGetModificationTime(MockTimeCallback callback) {
+  _mockTimeCallback = callback;
+}
+
+/// Gets the modification time for the file at [path].
+Future<DateTime> getModificationTime(String path) {
+  if (_mockTimeCallback != null) {
+    return new Future.value(_mockTimeCallback(path));
+  }
+
+  return Chain.track(FileStat.stat(path)).then((stat) => stat.modified);
+}
diff --git a/watcher/lib/src/utils.dart b/watcher/lib/src/utils.dart
new file mode 100644
index 0000000..007c84c
--- /dev/null
+++ b/watcher/lib/src/utils.dart
@@ -0,0 +1,117 @@
+// Copyright (c) 2013, 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 watcher.utils;
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:collection';
+
+/// Returns `true` if [error] is a [FileSystemException] for a missing
+/// directory.
+bool isDirectoryNotFoundException(error) {
+  if (error is! FileSystemException) return false;
+
+  // See dartbug.com/12461 and tests/standalone/io/directory_error_test.dart.
+  var notFoundCode = Platform.operatingSystem == "windows" ? 3 : 2;
+  return error.osError.errorCode == notFoundCode;
+}
+
+/// Returns the union of all elements in each set in [sets].
+Set unionAll(Iterable<Set> sets) =>
+    sets.fold(new Set(), (union, set) => union.union(set));
+
+/// Returns a buffered stream that will emit the same values as the stream
+/// returned by [future] once [future] completes.
+///
+/// If [future] completes to an error, the return value will emit that error and
+/// then close.
+///
+/// If [broadcast] is true, a broadcast stream is returned. This assumes that
+/// the stream returned by [future] will be a broadcast stream as well.
+/// [broadcast] defaults to false.
+Stream futureStream(Future<Stream> future, {bool broadcast: false}) {
+  var subscription;
+  var controller;
+
+  future = future.catchError((e, stackTrace) {
+    // Since [controller] is synchronous, it's likely that emitting an error
+    // will cause it to be cancelled before we call close.
+    if (controller != null) controller.addError(e, stackTrace);
+    if (controller != null) controller.close();
+    controller = null;
+  });
+
+  onListen() {
+    future.then((stream) {
+      if (controller == null) return;
+      subscription = stream.listen(
+          controller.add,
+          onError: controller.addError,
+          onDone: controller.close);
+    });
+  }
+
+  onCancel() {
+    if (subscription != null) subscription.cancel();
+    subscription = null;
+    controller = null;
+  }
+
+  if (broadcast) {
+    controller = new StreamController.broadcast(
+        sync: true, onListen: onListen, onCancel: onCancel);
+  } else {
+    controller = new StreamController(
+        sync: true, onListen: onListen, onCancel: onCancel);
+  }
+  return controller.stream;
+}
+
+/// Like [new Future], but avoids around issue 11911 by using [new Future.value]
+/// under the covers.
+Future newFuture(callback()) => new Future.value().then((_) => callback());
+
+/// Returns a [Future] that completes after pumping the event queue [times]
+/// times. By default, this should pump the event queue enough times to allow
+/// any code to run, as long as it's not waiting on some external event.
+Future pumpEventQueue([int times = 20]) {
+  if (times == 0) return new Future.value();
+  // We use a delayed future to allow microtask events to finish. The
+  // Future.value or Future() constructors use scheduleMicrotask themselves and
+  // would therefore not wait for microtask callbacks that are scheduled after
+  // invoking this method.
+  return new Future.delayed(Duration.ZERO, () => pumpEventQueue(times - 1));
+}
+
+/// A stream transformer that batches all events that are sent at the same time.
+///
+/// When multiple events are synchronously added to a stream controller, the
+/// [StreamController] implementation uses [scheduleMicrotask] to schedule the
+/// asynchronous firing of each event. In order to recreate the synchronous
+/// batches, this collates all the events that are received in "nearby"
+/// microtasks.
+class BatchedStreamTransformer<T> implements StreamTransformer<T, List<T>> {
+  Stream<List<T>> bind(Stream<T> input) {
+    var batch = new Queue();
+    return new StreamTransformer<T, List<T>>.fromHandlers(
+        handleData: (event, sink) {
+      batch.add(event);
+
+      // [Timer.run] schedules an event that runs after any microtasks that have
+      // been scheduled.
+      Timer.run(() {
+        if (batch.isEmpty) return;
+        sink.add(batch.toList());
+        batch.clear();
+      });
+    }, handleDone: (sink) {
+      if (batch.isNotEmpty) {
+        sink.add(batch.toList());
+        batch.clear();
+      }
+      sink.close();
+    }).bind(input);
+  }
+}
diff --git a/watcher/lib/src/watch_event.dart b/watcher/lib/src/watch_event.dart
new file mode 100644
index 0000000..be6d70c
--- /dev/null
+++ b/watcher/lib/src/watch_event.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2013, 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 watcher.watch_event;
+
+/// An event describing a single change to the file system.
+class WatchEvent {
+  /// The manner in which the file at [path] has changed.
+  final ChangeType type;
+
+  /// The path of the file that changed.
+  final String path;
+
+  WatchEvent(this.type, this.path);
+
+  String toString() => "$type $path";
+}
+
+/// Enum for what kind of change has happened to a file.
+class ChangeType {
+  /// A new file has been added.
+  static const ADD = const ChangeType("add");
+
+  /// A file has been removed.
+  static const REMOVE = const ChangeType("remove");
+
+  /// The contents of a file have changed.
+  static const MODIFY = const ChangeType("modify");
+
+  final String _name;
+  const ChangeType(this._name);
+
+  String toString() => _name;
+}
diff --git a/watcher/lib/watcher.dart b/watcher/lib/watcher.dart
new file mode 100644
index 0000000..88531f2
--- /dev/null
+++ b/watcher/lib/watcher.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2013, 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 watcher;
+
+export 'src/watch_event.dart';
+export 'src/directory_watcher.dart';
+export 'src/directory_watcher/polling.dart';
diff --git a/watcher/pubspec.yaml b/watcher/pubspec.yaml
new file mode 100644
index 0000000..a16a020
--- /dev/null
+++ b/watcher/pubspec.yaml
@@ -0,0 +1,15 @@
+name: watcher
+version: 0.9.5
+author: Dart Team <misc@dartlang.org>
+homepage: http://github.com/dart-lang/watcher
+description: >
+  A file system watcher. It monitors changes to contents of directories and
+  sends notifications when files have been added, removed, or modified.
+environment:
+  sdk: '>=1.0.0 <2.0.0'
+dependencies:
+  path: '>=0.9.0 <2.0.0'
+  stack_trace: '>=0.9.1 <2.0.0'
+dev_dependencies:
+  scheduled_test: '>=0.9.3 <0.12.0'
+  unittest: '>=0.9.2 <0.12.0'
diff --git a/web_components/lib/build.log b/web_components/lib/build.log
new file mode 100644
index 0000000..d640c60
--- /dev/null
+++ b/web_components/lib/build.log
@@ -0,0 +1,47 @@
+BUILD LOG
+---------
+Build Time: 2014-10-07T18:03:14
+
+NODEJS INFORMATION
+==================
+nodejs: v0.10.21
+chai: 1.9.0
+grunt: 0.4.2
+grunt-audit: 0.0.2
+grunt-concat-sourcemap: 0.4.1
+grunt-contrib-concat: 0.3.0
+grunt-contrib-uglify: 0.3.2
+grunt-contrib-yuidoc: 0.5.1
+grunt-karma: 0.6.2
+karma: 0.10.9
+karma-chrome-launcher: 0.1.2
+karma-coffee-preprocessor: 0.1.3
+karma-crbot-reporter: 0.0.4
+karma-firefox-launcher: 0.1.3
+karma-html2js-preprocessor: 0.1.0
+karma-ie-launcher: 0.1.1
+karma-jasmine: 0.1.5
+karma-mocha: 0.1.1
+karma-phantomjs-launcher: 0.1.2
+karma-requirejs: 0.2.1
+karma-safari-launcher: 0.1.1
+karma-script-launcher: 0.1.0
+mocha: 1.17.1
+requirejs: 2.1.11
+
+REPO REVISIONS
+==============
+WeakMap: 56eebff250e6cf82dfd767b7703599e047d8b10f
+observe-js: fa70c37099026225876f7c7a26bdee7c48129f1c
+
+ShadowDOM: c524de1304cc5a8863326ceeedb325cda43cd42f
+  + cherry pick pull request #515
+  (local hash: 14a48768d21603c7fc54dcef392b48dce0baa33f)
+URL: cf5d82e444d2a029834365a8b9d185fd792227f0
+MutationObservers: b1039ace292dec90b246317db28ff91d653375e7
+HTMLImports: 6dea2dd969dc4dfb594ae33d4b072130c4890da5
+CustomElements: 321f5428f0c0486d45dd6b384aea9d07021e431d
+
+BUILD HASHES
+============
+build/platform.js: 5b8a1707ff61149f8d491be777e197aeaae5da41
diff --git a/web_components/lib/build/common.dart b/web_components/lib/build/common.dart
new file mode 100644
index 0000000..41f4901
--- /dev/null
+++ b/web_components/lib/build/common.dart
@@ -0,0 +1,78 @@
+// Copyright (c) 2015, 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 web_components.build.common;
+
+import 'package:analyzer/analyzer.dart';
+import 'package:barback/barback.dart';
+import 'package:code_transformers/messages/build_logger.dart';
+import 'package:html/dom.dart' as dom;
+import 'package:html/parser.dart';
+import 'package:source_span/source_span.dart';
+import 'package:path/path.dart' as path;
+import 'messages.dart';
+
+/// Generate the import url for a file described by [id], referenced by a file
+/// with [sourceId].
+// TODO(sigmund): this should also be in barback (dartbug.com/12610)
+// TODO(jakemac): This is copied from polymer, we should move it to
+// the code_transformers package so it can be shared.
+String assetUrlFor(AssetId id, AssetId sourceId, BuildLogger logger,
+    {bool allowAssetUrl: false}) {
+  // use package: and asset: urls if possible
+  if (id.path.startsWith('lib/')) {
+    return 'package:${id.package}/${id.path.substring(4)}';
+  }
+
+  if (id.path.startsWith('asset/')) {
+    if (!allowAssetUrl) {
+      logger.error(internalErrorDontKnowHowToImport.create({
+        'target': id,
+        'source': sourceId,
+        'extra': ' (asset urls not allowed.)'
+      }));
+      return null;
+    }
+    return 'asset:${id.package}/${id.path.substring(6)}';
+  }
+
+  // Use relative urls only if it's possible.
+  if (id.package != sourceId.package) {
+    logger.error("don't know how to refer to $id from $sourceId");
+    return null;
+  }
+
+  var builder = path.url;
+  return builder.relative(builder.join('/', id.path),
+      from: builder.join('/', builder.dirname(sourceId.path)));
+}
+
+/// Gets the appropriate URL to use in a span to produce messages (e.g.
+/// warnings) for users. This will attempt to format the URL in the most useful
+/// way:
+///
+/// - If the asset is within the primary package, then use the [id.path],
+///   the user will know it is a file from their own code.
+/// - If the asset is from another package, then use [assetUrlFor], this will
+///   likely be a "package:" url to the file in the other package, which is
+///   enough for users to identify where the error is.
+String spanUrlFor(AssetId id, AssetId sourceId, logger) {
+  bool samePackage = id.package == sourceId.package;
+  return samePackage
+      ? id.path
+      : assetUrlFor(id, sourceId, logger, allowAssetUrl: true);
+}
+
+/// Return the span in a file for an [AstNode].
+FileSpan getSpan(SourceFile file, AstNode node) =>
+    file.span(node.offset, node.end);
+
+/// Parses an HTML file [contents] and returns a DOM-like tree.
+dom.Document parseHtml(String contents, String sourcePath) {
+  var parser = new HtmlParser(contents,
+      encoding: 'utf8', generateSpans: true, sourceUrl: sourcePath);
+  return parser.parse();
+}
+
+const String dartType = "application/dart";
+const String jsType = "text/javascript";
diff --git a/web_components/lib/build/html_import_annotation_recorder.dart b/web_components/lib/build/html_import_annotation_recorder.dart
new file mode 100644
index 0000000..ba5d2ab
--- /dev/null
+++ b/web_components/lib/build/html_import_annotation_recorder.dart
@@ -0,0 +1,100 @@
+// Copyright (c) 2015, 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 web_components.build.html_import_recorder_inliner;
+
+import 'package:analyzer/analyzer.dart';
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:barback/barback.dart';
+import 'package:initialize/transformer.dart';
+import 'package:path/path.dart' as path;
+import '../src/normalize_path.dart';
+
+/// [InitializerPlugin] for @HtmlImport annotations. This records all paths
+/// seen, normalizes them relative to the entry point, and records the values in
+/// [importPaths].
+///
+/// Note: This does nothing with the paths on its own, a separate step needs to
+/// add the imports to the entry point document.
+class HtmlImportAnnotationRecorder implements InitializerPlugin {
+  /// All the normalized import paths that were seen.
+  final Set<String> importPaths = new Set<String>();
+
+  TransformLogger _logger;
+
+  HtmlImportAnnotationRecorder();
+
+  /// Applies to anything named `HtmlImport` which annotates a library.
+  bool shouldApply(InitializerPluginData pluginData) {
+    var annotationElement = pluginData.initializer.annotationNode.element;
+    var logger = pluginData.logger;
+    DartType type;
+    if (annotationElement is ConstructorElement) {
+      type = annotationElement.returnType;
+    } else if (annotationElement is PropertyAccessorElement) {
+      type = annotationElement.variable.propagatedType;
+      if (type == null) {
+        type = pluginData.resolver.evaluateConstant(annotationElement.library,
+            pluginData.initializer.annotationNode.name).value.type;
+      }
+    } else {
+      logger.error('Unsupported annotation type. Only constructors and '
+          'properties are supported as initializers.');
+      return false;
+    }
+    if (type.name != 'HtmlImport') return false;
+    if (pluginData.initializer.targetElement is! LibraryElement) {
+      logger.error('Invalid HtmlImport annotation on non-library element.');
+      return false;
+    }
+    return true;
+  }
+
+  /// Records the normalized url and returns [null] so that no [InitEntry] will
+  /// be created.
+  String apply(InitializerPluginData pluginData) {
+    var bootstrapId = pluginData.bootstrapId;
+    var logger = pluginData.logger;
+    var annotation = pluginData.initializer.annotationNode;
+    var annotationElement = pluginData.initializer.annotationElement;
+    var element = pluginData.initializer.targetElement as LibraryElement;
+    var resolver = pluginData.resolver;
+    var libraryDirective =
+        pluginData.initializer.targetNode.parent.parent as LibraryDirective;
+
+    var originalImportPath;
+    if (annotationElement.element is PropertyAccessorElement) {
+      originalImportPath = resolver.evaluateConstant(
+              element.library, annotation.name).value.fields[
+          'filePath'].stringValue;
+    } else {
+      assert(annotationElement.element is ConstructorElement);
+      originalImportPath = resolver.evaluateConstant(element.library,
+          annotation.arguments.arguments.first).value.stringValue;
+    }
+
+    var libPath;
+    var segments = element.source.uri.pathSegments;
+    var package = segments[0];
+    if (bootstrapId.package == package &&
+        bootstrapId.path.startsWith('${segments[1]}/')) {
+      package = null;
+      libPath = path.url.relative(
+          path.url.joinAll(segments.getRange(1, segments.length)),
+          from: path.url.dirname(path.url.join(bootstrapId.path)));
+    } else if (segments[1] == 'lib') {
+      libPath = path.url.joinAll(segments.getRange(2, segments.length));
+    } else {
+      logger.error('Unable to import `${element.source.uri.path}` from '
+          '${bootstrapId}.');
+      return null;
+    }
+
+    importPaths
+        .add(normalizeHtmlImportPath(originalImportPath, package, libPath));
+
+    // Don't emit an InitEntry.
+    return null;
+  }
+}
diff --git a/web_components/lib/build/import_crawler.dart b/web_components/lib/build/import_crawler.dart
new file mode 100644
index 0000000..641a3bc
--- /dev/null
+++ b/web_components/lib/build/import_crawler.dart
@@ -0,0 +1,88 @@
+// Copyright (c) 2013, 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 web_components.build.import_crawler;
+
+import 'dart:async';
+import 'dart:collection' show LinkedHashMap;
+import 'package:code_transformers/assets.dart';
+import 'package:code_transformers/messages/build_logger.dart';
+import 'package:barback/barback.dart';
+import 'package:html/dom.dart' show Document, Element;
+import 'common.dart';
+import 'messages.dart';
+
+/// Information about an html import found in a document.
+class ImportData {
+  /// The [AssetId] where the html import appeared.
+  final AssetId fromId;
+
+  /// The [Document] where the html import appeared.
+  final Document document;
+
+  /// The html import element itself.
+  final Element element;
+
+  ImportData(this.document, this.element, {this.fromId});
+}
+
+/// A crawler for html imports.
+class ImportCrawler {
+  // Can be either an AggregateTransform or Transform.
+  final _transform;
+  final BuildLogger _logger;
+  final AssetId _primaryInputId;
+
+  // Optional parsed document for the primary id if available.
+  final Document _primaryDocument;
+
+  ImportCrawler(this._transform, this._primaryInputId, this._logger,
+      {Document primaryDocument})
+      : _primaryDocument = primaryDocument;
+
+  /// Returns a post-ordered map of [AssetId]'s to [ImportData]. The [AssetId]'s
+  /// represent an asset which was discovered via an html import, and the
+  /// [ImportData] represents the [Document] where it was discovered and the
+  /// html import [Element] itself.
+  Future<LinkedHashMap<AssetId, ImportData>> crawlImports() {
+    var documents = new LinkedHashMap<AssetId, ImportData>();
+    var seen = new Set<AssetId>();
+
+    Future doCrawl(AssetId assetId,
+        {Element import, Document document, AssetId from}) {
+      if (seen.contains(assetId)) return null;
+      seen.add(assetId);
+
+      Future crawlImports(Document document) {
+        var imports = document.querySelectorAll('link[rel="import"]');
+        var done = Future.forEach(imports,
+            (i) => doCrawl(_importId(assetId, i), import: i, from: assetId));
+
+        // Add this document after its dependencies.
+        return done.then((_) {
+          documents[assetId] = new ImportData(document, import, fromId: from);
+        });
+      }
+
+      if (document != null) {
+        return crawlImports(document);
+      } else {
+        return _transform.readInputAsString(assetId).then((html) {
+          return crawlImports(parseHtml(html, assetId.path));
+        }).catchError((error) {
+          var span;
+          if (import != null) span = import.sourceSpan;
+          _logger.error(inlineImportFail.create({'error': error}), span: span);
+        });
+      }
+    }
+
+    return doCrawl(_primaryInputId, document: _primaryDocument)
+        .then((_) => documents);
+  }
+
+  AssetId _importId(AssetId source, Element import) {
+    var url = import.attributes['href'];
+    return uriToAssetId(source, url, _transform.logger, import.sourceSpan);
+  }
+}
diff --git a/web_components/lib/build/import_inliner.dart b/web_components/lib/build/import_inliner.dart
new file mode 100644
index 0000000..787332a
--- /dev/null
+++ b/web_components/lib/build/import_inliner.dart
@@ -0,0 +1,363 @@
+// Copyright (c) 2015, 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 web_components.build.import_inliner;
+
+import 'dart:async';
+import 'dart:collection' show LinkedHashMap;
+import 'package:barback/barback.dart';
+import 'package:code_transformers/assets.dart';
+import 'package:code_transformers/messages/build_logger.dart';
+import 'package:html/dom.dart';
+import 'package:html/dom_parsing.dart' show TreeVisitor;
+import 'package:path/path.dart' as path;
+import 'package:source_span/source_span.dart';
+import 'common.dart';
+import 'import_crawler.dart';
+import 'messages.dart';
+
+/// Transformer which inlines all html imports found from the entry points. This
+/// deletes all dart scripts found during the inlining, so the
+/// [ScriptCompactorTransformer] should be ran first if there are any dart files
+/// in html imports.
+class ImportInlinerTransformer extends Transformer {
+  final List<String> entryPoints;
+  final List<String> bindingStartDelimiters;
+
+  ImportInlinerTransformer(
+      [this.entryPoints, this.bindingStartDelimiters = const []]);
+
+  bool isPrimary(AssetId id) {
+    if (entryPoints != null) return entryPoints.contains(id.path);
+    // If no entry point is supplied, then any html file under web/ or test/ is
+    // an entry point.
+    return (id.path.startsWith('web/') || id.path.startsWith('test/')) &&
+        id.path.endsWith('.html');
+  }
+
+  apply(Transform transform) {
+    var logger = new BuildLogger(transform, convertErrorsToWarnings: true);
+    return new ImportInliner(transform, transform.primaryInput.id, logger,
+        bindingStartDelimiters: bindingStartDelimiters).run();
+  }
+}
+
+/// Helper class which actually does all the inlining of html imports for a
+/// single entry point.
+class ImportInliner {
+  // Can be an AggregateTransform or Transform
+  final transform;
+  // The primary input to start from.
+  final AssetId primaryInput;
+  // The logger to use.
+  final BuildLogger logger;
+  // The start delimiters for template bindings, such as '{{' or '[['.
+  final List<String> bindingStartDelimiters;
+
+  ImportInliner(this.transform, this.primaryInput, this.logger,
+      {this.bindingStartDelimiters: const []});
+
+  Future run() {
+    var crawler = new ImportCrawler(transform, primaryInput, logger);
+    return crawler.crawlImports().then((imports) {
+      var primaryDocument = imports[primaryInput].document;
+
+      // Normalize urls in the entry point.
+      var changed = new _UrlNormalizer(
+              primaryInput, primaryInput, logger, bindingStartDelimiters)
+          .visit(primaryDocument);
+
+      // Inline things if needed, always have at least one (the entry point).
+      if (imports.length > 1) {
+        _inlineImports(primaryDocument, imports);
+      } else if (!changed &&
+          primaryDocument.querySelectorAll('link[rel="import"]').length == 0) {
+        // If there were no url changes and no imports, then we are done.
+        return;
+      }
+
+      primaryDocument
+          .querySelectorAll('link[rel="import"]')
+          .forEach((element) => element.remove());
+
+      transform.addOutput(
+          new Asset.fromString(primaryInput, primaryDocument.outerHtml));
+    });
+  }
+
+  void _inlineImports(
+      Document primaryDocument, LinkedHashMap<AssetId, ImportData> imports) {
+    // Add a hidden div at the top of the body, this is where we will inline
+    // all the imports.
+    var importWrapper = new Element.tag('div')..attributes['hidden'] = '';
+    var firstElement = primaryDocument.body.firstChild;
+    if (firstElement != null) {
+      primaryDocument.body.insertBefore(importWrapper, firstElement);
+    } else {
+      primaryDocument.body.append(importWrapper);
+    }
+
+    // Move all scripts/stylesheets/imports into the wrapper to maintain
+    // ordering.
+    _moveHeadToWrapper(primaryDocument, importWrapper);
+
+    // Add all the other imports!
+    imports.forEach((AssetId asset, ImportData data) {
+      if (asset == primaryInput) return;
+      var document = data.document;
+      // Remove all dart script tags.
+      document
+          .querySelectorAll('script[type="$dartType"]')
+          .forEach((script) => script.remove());
+      // Normalize urls in attributes and inline css.
+      new _UrlNormalizer(data.fromId, asset, logger, bindingStartDelimiters)
+          .visit(document);
+      // Replace the import with its contents by appending the nodes
+      // immediately before the import one at a time, and then removing the
+      // import from the document.
+      var element = data.element;
+      var parent = element.parent;
+      document.head.nodes
+          .toList(growable: false)
+          .forEach((child) => parent.insertBefore(child, element));
+      document.body.nodes
+          .toList(growable: false)
+          .forEach((child) => parent.insertBefore(child, element));
+      element.remove();
+    });
+  }
+}
+
+/// To preserve the order of scripts with respect to inlined
+/// link rel=import, we move both of those into the body before we do any
+/// inlining. We do not start doing this until the first import is found
+/// however, as some scripts do need to be ran in the head to work
+/// properly (webcomponents.js for instance).
+///
+/// Note: we do this for stylesheets as well to preserve ordering with
+/// respect to eachother, because stylesheets can be pulled in transitively
+/// from imports.
+void _moveHeadToWrapper(Document doc, Element wrapper) {
+  var foundImport = false;
+  for (var node in doc.head.nodes.toList(growable: false)) {
+    if (node is! Element) continue;
+    var tag = node.localName;
+    var type = node.attributes['type'];
+    var rel = node.attributes['rel'];
+    if (tag == 'link' && rel == 'import') foundImport = true;
+    if (!foundImport) continue;
+    if (tag == 'style' ||
+        tag == 'script' &&
+            (type == null || type == jsType || type == dartType) ||
+        tag == 'link' && (rel == 'stylesheet' || rel == 'import')) {
+      // Move the node into the wrapper, where its contents will be placed.
+      // This wrapper is a hidden div to prevent inlined html from causing a
+      // FOUC.
+      wrapper.append(node);
+    }
+  }
+}
+
+/// Internally adjusts urls in the html that we are about to inline.
+// TODO(jakemac): Everything from here down is almost an exact copy from the
+// polymer package. We should consolidate this logic by either removing it
+// completely from polymer or exposing it publicly here and using that in
+// polymer.
+class _UrlNormalizer extends TreeVisitor {
+  /// [AssetId] for the main entry point.
+  final AssetId primaryInput;
+
+  /// Asset where the original content (and original url) was found.
+  final AssetId sourceId;
+
+  /// Counter used to ensure that every library name we inject is unique.
+  int _count = 0;
+
+  /// Path to the top level folder relative to the transform primaryInput.
+  /// This should just be some arbitrary # of ../'s.
+  final String topLevelPath;
+
+  /// Whether or not the normalizer has changed something in the tree.
+  bool changed = false;
+
+  // The start delimiters for template bindings, such as '{{' or '[['. If these
+  // are found before the first `/` in a url, then the url will not be
+  // normalized.
+  final List<String> bindingStartDelimiters;
+
+  final BuildLogger logger;
+
+  _UrlNormalizer(AssetId primaryInput, this.sourceId, this.logger,
+      this.bindingStartDelimiters)
+      : primaryInput = primaryInput,
+        topLevelPath = '../' * (path.url.split(primaryInput.path).length - 2);
+
+  bool visit(Node node) {
+    super.visit(node);
+    return changed;
+  }
+
+  visitElement(Element node) {
+    // TODO(jakemac): Support custom elements that extend html elements which
+    // have url-like attributes. This probably means keeping a list of which
+    // html elements support each url-like attribute.
+    if (!isCustomTagName(node.localName)) {
+      node.attributes.forEach((name, value) {
+        if (_urlAttributes.contains(name)) {
+          node.attributes[name] = _newUrl(value, node.sourceSpan);
+          changed = value != node.attributes[name];
+        }
+      });
+    }
+    if (node.localName == 'style') {
+      node.text = visitCss(node.text);
+    } else if (node.localName == 'script' &&
+        node.attributes['type'] == dartType &&
+        !node.attributes.containsKey('src')) {
+      changed = true;
+    }
+    return super.visitElement(node);
+  }
+
+  static final _url = new RegExp(r'url\(([^)]*)\)', multiLine: true);
+  static final _quote = new RegExp('["\']', multiLine: true);
+
+  /// Visit the CSS text and replace any relative URLs so we can inline it.
+  // Ported from:
+  // https://github.com/Polymer/vulcanize/blob/c14f63696797cda18dc3d372b78aa3378acc691f/lib/vulcan.js#L149
+  // TODO(jmesserly): use csslib here instead? Parsing with RegEx is sadness.
+  // Maybe it's reliable enough for finding URLs in CSS? I'm not sure.
+  String visitCss(String cssText) {
+    var url = spanUrlFor(sourceId, primaryInput, logger);
+    var src = new SourceFile(cssText, url: url);
+    return cssText.replaceAllMapped(_url, (match) {
+      changed = true;
+      // Extract the URL, without any surrounding quotes.
+      var span = src.span(match.start, match.end);
+      var href = match[1].replaceAll(_quote, '');
+      href = _newUrl(href, span);
+      return 'url($href)';
+    });
+  }
+
+  String _newUrl(String href, SourceSpan span) {
+    // We only want to parse the part of the href leading up to the first
+    // folder, anything after that is not informative.
+    var hrefToParse;
+    var firstFolder = href.indexOf('/');
+    if (firstFolder == -1) {
+      hrefToParse = href;
+    } else if (firstFolder == 0) {
+      return href;
+    } else {
+      // Special case packages and assets urls.
+      if (href.contains('packages/')) {
+        var suffix = href.substring(href.indexOf('packages/') + 9);
+        return '${topLevelPath}packages/$suffix';
+      } else if (href.contains('assets/')) {
+        var suffix = href.substring(href.indexOf('assets/') + 7);
+        return '${topLevelPath}packages/$suffix';
+      }
+
+      hrefToParse = '${href.substring(0, firstFolder + 1)}';
+    }
+
+    // If we found a binding before the first `/`, then just return the original
+    // href, we can't determine anything about it.
+    if (bindingStartDelimiters.any((d) => hrefToParse.contains(d))) return href;
+
+    Uri uri;
+    // Various template systems introduce invalid characters to uris which would
+    // be typically replaced at runtime. Parse errors are assumed to be caused
+    // by this, and we just return the original href in that case.
+    try {
+      uri = Uri.parse(hrefToParse);
+    } catch (e) {
+      return href;
+    }
+    if (uri.isAbsolute) return href;
+    if (uri.scheme.isNotEmpty) return href;
+    if (uri.host.isNotEmpty) return href;
+    if (uri.path.isEmpty) return href; // Implies standalone ? or # in URI.
+    if (path.isAbsolute(hrefToParse)) return href;
+
+    var id = uriToAssetId(sourceId, hrefToParse, logger, span);
+    if (id == null) return href;
+
+    // Build the new path, placing back any suffixes that we stripped earlier.
+    var prefix =
+        (firstFolder == -1) ? id.path : id.path.substring(0, id.path.length);
+    var suffix = (firstFolder == -1) ? '' : href.substring(firstFolder);
+    var newPath = '$prefix$suffix';
+
+    if (newPath.startsWith('lib/')) {
+      return '${topLevelPath}packages/${id.package}/${newPath.substring(4)}';
+    }
+
+    if (newPath.startsWith('asset/')) {
+      return '${topLevelPath}assets/${id.package}/${newPath.substring(6)}';
+    }
+
+    if (primaryInput.package != id.package) {
+      // Technically we shouldn't get there
+      logger.error(internalErrorDontKnowHowToImport
+              .create({'target': id, 'source': primaryInput, 'extra': ''}),
+          span: span);
+      return href;
+    }
+
+    var builder = path.url;
+    return builder.normalize(builder.relative(builder.join('/', newPath),
+        from: builder.join('/', builder.dirname(primaryInput.path))));
+  }
+}
+
+/// Returns true if this is a valid custom element name. See:
+/// <http://w3c.github.io/webcomponents/spec/custom/#dfn-custom-element-type>
+bool isCustomTagName(String name) {
+  if (name == null || !name.contains('-')) return false;
+  return !invalidTagNames.containsKey(name);
+}
+
+/// These names have meaning in SVG or MathML, so they aren't allowed as custom
+/// tags. See [isCustomTagName].
+const invalidTagNames = const {
+  'annotation-xml': '',
+  'color-profile': '',
+  'font-face': '',
+  'font-face-src': '',
+  'font-face-uri': '',
+  'font-face-format': '',
+  'font-face-name': '',
+  'missing-glyph': '',
+};
+
+/// HTML attributes that expect a URL value.
+/// <http://dev.w3.org/html5/spec/section-index.html#attributes-1>
+///
+/// Every one of these attributes is a URL in every context where it is used in
+/// the DOM. The comments show every DOM element where an attribute can be used.
+///
+/// The _* version of each attribute is also supported, see http://goo.gl/5av8cU
+const _urlAttributes = const [
+  'action',
+  '_action', // in form
+  'background',
+  '_background', // in body
+  'cite',
+  '_cite', // in blockquote, del, ins, q
+  'data',
+  '_data', // in object
+  'formaction',
+  '_formaction', // in button, input
+  'href',
+  '_href', // in a, area, link, base, command
+  'icon',
+  '_icon', // in command
+  'manifest',
+  '_manifest', // in html
+  'poster',
+  '_poster', // in video
+  'src',
+  '_src', // in audio, embed, iframe, img, input, script, source, track,video
+];
diff --git a/web_components/lib/build/messages.dart b/web_components/lib/build/messages.dart
new file mode 100644
index 0000000..87f3fa3
--- /dev/null
+++ b/web_components/lib/build/messages.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2015, 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.
+
+/// Contains all error and warning messages produced by web_components.
+library web_components.build.messages;
+
+import 'package:code_transformers/messages/messages.dart';
+
+const scriptFileNotFound = const MessageTemplate(
+    const MessageId('web_components', 0), 'Script file at "%-url-%" not found.',
+    'URL to a script file might be incorrect', '''
+An error occurred trying to read a script tag on a given URL. This is often the
+result of a broken URL in a `<script src="...">`.
+''');
+
+const scriptIncludedMoreThanOnce = const MessageTemplate(
+    const MessageId('web_components', 1),
+    'The `%-url-%` script was included more than once.',
+    'Dart script file included more than once.', '''
+Duplicate dart scripts often happen if you have multiple html imports that
+include the same script. The simplest workaround for this is to move your dart
+script to its own html file, and import that instead of the script (html imports
+are automatically deduped).
+
+For example:
+
+    <script type="application/dart" src="foo.dart"></script>
+
+Should turn into:
+
+    <link rel="import" href="foo.html">
+
+And `foo.html` should look like:
+
+    <script type="application/dart" src="foo.dart"></script>
+''');
+
+const exactlyOneScriptPerEntryPoint = const MessageTemplate(
+    const MessageId('web_components', 2),
+    'Found either zero or multiple dart scripts in the entry point `%-url-%`. '
+    'Exactly one was expected.',
+    'Each entry point html file should contain exactly one dart script tag.',
+    'Each entry point html file should contain exactly one dart script tag.');
+
+const internalErrorDontKnowHowToImport = const MessageTemplate(
+    const MessageId('web_components', 3),
+    "internal error: don't know how to include %-target-% from"
+    " %-source-%.%-extra-%", "Internal error: don't know how to include a URL",
+    '''
+Sorry, you just ran into a bug in the web_components transformer code. Please
+file a bug at <https://github.com/dart-lang/web-components/issues/new>
+including, if possible, some example code that can help the team reproduce the
+issue.
+''');
+
+const inlineImportFail = const MessageTemplate(
+    const MessageId('web_components', 4),
+    'Failed to inline HTML import: %-error-%', 'Error while inlining an import',
+    '''
+An error occurred while inlining an import in the web_components build. This is
+often the result of a broken HTML import.
+
+One possible cause is using an @HtmlImport containing a relative path from
+within an inline script tag, see http://goo.gl/ZgrhaV. The workaround currently
+is to use a `package:` url instead, move the code to a dart file, or simply
+adding a real html import (since you are already in an html file).
+''');
diff --git a/web_components/lib/build/mirrors_remover.dart b/web_components/lib/build/mirrors_remover.dart
new file mode 100644
index 0000000..62dedaa
--- /dev/null
+++ b/web_components/lib/build/mirrors_remover.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2015, 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 web_components.build.mirrors_remover;
+
+import 'dart:async';
+import 'package:barback/barback.dart';
+
+/// Removes `mirror_initializer.dart` and replaces it with
+/// `static_initializer.dart`.
+class MirrorsRemoverTransformer extends Transformer {
+  MirrorsRemoverTransformer();
+  MirrorsRemoverTransformer.asPlugin(BarbackSettings settings);
+
+  bool isPrimary(AssetId id) => id.path == 'lib/src/init.dart';
+
+  @override
+  Future apply(Transform transform) async {
+    String source = await transform.primaryInput.readAsString();
+    source =
+        source.replaceAll('mirror_initializer.dart', 'static_initializer.dart');
+    transform
+        .addOutput(new Asset.fromString(transform.primaryInput.id, source));
+  }
+}
diff --git a/web_components/lib/build/script_compactor.dart b/web_components/lib/build/script_compactor.dart
new file mode 100644
index 0000000..12655b3
--- /dev/null
+++ b/web_components/lib/build/script_compactor.dart
@@ -0,0 +1,261 @@
+// Copyright (c) 2015, 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 web_components.build.script_compactor;
+
+import 'dart:async';
+import 'package:analyzer/analyzer.dart';
+import 'package:barback/barback.dart';
+import 'package:code_transformers/assets.dart';
+import 'package:code_transformers/messages/build_logger.dart';
+import 'package:html/dom.dart' as dom;
+import 'package:html/parser.dart' as parser;
+import 'package:path/path.dart' as path;
+import 'package:source_maps/refactor.dart' show TextEditTransaction;
+import 'package:source_span/source_span.dart';
+import 'common.dart';
+import 'import_crawler.dart';
+import 'messages.dart';
+
+/// Transformer which combines all dart scripts found in html imports into one
+/// new bootstrap file, and replaces the old entry point script with that file.
+///
+/// Note: Does not delete the original script files (it can't because the
+/// imports may live in other packages). The [ImportInlinerTransformer] will not
+/// copy scripts when inlining imports into your entry point to compensate for
+/// this.
+class ScriptCompactorTransformer extends Transformer {
+  final List<String> entryPoints;
+
+  ScriptCompactorTransformer([this.entryPoints]);
+
+  bool isPrimary(AssetId id) {
+    if (entryPoints != null) return entryPoints.contains(id.path);
+    // If no entry point is supplied, then any html file under web/ or test/ is
+    // an entry point.
+    return (id.path.startsWith('web/') || id.path.startsWith('test/')) &&
+        id.path.endsWith('.html');
+  }
+
+  apply(Transform transform) {
+    var logger = new BuildLogger(transform);
+    return new ScriptCompactor(transform, transform.primaryInput.id, logger)
+        .run()
+        .then((Asset bootstrap) {
+      if (bootstrap == null) return null;
+      return transform.primaryInput.readAsString().then((html) {
+        var doc = parser.parse(html);
+        var mainScriptTag = doc.querySelector('script[type="$dartType"]');
+        mainScriptTag.attributes['src'] =
+            _importPath(bootstrap.id, transform.primaryInput.id);
+        mainScriptTag.text = '';
+
+        transform.addOutput(
+            new Asset.fromString(transform.primaryInput.id, doc.outerHtml));
+      });
+    });
+  }
+}
+
+/// Helper class which does all the script compacting for a single entry point.
+class ScriptCompactor {
+  /// Can be an AggregateTransform or Transform
+  final transform;
+
+  /// The primary input to start from.
+  final AssetId primaryInput;
+
+  /// The logger to use.
+  final BuildLogger logger;
+
+  /// How many inline scripts were extracted.
+  int inlineScriptCounter = 0;
+
+  /// Id representing the dart script which lives in the primaryInput.
+  AssetId mainScript;
+
+  /// Ids of all the scripts found in html imports.
+  final Set<AssetId> importScripts = new Set<AssetId>();
+
+  ScriptCompactor(this.transform, this.primaryInput, this.logger);
+
+  Future<Asset> run() {
+    var crawler = new ImportCrawler(transform, primaryInput, logger);
+    return crawler.crawlImports().then((imports) {
+      Future extractScripts(id) =>
+          _extractInlineScripts(id, imports[id].document);
+
+      return Future.forEach(imports.keys, extractScripts).then((_) {
+        if (mainScript == null) {
+          logger.error(
+              exactlyOneScriptPerEntryPoint.create({'url': primaryInput.path}));
+          return null;
+        }
+
+        var primaryDocument = imports[primaryInput].document;
+        assert(primaryDocument != null);
+
+        // Create the new bootstrap file and return its AssetId.
+        return _buildBootstrapFile(mainScript, importScripts);
+      });
+    });
+  }
+
+  /// Builds the bootstrap file and returns the path to it relative to
+  /// [primaryInput].
+  Asset _buildBootstrapFile(AssetId mainScript, Set<AssetId> importScripts) {
+    var bootstrapId = new AssetId(primaryInput.package,
+        primaryInput.path.replaceFirst('.html', '.bootstrap.dart'));
+
+    var buffer = new StringBuffer();
+    buffer.writeln('library ${_libraryNameFor(bootstrapId)};');
+    buffer.writeln();
+    var i = 0;
+    for (var script in importScripts) {
+      var path = _importPath(script, primaryInput);
+      buffer.writeln("import '$path' as i$i;");
+      i++;
+    }
+    var mainScriptPath = _importPath(mainScript, primaryInput);
+    buffer.writeln("import '$mainScriptPath' as i$i;");
+    buffer.writeln();
+    buffer.writeln('main() => i$i.main();');
+
+    var bootstrap = new Asset.fromString(bootstrapId, '$buffer');
+    transform.addOutput(bootstrap);
+    return bootstrap;
+  }
+
+  /// Split inline scripts into their own files. We need to do this for dart2js
+  /// to be able to compile them.
+  ///
+  /// This also validates that there weren't any duplicate scripts.
+  Future _extractInlineScripts(AssetId asset, dom.Document doc) {
+    var scripts = doc.querySelectorAll('script[type="$dartType"]');
+    return Future.forEach(scripts, (script) {
+      var type = script.attributes['type'];
+      var src = script.attributes['src'];
+
+      if (src != null) {
+        return _addScript(
+            asset, uriToAssetId(asset, src, logger, script.sourceSpan),
+            span: script.sourceSpan);
+      }
+
+      final count = inlineScriptCounter++;
+      var code = script.text;
+      // TODO(sigmund): ensure this path is unique (dartbug.com/12618).
+      var newId = primaryInput.addExtension('.$count.dart');
+      if (!_hasLibraryDirective(code)) {
+        var libName = _libraryNameFor(primaryInput, count);
+        code = "library $libName;\n$code";
+      }
+
+      // Normalize dart import paths.
+      code = _normalizeDartImports(code, asset, primaryInput);
+
+      // Write out the file and record it.
+      transform.addOutput(new Asset.fromString(newId, code));
+
+      return _addScript(asset, newId, validate: false).then((_) {
+        // If in the entry point, replace the inline script with one pointing to
+        // the new source file.
+        if (primaryInput == asset) {
+          script.text = '';
+          script.attributes['src'] = path.url.relative(newId.path,
+              from: path.url.dirname(primaryInput.path));
+        }
+      });
+    });
+  }
+
+  // Normalize dart import paths when moving code from one asset to another.
+  String _normalizeDartImports(String code, AssetId from, AssetId to) {
+    var unit = parseDirectives(code, suppressErrors: true);
+    var file = new SourceFile(code, url: spanUrlFor(from, to, logger));
+    var output = new TextEditTransaction(code, file);
+    var foundLibraryDirective = false;
+    for (Directive directive in unit.directives) {
+      if (directive is UriBasedDirective) {
+        var uri = directive.uri.stringValue;
+        var span = getSpan(file, directive.uri);
+
+        var id = uriToAssetId(from, uri, logger, span, errorOnAbsolute: false);
+        if (id == null) continue;
+
+        var primaryId = primaryInput;
+        var newUri = assetUrlFor(id, primaryId, logger);
+        if (newUri != uri) {
+          output.edit(span.start.offset, span.end.offset, "'$newUri'");
+        }
+      } else if (directive is LibraryDirective) {
+        foundLibraryDirective = true;
+      }
+    }
+
+    if (!output.hasEdits) return code;
+
+    // TODO(sigmund): emit source maps when barback supports it (see
+    // dartbug.com/12340)
+    return (output.commit()..build(file.url.toString())).text;
+  }
+
+  Future _addScript(AssetId from, AssetId scriptId,
+      {bool validate: true, SourceSpan span}) {
+    var validateFuture;
+    if (validate && !importScripts.contains(scriptId)) {
+      validateFuture = transform.hasInput(scriptId);
+    } else {
+      validateFuture = new Future.value(true);
+    }
+    return validateFuture.then((exists) {
+      if (!exists) {
+        logger.warning(scriptFileNotFound.create({'url': scriptId}),
+            span: span);
+      }
+
+      if (from == primaryInput) {
+        if (mainScript != null) {
+          logger
+              .error(exactlyOneScriptPerEntryPoint.create({'url': from.path}));
+        }
+        mainScript = scriptId;
+      } else {
+        importScripts.add(scriptId);
+      }
+    });
+  }
+}
+
+/// Generate a library name for an asset.
+String _libraryNameFor(AssetId id, [int suffix]) {
+  var name = '${path.withoutExtension(id.path)}_'
+      '${path.extension(id.path).substring(1)}';
+  if (name.startsWith('lib/')) name = name.substring(4);
+  name = name.split('/').map((part) {
+    part = part.replaceAll(_invalidLibCharsRegex, '_');
+    if (part.startsWith(_numRegex)) part = '_${part}';
+    return part;
+  }).join(".");
+  var suffixString = suffix != null ? '_$suffix' : '';
+  return '${id.package}.${name}$suffixString';
+}
+
+/// Parse [code] and determine whether it has a library directive.
+bool _hasLibraryDirective(String code) =>
+    parseDirectives(code, suppressErrors: true).directives
+        .any((d) => d is LibraryDirective);
+
+/// Returns the dart import path to reach [id] relative to [primaryInput].
+String _importPath(AssetId id, AssetId primaryInput) {
+  var parts = path.url.split(id.path);
+  if (parts[0] == 'lib') {
+    parts[0] = id.package;
+    return 'package:${path.url.joinAll(parts)}';
+  }
+  return path.url.relative(id.path, from: path.url.dirname(primaryInput.path));
+}
+
+// Constant and final variables
+final _invalidLibCharsRegex = new RegExp('[^a-z0-9_]');
+final _numRegex = new RegExp('[0-9]');
diff --git a/web_components/lib/build/test_compatibility.dart b/web_components/lib/build/test_compatibility.dart
new file mode 100644
index 0000000..4687502
--- /dev/null
+++ b/web_components/lib/build/test_compatibility.dart
@@ -0,0 +1,79 @@
+// Copyright (c) 2015, 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.
+
+/// Some Transformers to maintain compatibility with the new `test` package,
+/// since it doesn't use normal dart script tags in html. We get around this by
+/// using two transformers:
+///   - A transformer to convert <lint rel="x-dart-test"> intro dart script tags
+///     so that they can be processed by the rest of the transformers normally.
+///   - A second transformer to convert the output back, so that the `test`
+///     package can find a <link rel="x-dart-test"> tag after all the
+///     transformations are done.
+library web_components.build.test_compatability.dart;
+
+import 'dart:async';
+import 'package:barback/barback.dart';
+import 'package:html/dom.dart';
+import 'package:html/parser.dart';
+
+/// The name of the attribute that will be added to script tags that started out
+/// as <link rel="x-dart-test"> tags.
+const testAttribute = '_was_test';
+
+/// The first transformer that should be ran, this does a query selector for
+/// link[rel="x-dart-test"] and changes them to a normal dart script tag with a
+/// special `_was_test` attribute so we know to change it back later on.
+class RewriteXDartTestToScript extends _EntryPointOnlyTransformer {
+  RewriteXDartTestToScript(List<String> entryPoints) : super(entryPoints);
+
+  Future apply(Transform transform) {
+    return transform.primaryInput.readAsString().then((String html) {
+      var doc = parse(html);
+      for (var tag in doc.querySelectorAll('link[rel="x-dart-test"]')) {
+        tag.replaceWith(new Element.tag('script')
+          ..attributes['type'] = 'application/dart'
+          ..attributes['src'] = tag.attributes['href']
+          ..attributes[testAttribute] = '');
+      }
+      transform.addOutput(
+          new Asset.fromString(transform.primaryInput.id, doc.outerHtml));
+    });
+  }
+}
+
+/// The last transformer that should be ran, this does a query selector for
+/// `script[type="application/dart"][_was_test]` and changes matching elements
+/// back to `<link rel="x-dart-test">` tags.
+class RewriteScriptToXDartTest extends _EntryPointOnlyTransformer {
+  RewriteScriptToXDartTest(List<String> entryPoints) : super(entryPoints);
+
+  Future apply(Transform transform) {
+    return transform.primaryInput.readAsString().then((String html) {
+      var doc = parse(html);
+      var scripts = doc.querySelectorAll(
+          'script[type="application/dart"][$testAttribute]');
+      for (var tag in scripts) {
+        tag.replaceWith(new Element.tag('link')
+          ..attributes['rel'] = 'x-dart-test'
+          ..attributes['href'] = tag.attributes['src']);
+      }
+      transform.addOutput(
+          new Asset.fromString(transform.primaryInput.id, doc.outerHtml));
+    });
+  }
+}
+
+/// Internal base class to encapsulate the isPrimary logic.
+abstract class _EntryPointOnlyTransformer extends Transformer {
+  final List<String> entryPoints;
+
+  _EntryPointOnlyTransformer(this.entryPoints) : super();
+
+  bool isPrimary(AssetId id) {
+    if (!id.path.startsWith('test/')) return false;
+    if (entryPoints != null) return entryPoints.contains(id.path);
+    // If no entry point is supplied, then any html file is an entry point.
+    return id.path.endsWith('.html');
+  }
+}
diff --git a/web_components/lib/build/web_components.dart b/web_components/lib/build/web_components.dart
new file mode 100644
index 0000000..4a71606
--- /dev/null
+++ b/web_components/lib/build/web_components.dart
@@ -0,0 +1,97 @@
+// Copyright (c) 2015, 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.
+
+/// Transformer used for pub serve and pub build
+library web_components.build.web_components;
+
+import 'dart:async';
+import 'package:barback/barback.dart';
+import 'package:code_transformers/assets.dart';
+import 'package:code_transformers/messages/build_logger.dart';
+import 'package:code_transformers/resolver.dart';
+import 'package:code_transformers/src/dart_sdk.dart' as dart_sdk;
+import 'package:html/dom.dart' as dom;
+import 'package:initialize/transformer.dart' show generateBootstrapFile;
+import 'package:initialize/build/initializer_plugin.dart';
+import 'package:path/path.dart' as path;
+import 'package:web_components/transformer.dart';
+import 'common.dart';
+
+/// Public method that can be used inside any [Transformer] which already has a
+/// [Resolver] and [Transform] to generate a bootstrap file for the
+/// web_components package.
+Asset generateWebComponentsBootstrap(Resolver resolver, Transform transform,
+    dom.Document document, AssetId scriptId, AssetId newScriptId,
+    {List<InitializerPlugin> extraPlugins: const []}) {
+  var htmlImportRecorder = new HtmlImportAnnotationRecorder();
+  var plugins = [htmlImportRecorder]..addAll(extraPlugins);
+
+  // Bootstrap the application using the `initialize` package and our
+  // plugins.
+  var initializeBootstrap = generateBootstrapFile(
+      resolver, transform, scriptId, newScriptId,
+      errorIfNotFound: false, plugins: plugins);
+
+  // Add all seen imports to the document, before the first dart script tag if
+  // it exists.
+  var dartScript =
+      document.head.querySelector('script[type="application/dart"]');
+  for (var importPath in htmlImportRecorder.importPaths) {
+    var import = new dom.Element.tag('link')
+      ..attributes = {'rel': 'import', 'href': importPath,};
+    document.head.insertBefore(import, dartScript);
+  }
+
+  return initializeBootstrap;
+}
+
+/// A [Transformer] which runs the `initialize` transformer with
+/// some special plugins and also inlines the html imports.
+class WebComponentsTransformer extends Transformer {
+  final Resolvers _resolvers;
+  TransformOptions options;
+
+  WebComponentsTransformer(this.options)
+      : _resolvers = new Resolvers.fromMock(dart_sdk.mockSdkSources);
+
+  bool isPrimary(AssetId id) {
+    if (options.entryPoints != null) {
+      return options.entryPoints.contains(id.path);
+    }
+    if (id.path == 'web/index.bootstrap.dart') return true;
+    // If no entry point is supplied, then any html file under web/ or test/ is
+    // an entry point.
+    return (id.path.startsWith('web/') || id.path.startsWith('test/')) &&
+        id.path.endsWith('.html');
+  }
+
+  Future apply(Transform transform) {
+    var logger = new BuildLogger(transform);
+    var primaryInput = transform.primaryInput;
+    return primaryInput.readAsString().then((html) {
+      // Find the dart script in the page.
+      var doc = parseHtml(html, primaryInput.id.path);
+      var mainScriptTag = doc.querySelector('script[type="$dartType"]');
+      var scriptId = uriToAssetId(primaryInput.id,
+          mainScriptTag.attributes['src'], logger, mainScriptTag.sourceSpan);
+
+      return _resolvers.get(transform, [scriptId]).then((resolver) {
+        var newScriptId = new AssetId(scriptId.package,
+            '${path.url.withoutExtension(scriptId.path)}.initialize.dart');
+
+        var bootstrap = generateWebComponentsBootstrap(
+            resolver, transform, doc, scriptId, newScriptId);
+
+        // Swap out the main script tag for the bootstrap version.
+        mainScriptTag.attributes['src'] = path.url.relative(bootstrap.id.path,
+            from: path.url.dirname(primaryInput.id.path));
+
+        // Output the new document and bootstrap file.
+        transform
+            .addOutput(new Asset.fromString(primaryInput.id, doc.outerHtml));
+        transform.addOutput(bootstrap);
+      });
+    });
+  }
+}
diff --git a/web_components/lib/custom_element_proxy.dart b/web_components/lib/custom_element_proxy.dart
new file mode 100644
index 0000000..4f57097
--- /dev/null
+++ b/web_components/lib/custom_element_proxy.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2015, 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 web_components.custom_element_proxy;
+
+import 'dart:js' as js;
+import 'package:initialize/initialize.dart';
+import 'interop.dart';
+
+/// Annotation for a dart class which proxies a javascript custom element.
+/// This will not work unless `interop_support.js` is loaded.
+// TODO(jakemac): Add an @HtmlImport here to a new file which includes
+// `interop_support.js`. We will need to point everything else at that html file
+// as well for deduplication purposes (could even just copy it in as an inline
+// script so the js file no longer exists?).
+class CustomElementProxy implements Initializer<Type> {
+  final String tagName;
+  final String extendsTag;
+
+  const CustomElementProxy(this.tagName, {this.extendsTag});
+
+  void initialize(Type t) {
+    registerDartType(tagName, t, extendsTag: extendsTag);
+  }
+}
+
+/// A simple mixin to make it easier to interoperate with the Javascript API of
+/// a browser object. This is mainly used by classes that expose a Dart API for
+/// Javascript custom elements.
+class CustomElementProxyMixin {
+  js.JsObject _proxy;
+
+  js.JsObject get jsElement {
+    if (_proxy == null) {
+      _proxy = new js.JsObject.fromBrowserObject(this);
+    }
+    return _proxy;
+  }
+}
diff --git a/web_components/lib/dart_support.js b/web_components/lib/dart_support.js
new file mode 100644
index 0000000..3f712ef
--- /dev/null
+++ b/web_components/lib/dart_support.js
@@ -0,0 +1,68 @@
+// 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.
+
+// Teaches dart2js about the wrapping that is done by the Shadow DOM polyfill.
+(function() {
+  var ShadowDOMPolyfill = window.ShadowDOMPolyfill;
+  if (!ShadowDOMPolyfill) return;
+
+  // TODO(sigmund): remove the userAgent check once 1.6 rolls as stable.
+  // See: dartbug.com/18463
+  if (navigator.dartEnabled || (navigator.userAgent.indexOf('(Dart)') !== -1)) {
+    console.error("ShadowDOMPolyfill polyfill was loaded in Dartium. This " +
+        "will not work. This indicates that Dartium's Chrome version is " +
+        "not compatible with this version of web_components.");
+  }
+
+  var needsConstructorFix = window.constructor === window.Window;
+
+  // TODO(jmesserly): we need to wrap document somehow (a dart:html hook?)
+
+  // dartNativeDispatchHooksTransformer is described on initHooks() in
+  // sdk/lib/_internal/lib/native_helper.dart.
+  if (typeof window.dartNativeDispatchHooksTransformer == 'undefined')
+    window.dartNativeDispatchHooksTransformer = [];
+
+  window.dartNativeDispatchHooksTransformer.push(function(hooks) {
+    var NodeList = ShadowDOMPolyfill.wrappers.NodeList;
+    var ShadowRoot = ShadowDOMPolyfill.wrappers.ShadowRoot;
+    var unwrapIfNeeded = ShadowDOMPolyfill.unwrapIfNeeded;
+    var originalGetTag = hooks.getTag;
+    hooks.getTag = function getTag(obj) {
+      // TODO(jmesserly): do we still need these?
+      if (obj instanceof NodeList) return 'NodeList';
+      if (obj instanceof ShadowRoot) return 'ShadowRoot';
+      if (MutationRecord && (obj instanceof MutationRecord))
+          return 'MutationRecord';
+      if (MutationObserver && (obj instanceof MutationObserver))
+          return 'MutationObserver';
+
+      // TODO(jmesserly): this prevents incorrect interaction between ShadowDOM
+      // and dart:html's <template> polyfill. Essentially, ShadowDOM is
+      // polyfilling native template, but our Dart polyfill fails to detect this
+      // because the unwrapped node is an HTMLUnknownElement, leading it to
+      // think the node has no content.
+      if (obj instanceof HTMLTemplateElement) return 'HTMLTemplateElement';
+
+      var unwrapped = unwrapIfNeeded(obj);
+      if (unwrapped && (needsConstructorFix || obj !== unwrapped)) {
+        // Fix up class names for Firefox, or if using the minified polyfill.
+        // dart2js prefers .constructor.name, but there are all kinds of cases
+        // where this will give the wrong answer.
+        var ctor = obj.constructor
+        if (ctor === unwrapped.constructor) {
+          var name = ctor._ShadowDOMPolyfill$cacheTag_;
+          if (!name) {
+            name = originalGetTag(unwrapped);
+            ctor._ShadowDOMPolyfill$cacheTag_ = name;
+          }
+          return name;
+        }
+
+        obj = unwrapped;
+      }
+      return originalGetTag(obj);
+    }
+  });
+})();
diff --git a/web_components/lib/html_import_annotation.dart b/web_components/lib/html_import_annotation.dart
new file mode 100644
index 0000000..04738d5
--- /dev/null
+++ b/web_components/lib/html_import_annotation.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2015, 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 web_components.html_import_annotation;
+
+import 'dart:async';
+import 'dart:html';
+import 'package:initialize/initialize.dart';
+import 'src/normalize_path.dart';
+
+/// Annotation for a dart library which injects an html import into the
+/// current html document. The imported file must not contain any dart script
+/// tags, as they cannot be dynamically loaded.
+class HtmlImport implements Initializer<LibraryIdentifier> {
+  final String filePath;
+
+  const HtmlImport(this.filePath);
+
+  Future initialize(LibraryIdentifier library) {
+    var element = new LinkElement()
+      ..rel = 'import'
+      ..href = normalizeHtmlImportPath(filePath, library.package, library.path);
+    document.head.append(element);
+    var completer = new Completer();
+    var listeners = [
+      element.on['load'].listen((_) => completer.complete()),
+      element.on['error'].listen((_) {
+        print('Error loading html import from path `$filePath`');
+        completer.complete();
+      }),
+    ];
+    return completer.future.then((_) {
+      listeners.forEach((listener) => listener.cancel());
+    });
+  }
+}
diff --git a/web_components/lib/init.dart b/web_components/lib/init.dart
new file mode 100644
index 0000000..1ebb141
--- /dev/null
+++ b/web_components/lib/init.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2015, 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 web_components.init;
+
+import 'dart:async';
+import 'src/init.dart';
+
+Future main() => initWebComponents();
diff --git a/web_components/lib/interop.dart b/web_components/lib/interop.dart
new file mode 100644
index 0000000..4779b80
--- /dev/null
+++ b/web_components/lib/interop.dart
@@ -0,0 +1,52 @@
+// 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.
+
+/// Provides support for associating a Dart type for Javascript Custom Elements.
+/// This will not work unless `dart_support.js` is loaded.
+library web_components.interop;
+
+import 'dart:html' show document, Element;
+import 'dart:js' show JsObject, JsFunction;
+
+final _doc = new JsObject.fromBrowserObject(document);
+
+/// Returns whether [registerDartType] is supported, which requires to have
+/// `dart_support.js` already loaded in the page.
+bool get isSupported => _doc.hasProperty('_registerDartTypeUpgrader');
+
+/// Watches when Javascript custom elements named [tagName] are created and
+/// associates the created element with the given [dartType]. Only one Dart type
+/// can be registered for a given tag name.
+void registerDartType(String tagName, Type dartType, {String extendsTag}) {
+  if (!isSupported) {
+    throw new UnsupportedError("Couldn't find "
+        "`document._registerDartTypeUpgrader`. Please make sure that "
+        "`packages/web_components/interop_support.html` is loaded and "
+        "available before calling this function.");
+  }
+
+  var upgrader =
+      document.createElementUpgrader(dartType, extendsTag: extendsTag);
+
+  // Unfortunately the dart:html upgrader will throw on an already-upgraded
+  // element, so we need to duplicate the type check to prevent that.
+  // An element can be upgraded twice if it extends another element and calls
+  // createdCallback on the superclass. Since that's a valid use case we must
+  // wrap at both levels, and guard against it here.
+  upgradeElement(e) {
+    if (e.runtimeType != dartType) upgrader.upgrade(e);
+  }
+
+  _doc.callMethod('_registerDartTypeUpgrader', [tagName, upgradeElement]);
+}
+
+/// This function is mainly used to save resources. By default, we save a log of
+/// elements that are created but have no Dart type associated with them. This
+/// is so we can upgrade them as soon as [registerDartType] is invoked. This
+/// function can be called to indicate that we no longer are interested in
+/// logging element creations and that it is sufficient to only upgrade new
+/// elements as they are being created. Typically this is called after the last
+/// call to [registerDartType] or as soon as you know that no element will be
+/// created until the call to [registerDartType] is made.
+void onlyUpgradeNewElements() => _doc.callMethod('_onlyUpgradeNewElements');
diff --git a/web_components/lib/interop_support.html b/web_components/lib/interop_support.html
new file mode 100644
index 0000000..469510c
--- /dev/null
+++ b/web_components/lib/interop_support.html
@@ -0,0 +1,6 @@
+<!--
+ Copyright 2014 The Dart project authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file.
+-->
+<script src="interop_support.js"></script>
diff --git a/web_components/lib/interop_support.js b/web_components/lib/interop_support.js
new file mode 100644
index 0000000..b4e80bd
--- /dev/null
+++ b/web_components/lib/interop_support.js
@@ -0,0 +1,126 @@
+// 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.
+
+// Updates document.registerElement so Dart can see when Javascript custom
+// elements are created, and wrap them to provide a Dart friendly API.
+(function (doc) {
+  if (window._dart_register_element_interop_support) return;
+  window._dart_register_element_interop_support = true;
+
+  var upgraders = {};       // upgrader associated with a custom-tag.
+  var unpatchableTags = {}; // set of custom-tags that can't be patched.
+  var pendingElements = {}; // will upgrade when/if an upgrader is installed.
+  var upgradeOldElements = true;
+
+  var originalRegisterElement = doc.registerElement;
+  if (!originalRegisterElement) {
+    throw new Error('document.registerElement is not present.');
+  }
+
+  function reportError(name) {
+    console.error("Couldn't patch prototype to notify Dart when " + name +
+        " elements are created. This can be fixed by making the " +
+        "createdCallback in " + name + " a configurable property.");
+  }
+
+  function registerElement(name, options) {
+    var proto, extendsOption;
+    if (options !== undefined) {
+      proto = options.prototype;
+    } else {
+      proto = Object.create(HTMLElement.prototype);
+      options = {protoptype: proto};
+    }
+
+    var original = proto.createdCallback;
+    var newCallback = function() {
+      if (original) original.call(this);
+      var name = (this.getAttribute('is') || this.localName).toLowerCase();
+      var upgrader = upgraders[name];
+      if (upgrader) {
+        upgrader(this);
+      } else if (upgradeOldElements) {
+        // Save this element in case we can upgrade it later when an upgrader is
+        // registered.
+        var list = pendingElements[name];
+        if (!list) {
+          list = pendingElements[name] = [];
+        }
+        list.push(this);
+      }
+    };
+
+    var descriptor = Object.getOwnPropertyDescriptor(proto, 'createdCallback');
+    if (!descriptor || descriptor.writable) {
+      proto.createdCallback = newCallback;
+    } else if (descriptor.configurable) {
+      descriptor['value'] = newCallback;
+      Object.defineProperty(proto, 'createdCallback', descriptor);
+    } else {
+      unpatchableTags[name] = true;
+      if (upgraders[name]) reportError(name);
+    }
+    return originalRegisterElement.call(this, name, options);
+  }
+
+  function registerDartTypeUpgrader(name, upgrader) {
+    if (!upgrader) return;
+    name = name.toLowerCase();
+    var existing = upgraders[name];
+    if (existing) {
+      console.error('Already have a Dart type associated with ' + name);
+      return;
+    }
+    upgraders[name] = upgrader;
+    if (unpatchableTags[name]) reportError(name);
+    if (upgradeOldElements) {
+      // Upgrade elements that were created before the upgrader was registered.
+      var list = pendingElements[name];
+      if (list) {
+        for (var i = 0; i < list.length; i++) {
+          upgrader(list[i]);
+        }
+      }
+      delete pendingElements[name];
+    } else {
+      console.warn("Didn't expect more Dart types to be registered. '" + name
+          + "' elements that already exist in the page might not be wrapped.");
+    }
+  }
+
+  function onlyUpgradeNewElements() {
+    upgradeOldElements = false;
+    pendingElements = null;
+  }
+
+  // Native custom elements outside the app in Chrome have constructor
+  // names like "x-tag", which need to be translated to the DOM
+  // element they extend.  When using the shadow dom polyfill this is
+  // taken care of in dart_support.js.
+  var ShadowDOMPolyfill = window.ShadowDOMPolyfill;
+  if (!ShadowDOMPolyfill) {
+    // dartNativeDispatchHooksTransformer is described on initHooks() in
+    // sdk/lib/_internal/lib/native_helper.dart.
+    if (typeof window.dartNativeDispatchHooksTransformer == 'undefined')
+    window.dartNativeDispatchHooksTransformer = [];
+
+    window.dartNativeDispatchHooksTransformer.push(function(hooks) {
+      var originalGetUnknownTag = hooks.getUnknownTag;
+      hooks.getUnknownTag = function(o, tag) {
+        if (/-/.test(tag)) {  // "x-tag"
+          var s = Object.prototype.toString.call(o);
+          var match = s.match(/^\[object ([A-Za-z]*Element)\]$/);
+          if (match) {
+            return match[1];
+          }
+        }
+        return originalGetUnknownTag(o, tag);
+      };
+    });
+  }
+
+  doc._registerDartTypeUpgrader = registerDartTypeUpgrader;
+  doc._onlyUpgradeNewElements = onlyUpgradeNewElements;
+  doc.registerElement = registerElement;
+})(document);
diff --git a/web_components/lib/polyfill.dart b/web_components/lib/polyfill.dart
new file mode 100644
index 0000000..6e7bbd9
--- /dev/null
+++ b/web_components/lib/polyfill.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2013, 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.
+
+/** Dart APIs for interacting with the JavaScript Custom Elements polyfill. */
+library web_components.polyfill;
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:js' as js;
+
+/**
+ * A future that completes once all custom elements in the initial HTML page
+ * have been upgraded.
+ *
+ * This is needed because the native implementation can update the elements
+ * while parsing the HTML document, but the custom element polyfill cannot,
+ * so it completes this future once all elements are upgraded.
+ */
+// TODO(jmesserly): rename to webComponentsReady to match the event?
+Future customElementsReady = () {
+  if (_isReady) return new Future.value();
+
+  // Not upgraded. Wait for the polyfill to fire the WebComponentsReady event.
+  // Note: we listen on document (not on document.body) to allow this polyfill
+  // to be loaded in the HEAD element.
+  return document.on['WebComponentsReady'].first;
+}();
+
+// Return true if we are using the polyfill and upgrade is complete, or if we
+// have native document.register and therefore the browser took care of it.
+// Otherwise return false, including the case where we can't find the polyfill.
+bool get _isReady {
+  // If we don't have dart:js, assume things are ready
+  if (js.context == null) return true;
+
+  var customElements = js.context['CustomElements'];
+  if (customElements == null) {
+    // Return true if native document.register, otherwise false.
+    // (Maybe the polyfill isn't loaded yet. Wait for it.)
+    return document.supportsRegister;
+  }
+
+  return customElements['ready'] == true;
+}
+
+/**
+ * *Note* this API is primarily intended for tests. In other code it is better
+ * to write it in a style that works with or without the polyfill, rather than
+ * using this method.
+ *
+ * Synchronously trigger evaluation of pending lifecycle events, which otherwise
+ * need to wait for a [MutationObserver] to signal the changes in the polyfill.
+ * This method can be used to resolve differences in timing between native and
+ * polyfilled custom elements.
+ */
+void customElementsTakeRecords([Node node]) {
+  var customElements = js.context['CustomElements'];
+  if (customElements == null) return;
+  customElements.callMethod('takeRecords', [node]);
+}
diff --git a/web_components/lib/src/custom_element.dart b/web_components/lib/src/custom_element.dart
new file mode 100644
index 0000000..1678fcc
--- /dev/null
+++ b/web_components/lib/src/custom_element.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2015, 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 web_components.custom_element;
+
+import 'dart:html' show document;
+import 'package:initialize/initialize.dart';
+
+/// Annotation which registers a custom element with the main document.
+class CustomElement implements Initializer<Type> {
+  /// The tag you want to register the class to handle.
+  final String tag;
+
+  /// If this element extends a native html element, then this is the tag that
+  /// represents that element.
+  final String extendsTag;
+
+  const CustomElement(this.tag, {this.extendsTag});
+
+  void initialize(Type t) =>
+      document.registerElement(tag, t, extendsTag: extendsTag);
+}
diff --git a/web_components/lib/src/init.dart b/web_components/lib/src/init.dart
new file mode 100644
index 0000000..d2906ef
--- /dev/null
+++ b/web_components/lib/src/init.dart
@@ -0,0 +1,31 @@
+// Copyright (c) 2015, 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 web_components.src.init;
+
+import 'dart:async';
+import 'package:initialize/initialize.dart' show InitializerFilter;
+import 'package:web_components/web_components.dart';
+import 'mirror_initializer.dart' as init;
+export 'mirror_initializer.dart' show deployMode;
+
+/// Performs html import aware initialization by crawling all imported documents
+/// and initializing any script tags which appear in them.
+///
+/// By default, this will run all [HtmlImport] initializers, followed in a 2nd
+/// phase by all [CustomElement] and [CustomElementProxy] initializers. Then,
+/// unless [initAll] is [false], it will run all remaining initializers.
+///
+/// If a [typeFilter] or [customFilter] are supplied, only one phase is ran
+/// with the supplied filters.
+Future initWebComponents({List<Type> typeFilter, InitializerFilter customFilter,
+    bool initAll: true}) {
+  if (typeFilter != null || customFilter != null) {
+    return init.run(typeFilter: typeFilter, customFilter: customFilter);
+  } else {
+    return init
+        .run(typeFilter: [HtmlImport])
+        .then((_) => init.run(typeFilter: [CustomElement, CustomElementProxy]))
+        .then((_) => initAll ? init.run() : null);
+  }
+}
diff --git a/web_components/lib/src/mirror_initializer.dart b/web_components/lib/src/mirror_initializer.dart
new file mode 100644
index 0000000..1e01b86
--- /dev/null
+++ b/web_components/lib/src/mirror_initializer.dart
@@ -0,0 +1,197 @@
+// 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.
+
+/// Contains logic to initialize web_components apps during development. This
+/// implementation uses dart:mirrors to load each library as they are discovered
+/// through HTML imports. This is only meant to be used during development in
+/// dartium, and the web_components transformers replace this implementation
+/// for deployment.
+library web_components.src.mirror_initializer;
+
+import 'dart:async';
+import 'dart:collection' show LinkedHashMap;
+import 'dart:mirrors';
+import 'dart:html';
+import 'package:initialize/initialize.dart' as init;
+import 'package:path/path.dart' show url;
+
+const bool deployMode = false;
+
+Future run({List<Type> typeFilter, init.InitializerFilter customFilter}) async {
+  var libraryUris =
+      _discoverLibrariesToLoad(document, window.location.href).map(Uri.parse);
+
+  for (var uri in libraryUris) {
+    await init.run(
+        typeFilter: typeFilter, customFilter: customFilter, from: uri);
+  }
+
+  _validatePackageImports(document);
+  return null;
+}
+
+/// Walks the HTML import structure to discover all script tags that are
+/// implicitly loaded. This code is only used in Dartium and should only be
+/// called after all HTML imports are resolved. Polymer ensures this by asking
+/// users to put their Dart script tags after all HTML imports (this is checked
+/// by the linter, and Dartium will otherwise show an error message).
+Iterable<_ScriptInfo> _discoverScripts(Document doc, String baseUri,
+    [_State state]) {
+  if (state == null) state = new _State();
+  if (doc == null) {
+    print('warning: $baseUri not found.');
+    return state.scripts.values;
+  }
+  if (!state.seen.add(doc)) return state.scripts.values;
+
+  for (var node in doc.querySelectorAll('script,link[rel="import"]')) {
+    if (node is LinkElement) {
+      _discoverScripts(node.import, node.href, state);
+    } else if (node is ScriptElement && node.type == 'application/dart') {
+      var info = _scriptInfoFor(node, baseUri);
+      if (state.scripts.containsKey(info.resolvedUrl)) {
+        // TODO(jakemac): Move this to a web_components uri.
+        print('warning: Script `${info.resolvedUrl}` included more than once. '
+            'See http://goo.gl/5HPeuP#polymer_44 for more details.');
+      } else {
+        state.scripts[info.resolvedUrl] = info;
+      }
+    }
+  }
+  return state.scripts.values;
+}
+
+/// Internal state used in [_discoverScripts].
+class _State {
+  /// Documents that we have visited thus far.
+  final Set<Document> seen = new Set();
+
+  /// Scripts that have been discovered, in tree order.
+  final LinkedHashMap<String, _ScriptInfo> scripts = {};
+}
+
+/// Holds information about a Dart script tag.
+class _ScriptInfo {
+  /// The original URL seen in the tag fully resolved.
+  final String resolvedUrl;
+
+  /// Whether it seems to be a 'package:' URL (starts with the package-root).
+  bool get isPackage => packageUrl != null;
+
+  /// The equivalent 'package:' URL, if any.
+  final String packageUrl;
+
+  _ScriptInfo(this.resolvedUrl, {this.packageUrl});
+}
+
+// TODO(sigmund): explore other (cheaper) ways to resolve URIs relative to the
+// root library (see dartbug.com/12612)
+final _rootUri = currentMirrorSystem().isolate.rootLibrary.uri;
+
+/// Returns [_ScriptInfo] for [script] which was seen in [baseUri].
+_ScriptInfo _scriptInfoFor(script, baseUri) {
+  var uriString = script.src;
+  if (uriString != '') {
+    var uri = _rootUri.resolve(uriString);
+    if (!_isHttpStylePackageUrl(uri)) return new _ScriptInfo('$uri');
+    // Use package: urls if available. This rule here is more permissive than
+    // how we translate urls in polymer-build, but we expect Dartium to limit
+    // the cases where there are differences. The polymer-build issues an error
+    // when using packages/ inside lib without properly stepping out all the way
+    // to the packages folder. If users don't create symlinks in the source
+    // tree, then Dartium will also complain because it won't find the file seen
+    // in an HTML import.
+    var packagePath = uri.path
+        .substring(uri.path.lastIndexOf('packages/') + 'packages/'.length);
+    return new _ScriptInfo('$uri', packageUrl: 'package:$packagePath');
+  }
+
+  // Even in the case of inline scripts its ok to just use the baseUri since
+  // there can only be one per page.
+  return new _ScriptInfo(baseUri);
+}
+
+/// Whether [uri] is an http URI that contains a 'packages' segment, and
+/// therefore could be converted into a 'package:' URI.
+bool _isHttpStylePackageUrl(Uri uri) {
+  var uriPath = uri.path;
+  return uri.scheme == _rootUri.scheme &&
+      // Don't process cross-domain uris.
+      uri.authority == _rootUri.authority &&
+      uriPath.endsWith('.dart') &&
+      (uriPath.contains('/packages/') || uriPath.startsWith('packages/'));
+}
+
+Iterable<String> _discoverLibrariesToLoad(Document doc, String baseUri) =>
+    _discoverScripts(doc, baseUri).map(
+        (info) => _packageUrlExists(info) ? info.packageUrl : info.resolvedUrl);
+
+/// All libraries in the current isolate.
+final _libs = currentMirrorSystem().libraries;
+
+bool _packageUrlExists(_ScriptInfo info) =>
+    info.isPackage && _libs[Uri.parse(info.packageUrl)] != null;
+
+/// All the imports that we have checked for package path validation.
+final _importsSeen = new Set<LinkElement>();
+
+/// All the documents that we have crawled for import validation.
+final _documentsSeen = new Set<Document>();
+
+/// Validates that all html imports to packages urls point to the right packages
+/// symlink (the one next to the entry point).
+void _validatePackageImports(Document doc) {
+  var imports = doc.querySelectorAll('link[rel="import"]');
+  for (LinkElement import in imports) {
+    // Don't re-validate imports.
+    if (!_importsSeen.add(import)) continue;
+
+    // Check that the href points to the right packages path. If it doesn't we
+    // don't continue checking as it will print extraneous errors.
+    if (import.href.contains('packages/') && !_checkPackagePath(import)) {
+      continue;
+    }
+
+    // Validate any imports contained in this import, if the document hasn't yet
+    // been seen.
+    var importDoc = import.import;
+    if (importDoc == null || !_documentsSeen.add(importDoc)) continue;
+    _validatePackageImports(importDoc);
+  }
+}
+
+/// The path to the entry document.
+final entryPath = document.baseUri;
+
+/// Checks that the relative path from the entry point to all packages imports
+/// starts with `packages/`.
+bool _checkPackagePath(LinkElement import) {
+  var pathFromEntryPoint =
+      url.relative(import.href, from: url.dirname(entryPath));
+  if (pathFromEntryPoint.startsWith('packages/')) return true;
+
+  LinkElement correctedImport = import.clone(false);
+
+  var relativeUriParts = url.split(
+      url.relative(import.ownerDocument.baseUri, from: url.dirname(entryPath)));
+  var pathToEntryPoint = '../' * (relativeUriParts.length - 1);
+  var packagePath = import.href.substring(import.href.indexOf('packages/'));
+  correctedImport.href = '$pathToEntryPoint$packagePath';
+
+  // TODO(jakemac): Throw an exception here at the next breaking change, and add
+  // a test at that point (no easy way to test console.error).
+  window.console.error('''
+Found bad packages uri in html import. All packages uris should point to the
+packages symlink in the same folder as the entry point.
+
+Entry point: $entryPath
+Owner document: ${import.ownerDocument.baseUri}
+Current import: ${import.outerHtml}
+Corrected import: ${correctedImport.outerHtml}
+
+For more information, please see:
+https://www.dartlang.org/polymer/app-directories.html#into-a-non-dart-non-entry-point
+''');
+  return false;
+}
diff --git a/web_components/lib/src/normalize_path.dart b/web_components/lib/src/normalize_path.dart
new file mode 100644
index 0000000..129dfe6
--- /dev/null
+++ b/web_components/lib/src/normalize_path.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2015, 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 web_components.src.normalizePath;
+
+import 'package:path/path.dart' as path;
+
+String normalizeHtmlImportPath(
+    String filePath, String dartFilePackage, String dartFilePath) {
+  // If they already supplied a packages path, just return that.
+  if (filePath.startsWith('package:')) {
+    return filePath.replaceFirst('package:', 'packages/');
+  }
+
+  var dartFileDir = path.url.dirname(dartFilePath);
+
+  // Relative paths have no package supplied.
+  if (dartFilePackage == null) {
+    return path.url.normalize(path.url.join(dartFileDir, filePath));
+  }
+
+  // Only option left is a packages/ path.
+  return path.url.normalize(
+      path.url.join('packages/', dartFilePackage, dartFileDir, filePath));
+}
diff --git a/web_components/lib/src/static_initializer.dart b/web_components/lib/src/static_initializer.dart
new file mode 100644
index 0000000..ca4b37b
--- /dev/null
+++ b/web_components/lib/src/static_initializer.dart
@@ -0,0 +1,15 @@
+// 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.
+
+/// Static logic for initializing web_components. This assumes the entire app
+/// is reachable from the entry point.
+library web_components.src.static_initializer;
+
+import 'dart:async';
+import 'package:initialize/initialize.dart' as init;
+
+const bool deployMode = true;
+
+Future run({List<Type> typeFilter, init.InitializerFilter customFilter}) =>
+    init.run(typeFilter: typeFilter, customFilter: customFilter);
diff --git a/web_components/lib/transformer.dart b/web_components/lib/transformer.dart
new file mode 100644
index 0000000..fe0ca34
--- /dev/null
+++ b/web_components/lib/transformer.dart
@@ -0,0 +1,122 @@
+// Copyright (c) 2015, 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.
+
+/// Transformer used for pub serve and pub build
+library web_components.transformer;
+
+import 'package:barback/barback.dart';
+
+export 'build/html_import_annotation_recorder.dart';
+import 'build/import_inliner.dart';
+export 'build/import_inliner.dart';
+import 'build/script_compactor.dart';
+export 'build/script_compactor.dart';
+import 'build/test_compatibility.dart';
+export 'build/test_compatibility.dart';
+import 'build/web_components.dart';
+export 'build/web_components.dart';
+
+/// The Web Components transformer group, which internally runs several phases
+/// that:
+///   * Extract inlined script tags into separate files.
+///   * Extract script tags from entry points and all html imports and add them
+///     as dart imports in a new *.bootstrap.dart file.
+///   * Run the `initialize` transformer.
+///   * Inlines @HtmlImport annotations as html imports into the entry point.
+///   * Inline imported html files and remove all but the main dart script tag.
+///
+/// At the end of these phases, this tranformer produces a single entrypoint
+/// HTML file with a single Dart script that can later be compiled with dart2js.
+class WebComponentsTransformerGroup implements TransformerGroup {
+  final Iterable<Iterable> phases;
+
+  WebComponentsTransformerGroup(TransformOptions options)
+      : phases = createDeployPhases(options);
+
+  WebComponentsTransformerGroup.asPlugin(BarbackSettings settings)
+      : this(_parseSettings(settings));
+}
+
+/// Create deploy phases for web_components.
+List<List<Transformer>> createDeployPhases(TransformOptions options,
+    {String sdkDir}) {
+  var phases = [];
+
+  /// Must happen first, temporarily rewrites <link rel="x-dart-test"> tags to
+  /// <script type="application/dart" _was_test></script> tags.
+  phases.add([new RewriteXDartTestToScript(options.entryPoints)]);
+
+  // Must happen before the WebComponents transformer, grabs all dart scripts
+  // and combines them into one bootstrap file.
+  phases.add([new ScriptCompactorTransformer(options.entryPoints)]);
+
+  // Runs the customized version of the `initialize` transformer and inlines
+  // @HtmlImport annotations.
+  phases.add([new WebComponentsTransformer(options)]);
+
+  // Inlines all html imports and removes all dart script tags in the process.
+  phases.add([new ImportInlinerTransformer(options.entryPoints)]);
+
+  /// Must happen last, rewrites
+  /// <script type="application/dart" _was_test></script> tags to
+  /// <link rel="x-dart-test"> tags.
+  phases.add([new RewriteScriptToXDartTest(options.entryPoints)]);
+  return phases;
+}
+
+/// Options used by web_components transformers
+class TransformOptions {
+  /// List of entrypoints paths. The paths are relative to the package root and
+  /// are represented using posix style, which matches the representation used
+  /// in asset ids in barback. If null, any html file under 'web/' or 'test/' is
+  /// considered an entry point.
+  final List<String> entryPoints;
+
+  /// Current Barback mode.
+  final bool releaseMode;
+
+  TransformOptions(this.entryPoints, this.releaseMode);
+
+  /// Whether an asset with [id] is an entry point HTML file.
+  bool isHtmlEntryPoint(AssetId id) {
+    if (id.extension != '.html') return false;
+
+    // Note: [id.path] is a relative path from the root of a package.
+    if (entryPoints == null) {
+      return id.path.startsWith('web/') || id.path.startsWith('test/');
+    }
+
+    return entryPoints.contains(id.path);
+  }
+}
+
+// Builds TransformOptions given some BarbackSettings
+TransformOptions _parseSettings(BarbackSettings settings) {
+  var args = settings.configuration;
+  bool releaseMode = settings.mode == BarbackMode.RELEASE;
+  var entryPoints = readFileList(args['entry_points']);
+  return new TransformOptions(entryPoints, releaseMode);
+}
+
+/// Reads a file list value from the [BarbackSettings]
+/// TODO(jakemac): This should also move to code_transformers.
+readFileList(value) {
+  if (value == null) return null;
+  var files = [];
+  bool error;
+  if (value is List) {
+    files = value;
+    error = value.any((e) => e is! String);
+  } else if (value is String) {
+    files = [value];
+    error = false;
+  } else {
+    error = true;
+  }
+  if (error) {
+    print('Bad value for "entry_points" in the web_components transformer. '
+        'Expected either one String or a list of Strings.');
+  }
+  return files;
+}
diff --git a/web_components/lib/web_components.dart b/web_components/lib/web_components.dart
new file mode 100644
index 0000000..9ad6d84
--- /dev/null
+++ b/web_components/lib/web_components.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2015, 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 web_components;
+
+export 'src/custom_element.dart';
+export 'src/init.dart';
+export 'custom_element_proxy.dart';
+export 'html_import_annotation.dart';
diff --git a/web_components/lib/webcomponents.js b/web_components/lib/webcomponents.js
new file mode 100644
index 0000000..8e6523a
--- /dev/null
+++ b/web_components/lib/webcomponents.js
@@ -0,0 +1,6373 @@
+/**
+ * @license
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+ * Code distributed by Google as part of the polymer project is also
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+ */
+// @version 0.5.1
+window.WebComponents = window.WebComponents || {};
+
+(function(scope) {
+  var flags = scope.flags || {};
+  var file = "webcomponents.js";
+  var script = document.querySelector('script[src*="' + file + '"]');
+  if (!flags.noOpts) {
+    location.search.slice(1).split("&").forEach(function(o) {
+      o = o.split("=");
+      o[0] && (flags[o[0]] = o[1] || true);
+    });
+    if (script) {
+      for (var i = 0, a; a = script.attributes[i]; i++) {
+        if (a.name !== "src") {
+          flags[a.name] = a.value || true;
+        }
+      }
+    }
+    if (flags.log) {
+      var parts = flags.log.split(",");
+      flags.log = {};
+      parts.forEach(function(f) {
+        flags.log[f] = true;
+      });
+    } else {
+      flags.log = {};
+    }
+  }
+  flags.shadow = flags.shadow || flags.shadowdom || flags.polyfill;
+  if (flags.shadow === "native") {
+    flags.shadow = false;
+  } else {
+    flags.shadow = flags.shadow || !HTMLElement.prototype.createShadowRoot;
+  }
+  if (flags.register) {
+    window.CustomElements = window.CustomElements || {
+      flags: {}
+    };
+    window.CustomElements.flags.register = flags.register;
+  }
+  scope.flags = flags;
+})(WebComponents);
+
+if (WebComponents.flags.shadow) {
+  if (typeof WeakMap === "undefined") {
+    (function() {
+      var defineProperty = Object.defineProperty;
+      var counter = Date.now() % 1e9;
+      var WeakMap = function() {
+        this.name = "__st" + (Math.random() * 1e9 >>> 0) + (counter++ + "__");
+      };
+      WeakMap.prototype = {
+        set: function(key, value) {
+          var entry = key[this.name];
+          if (entry && entry[0] === key) entry[1] = value; else defineProperty(key, this.name, {
+            value: [ key, value ],
+            writable: true
+          });
+          return this;
+        },
+        get: function(key) {
+          var entry;
+          return (entry = key[this.name]) && entry[0] === key ? entry[1] : undefined;
+        },
+        "delete": function(key) {
+          var entry = key[this.name];
+          if (!entry || entry[0] !== key) return false;
+          entry[0] = entry[1] = undefined;
+          return true;
+        },
+        has: function(key) {
+          var entry = key[this.name];
+          if (!entry) return false;
+          return entry[0] === key;
+        }
+      };
+      window.WeakMap = WeakMap;
+    })();
+  }
+  window.ShadowDOMPolyfill = {};
+  (function(scope) {
+    "use strict";
+    var constructorTable = new WeakMap();
+    var nativePrototypeTable = new WeakMap();
+    var wrappers = Object.create(null);
+    function detectEval() {
+      if (typeof chrome !== "undefined" && chrome.app && chrome.app.runtime) {
+        return false;
+      }
+      if (navigator.getDeviceStorage) {
+        return false;
+      }
+      try {
+        var f = new Function("return true;");
+        return f();
+      } catch (ex) {
+        return false;
+      }
+    }
+    var hasEval = detectEval();
+    function assert(b) {
+      if (!b) throw new Error("Assertion failed");
+    }
+    var defineProperty = Object.defineProperty;
+    var getOwnPropertyNames = Object.getOwnPropertyNames;
+    var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
+    function mixin(to, from) {
+      var names = getOwnPropertyNames(from);
+      for (var i = 0; i < names.length; i++) {
+        var name = names[i];
+        defineProperty(to, name, getOwnPropertyDescriptor(from, name));
+      }
+      return to;
+    }
+    function mixinStatics(to, from) {
+      var names = getOwnPropertyNames(from);
+      for (var i = 0; i < names.length; i++) {
+        var name = names[i];
+        switch (name) {
+         case "arguments":
+         case "caller":
+         case "length":
+         case "name":
+         case "prototype":
+         case "toString":
+          continue;
+        }
+        defineProperty(to, name, getOwnPropertyDescriptor(from, name));
+      }
+      return to;
+    }
+    function oneOf(object, propertyNames) {
+      for (var i = 0; i < propertyNames.length; i++) {
+        if (propertyNames[i] in object) return propertyNames[i];
+      }
+    }
+    var nonEnumerableDataDescriptor = {
+      value: undefined,
+      configurable: true,
+      enumerable: false,
+      writable: true
+    };
+    function defineNonEnumerableDataProperty(object, name, value) {
+      nonEnumerableDataDescriptor.value = value;
+      defineProperty(object, name, nonEnumerableDataDescriptor);
+    }
+    getOwnPropertyNames(window);
+    function getWrapperConstructor(node) {
+      var nativePrototype = node.__proto__ || Object.getPrototypeOf(node);
+      var wrapperConstructor = constructorTable.get(nativePrototype);
+      if (wrapperConstructor) return wrapperConstructor;
+      var parentWrapperConstructor = getWrapperConstructor(nativePrototype);
+      var GeneratedWrapper = createWrapperConstructor(parentWrapperConstructor);
+      registerInternal(nativePrototype, GeneratedWrapper, node);
+      return GeneratedWrapper;
+    }
+    function addForwardingProperties(nativePrototype, wrapperPrototype) {
+      installProperty(nativePrototype, wrapperPrototype, true);
+    }
+    function registerInstanceProperties(wrapperPrototype, instanceObject) {
+      installProperty(instanceObject, wrapperPrototype, false);
+    }
+    var isFirefox = /Firefox/.test(navigator.userAgent);
+    var dummyDescriptor = {
+      get: function() {},
+      set: function(v) {},
+      configurable: true,
+      enumerable: true
+    };
+    function isEventHandlerName(name) {
+      return /^on[a-z]+$/.test(name);
+    }
+    function isIdentifierName(name) {
+      return /^\w[a-zA-Z_0-9]*$/.test(name);
+    }
+    function getGetter(name) {
+      return hasEval && isIdentifierName(name) ? new Function("return this.__impl4cf1e782hg__." + name) : function() {
+        return this.__impl4cf1e782hg__[name];
+      };
+    }
+    function getSetter(name) {
+      return hasEval && isIdentifierName(name) ? new Function("v", "this.__impl4cf1e782hg__." + name + " = v") : function(v) {
+        this.__impl4cf1e782hg__[name] = v;
+      };
+    }
+    function getMethod(name) {
+      return hasEval && isIdentifierName(name) ? new Function("return this.__impl4cf1e782hg__." + name + ".apply(this.__impl4cf1e782hg__, arguments)") : function() {
+        return this.__impl4cf1e782hg__[name].apply(this.__impl4cf1e782hg__, arguments);
+      };
+    }
+    function getDescriptor(source, name) {
+      try {
+        return Object.getOwnPropertyDescriptor(source, name);
+      } catch (ex) {
+        return dummyDescriptor;
+      }
+    }
+    var isBrokenSafari = function() {
+      var descr = Object.getOwnPropertyDescriptor(Node.prototype, "nodeType");
+      return descr && !descr.get && !descr.set;
+    }();
+    function installProperty(source, target, allowMethod, opt_blacklist) {
+      var names = getOwnPropertyNames(source);
+      for (var i = 0; i < names.length; i++) {
+        var name = names[i];
+        if (name === "polymerBlackList_") continue;
+        if (name in target) continue;
+        if (source.polymerBlackList_ && source.polymerBlackList_[name]) continue;
+        if (isFirefox) {
+          source.__lookupGetter__(name);
+        }
+        var descriptor = getDescriptor(source, name);
+        var getter, setter;
+        if (allowMethod && typeof descriptor.value === "function") {
+          target[name] = getMethod(name);
+          continue;
+        }
+        var isEvent = isEventHandlerName(name);
+        if (isEvent) getter = scope.getEventHandlerGetter(name); else getter = getGetter(name);
+        if (descriptor.writable || descriptor.set || isBrokenSafari) {
+          if (isEvent) setter = scope.getEventHandlerSetter(name); else setter = getSetter(name);
+        }
+        defineProperty(target, name, {
+          get: getter,
+          set: setter,
+          configurable: descriptor.configurable,
+          enumerable: descriptor.enumerable
+        });
+      }
+    }
+    function register(nativeConstructor, wrapperConstructor, opt_instance) {
+      var nativePrototype = nativeConstructor.prototype;
+      registerInternal(nativePrototype, wrapperConstructor, opt_instance);
+      mixinStatics(wrapperConstructor, nativeConstructor);
+    }
+    function registerInternal(nativePrototype, wrapperConstructor, opt_instance) {
+      var wrapperPrototype = wrapperConstructor.prototype;
+      assert(constructorTable.get(nativePrototype) === undefined);
+      constructorTable.set(nativePrototype, wrapperConstructor);
+      nativePrototypeTable.set(wrapperPrototype, nativePrototype);
+      addForwardingProperties(nativePrototype, wrapperPrototype);
+      if (opt_instance) registerInstanceProperties(wrapperPrototype, opt_instance);
+      defineNonEnumerableDataProperty(wrapperPrototype, "constructor", wrapperConstructor);
+      wrapperConstructor.prototype = wrapperPrototype;
+    }
+    function isWrapperFor(wrapperConstructor, nativeConstructor) {
+      return constructorTable.get(nativeConstructor.prototype) === wrapperConstructor;
+    }
+    function registerObject(object) {
+      var nativePrototype = Object.getPrototypeOf(object);
+      var superWrapperConstructor = getWrapperConstructor(nativePrototype);
+      var GeneratedWrapper = createWrapperConstructor(superWrapperConstructor);
+      registerInternal(nativePrototype, GeneratedWrapper, object);
+      return GeneratedWrapper;
+    }
+    function createWrapperConstructor(superWrapperConstructor) {
+      function GeneratedWrapper(node) {
+        superWrapperConstructor.call(this, node);
+      }
+      var p = Object.create(superWrapperConstructor.prototype);
+      p.constructor = GeneratedWrapper;
+      GeneratedWrapper.prototype = p;
+      return GeneratedWrapper;
+    }
+    function isWrapper(object) {
+      return object && object.__impl4cf1e782hg__;
+    }
+    function isNative(object) {
+      return !isWrapper(object);
+    }
+    function wrap(impl) {
+      if (impl === null) return null;
+      assert(isNative(impl));
+      return impl.__wrapper8e3dd93a60__ || (impl.__wrapper8e3dd93a60__ = new (getWrapperConstructor(impl))(impl));
+    }
+    function unwrap(wrapper) {
+      if (wrapper === null) return null;
+      assert(isWrapper(wrapper));
+      return wrapper.__impl4cf1e782hg__;
+    }
+    function unsafeUnwrap(wrapper) {
+      return wrapper.__impl4cf1e782hg__;
+    }
+    function setWrapper(impl, wrapper) {
+      wrapper.__impl4cf1e782hg__ = impl;
+      impl.__wrapper8e3dd93a60__ = wrapper;
+    }
+    function unwrapIfNeeded(object) {
+      return object && isWrapper(object) ? unwrap(object) : object;
+    }
+    function wrapIfNeeded(object) {
+      return object && !isWrapper(object) ? wrap(object) : object;
+    }
+    function rewrap(node, wrapper) {
+      if (wrapper === null) return;
+      assert(isNative(node));
+      assert(wrapper === undefined || isWrapper(wrapper));
+      node.__wrapper8e3dd93a60__ = wrapper;
+    }
+    var getterDescriptor = {
+      get: undefined,
+      configurable: true,
+      enumerable: true
+    };
+    function defineGetter(constructor, name, getter) {
+      getterDescriptor.get = getter;
+      defineProperty(constructor.prototype, name, getterDescriptor);
+    }
+    function defineWrapGetter(constructor, name) {
+      defineGetter(constructor, name, function() {
+        return wrap(this.__impl4cf1e782hg__[name]);
+      });
+    }
+    function forwardMethodsToWrapper(constructors, names) {
+      constructors.forEach(function(constructor) {
+        names.forEach(function(name) {
+          constructor.prototype[name] = function() {
+            var w = wrapIfNeeded(this);
+            return w[name].apply(w, arguments);
+          };
+        });
+      });
+    }
+    scope.assert = assert;
+    scope.constructorTable = constructorTable;
+    scope.defineGetter = defineGetter;
+    scope.defineWrapGetter = defineWrapGetter;
+    scope.forwardMethodsToWrapper = forwardMethodsToWrapper;
+    scope.isWrapper = isWrapper;
+    scope.isWrapperFor = isWrapperFor;
+    scope.mixin = mixin;
+    scope.nativePrototypeTable = nativePrototypeTable;
+    scope.oneOf = oneOf;
+    scope.registerObject = registerObject;
+    scope.registerWrapper = register;
+    scope.rewrap = rewrap;
+    scope.setWrapper = setWrapper;
+    scope.unsafeUnwrap = unsafeUnwrap;
+    scope.unwrap = unwrap;
+    scope.unwrapIfNeeded = unwrapIfNeeded;
+    scope.wrap = wrap;
+    scope.wrapIfNeeded = wrapIfNeeded;
+    scope.wrappers = wrappers;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    function newSplice(index, removed, addedCount) {
+      return {
+        index: index,
+        removed: removed,
+        addedCount: addedCount
+      };
+    }
+    var EDIT_LEAVE = 0;
+    var EDIT_UPDATE = 1;
+    var EDIT_ADD = 2;
+    var EDIT_DELETE = 3;
+    function ArraySplice() {}
+    ArraySplice.prototype = {
+      calcEditDistances: function(current, currentStart, currentEnd, old, oldStart, oldEnd) {
+        var rowCount = oldEnd - oldStart + 1;
+        var columnCount = currentEnd - currentStart + 1;
+        var distances = new Array(rowCount);
+        for (var i = 0; i < rowCount; i++) {
+          distances[i] = new Array(columnCount);
+          distances[i][0] = i;
+        }
+        for (var j = 0; j < columnCount; j++) distances[0][j] = j;
+        for (var i = 1; i < rowCount; i++) {
+          for (var j = 1; j < columnCount; j++) {
+            if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1])) distances[i][j] = distances[i - 1][j - 1]; else {
+              var north = distances[i - 1][j] + 1;
+              var west = distances[i][j - 1] + 1;
+              distances[i][j] = north < west ? north : west;
+            }
+          }
+        }
+        return distances;
+      },
+      spliceOperationsFromEditDistances: function(distances) {
+        var i = distances.length - 1;
+        var j = distances[0].length - 1;
+        var current = distances[i][j];
+        var edits = [];
+        while (i > 0 || j > 0) {
+          if (i == 0) {
+            edits.push(EDIT_ADD);
+            j--;
+            continue;
+          }
+          if (j == 0) {
+            edits.push(EDIT_DELETE);
+            i--;
+            continue;
+          }
+          var northWest = distances[i - 1][j - 1];
+          var west = distances[i - 1][j];
+          var north = distances[i][j - 1];
+          var min;
+          if (west < north) min = west < northWest ? west : northWest; else min = north < northWest ? north : northWest;
+          if (min == northWest) {
+            if (northWest == current) {
+              edits.push(EDIT_LEAVE);
+            } else {
+              edits.push(EDIT_UPDATE);
+              current = northWest;
+            }
+            i--;
+            j--;
+          } else if (min == west) {
+            edits.push(EDIT_DELETE);
+            i--;
+            current = west;
+          } else {
+            edits.push(EDIT_ADD);
+            j--;
+            current = north;
+          }
+        }
+        edits.reverse();
+        return edits;
+      },
+      calcSplices: function(current, currentStart, currentEnd, old, oldStart, oldEnd) {
+        var prefixCount = 0;
+        var suffixCount = 0;
+        var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
+        if (currentStart == 0 && oldStart == 0) prefixCount = this.sharedPrefix(current, old, minLength);
+        if (currentEnd == current.length && oldEnd == old.length) suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
+        currentStart += prefixCount;
+        oldStart += prefixCount;
+        currentEnd -= suffixCount;
+        oldEnd -= suffixCount;
+        if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0) return [];
+        if (currentStart == currentEnd) {
+          var splice = newSplice(currentStart, [], 0);
+          while (oldStart < oldEnd) splice.removed.push(old[oldStart++]);
+          return [ splice ];
+        } else if (oldStart == oldEnd) return [ newSplice(currentStart, [], currentEnd - currentStart) ];
+        var ops = this.spliceOperationsFromEditDistances(this.calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
+        var splice = undefined;
+        var splices = [];
+        var index = currentStart;
+        var oldIndex = oldStart;
+        for (var i = 0; i < ops.length; i++) {
+          switch (ops[i]) {
+           case EDIT_LEAVE:
+            if (splice) {
+              splices.push(splice);
+              splice = undefined;
+            }
+            index++;
+            oldIndex++;
+            break;
+
+           case EDIT_UPDATE:
+            if (!splice) splice = newSplice(index, [], 0);
+            splice.addedCount++;
+            index++;
+            splice.removed.push(old[oldIndex]);
+            oldIndex++;
+            break;
+
+           case EDIT_ADD:
+            if (!splice) splice = newSplice(index, [], 0);
+            splice.addedCount++;
+            index++;
+            break;
+
+           case EDIT_DELETE:
+            if (!splice) splice = newSplice(index, [], 0);
+            splice.removed.push(old[oldIndex]);
+            oldIndex++;
+            break;
+          }
+        }
+        if (splice) {
+          splices.push(splice);
+        }
+        return splices;
+      },
+      sharedPrefix: function(current, old, searchLength) {
+        for (var i = 0; i < searchLength; i++) if (!this.equals(current[i], old[i])) return i;
+        return searchLength;
+      },
+      sharedSuffix: function(current, old, searchLength) {
+        var index1 = current.length;
+        var index2 = old.length;
+        var count = 0;
+        while (count < searchLength && this.equals(current[--index1], old[--index2])) count++;
+        return count;
+      },
+      calculateSplices: function(current, previous) {
+        return this.calcSplices(current, 0, current.length, previous, 0, previous.length);
+      },
+      equals: function(currentValue, previousValue) {
+        return currentValue === previousValue;
+      }
+    };
+    scope.ArraySplice = ArraySplice;
+  })(window.ShadowDOMPolyfill);
+  (function(context) {
+    "use strict";
+    var OriginalMutationObserver = window.MutationObserver;
+    var callbacks = [];
+    var pending = false;
+    var timerFunc;
+    function handle() {
+      pending = false;
+      var copies = callbacks.slice(0);
+      callbacks = [];
+      for (var i = 0; i < copies.length; i++) {
+        (0, copies[i])();
+      }
+    }
+    if (OriginalMutationObserver) {
+      var counter = 1;
+      var observer = new OriginalMutationObserver(handle);
+      var textNode = document.createTextNode(counter);
+      observer.observe(textNode, {
+        characterData: true
+      });
+      timerFunc = function() {
+        counter = (counter + 1) % 2;
+        textNode.data = counter;
+      };
+    } else {
+      timerFunc = window.setTimeout;
+    }
+    function setEndOfMicrotask(func) {
+      callbacks.push(func);
+      if (pending) return;
+      pending = true;
+      timerFunc(handle, 0);
+    }
+    context.setEndOfMicrotask = setEndOfMicrotask;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var setEndOfMicrotask = scope.setEndOfMicrotask;
+    var wrapIfNeeded = scope.wrapIfNeeded;
+    var wrappers = scope.wrappers;
+    var registrationsTable = new WeakMap();
+    var globalMutationObservers = [];
+    var isScheduled = false;
+    function scheduleCallback(observer) {
+      if (observer.scheduled_) return;
+      observer.scheduled_ = true;
+      globalMutationObservers.push(observer);
+      if (isScheduled) return;
+      setEndOfMicrotask(notifyObservers);
+      isScheduled = true;
+    }
+    function notifyObservers() {
+      isScheduled = false;
+      while (globalMutationObservers.length) {
+        var notifyList = globalMutationObservers;
+        globalMutationObservers = [];
+        notifyList.sort(function(x, y) {
+          return x.uid_ - y.uid_;
+        });
+        for (var i = 0; i < notifyList.length; i++) {
+          var mo = notifyList[i];
+          mo.scheduled_ = false;
+          var queue = mo.takeRecords();
+          removeTransientObserversFor(mo);
+          if (queue.length) {
+            mo.callback_(queue, mo);
+          }
+        }
+      }
+    }
+    function MutationRecord(type, target) {
+      this.type = type;
+      this.target = target;
+      this.addedNodes = new wrappers.NodeList();
+      this.removedNodes = new wrappers.NodeList();
+      this.previousSibling = null;
+      this.nextSibling = null;
+      this.attributeName = null;
+      this.attributeNamespace = null;
+      this.oldValue = null;
+    }
+    function registerTransientObservers(ancestor, node) {
+      for (;ancestor; ancestor = ancestor.parentNode) {
+        var registrations = registrationsTable.get(ancestor);
+        if (!registrations) continue;
+        for (var i = 0; i < registrations.length; i++) {
+          var registration = registrations[i];
+          if (registration.options.subtree) registration.addTransientObserver(node);
+        }
+      }
+    }
+    function removeTransientObserversFor(observer) {
+      for (var i = 0; i < observer.nodes_.length; i++) {
+        var node = observer.nodes_[i];
+        var registrations = registrationsTable.get(node);
+        if (!registrations) return;
+        for (var j = 0; j < registrations.length; j++) {
+          var registration = registrations[j];
+          if (registration.observer === observer) registration.removeTransientObservers();
+        }
+      }
+    }
+    function enqueueMutation(target, type, data) {
+      var interestedObservers = Object.create(null);
+      var associatedStrings = Object.create(null);
+      for (var node = target; node; node = node.parentNode) {
+        var registrations = registrationsTable.get(node);
+        if (!registrations) continue;
+        for (var j = 0; j < registrations.length; j++) {
+          var registration = registrations[j];
+          var options = registration.options;
+          if (node !== target && !options.subtree) continue;
+          if (type === "attributes" && !options.attributes) continue;
+          if (type === "attributes" && options.attributeFilter && (data.namespace !== null || options.attributeFilter.indexOf(data.name) === -1)) {
+            continue;
+          }
+          if (type === "characterData" && !options.characterData) continue;
+          if (type === "childList" && !options.childList) continue;
+          var observer = registration.observer;
+          interestedObservers[observer.uid_] = observer;
+          if (type === "attributes" && options.attributeOldValue || type === "characterData" && options.characterDataOldValue) {
+            associatedStrings[observer.uid_] = data.oldValue;
+          }
+        }
+      }
+      for (var uid in interestedObservers) {
+        var observer = interestedObservers[uid];
+        var record = new MutationRecord(type, target);
+        if ("name" in data && "namespace" in data) {
+          record.attributeName = data.name;
+          record.attributeNamespace = data.namespace;
+        }
+        if (data.addedNodes) record.addedNodes = data.addedNodes;
+        if (data.removedNodes) record.removedNodes = data.removedNodes;
+        if (data.previousSibling) record.previousSibling = data.previousSibling;
+        if (data.nextSibling) record.nextSibling = data.nextSibling;
+        if (associatedStrings[uid] !== undefined) record.oldValue = associatedStrings[uid];
+        scheduleCallback(observer);
+        observer.records_.push(record);
+      }
+    }
+    var slice = Array.prototype.slice;
+    function MutationObserverOptions(options) {
+      this.childList = !!options.childList;
+      this.subtree = !!options.subtree;
+      if (!("attributes" in options) && ("attributeOldValue" in options || "attributeFilter" in options)) {
+        this.attributes = true;
+      } else {
+        this.attributes = !!options.attributes;
+      }
+      if ("characterDataOldValue" in options && !("characterData" in options)) this.characterData = true; else this.characterData = !!options.characterData;
+      if (!this.attributes && (options.attributeOldValue || "attributeFilter" in options) || !this.characterData && options.characterDataOldValue) {
+        throw new TypeError();
+      }
+      this.characterData = !!options.characterData;
+      this.attributeOldValue = !!options.attributeOldValue;
+      this.characterDataOldValue = !!options.characterDataOldValue;
+      if ("attributeFilter" in options) {
+        if (options.attributeFilter == null || typeof options.attributeFilter !== "object") {
+          throw new TypeError();
+        }
+        this.attributeFilter = slice.call(options.attributeFilter);
+      } else {
+        this.attributeFilter = null;
+      }
+    }
+    var uidCounter = 0;
+    function MutationObserver(callback) {
+      this.callback_ = callback;
+      this.nodes_ = [];
+      this.records_ = [];
+      this.uid_ = ++uidCounter;
+      this.scheduled_ = false;
+    }
+    MutationObserver.prototype = {
+      constructor: MutationObserver,
+      observe: function(target, options) {
+        target = wrapIfNeeded(target);
+        var newOptions = new MutationObserverOptions(options);
+        var registration;
+        var registrations = registrationsTable.get(target);
+        if (!registrations) registrationsTable.set(target, registrations = []);
+        for (var i = 0; i < registrations.length; i++) {
+          if (registrations[i].observer === this) {
+            registration = registrations[i];
+            registration.removeTransientObservers();
+            registration.options = newOptions;
+          }
+        }
+        if (!registration) {
+          registration = new Registration(this, target, newOptions);
+          registrations.push(registration);
+          this.nodes_.push(target);
+        }
+      },
+      disconnect: function() {
+        this.nodes_.forEach(function(node) {
+          var registrations = registrationsTable.get(node);
+          for (var i = 0; i < registrations.length; i++) {
+            var registration = registrations[i];
+            if (registration.observer === this) {
+              registrations.splice(i, 1);
+              break;
+            }
+          }
+        }, this);
+        this.records_ = [];
+      },
+      takeRecords: function() {
+        var copyOfRecords = this.records_;
+        this.records_ = [];
+        return copyOfRecords;
+      }
+    };
+    function Registration(observer, target, options) {
+      this.observer = observer;
+      this.target = target;
+      this.options = options;
+      this.transientObservedNodes = [];
+    }
+    Registration.prototype = {
+      addTransientObserver: function(node) {
+        if (node === this.target) return;
+        scheduleCallback(this.observer);
+        this.transientObservedNodes.push(node);
+        var registrations = registrationsTable.get(node);
+        if (!registrations) registrationsTable.set(node, registrations = []);
+        registrations.push(this);
+      },
+      removeTransientObservers: function() {
+        var transientObservedNodes = this.transientObservedNodes;
+        this.transientObservedNodes = [];
+        for (var i = 0; i < transientObservedNodes.length; i++) {
+          var node = transientObservedNodes[i];
+          var registrations = registrationsTable.get(node);
+          for (var j = 0; j < registrations.length; j++) {
+            if (registrations[j] === this) {
+              registrations.splice(j, 1);
+              break;
+            }
+          }
+        }
+      }
+    };
+    scope.enqueueMutation = enqueueMutation;
+    scope.registerTransientObservers = registerTransientObservers;
+    scope.wrappers.MutationObserver = MutationObserver;
+    scope.wrappers.MutationRecord = MutationRecord;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    function TreeScope(root, parent) {
+      this.root = root;
+      this.parent = parent;
+    }
+    TreeScope.prototype = {
+      get renderer() {
+        if (this.root instanceof scope.wrappers.ShadowRoot) {
+          return scope.getRendererForHost(this.root.host);
+        }
+        return null;
+      },
+      contains: function(treeScope) {
+        for (;treeScope; treeScope = treeScope.parent) {
+          if (treeScope === this) return true;
+        }
+        return false;
+      }
+    };
+    function setTreeScope(node, treeScope) {
+      if (node.treeScope_ !== treeScope) {
+        node.treeScope_ = treeScope;
+        for (var sr = node.shadowRoot; sr; sr = sr.olderShadowRoot) {
+          sr.treeScope_.parent = treeScope;
+        }
+        for (var child = node.firstChild; child; child = child.nextSibling) {
+          setTreeScope(child, treeScope);
+        }
+      }
+    }
+    function getTreeScope(node) {
+      if (node instanceof scope.wrappers.Window) {
+        debugger;
+      }
+      if (node.treeScope_) return node.treeScope_;
+      var parent = node.parentNode;
+      var treeScope;
+      if (parent) treeScope = getTreeScope(parent); else treeScope = new TreeScope(node, null);
+      return node.treeScope_ = treeScope;
+    }
+    scope.TreeScope = TreeScope;
+    scope.getTreeScope = getTreeScope;
+    scope.setTreeScope = setTreeScope;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
+    var getTreeScope = scope.getTreeScope;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var setWrapper = scope.setWrapper;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var unwrap = scope.unwrap;
+    var wrap = scope.wrap;
+    var wrappers = scope.wrappers;
+    var wrappedFuns = new WeakMap();
+    var listenersTable = new WeakMap();
+    var handledEventsTable = new WeakMap();
+    var currentlyDispatchingEvents = new WeakMap();
+    var targetTable = new WeakMap();
+    var currentTargetTable = new WeakMap();
+    var relatedTargetTable = new WeakMap();
+    var eventPhaseTable = new WeakMap();
+    var stopPropagationTable = new WeakMap();
+    var stopImmediatePropagationTable = new WeakMap();
+    var eventHandlersTable = new WeakMap();
+    var eventPathTable = new WeakMap();
+    function isShadowRoot(node) {
+      return node instanceof wrappers.ShadowRoot;
+    }
+    function rootOfNode(node) {
+      return getTreeScope(node).root;
+    }
+    function getEventPath(node, event) {
+      var path = [];
+      var current = node;
+      path.push(current);
+      while (current) {
+        var destinationInsertionPoints = getDestinationInsertionPoints(current);
+        if (destinationInsertionPoints && destinationInsertionPoints.length > 0) {
+          for (var i = 0; i < destinationInsertionPoints.length; i++) {
+            var insertionPoint = destinationInsertionPoints[i];
+            if (isShadowInsertionPoint(insertionPoint)) {
+              var shadowRoot = rootOfNode(insertionPoint);
+              var olderShadowRoot = shadowRoot.olderShadowRoot;
+              if (olderShadowRoot) path.push(olderShadowRoot);
+            }
+            path.push(insertionPoint);
+          }
+          current = destinationInsertionPoints[destinationInsertionPoints.length - 1];
+        } else {
+          if (isShadowRoot(current)) {
+            if (inSameTree(node, current) && eventMustBeStopped(event)) {
+              break;
+            }
+            current = current.host;
+            path.push(current);
+          } else {
+            current = current.parentNode;
+            if (current) path.push(current);
+          }
+        }
+      }
+      return path;
+    }
+    function eventMustBeStopped(event) {
+      if (!event) return false;
+      switch (event.type) {
+       case "abort":
+       case "error":
+       case "select":
+       case "change":
+       case "load":
+       case "reset":
+       case "resize":
+       case "scroll":
+       case "selectstart":
+        return true;
+      }
+      return false;
+    }
+    function isShadowInsertionPoint(node) {
+      return node instanceof HTMLShadowElement;
+    }
+    function getDestinationInsertionPoints(node) {
+      return scope.getDestinationInsertionPoints(node);
+    }
+    function eventRetargetting(path, currentTarget) {
+      if (path.length === 0) return currentTarget;
+      if (currentTarget instanceof wrappers.Window) currentTarget = currentTarget.document;
+      var currentTargetTree = getTreeScope(currentTarget);
+      var originalTarget = path[0];
+      var originalTargetTree = getTreeScope(originalTarget);
+      var relativeTargetTree = lowestCommonInclusiveAncestor(currentTargetTree, originalTargetTree);
+      for (var i = 0; i < path.length; i++) {
+        var node = path[i];
+        if (getTreeScope(node) === relativeTargetTree) return node;
+      }
+      return path[path.length - 1];
+    }
+    function getTreeScopeAncestors(treeScope) {
+      var ancestors = [];
+      for (;treeScope; treeScope = treeScope.parent) {
+        ancestors.push(treeScope);
+      }
+      return ancestors;
+    }
+    function lowestCommonInclusiveAncestor(tsA, tsB) {
+      var ancestorsA = getTreeScopeAncestors(tsA);
+      var ancestorsB = getTreeScopeAncestors(tsB);
+      var result = null;
+      while (ancestorsA.length > 0 && ancestorsB.length > 0) {
+        var a = ancestorsA.pop();
+        var b = ancestorsB.pop();
+        if (a === b) result = a; else break;
+      }
+      return result;
+    }
+    function getTreeScopeRoot(ts) {
+      if (!ts.parent) return ts;
+      return getTreeScopeRoot(ts.parent);
+    }
+    function relatedTargetResolution(event, currentTarget, relatedTarget) {
+      if (currentTarget instanceof wrappers.Window) currentTarget = currentTarget.document;
+      var currentTargetTree = getTreeScope(currentTarget);
+      var relatedTargetTree = getTreeScope(relatedTarget);
+      var relatedTargetEventPath = getEventPath(relatedTarget, event);
+      var lowestCommonAncestorTree;
+      var lowestCommonAncestorTree = lowestCommonInclusiveAncestor(currentTargetTree, relatedTargetTree);
+      if (!lowestCommonAncestorTree) lowestCommonAncestorTree = relatedTargetTree.root;
+      for (var commonAncestorTree = lowestCommonAncestorTree; commonAncestorTree; commonAncestorTree = commonAncestorTree.parent) {
+        var adjustedRelatedTarget;
+        for (var i = 0; i < relatedTargetEventPath.length; i++) {
+          var node = relatedTargetEventPath[i];
+          if (getTreeScope(node) === commonAncestorTree) return node;
+        }
+      }
+      return null;
+    }
+    function inSameTree(a, b) {
+      return getTreeScope(a) === getTreeScope(b);
+    }
+    var NONE = 0;
+    var CAPTURING_PHASE = 1;
+    var AT_TARGET = 2;
+    var BUBBLING_PHASE = 3;
+    var pendingError;
+    function dispatchOriginalEvent(originalEvent) {
+      if (handledEventsTable.get(originalEvent)) return;
+      handledEventsTable.set(originalEvent, true);
+      dispatchEvent(wrap(originalEvent), wrap(originalEvent.target));
+      if (pendingError) {
+        var err = pendingError;
+        pendingError = null;
+        throw err;
+      }
+    }
+    function isLoadLikeEvent(event) {
+      switch (event.type) {
+       case "load":
+       case "beforeunload":
+       case "unload":
+        return true;
+      }
+      return false;
+    }
+    function dispatchEvent(event, originalWrapperTarget) {
+      if (currentlyDispatchingEvents.get(event)) throw new Error("InvalidStateError");
+      currentlyDispatchingEvents.set(event, true);
+      scope.renderAllPending();
+      var eventPath;
+      var overrideTarget;
+      var win;
+      if (isLoadLikeEvent(event) && !event.bubbles) {
+        var doc = originalWrapperTarget;
+        if (doc instanceof wrappers.Document && (win = doc.defaultView)) {
+          overrideTarget = doc;
+          eventPath = [];
+        }
+      }
+      if (!eventPath) {
+        if (originalWrapperTarget instanceof wrappers.Window) {
+          win = originalWrapperTarget;
+          eventPath = [];
+        } else {
+          eventPath = getEventPath(originalWrapperTarget, event);
+          if (!isLoadLikeEvent(event)) {
+            var doc = eventPath[eventPath.length - 1];
+            if (doc instanceof wrappers.Document) win = doc.defaultView;
+          }
+        }
+      }
+      eventPathTable.set(event, eventPath);
+      if (dispatchCapturing(event, eventPath, win, overrideTarget)) {
+        if (dispatchAtTarget(event, eventPath, win, overrideTarget)) {
+          dispatchBubbling(event, eventPath, win, overrideTarget);
+        }
+      }
+      eventPhaseTable.set(event, NONE);
+      currentTargetTable.delete(event, null);
+      currentlyDispatchingEvents.delete(event);
+      return event.defaultPrevented;
+    }
+    function dispatchCapturing(event, eventPath, win, overrideTarget) {
+      var phase = CAPTURING_PHASE;
+      if (win) {
+        if (!invoke(win, event, phase, eventPath, overrideTarget)) return false;
+      }
+      for (var i = eventPath.length - 1; i > 0; i--) {
+        if (!invoke(eventPath[i], event, phase, eventPath, overrideTarget)) return false;
+      }
+      return true;
+    }
+    function dispatchAtTarget(event, eventPath, win, overrideTarget) {
+      var phase = AT_TARGET;
+      var currentTarget = eventPath[0] || win;
+      return invoke(currentTarget, event, phase, eventPath, overrideTarget);
+    }
+    function dispatchBubbling(event, eventPath, win, overrideTarget) {
+      var phase = BUBBLING_PHASE;
+      for (var i = 1; i < eventPath.length; i++) {
+        if (!invoke(eventPath[i], event, phase, eventPath, overrideTarget)) return;
+      }
+      if (win && eventPath.length > 0) {
+        invoke(win, event, phase, eventPath, overrideTarget);
+      }
+    }
+    function invoke(currentTarget, event, phase, eventPath, overrideTarget) {
+      var listeners = listenersTable.get(currentTarget);
+      if (!listeners) return true;
+      var target = overrideTarget || eventRetargetting(eventPath, currentTarget);
+      if (target === currentTarget) {
+        if (phase === CAPTURING_PHASE) return true;
+        if (phase === BUBBLING_PHASE) phase = AT_TARGET;
+      } else if (phase === BUBBLING_PHASE && !event.bubbles) {
+        return true;
+      }
+      if ("relatedTarget" in event) {
+        var originalEvent = unwrap(event);
+        var unwrappedRelatedTarget = originalEvent.relatedTarget;
+        if (unwrappedRelatedTarget) {
+          if (unwrappedRelatedTarget instanceof Object && unwrappedRelatedTarget.addEventListener) {
+            var relatedTarget = wrap(unwrappedRelatedTarget);
+            var adjusted = relatedTargetResolution(event, currentTarget, relatedTarget);
+            if (adjusted === target) return true;
+          } else {
+            adjusted = null;
+          }
+          relatedTargetTable.set(event, adjusted);
+        }
+      }
+      eventPhaseTable.set(event, phase);
+      var type = event.type;
+      var anyRemoved = false;
+      targetTable.set(event, target);
+      currentTargetTable.set(event, currentTarget);
+      listeners.depth++;
+      for (var i = 0, len = listeners.length; i < len; i++) {
+        var listener = listeners[i];
+        if (listener.removed) {
+          anyRemoved = true;
+          continue;
+        }
+        if (listener.type !== type || !listener.capture && phase === CAPTURING_PHASE || listener.capture && phase === BUBBLING_PHASE) {
+          continue;
+        }
+        try {
+          if (typeof listener.handler === "function") listener.handler.call(currentTarget, event); else listener.handler.handleEvent(event);
+          if (stopImmediatePropagationTable.get(event)) return false;
+        } catch (ex) {
+          if (!pendingError) pendingError = ex;
+        }
+      }
+      listeners.depth--;
+      if (anyRemoved && listeners.depth === 0) {
+        var copy = listeners.slice();
+        listeners.length = 0;
+        for (var i = 0; i < copy.length; i++) {
+          if (!copy[i].removed) listeners.push(copy[i]);
+        }
+      }
+      return !stopPropagationTable.get(event);
+    }
+    function Listener(type, handler, capture) {
+      this.type = type;
+      this.handler = handler;
+      this.capture = Boolean(capture);
+    }
+    Listener.prototype = {
+      equals: function(that) {
+        return this.handler === that.handler && this.type === that.type && this.capture === that.capture;
+      },
+      get removed() {
+        return this.handler === null;
+      },
+      remove: function() {
+        this.handler = null;
+      }
+    };
+    var OriginalEvent = window.Event;
+    OriginalEvent.prototype.polymerBlackList_ = {
+      returnValue: true,
+      keyLocation: true
+    };
+    function Event(type, options) {
+      if (type instanceof OriginalEvent) {
+        var impl = type;
+        if (!OriginalBeforeUnloadEvent && impl.type === "beforeunload" && !(this instanceof BeforeUnloadEvent)) {
+          return new BeforeUnloadEvent(impl);
+        }
+        setWrapper(impl, this);
+      } else {
+        return wrap(constructEvent(OriginalEvent, "Event", type, options));
+      }
+    }
+    Event.prototype = {
+      get target() {
+        return targetTable.get(this);
+      },
+      get currentTarget() {
+        return currentTargetTable.get(this);
+      },
+      get eventPhase() {
+        return eventPhaseTable.get(this);
+      },
+      get path() {
+        var eventPath = eventPathTable.get(this);
+        if (!eventPath) return [];
+        return eventPath.slice();
+      },
+      stopPropagation: function() {
+        stopPropagationTable.set(this, true);
+      },
+      stopImmediatePropagation: function() {
+        stopPropagationTable.set(this, true);
+        stopImmediatePropagationTable.set(this, true);
+      }
+    };
+    registerWrapper(OriginalEvent, Event, document.createEvent("Event"));
+    function unwrapOptions(options) {
+      if (!options || !options.relatedTarget) return options;
+      return Object.create(options, {
+        relatedTarget: {
+          value: unwrap(options.relatedTarget)
+        }
+      });
+    }
+    function registerGenericEvent(name, SuperEvent, prototype) {
+      var OriginalEvent = window[name];
+      var GenericEvent = function(type, options) {
+        if (type instanceof OriginalEvent) setWrapper(type, this); else return wrap(constructEvent(OriginalEvent, name, type, options));
+      };
+      GenericEvent.prototype = Object.create(SuperEvent.prototype);
+      if (prototype) mixin(GenericEvent.prototype, prototype);
+      if (OriginalEvent) {
+        try {
+          registerWrapper(OriginalEvent, GenericEvent, new OriginalEvent("temp"));
+        } catch (ex) {
+          registerWrapper(OriginalEvent, GenericEvent, document.createEvent(name));
+        }
+      }
+      return GenericEvent;
+    }
+    var UIEvent = registerGenericEvent("UIEvent", Event);
+    var CustomEvent = registerGenericEvent("CustomEvent", Event);
+    var relatedTargetProto = {
+      get relatedTarget() {
+        var relatedTarget = relatedTargetTable.get(this);
+        if (relatedTarget !== undefined) return relatedTarget;
+        return wrap(unwrap(this).relatedTarget);
+      }
+    };
+    function getInitFunction(name, relatedTargetIndex) {
+      return function() {
+        arguments[relatedTargetIndex] = unwrap(arguments[relatedTargetIndex]);
+        var impl = unwrap(this);
+        impl[name].apply(impl, arguments);
+      };
+    }
+    var mouseEventProto = mixin({
+      initMouseEvent: getInitFunction("initMouseEvent", 14)
+    }, relatedTargetProto);
+    var focusEventProto = mixin({
+      initFocusEvent: getInitFunction("initFocusEvent", 5)
+    }, relatedTargetProto);
+    var MouseEvent = registerGenericEvent("MouseEvent", UIEvent, mouseEventProto);
+    var FocusEvent = registerGenericEvent("FocusEvent", UIEvent, focusEventProto);
+    var defaultInitDicts = Object.create(null);
+    var supportsEventConstructors = function() {
+      try {
+        new window.FocusEvent("focus");
+      } catch (ex) {
+        return false;
+      }
+      return true;
+    }();
+    function constructEvent(OriginalEvent, name, type, options) {
+      if (supportsEventConstructors) return new OriginalEvent(type, unwrapOptions(options));
+      var event = unwrap(document.createEvent(name));
+      var defaultDict = defaultInitDicts[name];
+      var args = [ type ];
+      Object.keys(defaultDict).forEach(function(key) {
+        var v = options != null && key in options ? options[key] : defaultDict[key];
+        if (key === "relatedTarget") v = unwrap(v);
+        args.push(v);
+      });
+      event["init" + name].apply(event, args);
+      return event;
+    }
+    if (!supportsEventConstructors) {
+      var configureEventConstructor = function(name, initDict, superName) {
+        if (superName) {
+          var superDict = defaultInitDicts[superName];
+          initDict = mixin(mixin({}, superDict), initDict);
+        }
+        defaultInitDicts[name] = initDict;
+      };
+      configureEventConstructor("Event", {
+        bubbles: false,
+        cancelable: false
+      });
+      configureEventConstructor("CustomEvent", {
+        detail: null
+      }, "Event");
+      configureEventConstructor("UIEvent", {
+        view: null,
+        detail: 0
+      }, "Event");
+      configureEventConstructor("MouseEvent", {
+        screenX: 0,
+        screenY: 0,
+        clientX: 0,
+        clientY: 0,
+        ctrlKey: false,
+        altKey: false,
+        shiftKey: false,
+        metaKey: false,
+        button: 0,
+        relatedTarget: null
+      }, "UIEvent");
+      configureEventConstructor("FocusEvent", {
+        relatedTarget: null
+      }, "UIEvent");
+    }
+    var OriginalBeforeUnloadEvent = window.BeforeUnloadEvent;
+    function BeforeUnloadEvent(impl) {
+      Event.call(this, impl);
+    }
+    BeforeUnloadEvent.prototype = Object.create(Event.prototype);
+    mixin(BeforeUnloadEvent.prototype, {
+      get returnValue() {
+        return unsafeUnwrap(this).returnValue;
+      },
+      set returnValue(v) {
+        unsafeUnwrap(this).returnValue = v;
+      }
+    });
+    if (OriginalBeforeUnloadEvent) registerWrapper(OriginalBeforeUnloadEvent, BeforeUnloadEvent);
+    function isValidListener(fun) {
+      if (typeof fun === "function") return true;
+      return fun && fun.handleEvent;
+    }
+    function isMutationEvent(type) {
+      switch (type) {
+       case "DOMAttrModified":
+       case "DOMAttributeNameChanged":
+       case "DOMCharacterDataModified":
+       case "DOMElementNameChanged":
+       case "DOMNodeInserted":
+       case "DOMNodeInsertedIntoDocument":
+       case "DOMNodeRemoved":
+       case "DOMNodeRemovedFromDocument":
+       case "DOMSubtreeModified":
+        return true;
+      }
+      return false;
+    }
+    var OriginalEventTarget = window.EventTarget;
+    function EventTarget(impl) {
+      setWrapper(impl, this);
+    }
+    var methodNames = [ "addEventListener", "removeEventListener", "dispatchEvent" ];
+    [ Node, Window ].forEach(function(constructor) {
+      var p = constructor.prototype;
+      methodNames.forEach(function(name) {
+        Object.defineProperty(p, name + "_", {
+          value: p[name]
+        });
+      });
+    });
+    function getTargetToListenAt(wrapper) {
+      if (wrapper instanceof wrappers.ShadowRoot) wrapper = wrapper.host;
+      return unwrap(wrapper);
+    }
+    EventTarget.prototype = {
+      addEventListener: function(type, fun, capture) {
+        if (!isValidListener(fun) || isMutationEvent(type)) return;
+        var listener = new Listener(type, fun, capture);
+        var listeners = listenersTable.get(this);
+        if (!listeners) {
+          listeners = [];
+          listeners.depth = 0;
+          listenersTable.set(this, listeners);
+        } else {
+          for (var i = 0; i < listeners.length; i++) {
+            if (listener.equals(listeners[i])) return;
+          }
+        }
+        listeners.push(listener);
+        var target = getTargetToListenAt(this);
+        target.addEventListener_(type, dispatchOriginalEvent, true);
+      },
+      removeEventListener: function(type, fun, capture) {
+        capture = Boolean(capture);
+        var listeners = listenersTable.get(this);
+        if (!listeners) return;
+        var count = 0, found = false;
+        for (var i = 0; i < listeners.length; i++) {
+          if (listeners[i].type === type && listeners[i].capture === capture) {
+            count++;
+            if (listeners[i].handler === fun) {
+              found = true;
+              listeners[i].remove();
+            }
+          }
+        }
+        if (found && count === 1) {
+          var target = getTargetToListenAt(this);
+          target.removeEventListener_(type, dispatchOriginalEvent, true);
+        }
+      },
+      dispatchEvent: function(event) {
+        var nativeEvent = unwrap(event);
+        var eventType = nativeEvent.type;
+        handledEventsTable.set(nativeEvent, false);
+        scope.renderAllPending();
+        var tempListener;
+        if (!hasListenerInAncestors(this, eventType)) {
+          tempListener = function() {};
+          this.addEventListener(eventType, tempListener, true);
+        }
+        try {
+          return unwrap(this).dispatchEvent_(nativeEvent);
+        } finally {
+          if (tempListener) this.removeEventListener(eventType, tempListener, true);
+        }
+      }
+    };
+    function hasListener(node, type) {
+      var listeners = listenersTable.get(node);
+      if (listeners) {
+        for (var i = 0; i < listeners.length; i++) {
+          if (!listeners[i].removed && listeners[i].type === type) return true;
+        }
+      }
+      return false;
+    }
+    function hasListenerInAncestors(target, type) {
+      for (var node = unwrap(target); node; node = node.parentNode) {
+        if (hasListener(wrap(node), type)) return true;
+      }
+      return false;
+    }
+    if (OriginalEventTarget) registerWrapper(OriginalEventTarget, EventTarget);
+    function wrapEventTargetMethods(constructors) {
+      forwardMethodsToWrapper(constructors, methodNames);
+    }
+    var originalElementFromPoint = document.elementFromPoint;
+    function elementFromPoint(self, document, x, y) {
+      scope.renderAllPending();
+      var element = wrap(originalElementFromPoint.call(unsafeUnwrap(document), x, y));
+      if (!element) return null;
+      var path = getEventPath(element, null);
+      var idx = path.lastIndexOf(self);
+      if (idx == -1) return null; else path = path.slice(0, idx);
+      return eventRetargetting(path, self);
+    }
+    function getEventHandlerGetter(name) {
+      return function() {
+        var inlineEventHandlers = eventHandlersTable.get(this);
+        return inlineEventHandlers && inlineEventHandlers[name] && inlineEventHandlers[name].value || null;
+      };
+    }
+    function getEventHandlerSetter(name) {
+      var eventType = name.slice(2);
+      return function(value) {
+        var inlineEventHandlers = eventHandlersTable.get(this);
+        if (!inlineEventHandlers) {
+          inlineEventHandlers = Object.create(null);
+          eventHandlersTable.set(this, inlineEventHandlers);
+        }
+        var old = inlineEventHandlers[name];
+        if (old) this.removeEventListener(eventType, old.wrapped, false);
+        if (typeof value === "function") {
+          var wrapped = function(e) {
+            var rv = value.call(this, e);
+            if (rv === false) e.preventDefault(); else if (name === "onbeforeunload" && typeof rv === "string") e.returnValue = rv;
+          };
+          this.addEventListener(eventType, wrapped, false);
+          inlineEventHandlers[name] = {
+            value: value,
+            wrapped: wrapped
+          };
+        }
+      };
+    }
+    scope.elementFromPoint = elementFromPoint;
+    scope.getEventHandlerGetter = getEventHandlerGetter;
+    scope.getEventHandlerSetter = getEventHandlerSetter;
+    scope.wrapEventTargetMethods = wrapEventTargetMethods;
+    scope.wrappers.BeforeUnloadEvent = BeforeUnloadEvent;
+    scope.wrappers.CustomEvent = CustomEvent;
+    scope.wrappers.Event = Event;
+    scope.wrappers.EventTarget = EventTarget;
+    scope.wrappers.FocusEvent = FocusEvent;
+    scope.wrappers.MouseEvent = MouseEvent;
+    scope.wrappers.UIEvent = UIEvent;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var UIEvent = scope.wrappers.UIEvent;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var setWrapper = scope.setWrapper;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var wrap = scope.wrap;
+    var OriginalTouchEvent = window.TouchEvent;
+    if (!OriginalTouchEvent) return;
+    var nativeEvent;
+    try {
+      nativeEvent = document.createEvent("TouchEvent");
+    } catch (ex) {
+      return;
+    }
+    var nonEnumDescriptor = {
+      enumerable: false
+    };
+    function nonEnum(obj, prop) {
+      Object.defineProperty(obj, prop, nonEnumDescriptor);
+    }
+    function Touch(impl) {
+      setWrapper(impl, this);
+    }
+    Touch.prototype = {
+      get target() {
+        return wrap(unsafeUnwrap(this).target);
+      }
+    };
+    var descr = {
+      configurable: true,
+      enumerable: true,
+      get: null
+    };
+    [ "clientX", "clientY", "screenX", "screenY", "pageX", "pageY", "identifier", "webkitRadiusX", "webkitRadiusY", "webkitRotationAngle", "webkitForce" ].forEach(function(name) {
+      descr.get = function() {
+        return unsafeUnwrap(this)[name];
+      };
+      Object.defineProperty(Touch.prototype, name, descr);
+    });
+    function TouchList() {
+      this.length = 0;
+      nonEnum(this, "length");
+    }
+    TouchList.prototype = {
+      item: function(index) {
+        return this[index];
+      }
+    };
+    function wrapTouchList(nativeTouchList) {
+      var list = new TouchList();
+      for (var i = 0; i < nativeTouchList.length; i++) {
+        list[i] = new Touch(nativeTouchList[i]);
+      }
+      list.length = i;
+      return list;
+    }
+    function TouchEvent(impl) {
+      UIEvent.call(this, impl);
+    }
+    TouchEvent.prototype = Object.create(UIEvent.prototype);
+    mixin(TouchEvent.prototype, {
+      get touches() {
+        return wrapTouchList(unsafeUnwrap(this).touches);
+      },
+      get targetTouches() {
+        return wrapTouchList(unsafeUnwrap(this).targetTouches);
+      },
+      get changedTouches() {
+        return wrapTouchList(unsafeUnwrap(this).changedTouches);
+      },
+      initTouchEvent: function() {
+        throw new Error("Not implemented");
+      }
+    });
+    registerWrapper(OriginalTouchEvent, TouchEvent, nativeEvent);
+    scope.wrappers.Touch = Touch;
+    scope.wrappers.TouchEvent = TouchEvent;
+    scope.wrappers.TouchList = TouchList;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var wrap = scope.wrap;
+    var nonEnumDescriptor = {
+      enumerable: false
+    };
+    function nonEnum(obj, prop) {
+      Object.defineProperty(obj, prop, nonEnumDescriptor);
+    }
+    function NodeList() {
+      this.length = 0;
+      nonEnum(this, "length");
+    }
+    NodeList.prototype = {
+      item: function(index) {
+        return this[index];
+      }
+    };
+    nonEnum(NodeList.prototype, "item");
+    function wrapNodeList(list) {
+      if (list == null) return list;
+      var wrapperList = new NodeList();
+      for (var i = 0, length = list.length; i < length; i++) {
+        wrapperList[i] = wrap(list[i]);
+      }
+      wrapperList.length = length;
+      return wrapperList;
+    }
+    function addWrapNodeListMethod(wrapperConstructor, name) {
+      wrapperConstructor.prototype[name] = function() {
+        return wrapNodeList(unsafeUnwrap(this)[name].apply(unsafeUnwrap(this), arguments));
+      };
+    }
+    scope.wrappers.NodeList = NodeList;
+    scope.addWrapNodeListMethod = addWrapNodeListMethod;
+    scope.wrapNodeList = wrapNodeList;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    scope.wrapHTMLCollection = scope.wrapNodeList;
+    scope.wrappers.HTMLCollection = scope.wrappers.NodeList;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var EventTarget = scope.wrappers.EventTarget;
+    var NodeList = scope.wrappers.NodeList;
+    var TreeScope = scope.TreeScope;
+    var assert = scope.assert;
+    var defineWrapGetter = scope.defineWrapGetter;
+    var enqueueMutation = scope.enqueueMutation;
+    var getTreeScope = scope.getTreeScope;
+    var isWrapper = scope.isWrapper;
+    var mixin = scope.mixin;
+    var registerTransientObservers = scope.registerTransientObservers;
+    var registerWrapper = scope.registerWrapper;
+    var setTreeScope = scope.setTreeScope;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var unwrap = scope.unwrap;
+    var unwrapIfNeeded = scope.unwrapIfNeeded;
+    var wrap = scope.wrap;
+    var wrapIfNeeded = scope.wrapIfNeeded;
+    var wrappers = scope.wrappers;
+    function assertIsNodeWrapper(node) {
+      assert(node instanceof Node);
+    }
+    function createOneElementNodeList(node) {
+      var nodes = new NodeList();
+      nodes[0] = node;
+      nodes.length = 1;
+      return nodes;
+    }
+    var surpressMutations = false;
+    function enqueueRemovalForInsertedNodes(node, parent, nodes) {
+      enqueueMutation(parent, "childList", {
+        removedNodes: nodes,
+        previousSibling: node.previousSibling,
+        nextSibling: node.nextSibling
+      });
+    }
+    function enqueueRemovalForInsertedDocumentFragment(df, nodes) {
+      enqueueMutation(df, "childList", {
+        removedNodes: nodes
+      });
+    }
+    function collectNodes(node, parentNode, previousNode, nextNode) {
+      if (node instanceof DocumentFragment) {
+        var nodes = collectNodesForDocumentFragment(node);
+        surpressMutations = true;
+        for (var i = nodes.length - 1; i >= 0; i--) {
+          node.removeChild(nodes[i]);
+          nodes[i].parentNode_ = parentNode;
+        }
+        surpressMutations = false;
+        for (var i = 0; i < nodes.length; i++) {
+          nodes[i].previousSibling_ = nodes[i - 1] || previousNode;
+          nodes[i].nextSibling_ = nodes[i + 1] || nextNode;
+        }
+        if (previousNode) previousNode.nextSibling_ = nodes[0];
+        if (nextNode) nextNode.previousSibling_ = nodes[nodes.length - 1];
+        return nodes;
+      }
+      var nodes = createOneElementNodeList(node);
+      var oldParent = node.parentNode;
+      if (oldParent) {
+        oldParent.removeChild(node);
+      }
+      node.parentNode_ = parentNode;
+      node.previousSibling_ = previousNode;
+      node.nextSibling_ = nextNode;
+      if (previousNode) previousNode.nextSibling_ = node;
+      if (nextNode) nextNode.previousSibling_ = node;
+      return nodes;
+    }
+    function collectNodesNative(node) {
+      if (node instanceof DocumentFragment) return collectNodesForDocumentFragment(node);
+      var nodes = createOneElementNodeList(node);
+      var oldParent = node.parentNode;
+      if (oldParent) enqueueRemovalForInsertedNodes(node, oldParent, nodes);
+      return nodes;
+    }
+    function collectNodesForDocumentFragment(node) {
+      var nodes = new NodeList();
+      var i = 0;
+      for (var child = node.firstChild; child; child = child.nextSibling) {
+        nodes[i++] = child;
+      }
+      nodes.length = i;
+      enqueueRemovalForInsertedDocumentFragment(node, nodes);
+      return nodes;
+    }
+    function snapshotNodeList(nodeList) {
+      return nodeList;
+    }
+    function nodeWasAdded(node, treeScope) {
+      setTreeScope(node, treeScope);
+      node.nodeIsInserted_();
+    }
+    function nodesWereAdded(nodes, parent) {
+      var treeScope = getTreeScope(parent);
+      for (var i = 0; i < nodes.length; i++) {
+        nodeWasAdded(nodes[i], treeScope);
+      }
+    }
+    function nodeWasRemoved(node) {
+      setTreeScope(node, new TreeScope(node, null));
+    }
+    function nodesWereRemoved(nodes) {
+      for (var i = 0; i < nodes.length; i++) {
+        nodeWasRemoved(nodes[i]);
+      }
+    }
+    function ensureSameOwnerDocument(parent, child) {
+      var ownerDoc = parent.nodeType === Node.DOCUMENT_NODE ? parent : parent.ownerDocument;
+      if (ownerDoc !== child.ownerDocument) ownerDoc.adoptNode(child);
+    }
+    function adoptNodesIfNeeded(owner, nodes) {
+      if (!nodes.length) return;
+      var ownerDoc = owner.ownerDocument;
+      if (ownerDoc === nodes[0].ownerDocument) return;
+      for (var i = 0; i < nodes.length; i++) {
+        scope.adoptNodeNoRemove(nodes[i], ownerDoc);
+      }
+    }
+    function unwrapNodesForInsertion(owner, nodes) {
+      adoptNodesIfNeeded(owner, nodes);
+      var length = nodes.length;
+      if (length === 1) return unwrap(nodes[0]);
+      var df = unwrap(owner.ownerDocument.createDocumentFragment());
+      for (var i = 0; i < length; i++) {
+        df.appendChild(unwrap(nodes[i]));
+      }
+      return df;
+    }
+    function clearChildNodes(wrapper) {
+      if (wrapper.firstChild_ !== undefined) {
+        var child = wrapper.firstChild_;
+        while (child) {
+          var tmp = child;
+          child = child.nextSibling_;
+          tmp.parentNode_ = tmp.previousSibling_ = tmp.nextSibling_ = undefined;
+        }
+      }
+      wrapper.firstChild_ = wrapper.lastChild_ = undefined;
+    }
+    function removeAllChildNodes(wrapper) {
+      if (wrapper.invalidateShadowRenderer()) {
+        var childWrapper = wrapper.firstChild;
+        while (childWrapper) {
+          assert(childWrapper.parentNode === wrapper);
+          var nextSibling = childWrapper.nextSibling;
+          var childNode = unwrap(childWrapper);
+          var parentNode = childNode.parentNode;
+          if (parentNode) originalRemoveChild.call(parentNode, childNode);
+          childWrapper.previousSibling_ = childWrapper.nextSibling_ = childWrapper.parentNode_ = null;
+          childWrapper = nextSibling;
+        }
+        wrapper.firstChild_ = wrapper.lastChild_ = null;
+      } else {
+        var node = unwrap(wrapper);
+        var child = node.firstChild;
+        var nextSibling;
+        while (child) {
+          nextSibling = child.nextSibling;
+          originalRemoveChild.call(node, child);
+          child = nextSibling;
+        }
+      }
+    }
+    function invalidateParent(node) {
+      var p = node.parentNode;
+      return p && p.invalidateShadowRenderer();
+    }
+    function cleanupNodes(nodes) {
+      for (var i = 0, n; i < nodes.length; i++) {
+        n = nodes[i];
+        n.parentNode.removeChild(n);
+      }
+    }
+    var originalImportNode = document.importNode;
+    var originalCloneNode = window.Node.prototype.cloneNode;
+    function cloneNode(node, deep, opt_doc) {
+      var clone;
+      if (opt_doc) clone = wrap(originalImportNode.call(opt_doc, unsafeUnwrap(node), false)); else clone = wrap(originalCloneNode.call(unsafeUnwrap(node), false));
+      if (deep) {
+        for (var child = node.firstChild; child; child = child.nextSibling) {
+          clone.appendChild(cloneNode(child, true, opt_doc));
+        }
+        if (node instanceof wrappers.HTMLTemplateElement) {
+          var cloneContent = clone.content;
+          for (var child = node.content.firstChild; child; child = child.nextSibling) {
+            cloneContent.appendChild(cloneNode(child, true, opt_doc));
+          }
+        }
+      }
+      return clone;
+    }
+    function contains(self, child) {
+      if (!child || getTreeScope(self) !== getTreeScope(child)) return false;
+      for (var node = child; node; node = node.parentNode) {
+        if (node === self) return true;
+      }
+      return false;
+    }
+    var OriginalNode = window.Node;
+    function Node(original) {
+      assert(original instanceof OriginalNode);
+      EventTarget.call(this, original);
+      this.parentNode_ = undefined;
+      this.firstChild_ = undefined;
+      this.lastChild_ = undefined;
+      this.nextSibling_ = undefined;
+      this.previousSibling_ = undefined;
+      this.treeScope_ = undefined;
+    }
+    var OriginalDocumentFragment = window.DocumentFragment;
+    var originalAppendChild = OriginalNode.prototype.appendChild;
+    var originalCompareDocumentPosition = OriginalNode.prototype.compareDocumentPosition;
+    var originalInsertBefore = OriginalNode.prototype.insertBefore;
+    var originalRemoveChild = OriginalNode.prototype.removeChild;
+    var originalReplaceChild = OriginalNode.prototype.replaceChild;
+    var isIe = /Trident/.test(navigator.userAgent);
+    var removeChildOriginalHelper = isIe ? function(parent, child) {
+      try {
+        originalRemoveChild.call(parent, child);
+      } catch (ex) {
+        if (!(parent instanceof OriginalDocumentFragment)) throw ex;
+      }
+    } : function(parent, child) {
+      originalRemoveChild.call(parent, child);
+    };
+    Node.prototype = Object.create(EventTarget.prototype);
+    mixin(Node.prototype, {
+      appendChild: function(childWrapper) {
+        return this.insertBefore(childWrapper, null);
+      },
+      insertBefore: function(childWrapper, refWrapper) {
+        assertIsNodeWrapper(childWrapper);
+        var refNode;
+        if (refWrapper) {
+          if (isWrapper(refWrapper)) {
+            refNode = unwrap(refWrapper);
+          } else {
+            refNode = refWrapper;
+            refWrapper = wrap(refNode);
+          }
+        } else {
+          refWrapper = null;
+          refNode = null;
+        }
+        refWrapper && assert(refWrapper.parentNode === this);
+        var nodes;
+        var previousNode = refWrapper ? refWrapper.previousSibling : this.lastChild;
+        var useNative = !this.invalidateShadowRenderer() && !invalidateParent(childWrapper);
+        if (useNative) nodes = collectNodesNative(childWrapper); else nodes = collectNodes(childWrapper, this, previousNode, refWrapper);
+        if (useNative) {
+          ensureSameOwnerDocument(this, childWrapper);
+          clearChildNodes(this);
+          originalInsertBefore.call(unsafeUnwrap(this), unwrap(childWrapper), refNode);
+        } else {
+          if (!previousNode) this.firstChild_ = nodes[0];
+          if (!refWrapper) {
+            this.lastChild_ = nodes[nodes.length - 1];
+            if (this.firstChild_ === undefined) this.firstChild_ = this.firstChild;
+          }
+          var parentNode = refNode ? refNode.parentNode : unsafeUnwrap(this);
+          if (parentNode) {
+            originalInsertBefore.call(parentNode, unwrapNodesForInsertion(this, nodes), refNode);
+          } else {
+            adoptNodesIfNeeded(this, nodes);
+          }
+        }
+        enqueueMutation(this, "childList", {
+          addedNodes: nodes,
+          nextSibling: refWrapper,
+          previousSibling: previousNode
+        });
+        nodesWereAdded(nodes, this);
+        return childWrapper;
+      },
+      removeChild: function(childWrapper) {
+        assertIsNodeWrapper(childWrapper);
+        if (childWrapper.parentNode !== this) {
+          var found = false;
+          var childNodes = this.childNodes;
+          for (var ieChild = this.firstChild; ieChild; ieChild = ieChild.nextSibling) {
+            if (ieChild === childWrapper) {
+              found = true;
+              break;
+            }
+          }
+          if (!found) {
+            throw new Error("NotFoundError");
+          }
+        }
+        var childNode = unwrap(childWrapper);
+        var childWrapperNextSibling = childWrapper.nextSibling;
+        var childWrapperPreviousSibling = childWrapper.previousSibling;
+        if (this.invalidateShadowRenderer()) {
+          var thisFirstChild = this.firstChild;
+          var thisLastChild = this.lastChild;
+          var parentNode = childNode.parentNode;
+          if (parentNode) removeChildOriginalHelper(parentNode, childNode);
+          if (thisFirstChild === childWrapper) this.firstChild_ = childWrapperNextSibling;
+          if (thisLastChild === childWrapper) this.lastChild_ = childWrapperPreviousSibling;
+          if (childWrapperPreviousSibling) childWrapperPreviousSibling.nextSibling_ = childWrapperNextSibling;
+          if (childWrapperNextSibling) {
+            childWrapperNextSibling.previousSibling_ = childWrapperPreviousSibling;
+          }
+          childWrapper.previousSibling_ = childWrapper.nextSibling_ = childWrapper.parentNode_ = undefined;
+        } else {
+          clearChildNodes(this);
+          removeChildOriginalHelper(unsafeUnwrap(this), childNode);
+        }
+        if (!surpressMutations) {
+          enqueueMutation(this, "childList", {
+            removedNodes: createOneElementNodeList(childWrapper),
+            nextSibling: childWrapperNextSibling,
+            previousSibling: childWrapperPreviousSibling
+          });
+        }
+        registerTransientObservers(this, childWrapper);
+        return childWrapper;
+      },
+      replaceChild: function(newChildWrapper, oldChildWrapper) {
+        assertIsNodeWrapper(newChildWrapper);
+        var oldChildNode;
+        if (isWrapper(oldChildWrapper)) {
+          oldChildNode = unwrap(oldChildWrapper);
+        } else {
+          oldChildNode = oldChildWrapper;
+          oldChildWrapper = wrap(oldChildNode);
+        }
+        if (oldChildWrapper.parentNode !== this) {
+          throw new Error("NotFoundError");
+        }
+        var nextNode = oldChildWrapper.nextSibling;
+        var previousNode = oldChildWrapper.previousSibling;
+        var nodes;
+        var useNative = !this.invalidateShadowRenderer() && !invalidateParent(newChildWrapper);
+        if (useNative) {
+          nodes = collectNodesNative(newChildWrapper);
+        } else {
+          if (nextNode === newChildWrapper) nextNode = newChildWrapper.nextSibling;
+          nodes = collectNodes(newChildWrapper, this, previousNode, nextNode);
+        }
+        if (!useNative) {
+          if (this.firstChild === oldChildWrapper) this.firstChild_ = nodes[0];
+          if (this.lastChild === oldChildWrapper) this.lastChild_ = nodes[nodes.length - 1];
+          oldChildWrapper.previousSibling_ = oldChildWrapper.nextSibling_ = oldChildWrapper.parentNode_ = undefined;
+          if (oldChildNode.parentNode) {
+            originalReplaceChild.call(oldChildNode.parentNode, unwrapNodesForInsertion(this, nodes), oldChildNode);
+          }
+        } else {
+          ensureSameOwnerDocument(this, newChildWrapper);
+          clearChildNodes(this);
+          originalReplaceChild.call(unsafeUnwrap(this), unwrap(newChildWrapper), oldChildNode);
+        }
+        enqueueMutation(this, "childList", {
+          addedNodes: nodes,
+          removedNodes: createOneElementNodeList(oldChildWrapper),
+          nextSibling: nextNode,
+          previousSibling: previousNode
+        });
+        nodeWasRemoved(oldChildWrapper);
+        nodesWereAdded(nodes, this);
+        return oldChildWrapper;
+      },
+      nodeIsInserted_: function() {
+        for (var child = this.firstChild; child; child = child.nextSibling) {
+          child.nodeIsInserted_();
+        }
+      },
+      hasChildNodes: function() {
+        return this.firstChild !== null;
+      },
+      get parentNode() {
+        return this.parentNode_ !== undefined ? this.parentNode_ : wrap(unsafeUnwrap(this).parentNode);
+      },
+      get firstChild() {
+        return this.firstChild_ !== undefined ? this.firstChild_ : wrap(unsafeUnwrap(this).firstChild);
+      },
+      get lastChild() {
+        return this.lastChild_ !== undefined ? this.lastChild_ : wrap(unsafeUnwrap(this).lastChild);
+      },
+      get nextSibling() {
+        return this.nextSibling_ !== undefined ? this.nextSibling_ : wrap(unsafeUnwrap(this).nextSibling);
+      },
+      get previousSibling() {
+        return this.previousSibling_ !== undefined ? this.previousSibling_ : wrap(unsafeUnwrap(this).previousSibling);
+      },
+      get parentElement() {
+        var p = this.parentNode;
+        while (p && p.nodeType !== Node.ELEMENT_NODE) {
+          p = p.parentNode;
+        }
+        return p;
+      },
+      get textContent() {
+        var s = "";
+        for (var child = this.firstChild; child; child = child.nextSibling) {
+          if (child.nodeType != Node.COMMENT_NODE) {
+            s += child.textContent;
+          }
+        }
+        return s;
+      },
+      set textContent(textContent) {
+        if (textContent == null) textContent = "";
+        var removedNodes = snapshotNodeList(this.childNodes);
+        if (this.invalidateShadowRenderer()) {
+          removeAllChildNodes(this);
+          if (textContent !== "") {
+            var textNode = unsafeUnwrap(this).ownerDocument.createTextNode(textContent);
+            this.appendChild(textNode);
+          }
+        } else {
+          clearChildNodes(this);
+          unsafeUnwrap(this).textContent = textContent;
+        }
+        var addedNodes = snapshotNodeList(this.childNodes);
+        enqueueMutation(this, "childList", {
+          addedNodes: addedNodes,
+          removedNodes: removedNodes
+        });
+        nodesWereRemoved(removedNodes);
+        nodesWereAdded(addedNodes, this);
+      },
+      get childNodes() {
+        var wrapperList = new NodeList();
+        var i = 0;
+        for (var child = this.firstChild; child; child = child.nextSibling) {
+          wrapperList[i++] = child;
+        }
+        wrapperList.length = i;
+        return wrapperList;
+      },
+      cloneNode: function(deep) {
+        return cloneNode(this, deep);
+      },
+      contains: function(child) {
+        return contains(this, wrapIfNeeded(child));
+      },
+      compareDocumentPosition: function(otherNode) {
+        return originalCompareDocumentPosition.call(unsafeUnwrap(this), unwrapIfNeeded(otherNode));
+      },
+      normalize: function() {
+        var nodes = snapshotNodeList(this.childNodes);
+        var remNodes = [];
+        var s = "";
+        var modNode;
+        for (var i = 0, n; i < nodes.length; i++) {
+          n = nodes[i];
+          if (n.nodeType === Node.TEXT_NODE) {
+            if (!modNode && !n.data.length) this.removeNode(n); else if (!modNode) modNode = n; else {
+              s += n.data;
+              remNodes.push(n);
+            }
+          } else {
+            if (modNode && remNodes.length) {
+              modNode.data += s;
+              cleanupNodes(remNodes);
+            }
+            remNodes = [];
+            s = "";
+            modNode = null;
+            if (n.childNodes.length) n.normalize();
+          }
+        }
+        if (modNode && remNodes.length) {
+          modNode.data += s;
+          cleanupNodes(remNodes);
+        }
+      }
+    });
+    defineWrapGetter(Node, "ownerDocument");
+    registerWrapper(OriginalNode, Node, document.createDocumentFragment());
+    delete Node.prototype.querySelector;
+    delete Node.prototype.querySelectorAll;
+    Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype);
+    scope.cloneNode = cloneNode;
+    scope.nodeWasAdded = nodeWasAdded;
+    scope.nodeWasRemoved = nodeWasRemoved;
+    scope.nodesWereAdded = nodesWereAdded;
+    scope.nodesWereRemoved = nodesWereRemoved;
+    scope.originalInsertBefore = originalInsertBefore;
+    scope.originalRemoveChild = originalRemoveChild;
+    scope.snapshotNodeList = snapshotNodeList;
+    scope.wrappers.Node = Node;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLCollection = scope.wrappers.HTMLCollection;
+    var NodeList = scope.wrappers.NodeList;
+    var getTreeScope = scope.getTreeScope;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var wrap = scope.wrap;
+    var originalDocumentQuerySelector = document.querySelector;
+    var originalElementQuerySelector = document.documentElement.querySelector;
+    var originalDocumentQuerySelectorAll = document.querySelectorAll;
+    var originalElementQuerySelectorAll = document.documentElement.querySelectorAll;
+    var originalDocumentGetElementsByTagName = document.getElementsByTagName;
+    var originalElementGetElementsByTagName = document.documentElement.getElementsByTagName;
+    var originalDocumentGetElementsByTagNameNS = document.getElementsByTagNameNS;
+    var originalElementGetElementsByTagNameNS = document.documentElement.getElementsByTagNameNS;
+    var OriginalElement = window.Element;
+    var OriginalDocument = window.HTMLDocument || window.Document;
+    function filterNodeList(list, index, result, deep) {
+      var wrappedItem = null;
+      var root = null;
+      for (var i = 0, length = list.length; i < length; i++) {
+        wrappedItem = wrap(list[i]);
+        if (!deep && (root = getTreeScope(wrappedItem).root)) {
+          if (root instanceof scope.wrappers.ShadowRoot) {
+            continue;
+          }
+        }
+        result[index++] = wrappedItem;
+      }
+      return index;
+    }
+    function shimSelector(selector) {
+      return String(selector).replace(/\/deep\//g, " ");
+    }
+    function findOne(node, selector) {
+      var m, el = node.firstElementChild;
+      while (el) {
+        if (el.matches(selector)) return el;
+        m = findOne(el, selector);
+        if (m) return m;
+        el = el.nextElementSibling;
+      }
+      return null;
+    }
+    function matchesSelector(el, selector) {
+      return el.matches(selector);
+    }
+    var XHTML_NS = "http://www.w3.org/1999/xhtml";
+    function matchesTagName(el, localName, localNameLowerCase) {
+      var ln = el.localName;
+      return ln === localName || ln === localNameLowerCase && el.namespaceURI === XHTML_NS;
+    }
+    function matchesEveryThing() {
+      return true;
+    }
+    function matchesLocalNameOnly(el, ns, localName) {
+      return el.localName === localName;
+    }
+    function matchesNameSpace(el, ns) {
+      return el.namespaceURI === ns;
+    }
+    function matchesLocalNameNS(el, ns, localName) {
+      return el.namespaceURI === ns && el.localName === localName;
+    }
+    function findElements(node, index, result, p, arg0, arg1) {
+      var el = node.firstElementChild;
+      while (el) {
+        if (p(el, arg0, arg1)) result[index++] = el;
+        index = findElements(el, index, result, p, arg0, arg1);
+        el = el.nextElementSibling;
+      }
+      return index;
+    }
+    function querySelectorAllFiltered(p, index, result, selector, deep) {
+      var target = unsafeUnwrap(this);
+      var list;
+      var root = getTreeScope(this).root;
+      if (root instanceof scope.wrappers.ShadowRoot) {
+        return findElements(this, index, result, p, selector, null);
+      } else if (target instanceof OriginalElement) {
+        list = originalElementQuerySelectorAll.call(target, selector);
+      } else if (target instanceof OriginalDocument) {
+        list = originalDocumentQuerySelectorAll.call(target, selector);
+      } else {
+        return findElements(this, index, result, p, selector, null);
+      }
+      return filterNodeList(list, index, result, deep);
+    }
+    var SelectorsInterface = {
+      querySelector: function(selector) {
+        var shimmed = shimSelector(selector);
+        var deep = shimmed !== selector;
+        selector = shimmed;
+        var target = unsafeUnwrap(this);
+        var wrappedItem;
+        var root = getTreeScope(this).root;
+        if (root instanceof scope.wrappers.ShadowRoot) {
+          return findOne(this, selector);
+        } else if (target instanceof OriginalElement) {
+          wrappedItem = wrap(originalElementQuerySelector.call(target, selector));
+        } else if (target instanceof OriginalDocument) {
+          wrappedItem = wrap(originalDocumentQuerySelector.call(target, selector));
+        } else {
+          return findOne(this, selector);
+        }
+        if (!wrappedItem) {
+          return wrappedItem;
+        } else if (!deep && (root = getTreeScope(wrappedItem).root)) {
+          if (root instanceof scope.wrappers.ShadowRoot) {
+            return findOne(this, selector);
+          }
+        }
+        return wrappedItem;
+      },
+      querySelectorAll: function(selector) {
+        var shimmed = shimSelector(selector);
+        var deep = shimmed !== selector;
+        selector = shimmed;
+        var result = new NodeList();
+        result.length = querySelectorAllFiltered.call(this, matchesSelector, 0, result, selector, deep);
+        return result;
+      }
+    };
+    function getElementsByTagNameFiltered(p, index, result, localName, lowercase) {
+      var target = unsafeUnwrap(this);
+      var list;
+      var root = getTreeScope(this).root;
+      if (root instanceof scope.wrappers.ShadowRoot) {
+        return findElements(this, index, result, p, localName, lowercase);
+      } else if (target instanceof OriginalElement) {
+        list = originalElementGetElementsByTagName.call(target, localName, lowercase);
+      } else if (target instanceof OriginalDocument) {
+        list = originalDocumentGetElementsByTagName.call(target, localName, lowercase);
+      } else {
+        return findElements(this, index, result, p, localName, lowercase);
+      }
+      return filterNodeList(list, index, result, false);
+    }
+    function getElementsByTagNameNSFiltered(p, index, result, ns, localName) {
+      var target = unsafeUnwrap(this);
+      var list;
+      var root = getTreeScope(this).root;
+      if (root instanceof scope.wrappers.ShadowRoot) {
+        return findElements(this, index, result, p, ns, localName);
+      } else if (target instanceof OriginalElement) {
+        list = originalElementGetElementsByTagNameNS.call(target, ns, localName);
+      } else if (target instanceof OriginalDocument) {
+        list = originalDocumentGetElementsByTagNameNS.call(target, ns, localName);
+      } else {
+        return findElements(this, index, result, p, ns, localName);
+      }
+      return filterNodeList(list, index, result, false);
+    }
+    var GetElementsByInterface = {
+      getElementsByTagName: function(localName) {
+        var result = new HTMLCollection();
+        var match = localName === "*" ? matchesEveryThing : matchesTagName;
+        result.length = getElementsByTagNameFiltered.call(this, match, 0, result, localName, localName.toLowerCase());
+        return result;
+      },
+      getElementsByClassName: function(className) {
+        return this.querySelectorAll("." + className);
+      },
+      getElementsByTagNameNS: function(ns, localName) {
+        var result = new HTMLCollection();
+        var match = null;
+        if (ns === "*") {
+          match = localName === "*" ? matchesEveryThing : matchesLocalNameOnly;
+        } else {
+          match = localName === "*" ? matchesNameSpace : matchesLocalNameNS;
+        }
+        result.length = getElementsByTagNameNSFiltered.call(this, match, 0, result, ns || null, localName);
+        return result;
+      }
+    };
+    scope.GetElementsByInterface = GetElementsByInterface;
+    scope.SelectorsInterface = SelectorsInterface;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var NodeList = scope.wrappers.NodeList;
+    function forwardElement(node) {
+      while (node && node.nodeType !== Node.ELEMENT_NODE) {
+        node = node.nextSibling;
+      }
+      return node;
+    }
+    function backwardsElement(node) {
+      while (node && node.nodeType !== Node.ELEMENT_NODE) {
+        node = node.previousSibling;
+      }
+      return node;
+    }
+    var ParentNodeInterface = {
+      get firstElementChild() {
+        return forwardElement(this.firstChild);
+      },
+      get lastElementChild() {
+        return backwardsElement(this.lastChild);
+      },
+      get childElementCount() {
+        var count = 0;
+        for (var child = this.firstElementChild; child; child = child.nextElementSibling) {
+          count++;
+        }
+        return count;
+      },
+      get children() {
+        var wrapperList = new NodeList();
+        var i = 0;
+        for (var child = this.firstElementChild; child; child = child.nextElementSibling) {
+          wrapperList[i++] = child;
+        }
+        wrapperList.length = i;
+        return wrapperList;
+      },
+      remove: function() {
+        var p = this.parentNode;
+        if (p) p.removeChild(this);
+      }
+    };
+    var ChildNodeInterface = {
+      get nextElementSibling() {
+        return forwardElement(this.nextSibling);
+      },
+      get previousElementSibling() {
+        return backwardsElement(this.previousSibling);
+      }
+    };
+    scope.ChildNodeInterface = ChildNodeInterface;
+    scope.ParentNodeInterface = ParentNodeInterface;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var ChildNodeInterface = scope.ChildNodeInterface;
+    var Node = scope.wrappers.Node;
+    var enqueueMutation = scope.enqueueMutation;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var OriginalCharacterData = window.CharacterData;
+    function CharacterData(node) {
+      Node.call(this, node);
+    }
+    CharacterData.prototype = Object.create(Node.prototype);
+    mixin(CharacterData.prototype, {
+      get textContent() {
+        return this.data;
+      },
+      set textContent(value) {
+        this.data = value;
+      },
+      get data() {
+        return unsafeUnwrap(this).data;
+      },
+      set data(value) {
+        var oldValue = unsafeUnwrap(this).data;
+        enqueueMutation(this, "characterData", {
+          oldValue: oldValue
+        });
+        unsafeUnwrap(this).data = value;
+      }
+    });
+    mixin(CharacterData.prototype, ChildNodeInterface);
+    registerWrapper(OriginalCharacterData, CharacterData, document.createTextNode(""));
+    scope.wrappers.CharacterData = CharacterData;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var CharacterData = scope.wrappers.CharacterData;
+    var enqueueMutation = scope.enqueueMutation;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    function toUInt32(x) {
+      return x >>> 0;
+    }
+    var OriginalText = window.Text;
+    function Text(node) {
+      CharacterData.call(this, node);
+    }
+    Text.prototype = Object.create(CharacterData.prototype);
+    mixin(Text.prototype, {
+      splitText: function(offset) {
+        offset = toUInt32(offset);
+        var s = this.data;
+        if (offset > s.length) throw new Error("IndexSizeError");
+        var head = s.slice(0, offset);
+        var tail = s.slice(offset);
+        this.data = head;
+        var newTextNode = this.ownerDocument.createTextNode(tail);
+        if (this.parentNode) this.parentNode.insertBefore(newTextNode, this.nextSibling);
+        return newTextNode;
+      }
+    });
+    registerWrapper(OriginalText, Text, document.createTextNode(""));
+    scope.wrappers.Text = Text;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var setWrapper = scope.setWrapper;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    function invalidateClass(el) {
+      scope.invalidateRendererBasedOnAttribute(el, "class");
+    }
+    function DOMTokenList(impl, ownerElement) {
+      setWrapper(impl, this);
+      this.ownerElement_ = ownerElement;
+    }
+    DOMTokenList.prototype = {
+      constructor: DOMTokenList,
+      get length() {
+        return unsafeUnwrap(this).length;
+      },
+      item: function(index) {
+        return unsafeUnwrap(this).item(index);
+      },
+      contains: function(token) {
+        return unsafeUnwrap(this).contains(token);
+      },
+      add: function() {
+        unsafeUnwrap(this).add.apply(unsafeUnwrap(this), arguments);
+        invalidateClass(this.ownerElement_);
+      },
+      remove: function() {
+        unsafeUnwrap(this).remove.apply(unsafeUnwrap(this), arguments);
+        invalidateClass(this.ownerElement_);
+      },
+      toggle: function(token) {
+        var rv = unsafeUnwrap(this).toggle.apply(unsafeUnwrap(this), arguments);
+        invalidateClass(this.ownerElement_);
+        return rv;
+      },
+      toString: function() {
+        return unsafeUnwrap(this).toString();
+      }
+    };
+    scope.wrappers.DOMTokenList = DOMTokenList;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var ChildNodeInterface = scope.ChildNodeInterface;
+    var GetElementsByInterface = scope.GetElementsByInterface;
+    var Node = scope.wrappers.Node;
+    var DOMTokenList = scope.wrappers.DOMTokenList;
+    var ParentNodeInterface = scope.ParentNodeInterface;
+    var SelectorsInterface = scope.SelectorsInterface;
+    var addWrapNodeListMethod = scope.addWrapNodeListMethod;
+    var enqueueMutation = scope.enqueueMutation;
+    var mixin = scope.mixin;
+    var oneOf = scope.oneOf;
+    var registerWrapper = scope.registerWrapper;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var wrappers = scope.wrappers;
+    var OriginalElement = window.Element;
+    var matchesNames = [ "matches", "mozMatchesSelector", "msMatchesSelector", "webkitMatchesSelector" ].filter(function(name) {
+      return OriginalElement.prototype[name];
+    });
+    var matchesName = matchesNames[0];
+    var originalMatches = OriginalElement.prototype[matchesName];
+    function invalidateRendererBasedOnAttribute(element, name) {
+      var p = element.parentNode;
+      if (!p || !p.shadowRoot) return;
+      var renderer = scope.getRendererForHost(p);
+      if (renderer.dependsOnAttribute(name)) renderer.invalidate();
+    }
+    function enqueAttributeChange(element, name, oldValue) {
+      enqueueMutation(element, "attributes", {
+        name: name,
+        namespace: null,
+        oldValue: oldValue
+      });
+    }
+    var classListTable = new WeakMap();
+    function Element(node) {
+      Node.call(this, node);
+    }
+    Element.prototype = Object.create(Node.prototype);
+    mixin(Element.prototype, {
+      createShadowRoot: function() {
+        var newShadowRoot = new wrappers.ShadowRoot(this);
+        unsafeUnwrap(this).polymerShadowRoot_ = newShadowRoot;
+        var renderer = scope.getRendererForHost(this);
+        renderer.invalidate();
+        return newShadowRoot;
+      },
+      get shadowRoot() {
+        return unsafeUnwrap(this).polymerShadowRoot_ || null;
+      },
+      setAttribute: function(name, value) {
+        var oldValue = unsafeUnwrap(this).getAttribute(name);
+        unsafeUnwrap(this).setAttribute(name, value);
+        enqueAttributeChange(this, name, oldValue);
+        invalidateRendererBasedOnAttribute(this, name);
+      },
+      removeAttribute: function(name) {
+        var oldValue = unsafeUnwrap(this).getAttribute(name);
+        unsafeUnwrap(this).removeAttribute(name);
+        enqueAttributeChange(this, name, oldValue);
+        invalidateRendererBasedOnAttribute(this, name);
+      },
+      matches: function(selector) {
+        return originalMatches.call(unsafeUnwrap(this), selector);
+      },
+      get classList() {
+        var list = classListTable.get(this);
+        if (!list) {
+          classListTable.set(this, list = new DOMTokenList(unsafeUnwrap(this).classList, this));
+        }
+        return list;
+      },
+      get className() {
+        return unsafeUnwrap(this).className;
+      },
+      set className(v) {
+        this.setAttribute("class", v);
+      },
+      get id() {
+        return unsafeUnwrap(this).id;
+      },
+      set id(v) {
+        this.setAttribute("id", v);
+      }
+    });
+    matchesNames.forEach(function(name) {
+      if (name !== "matches") {
+        Element.prototype[name] = function(selector) {
+          return this.matches(selector);
+        };
+      }
+    });
+    if (OriginalElement.prototype.webkitCreateShadowRoot) {
+      Element.prototype.webkitCreateShadowRoot = Element.prototype.createShadowRoot;
+    }
+    mixin(Element.prototype, ChildNodeInterface);
+    mixin(Element.prototype, GetElementsByInterface);
+    mixin(Element.prototype, ParentNodeInterface);
+    mixin(Element.prototype, SelectorsInterface);
+    registerWrapper(OriginalElement, Element, document.createElementNS(null, "x"));
+    scope.invalidateRendererBasedOnAttribute = invalidateRendererBasedOnAttribute;
+    scope.matchesNames = matchesNames;
+    scope.wrappers.Element = Element;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var Element = scope.wrappers.Element;
+    var defineGetter = scope.defineGetter;
+    var enqueueMutation = scope.enqueueMutation;
+    var mixin = scope.mixin;
+    var nodesWereAdded = scope.nodesWereAdded;
+    var nodesWereRemoved = scope.nodesWereRemoved;
+    var registerWrapper = scope.registerWrapper;
+    var snapshotNodeList = scope.snapshotNodeList;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var unwrap = scope.unwrap;
+    var wrap = scope.wrap;
+    var wrappers = scope.wrappers;
+    var escapeAttrRegExp = /[&\u00A0"]/g;
+    var escapeDataRegExp = /[&\u00A0<>]/g;
+    function escapeReplace(c) {
+      switch (c) {
+       case "&":
+        return "&amp;";
+
+       case "<":
+        return "&lt;";
+
+       case ">":
+        return "&gt;";
+
+       case '"':
+        return "&quot;";
+
+       case " ":
+        return "&nbsp;";
+      }
+    }
+    function escapeAttr(s) {
+      return s.replace(escapeAttrRegExp, escapeReplace);
+    }
+    function escapeData(s) {
+      return s.replace(escapeDataRegExp, escapeReplace);
+    }
+    function makeSet(arr) {
+      var set = {};
+      for (var i = 0; i < arr.length; i++) {
+        set[arr[i]] = true;
+      }
+      return set;
+    }
+    var voidElements = makeSet([ "area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr" ]);
+    var plaintextParents = makeSet([ "style", "script", "xmp", "iframe", "noembed", "noframes", "plaintext", "noscript" ]);
+    function getOuterHTML(node, parentNode) {
+      switch (node.nodeType) {
+       case Node.ELEMENT_NODE:
+        var tagName = node.tagName.toLowerCase();
+        var s = "<" + tagName;
+        var attrs = node.attributes;
+        for (var i = 0, attr; attr = attrs[i]; i++) {
+          s += " " + attr.name + '="' + escapeAttr(attr.value) + '"';
+        }
+        s += ">";
+        if (voidElements[tagName]) return s;
+        return s + getInnerHTML(node) + "</" + tagName + ">";
+
+       case Node.TEXT_NODE:
+        var data = node.data;
+        if (parentNode && plaintextParents[parentNode.localName]) return data;
+        return escapeData(data);
+
+       case Node.COMMENT_NODE:
+        return "<!--" + node.data + "-->";
+
+       default:
+        console.error(node);
+        throw new Error("not implemented");
+      }
+    }
+    function getInnerHTML(node) {
+      if (node instanceof wrappers.HTMLTemplateElement) node = node.content;
+      var s = "";
+      for (var child = node.firstChild; child; child = child.nextSibling) {
+        s += getOuterHTML(child, node);
+      }
+      return s;
+    }
+    function setInnerHTML(node, value, opt_tagName) {
+      var tagName = opt_tagName || "div";
+      node.textContent = "";
+      var tempElement = unwrap(node.ownerDocument.createElement(tagName));
+      tempElement.innerHTML = value;
+      var firstChild;
+      while (firstChild = tempElement.firstChild) {
+        node.appendChild(wrap(firstChild));
+      }
+    }
+    var oldIe = /MSIE/.test(navigator.userAgent);
+    var OriginalHTMLElement = window.HTMLElement;
+    var OriginalHTMLTemplateElement = window.HTMLTemplateElement;
+    function HTMLElement(node) {
+      Element.call(this, node);
+    }
+    HTMLElement.prototype = Object.create(Element.prototype);
+    mixin(HTMLElement.prototype, {
+      get innerHTML() {
+        return getInnerHTML(this);
+      },
+      set innerHTML(value) {
+        if (oldIe && plaintextParents[this.localName]) {
+          this.textContent = value;
+          return;
+        }
+        var removedNodes = snapshotNodeList(this.childNodes);
+        if (this.invalidateShadowRenderer()) {
+          if (this instanceof wrappers.HTMLTemplateElement) setInnerHTML(this.content, value); else setInnerHTML(this, value, this.tagName);
+        } else if (!OriginalHTMLTemplateElement && this instanceof wrappers.HTMLTemplateElement) {
+          setInnerHTML(this.content, value);
+        } else {
+          unsafeUnwrap(this).innerHTML = value;
+        }
+        var addedNodes = snapshotNodeList(this.childNodes);
+        enqueueMutation(this, "childList", {
+          addedNodes: addedNodes,
+          removedNodes: removedNodes
+        });
+        nodesWereRemoved(removedNodes);
+        nodesWereAdded(addedNodes, this);
+      },
+      get outerHTML() {
+        return getOuterHTML(this, this.parentNode);
+      },
+      set outerHTML(value) {
+        var p = this.parentNode;
+        if (p) {
+          p.invalidateShadowRenderer();
+          var df = frag(p, value);
+          p.replaceChild(df, this);
+        }
+      },
+      insertAdjacentHTML: function(position, text) {
+        var contextElement, refNode;
+        switch (String(position).toLowerCase()) {
+         case "beforebegin":
+          contextElement = this.parentNode;
+          refNode = this;
+          break;
+
+         case "afterend":
+          contextElement = this.parentNode;
+          refNode = this.nextSibling;
+          break;
+
+         case "afterbegin":
+          contextElement = this;
+          refNode = this.firstChild;
+          break;
+
+         case "beforeend":
+          contextElement = this;
+          refNode = null;
+          break;
+
+         default:
+          return;
+        }
+        var df = frag(contextElement, text);
+        contextElement.insertBefore(df, refNode);
+      },
+      get hidden() {
+        return this.hasAttribute("hidden");
+      },
+      set hidden(v) {
+        if (v) {
+          this.setAttribute("hidden", "");
+        } else {
+          this.removeAttribute("hidden");
+        }
+      }
+    });
+    function frag(contextElement, html) {
+      var p = unwrap(contextElement.cloneNode(false));
+      p.innerHTML = html;
+      var df = unwrap(document.createDocumentFragment());
+      var c;
+      while (c = p.firstChild) {
+        df.appendChild(c);
+      }
+      return wrap(df);
+    }
+    function getter(name) {
+      return function() {
+        scope.renderAllPending();
+        return unsafeUnwrap(this)[name];
+      };
+    }
+    function getterRequiresRendering(name) {
+      defineGetter(HTMLElement, name, getter(name));
+    }
+    [ "clientHeight", "clientLeft", "clientTop", "clientWidth", "offsetHeight", "offsetLeft", "offsetTop", "offsetWidth", "scrollHeight", "scrollWidth" ].forEach(getterRequiresRendering);
+    function getterAndSetterRequiresRendering(name) {
+      Object.defineProperty(HTMLElement.prototype, name, {
+        get: getter(name),
+        set: function(v) {
+          scope.renderAllPending();
+          unsafeUnwrap(this)[name] = v;
+        },
+        configurable: true,
+        enumerable: true
+      });
+    }
+    [ "scrollLeft", "scrollTop" ].forEach(getterAndSetterRequiresRendering);
+    function methodRequiresRendering(name) {
+      Object.defineProperty(HTMLElement.prototype, name, {
+        value: function() {
+          scope.renderAllPending();
+          return unsafeUnwrap(this)[name].apply(unsafeUnwrap(this), arguments);
+        },
+        configurable: true,
+        enumerable: true
+      });
+    }
+    [ "getBoundingClientRect", "getClientRects", "scrollIntoView" ].forEach(methodRequiresRendering);
+    registerWrapper(OriginalHTMLElement, HTMLElement, document.createElement("b"));
+    scope.wrappers.HTMLElement = HTMLElement;
+    scope.getInnerHTML = getInnerHTML;
+    scope.setInnerHTML = setInnerHTML;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var wrap = scope.wrap;
+    var OriginalHTMLCanvasElement = window.HTMLCanvasElement;
+    function HTMLCanvasElement(node) {
+      HTMLElement.call(this, node);
+    }
+    HTMLCanvasElement.prototype = Object.create(HTMLElement.prototype);
+    mixin(HTMLCanvasElement.prototype, {
+      getContext: function() {
+        var context = unsafeUnwrap(this).getContext.apply(unsafeUnwrap(this), arguments);
+        return context && wrap(context);
+      }
+    });
+    registerWrapper(OriginalHTMLCanvasElement, HTMLCanvasElement, document.createElement("canvas"));
+    scope.wrappers.HTMLCanvasElement = HTMLCanvasElement;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var OriginalHTMLContentElement = window.HTMLContentElement;
+    function HTMLContentElement(node) {
+      HTMLElement.call(this, node);
+    }
+    HTMLContentElement.prototype = Object.create(HTMLElement.prototype);
+    mixin(HTMLContentElement.prototype, {
+      constructor: HTMLContentElement,
+      get select() {
+        return this.getAttribute("select");
+      },
+      set select(value) {
+        this.setAttribute("select", value);
+      },
+      setAttribute: function(n, v) {
+        HTMLElement.prototype.setAttribute.call(this, n, v);
+        if (String(n).toLowerCase() === "select") this.invalidateShadowRenderer(true);
+      }
+    });
+    if (OriginalHTMLContentElement) registerWrapper(OriginalHTMLContentElement, HTMLContentElement);
+    scope.wrappers.HTMLContentElement = HTMLContentElement;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var wrapHTMLCollection = scope.wrapHTMLCollection;
+    var unwrap = scope.unwrap;
+    var OriginalHTMLFormElement = window.HTMLFormElement;
+    function HTMLFormElement(node) {
+      HTMLElement.call(this, node);
+    }
+    HTMLFormElement.prototype = Object.create(HTMLElement.prototype);
+    mixin(HTMLFormElement.prototype, {
+      get elements() {
+        return wrapHTMLCollection(unwrap(this).elements);
+      }
+    });
+    registerWrapper(OriginalHTMLFormElement, HTMLFormElement, document.createElement("form"));
+    scope.wrappers.HTMLFormElement = HTMLFormElement;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var registerWrapper = scope.registerWrapper;
+    var unwrap = scope.unwrap;
+    var rewrap = scope.rewrap;
+    var OriginalHTMLImageElement = window.HTMLImageElement;
+    function HTMLImageElement(node) {
+      HTMLElement.call(this, node);
+    }
+    HTMLImageElement.prototype = Object.create(HTMLElement.prototype);
+    registerWrapper(OriginalHTMLImageElement, HTMLImageElement, document.createElement("img"));
+    function Image(width, height) {
+      if (!(this instanceof Image)) {
+        throw new TypeError("DOM object constructor cannot be called as a function.");
+      }
+      var node = unwrap(document.createElement("img"));
+      HTMLElement.call(this, node);
+      rewrap(node, this);
+      if (width !== undefined) node.width = width;
+      if (height !== undefined) node.height = height;
+    }
+    Image.prototype = HTMLImageElement.prototype;
+    scope.wrappers.HTMLImageElement = HTMLImageElement;
+    scope.wrappers.Image = Image;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var mixin = scope.mixin;
+    var NodeList = scope.wrappers.NodeList;
+    var registerWrapper = scope.registerWrapper;
+    var OriginalHTMLShadowElement = window.HTMLShadowElement;
+    function HTMLShadowElement(node) {
+      HTMLElement.call(this, node);
+    }
+    HTMLShadowElement.prototype = Object.create(HTMLElement.prototype);
+    HTMLShadowElement.prototype.constructor = HTMLShadowElement;
+    if (OriginalHTMLShadowElement) registerWrapper(OriginalHTMLShadowElement, HTMLShadowElement);
+    scope.wrappers.HTMLShadowElement = HTMLShadowElement;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var unwrap = scope.unwrap;
+    var wrap = scope.wrap;
+    var contentTable = new WeakMap();
+    var templateContentsOwnerTable = new WeakMap();
+    function getTemplateContentsOwner(doc) {
+      if (!doc.defaultView) return doc;
+      var d = templateContentsOwnerTable.get(doc);
+      if (!d) {
+        d = doc.implementation.createHTMLDocument("");
+        while (d.lastChild) {
+          d.removeChild(d.lastChild);
+        }
+        templateContentsOwnerTable.set(doc, d);
+      }
+      return d;
+    }
+    function extractContent(templateElement) {
+      var doc = getTemplateContentsOwner(templateElement.ownerDocument);
+      var df = unwrap(doc.createDocumentFragment());
+      var child;
+      while (child = templateElement.firstChild) {
+        df.appendChild(child);
+      }
+      return df;
+    }
+    var OriginalHTMLTemplateElement = window.HTMLTemplateElement;
+    function HTMLTemplateElement(node) {
+      HTMLElement.call(this, node);
+      if (!OriginalHTMLTemplateElement) {
+        var content = extractContent(node);
+        contentTable.set(this, wrap(content));
+      }
+    }
+    HTMLTemplateElement.prototype = Object.create(HTMLElement.prototype);
+    mixin(HTMLTemplateElement.prototype, {
+      constructor: HTMLTemplateElement,
+      get content() {
+        if (OriginalHTMLTemplateElement) return wrap(unsafeUnwrap(this).content);
+        return contentTable.get(this);
+      }
+    });
+    if (OriginalHTMLTemplateElement) registerWrapper(OriginalHTMLTemplateElement, HTMLTemplateElement);
+    scope.wrappers.HTMLTemplateElement = HTMLTemplateElement;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var registerWrapper = scope.registerWrapper;
+    var OriginalHTMLMediaElement = window.HTMLMediaElement;
+    if (!OriginalHTMLMediaElement) return;
+    function HTMLMediaElement(node) {
+      HTMLElement.call(this, node);
+    }
+    HTMLMediaElement.prototype = Object.create(HTMLElement.prototype);
+    registerWrapper(OriginalHTMLMediaElement, HTMLMediaElement, document.createElement("audio"));
+    scope.wrappers.HTMLMediaElement = HTMLMediaElement;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLMediaElement = scope.wrappers.HTMLMediaElement;
+    var registerWrapper = scope.registerWrapper;
+    var unwrap = scope.unwrap;
+    var rewrap = scope.rewrap;
+    var OriginalHTMLAudioElement = window.HTMLAudioElement;
+    if (!OriginalHTMLAudioElement) return;
+    function HTMLAudioElement(node) {
+      HTMLMediaElement.call(this, node);
+    }
+    HTMLAudioElement.prototype = Object.create(HTMLMediaElement.prototype);
+    registerWrapper(OriginalHTMLAudioElement, HTMLAudioElement, document.createElement("audio"));
+    function Audio(src) {
+      if (!(this instanceof Audio)) {
+        throw new TypeError("DOM object constructor cannot be called as a function.");
+      }
+      var node = unwrap(document.createElement("audio"));
+      HTMLMediaElement.call(this, node);
+      rewrap(node, this);
+      node.setAttribute("preload", "auto");
+      if (src !== undefined) node.setAttribute("src", src);
+    }
+    Audio.prototype = HTMLAudioElement.prototype;
+    scope.wrappers.HTMLAudioElement = HTMLAudioElement;
+    scope.wrappers.Audio = Audio;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var rewrap = scope.rewrap;
+    var unwrap = scope.unwrap;
+    var wrap = scope.wrap;
+    var OriginalHTMLOptionElement = window.HTMLOptionElement;
+    function trimText(s) {
+      return s.replace(/\s+/g, " ").trim();
+    }
+    function HTMLOptionElement(node) {
+      HTMLElement.call(this, node);
+    }
+    HTMLOptionElement.prototype = Object.create(HTMLElement.prototype);
+    mixin(HTMLOptionElement.prototype, {
+      get text() {
+        return trimText(this.textContent);
+      },
+      set text(value) {
+        this.textContent = trimText(String(value));
+      },
+      get form() {
+        return wrap(unwrap(this).form);
+      }
+    });
+    registerWrapper(OriginalHTMLOptionElement, HTMLOptionElement, document.createElement("option"));
+    function Option(text, value, defaultSelected, selected) {
+      if (!(this instanceof Option)) {
+        throw new TypeError("DOM object constructor cannot be called as a function.");
+      }
+      var node = unwrap(document.createElement("option"));
+      HTMLElement.call(this, node);
+      rewrap(node, this);
+      if (text !== undefined) node.text = text;
+      if (value !== undefined) node.setAttribute("value", value);
+      if (defaultSelected === true) node.setAttribute("selected", "");
+      node.selected = selected === true;
+    }
+    Option.prototype = HTMLOptionElement.prototype;
+    scope.wrappers.HTMLOptionElement = HTMLOptionElement;
+    scope.wrappers.Option = Option;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var unwrap = scope.unwrap;
+    var wrap = scope.wrap;
+    var OriginalHTMLSelectElement = window.HTMLSelectElement;
+    function HTMLSelectElement(node) {
+      HTMLElement.call(this, node);
+    }
+    HTMLSelectElement.prototype = Object.create(HTMLElement.prototype);
+    mixin(HTMLSelectElement.prototype, {
+      add: function(element, before) {
+        if (typeof before === "object") before = unwrap(before);
+        unwrap(this).add(unwrap(element), before);
+      },
+      remove: function(indexOrNode) {
+        if (indexOrNode === undefined) {
+          HTMLElement.prototype.remove.call(this);
+          return;
+        }
+        if (typeof indexOrNode === "object") indexOrNode = unwrap(indexOrNode);
+        unwrap(this).remove(indexOrNode);
+      },
+      get form() {
+        return wrap(unwrap(this).form);
+      }
+    });
+    registerWrapper(OriginalHTMLSelectElement, HTMLSelectElement, document.createElement("select"));
+    scope.wrappers.HTMLSelectElement = HTMLSelectElement;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var unwrap = scope.unwrap;
+    var wrap = scope.wrap;
+    var wrapHTMLCollection = scope.wrapHTMLCollection;
+    var OriginalHTMLTableElement = window.HTMLTableElement;
+    function HTMLTableElement(node) {
+      HTMLElement.call(this, node);
+    }
+    HTMLTableElement.prototype = Object.create(HTMLElement.prototype);
+    mixin(HTMLTableElement.prototype, {
+      get caption() {
+        return wrap(unwrap(this).caption);
+      },
+      createCaption: function() {
+        return wrap(unwrap(this).createCaption());
+      },
+      get tHead() {
+        return wrap(unwrap(this).tHead);
+      },
+      createTHead: function() {
+        return wrap(unwrap(this).createTHead());
+      },
+      createTFoot: function() {
+        return wrap(unwrap(this).createTFoot());
+      },
+      get tFoot() {
+        return wrap(unwrap(this).tFoot);
+      },
+      get tBodies() {
+        return wrapHTMLCollection(unwrap(this).tBodies);
+      },
+      createTBody: function() {
+        return wrap(unwrap(this).createTBody());
+      },
+      get rows() {
+        return wrapHTMLCollection(unwrap(this).rows);
+      },
+      insertRow: function(index) {
+        return wrap(unwrap(this).insertRow(index));
+      }
+    });
+    registerWrapper(OriginalHTMLTableElement, HTMLTableElement, document.createElement("table"));
+    scope.wrappers.HTMLTableElement = HTMLTableElement;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var wrapHTMLCollection = scope.wrapHTMLCollection;
+    var unwrap = scope.unwrap;
+    var wrap = scope.wrap;
+    var OriginalHTMLTableSectionElement = window.HTMLTableSectionElement;
+    function HTMLTableSectionElement(node) {
+      HTMLElement.call(this, node);
+    }
+    HTMLTableSectionElement.prototype = Object.create(HTMLElement.prototype);
+    mixin(HTMLTableSectionElement.prototype, {
+      constructor: HTMLTableSectionElement,
+      get rows() {
+        return wrapHTMLCollection(unwrap(this).rows);
+      },
+      insertRow: function(index) {
+        return wrap(unwrap(this).insertRow(index));
+      }
+    });
+    registerWrapper(OriginalHTMLTableSectionElement, HTMLTableSectionElement, document.createElement("thead"));
+    scope.wrappers.HTMLTableSectionElement = HTMLTableSectionElement;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var wrapHTMLCollection = scope.wrapHTMLCollection;
+    var unwrap = scope.unwrap;
+    var wrap = scope.wrap;
+    var OriginalHTMLTableRowElement = window.HTMLTableRowElement;
+    function HTMLTableRowElement(node) {
+      HTMLElement.call(this, node);
+    }
+    HTMLTableRowElement.prototype = Object.create(HTMLElement.prototype);
+    mixin(HTMLTableRowElement.prototype, {
+      get cells() {
+        return wrapHTMLCollection(unwrap(this).cells);
+      },
+      insertCell: function(index) {
+        return wrap(unwrap(this).insertCell(index));
+      }
+    });
+    registerWrapper(OriginalHTMLTableRowElement, HTMLTableRowElement, document.createElement("tr"));
+    scope.wrappers.HTMLTableRowElement = HTMLTableRowElement;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLContentElement = scope.wrappers.HTMLContentElement;
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var HTMLShadowElement = scope.wrappers.HTMLShadowElement;
+    var HTMLTemplateElement = scope.wrappers.HTMLTemplateElement;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var OriginalHTMLUnknownElement = window.HTMLUnknownElement;
+    function HTMLUnknownElement(node) {
+      switch (node.localName) {
+       case "content":
+        return new HTMLContentElement(node);
+
+       case "shadow":
+        return new HTMLShadowElement(node);
+
+       case "template":
+        return new HTMLTemplateElement(node);
+      }
+      HTMLElement.call(this, node);
+    }
+    HTMLUnknownElement.prototype = Object.create(HTMLElement.prototype);
+    registerWrapper(OriginalHTMLUnknownElement, HTMLUnknownElement);
+    scope.wrappers.HTMLUnknownElement = HTMLUnknownElement;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var Element = scope.wrappers.Element;
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var registerObject = scope.registerObject;
+    var SVG_NS = "http://www.w3.org/2000/svg";
+    var svgTitleElement = document.createElementNS(SVG_NS, "title");
+    var SVGTitleElement = registerObject(svgTitleElement);
+    var SVGElement = Object.getPrototypeOf(SVGTitleElement.prototype).constructor;
+    if (!("classList" in svgTitleElement)) {
+      var descr = Object.getOwnPropertyDescriptor(Element.prototype, "classList");
+      Object.defineProperty(HTMLElement.prototype, "classList", descr);
+      delete Element.prototype.classList;
+    }
+    scope.wrappers.SVGElement = SVGElement;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var unwrap = scope.unwrap;
+    var wrap = scope.wrap;
+    var OriginalSVGUseElement = window.SVGUseElement;
+    var SVG_NS = "http://www.w3.org/2000/svg";
+    var gWrapper = wrap(document.createElementNS(SVG_NS, "g"));
+    var useElement = document.createElementNS(SVG_NS, "use");
+    var SVGGElement = gWrapper.constructor;
+    var parentInterfacePrototype = Object.getPrototypeOf(SVGGElement.prototype);
+    var parentInterface = parentInterfacePrototype.constructor;
+    function SVGUseElement(impl) {
+      parentInterface.call(this, impl);
+    }
+    SVGUseElement.prototype = Object.create(parentInterfacePrototype);
+    if ("instanceRoot" in useElement) {
+      mixin(SVGUseElement.prototype, {
+        get instanceRoot() {
+          return wrap(unwrap(this).instanceRoot);
+        },
+        get animatedInstanceRoot() {
+          return wrap(unwrap(this).animatedInstanceRoot);
+        }
+      });
+    }
+    registerWrapper(OriginalSVGUseElement, SVGUseElement, useElement);
+    scope.wrappers.SVGUseElement = SVGUseElement;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var EventTarget = scope.wrappers.EventTarget;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var wrap = scope.wrap;
+    var OriginalSVGElementInstance = window.SVGElementInstance;
+    if (!OriginalSVGElementInstance) return;
+    function SVGElementInstance(impl) {
+      EventTarget.call(this, impl);
+    }
+    SVGElementInstance.prototype = Object.create(EventTarget.prototype);
+    mixin(SVGElementInstance.prototype, {
+      get correspondingElement() {
+        return wrap(unsafeUnwrap(this).correspondingElement);
+      },
+      get correspondingUseElement() {
+        return wrap(unsafeUnwrap(this).correspondingUseElement);
+      },
+      get parentNode() {
+        return wrap(unsafeUnwrap(this).parentNode);
+      },
+      get childNodes() {
+        throw new Error("Not implemented");
+      },
+      get firstChild() {
+        return wrap(unsafeUnwrap(this).firstChild);
+      },
+      get lastChild() {
+        return wrap(unsafeUnwrap(this).lastChild);
+      },
+      get previousSibling() {
+        return wrap(unsafeUnwrap(this).previousSibling);
+      },
+      get nextSibling() {
+        return wrap(unsafeUnwrap(this).nextSibling);
+      }
+    });
+    registerWrapper(OriginalSVGElementInstance, SVGElementInstance);
+    scope.wrappers.SVGElementInstance = SVGElementInstance;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var setWrapper = scope.setWrapper;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var unwrap = scope.unwrap;
+    var unwrapIfNeeded = scope.unwrapIfNeeded;
+    var wrap = scope.wrap;
+    var OriginalCanvasRenderingContext2D = window.CanvasRenderingContext2D;
+    function CanvasRenderingContext2D(impl) {
+      setWrapper(impl, this);
+    }
+    mixin(CanvasRenderingContext2D.prototype, {
+      get canvas() {
+        return wrap(unsafeUnwrap(this).canvas);
+      },
+      drawImage: function() {
+        arguments[0] = unwrapIfNeeded(arguments[0]);
+        unsafeUnwrap(this).drawImage.apply(unsafeUnwrap(this), arguments);
+      },
+      createPattern: function() {
+        arguments[0] = unwrap(arguments[0]);
+        return unsafeUnwrap(this).createPattern.apply(unsafeUnwrap(this), arguments);
+      }
+    });
+    registerWrapper(OriginalCanvasRenderingContext2D, CanvasRenderingContext2D, document.createElement("canvas").getContext("2d"));
+    scope.wrappers.CanvasRenderingContext2D = CanvasRenderingContext2D;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var setWrapper = scope.setWrapper;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var unwrapIfNeeded = scope.unwrapIfNeeded;
+    var wrap = scope.wrap;
+    var OriginalWebGLRenderingContext = window.WebGLRenderingContext;
+    if (!OriginalWebGLRenderingContext) return;
+    function WebGLRenderingContext(impl) {
+      setWrapper(impl, this);
+    }
+    mixin(WebGLRenderingContext.prototype, {
+      get canvas() {
+        return wrap(unsafeUnwrap(this).canvas);
+      },
+      texImage2D: function() {
+        arguments[5] = unwrapIfNeeded(arguments[5]);
+        unsafeUnwrap(this).texImage2D.apply(unsafeUnwrap(this), arguments);
+      },
+      texSubImage2D: function() {
+        arguments[6] = unwrapIfNeeded(arguments[6]);
+        unsafeUnwrap(this).texSubImage2D.apply(unsafeUnwrap(this), arguments);
+      }
+    });
+    var instanceProperties = /WebKit/.test(navigator.userAgent) ? {
+      drawingBufferHeight: null,
+      drawingBufferWidth: null
+    } : {};
+    registerWrapper(OriginalWebGLRenderingContext, WebGLRenderingContext, instanceProperties);
+    scope.wrappers.WebGLRenderingContext = WebGLRenderingContext;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var registerWrapper = scope.registerWrapper;
+    var setWrapper = scope.setWrapper;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var unwrap = scope.unwrap;
+    var unwrapIfNeeded = scope.unwrapIfNeeded;
+    var wrap = scope.wrap;
+    var OriginalRange = window.Range;
+    function Range(impl) {
+      setWrapper(impl, this);
+    }
+    Range.prototype = {
+      get startContainer() {
+        return wrap(unsafeUnwrap(this).startContainer);
+      },
+      get endContainer() {
+        return wrap(unsafeUnwrap(this).endContainer);
+      },
+      get commonAncestorContainer() {
+        return wrap(unsafeUnwrap(this).commonAncestorContainer);
+      },
+      setStart: function(refNode, offset) {
+        unsafeUnwrap(this).setStart(unwrapIfNeeded(refNode), offset);
+      },
+      setEnd: function(refNode, offset) {
+        unsafeUnwrap(this).setEnd(unwrapIfNeeded(refNode), offset);
+      },
+      setStartBefore: function(refNode) {
+        unsafeUnwrap(this).setStartBefore(unwrapIfNeeded(refNode));
+      },
+      setStartAfter: function(refNode) {
+        unsafeUnwrap(this).setStartAfter(unwrapIfNeeded(refNode));
+      },
+      setEndBefore: function(refNode) {
+        unsafeUnwrap(this).setEndBefore(unwrapIfNeeded(refNode));
+      },
+      setEndAfter: function(refNode) {
+        unsafeUnwrap(this).setEndAfter(unwrapIfNeeded(refNode));
+      },
+      selectNode: function(refNode) {
+        unsafeUnwrap(this).selectNode(unwrapIfNeeded(refNode));
+      },
+      selectNodeContents: function(refNode) {
+        unsafeUnwrap(this).selectNodeContents(unwrapIfNeeded(refNode));
+      },
+      compareBoundaryPoints: function(how, sourceRange) {
+        return unsafeUnwrap(this).compareBoundaryPoints(how, unwrap(sourceRange));
+      },
+      extractContents: function() {
+        return wrap(unsafeUnwrap(this).extractContents());
+      },
+      cloneContents: function() {
+        return wrap(unsafeUnwrap(this).cloneContents());
+      },
+      insertNode: function(node) {
+        unsafeUnwrap(this).insertNode(unwrapIfNeeded(node));
+      },
+      surroundContents: function(newParent) {
+        unsafeUnwrap(this).surroundContents(unwrapIfNeeded(newParent));
+      },
+      cloneRange: function() {
+        return wrap(unsafeUnwrap(this).cloneRange());
+      },
+      isPointInRange: function(node, offset) {
+        return unsafeUnwrap(this).isPointInRange(unwrapIfNeeded(node), offset);
+      },
+      comparePoint: function(node, offset) {
+        return unsafeUnwrap(this).comparePoint(unwrapIfNeeded(node), offset);
+      },
+      intersectsNode: function(node) {
+        return unsafeUnwrap(this).intersectsNode(unwrapIfNeeded(node));
+      },
+      toString: function() {
+        return unsafeUnwrap(this).toString();
+      }
+    };
+    if (OriginalRange.prototype.createContextualFragment) {
+      Range.prototype.createContextualFragment = function(html) {
+        return wrap(unsafeUnwrap(this).createContextualFragment(html));
+      };
+    }
+    registerWrapper(window.Range, Range, document.createRange());
+    scope.wrappers.Range = Range;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var GetElementsByInterface = scope.GetElementsByInterface;
+    var ParentNodeInterface = scope.ParentNodeInterface;
+    var SelectorsInterface = scope.SelectorsInterface;
+    var mixin = scope.mixin;
+    var registerObject = scope.registerObject;
+    var DocumentFragment = registerObject(document.createDocumentFragment());
+    mixin(DocumentFragment.prototype, ParentNodeInterface);
+    mixin(DocumentFragment.prototype, SelectorsInterface);
+    mixin(DocumentFragment.prototype, GetElementsByInterface);
+    var Comment = registerObject(document.createComment(""));
+    scope.wrappers.Comment = Comment;
+    scope.wrappers.DocumentFragment = DocumentFragment;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var DocumentFragment = scope.wrappers.DocumentFragment;
+    var TreeScope = scope.TreeScope;
+    var elementFromPoint = scope.elementFromPoint;
+    var getInnerHTML = scope.getInnerHTML;
+    var getTreeScope = scope.getTreeScope;
+    var mixin = scope.mixin;
+    var rewrap = scope.rewrap;
+    var setInnerHTML = scope.setInnerHTML;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var unwrap = scope.unwrap;
+    var shadowHostTable = new WeakMap();
+    var nextOlderShadowTreeTable = new WeakMap();
+    var spaceCharRe = /[ \t\n\r\f]/;
+    function ShadowRoot(hostWrapper) {
+      var node = unwrap(unsafeUnwrap(hostWrapper).ownerDocument.createDocumentFragment());
+      DocumentFragment.call(this, node);
+      rewrap(node, this);
+      var oldShadowRoot = hostWrapper.shadowRoot;
+      nextOlderShadowTreeTable.set(this, oldShadowRoot);
+      this.treeScope_ = new TreeScope(this, getTreeScope(oldShadowRoot || hostWrapper));
+      shadowHostTable.set(this, hostWrapper);
+    }
+    ShadowRoot.prototype = Object.create(DocumentFragment.prototype);
+    mixin(ShadowRoot.prototype, {
+      constructor: ShadowRoot,
+      get innerHTML() {
+        return getInnerHTML(this);
+      },
+      set innerHTML(value) {
+        setInnerHTML(this, value);
+        this.invalidateShadowRenderer();
+      },
+      get olderShadowRoot() {
+        return nextOlderShadowTreeTable.get(this) || null;
+      },
+      get host() {
+        return shadowHostTable.get(this) || null;
+      },
+      invalidateShadowRenderer: function() {
+        return shadowHostTable.get(this).invalidateShadowRenderer();
+      },
+      elementFromPoint: function(x, y) {
+        return elementFromPoint(this, this.ownerDocument, x, y);
+      },
+      getElementById: function(id) {
+        if (spaceCharRe.test(id)) return null;
+        return this.querySelector('[id="' + id + '"]');
+      }
+    });
+    scope.wrappers.ShadowRoot = ShadowRoot;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var Element = scope.wrappers.Element;
+    var HTMLContentElement = scope.wrappers.HTMLContentElement;
+    var HTMLShadowElement = scope.wrappers.HTMLShadowElement;
+    var Node = scope.wrappers.Node;
+    var ShadowRoot = scope.wrappers.ShadowRoot;
+    var assert = scope.assert;
+    var getTreeScope = scope.getTreeScope;
+    var mixin = scope.mixin;
+    var oneOf = scope.oneOf;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var unwrap = scope.unwrap;
+    var wrap = scope.wrap;
+    var ArraySplice = scope.ArraySplice;
+    function updateWrapperUpAndSideways(wrapper) {
+      wrapper.previousSibling_ = wrapper.previousSibling;
+      wrapper.nextSibling_ = wrapper.nextSibling;
+      wrapper.parentNode_ = wrapper.parentNode;
+    }
+    function updateWrapperDown(wrapper) {
+      wrapper.firstChild_ = wrapper.firstChild;
+      wrapper.lastChild_ = wrapper.lastChild;
+    }
+    function updateAllChildNodes(parentNodeWrapper) {
+      assert(parentNodeWrapper instanceof Node);
+      for (var childWrapper = parentNodeWrapper.firstChild; childWrapper; childWrapper = childWrapper.nextSibling) {
+        updateWrapperUpAndSideways(childWrapper);
+      }
+      updateWrapperDown(parentNodeWrapper);
+    }
+    function insertBefore(parentNodeWrapper, newChildWrapper, refChildWrapper) {
+      var parentNode = unwrap(parentNodeWrapper);
+      var newChild = unwrap(newChildWrapper);
+      var refChild = refChildWrapper ? unwrap(refChildWrapper) : null;
+      remove(newChildWrapper);
+      updateWrapperUpAndSideways(newChildWrapper);
+      if (!refChildWrapper) {
+        parentNodeWrapper.lastChild_ = parentNodeWrapper.lastChild;
+        if (parentNodeWrapper.lastChild === parentNodeWrapper.firstChild) parentNodeWrapper.firstChild_ = parentNodeWrapper.firstChild;
+        var lastChildWrapper = wrap(parentNode.lastChild);
+        if (lastChildWrapper) lastChildWrapper.nextSibling_ = lastChildWrapper.nextSibling;
+      } else {
+        if (parentNodeWrapper.firstChild === refChildWrapper) parentNodeWrapper.firstChild_ = refChildWrapper;
+        refChildWrapper.previousSibling_ = refChildWrapper.previousSibling;
+      }
+      scope.originalInsertBefore.call(parentNode, newChild, refChild);
+    }
+    function remove(nodeWrapper) {
+      var node = unwrap(nodeWrapper);
+      var parentNode = node.parentNode;
+      if (!parentNode) return;
+      var parentNodeWrapper = wrap(parentNode);
+      updateWrapperUpAndSideways(nodeWrapper);
+      if (nodeWrapper.previousSibling) nodeWrapper.previousSibling.nextSibling_ = nodeWrapper;
+      if (nodeWrapper.nextSibling) nodeWrapper.nextSibling.previousSibling_ = nodeWrapper;
+      if (parentNodeWrapper.lastChild === nodeWrapper) parentNodeWrapper.lastChild_ = nodeWrapper;
+      if (parentNodeWrapper.firstChild === nodeWrapper) parentNodeWrapper.firstChild_ = nodeWrapper;
+      scope.originalRemoveChild.call(parentNode, node);
+    }
+    var distributedNodesTable = new WeakMap();
+    var destinationInsertionPointsTable = new WeakMap();
+    var rendererForHostTable = new WeakMap();
+    function resetDistributedNodes(insertionPoint) {
+      distributedNodesTable.set(insertionPoint, []);
+    }
+    function getDistributedNodes(insertionPoint) {
+      var rv = distributedNodesTable.get(insertionPoint);
+      if (!rv) distributedNodesTable.set(insertionPoint, rv = []);
+      return rv;
+    }
+    function getChildNodesSnapshot(node) {
+      var result = [], i = 0;
+      for (var child = node.firstChild; child; child = child.nextSibling) {
+        result[i++] = child;
+      }
+      return result;
+    }
+    var request = oneOf(window, [ "requestAnimationFrame", "mozRequestAnimationFrame", "webkitRequestAnimationFrame", "setTimeout" ]);
+    var pendingDirtyRenderers = [];
+    var renderTimer;
+    function renderAllPending() {
+      for (var i = 0; i < pendingDirtyRenderers.length; i++) {
+        var renderer = pendingDirtyRenderers[i];
+        var parentRenderer = renderer.parentRenderer;
+        if (parentRenderer && parentRenderer.dirty) continue;
+        renderer.render();
+      }
+      pendingDirtyRenderers = [];
+    }
+    function handleRequestAnimationFrame() {
+      renderTimer = null;
+      renderAllPending();
+    }
+    function getRendererForHost(host) {
+      var renderer = rendererForHostTable.get(host);
+      if (!renderer) {
+        renderer = new ShadowRenderer(host);
+        rendererForHostTable.set(host, renderer);
+      }
+      return renderer;
+    }
+    function getShadowRootAncestor(node) {
+      var root = getTreeScope(node).root;
+      if (root instanceof ShadowRoot) return root;
+      return null;
+    }
+    function getRendererForShadowRoot(shadowRoot) {
+      return getRendererForHost(shadowRoot.host);
+    }
+    var spliceDiff = new ArraySplice();
+    spliceDiff.equals = function(renderNode, rawNode) {
+      return unwrap(renderNode.node) === rawNode;
+    };
+    function RenderNode(node) {
+      this.skip = false;
+      this.node = node;
+      this.childNodes = [];
+    }
+    RenderNode.prototype = {
+      append: function(node) {
+        var rv = new RenderNode(node);
+        this.childNodes.push(rv);
+        return rv;
+      },
+      sync: function(opt_added) {
+        if (this.skip) return;
+        var nodeWrapper = this.node;
+        var newChildren = this.childNodes;
+        var oldChildren = getChildNodesSnapshot(unwrap(nodeWrapper));
+        var added = opt_added || new WeakMap();
+        var splices = spliceDiff.calculateSplices(newChildren, oldChildren);
+        var newIndex = 0, oldIndex = 0;
+        var lastIndex = 0;
+        for (var i = 0; i < splices.length; i++) {
+          var splice = splices[i];
+          for (;lastIndex < splice.index; lastIndex++) {
+            oldIndex++;
+            newChildren[newIndex++].sync(added);
+          }
+          var removedCount = splice.removed.length;
+          for (var j = 0; j < removedCount; j++) {
+            var wrapper = wrap(oldChildren[oldIndex++]);
+            if (!added.get(wrapper)) remove(wrapper);
+          }
+          var addedCount = splice.addedCount;
+          var refNode = oldChildren[oldIndex] && wrap(oldChildren[oldIndex]);
+          for (var j = 0; j < addedCount; j++) {
+            var newChildRenderNode = newChildren[newIndex++];
+            var newChildWrapper = newChildRenderNode.node;
+            insertBefore(nodeWrapper, newChildWrapper, refNode);
+            added.set(newChildWrapper, true);
+            newChildRenderNode.sync(added);
+          }
+          lastIndex += addedCount;
+        }
+        for (var i = lastIndex; i < newChildren.length; i++) {
+          newChildren[i].sync(added);
+        }
+      }
+    };
+    function ShadowRenderer(host) {
+      this.host = host;
+      this.dirty = false;
+      this.invalidateAttributes();
+      this.associateNode(host);
+    }
+    ShadowRenderer.prototype = {
+      render: function(opt_renderNode) {
+        if (!this.dirty) return;
+        this.invalidateAttributes();
+        var host = this.host;
+        this.distribution(host);
+        var renderNode = opt_renderNode || new RenderNode(host);
+        this.buildRenderTree(renderNode, host);
+        var topMostRenderer = !opt_renderNode;
+        if (topMostRenderer) renderNode.sync();
+        this.dirty = false;
+      },
+      get parentRenderer() {
+        return getTreeScope(this.host).renderer;
+      },
+      invalidate: function() {
+        if (!this.dirty) {
+          this.dirty = true;
+          var parentRenderer = this.parentRenderer;
+          if (parentRenderer) parentRenderer.invalidate();
+          pendingDirtyRenderers.push(this);
+          if (renderTimer) return;
+          renderTimer = window[request](handleRequestAnimationFrame, 0);
+        }
+      },
+      distribution: function(root) {
+        this.resetAllSubtrees(root);
+        this.distributionResolution(root);
+      },
+      resetAll: function(node) {
+        if (isInsertionPoint(node)) resetDistributedNodes(node); else resetDestinationInsertionPoints(node);
+        this.resetAllSubtrees(node);
+      },
+      resetAllSubtrees: function(node) {
+        for (var child = node.firstChild; child; child = child.nextSibling) {
+          this.resetAll(child);
+        }
+        if (node.shadowRoot) this.resetAll(node.shadowRoot);
+        if (node.olderShadowRoot) this.resetAll(node.olderShadowRoot);
+      },
+      distributionResolution: function(node) {
+        if (isShadowHost(node)) {
+          var shadowHost = node;
+          var pool = poolPopulation(shadowHost);
+          var shadowTrees = getShadowTrees(shadowHost);
+          for (var i = 0; i < shadowTrees.length; i++) {
+            this.poolDistribution(shadowTrees[i], pool);
+          }
+          for (var i = shadowTrees.length - 1; i >= 0; i--) {
+            var shadowTree = shadowTrees[i];
+            var shadow = getShadowInsertionPoint(shadowTree);
+            if (shadow) {
+              var olderShadowRoot = shadowTree.olderShadowRoot;
+              if (olderShadowRoot) {
+                pool = poolPopulation(olderShadowRoot);
+              }
+              for (var j = 0; j < pool.length; j++) {
+                destributeNodeInto(pool[j], shadow);
+              }
+            }
+            this.distributionResolution(shadowTree);
+          }
+        }
+        for (var child = node.firstChild; child; child = child.nextSibling) {
+          this.distributionResolution(child);
+        }
+      },
+      poolDistribution: function(node, pool) {
+        if (node instanceof HTMLShadowElement) return;
+        if (node instanceof HTMLContentElement) {
+          var content = node;
+          this.updateDependentAttributes(content.getAttribute("select"));
+          var anyDistributed = false;
+          for (var i = 0; i < pool.length; i++) {
+            var node = pool[i];
+            if (!node) continue;
+            if (matches(node, content)) {
+              destributeNodeInto(node, content);
+              pool[i] = undefined;
+              anyDistributed = true;
+            }
+          }
+          if (!anyDistributed) {
+            for (var child = content.firstChild; child; child = child.nextSibling) {
+              destributeNodeInto(child, content);
+            }
+          }
+          return;
+        }
+        for (var child = node.firstChild; child; child = child.nextSibling) {
+          this.poolDistribution(child, pool);
+        }
+      },
+      buildRenderTree: function(renderNode, node) {
+        var children = this.compose(node);
+        for (var i = 0; i < children.length; i++) {
+          var child = children[i];
+          var childRenderNode = renderNode.append(child);
+          this.buildRenderTree(childRenderNode, child);
+        }
+        if (isShadowHost(node)) {
+          var renderer = getRendererForHost(node);
+          renderer.dirty = false;
+        }
+      },
+      compose: function(node) {
+        var children = [];
+        var p = node.shadowRoot || node;
+        for (var child = p.firstChild; child; child = child.nextSibling) {
+          if (isInsertionPoint(child)) {
+            this.associateNode(p);
+            var distributedNodes = getDistributedNodes(child);
+            for (var j = 0; j < distributedNodes.length; j++) {
+              var distributedNode = distributedNodes[j];
+              if (isFinalDestination(child, distributedNode)) children.push(distributedNode);
+            }
+          } else {
+            children.push(child);
+          }
+        }
+        return children;
+      },
+      invalidateAttributes: function() {
+        this.attributes = Object.create(null);
+      },
+      updateDependentAttributes: function(selector) {
+        if (!selector) return;
+        var attributes = this.attributes;
+        if (/\.\w+/.test(selector)) attributes["class"] = true;
+        if (/#\w+/.test(selector)) attributes["id"] = true;
+        selector.replace(/\[\s*([^\s=\|~\]]+)/g, function(_, name) {
+          attributes[name] = true;
+        });
+      },
+      dependsOnAttribute: function(name) {
+        return this.attributes[name];
+      },
+      associateNode: function(node) {
+        unsafeUnwrap(node).polymerShadowRenderer_ = this;
+      }
+    };
+    function poolPopulation(node) {
+      var pool = [];
+      for (var child = node.firstChild; child; child = child.nextSibling) {
+        if (isInsertionPoint(child)) {
+          pool.push.apply(pool, getDistributedNodes(child));
+        } else {
+          pool.push(child);
+        }
+      }
+      return pool;
+    }
+    function getShadowInsertionPoint(node) {
+      if (node instanceof HTMLShadowElement) return node;
+      if (node instanceof HTMLContentElement) return null;
+      for (var child = node.firstChild; child; child = child.nextSibling) {
+        var res = getShadowInsertionPoint(child);
+        if (res) return res;
+      }
+      return null;
+    }
+    function destributeNodeInto(child, insertionPoint) {
+      getDistributedNodes(insertionPoint).push(child);
+      var points = destinationInsertionPointsTable.get(child);
+      if (!points) destinationInsertionPointsTable.set(child, [ insertionPoint ]); else points.push(insertionPoint);
+    }
+    function getDestinationInsertionPoints(node) {
+      return destinationInsertionPointsTable.get(node);
+    }
+    function resetDestinationInsertionPoints(node) {
+      destinationInsertionPointsTable.set(node, undefined);
+    }
+    var selectorStartCharRe = /^(:not\()?[*.#[a-zA-Z_|]/;
+    function matches(node, contentElement) {
+      var select = contentElement.getAttribute("select");
+      if (!select) return true;
+      select = select.trim();
+      if (!select) return true;
+      if (!(node instanceof Element)) return false;
+      if (!selectorStartCharRe.test(select)) return false;
+      try {
+        return node.matches(select);
+      } catch (ex) {
+        return false;
+      }
+    }
+    function isFinalDestination(insertionPoint, node) {
+      var points = getDestinationInsertionPoints(node);
+      return points && points[points.length - 1] === insertionPoint;
+    }
+    function isInsertionPoint(node) {
+      return node instanceof HTMLContentElement || node instanceof HTMLShadowElement;
+    }
+    function isShadowHost(shadowHost) {
+      return shadowHost.shadowRoot;
+    }
+    function getShadowTrees(host) {
+      var trees = [];
+      for (var tree = host.shadowRoot; tree; tree = tree.olderShadowRoot) {
+        trees.push(tree);
+      }
+      return trees;
+    }
+    function render(host) {
+      new ShadowRenderer(host).render();
+    }
+    Node.prototype.invalidateShadowRenderer = function(force) {
+      var renderer = unsafeUnwrap(this).polymerShadowRenderer_;
+      if (renderer) {
+        renderer.invalidate();
+        return true;
+      }
+      return false;
+    };
+    HTMLContentElement.prototype.getDistributedNodes = HTMLShadowElement.prototype.getDistributedNodes = function() {
+      renderAllPending();
+      return getDistributedNodes(this);
+    };
+    Element.prototype.getDestinationInsertionPoints = function() {
+      renderAllPending();
+      return getDestinationInsertionPoints(this) || [];
+    };
+    HTMLContentElement.prototype.nodeIsInserted_ = HTMLShadowElement.prototype.nodeIsInserted_ = function() {
+      this.invalidateShadowRenderer();
+      var shadowRoot = getShadowRootAncestor(this);
+      var renderer;
+      if (shadowRoot) renderer = getRendererForShadowRoot(shadowRoot);
+      unsafeUnwrap(this).polymerShadowRenderer_ = renderer;
+      if (renderer) renderer.invalidate();
+    };
+    scope.getRendererForHost = getRendererForHost;
+    scope.getShadowTrees = getShadowTrees;
+    scope.renderAllPending = renderAllPending;
+    scope.getDestinationInsertionPoints = getDestinationInsertionPoints;
+    scope.visual = {
+      insertBefore: insertBefore,
+      remove: remove
+    };
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var HTMLElement = scope.wrappers.HTMLElement;
+    var assert = scope.assert;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var unwrap = scope.unwrap;
+    var wrap = scope.wrap;
+    var elementsWithFormProperty = [ "HTMLButtonElement", "HTMLFieldSetElement", "HTMLInputElement", "HTMLKeygenElement", "HTMLLabelElement", "HTMLLegendElement", "HTMLObjectElement", "HTMLOutputElement", "HTMLTextAreaElement" ];
+    function createWrapperConstructor(name) {
+      if (!window[name]) return;
+      assert(!scope.wrappers[name]);
+      var GeneratedWrapper = function(node) {
+        HTMLElement.call(this, node);
+      };
+      GeneratedWrapper.prototype = Object.create(HTMLElement.prototype);
+      mixin(GeneratedWrapper.prototype, {
+        get form() {
+          return wrap(unwrap(this).form);
+        }
+      });
+      registerWrapper(window[name], GeneratedWrapper, document.createElement(name.slice(4, -7)));
+      scope.wrappers[name] = GeneratedWrapper;
+    }
+    elementsWithFormProperty.forEach(createWrapperConstructor);
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var registerWrapper = scope.registerWrapper;
+    var setWrapper = scope.setWrapper;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var unwrap = scope.unwrap;
+    var unwrapIfNeeded = scope.unwrapIfNeeded;
+    var wrap = scope.wrap;
+    var OriginalSelection = window.Selection;
+    function Selection(impl) {
+      setWrapper(impl, this);
+    }
+    Selection.prototype = {
+      get anchorNode() {
+        return wrap(unsafeUnwrap(this).anchorNode);
+      },
+      get focusNode() {
+        return wrap(unsafeUnwrap(this).focusNode);
+      },
+      addRange: function(range) {
+        unsafeUnwrap(this).addRange(unwrap(range));
+      },
+      collapse: function(node, index) {
+        unsafeUnwrap(this).collapse(unwrapIfNeeded(node), index);
+      },
+      containsNode: function(node, allowPartial) {
+        return unsafeUnwrap(this).containsNode(unwrapIfNeeded(node), allowPartial);
+      },
+      extend: function(node, offset) {
+        unsafeUnwrap(this).extend(unwrapIfNeeded(node), offset);
+      },
+      getRangeAt: function(index) {
+        return wrap(unsafeUnwrap(this).getRangeAt(index));
+      },
+      removeRange: function(range) {
+        unsafeUnwrap(this).removeRange(unwrap(range));
+      },
+      selectAllChildren: function(node) {
+        unsafeUnwrap(this).selectAllChildren(unwrapIfNeeded(node));
+      },
+      toString: function() {
+        return unsafeUnwrap(this).toString();
+      }
+    };
+    registerWrapper(window.Selection, Selection, window.getSelection());
+    scope.wrappers.Selection = Selection;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var GetElementsByInterface = scope.GetElementsByInterface;
+    var Node = scope.wrappers.Node;
+    var ParentNodeInterface = scope.ParentNodeInterface;
+    var Selection = scope.wrappers.Selection;
+    var SelectorsInterface = scope.SelectorsInterface;
+    var ShadowRoot = scope.wrappers.ShadowRoot;
+    var TreeScope = scope.TreeScope;
+    var cloneNode = scope.cloneNode;
+    var defineWrapGetter = scope.defineWrapGetter;
+    var elementFromPoint = scope.elementFromPoint;
+    var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
+    var matchesNames = scope.matchesNames;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var renderAllPending = scope.renderAllPending;
+    var rewrap = scope.rewrap;
+    var setWrapper = scope.setWrapper;
+    var unsafeUnwrap = scope.unsafeUnwrap;
+    var unwrap = scope.unwrap;
+    var wrap = scope.wrap;
+    var wrapEventTargetMethods = scope.wrapEventTargetMethods;
+    var wrapNodeList = scope.wrapNodeList;
+    var implementationTable = new WeakMap();
+    function Document(node) {
+      Node.call(this, node);
+      this.treeScope_ = new TreeScope(this, null);
+    }
+    Document.prototype = Object.create(Node.prototype);
+    defineWrapGetter(Document, "documentElement");
+    defineWrapGetter(Document, "body");
+    defineWrapGetter(Document, "head");
+    function wrapMethod(name) {
+      var original = document[name];
+      Document.prototype[name] = function() {
+        return wrap(original.apply(unsafeUnwrap(this), arguments));
+      };
+    }
+    [ "createComment", "createDocumentFragment", "createElement", "createElementNS", "createEvent", "createEventNS", "createRange", "createTextNode", "getElementById" ].forEach(wrapMethod);
+    var originalAdoptNode = document.adoptNode;
+    function adoptNodeNoRemove(node, doc) {
+      originalAdoptNode.call(unsafeUnwrap(doc), unwrap(node));
+      adoptSubtree(node, doc);
+    }
+    function adoptSubtree(node, doc) {
+      if (node.shadowRoot) doc.adoptNode(node.shadowRoot);
+      if (node instanceof ShadowRoot) adoptOlderShadowRoots(node, doc);
+      for (var child = node.firstChild; child; child = child.nextSibling) {
+        adoptSubtree(child, doc);
+      }
+    }
+    function adoptOlderShadowRoots(shadowRoot, doc) {
+      var oldShadowRoot = shadowRoot.olderShadowRoot;
+      if (oldShadowRoot) doc.adoptNode(oldShadowRoot);
+    }
+    var originalGetSelection = document.getSelection;
+    mixin(Document.prototype, {
+      adoptNode: function(node) {
+        if (node.parentNode) node.parentNode.removeChild(node);
+        adoptNodeNoRemove(node, this);
+        return node;
+      },
+      elementFromPoint: function(x, y) {
+        return elementFromPoint(this, this, x, y);
+      },
+      importNode: function(node, deep) {
+        return cloneNode(node, deep, unsafeUnwrap(this));
+      },
+      getSelection: function() {
+        renderAllPending();
+        return new Selection(originalGetSelection.call(unwrap(this)));
+      },
+      getElementsByName: function(name) {
+        return SelectorsInterface.querySelectorAll.call(this, "[name=" + JSON.stringify(String(name)) + "]");
+      }
+    });
+    if (document.registerElement) {
+      var originalRegisterElement = document.registerElement;
+      Document.prototype.registerElement = function(tagName, object) {
+        var prototype, extendsOption;
+        if (object !== undefined) {
+          prototype = object.prototype;
+          extendsOption = object.extends;
+        }
+        if (!prototype) prototype = Object.create(HTMLElement.prototype);
+        if (scope.nativePrototypeTable.get(prototype)) {
+          throw new Error("NotSupportedError");
+        }
+        var proto = Object.getPrototypeOf(prototype);
+        var nativePrototype;
+        var prototypes = [];
+        while (proto) {
+          nativePrototype = scope.nativePrototypeTable.get(proto);
+          if (nativePrototype) break;
+          prototypes.push(proto);
+          proto = Object.getPrototypeOf(proto);
+        }
+        if (!nativePrototype) {
+          throw new Error("NotSupportedError");
+        }
+        var newPrototype = Object.create(nativePrototype);
+        for (var i = prototypes.length - 1; i >= 0; i--) {
+          newPrototype = Object.create(newPrototype);
+        }
+        [ "createdCallback", "attachedCallback", "detachedCallback", "attributeChangedCallback" ].forEach(function(name) {
+          var f = prototype[name];
+          if (!f) return;
+          newPrototype[name] = function() {
+            if (!(wrap(this) instanceof CustomElementConstructor)) {
+              rewrap(this);
+            }
+            f.apply(wrap(this), arguments);
+          };
+        });
+        var p = {
+          prototype: newPrototype
+        };
+        if (extendsOption) p.extends = extendsOption;
+        function CustomElementConstructor(node) {
+          if (!node) {
+            if (extendsOption) {
+              return document.createElement(extendsOption, tagName);
+            } else {
+              return document.createElement(tagName);
+            }
+          }
+          setWrapper(node, this);
+        }
+        CustomElementConstructor.prototype = prototype;
+        CustomElementConstructor.prototype.constructor = CustomElementConstructor;
+        scope.constructorTable.set(newPrototype, CustomElementConstructor);
+        scope.nativePrototypeTable.set(prototype, newPrototype);
+        var nativeConstructor = originalRegisterElement.call(unwrap(this), tagName, p);
+        return CustomElementConstructor;
+      };
+      forwardMethodsToWrapper([ window.HTMLDocument || window.Document ], [ "registerElement" ]);
+    }
+    forwardMethodsToWrapper([ window.HTMLBodyElement, window.HTMLDocument || window.Document, window.HTMLHeadElement, window.HTMLHtmlElement ], [ "appendChild", "compareDocumentPosition", "contains", "getElementsByClassName", "getElementsByTagName", "getElementsByTagNameNS", "insertBefore", "querySelector", "querySelectorAll", "removeChild", "replaceChild" ].concat(matchesNames));
+    forwardMethodsToWrapper([ window.HTMLDocument || window.Document ], [ "adoptNode", "importNode", "contains", "createComment", "createDocumentFragment", "createElement", "createElementNS", "createEvent", "createEventNS", "createRange", "createTextNode", "elementFromPoint", "getElementById", "getElementsByName", "getSelection" ]);
+    mixin(Document.prototype, GetElementsByInterface);
+    mixin(Document.prototype, ParentNodeInterface);
+    mixin(Document.prototype, SelectorsInterface);
+    mixin(Document.prototype, {
+      get implementation() {
+        var implementation = implementationTable.get(this);
+        if (implementation) return implementation;
+        implementation = new DOMImplementation(unwrap(this).implementation);
+        implementationTable.set(this, implementation);
+        return implementation;
+      },
+      get defaultView() {
+        return wrap(unwrap(this).defaultView);
+      }
+    });
+    registerWrapper(window.Document, Document, document.implementation.createHTMLDocument(""));
+    if (window.HTMLDocument) registerWrapper(window.HTMLDocument, Document);
+    wrapEventTargetMethods([ window.HTMLBodyElement, window.HTMLDocument || window.Document, window.HTMLHeadElement ]);
+    function DOMImplementation(impl) {
+      setWrapper(impl, this);
+    }
+    function wrapImplMethod(constructor, name) {
+      var original = document.implementation[name];
+      constructor.prototype[name] = function() {
+        return wrap(original.apply(unsafeUnwrap(this), arguments));
+      };
+    }
+    function forwardImplMethod(constructor, name) {
+      var original = document.implementation[name];
+      constructor.prototype[name] = function() {
+        return original.apply(unsafeUnwrap(this), arguments);
+      };
+    }
+    wrapImplMethod(DOMImplementation, "createDocumentType");
+    wrapImplMethod(DOMImplementation, "createDocument");
+    wrapImplMethod(DOMImplementation, "createHTMLDocument");
+    forwardImplMethod(DOMImplementation, "hasFeature");
+    registerWrapper(window.DOMImplementation, DOMImplementation);
+    forwardMethodsToWrapper([ window.DOMImplementation ], [ "createDocumentType", "createDocument", "createHTMLDocument", "hasFeature" ]);
+    scope.adoptNodeNoRemove = adoptNodeNoRemove;
+    scope.wrappers.DOMImplementation = DOMImplementation;
+    scope.wrappers.Document = Document;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var EventTarget = scope.wrappers.EventTarget;
+    var Selection = scope.wrappers.Selection;
+    var mixin = scope.mixin;
+    var registerWrapper = scope.registerWrapper;
+    var renderAllPending = scope.renderAllPending;
+    var unwrap = scope.unwrap;
+    var unwrapIfNeeded = scope.unwrapIfNeeded;
+    var wrap = scope.wrap;
+    var OriginalWindow = window.Window;
+    var originalGetComputedStyle = window.getComputedStyle;
+    var originalGetDefaultComputedStyle = window.getDefaultComputedStyle;
+    var originalGetSelection = window.getSelection;
+    function Window(impl) {
+      EventTarget.call(this, impl);
+    }
+    Window.prototype = Object.create(EventTarget.prototype);
+    OriginalWindow.prototype.getComputedStyle = function(el, pseudo) {
+      return wrap(this || window).getComputedStyle(unwrapIfNeeded(el), pseudo);
+    };
+    if (originalGetDefaultComputedStyle) {
+      OriginalWindow.prototype.getDefaultComputedStyle = function(el, pseudo) {
+        return wrap(this || window).getDefaultComputedStyle(unwrapIfNeeded(el), pseudo);
+      };
+    }
+    OriginalWindow.prototype.getSelection = function() {
+      return wrap(this || window).getSelection();
+    };
+    delete window.getComputedStyle;
+    delete window.getDefaultComputedStyle;
+    delete window.getSelection;
+    [ "addEventListener", "removeEventListener", "dispatchEvent" ].forEach(function(name) {
+      OriginalWindow.prototype[name] = function() {
+        var w = wrap(this || window);
+        return w[name].apply(w, arguments);
+      };
+      delete window[name];
+    });
+    mixin(Window.prototype, {
+      getComputedStyle: function(el, pseudo) {
+        renderAllPending();
+        return originalGetComputedStyle.call(unwrap(this), unwrapIfNeeded(el), pseudo);
+      },
+      getSelection: function() {
+        renderAllPending();
+        return new Selection(originalGetSelection.call(unwrap(this)));
+      },
+      get document() {
+        return wrap(unwrap(this).document);
+      }
+    });
+    if (originalGetDefaultComputedStyle) {
+      Window.prototype.getDefaultComputedStyle = function(el, pseudo) {
+        renderAllPending();
+        return originalGetDefaultComputedStyle.call(unwrap(this), unwrapIfNeeded(el), pseudo);
+      };
+    }
+    registerWrapper(OriginalWindow, Window, window);
+    scope.wrappers.Window = Window;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var unwrap = scope.unwrap;
+    var OriginalDataTransfer = window.DataTransfer || window.Clipboard;
+    var OriginalDataTransferSetDragImage = OriginalDataTransfer.prototype.setDragImage;
+    if (OriginalDataTransferSetDragImage) {
+      OriginalDataTransfer.prototype.setDragImage = function(image, x, y) {
+        OriginalDataTransferSetDragImage.call(this, unwrap(image), x, y);
+      };
+    }
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var registerWrapper = scope.registerWrapper;
+    var setWrapper = scope.setWrapper;
+    var unwrap = scope.unwrap;
+    var OriginalFormData = window.FormData;
+    if (!OriginalFormData) return;
+    function FormData(formElement) {
+      var impl;
+      if (formElement instanceof OriginalFormData) {
+        impl = formElement;
+      } else {
+        impl = new OriginalFormData(formElement && unwrap(formElement));
+      }
+      setWrapper(impl, this);
+    }
+    registerWrapper(OriginalFormData, FormData, new OriginalFormData());
+    scope.wrappers.FormData = FormData;
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var unwrapIfNeeded = scope.unwrapIfNeeded;
+    var originalSend = XMLHttpRequest.prototype.send;
+    XMLHttpRequest.prototype.send = function(obj) {
+      return originalSend.call(this, unwrapIfNeeded(obj));
+    };
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    "use strict";
+    var isWrapperFor = scope.isWrapperFor;
+    var elements = {
+      a: "HTMLAnchorElement",
+      area: "HTMLAreaElement",
+      audio: "HTMLAudioElement",
+      base: "HTMLBaseElement",
+      body: "HTMLBodyElement",
+      br: "HTMLBRElement",
+      button: "HTMLButtonElement",
+      canvas: "HTMLCanvasElement",
+      caption: "HTMLTableCaptionElement",
+      col: "HTMLTableColElement",
+      content: "HTMLContentElement",
+      data: "HTMLDataElement",
+      datalist: "HTMLDataListElement",
+      del: "HTMLModElement",
+      dir: "HTMLDirectoryElement",
+      div: "HTMLDivElement",
+      dl: "HTMLDListElement",
+      embed: "HTMLEmbedElement",
+      fieldset: "HTMLFieldSetElement",
+      font: "HTMLFontElement",
+      form: "HTMLFormElement",
+      frame: "HTMLFrameElement",
+      frameset: "HTMLFrameSetElement",
+      h1: "HTMLHeadingElement",
+      head: "HTMLHeadElement",
+      hr: "HTMLHRElement",
+      html: "HTMLHtmlElement",
+      iframe: "HTMLIFrameElement",
+      img: "HTMLImageElement",
+      input: "HTMLInputElement",
+      keygen: "HTMLKeygenElement",
+      label: "HTMLLabelElement",
+      legend: "HTMLLegendElement",
+      li: "HTMLLIElement",
+      link: "HTMLLinkElement",
+      map: "HTMLMapElement",
+      marquee: "HTMLMarqueeElement",
+      menu: "HTMLMenuElement",
+      menuitem: "HTMLMenuItemElement",
+      meta: "HTMLMetaElement",
+      meter: "HTMLMeterElement",
+      object: "HTMLObjectElement",
+      ol: "HTMLOListElement",
+      optgroup: "HTMLOptGroupElement",
+      option: "HTMLOptionElement",
+      output: "HTMLOutputElement",
+      p: "HTMLParagraphElement",
+      param: "HTMLParamElement",
+      pre: "HTMLPreElement",
+      progress: "HTMLProgressElement",
+      q: "HTMLQuoteElement",
+      script: "HTMLScriptElement",
+      select: "HTMLSelectElement",
+      shadow: "HTMLShadowElement",
+      source: "HTMLSourceElement",
+      span: "HTMLSpanElement",
+      style: "HTMLStyleElement",
+      table: "HTMLTableElement",
+      tbody: "HTMLTableSectionElement",
+      template: "HTMLTemplateElement",
+      textarea: "HTMLTextAreaElement",
+      thead: "HTMLTableSectionElement",
+      time: "HTMLTimeElement",
+      title: "HTMLTitleElement",
+      tr: "HTMLTableRowElement",
+      track: "HTMLTrackElement",
+      ul: "HTMLUListElement",
+      video: "HTMLVideoElement"
+    };
+    function overrideConstructor(tagName) {
+      var nativeConstructorName = elements[tagName];
+      var nativeConstructor = window[nativeConstructorName];
+      if (!nativeConstructor) return;
+      var element = document.createElement(tagName);
+      var wrapperConstructor = element.constructor;
+      window[nativeConstructorName] = wrapperConstructor;
+    }
+    Object.keys(elements).forEach(overrideConstructor);
+    Object.getOwnPropertyNames(scope.wrappers).forEach(function(name) {
+      window[name] = scope.wrappers[name];
+    });
+  })(window.ShadowDOMPolyfill);
+  (function(scope) {
+    var ShadowCSS = {
+      strictStyling: false,
+      registry: {},
+      shimStyling: function(root, name, extendsName) {
+        var scopeStyles = this.prepareRoot(root, name, extendsName);
+        var typeExtension = this.isTypeExtension(extendsName);
+        var scopeSelector = this.makeScopeSelector(name, typeExtension);
+        var cssText = stylesToCssText(scopeStyles, true);
+        cssText = this.scopeCssText(cssText, scopeSelector);
+        if (root) {
+          root.shimmedStyle = cssText;
+        }
+        this.addCssToDocument(cssText, name);
+      },
+      shimStyle: function(style, selector) {
+        return this.shimCssText(style.textContent, selector);
+      },
+      shimCssText: function(cssText, selector) {
+        cssText = this.insertDirectives(cssText);
+        return this.scopeCssText(cssText, selector);
+      },
+      makeScopeSelector: function(name, typeExtension) {
+        if (name) {
+          return typeExtension ? "[is=" + name + "]" : name;
+        }
+        return "";
+      },
+      isTypeExtension: function(extendsName) {
+        return extendsName && extendsName.indexOf("-") < 0;
+      },
+      prepareRoot: function(root, name, extendsName) {
+        var def = this.registerRoot(root, name, extendsName);
+        this.replaceTextInStyles(def.rootStyles, this.insertDirectives);
+        this.removeStyles(root, def.rootStyles);
+        if (this.strictStyling) {
+          this.applyScopeToContent(root, name);
+        }
+        return def.scopeStyles;
+      },
+      removeStyles: function(root, styles) {
+        for (var i = 0, l = styles.length, s; i < l && (s = styles[i]); i++) {
+          s.parentNode.removeChild(s);
+        }
+      },
+      registerRoot: function(root, name, extendsName) {
+        var def = this.registry[name] = {
+          root: root,
+          name: name,
+          extendsName: extendsName
+        };
+        var styles = this.findStyles(root);
+        def.rootStyles = styles;
+        def.scopeStyles = def.rootStyles;
+        var extendee = this.registry[def.extendsName];
+        if (extendee) {
+          def.scopeStyles = extendee.scopeStyles.concat(def.scopeStyles);
+        }
+        return def;
+      },
+      findStyles: function(root) {
+        if (!root) {
+          return [];
+        }
+        var styles = root.querySelectorAll("style");
+        return Array.prototype.filter.call(styles, function(s) {
+          return !s.hasAttribute(NO_SHIM_ATTRIBUTE);
+        });
+      },
+      applyScopeToContent: function(root, name) {
+        if (root) {
+          Array.prototype.forEach.call(root.querySelectorAll("*"), function(node) {
+            node.setAttribute(name, "");
+          });
+          Array.prototype.forEach.call(root.querySelectorAll("template"), function(template) {
+            this.applyScopeToContent(template.content, name);
+          }, this);
+        }
+      },
+      insertDirectives: function(cssText) {
+        cssText = this.insertPolyfillDirectivesInCssText(cssText);
+        return this.insertPolyfillRulesInCssText(cssText);
+      },
+      insertPolyfillDirectivesInCssText: function(cssText) {
+        cssText = cssText.replace(cssCommentNextSelectorRe, function(match, p1) {
+          return p1.slice(0, -2) + "{";
+        });
+        return cssText.replace(cssContentNextSelectorRe, function(match, p1) {
+          return p1 + " {";
+        });
+      },
+      insertPolyfillRulesInCssText: function(cssText) {
+        cssText = cssText.replace(cssCommentRuleRe, function(match, p1) {
+          return p1.slice(0, -1);
+        });
+        return cssText.replace(cssContentRuleRe, function(match, p1, p2, p3) {
+          var rule = match.replace(p1, "").replace(p2, "");
+          return p3 + rule;
+        });
+      },
+      scopeCssText: function(cssText, scopeSelector) {
+        var unscoped = this.extractUnscopedRulesFromCssText(cssText);
+        cssText = this.insertPolyfillHostInCssText(cssText);
+        cssText = this.convertColonHost(cssText);
+        cssText = this.convertColonHostContext(cssText);
+        cssText = this.convertShadowDOMSelectors(cssText);
+        if (scopeSelector) {
+          var self = this, cssText;
+          withCssRules(cssText, function(rules) {
+            cssText = self.scopeRules(rules, scopeSelector);
+          });
+        }
+        cssText = cssText + "\n" + unscoped;
+        return cssText.trim();
+      },
+      extractUnscopedRulesFromCssText: function(cssText) {
+        var r = "", m;
+        while (m = cssCommentUnscopedRuleRe.exec(cssText)) {
+          r += m[1].slice(0, -1) + "\n\n";
+        }
+        while (m = cssContentUnscopedRuleRe.exec(cssText)) {
+          r += m[0].replace(m[2], "").replace(m[1], m[3]) + "\n\n";
+        }
+        return r;
+      },
+      convertColonHost: function(cssText) {
+        return this.convertColonRule(cssText, cssColonHostRe, this.colonHostPartReplacer);
+      },
+      convertColonHostContext: function(cssText) {
+        return this.convertColonRule(cssText, cssColonHostContextRe, this.colonHostContextPartReplacer);
+      },
+      convertColonRule: function(cssText, regExp, partReplacer) {
+        return cssText.replace(regExp, function(m, p1, p2, p3) {
+          p1 = polyfillHostNoCombinator;
+          if (p2) {
+            var parts = p2.split(","), r = [];
+            for (var i = 0, l = parts.length, p; i < l && (p = parts[i]); i++) {
+              p = p.trim();
+              r.push(partReplacer(p1, p, p3));
+            }
+            return r.join(",");
+          } else {
+            return p1 + p3;
+          }
+        });
+      },
+      colonHostContextPartReplacer: function(host, part, suffix) {
+        if (part.match(polyfillHost)) {
+          return this.colonHostPartReplacer(host, part, suffix);
+        } else {
+          return host + part + suffix + ", " + part + " " + host + suffix;
+        }
+      },
+      colonHostPartReplacer: function(host, part, suffix) {
+        return host + part.replace(polyfillHost, "") + suffix;
+      },
+      convertShadowDOMSelectors: function(cssText) {
+        for (var i = 0; i < shadowDOMSelectorsRe.length; i++) {
+          cssText = cssText.replace(shadowDOMSelectorsRe[i], " ");
+        }
+        return cssText;
+      },
+      scopeRules: function(cssRules, scopeSelector) {
+        var cssText = "";
+        if (cssRules) {
+          Array.prototype.forEach.call(cssRules, function(rule) {
+            if (rule.selectorText && (rule.style && rule.style.cssText !== undefined)) {
+              cssText += this.scopeSelector(rule.selectorText, scopeSelector, this.strictStyling) + " {\n	";
+              cssText += this.propertiesFromRule(rule) + "\n}\n\n";
+            } else if (rule.type === CSSRule.MEDIA_RULE) {
+              cssText += "@media " + rule.media.mediaText + " {\n";
+              cssText += this.scopeRules(rule.cssRules, scopeSelector);
+              cssText += "\n}\n\n";
+            } else {
+              try {
+                if (rule.cssText) {
+                  cssText += rule.cssText + "\n\n";
+                }
+              } catch (x) {
+                if (rule.type === CSSRule.KEYFRAMES_RULE && rule.cssRules) {
+                  cssText += this.ieSafeCssTextFromKeyFrameRule(rule);
+                }
+              }
+            }
+          }, this);
+        }
+        return cssText;
+      },
+      ieSafeCssTextFromKeyFrameRule: function(rule) {
+        var cssText = "@keyframes " + rule.name + " {";
+        Array.prototype.forEach.call(rule.cssRules, function(rule) {
+          cssText += " " + rule.keyText + " {" + rule.style.cssText + "}";
+        });
+        cssText += " }";
+        return cssText;
+      },
+      scopeSelector: function(selector, scopeSelector, strict) {
+        var r = [], parts = selector.split(",");
+        parts.forEach(function(p) {
+          p = p.trim();
+          if (this.selectorNeedsScoping(p, scopeSelector)) {
+            p = strict && !p.match(polyfillHostNoCombinator) ? this.applyStrictSelectorScope(p, scopeSelector) : this.applySelectorScope(p, scopeSelector);
+          }
+          r.push(p);
+        }, this);
+        return r.join(", ");
+      },
+      selectorNeedsScoping: function(selector, scopeSelector) {
+        if (Array.isArray(scopeSelector)) {
+          return true;
+        }
+        var re = this.makeScopeMatcher(scopeSelector);
+        return !selector.match(re);
+      },
+      makeScopeMatcher: function(scopeSelector) {
+        scopeSelector = scopeSelector.replace(/\[/g, "\\[").replace(/\[/g, "\\]");
+        return new RegExp("^(" + scopeSelector + ")" + selectorReSuffix, "m");
+      },
+      applySelectorScope: function(selector, selectorScope) {
+        return Array.isArray(selectorScope) ? this.applySelectorScopeList(selector, selectorScope) : this.applySimpleSelectorScope(selector, selectorScope);
+      },
+      applySelectorScopeList: function(selector, scopeSelectorList) {
+        var r = [];
+        for (var i = 0, s; s = scopeSelectorList[i]; i++) {
+          r.push(this.applySimpleSelectorScope(selector, s));
+        }
+        return r.join(", ");
+      },
+      applySimpleSelectorScope: function(selector, scopeSelector) {
+        if (selector.match(polyfillHostRe)) {
+          selector = selector.replace(polyfillHostNoCombinator, scopeSelector);
+          return selector.replace(polyfillHostRe, scopeSelector + " ");
+        } else {
+          return scopeSelector + " " + selector;
+        }
+      },
+      applyStrictSelectorScope: function(selector, scopeSelector) {
+        scopeSelector = scopeSelector.replace(/\[is=([^\]]*)\]/g, "$1");
+        var splits = [ " ", ">", "+", "~" ], scoped = selector, attrName = "[" + scopeSelector + "]";
+        splits.forEach(function(sep) {
+          var parts = scoped.split(sep);
+          scoped = parts.map(function(p) {
+            var t = p.trim().replace(polyfillHostRe, "");
+            if (t && splits.indexOf(t) < 0 && t.indexOf(attrName) < 0) {
+              p = t.replace(/([^:]*)(:*)(.*)/, "$1" + attrName + "$2$3");
+            }
+            return p;
+          }).join(sep);
+        });
+        return scoped;
+      },
+      insertPolyfillHostInCssText: function(selector) {
+        return selector.replace(colonHostContextRe, polyfillHostContext).replace(colonHostRe, polyfillHost);
+      },
+      propertiesFromRule: function(rule) {
+        var cssText = rule.style.cssText;
+        if (rule.style.content && !rule.style.content.match(/['"]+|attr/)) {
+          cssText = cssText.replace(/content:[^;]*;/g, "content: '" + rule.style.content + "';");
+        }
+        var style = rule.style;
+        for (var i in style) {
+          if (style[i] === "initial") {
+            cssText += i + ": initial; ";
+          }
+        }
+        return cssText;
+      },
+      replaceTextInStyles: function(styles, action) {
+        if (styles && action) {
+          if (!(styles instanceof Array)) {
+            styles = [ styles ];
+          }
+          Array.prototype.forEach.call(styles, function(s) {
+            s.textContent = action.call(this, s.textContent);
+          }, this);
+        }
+      },
+      addCssToDocument: function(cssText, name) {
+        if (cssText.match("@import")) {
+          addOwnSheet(cssText, name);
+        } else {
+          addCssToDocument(cssText);
+        }
+      }
+    };
+    var selectorRe = /([^{]*)({[\s\S]*?})/gim, cssCommentRe = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim, cssCommentNextSelectorRe = /\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^{]*?){/gim, cssContentNextSelectorRe = /polyfill-next-selector[^}]*content\:[\s]*?['"](.*?)['"][;\s]*}([^{]*?){/gim, cssCommentRuleRe = /\/\*\s@polyfill-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim, cssContentRuleRe = /(polyfill-rule)[^}]*(content\:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim, cssCommentUnscopedRuleRe = /\/\*\s@polyfill-unscoped-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim, cssContentUnscopedRuleRe = /(polyfill-unscoped-rule)[^}]*(content\:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim, cssPseudoRe = /::(x-[^\s{,(]*)/gim, cssPartRe = /::part\(([^)]*)\)/gim, polyfillHost = "-shadowcsshost", polyfillHostContext = "-shadowcsscontext", parenSuffix = ")(?:\\((" + "(?:\\([^)(]*\\)|[^)(]*)+?" + ")\\))?([^,{]*)";
+    var cssColonHostRe = new RegExp("(" + polyfillHost + parenSuffix, "gim"), cssColonHostContextRe = new RegExp("(" + polyfillHostContext + parenSuffix, "gim"), selectorReSuffix = "([>\\s~+[.,{:][\\s\\S]*)?$", colonHostRe = /\:host/gim, colonHostContextRe = /\:host-context/gim, polyfillHostNoCombinator = polyfillHost + "-no-combinator", polyfillHostRe = new RegExp(polyfillHost, "gim"), polyfillHostContextRe = new RegExp(polyfillHostContext, "gim"), shadowDOMSelectorsRe = [ /\^\^/g, /\^/g, /\/shadow\//g, /\/shadow-deep\//g, /::shadow/g, /\/deep\//g, /::content/g ];
+    function stylesToCssText(styles, preserveComments) {
+      var cssText = "";
+      Array.prototype.forEach.call(styles, function(s) {
+        cssText += s.textContent + "\n\n";
+      });
+      if (!preserveComments) {
+        cssText = cssText.replace(cssCommentRe, "");
+      }
+      return cssText;
+    }
+    function cssTextToStyle(cssText) {
+      var style = document.createElement("style");
+      style.textContent = cssText;
+      return style;
+    }
+    function cssToRules(cssText) {
+      var style = cssTextToStyle(cssText);
+      document.head.appendChild(style);
+      var rules = [];
+      if (style.sheet) {
+        try {
+          rules = style.sheet.cssRules;
+        } catch (e) {}
+      } else {
+        console.warn("sheet not found", style);
+      }
+      style.parentNode.removeChild(style);
+      return rules;
+    }
+    var frame = document.createElement("iframe");
+    frame.style.display = "none";
+    function initFrame() {
+      frame.initialized = true;
+      document.body.appendChild(frame);
+      var doc = frame.contentDocument;
+      var base = doc.createElement("base");
+      base.href = document.baseURI;
+      doc.head.appendChild(base);
+    }
+    function inFrame(fn) {
+      if (!frame.initialized) {
+        initFrame();
+      }
+      document.body.appendChild(frame);
+      fn(frame.contentDocument);
+      document.body.removeChild(frame);
+    }
+    var isChrome = navigator.userAgent.match("Chrome");
+    function withCssRules(cssText, callback) {
+      if (!callback) {
+        return;
+      }
+      var rules;
+      if (cssText.match("@import") && isChrome) {
+        var style = cssTextToStyle(cssText);
+        inFrame(function(doc) {
+          doc.head.appendChild(style.impl);
+          rules = Array.prototype.slice.call(style.sheet.cssRules, 0);
+          callback(rules);
+        });
+      } else {
+        rules = cssToRules(cssText);
+        callback(rules);
+      }
+    }
+    function rulesToCss(cssRules) {
+      for (var i = 0, css = []; i < cssRules.length; i++) {
+        css.push(cssRules[i].cssText);
+      }
+      return css.join("\n\n");
+    }
+    function addCssToDocument(cssText) {
+      if (cssText) {
+        getSheet().appendChild(document.createTextNode(cssText));
+      }
+    }
+    function addOwnSheet(cssText, name) {
+      var style = cssTextToStyle(cssText);
+      style.setAttribute(name, "");
+      style.setAttribute(SHIMMED_ATTRIBUTE, "");
+      document.head.appendChild(style);
+    }
+    var SHIM_ATTRIBUTE = "shim-shadowdom";
+    var SHIMMED_ATTRIBUTE = "shim-shadowdom-css";
+    var NO_SHIM_ATTRIBUTE = "no-shim";
+    var sheet;
+    function getSheet() {
+      if (!sheet) {
+        sheet = document.createElement("style");
+        sheet.setAttribute(SHIMMED_ATTRIBUTE, "");
+        sheet[SHIMMED_ATTRIBUTE] = true;
+      }
+      return sheet;
+    }
+    if (window.ShadowDOMPolyfill) {
+      addCssToDocument("style { display: none !important; }\n");
+      var doc = ShadowDOMPolyfill.wrap(document);
+      var head = doc.querySelector("head");
+      head.insertBefore(getSheet(), head.childNodes[0]);
+      document.addEventListener("DOMContentLoaded", function() {
+        var urlResolver = scope.urlResolver;
+        if (window.HTMLImports && !HTMLImports.useNative) {
+          var SHIM_SHEET_SELECTOR = "link[rel=stylesheet]" + "[" + SHIM_ATTRIBUTE + "]";
+          var SHIM_STYLE_SELECTOR = "style[" + SHIM_ATTRIBUTE + "]";
+          HTMLImports.importer.documentPreloadSelectors += "," + SHIM_SHEET_SELECTOR;
+          HTMLImports.importer.importsPreloadSelectors += "," + SHIM_SHEET_SELECTOR;
+          HTMLImports.parser.documentSelectors = [ HTMLImports.parser.documentSelectors, SHIM_SHEET_SELECTOR, SHIM_STYLE_SELECTOR ].join(",");
+          var originalParseGeneric = HTMLImports.parser.parseGeneric;
+          HTMLImports.parser.parseGeneric = function(elt) {
+            if (elt[SHIMMED_ATTRIBUTE]) {
+              return;
+            }
+            var style = elt.__importElement || elt;
+            if (!style.hasAttribute(SHIM_ATTRIBUTE)) {
+              originalParseGeneric.call(this, elt);
+              return;
+            }
+            if (elt.__resource) {
+              style = elt.ownerDocument.createElement("style");
+              style.textContent = elt.__resource;
+            }
+            HTMLImports.path.resolveUrlsInStyle(style);
+            style.textContent = ShadowCSS.shimStyle(style);
+            style.removeAttribute(SHIM_ATTRIBUTE, "");
+            style.setAttribute(SHIMMED_ATTRIBUTE, "");
+            style[SHIMMED_ATTRIBUTE] = true;
+            if (style.parentNode !== head) {
+              if (elt.parentNode === head) {
+                head.replaceChild(style, elt);
+              } else {
+                this.addElementToDocument(style);
+              }
+            }
+            style.__importParsed = true;
+            this.markParsingComplete(elt);
+            this.parseNext();
+          };
+          var hasResource = HTMLImports.parser.hasResource;
+          HTMLImports.parser.hasResource = function(node) {
+            if (node.localName === "link" && node.rel === "stylesheet" && node.hasAttribute(SHIM_ATTRIBUTE)) {
+              return node.__resource;
+            } else {
+              return hasResource.call(this, node);
+            }
+          };
+        }
+      });
+    }
+    scope.ShadowCSS = ShadowCSS;
+  })(window.WebComponents);
+}
+
+(function(scope) {
+  if (window.ShadowDOMPolyfill) {
+    window.wrap = ShadowDOMPolyfill.wrapIfNeeded;
+    window.unwrap = ShadowDOMPolyfill.unwrapIfNeeded;
+  } else {
+    window.wrap = window.unwrap = function(n) {
+      return n;
+    };
+  }
+})(window.WebComponents);
+
+(function(global) {
+  var registrationsTable = new WeakMap();
+  var setImmediate;
+  if (/Trident/.test(navigator.userAgent)) {
+    setImmediate = setTimeout;
+  } else if (window.setImmediate) {
+    setImmediate = window.setImmediate;
+  } else {
+    var setImmediateQueue = [];
+    var sentinel = String(Math.random());
+    window.addEventListener("message", function(e) {
+      if (e.data === sentinel) {
+        var queue = setImmediateQueue;
+        setImmediateQueue = [];
+        queue.forEach(function(func) {
+          func();
+        });
+      }
+    });
+    setImmediate = function(func) {
+      setImmediateQueue.push(func);
+      window.postMessage(sentinel, "*");
+    };
+  }
+  var isScheduled = false;
+  var scheduledObservers = [];
+  function scheduleCallback(observer) {
+    scheduledObservers.push(observer);
+    if (!isScheduled) {
+      isScheduled = true;
+      setImmediate(dispatchCallbacks);
+    }
+  }
+  function wrapIfNeeded(node) {
+    return window.ShadowDOMPolyfill && window.ShadowDOMPolyfill.wrapIfNeeded(node) || node;
+  }
+  function dispatchCallbacks() {
+    isScheduled = false;
+    var observers = scheduledObservers;
+    scheduledObservers = [];
+    observers.sort(function(o1, o2) {
+      return o1.uid_ - o2.uid_;
+    });
+    var anyNonEmpty = false;
+    observers.forEach(function(observer) {
+      var queue = observer.takeRecords();
+      removeTransientObserversFor(observer);
+      if (queue.length) {
+        observer.callback_(queue, observer);
+        anyNonEmpty = true;
+      }
+    });
+    if (anyNonEmpty) dispatchCallbacks();
+  }
+  function removeTransientObserversFor(observer) {
+    observer.nodes_.forEach(function(node) {
+      var registrations = registrationsTable.get(node);
+      if (!registrations) return;
+      registrations.forEach(function(registration) {
+        if (registration.observer === observer) registration.removeTransientObservers();
+      });
+    });
+  }
+  function forEachAncestorAndObserverEnqueueRecord(target, callback) {
+    for (var node = target; node; node = node.parentNode) {
+      var registrations = registrationsTable.get(node);
+      if (registrations) {
+        for (var j = 0; j < registrations.length; j++) {
+          var registration = registrations[j];
+          var options = registration.options;
+          if (node !== target && !options.subtree) continue;
+          var record = callback(options);
+          if (record) registration.enqueue(record);
+        }
+      }
+    }
+  }
+  var uidCounter = 0;
+  function JsMutationObserver(callback) {
+    this.callback_ = callback;
+    this.nodes_ = [];
+    this.records_ = [];
+    this.uid_ = ++uidCounter;
+  }
+  JsMutationObserver.prototype = {
+    observe: function(target, options) {
+      target = wrapIfNeeded(target);
+      if (!options.childList && !options.attributes && !options.characterData || options.attributeOldValue && !options.attributes || options.attributeFilter && options.attributeFilter.length && !options.attributes || options.characterDataOldValue && !options.characterData) {
+        throw new SyntaxError();
+      }
+      var registrations = registrationsTable.get(target);
+      if (!registrations) registrationsTable.set(target, registrations = []);
+      var registration;
+      for (var i = 0; i < registrations.length; i++) {
+        if (registrations[i].observer === this) {
+          registration = registrations[i];
+          registration.removeListeners();
+          registration.options = options;
+          break;
+        }
+      }
+      if (!registration) {
+        registration = new Registration(this, target, options);
+        registrations.push(registration);
+        this.nodes_.push(target);
+      }
+      registration.addListeners();
+    },
+    disconnect: function() {
+      this.nodes_.forEach(function(node) {
+        var registrations = registrationsTable.get(node);
+        for (var i = 0; i < registrations.length; i++) {
+          var registration = registrations[i];
+          if (registration.observer === this) {
+            registration.removeListeners();
+            registrations.splice(i, 1);
+            break;
+          }
+        }
+      }, this);
+      this.records_ = [];
+    },
+    takeRecords: function() {
+      var copyOfRecords = this.records_;
+      this.records_ = [];
+      return copyOfRecords;
+    }
+  };
+  function MutationRecord(type, target) {
+    this.type = type;
+    this.target = target;
+    this.addedNodes = [];
+    this.removedNodes = [];
+    this.previousSibling = null;
+    this.nextSibling = null;
+    this.attributeName = null;
+    this.attributeNamespace = null;
+    this.oldValue = null;
+  }
+  function copyMutationRecord(original) {
+    var record = new MutationRecord(original.type, original.target);
+    record.addedNodes = original.addedNodes.slice();
+    record.removedNodes = original.removedNodes.slice();
+    record.previousSibling = original.previousSibling;
+    record.nextSibling = original.nextSibling;
+    record.attributeName = original.attributeName;
+    record.attributeNamespace = original.attributeNamespace;
+    record.oldValue = original.oldValue;
+    return record;
+  }
+  var currentRecord, recordWithOldValue;
+  function getRecord(type, target) {
+    return currentRecord = new MutationRecord(type, target);
+  }
+  function getRecordWithOldValue(oldValue) {
+    if (recordWithOldValue) return recordWithOldValue;
+    recordWithOldValue = copyMutationRecord(currentRecord);
+    recordWithOldValue.oldValue = oldValue;
+    return recordWithOldValue;
+  }
+  function clearRecords() {
+    currentRecord = recordWithOldValue = undefined;
+  }
+  function recordRepresentsCurrentMutation(record) {
+    return record === recordWithOldValue || record === currentRecord;
+  }
+  function selectRecord(lastRecord, newRecord) {
+    if (lastRecord === newRecord) return lastRecord;
+    if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) return recordWithOldValue;
+    return null;
+  }
+  function Registration(observer, target, options) {
+    this.observer = observer;
+    this.target = target;
+    this.options = options;
+    this.transientObservedNodes = [];
+  }
+  Registration.prototype = {
+    enqueue: function(record) {
+      var records = this.observer.records_;
+      var length = records.length;
+      if (records.length > 0) {
+        var lastRecord = records[length - 1];
+        var recordToReplaceLast = selectRecord(lastRecord, record);
+        if (recordToReplaceLast) {
+          records[length - 1] = recordToReplaceLast;
+          return;
+        }
+      } else {
+        scheduleCallback(this.observer);
+      }
+      records[length] = record;
+    },
+    addListeners: function() {
+      this.addListeners_(this.target);
+    },
+    addListeners_: function(node) {
+      var options = this.options;
+      if (options.attributes) node.addEventListener("DOMAttrModified", this, true);
+      if (options.characterData) node.addEventListener("DOMCharacterDataModified", this, true);
+      if (options.childList) node.addEventListener("DOMNodeInserted", this, true);
+      if (options.childList || options.subtree) node.addEventListener("DOMNodeRemoved", this, true);
+    },
+    removeListeners: function() {
+      this.removeListeners_(this.target);
+    },
+    removeListeners_: function(node) {
+      var options = this.options;
+      if (options.attributes) node.removeEventListener("DOMAttrModified", this, true);
+      if (options.characterData) node.removeEventListener("DOMCharacterDataModified", this, true);
+      if (options.childList) node.removeEventListener("DOMNodeInserted", this, true);
+      if (options.childList || options.subtree) node.removeEventListener("DOMNodeRemoved", this, true);
+    },
+    addTransientObserver: function(node) {
+      if (node === this.target) return;
+      this.addListeners_(node);
+      this.transientObservedNodes.push(node);
+      var registrations = registrationsTable.get(node);
+      if (!registrations) registrationsTable.set(node, registrations = []);
+      registrations.push(this);
+    },
+    removeTransientObservers: function() {
+      var transientObservedNodes = this.transientObservedNodes;
+      this.transientObservedNodes = [];
+      transientObservedNodes.forEach(function(node) {
+        this.removeListeners_(node);
+        var registrations = registrationsTable.get(node);
+        for (var i = 0; i < registrations.length; i++) {
+          if (registrations[i] === this) {
+            registrations.splice(i, 1);
+            break;
+          }
+        }
+      }, this);
+    },
+    handleEvent: function(e) {
+      e.stopImmediatePropagation();
+      switch (e.type) {
+       case "DOMAttrModified":
+        var name = e.attrName;
+        var namespace = e.relatedNode.namespaceURI;
+        var target = e.target;
+        var record = new getRecord("attributes", target);
+        record.attributeName = name;
+        record.attributeNamespace = namespace;
+        var oldValue = e.attrChange === MutationEvent.ADDITION ? null : e.prevValue;
+        forEachAncestorAndObserverEnqueueRecord(target, function(options) {
+          if (!options.attributes) return;
+          if (options.attributeFilter && options.attributeFilter.length && options.attributeFilter.indexOf(name) === -1 && options.attributeFilter.indexOf(namespace) === -1) {
+            return;
+          }
+          if (options.attributeOldValue) return getRecordWithOldValue(oldValue);
+          return record;
+        });
+        break;
+
+       case "DOMCharacterDataModified":
+        var target = e.target;
+        var record = getRecord("characterData", target);
+        var oldValue = e.prevValue;
+        forEachAncestorAndObserverEnqueueRecord(target, function(options) {
+          if (!options.characterData) return;
+          if (options.characterDataOldValue) return getRecordWithOldValue(oldValue);
+          return record;
+        });
+        break;
+
+       case "DOMNodeRemoved":
+        this.addTransientObserver(e.target);
+
+       case "DOMNodeInserted":
+        var target = e.relatedNode;
+        var changedNode = e.target;
+        var addedNodes, removedNodes;
+        if (e.type === "DOMNodeInserted") {
+          addedNodes = [ changedNode ];
+          removedNodes = [];
+        } else {
+          addedNodes = [];
+          removedNodes = [ changedNode ];
+        }
+        var previousSibling = changedNode.previousSibling;
+        var nextSibling = changedNode.nextSibling;
+        var record = getRecord("childList", target);
+        record.addedNodes = addedNodes;
+        record.removedNodes = removedNodes;
+        record.previousSibling = previousSibling;
+        record.nextSibling = nextSibling;
+        forEachAncestorAndObserverEnqueueRecord(target, function(options) {
+          if (!options.childList) return;
+          return record;
+        });
+      }
+      clearRecords();
+    }
+  };
+  global.JsMutationObserver = JsMutationObserver;
+  if (!global.MutationObserver) global.MutationObserver = JsMutationObserver;
+})(this);
+
+window.HTMLImports = window.HTMLImports || {
+  flags: {}
+};
+
+(function(scope) {
+  var IMPORT_LINK_TYPE = "import";
+  var useNative = Boolean(IMPORT_LINK_TYPE in document.createElement("link"));
+  var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill);
+  var wrap = function(node) {
+    return hasShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) : node;
+  };
+  var rootDocument = wrap(document);
+  var currentScriptDescriptor = {
+    get: function() {
+      var script = HTMLImports.currentScript || document.currentScript || (document.readyState !== "complete" ? document.scripts[document.scripts.length - 1] : null);
+      return wrap(script);
+    },
+    configurable: true
+  };
+  Object.defineProperty(document, "_currentScript", currentScriptDescriptor);
+  Object.defineProperty(rootDocument, "_currentScript", currentScriptDescriptor);
+  var isIE = /Trident/.test(navigator.userAgent);
+  function whenReady(callback, doc) {
+    doc = doc || rootDocument;
+    whenDocumentReady(function() {
+      watchImportsLoad(callback, doc);
+    }, doc);
+  }
+  var requiredReadyState = isIE ? "complete" : "interactive";
+  var READY_EVENT = "readystatechange";
+  function isDocumentReady(doc) {
+    return doc.readyState === "complete" || doc.readyState === requiredReadyState;
+  }
+  function whenDocumentReady(callback, doc) {
+    if (!isDocumentReady(doc)) {
+      var checkReady = function() {
+        if (doc.readyState === "complete" || doc.readyState === requiredReadyState) {
+          doc.removeEventListener(READY_EVENT, checkReady);
+          whenDocumentReady(callback, doc);
+        }
+      };
+      doc.addEventListener(READY_EVENT, checkReady);
+    } else if (callback) {
+      callback();
+    }
+  }
+  function markTargetLoaded(event) {
+    event.target.__loaded = true;
+  }
+  function watchImportsLoad(callback, doc) {
+    var imports = doc.querySelectorAll("link[rel=import]");
+    var loaded = 0, l = imports.length;
+    function checkDone(d) {
+      if (loaded == l && callback) {
+        callback();
+      }
+    }
+    function loadedImport(e) {
+      markTargetLoaded(e);
+      loaded++;
+      checkDone();
+    }
+    if (l) {
+      for (var i = 0, imp; i < l && (imp = imports[i]); i++) {
+        if (isImportLoaded(imp)) {
+          loadedImport.call(imp, {
+            target: imp
+          });
+        } else {
+          imp.addEventListener("load", loadedImport);
+          imp.addEventListener("error", loadedImport);
+        }
+      }
+    } else {
+      checkDone();
+    }
+  }
+  function isImportLoaded(link) {
+    return useNative ? link.__loaded || link.import && link.import.readyState !== "loading" : link.__importParsed;
+  }
+  if (useNative) {
+    new MutationObserver(function(mxns) {
+      for (var i = 0, l = mxns.length, m; i < l && (m = mxns[i]); i++) {
+        if (m.addedNodes) {
+          handleImports(m.addedNodes);
+        }
+      }
+    }).observe(document.head, {
+      childList: true
+    });
+    function handleImports(nodes) {
+      for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+        if (isImport(n)) {
+          handleImport(n);
+        }
+      }
+    }
+    function isImport(element) {
+      return element.localName === "link" && element.rel === "import";
+    }
+    function handleImport(element) {
+      var loaded = element.import;
+      if (loaded) {
+        markTargetLoaded({
+          target: element
+        });
+      } else {
+        element.addEventListener("load", markTargetLoaded);
+        element.addEventListener("error", markTargetLoaded);
+      }
+    }
+    (function() {
+      if (document.readyState === "loading") {
+        var imports = document.querySelectorAll("link[rel=import]");
+        for (var i = 0, l = imports.length, imp; i < l && (imp = imports[i]); i++) {
+          handleImport(imp);
+        }
+      }
+    })();
+  }
+  whenReady(function() {
+    HTMLImports.ready = true;
+    HTMLImports.readyTime = new Date().getTime();
+    rootDocument.dispatchEvent(new CustomEvent("HTMLImportsLoaded", {
+      bubbles: true
+    }));
+  });
+  scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
+  scope.useNative = useNative;
+  scope.rootDocument = rootDocument;
+  scope.whenReady = whenReady;
+  scope.isIE = isIE;
+})(HTMLImports);
+
+(function(scope) {
+  var modules = [];
+  var addModule = function(module) {
+    modules.push(module);
+  };
+  var initializeModules = function() {
+    modules.forEach(function(module) {
+      module(scope);
+    });
+  };
+  scope.addModule = addModule;
+  scope.initializeModules = initializeModules;
+})(HTMLImports);
+
+HTMLImports.addModule(function(scope) {
+  var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g;
+  var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
+  var path = {
+    resolveUrlsInStyle: function(style) {
+      var doc = style.ownerDocument;
+      var resolver = doc.createElement("a");
+      style.textContent = this.resolveUrlsInCssText(style.textContent, resolver);
+      return style;
+    },
+    resolveUrlsInCssText: function(cssText, urlObj) {
+      var r = this.replaceUrls(cssText, urlObj, CSS_URL_REGEXP);
+      r = this.replaceUrls(r, urlObj, CSS_IMPORT_REGEXP);
+      return r;
+    },
+    replaceUrls: function(text, urlObj, regexp) {
+      return text.replace(regexp, function(m, pre, url, post) {
+        var urlPath = url.replace(/["']/g, "");
+        urlObj.href = urlPath;
+        urlPath = urlObj.href;
+        return pre + "'" + urlPath + "'" + post;
+      });
+    }
+  };
+  scope.path = path;
+});
+
+HTMLImports.addModule(function(scope) {
+  xhr = {
+    async: true,
+    ok: function(request) {
+      return request.status >= 200 && request.status < 300 || request.status === 304 || request.status === 0;
+    },
+    load: function(url, next, nextContext) {
+      var request = new XMLHttpRequest();
+      if (scope.flags.debug || scope.flags.bust) {
+        url += "?" + Math.random();
+      }
+      request.open("GET", url, xhr.async);
+      request.addEventListener("readystatechange", function(e) {
+        if (request.readyState === 4) {
+          var locationHeader = request.getResponseHeader("Location");
+          var redirectedUrl = null;
+          if (locationHeader) {
+            var redirectedUrl = locationHeader.substr(0, 1) === "/" ? location.origin + locationHeader : locationHeader;
+          }
+          next.call(nextContext, !xhr.ok(request) && request, request.response || request.responseText, redirectedUrl);
+        }
+      });
+      request.send();
+      return request;
+    },
+    loadDocument: function(url, next, nextContext) {
+      this.load(url, next, nextContext).responseType = "document";
+    }
+  };
+  scope.xhr = xhr;
+});
+
+HTMLImports.addModule(function(scope) {
+  var xhr = scope.xhr;
+  var flags = scope.flags;
+  var Loader = function(onLoad, onComplete) {
+    this.cache = {};
+    this.onload = onLoad;
+    this.oncomplete = onComplete;
+    this.inflight = 0;
+    this.pending = {};
+  };
+  Loader.prototype = {
+    addNodes: function(nodes) {
+      this.inflight += nodes.length;
+      for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+        this.require(n);
+      }
+      this.checkDone();
+    },
+    addNode: function(node) {
+      this.inflight++;
+      this.require(node);
+      this.checkDone();
+    },
+    require: function(elt) {
+      var url = elt.src || elt.href;
+      elt.__nodeUrl = url;
+      if (!this.dedupe(url, elt)) {
+        this.fetch(url, elt);
+      }
+    },
+    dedupe: function(url, elt) {
+      if (this.pending[url]) {
+        this.pending[url].push(elt);
+        return true;
+      }
+      var resource;
+      if (this.cache[url]) {
+        this.onload(url, elt, this.cache[url]);
+        this.tail();
+        return true;
+      }
+      this.pending[url] = [ elt ];
+      return false;
+    },
+    fetch: function(url, elt) {
+      flags.load && console.log("fetch", url, elt);
+      if (url.match(/^data:/)) {
+        var pieces = url.split(",");
+        var header = pieces[0];
+        var body = pieces[1];
+        if (header.indexOf(";base64") > -1) {
+          body = atob(body);
+        } else {
+          body = decodeURIComponent(body);
+        }
+        setTimeout(function() {
+          this.receive(url, elt, null, body);
+        }.bind(this), 0);
+      } else {
+        var receiveXhr = function(err, resource, redirectedUrl) {
+          this.receive(url, elt, err, resource, redirectedUrl);
+        }.bind(this);
+        xhr.load(url, receiveXhr);
+      }
+    },
+    receive: function(url, elt, err, resource, redirectedUrl) {
+      this.cache[url] = resource;
+      var $p = this.pending[url];
+      for (var i = 0, l = $p.length, p; i < l && (p = $p[i]); i++) {
+        this.onload(url, p, resource, err, redirectedUrl);
+        this.tail();
+      }
+      this.pending[url] = null;
+    },
+    tail: function() {
+      --this.inflight;
+      this.checkDone();
+    },
+    checkDone: function() {
+      if (!this.inflight) {
+        this.oncomplete();
+      }
+    }
+  };
+  scope.Loader = Loader;
+});
+
+HTMLImports.addModule(function(scope) {
+  var Observer = function(addCallback) {
+    this.addCallback = addCallback;
+    this.mo = new MutationObserver(this.handler.bind(this));
+  };
+  Observer.prototype = {
+    handler: function(mutations) {
+      for (var i = 0, l = mutations.length, m; i < l && (m = mutations[i]); i++) {
+        if (m.type === "childList" && m.addedNodes.length) {
+          this.addedNodes(m.addedNodes);
+        }
+      }
+    },
+    addedNodes: function(nodes) {
+      if (this.addCallback) {
+        this.addCallback(nodes);
+      }
+      for (var i = 0, l = nodes.length, n, loading; i < l && (n = nodes[i]); i++) {
+        if (n.children && n.children.length) {
+          this.addedNodes(n.children);
+        }
+      }
+    },
+    observe: function(root) {
+      this.mo.observe(root, {
+        childList: true,
+        subtree: true
+      });
+    }
+  };
+  scope.Observer = Observer;
+});
+
+HTMLImports.addModule(function(scope) {
+  var path = scope.path;
+  var rootDocument = scope.rootDocument;
+  var flags = scope.flags;
+  var isIE = scope.isIE;
+  var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE;
+  var IMPORT_SELECTOR = "link[rel=" + IMPORT_LINK_TYPE + "]";
+  var importParser = {
+    documentSelectors: IMPORT_SELECTOR,
+    importsSelectors: [ IMPORT_SELECTOR, "link[rel=stylesheet]", "style", "script:not([type])", 'script[type="text/javascript"]' ].join(","),
+    map: {
+      link: "parseLink",
+      script: "parseScript",
+      style: "parseStyle"
+    },
+    dynamicElements: [],
+    parseNext: function() {
+      var next = this.nextToParse();
+      if (next) {
+        this.parse(next);
+      }
+    },
+    parse: function(elt) {
+      if (this.isParsed(elt)) {
+        flags.parse && console.log("[%s] is already parsed", elt.localName);
+        return;
+      }
+      var fn = this[this.map[elt.localName]];
+      if (fn) {
+        this.markParsing(elt);
+        fn.call(this, elt);
+      }
+    },
+    parseDynamic: function(elt, quiet) {
+      this.dynamicElements.push(elt);
+      if (!quiet) {
+        this.parseNext();
+      }
+    },
+    markParsing: function(elt) {
+      flags.parse && console.log("parsing", elt);
+      this.parsingElement = elt;
+    },
+    markParsingComplete: function(elt) {
+      elt.__importParsed = true;
+      this.markDynamicParsingComplete(elt);
+      if (elt.__importElement) {
+        elt.__importElement.__importParsed = true;
+        this.markDynamicParsingComplete(elt.__importElement);
+      }
+      this.parsingElement = null;
+      flags.parse && console.log("completed", elt);
+    },
+    markDynamicParsingComplete: function(elt) {
+      var i = this.dynamicElements.indexOf(elt);
+      if (i >= 0) {
+        this.dynamicElements.splice(i, 1);
+      }
+    },
+    parseImport: function(elt) {
+      if (HTMLImports.__importsParsingHook) {
+        HTMLImports.__importsParsingHook(elt);
+      }
+      if (elt.import) {
+        elt.import.__importParsed = true;
+      }
+      this.markParsingComplete(elt);
+      if (elt.__resource && !elt.__error) {
+        elt.dispatchEvent(new CustomEvent("load", {
+          bubbles: false
+        }));
+      } else {
+        elt.dispatchEvent(new CustomEvent("error", {
+          bubbles: false
+        }));
+      }
+      if (elt.__pending) {
+        var fn;
+        while (elt.__pending.length) {
+          fn = elt.__pending.shift();
+          if (fn) {
+            fn({
+              target: elt
+            });
+          }
+        }
+      }
+      this.parseNext();
+    },
+    parseLink: function(linkElt) {
+      if (nodeIsImport(linkElt)) {
+        this.parseImport(linkElt);
+      } else {
+        linkElt.href = linkElt.href;
+        this.parseGeneric(linkElt);
+      }
+    },
+    parseStyle: function(elt) {
+      var src = elt;
+      elt = cloneStyle(elt);
+      elt.__importElement = src;
+      this.parseGeneric(elt);
+    },
+    parseGeneric: function(elt) {
+      this.trackElement(elt);
+      this.addElementToDocument(elt);
+    },
+    rootImportForElement: function(elt) {
+      var n = elt;
+      while (n.ownerDocument.__importLink) {
+        n = n.ownerDocument.__importLink;
+      }
+      return n;
+    },
+    addElementToDocument: function(elt) {
+      var port = this.rootImportForElement(elt.__importElement || elt);
+      var l = port.__insertedElements = port.__insertedElements || 0;
+      var refNode = port.nextElementSibling;
+      for (var i = 0; i < l; i++) {
+        refNode = refNode && refNode.nextElementSibling;
+      }
+      port.parentNode.insertBefore(elt, refNode);
+    },
+    trackElement: function(elt, callback) {
+      var self = this;
+      var done = function(e) {
+        if (callback) {
+          callback(e);
+        }
+        self.markParsingComplete(elt);
+        self.parseNext();
+      };
+      elt.addEventListener("load", done);
+      elt.addEventListener("error", done);
+      if (isIE && elt.localName === "style") {
+        var fakeLoad = false;
+        if (elt.textContent.indexOf("@import") == -1) {
+          fakeLoad = true;
+        } else if (elt.sheet) {
+          fakeLoad = true;
+          var csr = elt.sheet.cssRules;
+          var len = csr ? csr.length : 0;
+          for (var i = 0, r; i < len && (r = csr[i]); i++) {
+            if (r.type === CSSRule.IMPORT_RULE) {
+              fakeLoad = fakeLoad && Boolean(r.styleSheet);
+            }
+          }
+        }
+        if (fakeLoad) {
+          elt.dispatchEvent(new CustomEvent("load", {
+            bubbles: false
+          }));
+        }
+      }
+    },
+    parseScript: function(scriptElt) {
+      var script = document.createElement("script");
+      script.__importElement = scriptElt;
+      script.src = scriptElt.src ? scriptElt.src : generateScriptDataUrl(scriptElt);
+      scope.currentScript = scriptElt;
+      this.trackElement(script, function(e) {
+        script.parentNode.removeChild(script);
+        scope.currentScript = null;
+      });
+      this.addElementToDocument(script);
+    },
+    nextToParse: function() {
+      this._mayParse = [];
+      return !this.parsingElement && (this.nextToParseInDoc(rootDocument) || this.nextToParseDynamic());
+    },
+    nextToParseInDoc: function(doc, link) {
+      if (doc && this._mayParse.indexOf(doc) < 0) {
+        this._mayParse.push(doc);
+        var nodes = doc.querySelectorAll(this.parseSelectorsForNode(doc));
+        for (var i = 0, l = nodes.length, p = 0, n; i < l && (n = nodes[i]); i++) {
+          if (!this.isParsed(n)) {
+            if (this.hasResource(n)) {
+              return nodeIsImport(n) ? this.nextToParseInDoc(n.import, n) : n;
+            } else {
+              return;
+            }
+          }
+        }
+      }
+      return link;
+    },
+    nextToParseDynamic: function() {
+      return this.dynamicElements[0];
+    },
+    parseSelectorsForNode: function(node) {
+      var doc = node.ownerDocument || node;
+      return doc === rootDocument ? this.documentSelectors : this.importsSelectors;
+    },
+    isParsed: function(node) {
+      return node.__importParsed;
+    },
+    needsDynamicParsing: function(elt) {
+      return this.dynamicElements.indexOf(elt) >= 0;
+    },
+    hasResource: function(node) {
+      if (nodeIsImport(node) && node.import === undefined) {
+        return false;
+      }
+      return true;
+    }
+  };
+  function nodeIsImport(elt) {
+    return elt.localName === "link" && elt.rel === IMPORT_LINK_TYPE;
+  }
+  function generateScriptDataUrl(script) {
+    var scriptContent = generateScriptContent(script);
+    return "data:text/javascript;charset=utf-8," + encodeURIComponent(scriptContent);
+  }
+  function generateScriptContent(script) {
+    return script.textContent + generateSourceMapHint(script);
+  }
+  function generateSourceMapHint(script) {
+    var owner = script.ownerDocument;
+    owner.__importedScripts = owner.__importedScripts || 0;
+    var moniker = script.ownerDocument.baseURI;
+    var num = owner.__importedScripts ? "-" + owner.__importedScripts : "";
+    owner.__importedScripts++;
+    return "\n//# sourceURL=" + moniker + num + ".js\n";
+  }
+  function cloneStyle(style) {
+    var clone = style.ownerDocument.createElement("style");
+    clone.textContent = style.textContent;
+    path.resolveUrlsInStyle(clone);
+    return clone;
+  }
+  scope.parser = importParser;
+  scope.IMPORT_SELECTOR = IMPORT_SELECTOR;
+});
+
+HTMLImports.addModule(function(scope) {
+  var flags = scope.flags;
+  var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE;
+  var IMPORT_SELECTOR = scope.IMPORT_SELECTOR;
+  var rootDocument = scope.rootDocument;
+  var Loader = scope.Loader;
+  var Observer = scope.Observer;
+  var parser = scope.parser;
+  var importer = {
+    documents: {},
+    documentPreloadSelectors: IMPORT_SELECTOR,
+    importsPreloadSelectors: [ IMPORT_SELECTOR ].join(","),
+    loadNode: function(node) {
+      importLoader.addNode(node);
+    },
+    loadSubtree: function(parent) {
+      var nodes = this.marshalNodes(parent);
+      importLoader.addNodes(nodes);
+    },
+    marshalNodes: function(parent) {
+      return parent.querySelectorAll(this.loadSelectorsForNode(parent));
+    },
+    loadSelectorsForNode: function(node) {
+      var doc = node.ownerDocument || node;
+      return doc === rootDocument ? this.documentPreloadSelectors : this.importsPreloadSelectors;
+    },
+    loaded: function(url, elt, resource, err, redirectedUrl) {
+      flags.load && console.log("loaded", url, elt);
+      elt.__resource = resource;
+      elt.__error = err;
+      if (isImportLink(elt)) {
+        var doc = this.documents[url];
+        if (doc === undefined) {
+          doc = err ? null : makeDocument(resource, redirectedUrl || url);
+          if (doc) {
+            doc.__importLink = elt;
+            this.bootDocument(doc);
+          }
+          this.documents[url] = doc;
+        }
+        elt.import = doc;
+      }
+      parser.parseNext();
+    },
+    bootDocument: function(doc) {
+      this.loadSubtree(doc);
+      this.observer.observe(doc);
+      parser.parseNext();
+    },
+    loadedAll: function() {
+      parser.parseNext();
+    }
+  };
+  var importLoader = new Loader(importer.loaded.bind(importer), importer.loadedAll.bind(importer));
+  importer.observer = new Observer();
+  function isImportLink(elt) {
+    return isLinkRel(elt, IMPORT_LINK_TYPE);
+  }
+  function isLinkRel(elt, rel) {
+    return elt.localName === "link" && elt.getAttribute("rel") === rel;
+  }
+  function makeDocument(resource, url) {
+    var doc = document.implementation.createHTMLDocument(IMPORT_LINK_TYPE);
+    doc._URL = url;
+    var base = doc.createElement("base");
+    base.setAttribute("href", url);
+    if (!doc.baseURI) {
+      doc.baseURI = url;
+    }
+    var meta = doc.createElement("meta");
+    meta.setAttribute("charset", "utf-8");
+    doc.head.appendChild(meta);
+    doc.head.appendChild(base);
+    doc.body.innerHTML = resource;
+    if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) {
+      HTMLTemplateElement.bootstrap(doc);
+    }
+    return doc;
+  }
+  if (!document.baseURI) {
+    var baseURIDescriptor = {
+      get: function() {
+        var base = document.querySelector("base");
+        return base ? base.href : window.location.href;
+      },
+      configurable: true
+    };
+    Object.defineProperty(document, "baseURI", baseURIDescriptor);
+    Object.defineProperty(rootDocument, "baseURI", baseURIDescriptor);
+  }
+  scope.importer = importer;
+  scope.importLoader = importLoader;
+});
+
+HTMLImports.addModule(function(scope) {
+  var parser = scope.parser;
+  var importer = scope.importer;
+  var dynamic = {
+    added: function(nodes) {
+      var owner, parsed;
+      for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
+        if (!owner) {
+          owner = n.ownerDocument;
+          parsed = parser.isParsed(owner);
+        }
+        loading = this.shouldLoadNode(n);
+        if (loading) {
+          importer.loadNode(n);
+        }
+        if (this.shouldParseNode(n) && parsed) {
+          parser.parseDynamic(n, loading);
+        }
+      }
+    },
+    shouldLoadNode: function(node) {
+      return node.nodeType === 1 && matches.call(node, importer.loadSelectorsForNode(node));
+    },
+    shouldParseNode: function(node) {
+      return node.nodeType === 1 && matches.call(node, parser.parseSelectorsForNode(node));
+    }
+  };
+  importer.observer.addCallback = dynamic.added.bind(dynamic);
+  var matches = HTMLElement.prototype.matches || HTMLElement.prototype.matchesSelector || HTMLElement.prototype.webkitMatchesSelector || HTMLElement.prototype.mozMatchesSelector || HTMLElement.prototype.msMatchesSelector;
+});
+
+(function(scope) {
+  initializeModules = scope.initializeModules;
+  if (scope.useNative) {
+    return;
+  }
+  if (typeof window.CustomEvent !== "function") {
+    window.CustomEvent = function(inType, dictionary) {
+      var e = document.createEvent("HTMLEvents");
+      e.initEvent(inType, dictionary.bubbles === false ? false : true, dictionary.cancelable === false ? false : true, dictionary.detail);
+      return e;
+    };
+  }
+  initializeModules();
+  var rootDocument = scope.rootDocument;
+  function bootstrap() {
+    HTMLImports.importer.bootDocument(rootDocument);
+  }
+  if (document.readyState === "complete" || document.readyState === "interactive" && !window.attachEvent) {
+    bootstrap();
+  } else {
+    document.addEventListener("DOMContentLoaded", bootstrap);
+  }
+})(HTMLImports);
+
+window.CustomElements = window.CustomElements || {
+  flags: {}
+};
+
+(function(scope) {
+  var flags = scope.flags;
+  var modules = [];
+  var addModule = function(module) {
+    modules.push(module);
+  };
+  var initializeModules = function() {
+    modules.forEach(function(module) {
+      module(scope);
+    });
+  };
+  scope.addModule = addModule;
+  scope.initializeModules = initializeModules;
+  scope.hasNative = Boolean(document.registerElement);
+  scope.useNative = !flags.register && scope.hasNative && !window.ShadowDOMPolyfill && (!window.HTMLImports || HTMLImports.useNative);
+})(CustomElements);
+
+CustomElements.addModule(function(scope) {
+  var IMPORT_LINK_TYPE = window.HTMLImports ? HTMLImports.IMPORT_LINK_TYPE : "none";
+  function forSubtree(node, cb) {
+    findAllElements(node, function(e) {
+      if (cb(e)) {
+        return true;
+      }
+      forRoots(e, cb);
+    });
+    forRoots(node, cb);
+  }
+  function findAllElements(node, find, data) {
+    var e = node.firstElementChild;
+    if (!e) {
+      e = node.firstChild;
+      while (e && e.nodeType !== Node.ELEMENT_NODE) {
+        e = e.nextSibling;
+      }
+    }
+    while (e) {
+      if (find(e, data) !== true) {
+        findAllElements(e, find, data);
+      }
+      e = e.nextElementSibling;
+    }
+    return null;
+  }
+  function forRoots(node, cb) {
+    var root = node.shadowRoot;
+    while (root) {
+      forSubtree(root, cb);
+      root = root.olderShadowRoot;
+    }
+  }
+  var processingDocuments;
+  function forDocumentTree(doc, cb) {
+    processingDocuments = [];
+    _forDocumentTree(doc, cb);
+    processingDocuments = null;
+  }
+  function _forDocumentTree(doc, cb) {
+    doc = wrap(doc);
+    if (processingDocuments.indexOf(doc) >= 0) {
+      return;
+    }
+    processingDocuments.push(doc);
+    var imports = doc.querySelectorAll("link[rel=" + IMPORT_LINK_TYPE + "]");
+    for (var i = 0, l = imports.length, n; i < l && (n = imports[i]); i++) {
+      if (n.import) {
+        _forDocumentTree(n.import, cb);
+      }
+    }
+    cb(doc);
+  }
+  scope.forDocumentTree = forDocumentTree;
+  scope.forSubtree = forSubtree;
+});
+
+CustomElements.addModule(function(scope) {
+  var flags = scope.flags;
+  var forSubtree = scope.forSubtree;
+  var forDocumentTree = scope.forDocumentTree;
+  function addedNode(node) {
+    return added(node) || addedSubtree(node);
+  }
+  function added(node) {
+    if (scope.upgrade(node)) {
+      return true;
+    }
+    attached(node);
+  }
+  function addedSubtree(node) {
+    forSubtree(node, function(e) {
+      if (added(e)) {
+        return true;
+      }
+    });
+  }
+  function attachedNode(node) {
+    attached(node);
+    if (inDocument(node)) {
+      forSubtree(node, function(e) {
+        attached(e);
+      });
+    }
+  }
+  var hasPolyfillMutations = !window.MutationObserver || window.MutationObserver === window.JsMutationObserver;
+  scope.hasPolyfillMutations = hasPolyfillMutations;
+  var isPendingMutations = false;
+  var pendingMutations = [];
+  function deferMutation(fn) {
+    pendingMutations.push(fn);
+    if (!isPendingMutations) {
+      isPendingMutations = true;
+      setTimeout(takeMutations);
+    }
+  }
+  function takeMutations() {
+    isPendingMutations = false;
+    var $p = pendingMutations;
+    for (var i = 0, l = $p.length, p; i < l && (p = $p[i]); i++) {
+      p();
+    }
+    pendingMutations = [];
+  }
+  function attached(element) {
+    if (hasPolyfillMutations) {
+      deferMutation(function() {
+        _attached(element);
+      });
+    } else {
+      _attached(element);
+    }
+  }
+  function _attached(element) {
+    if (element.__upgraded__ && (element.attachedCallback || element.detachedCallback)) {
+      if (!element.__attached && inDocument(element)) {
+        element.__attached = true;
+        if (element.attachedCallback) {
+          element.attachedCallback();
+        }
+      }
+    }
+  }
+  function detachedNode(node) {
+    detached(node);
+    forSubtree(node, function(e) {
+      detached(e);
+    });
+  }
+  function detached(element) {
+    if (hasPolyfillMutations) {
+      deferMutation(function() {
+        _detached(element);
+      });
+    } else {
+      _detached(element);
+    }
+  }
+  function _detached(element) {
+    if (element.__upgraded__ && (element.attachedCallback || element.detachedCallback)) {
+      if (element.__attached && !inDocument(element)) {
+        element.__attached = false;
+        if (element.detachedCallback) {
+          element.detachedCallback();
+        }
+      }
+    }
+  }
+  function inDocument(element) {
+    var p = element;
+    var doc = wrap(document);
+    while (p) {
+      if (p == doc) {
+        return true;
+      }
+      p = p.parentNode || p.host;
+    }
+  }
+  function watchShadow(node) {
+    if (node.shadowRoot && !node.shadowRoot.__watched) {
+      flags.dom && console.log("watching shadow-root for: ", node.localName);
+      var root = node.shadowRoot;
+      while (root) {
+        observe(root);
+        root = root.olderShadowRoot;
+      }
+    }
+  }
+  function handler(mutations) {
+    if (flags.dom) {
+      var mx = mutations[0];
+      if (mx && mx.type === "childList" && mx.addedNodes) {
+        if (mx.addedNodes) {
+          var d = mx.addedNodes[0];
+          while (d && d !== document && !d.host) {
+            d = d.parentNode;
+          }
+          var u = d && (d.URL || d._URL || d.host && d.host.localName) || "";
+          u = u.split("/?").shift().split("/").pop();
+        }
+      }
+      console.group("mutations (%d) [%s]", mutations.length, u || "");
+    }
+    mutations.forEach(function(mx) {
+      if (mx.type === "childList") {
+        forEach(mx.addedNodes, function(n) {
+          if (!n.localName) {
+            return;
+          }
+          addedNode(n);
+        });
+        forEach(mx.removedNodes, function(n) {
+          if (!n.localName) {
+            return;
+          }
+          detachedNode(n);
+        });
+      }
+    });
+    flags.dom && console.groupEnd();
+  }
+  function takeRecords(node) {
+    node = wrap(node);
+    if (!node) {
+      node = wrap(document);
+    }
+    while (node.parentNode) {
+      node = node.parentNode;
+    }
+    var observer = node.__observer;
+    if (observer) {
+      handler(observer.takeRecords());
+      takeMutations();
+    }
+  }
+  var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
+  function observe(inRoot) {
+    if (inRoot.__observer) {
+      return;
+    }
+    var observer = new MutationObserver(handler);
+    observer.observe(inRoot, {
+      childList: true,
+      subtree: true
+    });
+    inRoot.__observer = observer;
+  }
+  function upgradeDocument(doc) {
+    doc = wrap(doc);
+    flags.dom && console.group("upgradeDocument: ", doc.baseURI.split("/").pop());
+    addedNode(doc);
+    observe(doc);
+    flags.dom && console.groupEnd();
+  }
+  function upgradeDocumentTree(doc) {
+    forDocumentTree(doc, upgradeDocument);
+  }
+  var originalCreateShadowRoot = Element.prototype.createShadowRoot;
+  Element.prototype.createShadowRoot = function() {
+    var root = originalCreateShadowRoot.call(this);
+    CustomElements.watchShadow(this);
+    return root;
+  };
+  scope.watchShadow = watchShadow;
+  scope.upgradeDocumentTree = upgradeDocumentTree;
+  scope.upgradeSubtree = addedSubtree;
+  scope.upgradeAll = addedNode;
+  scope.attachedNode = attachedNode;
+  scope.takeRecords = takeRecords;
+});
+
+CustomElements.addModule(function(scope) {
+  var flags = scope.flags;
+  function upgrade(node) {
+    if (!node.__upgraded__ && node.nodeType === Node.ELEMENT_NODE) {
+      var is = node.getAttribute("is");
+      var definition = scope.getRegisteredDefinition(is || node.localName);
+      if (definition) {
+        if (is && definition.tag == node.localName) {
+          return upgradeWithDefinition(node, definition);
+        } else if (!is && !definition.extends) {
+          return upgradeWithDefinition(node, definition);
+        }
+      }
+    }
+  }
+  function upgradeWithDefinition(element, definition) {
+    flags.upgrade && console.group("upgrade:", element.localName);
+    if (definition.is) {
+      element.setAttribute("is", definition.is);
+    }
+    implementPrototype(element, definition);
+    element.__upgraded__ = true;
+    created(element);
+    scope.attachedNode(element);
+    scope.upgradeSubtree(element);
+    flags.upgrade && console.groupEnd();
+    return element;
+  }
+  function implementPrototype(element, definition) {
+    if (Object.__proto__) {
+      element.__proto__ = definition.prototype;
+    } else {
+      customMixin(element, definition.prototype, definition.native);
+      element.__proto__ = definition.prototype;
+    }
+  }
+  function customMixin(inTarget, inSrc, inNative) {
+    var used = {};
+    var p = inSrc;
+    while (p !== inNative && p !== HTMLElement.prototype) {
+      var keys = Object.getOwnPropertyNames(p);
+      for (var i = 0, k; k = keys[i]; i++) {
+        if (!used[k]) {
+          Object.defineProperty(inTarget, k, Object.getOwnPropertyDescriptor(p, k));
+          used[k] = 1;
+        }
+      }
+      p = Object.getPrototypeOf(p);
+    }
+  }
+  function created(element) {
+    if (element.createdCallback) {
+      element.createdCallback();
+    }
+  }
+  scope.upgrade = upgrade;
+  scope.upgradeWithDefinition = upgradeWithDefinition;
+  scope.implementPrototype = implementPrototype;
+});
+
+CustomElements.addModule(function(scope) {
+  var upgradeDocumentTree = scope.upgradeDocumentTree;
+  var upgrade = scope.upgrade;
+  var upgradeWithDefinition = scope.upgradeWithDefinition;
+  var implementPrototype = scope.implementPrototype;
+  var useNative = scope.useNative;
+  function register(name, options) {
+    var definition = options || {};
+    if (!name) {
+      throw new Error("document.registerElement: first argument `name` must not be empty");
+    }
+    if (name.indexOf("-") < 0) {
+      throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '" + String(name) + "'.");
+    }
+    if (isReservedTag(name)) {
+      throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '" + String(name) + "'. The type name is invalid.");
+    }
+    if (getRegisteredDefinition(name)) {
+      throw new Error("DuplicateDefinitionError: a type with name '" + String(name) + "' is already registered");
+    }
+    if (!definition.prototype) {
+      definition.prototype = Object.create(HTMLElement.prototype);
+    }
+    definition.__name = name.toLowerCase();
+    definition.lifecycle = definition.lifecycle || {};
+    definition.ancestry = ancestry(definition.extends);
+    resolveTagName(definition);
+    resolvePrototypeChain(definition);
+    overrideAttributeApi(definition.prototype);
+    registerDefinition(definition.__name, definition);
+    definition.ctor = generateConstructor(definition);
+    definition.ctor.prototype = definition.prototype;
+    definition.prototype.constructor = definition.ctor;
+    if (scope.ready) {
+      upgradeDocumentTree(document);
+    }
+    return definition.ctor;
+  }
+  function overrideAttributeApi(prototype) {
+    if (prototype.setAttribute._polyfilled) {
+      return;
+    }
+    var setAttribute = prototype.setAttribute;
+    prototype.setAttribute = function(name, value) {
+      changeAttribute.call(this, name, value, setAttribute);
+    };
+    var removeAttribute = prototype.removeAttribute;
+    prototype.removeAttribute = function(name) {
+      changeAttribute.call(this, name, null, removeAttribute);
+    };
+    prototype.setAttribute._polyfilled = true;
+  }
+  function changeAttribute(name, value, operation) {
+    name = name.toLowerCase();
+    var oldValue = this.getAttribute(name);
+    operation.apply(this, arguments);
+    var newValue = this.getAttribute(name);
+    if (this.attributeChangedCallback && newValue !== oldValue) {
+      this.attributeChangedCallback(name, oldValue, newValue);
+    }
+  }
+  function isReservedTag(name) {
+    for (var i = 0; i < reservedTagList.length; i++) {
+      if (name === reservedTagList[i]) {
+        return true;
+      }
+    }
+  }
+  var reservedTagList = [ "annotation-xml", "color-profile", "font-face", "font-face-src", "font-face-uri", "font-face-format", "font-face-name", "missing-glyph" ];
+  function ancestry(extnds) {
+    var extendee = getRegisteredDefinition(extnds);
+    if (extendee) {
+      return ancestry(extendee.extends).concat([ extendee ]);
+    }
+    return [];
+  }
+  function resolveTagName(definition) {
+    var baseTag = definition.extends;
+    for (var i = 0, a; a = definition.ancestry[i]; i++) {
+      baseTag = a.is && a.tag;
+    }
+    definition.tag = baseTag || definition.__name;
+    if (baseTag) {
+      definition.is = definition.__name;
+    }
+  }
+  function resolvePrototypeChain(definition) {
+    if (!Object.__proto__) {
+      var nativePrototype = HTMLElement.prototype;
+      if (definition.is) {
+        var inst = document.createElement(definition.tag);
+        var expectedPrototype = Object.getPrototypeOf(inst);
+        if (expectedPrototype === definition.prototype) {
+          nativePrototype = expectedPrototype;
+        }
+      }
+      var proto = definition.prototype, ancestor;
+      while (proto && proto !== nativePrototype) {
+        ancestor = Object.getPrototypeOf(proto);
+        proto.__proto__ = ancestor;
+        proto = ancestor;
+      }
+      definition.native = nativePrototype;
+    }
+  }
+  function instantiate(definition) {
+    return upgradeWithDefinition(domCreateElement(definition.tag), definition);
+  }
+  var registry = {};
+  function getRegisteredDefinition(name) {
+    if (name) {
+      return registry[name.toLowerCase()];
+    }
+  }
+  function registerDefinition(name, definition) {
+    registry[name] = definition;
+  }
+  function generateConstructor(definition) {
+    return function() {
+      return instantiate(definition);
+    };
+  }
+  var HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
+  function createElementNS(namespace, tag, typeExtension) {
+    if (namespace === HTML_NAMESPACE) {
+      return createElement(tag, typeExtension);
+    } else {
+      return domCreateElementNS(namespace, tag);
+    }
+  }
+  function createElement(tag, typeExtension) {
+    var definition = getRegisteredDefinition(typeExtension || tag);
+    if (definition) {
+      if (tag == definition.tag && typeExtension == definition.is) {
+        return new definition.ctor();
+      }
+      if (!typeExtension && !definition.is) {
+        return new definition.ctor();
+      }
+    }
+    var element;
+    if (typeExtension) {
+      element = createElement(tag);
+      element.setAttribute("is", typeExtension);
+      return element;
+    }
+    element = domCreateElement(tag);
+    if (tag.indexOf("-") >= 0) {
+      implementPrototype(element, HTMLElement);
+    }
+    return element;
+  }
+  function cloneNode(deep) {
+    var n = domCloneNode.call(this, deep);
+    upgrade(n);
+    return n;
+  }
+  var domCreateElement = document.createElement.bind(document);
+  var domCreateElementNS = document.createElementNS.bind(document);
+  var domCloneNode = Node.prototype.cloneNode;
+  var isInstance;
+  if (!Object.__proto__ && !useNative) {
+    isInstance = function(obj, ctor) {
+      var p = obj;
+      while (p) {
+        if (p === ctor.prototype) {
+          return true;
+        }
+        p = p.__proto__;
+      }
+      return false;
+    };
+  } else {
+    isInstance = function(obj, base) {
+      return obj instanceof base;
+    };
+  }
+  document.registerElement = register;
+  document.createElement = createElement;
+  document.createElementNS = createElementNS;
+  Node.prototype.cloneNode = cloneNode;
+  scope.registry = registry;
+  scope.instanceof = isInstance;
+  scope.reservedTagList = reservedTagList;
+  scope.getRegisteredDefinition = getRegisteredDefinition;
+  document.register = document.registerElement;
+});
+
+(function(scope) {
+  var useNative = scope.useNative;
+  var initializeModules = scope.initializeModules;
+  if (useNative) {
+    var nop = function() {};
+    scope.watchShadow = nop;
+    scope.upgrade = nop;
+    scope.upgradeAll = nop;
+    scope.upgradeDocumentTree = nop;
+    scope.upgradeSubtree = nop;
+    scope.takeRecords = nop;
+    scope.instanceof = function(obj, base) {
+      return obj instanceof base;
+    };
+  } else {
+    initializeModules();
+  }
+  var upgradeDocumentTree = scope.upgradeDocumentTree;
+  if (!window.wrap) {
+    if (window.ShadowDOMPolyfill) {
+      window.wrap = ShadowDOMPolyfill.wrapIfNeeded;
+      window.unwrap = ShadowDOMPolyfill.unwrapIfNeeded;
+    } else {
+      window.wrap = window.unwrap = function(node) {
+        return node;
+      };
+    }
+  }
+  function bootstrap() {
+    upgradeDocumentTree(wrap(document));
+    if (window.HTMLImports) {
+      HTMLImports.__importsParsingHook = function(elt) {
+        upgradeDocumentTree(wrap(elt.import));
+      };
+    }
+    CustomElements.ready = true;
+    setTimeout(function() {
+      CustomElements.readyTime = Date.now();
+      if (window.HTMLImports) {
+        CustomElements.elapsed = CustomElements.readyTime - HTMLImports.readyTime;
+      }
+      document.dispatchEvent(new CustomEvent("WebComponentsReady", {
+        bubbles: true
+      }));
+    });
+  }
+  if (typeof window.CustomEvent !== "function") {
+    window.CustomEvent = function(inType, params) {
+      params = params || {};
+      var e = document.createEvent("CustomEvent");
+      e.initCustomEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable), params.detail);
+      return e;
+    };
+    window.CustomEvent.prototype = window.Event.prototype;
+  }
+  if (document.readyState === "complete" || scope.flags.eager) {
+    bootstrap();
+  } else if (document.readyState === "interactive" && !window.attachEvent && (!window.HTMLImports || window.HTMLImports.ready)) {
+    bootstrap();
+  } else {
+    var loadEvent = window.HTMLImports && !HTMLImports.ready ? "HTMLImportsLoaded" : "DOMContentLoaded";
+    window.addEventListener(loadEvent, bootstrap);
+  }
+})(window.CustomElements);
+
+(function(scope) {
+  if (!Function.prototype.bind) {
+    Function.prototype.bind = function(scope) {
+      var self = this;
+      var args = Array.prototype.slice.call(arguments, 1);
+      return function() {
+        var args2 = args.slice();
+        args2.push.apply(args2, arguments);
+        return self.apply(scope, args2);
+      };
+    };
+  }
+})(window.WebComponents);
+
+(function(scope) {
+  "use strict";
+  if (!window.performance) {
+    var start = Date.now();
+    window.performance = {
+      now: function() {
+        return Date.now() - start;
+      }
+    };
+  }
+  if (!window.requestAnimationFrame) {
+    window.requestAnimationFrame = function() {
+      var nativeRaf = window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
+      return nativeRaf ? function(callback) {
+        return nativeRaf(function() {
+          callback(performance.now());
+        });
+      } : function(callback) {
+        return window.setTimeout(callback, 1e3 / 60);
+      };
+    }();
+  }
+  if (!window.cancelAnimationFrame) {
+    window.cancelAnimationFrame = function() {
+      return window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || function(id) {
+        clearTimeout(id);
+      };
+    }();
+  }
+  var elementDeclarations = [];
+  var polymerStub = function(name, dictionary) {
+    if (typeof name !== "string" && arguments.length === 1) {
+      Array.prototype.push.call(arguments, document._currentScript);
+    }
+    elementDeclarations.push(arguments);
+  };
+  window.Polymer = polymerStub;
+  scope.consumeDeclarations = function(callback) {
+    scope.consumeDeclarations = function() {
+      throw "Possible attempt to load Polymer twice";
+    };
+    if (callback) {
+      callback(elementDeclarations);
+    }
+    elementDeclarations = null;
+  };
+  function installPolymerWarning() {
+    if (window.Polymer === polymerStub) {
+      window.Polymer = function() {
+        throw new Error("You tried to use polymer without loading it first. To " + 'load polymer, <link rel="import" href="' + 'components/polymer/polymer.html">');
+      };
+    }
+  }
+  if (HTMLImports.useNative) {
+    installPolymerWarning();
+  } else {
+    addEventListener("DOMContentLoaded", installPolymerWarning);
+  }
+})(window.WebComponents);
+
+(function(scope) {
+  var style = document.createElement("style");
+  style.textContent = "" + "body {" + "transition: opacity ease-in 0.2s;" + " } \n" + "body[unresolved] {" + "opacity: 0; display: block; overflow: hidden; position: relative;" + " } \n";
+  var head = document.querySelector("head");
+  head.insertBefore(style, head.firstChild);
+})(window.WebComponents);
+
+(function(scope) {
+  window.Platform = scope;
+})(window.WebComponents);
diff --git a/web_components/lib/webcomponents.min.js b/web_components/lib/webcomponents.min.js
new file mode 100644
index 0000000..9118695
--- /dev/null
+++ b/web_components/lib/webcomponents.min.js
@@ -0,0 +1,14 @@
+/**
+ * @license
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+ * Code distributed by Google as part of the polymer project is also
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+ */
+// @version 0.5.1
+window.WebComponents=window.WebComponents||{},function(e){var t=e.flags||{},n="webcomponents.js",r=document.querySelector('script[src*="'+n+'"]');if(!t.noOpts){if(location.search.slice(1).split("&").forEach(function(e){e=e.split("="),e[0]&&(t[e[0]]=e[1]||!0)}),r)for(var o,i=0;o=r.attributes[i];i++)"src"!==o.name&&(t[o.name]=o.value||!0);if(t.log){var a=t.log.split(",");t.log={},a.forEach(function(e){t.log[e]=!0})}else t.log={}}t.shadow=t.shadow||t.shadowdom||t.polyfill,t.shadow="native"===t.shadow?!1:t.shadow||!HTMLElement.prototype.createShadowRoot,t.register&&(window.CustomElements=window.CustomElements||{flags:{}},window.CustomElements.flags.register=t.register),e.flags=t}(WebComponents),WebComponents.flags.shadow&&("undefined"==typeof WeakMap&&!function(){var e=Object.defineProperty,t=Date.now()%1e9,n=function(){this.name="__st"+(1e9*Math.random()>>>0)+(t++ +"__")};n.prototype={set:function(t,n){var r=t[this.name];return r&&r[0]===t?r[1]=n:e(t,this.name,{value:[t,n],writable:!0}),this},get:function(e){var t;return(t=e[this.name])&&t[0]===e?t[1]:void 0},"delete":function(e){var t=e[this.name];return t&&t[0]===e?(t[0]=t[1]=void 0,!0):!1},has:function(e){var t=e[this.name];return t?t[0]===e:!1}},window.WeakMap=n}(),window.ShadowDOMPolyfill={},function(e){"use strict";function t(){if("undefined"!=typeof chrome&&chrome.app&&chrome.app.runtime)return!1;if(navigator.getDeviceStorage)return!1;try{var e=new Function("return true;");return e()}catch(t){return!1}}function n(e){if(!e)throw new Error("Assertion failed")}function r(e,t){for(var n=W(t),r=0;r<n.length;r++){var o=n[r];k(e,o,F(t,o))}return e}function o(e,t){for(var n=W(t),r=0;r<n.length;r++){var o=n[r];switch(o){case"arguments":case"caller":case"length":case"name":case"prototype":case"toString":continue}k(e,o,F(t,o))}return e}function i(e,t){for(var n=0;n<t.length;n++)if(t[n]in e)return t[n]}function a(e,t,n){U.value=n,k(e,t,U)}function s(e){var t=e.__proto__||Object.getPrototypeOf(e),n=R.get(t);if(n)return n;var r=s(t),o=E(r);return g(t,o,e),o}function c(e,t){w(e,t,!0)}function l(e,t){w(t,e,!1)}function u(e){return/^on[a-z]+$/.test(e)}function d(e){return/^\w[a-zA-Z_0-9]*$/.test(e)}function p(e){return A&&d(e)?new Function("return this.__impl4cf1e782hg__."+e):function(){return this.__impl4cf1e782hg__[e]}}function f(e){return A&&d(e)?new Function("v","this.__impl4cf1e782hg__."+e+" = v"):function(t){this.__impl4cf1e782hg__[e]=t}}function h(e){return A&&d(e)?new Function("return this.__impl4cf1e782hg__."+e+".apply(this.__impl4cf1e782hg__, arguments)"):function(){return this.__impl4cf1e782hg__[e].apply(this.__impl4cf1e782hg__,arguments)}}function m(e,t){try{return Object.getOwnPropertyDescriptor(e,t)}catch(n){return q}}function w(t,n,r){for(var o=W(t),i=0;i<o.length;i++){var a=o[i];if("polymerBlackList_"!==a&&!(a in n||t.polymerBlackList_&&t.polymerBlackList_[a])){B&&t.__lookupGetter__(a);var s,c,l=m(t,a);if(r&&"function"==typeof l.value)n[a]=h(a);else{var d=u(a);s=d?e.getEventHandlerGetter(a):p(a),(l.writable||l.set||V)&&(c=d?e.getEventHandlerSetter(a):f(a)),k(n,a,{get:s,set:c,configurable:l.configurable,enumerable:l.enumerable})}}}}function v(e,t,n){var r=e.prototype;g(r,t,n),o(t,e)}function g(e,t,r){var o=t.prototype;n(void 0===R.get(e)),R.set(e,t),P.set(o,e),c(e,o),r&&l(o,r),a(o,"constructor",t),t.prototype=o}function b(e,t){return R.get(t.prototype)===e}function y(e){var t=Object.getPrototypeOf(e),n=s(t),r=E(n);return g(t,r,e),r}function E(e){function t(t){e.call(this,t)}var n=Object.create(e.prototype);return n.constructor=t,t.prototype=n,t}function S(e){return e&&e.__impl4cf1e782hg__}function T(e){return!S(e)}function M(e){return null===e?null:(n(T(e)),e.__wrapper8e3dd93a60__||(e.__wrapper8e3dd93a60__=new(s(e))(e)))}function _(e){return null===e?null:(n(S(e)),e.__impl4cf1e782hg__)}function O(e){return e.__impl4cf1e782hg__}function L(e,t){t.__impl4cf1e782hg__=e,e.__wrapper8e3dd93a60__=t}function N(e){return e&&S(e)?_(e):e}function C(e){return e&&!S(e)?M(e):e}function D(e,t){null!==t&&(n(T(e)),n(void 0===t||S(t)),e.__wrapper8e3dd93a60__=t)}function j(e,t,n){G.get=n,k(e.prototype,t,G)}function H(e,t){j(e,t,function(){return M(this.__impl4cf1e782hg__[t])})}function x(e,t){e.forEach(function(e){t.forEach(function(t){e.prototype[t]=function(){var e=C(this);return e[t].apply(e,arguments)}})})}var R=new WeakMap,P=new WeakMap,I=Object.create(null),A=t(),k=Object.defineProperty,W=Object.getOwnPropertyNames,F=Object.getOwnPropertyDescriptor,U={value:void 0,configurable:!0,enumerable:!1,writable:!0};W(window);var B=/Firefox/.test(navigator.userAgent),q={get:function(){},set:function(){},configurable:!0,enumerable:!0},V=function(){var e=Object.getOwnPropertyDescriptor(Node.prototype,"nodeType");return e&&!e.get&&!e.set}(),G={get:void 0,configurable:!0,enumerable:!0};e.assert=n,e.constructorTable=R,e.defineGetter=j,e.defineWrapGetter=H,e.forwardMethodsToWrapper=x,e.isWrapper=S,e.isWrapperFor=b,e.mixin=r,e.nativePrototypeTable=P,e.oneOf=i,e.registerObject=y,e.registerWrapper=v,e.rewrap=D,e.setWrapper=L,e.unsafeUnwrap=O,e.unwrap=_,e.unwrapIfNeeded=N,e.wrap=M,e.wrapIfNeeded=C,e.wrappers=I}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t,n){return{index:e,removed:t,addedCount:n}}function n(){}var r=0,o=1,i=2,a=3;n.prototype={calcEditDistances:function(e,t,n,r,o,i){for(var a=i-o+1,s=n-t+1,c=new Array(a),l=0;a>l;l++)c[l]=new Array(s),c[l][0]=l;for(var u=0;s>u;u++)c[0][u]=u;for(var l=1;a>l;l++)for(var u=1;s>u;u++)if(this.equals(e[t+u-1],r[o+l-1]))c[l][u]=c[l-1][u-1];else{var d=c[l-1][u]+1,p=c[l][u-1]+1;c[l][u]=p>d?d:p}return c},spliceOperationsFromEditDistances:function(e){for(var t=e.length-1,n=e[0].length-1,s=e[t][n],c=[];t>0||n>0;)if(0!=t)if(0!=n){var l,u=e[t-1][n-1],d=e[t-1][n],p=e[t][n-1];l=p>d?u>d?d:u:u>p?p:u,l==u?(u==s?c.push(r):(c.push(o),s=u),t--,n--):l==d?(c.push(a),t--,s=d):(c.push(i),n--,s=p)}else c.push(a),t--;else c.push(i),n--;return c.reverse(),c},calcSplices:function(e,n,s,c,l,u){var d=0,p=0,f=Math.min(s-n,u-l);if(0==n&&0==l&&(d=this.sharedPrefix(e,c,f)),s==e.length&&u==c.length&&(p=this.sharedSuffix(e,c,f-d)),n+=d,l+=d,s-=p,u-=p,s-n==0&&u-l==0)return[];if(n==s){for(var h=t(n,[],0);u>l;)h.removed.push(c[l++]);return[h]}if(l==u)return[t(n,[],s-n)];for(var m=this.spliceOperationsFromEditDistances(this.calcEditDistances(e,n,s,c,l,u)),h=void 0,w=[],v=n,g=l,b=0;b<m.length;b++)switch(m[b]){case r:h&&(w.push(h),h=void 0),v++,g++;break;case o:h||(h=t(v,[],0)),h.addedCount++,v++,h.removed.push(c[g]),g++;break;case i:h||(h=t(v,[],0)),h.addedCount++,v++;break;case a:h||(h=t(v,[],0)),h.removed.push(c[g]),g++}return h&&w.push(h),w},sharedPrefix:function(e,t,n){for(var r=0;n>r;r++)if(!this.equals(e[r],t[r]))return r;return n},sharedSuffix:function(e,t,n){for(var r=e.length,o=t.length,i=0;n>i&&this.equals(e[--r],t[--o]);)i++;return i},calculateSplices:function(e,t){return this.calcSplices(e,0,e.length,t,0,t.length)},equals:function(e,t){return e===t}},e.ArraySplice=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(){a=!1;var e=i.slice(0);i=[];for(var t=0;t<e.length;t++)e[t]()}function n(e){i.push(e),a||(a=!0,r(t,0))}var r,o=window.MutationObserver,i=[],a=!1;if(o){var s=1,c=new o(t),l=document.createTextNode(s);c.observe(l,{characterData:!0}),r=function(){s=(s+1)%2,l.data=s}}else r=window.setTimeout;e.setEndOfMicrotask=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){e.scheduled_||(e.scheduled_=!0,h.push(e),m||(u(n),m=!0))}function n(){for(m=!1;h.length;){var e=h;h=[],e.sort(function(e,t){return e.uid_-t.uid_});for(var t=0;t<e.length;t++){var n=e[t];n.scheduled_=!1;var r=n.takeRecords();i(n),r.length&&n.callback_(r,n)}}}function r(e,t){this.type=e,this.target=t,this.addedNodes=new p.NodeList,this.removedNodes=new p.NodeList,this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function o(e,t){for(;e;e=e.parentNode){var n=f.get(e);if(n)for(var r=0;r<n.length;r++){var o=n[r];o.options.subtree&&o.addTransientObserver(t)}}}function i(e){for(var t=0;t<e.nodes_.length;t++){var n=e.nodes_[t],r=f.get(n);if(!r)return;for(var o=0;o<r.length;o++){var i=r[o];i.observer===e&&i.removeTransientObservers()}}}function a(e,n,o){for(var i=Object.create(null),a=Object.create(null),s=e;s;s=s.parentNode){var c=f.get(s);if(c)for(var l=0;l<c.length;l++){var u=c[l],d=u.options;if((s===e||d.subtree)&&!("attributes"===n&&!d.attributes||"attributes"===n&&d.attributeFilter&&(null!==o.namespace||-1===d.attributeFilter.indexOf(o.name))||"characterData"===n&&!d.characterData||"childList"===n&&!d.childList)){var p=u.observer;i[p.uid_]=p,("attributes"===n&&d.attributeOldValue||"characterData"===n&&d.characterDataOldValue)&&(a[p.uid_]=o.oldValue)}}}for(var h in i){var p=i[h],m=new r(n,e);"name"in o&&"namespace"in o&&(m.attributeName=o.name,m.attributeNamespace=o.namespace),o.addedNodes&&(m.addedNodes=o.addedNodes),o.removedNodes&&(m.removedNodes=o.removedNodes),o.previousSibling&&(m.previousSibling=o.previousSibling),o.nextSibling&&(m.nextSibling=o.nextSibling),void 0!==a[h]&&(m.oldValue=a[h]),t(p),p.records_.push(m)}}function s(e){if(this.childList=!!e.childList,this.subtree=!!e.subtree,this.attributes="attributes"in e||!("attributeOldValue"in e||"attributeFilter"in e)?!!e.attributes:!0,this.characterData="characterDataOldValue"in e&&!("characterData"in e)?!0:!!e.characterData,!this.attributes&&(e.attributeOldValue||"attributeFilter"in e)||!this.characterData&&e.characterDataOldValue)throw new TypeError;if(this.characterData=!!e.characterData,this.attributeOldValue=!!e.attributeOldValue,this.characterDataOldValue=!!e.characterDataOldValue,"attributeFilter"in e){if(null==e.attributeFilter||"object"!=typeof e.attributeFilter)throw new TypeError;this.attributeFilter=w.call(e.attributeFilter)}else this.attributeFilter=null}function c(e){this.callback_=e,this.nodes_=[],this.records_=[],this.uid_=++v,this.scheduled_=!1}function l(e,t,n){this.observer=e,this.target=t,this.options=n,this.transientObservedNodes=[]}var u=e.setEndOfMicrotask,d=e.wrapIfNeeded,p=e.wrappers,f=new WeakMap,h=[],m=!1,w=Array.prototype.slice,v=0;c.prototype={constructor:c,observe:function(e,t){e=d(e);var n,r=new s(t),o=f.get(e);o||f.set(e,o=[]);for(var i=0;i<o.length;i++)o[i].observer===this&&(n=o[i],n.removeTransientObservers(),n.options=r);n||(n=new l(this,e,r),o.push(n),this.nodes_.push(e))},disconnect:function(){this.nodes_.forEach(function(e){for(var t=f.get(e),n=0;n<t.length;n++){var r=t[n];if(r.observer===this){t.splice(n,1);break}}},this),this.records_=[]},takeRecords:function(){var e=this.records_;return this.records_=[],e}},l.prototype={addTransientObserver:function(e){if(e!==this.target){t(this.observer),this.transientObservedNodes.push(e);var n=f.get(e);n||f.set(e,n=[]),n.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[];for(var t=0;t<e.length;t++)for(var n=e[t],r=f.get(n),o=0;o<r.length;o++)if(r[o]===this){r.splice(o,1);break}}},e.enqueueMutation=a,e.registerTransientObservers=o,e.wrappers.MutationObserver=c,e.wrappers.MutationRecord=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t){this.root=e,this.parent=t}function n(e,t){if(e.treeScope_!==t){e.treeScope_=t;for(var r=e.shadowRoot;r;r=r.olderShadowRoot)r.treeScope_.parent=t;for(var o=e.firstChild;o;o=o.nextSibling)n(o,t)}}function r(n){if(n instanceof e.wrappers.Window,n.treeScope_)return n.treeScope_;var o,i=n.parentNode;return o=i?r(i):new t(n,null),n.treeScope_=o}t.prototype={get renderer(){return this.root instanceof e.wrappers.ShadowRoot?e.getRendererForHost(this.root.host):null},contains:function(e){for(;e;e=e.parent)if(e===this)return!0;return!1}},e.TreeScope=t,e.getTreeScope=r,e.setTreeScope=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return e instanceof G.ShadowRoot}function n(e){return k(e).root}function r(e,r){var s=[],c=e;for(s.push(c);c;){var l=a(c);if(l&&l.length>0){for(var u=0;u<l.length;u++){var p=l[u];if(i(p)){var f=n(p),h=f.olderShadowRoot;h&&s.push(h)}s.push(p)}c=l[l.length-1]}else if(t(c)){if(d(e,c)&&o(r))break;c=c.host,s.push(c)}else c=c.parentNode,c&&s.push(c)}return s}function o(e){if(!e)return!1;switch(e.type){case"abort":case"error":case"select":case"change":case"load":case"reset":case"resize":case"scroll":case"selectstart":return!0}return!1}function i(e){return e instanceof HTMLShadowElement}function a(t){return e.getDestinationInsertionPoints(t)}function s(e,t){if(0===e.length)return t;t instanceof G.Window&&(t=t.document);for(var n=k(t),r=e[0],o=k(r),i=l(n,o),a=0;a<e.length;a++){var s=e[a];if(k(s)===i)return s}return e[e.length-1]}function c(e){for(var t=[];e;e=e.parent)t.push(e);return t}function l(e,t){for(var n=c(e),r=c(t),o=null;n.length>0&&r.length>0;){var i=n.pop(),a=r.pop();if(i!==a)break;o=i}return o}function u(e,t,n){t instanceof G.Window&&(t=t.document);var o,i=k(t),a=k(n),s=r(n,e),o=l(i,a);o||(o=a.root);for(var c=o;c;c=c.parent)for(var u=0;u<s.length;u++){var d=s[u];if(k(d)===c)return d}return null}function d(e,t){return k(e)===k(t)}function p(e){if(!K.get(e)&&(K.set(e,!0),h(V(e),V(e.target)),I)){var t=I;throw I=null,t}}function f(e){switch(e.type){case"load":case"beforeunload":case"unload":return!0}return!1}function h(t,n){if(Y.get(t))throw new Error("InvalidStateError");Y.set(t,!0),e.renderAllPending();var o,i,a;if(f(t)&&!t.bubbles){var s=n;s instanceof G.Document&&(a=s.defaultView)&&(i=s,o=[])}if(!o)if(n instanceof G.Window)a=n,o=[];else if(o=r(n,t),!f(t)){var s=o[o.length-1];s instanceof G.Document&&(a=s.defaultView)}return nt.set(t,o),m(t,o,a,i)&&w(t,o,a,i)&&v(t,o,a,i),Z.set(t,rt),$.delete(t,null),Y.delete(t),t.defaultPrevented}function m(e,t,n,r){var o=ot;if(n&&!g(n,e,o,t,r))return!1;for(var i=t.length-1;i>0;i--)if(!g(t[i],e,o,t,r))return!1;return!0}function w(e,t,n,r){var o=it,i=t[0]||n;return g(i,e,o,t,r)}function v(e,t,n,r){for(var o=at,i=1;i<t.length;i++)if(!g(t[i],e,o,t,r))return;n&&t.length>0&&g(n,e,o,t,r)}function g(e,t,n,r,o){var i=z.get(e);if(!i)return!0;var a=o||s(r,e);if(a===e){if(n===ot)return!0;n===at&&(n=it)}else if(n===at&&!t.bubbles)return!0;if("relatedTarget"in t){var c=q(t),l=c.relatedTarget;if(l){if(l instanceof Object&&l.addEventListener){var d=V(l),p=u(t,e,d);if(p===a)return!0}else p=null;J.set(t,p)}}Z.set(t,n);var f=t.type,h=!1;X.set(t,a),$.set(t,e),i.depth++;for(var m=0,w=i.length;w>m;m++){var v=i[m];if(v.removed)h=!0;else if(!(v.type!==f||!v.capture&&n===ot||v.capture&&n===at))try{if("function"==typeof v.handler?v.handler.call(e,t):v.handler.handleEvent(t),et.get(t))return!1}catch(g){I||(I=g)}}if(i.depth--,h&&0===i.depth){var b=i.slice();i.length=0;for(var m=0;m<b.length;m++)b[m].removed||i.push(b[m])}return!Q.get(t)}function b(e,t,n){this.type=e,this.handler=t,this.capture=Boolean(n)}function y(e,t){if(!(e instanceof st))return V(M(st,"Event",e,t));var n=e;return gt||"beforeunload"!==n.type||this instanceof _?void U(n,this):new _(n)}function E(e){return e&&e.relatedTarget?Object.create(e,{relatedTarget:{value:q(e.relatedTarget)}}):e}function S(e,t,n){var r=window[e],o=function(t,n){return t instanceof r?void U(t,this):V(M(r,e,t,n))};if(o.prototype=Object.create(t.prototype),n&&W(o.prototype,n),r)try{F(r,o,new r("temp"))}catch(i){F(r,o,document.createEvent(e))}return o}function T(e,t){return function(){arguments[t]=q(arguments[t]);var n=q(this);n[e].apply(n,arguments)}}function M(e,t,n,r){if(wt)return new e(n,E(r));var o=q(document.createEvent(t)),i=mt[t],a=[n];return Object.keys(i).forEach(function(e){var t=null!=r&&e in r?r[e]:i[e];"relatedTarget"===e&&(t=q(t)),a.push(t)}),o["init"+t].apply(o,a),o}function _(e){y.call(this,e)}function O(e){return"function"==typeof e?!0:e&&e.handleEvent}function L(e){switch(e){case"DOMAttrModified":case"DOMAttributeNameChanged":case"DOMCharacterDataModified":case"DOMElementNameChanged":case"DOMNodeInserted":case"DOMNodeInsertedIntoDocument":case"DOMNodeRemoved":case"DOMNodeRemovedFromDocument":case"DOMSubtreeModified":return!0}return!1}function N(e){U(e,this)}function C(e){return e instanceof G.ShadowRoot&&(e=e.host),q(e)}function D(e,t){var n=z.get(e);if(n)for(var r=0;r<n.length;r++)if(!n[r].removed&&n[r].type===t)return!0;return!1}function j(e,t){for(var n=q(e);n;n=n.parentNode)if(D(V(n),t))return!0;return!1}function H(e){A(e,yt)}function x(t,n,o,i){e.renderAllPending();var a=V(Et.call(B(n),o,i));if(!a)return null;var c=r(a,null),l=c.lastIndexOf(t);return-1==l?null:(c=c.slice(0,l),s(c,t))}function R(e){return function(){var t=tt.get(this);return t&&t[e]&&t[e].value||null}}function P(e){var t=e.slice(2);return function(n){var r=tt.get(this);r||(r=Object.create(null),tt.set(this,r));var o=r[e];if(o&&this.removeEventListener(t,o.wrapped,!1),"function"==typeof n){var i=function(t){var r=n.call(this,t);r===!1?t.preventDefault():"onbeforeunload"===e&&"string"==typeof r&&(t.returnValue=r)};this.addEventListener(t,i,!1),r[e]={value:n,wrapped:i}}}}var I,A=e.forwardMethodsToWrapper,k=e.getTreeScope,W=e.mixin,F=e.registerWrapper,U=e.setWrapper,B=e.unsafeUnwrap,q=e.unwrap,V=e.wrap,G=e.wrappers,z=(new WeakMap,new WeakMap),K=new WeakMap,Y=new WeakMap,X=new WeakMap,$=new WeakMap,J=new WeakMap,Z=new WeakMap,Q=new WeakMap,et=new WeakMap,tt=new WeakMap,nt=new WeakMap,rt=0,ot=1,it=2,at=3;b.prototype={equals:function(e){return this.handler===e.handler&&this.type===e.type&&this.capture===e.capture},get removed(){return null===this.handler},remove:function(){this.handler=null}};var st=window.Event;st.prototype.polymerBlackList_={returnValue:!0,keyLocation:!0},y.prototype={get target(){return X.get(this)},get currentTarget(){return $.get(this)},get eventPhase(){return Z.get(this)},get path(){var e=nt.get(this);return e?e.slice():[]},stopPropagation:function(){Q.set(this,!0)},stopImmediatePropagation:function(){Q.set(this,!0),et.set(this,!0)}},F(st,y,document.createEvent("Event"));var ct=S("UIEvent",y),lt=S("CustomEvent",y),ut={get relatedTarget(){var e=J.get(this);return void 0!==e?e:V(q(this).relatedTarget)}},dt=W({initMouseEvent:T("initMouseEvent",14)},ut),pt=W({initFocusEvent:T("initFocusEvent",5)},ut),ft=S("MouseEvent",ct,dt),ht=S("FocusEvent",ct,pt),mt=Object.create(null),wt=function(){try{new window.FocusEvent("focus")}catch(e){return!1}return!0}();if(!wt){var vt=function(e,t,n){if(n){var r=mt[n];t=W(W({},r),t)}mt[e]=t};vt("Event",{bubbles:!1,cancelable:!1}),vt("CustomEvent",{detail:null},"Event"),vt("UIEvent",{view:null,detail:0},"Event"),vt("MouseEvent",{screenX:0,screenY:0,clientX:0,clientY:0,ctrlKey:!1,altKey:!1,shiftKey:!1,metaKey:!1,button:0,relatedTarget:null},"UIEvent"),vt("FocusEvent",{relatedTarget:null},"UIEvent")}var gt=window.BeforeUnloadEvent;_.prototype=Object.create(y.prototype),W(_.prototype,{get returnValue(){return B(this).returnValue},set returnValue(e){B(this).returnValue=e}}),gt&&F(gt,_);var bt=window.EventTarget,yt=["addEventListener","removeEventListener","dispatchEvent"];[Node,Window].forEach(function(e){var t=e.prototype;yt.forEach(function(e){Object.defineProperty(t,e+"_",{value:t[e]})})}),N.prototype={addEventListener:function(e,t,n){if(O(t)&&!L(e)){var r=new b(e,t,n),o=z.get(this);if(o){for(var i=0;i<o.length;i++)if(r.equals(o[i]))return}else o=[],o.depth=0,z.set(this,o);o.push(r);var a=C(this);a.addEventListener_(e,p,!0)}},removeEventListener:function(e,t,n){n=Boolean(n);var r=z.get(this);if(r){for(var o=0,i=!1,a=0;a<r.length;a++)r[a].type===e&&r[a].capture===n&&(o++,r[a].handler===t&&(i=!0,r[a].remove()));if(i&&1===o){var s=C(this);s.removeEventListener_(e,p,!0)}}},dispatchEvent:function(t){var n=q(t),r=n.type;K.set(n,!1),e.renderAllPending();var o;j(this,r)||(o=function(){},this.addEventListener(r,o,!0));try{return q(this).dispatchEvent_(n)}finally{o&&this.removeEventListener(r,o,!0)}}},bt&&F(bt,N);var Et=document.elementFromPoint;e.elementFromPoint=x,e.getEventHandlerGetter=R,e.getEventHandlerSetter=P,e.wrapEventTargetMethods=H,e.wrappers.BeforeUnloadEvent=_,e.wrappers.CustomEvent=lt,e.wrappers.Event=y,e.wrappers.EventTarget=N,e.wrappers.FocusEvent=ht,e.wrappers.MouseEvent=ft,e.wrappers.UIEvent=ct}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t){Object.defineProperty(e,t,m)}function n(e){l(e,this)}function r(){this.length=0,t(this,"length")}function o(e){for(var t=new r,o=0;o<e.length;o++)t[o]=new n(e[o]);return t.length=o,t}function i(e){a.call(this,e)}var a=e.wrappers.UIEvent,s=e.mixin,c=e.registerWrapper,l=e.setWrapper,u=e.unsafeUnwrap,d=e.wrap,p=window.TouchEvent;if(p){var f;try{f=document.createEvent("TouchEvent")}catch(h){return}var m={enumerable:!1};n.prototype={get target(){return d(u(this).target)}};var w={configurable:!0,enumerable:!0,get:null};["clientX","clientY","screenX","screenY","pageX","pageY","identifier","webkitRadiusX","webkitRadiusY","webkitRotationAngle","webkitForce"].forEach(function(e){w.get=function(){return u(this)[e]},Object.defineProperty(n.prototype,e,w)}),r.prototype={item:function(e){return this[e]}},i.prototype=Object.create(a.prototype),s(i.prototype,{get touches(){return o(u(this).touches)},get targetTouches(){return o(u(this).targetTouches)},get changedTouches(){return o(u(this).changedTouches)},initTouchEvent:function(){throw new Error("Not implemented")}}),c(p,i,f),e.wrappers.Touch=n,e.wrappers.TouchEvent=i,e.wrappers.TouchList=r}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e,t){Object.defineProperty(e,t,s)}function n(){this.length=0,t(this,"length")}function r(e){if(null==e)return e;for(var t=new n,r=0,o=e.length;o>r;r++)t[r]=a(e[r]);return t.length=o,t}function o(e,t){e.prototype[t]=function(){return r(i(this)[t].apply(i(this),arguments))}}var i=e.unsafeUnwrap,a=e.wrap,s={enumerable:!1};n.prototype={item:function(e){return this[e]}},t(n.prototype,"item"),e.wrappers.NodeList=n,e.addWrapNodeListMethod=o,e.wrapNodeList=r}(window.ShadowDOMPolyfill),function(e){"use strict";e.wrapHTMLCollection=e.wrapNodeList,e.wrappers.HTMLCollection=e.wrappers.NodeList}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){O(e instanceof S)}function n(e){var t=new M;return t[0]=e,t.length=1,t}function r(e,t,n){N(t,"childList",{removedNodes:n,previousSibling:e.previousSibling,nextSibling:e.nextSibling})}function o(e,t){N(e,"childList",{removedNodes:t})}function i(e,t,r,o){if(e instanceof DocumentFragment){var i=s(e);U=!0;for(var a=i.length-1;a>=0;a--)e.removeChild(i[a]),i[a].parentNode_=t;U=!1;for(var a=0;a<i.length;a++)i[a].previousSibling_=i[a-1]||r,i[a].nextSibling_=i[a+1]||o;return r&&(r.nextSibling_=i[0]),o&&(o.previousSibling_=i[i.length-1]),i}var i=n(e),c=e.parentNode;return c&&c.removeChild(e),e.parentNode_=t,e.previousSibling_=r,e.nextSibling_=o,r&&(r.nextSibling_=e),o&&(o.previousSibling_=e),i}function a(e){if(e instanceof DocumentFragment)return s(e);var t=n(e),o=e.parentNode;return o&&r(e,o,t),t}function s(e){for(var t=new M,n=0,r=e.firstChild;r;r=r.nextSibling)t[n++]=r;return t.length=n,o(e,t),t}function c(e){return e}function l(e,t){R(e,t),e.nodeIsInserted_()}function u(e,t){for(var n=C(t),r=0;r<e.length;r++)l(e[r],n)}function d(e){R(e,new _(e,null))}function p(e){for(var t=0;t<e.length;t++)d(e[t])}function f(e,t){var n=e.nodeType===S.DOCUMENT_NODE?e:e.ownerDocument;n!==t.ownerDocument&&n.adoptNode(t)}function h(t,n){if(n.length){var r=t.ownerDocument;if(r!==n[0].ownerDocument)for(var o=0;o<n.length;o++)e.adoptNodeNoRemove(n[o],r)}}function m(e,t){h(e,t);var n=t.length;if(1===n)return I(t[0]);for(var r=I(e.ownerDocument.createDocumentFragment()),o=0;n>o;o++)r.appendChild(I(t[o]));return r}function w(e){if(void 0!==e.firstChild_)for(var t=e.firstChild_;t;){var n=t;t=t.nextSibling_,n.parentNode_=n.previousSibling_=n.nextSibling_=void 0}e.firstChild_=e.lastChild_=void 0}function v(e){if(e.invalidateShadowRenderer()){for(var t=e.firstChild;t;){O(t.parentNode===e);var n=t.nextSibling,r=I(t),o=r.parentNode;o&&Y.call(o,r),t.previousSibling_=t.nextSibling_=t.parentNode_=null,t=n}e.firstChild_=e.lastChild_=null}else for(var n,i=I(e),a=i.firstChild;a;)n=a.nextSibling,Y.call(i,a),a=n}function g(e){var t=e.parentNode;return t&&t.invalidateShadowRenderer()}function b(e){for(var t,n=0;n<e.length;n++)t=e[n],t.parentNode.removeChild(t)}function y(e,t,n){var r;if(r=k(n?B.call(n,P(e),!1):q.call(P(e),!1)),t){for(var o=e.firstChild;o;o=o.nextSibling)r.appendChild(y(o,!0,n));if(e instanceof F.HTMLTemplateElement)for(var i=r.content,o=e.content.firstChild;o;o=o.nextSibling)i.appendChild(y(o,!0,n))}return r}function E(e,t){if(!t||C(e)!==C(t))return!1;for(var n=t;n;n=n.parentNode)if(n===e)return!0;return!1}function S(e){O(e instanceof V),T.call(this,e),this.parentNode_=void 0,this.firstChild_=void 0,this.lastChild_=void 0,this.nextSibling_=void 0,this.previousSibling_=void 0,this.treeScope_=void 0}var T=e.wrappers.EventTarget,M=e.wrappers.NodeList,_=e.TreeScope,O=e.assert,L=e.defineWrapGetter,N=e.enqueueMutation,C=e.getTreeScope,D=e.isWrapper,j=e.mixin,H=e.registerTransientObservers,x=e.registerWrapper,R=e.setTreeScope,P=e.unsafeUnwrap,I=e.unwrap,A=e.unwrapIfNeeded,k=e.wrap,W=e.wrapIfNeeded,F=e.wrappers,U=!1,B=document.importNode,q=window.Node.prototype.cloneNode,V=window.Node,G=window.DocumentFragment,z=(V.prototype.appendChild,V.prototype.compareDocumentPosition),K=V.prototype.insertBefore,Y=V.prototype.removeChild,X=V.prototype.replaceChild,$=/Trident/.test(navigator.userAgent),J=$?function(e,t){try{Y.call(e,t)}catch(n){if(!(e instanceof G))throw n}}:function(e,t){Y.call(e,t)};S.prototype=Object.create(T.prototype),j(S.prototype,{appendChild:function(e){return this.insertBefore(e,null)},insertBefore:function(e,n){t(e);var r;n?D(n)?r=I(n):(r=n,n=k(r)):(n=null,r=null),n&&O(n.parentNode===this);var o,s=n?n.previousSibling:this.lastChild,c=!this.invalidateShadowRenderer()&&!g(e);if(o=c?a(e):i(e,this,s,n),c)f(this,e),w(this),K.call(P(this),I(e),r);else{s||(this.firstChild_=o[0]),n||(this.lastChild_=o[o.length-1],void 0===this.firstChild_&&(this.firstChild_=this.firstChild));var l=r?r.parentNode:P(this);l?K.call(l,m(this,o),r):h(this,o)}return N(this,"childList",{addedNodes:o,nextSibling:n,previousSibling:s}),u(o,this),e},removeChild:function(e){if(t(e),e.parentNode!==this){for(var r=!1,o=(this.childNodes,this.firstChild);o;o=o.nextSibling)if(o===e){r=!0;break}if(!r)throw new Error("NotFoundError")}var i=I(e),a=e.nextSibling,s=e.previousSibling;if(this.invalidateShadowRenderer()){var c=this.firstChild,l=this.lastChild,u=i.parentNode;u&&J(u,i),c===e&&(this.firstChild_=a),l===e&&(this.lastChild_=s),s&&(s.nextSibling_=a),a&&(a.previousSibling_=s),e.previousSibling_=e.nextSibling_=e.parentNode_=void 0}else w(this),J(P(this),i);return U||N(this,"childList",{removedNodes:n(e),nextSibling:a,previousSibling:s}),H(this,e),e},replaceChild:function(e,r){t(e);var o;if(D(r)?o=I(r):(o=r,r=k(o)),r.parentNode!==this)throw new Error("NotFoundError");var s,c=r.nextSibling,l=r.previousSibling,p=!this.invalidateShadowRenderer()&&!g(e);return p?s=a(e):(c===e&&(c=e.nextSibling),s=i(e,this,l,c)),p?(f(this,e),w(this),X.call(P(this),I(e),o)):(this.firstChild===r&&(this.firstChild_=s[0]),this.lastChild===r&&(this.lastChild_=s[s.length-1]),r.previousSibling_=r.nextSibling_=r.parentNode_=void 0,o.parentNode&&X.call(o.parentNode,m(this,s),o)),N(this,"childList",{addedNodes:s,removedNodes:n(r),nextSibling:c,previousSibling:l}),d(r),u(s,this),r},nodeIsInserted_:function(){for(var e=this.firstChild;e;e=e.nextSibling)e.nodeIsInserted_()},hasChildNodes:function(){return null!==this.firstChild},get parentNode(){return void 0!==this.parentNode_?this.parentNode_:k(P(this).parentNode)},get firstChild(){return void 0!==this.firstChild_?this.firstChild_:k(P(this).firstChild)},get lastChild(){return void 0!==this.lastChild_?this.lastChild_:k(P(this).lastChild)},get nextSibling(){return void 0!==this.nextSibling_?this.nextSibling_:k(P(this).nextSibling)},get previousSibling(){return void 0!==this.previousSibling_?this.previousSibling_:k(P(this).previousSibling)},get parentElement(){for(var e=this.parentNode;e&&e.nodeType!==S.ELEMENT_NODE;)e=e.parentNode;return e},get textContent(){for(var e="",t=this.firstChild;t;t=t.nextSibling)t.nodeType!=S.COMMENT_NODE&&(e+=t.textContent);return e},set textContent(e){null==e&&(e="");var t=c(this.childNodes);if(this.invalidateShadowRenderer()){if(v(this),""!==e){var n=P(this).ownerDocument.createTextNode(e);this.appendChild(n)}}else w(this),P(this).textContent=e;var r=c(this.childNodes);N(this,"childList",{addedNodes:r,removedNodes:t}),p(t),u(r,this)},get childNodes(){for(var e=new M,t=0,n=this.firstChild;n;n=n.nextSibling)e[t++]=n;return e.length=t,e},cloneNode:function(e){return y(this,e)},contains:function(e){return E(this,W(e))},compareDocumentPosition:function(e){return z.call(P(this),A(e))},normalize:function(){for(var e,t,n=c(this.childNodes),r=[],o="",i=0;i<n.length;i++)t=n[i],t.nodeType===S.TEXT_NODE?e||t.data.length?e?(o+=t.data,r.push(t)):e=t:this.removeNode(t):(e&&r.length&&(e.data+=o,b(r)),r=[],o="",e=null,t.childNodes.length&&t.normalize());e&&r.length&&(e.data+=o,b(r))}}),L(S,"ownerDocument"),x(V,S,document.createDocumentFragment()),delete S.prototype.querySelector,delete S.prototype.querySelectorAll,S.prototype=j(Object.create(T.prototype),S.prototype),e.cloneNode=y,e.nodeWasAdded=l,e.nodeWasRemoved=d,e.nodesWereAdded=u,e.nodesWereRemoved=p,e.originalInsertBefore=K,e.originalRemoveChild=Y,e.snapshotNodeList=c,e.wrappers.Node=S}(window.ShadowDOMPolyfill),function(e){"use strict";function t(t,n,r,o){for(var i=null,a=null,s=0,c=t.length;c>s;s++)i=g(t[s]),!o&&(a=w(i).root)&&a instanceof e.wrappers.ShadowRoot||(r[n++]=i);return n}function n(e){return String(e).replace(/\/deep\//g," ")}function r(e,t){for(var n,o=e.firstElementChild;o;){if(o.matches(t))return o;if(n=r(o,t))return n;o=o.nextElementSibling}return null}function o(e,t){return e.matches(t)}function i(e,t,n){var r=e.localName;return r===t||r===n&&e.namespaceURI===C}function a(){return!0}function s(e,t,n){return e.localName===n}function c(e,t){return e.namespaceURI===t}function l(e,t,n){return e.namespaceURI===t&&e.localName===n}function u(e,t,n,r,o,i){for(var a=e.firstElementChild;a;)r(a,o,i)&&(n[t++]=a),t=u(a,t,n,r,o,i),a=a.nextElementSibling;return t}function d(n,r,o,i,a){var s,c=v(this),l=w(this).root;if(l instanceof e.wrappers.ShadowRoot)return u(this,r,o,n,i,null);if(c instanceof L)s=S.call(c,i);else{if(!(c instanceof N))return u(this,r,o,n,i,null);s=E.call(c,i)}return t(s,r,o,a)}function p(n,r,o,i,a){var s,c=v(this),l=w(this).root;if(l instanceof e.wrappers.ShadowRoot)return u(this,r,o,n,i,a);if(c instanceof L)s=M.call(c,i,a);else{if(!(c instanceof N))return u(this,r,o,n,i,a);s=T.call(c,i,a)}return t(s,r,o,!1)}function f(n,r,o,i,a){var s,c=v(this),l=w(this).root;if(l instanceof e.wrappers.ShadowRoot)return u(this,r,o,n,i,a);if(c instanceof L)s=O.call(c,i,a);else{if(!(c instanceof N))return u(this,r,o,n,i,a);s=_.call(c,i,a)}return t(s,r,o,!1)}var h=e.wrappers.HTMLCollection,m=e.wrappers.NodeList,w=e.getTreeScope,v=e.unsafeUnwrap,g=e.wrap,b=document.querySelector,y=document.documentElement.querySelector,E=document.querySelectorAll,S=document.documentElement.querySelectorAll,T=document.getElementsByTagName,M=document.documentElement.getElementsByTagName,_=document.getElementsByTagNameNS,O=document.documentElement.getElementsByTagNameNS,L=window.Element,N=window.HTMLDocument||window.Document,C="http://www.w3.org/1999/xhtml",D={querySelector:function(t){var o=n(t),i=o!==t;t=o;var a,s=v(this),c=w(this).root;if(c instanceof e.wrappers.ShadowRoot)return r(this,t);if(s instanceof L)a=g(y.call(s,t));else{if(!(s instanceof N))return r(this,t);a=g(b.call(s,t))}return a&&!i&&(c=w(a).root)&&c instanceof e.wrappers.ShadowRoot?r(this,t):a},querySelectorAll:function(e){var t=n(e),r=t!==e;e=t;var i=new m;return i.length=d.call(this,o,0,i,e,r),i}},j={getElementsByTagName:function(e){var t=new h,n="*"===e?a:i;return t.length=p.call(this,n,0,t,e,e.toLowerCase()),t},getElementsByClassName:function(e){return this.querySelectorAll("."+e)},getElementsByTagNameNS:function(e,t){var n=new h,r=null;return r="*"===e?"*"===t?a:s:"*"===t?c:l,n.length=f.call(this,r,0,n,e||null,t),n
+}};e.GetElementsByInterface=j,e.SelectorsInterface=D}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){for(;e&&e.nodeType!==Node.ELEMENT_NODE;)e=e.nextSibling;return e}function n(e){for(;e&&e.nodeType!==Node.ELEMENT_NODE;)e=e.previousSibling;return e}var r=e.wrappers.NodeList,o={get firstElementChild(){return t(this.firstChild)},get lastElementChild(){return n(this.lastChild)},get childElementCount(){for(var e=0,t=this.firstElementChild;t;t=t.nextElementSibling)e++;return e},get children(){for(var e=new r,t=0,n=this.firstElementChild;n;n=n.nextElementSibling)e[t++]=n;return e.length=t,e},remove:function(){var e=this.parentNode;e&&e.removeChild(this)}},i={get nextElementSibling(){return t(this.nextSibling)},get previousElementSibling(){return n(this.previousSibling)}};e.ChildNodeInterface=i,e.ParentNodeInterface=o}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r.call(this,e)}var n=e.ChildNodeInterface,r=e.wrappers.Node,o=e.enqueueMutation,i=e.mixin,a=e.registerWrapper,s=e.unsafeUnwrap,c=window.CharacterData;t.prototype=Object.create(r.prototype),i(t.prototype,{get textContent(){return this.data},set textContent(e){this.data=e},get data(){return s(this).data},set data(e){var t=s(this).data;o(this,"characterData",{oldValue:t}),s(this).data=e}}),i(t.prototype,n),a(c,t,document.createTextNode("")),e.wrappers.CharacterData=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return e>>>0}function n(e){r.call(this,e)}var r=e.wrappers.CharacterData,o=(e.enqueueMutation,e.mixin),i=e.registerWrapper,a=window.Text;n.prototype=Object.create(r.prototype),o(n.prototype,{splitText:function(e){e=t(e);var n=this.data;if(e>n.length)throw new Error("IndexSizeError");var r=n.slice(0,e),o=n.slice(e);this.data=r;var i=this.ownerDocument.createTextNode(o);return this.parentNode&&this.parentNode.insertBefore(i,this.nextSibling),i}}),i(a,n,document.createTextNode("")),e.wrappers.Text=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(t){e.invalidateRendererBasedOnAttribute(t,"class")}function n(e,t){r(e,this),this.ownerElement_=t}var r=e.setWrapper,o=e.unsafeUnwrap;n.prototype={constructor:n,get length(){return o(this).length},item:function(e){return o(this).item(e)},contains:function(e){return o(this).contains(e)},add:function(){o(this).add.apply(o(this),arguments),t(this.ownerElement_)},remove:function(){o(this).remove.apply(o(this),arguments),t(this.ownerElement_)},toggle:function(){var e=o(this).toggle.apply(o(this),arguments);return t(this.ownerElement_),e},toString:function(){return o(this).toString()}},e.wrappers.DOMTokenList=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(t,n){var r=t.parentNode;if(r&&r.shadowRoot){var o=e.getRendererForHost(r);o.dependsOnAttribute(n)&&o.invalidate()}}function n(e,t,n){u(e,"attributes",{name:t,namespace:null,oldValue:n})}function r(e){a.call(this,e)}var o=e.ChildNodeInterface,i=e.GetElementsByInterface,a=e.wrappers.Node,s=e.wrappers.DOMTokenList,c=e.ParentNodeInterface,l=e.SelectorsInterface,u=(e.addWrapNodeListMethod,e.enqueueMutation),d=e.mixin,p=(e.oneOf,e.registerWrapper),f=e.unsafeUnwrap,h=e.wrappers,m=window.Element,w=["matches","mozMatchesSelector","msMatchesSelector","webkitMatchesSelector"].filter(function(e){return m.prototype[e]}),v=w[0],g=m.prototype[v],b=new WeakMap;r.prototype=Object.create(a.prototype),d(r.prototype,{createShadowRoot:function(){var t=new h.ShadowRoot(this);f(this).polymerShadowRoot_=t;var n=e.getRendererForHost(this);return n.invalidate(),t},get shadowRoot(){return f(this).polymerShadowRoot_||null},setAttribute:function(e,r){var o=f(this).getAttribute(e);f(this).setAttribute(e,r),n(this,e,o),t(this,e)},removeAttribute:function(e){var r=f(this).getAttribute(e);f(this).removeAttribute(e),n(this,e,r),t(this,e)},matches:function(e){return g.call(f(this),e)},get classList(){var e=b.get(this);return e||b.set(this,e=new s(f(this).classList,this)),e},get className(){return f(this).className},set className(e){this.setAttribute("class",e)},get id(){return f(this).id},set id(e){this.setAttribute("id",e)}}),w.forEach(function(e){"matches"!==e&&(r.prototype[e]=function(e){return this.matches(e)})}),m.prototype.webkitCreateShadowRoot&&(r.prototype.webkitCreateShadowRoot=r.prototype.createShadowRoot),d(r.prototype,o),d(r.prototype,i),d(r.prototype,c),d(r.prototype,l),p(m,r,document.createElementNS(null,"x")),e.invalidateRendererBasedOnAttribute=t,e.matchesNames=w,e.wrappers.Element=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){switch(e){case"&":return"&amp;";case"<":return"&lt;";case">":return"&gt;";case'"':return"&quot;";case" ":return"&nbsp;"}}function n(e){return e.replace(O,t)}function r(e){return e.replace(L,t)}function o(e){for(var t={},n=0;n<e.length;n++)t[e[n]]=!0;return t}function i(e,t){switch(e.nodeType){case Node.ELEMENT_NODE:for(var o,i=e.tagName.toLowerCase(),s="<"+i,c=e.attributes,l=0;o=c[l];l++)s+=" "+o.name+'="'+n(o.value)+'"';return s+=">",N[i]?s:s+a(e)+"</"+i+">";case Node.TEXT_NODE:var u=e.data;return t&&C[t.localName]?u:r(u);case Node.COMMENT_NODE:return"<!--"+e.data+"-->";default:throw console.error(e),new Error("not implemented")}}function a(e){e instanceof _.HTMLTemplateElement&&(e=e.content);for(var t="",n=e.firstChild;n;n=n.nextSibling)t+=i(n,e);return t}function s(e,t,n){var r=n||"div";e.textContent="";var o=T(e.ownerDocument.createElement(r));o.innerHTML=t;for(var i;i=o.firstChild;)e.appendChild(M(i))}function c(e){h.call(this,e)}function l(e,t){var n=T(e.cloneNode(!1));n.innerHTML=t;for(var r,o=T(document.createDocumentFragment());r=n.firstChild;)o.appendChild(r);return M(o)}function u(t){return function(){return e.renderAllPending(),S(this)[t]}}function d(e){m(c,e,u(e))}function p(t){Object.defineProperty(c.prototype,t,{get:u(t),set:function(n){e.renderAllPending(),S(this)[t]=n},configurable:!0,enumerable:!0})}function f(t){Object.defineProperty(c.prototype,t,{value:function(){return e.renderAllPending(),S(this)[t].apply(S(this),arguments)},configurable:!0,enumerable:!0})}var h=e.wrappers.Element,m=e.defineGetter,w=e.enqueueMutation,v=e.mixin,g=e.nodesWereAdded,b=e.nodesWereRemoved,y=e.registerWrapper,E=e.snapshotNodeList,S=e.unsafeUnwrap,T=e.unwrap,M=e.wrap,_=e.wrappers,O=/[&\u00A0"]/g,L=/[&\u00A0<>]/g,N=o(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"]),C=o(["style","script","xmp","iframe","noembed","noframes","plaintext","noscript"]),D=/MSIE/.test(navigator.userAgent),j=window.HTMLElement,H=window.HTMLTemplateElement;c.prototype=Object.create(h.prototype),v(c.prototype,{get innerHTML(){return a(this)},set innerHTML(e){if(D&&C[this.localName])return void(this.textContent=e);var t=E(this.childNodes);this.invalidateShadowRenderer()?this instanceof _.HTMLTemplateElement?s(this.content,e):s(this,e,this.tagName):!H&&this instanceof _.HTMLTemplateElement?s(this.content,e):S(this).innerHTML=e;var n=E(this.childNodes);w(this,"childList",{addedNodes:n,removedNodes:t}),b(t),g(n,this)},get outerHTML(){return i(this,this.parentNode)},set outerHTML(e){var t=this.parentNode;if(t){t.invalidateShadowRenderer();var n=l(t,e);t.replaceChild(n,this)}},insertAdjacentHTML:function(e,t){var n,r;switch(String(e).toLowerCase()){case"beforebegin":n=this.parentNode,r=this;break;case"afterend":n=this.parentNode,r=this.nextSibling;break;case"afterbegin":n=this,r=this.firstChild;break;case"beforeend":n=this,r=null;break;default:return}var o=l(n,t);n.insertBefore(o,r)},get hidden(){return this.hasAttribute("hidden")},set hidden(e){e?this.setAttribute("hidden",""):this.removeAttribute("hidden")}}),["clientHeight","clientLeft","clientTop","clientWidth","offsetHeight","offsetLeft","offsetTop","offsetWidth","scrollHeight","scrollWidth"].forEach(d),["scrollLeft","scrollTop"].forEach(p),["getBoundingClientRect","getClientRects","scrollIntoView"].forEach(f),y(j,c,document.createElement("b")),e.wrappers.HTMLElement=c,e.getInnerHTML=a,e.setInnerHTML=s}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.unsafeUnwrap,a=e.wrap,s=window.HTMLCanvasElement;t.prototype=Object.create(n.prototype),r(t.prototype,{getContext:function(){var e=i(this).getContext.apply(i(this),arguments);return e&&a(e)}}),o(s,t,document.createElement("canvas")),e.wrappers.HTMLCanvasElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=window.HTMLContentElement;t.prototype=Object.create(n.prototype),r(t.prototype,{constructor:t,get select(){return this.getAttribute("select")},set select(e){this.setAttribute("select",e)},setAttribute:function(e,t){n.prototype.setAttribute.call(this,e,t),"select"===String(e).toLowerCase()&&this.invalidateShadowRenderer(!0)}}),i&&o(i,t),e.wrappers.HTMLContentElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.wrapHTMLCollection,a=e.unwrap,s=window.HTMLFormElement;t.prototype=Object.create(n.prototype),r(t.prototype,{get elements(){return i(a(this).elements)}}),o(s,t,document.createElement("form")),e.wrappers.HTMLFormElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r.call(this,e)}function n(e,t){if(!(this instanceof n))throw new TypeError("DOM object constructor cannot be called as a function.");var o=i(document.createElement("img"));r.call(this,o),a(o,this),void 0!==e&&(o.width=e),void 0!==t&&(o.height=t)}var r=e.wrappers.HTMLElement,o=e.registerWrapper,i=e.unwrap,a=e.rewrap,s=window.HTMLImageElement;t.prototype=Object.create(r.prototype),o(s,t,document.createElement("img")),n.prototype=t.prototype,e.wrappers.HTMLImageElement=t,e.wrappers.Image=n}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=(e.mixin,e.wrappers.NodeList,e.registerWrapper),o=window.HTMLShadowElement;t.prototype=Object.create(n.prototype),t.prototype.constructor=t,o&&r(o,t),e.wrappers.HTMLShadowElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){if(!e.defaultView)return e;var t=d.get(e);if(!t){for(t=e.implementation.createHTMLDocument("");t.lastChild;)t.removeChild(t.lastChild);d.set(e,t)}return t}function n(e){for(var n,r=t(e.ownerDocument),o=c(r.createDocumentFragment());n=e.firstChild;)o.appendChild(n);return o}function r(e){if(o.call(this,e),!p){var t=n(e);u.set(this,l(t))}}var o=e.wrappers.HTMLElement,i=e.mixin,a=e.registerWrapper,s=e.unsafeUnwrap,c=e.unwrap,l=e.wrap,u=new WeakMap,d=new WeakMap,p=window.HTMLTemplateElement;r.prototype=Object.create(o.prototype),i(r.prototype,{constructor:r,get content(){return p?l(s(this).content):u.get(this)}}),p&&a(p,r),e.wrappers.HTMLTemplateElement=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.registerWrapper,o=window.HTMLMediaElement;o&&(t.prototype=Object.create(n.prototype),r(o,t,document.createElement("audio")),e.wrappers.HTMLMediaElement=t)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r.call(this,e)}function n(e){if(!(this instanceof n))throw new TypeError("DOM object constructor cannot be called as a function.");var t=i(document.createElement("audio"));r.call(this,t),a(t,this),t.setAttribute("preload","auto"),void 0!==e&&t.setAttribute("src",e)}var r=e.wrappers.HTMLMediaElement,o=e.registerWrapper,i=e.unwrap,a=e.rewrap,s=window.HTMLAudioElement;s&&(t.prototype=Object.create(r.prototype),o(s,t,document.createElement("audio")),n.prototype=t.prototype,e.wrappers.HTMLAudioElement=t,e.wrappers.Audio=n)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){return e.replace(/\s+/g," ").trim()}function n(e){o.call(this,e)}function r(e,t,n,i){if(!(this instanceof r))throw new TypeError("DOM object constructor cannot be called as a function.");var a=c(document.createElement("option"));o.call(this,a),s(a,this),void 0!==e&&(a.text=e),void 0!==t&&a.setAttribute("value",t),n===!0&&a.setAttribute("selected",""),a.selected=i===!0}var o=e.wrappers.HTMLElement,i=e.mixin,a=e.registerWrapper,s=e.rewrap,c=e.unwrap,l=e.wrap,u=window.HTMLOptionElement;n.prototype=Object.create(o.prototype),i(n.prototype,{get text(){return t(this.textContent)},set text(e){this.textContent=t(String(e))},get form(){return l(c(this).form)}}),a(u,n,document.createElement("option")),r.prototype=n.prototype,e.wrappers.HTMLOptionElement=n,e.wrappers.Option=r}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.unwrap,a=e.wrap,s=window.HTMLSelectElement;t.prototype=Object.create(n.prototype),r(t.prototype,{add:function(e,t){"object"==typeof t&&(t=i(t)),i(this).add(i(e),t)},remove:function(e){return void 0===e?void n.prototype.remove.call(this):("object"==typeof e&&(e=i(e)),void i(this).remove(e))},get form(){return a(i(this).form)}}),o(s,t,document.createElement("select")),e.wrappers.HTMLSelectElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.unwrap,a=e.wrap,s=e.wrapHTMLCollection,c=window.HTMLTableElement;t.prototype=Object.create(n.prototype),r(t.prototype,{get caption(){return a(i(this).caption)},createCaption:function(){return a(i(this).createCaption())},get tHead(){return a(i(this).tHead)},createTHead:function(){return a(i(this).createTHead())},createTFoot:function(){return a(i(this).createTFoot())},get tFoot(){return a(i(this).tFoot)},get tBodies(){return s(i(this).tBodies)},createTBody:function(){return a(i(this).createTBody())},get rows(){return s(i(this).rows)},insertRow:function(e){return a(i(this).insertRow(e))}}),o(c,t,document.createElement("table")),e.wrappers.HTMLTableElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.wrapHTMLCollection,a=e.unwrap,s=e.wrap,c=window.HTMLTableSectionElement;t.prototype=Object.create(n.prototype),r(t.prototype,{constructor:t,get rows(){return i(a(this).rows)},insertRow:function(e){return s(a(this).insertRow(e))}}),o(c,t,document.createElement("thead")),e.wrappers.HTMLTableSectionElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.HTMLElement,r=e.mixin,o=e.registerWrapper,i=e.wrapHTMLCollection,a=e.unwrap,s=e.wrap,c=window.HTMLTableRowElement;t.prototype=Object.create(n.prototype),r(t.prototype,{get cells(){return i(a(this).cells)},insertCell:function(e){return s(a(this).insertCell(e))}}),o(c,t,document.createElement("tr")),e.wrappers.HTMLTableRowElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){switch(e.localName){case"content":return new n(e);case"shadow":return new o(e);case"template":return new i(e)}r.call(this,e)}var n=e.wrappers.HTMLContentElement,r=e.wrappers.HTMLElement,o=e.wrappers.HTMLShadowElement,i=e.wrappers.HTMLTemplateElement,a=(e.mixin,e.registerWrapper),s=window.HTMLUnknownElement;t.prototype=Object.create(r.prototype),a(s,t),e.wrappers.HTMLUnknownElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";var t=e.wrappers.Element,n=e.wrappers.HTMLElement,r=e.registerObject,o="http://www.w3.org/2000/svg",i=document.createElementNS(o,"title"),a=r(i),s=Object.getPrototypeOf(a.prototype).constructor;if(!("classList"in i)){var c=Object.getOwnPropertyDescriptor(t.prototype,"classList");Object.defineProperty(n.prototype,"classList",c),delete t.prototype.classList}e.wrappers.SVGElement=s}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){p.call(this,e)}var n=e.mixin,r=e.registerWrapper,o=e.unwrap,i=e.wrap,a=window.SVGUseElement,s="http://www.w3.org/2000/svg",c=i(document.createElementNS(s,"g")),l=document.createElementNS(s,"use"),u=c.constructor,d=Object.getPrototypeOf(u.prototype),p=d.constructor;t.prototype=Object.create(d),"instanceRoot"in l&&n(t.prototype,{get instanceRoot(){return i(o(this).instanceRoot)},get animatedInstanceRoot(){return i(o(this).animatedInstanceRoot)}}),r(a,t,l),e.wrappers.SVGUseElement=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.EventTarget,r=e.mixin,o=e.registerWrapper,i=e.unsafeUnwrap,a=e.wrap,s=window.SVGElementInstance;s&&(t.prototype=Object.create(n.prototype),r(t.prototype,{get correspondingElement(){return a(i(this).correspondingElement)},get correspondingUseElement(){return a(i(this).correspondingUseElement)},get parentNode(){return a(i(this).parentNode)},get childNodes(){throw new Error("Not implemented")},get firstChild(){return a(i(this).firstChild)},get lastChild(){return a(i(this).lastChild)},get previousSibling(){return a(i(this).previousSibling)},get nextSibling(){return a(i(this).nextSibling)}}),o(s,t),e.wrappers.SVGElementInstance=t)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){o(e,this)}var n=e.mixin,r=e.registerWrapper,o=e.setWrapper,i=e.unsafeUnwrap,a=e.unwrap,s=e.unwrapIfNeeded,c=e.wrap,l=window.CanvasRenderingContext2D;n(t.prototype,{get canvas(){return c(i(this).canvas)},drawImage:function(){arguments[0]=s(arguments[0]),i(this).drawImage.apply(i(this),arguments)},createPattern:function(){return arguments[0]=a(arguments[0]),i(this).createPattern.apply(i(this),arguments)}}),r(l,t,document.createElement("canvas").getContext("2d")),e.wrappers.CanvasRenderingContext2D=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){o(e,this)}var n=e.mixin,r=e.registerWrapper,o=e.setWrapper,i=e.unsafeUnwrap,a=e.unwrapIfNeeded,s=e.wrap,c=window.WebGLRenderingContext;if(c){n(t.prototype,{get canvas(){return s(i(this).canvas)},texImage2D:function(){arguments[5]=a(arguments[5]),i(this).texImage2D.apply(i(this),arguments)},texSubImage2D:function(){arguments[6]=a(arguments[6]),i(this).texSubImage2D.apply(i(this),arguments)}});var l=/WebKit/.test(navigator.userAgent)?{drawingBufferHeight:null,drawingBufferWidth:null}:{};r(c,t,l),e.wrappers.WebGLRenderingContext=t}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r(e,this)}var n=e.registerWrapper,r=e.setWrapper,o=e.unsafeUnwrap,i=e.unwrap,a=e.unwrapIfNeeded,s=e.wrap,c=window.Range;t.prototype={get startContainer(){return s(o(this).startContainer)},get endContainer(){return s(o(this).endContainer)},get commonAncestorContainer(){return s(o(this).commonAncestorContainer)},setStart:function(e,t){o(this).setStart(a(e),t)},setEnd:function(e,t){o(this).setEnd(a(e),t)},setStartBefore:function(e){o(this).setStartBefore(a(e))},setStartAfter:function(e){o(this).setStartAfter(a(e))},setEndBefore:function(e){o(this).setEndBefore(a(e))},setEndAfter:function(e){o(this).setEndAfter(a(e))},selectNode:function(e){o(this).selectNode(a(e))},selectNodeContents:function(e){o(this).selectNodeContents(a(e))},compareBoundaryPoints:function(e,t){return o(this).compareBoundaryPoints(e,i(t))},extractContents:function(){return s(o(this).extractContents())},cloneContents:function(){return s(o(this).cloneContents())},insertNode:function(e){o(this).insertNode(a(e))},surroundContents:function(e){o(this).surroundContents(a(e))},cloneRange:function(){return s(o(this).cloneRange())},isPointInRange:function(e,t){return o(this).isPointInRange(a(e),t)},comparePoint:function(e,t){return o(this).comparePoint(a(e),t)},intersectsNode:function(e){return o(this).intersectsNode(a(e))},toString:function(){return o(this).toString()}},c.prototype.createContextualFragment&&(t.prototype.createContextualFragment=function(e){return s(o(this).createContextualFragment(e))}),n(window.Range,t,document.createRange()),e.wrappers.Range=t}(window.ShadowDOMPolyfill),function(e){"use strict";var t=e.GetElementsByInterface,n=e.ParentNodeInterface,r=e.SelectorsInterface,o=e.mixin,i=e.registerObject,a=i(document.createDocumentFragment());o(a.prototype,n),o(a.prototype,r),o(a.prototype,t);var s=i(document.createComment(""));e.wrappers.Comment=s,e.wrappers.DocumentFragment=a}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t=d(u(e).ownerDocument.createDocumentFragment());n.call(this,t),c(t,this);var o=e.shadowRoot;f.set(this,o),this.treeScope_=new r(this,a(o||e)),p.set(this,e)}var n=e.wrappers.DocumentFragment,r=e.TreeScope,o=e.elementFromPoint,i=e.getInnerHTML,a=e.getTreeScope,s=e.mixin,c=e.rewrap,l=e.setInnerHTML,u=e.unsafeUnwrap,d=e.unwrap,p=new WeakMap,f=new WeakMap,h=/[ \t\n\r\f]/;t.prototype=Object.create(n.prototype),s(t.prototype,{constructor:t,get innerHTML(){return i(this)},set innerHTML(e){l(this,e),this.invalidateShadowRenderer()},get olderShadowRoot(){return f.get(this)||null},get host(){return p.get(this)||null},invalidateShadowRenderer:function(){return p.get(this).invalidateShadowRenderer()},elementFromPoint:function(e,t){return o(this,this.ownerDocument,e,t)},getElementById:function(e){return h.test(e)?null:this.querySelector('[id="'+e+'"]')}}),e.wrappers.ShadowRoot=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){e.previousSibling_=e.previousSibling,e.nextSibling_=e.nextSibling,e.parentNode_=e.parentNode}function n(n,o,i){var a=x(n),s=x(o),c=i?x(i):null;if(r(o),t(o),i)n.firstChild===i&&(n.firstChild_=i),i.previousSibling_=i.previousSibling;else{n.lastChild_=n.lastChild,n.lastChild===n.firstChild&&(n.firstChild_=n.firstChild);var l=R(a.lastChild);l&&(l.nextSibling_=l.nextSibling)}e.originalInsertBefore.call(a,s,c)}function r(n){var r=x(n),o=r.parentNode;if(o){var i=R(o);t(n),n.previousSibling&&(n.previousSibling.nextSibling_=n),n.nextSibling&&(n.nextSibling.previousSibling_=n),i.lastChild===n&&(i.lastChild_=n),i.firstChild===n&&(i.firstChild_=n),e.originalRemoveChild.call(o,r)}}function o(e){I.set(e,[])}function i(e){var t=I.get(e);return t||I.set(e,t=[]),t}function a(e){for(var t=[],n=0,r=e.firstChild;r;r=r.nextSibling)t[n++]=r;return t}function s(){for(var e=0;e<F.length;e++){var t=F[e],n=t.parentRenderer;n&&n.dirty||t.render()}F=[]}function c(){M=null,s()}function l(e){var t=k.get(e);return t||(t=new f(e),k.set(e,t)),t}function u(e){var t=D(e).root;return t instanceof C?t:null}function d(e){return l(e.host)}function p(e){this.skip=!1,this.node=e,this.childNodes=[]}function f(e){this.host=e,this.dirty=!1,this.invalidateAttributes(),this.associateNode(e)}function h(e){for(var t=[],n=e.firstChild;n;n=n.nextSibling)E(n)?t.push.apply(t,i(n)):t.push(n);return t}function m(e){if(e instanceof L)return e;if(e instanceof O)return null;for(var t=e.firstChild;t;t=t.nextSibling){var n=m(t);if(n)return n}return null}function w(e,t){i(t).push(e);var n=A.get(e);n?n.push(t):A.set(e,[t])}function v(e){return A.get(e)}function g(e){A.set(e,void 0)}function b(e,t){var n=t.getAttribute("select");if(!n)return!0;if(n=n.trim(),!n)return!0;if(!(e instanceof _))return!1;if(!B.test(n))return!1;try{return e.matches(n)}catch(r){return!1}}function y(e,t){var n=v(t);return n&&n[n.length-1]===e}function E(e){return e instanceof O||e instanceof L}function S(e){return e.shadowRoot}function T(e){for(var t=[],n=e.shadowRoot;n;n=n.olderShadowRoot)t.push(n);return t}var M,_=e.wrappers.Element,O=e.wrappers.HTMLContentElement,L=e.wrappers.HTMLShadowElement,N=e.wrappers.Node,C=e.wrappers.ShadowRoot,D=(e.assert,e.getTreeScope),j=(e.mixin,e.oneOf),H=e.unsafeUnwrap,x=e.unwrap,R=e.wrap,P=e.ArraySplice,I=new WeakMap,A=new WeakMap,k=new WeakMap,W=j(window,["requestAnimationFrame","mozRequestAnimationFrame","webkitRequestAnimationFrame","setTimeout"]),F=[],U=new P;U.equals=function(e,t){return x(e.node)===t},p.prototype={append:function(e){var t=new p(e);return this.childNodes.push(t),t},sync:function(e){if(!this.skip){for(var t=this.node,o=this.childNodes,i=a(x(t)),s=e||new WeakMap,c=U.calculateSplices(o,i),l=0,u=0,d=0,p=0;p<c.length;p++){for(var f=c[p];d<f.index;d++)u++,o[l++].sync(s);for(var h=f.removed.length,m=0;h>m;m++){var w=R(i[u++]);s.get(w)||r(w)}for(var v=f.addedCount,g=i[u]&&R(i[u]),m=0;v>m;m++){var b=o[l++],y=b.node;n(t,y,g),s.set(y,!0),b.sync(s)}d+=v}for(var p=d;p<o.length;p++)o[p].sync(s)}}},f.prototype={render:function(e){if(this.dirty){this.invalidateAttributes();var t=this.host;this.distribution(t);var n=e||new p(t);this.buildRenderTree(n,t);var r=!e;r&&n.sync(),this.dirty=!1}},get parentRenderer(){return D(this.host).renderer},invalidate:function(){if(!this.dirty){this.dirty=!0;var e=this.parentRenderer;if(e&&e.invalidate(),F.push(this),M)return;M=window[W](c,0)}},distribution:function(e){this.resetAllSubtrees(e),this.distributionResolution(e)},resetAll:function(e){E(e)?o(e):g(e),this.resetAllSubtrees(e)},resetAllSubtrees:function(e){for(var t=e.firstChild;t;t=t.nextSibling)this.resetAll(t);e.shadowRoot&&this.resetAll(e.shadowRoot),e.olderShadowRoot&&this.resetAll(e.olderShadowRoot)},distributionResolution:function(e){if(S(e)){for(var t=e,n=h(t),r=T(t),o=0;o<r.length;o++)this.poolDistribution(r[o],n);for(var o=r.length-1;o>=0;o--){var i=r[o],a=m(i);if(a){var s=i.olderShadowRoot;s&&(n=h(s));for(var c=0;c<n.length;c++)w(n[c],a)}this.distributionResolution(i)}}for(var l=e.firstChild;l;l=l.nextSibling)this.distributionResolution(l)},poolDistribution:function(e,t){if(!(e instanceof L))if(e instanceof O){var n=e;this.updateDependentAttributes(n.getAttribute("select"));for(var r=!1,o=0;o<t.length;o++){var e=t[o];e&&b(e,n)&&(w(e,n),t[o]=void 0,r=!0)}if(!r)for(var i=n.firstChild;i;i=i.nextSibling)w(i,n)}else for(var i=e.firstChild;i;i=i.nextSibling)this.poolDistribution(i,t)},buildRenderTree:function(e,t){for(var n=this.compose(t),r=0;r<n.length;r++){var o=n[r],i=e.append(o);this.buildRenderTree(i,o)}if(S(t)){var a=l(t);a.dirty=!1}},compose:function(e){for(var t=[],n=e.shadowRoot||e,r=n.firstChild;r;r=r.nextSibling)if(E(r)){this.associateNode(n);for(var o=i(r),a=0;a<o.length;a++){var s=o[a];y(r,s)&&t.push(s)}}else t.push(r);return t},invalidateAttributes:function(){this.attributes=Object.create(null)},updateDependentAttributes:function(e){if(e){var t=this.attributes;/\.\w+/.test(e)&&(t["class"]=!0),/#\w+/.test(e)&&(t.id=!0),e.replace(/\[\s*([^\s=\|~\]]+)/g,function(e,n){t[n]=!0})}},dependsOnAttribute:function(e){return this.attributes[e]},associateNode:function(e){H(e).polymerShadowRenderer_=this}};var B=/^(:not\()?[*.#[a-zA-Z_|]/;N.prototype.invalidateShadowRenderer=function(){var e=H(this).polymerShadowRenderer_;return e?(e.invalidate(),!0):!1},O.prototype.getDistributedNodes=L.prototype.getDistributedNodes=function(){return s(),i(this)},_.prototype.getDestinationInsertionPoints=function(){return s(),v(this)||[]},O.prototype.nodeIsInserted_=L.prototype.nodeIsInserted_=function(){this.invalidateShadowRenderer();var e,t=u(this);t&&(e=d(t)),H(this).polymerShadowRenderer_=e,e&&e.invalidate()},e.getRendererForHost=l,e.getShadowTrees=T,e.renderAllPending=s,e.getDestinationInsertionPoints=v,e.visual={insertBefore:n,remove:r}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(t){if(window[t]){r(!e.wrappers[t]);var c=function(e){n.call(this,e)};c.prototype=Object.create(n.prototype),o(c.prototype,{get form(){return s(a(this).form)}}),i(window[t],c,document.createElement(t.slice(4,-7))),e.wrappers[t]=c}}var n=e.wrappers.HTMLElement,r=e.assert,o=e.mixin,i=e.registerWrapper,a=e.unwrap,s=e.wrap,c=["HTMLButtonElement","HTMLFieldSetElement","HTMLInputElement","HTMLKeygenElement","HTMLLabelElement","HTMLLegendElement","HTMLObjectElement","HTMLOutputElement","HTMLTextAreaElement"];c.forEach(t)}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){r(e,this)}{var n=e.registerWrapper,r=e.setWrapper,o=e.unsafeUnwrap,i=e.unwrap,a=e.unwrapIfNeeded,s=e.wrap;window.Selection}t.prototype={get anchorNode(){return s(o(this).anchorNode)},get focusNode(){return s(o(this).focusNode)},addRange:function(e){o(this).addRange(i(e))},collapse:function(e,t){o(this).collapse(a(e),t)},containsNode:function(e,t){return o(this).containsNode(a(e),t)},extend:function(e,t){o(this).extend(a(e),t)},getRangeAt:function(e){return s(o(this).getRangeAt(e))},removeRange:function(e){o(this).removeRange(i(e))},selectAllChildren:function(e){o(this).selectAllChildren(a(e))},toString:function(){return o(this).toString()}},n(window.Selection,t,window.getSelection()),e.wrappers.Selection=t}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){u.call(this,e),this.treeScope_=new m(this,null)}function n(e){var n=document[e];t.prototype[e]=function(){return N(n.apply(O(this),arguments))}}function r(e,t){j.call(O(t),L(e)),o(e,t)}function o(e,t){e.shadowRoot&&t.adoptNode(e.shadowRoot),e instanceof h&&i(e,t);for(var n=e.firstChild;n;n=n.nextSibling)o(n,t)}function i(e,t){var n=e.olderShadowRoot;n&&t.adoptNode(n)}function a(e){_(e,this)}function s(e,t){var n=document.implementation[t];e.prototype[t]=function(){return N(n.apply(O(this),arguments))}}function c(e,t){var n=document.implementation[t];e.prototype[t]=function(){return n.apply(O(this),arguments)}}var l=e.GetElementsByInterface,u=e.wrappers.Node,d=e.ParentNodeInterface,p=e.wrappers.Selection,f=e.SelectorsInterface,h=e.wrappers.ShadowRoot,m=e.TreeScope,w=e.cloneNode,v=e.defineWrapGetter,g=e.elementFromPoint,b=e.forwardMethodsToWrapper,y=e.matchesNames,E=e.mixin,S=e.registerWrapper,T=e.renderAllPending,M=e.rewrap,_=e.setWrapper,O=e.unsafeUnwrap,L=e.unwrap,N=e.wrap,C=e.wrapEventTargetMethods,D=(e.wrapNodeList,new WeakMap);t.prototype=Object.create(u.prototype),v(t,"documentElement"),v(t,"body"),v(t,"head"),["createComment","createDocumentFragment","createElement","createElementNS","createEvent","createEventNS","createRange","createTextNode","getElementById"].forEach(n);var j=document.adoptNode,H=document.getSelection;if(E(t.prototype,{adoptNode:function(e){return e.parentNode&&e.parentNode.removeChild(e),r(e,this),e},elementFromPoint:function(e,t){return g(this,this,e,t)},importNode:function(e,t){return w(e,t,O(this))},getSelection:function(){return T(),new p(H.call(L(this)))},getElementsByName:function(e){return f.querySelectorAll.call(this,"[name="+JSON.stringify(String(e))+"]")}}),document.registerElement){var x=document.registerElement;t.prototype.registerElement=function(t,n){function r(e){return e?void _(e,this):i?document.createElement(i,t):document.createElement(t)}var o,i;if(void 0!==n&&(o=n.prototype,i=n.extends),o||(o=Object.create(HTMLElement.prototype)),e.nativePrototypeTable.get(o))throw new Error("NotSupportedError");for(var a,s=Object.getPrototypeOf(o),c=[];s&&!(a=e.nativePrototypeTable.get(s));)c.push(s),s=Object.getPrototypeOf(s);if(!a)throw new Error("NotSupportedError");for(var l=Object.create(a),u=c.length-1;u>=0;u--)l=Object.create(l);["createdCallback","attachedCallback","detachedCallback","attributeChangedCallback"].forEach(function(e){var t=o[e];t&&(l[e]=function(){N(this)instanceof r||M(this),t.apply(N(this),arguments)})});var d={prototype:l};i&&(d.extends=i),r.prototype=o,r.prototype.constructor=r,e.constructorTable.set(l,r),e.nativePrototypeTable.set(o,l);x.call(L(this),t,d);return r},b([window.HTMLDocument||window.Document],["registerElement"])}b([window.HTMLBodyElement,window.HTMLDocument||window.Document,window.HTMLHeadElement,window.HTMLHtmlElement],["appendChild","compareDocumentPosition","contains","getElementsByClassName","getElementsByTagName","getElementsByTagNameNS","insertBefore","querySelector","querySelectorAll","removeChild","replaceChild"].concat(y)),b([window.HTMLDocument||window.Document],["adoptNode","importNode","contains","createComment","createDocumentFragment","createElement","createElementNS","createEvent","createEventNS","createRange","createTextNode","elementFromPoint","getElementById","getElementsByName","getSelection"]),E(t.prototype,l),E(t.prototype,d),E(t.prototype,f),E(t.prototype,{get implementation(){var e=D.get(this);return e?e:(e=new a(L(this).implementation),D.set(this,e),e)},get defaultView(){return N(L(this).defaultView)}}),S(window.Document,t,document.implementation.createHTMLDocument("")),window.HTMLDocument&&S(window.HTMLDocument,t),C([window.HTMLBodyElement,window.HTMLDocument||window.Document,window.HTMLHeadElement]),s(a,"createDocumentType"),s(a,"createDocument"),s(a,"createHTMLDocument"),c(a,"hasFeature"),S(window.DOMImplementation,a),b([window.DOMImplementation],["createDocumentType","createDocument","createHTMLDocument","hasFeature"]),e.adoptNodeNoRemove=r,e.wrappers.DOMImplementation=a,e.wrappers.Document=t
+}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){n.call(this,e)}var n=e.wrappers.EventTarget,r=e.wrappers.Selection,o=e.mixin,i=e.registerWrapper,a=e.renderAllPending,s=e.unwrap,c=e.unwrapIfNeeded,l=e.wrap,u=window.Window,d=window.getComputedStyle,p=window.getDefaultComputedStyle,f=window.getSelection;t.prototype=Object.create(n.prototype),u.prototype.getComputedStyle=function(e,t){return l(this||window).getComputedStyle(c(e),t)},p&&(u.prototype.getDefaultComputedStyle=function(e,t){return l(this||window).getDefaultComputedStyle(c(e),t)}),u.prototype.getSelection=function(){return l(this||window).getSelection()},delete window.getComputedStyle,delete window.getDefaultComputedStyle,delete window.getSelection,["addEventListener","removeEventListener","dispatchEvent"].forEach(function(e){u.prototype[e]=function(){var t=l(this||window);return t[e].apply(t,arguments)},delete window[e]}),o(t.prototype,{getComputedStyle:function(e,t){return a(),d.call(s(this),c(e),t)},getSelection:function(){return a(),new r(f.call(s(this)))},get document(){return l(s(this).document)}}),p&&(t.prototype.getDefaultComputedStyle=function(e,t){return a(),p.call(s(this),c(e),t)}),i(u,t,window),e.wrappers.Window=t}(window.ShadowDOMPolyfill),function(e){"use strict";var t=e.unwrap,n=window.DataTransfer||window.Clipboard,r=n.prototype.setDragImage;r&&(n.prototype.setDragImage=function(e,n,o){r.call(this,t(e),n,o)})}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t;t=e instanceof i?e:new i(e&&o(e)),r(t,this)}var n=e.registerWrapper,r=e.setWrapper,o=e.unwrap,i=window.FormData;i&&(n(i,t,new i),e.wrappers.FormData=t)}(window.ShadowDOMPolyfill),function(e){"use strict";var t=e.unwrapIfNeeded,n=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.send=function(e){return n.call(this,t(e))}}(window.ShadowDOMPolyfill),function(e){"use strict";function t(e){var t=n[e],r=window[t];if(r){var o=document.createElement(e),i=o.constructor;window[t]=i}}var n=(e.isWrapperFor,{a:"HTMLAnchorElement",area:"HTMLAreaElement",audio:"HTMLAudioElement",base:"HTMLBaseElement",body:"HTMLBodyElement",br:"HTMLBRElement",button:"HTMLButtonElement",canvas:"HTMLCanvasElement",caption:"HTMLTableCaptionElement",col:"HTMLTableColElement",content:"HTMLContentElement",data:"HTMLDataElement",datalist:"HTMLDataListElement",del:"HTMLModElement",dir:"HTMLDirectoryElement",div:"HTMLDivElement",dl:"HTMLDListElement",embed:"HTMLEmbedElement",fieldset:"HTMLFieldSetElement",font:"HTMLFontElement",form:"HTMLFormElement",frame:"HTMLFrameElement",frameset:"HTMLFrameSetElement",h1:"HTMLHeadingElement",head:"HTMLHeadElement",hr:"HTMLHRElement",html:"HTMLHtmlElement",iframe:"HTMLIFrameElement",img:"HTMLImageElement",input:"HTMLInputElement",keygen:"HTMLKeygenElement",label:"HTMLLabelElement",legend:"HTMLLegendElement",li:"HTMLLIElement",link:"HTMLLinkElement",map:"HTMLMapElement",marquee:"HTMLMarqueeElement",menu:"HTMLMenuElement",menuitem:"HTMLMenuItemElement",meta:"HTMLMetaElement",meter:"HTMLMeterElement",object:"HTMLObjectElement",ol:"HTMLOListElement",optgroup:"HTMLOptGroupElement",option:"HTMLOptionElement",output:"HTMLOutputElement",p:"HTMLParagraphElement",param:"HTMLParamElement",pre:"HTMLPreElement",progress:"HTMLProgressElement",q:"HTMLQuoteElement",script:"HTMLScriptElement",select:"HTMLSelectElement",shadow:"HTMLShadowElement",source:"HTMLSourceElement",span:"HTMLSpanElement",style:"HTMLStyleElement",table:"HTMLTableElement",tbody:"HTMLTableSectionElement",template:"HTMLTemplateElement",textarea:"HTMLTextAreaElement",thead:"HTMLTableSectionElement",time:"HTMLTimeElement",title:"HTMLTitleElement",tr:"HTMLTableRowElement",track:"HTMLTrackElement",ul:"HTMLUListElement",video:"HTMLVideoElement"});Object.keys(n).forEach(t),Object.getOwnPropertyNames(e.wrappers).forEach(function(t){window[t]=e.wrappers[t]})}(window.ShadowDOMPolyfill),function(e){function t(e,t){var n="";return Array.prototype.forEach.call(e,function(e){n+=e.textContent+"\n\n"}),t||(n=n.replace(d,"")),n}function n(e){var t=document.createElement("style");return t.textContent=e,t}function r(e){var t=n(e);document.head.appendChild(t);var r=[];if(t.sheet)try{r=t.sheet.cssRules}catch(o){}else console.warn("sheet not found",t);return t.parentNode.removeChild(t),r}function o(){C.initialized=!0,document.body.appendChild(C);var e=C.contentDocument,t=e.createElement("base");t.href=document.baseURI,e.head.appendChild(t)}function i(e){C.initialized||o(),document.body.appendChild(C),e(C.contentDocument),document.body.removeChild(C)}function a(e,t){if(t){var o;if(e.match("@import")&&j){var a=n(e);i(function(e){e.head.appendChild(a.impl),o=Array.prototype.slice.call(a.sheet.cssRules,0),t(o)})}else o=r(e),t(o)}}function s(e){e&&l().appendChild(document.createTextNode(e))}function c(e,t){var r=n(e);r.setAttribute(t,""),r.setAttribute(x,""),document.head.appendChild(r)}function l(){return D||(D=document.createElement("style"),D.setAttribute(x,""),D[x]=!0),D}var u={strictStyling:!1,registry:{},shimStyling:function(e,n,r){var o=this.prepareRoot(e,n,r),i=this.isTypeExtension(r),a=this.makeScopeSelector(n,i),s=t(o,!0);s=this.scopeCssText(s,a),e&&(e.shimmedStyle=s),this.addCssToDocument(s,n)},shimStyle:function(e,t){return this.shimCssText(e.textContent,t)},shimCssText:function(e,t){return e=this.insertDirectives(e),this.scopeCssText(e,t)},makeScopeSelector:function(e,t){return e?t?"[is="+e+"]":e:""},isTypeExtension:function(e){return e&&e.indexOf("-")<0},prepareRoot:function(e,t,n){var r=this.registerRoot(e,t,n);return this.replaceTextInStyles(r.rootStyles,this.insertDirectives),this.removeStyles(e,r.rootStyles),this.strictStyling&&this.applyScopeToContent(e,t),r.scopeStyles},removeStyles:function(e,t){for(var n,r=0,o=t.length;o>r&&(n=t[r]);r++)n.parentNode.removeChild(n)},registerRoot:function(e,t,n){var r=this.registry[t]={root:e,name:t,extendsName:n},o=this.findStyles(e);r.rootStyles=o,r.scopeStyles=r.rootStyles;var i=this.registry[r.extendsName];return i&&(r.scopeStyles=i.scopeStyles.concat(r.scopeStyles)),r},findStyles:function(e){if(!e)return[];var t=e.querySelectorAll("style");return Array.prototype.filter.call(t,function(e){return!e.hasAttribute(R)})},applyScopeToContent:function(e,t){e&&(Array.prototype.forEach.call(e.querySelectorAll("*"),function(e){e.setAttribute(t,"")}),Array.prototype.forEach.call(e.querySelectorAll("template"),function(e){this.applyScopeToContent(e.content,t)},this))},insertDirectives:function(e){return e=this.insertPolyfillDirectivesInCssText(e),this.insertPolyfillRulesInCssText(e)},insertPolyfillDirectivesInCssText:function(e){return e=e.replace(p,function(e,t){return t.slice(0,-2)+"{"}),e.replace(f,function(e,t){return t+" {"})},insertPolyfillRulesInCssText:function(e){return e=e.replace(h,function(e,t){return t.slice(0,-1)}),e.replace(m,function(e,t,n,r){var o=e.replace(t,"").replace(n,"");return r+o})},scopeCssText:function(e,t){var n=this.extractUnscopedRulesFromCssText(e);if(e=this.insertPolyfillHostInCssText(e),e=this.convertColonHost(e),e=this.convertColonHostContext(e),e=this.convertShadowDOMSelectors(e),t){var e,r=this;a(e,function(n){e=r.scopeRules(n,t)})}return e=e+"\n"+n,e.trim()},extractUnscopedRulesFromCssText:function(e){for(var t,n="";t=w.exec(e);)n+=t[1].slice(0,-1)+"\n\n";for(;t=v.exec(e);)n+=t[0].replace(t[2],"").replace(t[1],t[3])+"\n\n";return n},convertColonHost:function(e){return this.convertColonRule(e,E,this.colonHostPartReplacer)},convertColonHostContext:function(e){return this.convertColonRule(e,S,this.colonHostContextPartReplacer)},convertColonRule:function(e,t,n){return e.replace(t,function(e,t,r,o){if(t=O,r){for(var i,a=r.split(","),s=[],c=0,l=a.length;l>c&&(i=a[c]);c++)i=i.trim(),s.push(n(t,i,o));return s.join(",")}return t+o})},colonHostContextPartReplacer:function(e,t,n){return t.match(g)?this.colonHostPartReplacer(e,t,n):e+t+n+", "+t+" "+e+n},colonHostPartReplacer:function(e,t,n){return e+t.replace(g,"")+n},convertShadowDOMSelectors:function(e){for(var t=0;t<N.length;t++)e=e.replace(N[t]," ");return e},scopeRules:function(e,t){var n="";return e&&Array.prototype.forEach.call(e,function(e){if(e.selectorText&&e.style&&void 0!==e.style.cssText)n+=this.scopeSelector(e.selectorText,t,this.strictStyling)+" {\n	",n+=this.propertiesFromRule(e)+"\n}\n\n";else if(e.type===CSSRule.MEDIA_RULE)n+="@media "+e.media.mediaText+" {\n",n+=this.scopeRules(e.cssRules,t),n+="\n}\n\n";else try{e.cssText&&(n+=e.cssText+"\n\n")}catch(r){e.type===CSSRule.KEYFRAMES_RULE&&e.cssRules&&(n+=this.ieSafeCssTextFromKeyFrameRule(e))}},this),n},ieSafeCssTextFromKeyFrameRule:function(e){var t="@keyframes "+e.name+" {";return Array.prototype.forEach.call(e.cssRules,function(e){t+=" "+e.keyText+" {"+e.style.cssText+"}"}),t+=" }"},scopeSelector:function(e,t,n){var r=[],o=e.split(",");return o.forEach(function(e){e=e.trim(),this.selectorNeedsScoping(e,t)&&(e=n&&!e.match(O)?this.applyStrictSelectorScope(e,t):this.applySelectorScope(e,t)),r.push(e)},this),r.join(", ")},selectorNeedsScoping:function(e,t){if(Array.isArray(t))return!0;var n=this.makeScopeMatcher(t);return!e.match(n)},makeScopeMatcher:function(e){return e=e.replace(/\[/g,"\\[").replace(/\[/g,"\\]"),new RegExp("^("+e+")"+T,"m")},applySelectorScope:function(e,t){return Array.isArray(t)?this.applySelectorScopeList(e,t):this.applySimpleSelectorScope(e,t)},applySelectorScopeList:function(e,t){for(var n,r=[],o=0;n=t[o];o++)r.push(this.applySimpleSelectorScope(e,n));return r.join(", ")},applySimpleSelectorScope:function(e,t){return e.match(L)?(e=e.replace(O,t),e.replace(L,t+" ")):t+" "+e},applyStrictSelectorScope:function(e,t){t=t.replace(/\[is=([^\]]*)\]/g,"$1");var n=[" ",">","+","~"],r=e,o="["+t+"]";return n.forEach(function(e){var t=r.split(e);r=t.map(function(e){var t=e.trim().replace(L,"");return t&&n.indexOf(t)<0&&t.indexOf(o)<0&&(e=t.replace(/([^:]*)(:*)(.*)/,"$1"+o+"$2$3")),e}).join(e)}),r},insertPolyfillHostInCssText:function(e){return e.replace(_,b).replace(M,g)},propertiesFromRule:function(e){var t=e.style.cssText;e.style.content&&!e.style.content.match(/['"]+|attr/)&&(t=t.replace(/content:[^;]*;/g,"content: '"+e.style.content+"';"));var n=e.style;for(var r in n)"initial"===n[r]&&(t+=r+": initial; ");return t},replaceTextInStyles:function(e,t){e&&t&&(e instanceof Array||(e=[e]),Array.prototype.forEach.call(e,function(e){e.textContent=t.call(this,e.textContent)},this))},addCssToDocument:function(e,t){e.match("@import")?c(e,t):s(e)}},d=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,p=/\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^{]*?){/gim,f=/polyfill-next-selector[^}]*content\:[\s]*?['"](.*?)['"][;\s]*}([^{]*?){/gim,h=/\/\*\s@polyfill-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim,m=/(polyfill-rule)[^}]*(content\:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim,w=/\/\*\s@polyfill-unscoped-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim,v=/(polyfill-unscoped-rule)[^}]*(content\:[\s]*['"](.*?)['"])[;\s]*[^}]*}/gim,g="-shadowcsshost",b="-shadowcsscontext",y=")(?:\\(((?:\\([^)(]*\\)|[^)(]*)+?)\\))?([^,{]*)",E=new RegExp("("+g+y,"gim"),S=new RegExp("("+b+y,"gim"),T="([>\\s~+[.,{:][\\s\\S]*)?$",M=/\:host/gim,_=/\:host-context/gim,O=g+"-no-combinator",L=new RegExp(g,"gim"),N=(new RegExp(b,"gim"),[/\^\^/g,/\^/g,/\/shadow\//g,/\/shadow-deep\//g,/::shadow/g,/\/deep\//g,/::content/g]),C=document.createElement("iframe");C.style.display="none";var D,j=navigator.userAgent.match("Chrome"),H="shim-shadowdom",x="shim-shadowdom-css",R="no-shim";if(window.ShadowDOMPolyfill){s("style { display: none !important; }\n");var P=ShadowDOMPolyfill.wrap(document),I=P.querySelector("head");I.insertBefore(l(),I.childNodes[0]),document.addEventListener("DOMContentLoaded",function(){e.urlResolver;if(window.HTMLImports&&!HTMLImports.useNative){var t="link[rel=stylesheet]["+H+"]",n="style["+H+"]";HTMLImports.importer.documentPreloadSelectors+=","+t,HTMLImports.importer.importsPreloadSelectors+=","+t,HTMLImports.parser.documentSelectors=[HTMLImports.parser.documentSelectors,t,n].join(",");var r=HTMLImports.parser.parseGeneric;HTMLImports.parser.parseGeneric=function(e){if(!e[x]){var t=e.__importElement||e;if(!t.hasAttribute(H))return void r.call(this,e);e.__resource&&(t=e.ownerDocument.createElement("style"),t.textContent=e.__resource),HTMLImports.path.resolveUrlsInStyle(t),t.textContent=u.shimStyle(t),t.removeAttribute(H,""),t.setAttribute(x,""),t[x]=!0,t.parentNode!==I&&(e.parentNode===I?I.replaceChild(t,e):this.addElementToDocument(t)),t.__importParsed=!0,this.markParsingComplete(e),this.parseNext()}};var o=HTMLImports.parser.hasResource;HTMLImports.parser.hasResource=function(e){return"link"===e.localName&&"stylesheet"===e.rel&&e.hasAttribute(H)?e.__resource:o.call(this,e)}}})}e.ShadowCSS=u}(window.WebComponents)),function(){window.ShadowDOMPolyfill?(window.wrap=ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}}(window.WebComponents),function(e){function t(e){y.push(e),b||(b=!0,m(r))}function n(e){return window.ShadowDOMPolyfill&&window.ShadowDOMPolyfill.wrapIfNeeded(e)||e}function r(){b=!1;var e=y;y=[],e.sort(function(e,t){return e.uid_-t.uid_});var t=!1;e.forEach(function(e){var n=e.takeRecords();o(e),n.length&&(e.callback_(n,e),t=!0)}),t&&r()}function o(e){e.nodes_.forEach(function(t){var n=w.get(t);n&&n.forEach(function(t){t.observer===e&&t.removeTransientObservers()})})}function i(e,t){for(var n=e;n;n=n.parentNode){var r=w.get(n);if(r)for(var o=0;o<r.length;o++){var i=r[o],a=i.options;if(n===e||a.subtree){var s=t(a);s&&i.enqueue(s)}}}}function a(e){this.callback_=e,this.nodes_=[],this.records_=[],this.uid_=++E}function s(e,t){this.type=e,this.target=t,this.addedNodes=[],this.removedNodes=[],this.previousSibling=null,this.nextSibling=null,this.attributeName=null,this.attributeNamespace=null,this.oldValue=null}function c(e){var t=new s(e.type,e.target);return t.addedNodes=e.addedNodes.slice(),t.removedNodes=e.removedNodes.slice(),t.previousSibling=e.previousSibling,t.nextSibling=e.nextSibling,t.attributeName=e.attributeName,t.attributeNamespace=e.attributeNamespace,t.oldValue=e.oldValue,t}function l(e,t){return S=new s(e,t)}function u(e){return T?T:(T=c(S),T.oldValue=e,T)}function d(){S=T=void 0}function p(e){return e===T||e===S}function f(e,t){return e===t?e:T&&p(e)?T:null}function h(e,t,n){this.observer=e,this.target=t,this.options=n,this.transientObservedNodes=[]}var m,w=new WeakMap;if(/Trident/.test(navigator.userAgent))m=setTimeout;else if(window.setImmediate)m=window.setImmediate;else{var v=[],g=String(Math.random());window.addEventListener("message",function(e){if(e.data===g){var t=v;v=[],t.forEach(function(e){e()})}}),m=function(e){v.push(e),window.postMessage(g,"*")}}var b=!1,y=[],E=0;a.prototype={observe:function(e,t){if(e=n(e),!t.childList&&!t.attributes&&!t.characterData||t.attributeOldValue&&!t.attributes||t.attributeFilter&&t.attributeFilter.length&&!t.attributes||t.characterDataOldValue&&!t.characterData)throw new SyntaxError;var r=w.get(e);r||w.set(e,r=[]);for(var o,i=0;i<r.length;i++)if(r[i].observer===this){o=r[i],o.removeListeners(),o.options=t;break}o||(o=new h(this,e,t),r.push(o),this.nodes_.push(e)),o.addListeners()},disconnect:function(){this.nodes_.forEach(function(e){for(var t=w.get(e),n=0;n<t.length;n++){var r=t[n];if(r.observer===this){r.removeListeners(),t.splice(n,1);break}}},this),this.records_=[]},takeRecords:function(){var e=this.records_;return this.records_=[],e}};var S,T;h.prototype={enqueue:function(e){var n=this.observer.records_,r=n.length;if(n.length>0){var o=n[r-1],i=f(o,e);if(i)return void(n[r-1]=i)}else t(this.observer);n[r]=e},addListeners:function(){this.addListeners_(this.target)},addListeners_:function(e){var t=this.options;t.attributes&&e.addEventListener("DOMAttrModified",this,!0),t.characterData&&e.addEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.addEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.addEventListener("DOMNodeRemoved",this,!0)},removeListeners:function(){this.removeListeners_(this.target)},removeListeners_:function(e){var t=this.options;t.attributes&&e.removeEventListener("DOMAttrModified",this,!0),t.characterData&&e.removeEventListener("DOMCharacterDataModified",this,!0),t.childList&&e.removeEventListener("DOMNodeInserted",this,!0),(t.childList||t.subtree)&&e.removeEventListener("DOMNodeRemoved",this,!0)},addTransientObserver:function(e){if(e!==this.target){this.addListeners_(e),this.transientObservedNodes.push(e);var t=w.get(e);t||w.set(e,t=[]),t.push(this)}},removeTransientObservers:function(){var e=this.transientObservedNodes;this.transientObservedNodes=[],e.forEach(function(e){this.removeListeners_(e);for(var t=w.get(e),n=0;n<t.length;n++)if(t[n]===this){t.splice(n,1);break}},this)},handleEvent:function(e){switch(e.stopImmediatePropagation(),e.type){case"DOMAttrModified":var t=e.attrName,n=e.relatedNode.namespaceURI,r=e.target,o=new l("attributes",r);o.attributeName=t,o.attributeNamespace=n;var a=e.attrChange===MutationEvent.ADDITION?null:e.prevValue;i(r,function(e){return!e.attributes||e.attributeFilter&&e.attributeFilter.length&&-1===e.attributeFilter.indexOf(t)&&-1===e.attributeFilter.indexOf(n)?void 0:e.attributeOldValue?u(a):o});break;case"DOMCharacterDataModified":var r=e.target,o=l("characterData",r),a=e.prevValue;i(r,function(e){return e.characterData?e.characterDataOldValue?u(a):o:void 0});break;case"DOMNodeRemoved":this.addTransientObserver(e.target);case"DOMNodeInserted":var s,c,r=e.relatedNode,p=e.target;"DOMNodeInserted"===e.type?(s=[p],c=[]):(s=[],c=[p]);var f=p.previousSibling,h=p.nextSibling,o=l("childList",r);o.addedNodes=s,o.removedNodes=c,o.previousSibling=f,o.nextSibling=h,i(r,function(e){return e.childList?o:void 0})}d()}},e.JsMutationObserver=a,e.MutationObserver||(e.MutationObserver=a)}(this),window.HTMLImports=window.HTMLImports||{flags:{}},function(e){function t(e,t){t=t||h,r(function(){i(e,t)},t)}function n(e){return"complete"===e.readyState||e.readyState===v}function r(e,t){if(n(t))e&&e();else{var o=function(){("complete"===t.readyState||t.readyState===v)&&(t.removeEventListener(g,o),r(e,t))};t.addEventListener(g,o)}}function o(e){e.target.__loaded=!0}function i(e,t){function n(){s==c&&e&&e()}function r(e){o(e),s++,n()}var i=t.querySelectorAll("link[rel=import]"),s=0,c=i.length;if(c)for(var l,u=0;c>u&&(l=i[u]);u++)a(l)?r.call(l,{target:l}):(l.addEventListener("load",r),l.addEventListener("error",r));else n()}function a(e){return d?e.__loaded||e.import&&"loading"!==e.import.readyState:e.__importParsed}function s(e){for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)c(t)&&l(t)}function c(e){return"link"===e.localName&&"import"===e.rel}function l(e){var t=e.import;t?o({target:e}):(e.addEventListener("load",o),e.addEventListener("error",o))}var u="import",d=Boolean(u in document.createElement("link")),p=Boolean(window.ShadowDOMPolyfill),f=function(e){return p?ShadowDOMPolyfill.wrapIfNeeded(e):e},h=f(document),m={get:function(){var e=HTMLImports.currentScript||document.currentScript||("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null);return f(e)},configurable:!0};Object.defineProperty(document,"_currentScript",m),Object.defineProperty(h,"_currentScript",m);var w=/Trident/.test(navigator.userAgent),v=w?"complete":"interactive",g="readystatechange";d&&(new MutationObserver(function(e){for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)t.addedNodes&&s(t.addedNodes)}).observe(document.head,{childList:!0}),function(){if("loading"===document.readyState)for(var e,t=document.querySelectorAll("link[rel=import]"),n=0,r=t.length;r>n&&(e=t[n]);n++)l(e)}()),t(function(){HTMLImports.ready=!0,HTMLImports.readyTime=(new Date).getTime(),h.dispatchEvent(new CustomEvent("HTMLImportsLoaded",{bubbles:!0}))}),e.IMPORT_LINK_TYPE=u,e.useNative=d,e.rootDocument=h,e.whenReady=t,e.isIE=w}(HTMLImports),function(e){var t=[],n=function(e){t.push(e)},r=function(){t.forEach(function(t){t(e)})};e.addModule=n,e.initializeModules=r}(HTMLImports),HTMLImports.addModule(function(e){var t=/(url\()([^)]*)(\))/g,n=/(@import[\s]+(?!url\())([^;]*)(;)/g,r={resolveUrlsInStyle:function(e){var t=e.ownerDocument,n=t.createElement("a");return e.textContent=this.resolveUrlsInCssText(e.textContent,n),e},resolveUrlsInCssText:function(e,r){var o=this.replaceUrls(e,r,t);return o=this.replaceUrls(o,r,n)},replaceUrls:function(e,t,n){return e.replace(n,function(e,n,r,o){var i=r.replace(/["']/g,"");return t.href=i,i=t.href,n+"'"+i+"'"+o})}};e.path=r}),HTMLImports.addModule(function(e){xhr={async:!0,ok:function(e){return e.status>=200&&e.status<300||304===e.status||0===e.status},load:function(t,n,r){var o=new XMLHttpRequest;return(e.flags.debug||e.flags.bust)&&(t+="?"+Math.random()),o.open("GET",t,xhr.async),o.addEventListener("readystatechange",function(){if(4===o.readyState){var e=o.getResponseHeader("Location"),t=null;if(e)var t="/"===e.substr(0,1)?location.origin+e:e;n.call(r,!xhr.ok(o)&&o,o.response||o.responseText,t)}}),o.send(),o},loadDocument:function(e,t,n){this.load(e,t,n).responseType="document"}},e.xhr=xhr}),HTMLImports.addModule(function(e){var t=e.xhr,n=e.flags,r=function(e,t){this.cache={},this.onload=e,this.oncomplete=t,this.inflight=0,this.pending={}};r.prototype={addNodes:function(e){this.inflight+=e.length;for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)this.require(t);this.checkDone()},addNode:function(e){this.inflight++,this.require(e),this.checkDone()},require:function(e){var t=e.src||e.href;e.__nodeUrl=t,this.dedupe(t,e)||this.fetch(t,e)},dedupe:function(e,t){if(this.pending[e])return this.pending[e].push(t),!0;return this.cache[e]?(this.onload(e,t,this.cache[e]),this.tail(),!0):(this.pending[e]=[t],!1)},fetch:function(e,r){if(n.load&&console.log("fetch",e,r),e.match(/^data:/)){var o=e.split(","),i=o[0],a=o[1];a=i.indexOf(";base64")>-1?atob(a):decodeURIComponent(a),setTimeout(function(){this.receive(e,r,null,a)}.bind(this),0)}else{var s=function(t,n,o){this.receive(e,r,t,n,o)}.bind(this);t.load(e,s)}},receive:function(e,t,n,r,o){this.cache[e]=r;for(var i,a=this.pending[e],s=0,c=a.length;c>s&&(i=a[s]);s++)this.onload(e,i,r,n,o),this.tail();this.pending[e]=null},tail:function(){--this.inflight,this.checkDone()},checkDone:function(){this.inflight||this.oncomplete()}},e.Loader=r}),HTMLImports.addModule(function(e){var t=function(e){this.addCallback=e,this.mo=new MutationObserver(this.handler.bind(this))};t.prototype={handler:function(e){for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)"childList"===t.type&&t.addedNodes.length&&this.addedNodes(t.addedNodes)},addedNodes:function(e){this.addCallback&&this.addCallback(e);for(var t,n=0,r=e.length;r>n&&(t=e[n]);n++)t.children&&t.children.length&&this.addedNodes(t.children)},observe:function(e){this.mo.observe(e,{childList:!0,subtree:!0})}},e.Observer=t}),HTMLImports.addModule(function(e){function t(e){return"link"===e.localName&&e.rel===u}function n(e){var t=r(e);return"data:text/javascript;charset=utf-8,"+encodeURIComponent(t)}function r(e){return e.textContent+o(e)}function o(e){var t=e.ownerDocument;t.__importedScripts=t.__importedScripts||0;var n=e.ownerDocument.baseURI,r=t.__importedScripts?"-"+t.__importedScripts:"";return t.__importedScripts++,"\n//# sourceURL="+n+r+".js\n"}function i(e){var t=e.ownerDocument.createElement("style");return t.textContent=e.textContent,a.resolveUrlsInStyle(t),t}var a=e.path,s=e.rootDocument,c=e.flags,l=e.isIE,u=e.IMPORT_LINK_TYPE,d="link[rel="+u+"]",p={documentSelectors:d,importsSelectors:[d,"link[rel=stylesheet]","style","script:not([type])",'script[type="text/javascript"]'].join(","),map:{link:"parseLink",script:"parseScript",style:"parseStyle"},dynamicElements:[],parseNext:function(){var e=this.nextToParse();e&&this.parse(e)},parse:function(e){if(this.isParsed(e))return void(c.parse&&console.log("[%s] is already parsed",e.localName));var t=this[this.map[e.localName]];t&&(this.markParsing(e),t.call(this,e))},parseDynamic:function(e,t){this.dynamicElements.push(e),t||this.parseNext()},markParsing:function(e){c.parse&&console.log("parsing",e),this.parsingElement=e},markParsingComplete:function(e){e.__importParsed=!0,this.markDynamicParsingComplete(e),e.__importElement&&(e.__importElement.__importParsed=!0,this.markDynamicParsingComplete(e.__importElement)),this.parsingElement=null,c.parse&&console.log("completed",e)},markDynamicParsingComplete:function(e){var t=this.dynamicElements.indexOf(e);t>=0&&this.dynamicElements.splice(t,1)},parseImport:function(e){if(HTMLImports.__importsParsingHook&&HTMLImports.__importsParsingHook(e),e.import&&(e.import.__importParsed=!0),this.markParsingComplete(e),e.dispatchEvent(e.__resource&&!e.__error?new CustomEvent("load",{bubbles:!1}):new CustomEvent("error",{bubbles:!1})),e.__pending)for(var t;e.__pending.length;)t=e.__pending.shift(),t&&t({target:e});this.parseNext()},parseLink:function(e){t(e)?this.parseImport(e):(e.href=e.href,this.parseGeneric(e))},parseStyle:function(e){var t=e;e=i(e),e.__importElement=t,this.parseGeneric(e)},parseGeneric:function(e){this.trackElement(e),this.addElementToDocument(e)},rootImportForElement:function(e){for(var t=e;t.ownerDocument.__importLink;)t=t.ownerDocument.__importLink;return t},addElementToDocument:function(e){for(var t=this.rootImportForElement(e.__importElement||e),n=t.__insertedElements=t.__insertedElements||0,r=t.nextElementSibling,o=0;n>o;o++)r=r&&r.nextElementSibling;t.parentNode.insertBefore(e,r)},trackElement:function(e,t){var n=this,r=function(r){t&&t(r),n.markParsingComplete(e),n.parseNext()};if(e.addEventListener("load",r),e.addEventListener("error",r),l&&"style"===e.localName){var o=!1;if(-1==e.textContent.indexOf("@import"))o=!0;else if(e.sheet){o=!0;for(var i,a=e.sheet.cssRules,s=a?a.length:0,c=0;s>c&&(i=a[c]);c++)i.type===CSSRule.IMPORT_RULE&&(o=o&&Boolean(i.styleSheet))}o&&e.dispatchEvent(new CustomEvent("load",{bubbles:!1}))}},parseScript:function(t){var r=document.createElement("script");r.__importElement=t,r.src=t.src?t.src:n(t),e.currentScript=t,this.trackElement(r,function(){r.parentNode.removeChild(r),e.currentScript=null}),this.addElementToDocument(r)},nextToParse:function(){return this._mayParse=[],!this.parsingElement&&(this.nextToParseInDoc(s)||this.nextToParseDynamic())},nextToParseInDoc:function(e,n){if(e&&this._mayParse.indexOf(e)<0){this._mayParse.push(e);for(var r,o=e.querySelectorAll(this.parseSelectorsForNode(e)),i=0,a=o.length;a>i&&(r=o[i]);i++)if(!this.isParsed(r))return this.hasResource(r)?t(r)?this.nextToParseInDoc(r.import,r):r:void 0}return n},nextToParseDynamic:function(){return this.dynamicElements[0]},parseSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===s?this.documentSelectors:this.importsSelectors},isParsed:function(e){return e.__importParsed},needsDynamicParsing:function(e){return this.dynamicElements.indexOf(e)>=0},hasResource:function(e){return t(e)&&void 0===e.import?!1:!0}};e.parser=p,e.IMPORT_SELECTOR=d}),HTMLImports.addModule(function(e){function t(e){return n(e,i)}function n(e,t){return"link"===e.localName&&e.getAttribute("rel")===t}function r(e,t){var n=document.implementation.createHTMLDocument(i);n._URL=t;var r=n.createElement("base");r.setAttribute("href",t),n.baseURI||(n.baseURI=t);var o=n.createElement("meta");return o.setAttribute("charset","utf-8"),n.head.appendChild(o),n.head.appendChild(r),n.body.innerHTML=e,window.HTMLTemplateElement&&HTMLTemplateElement.bootstrap&&HTMLTemplateElement.bootstrap(n),n}var o=e.flags,i=e.IMPORT_LINK_TYPE,a=e.IMPORT_SELECTOR,s=e.rootDocument,c=e.Loader,l=e.Observer,u=e.parser,d={documents:{},documentPreloadSelectors:a,importsPreloadSelectors:[a].join(","),loadNode:function(e){p.addNode(e)},loadSubtree:function(e){var t=this.marshalNodes(e);p.addNodes(t)},marshalNodes:function(e){return e.querySelectorAll(this.loadSelectorsForNode(e))},loadSelectorsForNode:function(e){var t=e.ownerDocument||e;return t===s?this.documentPreloadSelectors:this.importsPreloadSelectors},loaded:function(e,n,i,a,s){if(o.load&&console.log("loaded",e,n),n.__resource=i,n.__error=a,t(n)){var c=this.documents[e];void 0===c&&(c=a?null:r(i,s||e),c&&(c.__importLink=n,this.bootDocument(c)),this.documents[e]=c),n.import=c}u.parseNext()},bootDocument:function(e){this.loadSubtree(e),this.observer.observe(e),u.parseNext()},loadedAll:function(){u.parseNext()}},p=new c(d.loaded.bind(d),d.loadedAll.bind(d));if(d.observer=new l,!document.baseURI){var f={get:function(){var e=document.querySelector("base");return e?e.href:window.location.href},configurable:!0};Object.defineProperty(document,"baseURI",f),Object.defineProperty(s,"baseURI",f)}e.importer=d,e.importLoader=p}),HTMLImports.addModule(function(e){var t=e.parser,n=e.importer,r={added:function(e){for(var r,o,i,a=0,s=e.length;s>a&&(i=e[a]);a++)r||(r=i.ownerDocument,o=t.isParsed(r)),loading=this.shouldLoadNode(i),loading&&n.loadNode(i),this.shouldParseNode(i)&&o&&t.parseDynamic(i,loading)},shouldLoadNode:function(e){return 1===e.nodeType&&o.call(e,n.loadSelectorsForNode(e))},shouldParseNode:function(e){return 1===e.nodeType&&o.call(e,t.parseSelectorsForNode(e))}};n.observer.addCallback=r.added.bind(r);var o=HTMLElement.prototype.matches||HTMLElement.prototype.matchesSelector||HTMLElement.prototype.webkitMatchesSelector||HTMLElement.prototype.mozMatchesSelector||HTMLElement.prototype.msMatchesSelector}),function(e){function t(){HTMLImports.importer.bootDocument(n)}if(initializeModules=e.initializeModules,!e.useNative){"function"!=typeof window.CustomEvent&&(window.CustomEvent=function(e,t){var n=document.createEvent("HTMLEvents");return n.initEvent(e,t.bubbles===!1?!1:!0,t.cancelable===!1?!1:!0,t.detail),n}),initializeModules();var n=e.rootDocument;"complete"===document.readyState||"interactive"===document.readyState&&!window.attachEvent?t():document.addEventListener("DOMContentLoaded",t)}}(HTMLImports),window.CustomElements=window.CustomElements||{flags:{}},function(e){var t=e.flags,n=[],r=function(e){n.push(e)},o=function(){n.forEach(function(t){t(e)})};e.addModule=r,e.initializeModules=o,e.hasNative=Boolean(document.registerElement),e.useNative=!t.register&&e.hasNative&&!window.ShadowDOMPolyfill&&(!window.HTMLImports||HTMLImports.useNative)}(CustomElements),CustomElements.addModule(function(e){function t(e,t){n(e,function(e){return t(e)?!0:void r(e,t)}),r(e,t)}function n(e,t,r){var o=e.firstElementChild;if(!o)for(o=e.firstChild;o&&o.nodeType!==Node.ELEMENT_NODE;)o=o.nextSibling;for(;o;)t(o,r)!==!0&&n(o,t,r),o=o.nextElementSibling;return null}function r(e,n){for(var r=e.shadowRoot;r;)t(r,n),r=r.olderShadowRoot}function o(e,t){a=[],i(e,t),a=null}function i(e,t){if(e=wrap(e),!(a.indexOf(e)>=0)){a.push(e);for(var n,r=e.querySelectorAll("link[rel="+s+"]"),o=0,c=r.length;c>o&&(n=r[o]);o++)n.import&&i(n.import,t);t(e)}}var a,s=window.HTMLImports?HTMLImports.IMPORT_LINK_TYPE:"none";e.forDocumentTree=o,e.forSubtree=t}),CustomElements.addModule(function(e){function t(e){return n(e)||r(e)}function n(t){return e.upgrade(t)?!0:void s(t)}function r(e){y(e,function(e){return n(e)?!0:void 0})}function o(e){s(e),p(e)&&y(e,function(e){s(e)})}function i(e){M.push(e),T||(T=!0,setTimeout(a))}function a(){T=!1;for(var e,t=M,n=0,r=t.length;r>n&&(e=t[n]);n++)e();M=[]}function s(e){S?i(function(){c(e)}):c(e)}function c(e){e.__upgraded__&&(e.attachedCallback||e.detachedCallback)&&!e.__attached&&p(e)&&(e.__attached=!0,e.attachedCallback&&e.attachedCallback())}function l(e){u(e),y(e,function(e){u(e)})}function u(e){S?i(function(){d(e)}):d(e)}function d(e){e.__upgraded__&&(e.attachedCallback||e.detachedCallback)&&e.__attached&&!p(e)&&(e.__attached=!1,e.detachedCallback&&e.detachedCallback())}function p(e){for(var t=e,n=wrap(document);t;){if(t==n)return!0;t=t.parentNode||t.host}}function f(e){if(e.shadowRoot&&!e.shadowRoot.__watched){b.dom&&console.log("watching shadow-root for: ",e.localName);for(var t=e.shadowRoot;t;)w(t),t=t.olderShadowRoot}}function h(e){if(b.dom){var n=e[0];if(n&&"childList"===n.type&&n.addedNodes&&n.addedNodes){for(var r=n.addedNodes[0];r&&r!==document&&!r.host;)r=r.parentNode;var o=r&&(r.URL||r._URL||r.host&&r.host.localName)||"";
+o=o.split("/?").shift().split("/").pop()}console.group("mutations (%d) [%s]",e.length,o||"")}e.forEach(function(e){"childList"===e.type&&(_(e.addedNodes,function(e){e.localName&&t(e)}),_(e.removedNodes,function(e){e.localName&&l(e)}))}),b.dom&&console.groupEnd()}function m(e){for(e=wrap(e),e||(e=wrap(document));e.parentNode;)e=e.parentNode;var t=e.__observer;t&&(h(t.takeRecords()),a())}function w(e){if(!e.__observer){var t=new MutationObserver(h);t.observe(e,{childList:!0,subtree:!0}),e.__observer=t}}function v(e){e=wrap(e),b.dom&&console.group("upgradeDocument: ",e.baseURI.split("/").pop()),t(e),w(e),b.dom&&console.groupEnd()}function g(e){E(e,v)}var b=e.flags,y=e.forSubtree,E=e.forDocumentTree,S=!window.MutationObserver||window.MutationObserver===window.JsMutationObserver;e.hasPolyfillMutations=S;var T=!1,M=[],_=Array.prototype.forEach.call.bind(Array.prototype.forEach),O=Element.prototype.createShadowRoot;Element.prototype.createShadowRoot=function(){var e=O.call(this);return CustomElements.watchShadow(this),e},e.watchShadow=f,e.upgradeDocumentTree=g,e.upgradeSubtree=r,e.upgradeAll=t,e.attachedNode=o,e.takeRecords=m}),CustomElements.addModule(function(e){function t(t){if(!t.__upgraded__&&t.nodeType===Node.ELEMENT_NODE){var r=t.getAttribute("is"),o=e.getRegisteredDefinition(r||t.localName);if(o){if(r&&o.tag==t.localName)return n(t,o);if(!r&&!o.extends)return n(t,o)}}}function n(t,n){return a.upgrade&&console.group("upgrade:",t.localName),n.is&&t.setAttribute("is",n.is),r(t,n),t.__upgraded__=!0,i(t),e.attachedNode(t),e.upgradeSubtree(t),a.upgrade&&console.groupEnd(),t}function r(e,t){Object.__proto__?e.__proto__=t.prototype:(o(e,t.prototype,t.native),e.__proto__=t.prototype)}function o(e,t,n){for(var r={},o=t;o!==n&&o!==HTMLElement.prototype;){for(var i,a=Object.getOwnPropertyNames(o),s=0;i=a[s];s++)r[i]||(Object.defineProperty(e,i,Object.getOwnPropertyDescriptor(o,i)),r[i]=1);o=Object.getPrototypeOf(o)}}function i(e){e.createdCallback&&e.createdCallback()}var a=e.flags;e.upgrade=t,e.upgradeWithDefinition=n,e.implementPrototype=r}),CustomElements.addModule(function(e){function t(t,r){var c=r||{};if(!t)throw new Error("document.registerElement: first argument `name` must not be empty");if(t.indexOf("-")<0)throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '"+String(t)+"'.");if(o(t))throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '"+String(t)+"'. The type name is invalid.");if(l(t))throw new Error("DuplicateDefinitionError: a type with name '"+String(t)+"' is already registered");return c.prototype||(c.prototype=Object.create(HTMLElement.prototype)),c.__name=t.toLowerCase(),c.lifecycle=c.lifecycle||{},c.ancestry=i(c.extends),a(c),s(c),n(c.prototype),u(c.__name,c),c.ctor=d(c),c.ctor.prototype=c.prototype,c.prototype.constructor=c.ctor,e.ready&&w(document),c.ctor}function n(e){if(!e.setAttribute._polyfilled){var t=e.setAttribute;e.setAttribute=function(e,n){r.call(this,e,n,t)};var n=e.removeAttribute;e.removeAttribute=function(e){r.call(this,e,null,n)},e.setAttribute._polyfilled=!0}}function r(e,t,n){e=e.toLowerCase();var r=this.getAttribute(e);n.apply(this,arguments);var o=this.getAttribute(e);this.attributeChangedCallback&&o!==r&&this.attributeChangedCallback(e,r,o)}function o(e){for(var t=0;t<E.length;t++)if(e===E[t])return!0}function i(e){var t=l(e);return t?i(t.extends).concat([t]):[]}function a(e){for(var t,n=e.extends,r=0;t=e.ancestry[r];r++)n=t.is&&t.tag;e.tag=n||e.__name,n&&(e.is=e.__name)}function s(e){if(!Object.__proto__){var t=HTMLElement.prototype;if(e.is){var n=document.createElement(e.tag),r=Object.getPrototypeOf(n);r===e.prototype&&(t=r)}for(var o,i=e.prototype;i&&i!==t;)o=Object.getPrototypeOf(i),i.__proto__=o,i=o;e.native=t}}function c(e){return g(M(e.tag),e)}function l(e){return e?S[e.toLowerCase()]:void 0}function u(e,t){S[e]=t}function d(e){return function(){return c(e)}}function p(e,t,n){return e===T?f(t,n):_(e,t)}function f(e,t){var n=l(t||e);if(n){if(e==n.tag&&t==n.is)return new n.ctor;if(!t&&!n.is)return new n.ctor}var r;return t?(r=f(e),r.setAttribute("is",t),r):(r=M(e),e.indexOf("-")>=0&&b(r,HTMLElement),r)}function h(e){var t=O.call(this,e);return v(t),t}var m,w=e.upgradeDocumentTree,v=e.upgrade,g=e.upgradeWithDefinition,b=e.implementPrototype,y=e.useNative,E=["annotation-xml","color-profile","font-face","font-face-src","font-face-uri","font-face-format","font-face-name","missing-glyph"],S={},T="http://www.w3.org/1999/xhtml",M=document.createElement.bind(document),_=document.createElementNS.bind(document),O=Node.prototype.cloneNode;m=Object.__proto__||y?function(e,t){return e instanceof t}:function(e,t){for(var n=e;n;){if(n===t.prototype)return!0;n=n.__proto__}return!1},document.registerElement=t,document.createElement=f,document.createElementNS=p,Node.prototype.cloneNode=h,e.registry=S,e.instanceof=m,e.reservedTagList=E,e.getRegisteredDefinition=l,document.register=document.registerElement}),function(e){function t(){i(wrap(document)),window.HTMLImports&&(HTMLImports.__importsParsingHook=function(e){i(wrap(e.import))}),CustomElements.ready=!0,setTimeout(function(){CustomElements.readyTime=Date.now(),window.HTMLImports&&(CustomElements.elapsed=CustomElements.readyTime-HTMLImports.readyTime),document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))})}var n=e.useNative,r=e.initializeModules;if(n){var o=function(){};e.watchShadow=o,e.upgrade=o,e.upgradeAll=o,e.upgradeDocumentTree=o,e.upgradeSubtree=o,e.takeRecords=o,e.instanceof=function(e,t){return e instanceof t}}else r();var i=e.upgradeDocumentTree;if(window.wrap||(window.ShadowDOMPolyfill?(window.wrap=ShadowDOMPolyfill.wrapIfNeeded,window.unwrap=ShadowDOMPolyfill.unwrapIfNeeded):window.wrap=window.unwrap=function(e){return e}),"function"!=typeof window.CustomEvent&&(window.CustomEvent=function(e,t){t=t||{};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,Boolean(t.bubbles),Boolean(t.cancelable),t.detail),n},window.CustomEvent.prototype=window.Event.prototype),"complete"===document.readyState||e.flags.eager)t();else if("interactive"!==document.readyState||window.attachEvent||window.HTMLImports&&!window.HTMLImports.ready){var a=window.HTMLImports&&!HTMLImports.ready?"HTMLImportsLoaded":"DOMContentLoaded";window.addEventListener(a,t)}else t()}(window.CustomElements),function(){Function.prototype.bind||(Function.prototype.bind=function(e){var t=this,n=Array.prototype.slice.call(arguments,1);return function(){var r=n.slice();return r.push.apply(r,arguments),t.apply(e,r)}})}(window.WebComponents),function(e){"use strict";function t(){window.Polymer===o&&(window.Polymer=function(){throw new Error('You tried to use polymer without loading it first. To load polymer, <link rel="import" href="components/polymer/polymer.html">')})}if(!window.performance){var n=Date.now();window.performance={now:function(){return Date.now()-n}}}window.requestAnimationFrame||(window.requestAnimationFrame=function(){var e=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;return e?function(t){return e(function(){t(performance.now())})}:function(e){return window.setTimeout(e,1e3/60)}}()),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(){return window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||function(e){clearTimeout(e)}}());var r=[],o=function(e){"string"!=typeof e&&1===arguments.length&&Array.prototype.push.call(arguments,document._currentScript),r.push(arguments)};window.Polymer=o,e.consumeDeclarations=function(t){e.consumeDeclarations=function(){throw"Possible attempt to load Polymer twice"},t&&t(r),r=null},HTMLImports.useNative?t():addEventListener("DOMContentLoaded",t)}(window.WebComponents),function(){var e=document.createElement("style");e.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; position: relative; } \n";var t=document.querySelector("head");t.insertBefore(e,t.firstChild)}(window.WebComponents),function(e){window.Platform=e}(window.WebComponents);
diff --git a/web_components/pubspec.yaml b/web_components/pubspec.yaml
new file mode 100644
index 0000000..9a401a3
--- /dev/null
+++ b/web_components/pubspec.yaml
@@ -0,0 +1,34 @@
+name: web_components
+version: 0.11.4
+author: Polymer.dart Authors <web-ui-dev@dartlang.org>
+homepage: https://www.dartlang.org/polymer-dart/
+description: >
+  Polyfills for Shadow DOM, Custom Elements, and HTML Imports.
+  Custom Elements let authors define their own elements. Authors associate code
+  with custom tag names, and then use those custom tag names as they would any
+  standard tag. Shadow DOM is designed to provide encapsulation for custom
+  elements, by hiding DOM subtrees under shadow roots. HTML Imports let authors
+  bundle code and HTML as if they were libraries.
+dependencies:
+  analyzer: '>=0.22.4 <0.26.0'
+  barback: '>=0.14.2 <0.16.0'
+  code_transformers: '^0.2.7'
+  html: '^0.12.0'
+  initialize: '^0.6.0'
+  path: '^1.3.0'
+dev_dependencies:
+  unittest: '^0.11.0'
+  browser: '^0.10.0'
+transformers:
+- web_components/build/mirrors_remover:
+    $include: 'lib/src/init.dart'
+- web_components:
+    $include: '**/*_test.html'
+    entry_points:
+      - test/custom_element_test.html
+      - test/custom_element_proxy_test.html
+      - test/html_import_annotation_test.html
+      - test/init_web_components_test.html
+
+environment:
+  sdk: ">=1.9.0-dev.7.1 <2.0.0"
diff --git a/when/lib/when.dart b/when/lib/when.dart
new file mode 100755
index 0000000..703ecc2
--- /dev/null
+++ b/when/lib/when.dart
@@ -0,0 +1,68 @@
+

+library when;

+

+import 'dart:async';

+

+/// Registers callbacks on the result of a [callback], which may or may not be

+/// a [Future].

+///

+/// If [callback] returns a future, any of [onSuccess], [onError], or

+/// [onComplete] that are provided are registered on the future,

+/// and the resulting future is returned.

+///

+/// Otherwise, if [callback] did not throw, if [onSuccess] is provided, it is

+/// called with the with the result of [callback], and the return value of

+/// [onSuccess] is captured.

+///

+/// Otherwise, if [onError] was provided, it is called.  It can take either

+/// just an error, or a stack trace as well.  If [onError] was not provided,

+/// the error is not caught.

+///

+/// [onComplete] is then called synchronously.

+///

+/// The captured value is then returned.

+when(callback, {onSuccess(result), onError, onComplete}) {

+  var result, hasResult = false;

+

+  try {

+    result = callback();

+    hasResult = true;

+  } catch (e, s) {

+    if (onError != null) {

+      if (onError is _Unary) {

+        onError(e);

+      } else if (onError is _Binary) {

+        onError(e, s);

+      } else {

+        throw new ArgumentError(

+            '"onError" must accept 1 or 2 arguments: $onError');

+      }

+    } else {

+      rethrow;

+    }

+  } finally {

+    if (result is Future) {

+      if (onSuccess != null) {

+        result = result.then(onSuccess);

+      }

+      if (onError != null) {

+        result = result.catchError(onError);

+      }

+      if (onComplete != null) {

+        result = result.whenComplete(onComplete);

+      }

+    } else {

+      if (hasResult) {

+        if (onSuccess != null) {

+          result = onSuccess(result);

+        }

+      }

+      if (onComplete != null) onComplete();

+    }

+  }

+

+  return result;

+}

+

+typedef _Unary(x);

+typedef _Binary(x, y);

diff --git a/when/pubspec.yaml b/when/pubspec.yaml
new file mode 100755
index 0000000..ac206a0
--- /dev/null
+++ b/when/pubspec.yaml
@@ -0,0 +1,7 @@
+name: when
+version: 0.2.0
+author: Sean Eagan <seaneagan1@gmail.com>
+description: Register callbacks on code which is conditionally sync or async.
+homepage: https://github.com/seaneagan/when.dart
+dev_dependencies:
+  unittest: '>=0.11.4 <0.12.0'
diff --git a/which/lib/src/candidate_paths.dart b/which/lib/src/candidate_paths.dart
new file mode 100755
index 0000000..3c98767
--- /dev/null
+++ b/which/lib/src/candidate_paths.dart
@@ -0,0 +1,36 @@
+

+library which.src.candidate_paths;

+

+import 'dart:io';

+

+import 'package:path/path.dart';

+

+Iterable<String> getCandidatePaths(String command, Map<String, String> environment, bool isWindows, Context context) {

+  if (context.isAbsolute(command)) return [command];

+

+  String getEnvVar(String envVar, String defaultValue) {

+    var v = environment[envVar];

+    return v == null ? defaultValue : v;

+  }

+

+  var pathVarSeparator = isWindows ? ";" : ":";

+

+  List<String> splitEnvVar(String envVar, String defaultValue) =>

+      getEnvVar(envVar, defaultValue).split(pathVarSeparator);

+

+  var pathEnv = splitEnvVar('PATH', '');

+

+  var noExtPaths =

+      pathEnv.map((pathEntry) => context.join(pathEntry, command));

+

+  if (!isWindows) return noExtPaths;

+

+  pathEnv.insert(0, context.current);

+  var pathExt = splitEnvVar('PATHEXT', ".EXE");

+  if (command.contains('.')) pathExt.insert(0, '');

+  return noExtPaths.expand((commandPath) =>

+      pathExt.map((pathExtEntry) => commandPath + pathExtEntry));

+}

+

+Iterable<String> getRealCandidatePaths(String command) =>

+    getCandidatePaths(command, Platform.environment, Platform.isWindows, context);

diff --git a/which/lib/src/has_permission.dart b/which/lib/src/has_permission.dart
new file mode 100755
index 0000000..415c498
--- /dev/null
+++ b/which/lib/src/has_permission.dart
@@ -0,0 +1,52 @@
+

+/// See http://dartbug.com/22036

+library which.src.has_permission;

+

+class FilePermission {

+

+  final int index;

+  final String _name;

+

+  const FilePermission._(this.index, this._name);

+

+  static const EXECUTE = const FilePermission._(0, 'EXECUTE');

+  static const WRITE = const FilePermission._(1, 'WRITE');

+  static const READ = const FilePermission._(2, 'READ');

+  static const SET_UID = const FilePermission._(3, 'SET_UID');

+  static const SET_GID = const FilePermission._(4, 'SET_GID');

+  static const STICKY = const FilePermission._(5, 'STICKY');

+

+  static const List<FilePermission> values = const [EXECUTE, WRITE, READ, SET_UID, SET_GID, STICKY];

+

+  String toString() => 'FilePermission.$_name';

+}

+

+class FilePermissionRole {

+

+  final int index;

+  final String _name;

+

+  const FilePermissionRole._(this.index, this._name);

+

+  static const WORLD = const FilePermissionRole._(0, 'WORLD');

+  static const GROUP = const FilePermissionRole._(1, 'GROUP');

+  static const OWNER = const FilePermissionRole._(2, 'OWNER');

+

+  static const List<FilePermissionRole> values = const [WORLD, GROUP, OWNER];

+

+  String toString() => 'FilePermissionRole.$_name';

+}

+

+bool hasPermission(int fileStatMode, FilePermission permission, {FilePermissionRole role: FilePermissionRole.WORLD}) {

+  var bitIndex = _getPermissionBitIndex(permission, role);

+  return (fileStatMode & (1 << bitIndex)) != 0;

+}

+

+int _getPermissionBitIndex(FilePermission permission, FilePermissionRole role) {

+  switch (permission) {

+    case FilePermission.SET_UID: return 11;

+    case FilePermission.SET_GID: return 10;

+    case FilePermission.STICKY: return 9;

+    default: return (role.index * 3) + permission.index;

+  }

+}

diff --git a/which/lib/src/is_executable.dart b/which/lib/src/is_executable.dart
new file mode 100755
index 0000000..02fb834
--- /dev/null
+++ b/which/lib/src/is_executable.dart
@@ -0,0 +1,33 @@
+

+library which.src.is_executable;

+

+import 'dart:async';

+import 'dart:io';

+

+import 'package:when/when.dart';

+

+import 'has_permission.dart';

+

+Future<bool> isExecutable(String path, bool isWindows, Future<FileStat> getStat(path)) =>

+    _isExecutable(path, isWindows, getStat);

+

+bool isExecutableSync(String path, bool isWindows, FileStat getStat(path)) =>

+    _isExecutable(path, isWindows, getStat);

+

+_isExecutable(String path, bool isWindows, getStat(path)) =>

+    when(() => getStat(path), onSuccess: (stat) => isExecutableStat(stat, isWindows));

+

+/// Tests whether the file exists and is executable.

+bool isExecutableStat(FileStat stat, bool isWindows) {

+  if (FileSystemEntityType.FILE != stat.type) return false;

+

+  // There is no concept of executable on windows.

+  if (isWindows) return true;

+

+  // TODO: This currently produces false positives (returns true when it

+  //       shouldn't) when the uid/gid of current user and executable don't

+  //       match.  Fix if/when uid/gid support is added:

+  //       http://dartbug.com/22037.

+  return FilePermissionRole.values.any((role) =>

+      hasPermission(stat.mode, FilePermission.EXECUTE, role: role));

+}

diff --git a/which/lib/src/util.dart b/which/lib/src/util.dart
new file mode 100755
index 0000000..41af139
--- /dev/null
+++ b/which/lib/src/util.dart
@@ -0,0 +1,18 @@
+

+library which.src.util;

+

+import 'dart:async';

+

+/// Transparently call `firstWhere` on a [Stream] or [Iterable].

+// TODO: Remove once https://dartbug.com/ is fixed.

+firstWhere(sequence, test, { orElse() }) => sequence is Iterable ?

+  sequence.firstWhere(test, orElse: orElse) :

+    _streamFirstWhere(sequence, test, orElse: orElse);

+

+Future _streamFirstWhere(Stream stream, test(item), { orElse() }) {

+  var pairs = stream.asyncMap((item) => test(item).then((result) => [item, result]));

+  return pairs.firstWhere((pair) => pair.last, defaultValue: () => [orElse(), null]).then((pair) => pair.first);

+}

+

+/// The identity function simply returns its argument ([x]).

+identity(x) => x;

diff --git a/which/lib/src/which_impl.dart b/which/lib/src/which_impl.dart
new file mode 100755
index 0000000..2f49295
--- /dev/null
+++ b/which/lib/src/which_impl.dart
@@ -0,0 +1,52 @@
+

+library which.src.which_impl;

+

+import 'dart:async';

+

+import 'package:when/when.dart';

+

+import 'util.dart';

+

+Future<String> which(

+    String command,

+    Iterable<String> candidatePaths,

+    bool isWindows,

+    Future<bool> isExecutable(String path, bool isWindows),

+    orElse()) => new Future(() => _which(

+    command,

+    candidatePaths,

+    isWindows,

+    isExecutable,

+    orElse,

+    toSequence: (items) => new Stream.fromIterable(items)));

+

+String whichSync(

+    String command,

+    Iterable<String> candidatePaths,

+    bool isWindows,

+    bool isExecutable(String path, bool isWindows),

+    orElse()) => _which(

+    command,

+    candidatePaths,

+    isWindows,

+    isExecutable,

+    orElse);

+

+_which(

+    String command,

+    Iterable<String> candidatePaths,

+    bool isWindows,

+    isExecutable(String path, bool isWindows),

+    orElse(),

+    {toSequence(Iterable items): identity}) => when(

+    () => firstWhere(

+        toSequence(candidatePaths),

+        (path) => isExecutable(path, isWindows),

+        orElse: orElse != null ? orElse : () => _commandNotFound(command, null)),

+    onError: (e) => _commandNotFound(command, e));

+

+_commandNotFound(String command, e) {

+  var message = 'Command not found: $command';

+  if (e != null) message += '\n$e';

+  throw new StateError(message);

+}

diff --git a/which/lib/which.dart b/which/lib/which.dart
new file mode 100755
index 0000000..c9047a3
--- /dev/null
+++ b/which/lib/which.dart
@@ -0,0 +1,29 @@
+

+library which;

+

+import 'dart:async';

+import 'dart:io';

+

+import 'src/candidate_paths.dart';

+import 'src/is_executable.dart';

+import 'src/which_impl.dart' as impl;

+

+/// Returns a future for the first [command] executable in the `PATH`.

+///

+/// If [command] is not found, [orElse] is called, which defaults to throwing.

+Future<String> which(String command, { orElse() }) => new Future(() => impl.which(

+    command,

+    getRealCandidatePaths(command),

+    Platform.isWindows,

+    (path, isWindows) => isExecutable(path, isWindows, FileStat.stat),

+    orElse));

+

+/// Returns the first [command] executable in the `PATH`.

+///

+/// If [command] is not found, [orElse] is called, which defaults to throwing.

+String whichSync(String command, { orElse() }) => impl.whichSync(

+    command,

+    getRealCandidatePaths(command),

+    Platform.isWindows,

+    (path, isWindows) => isExecutableSync(path, isWindows, FileStat.statSync),

+    orElse);

diff --git a/which/pubspec.yaml b/which/pubspec.yaml
new file mode 100755
index 0000000..a6b2bc6
--- /dev/null
+++ b/which/pubspec.yaml
@@ -0,0 +1,11 @@
+name: which
+version: 0.1.3
+author: Sean Eagan <seaneagan1@gmail.com>
+description: Like unix which(1).  Check for and locate installed executables.
+homepage: https://github.com/seaneagan/which.dart
+dependencies:
+  path: '>=1.3.1 <2.0.0'
+  when: '>=0.2.0 <0.3.0'
+dev_dependencies:
+  mockito: '>=0.8.1 <0.9.0'
+  unittest: '>=0.11.4 <0.12.0'
diff --git a/yaml/lib/src/equality.dart b/yaml/lib/src/equality.dart
new file mode 100644
index 0000000..5408135
--- /dev/null
+++ b/yaml/lib/src/equality.dart
@@ -0,0 +1,126 @@
+// 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 yaml.equality;
+
+import 'dart:collection';
+
+import 'package:collection/collection.dart';
+
+import 'yaml_node.dart';
+
+/// Returns a [Map] that compares its keys based on [deepEquals].
+Map deepEqualsMap() => new HashMap(equals: deepEquals, hashCode: deepHashCode);
+
+/// Returns whether two objects are structurally equivalent.
+///
+/// This considers `NaN` values to be equivalent, handles self-referential
+/// structures, and considers [YamlScalar]s to be equal to their values.
+bool deepEquals(obj1, obj2) => new _DeepEquals().equals(obj1, obj2);
+
+/// A class that provides access to the list of parent objects used for loop
+/// detection.
+class _DeepEquals {
+  final _parents1 = [];
+  final _parents2 = [];
+
+  /// Returns whether [obj1] and [obj2] are structurally equivalent.
+  bool equals(obj1, obj2) {
+    if (obj1 is YamlScalar) obj1 = obj1.value;
+    if (obj2 is YamlScalar) obj2 = obj2.value;
+
+    // _parents1 and _parents2 are guaranteed to be the same size.
+    for (var i = 0; i < _parents1.length; i++) {
+      var loop1 = identical(obj1, _parents1[i]);
+      var loop2 = identical(obj2, _parents2[i]);
+      // If both structures loop in the same place, they're equal at that point
+      // in the structure. If one loops and the other doesn't, they're not
+      // equal.
+      if (loop1 && loop2) return true;
+      if (loop1 || loop2) return false;
+    }
+
+    _parents1.add(obj1);
+    _parents2.add(obj2);
+    try {
+      if (obj1 is List && obj2 is List) {
+        return _listEquals(obj1, obj2);
+      } else if (obj1 is Map && obj2 is Map) {
+        return _mapEquals(obj1, obj2);
+      } else if (obj1 is num && obj2 is num) {
+        return _numEquals(obj1, obj2);
+      } else {
+        return obj1 == obj2;
+      }
+    } finally {
+      _parents1.removeLast();
+      _parents2.removeLast();
+    }
+  }
+
+  /// Returns whether [list1] and [list2] are structurally equal. 
+  bool _listEquals(List list1, List list2) {
+    if (list1.length != list2.length) return false;
+
+    for (var i = 0; i < list1.length; i++) {
+      if (!equals(list1[i], list2[i])) return false;
+    }
+
+    return true;
+  }
+
+  /// Returns whether [map1] and [map2] are structurally equal. 
+  bool _mapEquals(Map map1, Map map2) {
+    if (map1.length != map2.length) return false;
+
+    for (var key in map1.keys) {
+      if (!map2.containsKey(key)) return false;
+      if (!equals(map1[key], map2[key])) return false;
+    }
+
+    return true;
+  }
+
+  /// Returns whether two numbers are equivalent.
+  ///
+  /// This differs from `n1 == n2` in that it considers `NaN` to be equal to
+  /// itself.
+  bool _numEquals(num n1, num n2) {
+    if (n1.isNaN && n2.isNaN) return true;
+    return n1 == n2;
+  }
+}
+
+/// Returns a hash code for [obj] such that structurally equivalent objects
+/// will have the same hash code.
+///
+/// This supports deep equality for maps and lists, including those with
+/// self-referential structures, and returns the same hash code for
+/// [YamlScalar]s and their values.
+int deepHashCode(obj) {
+  var parents = [];
+
+  _deepHashCode(value) {
+    if (parents.any((parent) => identical(parent, value))) return -1;
+
+    parents.add(value);
+    try {
+      if (value is Map) {
+        var equality = const UnorderedIterableEquality();
+        return equality.hash(value.keys.map(_deepHashCode)) ^
+            equality.hash(value.values.map(_deepHashCode));
+      } else if (value is Iterable) {
+        return const IterableEquality().hash(value.map(deepHashCode));
+      } else if (value is YamlScalar) {
+        return value.value.hashCode;
+      } else {
+        return value.hashCode;
+      }
+    } finally {
+      parents.removeLast();
+    }
+  }
+
+  return _deepHashCode(obj);
+}
diff --git a/yaml/lib/src/event.dart b/yaml/lib/src/event.dart
new file mode 100644
index 0000000..96e2f16
--- /dev/null
+++ b/yaml/lib/src/event.dart
@@ -0,0 +1,157 @@
+// 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 yaml.event;
+
+import 'package:source_span/source_span.dart';
+
+import 'style.dart';
+import 'yaml_document.dart';
+
+/// An event emitted by a [Parser].
+class Event {
+  /// The event type.
+  final EventType type;
+
+  /// The span associated with the event.
+  final FileSpan span;
+
+  Event(this.type, this.span);
+
+  String toString() => type.toString();
+}
+
+/// An event indicating the beginning of a YAML document.
+class DocumentStartEvent implements Event {
+  get type => EventType.DOCUMENT_START;
+  final FileSpan span;
+
+  /// The document's `%YAML` directive, or `null` if there was none.
+  final VersionDirective versionDirective;
+
+  /// The document's `%TAG` directives, if any.
+  final List<TagDirective> tagDirectives;
+
+  /// Whether the document started implicitly (that is, without an explicit
+  /// `===` sequence).
+  final bool isImplicit;
+
+  DocumentStartEvent(this.span, {this.versionDirective,
+          List<TagDirective> tagDirectives, this.isImplicit: true})
+      : tagDirectives = tagDirectives == null ? [] : tagDirectives;
+
+  String toString() => "DOCUMENT_START";
+}
+
+/// An event indicating the end of a YAML document.
+class DocumentEndEvent implements Event {
+  get type => EventType.DOCUMENT_END;
+  final FileSpan span;
+
+  /// Whether the document ended implicitly (that is, without an explicit
+  /// `...` sequence).
+  final bool isImplicit;
+
+  DocumentEndEvent(this.span, {this.isImplicit: true});
+
+  String toString() => "DOCUMENT_END";
+}
+
+/// An event indicating that an alias was referenced.
+class AliasEvent implements Event {
+  get type => EventType.ALIAS;
+  final FileSpan span;
+
+  /// The name of the anchor.
+  final String name;
+
+  AliasEvent(this.span, this.name);
+
+  String toString() => "ALIAS $name";
+}
+
+/// A base class for events that can have anchor and tag properties associated
+/// with them.
+abstract class _ValueEvent implements Event {
+  /// The name of the value's anchor, or `null` if it wasn't anchored.
+  String get anchor;
+
+  /// The text of the value's tag, or `null` if it wasn't tagged.
+  String get tag;
+
+  String toString() {
+    var buffer = new StringBuffer('$type');
+    if (anchor != null) buffer.write(" &$anchor");
+    if (tag != null) buffer.write(" $tag");
+    return buffer.toString();
+  }
+}
+
+/// An event indicating a single scalar value.
+class ScalarEvent extends _ValueEvent {
+  get type => EventType.SCALAR;
+  final FileSpan span;
+  final String anchor;
+  final String tag;
+
+  /// The contents of the scalar.
+  final String value;
+
+  /// The style of the scalar in the original source.
+  final ScalarStyle style;
+
+  ScalarEvent(this.span, this.value, this.style, {this.anchor, this.tag});
+
+  String toString() => "${super.toString()} \"$value\"";
+}
+
+/// An event indicating the beginning of a sequence.
+class SequenceStartEvent extends _ValueEvent {
+  get type => EventType.SEQUENCE_START;
+  final FileSpan span;
+  final String anchor;
+  final String tag;
+
+  /// The style of the collection in the original source.
+  final CollectionStyle style;
+
+  SequenceStartEvent(this.span, this.style, {this.anchor, this.tag});
+}
+
+/// An event indicating the beginning of a mapping.
+class MappingStartEvent extends _ValueEvent {
+  get type => EventType.MAPPING_START;
+  final FileSpan span;
+  final String anchor;
+  final String tag;
+
+  /// The style of the collection in the original source.
+  final CollectionStyle style;
+
+  MappingStartEvent(this.span, this.style, {this.anchor, this.tag});
+}
+
+/// An enum of types of [Event] object.
+class EventType {
+  static const STREAM_START = const EventType._("STREAM_START");
+  static const STREAM_END = const EventType._("STREAM_END");
+
+  static const DOCUMENT_START = const EventType._("DOCUMENT_START");
+  static const DOCUMENT_END = const EventType._("DOCUMENT_END");
+
+  static const ALIAS = const EventType._("ALIAS");
+  static const SCALAR = const EventType._("SCALAR");
+
+  static const SEQUENCE_START = const EventType._("SEQUENCE_START");
+  static const SEQUENCE_END = const EventType._("SEQUENCE_END");
+
+  static const MAPPING_START = const EventType._("MAPPING_START");
+  static const MAPPING_END = const EventType._("MAPPING_END");
+
+  final String name;
+
+  const EventType._(this.name);
+
+  String toString() => name;
+}
diff --git a/yaml/lib/src/loader.dart b/yaml/lib/src/loader.dart
new file mode 100644
index 0000000..d80578f
--- /dev/null
+++ b/yaml/lib/src/loader.dart
@@ -0,0 +1,257 @@
+// 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 yaml.loader;
+
+import 'package:source_span/source_span.dart';
+
+import 'equality.dart';
+import 'event.dart';
+import 'parser.dart';
+import 'yaml_document.dart';
+import 'yaml_exception.dart';
+import 'yaml_node.dart';
+
+/// A loader that reads [Event]s emitted by a [Parser] and emits
+/// [YamlDocument]s.
+///
+/// This is based on the libyaml loader, available at
+/// https://github.com/yaml/libyaml/blob/master/src/loader.c. The license for
+/// that is available in ../../libyaml-license.txt.
+class Loader {
+  /// The underlying [Parser] that generates [Event]s.
+  final Parser _parser;
+
+  /// Aliases by the alias name.
+  final _aliases = new Map<String, YamlNode>();
+
+  /// The span of the entire stream emitted so far.
+  FileSpan get span => _span;
+  FileSpan _span;
+
+  /// Creates a loader that loads [source].
+  ///
+  /// [sourceUrl] can be a String or a [Uri].
+  Loader(String source, {sourceUrl})
+      : _parser = new Parser(source, sourceUrl: sourceUrl) {
+    var event = _parser.parse();
+    _span = event.span;
+    assert(event.type == EventType.STREAM_START);
+  }
+
+  /// Loads the next document from the stream.
+  ///
+  /// If there are no more documents, returns `null`.
+  YamlDocument load() {
+    if (_parser.isDone) return null;
+
+    var event = _parser.parse();
+    if (event.type == EventType.STREAM_END) {
+      _span = _span.expand(event.span);
+      return null;
+    }
+
+    var document = _loadDocument(event);
+    _span = _span.expand(document.span);
+    _aliases.clear();
+    return document;
+  }
+
+  /// Composes a document object.
+  YamlDocument _loadDocument(DocumentStartEvent firstEvent) {
+    var contents = _loadNode(_parser.parse());
+
+    var lastEvent = _parser.parse();
+    assert(lastEvent.type == EventType.DOCUMENT_END);
+
+    return new YamlDocument.internal(
+        contents,
+        firstEvent.span.expand(lastEvent.span),
+        firstEvent.versionDirective,
+        firstEvent.tagDirectives,
+        startImplicit: firstEvent.isImplicit,
+        endImplicit: lastEvent.isImplicit);
+  }
+
+  /// Composes a node.
+  YamlNode _loadNode(Event firstEvent) {
+    switch (firstEvent.type) {
+      case EventType.ALIAS: return _loadAlias(firstEvent);
+      case EventType.SCALAR: return _loadScalar(firstEvent);
+      case EventType.SEQUENCE_START: return _loadSequence(firstEvent);
+      case EventType.MAPPING_START: return _loadMapping(firstEvent);
+      default: throw "Unreachable";
+    }
+  }
+
+  /// Registers an anchor.
+  void _registerAnchor(String anchor, YamlNode node) {
+    if (anchor == null) return;
+
+    // libyaml throws an error for duplicate anchors, but example 7.1 makes it
+    // clear that they should be overridden:
+    // http://yaml.org/spec/1.2/spec.html#id2786448.
+
+    _aliases[anchor] = node;
+  }
+
+  /// Composes a node corresponding to an alias.
+  YamlNode _loadAlias(AliasEvent event) {
+    var alias = _aliases[event.name];
+    if (alias != null) return alias;
+
+    throw new YamlException("Undefined alias.", event.span);
+  }
+
+  /// Composes a scalar node.
+  YamlNode _loadScalar(ScalarEvent scalar) {
+    var node;
+    if (scalar.tag == "!") {
+      node = _parseString(scalar);
+    } else if (scalar.tag != null) {
+      node = _parseByTag(scalar);
+    } else {
+      node = _parseNull(scalar);
+      if (node == null) node = _parseBool(scalar);
+      if (node == null) node = _parseInt(scalar);
+      if (node == null) node = _parseFloat(scalar);
+      if (node == null) node = _parseString(scalar);
+    }
+
+    _registerAnchor(scalar.anchor, node);
+    return node;
+  }
+
+  /// Composes a sequence node.
+  YamlNode _loadSequence(SequenceStartEvent firstEvent) {
+    if (firstEvent.tag != "!" && firstEvent.tag != null &&
+        firstEvent.tag != "tag:yaml.org,2002:seq") {
+      throw new YamlException("Invalid tag for sequence.", firstEvent.span);
+    }
+
+    var children = [];
+    var node = new YamlList.internal(
+        children, firstEvent.span, firstEvent.style);
+    _registerAnchor(firstEvent.anchor, node);
+
+    var event = _parser.parse();
+    while (event.type != EventType.SEQUENCE_END) {
+      children.add(_loadNode(event));
+      event = _parser.parse();
+    }
+
+    setSpan(node, firstEvent.span.expand(event.span));
+    return node;
+  }
+
+  /// Composes a mapping node.
+  YamlNode _loadMapping(MappingStartEvent firstEvent) {
+    if (firstEvent.tag != "!" && firstEvent.tag != null &&
+        firstEvent.tag != "tag:yaml.org,2002:map") {
+      throw new YamlException("Invalid tag for mapping.", firstEvent.span);
+    }
+
+    var children = deepEqualsMap();
+    var node = new YamlMap.internal(
+        children, firstEvent.span, firstEvent.style);
+    _registerAnchor(firstEvent.anchor, node);
+
+    var event = _parser.parse();
+    while (event.type != EventType.MAPPING_END) {
+      var key = _loadNode(event);
+      var value = _loadNode(_parser.parse());
+      children[key] = value;
+      event = _parser.parse();
+    }
+
+    setSpan(node, firstEvent.span.expand(event.span));
+    return node;
+  }
+
+  /// Parses a scalar according to its tag name.
+  YamlScalar _parseByTag(ScalarEvent scalar) {
+    switch (scalar.tag) {
+      case "tag:yaml.org,2002:null": return _parseNull(scalar);
+      case "tag:yaml.org,2002:bool": return _parseBool(scalar);
+      case "tag:yaml.org,2002:int": return _parseInt(scalar);
+      case "tag:yaml.org,2002:float": return _parseFloat(scalar);
+      case "tag:yaml.org,2002:str": return _parseString(scalar);
+    }
+    throw new YamlException('Undefined tag: ${scalar.tag}.', scalar.span);
+  }
+
+  /// Parses a null scalar.
+  YamlScalar _parseNull(ScalarEvent scalar) {
+    // TODO(nweiz): stop using regexps.
+    // TODO(nweiz): add ScalarStyle and implicit metadata to the scalars.
+    if (new RegExp(r"^(null|Null|NULL|~|)$").hasMatch(scalar.value)) {
+      return new YamlScalar.internal(null, scalar.span, scalar.style);
+    } else {
+      return null;
+    }
+  }
+
+  /// Parses a boolean scalar.
+  YamlScalar _parseBool(ScalarEvent scalar) {
+    var match = new RegExp(r"^(?:(true|True|TRUE)|(false|False|FALSE))$").
+        firstMatch(scalar.value);
+    if (match == null) return null;
+    return new YamlScalar.internal(
+        match.group(1) != null, scalar.span, scalar.style);
+  }
+
+  /// Parses an integer scalar.
+  YamlScalar _parseInt(ScalarEvent scalar) {
+    var match = new RegExp(r"^[-+]?[0-9]+$").firstMatch(scalar.value);
+    if (match != null) {
+      return new YamlScalar.internal(
+          int.parse(match.group(0)), scalar.span, scalar.style);
+    }
+
+    match = new RegExp(r"^0o([0-7]+)$").firstMatch(scalar.value);
+    if (match != null) {
+      var n = int.parse(match.group(1), radix: 8);
+      return new YamlScalar.internal(n, scalar.span, scalar.style);
+    }
+
+    match = new RegExp(r"^0x[0-9a-fA-F]+$").firstMatch(scalar.value);
+    if (match != null) {
+      return new YamlScalar.internal(
+          int.parse(match.group(0)), scalar.span, scalar.style);
+    }
+
+    return null;
+  }
+
+  /// Parses a floating-point scalar.
+  YamlScalar _parseFloat(ScalarEvent scalar) {
+    var match = new RegExp(
+          r"^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$").
+        firstMatch(scalar.value);
+    if (match != null) {
+      // YAML allows floats of the form "0.", but Dart does not. Fix up those
+      // floats by removing the trailing dot.
+      var matchStr = match.group(0).replaceAll(new RegExp(r"\.$"), "");
+      return new YamlScalar.internal(
+          double.parse(matchStr), scalar.span, scalar.style);
+    }
+
+    match = new RegExp(r"^([+-]?)\.(inf|Inf|INF)$").firstMatch(scalar.value);
+    if (match != null) {
+      var value = match.group(1) == "-" ? -double.INFINITY : double.INFINITY;
+      return new YamlScalar.internal(value, scalar.span, scalar.style);
+    }
+
+    match = new RegExp(r"^\.(nan|NaN|NAN)$").firstMatch(scalar.value);
+    if (match != null) {
+      return new YamlScalar.internal(double.NAN, scalar.span, scalar.style);
+    }
+
+    return null;
+  }
+
+  /// Parses a string scalar.
+  YamlScalar _parseString(ScalarEvent scalar) =>
+      new YamlScalar.internal(scalar.value, scalar.span, scalar.style);
+}
diff --git a/yaml/lib/src/null_span.dart b/yaml/lib/src/null_span.dart
new file mode 100644
index 0000000..06b42f0
--- /dev/null
+++ b/yaml/lib/src/null_span.dart
@@ -0,0 +1,21 @@
+// 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 yaml.null_span;
+
+import 'package:source_span/source_span.dart';
+
+/// A [SourceSpan] with no location information.
+///
+/// This is used with [YamlMap.wrap] and [YamlList.wrap] to provide means of
+/// accessing a non-YAML map that behaves transparently like a map parsed from
+/// YAML.
+class NullSpan extends SourceSpanMixin {
+  final SourceLocation start;
+  SourceLocation get end => start;
+  final text = "";
+
+  NullSpan(sourceUrl)
+      : start = new SourceLocation(0, sourceUrl: sourceUrl);
+}
diff --git a/yaml/lib/src/parser.dart b/yaml/lib/src/parser.dart
new file mode 100644
index 0000000..72c01dc
--- /dev/null
+++ b/yaml/lib/src/parser.dart
@@ -0,0 +1,817 @@
+// 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 yaml.parser;
+
+import 'package:source_span/source_span.dart';
+import 'package:string_scanner/string_scanner.dart';
+
+import 'event.dart';
+import 'scanner.dart';
+import 'style.dart';
+import 'token.dart';
+import 'utils.dart';
+import 'yaml_document.dart';
+import 'yaml_exception.dart';
+
+/// A parser that reads [Token]s emitted by a [Scanner] and emits [Event]s.
+///
+/// This is based on the libyaml parser, available at
+/// https://github.com/yaml/libyaml/blob/master/src/parser.c. The license for
+/// that is available in ../../libyaml-license.txt.
+class Parser {
+  /// The underlying [Scanner] that generates [Token]s.
+  final Scanner _scanner;
+
+  /// The stack of parse states for nested contexts.
+  final _states = new List<_State>();
+
+  /// The current parse state.
+  var _state = _State.STREAM_START;
+
+  /// The custom tag directives, by tag handle.
+  final _tagDirectives = new Map<String, TagDirective>();
+
+  /// Whether the parser has finished parsing.
+  bool get isDone => _state == _State.END;
+
+  /// Creates a parser that parses [source].
+  ///
+  /// [sourceUrl] can be a String or a [Uri].
+  Parser(String source, {sourceUrl})
+      : _scanner = new Scanner(source, sourceUrl: sourceUrl);
+
+  /// Consumes and returns the next event.
+  Event parse() {
+    try {
+      if (isDone) throw new StateError("No more events.");
+      var event = _stateMachine();
+      return event;
+    } on StringScannerException catch (error) {
+      throw new YamlException(error.message, error.span);
+    }
+  }
+
+  /// Dispatches parsing based on the current state.
+  Event _stateMachine() {
+    switch (_state) {
+      case _State.STREAM_START:
+        return _parseStreamStart();
+      case _State.DOCUMENT_START:
+        return _parseDocumentStart();
+      case _State.DOCUMENT_CONTENT:
+        return _parseDocumentContent();
+      case _State.DOCUMENT_END:
+        return _parseDocumentEnd();
+      case _State.BLOCK_NODE:
+        return _parseNode(block: true);
+      case _State.BLOCK_NODE_OR_INDENTLESS_SEQUENCE:
+        return _parseNode(block: true, indentlessSequence: true);
+      case _State.FLOW_NODE:
+        return _parseNode();
+      case _State.BLOCK_SEQUENCE_FIRST_ENTRY:
+        // Scan past the `BLOCK-SEQUENCE-FIRST-ENTRY` token to the
+        // `BLOCK-SEQUENCE-ENTRY` token.
+        _scanner.scan();
+        return _parseBlockSequenceEntry();
+      case _State.BLOCK_SEQUENCE_ENTRY:
+        return _parseBlockSequenceEntry();
+      case _State.INDENTLESS_SEQUENCE_ENTRY:
+        return _parseIndentlessSequenceEntry();
+      case _State.BLOCK_MAPPING_FIRST_KEY:
+        // Scan past the `BLOCK-MAPPING-FIRST-KEY` token to the
+        // `BLOCK-MAPPING-KEY` token.
+        _scanner.scan();
+        return _parseBlockMappingKey();
+      case _State.BLOCK_MAPPING_KEY:
+        return _parseBlockMappingKey();
+      case _State.BLOCK_MAPPING_VALUE:
+        return _parseBlockMappingValue();
+      case _State.FLOW_SEQUENCE_FIRST_ENTRY:
+        return _parseFlowSequenceEntry(first: true);
+      case _State.FLOW_SEQUENCE_ENTRY:
+        return _parseFlowSequenceEntry();
+      case _State.FLOW_SEQUENCE_ENTRY_MAPPING_KEY:
+        return _parseFlowSequenceEntryMappingKey();
+      case _State.FLOW_SEQUENCE_ENTRY_MAPPING_VALUE:
+        return _parseFlowSequenceEntryMappingValue();
+      case _State.FLOW_SEQUENCE_ENTRY_MAPPING_END:
+        return _parseFlowSequenceEntryMappingEnd();
+      case _State.FLOW_MAPPING_FIRST_KEY:
+        return _parseFlowMappingKey(first: true);
+      case _State.FLOW_MAPPING_KEY:
+        return _parseFlowMappingKey();
+      case _State.FLOW_MAPPING_VALUE:
+        return _parseFlowMappingValue();
+      case _State.FLOW_MAPPING_EMPTY_VALUE:
+        return _parseFlowMappingValue(empty: true);
+      default:
+        throw "Unreachable";
+    }
+  }
+
+  /// Parses the production:
+  ///
+  ///     stream ::=
+  ///       STREAM-START implicit_document? explicit_document* STREAM-END
+  ///       ************  
+  Event _parseStreamStart() {
+    var token = _scanner.scan();
+    assert(token.type == TokenType.STREAM_START);
+
+    _state = _State.DOCUMENT_START;
+    return new Event(EventType.STREAM_START, token.span);
+  }
+
+  /// Parses the productions:
+  ///
+  ///     implicit_document    ::= block_node DOCUMENT-END*
+  ///                              *
+  ///     explicit_document    ::=
+  ///       DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+  ///       *************************
+  Event _parseDocumentStart() {
+    var token = _scanner.peek();
+
+    // libyaml requires any document beyond the first in the stream to have an
+    // explicit document start indicator, but the spec allows it to be omitted
+    // as long as there was an end indicator.
+
+    // Parse extra document end indicators.
+    while (token.type == TokenType.DOCUMENT_END) {
+      token = _scanner.advance();
+    }
+
+    if (token.type != TokenType.VERSION_DIRECTIVE &&
+        token.type != TokenType.TAG_DIRECTIVE &&
+        token.type != TokenType.DOCUMENT_START &&
+        token.type != TokenType.STREAM_END) {
+      // Parse an implicit document.
+      _processDirectives();
+      _states.add(_State.DOCUMENT_END);
+      _state = _State.BLOCK_NODE;
+      return new DocumentStartEvent(token.span.start.pointSpan());
+    }
+
+    if (token.type == TokenType.STREAM_END) {
+      _state = _State.END;
+      _scanner.scan();
+      return new Event(EventType.STREAM_END, token.span);
+    }
+
+    // Parse an explicit document.
+    var start = token.span;
+    var pair = _processDirectives();
+    var versionDirective = pair.first;
+    var tagDirectives = pair.last;
+    token = _scanner.peek();
+    if (token.type != TokenType.DOCUMENT_START) {
+      throw new YamlException("Expected document start.", token.span);
+    }
+
+    _states.add(_State.DOCUMENT_END);
+    _state = _State.DOCUMENT_CONTENT;
+    _scanner.scan();
+    return new DocumentStartEvent(start.expand(token.span),
+        versionDirective: versionDirective,
+        tagDirectives: tagDirectives,
+        isImplicit: false);
+  }
+
+  /// Parses the productions:
+  ///
+  ///     explicit_document    ::=
+  ///       DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+  ///                                 ***********  
+  Event _parseDocumentContent() {
+    var token = _scanner.peek();
+
+    switch (token.type) {
+      case TokenType.VERSION_DIRECTIVE:
+      case TokenType.TAG_DIRECTIVE:
+      case TokenType.DOCUMENT_START:
+      case TokenType.DOCUMENT_END:
+      case TokenType.STREAM_END:
+        _state = _states.removeLast();
+        return _processEmptyScalar(token.span.start);
+      default:
+        return _parseNode(block: true);
+    }
+  }
+
+  /// Parses the productions:
+  ///
+  ///     implicit_document    ::= block_node DOCUMENT-END*
+  ///                                         *************
+  ///     explicit_document    ::=
+  ///       DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+  ///                                             *************
+  Event _parseDocumentEnd() {
+    _tagDirectives.clear();
+    _state = _State.DOCUMENT_START;
+
+    var token = _scanner.peek();
+    if (token.type == TokenType.DOCUMENT_END) {
+      _scanner.scan();
+      return new DocumentEndEvent(token.span, isImplicit: false);
+    } else {
+      return new DocumentEndEvent(
+          token.span.start.pointSpan(), isImplicit: true);
+    }
+  }
+
+  /// Parses the productions:
+  ///
+  ///     block_node_or_indentless_sequence    ::=
+  ///       ALIAS
+  ///       *****
+  ///       | properties (block_content | indentless_block_sequence)?
+  ///         **********  *
+  ///       | block_content | indentless_block_sequence
+  ///         *
+  ///     block_node           ::= ALIAS
+  ///                              *****
+  ///                              | properties block_content?
+  ///                                ********** *
+  ///                              | block_content
+  ///                                *
+  ///     flow_node            ::= ALIAS
+  ///                              *****
+  ///                              | properties flow_content?
+  ///                                ********** *
+  ///                              | flow_content
+  ///                                *
+  ///     properties           ::= TAG ANCHOR? | ANCHOR TAG?
+  ///                              *************************
+  ///     block_content        ::= block_collection | flow_collection | SCALAR
+  ///                                                                   ******
+  ///     flow_content         ::= flow_collection | SCALAR
+  ///                                                ******
+  Event _parseNode({bool block: false, bool indentlessSequence: false}) {
+    var token = _scanner.peek();
+
+    if (token is AliasToken) {
+      _scanner.scan();
+      _state = _states.removeLast();
+      return new AliasEvent(token.span, token.name);
+    }
+
+    var anchor;
+    var tagToken;
+    var span = token.span.start.pointSpan();
+    parseAnchor() {
+      anchor = token.name;
+      span = span.expand(token.span);
+      token = _scanner.advance();
+    }
+
+    parseTag() {
+      tagToken = token;
+      span = span.expand(token.span);
+      token = _scanner.advance();
+    }
+
+    if (token is AnchorToken) {
+      parseAnchor();
+      if (token is TagToken) parseTag();
+    } else if (token is TagToken) {
+      parseTag();
+      if (token is AnchorToken) parseAnchor();
+    }
+
+    var tag;
+    if (tagToken != null) {
+      if (tagToken.handle == null) {
+        tag = tagToken.suffix;
+      } else {
+        var tagDirective = _tagDirectives[tagToken.handle];
+        if (tagDirective == null) {
+          throw new YamlException("Undefined tag handle.", tagToken.span);
+        }
+
+        tag = tagDirective.prefix + tagToken.suffix;
+      }
+    }
+
+    if (indentlessSequence && token.type == TokenType.BLOCK_ENTRY) {
+      _state = _State.INDENTLESS_SEQUENCE_ENTRY;
+      return new SequenceStartEvent(
+          span.expand(token.span), CollectionStyle.BLOCK,
+          anchor: anchor, tag: tag);
+    }
+
+    if (token is ScalarToken) {
+      // All non-plain scalars have the "!" tag by default.
+      if (tag == null && token.style != ScalarStyle.PLAIN) tag = "!";
+
+      _state = _states.removeLast();
+      _scanner.scan();
+      return new ScalarEvent(
+          span.expand(token.span), token.value, token.style,
+          anchor: anchor, tag: tag);
+    }
+
+    if (token.type == TokenType.FLOW_SEQUENCE_START) {
+      _state = _State.FLOW_SEQUENCE_FIRST_ENTRY;
+      return new SequenceStartEvent(
+          span.expand(token.span), CollectionStyle.FLOW,
+          anchor: anchor, tag: tag);
+    }
+
+    if (token.type == TokenType.FLOW_MAPPING_START) {
+      _state = _State.FLOW_MAPPING_FIRST_KEY;
+      return new MappingStartEvent(
+          span.expand(token.span), CollectionStyle.FLOW,
+          anchor: anchor, tag: tag);
+    }
+
+    if (block && token.type == TokenType.BLOCK_SEQUENCE_START) {
+      _state = _State.BLOCK_SEQUENCE_FIRST_ENTRY;
+      return new SequenceStartEvent(
+          span.expand(token.span), CollectionStyle.BLOCK,
+          anchor: anchor, tag: tag);
+    }
+
+
+    if (block && token.type == TokenType.BLOCK_MAPPING_START) {
+      _state = _State.BLOCK_MAPPING_FIRST_KEY;
+      return new MappingStartEvent(
+          span.expand(token.span), CollectionStyle.BLOCK,
+          anchor: anchor, tag: tag);
+    }
+
+    if (anchor != null || tag != null) {
+      _state = _states.removeLast();
+      return new ScalarEvent(
+          span, '', ScalarStyle.PLAIN,
+          anchor: anchor, tag: tag);
+    }
+
+    throw new YamlException("Expected node content.", span);
+  }
+
+  /// Parses the productions:
+  ///
+  ///     block_sequence ::=
+  ///       BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
+  ///       ********************  *********** *             *********
+  Event _parseBlockSequenceEntry() {
+    var token = _scanner.peek();
+
+    if (token.type == TokenType.BLOCK_ENTRY) {
+      token = _scanner.advance();
+
+      if (token.type == TokenType.BLOCK_ENTRY ||
+          token.type == TokenType.BLOCK_END) {
+        _state = _State.BLOCK_SEQUENCE_ENTRY;
+        return _processEmptyScalar(token.span.end);
+      } else {
+        _states.add(_State.BLOCK_SEQUENCE_ENTRY);
+        return _parseNode(block: true);
+      }
+    }
+
+    if (token.type == TokenType.BLOCK_END) {
+      _scanner.scan();
+      _state = _states.removeLast();
+      return new Event(EventType.SEQUENCE_END, token.span);
+    }
+
+    throw new YamlException("While parsing a block collection, expected '-'.",
+        token.span.start.pointSpan());
+  }
+
+  /// Parses the productions:
+  ///
+  ///     indentless_sequence  ::= (BLOCK-ENTRY block_node?)+
+  ///                               *********** *
+  Event _parseIndentlessSequenceEntry() {
+    var token = _scanner.peek();
+
+    if (token.type != TokenType.BLOCK_ENTRY) {
+      _state = _states.removeLast();
+      return new Event(EventType.SEQUENCE_END, token.span.start.pointSpan());
+    }
+
+    var start = token.span.start;
+    token = _scanner.advance();
+
+    if (token.type == TokenType.BLOCK_ENTRY ||
+        token.type == TokenType.KEY ||
+        token.type == TokenType.VALUE ||
+        token.type == TokenType.BLOCK_END) {
+      _state = _State.INDENTLESS_SEQUENCE_ENTRY;
+      return _processEmptyScalar(start);
+    } else {
+      _states.add(_State.INDENTLESS_SEQUENCE_ENTRY);
+      return _parseNode(block: true);
+    }
+  }
+
+  /// Parses the productions:
+  ///
+  ///     block_mapping        ::= BLOCK-MAPPING_START
+  ///                              *******************
+  ///                              ((KEY block_node_or_indentless_sequence?)?
+  ///                                *** *
+  ///                              (VALUE block_node_or_indentless_sequence?)?)*
+  ///
+  ///                              BLOCK-END
+  ///                              *********
+  Event _parseBlockMappingKey() {
+    var token = _scanner.peek();
+    if (token.type == TokenType.KEY) {
+      var start = token.span.start;
+      token = _scanner.advance();
+
+      if (token.type == TokenType.KEY ||
+          token.type == TokenType.VALUE ||
+          token.type == TokenType.BLOCK_END) {
+        _state = _State.BLOCK_MAPPING_VALUE;
+        return _processEmptyScalar(start);
+      } else {
+        _states.add(_State.BLOCK_MAPPING_VALUE);
+        return _parseNode(block: true, indentlessSequence: true);
+      }
+    }
+
+    // libyaml doesn't allow empty keys without an explicit key indicator, but
+    // the spec does. See example 8.18:
+    // http://yaml.org/spec/1.2/spec.html#id2798896.
+    if (token.type == TokenType.VALUE) {
+      _state = _State.BLOCK_MAPPING_VALUE;
+      return _processEmptyScalar(token.span.start);
+    }
+
+    if (token.type == TokenType.BLOCK_END) {
+      _scanner.scan();
+      _state = _states.removeLast();
+      return new Event(EventType.MAPPING_END, token.span);
+    }
+
+    throw new YamlException("Expected a key while parsing a block mapping.",
+        token.span.start.pointSpan());
+  }
+
+  /// Parses the productions:
+  ///
+  ///     block_mapping        ::= BLOCK-MAPPING_START
+  ///
+  ///                              ((KEY block_node_or_indentless_sequence?)?
+  ///
+  ///                              (VALUE block_node_or_indentless_sequence?)?)*
+  ///                               ***** *
+  ///                              BLOCK-END  
+  ///
+  Event _parseBlockMappingValue() {
+    var token = _scanner.peek();
+
+    if (token.type != TokenType.VALUE) {
+      _state = _State.BLOCK_MAPPING_KEY;
+      return _processEmptyScalar(token.span.start);
+    }
+
+    var start = token.span.start;
+    token = _scanner.advance();
+    if (token.type == TokenType.KEY ||
+        token.type == TokenType.VALUE ||
+        token.type == TokenType.BLOCK_END) {
+      _state = _State.BLOCK_MAPPING_KEY;
+      return _processEmptyScalar(start);
+    } else {
+      _states.add(_State.BLOCK_MAPPING_KEY);
+      return _parseNode(block: true, indentlessSequence: true);
+    }
+  }
+
+  /// Parses the productions:
+  ///
+  ///     flow_sequence        ::= FLOW-SEQUENCE-START
+  ///                              *******************
+  ///                              (flow_sequence_entry FLOW-ENTRY)*
+  ///                               *                   **********
+  ///                              flow_sequence_entry?
+  ///                              *
+  ///                              FLOW-SEQUENCE-END
+  ///                              *****************
+  ///     flow_sequence_entry  ::=
+  ///       flow_node | KEY flow_node? (VALUE flow_node?)?
+  ///       *
+  Event _parseFlowSequenceEntry({bool first: false}) {
+    if (first) _scanner.scan();
+    var token = _scanner.peek();
+
+    if (token.type != TokenType.FLOW_SEQUENCE_END) {
+      if (!first) {
+        if (token.type != TokenType.FLOW_ENTRY) {
+          throw new YamlException(
+              "While parsing a flow sequence, expected ',' or ']'.",
+              token.span.start.pointSpan());
+        }
+
+        token = _scanner.advance();
+      }
+
+      if (token.type == TokenType.KEY) {
+        _state = _State.FLOW_SEQUENCE_ENTRY_MAPPING_KEY;
+        _scanner.scan();
+        return new MappingStartEvent(
+            token.span, CollectionStyle.FLOW);
+      } else if (token.type != TokenType.FLOW_SEQUENCE_END) {
+        _states.add(_State.FLOW_SEQUENCE_ENTRY);
+        return _parseNode();
+      }
+    }
+
+    _scanner.scan();
+    _state = _states.removeLast();
+    return new Event(EventType.SEQUENCE_END, token.span);
+  }  
+
+  /// Parses the productions:
+  ///
+  ///     flow_sequence_entry  ::=
+  ///       flow_node | KEY flow_node? (VALUE flow_node?)?
+  ///                   *** *
+  Event _parseFlowSequenceEntryMappingKey() {
+    var token = _scanner.peek();
+
+    if (token.type == TokenType.VALUE ||
+        token.type == TokenType.FLOW_ENTRY ||
+        token.type == TokenType.FLOW_SEQUENCE_END) {
+      // libyaml consumes the token here, but that seems like a bug, since it
+      // always causes [_parseFlowSequenceEntryMappingValue] to emit an empty
+      // scalar.
+
+      var start = token.span.start;
+      _state = _State.FLOW_SEQUENCE_ENTRY_MAPPING_VALUE;
+      return _processEmptyScalar(start);
+    } else {
+      _states.add(_State.FLOW_SEQUENCE_ENTRY_MAPPING_VALUE);
+      return _parseNode();
+    }
+  }
+
+  /// Parses the productions:
+  ///
+  ///     flow_sequence_entry  ::=
+  ///       flow_node | KEY flow_node? (VALUE flow_node?)?
+  ///                                   ***** *
+  Event _parseFlowSequenceEntryMappingValue() {
+    var token = _scanner.peek();
+
+    if (token.type == TokenType.VALUE) {
+      token = _scanner.advance();
+      if (token.type != TokenType.FLOW_ENTRY &&
+          token.type != TokenType.FLOW_SEQUENCE_END) {
+        _states.add(_State.FLOW_SEQUENCE_ENTRY_MAPPING_END);
+        return _parseNode();
+      }
+    }
+
+    _state = _State.FLOW_SEQUENCE_ENTRY_MAPPING_END;
+    return _processEmptyScalar(token.span.start);
+  }
+
+  /// Parses the productions:
+  ///
+  ///     flow_sequence_entry  ::=
+  ///       flow_node | KEY flow_node? (VALUE flow_node?)?
+  ///                                                   *
+  Event _parseFlowSequenceEntryMappingEnd() {
+    _state = _State.FLOW_SEQUENCE_ENTRY;
+    return new Event(EventType.MAPPING_END,
+        _scanner.peek().span.start.pointSpan());
+  }
+
+  /// Parses the productions:
+  ///
+  ///     flow_mapping         ::= FLOW-MAPPING-START
+  ///                              ******************
+  ///                              (flow_mapping_entry FLOW-ENTRY)*
+  ///                               *                  **********
+  ///                              flow_mapping_entry?
+  ///                              ******************
+  ///                              FLOW-MAPPING-END
+  ///                              ****************
+  ///     flow_mapping_entry   ::=
+  ///       flow_node | KEY flow_node? (VALUE flow_node?)?
+  ///       *           *** *
+  Event _parseFlowMappingKey({bool first: false}) {
+    if (first) _scanner.scan();
+    var token = _scanner.peek();
+
+    if (token.type != TokenType.FLOW_MAPPING_END) {
+      if (!first) {
+        if (token.type != TokenType.FLOW_ENTRY) {
+          throw new YamlException(
+              "While parsing a flow mapping, expected ',' or '}'.",
+              token.span.start.pointSpan());
+        }
+
+        token = _scanner.advance();
+      }
+
+      if (token.type == TokenType.KEY) {
+        token = _scanner.advance();
+        if (token.type != TokenType.VALUE &&
+            token.type != TokenType.FLOW_ENTRY &&
+            token.type != TokenType.FLOW_MAPPING_END) {
+          _states.add(_State.FLOW_MAPPING_VALUE);
+          return _parseNode();
+        } else {
+          _state = _State.FLOW_MAPPING_VALUE;
+          return _processEmptyScalar(token.span.start);
+        }
+      } else if (token.type != TokenType.FLOW_MAPPING_END) {
+        _states.add(_State.FLOW_MAPPING_EMPTY_VALUE);
+        return _parseNode();
+      }
+    }
+
+    _scanner.scan();
+    _state = _states.removeLast();
+    return new Event(EventType.MAPPING_END, token.span);
+  }
+
+  /// Parses the productions:
+  ///
+  ///     flow_mapping_entry   ::=
+  ///       flow_node | KEY flow_node? (VALUE flow_node?)?
+  ///                *                  ***** *
+  Event _parseFlowMappingValue({bool empty: false}) {
+    var token = _scanner.peek();
+
+    if (empty) {
+      _state = _State.FLOW_MAPPING_KEY;
+      return _processEmptyScalar(token.span.start);
+    }
+
+    if (token.type == TokenType.VALUE) {
+      token = _scanner.advance();
+      if (token.type != TokenType.FLOW_ENTRY &&
+          token.type != TokenType.FLOW_MAPPING_END) {
+        _states.add(_State.FLOW_MAPPING_KEY);
+        return _parseNode();
+      }
+    }
+
+    _state = _State.FLOW_MAPPING_KEY;
+    return _processEmptyScalar(token.span.start);
+  }
+
+  /// Generate an empty scalar event.
+  Event _processEmptyScalar(SourceLocation location) =>
+      new ScalarEvent(location.pointSpan(), '', ScalarStyle.PLAIN);
+
+  /// Parses directives.
+  Pair<VersionDirective, List<TagDirective>> _processDirectives() {
+    var token = _scanner.peek();
+
+    var versionDirective;
+    var tagDirectives = [];
+    var reservedDirectives = [];
+    while (token.type == TokenType.VERSION_DIRECTIVE ||
+           token.type == TokenType.TAG_DIRECTIVE) {
+      if (token is VersionDirectiveToken) {
+        if (versionDirective != null) {
+          throw new YamlException("Duplicate %YAML directive.", token.span);
+        }
+
+        if (token.major != 1 || token.minor == 0) {
+          throw new YamlException(
+              "Incompatible YAML document. This parser only supports YAML 1.1 "
+                "and 1.2.",
+              token.span);
+        } else if (token.minor > 2) {
+          // TODO(nweiz): Print to stderr when issue 6943 is fixed and dart:io
+          // is available.
+          warn("Warning: this parser only supports YAML 1.1 and 1.2.",
+              token.span);
+        }
+
+        versionDirective = new VersionDirective(token.major, token.minor);
+      } else if (token is TagDirectiveToken) {
+        var tagDirective = new TagDirective(token.handle, token.prefix);
+        _appendTagDirective(tagDirective, token.span);
+        tagDirectives.add(tagDirective);
+      }
+
+      token = _scanner.advance();
+    }
+    
+    _appendTagDirective(
+        new TagDirective("!", "!"),
+        token.span.start.pointSpan(),
+        allowDuplicates: true);
+    _appendTagDirective(
+        new TagDirective("!!", "tag:yaml.org,2002:"),
+        token.span.start.pointSpan(),
+        allowDuplicates: true);
+
+    return new Pair(versionDirective, tagDirectives);
+  }
+
+  /// Adds a tag directive to the directives stack.
+  void _appendTagDirective(TagDirective newDirective, FileSpan span,
+      {bool allowDuplicates: false}) {
+    if (_tagDirectives.containsKey(newDirective.handle)) {
+      if (allowDuplicates) return;
+      throw new YamlException("Duplicate %TAG directive.", span);
+    }
+
+    _tagDirectives[newDirective.handle] = newDirective;
+  }
+}
+
+/// The possible states for the parser.
+class _State {
+  /// Expect [TokenType.STREAM_START].
+  static const STREAM_START = const _State("STREAM_START");
+
+  /// Expect the beginning of an implicit document.
+  static const IMPLICIT_DOCUMENT_START =
+      const _State("IMPLICIT_DOCUMENT_START");
+
+  /// Expect [TokenType.DOCUMENT_START].
+  static const DOCUMENT_START = const _State("DOCUMENT_START");
+
+  /// Expect the content of a document.
+  static const DOCUMENT_CONTENT = const _State("DOCUMENT_CONTENT");
+
+  /// Expect [TokenType.DOCUMENT_END].
+  static const DOCUMENT_END = const _State("DOCUMENT_END");
+
+  /// Expect a block node.
+  static const BLOCK_NODE = const _State("BLOCK_NODE");
+
+  /// Expect a block node or indentless sequence.
+  static const BLOCK_NODE_OR_INDENTLESS_SEQUENCE =
+      const _State("BLOCK_NODE_OR_INDENTLESS_SEQUENCE");
+
+  /// Expect a flow node.
+  static const FLOW_NODE = const _State("FLOW_NODE");
+
+  /// Expect the first entry of a block sequence.
+  static const BLOCK_SEQUENCE_FIRST_ENTRY =
+      const _State("BLOCK_SEQUENCE_FIRST_ENTRY");
+
+  /// Expect an entry of a block sequence.
+  static const BLOCK_SEQUENCE_ENTRY = const _State("BLOCK_SEQUENCE_ENTRY");
+
+  /// Expect an entry of an indentless sequence.
+  static const INDENTLESS_SEQUENCE_ENTRY =
+      const _State("INDENTLESS_SEQUENCE_ENTRY");
+
+  /// Expect the first key of a block mapping.
+  static const BLOCK_MAPPING_FIRST_KEY =
+      const _State("BLOCK_MAPPING_FIRST_KEY");
+
+  /// Expect a block mapping key.
+  static const BLOCK_MAPPING_KEY = const _State("BLOCK_MAPPING_KEY");
+
+  /// Expect a block mapping value.
+  static const BLOCK_MAPPING_VALUE = const _State("BLOCK_MAPPING_VALUE");
+
+  /// Expect the first entry of a flow sequence.
+  static const FLOW_SEQUENCE_FIRST_ENTRY =
+      const _State("FLOW_SEQUENCE_FIRST_ENTRY");
+
+  /// Expect an entry of a flow sequence.
+  static const FLOW_SEQUENCE_ENTRY = const _State("FLOW_SEQUENCE_ENTRY");
+
+  /// Expect a key of an ordered mapping.
+  static const FLOW_SEQUENCE_ENTRY_MAPPING_KEY =
+      const _State("FLOW_SEQUENCE_ENTRY_MAPPING_KEY");
+
+  /// Expect a value of an ordered mapping.
+  static const FLOW_SEQUENCE_ENTRY_MAPPING_VALUE =
+      const _State("FLOW_SEQUENCE_ENTRY_MAPPING_VALUE");
+
+  /// Expect the and of an ordered mapping entry.
+  static const FLOW_SEQUENCE_ENTRY_MAPPING_END =
+      const _State("FLOW_SEQUENCE_ENTRY_MAPPING_END");
+
+  /// Expect the first key of a flow mapping.
+  static const FLOW_MAPPING_FIRST_KEY = const _State("FLOW_MAPPING_FIRST_KEY");
+
+  /// Expect a key of a flow mapping.
+  static const FLOW_MAPPING_KEY = const _State("FLOW_MAPPING_KEY");
+
+  /// Expect a value of a flow mapping.
+  static const FLOW_MAPPING_VALUE = const _State("FLOW_MAPPING_VALUE");
+
+  /// Expect an empty value of a flow mapping.
+  static const FLOW_MAPPING_EMPTY_VALUE =
+      const _State("FLOW_MAPPING_EMPTY_VALUE");
+
+  /// Expect nothing.
+  static const END = const _State("END");
+
+  final String name;
+
+  const _State(this.name);
+
+  String toString() => name;
+}
diff --git a/yaml/lib/src/scanner.dart b/yaml/lib/src/scanner.dart
new file mode 100644
index 0000000..e1b578e
--- /dev/null
+++ b/yaml/lib/src/scanner.dart
@@ -0,0 +1,1667 @@
+// 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 yaml.scanner;
+
+import 'package:collection/collection.dart';
+import 'package:string_scanner/string_scanner.dart';
+import 'package:source_span/source_span.dart';
+
+import 'style.dart';
+import 'token.dart';
+import 'utils.dart';
+import 'yaml_exception.dart';
+
+/// A scanner that reads a string of Unicode characters and emits [Token]s.
+///
+/// This is based on the libyaml scanner, available at
+/// https://github.com/yaml/libyaml/blob/master/src/scanner.c. The license for
+/// that is available in ../../libyaml-license.txt.
+class Scanner {
+  static const TAB = 0x9;
+  static const LF = 0xA;
+  static const CR = 0xD;
+  static const SP = 0x20;
+  static const DOLLAR = 0x24;
+  static const LEFT_PAREN = 0x28;
+  static const RIGHT_PAREN = 0x29;
+  static const PLUS = 0x2B;
+  static const COMMA = 0x2C;
+  static const HYPHEN = 0x2D;
+  static const PERIOD = 0x2E;
+  static const QUESTION = 0x3F;
+  static const COLON = 0x3A;
+  static const SEMICOLON = 0x3B;
+  static const EQUALS = 0x3D;
+  static const LEFT_SQUARE = 0x5B;
+  static const RIGHT_SQUARE = 0x5D;
+  static const LEFT_CURLY = 0x7B;
+  static const RIGHT_CURLY = 0x7D;
+  static const HASH = 0x23;
+  static const AMPERSAND = 0x26;
+  static const ASTERISK = 0x2A;
+  static const EXCLAMATION = 0x21;
+  static const VERTICAL_BAR = 0x7C;
+  static const LEFT_ANGLE = 0x3C;
+  static const RIGHT_ANGLE = 0x3E;
+  static const SINGLE_QUOTE = 0x27;
+  static const DOUBLE_QUOTE = 0x22;
+  static const PERCENT = 0x25;
+  static const AT = 0x40;
+  static const GRAVE_ACCENT = 0x60;
+  static const TILDE = 0x7E;
+
+  static const NULL = 0x0;
+  static const BELL = 0x7;
+  static const BACKSPACE = 0x8;
+  static const VERTICAL_TAB = 0xB;
+  static const FORM_FEED = 0xC;
+  static const ESCAPE = 0x1B;
+  static const SLASH = 0x2F;
+  static const BACKSLASH = 0x5C;
+  static const UNDERSCORE = 0x5F;
+  static const NEL = 0x85;
+  static const NBSP = 0xA0;
+  static const LINE_SEPARATOR = 0x2028;
+  static const PARAGRAPH_SEPARATOR = 0x2029;
+  static const BOM = 0xFEFF;
+
+  static const NUMBER_0 = 0x30;
+  static const NUMBER_9 = 0x39;
+
+  static const LETTER_A = 0x61;
+  static const LETTER_B = 0x62;
+  static const LETTER_E = 0x65;
+  static const LETTER_F = 0x66;
+  static const LETTER_N = 0x6E;
+  static const LETTER_R = 0x72;
+  static const LETTER_T = 0x74;
+  static const LETTER_U = 0x75;
+  static const LETTER_V = 0x76;
+  static const LETTER_X = 0x78;
+  static const LETTER_Z = 0x7A;
+
+  static const LETTER_CAP_A = 0x41;
+  static const LETTER_CAP_F = 0x46;
+  static const LETTER_CAP_L = 0x4C;
+  static const LETTER_CAP_N = 0x4E;
+  static const LETTER_CAP_P = 0x50;
+  static const LETTER_CAP_U = 0x55;
+  static const LETTER_CAP_X = 0x58;
+  static const LETTER_CAP_Z = 0x5A;
+
+  /// The underlying [SpanScanner] used to read characters from the source text.
+  ///
+  /// This is also used to track line and column information and to generate
+  /// [SourceSpan]s.
+  final SpanScanner _scanner;
+
+  /// Whether this scanner has produced a [TokenType.STREAM_START] token
+  /// indicating the beginning of the YAML stream.
+  var _streamStartProduced = false;
+
+  /// Whether this scanner has produced a [TokenType.STREAM_END] token
+  /// indicating the end of the YAML stream.
+  var _streamEndProduced = false;
+
+  /// The queue of tokens yet to be emitted.
+  ///
+  /// These are queued up in advance so that [TokenType.KEY] tokens can be
+  /// inserted once the scanner determines that a series of tokens represents a
+  /// mapping key.
+  final _tokens = new QueueList<Token>();
+
+  /// The number of tokens that have been emitted.
+  ///
+  /// This doesn't count tokens in [tokens].
+  var _tokensParsed = 0;
+
+  /// Whether the next token in [_tokens] is ready to be returned.
+  ///
+  /// It might not be ready if there may still be a [TokenType.KEY] inserted
+  /// before it.
+  var _tokenAvailable = false;
+
+  /// The stack of indent levels for the current nested block contexts.
+  ///
+  /// The YAML spec specifies that the initial indentation level is -1 spaces.
+  final _indents = <int>[-1];
+
+  /// Whether a simple key is allowed in this context.
+  ///
+  /// A simple key refers to any mapping key that doesn't have an explicit "?".
+  var _simpleKeyAllowed = true;
+
+  /// The stack of potential simple keys for each level of flow nesting.
+  ///
+  /// Entries in this list may be `null`, indicating that there is no valid
+  /// simple key for the associated level of nesting.
+  /// 
+  /// When a ":" is parsed and there's a simple key available, a [TokenType.KEY]
+  /// token is inserted in [_tokens] before that key's token. This allows the
+  /// parser to tell that the key is intended to be a mapping key.
+  final _simpleKeys = <_SimpleKey>[null];
+
+  /// The current indentation level.
+  int get _indent => _indents.last;
+
+  /// Whether the scanner's currently positioned in a block-level structure (as
+  /// opposed to flow-level).
+  bool get _inBlockContext => _simpleKeys.length == 1;
+
+  /// Whether the current character is a line break or the end of the source.
+  bool get _isBreakOrEnd => _scanner.isDone || _isBreak;
+
+  /// Whether the current character is a line break.
+  bool get _isBreak => _isBreakAt(0);
+
+  /// Whether the current character is whitespace or the end of the source.
+  bool get _isBlankOrEnd => _isBlankOrEndAt(0);
+
+  /// Whether the current character is whitespace.
+  bool get _isBlank => _isBlankAt(0);
+
+  /// Whether the current character is a valid tag name character.
+  ///
+  /// See http://yaml.org/spec/1.2/spec.html#ns-tag-name.
+  bool get _isTagChar {
+    var char = _scanner.peekChar();
+    if (char == null) return false;
+    switch (char) {
+      case HYPHEN:
+      case SEMICOLON:
+      case SLASH:
+      case COLON:
+      case AT:
+      case AMPERSAND:
+      case EQUALS:
+      case PLUS:
+      case DOLLAR:
+      case PERIOD:
+      case TILDE:
+      case QUESTION:
+      case ASTERISK:
+      case SINGLE_QUOTE:
+      case LEFT_PAREN:
+      case RIGHT_PAREN:
+      case PERCENT:
+        return true;
+      default:
+        return (char >= NUMBER_0 && char <= NUMBER_9) ||
+               (char >= LETTER_A && char <= LETTER_Z) ||
+               (char >= LETTER_CAP_A && char <= LETTER_CAP_Z);
+    }
+  }
+
+  /// Whether the current character is a valid anchor name character.
+  ///
+  /// See http://yaml.org/spec/1.2/spec.html#ns-anchor-name.
+  bool get _isAnchorChar {
+    if (!_isNonSpace) return false;
+
+    switch (_scanner.peekChar()) {
+      case COMMA:
+      case LEFT_SQUARE:
+      case RIGHT_SQUARE:
+      case LEFT_CURLY:
+      case RIGHT_CURLY:
+        return false;
+      default:
+        return true;
+    }
+  }
+
+  /// Whether the character at the current position is a decimal digit.
+  bool get _isDigit {
+    var char = _scanner.peekChar();
+    return char != null && (char >= NUMBER_0 && char <= NUMBER_9);
+  }
+
+  /// Whether the character at the current position is a hexidecimal
+  /// digit.
+  bool get _isHex {
+    var char = _scanner.peekChar();
+    if (char == null) return false;
+    return (char >= NUMBER_0 && char <= NUMBER_9) ||
+           (char >= LETTER_A && char <= LETTER_F) ||
+           (char >= LETTER_CAP_A && char <= LETTER_CAP_F);
+  }
+
+  /// Whether the character at the current position is a plain character.
+  ///
+  /// See http://yaml.org/spec/1.2/spec.html#ns-plain-char(c).
+  bool get _isPlainChar => _isPlainCharAt(0);
+
+  /// Whether the character at the current position is a printable character
+  /// other than a line break or byte-order mark.
+  ///
+  /// See http://yaml.org/spec/1.2/spec.html#nb-char.
+  bool get _isNonBreak {
+    var char = _scanner.peekChar();
+    if (char == null) return false;
+    switch (char) {
+      case LF:
+      case CR:
+      case BOM:
+        return false;
+      case TAB:
+      case NEL:
+        return true;
+      default:
+        return (char >= 0x00020 && char <= 0x00007E) ||
+               (char >= 0x000A0 && char <= 0x00D7FF) ||
+               (char >= 0x0E000 && char <= 0x00FFFD) ||
+               (char >= 0x10000 && char <= 0x10FFFF);
+    }
+  }
+
+  /// Whether the character at the current position is a printable character
+  /// other than whitespace.
+  ///
+  /// See http://yaml.org/spec/1.2/spec.html#nb-char.
+  bool get _isNonSpace {
+    var char = _scanner.peekChar();
+    if (char == null) return false;
+    switch (char) {
+      case LF:
+      case CR:
+      case BOM:
+      case SP:
+        return false;
+      case NEL:
+        return true;
+      default:
+        return (char >= 0x00020 && char <= 0x00007E) ||
+               (char >= 0x000A0 && char <= 0x00D7FF) ||
+               (char >= 0x0E000 && char <= 0x00FFFD) ||
+               (char >= 0x10000 && char <= 0x10FFFF);
+    }
+  }
+
+  /// Returns Whether or not the current character begins a documentation
+  /// indicator.
+  ///
+  /// If so, this sets the scanner's last match to that indicator.
+  bool get _isDocumentIndicator {
+    return _scanner.column == 0 && _isBlankOrEndAt(3) &&
+        (_scanner.matches('---') || _scanner.matches('...'));
+  }
+
+  /// Creates a scanner that scans [source].
+  ///
+  /// [sourceUrl] can be a String or a [Uri].
+  Scanner(String source, {sourceUrl})
+      : _scanner = new SpanScanner(source, sourceUrl: sourceUrl);
+
+  /// Consumes and returns the next token.
+  Token scan() {
+    if (_streamEndProduced) throw new StateError("Out of tokens.");
+    if (!_tokenAvailable) _fetchMoreTokens();
+
+    var token = _tokens.removeFirst();
+    _tokenAvailable = false;
+    _tokensParsed++;
+    _streamEndProduced = token is Token &&
+        token.type == TokenType.STREAM_END;
+    return token;
+  }
+
+  /// Consumes the next token and returns the one after that.
+  Token advance() {
+    scan();
+    return peek();
+  }
+
+  /// Returns the next token without consuming it.
+  Token peek() {
+    if (_streamEndProduced) return null;
+    if (!_tokenAvailable) _fetchMoreTokens();
+    return _tokens.first;
+  }
+
+  /// Ensures that [_tokens] contains at least one token which can be returned.
+  void _fetchMoreTokens() {
+    while (true) {
+      if (_tokens.isNotEmpty) {
+        _staleSimpleKeys();
+
+        // If the current token could be a simple key, we need to scan more
+        // tokens until we determine whether it is or not. Otherwise we might
+        // not emit the `KEY` token before we emit the value of the key.
+        if (!_simpleKeys.any((key) =>
+            key != null && key.tokenNumber == _tokensParsed)) {
+          break;
+        }
+      }
+
+      _fetchNextToken();
+    }
+    _tokenAvailable = true;
+  }
+
+  /// The dispatcher for token fetchers.
+  void _fetchNextToken() {
+    if (!_streamStartProduced) {
+      _fetchStreamStart();
+      return;
+    }
+
+    _scanToNextToken();
+    _staleSimpleKeys();
+    _unrollIndent(_scanner.column);
+
+    if (_scanner.isDone) {
+      _fetchStreamEnd();
+      return;
+    }
+
+    if (_scanner.column == 0) {
+      if (_scanner.peekChar() == PERCENT) {
+        _fetchDirective();
+        return;
+      }
+
+      if (_isBlankOrEndAt(3)) {
+        if (_scanner.matches('---')) {
+          _fetchDocumentIndicator(TokenType.DOCUMENT_START);
+          return;
+        }
+
+        if (_scanner.matches('...')) {
+          _fetchDocumentIndicator(TokenType.DOCUMENT_END);
+          return;
+        }
+      }
+    }
+
+    switch (_scanner.peekChar()) {
+      case LEFT_SQUARE:
+        _fetchFlowCollectionStart(TokenType.FLOW_SEQUENCE_START);
+        return;
+      case LEFT_CURLY:
+        _fetchFlowCollectionStart(TokenType.FLOW_MAPPING_START);
+        return;
+      case RIGHT_SQUARE:
+        _fetchFlowCollectionEnd(TokenType.FLOW_SEQUENCE_END);
+        return;
+      case RIGHT_CURLY:
+        _fetchFlowCollectionEnd(TokenType.FLOW_MAPPING_END);
+        return;
+      case COMMA:
+        _fetchFlowEntry();
+        return;
+      case ASTERISK:
+        _fetchAnchor(anchor: false);
+        return;
+      case AMPERSAND:
+        _fetchAnchor(anchor: true);
+        return;
+      case EXCLAMATION:
+        _fetchTag();
+        return;
+      case SINGLE_QUOTE:
+        _fetchFlowScalar(singleQuote: true);
+        return;
+      case DOUBLE_QUOTE:
+        _fetchFlowScalar(singleQuote: false);
+        return;
+      case VERTICAL_BAR:
+        if (!_inBlockContext) _invalidScalarCharacter();
+        _fetchBlockScalar(literal: true);
+        return;
+      case RIGHT_ANGLE:
+        if (!_inBlockContext) _invalidScalarCharacter();
+        _fetchBlockScalar(literal: false);
+        return;
+      case PERCENT:
+      case AT:
+      case GRAVE_ACCENT:
+        _invalidScalarCharacter();
+        return;
+
+      // These characters may sometimes begin plain scalars.
+      case HYPHEN:
+        if (_isPlainCharAt(1)) {
+          _fetchPlainScalar();
+        } else {
+          _fetchBlockEntry();
+        }
+        return;
+      case QUESTION:
+        if (_isPlainCharAt(1)) {
+          _fetchPlainScalar();
+        } else {
+          _fetchKey();
+        }
+        return;
+      case COLON:
+        if (!_inBlockContext && _tokens.isNotEmpty) {
+          // If a colon follows a "JSON-like" value (an explicit map or list, or
+          // a quoted string) it isn't required to have whitespace after it
+          // since it unambiguously describes a map.
+          var token = _tokens.last;
+          if (token.type == TokenType.FLOW_SEQUENCE_END ||
+              token.type == TokenType.FLOW_MAPPING_END ||
+              (token.type == TokenType.SCALAR && token.style.isQuoted)) {
+            _fetchValue();
+            return;
+          }
+        }
+
+        if (_isPlainCharAt(1)) {
+          _fetchPlainScalar();
+        } else {
+          _fetchValue();
+        }
+        return;
+      default:
+        if (!_isNonBreak) _invalidScalarCharacter();
+
+        _fetchPlainScalar();
+        return;
+    }
+
+    throw 'Inaccessible';
+  }
+
+  /// Throws an error about a disallowed character.
+  void _invalidScalarCharacter() =>
+      _scanner.error("Unexpected character.", length: 1);
+
+  /// Checks the list of potential simple keys and remove the positions that
+  /// cannot contain simple keys anymore.
+  void _staleSimpleKeys() {
+    for (var i = 0; i < _simpleKeys.length; i++) {
+      var key = _simpleKeys[i];
+      if (key == null) continue;
+
+      // libyaml requires that all simple keys be a single line and no longer
+      // than 1024 characters. However, in section 7.4.2 of the spec
+      // (http://yaml.org/spec/1.2/spec.html#id2790832), these restrictions are
+      // only applied when the curly braces are omitted. It's difficult to
+      // retain enough context to know which keys need to have the restriction
+      // placed on them, so for now we go the other direction and allow
+      // everything but multiline simple keys in a block context.
+      if (!_inBlockContext) continue;
+
+      if (key.location.line == _scanner.line) continue;
+
+      if (key.required) {
+        throw new YamlException("Expected ':'.", _scanner.emptySpan);
+      }
+
+      _simpleKeys[i] = null;
+    }
+  }
+
+  /// Checks if a simple key may start at the current position and saves it if
+  /// so.
+  void _saveSimpleKey() {
+    // A simple key is required at the current position if the scanner is in the
+    // block context and the current column coincides with the indentation
+    // level.
+    var required = _inBlockContext && _indent == _scanner.column;
+
+    // A simple key is required only when it is the first token in the current
+    // line. Therefore it is always allowed. But we add a check anyway.
+    assert(_simpleKeyAllowed || !required);
+
+    if (!_simpleKeyAllowed) return;
+
+    // If the current position may start a simple key, save it.
+    _removeSimpleKey();
+    _simpleKeys[_simpleKeys.length - 1] = new _SimpleKey(
+        _tokensParsed + _tokens.length,
+        _scanner.location,
+        required: required);
+  }
+
+  /// Removes a potential simple key at the current flow level.
+  void _removeSimpleKey() {
+    var key = _simpleKeys.last;
+    if (key != null && key.required) {
+      throw new YamlException("Could not find expected ':' for simple key.",
+          key.location.pointSpan());
+    }
+
+    _simpleKeys[_simpleKeys.length - 1] = null;
+  }
+
+  /// Increases the flow level and resizes the simple key list.
+  void _increaseFlowLevel() {
+    _simpleKeys.add(null);
+  }
+
+  /// Decreases the flow level.
+  void _decreaseFlowLevel() {
+    if (_inBlockContext) return;
+    _simpleKeys.removeLast();
+  }
+
+  /// Pushes the current indentation level to the stack and sets the new level
+  /// if [column] is greater than [_indent].
+  ///
+  /// If it is, appends or inserts the specified token into [_tokens]. If
+  /// [tokenNumber] is provided, the corresponding token will be replaced;
+  /// otherwise, the token will be added at the end.
+  void _rollIndent(int column, TokenType type, SourceLocation location,
+      {int tokenNumber}) {
+    if (!_inBlockContext) return;
+    if (_indent != -1 && _indent >= column) return;
+
+    // Push the current indentation level to the stack and set the new
+    // indentation level.
+    _indents.add(column);
+
+    // Create a token and insert it into the queue.
+    var token = new Token(type, location.pointSpan());
+    if (tokenNumber == null) {
+      _tokens.add(token);
+    } else {
+      _tokens.insert(tokenNumber - _tokensParsed, token);
+    }
+  }
+
+  /// Pops indentation levels from [_indents] until the current level becomes
+  /// less than or equal to [column].
+  ///
+  /// For each indentation level, appends a [TokenType.BLOCK_END] token.
+  void _unrollIndent(int column) {
+    if (!_inBlockContext) return;
+
+    while (_indent > column) {
+      _tokens.add(new Token(TokenType.BLOCK_END, _scanner.emptySpan));
+      _indents.removeLast();
+    }
+  }
+
+  /// Pops indentation levels from [_indents] until the current level resets to
+  /// -1.
+  ///
+  /// For each indentation level, appends a [TokenType.BLOCK_END] token.
+  void _resetIndent() => _unrollIndent(-1);
+
+  /// Produces a [TokenType.STREAM_START] token.
+  void _fetchStreamStart() {
+    // Much of libyaml's initialization logic here is done in variable
+    // initializers instead.
+    _streamStartProduced = true;
+    _tokens.add(new Token(TokenType.STREAM_START, _scanner.emptySpan));
+  }
+
+  /// Produces a [TokenType.STREAM_END] token.
+  void _fetchStreamEnd() {
+    _resetIndent();
+    _removeSimpleKey();
+    _simpleKeyAllowed = false;
+    _tokens.add(new Token(TokenType.STREAM_END, _scanner.emptySpan));
+  }
+
+  /// Produces a [TokenType.VERSION_DIRECTIVE] or [TokenType.TAG_DIRECTIVE]
+  /// token.
+  void _fetchDirective() {
+    _resetIndent();
+    _removeSimpleKey();
+    _simpleKeyAllowed = false;
+    var directive = _scanDirective();
+    if (directive != null) _tokens.add(directive);
+  }
+
+  /// Produces a [TokenType.DOCUMENT_START] or [TokenType.DOCUMENT_END] token.
+  void _fetchDocumentIndicator(TokenType type) {
+    _resetIndent();
+    _removeSimpleKey();
+    _simpleKeyAllowed = false;
+
+    // Consume the indicator token.
+    var start = _scanner.state;
+    _scanner.readChar();
+    _scanner.readChar();
+    _scanner.readChar();
+
+    _tokens.add(new Token(type, _scanner.spanFrom(start)));
+  }
+
+  /// Produces a [TokenType.FLOW_SEQUENCE_START] or
+  /// [TokenType.FLOW_MAPPING_START] token.
+  void _fetchFlowCollectionStart(TokenType type) {
+    _saveSimpleKey();
+    _increaseFlowLevel();
+    _simpleKeyAllowed = true;
+    _addCharToken(type);
+  }
+
+  /// Produces a [TokenType.FLOW_SEQUENCE_END] or [TokenType.FLOW_MAPPING_END]
+  /// token.
+  void _fetchFlowCollectionEnd(TokenType type) {
+    _removeSimpleKey();
+    _decreaseFlowLevel();
+    _simpleKeyAllowed = false;
+    _addCharToken(type);
+  }
+
+  /// Produces a [TokenType.FLOW_ENTRY] token.
+  void _fetchFlowEntry() {
+    _removeSimpleKey();
+    _simpleKeyAllowed = true;
+    _addCharToken(TokenType.FLOW_ENTRY);
+  }
+
+  /// Produces a [TokenType.BLOCK_ENTRY] token.
+  void _fetchBlockEntry() {
+    if (_inBlockContext) {
+      if (!_simpleKeyAllowed) {
+        throw new YamlException(
+            "Block sequence entries are not allowed in this context.",
+            _scanner.emptySpan);
+      }
+
+      _rollIndent(
+          _scanner.column,
+          TokenType.BLOCK_SEQUENCE_START,
+          _scanner.emptySpan.start);
+    } else {
+      // It is an error for the '-' indicator to occur in the flow context, but
+      // we let the Parser detect and report it because it's able to point to
+      // the context.
+    }
+
+    _removeSimpleKey();
+    _simpleKeyAllowed = true;
+    _addCharToken(TokenType.BLOCK_ENTRY);
+  }
+
+  /// Produces the [TokenType.KEY] token.
+  void _fetchKey() {
+    if (_inBlockContext) {
+      if (!_simpleKeyAllowed) {
+        throw new YamlException("Mapping keys are not allowed in this context.",
+            _scanner.emptySpan);
+      }
+
+      _rollIndent(
+          _scanner.column,
+          TokenType.BLOCK_MAPPING_START,
+          _scanner.emptySpan.start);
+    }
+
+    // Simple keys are allowed after `?` in a block context.
+    _simpleKeyAllowed = _inBlockContext;
+    _addCharToken(TokenType.KEY);
+  }
+
+  /// Produces the [TokenType.VALUE] token.
+  void _fetchValue() {
+    var simpleKey = _simpleKeys.last;
+    if (simpleKey != null) {
+      // Add a [TokenType.KEY] directive before the first token of the simple
+      // key so the parser knows that it's part of a key/value pair.
+      _tokens.insert(simpleKey.tokenNumber - _tokensParsed,
+          new Token(TokenType.KEY, simpleKey.location.pointSpan()));
+
+      // In the block context, we may need to add the
+      // [TokenType.BLOCK_MAPPING_START] token.
+      _rollIndent(
+          simpleKey.location.column,
+          TokenType.BLOCK_MAPPING_START,
+          simpleKey.location,
+          tokenNumber: simpleKey.tokenNumber);
+
+      // Remove the simple key.
+      _simpleKeys[_simpleKeys.length - 1] = null;
+
+      // A simple key cannot follow another simple key.
+      _simpleKeyAllowed = false;
+    } else if (_inBlockContext) {
+      // If we're here, we've found the ':' indicator following a complex key.
+
+      if (!_simpleKeyAllowed) {
+        throw new YamlException(
+            "Mapping values are not allowed in this context.",
+            _scanner.emptySpan);
+      }
+
+      _rollIndent(
+          _scanner.column,
+          TokenType.BLOCK_MAPPING_START,
+          _scanner.location);
+      _simpleKeyAllowed = true;
+    } else if (_simpleKeyAllowed) {
+      // If we're here, we've found the ':' indicator with an empty key. This
+      // behavior differs from libyaml, which disallows empty implicit keys.
+      _simpleKeyAllowed = false;
+      _addCharToken(TokenType.KEY);
+    }
+
+    _addCharToken(TokenType.VALUE);
+  }
+
+  /// Adds a token with [type] to [_tokens].
+  ///
+  /// The span of the new token is the current character.
+  void _addCharToken(TokenType type) {
+    var start = _scanner.state;
+    _scanner.readChar();
+    _tokens.add(new Token(type, _scanner.spanFrom(start)));
+  }
+
+  /// Produces a [TokenType.ALIAS] or [TokenType.ANCHOR] token.
+  void _fetchAnchor({bool anchor: true}) {
+    _saveSimpleKey();
+    _simpleKeyAllowed = false;
+    _tokens.add(_scanAnchor(anchor: anchor));
+  }
+
+  /// Produces a [TokenType.TAG] token.
+  void _fetchTag() {
+    _saveSimpleKey();
+    _simpleKeyAllowed = false;
+    _tokens.add(_scanTag());
+  }
+
+  /// Produces a [TokenType.SCALAR] token with style [ScalarStyle.LITERAL] or
+  /// [ScalarStyle.FOLDED].
+  void _fetchBlockScalar({bool literal: false}) {
+    _removeSimpleKey();
+    _simpleKeyAllowed = true;
+    _tokens.add(_scanBlockScalar(literal: literal));
+  }
+
+  /// Produces a [TokenType.SCALAR] token with style [ScalarStyle.SINGLE_QUOTED]
+  /// or [ScalarStyle.DOUBLE_QUOTED].
+  void _fetchFlowScalar({bool singleQuote: false}) {
+    _saveSimpleKey();
+    _simpleKeyAllowed = false;
+    _tokens.add(_scanFlowScalar(singleQuote: singleQuote));
+  }
+
+  /// Produces a [TokenType.SCALAR] token with style [ScalarStyle.PLAIN].
+  void _fetchPlainScalar() {
+    _saveSimpleKey();
+    _simpleKeyAllowed = false;
+    _tokens.add(_scanPlainScalar());
+  }
+
+  /// Eats whitespace and comments until the next token is found.
+  void _scanToNextToken() {
+    var afterLineBreak = false;
+    while (true) {
+      // Allow the BOM to start a line.
+      if (_scanner.column == 0) _scanner.scan("\uFEFF");
+
+      // Eat whitespace.
+      //
+      // libyaml disallows tabs after "-", "?", or ":", but the spec allows
+      // them. See section 6.2: http://yaml.org/spec/1.2/spec.html#id2778241.
+      while (_scanner.peekChar() == SP ||
+             ((!_inBlockContext || !afterLineBreak) &&
+              _scanner.peekChar() == TAB)) {
+        _scanner.readChar();
+      }
+
+      if (_scanner.peekChar() == TAB) {
+        _scanner.error("Tab characters are not allowed as indentation.",
+            length: 1);
+      }
+
+      // Eat a comment until a line break.
+      _skipComment();
+
+      // If we're at a line break, eat it.
+      if (_isBreak) {
+        _skipLine();
+
+        // In the block context, a new line may start a simple key.
+        if (_inBlockContext) _simpleKeyAllowed = true;
+        afterLineBreak = true;
+      } else {
+        // Otherwise we've found a token.
+        break;
+      }
+    }
+  }
+
+  /// Scans a [TokenType.YAML_DIRECTIVE] or [TokenType.TAG_DIRECTIVE] token.
+  ///
+  ///     %YAML    1.2    # a comment \n
+  ///     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  ///     %TAG    !yaml!  tag:yaml.org,2002:  \n
+  ///     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
+  Token _scanDirective() {
+    var start = _scanner.state;
+
+    // Eat '%'.
+    _scanner.readChar();
+
+    var token;
+    var name = _scanDirectiveName();
+    if (name == "YAML") {
+      token = _scanVersionDirectiveValue(start);
+    } else if (name == "TAG") {
+      token = _scanTagDirectiveValue(start);
+    } else {
+      warn("Warning: unknown directive.", _scanner.spanFrom(start));
+
+      // libyaml doesn't support unknown directives, but the spec says to ignore
+      // them and warn: http://yaml.org/spec/1.2/spec.html#id2781147.
+      while (!_isBreakOrEnd) {
+        _scanner.readChar();
+      }
+
+      return null;
+    }
+
+    // Eat the rest of the line, including any comments.
+    _skipBlanks();
+    _skipComment();
+
+    if (!_isBreakOrEnd) {
+      throw new YamlException(
+          "Expected comment or line break after directive.",
+          _scanner.spanFrom(start));
+    }
+
+    _skipLine();
+    return token;
+  }
+
+  /// Scans a directive name.
+  ///
+  ///      %YAML   1.2     # a comment \n
+  ///       ^^^^
+  ///      %TAG    !yaml!  tag:yaml.org,2002:  \n
+  ///       ^^^
+  String _scanDirectiveName() {
+    // libyaml only allows word characters in directive names, but the spec
+    // disagrees: http://yaml.org/spec/1.2/spec.html#ns-directive-name.
+    var start = _scanner.position;
+    while (_isNonSpace) {
+      _scanner.readChar();
+    }
+
+    var name = _scanner.substring(start);
+    if (name.isEmpty) {
+      throw new YamlException("Expected directive name.", _scanner.emptySpan);
+    } else if (!_isBlankOrEnd) {
+      throw new YamlException(
+          "Unexpected character in directive name.", _scanner.emptySpan);
+    }
+
+    return name;
+  }
+
+  /// Scans the value of a version directive.
+  ///
+  ///      %YAML   1.2     # a comment \n
+  ///           ^^^^^^
+  Token _scanVersionDirectiveValue(LineScannerState start) {
+    _skipBlanks();
+
+    var major = _scanVersionDirectiveNumber();
+    _scanner.expect('.');
+    var minor = _scanVersionDirectiveNumber();
+
+    return new VersionDirectiveToken(_scanner.spanFrom(start), major, minor);
+  }
+
+  /// Scans the version number of a version directive.
+  ///
+  ///      %YAML   1.2     # a comment \n
+  ///              ^
+  ///      %YAML   1.2     # a comment \n
+  ///                ^
+  int _scanVersionDirectiveNumber() {
+    var start = _scanner.position;
+    while (_isDigit) {
+      _scanner.readChar();
+    }
+
+    var number = _scanner.substring(start);
+    if (number.isEmpty) {
+      throw new YamlException("Expected version number.", _scanner.emptySpan);
+    }
+
+    return int.parse(number);
+  }
+
+  /// Scans the value of a tag directive.
+  ///
+  ///      %TAG    !yaml!  tag:yaml.org,2002:  \n
+  ///          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  Token _scanTagDirectiveValue(LineScannerState start) {
+    _skipBlanks();
+
+    var handle = _scanTagHandle(directive: true);
+    if (!_isBlank) {
+      throw new YamlException("Expected whitespace.", _scanner.emptySpan);
+    }
+
+    _skipBlanks();
+
+    var prefix = _scanTagUri();
+    if (!_isBlankOrEnd) {
+      throw new YamlException("Expected whitespace.", _scanner.emptySpan);
+    }
+
+    return new TagDirectiveToken(_scanner.spanFrom(start), handle, prefix);
+  }
+
+  /// Scans a [TokenType.ANCHOR] token.
+  Token _scanAnchor({bool anchor: true}) {
+    var start = _scanner.state;
+
+    // Eat the indicator character.
+    _scanner.readChar();
+
+    // libyaml only allows word characters in anchor names, but the spec
+    // disagrees: http://yaml.org/spec/1.2/spec.html#ns-anchor-char.
+    var startPosition = _scanner.position;
+    while (_isAnchorChar) {
+      _scanner.readChar();
+    }
+    var name = _scanner.substring(startPosition);
+
+    var next = _scanner.peekChar();
+    if (name.isEmpty ||
+        (!_isBlankOrEnd  && next != QUESTION     && next != COLON &&
+         next != COMMA   && next != RIGHT_SQUARE && next != RIGHT_CURLY &&
+         next != PERCENT && next != AT           && next != GRAVE_ACCENT)) {
+      throw new YamlException("Expected alphanumeric character.",
+          _scanner.emptySpan);
+    }
+
+    if (anchor) {
+      return new AnchorToken(_scanner.spanFrom(start), name);
+    } else {
+      return new AliasToken(_scanner.spanFrom(start), name);
+    }
+  }
+
+  /// Scans a [TokenType.TAG] token.
+  Token _scanTag() {
+    var handle;
+    var suffix;
+    var start = _scanner.state;
+
+    // Check if the tag is in the canonical form.
+    if (_scanner.peekChar(1) == LEFT_ANGLE) {
+      // Eat '!<'.
+      _scanner.readChar();
+      _scanner.readChar();
+
+      handle = '';
+      suffix = _scanTagUri();
+
+      _scanner.expect('>');
+    } else {
+      // The tag has either the '!suffix' or the '!handle!suffix' form.
+
+      // First, try to scan a handle.
+      handle = _scanTagHandle();
+
+      if (handle.length > 1 && handle.startsWith('!') && handle.endsWith('!')) {
+        suffix = _scanTagUri(flowSeparators: false);
+      } else {
+        suffix = _scanTagUri(head: handle, flowSeparators: false);
+
+        // There was no explicit handle.
+        if (suffix.isEmpty) {
+          // This is the special '!' tag.
+          handle = null;
+          suffix = '!';
+        } else {
+          handle = '!';
+        }
+      }
+    }
+
+    // libyaml insists on whitespace after a tag, but example 7.2 indicates
+    // that it's not required: http://yaml.org/spec/1.2/spec.html#id2786720.
+
+    return new TagToken(_scanner.spanFrom(start), handle, suffix);
+  }
+
+  /// Scans a tag handle.
+  String _scanTagHandle({bool directive: false}) {
+    _scanner.expect('!');
+
+    var buffer = new StringBuffer('!');
+
+    // libyaml only allows word characters in tags, but the spec disagrees:
+    // http://yaml.org/spec/1.2/spec.html#ns-tag-char.
+    var start = _scanner.position;
+    while (_isTagChar) {
+      _scanner.readChar();
+    }
+    buffer.write(_scanner.substring(start));
+
+    if (_scanner.peekChar() == EXCLAMATION) {
+      buffer.writeCharCode(_scanner.readChar());
+    } else {
+      // It's either the '!' tag or not really a tag handle. If it's a %TAG
+      // directive, it's an error. If it's a tag token, it must be part of a
+      // URI.
+      if (directive && buffer.toString() != '!') _scanner.expect('!');
+    }
+
+    return buffer.toString();
+  }
+
+  /// Scans a tag URI.
+  ///
+  /// [head] is the initial portion of the tag that's already been scanned.
+  /// [flowSeparators] indicates whether the tag URI can contain flow
+  /// separators.
+  String _scanTagUri({String head, bool flowSeparators: true}) {
+    var length = head == null ? 0 : head.length;
+    var buffer = new StringBuffer();
+
+    // Copy the head if needed.
+    //
+    // Note that we don't copy the leading '!' character.
+    if (length > 1) buffer.write(head.substring(1));
+
+    // The set of characters that may appear in URI is as follows:
+    //
+    //      '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&',
+    //      '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']',
+    //      '%'.
+    //
+    // In a shorthand tag annotation, the flow separators ',', '[', and ']' are
+    // disallowed.
+    var start = _scanner.position;
+    var char = _scanner.peekChar();
+    while (_isTagChar || (flowSeparators &&
+            (char == COMMA || char == LEFT_SQUARE || char == RIGHT_SQUARE))) {
+      _scanner.readChar();
+      char = _scanner.peekChar();
+    }
+
+    // libyaml manually decodes the URL, but we don't have to do that.
+    return Uri.decodeFull(_scanner.substring(start));
+  }
+
+  /// Scans a block scalar.
+  Token _scanBlockScalar({bool literal: false}) {
+    var start = _scanner.state;
+
+    // Eat the indicator '|' or '>'.
+    _scanner.readChar();
+
+    // Check for a chomping indicator.
+    var chomping = _Chomping.CLIP;
+    var increment = 0;
+    var char = _scanner.peekChar();
+    if (char == PLUS || char == HYPHEN) {
+      chomping = char == PLUS ? _Chomping.KEEP : _Chomping.STRIP;
+      _scanner.readChar();
+
+      // Check for an indentation indicator.
+      if (_isDigit) {
+        // Check that the indentation is greater than 0.
+        if (_scanner.peekChar() == NUMBER_0) {
+          throw new YamlException(
+              "0 may not be used as an indentation indicator.",
+              _scanner.spanFrom(start));
+        }
+
+        increment = _scanner.readChar() - NUMBER_0;
+      }
+    } else if (_isDigit) {
+      // Do the same as above, but in the opposite order.
+      if (_scanner.peekChar() == NUMBER_0) {
+        throw new YamlException(
+            "0 may not be used as an indentation indicator.",
+            _scanner.spanFrom(start));
+      }
+
+      increment = _scanner.readChar() - NUMBER_0;
+
+      char = _scanner.peekChar();
+      if (char == PLUS || char == HYPHEN) {
+        chomping = char == PLUS ? _Chomping.KEEP : _Chomping.STRIP;
+        _scanner.readChar();
+      }
+    }
+
+    // Eat whitespace and comments to the end of the line.
+    _skipBlanks();
+    _skipComment();
+
+    // Check if we're at the end of the line.
+    if (!_isBreakOrEnd) {
+      throw new YamlException("Expected comment or line break.",
+          _scanner.emptySpan);
+    }
+
+    _skipLine();
+
+    // If the block scalar has an explicit indentation indicator, add that to
+    // the current indentation to get the indentation level for the scalar's
+    // contents.
+    var indent = 0;
+    if (increment != 0) {
+      indent = _indent >= 0 ? _indent + increment : increment;
+    }
+
+    // Scan the leading line breaks to determine the indentation level if
+    // needed.
+    var pair = _scanBlockScalarBreaks(indent);
+    indent = pair.first;
+    var trailingBreaks = pair.last;
+
+    // Scan the block scalar contents.
+    var buffer = new StringBuffer();
+    var leadingBreak = '';
+    var leadingBlank = false;
+    var trailingBlank = false;
+    var end = _scanner.state;
+    while (_scanner.column == indent && !_scanner.isDone) {
+      // Check for a document indicator. libyaml doesn't do this, but the spec
+      // mandates it. See example 9.5:
+      // http://yaml.org/spec/1.2/spec.html#id2801606.
+      if (_isDocumentIndicator) break;
+
+      // We are at the beginning of a non-empty line.
+
+      // Is there trailing whitespace?
+      trailingBlank = _isBlank;
+
+      // Check if we need to fold the leading line break.
+      if (!literal && leadingBreak.isNotEmpty && !leadingBlank &&
+          !trailingBlank) {
+        // Do we need to join the lines with a space?
+        if (trailingBreaks.isEmpty) buffer.writeCharCode(SP);
+      } else {
+        buffer.write(leadingBreak);
+      }
+      leadingBreak = '';
+
+      // Append the remaining line breaks.
+      buffer.write(trailingBreaks);
+
+      // Is there leading whitespace?
+      leadingBlank = _isBlank;
+
+      var startPosition = _scanner.position;
+      while (!_isBreakOrEnd) {
+        _scanner.readChar();
+      }
+      buffer.write(_scanner.substring(startPosition));
+      end = _scanner.state;
+
+      // libyaml always reads a line here, but this breaks on block scalars at
+      // the end of the document that end without newlines. See example 8.1:
+      // http://yaml.org/spec/1.2/spec.html#id2793888.
+      if (!_scanner.isDone) leadingBreak = _readLine();
+
+      // Eat the following indentation and spaces.
+      var pair = _scanBlockScalarBreaks(indent);
+      indent = pair.first;
+      trailingBreaks = pair.last;
+    }
+
+    // Chomp the tail.
+    if (chomping != _Chomping.STRIP) buffer.write(leadingBreak);
+    if (chomping == _Chomping.KEEP) buffer.write(trailingBreaks);
+
+    return new ScalarToken(_scanner.spanFrom(start, end), buffer.toString(),
+        literal ? ScalarStyle.LITERAL : ScalarStyle.FOLDED);
+  }
+
+  /// Scans indentation spaces and line breaks for a block scalar.
+  ///
+  /// Determines the intendation level if needed. Returns the new indentation
+  /// level and the text of the line breaks.
+  Pair<int, String> _scanBlockScalarBreaks(int indent) {
+    var maxIndent = 0;
+    var breaks = new StringBuffer();
+
+    while (true) {
+      while ((indent == 0 || _scanner.column < indent) &&
+          _scanner.peekChar() == SP) {
+        _scanner.readChar();
+      }
+
+      if (_scanner.column > maxIndent) maxIndent = _scanner.column;
+
+      // libyaml throws an error here if a tab character is detected, but the
+      // spec treats tabs like any other non-space character. See example 8.2:
+      // http://yaml.org/spec/1.2/spec.html#id2794311.
+
+      if (!_isBreak) break;
+      breaks.write(_readLine());
+    }
+
+    if (indent == 0) {
+      indent = maxIndent;
+      if (indent < _indent + 1) indent = _indent + 1;
+
+      // libyaml forces indent to be at least 1 here, but that doesn't seem to
+      // be supported by the spec.
+    }
+
+    return new Pair(indent, breaks.toString());
+  }
+
+  // Scans a quoted scalar.
+  Token _scanFlowScalar({bool singleQuote: false}) {
+    var start = _scanner.state;
+    var buffer = new StringBuffer();
+
+    // Eat the left quote.
+    _scanner.readChar();
+
+    while (true) {
+      // Check that there are no document indicators at the beginning of the
+      // line.
+      if (_isDocumentIndicator) {
+        _scanner.error("Unexpected document indicator.");
+      }
+
+      if (_scanner.isDone) {
+        throw new YamlException("Unexpected end of file.", _scanner.emptySpan);
+      }
+
+      var leadingBlanks = false;
+      while (!_isBlankOrEnd) {
+        var char = _scanner.peekChar();
+        if (singleQuote && char == SINGLE_QUOTE &&
+            _scanner.peekChar(1) == SINGLE_QUOTE) {
+          // An escaped single quote.
+          _scanner.readChar();
+          _scanner.readChar();
+          buffer.writeCharCode(SINGLE_QUOTE);
+        } else if (char == (singleQuote ? SINGLE_QUOTE : DOUBLE_QUOTE)) {
+          // The closing quote.
+          break;
+        } else if (!singleQuote && char == BACKSLASH && _isBreakAt(1)) {
+          // An escaped newline.
+          _scanner.readChar();
+          _skipLine();
+          leadingBlanks = true;
+          break;
+        } else if (!singleQuote && char == BACKSLASH) {
+          var escapeStart = _scanner.state;
+
+          // An escape sequence.
+          var codeLength = null;
+          switch (_scanner.peekChar(1)) {
+            case NUMBER_0:
+              buffer.writeCharCode(NULL);
+              break;
+            case LETTER_A:
+              buffer.writeCharCode(BELL);
+              break;
+            case LETTER_B:
+              buffer.writeCharCode(BACKSPACE);
+              break;
+            case LETTER_T:
+            case TAB:
+              buffer.writeCharCode(TAB);
+              break;
+            case LETTER_N:
+              buffer.writeCharCode(LF);
+              break;
+            case LETTER_V:
+              buffer.writeCharCode(VERTICAL_TAB);
+              break;
+            case LETTER_F:
+              buffer.writeCharCode(FORM_FEED);
+              break;
+            case LETTER_R:
+              buffer.writeCharCode(CR);
+              break;
+            case LETTER_E:
+              buffer.writeCharCode(ESCAPE);
+              break;
+            case SP:
+            case DOUBLE_QUOTE:
+            case SLASH:
+            case BACKSLASH:
+              // libyaml doesn't support an escaped forward slash, but it was
+              // added in YAML 1.2. See section 5.7:
+              // http://yaml.org/spec/1.2/spec.html#id2776092
+              buffer.writeCharCode(_scanner.peekChar(1));
+              break;
+            case LETTER_CAP_N:
+              buffer.writeCharCode(NEL);
+              break;
+            case UNDERSCORE:
+              buffer.writeCharCode(NBSP);
+              break;
+            case LETTER_CAP_L:
+              buffer.writeCharCode(LINE_SEPARATOR);
+              break;
+            case LETTER_CAP_P:
+              buffer.writeCharCode(PARAGRAPH_SEPARATOR);
+              break;
+            case LETTER_X:
+              codeLength = 2;
+              break;
+            case LETTER_U:
+              codeLength = 4;
+              break;
+            case LETTER_CAP_U:
+              codeLength = 8;
+              break;
+            default:
+              throw new YamlException("Unknown escape character.",
+                  _scanner.spanFrom(escapeStart));
+          }
+
+          _scanner.readChar();
+          _scanner.readChar();
+
+          if (codeLength != null) {
+            var value = 0;
+            for (var i = 0; i < codeLength; i++) {
+              if (!_isHex) {
+                _scanner.readChar();
+                throw new YamlException(
+                    "Expected $codeLength-digit hexidecimal number.",
+                    _scanner.spanFrom(escapeStart));
+              }
+
+              value = (value << 4) + _asHex(_scanner.readChar());
+            }
+
+            // Check the value and write the character.
+            if ((value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF) {
+              throw new YamlException(
+                  "Invalid Unicode character escape code.",
+                  _scanner.spanFrom(escapeStart));
+            }
+
+            buffer.writeCharCode(value);
+          }
+        } else {
+          buffer.writeCharCode(_scanner.readChar());
+        }
+      }
+
+      // Check if we're at the end of a scalar.
+      if (_scanner.peekChar() == (singleQuote ? SINGLE_QUOTE : DOUBLE_QUOTE)) {
+        break;
+      }
+
+      var whitespace = new StringBuffer();
+      var leadingBreak = '';
+      var trailingBreaks = new StringBuffer();
+      while (_isBlank || _isBreak) {
+        if (_isBlank) {
+          // Consume a space or a tab.
+          if (!leadingBlanks) {
+            whitespace.writeCharCode(_scanner.readChar());
+          } else {
+            _scanner.readChar();
+          }
+        } else {
+          // Check if it's a first line break.
+          if (!leadingBlanks) {
+            whitespace.clear();
+            leadingBreak = _readLine();
+            leadingBlanks = true;
+          } else {
+            trailingBreaks.write(_readLine());
+          }
+        }
+      }
+
+      // Join the whitespace or fold line breaks.
+      if (leadingBlanks) {
+        if (leadingBreak.isNotEmpty && trailingBreaks.isEmpty) {
+          buffer.writeCharCode(SP);
+        } else {
+          buffer.write(trailingBreaks);
+        }
+      } else {
+        buffer.write(whitespace);
+        whitespace.clear();
+      }
+    }
+
+    // Eat the right quote.
+    _scanner.readChar();
+
+    return new ScalarToken(_scanner.spanFrom(start), buffer.toString(),
+        singleQuote ? ScalarStyle.SINGLE_QUOTED : ScalarStyle.DOUBLE_QUOTED);
+  }
+
+  /// Scans a plain scalar.
+  Token _scanPlainScalar() {
+    var start = _scanner.state;
+    var end = _scanner.state;
+    var buffer = new StringBuffer();
+    var leadingBreak = '';
+    var trailingBreaks = '';
+    var whitespace = new StringBuffer();
+    var indent = _indent + 1;
+
+    while (true) {
+      // Check for a document indicator.
+      if (_isDocumentIndicator) break;
+
+      // Check for a comment.
+      if (_scanner.peekChar() == HASH) break;
+
+      if (_isPlainChar) {
+        // Join the whitespace or fold line breaks.
+        if (leadingBreak.isNotEmpty) {
+          if (trailingBreaks.isEmpty) {
+            buffer.writeCharCode(SP);
+          } else {
+            buffer.write(trailingBreaks);
+          }
+          leadingBreak = '';
+          trailingBreaks = '';
+        } else {
+          buffer.write(whitespace);
+          whitespace.clear();
+        }
+      }
+
+      // libyaml's notion of valid identifiers differs substantially from YAML
+      // 1.2's. We use [_isPlainChar] instead of libyaml's character here.
+      var startPosition = _scanner.position;
+      while (_isPlainChar) {
+        _scanner.readChar();
+      }
+      buffer.write(_scanner.substring(startPosition));
+      end = _scanner.state;
+
+      // Is it the end?
+      if (!_isBlank && !_isBreak) break;
+
+      while (_isBlank || _isBreak) {
+        if (_isBlank) {
+          // Check for a tab character messing up the intendation.
+          if (leadingBreak.isNotEmpty && _scanner.column < indent &&
+              _scanner.peekChar() == TAB) {
+            _scanner.error("Expected a space but found a tab.", length: 1);
+          }
+
+          if (leadingBreak.isEmpty) {
+            whitespace.writeCharCode(_scanner.readChar());
+          } else {
+            _scanner.readChar();
+          }
+        } else {
+          // Check if it's a first line break.
+          if (leadingBreak.isEmpty) {
+            leadingBreak = _readLine();
+            whitespace.clear();
+          } else {
+            trailingBreaks = _readLine();
+          }
+        }
+      }
+
+      // Check the indentation level.
+      if (_inBlockContext && _scanner.column < indent) break;
+    }
+
+    // Allow a simple key after a plain scalar with leading blanks.
+    if (leadingBreak.isNotEmpty) _simpleKeyAllowed = true;
+
+    return new ScalarToken(_scanner.spanFrom(start, end), buffer.toString(),
+        ScalarStyle.PLAIN);
+  }
+
+  /// Moves past the current line break, if there is one.
+  void _skipLine() {
+    var char = _scanner.peekChar();
+    if (char != CR && char != LF) return;
+    _scanner.readChar();
+    if (char == CR && _scanner.peekChar() == LF) _scanner.readChar();
+  }
+
+  // Moves past the current line break and returns a newline.
+  String _readLine() {
+    var char = _scanner.peekChar();
+
+    // libyaml supports NEL, PS, and LS characters as line separators, but this
+    // is explicitly forbidden in section 5.4 of the YAML spec.
+    if (char != CR && char != LF) {
+      throw new YamlException("Expected newline.", _scanner.emptySpan);
+    }
+
+    _scanner.readChar();
+    // CR LF | CR | LF -> LF
+    if (char == CR && _scanner.peekChar() == LF) _scanner.readChar();
+    return "\n";
+  }
+
+  // Returns whether the character at [offset] is whitespace.
+  bool _isBlankAt(int offset) {
+    var char = _scanner.peekChar(offset);
+    return char == SP || char == TAB;
+  }
+
+  // Returns whether the character at [offset] is a line break.
+  bool _isBreakAt(int offset) {
+    // Libyaml considers NEL, LS, and PS to be line breaks as well, but that's
+    // contrary to the spec.
+    var char = _scanner.peekChar(offset);
+    return char == CR || char == LF;
+  }
+
+  // Returns whether the character at [offset] is whitespace or past the end of
+  // the source.
+  bool _isBlankOrEndAt(int offset) {
+    var char = _scanner.peekChar(offset);
+    return char == null || char == SP || char == TAB || char == CR ||
+        char == LF;
+  }
+
+  /// Returns whether the character at [offset] is a plain character.
+  ///
+  /// See http://yaml.org/spec/1.2/spec.html#ns-plain-char(c).
+  bool _isPlainCharAt(int offset) {
+    switch (_scanner.peekChar(offset)) {
+      case COLON:
+        return _isPlainSafeAt(offset + 1);
+      case HASH:
+        var previous = _scanner.peekChar(offset - 1);
+        return previous != SP && previous != TAB;
+      default:
+        return _isPlainSafeAt(offset);
+    }
+  }
+
+  /// Returns whether the character at [offset] is a plain-safe character.
+  ///
+  /// See http://yaml.org/spec/1.2/spec.html#ns-plain-safe(c).
+  bool _isPlainSafeAt(int offset) {
+    var char = _scanner.peekChar(offset);
+    switch (char) {
+      case COMMA:
+      case LEFT_SQUARE:
+      case RIGHT_SQUARE:
+      case LEFT_CURLY:
+      case RIGHT_CURLY:
+        // These characters are delimiters in a flow context and thus are only
+        // safe in a block context.
+        return _inBlockContext;
+      case SP:
+      case TAB:
+      case LF:
+      case CR:
+      case BOM:
+        return false;
+      case NEL:
+        return true;
+      default:
+        return char != null &&
+          ((char >= 0x00020 && char <= 0x00007E) ||
+           (char >= 0x000A0 && char <= 0x00D7FF) ||
+           (char >= 0x0E000 && char <= 0x00FFFD) ||
+           (char >= 0x10000 && char <= 0x10FFFF));
+    }
+  }
+
+  /// Returns the hexidecimal value of [char].
+  int _asHex(int char) {
+    if (char <= NUMBER_9) return char - NUMBER_0;
+    if (char <= LETTER_CAP_F) return 10 + char - LETTER_CAP_A;
+    return 10 + char - LETTER_A;
+  }
+
+  /// Moves the scanner past any blank characters.
+  void _skipBlanks() {
+    while (_isBlank) {
+      _scanner.readChar();
+    }
+  }
+
+  /// Moves the scanner past a comment, if one starts at the current position.
+  void _skipComment() {
+    if (_scanner.peekChar() != HASH) return;
+    while (!_isBreakOrEnd) {
+      _scanner.readChar();
+    }
+  }
+}
+
+/// A record of the location of a potential simple key.
+class _SimpleKey {
+  /// The index of the token that begins the simple key.
+  ///
+  /// This is the index relative to all tokens emitted, rather than relative to
+  /// [_tokens].
+  final int tokenNumber;
+
+  /// The source location of the beginning of the simple key.
+  ///
+  /// This is used for error reporting and for determining when a simple key is
+  /// no longer on the current line.
+  final SourceLocation location;
+
+  /// Whether this key must exist for the document to be scanned.
+  final bool required;
+
+  _SimpleKey(this.tokenNumber, this.location, {bool required})
+      : required = required;
+}
+
+/// An enum of chomping indicators that describe how to handle trailing
+/// whitespace for a block scalar.
+///
+/// See http://yaml.org/spec/1.2/spec.html#id2794534.
+class _Chomping {
+  /// All trailing whitespace is discarded.
+  static const STRIP = const _Chomping("STRIP");
+
+  /// A single trailing newline is retained.
+  static const CLIP = const _Chomping("CLIP");
+
+  /// All trailing whitespace is preserved.
+  static const KEEP = const _Chomping("KEEP");
+
+  final String name;
+
+  const _Chomping(this.name);
+
+  String toString() => name;
+}
diff --git a/yaml/lib/src/style.dart b/yaml/lib/src/style.dart
new file mode 100644
index 0000000..6305fce
--- /dev/null
+++ b/yaml/lib/src/style.dart
@@ -0,0 +1,73 @@
+// 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 yaml.style;
+
+/// An enum of source scalar styles.
+class ScalarStyle {
+  /// No source style was specified.
+  ///
+  /// This usually indicates a scalar constructed with [YamlScalar.wrap].
+  static const ANY = const ScalarStyle._("ANY");
+
+  /// The plain scalar style, unquoted and without a prefix.
+  ///
+  /// See http://yaml.org/spec/1.2/spec.html#style/flow/plain.
+  static const PLAIN = const ScalarStyle._("PLAIN");
+
+  /// The literal scalar style, with a `|` prefix.
+  ///
+  /// See http://yaml.org/spec/1.2/spec.html#id2795688.
+  static const LITERAL = const ScalarStyle._("LITERAL");
+
+
+  /// The folded scalar style, with a `>` prefix.
+  ///
+  /// See http://yaml.org/spec/1.2/spec.html#id2796251.
+  static const FOLDED = const ScalarStyle._("FOLDED");
+
+  /// The single-quoted scalar style.
+  ///
+  /// See http://yaml.org/spec/1.2/spec.html#style/flow/single-quoted.
+  static const SINGLE_QUOTED = const ScalarStyle._("SINGLE_QUOTED");
+
+  /// The double-quoted scalar style.
+  ///
+  /// See http://yaml.org/spec/1.2/spec.html#style/flow/double-quoted.
+  static const DOUBLE_QUOTED = const ScalarStyle._("DOUBLE_QUOTED");
+
+  final String name;
+
+  /// Whether this is a quoted style ([SINGLE_QUOTED] or [DOUBLE_QUOTED]).
+  bool get isQuoted => this == SINGLE_QUOTED || this == DOUBLE_QUOTED;
+
+  const ScalarStyle._(this.name);
+
+  String toString() => name;
+}
+
+/// An enum of collection styles.
+class CollectionStyle {
+  /// No source style was specified.
+  ///
+  /// This usually indicates a collection constructed with [YamlList.wrap] or
+  /// [YamlMap.wrap].
+  static const ANY = const CollectionStyle._("ANY");
+
+  /// The indentation-based block style.
+  ///
+  /// See http://yaml.org/spec/1.2/spec.html#id2797293.
+  static const BLOCK = const CollectionStyle._("BLOCK");
+
+  /// The delimiter-based block style.
+  ///
+  /// See http://yaml.org/spec/1.2/spec.html#id2790088.
+  static const FLOW = const CollectionStyle._("FLOW");
+
+  final String name;
+
+  const CollectionStyle._(this.name);
+
+  String toString() => name;
+}
diff --git a/yaml/lib/src/token.dart b/yaml/lib/src/token.dart
new file mode 100644
index 0000000..20ae547
--- /dev/null
+++ b/yaml/lib/src/token.dart
@@ -0,0 +1,148 @@
+// 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 yaml.token;
+
+import 'package:source_span/source_span.dart';
+
+import 'style.dart';
+
+/// A token emitted by a [Scanner].
+class Token {
+  /// The token type.
+  final TokenType type;
+
+  /// The span associated with the token.
+  final FileSpan span;
+
+  Token(this.type, this.span);
+
+  String toString() => type.toString();
+}
+
+/// A token representing a `%YAML` directive.
+class VersionDirectiveToken implements Token {
+  get type => TokenType.VERSION_DIRECTIVE;
+  final FileSpan span;
+
+  /// The declared major version of the document.
+  final int major;
+
+  /// The declared minor version of the document.
+  final int minor;
+
+  VersionDirectiveToken(this.span, this.major, this.minor);
+
+  String toString() => "VERSION_DIRECTIVE $major.$minor";
+}
+
+/// A token representing a `%TAG` directive.
+class TagDirectiveToken implements Token {
+  get type => TokenType.TAG_DIRECTIVE;
+  final FileSpan span;
+
+  /// The tag handle used in the document.
+  final String handle;
+
+  /// The tag prefix that the handle maps to.
+  final String prefix;
+
+  TagDirectiveToken(this.span, this.handle, this.prefix);
+
+  String toString() => "TAG_DIRECTIVE $handle $prefix";
+}
+
+/// A token representing an anchor (`&foo`).
+class AnchorToken implements Token {
+  get type => TokenType.ANCHOR;
+  final FileSpan span;
+
+  /// The name of the anchor.
+  final String name;
+
+  AnchorToken(this.span, this.name);
+
+  String toString() => "ANCHOR $name";
+}
+
+/// A token representing an alias (`*foo`).
+class AliasToken implements Token {
+  get type => TokenType.ALIAS;
+  final FileSpan span;
+
+  /// The name of the anchor.
+  final String name;
+
+  AliasToken(this.span, this.name);
+
+  String toString() => "ALIAS $name";
+}
+
+/// A token representing a tag (`!foo`).
+class TagToken implements Token {
+  get type => TokenType.TAG;
+  final FileSpan span;
+
+  /// The tag handle.
+  final String handle;
+
+  /// The tag suffix, or `null`.
+  final String suffix;
+
+  TagToken(this.span, this.handle, this.suffix);
+
+  String toString() => "TAG $handle $suffix";
+}
+
+/// A tkoen representing a scalar value.
+class ScalarToken implements Token {
+  get type => TokenType.SCALAR;
+  final FileSpan span;
+
+  /// The contents of the scalar.
+  final String value;
+
+  /// The style of the scalar in the original source.
+  final ScalarStyle style;
+
+  ScalarToken(this.span, this.value, this.style);
+
+  String toString() => "SCALAR $style \"$value\"";
+}
+
+/// An enum of types of [Token] object.
+class TokenType {
+  static const STREAM_START = const TokenType._("STREAM_START");
+  static const STREAM_END = const TokenType._("STREAM_END");
+
+  static const VERSION_DIRECTIVE = const TokenType._("VERSION_DIRECTIVE");
+  static const TAG_DIRECTIVE = const TokenType._("TAG_DIRECTIVE");
+  static const DOCUMENT_START = const TokenType._("DOCUMENT_START");
+  static const DOCUMENT_END = const TokenType._("DOCUMENT_END");
+
+  static const BLOCK_SEQUENCE_START = const TokenType._("BLOCK_SEQUENCE_START");
+  static const BLOCK_MAPPING_START = const TokenType._("BLOCK_MAPPING_START");
+  static const BLOCK_END = const TokenType._("BLOCK_END");
+
+  static const FLOW_SEQUENCE_START = const TokenType._("FLOW_SEQUENCE_START");
+  static const FLOW_SEQUENCE_END = const TokenType._("FLOW_SEQUENCE_END");
+  static const FLOW_MAPPING_START = const TokenType._("FLOW_MAPPING_START");
+  static const FLOW_MAPPING_END = const TokenType._("FLOW_MAPPING_END");
+
+  static const BLOCK_ENTRY = const TokenType._("BLOCK_ENTRY");
+  static const FLOW_ENTRY = const TokenType._("FLOW_ENTRY");
+  static const KEY = const TokenType._("KEY");
+  static const VALUE = const TokenType._("VALUE");
+
+  static const ALIAS = const TokenType._("ALIAS");
+  static const ANCHOR = const TokenType._("ANCHOR");
+  static const TAG = const TokenType._("TAG");
+  static const SCALAR = const TokenType._("SCALAR");
+
+  final String name;
+
+  const TokenType._(this.name);
+
+  String toString() => name;
+}
diff --git a/yaml/lib/src/utils.dart b/yaml/lib/src/utils.dart
new file mode 100644
index 0000000..445221f
--- /dev/null
+++ b/yaml/lib/src/utils.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2013, 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 yaml.utils;
+
+import 'package:source_span/source_span.dart';
+
+/// A pair of values.
+class Pair<E, F> {
+  final E first;
+  final F last;
+
+  Pair(this.first, this.last);
+
+  String toString() => '($first, $last)';
+}
+
+/// Print a warning.
+///
+/// If [span] is passed, associates the warning with that span.
+void warn(String message, [SourceSpan span]) =>
+    yamlWarningCallback(message, span);
+
+/// A callback for emitting a warning.
+///
+/// [message] is the text of the warning. If [span] is passed, it's the portion
+/// of the document that the warning is associated with and should be included
+/// in the printed warning.
+typedef YamlWarningCallback(String message, [SourceSpan span]);
+
+/// A callback for emitting a warning.
+///
+/// In a very few cases, the YAML spec indicates that an implementation should
+/// emit a warning. To do so, it calls this callback. The default implementation
+/// prints a message using [print].
+YamlWarningCallback yamlWarningCallback = (message, [span]) {
+  // TODO(nweiz): Print to stderr with color when issue 6943 is fixed and
+  // dart:io is available.
+  if (span != null) message = span.message(message);
+  print(message);
+};
+
+
diff --git a/yaml/lib/src/yaml_document.dart b/yaml/lib/src/yaml_document.dart
new file mode 100644
index 0000000..4c249a0
--- /dev/null
+++ b/yaml/lib/src/yaml_document.dart
@@ -0,0 +1,67 @@
+// 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 yaml.yaml_document;
+
+import 'dart:collection';
+
+import 'package:source_span/source_span.dart';
+
+import 'yaml_node.dart';
+
+/// A YAML document, complete with metadata.
+class YamlDocument {
+  /// The contents of the document.
+  final YamlNode contents;
+
+  /// The span covering the entire document.
+  final SourceSpan span;
+
+  /// The version directive for the document, if any.
+  final VersionDirective versionDirective;
+
+  /// The tag directives for the document.
+  final List<TagDirective> tagDirectives;
+
+  /// Whether the beginning of the document was implicit (versus explicit via
+  /// `===`).
+  final bool startImplicit;
+
+  /// Whether the end of the document was implicit (versus explicit via `...`).
+  final bool endImplicit;
+
+  /// Users of the library should not use this constructor.
+  YamlDocument.internal(this.contents, this.span, this.versionDirective,
+          List<TagDirective> tagDirectives, {this.startImplicit: false,
+          this.endImplicit: false})
+      : tagDirectives = new UnmodifiableListView(tagDirectives);
+
+  String toString() => contents.toString();
+}
+
+/// A directive indicating which version of YAML a document was written to.
+class VersionDirective {
+  /// The major version number.
+  final int major;
+
+  /// The minor version number.
+  final int minor;
+
+  VersionDirective(this.major, this.minor);
+
+  String toString() => "%YAML $major.$minor";
+}
+
+/// A directive describing a custom tag handle.
+class TagDirective {
+  /// The handle for use in the document.
+  final String handle;
+
+  /// The prefix that the handle maps to.
+  final String prefix;
+
+  TagDirective(this.handle, this.prefix);
+
+  String toString() => "%TAG $handle $prefix";
+}
diff --git a/yaml/lib/src/yaml_exception.dart b/yaml/lib/src/yaml_exception.dart
new file mode 100644
index 0000000..8689953
--- /dev/null
+++ b/yaml/lib/src/yaml_exception.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2013, 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 yaml.exception;
+
+import 'package:source_span/source_span.dart';
+
+/// An error thrown by the YAML processor.
+class YamlException extends SourceSpanFormatException {
+  YamlException(String message, SourceSpan span)
+      : super(message, span);
+}
+
diff --git a/yaml/lib/src/yaml_node.dart b/yaml/lib/src/yaml_node.dart
new file mode 100644
index 0000000..027bc1b
--- /dev/null
+++ b/yaml/lib/src/yaml_node.dart
@@ -0,0 +1,178 @@
+// Copyright (c) 2012, 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 yaml.yaml_node;
+
+import 'dart:collection' as collection;
+
+import 'package:collection/collection.dart';
+import 'package:source_span/source_span.dart';
+
+import 'null_span.dart';
+import 'style.dart';
+import 'yaml_node_wrapper.dart';
+
+/// An interface for parsed nodes from a YAML source tree.
+///
+/// [YamlMap]s and [YamlList]s implement this interface in addition to the
+/// normal [Map] and [List] interfaces, so any maps and lists will be
+/// [YamlNode]s regardless of how they're accessed.
+///
+/// Scalars values like strings and numbers, on the other hand, don't have this
+/// interface by default. Instead, they can be accessed as [YamlScalar]s via
+/// [YamlMap.nodes] or [YamlList.nodes].
+abstract class YamlNode {
+  /// The source span for this node.
+  ///
+  /// [SourceSpan.message] can be used to produce a human-friendly message about
+  /// this node.
+  SourceSpan get span => _span;
+
+  SourceSpan _span;
+
+  /// The inner value of this node.
+  ///
+  /// For [YamlScalar]s, this will return the wrapped value. For [YamlMap] and
+  /// [YamlList], it will return [this], since they already implement [Map] and
+  /// [List], respectively.
+  get value;
+}
+
+/// A read-only [Map] parsed from YAML.
+class YamlMap extends YamlNode with collection.MapMixin, UnmodifiableMapMixin  {
+  /// A view of [this] where the keys and values are guaranteed to be
+  /// [YamlNode]s.
+  ///
+  /// The key type is `dynamic` to allow values to be accessed using
+  /// non-[YamlNode] keys, but [Map.keys] and [Map.forEach] will always expose
+  /// them as [YamlNode]s. For example, for `{"foo": [1, 2, 3]}` [nodes] will be
+  /// a map from a [YamlScalar] to a [YamlList], but since the key type is
+  /// `dynamic` `map.nodes["foo"]` will still work.
+  final Map<dynamic, YamlNode> nodes;
+
+  /// The style used for the map in the original document.
+  final CollectionStyle style;
+
+  Map get value => this;
+
+  Iterable get keys => nodes.keys.map((node) => node.value);
+
+  /// Creates an empty YamlMap.
+  ///
+  /// This map's [span] won't have useful location information. However, it will
+  /// have a reasonable implementation of [SourceSpan.message]. If [sourceUrl]
+  /// is passed, it's used as the [SourceSpan.sourceUrl].
+  ///
+  /// [sourceUrl] may be either a [String], a [Uri], or `null`.
+  factory YamlMap({sourceUrl}) =>
+      new YamlMapWrapper(const {}, sourceUrl);
+
+  /// Wraps a Dart map so that it can be accessed (recursively) like a
+  /// [YamlMap].
+  ///
+  /// Any [SourceSpan]s returned by this map or its children will be dummies
+  /// without useful location information. However, they will have a reasonable
+  /// implementation of [SourceSpan.getLocationMessage]. If [sourceUrl] is
+  /// passed, it's used as the [SourceSpan.sourceUrl].
+  ///
+  /// [sourceUrl] may be either a [String], a [Uri], or `null`.
+  factory YamlMap.wrap(Map dartMap, {sourceUrl}) =>
+      new YamlMapWrapper(dartMap, sourceUrl);
+
+  /// Users of the library should not use this constructor.
+  YamlMap.internal(Map<dynamic, YamlNode> nodes, SourceSpan span, this.style)
+      : nodes = new UnmodifiableMapView<dynamic, YamlNode>(nodes) {
+    _span = span;
+  }
+
+  operator [](key) {
+    var node = nodes[key];
+    return node == null ? null : node.value;
+  }
+}
+
+// TODO(nweiz): Use UnmodifiableListMixin when issue 18970 is fixed.
+/// A read-only [List] parsed from YAML.
+class YamlList extends YamlNode with collection.ListMixin {
+  final List<YamlNode> nodes;
+
+  /// The style used for the list in the original document.
+  final CollectionStyle style;
+
+  List get value => this;
+
+  int get length => nodes.length;
+
+  set length(int index) {
+    throw new UnsupportedError("Cannot modify an unmodifiable List");
+  }
+
+  /// Creates an empty YamlList.
+  ///
+  /// This list's [span] won't have useful location information. However, it
+  /// will have a reasonable implementation of [SourceSpan.message]. If
+  /// [sourceUrl] is passed, it's used as the [SourceSpan.sourceUrl].
+  ///
+  /// [sourceUrl] may be either a [String], a [Uri], or `null`.
+  factory YamlList({sourceUrl}) =>
+      new YamlListWrapper(const [], sourceUrl);
+
+  /// Wraps a Dart list so that it can be accessed (recursively) like a
+  /// [YamlList].
+  ///
+  /// Any [SourceSpan]s returned by this list or its children will be dummies
+  /// without useful location information. However, they will have a reasonable
+  /// implementation of [SourceSpan.getLocationMessage]. If [sourceUrl] is
+  /// passed, it's used as the [SourceSpan.sourceUrl].
+  ///
+  /// [sourceUrl] may be either a [String], a [Uri], or `null`.
+  factory YamlList.wrap(List dartList, {sourceUrl}) =>
+      new YamlListWrapper(dartList, sourceUrl);
+
+  /// Users of the library should not use this constructor.
+  YamlList.internal(List<YamlNode> nodes, SourceSpan span, this.style)
+      : nodes = new UnmodifiableListView<YamlNode>(nodes) {
+    _span = span;
+  }
+
+  operator [](int index) => nodes[index].value;
+
+  operator []=(int index, value) {
+    throw new UnsupportedError("Cannot modify an unmodifiable List");
+  }
+}
+
+/// A wrapped scalar value parsed from YAML.
+class YamlScalar extends YamlNode {
+  final value;
+
+  /// The style used for the scalar in the original document.
+  final ScalarStyle style;
+
+  /// Wraps a Dart value in a [YamlScalar].
+  ///
+  /// This scalar's [span] won't have useful location information. However, it
+  /// will have a reasonable implementation of [SourceSpan.message]. If
+  /// [sourceUrl] is passed, it's used as the [SourceSpan.sourceUrl].
+  ///
+  /// [sourceUrl] may be either a [String], a [Uri], or `null`.
+  YamlScalar.wrap(this.value, {sourceUrl})
+      : style = ScalarStyle.ANY {
+    _span = new NullSpan(sourceUrl);
+  }
+
+  /// Users of the library should not use this constructor.
+  YamlScalar.internal(this.value, SourceSpan span, this.style) {
+    _span = span;
+  }
+
+  String toString() => value.toString();
+}
+
+/// Sets the source span of a [YamlNode].
+///
+/// This method is not exposed publicly.
+void setSpan(YamlNode node, SourceSpan span) {
+  node._span = span;
+}
diff --git a/yaml/lib/src/yaml_node_wrapper.dart b/yaml/lib/src/yaml_node_wrapper.dart
new file mode 100644
index 0000000..be96ba4
--- /dev/null
+++ b/yaml/lib/src/yaml_node_wrapper.dart
@@ -0,0 +1,155 @@
+// 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 yaml.yaml_node_wrapper;
+
+import 'dart:collection';
+
+import 'package:collection/collection.dart' as pkg_collection;
+import 'package:source_span/source_span.dart';
+
+import 'null_span.dart';
+import 'style.dart';
+import 'yaml_node.dart';
+
+/// A wrapper that makes a normal Dart map behave like a [YamlMap].
+class YamlMapWrapper extends MapBase
+    with pkg_collection.UnmodifiableMapMixin
+    implements YamlMap {
+  final CollectionStyle style = CollectionStyle.ANY;
+
+  final Map _dartMap;
+
+  final SourceSpan span;
+
+  final Map<dynamic, YamlNode> nodes;
+
+  Map get value => this;
+
+  Iterable get keys => _dartMap.keys;
+
+  YamlMapWrapper(Map dartMap, sourceUrl)
+      : this._(dartMap, new NullSpan(sourceUrl));
+
+  YamlMapWrapper._(Map dartMap, SourceSpan span)
+      : _dartMap = dartMap,
+        span = span,
+        nodes = new _YamlMapNodes(dartMap, span);
+
+  operator [](Object key) {
+    var value = _dartMap[key];
+    if (value is Map) return new YamlMapWrapper._(value, span);
+    if (value is List) return new YamlListWrapper._(value, span);
+    return value;
+  }
+
+  int get hashCode => _dartMap.hashCode;
+
+  operator ==(Object other) =>
+      other is YamlMapWrapper && other._dartMap == _dartMap;
+}
+
+/// The implementation of [YamlMapWrapper.nodes] as a wrapper around the Dart
+/// map.
+class _YamlMapNodes extends MapBase<dynamic, YamlNode>
+    with pkg_collection.UnmodifiableMapMixin<dynamic, YamlNode> {
+  final Map _dartMap;
+
+  final SourceSpan _span;
+
+  Iterable get keys => _dartMap.keys.map((key) =>
+      new YamlScalar.internal(key, _span, ScalarStyle.ANY));
+
+  _YamlMapNodes(this._dartMap, this._span);
+
+  YamlNode operator [](Object key) {
+    // Use "as" here because key being assigned to invalidates type propagation.
+    if (key is YamlScalar) key = (key as YamlScalar).value;
+    if (!_dartMap.containsKey(key)) return null;
+    return _nodeForValue(_dartMap[key], _span);
+  }
+
+  int get hashCode => _dartMap.hashCode;
+
+  operator ==(Object other) =>
+      other is _YamlMapNodes && other._dartMap == _dartMap;
+}
+
+// TODO(nweiz): Use UnmodifiableListMixin when issue 18970 is fixed.
+/// A wrapper that makes a normal Dart list behave like a [YamlList].
+class YamlListWrapper extends ListBase implements YamlList {
+  final CollectionStyle style = CollectionStyle.ANY;
+
+  final List _dartList;
+
+  final SourceSpan span;
+
+  final List<YamlNode> nodes;
+
+  List get value => this;
+
+  int get length => _dartList.length;
+
+  set length(int index) {
+    throw new UnsupportedError("Cannot modify an unmodifiable List.");
+  }
+
+  YamlListWrapper(List dartList, sourceUrl)
+      : this._(dartList, new NullSpan(sourceUrl));
+
+  YamlListWrapper._(List dartList, SourceSpan span)
+      : _dartList = dartList,
+        span = span,
+        nodes = new _YamlListNodes(dartList, span);
+
+  operator [](int index) {
+    var value = _dartList[index];
+    if (value is Map) return new YamlMapWrapper._(value, span);
+    if (value is List) return new YamlListWrapper._(value, span);
+    return value;
+  }
+
+  operator []=(int index, value) {
+    throw new UnsupportedError("Cannot modify an unmodifiable List.");
+  }
+
+  int get hashCode => _dartList.hashCode;
+
+  operator ==(Object other) =>
+      other is YamlListWrapper && other._dartList == _dartList;
+}
+
+// TODO(nweiz): Use UnmodifiableListMixin when issue 18970 is fixed.
+/// The implementation of [YamlListWrapper.nodes] as a wrapper around the Dart
+/// list.
+class _YamlListNodes extends ListBase<YamlNode> {
+  final List _dartList;
+
+  final SourceSpan _span;
+
+  int get length => _dartList.length;
+
+  set length(int index) {
+    throw new UnsupportedError("Cannot modify an unmodifiable List.");
+  }
+
+  _YamlListNodes(this._dartList, this._span);
+
+  YamlNode operator [](int index) => _nodeForValue(_dartList[index], _span);
+
+  operator []=(int index, value) {
+    throw new UnsupportedError("Cannot modify an unmodifiable List.");
+  }
+
+  int get hashCode => _dartList.hashCode;
+
+  operator ==(Object other) =>
+      other is _YamlListNodes && other._dartList == _dartList;
+}
+
+YamlNode _nodeForValue(value, SourceSpan span) {
+  if (value is Map) return new YamlMapWrapper._(value, span);
+  if (value is List) return new YamlListWrapper._(value, span);
+  return new YamlScalar.internal(value, span, ScalarStyle.ANY);
+}
diff --git a/yaml/lib/yaml.dart b/yaml/lib/yaml.dart
new file mode 100644
index 0000000..9af329a
--- /dev/null
+++ b/yaml/lib/yaml.dart
@@ -0,0 +1,112 @@
+// Copyright (c) 2012, 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 yaml;
+
+import 'src/loader.dart';
+import 'src/style.dart';
+import 'src/yaml_document.dart';
+import 'src/yaml_exception.dart';
+import 'src/yaml_node.dart';
+
+export 'src/style.dart';
+export 'src/utils.dart' show YamlWarningCallback, yamlWarningCallback;
+export 'src/yaml_document.dart';
+export 'src/yaml_exception.dart';
+export 'src/yaml_node.dart' hide setSpan;
+
+/// Loads a single document from a YAML string.
+///
+/// If the string contains more than one document, this throws a
+/// [YamlException]. In future releases, this will become an [ArgumentError].
+///
+/// The return value is mostly normal Dart objects. However, since YAML mappings
+/// support some key types that the default Dart map implementation doesn't
+/// (NaN, lists, and maps), all maps in the returned document are [YamlMap]s.
+/// These have a few small behavioral differences from the default Map
+/// implementation; for details, see the [YamlMap] class.
+///
+/// In future versions, maps will instead be [HashMap]s with a custom equality
+/// operation.
+///
+/// If [sourceUrl] is passed, it's used as the URL from which the YAML
+/// originated for error reporting. It can be a [String], a [Uri], or `null`.
+loadYaml(String yaml, {sourceUrl}) =>
+    loadYamlNode(yaml, sourceUrl: sourceUrl).value;
+
+/// Loads a single document from a YAML string as a [YamlNode].
+///
+/// This is just like [loadYaml], except that where [loadYaml] would return a
+/// normal Dart value this returns a [YamlNode] instead. This allows the caller
+/// to be confident that the return value will always be a [YamlNode].
+YamlNode loadYamlNode(String yaml, {sourceUrl}) =>
+    loadYamlDocument(yaml, sourceUrl: sourceUrl).contents;
+
+/// Loads a single document from a YAML string as a [YamlDocument].
+///
+/// This is just like [loadYaml], except that where [loadYaml] would return a
+/// normal Dart value this returns a [YamlDocument] instead. This allows the
+/// caller to access document metadata.
+YamlDocument loadYamlDocument(String yaml, {sourceUrl}) {
+  var loader = new Loader(yaml, sourceUrl: sourceUrl);
+  var document = loader.load();
+  if (document == null) {
+    return new YamlDocument.internal(
+        new YamlScalar.internal(null, loader.span, ScalarStyle.ANY),
+        loader.span, null, const []);
+  }
+
+  var nextDocument = loader.load();
+  if (nextDocument != null) {
+    throw new YamlException("Only expected one document.", nextDocument.span);
+  }
+
+  return document;
+}
+
+/// Loads a stream of documents from a YAML string.
+///
+/// The return value is mostly normal Dart objects. However, since YAML mappings
+/// support some key types that the default Dart map implementation doesn't
+/// (NaN, lists, and maps), all maps in the returned document are [YamlMap]s.
+/// These have a few small behavioral differences from the default Map
+/// implementation; for details, see the [YamlMap] class.
+///
+/// In future versions, maps will instead be [HashMap]s with a custom equality
+/// operation.
+///
+/// If [sourceUrl] is passed, it's used as the URL from which the YAML
+/// originated for error reporting. It can be a [String], a [Uri], or `null`.
+YamlList loadYamlStream(String yaml, {sourceUrl}) {
+  var loader = new Loader(yaml, sourceUrl: sourceUrl);
+
+  var documents = [];
+  var document = loader.load();
+  while (document != null) {
+    documents.add(document);
+    document = loader.load();
+  }
+
+  return new YamlList.internal(
+      documents.map((document) => document.contents).toList(),
+      loader.span,
+      CollectionStyle.ANY);
+}
+
+/// Loads a stream of documents from a YAML string.
+///
+/// This is like [loadYamlStream], except that it returns [YamlDocument]s with
+/// metadata wrapping the document contents.
+List<YamlDocument> loadYamlDocuments(String yaml, {sourceUrl}) {
+  var loader = new Loader(yaml, sourceUrl: sourceUrl);
+
+  var documents = [];
+  var document = loader.load();
+  while (document != null) {
+    documents.add(document);
+    document = loader.load();
+  }
+
+  return documents;
+}
diff --git a/yaml/pubspec.yaml b/yaml/pubspec.yaml
new file mode 100644
index 0000000..88650fc
--- /dev/null
+++ b/yaml/pubspec.yaml
@@ -0,0 +1,14 @@
+name: yaml
+version: 2.1.2
+author: "Dart Team <misc@dartlang.org>"
+homepage: http://www.dartlang.org
+description: A parser for YAML.
+dependencies:
+  collection: ">=1.1.0 <2.0.0"
+  path: ">=1.2.0 <2.0.0"
+  string_scanner: ">=0.1.3 <0.2.0"
+  source_span: ">=1.0.0 <2.0.0"
+dev_dependencies:
+  unittest: ">=0.9.0 <0.12.0"
+environment:
+  sdk: '>=1.5.0 <2.0.0'